Skip to content
Draft
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
490 changes: 490 additions & 0 deletions msm/encoding.canoto.go

Large diffs are not rendered by default.

59 changes: 59 additions & 0 deletions msm/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,69 @@ type StateMachineMetadata struct {
PChainHeight uint64 `canoto:"uint,4"`
// Timestamp is the time when the block is being built, in milliseconds since Unix epoch.
Timestamp uint64 `canoto:"uint,5"`
// AuxiliaryInfo is application-specific information that the StateMachine doesn't need to understand,
// but can be used by applications that care about epoch changes, such as threshold distributed public key generation.
AuxiliaryInfo *AuxiliaryInfo `canoto:"pointer,6"`
// ICMEpochInfo is the metadata that the StateMachine uses for ICM epoching.
ICMEpochInfo ICMEpochInfo `canoto:"value,7"`

canotoData canotoData_StateMachineMetadata
}

// ICMEpochInfo is metadata used for the ICM protocol.
// The StateMachine maintains this metadata in a similar fashion to proposerVM.
type ICMEpochInfo struct {
EpochStartTime uint64 `canoto:"uint,1"`
EpochNumber uint64 `canoto:"uint,2"`
PChainEpochHeight uint64 `canoto:"uint,3"`

canotoData canotoData_ICMEpochInfo
}

func (ei *ICMEpochInfo) Equal(other *ICMEpochInfo) bool {
if ei == nil {
return other == nil
}
if other == nil {
return ei == nil
}
return ei.EpochStartTime == other.EpochStartTime && ei.EpochNumber == other.EpochNumber && ei.PChainEpochHeight == other.PChainEpochHeight
}

// AppID is an identifier for applications that care about epoch changes.
type AppID uint32

// AuxiliaryInfo defines application-specific information for applications that might care about epoch change,
// such as threshold distributed public key generation.
type AuxiliaryInfo struct {
// Info is opaque bytes that can be used by applications to encode any information that describes
// the current state for the application.
Info []byte `canoto:"bytes,1"`
// PrevAuxInfoSeq is a sequence number that applications can use to find previous AuxiliaryInfo in the chain.
// It is zero if this is the first AuxiliaryInfo for this epoch.
PrevAuxInfoSeq uint64 `canoto:"uint,2"`
// ApplicationID is an identifier that identifies the application.
// Can be used for backward-compatibility and upgrade purposes.
ApplicationID AppID `canoto:"uint,3"`

canotoData canotoData_AuxiliaryInfo
}

func (ai *AuxiliaryInfo) IsZero() bool {
var zero AuxiliaryInfo
return ai.Equal(&zero)
}

func (ai *AuxiliaryInfo) Equal(a *AuxiliaryInfo) bool {
if ai == nil {
return a == nil
}
if a == nil {
return ai == nil
}
return bytes.Equal(ai.Info, a.Info) && ai.PrevAuxInfoSeq == a.PrevAuxInfoSeq && ai.ApplicationID == a.ApplicationID
}

// SimplexEpochInfo is metadata used by the StateMachine.
type SimplexEpochInfo struct {
// PChainReferenceHeight is the P-Chain height that the StateMachine uses as a reference for the current epoch.
Expand Down
28 changes: 13 additions & 15 deletions msm/fake_node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ func TestFakeNodeEpochChangesDespiteEmptyMempool(t *testing.T) {

pChainHeight.Store(200)

for node.Epoch() == 1 {
firstEpoch := node.Epoch()

for node.Epoch() == firstEpoch {
node.buildAndNotarizeBlock()
if node.canFinalize() {
node.tryFinalizeNextBlock()
Expand Down Expand Up @@ -229,6 +231,7 @@ type blockState struct {

type fakeNode struct {
t *testing.T
epoch uint64
sm *StateMachine
mempoolEmpty bool
// blocks holds notarized blocks in order. Finalized blocks always form a
Expand Down Expand Up @@ -260,8 +263,9 @@ func newFakeNode(t *testing.T) *fakeNode {
sm, _ := newStateMachine(t)

fn := &fakeNode{
t: t,
sm: sm,
t: t,
sm: sm,
epoch: 1,
}

fn.sm.BlockBuilder = fn
Expand Down Expand Up @@ -293,17 +297,6 @@ func newFakeNode(t *testing.T) *fakeNode {
return StateMachineBlock{}, nil, fmt.Errorf("block not found")
}

fn.sm.FirstEverSimplexBlock = func() *StateMachineBlock {
for _, block := range fn.blocks {
if block.block.Metadata.SimplexEpochInfo.EpochNumber == 0 {
continue
}
return &block.block
}
require.FailNow(t, "block not found")
return nil
}

return fn
}

Expand Down Expand Up @@ -378,6 +371,9 @@ func (fn *fakeNode) tryFinalizeNextBlock() {
if block.Metadata.SimplexEpochInfo.BlockValidationDescriptor != nil {
fn.blocks = fn.blocks[:nextIndex+1]
fn.t.Logf("Trimmed notarized blocks, new length: %d", len(fn.blocks))
prevEpoch := fn.epoch
fn.epoch = md.Seq
fn.t.Logf("Epoch change from %d to %d", prevEpoch, fn.epoch)
}
}

Expand Down Expand Up @@ -421,6 +417,7 @@ 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.epoch,
Prev: prevBlockDigest,
}, nil)
require.NoError(fn.t, err)
Expand All @@ -439,7 +436,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
9 changes: 5 additions & 4 deletions 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 Expand Up @@ -45,11 +48,9 @@ type VMBlock interface {
//
// If nil is returned, it is guaranteed that either Accept or Reject will be
// called on this block, unless the VM is shut down.
Verify(context.Context) error
Verify(ctx context.Context, pChainHeight uint64) error
}

type UpgradeConfig = any

type bitmask big.Int

func (bm *bitmask) Bytes() []byte {
Expand Down
Loading
Loading