ceremonyclient/node/consensus/time/data_time_reel_test.go

427 lines
10 KiB
Go
Raw Permalink Normal View History

2024-02-13 07:04:56 +00:00
package time_test
import (
2024-02-21 08:10:23 +00:00
"bytes"
2024-02-13 07:04:56 +00:00
"fmt"
"strings"
"sync"
"testing"
2024-02-16 09:42:37 +00:00
gotime "time"
2024-02-13 07:04:56 +00:00
"github.com/cloudflare/circl/sign/ed448"
"github.com/iden3/go-iden3-crypto/poseidon"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"source.quilibrium.com/quilibrium/monorepo/node/config"
"source.quilibrium.com/quilibrium/monorepo/node/consensus/time"
qcrypto "source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/keys"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
"source.quilibrium.com/quilibrium/monorepo/node/store"
"source.quilibrium.com/quilibrium/monorepo/node/tries"
)
func generateTestProvers() (
keys.KeyManager,
[]peer.ID,
[][]byte,
[][]byte,
map[string]string,
*tries.RollingFrecencyCritbitTrie,
) {
keyManager := keys.NewInMemoryKeyManager()
peers := []peer.ID{}
pubKeys := [][]byte{}
privKeys := [][]byte{}
addrMap := map[string]string{}
for i := 0; i < 1000; i++ {
keyManager.CreateSigningKey(
fmt.Sprintf("test-key-%d", i),
keys.KeyTypeEd448,
)
k, err := keyManager.GetRawKey(fmt.Sprintf("test-key-%d", i))
if err != nil {
panic(err)
}
privKey, err := crypto.UnmarshalEd448PrivateKey([]byte(k.PrivateKey))
if err != nil {
panic(err)
}
privKeys = append(privKeys, []byte(k.PrivateKey))
pub := privKey.GetPublic()
id, err := peer.IDFromPublicKey(pub)
if err != nil {
panic(err)
}
peers = append(peers, id)
keyManager.CreateSigningKey(
fmt.Sprintf("proving-key-%d", i),
keys.KeyTypeEd448,
)
pk, err := keyManager.GetRawKey(fmt.Sprintf("proving-key-%d", i))
if err != nil {
panic(err)
}
pprivKey, err := crypto.UnmarshalEd448PrivateKey([]byte(pk.PrivateKey))
if err != nil {
panic(err)
}
ppub := pprivKey.GetPublic()
ppubKey, err := ppub.Raw()
if err != nil {
panic(err)
}
pubKeys = append(pubKeys, ppubKey)
}
proverTrie := &tries.RollingFrecencyCritbitTrie{}
for i, s := range pubKeys {
addr, err := poseidon.HashBytes(s)
if err != nil {
panic(err)
}
addrBytes := addr.Bytes()
addrBytes = append(make([]byte, 32-len(addrBytes)), addrBytes...)
proverTrie.Add(addrBytes, 0)
addrMap[string(addrBytes)] = fmt.Sprintf("proving-key-%d", i)
}
return keyManager,
peers,
pubKeys,
privKeys,
addrMap,
proverTrie
}
func TestDataTimeReel(t *testing.T) {
2024-03-04 03:20:24 +00:00
logger, _ := zap.NewDevelopment()
2024-02-13 07:04:56 +00:00
db := store.NewInMemKVDB()
clockStore := store.NewPebbleClockStore(db, logger)
prover := qcrypto.NewWesolowskiFrameProver(logger)
filter := "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
keyManager,
_,
pubKeys,
_,
addrMap,
proverTrie := generateTestProvers()
2024-02-16 09:42:37 +00:00
// We're going to set this up by churning 40 master frames so we don't
2024-02-13 07:04:56 +00:00
// have to zig zag on master and data frames to confirm data time reel
// behaviors
m := time.NewMasterTimeReel(
logger,
clockStore,
&config.EngineConfig{
Filter: filter,
GenesisSeed: strings.Repeat("00", 516),
Difficulty: 10,
},
prover,
)
err := m.Start()
assert.NoError(t, err)
frame, err := m.Head()
assert.NoError(t, err)
frames := []*protobufs.ClockFrame{}
wg := sync.WaitGroup{}
wg.Add(1)
frameCh := m.NewFrameCh()
go func() {
2024-02-16 09:42:37 +00:00
for i := 0; i < 40; i++ {
2024-02-13 07:04:56 +00:00
frames = append(frames, <-frameCh)
}
wg.Done()
}()
// in order
2024-02-16 09:42:37 +00:00
for i := int64(0); i < 40; i++ {
2024-02-13 07:04:56 +00:00
frame, err = prover.ProveMasterClockFrame(frame, i+1, 10)
assert.NoError(t, err)
2024-03-17 21:14:37 +00:00
err := m.Insert(frame, false)
2024-02-13 07:04:56 +00:00
assert.NoError(t, err)
}
wg.Wait()
2024-02-16 09:42:37 +00:00
for i := 0; i < 40; i++ {
2024-02-13 07:04:56 +00:00
assert.NotNil(t, frames[i])
assert.Equal(t, frames[i].FrameNumber, uint64(i+1))
}
2024-02-16 09:42:37 +00:00
filterBytes := []byte{
0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff,
}
2024-02-13 07:04:56 +00:00
// Ok, now onto the data time reel. We're going to test the following
// scenarios:
// 1. Z-dist optimal, in order
// 2. Z-dist optimal, out of order
// 3. 90% optimal, out of order
// 4. Malicious majority, out of order
d := time.NewDataTimeReel(
filterBytes,
logger,
clockStore,
&config.EngineConfig{
Filter: filter,
GenesisSeed: strings.Repeat("00", 516),
Difficulty: 10,
},
prover,
frames[0].Output,
&qcrypto.InclusionAggregateProof{
InclusionCommitments: []*qcrypto.InclusionCommitment{},
AggregateCommitment: []byte{},
Proof: []byte{},
},
pubKeys,
)
err = d.Start()
assert.NoError(t, err)
frame, err = d.Head()
assert.NoError(t, err)
dataFrames := []*protobufs.ClockFrame{}
datawg := sync.WaitGroup{}
datawg.Add(1)
dataFrameCh := d.NewFrameCh()
2024-02-21 08:10:23 +00:00
targetFrameParentSelector := []byte{}
2024-02-13 07:04:56 +00:00
go func() {
2024-02-21 08:10:23 +00:00
for {
frame := <-dataFrameCh
dataFrames = append(dataFrames, frame)
if frame.FrameNumber == 40 && bytes.Equal(
frame.ParentSelector,
targetFrameParentSelector,
) {
break
}
2024-02-13 07:04:56 +00:00
}
datawg.Done()
}()
// 1. z-dist optimal proof submission is strictly master-frame evoked leader
for i := int64(0); i < 10; i++ {
2024-02-16 09:42:37 +00:00
masterSelector, err := frames[i].GetSelector()
2024-02-13 07:04:56 +00:00
assert.NoError(t, err)
proverSelection := proverTrie.FindNearest(
masterSelector.FillBytes(make([]byte, 32)),
)
optimalSigner, _ := keyManager.GetSigningKey(
addrMap[string(proverSelection.External.Key)],
)
frame, err = prover.ProveDataClockFrame(
frame,
[][]byte{},
[]*protobufs.InclusionAggregateProof{},
optimalSigner,
i+1,
10,
)
2024-03-17 21:14:37 +00:00
d.Insert(frame, false)
2024-02-13 07:04:56 +00:00
}
// 2. z-dist optimal, out of order proof submission is strictly master-frame
// evoked leader, but arrived completely backwards
insertFrames := []*protobufs.ClockFrame{}
for i := int64(10); i < 20; i++ {
2024-02-16 09:42:37 +00:00
masterSelector, err := frames[i].GetSelector()
2024-02-13 07:04:56 +00:00
assert.NoError(t, err)
proverSelection := proverTrie.FindNearest(
masterSelector.FillBytes(make([]byte, 32)),
)
optimalSigner, _ := keyManager.GetSigningKey(
addrMap[string(proverSelection.External.Key)],
)
frame, err = prover.ProveDataClockFrame(
frame,
[][]byte{},
[]*protobufs.InclusionAggregateProof{},
optimalSigner,
i+1,
10,
)
insertFrames = append(insertFrames, frame)
}
for i := 9; i >= 0; i-- {
2024-03-17 21:14:37 +00:00
err := d.Insert(insertFrames[i], false)
2024-02-13 07:04:56 +00:00
assert.NoError(t, err)
}
// 3. 90% optimal, out of order
insertFrames = []*protobufs.ClockFrame{}
for i := int64(20); i < 25; i++ {
2024-02-16 09:42:37 +00:00
masterSelector, err := frames[i].GetSelector()
2024-02-13 07:04:56 +00:00
assert.NoError(t, err)
proverSelection := proverTrie.FindNearest(
masterSelector.FillBytes(make([]byte, 32)),
)
optimalSigner, _ := keyManager.GetSigningKey(
addrMap[string(proverSelection.External.Key)],
)
frame, err = prover.ProveDataClockFrame(
frame,
[][]byte{},
[]*protobufs.InclusionAggregateProof{},
optimalSigner,
i+1,
10,
)
2024-03-17 21:14:37 +00:00
d.Insert(frame, false)
2024-02-13 07:04:56 +00:00
}
2024-02-16 09:42:37 +00:00
masterSelector, err := frames[25].GetSelector()
2024-02-13 07:04:56 +00:00
assert.NoError(t, err)
proverSelections := proverTrie.FindNearestAndApproximateNeighbors(
masterSelector.FillBytes(make([]byte, 32)),
)
suboptimalSigner2, _ := keyManager.GetSigningKey(
addrMap[string(proverSelections[2].External.Key)],
)
// What we're trying to simulate: consensus heads progressed on a slightly
// less optimal prover.
frame, err = prover.ProveDataClockFrame(
frame,
[][]byte{},
[]*protobufs.InclusionAggregateProof{},
suboptimalSigner2,
26,
10,
)
insertFrames = append(insertFrames, frame)
for i := int64(26); i < 30; i++ {
2024-02-16 09:42:37 +00:00
masterSelector, err := frames[i].GetSelector()
2024-02-13 07:04:56 +00:00
assert.NoError(t, err)
proverSelection := proverTrie.FindNearest(
masterSelector.FillBytes(make([]byte, 32)),
)
optimalSigner, _ := keyManager.GetSigningKey(
addrMap[string(proverSelection.External.Key)],
)
frame, err = prover.ProveDataClockFrame(
frame,
[][]byte{},
[]*protobufs.InclusionAggregateProof{},
optimalSigner,
i+1,
10,
)
insertFrames = append(insertFrames, frame)
}
for i := 4; i >= 0; i-- {
2024-03-17 21:14:37 +00:00
err := d.Insert(insertFrames[i], false)
2024-02-13 07:04:56 +00:00
assert.NoError(t, err)
}
// 4. Malicious majority, out of order handle a suppressive majority and
// force consensus on the lowest distance sub-tree:
insertFrames = []*protobufs.ClockFrame{}
conflictFrames := []*protobufs.ClockFrame{}
optimalKeySet := [][]byte{}
suppressedFrame := frame
for i := int64(30); i < 40; i++ {
2024-02-16 09:42:37 +00:00
masterSelector, err := frames[i].GetSelector()
2024-02-13 07:04:56 +00:00
assert.NoError(t, err)
proverSelections := proverTrie.FindNearestAndApproximateNeighbors(
masterSelector.FillBytes(make([]byte, 32)),
)
optimalSigner, _ := keyManager.GetSigningKey(
addrMap[string(proverSelections[0].External.Key)],
)
suboptimalSigner2, _ := keyManager.GetSigningKey(
addrMap[string(proverSelections[2].External.Key)],
)
optimalKeySet = append(optimalKeySet, []byte(
(optimalSigner.Public()).(ed448.PublicKey),
))
// What we're trying to simulate: the majority is intentionally ignoring
// the most optimal signer
2024-02-16 09:42:37 +00:00
suppressedFrame, err = prover.ProveDataClockFrame(
2024-02-13 07:04:56 +00:00
suppressedFrame,
[][]byte{},
[]*protobufs.InclusionAggregateProof{},
optimalSigner,
i+1,
10,
)
insertFrames = append(insertFrames, suppressedFrame)
2024-02-21 08:10:23 +00:00
if i == 39 {
targetFrameParentSelector = suppressedFrame.ParentSelector
}
2024-02-13 07:04:56 +00:00
frame, err = prover.ProveDataClockFrame(
frame,
[][]byte{},
[]*protobufs.InclusionAggregateProof{},
suboptimalSigner2,
i+1,
10,
)
conflictFrames = append(conflictFrames, frame)
}
for i := 9; i >= 0; i-- {
2024-03-17 21:14:37 +00:00
err := d.Insert(conflictFrames[i], false)
2024-02-16 09:42:37 +00:00
// force linear ordering
gotime.Sleep(1 * gotime.Second)
2024-02-13 07:04:56 +00:00
assert.NoError(t, err)
}
2024-02-16 09:42:37 +00:00
// Someone is honest, but running backwards:
2024-02-13 07:04:56 +00:00
for i := 9; i >= 0; i-- {
2024-03-17 21:14:37 +00:00
err := d.Insert(insertFrames[i], false)
2024-02-16 09:42:37 +00:00
gotime.Sleep(1 * gotime.Second)
2024-02-13 07:04:56 +00:00
assert.NoError(t, err)
}
datawg.Wait()
2024-02-21 08:10:23 +00:00
assert.Equal(t, uint64(40), dataFrames[len(dataFrames)-1].FrameNumber)
assert.Equal(
t,
optimalKeySet[len(optimalKeySet)-1],
dataFrames[len(dataFrames)-1].GetPublicKeySignatureEd448().PublicKey.KeyValue,
)
2024-02-13 07:04:56 +00:00
}