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
7,997 changes: 7,997 additions & 0 deletions 0_build.txt

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 am not sure this file belongs to the PR

Large diffs are not rendered by default.

30 changes: 25 additions & 5 deletions adapters.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ type Communication struct {
Broadcaster
}

func newCommunication(sender Sender, broadcaster Broadcaster, validators common.Nodes) *Communication {
c := &Communication{
Sender: sender,
Broadcaster: broadcaster,
}
c.SetValidators(validators)
return c
}

func (c *Communication) SetValidators(nodes common.Nodes) {
c.nodes.Store(nodes)
}
Expand Down Expand Up @@ -114,6 +123,16 @@ func (cs *CachedStorage) RetrieveBlock(seq uint64, digest common.Digest) (metada
func (cs *CachedStorage) Retrieve(seq uint64, digest common.Digest) (common.VerifiedBlock, *common.Finalization, error) {
cs.lock.RLock()
item, exists := cs.cache[digest]
if !exists && digest == (common.Digest{}) {
// Seq-only lookups pass a zero digest, so scan the cache by seq.
// Otherwise a verified but not yet finalized block is invisible to them.
for _, cb := range cs.cache {
if cb.Metadata.SimplexProtocolMetadata.Seq == seq {
item, exists = cb, true
break
}
}
}
if exists {
cs.lock.RUnlock()
// If the block is cached, it means it's not finalized yet, because upon finalizing the block (indexing)
Expand Down Expand Up @@ -217,6 +236,7 @@ func (bw *BlockBuilderWaiter) WaitForPendingBlock(ctx context.Context) {
func (bw *BlockBuilderWaiter) BuildBlock(ctx context.Context, metadata common.ProtocolMetadata, blacklist common.Blacklist) (common.VerifiedBlock, bool) {
block, err := bw.msm.BuildBlock(ctx, metadata, blacklist)
if err != nil {
fmt.Println("what 22 ", err)
return nil, false
}

Expand All @@ -229,17 +249,17 @@ func (bw *BlockBuilderWaiter) BuildBlock(ctx context.Context, metadata common.Pr
}

type blockDeserializer struct {
vm VM
msm *metadata.StateMachine
deserializer BlockDeserializer
msm *metadata.StateMachine
}

func (bp *blockDeserializer) DeserializeBlock(ctx context.Context, bytes []byte) (common.Block, error) {
func (bd *blockDeserializer) DeserializeBlock(ctx context.Context, bytes []byte) (common.Block, error) {
var rawBlock metadata.RawBlock
if err := rawBlock.UnmarshalCanoto(bytes); err != nil {
return nil, err
}

block, err := bp.vm.ParseBlock(ctx, rawBlock.InnerBlockBytes)
block, err := bd.deserializer.ParseBlock(ctx, rawBlock.InnerBlockBytes)
if err != nil {
return nil, err
}
Expand All @@ -248,6 +268,6 @@ func (bp *blockDeserializer) DeserializeBlock(ctx context.Context, bytes []byte)
InnerBlock: block,
Metadata: rawBlock.Metadata,
},
msm: bp.msm,
msm: bd.msm,
}, nil
}
176 changes: 176 additions & 0 deletions bootstrap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package simplex

import (
"fmt"
"sync"
"testing"
"time"

"github.com/ava-labs/simplex/common"
metadata "github.com/ava-labs/simplex/msm"
"github.com/stretchr/testify/require"
)

func TestValidatorIndexes(t *testing.T) {
validatorID := generateNodeIDMapping()

genesisSet := []metadata.NodeBLSMapping{validatorID}

pChain := newTestPChain(genesisSet)
chain := newChain(t, pChain)
chain.addNode(validatorID.NodeID[:])

_, err := chain.index()
require.NoError(t, err)
}

func TestNonValidatorSyncs(t *testing.T) {
validatorID := generateNodeIDMapping()
nonValidatorID := generateNodeIDMapping()

genesisSet := []metadata.NodeBLSMapping{validatorID}

pChain := newTestPChain(genesisSet)
chain := newChain(t, pChain)
chain.addNode(validatorID.NodeID[:])

_, err := chain.index()
require.NoError(t, err)
_, err = chain.index()
require.NoError(t, err)

chain.addNode(nonValidatorID.NodeID[:])
}

func TestNonValidator_BecomesValidator(t *testing.T) {
validatorID := generateNodeIDMapping()
upcomingValidator := generateNodeIDMapping()

genesisSet := []metadata.NodeBLSMapping{validatorID}

pChain := newTestPChain(genesisSet)
chain := newChain(t, pChain)
chain.addNode(validatorID.NodeID[:])

_, err := chain.index()
require.NoError(t, err)
_, err = chain.index()
require.NoError(t, err)

chain.addNode(upcomingValidator.NodeID[:])

fmt.Println("advancing height")
// initiate an epoch change
pChain.setValidatorSetAt(10, []metadata.NodeBLSMapping{validatorID, upcomingValidator})
pChain.advanceHeight(10)

// now that we advanced the height the validator will keep building empty blocks until the upcoming validator sends an approval
time.Sleep(5 * time.Second)
}

func TestValidator_ValidatorSetNotChanged(t *testing.T) {
validatorID := generateNodeIDMapping()

genesisSet := []metadata.NodeBLSMapping{validatorID}

pChain := newTestPChain(genesisSet)
chain := newChain(t, pChain)
chain.addNode(validatorID.NodeID[:])

_, err := chain.index()
require.NoError(t, err)

// initiate an epoch change
pChain.setValidatorSetAt(10, []metadata.NodeBLSMapping{validatorID})
pChain.advanceHeight(10)

// potential time to propose blocks
time.Sleep(3 * time.Second)

block, err := chain.index()
require.NoError(t, err)
require.Equal(t, uint64(1), block.BlockHeader().Epoch)
}

func TestValidator_ValidatorSetDecreased(t *testing.T) {
validatorID := generateNodeIDMapping([20]byte{1})
leavingValidatorID := generateNodeIDMapping([20]byte{2})

genesisSet := []metadata.NodeBLSMapping{validatorID, leavingValidatorID}

pChain := newTestPChain(genesisSet)
chain := newChain(t, pChain)
wg := sync.WaitGroup{}

wg.Add(1)
go func() {
chain.addNode(validatorID.NodeID[:])
wg.Done()
}()
chain.addNode(leavingValidatorID.NodeID[:])

// all nodes have synced the first every simplex block
wg.Wait()

// time.Sleep(1 * time.Second)
block, err := chain.index()
require.NoError(t, err)
require.Equal(t, uint64(2), block.BlockHeader().Round)

// initiate an epoch change
pChain.setValidatorSetAt(10, []metadata.NodeBLSMapping{validatorID})
pChain.advanceHeight(10)

sealing := chain.waitUntilSealingBlock()
require.Contains(t, sealing.SealingBlockInfo().ValidatorSet.NodeIDs(), common.NodeID(validatorID.NodeID[:]))
require.NotContains(t, sealing.SealingBlockInfo().ValidatorSet.NodeIDs(), common.NodeID(leavingValidatorID.NodeID[:]))
}

// Tests a non-validator converts to a validator when the epoch admitting it is committed,
// proven by the finalization of the next block carrying both nodes' signatures.
// func TestNonValidatorJoins(t *testing.T) {
// chain := newChain(t)

// // The initial validator set is just currentValidator, so futureValidator comes up as a
// // non-validator that tracks the chain.
// currentValidator := chain.newNode(nodeConfig{Name: "current-validator", Validator: true})
// futureValidator := chain.newNode(nodeConfig{Name: "future-validator"})

// chain.AddNodes(currentValidator, futureValidator)

// chain.IndexBlock()

// // The new validator set is current + future.
// chain.IndexSealing(currentValidator, futureValidator)

// block := chain.IndexBlock()

// // A quorum of the new epoch needs both nodes, so both signing the finalization proves the
// // joined node takes part in consensus rather than merely tracking the chain.
// chain.RequireFinalizedBy(block, currentValidator, futureValidator)
// }

// // Tests that a non-validator joining a chain that has already sealed an epoch syncs across
// // every epoch up to the tip.
// func TestNonValidatorSyncs(t *testing.T) {
// chain := newChain(t, chainConfig{})

// // The initial validator set is just currentValidator.
// currentValidator := chain.newNode(nodeConfig{Name: "current-validator", Validator: true})
// syncingNonValidator := chain.newNode(nodeConfig{Name: "syncing-non-validator"})

// chain.AddNodes(currentValidator)

// chain.IndexBlock()
// chain.IndexBlock()
// chain.IndexSealing(currentValidator)
// latestBlock := chain.IndexBlock()

// chain.AddNodes(syncingNonValidator)

// syncingNonValidator.WaitForCommit(latestBlock)
// syncingNonValidator.RequireNonValidator()
// }
5 changes: 2 additions & 3 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,11 @@ type VM interface {
// WaitForPendingBlock returns when either the given context is cancelled,
// or when the VM signals that a block should be built.
WaitForPendingBlock(ctx context.Context)
}

type BlockDeserializer interface {
// ParseBlock parses the given block bytes into a VMBlock.
ParseBlock(context.Context, []byte) (avalanchego.VMBlock, error)

// ComputeICMEpoch computes the ICM epoch transition given the input parameters.
ComputeICMEpoch(input metadata.ICMEpochInput) metadata.ICMEpochInfo
}

type Storage interface {
Expand Down
Loading