Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion msm/fake_node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,13 +421,44 @@ func (fn *fakeNode) buildBlock() (VMBlock, *StateMachineBlock) {
block, err := fn.sm.BuildBlock(context.Background(), simplex.ProtocolMetadata{
Seq: lastMD.Seq + 1,
Round: lastMD.Round + 1,
Epoch: fn.expectedEpochNumber(parentBlock, lastMD.Seq),
Prev: prevBlockDigest,
}, nil)
require.NoError(fn.t, err)

return block.InnerBlock, block
}

// expectedEpochNumber predicts the SimplexEpochInfo.EpochNumber that the next
// block will end up with, so the caller can populate ProtocolMetadata.Epoch
// to satisfy verifyEpochNumber. Mirrors the build-side logic: inherit from the
// parent, except when crossing into a new epoch (sealing block of the previous
// epoch is finalized) in which case the new epoch number is the sealing block's
// sequence number.
func (fn *fakeNode) expectedEpochNumber(parentBlock StateMachineBlock, parentSeq uint64) uint64 {
parentEpochInfo := parentBlock.Metadata.SimplexEpochInfo
if parentEpochInfo.EpochNumber == 0 {
// About to build the zero block; verifyEpochNumber isn't run for it, but
// EpochNumber will be 1 once built.
return 1
}
if parentEpochInfo.NextState() == stateBuildBlockEpochSealed {
sealingBlockSeq := parentEpochInfo.SealingBlockSeq
if sealingBlockSeq == 0 {
// Parent is the sealing block itself.
sealingBlockSeq = parentSeq
}
_, finalization, err := fn.sm.GetBlock(sealingBlockSeq, [32]byte{})
require.NoError(fn.t, err)
if finalization != nil {
// Sealing block of previous epoch is finalized, so we can move to the new epoch.
return sealingBlockSeq
}
}
// Else, we're not crossing into a new epoch, so we should inherit the epoch number from the parent.
return parentEpochInfo.EpochNumber
}

func (fn *fakeNode) prepareMetadataAndPrevBlockDigest() (*simplex.ProtocolMetadata, [32]byte) {
var lastMD *simplex.ProtocolMetadata
var err error
Expand All @@ -439,7 +470,8 @@ func (fn *fakeNode) prepareMetadataAndPrevBlockDigest() (*simplex.ProtocolMetada
require.NoError(fn.t, err)
} else {
lastMD = &simplex.ProtocolMetadata{
Prev: lastBlockDigest,
Prev: lastBlockDigest,
Epoch: 1,
}
}
return lastMD, lastBlockDigest
Expand Down
5 changes: 4 additions & 1 deletion msm/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package metadata

import (
"context"
"errors"
"fmt"
"math"
"math/big"
Expand All @@ -15,9 +16,11 @@ import (
// but are not imported here to prevent us from importing the entire Avalanchego codebase.
// Once we incorporate Simplex into Avalanchego, we can remove this file and import the relevant code from Avalanchego instead.

var errOverflow = errors.New("overflow")

func safeAdd(a, b uint64) (uint64, error) {
if a > math.MaxUint64-b {
return 0, fmt.Errorf("overflow: %d + %d > maxuint64", a, b)
return 0, fmt.Errorf("%w: %d + %d > maxuint64", errOverflow, a, b)
}
return a + b, nil
}
Expand Down
69 changes: 63 additions & 6 deletions msm/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"crypto/rand"
"crypto/sha256"
"encoding/asn1"
"errors"
"fmt"
"maps"
"math"
Expand All @@ -25,7 +26,7 @@ func TestSafeAdd(t *testing.T) {
name string
a, b uint64
sum uint64
err string
err error
}{
{
name: "zero plus zero",
Expand All @@ -50,12 +51,12 @@ func TestSafeAdd(t *testing.T) {
{
name: "overflow by one",
a: math.MaxUint64, b: 1,
err: "overflow",
err: errOverflow,
},
{
name: "overflow both large",
a: math.MaxUint64 - 5, b: 10,
err: "overflow",
err: errOverflow,
},
{
name: "max uint64 boundary no overflow",
Expand All @@ -65,8 +66,8 @@ func TestSafeAdd(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
result, err := safeAdd(tc.a, tc.b)
if tc.err != "" {
require.ErrorContains(t, err, tc.err)
if tc.err != nil {
require.ErrorIs(t, err, tc.err)
} else {
require.NoError(t, err)
require.Equal(t, tc.sum, result)
Expand Down Expand Up @@ -487,10 +488,66 @@ func (failingAggregator) Aggregate([]simplex.Signature) (simplex.QuorumCertifica
panic("unused in tests")
}

var errTestAggregationFailed = errors.New("aggregation failed")

func (failingAggregator) AppendSignatures([]byte, ...[]byte) ([]byte, error) {
return nil, fmt.Errorf("aggregation failed")
return nil, errTestAggregationFailed
}

func (failingAggregator) IsQuorum([]simplex.NodeID) bool {
return false
}

type testBlockStore map[uint64]StateMachineBlock
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dont think we should be testing anything other structs in misc.go in this file. When we bridge over these avalanchego utilites defined in misc.go this file should just be deleted.

Unfortunately I think this means moving everything starting from line 181 and onwards.


func (bs testBlockStore) getBlock(seq uint64, _ [32]byte) (StateMachineBlock, *simplex.Finalization, error) {
blk, ok := bs[seq]
if !ok {
return StateMachineBlock{}, nil, fmt.Errorf("%w: block %d", simplex.ErrBlockNotFound, seq)
}
return blk, nil, nil
}

type testVMBlock struct {
bytes []byte
height uint64
}

func (b *testVMBlock) Digest() [32]byte {
return sha256.Sum256(b.bytes)
}

func (b *testVMBlock) Height() uint64 {
return b.height
}

func (b *testVMBlock) Timestamp() time.Time {
return time.Now()
}

func (b *testVMBlock) Verify(_ context.Context) error {
return nil
}

type testSigVerifier struct {
err error
}

func (sv *testSigVerifier) VerifySignature(_, _, _ []byte) error {
return sv.err
}

type testKeyAggregator struct {
err error
}

func (ka *testKeyAggregator) AggregateKeys(keys ...[]byte) ([]byte, error) {
if ka.err != nil {
return nil, ka.err
}
var agg []byte
for _, k := range keys {
agg = append(agg, k...)
}
return agg, nil
}
Loading
Loading