github.com/MetalBlockchain/metalgo@v1.11.9/snow/consensus/snowball/test_snowflake.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package snowball
     5  
     6  import "testing"
     7  
     8  const alphaPreference = 3
     9  
    10  var terminationConditions = []terminationCondition{
    11  	{
    12  		alphaConfidence: 3,
    13  		beta:            4,
    14  	},
    15  	{
    16  		alphaConfidence: 4,
    17  		beta:            3,
    18  	},
    19  	{
    20  		alphaConfidence: 5,
    21  		beta:            2,
    22  	},
    23  }
    24  
    25  type snowflakeTestConstructor[T comparable] func(t *testing.T, alphaPreference int, terminationConditions []terminationCondition) snowflakeTest[T]
    26  
    27  type snowflakeTest[T comparable] interface {
    28  	RecordPoll(count int, optionalMode T)
    29  	RecordUnsuccessfulPoll()
    30  	AssertEqual(expectedConfidences []int, expectedFinalized bool, expectedPreference T)
    31  }
    32  
    33  func executeErrorDrivenTerminatesInBetaPolls[T comparable](t *testing.T, newSnowflakeTest snowflakeTestConstructor[T], choice T) {
    34  	for i, terminationCondition := range terminationConditions {
    35  		sfTest := newSnowflakeTest(t, alphaPreference, terminationConditions)
    36  
    37  		for poll := 0; poll < terminationCondition.beta; poll++ {
    38  			sfTest.RecordPoll(terminationCondition.alphaConfidence, choice)
    39  
    40  			expectedConfidences := make([]int, len(terminationConditions))
    41  			for j := 0; j < i+1; j++ {
    42  				expectedConfidences[j] = poll + 1
    43  			}
    44  			sfTest.AssertEqual(expectedConfidences, poll+1 >= terminationCondition.beta, choice)
    45  		}
    46  	}
    47  }
    48  
    49  func executeErrorDrivenReset[T comparable](t *testing.T, newSnowflakeTest snowflakeTestConstructor[T], choice T) {
    50  	for i, terminationCondition := range terminationConditions {
    51  		sfTest := newSnowflakeTest(t, alphaPreference, terminationConditions)
    52  
    53  		// Accumulate confidence up to 1 less than beta, reset, and confirm
    54  		// expected behavior from fresh state.
    55  		for poll := 0; poll < terminationCondition.beta-1; poll++ {
    56  			sfTest.RecordPoll(terminationCondition.alphaConfidence, choice)
    57  		}
    58  		sfTest.RecordUnsuccessfulPoll()
    59  		zeroConfidence := make([]int, len(terminationConditions))
    60  		sfTest.AssertEqual(zeroConfidence, false, choice)
    61  
    62  		for poll := 0; poll < terminationCondition.beta; poll++ {
    63  			sfTest.RecordPoll(terminationCondition.alphaConfidence, choice)
    64  
    65  			expectedConfidences := make([]int, len(terminationConditions))
    66  			for j := 0; j < i+1; j++ {
    67  				expectedConfidences[j] = poll + 1
    68  			}
    69  			sfTest.AssertEqual(expectedConfidences, poll+1 >= terminationCondition.beta, choice)
    70  		}
    71  	}
    72  }
    73  
    74  func executeErrorDrivenResetHighestAlphaConfidence[T comparable](t *testing.T, newSnowflakeTest snowflakeTestConstructor[T], choice T) {
    75  	sfTest := newSnowflakeTest(t, alphaPreference, terminationConditions)
    76  
    77  	sfTest.RecordPoll(5, choice)
    78  	sfTest.AssertEqual([]int{1, 1, 1}, false, choice)
    79  	sfTest.RecordPoll(4, choice)
    80  	sfTest.AssertEqual([]int{2, 2, 0}, false, choice)
    81  	sfTest.RecordPoll(3, choice)
    82  	sfTest.AssertEqual([]int{3, 0, 0}, false, choice)
    83  	sfTest.RecordPoll(5, choice)
    84  	sfTest.AssertEqual([]int{4, 0, 0}, true, choice)
    85  }
    86  
    87  type snowflakeTestSingleChoice[T comparable] struct {
    88  	name string
    89  	f    func(*testing.T, snowflakeTestConstructor[T], T)
    90  }
    91  
    92  func getErrorDrivenSnowflakeSingleChoiceSuite[T comparable]() []snowflakeTestSingleChoice[T] {
    93  	return []snowflakeTestSingleChoice[T]{
    94  		{
    95  			name: "TerminateInBetaPolls",
    96  			f:    executeErrorDrivenTerminatesInBetaPolls[T],
    97  		},
    98  		{
    99  			name: "Reset",
   100  			f:    executeErrorDrivenReset[T],
   101  		},
   102  		{
   103  			name: "ResetHighestAlphaConfidence",
   104  			f:    executeErrorDrivenResetHighestAlphaConfidence[T],
   105  		},
   106  	}
   107  }
   108  
   109  func executeErrorDrivenSwitchChoices[T comparable](t *testing.T, newSnowflakeTest snowflakeTestConstructor[T], choice0, choice1 T) {
   110  	sfTest := newSnowflakeTest(t, alphaPreference, terminationConditions)
   111  
   112  	sfTest.RecordPoll(3, choice0)
   113  	sfTest.AssertEqual([]int{1, 0, 0}, false, choice0)
   114  
   115  	sfTest.RecordPoll(2, choice1)
   116  	sfTest.AssertEqual([]int{0, 0, 0}, false, choice0)
   117  
   118  	sfTest.RecordPoll(3, choice0)
   119  	sfTest.AssertEqual([]int{1, 0, 0}, false, choice0)
   120  
   121  	sfTest.RecordPoll(0, choice0)
   122  	sfTest.AssertEqual([]int{0, 0, 0}, false, choice0)
   123  
   124  	sfTest.RecordPoll(3, choice1)
   125  	sfTest.AssertEqual([]int{1, 0, 0}, false, choice1)
   126  
   127  	sfTest.RecordPoll(5, choice1)
   128  	sfTest.AssertEqual([]int{2, 1, 1}, false, choice1)
   129  	sfTest.RecordPoll(5, choice1)
   130  	sfTest.AssertEqual([]int{3, 2, 2}, true, choice1)
   131  }
   132  
   133  type snowflakeTestMultiChoice[T comparable] struct {
   134  	name string
   135  	f    func(*testing.T, snowflakeTestConstructor[T], T, T)
   136  }
   137  
   138  func getErrorDrivenSnowflakeMultiChoiceSuite[T comparable]() []snowflakeTestMultiChoice[T] {
   139  	return []snowflakeTestMultiChoice[T]{
   140  		{
   141  			name: "SwitchChoices",
   142  			f:    executeErrorDrivenSwitchChoices[T],
   143  		},
   144  	}
   145  }