From e5ecf69d2a22d137884434d6dc067a8670a9e2b0 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:34:02 -0500 Subject: [PATCH 01/43] eliminate emapChunk --- x/dsmr/block.go | 12 ++++++++++-- x/dsmr/storage.go | 20 +++----------------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/x/dsmr/block.go b/x/dsmr/block.go index d402a7086e..619b4de051 100644 --- a/x/dsmr/block.go +++ b/x/dsmr/block.go @@ -11,14 +11,14 @@ import ( "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" + "github.com/ava-labs/hypersdk/internal/emap" "github.com/ava-labs/hypersdk/utils" ) const InitialChunkSize = 250 * 1024 type Tx interface { - GetID() ids.ID - GetExpiry() int64 + emap.Item GetSponsor() codec.Address } @@ -51,6 +51,14 @@ func (c *Chunk[T]) init() error { return nil } +func (c Chunk[T]) GetID() ids.ID { + return c.id +} + +func (c Chunk[T]) GetExpiry() int64 { + return c.Expiry +} + func signChunk[T Tx]( chunk UnsignedChunk[T], networkID uint32, diff --git a/x/dsmr/storage.go b/x/dsmr/storage.go index 0cd5f98e37..e713ed2a64 100644 --- a/x/dsmr/storage.go +++ b/x/dsmr/storage.go @@ -59,7 +59,7 @@ type chunkStorage[T Tx] struct { lock sync.RWMutex // Chunk storage - chunkEMap *emap.EMap[emapChunk[T]] + chunkEMap *emap.EMap[Chunk[T]] minimumExpiry int64 // pendingByte | slot | chunkID -> chunkBytes // acceptedByte | slot | chunkID -> chunkBytes @@ -90,7 +90,7 @@ func newChunkStorage[T Tx]( storage := &chunkStorage[T]{ minimumExpiry: minSlot, - chunkEMap: emap.NewEMap[emapChunk[T]](), + chunkEMap: emap.NewEMap[Chunk[T]](), chunkMap: make(map[ids.ID]*StoredChunkSignature[T]), chunkDB: db, verifier: verifier, @@ -173,7 +173,7 @@ func (s *chunkStorage[T]) putVerifiedChunk(c Chunk[T], cert *ChunkCertificate) e if err := s.chunkDB.Put(pendingChunkKey(c.Expiry, c.id), c.bytes); err != nil { return err } - s.chunkEMap.Add([]emapChunk[T]{{chunk: c}}) + s.chunkEMap.Add([]Chunk[T]{c}) chunkCert := &StoredChunkSignature[T]{ Chunk: c, @@ -308,17 +308,3 @@ func parsePendingChunkKey(key []byte) (slot int64, chunkID ids.ID, err error) { func acceptedChunkKey(slot int64, chunkID ids.ID) []byte { return createChunkKey(acceptedByte, slot, chunkID) } - -var _ emap.Item = (*emapChunk[Tx])(nil) - -type emapChunk[T Tx] struct { - chunk Chunk[T] -} - -func (e emapChunk[_]) GetID() ids.ID { - return e.chunk.id -} - -func (e emapChunk[_]) GetExpiry() int64 { - return e.chunk.Expiry -} From ba644c3a0d55c0480bc7712a2cee16d799c79c5b Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Fri, 22 Nov 2024 11:52:20 -0500 Subject: [PATCH 02/43] update --- x/dsmr/assembler.go | 2 +- x/dsmr/block.go | 31 ++++++++++++-- x/dsmr/node.go | 62 ++++++++++++++++++---------- x/dsmr/node_test.go | 98 ++++++++++++++++++++++----------------------- 4 files changed, 118 insertions(+), 75 deletions(-) diff --git a/x/dsmr/assembler.go b/x/dsmr/assembler.go index e5e729dc15..50175a383b 100644 --- a/x/dsmr/assembler.go +++ b/x/dsmr/assembler.go @@ -57,7 +57,7 @@ func (b *BlockHandler[T, S, B, R]) Accept(ctx context.Context, block *Block) err } // Assemble and execute the block - innerBlock, result, state, err := b.Assembler.AssembleBlock(ctx, b.lastAcceptedState, b.lastAcceptedBlock, block.Timestamp, block.Height+1, txs) + innerBlock, result, state, err := b.Assembler.AssembleBlock(ctx, b.lastAcceptedState, b.lastAcceptedBlock, block.Tmstmp, block.Hght+1, txs) if err != nil { return err } diff --git a/x/dsmr/block.go b/x/dsmr/block.go index 619b4de051..4f836b2758 100644 --- a/x/dsmr/block.go +++ b/x/dsmr/block.go @@ -114,9 +114,9 @@ func ParseChunk[T Tx](chunkBytes []byte) (Chunk[T], error) { } type Block struct { - ParentID ids.ID `serialize:"true"` - Height uint64 `serialize:"true"` - Timestamp int64 `serialize:"true"` + ParentID ids.ID `serialize:"true"` + Hght uint64 `serialize:"true"` + Tmstmp int64 `serialize:"true"` ChunkCerts []*ChunkCertificate `serialize:"true"` @@ -127,3 +127,28 @@ type Block struct { func (b Block) GetID() ids.ID { return b.blkID } + +func (b Block) Parent() ids.ID { + return b.ParentID +} + +func (b Block) Timestamp() int64 { + return b.Tmstmp +} + +func (b Block) Height() uint64 { + return uint64(b.Hght) +} + +func (b Block) Txs() []*ChunkCertificate { + return b.ChunkCerts +} + +func (b Block) ContainsTx(id ids.ID) bool { + for _, c := range b.ChunkCerts { + if c.ChunkID == id { + return true + } + } + return false +} diff --git a/x/dsmr/node.go b/x/dsmr/node.go index 9151b7d8a2..1e963a96aa 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -14,12 +14,15 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" "github.com/ava-labs/avalanchego/network/p2p/acp118" + "github.com/ava-labs/avalanchego/trace" "github.com/ava-labs/avalanchego/utils/crypto/bls" + "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" + "github.com/ava-labs/hypersdk/internal/validitywindow" "github.com/ava-labs/hypersdk/proto/pb/dsmr" "github.com/ava-labs/hypersdk/utils" ) @@ -52,7 +55,7 @@ func New[T Tx]( return nil, err } - return &Node[T]{ + node := &Node[T]{ nodeID: nodeID, networkID: networkID, chainID: chainID, @@ -80,25 +83,35 @@ func New[T Tx]( storage: storage, }, storage: storage, - }, nil + log: logging.NewLogger("dsmr"), + } + node.validityWindow = validitywindow.NewTimeValidityWindow(node.log, node.tracer, node) + return node, nil } -type Node[T Tx] struct { - nodeID ids.NodeID - networkID uint32 - chainID ids.ID - pk *bls.PublicKey - signer warp.Signer - getChunkClient *TypedClient[*dsmr.GetChunkRequest, Chunk[T], []byte] - getChunkSignatureClient *TypedClient[*dsmr.GetChunkSignatureRequest, *dsmr.GetChunkSignatureResponse, []byte] - chunkCertificateGossipClient *TypedClient[[]byte, []byte, *dsmr.ChunkCertificateGossip] - validators []Validator - - GetChunkHandler *GetChunkHandler[T] - GetChunkSignatureHandler *acp118.Handler - ChunkCertificateGossipHandler *ChunkCertificateGossipHandler[T] - storage *chunkStorage[T] -} +type ( + validityWindow[T Tx] *validitywindow.TimeValidityWindow[Chunk[T]] + + Node[T Tx] struct { + nodeID ids.NodeID + networkID uint32 + chainID ids.ID + pk *bls.PublicKey + signer warp.Signer + getChunkClient *TypedClient[*dsmr.GetChunkRequest, Chunk[T], []byte] + getChunkSignatureClient *TypedClient[*dsmr.GetChunkSignatureRequest, *dsmr.GetChunkSignatureResponse, []byte] + chunkCertificateGossipClient *TypedClient[[]byte, []byte, *dsmr.ChunkCertificateGossip] + validators []Validator + validityWindow validityWindow[T] + + GetChunkHandler *GetChunkHandler[T] + GetChunkSignatureHandler *acp118.Handler + ChunkCertificateGossipHandler *ChunkCertificateGossipHandler[T] + storage *chunkStorage[T] + log logging.Logger + tracer trace.Tracer + } +) // BuildChunk builds transactions into a Chunk // TODO handle frozen sponsor + validator assignments @@ -172,8 +185,9 @@ func (n *Node[T]) BuildChunk( return chunk, n.storage.AddLocalChunkWithCert(chunk, &chunkCert) } +// BuildBlock(ctx context.Context, parentView state.View, parent *ExecutionBlock) (*ExecutionBlock, *ExecutedBlock, merkledb.View, error) func (n *Node[T]) BuildBlock(parent Block, timestamp int64) (Block, error) { - if timestamp <= parent.Timestamp { + if timestamp <= parent.Tmstmp { return Block{}, ErrTimestampNotMonotonicallyIncreasing } @@ -193,8 +207,8 @@ func (n *Node[T]) BuildBlock(parent Block, timestamp int64) (Block, error) { blk := Block{ ParentID: parent.GetID(), - Height: parent.Height + 1, - Timestamp: timestamp, + Hght: parent.Hght + 1, + Tmstmp: timestamp, ChunkCerts: availableChunkCerts, } @@ -264,5 +278,9 @@ func (n *Node[T]) Accept(ctx context.Context, block Block) error { } } - return n.storage.SetMin(block.Timestamp, chunkIDs) + return n.storage.SetMin(block.Tmstmp, chunkIDs) +} + +func (n *Node[T]) GetExecutionBlock(ctx context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[Chunk[T]], error) { + return nil, nil } diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 12b2e1cf3d..f3f5fc0065 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -193,9 +193,9 @@ func TestNode_GetChunk_AvailableChunk(t *testing.T) { blk, err := node.BuildBlock( Block{ - ParentID: ids.GenerateTestID(), - Height: 0, - Timestamp: 1, + ParentID: ids.GenerateTestID(), + Hght: 0, + Tmstmp: 1, }, 2, ) @@ -579,9 +579,9 @@ func TestNode_BuiltChunksAvailableOverGetChunk(t *testing.T) { block, err := node.BuildBlock( Block{ - ParentID: ids.GenerateTestID(), - Height: 0, - Timestamp: 1, + ParentID: ids.GenerateTestID(), + Hght: 0, + Tmstmp: 1, }, 2, ) @@ -883,9 +883,9 @@ func TestNode_GetChunkSignature_DuplicateChunk(t *testing.T) { r.NoError(err) blk, err := node.BuildBlock( Block{ - ParentID: ids.GenerateTestID(), - Height: 0, - Timestamp: 1, + ParentID: ids.GenerateTestID(), + Hght: 0, + Tmstmp: 1, }, 2, ) @@ -1004,9 +1004,9 @@ func TestGetChunkSignature_PersistAttestedBlocks(t *testing.T) { for { blk, err = node1.BuildBlock( Block{ - ParentID: ids.Empty, - Height: 0, - Timestamp: 0, + ParentID: ids.Empty, + Hght: 0, + Tmstmp: 0, }, 1, ) @@ -1062,10 +1062,10 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { { name: "no chunk certs", parent: Block{ - ParentID: ids.GenerateTestID(), - Height: 1, - Timestamp: 1, - blkID: ids.GenerateTestID(), + ParentID: ids.GenerateTestID(), + Hght: 1, + Tmstmp: 1, + blkID: ids.GenerateTestID(), }, timestamp: 2, // TODO should we be able to build empty blocks? @@ -1074,10 +1074,10 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { { name: "timestamp equal to parent", parent: Block{ - ParentID: ids.GenerateTestID(), - Height: 1, - Timestamp: 1, - blkID: ids.GenerateTestID(), + ParentID: ids.GenerateTestID(), + Hght: 1, + Tmstmp: 1, + blkID: ids.GenerateTestID(), }, timestamp: 1, wantErr: ErrTimestampNotMonotonicallyIncreasing, @@ -1085,10 +1085,10 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { { name: "timestamp older than parent", parent: Block{ - ParentID: ids.GenerateTestID(), - Height: 1, - Timestamp: 1, - blkID: ids.GenerateTestID(), + ParentID: ids.GenerateTestID(), + Hght: 1, + Tmstmp: 1, + blkID: ids.GenerateTestID(), }, timestamp: 0, wantErr: ErrTimestampNotMonotonicallyIncreasing, @@ -1107,10 +1107,10 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { }, }, parent: Block{ - ParentID: ids.GenerateTestID(), - Height: 1, - Timestamp: 1, - blkID: ids.GenerateTestID(), + ParentID: ids.GenerateTestID(), + Hght: 1, + Tmstmp: 1, + blkID: ids.GenerateTestID(), }, timestamp: 2, wantErr: ErrNoAvailableChunkCerts, @@ -1147,10 +1147,10 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { }, }, parent: Block{ - ParentID: ids.GenerateTestID(), - Height: 1, - Timestamp: 1, - blkID: ids.GenerateTestID(), + ParentID: ids.GenerateTestID(), + Hght: 1, + Tmstmp: 1, + blkID: ids.GenerateTestID(), }, timestamp: 5, wantErr: ErrNoAvailableChunkCerts, @@ -1169,10 +1169,10 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { }, }, parent: Block{ - ParentID: ids.GenerateTestID(), - Height: 1, - Timestamp: 1, - blkID: ids.GenerateTestID(), + ParentID: ids.GenerateTestID(), + Hght: 1, + Tmstmp: 1, + blkID: ids.GenerateTestID(), }, timestamp: 2, }, @@ -1208,10 +1208,10 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { }, }, parent: Block{ - ParentID: ids.GenerateTestID(), - Height: 1, - Timestamp: 1, - blkID: ids.GenerateTestID(), + ParentID: ids.GenerateTestID(), + Hght: 1, + Tmstmp: 1, + blkID: ids.GenerateTestID(), }, timestamp: 2, }, @@ -1238,10 +1238,10 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { }, }, parent: Block{ - ParentID: ids.GenerateTestID(), - Height: 1, - Timestamp: 1, - blkID: ids.GenerateTestID(), + ParentID: ids.GenerateTestID(), + Hght: 1, + Tmstmp: 1, + blkID: ids.GenerateTestID(), }, timestamp: 2, }, @@ -1315,8 +1315,8 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { } r.Equal(tt.parent.GetID(), blk.ParentID) - r.Equal(tt.parent.Height+1, blk.Height) - r.Greater(blk.Timestamp, tt.parent.Timestamp) + r.Equal(tt.parent.Hght+1, blk.Hght) + r.Greater(blk.Tmstmp, tt.parent.Tmstmp) r.NotEmpty(blk.GetID()) r.Len(blk.ChunkCerts, len(wantChunks)) @@ -1384,9 +1384,9 @@ func TestAccept_RequestReferencedChunks(t *testing.T) { ) r.NoError(err) blk, err := node1.BuildBlock(Block{ - ParentID: ids.GenerateTestID(), - Height: 0, - Timestamp: 0, + ParentID: ids.GenerateTestID(), + Hght: 0, + Tmstmp: 0, }, 1) r.NoError(err) r.NoError(node1.Accept(context.Background(), blk)) From 2939543d09663d4d53f1a4aa0b3284423f626e4f Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 25 Nov 2024 09:29:08 -0500 Subject: [PATCH 03/43] update --- x/dsmr/certificate.go | 5 ++- x/dsmr/node.go | 46 ++++++++++++++------ x/dsmr/node_test.go | 99 +++++++++++++++++++++++-------------------- 3 files changed, 88 insertions(+), 62 deletions(-) diff --git a/x/dsmr/certificate.go b/x/dsmr/certificate.go index 6f931fe142..abc440f2de 100644 --- a/x/dsmr/certificate.go +++ b/x/dsmr/certificate.go @@ -54,7 +54,8 @@ type ChunkCertificate struct { Signature NoVerifyChunkSignature `serialize:"true"` } -func (c *ChunkCertificate) GetChunkID() ids.ID { return c.ChunkID } +func (c ChunkCertificate) GetID() ids.ID { return c.ChunkID } +func (c ChunkCertificate) GetExpiry() int64 { return c.Expiry } func (c *ChunkCertificate) GetSlot() int64 { return c.Expiry } @@ -209,7 +210,7 @@ func ParseWarpChunkCertificate(b []byte) (*WarpChunkCertificate, error) { }, nil } -func (c *WarpChunkCertificate) GetChunkID() ids.ID { return c.UnsignedCertificate.ChunkID() } +func (c *WarpChunkCertificate) GetID() ids.ID { return c.UnsignedCertificate.ChunkID() } func (c *WarpChunkCertificate) GetSlot() int64 { return c.UnsignedCertificate.Slot() } diff --git a/x/dsmr/node.go b/x/dsmr/node.go index 1e963a96aa..1845c04060 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -85,12 +85,13 @@ func New[T Tx]( storage: storage, log: logging.NewLogger("dsmr"), } + node.tracer, err = trace.New(trace.Config{}) node.validityWindow = validitywindow.NewTimeValidityWindow(node.log, node.tracer, node) - return node, nil + return node, err } type ( - validityWindow[T Tx] *validitywindow.TimeValidityWindow[Chunk[T]] + timeValidityWindow = *validitywindow.TimeValidityWindow[*ChunkCertificate] Node[T Tx] struct { nodeID ids.NodeID @@ -102,7 +103,7 @@ type ( getChunkSignatureClient *TypedClient[*dsmr.GetChunkSignatureRequest, *dsmr.GetChunkSignatureResponse, []byte] chunkCertificateGossipClient *TypedClient[[]byte, []byte, *dsmr.ChunkCertificateGossip] validators []Validator - validityWindow validityWindow[T] + validityWindow timeValidityWindow GetChunkHandler *GetChunkHandler[T] GetChunkSignatureHandler *acp118.Handler @@ -185,25 +186,35 @@ func (n *Node[T]) BuildChunk( return chunk, n.storage.AddLocalChunkWithCert(chunk, &chunkCert) } +const validityWindowDuration = int64(5) + // BuildBlock(ctx context.Context, parentView state.View, parent *ExecutionBlock) (*ExecutionBlock, *ExecutedBlock, merkledb.View, error) -func (n *Node[T]) BuildBlock(parent Block, timestamp int64) (Block, error) { +func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) (Block, error) { if timestamp <= parent.Tmstmp { return Block{}, ErrTimestampNotMonotonicallyIncreasing } chunkCerts := n.storage.GatherChunkCerts() + oldestAllowed := timestamp - validityWindowDuration + if oldestAllowed < 0 { + oldestAllowed = 0 + } + dup, err := n.validityWindow.IsRepeat(ctx, parent, chunkCerts, oldestAllowed) + if err != nil { + return Block{}, err + } + + if dup.Len() == len(chunkCerts) { + return Block{}, ErrNoAvailableChunkCerts + } + availableChunkCerts := make([]*ChunkCertificate, 0) - for _, chunkCert := range chunkCerts { - // avoid building blocks with expired chunk certs - if chunkCert.Expiry < timestamp { + for i, chunkCert := range chunkCerts { + if dup.Contains(i) { continue } - availableChunkCerts = append(availableChunkCerts, chunkCert) } - if len(availableChunkCerts) == 0 { - return Block{}, ErrNoAvailableChunkCerts - } blk := Block{ ParentID: parent.GetID(), @@ -222,9 +233,14 @@ func (n *Node[T]) BuildBlock(parent Block, timestamp int64) (Block, error) { return blk, nil } -func (*Node[T]) Execute(ctx context.Context, _ Block, block Block) error { +func (n *Node[T]) Execute(ctx context.Context, parentBlock Block, block Block) error { // TODO: Verify header fields - // TODO: de-duplicate chunk certificates (internal to block and across history) + + // Find repeats + if err := n.validityWindow.VerifyExpiryReplayProtection(ctx, block, parentBlock.Tmstmp); err != nil { + return err + } + for _, chunkCert := range block.ChunkCerts { // TODO: verify chunks within a provided context if err := chunkCert.Verify(ctx, struct{}{}); err != nil { @@ -277,10 +293,12 @@ func (n *Node[T]) Accept(ctx context.Context, block Block) error { } } } + // update the validity window with the accepted block. + n.validityWindow.Accept(block) return n.storage.SetMin(block.Tmstmp, chunkIDs) } -func (n *Node[T]) GetExecutionBlock(ctx context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[Chunk[T]], error) { +func (n *Node[T]) GetExecutionBlock(ctx context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[*ChunkCertificate], error) { return nil, nil } diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index f3f5fc0065..cc399eb890 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -192,6 +192,7 @@ func TestNode_GetChunk_AvailableChunk(t *testing.T) { r.NoError(err) blk, err := node.BuildBlock( + context.Background(), Block{ ParentID: ids.GenerateTestID(), Hght: 0, @@ -578,6 +579,7 @@ func TestNode_BuiltChunksAvailableOverGetChunk(t *testing.T) { } block, err := node.BuildBlock( + context.Background(), Block{ ParentID: ids.GenerateTestID(), Hght: 0, @@ -882,6 +884,7 @@ func TestNode_GetChunkSignature_DuplicateChunk(t *testing.T) { ) r.NoError(err) blk, err := node.BuildBlock( + context.Background(), Block{ ParentID: ids.GenerateTestID(), Hght: 0, @@ -1003,6 +1006,7 @@ func TestGetChunkSignature_PersistAttestedBlocks(t *testing.T) { var blk Block for { blk, err = node1.BuildBlock( + context.Background(), Block{ ParentID: ids.Empty, Hght: 0, @@ -1058,41 +1062,41 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { parent Block timestamp int64 wantErr error - }{ - { - name: "no chunk certs", - parent: Block{ - ParentID: ids.GenerateTestID(), - Hght: 1, - Tmstmp: 1, - blkID: ids.GenerateTestID(), - }, - timestamp: 2, - // TODO should we be able to build empty blocks? - wantErr: ErrNoAvailableChunkCerts, - }, - { - name: "timestamp equal to parent", - parent: Block{ - ParentID: ids.GenerateTestID(), - Hght: 1, - Tmstmp: 1, - blkID: ids.GenerateTestID(), + }{ /* + { + name: "no chunk certs", + parent: Block{ + ParentID: ids.GenerateTestID(), + Hght: 1, + Tmstmp: 1, + blkID: ids.GenerateTestID(), + }, + timestamp: 2, + // TODO should we be able to build empty blocks? + wantErr: ErrNoAvailableChunkCerts, }, - timestamp: 1, - wantErr: ErrTimestampNotMonotonicallyIncreasing, - }, - { - name: "timestamp older than parent", - parent: Block{ - ParentID: ids.GenerateTestID(), - Hght: 1, - Tmstmp: 1, - blkID: ids.GenerateTestID(), + { + name: "timestamp equal to parent", + parent: Block{ + ParentID: ids.GenerateTestID(), + Hght: 1, + Tmstmp: 1, + blkID: ids.GenerateTestID(), + }, + timestamp: 1, + wantErr: ErrTimestampNotMonotonicallyIncreasing, }, - timestamp: 0, - wantErr: ErrTimestampNotMonotonicallyIncreasing, - }, + { + name: "timestamp older than parent", + parent: Block{ + ParentID: ids.GenerateTestID(), + Hght: 1, + Tmstmp: 1, + blkID: ids.GenerateTestID(), + }, + timestamp: 0, + wantErr: ErrTimestampNotMonotonicallyIncreasing, + },*/ { name: "expired chunk cert", chunks: []chunk{ @@ -1100,10 +1104,10 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { txs: []tx{ { ID: ids.GenerateTestID(), - Expiry: 1, + Expiry: 2, }, }, - expiry: 1, + expiry: 2, }, }, parent: Block{ @@ -1112,7 +1116,7 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { Tmstmp: 1, blkID: ids.GenerateTestID(), }, - timestamp: 2, + timestamp: 3, wantErr: ErrNoAvailableChunkCerts, }, { @@ -1239,11 +1243,11 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { }, parent: Block{ ParentID: ids.GenerateTestID(), - Hght: 1, - Tmstmp: 1, + Hght: 2, + Tmstmp: 2, blkID: ids.GenerateTestID(), }, - timestamp: 2, + timestamp: 3, }, } @@ -1307,8 +1311,8 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { wantChunks = append(wantChunks, chunk) } - - blk, err := node.BuildBlock(tt.parent, tt.timestamp) + r.NoError(node.Accept(context.Background(), tt.parent)) + blk, err := node.BuildBlock(context.Background(), tt.parent, tt.timestamp) r.ErrorIs(err, tt.wantErr) if err != nil { return @@ -1383,11 +1387,14 @@ func TestAccept_RequestReferencedChunks(t *testing.T) { codec.Address{123}, ) r.NoError(err) - blk, err := node1.BuildBlock(Block{ - ParentID: ids.GenerateTestID(), - Hght: 0, - Tmstmp: 0, - }, 1) + blk, err := node1.BuildBlock( + context.Background(), + Block{ + ParentID: ids.GenerateTestID(), + Hght: 0, + Tmstmp: 0, + }, + 1) r.NoError(err) r.NoError(node1.Accept(context.Background(), blk)) From 659f1fbbb7f05c9dda4ef27ff92a63bb8976c33b Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:34:28 -0500 Subject: [PATCH 04/43] update per CR --- x/dsmr/node.go | 7 +++++-- x/dsmr/node_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/x/dsmr/node.go b/x/dsmr/node.go index 1845c04060..a421ffd256 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -49,6 +49,8 @@ func New[T Tx]( getChunkSignatureClient *p2p.Client, chunkCertificateGossipClient *p2p.Client, validators []Validator, + log logging.Logger, + tracer trace.Tracer, ) (*Node[T], error) { storage, err := newChunkStorage[T](NoVerifier[T]{}, memdb.New()) if err != nil { @@ -83,9 +85,10 @@ func New[T Tx]( storage: storage, }, storage: storage, - log: logging.NewLogger("dsmr"), + log: log, + tracer: tracer, } - node.tracer, err = trace.New(trace.Config{}) + node.validityWindow = validitywindow.NewTimeValidityWindow(node.log, node.tracer, node) return node, err } diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index cc399eb890..d6c8af5165 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -14,7 +14,9 @@ import ( "github.com/ava-labs/avalanchego/network/p2p/p2ptest" "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/snow/validators/validatorstest" + "github.com/ava-labs/avalanchego/trace" "github.com/ava-labs/avalanchego/utils/crypto/bls" + "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/vms/platformvm/warp" @@ -29,6 +31,16 @@ var ( _ Verifier[tx] = (*failVerifier)(nil) ) +func newTestingLog() logging.Logger { + return logging.NewLogger("dsmr") +} + +func newTestingTracer(t *testing.T) trace.Tracer { + tracer, err := trace.New(trace.Config{}) + require.NoError(t, err) + return tracer +} + // Test that chunks can be built through Node.NewChunk func TestNode_BuildChunk(t *testing.T) { tests := []struct { @@ -116,6 +128,8 @@ func TestNode_BuildChunk(t *testing.T) { ids.EmptyNodeID, ), nil, + newTestingLog(), + newTestingTracer(t), ) r.NoError(err) @@ -180,6 +194,8 @@ func TestNode_GetChunk_AvailableChunk(t *testing.T) { ids.EmptyNodeID, ), nil, + newTestingLog(), + newTestingTracer(t), ) r.NoError(err) @@ -271,6 +287,8 @@ func TestNode_GetChunk_PendingChunk(t *testing.T) { ids.EmptyNodeID, ), nil, + newTestingLog(), + newTestingTracer(t), ) r.NoError(err) @@ -348,6 +366,8 @@ func TestNode_GetChunk_UnknownChunk(t *testing.T) { ids.EmptyNodeID, ), nil, + newTestingLog(), + newTestingTracer(t), ) r.NoError(err) @@ -561,6 +581,8 @@ func TestNode_BuiltChunksAvailableOverGetChunk(t *testing.T) { ids.EmptyNodeID, ), nil, + newTestingLog(), + newTestingTracer(t), ) r.NoError(err) @@ -694,6 +716,8 @@ func TestNode_GetChunkSignature_SignValidChunk(t *testing.T) { ids.EmptyNodeID, ), nil, + newTestingLog(), + newTestingTracer(t), ) r.NoError(err) @@ -742,6 +766,8 @@ func TestNode_GetChunkSignature_SignValidChunk(t *testing.T) { ids.EmptyNodeID, ), nil, + newTestingLog(), + newTestingTracer(t), ) r.NoError(err) chunk, err := node2.BuildChunk( @@ -860,6 +886,8 @@ func TestNode_GetChunkSignature_DuplicateChunk(t *testing.T) { ids.EmptyNodeID, ), nil, + newTestingLog(), + newTestingTracer(t), ) r.NoError(err) @@ -954,6 +982,8 @@ func TestGetChunkSignature_PersistAttestedBlocks(t *testing.T) { ids.EmptyNodeID, ), nil, + newTestingLog(), + newTestingTracer(t), ) r.NoError(err) @@ -990,6 +1020,8 @@ func TestGetChunkSignature_PersistAttestedBlocks(t *testing.T) { ids.EmptyNodeID, ), []Validator{{NodeID: node1.nodeID}}, + newTestingLog(), + newTestingTracer(t), ) r.NoError(err) @@ -1291,6 +1323,8 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { ids.EmptyNodeID, ), nil, + newTestingLog(), + newTestingTracer(t), ) r.NoError(err) @@ -1377,6 +1411,8 @@ func TestAccept_RequestReferencedChunks(t *testing.T) { ids.EmptyNodeID, ), nil, + newTestingLog(), + newTestingTracer(t), ) r.NoError(err) @@ -1431,6 +1467,8 @@ func TestAccept_RequestReferencedChunks(t *testing.T) { ids.EmptyNodeID, ), []Validator{{NodeID: node1.nodeID}}, + newTestingLog(), + newTestingTracer(t), ) r.NoError(err) r.NoError(node2.Accept(context.Background(), blk)) From 921b908a2ee9b29d0bcfbd69c59e53c59f9edec1 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:02:10 -0500 Subject: [PATCH 05/43] lint --- x/dsmr/block.go | 2 +- x/dsmr/node.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x/dsmr/block.go b/x/dsmr/block.go index 4f836b2758..6eb329e128 100644 --- a/x/dsmr/block.go +++ b/x/dsmr/block.go @@ -137,7 +137,7 @@ func (b Block) Timestamp() int64 { } func (b Block) Height() uint64 { - return uint64(b.Hght) + return b.Hght } func (b Block) Txs() []*ChunkCertificate { diff --git a/x/dsmr/node.go b/x/dsmr/node.go index a421ffd256..ef54abbdbf 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -302,6 +302,6 @@ func (n *Node[T]) Accept(ctx context.Context, block Block) error { return n.storage.SetMin(block.Tmstmp, chunkIDs) } -func (n *Node[T]) GetExecutionBlock(ctx context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[*ChunkCertificate], error) { +func (*Node[T]) GetExecutionBlock(context.Context, ids.ID) (validitywindow.ExecutionBlock[*ChunkCertificate], error) { return nil, nil } From 508cc921902bf4adb6067bc93362405edcdd1953 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:23:38 -0500 Subject: [PATCH 06/43] apply requested changes. --- chain/processor.go | 2 +- internal/validitywindow/dependencies.go | 2 +- internal/validitywindow/validitywindow.go | 2 +- x/dsmr/block.go | 2 +- x/dsmr/node.go | 22 ++++++++++----------- x/dsmr/node_test.go | 24 +++++++++++++++++++++++ 6 files changed, 39 insertions(+), 15 deletions(-) diff --git a/chain/processor.go b/chain/processor.go index 640ed40d9d..d5638912d5 100644 --- a/chain/processor.go +++ b/chain/processor.go @@ -53,7 +53,7 @@ func NewExecutionBlock(block *StatelessBlock) (*ExecutionBlock, error) { }, nil } -func (b *ExecutionBlock) ContainsTx(id ids.ID) bool { +func (b *ExecutionBlock) Contains(id ids.ID) bool { return b.txsSet.Contains(id) } diff --git a/internal/validitywindow/dependencies.go b/internal/validitywindow/dependencies.go index fd76485cfb..cf7d559084 100644 --- a/internal/validitywindow/dependencies.go +++ b/internal/validitywindow/dependencies.go @@ -16,7 +16,7 @@ type ExecutionBlock[Container emap.Item] interface { Timestamp() int64 Height() uint64 Txs() []Container - ContainsTx(ids.ID) bool + Contains(ids.ID) bool } type ChainIndex[Container emap.Item] interface { diff --git a/internal/validitywindow/validitywindow.go b/internal/validitywindow/validitywindow.go index 68e6b21689..5d8d10d418 100644 --- a/internal/validitywindow/validitywindow.go +++ b/internal/validitywindow/validitywindow.go @@ -110,7 +110,7 @@ func (v *TimeValidityWindow[Container]) isRepeat( if marker.Contains(i) { continue } - if ancestorBlk.ContainsTx(tx.GetID()) { + if ancestorBlk.Contains(tx.GetID()) { marker.Add(i) if stop { return marker, nil diff --git a/x/dsmr/block.go b/x/dsmr/block.go index 6eb329e128..84f8a28afb 100644 --- a/x/dsmr/block.go +++ b/x/dsmr/block.go @@ -144,7 +144,7 @@ func (b Block) Txs() []*ChunkCertificate { return b.ChunkCerts } -func (b Block) ContainsTx(id ids.ID) bool { +func (b Block) Contains(id ids.ID) bool { for _, c := range b.ChunkCerts { if c.ChunkID == id { return true diff --git a/x/dsmr/node.go b/x/dsmr/node.go index ef54abbdbf..57f1c73408 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -33,6 +33,11 @@ var ( ErrTimestampNotMonotonicallyIncreasing = errors.New("block timestamp must be greater than parent timestamp") ) +type ( + ChainIndex = validitywindow.ChainIndex[*ChunkCertificate] + timeValidityWindow = *validitywindow.TimeValidityWindow[*ChunkCertificate] +) + type Validator struct { NodeID ids.NodeID Weight uint64 @@ -51,6 +56,7 @@ func New[T Tx]( validators []Validator, log logging.Logger, tracer trace.Tracer, + chainIndex ChainIndex, ) (*Node[T], error) { storage, err := newChunkStorage[T](NoVerifier[T]{}, memdb.New()) if err != nil { @@ -89,13 +95,11 @@ func New[T Tx]( tracer: tracer, } - node.validityWindow = validitywindow.NewTimeValidityWindow(node.log, node.tracer, node) + node.validityWindow = validitywindow.NewTimeValidityWindow(node.log, node.tracer, chainIndex) return node, err } type ( - timeValidityWindow = *validitywindow.TimeValidityWindow[*ChunkCertificate] - Node[T Tx] struct { nodeID ids.NodeID networkID uint32 @@ -207,10 +211,6 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) return Block{}, err } - if dup.Len() == len(chunkCerts) { - return Block{}, ErrNoAvailableChunkCerts - } - availableChunkCerts := make([]*ChunkCertificate, 0) for i, chunkCert := range chunkCerts { if dup.Contains(i) { @@ -219,6 +219,10 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) availableChunkCerts = append(availableChunkCerts, chunkCert) } + if len(availableChunkCerts) == 0 { + return Block{}, ErrNoAvailableChunkCerts + } + blk := Block{ ParentID: parent.GetID(), Hght: parent.Hght + 1, @@ -301,7 +305,3 @@ func (n *Node[T]) Accept(ctx context.Context, block Block) error { return n.storage.SetMin(block.Tmstmp, chunkIDs) } - -func (*Node[T]) GetExecutionBlock(context.Context, ids.ID) (validitywindow.ExecutionBlock[*ChunkCertificate], error) { - return nil, nil -} diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index d6c8af5165..39bb1e5486 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -23,6 +23,7 @@ import ( "github.com/stretchr/testify/require" "github.com/ava-labs/hypersdk/codec" + "github.com/ava-labs/hypersdk/internal/validitywindow" "github.com/ava-labs/hypersdk/proto/pb/dsmr" ) @@ -41,6 +42,16 @@ func newTestingTracer(t *testing.T) trace.Tracer { return tracer } +type testingChainIndex struct{} + +func (*testingChainIndex) GetExecutionBlock(context.Context, ids.ID) (validitywindow.ExecutionBlock[*ChunkCertificate], error) { + return nil, nil +} + +func newChainIndexer() ChainIndex { + return &testingChainIndex{} +} + // Test that chunks can be built through Node.NewChunk func TestNode_BuildChunk(t *testing.T) { tests := []struct { @@ -130,6 +141,7 @@ func TestNode_BuildChunk(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), + newChainIndexer(), ) r.NoError(err) @@ -196,6 +208,7 @@ func TestNode_GetChunk_AvailableChunk(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), + newChainIndexer(), ) r.NoError(err) @@ -289,6 +302,7 @@ func TestNode_GetChunk_PendingChunk(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), + newChainIndexer(), ) r.NoError(err) @@ -368,6 +382,7 @@ func TestNode_GetChunk_UnknownChunk(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), + newChainIndexer(), ) r.NoError(err) @@ -583,6 +598,7 @@ func TestNode_BuiltChunksAvailableOverGetChunk(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), + newChainIndexer(), ) r.NoError(err) @@ -718,6 +734,7 @@ func TestNode_GetChunkSignature_SignValidChunk(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), + newChainIndexer(), ) r.NoError(err) @@ -768,6 +785,7 @@ func TestNode_GetChunkSignature_SignValidChunk(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), + newChainIndexer(), ) r.NoError(err) chunk, err := node2.BuildChunk( @@ -888,6 +906,7 @@ func TestNode_GetChunkSignature_DuplicateChunk(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), + newChainIndexer(), ) r.NoError(err) @@ -984,6 +1003,7 @@ func TestGetChunkSignature_PersistAttestedBlocks(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), + newChainIndexer(), ) r.NoError(err) @@ -1022,6 +1042,7 @@ func TestGetChunkSignature_PersistAttestedBlocks(t *testing.T) { []Validator{{NodeID: node1.nodeID}}, newTestingLog(), newTestingTracer(t), + newChainIndexer(), ) r.NoError(err) @@ -1325,6 +1346,7 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), + newChainIndexer(), ) r.NoError(err) @@ -1413,6 +1435,7 @@ func TestAccept_RequestReferencedChunks(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), + newChainIndexer(), ) r.NoError(err) @@ -1469,6 +1492,7 @@ func TestAccept_RequestReferencedChunks(t *testing.T) { []Validator{{NodeID: node1.nodeID}}, newTestingLog(), newTestingTracer(t), + newChainIndexer(), ) r.NoError(err) r.NoError(node2.Accept(context.Background(), blk)) From 7a3fed6d7b0187e18666c6df757bac94ef037f12 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:12:19 -0500 Subject: [PATCH 07/43] fix code + test. --- x/dsmr/node.go | 3 ++- x/dsmr/node_test.go | 66 ++++++++++++++++++++++----------------------- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/x/dsmr/node.go b/x/dsmr/node.go index 57f1c73408..122299d8be 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -213,7 +213,8 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) availableChunkCerts := make([]*ChunkCertificate, 0) for i, chunkCert := range chunkCerts { - if dup.Contains(i) { + // avoid building blocks with duplicate or expired chunk certs + if chunkCert.Expiry < timestamp || dup.Contains(i) { continue } availableChunkCerts = append(availableChunkCerts, chunkCert) diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 39bb1e5486..1c5d3c2aee 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -1115,41 +1115,41 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { parent Block timestamp int64 wantErr error - }{ /* - { - name: "no chunk certs", - parent: Block{ - ParentID: ids.GenerateTestID(), - Hght: 1, - Tmstmp: 1, - blkID: ids.GenerateTestID(), - }, - timestamp: 2, - // TODO should we be able to build empty blocks? - wantErr: ErrNoAvailableChunkCerts, + }{ + { + name: "no chunk certs", + parent: Block{ + ParentID: ids.GenerateTestID(), + Hght: 1, + Tmstmp: 1, + blkID: ids.GenerateTestID(), }, - { - name: "timestamp equal to parent", - parent: Block{ - ParentID: ids.GenerateTestID(), - Hght: 1, - Tmstmp: 1, - blkID: ids.GenerateTestID(), - }, - timestamp: 1, - wantErr: ErrTimestampNotMonotonicallyIncreasing, + timestamp: 2, + // TODO should we be able to build empty blocks? + wantErr: ErrNoAvailableChunkCerts, + }, + { + name: "timestamp equal to parent", + parent: Block{ + ParentID: ids.GenerateTestID(), + Hght: 1, + Tmstmp: 1, + blkID: ids.GenerateTestID(), }, - { - name: "timestamp older than parent", - parent: Block{ - ParentID: ids.GenerateTestID(), - Hght: 1, - Tmstmp: 1, - blkID: ids.GenerateTestID(), - }, - timestamp: 0, - wantErr: ErrTimestampNotMonotonicallyIncreasing, - },*/ + timestamp: 1, + wantErr: ErrTimestampNotMonotonicallyIncreasing, + }, + { + name: "timestamp older than parent", + parent: Block{ + ParentID: ids.GenerateTestID(), + Hght: 1, + Tmstmp: 1, + blkID: ids.GenerateTestID(), + }, + timestamp: 0, + wantErr: ErrTimestampNotMonotonicallyIncreasing, + }, { name: "expired chunk cert", chunks: []chunk{ From b312a3cdfcecd52b87e9a6208caaaa9e4bc22a50 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:19:50 -0500 Subject: [PATCH 08/43] rollback unneeded changes. --- x/dsmr/node.go | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/x/dsmr/node.go b/x/dsmr/node.go index 122299d8be..3e57f16d78 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -33,6 +33,8 @@ var ( ErrTimestampNotMonotonicallyIncreasing = errors.New("block timestamp must be greater than parent timestamp") ) +const validityWindowDuration = int64(5) + type ( ChainIndex = validitywindow.ChainIndex[*ChunkCertificate] timeValidityWindow = *validitywindow.TimeValidityWindow[*ChunkCertificate] @@ -63,7 +65,7 @@ func New[T Tx]( return nil, err } - node := &Node[T]{ + return &Node[T]{ nodeID: nodeID, networkID: networkID, chainID: chainID, @@ -90,13 +92,11 @@ func New[T Tx]( ChunkCertificateGossipHandler: &ChunkCertificateGossipHandler[T]{ storage: storage, }, - storage: storage, - log: log, - tracer: tracer, - } - - node.validityWindow = validitywindow.NewTimeValidityWindow(node.log, node.tracer, chainIndex) - return node, err + storage: storage, + log: log, + tracer: tracer, + validityWindow: validitywindow.NewTimeValidityWindow(log, tracer, chainIndex), + }, nil } type ( @@ -193,8 +193,6 @@ func (n *Node[T]) BuildChunk( return chunk, n.storage.AddLocalChunkWithCert(chunk, &chunkCert) } -const validityWindowDuration = int64(5) - // BuildBlock(ctx context.Context, parentView state.View, parent *ExecutionBlock) (*ExecutionBlock, *ExecutedBlock, merkledb.View, error) func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) (Block, error) { if timestamp <= parent.Tmstmp { @@ -219,7 +217,6 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) } availableChunkCerts = append(availableChunkCerts, chunkCert) } - if len(availableChunkCerts) == 0 { return Block{}, ErrNoAvailableChunkCerts } From 123846017fc1ab8c45061d8fbfbf2973d9ef8bf1 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:22:45 -0500 Subject: [PATCH 09/43] rename testing chain indexer. --- x/dsmr/node_test.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 1c5d3c2aee..ccf13d1170 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -48,7 +48,7 @@ func (*testingChainIndex) GetExecutionBlock(context.Context, ids.ID) (validitywi return nil, nil } -func newChainIndexer() ChainIndex { +func newTestingChainIndexer() ChainIndex { return &testingChainIndex{} } @@ -141,7 +141,7 @@ func TestNode_BuildChunk(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), - newChainIndexer(), + newTestingChainIndexer(), ) r.NoError(err) @@ -208,7 +208,7 @@ func TestNode_GetChunk_AvailableChunk(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), - newChainIndexer(), + newTestingChainIndexer(), ) r.NoError(err) @@ -302,7 +302,7 @@ func TestNode_GetChunk_PendingChunk(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), - newChainIndexer(), + newTestingChainIndexer(), ) r.NoError(err) @@ -382,7 +382,7 @@ func TestNode_GetChunk_UnknownChunk(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), - newChainIndexer(), + newTestingChainIndexer(), ) r.NoError(err) @@ -598,7 +598,7 @@ func TestNode_BuiltChunksAvailableOverGetChunk(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), - newChainIndexer(), + newTestingChainIndexer(), ) r.NoError(err) @@ -734,7 +734,7 @@ func TestNode_GetChunkSignature_SignValidChunk(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), - newChainIndexer(), + newTestingChainIndexer(), ) r.NoError(err) @@ -785,7 +785,7 @@ func TestNode_GetChunkSignature_SignValidChunk(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), - newChainIndexer(), + newTestingChainIndexer(), ) r.NoError(err) chunk, err := node2.BuildChunk( @@ -906,7 +906,7 @@ func TestNode_GetChunkSignature_DuplicateChunk(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), - newChainIndexer(), + newTestingChainIndexer(), ) r.NoError(err) @@ -1003,7 +1003,7 @@ func TestGetChunkSignature_PersistAttestedBlocks(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), - newChainIndexer(), + newTestingChainIndexer(), ) r.NoError(err) @@ -1042,7 +1042,7 @@ func TestGetChunkSignature_PersistAttestedBlocks(t *testing.T) { []Validator{{NodeID: node1.nodeID}}, newTestingLog(), newTestingTracer(t), - newChainIndexer(), + newTestingChainIndexer(), ) r.NoError(err) @@ -1346,7 +1346,7 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), - newChainIndexer(), + newTestingChainIndexer(), ) r.NoError(err) @@ -1435,7 +1435,7 @@ func TestAccept_RequestReferencedChunks(t *testing.T) { nil, newTestingLog(), newTestingTracer(t), - newChainIndexer(), + newTestingChainIndexer(), ) r.NoError(err) @@ -1492,7 +1492,7 @@ func TestAccept_RequestReferencedChunks(t *testing.T) { []Validator{{NodeID: node1.nodeID}}, newTestingLog(), newTestingTracer(t), - newChainIndexer(), + newTestingChainIndexer(), ) r.NoError(err) r.NoError(node2.Accept(context.Background(), blk)) From 8c1ab98a015e99ee033633dd0bad55ec027aac89 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:04:28 -0500 Subject: [PATCH 10/43] update --- x/dsmr/node_test.go | 62 +++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index ccf13d1170..3a1dc4fb18 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -32,16 +32,6 @@ var ( _ Verifier[tx] = (*failVerifier)(nil) ) -func newTestingLog() logging.Logger { - return logging.NewLogger("dsmr") -} - -func newTestingTracer(t *testing.T) trace.Tracer { - tracer, err := trace.New(trace.Config{}) - require.NoError(t, err) - return tracer -} - type testingChainIndex struct{} func (*testingChainIndex) GetExecutionBlock(context.Context, ids.ID) (validitywindow.ExecutionBlock[*ChunkCertificate], error) { @@ -139,8 +129,8 @@ func TestNode_BuildChunk(t *testing.T) { ids.EmptyNodeID, ), nil, - newTestingLog(), - newTestingTracer(t), + logging.NoLog{}, + trace.Noop, newTestingChainIndexer(), ) r.NoError(err) @@ -206,8 +196,8 @@ func TestNode_GetChunk_AvailableChunk(t *testing.T) { ids.EmptyNodeID, ), nil, - newTestingLog(), - newTestingTracer(t), + logging.NoLog{}, + trace.Noop, newTestingChainIndexer(), ) r.NoError(err) @@ -300,8 +290,8 @@ func TestNode_GetChunk_PendingChunk(t *testing.T) { ids.EmptyNodeID, ), nil, - newTestingLog(), - newTestingTracer(t), + logging.NoLog{}, + trace.Noop, newTestingChainIndexer(), ) r.NoError(err) @@ -380,8 +370,8 @@ func TestNode_GetChunk_UnknownChunk(t *testing.T) { ids.EmptyNodeID, ), nil, - newTestingLog(), - newTestingTracer(t), + logging.NoLog{}, + trace.Noop, newTestingChainIndexer(), ) r.NoError(err) @@ -596,8 +586,8 @@ func TestNode_BuiltChunksAvailableOverGetChunk(t *testing.T) { ids.EmptyNodeID, ), nil, - newTestingLog(), - newTestingTracer(t), + logging.NoLog{}, + trace.Noop, newTestingChainIndexer(), ) r.NoError(err) @@ -732,8 +722,8 @@ func TestNode_GetChunkSignature_SignValidChunk(t *testing.T) { ids.EmptyNodeID, ), nil, - newTestingLog(), - newTestingTracer(t), + logging.NoLog{}, + trace.Noop, newTestingChainIndexer(), ) r.NoError(err) @@ -783,8 +773,8 @@ func TestNode_GetChunkSignature_SignValidChunk(t *testing.T) { ids.EmptyNodeID, ), nil, - newTestingLog(), - newTestingTracer(t), + logging.NoLog{}, + trace.Noop, newTestingChainIndexer(), ) r.NoError(err) @@ -904,8 +894,8 @@ func TestNode_GetChunkSignature_DuplicateChunk(t *testing.T) { ids.EmptyNodeID, ), nil, - newTestingLog(), - newTestingTracer(t), + logging.NoLog{}, + trace.Noop, newTestingChainIndexer(), ) r.NoError(err) @@ -1001,8 +991,8 @@ func TestGetChunkSignature_PersistAttestedBlocks(t *testing.T) { ids.EmptyNodeID, ), nil, - newTestingLog(), - newTestingTracer(t), + logging.NoLog{}, + trace.Noop, newTestingChainIndexer(), ) r.NoError(err) @@ -1040,8 +1030,8 @@ func TestGetChunkSignature_PersistAttestedBlocks(t *testing.T) { ids.EmptyNodeID, ), []Validator{{NodeID: node1.nodeID}}, - newTestingLog(), - newTestingTracer(t), + logging.NoLog{}, + trace.Noop, newTestingChainIndexer(), ) r.NoError(err) @@ -1344,8 +1334,8 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { ids.EmptyNodeID, ), nil, - newTestingLog(), - newTestingTracer(t), + logging.NoLog{}, + trace.Noop, newTestingChainIndexer(), ) r.NoError(err) @@ -1433,8 +1423,8 @@ func TestAccept_RequestReferencedChunks(t *testing.T) { ids.EmptyNodeID, ), nil, - newTestingLog(), - newTestingTracer(t), + logging.NoLog{}, + trace.Noop, newTestingChainIndexer(), ) r.NoError(err) @@ -1490,8 +1480,8 @@ func TestAccept_RequestReferencedChunks(t *testing.T) { ids.EmptyNodeID, ), []Validator{{NodeID: node1.nodeID}}, - newTestingLog(), - newTestingTracer(t), + logging.NoLog{}, + trace.Noop, newTestingChainIndexer(), ) r.NoError(err) From f8e42a8e2dcab961a248608a68b4b366c7dcc528 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Tue, 26 Nov 2024 11:24:25 -0500 Subject: [PATCH 11/43] add unit test. --- x/dsmr/node_test.go | 112 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 3a1dc4fb18..eb8cdc0967 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -1383,6 +1383,118 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { } } +// TestDuplicateChunksElimination tests that duplicate chunks that have appeared before are getting correctly eliminated. +func TestDuplicateChunksElimination(t *testing.T) { + r := require.New(t) + + networkID := uint32(123) + chainID := ids.Empty + sk, err := bls.NewSecretKey() + r.NoError(err) + pk := bls.PublicFromSecretKey(sk) + signer := warp.NewSigner(sk, networkID, chainID) + + node, err := New[tx]( + ids.EmptyNodeID, + networkID, + chainID, + pk, + signer, + NoVerifier[tx]{}, + p2ptest.NewClient( + t, + context.Background(), + &p2p.NoOpHandler{}, + ids.EmptyNodeID, + ids.EmptyNodeID, + ), + p2ptest.NewClient( + t, + context.Background(), + &p2p.NoOpHandler{}, + ids.EmptyNodeID, + ids.EmptyNodeID, + ), + p2ptest.NewClient( + t, + context.Background(), + &p2p.NoOpHandler{}, + ids.EmptyNodeID, + ids.EmptyNodeID, + ), + nil, + logging.NoLog{}, + trace.Noop, + newTestingChainIndexer(), + ) + r.NoError(err) + + blk := Block{ + ParentID: ids.GenerateTestID(), + Hght: 1, + Tmstmp: 1, + blkID: ids.GenerateTestID(), + } + r.NoError(node.Accept(context.Background(), blk)) + + chunk, err := node.BuildChunk( + context.Background(), + []tx{ + { + ID: ids.GenerateTestID(), + Expiry: 4, + }, + }, + 4, + codec.Address{}, + ) + r.NoError(err) + chunkCert := &ChunkCertificate{ + ChunkID: chunk.GetID(), + Expiry: chunk.GetExpiry(), + Signature: NoVerifyChunkSignature{}, + } + + blk = Block{ + ParentID: ids.GenerateTestID(), + Hght: 2, + Tmstmp: 2, + blkID: ids.GenerateTestID(), + ChunkCerts: []*ChunkCertificate{ + chunkCert, + }, + } + r.NoError(node.Accept(context.Background(), blk)) + + r.NoError(node.storage.AddLocalChunkWithCert(chunk, chunkCert)) + _, err = node.BuildBlock(context.Background(), blk, 3) + r.ErrorIs(err, ErrNoAvailableChunkCerts) + + // make sure that it's not the case with any other chunk. + chunk, err = node.BuildChunk( + context.Background(), + []tx{ + { + ID: ids.GenerateTestID(), + Expiry: 4, + }, + }, + 4, + codec.Address{}, + ) + r.NoError(err) + chunkCert = &ChunkCertificate{ + ChunkID: chunk.GetID(), + Expiry: chunk.GetExpiry(), + Signature: NoVerifyChunkSignature{}, + } + r.NoError(node.Accept(context.Background(), blk)) + + r.NoError(node.storage.AddLocalChunkWithCert(chunk, chunkCert)) + _, err = node.BuildBlock(context.Background(), blk, 3) + r.NoError(err) +} + // Nodes should request chunks referenced in accepted blocks func TestAccept_RequestReferencedChunks(t *testing.T) { r := require.New(t) From b000edfb73ac741a341e167e93ef664347c2abba Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:16:42 -0500 Subject: [PATCH 12/43] update unit test. --- x/dsmr/node.go | 5 +- x/dsmr/node_test.go | 164 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 163 insertions(+), 6 deletions(-) diff --git a/x/dsmr/node.go b/x/dsmr/node.go index 3e57f16d78..ff19917b31 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -30,6 +30,7 @@ import ( var ( ErrEmptyChunk = errors.New("empty chunk") ErrNoAvailableChunkCerts = errors.New("no available chunk certs") + ErrAllChunkCertsDuplicate = errors.New("all chunk certs are duplicated") ErrTimestampNotMonotonicallyIncreasing = errors.New("block timestamp must be greater than parent timestamp") ) @@ -193,7 +194,6 @@ func (n *Node[T]) BuildChunk( return chunk, n.storage.AddLocalChunkWithCert(chunk, &chunkCert) } -// BuildBlock(ctx context.Context, parentView state.View, parent *ExecutionBlock) (*ExecutionBlock, *ExecutedBlock, merkledb.View, error) func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) (Block, error) { if timestamp <= parent.Tmstmp { return Block{}, ErrTimestampNotMonotonicallyIncreasing @@ -218,6 +218,9 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) availableChunkCerts = append(availableChunkCerts, chunkCert) } if len(availableChunkCerts) == 0 { + if dup.Len() == len(chunkCerts) && len(chunkCerts) > 0 { + return Block{}, ErrAllChunkCertsDuplicate + } return Block{}, ErrNoAvailableChunkCerts } diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index eb8cdc0967..c5513abd8b 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -32,14 +32,25 @@ var ( _ Verifier[tx] = (*failVerifier)(nil) ) -type testingChainIndex struct{} +type testingChainIndex struct { + blocks map[ids.ID]validitywindow.ExecutionBlock[*ChunkCertificate] +} -func (*testingChainIndex) GetExecutionBlock(context.Context, ids.ID) (validitywindow.ExecutionBlock[*ChunkCertificate], error) { +func (ti *testingChainIndex) GetExecutionBlock(_ context.Context, id ids.ID) (validitywindow.ExecutionBlock[*ChunkCertificate], error) { + if blk, has := ti.blocks[id]; has { + return blk, nil + } return nil, nil } -func newTestingChainIndexer() ChainIndex { - return &testingChainIndex{} +func (ti *testingChainIndex) set(id ids.ID, blk validitywindow.ExecutionBlock[*ChunkCertificate]) { + ti.blocks[id] = blk +} + +func newTestingChainIndexer() *testingChainIndex { + return &testingChainIndex{ + blocks: make(map[ids.ID]validitywindow.ExecutionBlock[*ChunkCertificate]), + } } // Test that chunks can be built through Node.NewChunk @@ -1468,7 +1479,7 @@ func TestDuplicateChunksElimination(t *testing.T) { r.NoError(node.storage.AddLocalChunkWithCert(chunk, chunkCert)) _, err = node.BuildBlock(context.Background(), blk, 3) - r.ErrorIs(err, ErrNoAvailableChunkCerts) + r.ErrorIs(err, ErrAllChunkCertsDuplicate) // make sure that it's not the case with any other chunk. chunk, err = node.BuildChunk( @@ -1495,6 +1506,149 @@ func TestDuplicateChunksElimination(t *testing.T) { r.NoError(err) } +func TestNode_Execute_Chunks(t *testing.T) { + r := require.New(t) + networkID := uint32(123) + chainID := ids.Empty + sk1, err := bls.NewSecretKey() + r.NoError(err) + pk := bls.PublicFromSecretKey(sk1) + signer := warp.NewSigner(sk1, networkID, chainID) + r.NoError(err) + + makeChunkCert := func(chunk Chunk[tx]) *ChunkCertificate { + return &ChunkCertificate{ + ChunkID: chunk.GetID(), + Expiry: chunk.GetExpiry(), + Signature: NoVerifyChunkSignature{}, + } + } + initChunks := func(node *Node[tx]) []Chunk[tx] { + var chunks []Chunk[tx] + for expiry := int64(0); expiry < 5; expiry++ { + chunk, err := node.BuildChunk( + context.Background(), + []tx{ + { + ID: ids.GenerateTestID(), + Expiry: expiry, + }, + }, + expiry, + codec.Address{}, + ) + r.NoError(err) + chunks = append(chunks, chunk) + + } + return chunks + } + testCases := []struct { + name string + parentBlocks [][]int // for each parent, a list of the chunks included. + chunks []int + timestamp int64 + executeWantErr error + buildWantErr error + }{ + { + name: "three empty blocks", + parentBlocks: [][]int{{}, {}, {}}, + chunks: []int{}, + timestamp: 4, + executeWantErr: nil, + }, + { + name: "three blocks, unique chunks", + parentBlocks: [][]int{{1}, {2}, {3}}, + chunks: []int{}, + timestamp: 4, + executeWantErr: nil, + }, + { + name: "two blocks one duplicate chunk", + parentBlocks: [][]int{{0, 1}, {1, 2}}, + chunks: []int{}, + timestamp: 4, + executeWantErr: validitywindow.ErrDuplicateContainer, + }, + { + name: "one block duplicate chunks", + parentBlocks: [][]int{{1, 1}}, + chunks: []int{}, + timestamp: 4, + executeWantErr: validitywindow.ErrDuplicateContainer, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + indexer := newTestingChainIndexer() + node, err := New[tx]( + ids.EmptyNodeID, + networkID, + chainID, + pk, + signer, + NoVerifier[tx]{}, + p2ptest.NewClient( + t, + context.Background(), + &p2p.NoOpHandler{}, + ids.EmptyNodeID, + ids.EmptyNodeID, + ), + p2ptest.NewClient( + t, + context.Background(), + &p2p.NoOpHandler{}, + ids.EmptyNodeID, + ids.EmptyNodeID, + ), + p2ptest.NewClient( + t, + context.Background(), + &p2p.NoOpHandler{}, + ids.EmptyNodeID, + ids.EmptyNodeID, + ), + nil, + logging.NoLog{}, + trace.Noop, + indexer, + ) + r.NoError(err) + + chunks := initChunks(node) + + // initialize node history. + var parentBlk Block + for blockNum, chunkList := range testCase.parentBlocks { + blk := Block{ + ParentID: parentBlk.GetID(), + Hght: uint64(blockNum), + Tmstmp: int64(blockNum), + blkID: ids.GenerateTestID(), + } + for _, chunkIndex := range chunkList { + blk.ChunkCerts = append(blk.ChunkCerts, makeChunkCert(chunks[chunkIndex])) + } + if blockNum > 0 { + r.ErrorIs(node.Execute(context.Background(), parentBlk, blk), testCase.executeWantErr) + } + r.NoError(node.Accept(context.Background(), blk)) + indexer.set(blk.GetID(), blk) + parentBlk = blk + } + // feed the chunks into the storage and build a block. + for _, chunkIdx := range testCase.chunks { + r.NoError(node.storage.AddLocalChunkWithCert(chunks[chunkIdx], makeChunkCert(chunks[chunkIdx]))) + } + _, err = node.BuildBlock(context.Background(), parentBlk, testCase.timestamp) + r.ErrorIs(err, testCase.buildWantErr) + }) + } +} + // Nodes should request chunks referenced in accepted blocks func TestAccept_RequestReferencedChunks(t *testing.T) { r := require.New(t) From 6911f26873f5025e2273061511ab23ba0cec94d3 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:15:10 -0500 Subject: [PATCH 13/43] lint --- x/dsmr/node_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index c5513abd8b..ebc311770a 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -1539,7 +1539,6 @@ func TestNode_Execute_Chunks(t *testing.T) { ) r.NoError(err) chunks = append(chunks, chunk) - } return chunks } From f2816e7204149fb00cb65e67a2df88b97d4b0459 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:52:02 -0500 Subject: [PATCH 14/43] update --- internal/validitywindow/validitywindow.go | 10 +++ x/dsmr/node.go | 22 +++--- x/dsmr/node_test.go | 83 ++++++++++++++++++----- 3 files changed, 91 insertions(+), 24 deletions(-) diff --git a/internal/validitywindow/validitywindow.go b/internal/validitywindow/validitywindow.go index 5d8d10d418..0393df9724 100644 --- a/internal/validitywindow/validitywindow.go +++ b/internal/validitywindow/validitywindow.go @@ -9,6 +9,7 @@ import ( "fmt" "sync" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/trace" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" @@ -69,6 +70,15 @@ func (v *TimeValidityWindow[Container]) VerifyExpiryReplayProtection( if dup.Len() > 0 { return fmt.Errorf("%w: duplicate in ancestry", ErrDuplicateContainer) } + // make sure we have no repeats within the block itself. + blkTxsIDs := make(map[ids.ID]bool, len(blk.Txs())) + for _, tx := range blk.Txs() { + id := tx.GetID() + if _, has := blkTxsIDs[id]; has { + return fmt.Errorf("%w: duplicate in block", ErrDuplicateContainer) + } + blkTxsIDs[id] = true + } return nil } diff --git a/x/dsmr/node.go b/x/dsmr/node.go index ff19917b31..b26631a0e7 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -34,8 +34,6 @@ var ( ErrTimestampNotMonotonicallyIncreasing = errors.New("block timestamp must be greater than parent timestamp") ) -const validityWindowDuration = int64(5) - type ( ChainIndex = validitywindow.ChainIndex[*ChunkCertificate] timeValidityWindow = *validitywindow.TimeValidityWindow[*ChunkCertificate] @@ -60,6 +58,7 @@ func New[T Tx]( log logging.Logger, tracer trace.Tracer, chainIndex ChainIndex, + validityWindowDuration int64, ) (*Node[T], error) { storage, err := newChunkStorage[T](NoVerifier[T]{}, memdb.New()) if err != nil { @@ -93,10 +92,11 @@ func New[T Tx]( ChunkCertificateGossipHandler: &ChunkCertificateGossipHandler[T]{ storage: storage, }, - storage: storage, - log: log, - tracer: tracer, - validityWindow: validitywindow.NewTimeValidityWindow(log, tracer, chainIndex), + storage: storage, + log: log, + tracer: tracer, + validityWindow: validitywindow.NewTimeValidityWindow(log, tracer, chainIndex), + validityWindowDuration: validityWindowDuration, }, nil } @@ -119,6 +119,7 @@ type ( storage *chunkStorage[T] log logging.Logger tracer trace.Tracer + validityWindowDuration int64 } ) @@ -200,7 +201,7 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) } chunkCerts := n.storage.GatherChunkCerts() - oldestAllowed := timestamp - validityWindowDuration + oldestAllowed := timestamp - n.validityWindowDuration if oldestAllowed < 0 { oldestAllowed = 0 } @@ -245,7 +246,12 @@ func (n *Node[T]) Execute(ctx context.Context, parentBlock Block, block Block) e // TODO: Verify header fields // Find repeats - if err := n.validityWindow.VerifyExpiryReplayProtection(ctx, block, parentBlock.Tmstmp); err != nil { + + oldestAllowed := block.Timestamp() - n.validityWindowDuration + if oldestAllowed < 0 { + oldestAllowed = 0 + } + if err := n.validityWindow.VerifyExpiryReplayProtection(ctx, block, oldestAllowed); err != nil { return err } diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index ebc311770a..e0d3532809 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -32,6 +32,8 @@ var ( _ Verifier[tx] = (*failVerifier)(nil) ) +const testingDefaultValidityWindowDuration = int64(5) + type testingChainIndex struct { blocks map[ids.ID]validitywindow.ExecutionBlock[*ChunkCertificate] } @@ -143,6 +145,7 @@ func TestNode_BuildChunk(t *testing.T) { logging.NoLog{}, trace.Noop, newTestingChainIndexer(), + testingDefaultValidityWindowDuration, ) r.NoError(err) @@ -210,6 +213,7 @@ func TestNode_GetChunk_AvailableChunk(t *testing.T) { logging.NoLog{}, trace.Noop, newTestingChainIndexer(), + testingDefaultValidityWindowDuration, ) r.NoError(err) @@ -304,6 +308,7 @@ func TestNode_GetChunk_PendingChunk(t *testing.T) { logging.NoLog{}, trace.Noop, newTestingChainIndexer(), + testingDefaultValidityWindowDuration, ) r.NoError(err) @@ -384,6 +389,7 @@ func TestNode_GetChunk_UnknownChunk(t *testing.T) { logging.NoLog{}, trace.Noop, newTestingChainIndexer(), + testingDefaultValidityWindowDuration, ) r.NoError(err) @@ -600,6 +606,7 @@ func TestNode_BuiltChunksAvailableOverGetChunk(t *testing.T) { logging.NoLog{}, trace.Noop, newTestingChainIndexer(), + testingDefaultValidityWindowDuration, ) r.NoError(err) @@ -736,6 +743,7 @@ func TestNode_GetChunkSignature_SignValidChunk(t *testing.T) { logging.NoLog{}, trace.Noop, newTestingChainIndexer(), + testingDefaultValidityWindowDuration, ) r.NoError(err) @@ -787,6 +795,7 @@ func TestNode_GetChunkSignature_SignValidChunk(t *testing.T) { logging.NoLog{}, trace.Noop, newTestingChainIndexer(), + testingDefaultValidityWindowDuration, ) r.NoError(err) chunk, err := node2.BuildChunk( @@ -908,6 +917,7 @@ func TestNode_GetChunkSignature_DuplicateChunk(t *testing.T) { logging.NoLog{}, trace.Noop, newTestingChainIndexer(), + testingDefaultValidityWindowDuration, ) r.NoError(err) @@ -1005,6 +1015,7 @@ func TestGetChunkSignature_PersistAttestedBlocks(t *testing.T) { logging.NoLog{}, trace.Noop, newTestingChainIndexer(), + testingDefaultValidityWindowDuration, ) r.NoError(err) @@ -1044,6 +1055,7 @@ func TestGetChunkSignature_PersistAttestedBlocks(t *testing.T) { logging.NoLog{}, trace.Noop, newTestingChainIndexer(), + testingDefaultValidityWindowDuration, ) r.NoError(err) @@ -1348,6 +1360,7 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { logging.NoLog{}, trace.Noop, newTestingChainIndexer(), + testingDefaultValidityWindowDuration, ) r.NoError(err) @@ -1437,6 +1450,7 @@ func TestDuplicateChunksElimination(t *testing.T) { logging.NoLog{}, trace.Noop, newTestingChainIndexer(), + testingDefaultValidityWindowDuration, ) r.NoError(err) @@ -1543,12 +1557,13 @@ func TestNode_Execute_Chunks(t *testing.T) { return chunks } testCases := []struct { - name string - parentBlocks [][]int // for each parent, a list of the chunks included. - chunks []int - timestamp int64 - executeWantErr error - buildWantErr error + name string + parentBlocks [][]int // for each parent, a list of the chunks included. + chunks []int + timestamp int64 + executeWantErr error + buildWantErr error + validalidityWindowDuration int64 }{ { name: "three empty blocks", @@ -1566,22 +1581,43 @@ func TestNode_Execute_Chunks(t *testing.T) { }, { name: "two blocks one duplicate chunk", - parentBlocks: [][]int{{0, 1}, {1, 2}}, - chunks: []int{}, - timestamp: 4, + parentBlocks: [][]int{{0, 1}}, + chunks: []int{1, 2}, + timestamp: 2, executeWantErr: validitywindow.ErrDuplicateContainer, + buildWantErr: nil, // build would filter out duplicate chunks, hence no error. }, { name: "one block duplicate chunks", - parentBlocks: [][]int{{1, 1}}, - chunks: []int{}, - timestamp: 4, + parentBlocks: [][]int{{}}, + chunks: []int{1, 1}, + timestamp: 2, executeWantErr: validitywindow.ErrDuplicateContainer, + buildWantErr: nil, // build would filter out duplicate chunks, hence no error. + }, + { + name: "three blocks non consecutive duplicate chunks", + parentBlocks: [][]int{{1}, {2}}, + chunks: []int{1}, + timestamp: 3, + executeWantErr: validitywindow.ErrDuplicateContainer, + }, + { + name: "three blocks non consecutive duplicate chunks outside validity window", + parentBlocks: [][]int{{1}, {2}}, + chunks: []int{1}, + timestamp: 3, + executeWantErr: nil, + validalidityWindowDuration: 1, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { indexer := newTestingChainIndexer() + valWind := testCase.validalidityWindowDuration + if valWind == 0 { + valWind = testingDefaultValidityWindowDuration + } node, err := New[tx]( ids.EmptyNodeID, networkID, @@ -1614,6 +1650,7 @@ func TestNode_Execute_Chunks(t *testing.T) { logging.NoLog{}, trace.Noop, indexer, + valWind, ) r.NoError(err) @@ -1631,9 +1668,9 @@ func TestNode_Execute_Chunks(t *testing.T) { for _, chunkIndex := range chunkList { blk.ChunkCerts = append(blk.ChunkCerts, makeChunkCert(chunks[chunkIndex])) } - if blockNum > 0 { - r.ErrorIs(node.Execute(context.Background(), parentBlk, blk), testCase.executeWantErr) - } + + r.NoError(node.Execute(context.Background(), parentBlk, blk)) + r.NoError(node.Accept(context.Background(), blk)) indexer.set(blk.GetID(), blk) parentBlk = blk @@ -1642,8 +1679,20 @@ func TestNode_Execute_Chunks(t *testing.T) { for _, chunkIdx := range testCase.chunks { r.NoError(node.storage.AddLocalChunkWithCert(chunks[chunkIdx], makeChunkCert(chunks[chunkIdx]))) } - _, err = node.BuildBlock(context.Background(), parentBlk, testCase.timestamp) + newBlk, err := node.BuildBlock(context.Background(), parentBlk, testCase.timestamp) r.ErrorIs(err, testCase.buildWantErr) + + // create the block so that we can test it against the execute directly. + newBlk = Block{ + ParentID: parentBlk.GetID(), + Hght: uint64(testCase.timestamp), + Tmstmp: int64(testCase.timestamp), + blkID: ids.GenerateTestID(), + } + for _, chunkIndex := range testCase.chunks { + newBlk.ChunkCerts = append(newBlk.ChunkCerts, makeChunkCert(chunks[chunkIndex])) + } + r.ErrorIs(node.Execute(context.Background(), parentBlk, newBlk), testCase.executeWantErr) }) } } @@ -1691,6 +1740,7 @@ func TestAccept_RequestReferencedChunks(t *testing.T) { logging.NoLog{}, trace.Noop, newTestingChainIndexer(), + testingDefaultValidityWindowDuration, ) r.NoError(err) @@ -1748,6 +1798,7 @@ func TestAccept_RequestReferencedChunks(t *testing.T) { logging.NoLog{}, trace.Noop, newTestingChainIndexer(), + testingDefaultValidityWindowDuration, ) r.NoError(err) r.NoError(node2.Accept(context.Background(), blk)) From e78f84751b620e663d0893ebf4ad896309f0850c Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Thu, 28 Nov 2024 12:00:04 -0500 Subject: [PATCH 15/43] lint --- x/dsmr/node.go | 3 +++ x/dsmr/node_test.go | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/x/dsmr/node.go b/x/dsmr/node.go index b26631a0e7..5754a405d4 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -244,6 +244,9 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) func (n *Node[T]) Execute(ctx context.Context, parentBlock Block, block Block) error { // TODO: Verify header fields + if block.Tmstmp <= parentBlock.Tmstmp { + return ErrTimestampNotMonotonicallyIncreasing + } // Find repeats diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index e0d3532809..9bacc6fa54 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -1679,14 +1679,14 @@ func TestNode_Execute_Chunks(t *testing.T) { for _, chunkIdx := range testCase.chunks { r.NoError(node.storage.AddLocalChunkWithCert(chunks[chunkIdx], makeChunkCert(chunks[chunkIdx]))) } - newBlk, err := node.BuildBlock(context.Background(), parentBlk, testCase.timestamp) + _, err = node.BuildBlock(context.Background(), parentBlk, testCase.timestamp) r.ErrorIs(err, testCase.buildWantErr) // create the block so that we can test it against the execute directly. - newBlk = Block{ + newBlk := Block{ ParentID: parentBlk.GetID(), Hght: uint64(testCase.timestamp), - Tmstmp: int64(testCase.timestamp), + Tmstmp: testCase.timestamp, blkID: ids.GenerateTestID(), } for _, chunkIndex := range testCase.chunks { From 270245ab0d7ec7e87757859e38b2d23223b6a449 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Thu, 28 Nov 2024 12:09:37 -0500 Subject: [PATCH 16/43] update --- x/dsmr/node.go | 2 +- x/dsmr/node_test.go | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/x/dsmr/node.go b/x/dsmr/node.go index 5754a405d4..2fe35b8d5b 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -244,7 +244,7 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) func (n *Node[T]) Execute(ctx context.Context, parentBlock Block, block Block) error { // TODO: Verify header fields - if block.Tmstmp <= parentBlock.Tmstmp { + if block.Tmstmp <= parentBlock.Tmstmp && parentBlock.Hght > 0 { return ErrTimestampNotMonotonicallyIncreasing } diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 9bacc6fa54..90cad2c93a 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -1610,6 +1610,14 @@ func TestNode_Execute_Chunks(t *testing.T) { executeWantErr: nil, validalidityWindowDuration: 1, }, + { + name: "monotonic timestamping", + parentBlocks: [][]int{{}, {}}, + chunks: []int{2}, + timestamp: 0, + executeWantErr: ErrTimestampNotMonotonicallyIncreasing, + buildWantErr: ErrTimestampNotMonotonicallyIncreasing, + }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { From 2fa701d127a372f9acaa98c22d833e20818f6769 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:39:54 -0500 Subject: [PATCH 17/43] fix few CR feedback. --- internal/validitywindow/validitywindow.go | 7 ++++--- x/dsmr/node_test.go | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/internal/validitywindow/validitywindow.go b/internal/validitywindow/validitywindow.go index f234a2f58d..7ed51a1e82 100644 --- a/internal/validitywindow/validitywindow.go +++ b/internal/validitywindow/validitywindow.go @@ -71,13 +71,14 @@ func (v *TimeValidityWindow[Container]) VerifyExpiryReplayProtection( return fmt.Errorf("%w: duplicate in ancestry", ErrDuplicateContainer) } // make sure we have no repeats within the block itself. - blkTxsIDs := make(map[ids.ID]bool, len(blk.Txs())) + // set.Set + blkTxsIDs := set.NewSet[ids.ID](len(blk.Txs())) for _, tx := range blk.Txs() { id := tx.GetID() - if _, has := blkTxsIDs[id]; has { + if blkTxsIDs.Contains(id) { return fmt.Errorf("%w: duplicate in block", ErrDuplicateContainer) } - blkTxsIDs[id] = true + blkTxsIDs.Add(id) } return nil } diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 1fbd1baabb..6f9ab9ca84 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -46,15 +46,15 @@ type testingChainIndex struct { blocks map[ids.ID]validitywindow.ExecutionBlock[*ChunkCertificate] } -func (ti *testingChainIndex) GetExecutionBlock(_ context.Context, id ids.ID) (validitywindow.ExecutionBlock[*ChunkCertificate], bool, error) { - if blk, has := ti.blocks[id]; has { +func (t *testingChainIndex) GetExecutionBlock(_ context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[*ChunkCertificate], bool, error) { + if blk, ok := t.blocks[blkID]; ok { return blk, true, nil } return nil, false, nil } -func (ti *testingChainIndex) set(id ids.ID, blk validitywindow.ExecutionBlock[*ChunkCertificate]) { - ti.blocks[id] = blk +func (t *testingChainIndex) set(blkID ids.ID, blk validitywindow.ExecutionBlock[*ChunkCertificate]) { + t.blocks[blkID] = blk } func newTestingChainIndexer() *testingChainIndex { From 2220b5e60eda8b26a3b1daa325d3d801988c3a22 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Fri, 6 Dec 2024 09:25:39 -0500 Subject: [PATCH 18/43] add certSet --- x/dsmr/block.go | 22 ++++++++++++++++++---- x/dsmr/node.go | 12 ++++++------ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/x/dsmr/block.go b/x/dsmr/block.go index 84f8a28afb..43b68e4ed6 100644 --- a/x/dsmr/block.go +++ b/x/dsmr/block.go @@ -6,6 +6,7 @@ package dsmr import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/crypto/bls" + "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/avalanchego/vms/platformvm/warp" @@ -122,6 +123,18 @@ type Block struct { blkID ids.ID blkBytes []byte + certSet set.Set[ids.ID] +} + +func NewBlock(parentID ids.ID, height uint64, timestamp int64, chunkCerts []*ChunkCertificate) Block { + blk := Block{ + ParentID: parentID, + Hght: height, + Tmstmp: timestamp, + ChunkCerts: chunkCerts, + } + blk.init() + return blk } func (b Block) GetID() ids.ID { @@ -145,10 +158,11 @@ func (b Block) Txs() []*ChunkCertificate { } func (b Block) Contains(id ids.ID) bool { + return b.certSet.Contains(id) +} + +func (b Block) init() { for _, c := range b.ChunkCerts { - if c.ChunkID == id { - return true - } + b.certSet.Add(c.ChunkID) } - return false } diff --git a/x/dsmr/node.go b/x/dsmr/node.go index 81f4185087..cf0c51ff97 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -266,12 +266,12 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) return Block{}, ErrNoAvailableChunkCerts } - blk := Block{ - ParentID: parent.GetID(), - Hght: parent.Hght + 1, - Tmstmp: timestamp, - ChunkCerts: availableChunkCerts, - } + blk := NewBlock( + parent.GetID(), + parent.Hght+1, + timestamp, + availableChunkCerts, + ) packer := wrappers.Packer{Bytes: make([]byte, 0, InitialChunkSize), MaxSize: consts.NetworkSizeLimit} if err := codec.LinearCodec.MarshalInto(blk, &packer); err != nil { From 39905b91187b52f83a6a4d609c122f54f89e6f6b Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Fri, 6 Dec 2024 09:28:04 -0500 Subject: [PATCH 19/43] remove type declaration block. --- x/dsmr/node.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/x/dsmr/node.go b/x/dsmr/node.go index cf0c51ff97..f78c19b424 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -49,11 +49,6 @@ var ( ErrFailedToReplicate = errors.New("failed to replicate to sufficient stake") ) -type ( - ChainIndex = validitywindow.ChainIndex[*ChunkCertificate] - timeValidityWindow = *validitywindow.TimeValidityWindow[*ChunkCertificate] -) - type Validator struct { NodeID ids.NodeID Weight uint64 @@ -79,7 +74,7 @@ func New[T Tx]( quorumNum uint64, quorumDen uint64, tracer trace.Tracer, - chainIndex ChainIndex, + chainIndex validitywindow.ChainIndex[*ChunkCertificate], validityWindowDuration int64, ) (*Node[T], error) { return &Node[T]{ @@ -127,7 +122,7 @@ type Node[T Tx] struct { log logging.Logger tracer trace.Tracer validityWindowDuration int64 - validityWindow timeValidityWindow + validityWindow *validitywindow.TimeValidityWindow[*ChunkCertificate] } // BuildChunk builds transactions into a Chunk From d54e1a0b93a7c291c78e0af060fc1f1c16bba21d Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Fri, 6 Dec 2024 10:19:52 -0500 Subject: [PATCH 20/43] fix tests --- x/dsmr/certificate.go | 2 - x/dsmr/node.go | 15 +++---- x/dsmr/node_test.go | 98 ++++++++++++++++++++----------------------- 3 files changed, 51 insertions(+), 64 deletions(-) diff --git a/x/dsmr/certificate.go b/x/dsmr/certificate.go index 621cf4a898..262cc8d27e 100644 --- a/x/dsmr/certificate.go +++ b/x/dsmr/certificate.go @@ -54,8 +54,6 @@ type ChunkCertificate struct { func (c ChunkCertificate) GetID() ids.ID { return c.ChunkID } func (c ChunkCertificate) GetExpiry() int64 { return c.Expiry } -func (c *ChunkCertificate) GetSlot() int64 { return c.Expiry } - func (c *ChunkCertificate) Bytes() []byte { bytes, err := Codec.Marshal(CodecVersion, c) if err != nil { diff --git a/x/dsmr/node.go b/x/dsmr/node.go index f78c19b424..9243fab023 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -75,7 +75,7 @@ func New[T Tx]( quorumDen uint64, tracer trace.Tracer, chainIndex validitywindow.ChainIndex[*ChunkCertificate], - validityWindowDuration int64, + validityWindowDuration time.Duration, ) (*Node[T], error) { return &Node[T]{ ID: nodeID, @@ -121,7 +121,7 @@ type Node[T Tx] struct { storage *ChunkStorage[T] log logging.Logger tracer trace.Tracer - validityWindowDuration int64 + validityWindowDuration time.Duration validityWindow *validitywindow.TimeValidityWindow[*ChunkCertificate] } @@ -237,10 +237,8 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) } chunkCerts := n.storage.GatherChunkCerts() - oldestAllowed := timestamp - n.validityWindowDuration - if oldestAllowed < 0 { - oldestAllowed = 0 - } + oldestAllowed := max(0, timestamp-int64(n.validityWindowDuration)) + dup, err := n.validityWindow.IsRepeat(ctx, parent, chunkCerts, oldestAllowed) if err != nil { return Block{}, err @@ -307,11 +305,8 @@ func (n *Node[T]) Verify(ctx context.Context, parent Block, block Block) error { } // Find repeats + oldestAllowed := max(0, block.Timestamp()-int64(n.validityWindowDuration)) - oldestAllowed := block.Timestamp() - n.validityWindowDuration - if oldestAllowed < 0 { - oldestAllowed = 0 - } if err := n.validityWindow.VerifyExpiryReplayProtection(ctx, block, oldestAllowed); err != nil { return err } diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 6f9ab9ca84..5877605bcf 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -40,7 +40,7 @@ var ( chainID = ids.Empty ) -const testingDefaultValidityWindowDuration = int64(5) +const testingDefaultValidityWindowDuration = time.Duration(5) type testingChainIndex struct { blocks map[ids.ID]validitywindow.ExecutionBlock[*ChunkCertificate] @@ -1120,63 +1120,56 @@ func TestNode_Verify_Chunks(t *testing.T) { parentBlocks [][]int // for each parent, a list of the chunks included. chunks []int timestamp int64 - executeWantErr error + verifyWantErr error buildWantErr error - validalidityWindowDuration int64 - }{ /* - { - name: "three empty blocks", - parentBlocks: [][]int{{}, {}, {}}, - chunks: []int{}, - timestamp: 4, - executeWantErr: nil, - }, - { - name: "three blocks, unique chunks", - parentBlocks: [][]int{{1}, {2}, {3}}, - chunks: []int{}, - timestamp: 4, - executeWantErr: nil, - }, - { - name: "two blocks one duplicate chunk", - parentBlocks: [][]int{{0, 1}}, - chunks: []int{1, 2}, - timestamp: 2, - executeWantErr: validitywindow.ErrDuplicateContainer, - buildWantErr: nil, // build would filter out duplicate chunks, hence no error. - }, - { - name: "one block duplicate chunks", - parentBlocks: [][]int{{}}, - chunks: []int{1, 1}, - timestamp: 2, - executeWantErr: validitywindow.ErrDuplicateContainer, - buildWantErr: nil, // build would filter out duplicate chunks, hence no error. - }, - { - name: "three blocks non consecutive duplicate chunks", - parentBlocks: [][]int{{1}, {2}}, - chunks: []int{1}, - timestamp: 3, - executeWantErr: validitywindow.ErrDuplicateContainer, - },*/ + validalidityWindowDuration time.Duration + }{ + { + name: "three blocks, unique chunks", + parentBlocks: [][]int{{1}, {2}, {3}}, + chunks: []int{4}, + timestamp: 4, + verifyWantErr: nil, + }, + { + name: "two blocks one duplicate chunk", + parentBlocks: [][]int{{0, 2}}, + chunks: []int{2, 4}, + timestamp: 2, + verifyWantErr: validitywindow.ErrDuplicateContainer, + buildWantErr: nil, // build would filter out duplicate chunks, hence no error. + }, + { + name: "one block duplicate chunks", + parentBlocks: [][]int{}, + chunks: []int{1, 1}, + timestamp: 1, + verifyWantErr: validitywindow.ErrDuplicateContainer, + buildWantErr: nil, // build would filter out duplicate chunks, hence no error. + }, + { + name: "three blocks non consecutive duplicate chunks", + parentBlocks: [][]int{{3}, {2}}, + chunks: []int{3}, + timestamp: 3, + verifyWantErr: validitywindow.ErrDuplicateContainer, + }, { name: "three blocks non consecutive duplicate chunks outside validity window", parentBlocks: [][]int{{1}, {2}}, chunks: []int{1}, timestamp: 3, - executeWantErr: nil, + verifyWantErr: nil, validalidityWindowDuration: 1, - }, /* - { - name: "monotonic timestamping", - parentBlocks: [][]int{{}, {}}, - chunks: []int{2}, - timestamp: 0, - executeWantErr: ErrTimestampNotMonotonicallyIncreasing, - buildWantErr: ErrTimestampNotMonotonicallyIncreasing, - },*/ + }, + { + name: "monotonic timestamping", + parentBlocks: [][]int{}, + chunks: []int{2}, + timestamp: 0, + verifyWantErr: ErrInvalidBlockHeight, + buildWantErr: ErrTimestampNotMonotonicallyIncreasing, + }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { @@ -1268,6 +1261,7 @@ func TestNode_Verify_Chunks(t *testing.T) { chunks, chunkCerts := initChunks(node) + indexer.set(genesisBlk.GetID(), genesisBlk) // initialize node history. parentBlk := genesisBlk for blockNum, chunkList := range testCase.parentBlocks { @@ -1304,7 +1298,7 @@ func TestNode_Verify_Chunks(t *testing.T) { for _, chunkIndex := range testCase.chunks { newBlk.ChunkCerts = append(newBlk.ChunkCerts, chunkCerts[chunkIndex]) } - r.ErrorIs(node.Verify(context.Background(), parentBlk, newBlk), testCase.executeWantErr) + r.ErrorIs(node.Verify(context.Background(), parentBlk, newBlk), testCase.verifyWantErr) }) } } From 8e83b6d7e28b2fd8487e40a4cf0b01b5b2d9de5f Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Fri, 6 Dec 2024 10:29:24 -0500 Subject: [PATCH 21/43] update --- x/dsmr/node.go | 2 +- x/dsmr/node_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x/dsmr/node.go b/x/dsmr/node.go index 9243fab023..f5b601946f 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -57,6 +57,7 @@ type Validator struct { func New[T Tx]( log logging.Logger, + tracer trace.Tracer, nodeID ids.NodeID, networkID uint32, chainID ids.ID, @@ -73,7 +74,6 @@ func New[T Tx]( lastAccepted Block, quorumNum uint64, quorumDen uint64, - tracer trace.Tracer, chainIndex validitywindow.ChainIndex[*ChunkCertificate], validityWindowDuration time.Duration, ) (*Node[T], error) { diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 5877605bcf..cbf50b74d5 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -533,6 +533,7 @@ func TestNode_GetChunkSignature_SignValidChunk(t *testing.T) { node, err := New[tx]( logging.NoLog{}, + trace.Noop, nodeID, networkID, chainID, @@ -575,7 +576,6 @@ func TestNode_GetChunkSignature_SignValidChunk(t *testing.T) { }, 1, 1, - trace.Noop, newTestingChainIndexer(), testingDefaultValidityWindowDuration, ) @@ -1219,6 +1219,7 @@ func TestNode_Verify_Chunks(t *testing.T) { node, err := New[tx]( logging.NoLog{}, + trace.Noop, nodeID, networkID, chainID, @@ -1253,7 +1254,6 @@ func TestNode_Verify_Chunks(t *testing.T) { genesisBlk, 1, 1, - trace.Noop, indexer, valWind, ) @@ -1642,6 +1642,7 @@ func newNodes(t *testing.T, n int) []*Node[tx] { node, err := New[tx]( logging.NoLog{}, + trace.Noop, validators[i].NodeID, networkID, chainID, @@ -1681,7 +1682,6 @@ func newNodes(t *testing.T, n int) []*Node[tx] { }, 1, 1, - trace.Noop, newTestingChainIndexer(), testingDefaultValidityWindowDuration, ) From fdd4639b1d0e03a3f025f4bd174f2f8738c9b2ea Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Fri, 6 Dec 2024 10:33:59 -0500 Subject: [PATCH 22/43] update --- x/dsmr/block.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x/dsmr/block.go b/x/dsmr/block.go index 43b68e4ed6..7db1a01caf 100644 --- a/x/dsmr/block.go +++ b/x/dsmr/block.go @@ -12,14 +12,15 @@ import ( "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" - "github.com/ava-labs/hypersdk/internal/emap" "github.com/ava-labs/hypersdk/utils" ) const InitialChunkSize = 250 * 1024 +// Tx implements emap.Item type Tx interface { - emap.Item + GetID() ids.ID + GetExpiry() int64 GetSponsor() codec.Address } From 9e127f16e48e457ec2d2e8ed87e5b2794670533d Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 9 Dec 2024 10:14:03 -0500 Subject: [PATCH 23/43] update --- x/dsmr/node.go | 3 --- x/dsmr/node_test.go | 10 +++++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/x/dsmr/node.go b/x/dsmr/node.go index f5b601946f..22ab04b7e5 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -253,9 +253,6 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) availableChunkCerts = append(availableChunkCerts, chunkCert) } if len(availableChunkCerts) == 0 { - if dup.Len() == len(chunkCerts) && len(chunkCerts) > 0 { - return Block{}, ErrAllChunkCertsDuplicate - } return Block{}, ErrNoAvailableChunkCerts } diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index cbf50b74d5..5bdc131bcf 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -1062,7 +1062,7 @@ func TestDuplicateChunksElimination(t *testing.T) { r.NoError(node.storage.AddLocalChunkWithCert(chunk, &chunkCert)) _, err = node.BuildBlock(context.Background(), blk, 3) - r.ErrorIs(err, ErrAllChunkCertsDuplicate) + r.ErrorIs(err, ErrNoAvailableChunkCerts) // make sure that it's not the case with any other chunk. anotherChunk, anotherChunkCert, err := node.BuildChunk( @@ -1170,6 +1170,14 @@ func TestNode_Verify_Chunks(t *testing.T) { verifyWantErr: ErrInvalidBlockHeight, buildWantErr: ErrTimestampNotMonotonicallyIncreasing, }, + { + name: "empty block", + parentBlocks: [][]int{{1}, {2}, {3}, {4}}, + chunks: []int{}, + timestamp: 5, + verifyWantErr: ErrEmptyBlock, + buildWantErr: ErrNoAvailableChunkCerts, + }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { From 0f3fe737b5de1bf4cb1ebecd5dc492d4b2337e74 Mon Sep 17 00:00:00 2001 From: Tsachi Herman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 9 Dec 2024 10:39:27 -0500 Subject: [PATCH 24/43] create a wrapper for the block in dsmr (#1829) --- x/dsmr/assembler.go | 2 +- x/dsmr/block.go | 55 +++++++++----- x/dsmr/node.go | 26 +++---- x/dsmr/node_test.go | 180 ++++++++++++++++++++++---------------------- 4 files changed, 141 insertions(+), 122 deletions(-) diff --git a/x/dsmr/assembler.go b/x/dsmr/assembler.go index 50175a383b..e5e729dc15 100644 --- a/x/dsmr/assembler.go +++ b/x/dsmr/assembler.go @@ -57,7 +57,7 @@ func (b *BlockHandler[T, S, B, R]) Accept(ctx context.Context, block *Block) err } // Assemble and execute the block - innerBlock, result, state, err := b.Assembler.AssembleBlock(ctx, b.lastAcceptedState, b.lastAcceptedBlock, block.Tmstmp, block.Hght+1, txs) + innerBlock, result, state, err := b.Assembler.AssembleBlock(ctx, b.lastAcceptedState, b.lastAcceptedBlock, block.Timestamp, block.Height+1, txs) if err != nil { return err } diff --git a/x/dsmr/block.go b/x/dsmr/block.go index 7db1a01caf..6f515f23e3 100644 --- a/x/dsmr/block.go +++ b/x/dsmr/block.go @@ -115,10 +115,41 @@ func ParseChunk[T Tx](chunkBytes []byte) (Chunk[T], error) { return c, c.init() } +// ExecutionBlock bridge the gap between the dsmr's block implementation and the validity window's execution block interface. +type ExecutionBlock struct { + innerBlock Block +} + +func (e ExecutionBlock) Timestamp() int64 { + return e.innerBlock.Timestamp +} + +func (e ExecutionBlock) Height() uint64 { + return e.innerBlock.Height +} + +func (e ExecutionBlock) Contains(id ids.ID) bool { + return e.innerBlock.certSet.Contains(id) +} + +func (e ExecutionBlock) Parent() ids.ID { + return e.innerBlock.ParentID +} + +func (e ExecutionBlock) Txs() []*ChunkCertificate { + return e.innerBlock.Containers() +} + +func NewExecutionBlock(innerBlock Block) ExecutionBlock { + return ExecutionBlock{ + innerBlock: innerBlock, + } +} + type Block struct { - ParentID ids.ID `serialize:"true"` - Hght uint64 `serialize:"true"` - Tmstmp int64 `serialize:"true"` + ParentID ids.ID `serialize:"true"` + Height uint64 `serialize:"true"` + Timestamp int64 `serialize:"true"` ChunkCerts []*ChunkCertificate `serialize:"true"` @@ -130,8 +161,8 @@ type Block struct { func NewBlock(parentID ids.ID, height uint64, timestamp int64, chunkCerts []*ChunkCertificate) Block { blk := Block{ ParentID: parentID, - Hght: height, - Tmstmp: timestamp, + Height: height, + Timestamp: timestamp, ChunkCerts: chunkCerts, } blk.init() @@ -146,22 +177,10 @@ func (b Block) Parent() ids.ID { return b.ParentID } -func (b Block) Timestamp() int64 { - return b.Tmstmp -} - -func (b Block) Height() uint64 { - return b.Hght -} - -func (b Block) Txs() []*ChunkCertificate { +func (b Block) Containers() []*ChunkCertificate { return b.ChunkCerts } -func (b Block) Contains(id ids.ID) bool { - return b.certSet.Contains(id) -} - func (b Block) init() { for _, c := range b.ChunkCerts { b.certSet.Add(c.ChunkID) diff --git a/x/dsmr/node.go b/x/dsmr/node.go index 22ab04b7e5..a992439015 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -232,14 +232,14 @@ func (n *Node[T]) BuildChunk( } func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) (Block, error) { - if timestamp <= parent.Tmstmp { + if timestamp <= parent.Timestamp { return Block{}, ErrTimestampNotMonotonicallyIncreasing } chunkCerts := n.storage.GatherChunkCerts() oldestAllowed := max(0, timestamp-int64(n.validityWindowDuration)) - dup, err := n.validityWindow.IsRepeat(ctx, parent, chunkCerts, oldestAllowed) + dup, err := n.validityWindow.IsRepeat(ctx, NewExecutionBlock(parent), chunkCerts, oldestAllowed) if err != nil { return Block{}, err } @@ -258,7 +258,7 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) blk := NewBlock( parent.GetID(), - parent.Hght+1, + parent.Height+1, timestamp, availableChunkCerts, ) @@ -283,18 +283,18 @@ func (n *Node[T]) Verify(ctx context.Context, parent Block, block Block) error { ) } - if block.Hght != parent.Hght+1 { + if block.Height != parent.Height+1 { return fmt.Errorf( "%w %d: expected %d", ErrInvalidBlockHeight, - block.Hght, - parent.Hght+1, + block.Height, + parent.Height+1, ) } - if block.Tmstmp <= parent.Tmstmp || - block.Tmstmp > parent.Tmstmp+maxTimeSkew.Nanoseconds() { - return fmt.Errorf("%w %d: parent - %d", ErrInvalidBlockTimestamp, block.Tmstmp, parent.Tmstmp) + if block.Timestamp <= parent.Timestamp || + block.Timestamp > parent.Timestamp+maxTimeSkew.Nanoseconds() { + return fmt.Errorf("%w %d: parent - %d", ErrInvalidBlockTimestamp, block.Timestamp, parent.Timestamp) } if len(block.ChunkCerts) == 0 { @@ -302,9 +302,9 @@ func (n *Node[T]) Verify(ctx context.Context, parent Block, block Block) error { } // Find repeats - oldestAllowed := max(0, block.Timestamp()-int64(n.validityWindowDuration)) + oldestAllowed := max(0, block.Timestamp-int64(n.validityWindowDuration)) - if err := n.validityWindow.VerifyExpiryReplayProtection(ctx, block, oldestAllowed); err != nil { + if err := n.validityWindow.VerifyExpiryReplayProtection(ctx, NewExecutionBlock(block), oldestAllowed); err != nil { return err } @@ -368,9 +368,9 @@ func (n *Node[T]) Accept(ctx context.Context, block Block) error { } } // update the validity window with the accepted block. - n.validityWindow.Accept(block) + n.validityWindow.Accept(NewExecutionBlock(block)) - if err := n.storage.SetMin(block.Tmstmp, chunkIDs); err != nil { + if err := n.storage.SetMin(block.Timestamp, chunkIDs); err != nil { return fmt.Errorf("failed to prune chunks: %w", err) } diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 5bdc131bcf..4d5b0a9ba5 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -161,7 +161,7 @@ func TestNode_GetChunk_AvailableChunk(t *testing.T) { ) r.NoError(err) - blk, err := node.BuildBlock(context.Background(), node.LastAccepted, node.LastAccepted.Tmstmp+1) + blk, err := node.BuildBlock(context.Background(), node.LastAccepted, node.LastAccepted.Timestamp+1) r.NoError(err) r.NoError(node.Verify(context.Background(), node.LastAccepted, blk)) r.NoError(node.Accept(context.Background(), blk)) @@ -431,7 +431,7 @@ func TestNode_BuiltChunksAvailableOverGetChunk(t *testing.T) { wantChunks = append(wantChunks, chunk) } - blk, err := node.BuildBlock(context.Background(), node.LastAccepted, node.LastAccepted.Tmstmp+1) + blk, err := node.BuildBlock(context.Background(), node.LastAccepted, node.LastAccepted.Timestamp+1) r.NoError(err) r.NoError(node.Verify(context.Background(), node.LastAccepted, blk)) r.NoError(node.Accept(context.Background(), blk)) @@ -569,10 +569,10 @@ func TestNode_GetChunkSignature_SignValidChunk(t *testing.T) { ), validators, Block{ - ParentID: ids.GenerateTestID(), - Hght: 0, - Tmstmp: 0, - blkID: ids.GenerateTestID(), + ParentID: ids.GenerateTestID(), + Height: 0, + Timestamp: 0, + blkID: ids.GenerateTestID(), }, 1, 1, @@ -683,7 +683,7 @@ func TestNode_GetChunkSignature_DuplicateChunk(t *testing.T) { codec.Address{123}, ) r.NoError(err) - blk, err := node.BuildBlock(context.Background(), node.LastAccepted, node.LastAccepted.Tmstmp+1) + blk, err := node.BuildBlock(context.Background(), node.LastAccepted, node.LastAccepted.Timestamp+1) r.NoError(err) r.NoError(node.Verify(context.Background(), node.LastAccepted, blk)) r.NoError(node.Accept(context.Background(), blk)) @@ -749,7 +749,7 @@ func TestGetChunkSignature_PersistAttestedBlocks(t *testing.T) { // chunk cert var blk Block for { - blk, err = node2.BuildBlock(context.Background(), node2.LastAccepted, node2.LastAccepted.Tmstmp+1) + blk, err = node2.BuildBlock(context.Background(), node2.LastAccepted, node2.LastAccepted.Timestamp+1) if err == nil { break } @@ -803,7 +803,7 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { { name: "no chunk certs", timestamp: func(parent Block) int64 { - return parent.Tmstmp + 1 + return parent.Timestamp + 1 }, // TODO should we be able to build empty blocks? wantErr: ErrNoAvailableChunkCerts, @@ -811,14 +811,14 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { { name: "timestamp equal to parent", timestamp: func(parent Block) int64 { - return parent.Tmstmp + return parent.Timestamp }, wantErr: ErrTimestampNotMonotonicallyIncreasing, }, { name: "timestamp older than parent", timestamp: func(parent Block) int64 { - return parent.Tmstmp - 1 + return parent.Timestamp - 1 }, wantErr: ErrTimestampNotMonotonicallyIncreasing, }, @@ -830,15 +830,15 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { txs: []tx{ { ID: ids.GenerateTestID(), - Expiry: parent.Tmstmp + 1, + Expiry: parent.Timestamp + 1, }, }, - expiry: parent.Tmstmp + 1, + expiry: parent.Timestamp + 1, }, } }, timestamp: func(parent Block) int64 { - return parent.Tmstmp + 100 + return parent.Timestamp + 100 }, wantErr: ErrNoAvailableChunkCerts, }, @@ -850,33 +850,33 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { txs: []tx{ { ID: ids.GenerateTestID(), - Expiry: parent.Tmstmp + 1, + Expiry: parent.Timestamp + 1, }, }, - expiry: parent.Tmstmp + 1, + expiry: parent.Timestamp + 1, }, { txs: []tx{ { ID: ids.GenerateTestID(), - Expiry: parent.Tmstmp + 2, + Expiry: parent.Timestamp + 2, }, }, - expiry: parent.Tmstmp + 2, + expiry: parent.Timestamp + 2, }, { txs: []tx{ { ID: ids.GenerateTestID(), - Expiry: parent.Tmstmp + 3, + Expiry: parent.Timestamp + 3, }, }, - expiry: parent.Tmstmp + 3, + expiry: parent.Timestamp + 3, }, } }, timestamp: func(parent Block) int64 { - return parent.Tmstmp + 100 + return parent.Timestamp + 100 }, wantErr: ErrNoAvailableChunkCerts, }, @@ -888,15 +888,15 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { txs: []tx{ { ID: ids.GenerateTestID(), - Expiry: parent.Tmstmp + 1, + Expiry: parent.Timestamp + 1, }, }, - expiry: parent.Tmstmp + 1, + expiry: parent.Timestamp + 1, }, } }, timestamp: func(parent Block) int64 { - return parent.Tmstmp + 1 + return parent.Timestamp + 1 }, }, { @@ -907,33 +907,33 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { txs: []tx{ { ID: ids.GenerateTestID(), - Expiry: parent.Tmstmp + 1_000, + Expiry: parent.Timestamp + 1_000, }, }, - expiry: parent.Tmstmp + 1_000, + expiry: parent.Timestamp + 1_000, }, { txs: []tx{ { ID: ids.GenerateTestID(), - Expiry: parent.Tmstmp + 2_000, + Expiry: parent.Timestamp + 2_000, }, }, - expiry: parent.Tmstmp + 2_000, + expiry: parent.Timestamp + 2_000, }, { txs: []tx{ { ID: ids.GenerateTestID(), - Expiry: parent.Tmstmp + 3_000, + Expiry: parent.Timestamp + 3_000, }, }, - expiry: parent.Tmstmp + 3_000, + expiry: parent.Timestamp + 3_000, }, } }, timestamp: func(parent Block) int64 { - return parent.Tmstmp + 100 + return parent.Timestamp + 100 }, }, { @@ -944,24 +944,24 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { txs: []tx{ { ID: ids.GenerateTestID(), - Expiry: parent.Tmstmp + 1_000, + Expiry: parent.Timestamp + 1_000, }, }, - expiry: parent.Tmstmp + 1_000, + expiry: parent.Timestamp + 1_000, }, { txs: []tx{ { ID: ids.GenerateTestID(), - Expiry: parent.Tmstmp + 1, + Expiry: parent.Timestamp + 1, }, }, - expiry: parent.Tmstmp + 1, + expiry: parent.Timestamp + 1, }, } }, timestamp: func(parent Block) int64 { - return parent.Tmstmp + 100 + return parent.Timestamp + 100 }, }, } @@ -1004,8 +1004,8 @@ func TestNode_NewBlock_IncludesChunkCerts(t *testing.T) { } r.Equal(node.LastAccepted.GetID(), blk.ParentID) - r.Equal(node.LastAccepted.Hght+1, blk.Hght) - r.Greater(blk.Tmstmp, node.LastAccepted.Tmstmp) + r.Equal(node.LastAccepted.Height+1, blk.Height) + r.Greater(blk.Timestamp, node.LastAccepted.Timestamp) r.NotEmpty(blk.GetID()) r.Len(blk.ChunkCerts, len(wantChunks)) @@ -1029,10 +1029,10 @@ func TestDuplicateChunksElimination(t *testing.T) { node := newTestNode(t) blk := Block{ - ParentID: ids.GenerateTestID(), - Hght: 1, - Tmstmp: 1, - blkID: ids.GenerateTestID(), + ParentID: ids.GenerateTestID(), + Height: 1, + Timestamp: 1, + blkID: ids.GenerateTestID(), } r.NoError(node.Accept(context.Background(), blk)) @@ -1050,10 +1050,10 @@ func TestDuplicateChunksElimination(t *testing.T) { r.NoError(err) blk = Block{ - ParentID: ids.GenerateTestID(), - Hght: 2, - Tmstmp: 2, - blkID: ids.GenerateTestID(), + ParentID: ids.GenerateTestID(), + Height: 2, + Timestamp: 2, + blkID: ids.GenerateTestID(), ChunkCerts: []*ChunkCertificate{ &chunkCert, }, @@ -1205,10 +1205,10 @@ func TestNode_Verify_Chunks(t *testing.T) { storage: chunkStorage, } genesisBlk := Block{ - ParentID: ids.Empty, - Hght: 0, - Tmstmp: 0, - blkID: ids.Empty, + ParentID: ids.Empty, + Height: 0, + Timestamp: 0, + blkID: ids.Empty, } nodeID := ids.GenerateTestNodeID() @@ -1269,15 +1269,15 @@ func TestNode_Verify_Chunks(t *testing.T) { chunks, chunkCerts := initChunks(node) - indexer.set(genesisBlk.GetID(), genesisBlk) + indexer.set(genesisBlk.GetID(), NewExecutionBlock(genesisBlk)) // initialize node history. parentBlk := genesisBlk for blockNum, chunkList := range testCase.parentBlocks { blk := Block{ - ParentID: parentBlk.GetID(), - Hght: uint64(blockNum + 1), - Tmstmp: int64(blockNum + 1), - blkID: ids.GenerateTestID(), + ParentID: parentBlk.GetID(), + Height: uint64(blockNum + 1), + Timestamp: int64(blockNum + 1), + blkID: ids.GenerateTestID(), } for _, chunkIndex := range chunkList { blk.ChunkCerts = append(blk.ChunkCerts, chunkCerts[chunkIndex]) @@ -1286,7 +1286,7 @@ func TestNode_Verify_Chunks(t *testing.T) { r.NoError(node.Verify(context.Background(), parentBlk, blk)) r.NoError(node.Accept(context.Background(), blk)) - indexer.set(blk.GetID(), blk) + indexer.set(blk.GetID(), NewExecutionBlock(blk)) parentBlk = blk } // feed the chunks into the storage and build a block. @@ -1298,10 +1298,10 @@ func TestNode_Verify_Chunks(t *testing.T) { // create the block so that we can test it against the execute directly. newBlk := Block{ - ParentID: parentBlk.GetID(), - Hght: uint64(testCase.timestamp), - Tmstmp: testCase.timestamp, - blkID: ids.GenerateTestID(), + ParentID: parentBlk.GetID(), + Height: uint64(testCase.timestamp), + Timestamp: testCase.timestamp, + blkID: ids.GenerateTestID(), } for _, chunkIndex := range testCase.chunks { newBlk.ChunkCerts = append(newBlk.ChunkCerts, chunkCerts[chunkIndex]) @@ -1326,7 +1326,7 @@ func TestAccept_RequestReferencedChunks(t *testing.T) { codec.Address{123}, ) r.NoError(err) - blk, err := node1.BuildBlock(context.Background(), node1.LastAccepted, node1.LastAccepted.Tmstmp+1) + blk, err := node1.BuildBlock(context.Background(), node1.LastAccepted, node1.LastAccepted.Timestamp+1) r.NoError(err) r.NoError(node1.Verify(context.Background(), node1.LastAccepted, blk)) r.NoError(node1.Accept(context.Background(), blk)) @@ -1396,7 +1396,7 @@ func Test_Verify(t *testing.T) { ) r.NoError(err) - blk, err := node.BuildBlock(context.Background(), node.LastAccepted, node.LastAccepted.Tmstmp+1) + blk, err := node.BuildBlock(context.Background(), node.LastAccepted, node.LastAccepted.Timestamp+1) r.NoError(err) r.NoError(node.Verify(context.Background(), node.LastAccepted, blk)) } @@ -1412,8 +1412,8 @@ func Test_Verify_BadBlock(t *testing.T) { blk: func(chunkCert ChunkCertificate, parent Block) Block { return Block{ ParentID: ids.GenerateTestID(), - Hght: parent.Hght + 1, - Tmstmp: parent.Tmstmp + 1, + Height: parent.Height + 1, + Timestamp: parent.Timestamp + 1, ChunkCerts: []*ChunkCertificate{&chunkCert}, } }, @@ -1424,8 +1424,8 @@ func Test_Verify_BadBlock(t *testing.T) { blk: func(chunkCert ChunkCertificate, parent Block) Block { return Block{ ParentID: parent.GetID(), - Hght: parent.Hght - 1, - Tmstmp: parent.Tmstmp + 1, + Height: parent.Height - 1, + Timestamp: parent.Timestamp + 1, ChunkCerts: []*ChunkCertificate{&chunkCert}, } }, @@ -1436,8 +1436,8 @@ func Test_Verify_BadBlock(t *testing.T) { blk: func(chunkCert ChunkCertificate, parent Block) Block { return Block{ ParentID: parent.GetID(), - Hght: parent.Hght, - Tmstmp: parent.Tmstmp + 1, + Height: parent.Height, + Timestamp: parent.Timestamp + 1, ChunkCerts: []*ChunkCertificate{&chunkCert}, } }, @@ -1448,8 +1448,8 @@ func Test_Verify_BadBlock(t *testing.T) { blk: func(chunkCert ChunkCertificate, parent Block) Block { return Block{ ParentID: parent.GetID(), - Hght: parent.Hght + 2, - Tmstmp: parent.Tmstmp + 1, + Height: parent.Height + 2, + Timestamp: parent.Timestamp + 1, ChunkCerts: []*ChunkCertificate{&chunkCert}, } }, @@ -1460,8 +1460,8 @@ func Test_Verify_BadBlock(t *testing.T) { blk: func(chunkCert ChunkCertificate, parent Block) Block { return Block{ ParentID: parent.GetID(), - Hght: parent.Hght + 1, - Tmstmp: parent.Tmstmp - 1, + Height: parent.Height + 1, + Timestamp: parent.Timestamp - 1, ChunkCerts: []*ChunkCertificate{&chunkCert}, } }, @@ -1472,8 +1472,8 @@ func Test_Verify_BadBlock(t *testing.T) { blk: func(chunkCert ChunkCertificate, parent Block) Block { return Block{ ParentID: parent.GetID(), - Hght: parent.Hght + 1, - Tmstmp: parent.Tmstmp, + Height: parent.Height + 1, + Timestamp: parent.Timestamp, ChunkCerts: []*ChunkCertificate{&chunkCert}, } }, @@ -1483,9 +1483,9 @@ func Test_Verify_BadBlock(t *testing.T) { name: "invalid timestamp - too far into future", blk: func(_ ChunkCertificate, parent Block) Block { return Block{ - ParentID: parent.GetID(), - Hght: parent.Hght + 1, - Tmstmp: parent.Tmstmp + time.Minute.Nanoseconds(), + ParentID: parent.GetID(), + Height: parent.Height + 1, + Timestamp: parent.Timestamp + time.Minute.Nanoseconds(), } }, wantErr: ErrInvalidBlockTimestamp, @@ -1494,9 +1494,9 @@ func Test_Verify_BadBlock(t *testing.T) { name: "nil chunk certs", blk: func(_ ChunkCertificate, parent Block) Block { return Block{ - ParentID: parent.GetID(), - Hght: parent.Hght + 1, - Tmstmp: parent.Tmstmp + 1, + ParentID: parent.GetID(), + Height: parent.Height + 1, + Timestamp: parent.Timestamp + 1, } }, wantErr: ErrEmptyBlock, @@ -1506,8 +1506,8 @@ func Test_Verify_BadBlock(t *testing.T) { blk: func(_ ChunkCertificate, parent Block) Block { return Block{ ParentID: parent.GetID(), - Hght: parent.Hght + 1, - Tmstmp: parent.Tmstmp + 1, + Height: parent.Height + 1, + Timestamp: parent.Timestamp + 1, ChunkCerts: []*ChunkCertificate{}, } }, @@ -1517,9 +1517,9 @@ func Test_Verify_BadBlock(t *testing.T) { name: "invalid signature", blk: func(_ ChunkCertificate, parent Block) Block { return Block{ - ParentID: parent.GetID(), - Hght: parent.Hght + 1, - Tmstmp: parent.Tmstmp + 1, + ParentID: parent.GetID(), + Height: parent.Height + 1, + Timestamp: parent.Timestamp + 1, ChunkCerts: []*ChunkCertificate{ { ChunkReference: ChunkReference{ @@ -1683,10 +1683,10 @@ func newNodes(t *testing.T, n int) []*Node[tx] { ), validators, Block{ - ParentID: ids.Empty, - Hght: 0, - Tmstmp: 0, - blkID: ids.Empty, + ParentID: ids.Empty, + Height: 0, + Timestamp: 0, + blkID: ids.Empty, }, 1, 1, @@ -1714,7 +1714,7 @@ func newNodes(t *testing.T, n int) []*Node[tx] { ) require.NoError(t, err) - blk, err := node.BuildBlock(context.Background(), node.LastAccepted, node.LastAccepted.Tmstmp+1) + blk, err := node.BuildBlock(context.Background(), node.LastAccepted, node.LastAccepted.Timestamp+1) require.NoError(t, err) require.NoError(t, node.Verify(context.Background(), node.LastAccepted, blk)) From fc87f317ef70308da2acb6e7c0b0e50205cba6e1 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:04:02 -0500 Subject: [PATCH 25/43] update --- x/dsmr/block.go | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/x/dsmr/block.go b/x/dsmr/block.go index 6f515f23e3..cb65a09bc1 100644 --- a/x/dsmr/block.go +++ b/x/dsmr/block.go @@ -137,7 +137,7 @@ func (e ExecutionBlock) Parent() ids.ID { } func (e ExecutionBlock) Txs() []*ChunkCertificate { - return e.innerBlock.Containers() + return e.innerBlock.ChunkCerts } func NewExecutionBlock(innerBlock Block) ExecutionBlock { @@ -173,14 +173,6 @@ func (b Block) GetID() ids.ID { return b.blkID } -func (b Block) Parent() ids.ID { - return b.ParentID -} - -func (b Block) Containers() []*ChunkCertificate { - return b.ChunkCerts -} - func (b Block) init() { for _, c := range b.ChunkCerts { b.certSet.Add(c.ChunkID) From cf8289161c4a699ff9702023c702449cfc636e93 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:08:06 -0500 Subject: [PATCH 26/43] update --- x/dsmr/block.go | 9 --------- x/dsmr/storage.go | 20 +++++++++++++++++--- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/x/dsmr/block.go b/x/dsmr/block.go index cb65a09bc1..c08fc5b22e 100644 --- a/x/dsmr/block.go +++ b/x/dsmr/block.go @@ -17,7 +17,6 @@ import ( const InitialChunkSize = 250 * 1024 -// Tx implements emap.Item type Tx interface { GetID() ids.ID GetExpiry() int64 @@ -53,14 +52,6 @@ func (c *Chunk[T]) init() error { return nil } -func (c Chunk[T]) GetID() ids.ID { - return c.id -} - -func (c Chunk[T]) GetExpiry() int64 { - return c.Expiry -} - func signChunk[T Tx]( chunk UnsignedChunk[T], networkID uint32, diff --git a/x/dsmr/storage.go b/x/dsmr/storage.go index a7efedbaf6..c03069a3f4 100644 --- a/x/dsmr/storage.go +++ b/x/dsmr/storage.go @@ -60,7 +60,7 @@ type ChunkStorage[T Tx] struct { lock sync.RWMutex // Chunk storage - chunkEMap *emap.EMap[Chunk[T]] + chunkEMap *emap.EMap[emapChunk[T]] minimumExpiry int64 // pendingByte | slot | chunkID -> chunkBytes // acceptedByte | slot | chunkID -> chunkBytes @@ -91,7 +91,7 @@ func NewChunkStorage[T Tx]( storage := &ChunkStorage[T]{ minimumExpiry: minSlot, - chunkEMap: emap.NewEMap[Chunk[T]](), + chunkEMap: emap.NewEMap[emapChunk[T]](), chunkMap: make(map[ids.ID]*StoredChunkSignature[T]), chunkDB: db, verifier: verifier, @@ -173,7 +173,7 @@ func (s *ChunkStorage[T]) putVerifiedChunk(c Chunk[T], cert *ChunkCertificate) e if err := s.chunkDB.Put(pendingChunkKey(c.Expiry, c.id), c.bytes); err != nil { return err } - s.chunkEMap.Add([]Chunk[T]{c}) + s.chunkEMap.Add([]emapChunk[T]{{chunk: c}}) chunkCert := &StoredChunkSignature[T]{Chunk: c, Cert: cert} s.chunkMap[c.id] = chunkCert @@ -304,3 +304,17 @@ func parsePendingChunkKey(key []byte) (slot int64, chunkID ids.ID, err error) { func acceptedChunkKey(slot int64, chunkID ids.ID) []byte { return createChunkKey(acceptedByte, slot, chunkID) } + +var _ emap.Item = (*emapChunk[Tx])(nil) + +type emapChunk[T Tx] struct { + chunk Chunk[T] +} + +func (e emapChunk[_]) GetID() ids.ID { + return e.chunk.id +} + +func (e emapChunk[_]) GetExpiry() int64 { + return e.chunk.Expiry +} From 864acc1b29690d704aea336a237573d56a0622c7 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:16:41 -0500 Subject: [PATCH 27/43] updatge --- x/dsmr/block.go | 8 ++++++-- x/dsmr/certificate.go | 11 +++++++++-- x/dsmr/node.go | 10 +++++++--- x/dsmr/node_test.go | 8 ++++---- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/x/dsmr/block.go b/x/dsmr/block.go index c08fc5b22e..91d6184194 100644 --- a/x/dsmr/block.go +++ b/x/dsmr/block.go @@ -127,8 +127,12 @@ func (e ExecutionBlock) Parent() ids.ID { return e.innerBlock.ParentID } -func (e ExecutionBlock) Txs() []*ChunkCertificate { - return e.innerBlock.ChunkCerts +func (e ExecutionBlock) Txs() []*emapChunkCertification { + emapChunkCert := make([]*emapChunkCertification, len(e.innerBlock.ChunkCerts)) + for i := range emapChunkCert { + emapChunkCert[i] = &emapChunkCertification{*e.innerBlock.ChunkCerts[i]} + } + return emapChunkCert } func NewExecutionBlock(innerBlock Block) ExecutionBlock { diff --git a/x/dsmr/certificate.go b/x/dsmr/certificate.go index 262cc8d27e..b5d7422050 100644 --- a/x/dsmr/certificate.go +++ b/x/dsmr/certificate.go @@ -46,13 +46,20 @@ type ChunkReference struct { Expiry int64 `serialize:"true"` } +type emapChunkCertification struct { + ChunkCertificate +} + +func (e emapChunkCertification) GetID() ids.ID { return e.ChunkID } +func (e emapChunkCertification) GetExpiry() int64 { return e.Expiry } + type ChunkCertificate struct { ChunkReference `serialize:"true"` Signature *warp.BitSetSignature `serialize:"true"` } -func (c ChunkCertificate) GetID() ids.ID { return c.ChunkID } -func (c ChunkCertificate) GetExpiry() int64 { return c.Expiry } +func (c ChunkCertificate) GetChunkID() ids.ID { return c.ChunkID } +func (c ChunkCertificate) GetSlot() int64 { return c.Expiry } func (c *ChunkCertificate) Bytes() []byte { bytes, err := Codec.Marshal(CodecVersion, c) diff --git a/x/dsmr/node.go b/x/dsmr/node.go index a992439015..1a92cbeeda 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -74,7 +74,7 @@ func New[T Tx]( lastAccepted Block, quorumNum uint64, quorumDen uint64, - chainIndex validitywindow.ChainIndex[*ChunkCertificate], + chainIndex validitywindow.ChainIndex[*emapChunkCertification], validityWindowDuration time.Duration, ) (*Node[T], error) { return &Node[T]{ @@ -122,7 +122,7 @@ type Node[T Tx] struct { log logging.Logger tracer trace.Tracer validityWindowDuration time.Duration - validityWindow *validitywindow.TimeValidityWindow[*ChunkCertificate] + validityWindow *validitywindow.TimeValidityWindow[*emapChunkCertification] } // BuildChunk builds transactions into a Chunk @@ -239,7 +239,11 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) chunkCerts := n.storage.GatherChunkCerts() oldestAllowed := max(0, timestamp-int64(n.validityWindowDuration)) - dup, err := n.validityWindow.IsRepeat(ctx, NewExecutionBlock(parent), chunkCerts, oldestAllowed) + emapChunkCert := make([]*emapChunkCertification, len(chunkCerts)) + for i := range emapChunkCert { + emapChunkCert[i] = &emapChunkCertification{*chunkCerts[i]} + } + dup, err := n.validityWindow.IsRepeat(ctx, NewExecutionBlock(parent), emapChunkCert, oldestAllowed) if err != nil { return Block{}, err } diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 4d5b0a9ba5..f39919b794 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -43,23 +43,23 @@ var ( const testingDefaultValidityWindowDuration = time.Duration(5) type testingChainIndex struct { - blocks map[ids.ID]validitywindow.ExecutionBlock[*ChunkCertificate] + blocks map[ids.ID]validitywindow.ExecutionBlock[*emapChunkCertification] } -func (t *testingChainIndex) GetExecutionBlock(_ context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[*ChunkCertificate], bool, error) { +func (t *testingChainIndex) GetExecutionBlock(_ context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[*emapChunkCertification], bool, error) { if blk, ok := t.blocks[blkID]; ok { return blk, true, nil } return nil, false, nil } -func (t *testingChainIndex) set(blkID ids.ID, blk validitywindow.ExecutionBlock[*ChunkCertificate]) { +func (t *testingChainIndex) set(blkID ids.ID, blk validitywindow.ExecutionBlock[*emapChunkCertification]) { t.blocks[blkID] = blk } func newTestingChainIndexer() *testingChainIndex { return &testingChainIndex{ - blocks: make(map[ids.ID]validitywindow.ExecutionBlock[*ChunkCertificate]), + blocks: make(map[ids.ID]validitywindow.ExecutionBlock[*emapChunkCertification]), } } From d96160eb8a5dcfe18ef566c75f481886998109e8 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:17:45 -0500 Subject: [PATCH 28/43] fix typo --- x/dsmr/block.go | 6 +++--- x/dsmr/certificate.go | 6 +++--- x/dsmr/node.go | 8 ++++---- x/dsmr/node_test.go | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/x/dsmr/block.go b/x/dsmr/block.go index 91d6184194..99f1bbcd21 100644 --- a/x/dsmr/block.go +++ b/x/dsmr/block.go @@ -127,10 +127,10 @@ func (e ExecutionBlock) Parent() ids.ID { return e.innerBlock.ParentID } -func (e ExecutionBlock) Txs() []*emapChunkCertification { - emapChunkCert := make([]*emapChunkCertification, len(e.innerBlock.ChunkCerts)) +func (e ExecutionBlock) Txs() []*emapChunkCertificate { + emapChunkCert := make([]*emapChunkCertificate, len(e.innerBlock.ChunkCerts)) for i := range emapChunkCert { - emapChunkCert[i] = &emapChunkCertification{*e.innerBlock.ChunkCerts[i]} + emapChunkCert[i] = &emapChunkCertificate{*e.innerBlock.ChunkCerts[i]} } return emapChunkCert } diff --git a/x/dsmr/certificate.go b/x/dsmr/certificate.go index b5d7422050..6285f749b3 100644 --- a/x/dsmr/certificate.go +++ b/x/dsmr/certificate.go @@ -46,12 +46,12 @@ type ChunkReference struct { Expiry int64 `serialize:"true"` } -type emapChunkCertification struct { +type emapChunkCertificate struct { ChunkCertificate } -func (e emapChunkCertification) GetID() ids.ID { return e.ChunkID } -func (e emapChunkCertification) GetExpiry() int64 { return e.Expiry } +func (e emapChunkCertificate) GetID() ids.ID { return e.ChunkID } +func (e emapChunkCertificate) GetExpiry() int64 { return e.Expiry } type ChunkCertificate struct { ChunkReference `serialize:"true"` diff --git a/x/dsmr/node.go b/x/dsmr/node.go index 1a92cbeeda..ee6f042bea 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -74,7 +74,7 @@ func New[T Tx]( lastAccepted Block, quorumNum uint64, quorumDen uint64, - chainIndex validitywindow.ChainIndex[*emapChunkCertification], + chainIndex validitywindow.ChainIndex[*emapChunkCertificate], validityWindowDuration time.Duration, ) (*Node[T], error) { return &Node[T]{ @@ -122,7 +122,7 @@ type Node[T Tx] struct { log logging.Logger tracer trace.Tracer validityWindowDuration time.Duration - validityWindow *validitywindow.TimeValidityWindow[*emapChunkCertification] + validityWindow *validitywindow.TimeValidityWindow[*emapChunkCertificate] } // BuildChunk builds transactions into a Chunk @@ -239,9 +239,9 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) chunkCerts := n.storage.GatherChunkCerts() oldestAllowed := max(0, timestamp-int64(n.validityWindowDuration)) - emapChunkCert := make([]*emapChunkCertification, len(chunkCerts)) + emapChunkCert := make([]*emapChunkCertificate, len(chunkCerts)) for i := range emapChunkCert { - emapChunkCert[i] = &emapChunkCertification{*chunkCerts[i]} + emapChunkCert[i] = &emapChunkCertificate{*chunkCerts[i]} } dup, err := n.validityWindow.IsRepeat(ctx, NewExecutionBlock(parent), emapChunkCert, oldestAllowed) if err != nil { diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index f39919b794..feba65ca27 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -43,23 +43,23 @@ var ( const testingDefaultValidityWindowDuration = time.Duration(5) type testingChainIndex struct { - blocks map[ids.ID]validitywindow.ExecutionBlock[*emapChunkCertification] + blocks map[ids.ID]validitywindow.ExecutionBlock[*emapChunkCertificate] } -func (t *testingChainIndex) GetExecutionBlock(_ context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[*emapChunkCertification], bool, error) { +func (t *testingChainIndex) GetExecutionBlock(_ context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[*emapChunkCertificate], bool, error) { if blk, ok := t.blocks[blkID]; ok { return blk, true, nil } return nil, false, nil } -func (t *testingChainIndex) set(blkID ids.ID, blk validitywindow.ExecutionBlock[*emapChunkCertification]) { +func (t *testingChainIndex) set(blkID ids.ID, blk validitywindow.ExecutionBlock[*emapChunkCertificate]) { t.blocks[blkID] = blk } func newTestingChainIndexer() *testingChainIndex { return &testingChainIndex{ - blocks: make(map[ids.ID]validitywindow.ExecutionBlock[*emapChunkCertification]), + blocks: make(map[ids.ID]validitywindow.ExecutionBlock[*emapChunkCertificate]), } } From 9ea09b4e310e20506ae69d9cf90f64a21f6dc6b6 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:19:12 -0500 Subject: [PATCH 29/43] undo unwanted changes --- x/dsmr/certificate.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/x/dsmr/certificate.go b/x/dsmr/certificate.go index 6285f749b3..f073756020 100644 --- a/x/dsmr/certificate.go +++ b/x/dsmr/certificate.go @@ -50,7 +50,8 @@ type emapChunkCertificate struct { ChunkCertificate } -func (e emapChunkCertificate) GetID() ids.ID { return e.ChunkID } +func (e emapChunkCertificate) GetID() ids.ID { return e.ChunkID } + func (e emapChunkCertificate) GetExpiry() int64 { return e.Expiry } type ChunkCertificate struct { @@ -58,8 +59,9 @@ type ChunkCertificate struct { Signature *warp.BitSetSignature `serialize:"true"` } -func (c ChunkCertificate) GetChunkID() ids.ID { return c.ChunkID } -func (c ChunkCertificate) GetSlot() int64 { return c.Expiry } +func (c *ChunkCertificate) GetChunkID() ids.ID { return c.ChunkID } + +func (c *ChunkCertificate) GetSlot() int64 { return c.Expiry } func (c *ChunkCertificate) Bytes() []byte { bytes, err := Codec.Marshal(CodecVersion, c) From 30bcdce864e3050e25508912fa2718d2a5a9ed98 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:08:52 -0500 Subject: [PATCH 30/43] update --- internal/validitywindow/validitywindow.go | 13 ++++++-- x/dsmr/node_test.go | 39 ++++++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/internal/validitywindow/validitywindow.go b/internal/validitywindow/validitywindow.go index 7ed51a1e82..63b605336a 100644 --- a/internal/validitywindow/validitywindow.go +++ b/internal/validitywindow/validitywindow.go @@ -18,7 +18,10 @@ import ( "github.com/ava-labs/hypersdk/internal/emap" ) -var ErrDuplicateContainer = errors.New("duplicate container") +var ( + ErrDuplicateContainer = errors.New("duplicate container") + ErrExecutionBlockRetrievalFailed = errors.New("unable to retrieve block") +) type TimeValidityWindow[Container emap.Item] struct { log logging.Logger @@ -59,9 +62,15 @@ func (v *TimeValidityWindow[Container]) VerifyExpiryReplayProtection( return nil } parent, hasBlock, err := v.chainIndex.GetExecutionBlock(ctx, blk.Parent()) - if err != nil || !hasBlock { + if err != nil { return err } + if !hasBlock { + // if we can't get the block, we won't be able to determine if any of the transaction are duplicate. + // this is not an expected result, and is equivilient to the case where we cannot perform the validation + // due to disk issue. + return ErrExecutionBlockRetrievalFailed + } dup, err := v.isRepeat(ctx, parent, oldestAllowed, blk.Txs(), true) if err != nil { diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index feba65ca27..9b09011aca 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -194,6 +194,38 @@ func TestNode_GetChunk_AvailableChunk(t *testing.T) { <-done } +func TestIndexerMissingBlock(t *testing.T) { + r := require.New(t) + + node := newTestNode(t) + _, _, err := node.BuildChunk( + context.Background(), + []tx{{ID: ids.GenerateTestID(), Expiry: 123}}, + 123, + codec.Address{123}, + ) + r.NoError(err) + + blk, err := node.BuildBlock(context.Background(), node.LastAccepted, 3) + r.NoError(err) + + r.NoError(node.Verify(context.Background(), node.LastAccepted, blk)) + r.NoError(node.Accept(context.Background(), blk)) + + _, _, err = node.BuildChunk( + context.Background(), + []tx{{ID: ids.GenerateTestID(), Expiry: 123}}, + 123, + codec.Address{123}, + ) + r.NoError(err) + + blkNext, err := node.BuildBlock(context.Background(), node.LastAccepted, 4) + r.NoError(err) + + r.ErrorIs(node.Verify(context.Background(), node.LastAccepted, blkNext), validitywindow.ErrExecutionBlockRetrievalFailed) +} + // Tests that pending chunks are not available over p2p func TestNode_GetChunk_PendingChunk(t *testing.T) { r := require.New(t) @@ -1633,6 +1665,8 @@ func newNodes(t *testing.T, n int) []*Node[tx] { }) } + indexer := newTestingChainIndexer() + result := make([]*Node[tx], 0, n) for i, n := range nodes { getChunkPeers := make(map[ids.NodeID]p2p.Handler) @@ -1690,7 +1724,7 @@ func newNodes(t *testing.T, n int) []*Node[tx] { }, 1, 1, - newTestingChainIndexer(), + indexer, testingDefaultValidityWindowDuration, ) require.NoError(t, err) @@ -1713,16 +1747,19 @@ func newNodes(t *testing.T, n int) []*Node[tx] { codec.Address{}, ) require.NoError(t, err) + indexer.set(node.LastAccepted.GetID(), ExecutionBlock{node.LastAccepted}) blk, err := node.BuildBlock(context.Background(), node.LastAccepted, node.LastAccepted.Timestamp+1) require.NoError(t, err) require.NoError(t, node.Verify(context.Background(), node.LastAccepted, blk)) require.NoError(t, node.Accept(context.Background(), blk)) + indexer.set(blk.GetID(), ExecutionBlock{blk}) for _, n := range result[1:] { require.NoError(t, n.Verify(context.Background(), n.LastAccepted, blk)) require.NoError(t, n.Accept(context.Background(), blk)) + indexer.set(blk.GetID(), ExecutionBlock{blk}) } return result From 8d52c05a4a9a4916e2597fd1a7e6f5baef34e438 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:19:56 -0500 Subject: [PATCH 31/43] update --- x/dsmr/node_test.go | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 9b09011aca..9ffe654910 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -1126,28 +1126,7 @@ func TestNode_Verify_Chunks(t *testing.T) { signer := warp.NewSigner(sk1, networkID, chainID) r.NoError(err) - initChunks := func(node *Node[tx]) ([]Chunk[tx], []*ChunkCertificate) { - var chunks []Chunk[tx] - var chunksCerts []*ChunkCertificate - for expiry := int64(0); expiry < 5; expiry++ { - chunk, cert, err := node.BuildChunk( - context.Background(), - []tx{ - { - ID: ids.GenerateTestID(), - Expiry: expiry, - }, - }, - expiry, - codec.Address{}, - ) - r.NoError(err) - chunks = append(chunks, chunk) - chunksCerts = append(chunksCerts, &cert) - } - return chunks, chunksCerts - } - testCases := []struct { + tests := []struct { name string parentBlocks [][]int // for each parent, a list of the chunks included. chunks []int @@ -1211,7 +1190,7 @@ func TestNode_Verify_Chunks(t *testing.T) { buildWantErr: ErrNoAvailableChunkCerts, }, } - for _, testCase := range testCases { + for _, testCase := range tests { t.Run(testCase.name, func(t *testing.T) { indexer := newTestingChainIndexer() valWind := testCase.validalidityWindowDuration @@ -1299,7 +1278,24 @@ func TestNode_Verify_Chunks(t *testing.T) { ) r.NoError(err) - chunks, chunkCerts := initChunks(node) + var chunks []Chunk[tx] + var chunkCerts []*ChunkCertificate + for expiry := int64(0); expiry < 5; expiry++ { + chunk, cert, err := node.BuildChunk( + context.Background(), + []tx{ + { + ID: ids.GenerateTestID(), + Expiry: expiry, + }, + }, + expiry, + codec.Address{}, + ) + r.NoError(err) + chunks = append(chunks, chunk) + chunkCerts = append(chunkCerts, &cert) + } indexer.set(genesisBlk.GetID(), NewExecutionBlock(genesisBlk)) // initialize node history. From 32207b90c0f795bab662b88c931d4eed69aea51d Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:39:25 -0500 Subject: [PATCH 32/43] update unit test --- x/dsmr/node_test.go | 137 +++++++++----------------------------------- 1 file changed, 28 insertions(+), 109 deletions(-) diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 9ffe654910..5afc93c701 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -150,7 +150,7 @@ func TestNode_BuildChunk(t *testing.T) { func TestNode_GetChunk_AvailableChunk(t *testing.T) { r := require.New(t) - nodes := newNodes(t, 2) + nodes, _ := newNodes(t, 2) node := nodes[0] chunk, _, err := node.BuildChunk( @@ -765,7 +765,7 @@ func TestNode_GetChunkSignature_DuplicateChunk(t *testing.T) { func TestGetChunkSignature_PersistAttestedBlocks(t *testing.T) { r := require.New(t) - nodes := newNodes(t, 2) + nodes, _ := newNodes(t, 2) node1 := nodes[0] node2 := nodes[1] @@ -1118,13 +1118,6 @@ func TestDuplicateChunksElimination(t *testing.T) { func TestNode_Verify_Chunks(t *testing.T) { r := require.New(t) - networkID := uint32(123) - chainID := ids.Empty - sk1, err := bls.NewSecretKey() - r.NoError(err) - pk := bls.PublicFromSecretKey(sk1) - signer := warp.NewSigner(sk1, networkID, chainID) - r.NoError(err) tests := []struct { name string @@ -1137,7 +1130,7 @@ func TestNode_Verify_Chunks(t *testing.T) { }{ { name: "three blocks, unique chunks", - parentBlocks: [][]int{{1}, {2}, {3}}, + parentBlocks: [][]int{{1}, {2}}, chunks: []int{4}, timestamp: 4, verifyWantErr: nil, @@ -1146,15 +1139,15 @@ func TestNode_Verify_Chunks(t *testing.T) { name: "two blocks one duplicate chunk", parentBlocks: [][]int{{0, 2}}, chunks: []int{2, 4}, - timestamp: 2, + timestamp: 3, verifyWantErr: validitywindow.ErrDuplicateContainer, buildWantErr: nil, // build would filter out duplicate chunks, hence no error. }, { name: "one block duplicate chunks", parentBlocks: [][]int{}, - chunks: []int{1, 1}, - timestamp: 1, + chunks: []int{2, 2}, + timestamp: 2, verifyWantErr: validitywindow.ErrDuplicateContainer, buildWantErr: nil, // build would filter out duplicate chunks, hence no error. }, @@ -1162,14 +1155,14 @@ func TestNode_Verify_Chunks(t *testing.T) { name: "three blocks non consecutive duplicate chunks", parentBlocks: [][]int{{3}, {2}}, chunks: []int{3}, - timestamp: 3, + timestamp: 4, verifyWantErr: validitywindow.ErrDuplicateContainer, }, { name: "three blocks non consecutive duplicate chunks outside validity window", parentBlocks: [][]int{{1}, {2}}, chunks: []int{1}, - timestamp: 3, + timestamp: 4, verifyWantErr: nil, validalidityWindowDuration: 1, }, @@ -1185,98 +1178,24 @@ func TestNode_Verify_Chunks(t *testing.T) { name: "empty block", parentBlocks: [][]int{{1}, {2}, {3}, {4}}, chunks: []int{}, - timestamp: 5, + timestamp: 6, verifyWantErr: ErrEmptyBlock, buildWantErr: ErrNoAvailableChunkCerts, }, } for _, testCase := range tests { t.Run(testCase.name, func(t *testing.T) { - indexer := newTestingChainIndexer() - valWind := testCase.validalidityWindowDuration - if valWind == 0 { - valWind = testingDefaultValidityWindowDuration - } - - getChunkPeers := make(map[ids.NodeID]p2p.Handler) - - chunkCertGossipPeers := make(map[ids.NodeID]p2p.Handler) - - chunkStorage, err := NewChunkStorage[tx](NoVerifier[tx]{}, memdb.New()) - require.NoError(t, err) - - getChunkHandler := &GetChunkHandler[tx]{ - storage: chunkStorage, - } - chunkSignatureRequestHandler := acp118.NewHandler(ChunkSignatureRequestVerifier[tx]{ - verifier: NoVerifier[tx]{}, - storage: chunkStorage, - }, signer) - chunkCertificateGossipHandler := ChunkCertificateGossipHandler[tx]{ - storage: chunkStorage, - } - genesisBlk := Block{ - ParentID: ids.Empty, - Height: 0, - Timestamp: 0, - blkID: ids.Empty, - } - - nodeID := ids.GenerateTestNodeID() - - validators := []Validator{ - { - NodeID: nodeID, - Weight: 1, - PublicKey: pk, - }, + validationWindow := testCase.validalidityWindowDuration + if validationWindow == 0 { + validationWindow = testingDefaultValidityWindowDuration } - chunkSignaturePeers := map[ids.NodeID]p2p.Handler{ - nodeID: chunkSignatureRequestHandler, - } - - node, err := New[tx]( - logging.NoLog{}, - trace.Noop, - nodeID, - networkID, - chainID, - pk, - signer, - chunkStorage, - getChunkHandler, - chunkSignatureRequestHandler, - chunkCertificateGossipHandler, - p2ptest.NewClientWithPeers( - t, - context.Background(), - ids.EmptyNodeID, - getChunkHandler, - getChunkPeers, - ), - p2ptest.NewClientWithPeers( - t, - context.Background(), - ids.EmptyNodeID, - chunkSignatureRequestHandler, - chunkSignaturePeers, - ), - p2ptest.NewClientWithPeers( - t, - context.Background(), - ids.EmptyNodeID, - chunkCertificateGossipHandler, - chunkCertGossipPeers, - ), - validators, - genesisBlk, - 1, - 1, - indexer, - valWind, - ) - r.NoError(err) + var nodes []*Node[tx] + var node *Node[tx] + var indexer *testingChainIndex + nodes, indexer = newNodes(t, 1) + node = nodes[0] + node.validityWindowDuration = validationWindow var chunks []Chunk[tx] var chunkCerts []*ChunkCertificate @@ -1297,14 +1216,13 @@ func TestNode_Verify_Chunks(t *testing.T) { chunkCerts = append(chunkCerts, &cert) } - indexer.set(genesisBlk.GetID(), NewExecutionBlock(genesisBlk)) // initialize node history. - parentBlk := genesisBlk - for blockNum, chunkList := range testCase.parentBlocks { + parentBlk := node.LastAccepted + for _, chunkList := range testCase.parentBlocks { blk := Block{ ParentID: parentBlk.GetID(), - Height: uint64(blockNum + 1), - Timestamp: int64(blockNum + 1), + Height: uint64(int(node.LastAccepted.Height) + 1), + Timestamp: int64(int(node.LastAccepted.Timestamp) + 1), blkID: ids.GenerateTestID(), } for _, chunkIndex := range chunkList { @@ -1321,7 +1239,7 @@ func TestNode_Verify_Chunks(t *testing.T) { for _, chunkIdx := range testCase.chunks { r.NoError(node.storage.AddLocalChunkWithCert(chunks[chunkIdx], chunkCerts[chunkIdx])) } - _, err = node.BuildBlock(context.Background(), parentBlk, testCase.timestamp) + _, err := node.BuildBlock(context.Background(), parentBlk, testCase.timestamp) r.ErrorIs(err, testCase.buildWantErr) // create the block so that we can test it against the execute directly. @@ -1343,7 +1261,7 @@ func TestNode_Verify_Chunks(t *testing.T) { func TestAccept_RequestReferencedChunks(t *testing.T) { r := require.New(t) - nodes := newNodes(t, 2) + nodes, _ := newNodes(t, 2) node1 := nodes[0] node2 := nodes[1] @@ -1620,10 +1538,11 @@ type testNode struct { } func newTestNode(t *testing.T) *Node[tx] { - return newNodes(t, 1)[0] + nodes, _ := newNodes(t, 1) + return nodes[0] } -func newNodes(t *testing.T, n int) []*Node[tx] { +func newNodes(t *testing.T, n int) ([]*Node[tx], *testingChainIndex) { nodes := make([]testNode, 0, n) validators := make([]Validator, 0, n) for i := 0; i < n; i++ { @@ -1758,5 +1677,5 @@ func newNodes(t *testing.T, n int) []*Node[tx] { indexer.set(blk.GetID(), ExecutionBlock{blk}) } - return result + return result, indexer } From 5d62af7178b19ef91f3808af1ab37f0931bd6913 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Thu, 12 Dec 2024 17:00:21 -0500 Subject: [PATCH 33/43] update test --- x/dsmr/node_test.go | 82 ++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 5afc93c701..5836f76193 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -1157,19 +1157,12 @@ func TestNode_Verify_Chunks(t *testing.T) { chunks: []int{3}, timestamp: 4, verifyWantErr: validitywindow.ErrDuplicateContainer, - }, - { - name: "three blocks non consecutive duplicate chunks outside validity window", - parentBlocks: [][]int{{1}, {2}}, - chunks: []int{1}, - timestamp: 4, - verifyWantErr: nil, - validalidityWindowDuration: 1, + buildWantErr: ErrNoAvailableChunkCerts, }, { name: "monotonic timestamping", parentBlocks: [][]int{}, - chunks: []int{2}, + chunks: []int{1}, timestamp: 0, verifyWantErr: ErrInvalidBlockHeight, buildWantErr: ErrTimestampNotMonotonicallyIncreasing, @@ -1182,6 +1175,15 @@ func TestNode_Verify_Chunks(t *testing.T) { verifyWantErr: ErrEmptyBlock, buildWantErr: ErrNoAvailableChunkCerts, }, + { + name: "three blocks non consecutive duplicate chunks outside validity window", + parentBlocks: [][]int{{4}, {5}}, + chunks: []int{4}, + timestamp: 4, + verifyWantErr: validitywindow.ErrDuplicateContainer, // this isn't ideal, since it would disqualify a duplicate chunk even when it's outside the validity window. However, it default to the correct direction. + buildWantErr: ErrNoAvailableChunkCerts, + validalidityWindowDuration: 1, + }, } for _, testCase := range tests { t.Run(testCase.name, func(t *testing.T) { @@ -1197,25 +1199,6 @@ func TestNode_Verify_Chunks(t *testing.T) { node = nodes[0] node.validityWindowDuration = validationWindow - var chunks []Chunk[tx] - var chunkCerts []*ChunkCertificate - for expiry := int64(0); expiry < 5; expiry++ { - chunk, cert, err := node.BuildChunk( - context.Background(), - []tx{ - { - ID: ids.GenerateTestID(), - Expiry: expiry, - }, - }, - expiry, - codec.Address{}, - ) - r.NoError(err) - chunks = append(chunks, chunk) - chunkCerts = append(chunkCerts, &cert) - } - // initialize node history. parentBlk := node.LastAccepted for _, chunkList := range testCase.parentBlocks { @@ -1225,8 +1208,21 @@ func TestNode_Verify_Chunks(t *testing.T) { Timestamp: int64(int(node.LastAccepted.Timestamp) + 1), blkID: ids.GenerateTestID(), } - for _, chunkIndex := range chunkList { - blk.ChunkCerts = append(blk.ChunkCerts, chunkCerts[chunkIndex]) + + for _, chunkExpiry := range chunkList { + _, chunkCert, err := node.BuildChunk( + context.Background(), + []tx{ + { + ID: ids.Empty, + Expiry: int64(chunkExpiry), + }, + }, + int64(chunkExpiry), + codec.Address{}, + ) + r.NoError(err) + blk.ChunkCerts = append(blk.ChunkCerts, &chunkCert) } r.NoError(node.Verify(context.Background(), parentBlk, blk)) @@ -1235,12 +1231,6 @@ func TestNode_Verify_Chunks(t *testing.T) { indexer.set(blk.GetID(), NewExecutionBlock(blk)) parentBlk = blk } - // feed the chunks into the storage and build a block. - for _, chunkIdx := range testCase.chunks { - r.NoError(node.storage.AddLocalChunkWithCert(chunks[chunkIdx], chunkCerts[chunkIdx])) - } - _, err := node.BuildBlock(context.Background(), parentBlk, testCase.timestamp) - r.ErrorIs(err, testCase.buildWantErr) // create the block so that we can test it against the execute directly. newBlk := Block{ @@ -1249,9 +1239,25 @@ func TestNode_Verify_Chunks(t *testing.T) { Timestamp: testCase.timestamp, blkID: ids.GenerateTestID(), } - for _, chunkIndex := range testCase.chunks { - newBlk.ChunkCerts = append(newBlk.ChunkCerts, chunkCerts[chunkIndex]) + + for _, chunkExpiry := range testCase.chunks { + _, chunkCert, err := node.BuildChunk( + context.Background(), + []tx{ + { + ID: ids.Empty, // ids.GenerateTestID(), + Expiry: int64(chunkExpiry), + }, + }, + int64(chunkExpiry), + codec.Address{}, + ) + r.NoError(err) + newBlk.ChunkCerts = append(newBlk.ChunkCerts, &chunkCert) } + _, err := node.BuildBlock(context.Background(), parentBlk, testCase.timestamp) + r.ErrorIs(err, testCase.buildWantErr) + r.ErrorIs(node.Verify(context.Background(), parentBlk, newBlk), testCase.verifyWantErr) }) } From 7ca779b3efb4e2f5893b0f351eaaefc8d922596c Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Thu, 12 Dec 2024 17:06:14 -0500 Subject: [PATCH 34/43] update --- x/dsmr/node_test.go | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 5836f76193..300e51ec4a 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -31,34 +31,36 @@ import ( snowValidators "github.com/ava-labs/avalanchego/snow/validators" ) -const networkID = uint32(123) +const ( + networkID = uint32(123) + testingDefaultValidityWindowDuration = time.Duration(5) +) var ( - _ Tx = (*tx)(nil) - _ Verifier[tx] = (*failVerifier)(nil) + _ Tx = (*tx)(nil) + _ Verifier[tx] = (*failVerifier)(nil) + _ validitywindow.ChainIndex[*emapChunkCertificate] = (*testValidityWindowChainIndex)(nil) chainID = ids.Empty ) -const testingDefaultValidityWindowDuration = time.Duration(5) - -type testingChainIndex struct { +type testValidityWindowChainIndex struct { blocks map[ids.ID]validitywindow.ExecutionBlock[*emapChunkCertificate] } -func (t *testingChainIndex) GetExecutionBlock(_ context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[*emapChunkCertificate], bool, error) { +func (t *testValidityWindowChainIndex) GetExecutionBlock(_ context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[*emapChunkCertificate], bool, error) { if blk, ok := t.blocks[blkID]; ok { return blk, true, nil } return nil, false, nil } -func (t *testingChainIndex) set(blkID ids.ID, blk validitywindow.ExecutionBlock[*emapChunkCertificate]) { +func (t *testValidityWindowChainIndex) set(blkID ids.ID, blk validitywindow.ExecutionBlock[*emapChunkCertificate]) { t.blocks[blkID] = blk } -func newTestingChainIndexer() *testingChainIndex { - return &testingChainIndex{ +func newTestValidityWindowChainIndex() *testValidityWindowChainIndex { + return &testValidityWindowChainIndex{ blocks: make(map[ids.ID]validitywindow.ExecutionBlock[*emapChunkCertificate]), } } @@ -608,7 +610,7 @@ func TestNode_GetChunkSignature_SignValidChunk(t *testing.T) { }, 1, 1, - newTestingChainIndexer(), + newTestValidityWindowChainIndex(), testingDefaultValidityWindowDuration, ) r.NoError(err) @@ -1192,10 +1194,8 @@ func TestNode_Verify_Chunks(t *testing.T) { validationWindow = testingDefaultValidityWindowDuration } - var nodes []*Node[tx] var node *Node[tx] - var indexer *testingChainIndex - nodes, indexer = newNodes(t, 1) + nodes, indexer := newNodes(t, 1) node = nodes[0] node.validityWindowDuration = validationWindow @@ -1548,7 +1548,7 @@ func newTestNode(t *testing.T) *Node[tx] { return nodes[0] } -func newNodes(t *testing.T, n int) ([]*Node[tx], *testingChainIndex) { +func newNodes(t *testing.T, n int) ([]*Node[tx], *testValidityWindowChainIndex) { nodes := make([]testNode, 0, n) validators := make([]Validator, 0, n) for i := 0; i < n; i++ { @@ -1586,7 +1586,7 @@ func newNodes(t *testing.T, n int) ([]*Node[tx], *testingChainIndex) { }) } - indexer := newTestingChainIndexer() + indexer := newTestValidityWindowChainIndex() result := make([]*Node[tx], 0, n) for i, n := range nodes { From 383a7ef0850b56238fa054c5ba473301c6c14ee8 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Thu, 12 Dec 2024 17:08:36 -0500 Subject: [PATCH 35/43] update --- internal/validitywindow/syncer.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/validitywindow/syncer.go b/internal/validitywindow/syncer.go index e2d104118a..677a445ca0 100644 --- a/internal/validitywindow/syncer.go +++ b/internal/validitywindow/syncer.go @@ -39,11 +39,11 @@ func (s *Syncer[Container]) start(ctx context.Context, lastAcceptedBlock Executi seenValidityWindow = false validityWindow = s.getValidityWindow(lastAcceptedBlock.Timestamp()) err error - hasBlock bool + ok bool ) for { - parent, hasBlock, err = s.chainIndex.GetExecutionBlock(ctx, parent.Parent()) - if err != nil || !hasBlock { + parent, ok, err = s.chainIndex.GetExecutionBlock(ctx, parent.Parent()) + if err != nil || !ok { break // If we can't fetch far enough back or we've gone past genesis, execute what we can } parents = append(parents, parent) From 7902a6a1bbe4a6571e6ea692da54a8820ca91b2f Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:24:07 -0500 Subject: [PATCH 36/43] update per review feedback,. --- chain/processor.go | 2 +- internal/validitywindow/dependencies.go | 4 ++-- internal/validitywindow/syncer.go | 5 ++--- internal/validitywindow/validitywindow.go | 26 +++++++---------------- vm/resolutions.go | 6 +++--- x/dsmr/block.go | 2 +- x/dsmr/node_test.go | 9 ++++---- 7 files changed, 22 insertions(+), 32 deletions(-) diff --git a/chain/processor.go b/chain/processor.go index d5638912d5..b9a9d4f1a6 100644 --- a/chain/processor.go +++ b/chain/processor.go @@ -69,7 +69,7 @@ func (b *ExecutionBlock) Timestamp() int64 { return b.Tmstmp } -func (b *ExecutionBlock) Txs() []*Transaction { +func (b *ExecutionBlock) Containers() []*Transaction { return b.StatelessBlock.Txs } diff --git a/internal/validitywindow/dependencies.go b/internal/validitywindow/dependencies.go index 2b52e31447..08d4a11559 100644 --- a/internal/validitywindow/dependencies.go +++ b/internal/validitywindow/dependencies.go @@ -15,10 +15,10 @@ type ExecutionBlock[Container emap.Item] interface { Parent() ids.ID Timestamp() int64 Height() uint64 - Txs() []Container + Containers() []Container Contains(ids.ID) bool } type ChainIndex[Container emap.Item] interface { - GetExecutionBlock(ctx context.Context, blkID ids.ID) (ExecutionBlock[Container], bool, error) + GetExecutionBlock(ctx context.Context, blkID ids.ID) (ExecutionBlock[Container], error) } diff --git a/internal/validitywindow/syncer.go b/internal/validitywindow/syncer.go index 677a445ca0..bfd4060f68 100644 --- a/internal/validitywindow/syncer.go +++ b/internal/validitywindow/syncer.go @@ -39,11 +39,10 @@ func (s *Syncer[Container]) start(ctx context.Context, lastAcceptedBlock Executi seenValidityWindow = false validityWindow = s.getValidityWindow(lastAcceptedBlock.Timestamp()) err error - ok bool ) for { - parent, ok, err = s.chainIndex.GetExecutionBlock(ctx, parent.Parent()) - if err != nil || !ok { + parent, err = s.chainIndex.GetExecutionBlock(ctx, parent.Parent()) + if err != nil { break // If we can't fetch far enough back or we've gone past genesis, execute what we can } parents = append(parents, parent) diff --git a/internal/validitywindow/validitywindow.go b/internal/validitywindow/validitywindow.go index 63b605336a..b6cf4b1c8b 100644 --- a/internal/validitywindow/validitywindow.go +++ b/internal/validitywindow/validitywindow.go @@ -18,10 +18,7 @@ import ( "github.com/ava-labs/hypersdk/internal/emap" ) -var ( - ErrDuplicateContainer = errors.New("duplicate container") - ErrExecutionBlockRetrievalFailed = errors.New("unable to retrieve block") -) +var ErrDuplicateContainer = errors.New("duplicate container") type TimeValidityWindow[Container emap.Item] struct { log logging.Logger @@ -49,7 +46,7 @@ func (v *TimeValidityWindow[Container]) Accept(blk ExecutionBlock[Container]) { evicted := v.seen.SetMin(blk.Timestamp()) v.log.Debug("txs evicted from seen", zap.Int("len", len(evicted))) - v.seen.Add(blk.Txs()) + v.seen.Add(blk.Containers()) v.lastAcceptedBlockHeight = blk.Height() } @@ -61,18 +58,12 @@ func (v *TimeValidityWindow[Container]) VerifyExpiryReplayProtection( if blk.Height() <= v.lastAcceptedBlockHeight { return nil } - parent, hasBlock, err := v.chainIndex.GetExecutionBlock(ctx, blk.Parent()) + parent, err := v.chainIndex.GetExecutionBlock(ctx, blk.Parent()) if err != nil { return err } - if !hasBlock { - // if we can't get the block, we won't be able to determine if any of the transaction are duplicate. - // this is not an expected result, and is equivilient to the case where we cannot perform the validation - // due to disk issue. - return ErrExecutionBlockRetrievalFailed - } - dup, err := v.isRepeat(ctx, parent, oldestAllowed, blk.Txs(), true) + dup, err := v.isRepeat(ctx, parent, oldestAllowed, blk.Containers(), true) if err != nil { return err } @@ -81,8 +72,8 @@ func (v *TimeValidityWindow[Container]) VerifyExpiryReplayProtection( } // make sure we have no repeats within the block itself. // set.Set - blkTxsIDs := set.NewSet[ids.ID](len(blk.Txs())) - for _, tx := range blk.Txs() { + blkTxsIDs := set.NewSet[ids.ID](len(blk.Containers())) + for _, tx := range blk.Containers() { id := tx.GetID() if blkTxsIDs.Contains(id) { return fmt.Errorf("%w: duplicate in block", ErrDuplicateContainer) @@ -117,7 +108,6 @@ func (v *TimeValidityWindow[Container]) isRepeat( defer v.lock.Unlock() var err error - var hasBlock bool for { if ancestorBlk.Timestamp() < oldestAllowed { return marker, nil @@ -139,8 +129,8 @@ func (v *TimeValidityWindow[Container]) isRepeat( } } - ancestorBlk, hasBlock, err = v.chainIndex.GetExecutionBlock(ctx, ancestorBlk.Parent()) - if err != nil || !hasBlock { + ancestorBlk, err = v.chainIndex.GetExecutionBlock(ctx, ancestorBlk.Parent()) + if err != nil { return marker, err } } diff --git a/vm/resolutions.go b/vm/resolutions.go index 60560564c7..a90c70d69d 100644 --- a/vm/resolutions.go +++ b/vm/resolutions.go @@ -92,15 +92,15 @@ func (vm *VM) LastAcceptedStatefulBlock() *StatefulBlock { return vm.lastAccepted } -func (vm *VM) GetExecutionBlock(ctx context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[*chain.Transaction], bool, error) { +func (vm *VM) GetExecutionBlock(ctx context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[*chain.Transaction], error) { _, span := vm.tracer.Start(ctx, "VM.GetExecutionBlock") defer span.End() blk, err := vm.GetStatefulBlock(ctx, blkID) if err != nil { - return nil, true, err + return nil, err } - return blk.ExecutionBlock, true, nil + return blk.ExecutionBlock, nil } func (vm *VM) LastAcceptedBlockResult() *chain.ExecutedBlock { diff --git a/x/dsmr/block.go b/x/dsmr/block.go index 99f1bbcd21..22600e105d 100644 --- a/x/dsmr/block.go +++ b/x/dsmr/block.go @@ -127,7 +127,7 @@ func (e ExecutionBlock) Parent() ids.ID { return e.innerBlock.ParentID } -func (e ExecutionBlock) Txs() []*emapChunkCertificate { +func (e ExecutionBlock) Containers() []*emapChunkCertificate { emapChunkCert := make([]*emapChunkCertificate, len(e.innerBlock.ChunkCerts)) for i := range emapChunkCert { emapChunkCert[i] = &emapChunkCertificate{*e.innerBlock.ChunkCerts[i]} diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 300e51ec4a..8052854c70 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/database/memdb" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" @@ -48,11 +49,11 @@ type testValidityWindowChainIndex struct { blocks map[ids.ID]validitywindow.ExecutionBlock[*emapChunkCertificate] } -func (t *testValidityWindowChainIndex) GetExecutionBlock(_ context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[*emapChunkCertificate], bool, error) { +func (t *testValidityWindowChainIndex) GetExecutionBlock(_ context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[*emapChunkCertificate], error) { if blk, ok := t.blocks[blkID]; ok { - return blk, true, nil + return blk, nil } - return nil, false, nil + return nil, database.ErrNotFound } func (t *testValidityWindowChainIndex) set(blkID ids.ID, blk validitywindow.ExecutionBlock[*emapChunkCertificate]) { @@ -225,7 +226,7 @@ func TestIndexerMissingBlock(t *testing.T) { blkNext, err := node.BuildBlock(context.Background(), node.LastAccepted, 4) r.NoError(err) - r.ErrorIs(node.Verify(context.Background(), node.LastAccepted, blkNext), validitywindow.ErrExecutionBlockRetrievalFailed) + r.ErrorIs(node.Verify(context.Background(), node.LastAccepted, blkNext), database.ErrNotFound) } // Tests that pending chunks are not available over p2p From 1f143ea4af6b71dc712274d91288d9daa6ec94d6 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Thu, 19 Dec 2024 13:14:18 -0500 Subject: [PATCH 37/43] address feedback. --- internal/validitywindow/validitywindow.go | 18 +++++++++--------- x/dsmr/block.go | 19 ++++++++----------- x/dsmr/node_test.go | 22 +++++++++++++--------- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/internal/validitywindow/validitywindow.go b/internal/validitywindow/validitywindow.go index b6cf4b1c8b..65bf90ad16 100644 --- a/internal/validitywindow/validitywindow.go +++ b/internal/validitywindow/validitywindow.go @@ -72,13 +72,13 @@ func (v *TimeValidityWindow[Container]) VerifyExpiryReplayProtection( } // make sure we have no repeats within the block itself. // set.Set - blkTxsIDs := set.NewSet[ids.ID](len(blk.Containers())) - for _, tx := range blk.Containers() { - id := tx.GetID() - if blkTxsIDs.Contains(id) { + blkContainerIDs := set.NewSet[ids.ID](len(blk.Containers())) + for _, container := range blk.Containers() { + id := container.GetID() + if blkContainerIDs.Contains(id) { return fmt.Errorf("%w: duplicate in block", ErrDuplicateContainer) } - blkTxsIDs.Add(id) + blkContainerIDs.Add(id) } return nil } @@ -96,7 +96,7 @@ func (v *TimeValidityWindow[Container]) isRepeat( ctx context.Context, ancestorBlk ExecutionBlock[Container], oldestAllowed int64, - txs []Container, + containers []Container, stop bool, ) (set.Bits, error) { marker := set.NewBits() @@ -114,14 +114,14 @@ func (v *TimeValidityWindow[Container]) isRepeat( } if ancestorBlk.Height() <= v.lastAcceptedBlockHeight || ancestorBlk.Height() == 0 { - return v.seen.Contains(txs, marker, stop), nil + return v.seen.Contains(containers, marker, stop), nil } - for i, tx := range txs { + for i, container := range containers { if marker.Contains(i) { continue } - if ancestorBlk.Contains(tx.GetID()) { + if ancestorBlk.Contains(container.GetID()) { marker.Add(i) if stop { return marker, nil diff --git a/x/dsmr/block.go b/x/dsmr/block.go index 22600e105d..78eade8396 100644 --- a/x/dsmr/block.go +++ b/x/dsmr/block.go @@ -109,6 +109,7 @@ func ParseChunk[T Tx](chunkBytes []byte) (Chunk[T], error) { // ExecutionBlock bridge the gap between the dsmr's block implementation and the validity window's execution block interface. type ExecutionBlock struct { innerBlock Block + certSet set.Set[ids.ID] } func (e ExecutionBlock) Timestamp() int64 { @@ -120,7 +121,7 @@ func (e ExecutionBlock) Height() uint64 { } func (e ExecutionBlock) Contains(id ids.ID) bool { - return e.innerBlock.certSet.Contains(id) + return e.certSet.Contains(id) } func (e ExecutionBlock) Parent() ids.ID { @@ -136,8 +137,13 @@ func (e ExecutionBlock) Containers() []*emapChunkCertificate { } func NewExecutionBlock(innerBlock Block) ExecutionBlock { + certSet := set.Set[ids.ID]{} + for _, c := range innerBlock.ChunkCerts { + certSet.Add(c.ChunkID) + } return ExecutionBlock{ innerBlock: innerBlock, + certSet: certSet, } } @@ -150,26 +156,17 @@ type Block struct { blkID ids.ID blkBytes []byte - certSet set.Set[ids.ID] } func NewBlock(parentID ids.ID, height uint64, timestamp int64, chunkCerts []*ChunkCertificate) Block { - blk := Block{ + return Block{ ParentID: parentID, Height: height, Timestamp: timestamp, ChunkCerts: chunkCerts, } - blk.init() - return blk } func (b Block) GetID() ids.ID { return b.blkID } - -func (b Block) init() { - for _, c := range b.ChunkCerts { - b.certSet.Add(c.ChunkID) - } -} diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 8052854c70..8198c0b52d 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -1120,8 +1120,6 @@ func TestDuplicateChunksElimination(t *testing.T) { } func TestNode_Verify_Chunks(t *testing.T) { - r := require.New(t) - tests := []struct { name string parentBlocks [][]int // for each parent, a list of the chunks included. @@ -1190,6 +1188,7 @@ func TestNode_Verify_Chunks(t *testing.T) { } for _, testCase := range tests { t.Run(testCase.name, func(t *testing.T) { + r := require.New(t) validationWindow := testCase.validalidityWindowDuration if validationWindow == 0 { validationWindow = testingDefaultValidityWindowDuration @@ -1215,7 +1214,7 @@ func TestNode_Verify_Chunks(t *testing.T) { context.Background(), []tx{ { - ID: ids.Empty, + ID: ids.Empty.Prefix(uint64(chunkExpiry)), Expiry: int64(chunkExpiry), }, }, @@ -1233,7 +1232,7 @@ func TestNode_Verify_Chunks(t *testing.T) { parentBlk = blk } - // create the block so that we can test it against the execute directly. + // create the block so that we can test it against Execute directly. newBlk := Block{ ParentID: parentBlk.GetID(), Height: uint64(testCase.timestamp), @@ -1246,7 +1245,7 @@ func TestNode_Verify_Chunks(t *testing.T) { context.Background(), []tx{ { - ID: ids.Empty, // ids.GenerateTestID(), + ID: ids.Empty.Prefix(uint64(chunkExpiry)), Expiry: int64(chunkExpiry), }, }, @@ -1256,8 +1255,13 @@ func TestNode_Verify_Chunks(t *testing.T) { r.NoError(err) newBlk.ChunkCerts = append(newBlk.ChunkCerts, &chunkCert) } - _, err := node.BuildBlock(context.Background(), parentBlk, testCase.timestamp) + builtBlk, err := node.BuildBlock(context.Background(), parentBlk, testCase.timestamp) r.ErrorIs(err, testCase.buildWantErr) + if err == nil { + r.Equal(newBlk.ParentID, builtBlk.ParentID) + r.Equal(newBlk.Height, builtBlk.Height) + r.Equal(newBlk.Timestamp, builtBlk.Timestamp) + } r.ErrorIs(node.Verify(context.Background(), parentBlk, newBlk), testCase.verifyWantErr) }) @@ -1669,19 +1673,19 @@ func newNodes(t *testing.T, n int) ([]*Node[tx], *testValidityWindowChainIndex) codec.Address{}, ) require.NoError(t, err) - indexer.set(node.LastAccepted.GetID(), ExecutionBlock{node.LastAccepted}) + indexer.set(node.LastAccepted.GetID(), ExecutionBlock{innerBlock: node.LastAccepted}) blk, err := node.BuildBlock(context.Background(), node.LastAccepted, node.LastAccepted.Timestamp+1) require.NoError(t, err) require.NoError(t, node.Verify(context.Background(), node.LastAccepted, blk)) require.NoError(t, node.Accept(context.Background(), blk)) - indexer.set(blk.GetID(), ExecutionBlock{blk}) + indexer.set(blk.GetID(), ExecutionBlock{innerBlock: blk}) for _, n := range result[1:] { require.NoError(t, n.Verify(context.Background(), n.LastAccepted, blk)) require.NoError(t, n.Accept(context.Background(), blk)) - indexer.set(blk.GetID(), ExecutionBlock{blk}) + indexer.set(blk.GetID(), ExecutionBlock{innerBlock: blk}) } return result, indexer From 6d4f94c7bfadda48938f038f588d292244d2fc8b Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:13:29 -0500 Subject: [PATCH 38/43] update per cr --- x/dsmr/node_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 8198c0b52d..77d96ed601 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -1071,7 +1071,7 @@ func TestDuplicateChunksElimination(t *testing.T) { } r.NoError(node.Accept(context.Background(), blk)) - chunk, chunkCert, err := node.BuildChunk( + _, chunkCert, err := node.BuildChunk( context.Background(), []tx{ { @@ -1095,12 +1095,11 @@ func TestDuplicateChunksElimination(t *testing.T) { } r.NoError(node.Accept(context.Background(), blk)) - r.NoError(node.storage.AddLocalChunkWithCert(chunk, &chunkCert)) _, err = node.BuildBlock(context.Background(), blk, 3) r.ErrorIs(err, ErrNoAvailableChunkCerts) // make sure that it's not the case with any other chunk. - anotherChunk, anotherChunkCert, err := node.BuildChunk( + _, _, err = node.BuildChunk( context.Background(), []tx{ { @@ -1114,7 +1113,6 @@ func TestDuplicateChunksElimination(t *testing.T) { r.NoError(err) r.NoError(node.Accept(context.Background(), blk)) - r.NoError(node.storage.AddLocalChunkWithCert(anotherChunk, &anotherChunkCert)) _, err = node.BuildBlock(context.Background(), blk, 3) r.NoError(err) } From c87b033566faad6121b8a1aeb45ee50ad341c785 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:21:08 -0500 Subject: [PATCH 39/43] update --- x/dsmr/block.go | 18 +++++++++--------- x/dsmr/node.go | 6 +++--- x/dsmr/node_test.go | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/x/dsmr/block.go b/x/dsmr/block.go index 78eade8396..68dac95c1e 100644 --- a/x/dsmr/block.go +++ b/x/dsmr/block.go @@ -106,29 +106,29 @@ func ParseChunk[T Tx](chunkBytes []byte) (Chunk[T], error) { return c, c.init() } -// ExecutionBlock bridge the gap between the dsmr's block implementation and the validity window's execution block interface. -type ExecutionBlock struct { +// validityWindowBlock bridge the gap between the dsmr's block implementation and the validity window's execution block interface. +type validityWindowBlock struct { innerBlock Block certSet set.Set[ids.ID] } -func (e ExecutionBlock) Timestamp() int64 { +func (e validityWindowBlock) Timestamp() int64 { return e.innerBlock.Timestamp } -func (e ExecutionBlock) Height() uint64 { +func (e validityWindowBlock) Height() uint64 { return e.innerBlock.Height } -func (e ExecutionBlock) Contains(id ids.ID) bool { +func (e validityWindowBlock) Contains(id ids.ID) bool { return e.certSet.Contains(id) } -func (e ExecutionBlock) Parent() ids.ID { +func (e validityWindowBlock) Parent() ids.ID { return e.innerBlock.ParentID } -func (e ExecutionBlock) Containers() []*emapChunkCertificate { +func (e validityWindowBlock) Containers() []*emapChunkCertificate { emapChunkCert := make([]*emapChunkCertificate, len(e.innerBlock.ChunkCerts)) for i := range emapChunkCert { emapChunkCert[i] = &emapChunkCertificate{*e.innerBlock.ChunkCerts[i]} @@ -136,12 +136,12 @@ func (e ExecutionBlock) Containers() []*emapChunkCertificate { return emapChunkCert } -func NewExecutionBlock(innerBlock Block) ExecutionBlock { +func NewValidityWindowBlock(innerBlock Block) validityWindowBlock { certSet := set.Set[ids.ID]{} for _, c := range innerBlock.ChunkCerts { certSet.Add(c.ChunkID) } - return ExecutionBlock{ + return validityWindowBlock{ innerBlock: innerBlock, certSet: certSet, } diff --git a/x/dsmr/node.go b/x/dsmr/node.go index ee6f042bea..14bb263c25 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -243,7 +243,7 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) for i := range emapChunkCert { emapChunkCert[i] = &emapChunkCertificate{*chunkCerts[i]} } - dup, err := n.validityWindow.IsRepeat(ctx, NewExecutionBlock(parent), emapChunkCert, oldestAllowed) + dup, err := n.validityWindow.IsRepeat(ctx, NewValidityWindowBlock(parent), emapChunkCert, oldestAllowed) if err != nil { return Block{}, err } @@ -308,7 +308,7 @@ func (n *Node[T]) Verify(ctx context.Context, parent Block, block Block) error { // Find repeats oldestAllowed := max(0, block.Timestamp-int64(n.validityWindowDuration)) - if err := n.validityWindow.VerifyExpiryReplayProtection(ctx, NewExecutionBlock(block), oldestAllowed); err != nil { + if err := n.validityWindow.VerifyExpiryReplayProtection(ctx, NewValidityWindowBlock(block), oldestAllowed); err != nil { return err } @@ -372,7 +372,7 @@ func (n *Node[T]) Accept(ctx context.Context, block Block) error { } } // update the validity window with the accepted block. - n.validityWindow.Accept(NewExecutionBlock(block)) + n.validityWindow.Accept(NewValidityWindowBlock(block)) if err := n.storage.SetMin(block.Timestamp, chunkIDs); err != nil { return fmt.Errorf("failed to prune chunks: %w", err) diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 77d96ed601..1e67378958 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -1226,7 +1226,7 @@ func TestNode_Verify_Chunks(t *testing.T) { r.NoError(node.Verify(context.Background(), parentBlk, blk)) r.NoError(node.Accept(context.Background(), blk)) - indexer.set(blk.GetID(), NewExecutionBlock(blk)) + indexer.set(blk.GetID(), NewValidityWindowBlock(blk)) parentBlk = blk } @@ -1671,19 +1671,19 @@ func newNodes(t *testing.T, n int) ([]*Node[tx], *testValidityWindowChainIndex) codec.Address{}, ) require.NoError(t, err) - indexer.set(node.LastAccepted.GetID(), ExecutionBlock{innerBlock: node.LastAccepted}) + indexer.set(node.LastAccepted.GetID(), validityWindowBlock{innerBlock: node.LastAccepted}) blk, err := node.BuildBlock(context.Background(), node.LastAccepted, node.LastAccepted.Timestamp+1) require.NoError(t, err) require.NoError(t, node.Verify(context.Background(), node.LastAccepted, blk)) require.NoError(t, node.Accept(context.Background(), blk)) - indexer.set(blk.GetID(), ExecutionBlock{innerBlock: blk}) + indexer.set(blk.GetID(), validityWindowBlock{innerBlock: blk}) for _, n := range result[1:] { require.NoError(t, n.Verify(context.Background(), n.LastAccepted, blk)) require.NoError(t, n.Accept(context.Background(), blk)) - indexer.set(blk.GetID(), ExecutionBlock{innerBlock: blk}) + indexer.set(blk.GetID(), validityWindowBlock{innerBlock: blk}) } return result, indexer From b501cec131a929a1f81d0bf1cd6dd9a1afb2a037 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 23 Dec 2024 12:57:35 -0500 Subject: [PATCH 40/43] update per CR feedback. --- internal/validitywindow/validitywindow.go | 1 - x/dsmr/block.go | 24 ++++---- x/dsmr/node_test.go | 70 +++++++++-------------- 3 files changed, 40 insertions(+), 55 deletions(-) diff --git a/internal/validitywindow/validitywindow.go b/internal/validitywindow/validitywindow.go index 65bf90ad16..8296ac7f0d 100644 --- a/internal/validitywindow/validitywindow.go +++ b/internal/validitywindow/validitywindow.go @@ -71,7 +71,6 @@ func (v *TimeValidityWindow[Container]) VerifyExpiryReplayProtection( return fmt.Errorf("%w: duplicate in ancestry", ErrDuplicateContainer) } // make sure we have no repeats within the block itself. - // set.Set blkContainerIDs := set.NewSet[ids.ID](len(blk.Containers())) for _, container := range blk.Containers() { id := container.GetID() diff --git a/x/dsmr/block.go b/x/dsmr/block.go index 68dac95c1e..1367d4fba5 100644 --- a/x/dsmr/block.go +++ b/x/dsmr/block.go @@ -108,32 +108,32 @@ func ParseChunk[T Tx](chunkBytes []byte) (Chunk[T], error) { // validityWindowBlock bridge the gap between the dsmr's block implementation and the validity window's execution block interface. type validityWindowBlock struct { - innerBlock Block - certSet set.Set[ids.ID] + Block + certs set.Set[ids.ID] } func (e validityWindowBlock) Timestamp() int64 { - return e.innerBlock.Timestamp + return e.Block.Timestamp } func (e validityWindowBlock) Height() uint64 { - return e.innerBlock.Height + return e.Block.Height } func (e validityWindowBlock) Contains(id ids.ID) bool { - return e.certSet.Contains(id) + return e.certs.Contains(id) } func (e validityWindowBlock) Parent() ids.ID { - return e.innerBlock.ParentID + return e.Block.ParentID } func (e validityWindowBlock) Containers() []*emapChunkCertificate { - emapChunkCert := make([]*emapChunkCertificate, len(e.innerBlock.ChunkCerts)) - for i := range emapChunkCert { - emapChunkCert[i] = &emapChunkCertificate{*e.innerBlock.ChunkCerts[i]} + chunkCerts := make([]*emapChunkCertificate, len(e.Block.ChunkCerts)) + for i := range chunkCerts { + chunkCerts[i] = &emapChunkCertificate{*e.Block.ChunkCerts[i]} } - return emapChunkCert + return chunkCerts } func NewValidityWindowBlock(innerBlock Block) validityWindowBlock { @@ -142,8 +142,8 @@ func NewValidityWindowBlock(innerBlock Block) validityWindowBlock { certSet.Add(c.ChunkID) } return validityWindowBlock{ - innerBlock: innerBlock, - certSet: certSet, + Block: innerBlock, + certs: certSet, } } diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 1e67378958..cf2aa28da3 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -45,27 +45,6 @@ var ( chainID = ids.Empty ) -type testValidityWindowChainIndex struct { - blocks map[ids.ID]validitywindow.ExecutionBlock[*emapChunkCertificate] -} - -func (t *testValidityWindowChainIndex) GetExecutionBlock(_ context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[*emapChunkCertificate], error) { - if blk, ok := t.blocks[blkID]; ok { - return blk, nil - } - return nil, database.ErrNotFound -} - -func (t *testValidityWindowChainIndex) set(blkID ids.ID, blk validitywindow.ExecutionBlock[*emapChunkCertificate]) { - t.blocks[blkID] = blk -} - -func newTestValidityWindowChainIndex() *testValidityWindowChainIndex { - return &testValidityWindowChainIndex{ - blocks: make(map[ids.ID]validitywindow.ExecutionBlock[*emapChunkCertificate]), - } -} - // Test that chunks can be built through Node.NewChunk func TestNode_BuildChunk(t *testing.T) { tests := []struct { @@ -1063,15 +1042,7 @@ func TestDuplicateChunksElimination(t *testing.T) { node := newTestNode(t) - blk := Block{ - ParentID: ids.GenerateTestID(), - Height: 1, - Timestamp: 1, - blkID: ids.GenerateTestID(), - } - r.NoError(node.Accept(context.Background(), blk)) - - _, chunkCert, err := node.BuildChunk( + _, _, err := node.BuildChunk( context.Background(), []tx{ { @@ -1084,15 +1055,9 @@ func TestDuplicateChunksElimination(t *testing.T) { ) r.NoError(err) - blk = Block{ - ParentID: ids.GenerateTestID(), - Height: 2, - Timestamp: 2, - blkID: ids.GenerateTestID(), - ChunkCerts: []*ChunkCertificate{ - &chunkCert, - }, - } + blk, err := node.BuildBlock(context.Background(), node.LastAccepted, 2) + r.NoError(err) + r.NoError(node.Verify(context.Background(), node.LastAccepted, blk)) r.NoError(node.Accept(context.Background(), blk)) _, err = node.BuildBlock(context.Background(), blk, 3) @@ -1671,20 +1636,41 @@ func newNodes(t *testing.T, n int) ([]*Node[tx], *testValidityWindowChainIndex) codec.Address{}, ) require.NoError(t, err) - indexer.set(node.LastAccepted.GetID(), validityWindowBlock{innerBlock: node.LastAccepted}) + indexer.set(node.LastAccepted.GetID(), validityWindowBlock{Block: node.LastAccepted}) blk, err := node.BuildBlock(context.Background(), node.LastAccepted, node.LastAccepted.Timestamp+1) require.NoError(t, err) require.NoError(t, node.Verify(context.Background(), node.LastAccepted, blk)) require.NoError(t, node.Accept(context.Background(), blk)) - indexer.set(blk.GetID(), validityWindowBlock{innerBlock: blk}) + indexer.set(blk.GetID(), validityWindowBlock{Block: blk}) for _, n := range result[1:] { require.NoError(t, n.Verify(context.Background(), n.LastAccepted, blk)) require.NoError(t, n.Accept(context.Background(), blk)) - indexer.set(blk.GetID(), validityWindowBlock{innerBlock: blk}) + indexer.set(blk.GetID(), validityWindowBlock{Block: blk}) } return result, indexer } + +type testValidityWindowChainIndex struct { + blocks map[ids.ID]validitywindow.ExecutionBlock[*emapChunkCertificate] +} + +func (t *testValidityWindowChainIndex) GetExecutionBlock(_ context.Context, blkID ids.ID) (validitywindow.ExecutionBlock[*emapChunkCertificate], error) { + if blk, ok := t.blocks[blkID]; ok { + return blk, nil + } + return nil, database.ErrNotFound +} + +func (t *testValidityWindowChainIndex) set(blkID ids.ID, blk validitywindow.ExecutionBlock[*emapChunkCertificate]) { + t.blocks[blkID] = blk +} + +func newTestValidityWindowChainIndex() *testValidityWindowChainIndex { + return &testValidityWindowChainIndex{ + blocks: make(map[ids.ID]validitywindow.ExecutionBlock[*emapChunkCertificate]), + } +} From 68603dbc7979deb0d3e9e575c82885798585f864 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 23 Dec 2024 13:23:38 -0500 Subject: [PATCH 41/43] update per CR --- x/dsmr/block.go | 9 --------- x/dsmr/node.go | 26 ++++++++++++-------------- x/dsmr/node_test.go | 27 +++++++++++++++------------ 3 files changed, 27 insertions(+), 35 deletions(-) diff --git a/x/dsmr/block.go b/x/dsmr/block.go index 1367d4fba5..9833622d0b 100644 --- a/x/dsmr/block.go +++ b/x/dsmr/block.go @@ -158,15 +158,6 @@ type Block struct { blkBytes []byte } -func NewBlock(parentID ids.ID, height uint64, timestamp int64, chunkCerts []*ChunkCertificate) Block { - return Block{ - ParentID: parentID, - Height: height, - Timestamp: timestamp, - ChunkCerts: chunkCerts, - } -} - func (b Block) GetID() ids.ID { return b.blkID } diff --git a/x/dsmr/node.go b/x/dsmr/node.go index 14bb263c25..62f994eabd 100644 --- a/x/dsmr/node.go +++ b/x/dsmr/node.go @@ -38,7 +38,6 @@ var ( ErrEmptyChunk = errors.New("empty chunk") ErrNoAvailableChunkCerts = errors.New("no available chunk certs") - ErrAllChunkCertsDuplicate = errors.New("all chunk certs are duplicated") ErrTimestampNotMonotonicallyIncreasing = errors.New("block timestamp must be greater than parent timestamp") ErrEmptyBlock = errors.New("block must reference chunks") ErrInvalidBlockParent = errors.New("invalid referenced block parent") @@ -238,12 +237,11 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) chunkCerts := n.storage.GatherChunkCerts() oldestAllowed := max(0, timestamp-int64(n.validityWindowDuration)) - - emapChunkCert := make([]*emapChunkCertificate, len(chunkCerts)) - for i := range emapChunkCert { - emapChunkCert[i] = &emapChunkCertificate{*chunkCerts[i]} + chunkCert := make([]*emapChunkCertificate, len(chunkCerts)) + for i := range chunkCert { + chunkCert[i] = &emapChunkCertificate{*chunkCerts[i]} } - dup, err := n.validityWindow.IsRepeat(ctx, NewValidityWindowBlock(parent), emapChunkCert, oldestAllowed) + duplicates, err := n.validityWindow.IsRepeat(ctx, NewValidityWindowBlock(parent), chunkCert, oldestAllowed) if err != nil { return Block{}, err } @@ -251,7 +249,7 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) availableChunkCerts := make([]*ChunkCertificate, 0) for i, chunkCert := range chunkCerts { // avoid building blocks with duplicate or expired chunk certs - if chunkCert.Expiry < timestamp || dup.Contains(i) { + if chunkCert.Expiry < timestamp || duplicates.Contains(i) { continue } availableChunkCerts = append(availableChunkCerts, chunkCert) @@ -260,12 +258,12 @@ func (n *Node[T]) BuildBlock(ctx context.Context, parent Block, timestamp int64) return Block{}, ErrNoAvailableChunkCerts } - blk := NewBlock( - parent.GetID(), - parent.Height+1, - timestamp, - availableChunkCerts, - ) + blk := Block{ + ParentID: parent.GetID(), + Height: parent.Height + 1, + Timestamp: timestamp, + ChunkCerts: availableChunkCerts, + } packer := wrappers.Packer{Bytes: make([]byte, 0, InitialChunkSize), MaxSize: consts.NetworkSizeLimit} if err := codec.LinearCodec.MarshalInto(blk, &packer); err != nil { @@ -309,7 +307,7 @@ func (n *Node[T]) Verify(ctx context.Context, parent Block, block Block) error { oldestAllowed := max(0, block.Timestamp-int64(n.validityWindowDuration)) if err := n.validityWindow.VerifyExpiryReplayProtection(ctx, NewValidityWindowBlock(block), oldestAllowed); err != nil { - return err + return fmt.Errorf("%w: block %s oldestAllowed - %d", err, block.GetID(), oldestAllowed) } for _, chunkCert := range block.ChunkCerts { diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index cf2aa28da3..ddfe0f26a2 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -1060,7 +1060,7 @@ func TestDuplicateChunksElimination(t *testing.T) { r.NoError(node.Verify(context.Background(), node.LastAccepted, blk)) r.NoError(node.Accept(context.Background(), blk)) - _, err = node.BuildBlock(context.Background(), blk, 3) + _, err = node.BuildBlock(context.Background(), node.LastAccepted, 3) r.ErrorIs(err, ErrNoAvailableChunkCerts) // make sure that it's not the case with any other chunk. @@ -1078,7 +1078,7 @@ func TestDuplicateChunksElimination(t *testing.T) { r.NoError(err) r.NoError(node.Accept(context.Background(), blk)) - _, err = node.BuildBlock(context.Background(), blk, 3) + _, err = node.BuildBlock(context.Background(), node.LastAccepted, 3) r.NoError(err) } @@ -1157,9 +1157,8 @@ func TestNode_Verify_Chunks(t *testing.T) { validationWindow = testingDefaultValidityWindowDuration } - var node *Node[tx] - nodes, indexer := newNodes(t, 1) - node = nodes[0] + nodes, indexers := newNodes(t, 1) + node, indexer := nodes[0], indexers[0] node.validityWindowDuration = validationWindow // initialize node history. @@ -1516,7 +1515,7 @@ func newTestNode(t *testing.T) *Node[tx] { return nodes[0] } -func newNodes(t *testing.T, n int) ([]*Node[tx], *testValidityWindowChainIndex) { +func newNodes(t *testing.T, n int) ([]*Node[tx], []*testValidityWindowChainIndex) { nodes := make([]testNode, 0, n) validators := make([]Validator, 0, n) for i := 0; i < n; i++ { @@ -1554,10 +1553,11 @@ func newNodes(t *testing.T, n int) ([]*Node[tx], *testValidityWindowChainIndex) }) } - indexer := newTestValidityWindowChainIndex() + indexers := []*testValidityWindowChainIndex{} result := make([]*Node[tx], 0, n) for i, n := range nodes { + indexers = append(indexers, newTestValidityWindowChainIndex()) getChunkPeers := make(map[ids.NodeID]p2p.Handler) chunkSignaturePeers := make(map[ids.NodeID]p2p.Handler) chunkCertGossipPeers := make(map[ids.NodeID]p2p.Handler) @@ -1613,7 +1613,7 @@ func newNodes(t *testing.T, n int) ([]*Node[tx], *testValidityWindowChainIndex) }, 1, 1, - indexer, + indexers[i], testingDefaultValidityWindowDuration, ) require.NoError(t, err) @@ -1636,22 +1636,25 @@ func newNodes(t *testing.T, n int) ([]*Node[tx], *testValidityWindowChainIndex) codec.Address{}, ) require.NoError(t, err) - indexer.set(node.LastAccepted.GetID(), validityWindowBlock{Block: node.LastAccepted}) + for _, indexer := range indexers { + indexer.set(node.LastAccepted.GetID(), validityWindowBlock{Block: node.LastAccepted}) + } blk, err := node.BuildBlock(context.Background(), node.LastAccepted, node.LastAccepted.Timestamp+1) require.NoError(t, err) require.NoError(t, node.Verify(context.Background(), node.LastAccepted, blk)) require.NoError(t, node.Accept(context.Background(), blk)) - indexer.set(blk.GetID(), validityWindowBlock{Block: blk}) for _, n := range result[1:] { require.NoError(t, n.Verify(context.Background(), n.LastAccepted, blk)) require.NoError(t, n.Accept(context.Background(), blk)) - indexer.set(blk.GetID(), validityWindowBlock{Block: blk}) } - return result, indexer + for _, indexer := range indexers { + indexer.set(blk.GetID(), validityWindowBlock{Block: blk}) + } + return result, indexers } type testValidityWindowChainIndex struct { From 7e72626104340f680ec0f7b29d77fba44eb4dad6 Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 23 Dec 2024 14:46:30 -0500 Subject: [PATCH 42/43] update per feedback. --- x/dsmr/node_test.go | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index ddfe0f26a2..3835dc9789 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -1094,7 +1094,7 @@ func TestNode_Verify_Chunks(t *testing.T) { }{ { name: "three blocks, unique chunks", - parentBlocks: [][]int{{1}, {2}}, + parentBlocks: [][]int{{2}, {3}}, chunks: []int{4}, timestamp: 4, verifyWantErr: nil, @@ -1117,7 +1117,7 @@ func TestNode_Verify_Chunks(t *testing.T) { }, { name: "three blocks non consecutive duplicate chunks", - parentBlocks: [][]int{{3}, {2}}, + parentBlocks: [][]int{{3}, {5}}, chunks: []int{3}, timestamp: 4, verifyWantErr: validitywindow.ErrDuplicateContainer, @@ -1133,7 +1133,7 @@ func TestNode_Verify_Chunks(t *testing.T) { }, { name: "empty block", - parentBlocks: [][]int{{1}, {2}, {3}, {4}}, + parentBlocks: [][]int{{2}, {3}, {4}, {5}}, chunks: []int{}, timestamp: 6, verifyWantErr: ErrEmptyBlock, @@ -1162,17 +1162,9 @@ func TestNode_Verify_Chunks(t *testing.T) { node.validityWindowDuration = validationWindow // initialize node history. - parentBlk := node.LastAccepted for _, chunkList := range testCase.parentBlocks { - blk := Block{ - ParentID: parentBlk.GetID(), - Height: uint64(int(node.LastAccepted.Height) + 1), - Timestamp: int64(int(node.LastAccepted.Timestamp) + 1), - blkID: ids.GenerateTestID(), - } - for _, chunkExpiry := range chunkList { - _, chunkCert, err := node.BuildChunk( + _, _, err := node.BuildChunk( context.Background(), []tx{ { @@ -1184,19 +1176,18 @@ func TestNode_Verify_Chunks(t *testing.T) { codec.Address{}, ) r.NoError(err) - blk.ChunkCerts = append(blk.ChunkCerts, &chunkCert) } - - r.NoError(node.Verify(context.Background(), parentBlk, blk)) + blk, err := node.BuildBlock(context.Background(), node.LastAccepted, int64(int(node.LastAccepted.Timestamp)+1)) + r.NoError(err, "unable to build block for height %d", node.LastAccepted.Height+1) + r.NoError(node.Verify(context.Background(), node.LastAccepted, blk)) r.NoError(node.Accept(context.Background(), blk)) indexer.set(blk.GetID(), NewValidityWindowBlock(blk)) - parentBlk = blk } // create the block so that we can test it against Execute directly. newBlk := Block{ - ParentID: parentBlk.GetID(), + ParentID: node.LastAccepted.GetID(), Height: uint64(testCase.timestamp), Timestamp: testCase.timestamp, blkID: ids.GenerateTestID(), @@ -1217,7 +1208,7 @@ func TestNode_Verify_Chunks(t *testing.T) { r.NoError(err) newBlk.ChunkCerts = append(newBlk.ChunkCerts, &chunkCert) } - builtBlk, err := node.BuildBlock(context.Background(), parentBlk, testCase.timestamp) + builtBlk, err := node.BuildBlock(context.Background(), node.LastAccepted, testCase.timestamp) r.ErrorIs(err, testCase.buildWantErr) if err == nil { r.Equal(newBlk.ParentID, builtBlk.ParentID) @@ -1225,7 +1216,7 @@ func TestNode_Verify_Chunks(t *testing.T) { r.Equal(newBlk.Timestamp, builtBlk.Timestamp) } - r.ErrorIs(node.Verify(context.Background(), parentBlk, newBlk), testCase.verifyWantErr) + r.ErrorIs(node.Verify(context.Background(), node.LastAccepted, newBlk), testCase.verifyWantErr) }) } } From 7edef6edb0b201333ab4535f8ed48de303d5b85d Mon Sep 17 00:00:00 2001 From: tsachiherman <24438559+tsachiherman@users.noreply.github.com> Date: Mon, 23 Dec 2024 14:52:23 -0500 Subject: [PATCH 43/43] update --- x/dsmr/node_test.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/x/dsmr/node_test.go b/x/dsmr/node_test.go index 3835dc9789..d2cedbcdc8 100644 --- a/x/dsmr/node_test.go +++ b/x/dsmr/node_test.go @@ -132,7 +132,7 @@ func TestNode_BuildChunk(t *testing.T) { func TestNode_GetChunk_AvailableChunk(t *testing.T) { r := require.New(t) - nodes, _ := newNodes(t, 2) + nodes, _ := newNodes(t, 2, testingDefaultValidityWindowDuration) node := nodes[0] chunk, _, err := node.BuildChunk( @@ -747,7 +747,7 @@ func TestNode_GetChunkSignature_DuplicateChunk(t *testing.T) { func TestGetChunkSignature_PersistAttestedBlocks(t *testing.T) { r := require.New(t) - nodes, _ := newNodes(t, 2) + nodes, _ := newNodes(t, 2, testingDefaultValidityWindowDuration) node1 := nodes[0] node2 := nodes[1] @@ -1157,9 +1157,8 @@ func TestNode_Verify_Chunks(t *testing.T) { validationWindow = testingDefaultValidityWindowDuration } - nodes, indexers := newNodes(t, 1) + nodes, indexers := newNodes(t, 1, validationWindow) node, indexer := nodes[0], indexers[0] - node.validityWindowDuration = validationWindow // initialize node history. for _, chunkList := range testCase.parentBlocks { @@ -1225,7 +1224,7 @@ func TestNode_Verify_Chunks(t *testing.T) { func TestAccept_RequestReferencedChunks(t *testing.T) { r := require.New(t) - nodes, _ := newNodes(t, 2) + nodes, _ := newNodes(t, 2, testingDefaultValidityWindowDuration) node1 := nodes[0] node2 := nodes[1] @@ -1502,11 +1501,11 @@ type testNode struct { } func newTestNode(t *testing.T) *Node[tx] { - nodes, _ := newNodes(t, 1) + nodes, _ := newNodes(t, 1, testingDefaultValidityWindowDuration) return nodes[0] } -func newNodes(t *testing.T, n int) ([]*Node[tx], []*testValidityWindowChainIndex) { +func newNodes(t *testing.T, n int, validityWindowDuration time.Duration) ([]*Node[tx], []*testValidityWindowChainIndex) { nodes := make([]testNode, 0, n) validators := make([]Validator, 0, n) for i := 0; i < n; i++ { @@ -1605,7 +1604,7 @@ func newNodes(t *testing.T, n int) ([]*Node[tx], []*testValidityWindowChainIndex 1, 1, indexers[i], - testingDefaultValidityWindowDuration, + validityWindowDuration, ) require.NoError(t, err)