// (c) 2019-2020, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package snowman import ( "errors" "math/rand" "testing" "github.com/prometheus/client_golang/prometheus" "github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/snow" "github.com/ava-labs/gecko/snow/choices" "github.com/ava-labs/gecko/snow/consensus/snowball" ) var ( GenesisID = ids.Empty.Prefix(0) Genesis = &TestBlock{ id: GenesisID, status: choices.Accepted, } Tests = []func(*testing.T, Factory){ InitializeTest, AddToTailTest, AddToNonTailTest, AddToUnknownTest, IssuedPreviouslyAcceptedTest, IssuedPreviouslyRejectedTest, IssuedUnissuedTest, IssuedIssuedTest, RecordPollAcceptSingleBlockTest, RecordPollAcceptAndRejectTest, RecordPollWhenFinalizedTest, RecordPollRejectTransitivelyTest, RecordPollTransitivelyResetConfidenceTest, RecordPollInvalidVoteTest, RecordPollTransitiveVotingTest, RecordPollDivergedVotingTest, MetricsProcessingErrorTest, MetricsAcceptedErrorTest, MetricsRejectedErrorTest, ErrorOnInitialRejectionTest, ErrorOnAcceptTest, ErrorOnRejectSiblingTest, ErrorOnTransitiveRejectionTest, RandomizedConsistencyTest, } ) // Execute all tests against a consensus implementation func ConsensusTest(t *testing.T, factory Factory) { for _, test := range Tests { test(t, factory) } } // Make sure that initialize sets the state correctly func InitializeTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 3, BetaRogue: 5, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) if p := sm.Parameters(); p != params { t.Fatalf("Wrong returned parameters") } else if pref := sm.Preference(); !pref.Equals(GenesisID) { t.Fatalf("Wrong preference returned") } else if !sm.Finalized() { t.Fatalf("Wrong should have marked the instance as being finalized") } } // Make sure that adding a block to the tail updates the preference func AddToTailTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 3, BetaRogue: 5, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) block := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), } // Adding to the previous preference will update the preference if err := sm.Add(block); err != nil { t.Fatal(err) } else if pref := sm.Preference(); !pref.Equals(block.id) { t.Fatalf("Wrong preference. Expected %s, got %s", block.id, pref) } } // Make sure that adding a block not to the tail doesn't change the preference func AddToNonTailTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 3, BetaRogue: 5, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) firstBlock := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), } secondBlock := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(2), } // Adding to the previous preference will update the preference if err := sm.Add(firstBlock); err != nil { t.Fatal(err) } else if pref := sm.Preference(); !pref.Equals(firstBlock.id) { t.Fatalf("Wrong preference. Expected %s, got %s", firstBlock.id, pref) } // Adding to something other than the previous preference won't update the // preference if err := sm.Add(secondBlock); err != nil { t.Fatal(err) } else if pref := sm.Preference(); !pref.Equals(firstBlock.id) { t.Fatalf("Wrong preference. Expected %s, got %s", firstBlock.id, pref) } } // Make sure that adding a block that is detached from the rest of the tree // rejects the block func AddToUnknownTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 3, BetaRogue: 5, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) block := &TestBlock{ parent: &TestBlock{id: ids.Empty.Prefix(1)}, id: ids.Empty.Prefix(2), } // Adding a block with an unknown parent means the parent must have already // been rejected. Therefore the block should be immediately rejected if err := sm.Add(block); err != nil { t.Fatal(err) } else if pref := sm.Preference(); !pref.Equals(GenesisID) { t.Fatalf("Wrong preference. Expected %s, got %s", GenesisID, pref) } else if status := block.Status(); status != choices.Rejected { t.Fatalf("Should have rejected the block") } } func IssuedPreviouslyAcceptedTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 3, BetaRogue: 5, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) if !sm.Issued(Genesis) { t.Fatalf("Should have marked an accepted block as having been issued") } } func IssuedPreviouslyRejectedTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 3, BetaRogue: 5, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) block := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), status: choices.Rejected, } if !sm.Issued(block) { t.Fatalf("Should have marked a rejected block as having been issued") } } func IssuedUnissuedTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 3, BetaRogue: 5, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) block := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), status: choices.Processing, } if sm.Issued(block) { t.Fatalf("Shouldn't have marked an unissued block as having been issued") } } func IssuedIssuedTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 3, BetaRogue: 5, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) block := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), status: choices.Processing, } if err := sm.Add(block); err != nil { t.Fatal(err) } else if !sm.Issued(block) { t.Fatalf("Should have marked a pending block as having been issued") } } func RecordPollAcceptSingleBlockTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 2, BetaRogue: 3, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) block := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), status: choices.Processing, } if err := sm.Add(block); err != nil { t.Fatal(err) } votes := ids.Bag{} votes.Add(block.id) if err := sm.RecordPoll(votes); err != nil { t.Fatal(err) } else if pref := sm.Preference(); !pref.Equals(block.id) { t.Fatalf("Preference returned the wrong block") } else if sm.Finalized() { t.Fatalf("Snowman instance finalized too soon") } else if status := block.Status(); status != choices.Processing { t.Fatalf("Block's status changed unexpectedly") } else if err := sm.RecordPoll(votes); err != nil { t.Fatal(err) } else if pref := sm.Preference(); !pref.Equals(block.id) { t.Fatalf("Preference returned the wrong block") } else if !sm.Finalized() { t.Fatalf("Snowman instance didn't finalize") } else if status := block.Status(); status != choices.Accepted { t.Fatalf("Block's status should have been set to accepted") } } func RecordPollAcceptAndRejectTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 2, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) firstBlock := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), status: choices.Processing, } secondBlock := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(2), status: choices.Processing, } if err := sm.Add(firstBlock); err != nil { t.Fatal(err) } else if err := sm.Add(secondBlock); err != nil { t.Fatal(err) } votes := ids.Bag{} votes.Add(firstBlock.id) if err := sm.RecordPoll(votes); err != nil { t.Fatal(err) } else if pref := sm.Preference(); !pref.Equals(firstBlock.id) { t.Fatalf("Preference returned the wrong block") } else if sm.Finalized() { t.Fatalf("Snowman instance finalized too soon") } else if status := firstBlock.Status(); status != choices.Processing { t.Fatalf("Block's status changed unexpectedly") } else if status := secondBlock.Status(); status != choices.Processing { t.Fatalf("Block's status changed unexpectedly") } else if err := sm.RecordPoll(votes); err != nil { t.Fatal(err) } else if pref := sm.Preference(); !pref.Equals(firstBlock.id) { t.Fatalf("Preference returned the wrong block") } else if !sm.Finalized() { t.Fatalf("Snowman instance didn't finalize") } else if status := firstBlock.Status(); status != choices.Accepted { t.Fatalf("Block's status should have been set to accepted") } else if status := secondBlock.Status(); status != choices.Rejected { t.Fatalf("Block's status should have been set to rejected") } } func RecordPollWhenFinalizedTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 2, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) votes := ids.Bag{} votes.Add(GenesisID) if err := sm.RecordPoll(votes); err != nil { t.Fatal(err) } else if !sm.Finalized() { t.Fatalf("Consensus should still be finalized") } else if pref := sm.Preference(); !GenesisID.Equals(pref) { t.Fatalf("Wrong preference listed") } } func RecordPollRejectTransitivelyTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 1, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) block0 := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), status: choices.Processing, } block1 := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(2), status: choices.Processing, } block2 := &TestBlock{ parent: block1, id: ids.Empty.Prefix(3), status: choices.Processing, } if err := sm.Add(block0); err != nil { t.Fatal(err) } else if err := sm.Add(block1); err != nil { t.Fatal(err) } else if err := sm.Add(block2); err != nil { t.Fatal(err) } // Current graph structure: // G // / \ // 0 1 // | // 2 // Tail = 0 votes := ids.Bag{} votes.Add(block0.id) if err := sm.RecordPoll(votes); err != nil { t.Fatal(err) } // Current graph structure: // 0 // Tail = 0 if !sm.Finalized() { t.Fatalf("Finalized too late") } else if pref := sm.Preference(); !block0.id.Equals(pref) { t.Fatalf("Wrong preference listed") } else if status := block0.Status(); status != choices.Accepted { t.Fatalf("Wrong status returned") } else if status := block1.Status(); status != choices.Rejected { t.Fatalf("Wrong status returned") } else if status := block2.Status(); status != choices.Rejected { t.Fatalf("Wrong status returned") } } func RecordPollTransitivelyResetConfidenceTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 2, BetaRogue: 2, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) block0 := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), status: choices.Processing, } block1 := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(2), status: choices.Processing, } block2 := &TestBlock{ parent: block1, id: ids.Empty.Prefix(3), status: choices.Processing, } block3 := &TestBlock{ parent: block1, id: ids.Empty.Prefix(4), status: choices.Processing, } if err := sm.Add(block0); err != nil { t.Fatal(err) } else if err := sm.Add(block1); err != nil { t.Fatal(err) } else if err := sm.Add(block2); err != nil { t.Fatal(err) } else if err := sm.Add(block3); err != nil { t.Fatal(err) } // Current graph structure: // G // / \ // 0 1 // / \ // 2 3 votesFor2 := ids.Bag{} votesFor2.Add(block2.id) if err := sm.RecordPoll(votesFor2); err != nil { t.Fatal(err) } else if sm.Finalized() { t.Fatalf("Finalized too early") } else if pref := sm.Preference(); !block2.id.Equals(pref) { t.Fatalf("Wrong preference listed") } emptyVotes := ids.Bag{} if err := sm.RecordPoll(emptyVotes); err != nil { t.Fatal(err) } else if sm.Finalized() { t.Fatalf("Finalized too early") } else if pref := sm.Preference(); !block2.id.Equals(pref) { t.Fatalf("Wrong preference listed") } else if err := sm.RecordPoll(votesFor2); err != nil { t.Fatal(err) } else if sm.Finalized() { t.Fatalf("Finalized too early") } else if pref := sm.Preference(); !block2.id.Equals(pref) { t.Fatalf("Wrong preference listed") } votesFor3 := ids.Bag{} votesFor3.Add(block3.id) if err := sm.RecordPoll(votesFor3); err != nil { t.Fatal(err) } else if sm.Finalized() { t.Fatalf("Finalized too early") } else if pref := sm.Preference(); !block2.id.Equals(pref) { t.Fatalf("Wrong preference listed") } else if err := sm.RecordPoll(votesFor3); err != nil { t.Fatal(err) } else if !sm.Finalized() { t.Fatalf("Finalized too late") } else if pref := sm.Preference(); !block3.id.Equals(pref) { t.Fatalf("Wrong preference listed") } else if status := block0.Status(); status != choices.Rejected { t.Fatalf("Wrong status returned") } else if status := block1.Status(); status != choices.Accepted { t.Fatalf("Wrong status returned") } else if status := block2.Status(); status != choices.Rejected { t.Fatalf("Wrong status returned") } else if status := block3.Status(); status != choices.Accepted { t.Fatalf("Wrong status returned") } } func RecordPollInvalidVoteTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 2, BetaRogue: 2, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) block := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), status: choices.Processing, } unknownBlockID := ids.Empty.Prefix(2) if err := sm.Add(block); err != nil { t.Fatal(err) } validVotes := ids.Bag{} validVotes.Add(block.id) if err := sm.RecordPoll(validVotes); err != nil { t.Fatal(err) } invalidVotes := ids.Bag{} invalidVotes.Add(unknownBlockID) if err := sm.RecordPoll(invalidVotes); err != nil { t.Fatal(err) } else if err := sm.RecordPoll(validVotes); err != nil { t.Fatal(err) } else if sm.Finalized() { t.Fatalf("Finalized too early") } else if pref := sm.Preference(); !block.id.Equals(pref) { t.Fatalf("Wrong preference listed") } } func RecordPollTransitiveVotingTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 3, Alpha: 3, BetaVirtuous: 1, BetaRogue: 1, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) block0 := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), status: choices.Processing, } block1 := &TestBlock{ parent: block0, id: ids.Empty.Prefix(2), status: choices.Processing, } block2 := &TestBlock{ parent: block1, id: ids.Empty.Prefix(3), status: choices.Processing, } block3 := &TestBlock{ parent: block0, id: ids.Empty.Prefix(4), status: choices.Processing, } block4 := &TestBlock{ parent: block3, id: ids.Empty.Prefix(5), status: choices.Processing, } if err := sm.Add(block0); err != nil { t.Fatal(err) } else if err := sm.Add(block1); err != nil { t.Fatal(err) } else if err := sm.Add(block2); err != nil { t.Fatal(err) } else if err := sm.Add(block3); err != nil { t.Fatal(err) } else if err := sm.Add(block4); err != nil { t.Fatal(err) } // Current graph structure: // G // | // 0 // / \ // 1 3 // | | // 2 4 // Tail = 2 votes0_2_4 := ids.Bag{} votes0_2_4.Add( block0.id, block2.id, block4.id, ) if err := sm.RecordPoll(votes0_2_4); err != nil { t.Fatal(err) } // Current graph structure: // 0 // / \ // 1 3 // | | // 2 4 // Tail = 2 if pref := sm.Preference(); !block2.id.Equals(pref) { t.Fatalf("Wrong preference listed") } else if sm.Finalized() { t.Fatalf("Finalized too early") } else if block0.Status() != choices.Accepted { t.Fatalf("Should have accepted") } else if block1.Status() != choices.Processing { t.Fatalf("Should have accepted") } else if block2.Status() != choices.Processing { t.Fatalf("Should have accepted") } else if block3.Status() != choices.Processing { t.Fatalf("Should have rejected") } else if block4.Status() != choices.Processing { t.Fatalf("Should have rejected") } dep2_2_2 := ids.Bag{} dep2_2_2.AddCount(block2.id, 3) if err := sm.RecordPoll(dep2_2_2); err != nil { t.Fatal(err) } // Current graph structure: // 2 // Tail = 2 if pref := sm.Preference(); !block2.id.Equals(pref) { t.Fatalf("Wrong preference listed") } else if !sm.Finalized() { t.Fatalf("Finalized too late") } else if block0.Status() != choices.Accepted { t.Fatalf("Should have accepted") } else if block1.Status() != choices.Accepted { t.Fatalf("Should have accepted") } else if block2.Status() != choices.Accepted { t.Fatalf("Should have accepted") } else if block3.Status() != choices.Rejected { t.Fatalf("Should have rejected") } else if block4.Status() != choices.Rejected { t.Fatalf("Should have rejected") } } func RecordPollDivergedVotingTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 2, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) block0 := &TestBlock{ parent: Genesis, id: ids.NewID([32]byte{0x0f}), // 0b1111 status: choices.Processing, } block1 := &TestBlock{ parent: Genesis, id: ids.NewID([32]byte{0x08}), // 0b1000 status: choices.Processing, } block2 := &TestBlock{ parent: Genesis, id: ids.NewID([32]byte{0x01}), // 0b0001 status: choices.Processing, } block3 := &TestBlock{ parent: block2, id: ids.Empty.Prefix(1), status: choices.Processing, } if err := sm.Add(block0); err != nil { t.Fatal(err) } else if err := sm.Add(block1); err != nil { t.Fatal(err) } votes0 := ids.Bag{} votes0.Add(block0.id) if err := sm.RecordPoll(votes0); err != nil { t.Fatal(err) } else if err := sm.Add(block2); err != nil { t.Fatal(err) } // dep2 is already rejected. if err := sm.Add(block3); err != nil { t.Fatal(err) } else if status := block0.Status(); status == choices.Accepted { t.Fatalf("Shouldn't be accepted yet") } // Transitively increases dep2. However, dep2 shares the first bit with // dep0. Because dep2 is already rejected, this will accept dep0. votes3 := ids.Bag{} votes3.Add(block3.id) if err := sm.RecordPoll(votes3); err != nil { t.Fatal(err) } else if !sm.Finalized() { t.Fatalf("Finalized too late") } else if status := block0.Status(); status != choices.Accepted { t.Fatalf("Should be accepted") } } func MetricsProcessingErrorTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 1, ConcurrentRepolls: 1, } numProcessing := prometheus.NewGauge( prometheus.GaugeOpts{ Namespace: params.Namespace, Name: "processing", }) if err := params.Metrics.Register(numProcessing); err != nil { t.Fatal(err) } sm.Initialize(ctx, params, GenesisID) block := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), status: choices.Processing, } if err := sm.Add(block); err != nil { t.Fatal(err) } votes := ids.Bag{} votes.Add(block.id) if err := sm.RecordPoll(votes); err != nil { t.Fatal(err) } else if !sm.Finalized() { t.Fatalf("Snowman instance didn't finalize") } } func MetricsAcceptedErrorTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 1, ConcurrentRepolls: 1, } numAccepted := prometheus.NewGauge( prometheus.GaugeOpts{ Namespace: params.Namespace, Name: "accepted", }) if err := params.Metrics.Register(numAccepted); err != nil { t.Fatal(err) } sm.Initialize(ctx, params, GenesisID) block := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), status: choices.Processing, } if err := sm.Add(block); err != nil { t.Fatal(err) } votes := ids.Bag{} votes.Add(block.id) if err := sm.RecordPoll(votes); err != nil { t.Fatal(err) } else if !sm.Finalized() { t.Fatalf("Snowman instance didn't finalize") } } func MetricsRejectedErrorTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 1, ConcurrentRepolls: 1, } numRejected := prometheus.NewGauge( prometheus.GaugeOpts{ Namespace: params.Namespace, Name: "rejected", }) if err := params.Metrics.Register(numRejected); err != nil { t.Fatal(err) } sm.Initialize(ctx, params, GenesisID) block := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), status: choices.Processing, } if err := sm.Add(block); err != nil { t.Fatal(err) } votes := ids.Bag{} votes.Add(block.id) if err := sm.RecordPoll(votes); err != nil { t.Fatal(err) } else if !sm.Finalized() { t.Fatalf("Snowman instance didn't finalize") } } func ErrorOnInitialRejectionTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 1, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) rejectedBlock := &TestBlock{ id: ids.Empty.Prefix(1), status: choices.Rejected, } block := &TestBlock{ parent: rejectedBlock, id: ids.Empty.Prefix(2), status: choices.Processing, err: errors.New(""), } if err := sm.Add(block); err == nil { t.Fatalf("Should have errored on rejecting the rejectable block") } } func ErrorOnAcceptTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 1, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) block := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), status: choices.Processing, err: errors.New(""), } if err := sm.Add(block); err != nil { t.Fatal(err) } votes := ids.Bag{} votes.Add(block.id) if err := sm.RecordPoll(votes); err == nil { t.Fatalf("Should have errored on accepted the block") } } func ErrorOnRejectSiblingTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 1, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) block0 := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), status: choices.Processing, } block1 := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(2), status: choices.Processing, err: errors.New(""), } if err := sm.Add(block0); err != nil { t.Fatal(err) } else if err := sm.Add(block1); err != nil { t.Fatal(err) } votes := ids.Bag{} votes.Add(block0.id) if err := sm.RecordPoll(votes); err == nil { t.Fatalf("Should have errored on rejecting the block's sibling") } } func ErrorOnTransitiveRejectionTest(t *testing.T, factory Factory) { sm := factory.New() ctx := snow.DefaultContextTest() params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 1, Alpha: 1, BetaVirtuous: 1, BetaRogue: 1, ConcurrentRepolls: 1, } sm.Initialize(ctx, params, GenesisID) block0 := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(1), status: choices.Processing, } block1 := &TestBlock{ parent: Genesis, id: ids.Empty.Prefix(2), status: choices.Processing, } block2 := &TestBlock{ parent: block1, id: ids.Empty.Prefix(3), status: choices.Processing, err: errors.New(""), } if err := sm.Add(block0); err != nil { t.Fatal(err) } else if err := sm.Add(block1); err != nil { t.Fatal(err) } else if err := sm.Add(block2); err != nil { t.Fatal(err) } votes := ids.Bag{} votes.Add(block0.id) if err := sm.RecordPoll(votes); err == nil { t.Fatalf("Should have errored on transitively rejecting the block") } } func RandomizedConsistencyTest(t *testing.T, factory Factory) { numColors := 50 numNodes := 100 params := snowball.Parameters{ Metrics: prometheus.NewRegistry(), K: 20, Alpha: 15, BetaVirtuous: 20, BetaRogue: 30, } seed := int64(0) rand.Seed(seed) n := Network{} n.Initialize(params, numColors) for i := 0; i < numNodes; i++ { n.AddNode(factory.New()) } for !n.Finalized() { n.Round() } if !n.Agreement() { t.Fatalf("Network agreed on inconsistent values") } }