github.com/MetalBlockchain/metalgo@v1.11.9/snow/consensus/snowman/consensus_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package snowman
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"path"
    10  	"reflect"
    11  	"runtime"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/prometheus/client_golang/prometheus"
    16  	"github.com/stretchr/testify/require"
    17  	"gonum.org/v1/gonum/mathext/prng"
    18  
    19  	"github.com/MetalBlockchain/metalgo/ids"
    20  	"github.com/MetalBlockchain/metalgo/snow/choices"
    21  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowball"
    22  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowman/snowmantest"
    23  	"github.com/MetalBlockchain/metalgo/snow/snowtest"
    24  	"github.com/MetalBlockchain/metalgo/utils/bag"
    25  )
    26  
    27  type testFunc func(*testing.T, Factory)
    28  
    29  var (
    30  	testFuncs = []testFunc{
    31  		InitializeTest,
    32  		NumProcessingTest,
    33  		AddToTailTest,
    34  		AddToNonTailTest,
    35  		AddOnUnknownParentTest,
    36  		StatusOrProcessingPreviouslyAcceptedTest,
    37  		StatusOrProcessingPreviouslyRejectedTest,
    38  		StatusOrProcessingUnissuedTest,
    39  		StatusOrProcessingIssuedTest,
    40  		RecordPollAcceptSingleBlockTest,
    41  		RecordPollAcceptAndRejectTest,
    42  		RecordPollSplitVoteNoChangeTest,
    43  		RecordPollWhenFinalizedTest,
    44  		RecordPollRejectTransitivelyTest,
    45  		RecordPollTransitivelyResetConfidenceTest,
    46  		RecordPollInvalidVoteTest,
    47  		RecordPollTransitiveVotingTest,
    48  		RecordPollDivergedVotingWithNoConflictingBitTest,
    49  		RecordPollChangePreferredChainTest,
    50  		LastAcceptedTest,
    51  		MetricsProcessingErrorTest,
    52  		MetricsAcceptedErrorTest,
    53  		MetricsRejectedErrorTest,
    54  		ErrorOnAcceptTest,
    55  		ErrorOnRejectSiblingTest,
    56  		ErrorOnTransitiveRejectionTest,
    57  		RandomizedConsistencyTest,
    58  		ErrorOnAddDecidedBlockTest,
    59  		RecordPollWithDefaultParameters,
    60  		RecordPollRegressionCalculateInDegreeIndegreeCalculation,
    61  	}
    62  
    63  	errTest = errors.New("non-nil error")
    64  )
    65  
    66  // Execute all tests against a consensus implementation
    67  func runConsensusTests(t *testing.T, factory Factory) {
    68  	for _, test := range testFuncs {
    69  		t.Run(getTestName(test), func(tt *testing.T) {
    70  			test(tt, factory)
    71  		})
    72  	}
    73  }
    74  
    75  func getTestName(i interface{}) string {
    76  	return strings.Split(path.Base(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()), ".")[1]
    77  }
    78  
    79  // Make sure that initialize sets the state correctly
    80  func InitializeTest(t *testing.T, factory Factory) {
    81  	require := require.New(t)
    82  
    83  	sm := factory.New()
    84  
    85  	snowCtx := snowtest.Context(t, snowtest.CChainID)
    86  	ctx := snowtest.ConsensusContext(snowCtx)
    87  	params := snowball.Parameters{
    88  		K:                     1,
    89  		AlphaPreference:       1,
    90  		AlphaConfidence:       1,
    91  		Beta:                  3,
    92  		ConcurrentRepolls:     1,
    93  		OptimalProcessing:     1,
    94  		MaxOutstandingItems:   1,
    95  		MaxItemProcessingTime: 1,
    96  	}
    97  
    98  	require.NoError(sm.Initialize(
    99  		ctx,
   100  		params,
   101  		snowmantest.GenesisID,
   102  		snowmantest.GenesisHeight,
   103  		snowmantest.GenesisTimestamp,
   104  	))
   105  
   106  	require.Equal(snowmantest.GenesisID, sm.Preference())
   107  	require.Zero(sm.NumProcessing())
   108  }
   109  
   110  // Make sure that the number of processing blocks is tracked correctly
   111  func NumProcessingTest(t *testing.T, factory Factory) {
   112  	require := require.New(t)
   113  
   114  	sm := factory.New()
   115  
   116  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   117  	ctx := snowtest.ConsensusContext(snowCtx)
   118  	params := snowball.Parameters{
   119  		K:                     1,
   120  		AlphaPreference:       1,
   121  		AlphaConfidence:       1,
   122  		Beta:                  1,
   123  		ConcurrentRepolls:     1,
   124  		OptimalProcessing:     1,
   125  		MaxOutstandingItems:   1,
   126  		MaxItemProcessingTime: 1,
   127  	}
   128  	require.NoError(sm.Initialize(
   129  		ctx,
   130  		params,
   131  		snowmantest.GenesisID,
   132  		snowmantest.GenesisHeight,
   133  		snowmantest.GenesisTimestamp,
   134  	))
   135  
   136  	block := snowmantest.BuildChild(snowmantest.Genesis)
   137  
   138  	require.Zero(sm.NumProcessing())
   139  
   140  	// Adding to the previous preference will update the preference
   141  	require.NoError(sm.Add(block))
   142  	require.Equal(1, sm.NumProcessing())
   143  
   144  	votes := bag.Of(block.ID())
   145  	require.NoError(sm.RecordPoll(context.Background(), votes))
   146  	require.Zero(sm.NumProcessing())
   147  }
   148  
   149  // Make sure that adding a block to the tail updates the preference
   150  func AddToTailTest(t *testing.T, factory Factory) {
   151  	require := require.New(t)
   152  
   153  	sm := factory.New()
   154  
   155  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   156  	ctx := snowtest.ConsensusContext(snowCtx)
   157  	params := snowball.Parameters{
   158  		K:                     1,
   159  		AlphaPreference:       1,
   160  		AlphaConfidence:       1,
   161  		Beta:                  3,
   162  		ConcurrentRepolls:     1,
   163  		OptimalProcessing:     1,
   164  		MaxOutstandingItems:   1,
   165  		MaxItemProcessingTime: 1,
   166  	}
   167  	require.NoError(sm.Initialize(
   168  		ctx,
   169  		params,
   170  		snowmantest.GenesisID,
   171  		snowmantest.GenesisHeight,
   172  		snowmantest.GenesisTimestamp,
   173  	))
   174  
   175  	block := snowmantest.BuildChild(snowmantest.Genesis)
   176  
   177  	// Adding to the previous preference will update the preference
   178  	require.NoError(sm.Add(block))
   179  	require.Equal(block.ID(), sm.Preference())
   180  	require.True(sm.IsPreferred(block.ID()))
   181  
   182  	pref, ok := sm.PreferenceAtHeight(block.Height())
   183  	require.True(ok)
   184  	require.Equal(block.ID(), pref)
   185  }
   186  
   187  // Make sure that adding a block not to the tail doesn't change the preference
   188  func AddToNonTailTest(t *testing.T, factory Factory) {
   189  	require := require.New(t)
   190  
   191  	sm := factory.New()
   192  
   193  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   194  	ctx := snowtest.ConsensusContext(snowCtx)
   195  	params := snowball.Parameters{
   196  		K:                     1,
   197  		AlphaPreference:       1,
   198  		AlphaConfidence:       1,
   199  		Beta:                  3,
   200  		ConcurrentRepolls:     1,
   201  		OptimalProcessing:     1,
   202  		MaxOutstandingItems:   1,
   203  		MaxItemProcessingTime: 1,
   204  	}
   205  	require.NoError(sm.Initialize(
   206  		ctx,
   207  		params,
   208  		snowmantest.GenesisID,
   209  		snowmantest.GenesisHeight,
   210  		snowmantest.GenesisTimestamp,
   211  	))
   212  
   213  	firstBlock := snowmantest.BuildChild(snowmantest.Genesis)
   214  	secondBlock := snowmantest.BuildChild(snowmantest.Genesis)
   215  
   216  	// Adding to the previous preference will update the preference
   217  	require.NoError(sm.Add(firstBlock))
   218  	require.Equal(firstBlock.IDV, sm.Preference())
   219  
   220  	// Adding to something other than the previous preference won't update the
   221  	// preference
   222  	require.NoError(sm.Add(secondBlock))
   223  	require.Equal(firstBlock.IDV, sm.Preference())
   224  }
   225  
   226  // Make sure that adding a block that is detached from the rest of the tree
   227  // returns an error
   228  func AddOnUnknownParentTest(t *testing.T, factory Factory) {
   229  	require := require.New(t)
   230  
   231  	sm := factory.New()
   232  
   233  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   234  	ctx := snowtest.ConsensusContext(snowCtx)
   235  	params := snowball.Parameters{
   236  		K:                     1,
   237  		AlphaPreference:       1,
   238  		AlphaConfidence:       1,
   239  		Beta:                  3,
   240  		ConcurrentRepolls:     1,
   241  		OptimalProcessing:     1,
   242  		MaxOutstandingItems:   1,
   243  		MaxItemProcessingTime: 1,
   244  	}
   245  	require.NoError(sm.Initialize(
   246  		ctx,
   247  		params,
   248  		snowmantest.GenesisID,
   249  		snowmantest.GenesisHeight,
   250  		snowmantest.GenesisTimestamp,
   251  	))
   252  
   253  	block := &snowmantest.Block{
   254  		TestDecidable: choices.TestDecidable{
   255  			IDV:     ids.GenerateTestID(),
   256  			StatusV: choices.Processing,
   257  		},
   258  		ParentV: ids.GenerateTestID(),
   259  		HeightV: snowmantest.GenesisHeight + 2,
   260  	}
   261  
   262  	// Adding a block with an unknown parent should error.
   263  	err := sm.Add(block)
   264  	require.ErrorIs(err, errUnknownParentBlock)
   265  }
   266  
   267  func StatusOrProcessingPreviouslyAcceptedTest(t *testing.T, factory Factory) {
   268  	require := require.New(t)
   269  
   270  	sm := factory.New()
   271  
   272  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   273  	ctx := snowtest.ConsensusContext(snowCtx)
   274  	params := snowball.Parameters{
   275  		K:                     1,
   276  		AlphaPreference:       1,
   277  		AlphaConfidence:       1,
   278  		Beta:                  3,
   279  		ConcurrentRepolls:     1,
   280  		OptimalProcessing:     1,
   281  		MaxOutstandingItems:   1,
   282  		MaxItemProcessingTime: 1,
   283  	}
   284  	require.NoError(sm.Initialize(
   285  		ctx,
   286  		params,
   287  		snowmantest.GenesisID,
   288  		snowmantest.GenesisHeight,
   289  		snowmantest.GenesisTimestamp,
   290  	))
   291  
   292  	require.Equal(choices.Accepted, snowmantest.Genesis.Status())
   293  	require.False(sm.Processing(snowmantest.Genesis.ID()))
   294  	require.True(sm.IsPreferred(snowmantest.Genesis.ID()))
   295  
   296  	pref, ok := sm.PreferenceAtHeight(snowmantest.Genesis.Height())
   297  	require.True(ok)
   298  	require.Equal(snowmantest.Genesis.ID(), pref)
   299  }
   300  
   301  func StatusOrProcessingPreviouslyRejectedTest(t *testing.T, factory Factory) {
   302  	require := require.New(t)
   303  
   304  	sm := factory.New()
   305  
   306  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   307  	ctx := snowtest.ConsensusContext(snowCtx)
   308  	params := snowball.Parameters{
   309  		K:                     1,
   310  		AlphaPreference:       1,
   311  		AlphaConfidence:       1,
   312  		Beta:                  3,
   313  		ConcurrentRepolls:     1,
   314  		OptimalProcessing:     1,
   315  		MaxOutstandingItems:   1,
   316  		MaxItemProcessingTime: 1,
   317  	}
   318  	require.NoError(sm.Initialize(
   319  		ctx,
   320  		params,
   321  		snowmantest.GenesisID,
   322  		snowmantest.GenesisHeight,
   323  		snowmantest.GenesisTimestamp,
   324  	))
   325  
   326  	block := snowmantest.BuildChild(snowmantest.Genesis)
   327  	require.NoError(block.Reject(context.Background()))
   328  
   329  	require.Equal(choices.Rejected, block.Status())
   330  	require.False(sm.Processing(block.ID()))
   331  	require.False(sm.IsPreferred(block.ID()))
   332  
   333  	_, ok := sm.PreferenceAtHeight(block.Height())
   334  	require.False(ok)
   335  }
   336  
   337  func StatusOrProcessingUnissuedTest(t *testing.T, factory Factory) {
   338  	require := require.New(t)
   339  
   340  	sm := factory.New()
   341  
   342  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   343  	ctx := snowtest.ConsensusContext(snowCtx)
   344  	params := snowball.Parameters{
   345  		K:                     1,
   346  		AlphaPreference:       1,
   347  		AlphaConfidence:       1,
   348  		Beta:                  3,
   349  		ConcurrentRepolls:     1,
   350  		OptimalProcessing:     1,
   351  		MaxOutstandingItems:   1,
   352  		MaxItemProcessingTime: 1,
   353  	}
   354  	require.NoError(sm.Initialize(
   355  		ctx,
   356  		params,
   357  		snowmantest.GenesisID,
   358  		snowmantest.GenesisHeight,
   359  		snowmantest.GenesisTimestamp,
   360  	))
   361  
   362  	block := snowmantest.BuildChild(snowmantest.Genesis)
   363  
   364  	require.Equal(choices.Processing, block.Status())
   365  	require.False(sm.Processing(block.ID()))
   366  	require.False(sm.IsPreferred(block.ID()))
   367  
   368  	_, ok := sm.PreferenceAtHeight(block.Height())
   369  	require.False(ok)
   370  }
   371  
   372  func StatusOrProcessingIssuedTest(t *testing.T, factory Factory) {
   373  	require := require.New(t)
   374  
   375  	sm := factory.New()
   376  
   377  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   378  	ctx := snowtest.ConsensusContext(snowCtx)
   379  	params := snowball.Parameters{
   380  		K:                     1,
   381  		AlphaPreference:       1,
   382  		AlphaConfidence:       1,
   383  		Beta:                  3,
   384  		ConcurrentRepolls:     1,
   385  		OptimalProcessing:     1,
   386  		MaxOutstandingItems:   1,
   387  		MaxItemProcessingTime: 1,
   388  	}
   389  	require.NoError(sm.Initialize(
   390  		ctx,
   391  		params,
   392  		snowmantest.GenesisID,
   393  		snowmantest.GenesisHeight,
   394  		snowmantest.GenesisTimestamp,
   395  	))
   396  
   397  	block := snowmantest.BuildChild(snowmantest.Genesis)
   398  
   399  	require.NoError(sm.Add(block))
   400  	require.Equal(choices.Processing, block.Status())
   401  	require.True(sm.Processing(block.ID()))
   402  	require.True(sm.IsPreferred(block.ID()))
   403  
   404  	pref, ok := sm.PreferenceAtHeight(block.Height())
   405  	require.True(ok)
   406  	require.Equal(block.ID(), pref)
   407  }
   408  
   409  func RecordPollAcceptSingleBlockTest(t *testing.T, factory Factory) {
   410  	require := require.New(t)
   411  
   412  	sm := factory.New()
   413  
   414  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   415  	ctx := snowtest.ConsensusContext(snowCtx)
   416  	params := snowball.Parameters{
   417  		K:                     1,
   418  		AlphaPreference:       1,
   419  		AlphaConfidence:       1,
   420  		Beta:                  2,
   421  		ConcurrentRepolls:     1,
   422  		OptimalProcessing:     1,
   423  		MaxOutstandingItems:   1,
   424  		MaxItemProcessingTime: 1,
   425  	}
   426  	require.NoError(sm.Initialize(
   427  		ctx,
   428  		params,
   429  		snowmantest.GenesisID,
   430  		snowmantest.GenesisHeight,
   431  		snowmantest.GenesisTimestamp,
   432  	))
   433  
   434  	block := snowmantest.BuildChild(snowmantest.Genesis)
   435  
   436  	require.NoError(sm.Add(block))
   437  
   438  	votes := bag.Of(block.ID())
   439  	require.NoError(sm.RecordPoll(context.Background(), votes))
   440  	require.Equal(block.ID(), sm.Preference())
   441  	require.Equal(1, sm.NumProcessing())
   442  	require.Equal(choices.Processing, block.Status())
   443  
   444  	require.NoError(sm.RecordPoll(context.Background(), votes))
   445  	require.Equal(block.ID(), sm.Preference())
   446  	require.Zero(sm.NumProcessing())
   447  	require.Equal(choices.Accepted, block.Status())
   448  }
   449  
   450  func RecordPollAcceptAndRejectTest(t *testing.T, factory Factory) {
   451  	require := require.New(t)
   452  
   453  	sm := factory.New()
   454  
   455  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   456  	ctx := snowtest.ConsensusContext(snowCtx)
   457  	params := snowball.Parameters{
   458  		K:                     1,
   459  		AlphaPreference:       1,
   460  		AlphaConfidence:       1,
   461  		Beta:                  2,
   462  		ConcurrentRepolls:     1,
   463  		OptimalProcessing:     1,
   464  		MaxOutstandingItems:   1,
   465  		MaxItemProcessingTime: 1,
   466  	}
   467  	require.NoError(sm.Initialize(
   468  		ctx,
   469  		params,
   470  		snowmantest.GenesisID,
   471  		snowmantest.GenesisHeight,
   472  		snowmantest.GenesisTimestamp,
   473  	))
   474  
   475  	firstBlock := snowmantest.BuildChild(snowmantest.Genesis)
   476  	secondBlock := snowmantest.BuildChild(snowmantest.Genesis)
   477  
   478  	require.NoError(sm.Add(firstBlock))
   479  	require.NoError(sm.Add(secondBlock))
   480  
   481  	votes := bag.Of(firstBlock.ID())
   482  
   483  	require.NoError(sm.RecordPoll(context.Background(), votes))
   484  	require.Equal(firstBlock.ID(), sm.Preference())
   485  	require.Equal(2, sm.NumProcessing())
   486  	require.Equal(choices.Processing, firstBlock.Status())
   487  	require.Equal(choices.Processing, secondBlock.Status())
   488  
   489  	require.NoError(sm.RecordPoll(context.Background(), votes))
   490  	require.Equal(firstBlock.ID(), sm.Preference())
   491  	require.Zero(sm.NumProcessing())
   492  	require.Equal(choices.Accepted, firstBlock.Status())
   493  	require.Equal(choices.Rejected, secondBlock.Status())
   494  }
   495  
   496  func RecordPollSplitVoteNoChangeTest(t *testing.T, factory Factory) {
   497  	require := require.New(t)
   498  	sm := factory.New()
   499  
   500  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   501  	ctx := snowtest.ConsensusContext(snowCtx)
   502  	registerer := prometheus.NewRegistry()
   503  	ctx.Registerer = registerer
   504  
   505  	params := snowball.Parameters{
   506  		K:                     2,
   507  		AlphaPreference:       2,
   508  		AlphaConfidence:       2,
   509  		Beta:                  1,
   510  		ConcurrentRepolls:     1,
   511  		OptimalProcessing:     1,
   512  		MaxOutstandingItems:   1,
   513  		MaxItemProcessingTime: 1,
   514  	}
   515  	require.NoError(sm.Initialize(
   516  		ctx,
   517  		params,
   518  		snowmantest.GenesisID,
   519  		snowmantest.GenesisHeight,
   520  		snowmantest.GenesisTimestamp,
   521  	))
   522  
   523  	firstBlock := snowmantest.BuildChild(snowmantest.Genesis)
   524  	secondBlock := snowmantest.BuildChild(snowmantest.Genesis)
   525  
   526  	require.NoError(sm.Add(firstBlock))
   527  	require.NoError(sm.Add(secondBlock))
   528  
   529  	votes := bag.Of(firstBlock.ID(), secondBlock.ID())
   530  
   531  	// The first poll will accept shared bits
   532  	require.NoError(sm.RecordPoll(context.Background(), votes))
   533  	require.Equal(firstBlock.ID(), sm.Preference())
   534  	require.Equal(2, sm.NumProcessing())
   535  
   536  	metrics := gatherCounterGauge(t, registerer)
   537  	require.Zero(metrics["polls_failed"])
   538  	require.Equal(float64(1), metrics["polls_successful"])
   539  
   540  	// The second poll will do nothing
   541  	require.NoError(sm.RecordPoll(context.Background(), votes))
   542  	require.Equal(firstBlock.ID(), sm.Preference())
   543  	require.Equal(2, sm.NumProcessing())
   544  
   545  	metrics = gatherCounterGauge(t, registerer)
   546  	require.Equal(float64(1), metrics["polls_failed"])
   547  	require.Equal(float64(1), metrics["polls_successful"])
   548  }
   549  
   550  func RecordPollWhenFinalizedTest(t *testing.T, factory Factory) {
   551  	require := require.New(t)
   552  
   553  	sm := factory.New()
   554  
   555  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   556  	ctx := snowtest.ConsensusContext(snowCtx)
   557  	params := snowball.Parameters{
   558  		K:                     1,
   559  		AlphaPreference:       1,
   560  		AlphaConfidence:       1,
   561  		Beta:                  1,
   562  		ConcurrentRepolls:     1,
   563  		OptimalProcessing:     1,
   564  		MaxOutstandingItems:   1,
   565  		MaxItemProcessingTime: 1,
   566  	}
   567  	require.NoError(sm.Initialize(
   568  		ctx,
   569  		params,
   570  		snowmantest.GenesisID,
   571  		snowmantest.GenesisHeight,
   572  		snowmantest.GenesisTimestamp,
   573  	))
   574  
   575  	votes := bag.Of(snowmantest.GenesisID)
   576  	require.NoError(sm.RecordPoll(context.Background(), votes))
   577  	require.Zero(sm.NumProcessing())
   578  	require.Equal(snowmantest.GenesisID, sm.Preference())
   579  }
   580  
   581  func RecordPollRejectTransitivelyTest(t *testing.T, factory Factory) {
   582  	require := require.New(t)
   583  
   584  	sm := factory.New()
   585  
   586  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   587  	ctx := snowtest.ConsensusContext(snowCtx)
   588  	params := snowball.Parameters{
   589  		K:                     1,
   590  		AlphaPreference:       1,
   591  		AlphaConfidence:       1,
   592  		Beta:                  1,
   593  		ConcurrentRepolls:     1,
   594  		OptimalProcessing:     1,
   595  		MaxOutstandingItems:   1,
   596  		MaxItemProcessingTime: 1,
   597  	}
   598  	require.NoError(sm.Initialize(
   599  		ctx,
   600  		params,
   601  		snowmantest.GenesisID,
   602  		snowmantest.GenesisHeight,
   603  		snowmantest.GenesisTimestamp,
   604  	))
   605  
   606  	block0 := snowmantest.BuildChild(snowmantest.Genesis)
   607  	block1 := snowmantest.BuildChild(snowmantest.Genesis)
   608  	block2 := snowmantest.BuildChild(block1)
   609  
   610  	require.NoError(sm.Add(block0))
   611  	require.NoError(sm.Add(block1))
   612  	require.NoError(sm.Add(block2))
   613  
   614  	// Current graph structure:
   615  	//   G
   616  	//  / \
   617  	// 0   1
   618  	//     |
   619  	//     2
   620  	// Tail = 0
   621  
   622  	votes := bag.Of(block0.ID())
   623  	require.NoError(sm.RecordPoll(context.Background(), votes))
   624  
   625  	// Current graph structure:
   626  	// 0
   627  	// Tail = 0
   628  
   629  	require.Zero(sm.NumProcessing())
   630  	require.Equal(block0.ID(), sm.Preference())
   631  	require.Equal(choices.Accepted, block0.Status())
   632  	require.Equal(choices.Rejected, block1.Status())
   633  	require.Equal(choices.Rejected, block2.Status())
   634  }
   635  
   636  func RecordPollTransitivelyResetConfidenceTest(t *testing.T, factory Factory) {
   637  	require := require.New(t)
   638  
   639  	sm := factory.New()
   640  
   641  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   642  	ctx := snowtest.ConsensusContext(snowCtx)
   643  	params := snowball.Parameters{
   644  		K:                     1,
   645  		AlphaPreference:       1,
   646  		AlphaConfidence:       1,
   647  		Beta:                  2,
   648  		ConcurrentRepolls:     1,
   649  		OptimalProcessing:     1,
   650  		MaxOutstandingItems:   1,
   651  		MaxItemProcessingTime: 1,
   652  	}
   653  	require.NoError(sm.Initialize(
   654  		ctx,
   655  		params,
   656  		snowmantest.GenesisID,
   657  		snowmantest.GenesisHeight,
   658  		snowmantest.GenesisTimestamp,
   659  	))
   660  
   661  	block0 := snowmantest.BuildChild(snowmantest.Genesis)
   662  	block1 := snowmantest.BuildChild(snowmantest.Genesis)
   663  	block2 := snowmantest.BuildChild(block1)
   664  	block3 := snowmantest.BuildChild(block1)
   665  
   666  	require.NoError(sm.Add(block0))
   667  	require.NoError(sm.Add(block1))
   668  	require.NoError(sm.Add(block2))
   669  	require.NoError(sm.Add(block3))
   670  
   671  	// Current graph structure:
   672  	//   G
   673  	//  / \
   674  	// 0   1
   675  	//    / \
   676  	//   2   3
   677  
   678  	votesFor2 := bag.Of(block2.ID())
   679  	require.NoError(sm.RecordPoll(context.Background(), votesFor2))
   680  	require.Equal(4, sm.NumProcessing())
   681  	require.Equal(block2.ID(), sm.Preference())
   682  
   683  	emptyVotes := bag.Bag[ids.ID]{}
   684  	require.NoError(sm.RecordPoll(context.Background(), emptyVotes))
   685  	require.Equal(4, sm.NumProcessing())
   686  	require.Equal(block2.ID(), sm.Preference())
   687  
   688  	require.NoError(sm.RecordPoll(context.Background(), votesFor2))
   689  	require.Equal(4, sm.NumProcessing())
   690  	require.Equal(block2.ID(), sm.Preference())
   691  
   692  	votesFor3 := bag.Of(block3.ID())
   693  	require.NoError(sm.RecordPoll(context.Background(), votesFor3))
   694  	require.Equal(2, sm.NumProcessing())
   695  	require.Equal(block2.ID(), sm.Preference())
   696  
   697  	require.NoError(sm.RecordPoll(context.Background(), votesFor3))
   698  	require.Zero(sm.NumProcessing())
   699  	require.Equal(block3.ID(), sm.Preference())
   700  	require.Equal(choices.Rejected, block0.Status())
   701  	require.Equal(choices.Accepted, block1.Status())
   702  	require.Equal(choices.Rejected, block2.Status())
   703  	require.Equal(choices.Accepted, block3.Status())
   704  }
   705  
   706  func RecordPollInvalidVoteTest(t *testing.T, factory Factory) {
   707  	require := require.New(t)
   708  
   709  	sm := factory.New()
   710  
   711  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   712  	ctx := snowtest.ConsensusContext(snowCtx)
   713  	params := snowball.Parameters{
   714  		K:                     1,
   715  		AlphaPreference:       1,
   716  		AlphaConfidence:       1,
   717  		Beta:                  2,
   718  		ConcurrentRepolls:     1,
   719  		OptimalProcessing:     1,
   720  		MaxOutstandingItems:   1,
   721  		MaxItemProcessingTime: 1,
   722  	}
   723  	require.NoError(sm.Initialize(
   724  		ctx,
   725  		params,
   726  		snowmantest.GenesisID,
   727  		snowmantest.GenesisHeight,
   728  		snowmantest.GenesisTimestamp,
   729  	))
   730  
   731  	block := snowmantest.BuildChild(snowmantest.Genesis)
   732  	unknownBlockID := ids.GenerateTestID()
   733  
   734  	require.NoError(sm.Add(block))
   735  
   736  	validVotes := bag.Of(block.ID())
   737  	require.NoError(sm.RecordPoll(context.Background(), validVotes))
   738  
   739  	invalidVotes := bag.Of(unknownBlockID)
   740  	require.NoError(sm.RecordPoll(context.Background(), invalidVotes))
   741  	require.NoError(sm.RecordPoll(context.Background(), validVotes))
   742  	require.Equal(1, sm.NumProcessing())
   743  	require.Equal(block.ID(), sm.Preference())
   744  }
   745  
   746  func RecordPollTransitiveVotingTest(t *testing.T, factory Factory) {
   747  	require := require.New(t)
   748  
   749  	sm := factory.New()
   750  
   751  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   752  	ctx := snowtest.ConsensusContext(snowCtx)
   753  	params := snowball.Parameters{
   754  		K:                     3,
   755  		AlphaPreference:       3,
   756  		AlphaConfidence:       3,
   757  		Beta:                  1,
   758  		ConcurrentRepolls:     1,
   759  		OptimalProcessing:     1,
   760  		MaxOutstandingItems:   1,
   761  		MaxItemProcessingTime: 1,
   762  	}
   763  	require.NoError(sm.Initialize(
   764  		ctx,
   765  		params,
   766  		snowmantest.GenesisID,
   767  		snowmantest.GenesisHeight,
   768  		snowmantest.GenesisTimestamp,
   769  	))
   770  
   771  	block0 := snowmantest.BuildChild(snowmantest.Genesis)
   772  	block1 := snowmantest.BuildChild(block0)
   773  	block2 := snowmantest.BuildChild(block1)
   774  	block3 := snowmantest.BuildChild(block0)
   775  	block4 := snowmantest.BuildChild(block3)
   776  
   777  	require.NoError(sm.Add(block0))
   778  	require.NoError(sm.Add(block1))
   779  	require.NoError(sm.Add(block2))
   780  	require.NoError(sm.Add(block3))
   781  	require.NoError(sm.Add(block4))
   782  
   783  	// Current graph structure:
   784  	//   G
   785  	//   |
   786  	//   0
   787  	//  / \
   788  	// 1   3
   789  	// |   |
   790  	// 2   4
   791  	// Tail = 2
   792  
   793  	votes0_2_4 := bag.Of(block0.ID(), block2.ID(), block4.ID())
   794  	require.NoError(sm.RecordPoll(context.Background(), votes0_2_4))
   795  
   796  	// Current graph structure:
   797  	//   0
   798  	//  / \
   799  	// 1   3
   800  	// |   |
   801  	// 2   4
   802  	// Tail = 2
   803  
   804  	require.Equal(4, sm.NumProcessing())
   805  	require.Equal(block2.ID(), sm.Preference())
   806  	require.Equal(choices.Accepted, block0.Status())
   807  	require.Equal(choices.Processing, block1.Status())
   808  	require.Equal(choices.Processing, block2.Status())
   809  	require.Equal(choices.Processing, block3.Status())
   810  	require.Equal(choices.Processing, block4.Status())
   811  
   812  	dep2_2_2 := bag.Of(block2.ID(), block2.ID(), block2.ID())
   813  	require.NoError(sm.RecordPoll(context.Background(), dep2_2_2))
   814  
   815  	// Current graph structure:
   816  	//   2
   817  	// Tail = 2
   818  
   819  	require.Zero(sm.NumProcessing())
   820  	require.Equal(block2.ID(), sm.Preference())
   821  	require.Equal(choices.Accepted, block0.Status())
   822  	require.Equal(choices.Accepted, block1.Status())
   823  	require.Equal(choices.Accepted, block2.Status())
   824  	require.Equal(choices.Rejected, block3.Status())
   825  	require.Equal(choices.Rejected, block4.Status())
   826  }
   827  
   828  func RecordPollDivergedVotingWithNoConflictingBitTest(t *testing.T, factory Factory) {
   829  	sm := factory.New()
   830  	require := require.New(t)
   831  
   832  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   833  	ctx := snowtest.ConsensusContext(snowCtx)
   834  	params := snowball.Parameters{
   835  		K:                     1,
   836  		AlphaPreference:       1,
   837  		AlphaConfidence:       1,
   838  		Beta:                  2,
   839  		ConcurrentRepolls:     1,
   840  		OptimalProcessing:     1,
   841  		MaxOutstandingItems:   1,
   842  		MaxItemProcessingTime: 1,
   843  	}
   844  	require.NoError(sm.Initialize(
   845  		ctx,
   846  		params,
   847  		snowmantest.GenesisID,
   848  		snowmantest.GenesisHeight,
   849  		snowmantest.GenesisTimestamp,
   850  	))
   851  
   852  	block0 := &snowmantest.Block{
   853  		TestDecidable: choices.TestDecidable{
   854  			IDV:     ids.ID{0x06}, // 0110
   855  			StatusV: choices.Processing,
   856  		},
   857  		ParentV: snowmantest.GenesisID,
   858  		HeightV: snowmantest.GenesisHeight + 1,
   859  	}
   860  	block1 := &snowmantest.Block{
   861  		TestDecidable: choices.TestDecidable{
   862  			IDV:     ids.ID{0x08}, // 0001
   863  			StatusV: choices.Processing,
   864  		},
   865  		ParentV: snowmantest.GenesisID,
   866  		HeightV: snowmantest.GenesisHeight + 1,
   867  	}
   868  	block2 := &snowmantest.Block{
   869  		TestDecidable: choices.TestDecidable{
   870  			IDV:     ids.ID{0x01}, // 1000
   871  			StatusV: choices.Processing,
   872  		},
   873  		ParentV: snowmantest.GenesisID,
   874  		HeightV: snowmantest.GenesisHeight + 1,
   875  	}
   876  	block3 := snowmantest.BuildChild(block2)
   877  
   878  	require.NoError(sm.Add(block0))
   879  	require.NoError(sm.Add(block1))
   880  
   881  	// When voting for [block0], we end up finalizing the first bit as 0. The
   882  	// second bit is contested as either 0 or 1. For when the second bit is 1,
   883  	// the following bits have been decided to follow the 254 remaining bits of
   884  	// [block0].
   885  	votes0 := bag.Of(block0.ID())
   886  	require.NoError(sm.RecordPoll(context.Background(), votes0))
   887  
   888  	// Although we are adding in [block2] here - the underlying snowball
   889  	// instance has already decided it is rejected. Snowman doesn't actually
   890  	// know that though, because that is an implementation detail of the
   891  	// Snowball trie that is used.
   892  	require.NoError(sm.Add(block2))
   893  
   894  	// Because [block2] is effectively rejected, [block3] is also effectively
   895  	// rejected.
   896  	require.NoError(sm.Add(block3))
   897  
   898  	require.Equal(block0.ID(), sm.Preference())
   899  	require.Equal(choices.Processing, block0.Status(), "should not be decided yet")
   900  	require.Equal(choices.Processing, block1.Status(), "should not be decided yet")
   901  	require.Equal(choices.Processing, block2.Status(), "should not be decided yet")
   902  	require.Equal(choices.Processing, block3.Status(), "should not be decided yet")
   903  
   904  	// Current graph structure:
   905  	//       G
   906  	//     /   \
   907  	//    *     |
   908  	//   / \    |
   909  	//  0   1   2
   910  	//          |
   911  	//          3
   912  	// Tail = 0
   913  
   914  	// Transitively votes for [block2] by voting for its child [block3]. Because
   915  	// [block2] doesn't share any processing bits with [block0] or [block1], the
   916  	// votes are over only rejected bits. Therefore, the votes for [block2] are
   917  	// dropped. Although the votes for [block3] are still applied, [block3] will
   918  	// only be marked as accepted after [block2] is marked as accepted; which
   919  	// will never happen.
   920  	votes3 := bag.Of(block3.ID())
   921  	require.NoError(sm.RecordPoll(context.Background(), votes3))
   922  
   923  	require.Equal(4, sm.NumProcessing())
   924  	require.Equal(choices.Processing, block0.Status())
   925  	require.Equal(choices.Processing, block1.Status())
   926  	require.Equal(choices.Processing, block2.Status())
   927  	require.Equal(choices.Processing, block3.Status())
   928  }
   929  
   930  func RecordPollChangePreferredChainTest(t *testing.T, factory Factory) {
   931  	require := require.New(t)
   932  
   933  	sm := factory.New()
   934  
   935  	snowCtx := snowtest.Context(t, snowtest.CChainID)
   936  	ctx := snowtest.ConsensusContext(snowCtx)
   937  	params := snowball.Parameters{
   938  		K:                     1,
   939  		AlphaPreference:       1,
   940  		AlphaConfidence:       1,
   941  		Beta:                  10,
   942  		ConcurrentRepolls:     1,
   943  		OptimalProcessing:     1,
   944  		MaxOutstandingItems:   1,
   945  		MaxItemProcessingTime: 1,
   946  	}
   947  	require.NoError(sm.Initialize(
   948  		ctx,
   949  		params,
   950  		snowmantest.GenesisID,
   951  		snowmantest.GenesisHeight,
   952  		snowmantest.GenesisTimestamp,
   953  	))
   954  
   955  	a1Block := snowmantest.BuildChild(snowmantest.Genesis)
   956  	b1Block := snowmantest.BuildChild(snowmantest.Genesis)
   957  	a2Block := snowmantest.BuildChild(a1Block)
   958  	b2Block := snowmantest.BuildChild(b1Block)
   959  
   960  	require.NoError(sm.Add(a1Block))
   961  	require.NoError(sm.Add(a2Block))
   962  	require.NoError(sm.Add(b1Block))
   963  	require.NoError(sm.Add(b2Block))
   964  
   965  	require.Equal(a2Block.ID(), sm.Preference())
   966  
   967  	require.True(sm.IsPreferred(a1Block.ID()))
   968  	require.True(sm.IsPreferred(a2Block.ID()))
   969  	require.False(sm.IsPreferred(b1Block.ID()))
   970  	require.False(sm.IsPreferred(b2Block.ID()))
   971  
   972  	pref, ok := sm.PreferenceAtHeight(a1Block.Height())
   973  	require.True(ok)
   974  	require.Equal(a1Block.ID(), pref)
   975  
   976  	pref, ok = sm.PreferenceAtHeight(a2Block.Height())
   977  	require.True(ok)
   978  	require.Equal(a2Block.ID(), pref)
   979  
   980  	b2Votes := bag.Of(b2Block.ID())
   981  	require.NoError(sm.RecordPoll(context.Background(), b2Votes))
   982  
   983  	require.Equal(b2Block.ID(), sm.Preference())
   984  	require.False(sm.IsPreferred(a1Block.ID()))
   985  	require.False(sm.IsPreferred(a2Block.ID()))
   986  	require.True(sm.IsPreferred(b1Block.ID()))
   987  	require.True(sm.IsPreferred(b2Block.ID()))
   988  
   989  	pref, ok = sm.PreferenceAtHeight(b1Block.Height())
   990  	require.True(ok)
   991  	require.Equal(b1Block.ID(), pref)
   992  
   993  	pref, ok = sm.PreferenceAtHeight(b2Block.Height())
   994  	require.True(ok)
   995  	require.Equal(b2Block.ID(), pref)
   996  
   997  	a1Votes := bag.Of(a1Block.ID())
   998  	require.NoError(sm.RecordPoll(context.Background(), a1Votes))
   999  	require.NoError(sm.RecordPoll(context.Background(), a1Votes))
  1000  
  1001  	require.Equal(a2Block.ID(), sm.Preference())
  1002  	require.True(sm.IsPreferred(a1Block.ID()))
  1003  	require.True(sm.IsPreferred(a2Block.ID()))
  1004  	require.False(sm.IsPreferred(b1Block.ID()))
  1005  	require.False(sm.IsPreferred(b2Block.ID()))
  1006  
  1007  	pref, ok = sm.PreferenceAtHeight(a1Block.Height())
  1008  	require.True(ok)
  1009  	require.Equal(a1Block.ID(), pref)
  1010  
  1011  	pref, ok = sm.PreferenceAtHeight(a2Block.Height())
  1012  	require.True(ok)
  1013  	require.Equal(a2Block.ID(), pref)
  1014  }
  1015  
  1016  func LastAcceptedTest(t *testing.T, factory Factory) {
  1017  	sm := factory.New()
  1018  	require := require.New(t)
  1019  
  1020  	snowCtx := snowtest.Context(t, snowtest.CChainID)
  1021  	ctx := snowtest.ConsensusContext(snowCtx)
  1022  	params := snowball.Parameters{
  1023  		K:                     1,
  1024  		AlphaPreference:       1,
  1025  		AlphaConfidence:       1,
  1026  		Beta:                  2,
  1027  		ConcurrentRepolls:     1,
  1028  		OptimalProcessing:     1,
  1029  		MaxOutstandingItems:   1,
  1030  		MaxItemProcessingTime: 1,
  1031  	}
  1032  	require.NoError(sm.Initialize(
  1033  		ctx,
  1034  		params,
  1035  		snowmantest.GenesisID,
  1036  		snowmantest.GenesisHeight,
  1037  		snowmantest.GenesisTimestamp,
  1038  	))
  1039  
  1040  	block0 := snowmantest.BuildChild(snowmantest.Genesis)
  1041  	block1 := snowmantest.BuildChild(block0)
  1042  	block2 := snowmantest.BuildChild(block1)
  1043  	block1Conflict := snowmantest.BuildChild(block0)
  1044  
  1045  	lastAcceptedID, lastAcceptedHeight := sm.LastAccepted()
  1046  	require.Equal(snowmantest.GenesisID, lastAcceptedID)
  1047  	require.Equal(snowmantest.GenesisHeight, lastAcceptedHeight)
  1048  
  1049  	require.NoError(sm.Add(block0))
  1050  	require.NoError(sm.Add(block1))
  1051  	require.NoError(sm.Add(block1Conflict))
  1052  	require.NoError(sm.Add(block2))
  1053  
  1054  	lastAcceptedID, lastAcceptedHeight = sm.LastAccepted()
  1055  	require.Equal(snowmantest.GenesisID, lastAcceptedID)
  1056  	require.Equal(snowmantest.GenesisHeight, lastAcceptedHeight)
  1057  
  1058  	require.NoError(sm.RecordPoll(context.Background(), bag.Of(block0.IDV)))
  1059  
  1060  	lastAcceptedID, lastAcceptedHeight = sm.LastAccepted()
  1061  	require.Equal(snowmantest.GenesisID, lastAcceptedID)
  1062  	require.Equal(snowmantest.GenesisHeight, lastAcceptedHeight)
  1063  
  1064  	require.NoError(sm.RecordPoll(context.Background(), bag.Of(block1.IDV)))
  1065  
  1066  	lastAcceptedID, lastAcceptedHeight = sm.LastAccepted()
  1067  	require.Equal(block0.IDV, lastAcceptedID)
  1068  	require.Equal(block0.HeightV, lastAcceptedHeight)
  1069  
  1070  	require.NoError(sm.RecordPoll(context.Background(), bag.Of(block1.IDV)))
  1071  
  1072  	lastAcceptedID, lastAcceptedHeight = sm.LastAccepted()
  1073  	require.Equal(block1.IDV, lastAcceptedID)
  1074  	require.Equal(block1.HeightV, lastAcceptedHeight)
  1075  
  1076  	require.NoError(sm.RecordPoll(context.Background(), bag.Of(block2.IDV)))
  1077  
  1078  	lastAcceptedID, lastAcceptedHeight = sm.LastAccepted()
  1079  	require.Equal(block1.IDV, lastAcceptedID)
  1080  	require.Equal(block1.HeightV, lastAcceptedHeight)
  1081  
  1082  	require.NoError(sm.RecordPoll(context.Background(), bag.Of(block2.IDV)))
  1083  
  1084  	lastAcceptedID, lastAcceptedHeight = sm.LastAccepted()
  1085  	require.Equal(block2.IDV, lastAcceptedID)
  1086  	require.Equal(block2.HeightV, lastAcceptedHeight)
  1087  }
  1088  
  1089  func MetricsProcessingErrorTest(t *testing.T, factory Factory) {
  1090  	require := require.New(t)
  1091  
  1092  	sm := factory.New()
  1093  
  1094  	snowCtx := snowtest.Context(t, snowtest.CChainID)
  1095  	ctx := snowtest.ConsensusContext(snowCtx)
  1096  	params := snowball.Parameters{
  1097  		K:                     1,
  1098  		AlphaPreference:       1,
  1099  		AlphaConfidence:       1,
  1100  		Beta:                  1,
  1101  		ConcurrentRepolls:     1,
  1102  		OptimalProcessing:     1,
  1103  		MaxOutstandingItems:   1,
  1104  		MaxItemProcessingTime: 1,
  1105  	}
  1106  
  1107  	numProcessing := prometheus.NewGauge(prometheus.GaugeOpts{
  1108  		Name: "blks_processing",
  1109  	})
  1110  
  1111  	require.NoError(ctx.Registerer.Register(numProcessing))
  1112  
  1113  	err := sm.Initialize(
  1114  		ctx,
  1115  		params,
  1116  		snowmantest.GenesisID,
  1117  		snowmantest.GenesisHeight,
  1118  		snowmantest.GenesisTimestamp,
  1119  	)
  1120  	require.Error(err) //nolint:forbidigo // error is not exported https://github.com/prometheus/client_golang/blob/main/prometheus/registry.go#L315
  1121  }
  1122  
  1123  func MetricsAcceptedErrorTest(t *testing.T, factory Factory) {
  1124  	require := require.New(t)
  1125  
  1126  	sm := factory.New()
  1127  
  1128  	snowCtx := snowtest.Context(t, snowtest.CChainID)
  1129  	ctx := snowtest.ConsensusContext(snowCtx)
  1130  	params := snowball.Parameters{
  1131  		K:                     1,
  1132  		AlphaPreference:       1,
  1133  		AlphaConfidence:       1,
  1134  		Beta:                  1,
  1135  		ConcurrentRepolls:     1,
  1136  		OptimalProcessing:     1,
  1137  		MaxOutstandingItems:   1,
  1138  		MaxItemProcessingTime: 1,
  1139  	}
  1140  
  1141  	numAccepted := prometheus.NewGauge(prometheus.GaugeOpts{
  1142  		Name: "blks_accepted_count",
  1143  	})
  1144  
  1145  	require.NoError(ctx.Registerer.Register(numAccepted))
  1146  
  1147  	err := sm.Initialize(
  1148  		ctx,
  1149  		params,
  1150  		snowmantest.GenesisID,
  1151  		snowmantest.GenesisHeight,
  1152  		snowmantest.GenesisTimestamp,
  1153  	)
  1154  	require.Error(err) //nolint:forbidigo // error is not exported https://github.com/prometheus/client_golang/blob/main/prometheus/registry.go#L315
  1155  }
  1156  
  1157  func MetricsRejectedErrorTest(t *testing.T, factory Factory) {
  1158  	require := require.New(t)
  1159  
  1160  	sm := factory.New()
  1161  
  1162  	snowCtx := snowtest.Context(t, snowtest.CChainID)
  1163  	ctx := snowtest.ConsensusContext(snowCtx)
  1164  	params := snowball.Parameters{
  1165  		K:                     1,
  1166  		AlphaPreference:       1,
  1167  		AlphaConfidence:       1,
  1168  		Beta:                  1,
  1169  		ConcurrentRepolls:     1,
  1170  		OptimalProcessing:     1,
  1171  		MaxOutstandingItems:   1,
  1172  		MaxItemProcessingTime: 1,
  1173  	}
  1174  
  1175  	numRejected := prometheus.NewGauge(prometheus.GaugeOpts{
  1176  		Name: "blks_rejected_count",
  1177  	})
  1178  
  1179  	require.NoError(ctx.Registerer.Register(numRejected))
  1180  
  1181  	err := sm.Initialize(
  1182  		ctx,
  1183  		params,
  1184  		snowmantest.GenesisID,
  1185  		snowmantest.GenesisHeight,
  1186  		snowmantest.GenesisTimestamp,
  1187  	)
  1188  	require.Error(err) //nolint:forbidigo // error is not exported https://github.com/prometheus/client_golang/blob/main/prometheus/registry.go#L315
  1189  }
  1190  
  1191  func ErrorOnAcceptTest(t *testing.T, factory Factory) {
  1192  	require := require.New(t)
  1193  
  1194  	sm := factory.New()
  1195  
  1196  	snowCtx := snowtest.Context(t, snowtest.CChainID)
  1197  	ctx := snowtest.ConsensusContext(snowCtx)
  1198  	params := snowball.Parameters{
  1199  		K:                     1,
  1200  		AlphaPreference:       1,
  1201  		AlphaConfidence:       1,
  1202  		Beta:                  1,
  1203  		ConcurrentRepolls:     1,
  1204  		OptimalProcessing:     1,
  1205  		MaxOutstandingItems:   1,
  1206  		MaxItemProcessingTime: 1,
  1207  	}
  1208  
  1209  	require.NoError(sm.Initialize(
  1210  		ctx,
  1211  		params,
  1212  		snowmantest.GenesisID,
  1213  		snowmantest.GenesisHeight,
  1214  		snowmantest.GenesisTimestamp,
  1215  	))
  1216  
  1217  	block := snowmantest.BuildChild(snowmantest.Genesis)
  1218  	block.AcceptV = errTest
  1219  
  1220  	require.NoError(sm.Add(block))
  1221  
  1222  	votes := bag.Of(block.ID())
  1223  	err := sm.RecordPoll(context.Background(), votes)
  1224  	require.ErrorIs(err, errTest)
  1225  }
  1226  
  1227  func ErrorOnRejectSiblingTest(t *testing.T, factory Factory) {
  1228  	require := require.New(t)
  1229  
  1230  	sm := factory.New()
  1231  
  1232  	snowCtx := snowtest.Context(t, snowtest.CChainID)
  1233  	ctx := snowtest.ConsensusContext(snowCtx)
  1234  	params := snowball.Parameters{
  1235  		K:                     1,
  1236  		AlphaPreference:       1,
  1237  		AlphaConfidence:       1,
  1238  		Beta:                  1,
  1239  		ConcurrentRepolls:     1,
  1240  		OptimalProcessing:     1,
  1241  		MaxOutstandingItems:   1,
  1242  		MaxItemProcessingTime: 1,
  1243  	}
  1244  
  1245  	require.NoError(sm.Initialize(
  1246  		ctx,
  1247  		params,
  1248  		snowmantest.GenesisID,
  1249  		snowmantest.GenesisHeight,
  1250  		snowmantest.GenesisTimestamp,
  1251  	))
  1252  
  1253  	block0 := snowmantest.BuildChild(snowmantest.Genesis)
  1254  	block1 := snowmantest.BuildChild(snowmantest.Genesis)
  1255  	block1.RejectV = errTest
  1256  
  1257  	require.NoError(sm.Add(block0))
  1258  	require.NoError(sm.Add(block1))
  1259  
  1260  	votes := bag.Of(block0.ID())
  1261  	err := sm.RecordPoll(context.Background(), votes)
  1262  	require.ErrorIs(err, errTest)
  1263  }
  1264  
  1265  func ErrorOnTransitiveRejectionTest(t *testing.T, factory Factory) {
  1266  	require := require.New(t)
  1267  
  1268  	sm := factory.New()
  1269  
  1270  	snowCtx := snowtest.Context(t, snowtest.CChainID)
  1271  	ctx := snowtest.ConsensusContext(snowCtx)
  1272  	params := snowball.Parameters{
  1273  		K:                     1,
  1274  		AlphaPreference:       1,
  1275  		AlphaConfidence:       1,
  1276  		Beta:                  1,
  1277  		ConcurrentRepolls:     1,
  1278  		OptimalProcessing:     1,
  1279  		MaxOutstandingItems:   1,
  1280  		MaxItemProcessingTime: 1,
  1281  	}
  1282  
  1283  	require.NoError(sm.Initialize(
  1284  		ctx,
  1285  		params,
  1286  		snowmantest.GenesisID,
  1287  		snowmantest.GenesisHeight,
  1288  		snowmantest.GenesisTimestamp,
  1289  	))
  1290  
  1291  	block0 := snowmantest.BuildChild(snowmantest.Genesis)
  1292  	block1 := snowmantest.BuildChild(snowmantest.Genesis)
  1293  	block2 := snowmantest.BuildChild(block1)
  1294  	block2.RejectV = errTest
  1295  
  1296  	require.NoError(sm.Add(block0))
  1297  	require.NoError(sm.Add(block1))
  1298  	require.NoError(sm.Add(block2))
  1299  
  1300  	votes := bag.Of(block0.ID())
  1301  	err := sm.RecordPoll(context.Background(), votes)
  1302  	require.ErrorIs(err, errTest)
  1303  }
  1304  
  1305  func RandomizedConsistencyTest(t *testing.T, factory Factory) {
  1306  	require := require.New(t)
  1307  
  1308  	var (
  1309  		numColors = 50
  1310  		numNodes  = 100
  1311  		params    = snowball.Parameters{
  1312  			K:                     20,
  1313  			AlphaPreference:       15,
  1314  			AlphaConfidence:       15,
  1315  			Beta:                  20,
  1316  			ConcurrentRepolls:     1,
  1317  			OptimalProcessing:     1,
  1318  			MaxOutstandingItems:   1,
  1319  			MaxItemProcessingTime: 1,
  1320  		}
  1321  		seed   uint64 = 0
  1322  		source        = prng.NewMT19937()
  1323  	)
  1324  
  1325  	source.Seed(seed)
  1326  
  1327  	n := NewNetwork(params, numColors, source)
  1328  
  1329  	for i := 0; i < numNodes; i++ {
  1330  		require.NoError(n.AddNode(t, factory.New()))
  1331  	}
  1332  
  1333  	for !n.Finalized() {
  1334  		require.NoError(n.Round())
  1335  	}
  1336  
  1337  	require.True(n.Agreement())
  1338  }
  1339  
  1340  func ErrorOnAddDecidedBlockTest(t *testing.T, factory Factory) {
  1341  	sm := factory.New()
  1342  	require := require.New(t)
  1343  
  1344  	snowCtx := snowtest.Context(t, snowtest.CChainID)
  1345  	ctx := snowtest.ConsensusContext(snowCtx)
  1346  	params := snowball.Parameters{
  1347  		K:                     1,
  1348  		AlphaPreference:       1,
  1349  		AlphaConfidence:       1,
  1350  		Beta:                  1,
  1351  		ConcurrentRepolls:     1,
  1352  		OptimalProcessing:     1,
  1353  		MaxOutstandingItems:   1,
  1354  		MaxItemProcessingTime: 1,
  1355  	}
  1356  	require.NoError(sm.Initialize(
  1357  		ctx,
  1358  		params,
  1359  		snowmantest.GenesisID,
  1360  		snowmantest.GenesisHeight,
  1361  		snowmantest.GenesisTimestamp,
  1362  	))
  1363  
  1364  	err := sm.Add(snowmantest.Genesis)
  1365  	require.ErrorIs(err, errUnknownParentBlock)
  1366  }
  1367  
  1368  func gatherCounterGauge(t *testing.T, reg prometheus.Gatherer) map[string]float64 {
  1369  	ms, err := reg.Gather()
  1370  	require.NoError(t, err)
  1371  	mss := make(map[string]float64)
  1372  	for _, mf := range ms {
  1373  		name := mf.GetName()
  1374  		for _, m := range mf.GetMetric() {
  1375  			cnt := m.GetCounter()
  1376  			if cnt != nil {
  1377  				mss[name] = cnt.GetValue()
  1378  				break
  1379  			}
  1380  			gg := m.GetGauge()
  1381  			if gg != nil {
  1382  				mss[name] = gg.GetValue()
  1383  				break
  1384  			}
  1385  		}
  1386  	}
  1387  	return mss
  1388  }
  1389  
  1390  // You can run this test with "go test -v -run TestTopological/RecordPollWithDefaultParameters"
  1391  func RecordPollWithDefaultParameters(t *testing.T, factory Factory) {
  1392  	require := require.New(t)
  1393  
  1394  	sm := factory.New()
  1395  
  1396  	snowCtx := snowtest.Context(t, snowtest.CChainID)
  1397  	ctx := snowtest.ConsensusContext(snowCtx)
  1398  	params := snowball.DefaultParameters
  1399  	require.NoError(sm.Initialize(
  1400  		ctx,
  1401  		params,
  1402  		snowmantest.GenesisID,
  1403  		snowmantest.GenesisHeight,
  1404  		snowmantest.GenesisTimestamp,
  1405  	))
  1406  
  1407  	// "blk1" and "blk2" are in conflict
  1408  	blk1 := snowmantest.BuildChild(snowmantest.Genesis)
  1409  	blk2 := snowmantest.BuildChild(snowmantest.Genesis)
  1410  
  1411  	require.NoError(sm.Add(blk1))
  1412  	require.NoError(sm.Add(blk2))
  1413  
  1414  	votes := bag.Bag[ids.ID]{}
  1415  	votes.AddCount(blk1.ID(), params.AlphaConfidence)
  1416  	// Require beta rounds to finalize
  1417  	for i := 0; i < params.Beta; i++ {
  1418  		// should not finalize with less than beta rounds
  1419  		require.Equal(2, sm.NumProcessing())
  1420  		require.NoError(sm.RecordPoll(context.Background(), votes))
  1421  	}
  1422  	require.Zero(sm.NumProcessing())
  1423  }
  1424  
  1425  // If a block that was voted for received additional votes from another block,
  1426  // the indegree of the topological sort should not traverse into the parent
  1427  // node.
  1428  func RecordPollRegressionCalculateInDegreeIndegreeCalculation(t *testing.T, factory Factory) {
  1429  	require := require.New(t)
  1430  
  1431  	sm := factory.New()
  1432  
  1433  	snowCtx := snowtest.Context(t, snowtest.CChainID)
  1434  	ctx := snowtest.ConsensusContext(snowCtx)
  1435  	params := snowball.Parameters{
  1436  		K:                     3,
  1437  		AlphaPreference:       2,
  1438  		AlphaConfidence:       2,
  1439  		Beta:                  1,
  1440  		ConcurrentRepolls:     1,
  1441  		OptimalProcessing:     1,
  1442  		MaxOutstandingItems:   1,
  1443  		MaxItemProcessingTime: 1,
  1444  	}
  1445  	require.NoError(sm.Initialize(
  1446  		ctx,
  1447  		params,
  1448  		snowmantest.GenesisID,
  1449  		snowmantest.GenesisHeight,
  1450  		snowmantest.GenesisTimestamp,
  1451  	))
  1452  
  1453  	blk1 := snowmantest.BuildChild(snowmantest.Genesis)
  1454  	blk2 := snowmantest.BuildChild(blk1)
  1455  	blk3 := snowmantest.BuildChild(blk2)
  1456  
  1457  	require.NoError(sm.Add(blk1))
  1458  	require.NoError(sm.Add(blk2))
  1459  	require.NoError(sm.Add(blk3))
  1460  
  1461  	votes := bag.Bag[ids.ID]{}
  1462  	votes.AddCount(blk2.ID(), 1)
  1463  	votes.AddCount(blk3.ID(), 2)
  1464  	require.NoError(sm.RecordPoll(context.Background(), votes))
  1465  	require.Equal(choices.Accepted, blk1.Status())
  1466  	require.Equal(choices.Accepted, blk2.Status())
  1467  	require.Equal(choices.Accepted, blk3.Status())
  1468  }