github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/block/executor/block_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 executor
     5  
     6  import (
     7  	"context"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/require"
    12  	"go.uber.org/mock/gomock"
    13  
    14  	"github.com/ava-labs/avalanchego/database"
    15  	"github.com/ava-labs/avalanchego/ids"
    16  	"github.com/ava-labs/avalanchego/snow/snowtest"
    17  	"github.com/ava-labs/avalanchego/snow/uptime/uptimemock"
    18  	"github.com/ava-labs/avalanchego/utils/constants"
    19  	"github.com/ava-labs/avalanchego/vms/platformvm/block"
    20  	"github.com/ava-labs/avalanchego/vms/platformvm/config"
    21  	"github.com/ava-labs/avalanchego/vms/platformvm/reward"
    22  	"github.com/ava-labs/avalanchego/vms/platformvm/state"
    23  	"github.com/ava-labs/avalanchego/vms/platformvm/status"
    24  	"github.com/ava-labs/avalanchego/vms/platformvm/txs"
    25  	"github.com/ava-labs/avalanchego/vms/platformvm/txs/executor"
    26  )
    27  
    28  func TestBlockOptions(t *testing.T) {
    29  	type test struct {
    30  		name                   string
    31  		blkF                   func(*gomock.Controller) *Block
    32  		expectedPreferenceType block.Block
    33  	}
    34  
    35  	tests := []test{
    36  		{
    37  			name: "apricot proposal block; commit preferred",
    38  			blkF: func(ctrl *gomock.Controller) *Block {
    39  				state := state.NewMockState(ctrl)
    40  
    41  				uptimes := uptimemock.NewCalculator(ctrl)
    42  
    43  				manager := &manager{
    44  					backend: &backend{
    45  						state: state,
    46  						ctx:   snowtest.Context(t, snowtest.PChainID),
    47  					},
    48  					txExecutorBackend: &executor.Backend{
    49  						Config: &config.Config{
    50  							UptimePercentage: 0,
    51  						},
    52  						Uptimes: uptimes,
    53  					},
    54  				}
    55  
    56  				return &Block{
    57  					Block:   &block.ApricotProposalBlock{},
    58  					manager: manager,
    59  				}
    60  			},
    61  			expectedPreferenceType: &block.ApricotCommitBlock{},
    62  		},
    63  		{
    64  			name: "banff proposal block; invalid proposal tx",
    65  			blkF: func(ctrl *gomock.Controller) *Block {
    66  				state := state.NewMockState(ctrl)
    67  
    68  				uptimes := uptimemock.NewCalculator(ctrl)
    69  
    70  				manager := &manager{
    71  					backend: &backend{
    72  						state: state,
    73  						ctx:   snowtest.Context(t, snowtest.PChainID),
    74  					},
    75  					txExecutorBackend: &executor.Backend{
    76  						Config: &config.Config{
    77  							UptimePercentage: 0,
    78  						},
    79  						Uptimes: uptimes,
    80  					},
    81  				}
    82  
    83  				return &Block{
    84  					Block: &block.BanffProposalBlock{
    85  						ApricotProposalBlock: block.ApricotProposalBlock{
    86  							Tx: &txs.Tx{
    87  								Unsigned: &txs.CreateChainTx{},
    88  							},
    89  						},
    90  					},
    91  					manager: manager,
    92  				}
    93  			},
    94  			expectedPreferenceType: &block.BanffCommitBlock{},
    95  		},
    96  		{
    97  			name: "banff proposal block; missing tx",
    98  			blkF: func(ctrl *gomock.Controller) *Block {
    99  				stakerTxID := ids.GenerateTestID()
   100  
   101  				state := state.NewMockState(ctrl)
   102  				state.EXPECT().GetTx(stakerTxID).Return(nil, status.Unknown, database.ErrNotFound)
   103  
   104  				uptimes := uptimemock.NewCalculator(ctrl)
   105  
   106  				manager := &manager{
   107  					backend: &backend{
   108  						state: state,
   109  						ctx:   snowtest.Context(t, snowtest.PChainID),
   110  					},
   111  					txExecutorBackend: &executor.Backend{
   112  						Config: &config.Config{
   113  							UptimePercentage: 0,
   114  						},
   115  						Uptimes: uptimes,
   116  					},
   117  				}
   118  
   119  				return &Block{
   120  					Block: &block.BanffProposalBlock{
   121  						ApricotProposalBlock: block.ApricotProposalBlock{
   122  							Tx: &txs.Tx{
   123  								Unsigned: &txs.RewardValidatorTx{
   124  									TxID: stakerTxID,
   125  								},
   126  							},
   127  						},
   128  					},
   129  					manager: manager,
   130  				}
   131  			},
   132  			expectedPreferenceType: &block.BanffCommitBlock{},
   133  		},
   134  		{
   135  			name: "banff proposal block; error fetching staker tx",
   136  			blkF: func(ctrl *gomock.Controller) *Block {
   137  				stakerTxID := ids.GenerateTestID()
   138  
   139  				state := state.NewMockState(ctrl)
   140  				state.EXPECT().GetTx(stakerTxID).Return(nil, status.Unknown, database.ErrClosed)
   141  
   142  				uptimes := uptimemock.NewCalculator(ctrl)
   143  
   144  				manager := &manager{
   145  					backend: &backend{
   146  						state: state,
   147  						ctx:   snowtest.Context(t, snowtest.PChainID),
   148  					},
   149  					txExecutorBackend: &executor.Backend{
   150  						Config: &config.Config{
   151  							UptimePercentage: 0,
   152  						},
   153  						Uptimes: uptimes,
   154  					},
   155  				}
   156  
   157  				return &Block{
   158  					Block: &block.BanffProposalBlock{
   159  						ApricotProposalBlock: block.ApricotProposalBlock{
   160  							Tx: &txs.Tx{
   161  								Unsigned: &txs.RewardValidatorTx{
   162  									TxID: stakerTxID,
   163  								},
   164  							},
   165  						},
   166  					},
   167  					manager: manager,
   168  				}
   169  			},
   170  			expectedPreferenceType: &block.BanffCommitBlock{},
   171  		},
   172  		{
   173  			name: "banff proposal block; unexpected staker tx type",
   174  			blkF: func(ctrl *gomock.Controller) *Block {
   175  				stakerTxID := ids.GenerateTestID()
   176  				stakerTx := &txs.Tx{
   177  					Unsigned: &txs.CreateChainTx{},
   178  				}
   179  
   180  				state := state.NewMockState(ctrl)
   181  				state.EXPECT().GetTx(stakerTxID).Return(stakerTx, status.Committed, nil)
   182  
   183  				uptimes := uptimemock.NewCalculator(ctrl)
   184  
   185  				manager := &manager{
   186  					backend: &backend{
   187  						state: state,
   188  						ctx:   snowtest.Context(t, snowtest.PChainID),
   189  					},
   190  					txExecutorBackend: &executor.Backend{
   191  						Config: &config.Config{
   192  							UptimePercentage: 0,
   193  						},
   194  						Uptimes: uptimes,
   195  					},
   196  				}
   197  
   198  				return &Block{
   199  					Block: &block.BanffProposalBlock{
   200  						ApricotProposalBlock: block.ApricotProposalBlock{
   201  							Tx: &txs.Tx{
   202  								Unsigned: &txs.RewardValidatorTx{
   203  									TxID: stakerTxID,
   204  								},
   205  							},
   206  						},
   207  					},
   208  					manager: manager,
   209  				}
   210  			},
   211  			expectedPreferenceType: &block.BanffCommitBlock{},
   212  		},
   213  		{
   214  			name: "banff proposal block; missing primary network validator",
   215  			blkF: func(ctrl *gomock.Controller) *Block {
   216  				var (
   217  					stakerTxID = ids.GenerateTestID()
   218  					nodeID     = ids.GenerateTestNodeID()
   219  					subnetID   = ids.GenerateTestID()
   220  					stakerTx   = &txs.Tx{
   221  						Unsigned: &txs.AddPermissionlessValidatorTx{
   222  							Validator: txs.Validator{
   223  								NodeID: nodeID,
   224  							},
   225  							Subnet: subnetID,
   226  						},
   227  					}
   228  				)
   229  
   230  				state := state.NewMockState(ctrl)
   231  				state.EXPECT().GetTx(stakerTxID).Return(stakerTx, status.Committed, nil)
   232  				state.EXPECT().GetCurrentValidator(constants.PrimaryNetworkID, nodeID).Return(nil, database.ErrNotFound)
   233  
   234  				uptimes := uptimemock.NewCalculator(ctrl)
   235  
   236  				manager := &manager{
   237  					backend: &backend{
   238  						state: state,
   239  						ctx:   snowtest.Context(t, snowtest.PChainID),
   240  					},
   241  					txExecutorBackend: &executor.Backend{
   242  						Config: &config.Config{
   243  							UptimePercentage: 0,
   244  						},
   245  						Uptimes: uptimes,
   246  					},
   247  				}
   248  
   249  				return &Block{
   250  					Block: &block.BanffProposalBlock{
   251  						ApricotProposalBlock: block.ApricotProposalBlock{
   252  							Tx: &txs.Tx{
   253  								Unsigned: &txs.RewardValidatorTx{
   254  									TxID: stakerTxID,
   255  								},
   256  							},
   257  						},
   258  					},
   259  					manager: manager,
   260  				}
   261  			},
   262  			expectedPreferenceType: &block.BanffCommitBlock{},
   263  		},
   264  		{
   265  			name: "banff proposal block; failed calculating primary network uptime",
   266  			blkF: func(ctrl *gomock.Controller) *Block {
   267  				var (
   268  					stakerTxID = ids.GenerateTestID()
   269  					nodeID     = ids.GenerateTestNodeID()
   270  					subnetID   = constants.PrimaryNetworkID
   271  					stakerTx   = &txs.Tx{
   272  						Unsigned: &txs.AddPermissionlessValidatorTx{
   273  							Validator: txs.Validator{
   274  								NodeID: nodeID,
   275  							},
   276  							Subnet: subnetID,
   277  						},
   278  					}
   279  					primaryNetworkValidatorStartTime = time.Now()
   280  					staker                           = &state.Staker{
   281  						StartTime: primaryNetworkValidatorStartTime,
   282  					}
   283  				)
   284  
   285  				state := state.NewMockState(ctrl)
   286  				state.EXPECT().GetTx(stakerTxID).Return(stakerTx, status.Committed, nil)
   287  				state.EXPECT().GetCurrentValidator(constants.PrimaryNetworkID, nodeID).Return(staker, nil)
   288  
   289  				uptimes := uptimemock.NewCalculator(ctrl)
   290  				uptimes.EXPECT().CalculateUptimePercentFrom(nodeID, constants.PrimaryNetworkID, primaryNetworkValidatorStartTime).Return(0.0, database.ErrNotFound)
   291  
   292  				manager := &manager{
   293  					backend: &backend{
   294  						state: state,
   295  						ctx:   snowtest.Context(t, snowtest.PChainID),
   296  					},
   297  					txExecutorBackend: &executor.Backend{
   298  						Config: &config.Config{
   299  							UptimePercentage: 0,
   300  						},
   301  						Uptimes: uptimes,
   302  					},
   303  				}
   304  
   305  				return &Block{
   306  					Block: &block.BanffProposalBlock{
   307  						ApricotProposalBlock: block.ApricotProposalBlock{
   308  							Tx: &txs.Tx{
   309  								Unsigned: &txs.RewardValidatorTx{
   310  									TxID: stakerTxID,
   311  								},
   312  							},
   313  						},
   314  					},
   315  					manager: manager,
   316  				}
   317  			},
   318  			expectedPreferenceType: &block.BanffCommitBlock{},
   319  		},
   320  		{
   321  			name: "banff proposal block; failed fetching subnet transformation",
   322  			blkF: func(ctrl *gomock.Controller) *Block {
   323  				var (
   324  					stakerTxID = ids.GenerateTestID()
   325  					nodeID     = ids.GenerateTestNodeID()
   326  					subnetID   = ids.GenerateTestID()
   327  					stakerTx   = &txs.Tx{
   328  						Unsigned: &txs.AddPermissionlessValidatorTx{
   329  							Validator: txs.Validator{
   330  								NodeID: nodeID,
   331  							},
   332  							Subnet: subnetID,
   333  						},
   334  					}
   335  					primaryNetworkValidatorStartTime = time.Now()
   336  					staker                           = &state.Staker{
   337  						StartTime: primaryNetworkValidatorStartTime,
   338  					}
   339  				)
   340  
   341  				state := state.NewMockState(ctrl)
   342  				state.EXPECT().GetTx(stakerTxID).Return(stakerTx, status.Committed, nil)
   343  				state.EXPECT().GetCurrentValidator(constants.PrimaryNetworkID, nodeID).Return(staker, nil)
   344  				state.EXPECT().GetSubnetTransformation(subnetID).Return(nil, database.ErrNotFound)
   345  
   346  				uptimes := uptimemock.NewCalculator(ctrl)
   347  
   348  				manager := &manager{
   349  					backend: &backend{
   350  						state: state,
   351  						ctx:   snowtest.Context(t, snowtest.PChainID),
   352  					},
   353  					txExecutorBackend: &executor.Backend{
   354  						Config: &config.Config{
   355  							UptimePercentage: 0,
   356  						},
   357  						Uptimes: uptimes,
   358  					},
   359  				}
   360  
   361  				return &Block{
   362  					Block: &block.BanffProposalBlock{
   363  						ApricotProposalBlock: block.ApricotProposalBlock{
   364  							Tx: &txs.Tx{
   365  								Unsigned: &txs.RewardValidatorTx{
   366  									TxID: stakerTxID,
   367  								},
   368  							},
   369  						},
   370  					},
   371  					manager: manager,
   372  				}
   373  			},
   374  			expectedPreferenceType: &block.BanffCommitBlock{},
   375  		},
   376  		{
   377  			name: "banff proposal block; prefers commit",
   378  			blkF: func(ctrl *gomock.Controller) *Block {
   379  				var (
   380  					stakerTxID = ids.GenerateTestID()
   381  					nodeID     = ids.GenerateTestNodeID()
   382  					subnetID   = ids.GenerateTestID()
   383  					stakerTx   = &txs.Tx{
   384  						Unsigned: &txs.AddPermissionlessValidatorTx{
   385  							Validator: txs.Validator{
   386  								NodeID: nodeID,
   387  							},
   388  							Subnet: subnetID,
   389  						},
   390  					}
   391  					primaryNetworkValidatorStartTime = time.Now()
   392  					staker                           = &state.Staker{
   393  						StartTime: primaryNetworkValidatorStartTime,
   394  					}
   395  					transformSubnetTx = &txs.Tx{
   396  						Unsigned: &txs.TransformSubnetTx{
   397  							UptimeRequirement: .2 * reward.PercentDenominator,
   398  						},
   399  					}
   400  				)
   401  
   402  				state := state.NewMockState(ctrl)
   403  				state.EXPECT().GetTx(stakerTxID).Return(stakerTx, status.Committed, nil)
   404  				state.EXPECT().GetCurrentValidator(constants.PrimaryNetworkID, nodeID).Return(staker, nil)
   405  				state.EXPECT().GetSubnetTransformation(subnetID).Return(transformSubnetTx, nil)
   406  
   407  				uptimes := uptimemock.NewCalculator(ctrl)
   408  				uptimes.EXPECT().CalculateUptimePercentFrom(nodeID, constants.PrimaryNetworkID, primaryNetworkValidatorStartTime).Return(.5, nil)
   409  
   410  				manager := &manager{
   411  					backend: &backend{
   412  						state: state,
   413  						ctx:   snowtest.Context(t, snowtest.PChainID),
   414  					},
   415  					txExecutorBackend: &executor.Backend{
   416  						Config: &config.Config{
   417  							UptimePercentage: .8,
   418  						},
   419  						Uptimes: uptimes,
   420  					},
   421  				}
   422  
   423  				return &Block{
   424  					Block: &block.BanffProposalBlock{
   425  						ApricotProposalBlock: block.ApricotProposalBlock{
   426  							Tx: &txs.Tx{
   427  								Unsigned: &txs.RewardValidatorTx{
   428  									TxID: stakerTxID,
   429  								},
   430  							},
   431  						},
   432  					},
   433  					manager: manager,
   434  				}
   435  			},
   436  			expectedPreferenceType: &block.BanffCommitBlock{},
   437  		},
   438  		{
   439  			name: "banff proposal block; prefers abort",
   440  			blkF: func(ctrl *gomock.Controller) *Block {
   441  				var (
   442  					stakerTxID = ids.GenerateTestID()
   443  					nodeID     = ids.GenerateTestNodeID()
   444  					subnetID   = ids.GenerateTestID()
   445  					stakerTx   = &txs.Tx{
   446  						Unsigned: &txs.AddPermissionlessValidatorTx{
   447  							Validator: txs.Validator{
   448  								NodeID: nodeID,
   449  							},
   450  							Subnet: subnetID,
   451  						},
   452  					}
   453  					primaryNetworkValidatorStartTime = time.Now()
   454  					staker                           = &state.Staker{
   455  						StartTime: primaryNetworkValidatorStartTime,
   456  					}
   457  					transformSubnetTx = &txs.Tx{
   458  						Unsigned: &txs.TransformSubnetTx{
   459  							UptimeRequirement: .6 * reward.PercentDenominator,
   460  						},
   461  					}
   462  				)
   463  
   464  				state := state.NewMockState(ctrl)
   465  				state.EXPECT().GetTx(stakerTxID).Return(stakerTx, status.Committed, nil)
   466  				state.EXPECT().GetCurrentValidator(constants.PrimaryNetworkID, nodeID).Return(staker, nil)
   467  				state.EXPECT().GetSubnetTransformation(subnetID).Return(transformSubnetTx, nil)
   468  
   469  				uptimes := uptimemock.NewCalculator(ctrl)
   470  				uptimes.EXPECT().CalculateUptimePercentFrom(nodeID, constants.PrimaryNetworkID, primaryNetworkValidatorStartTime).Return(.5, nil)
   471  
   472  				manager := &manager{
   473  					backend: &backend{
   474  						state: state,
   475  						ctx:   snowtest.Context(t, snowtest.PChainID),
   476  					},
   477  					txExecutorBackend: &executor.Backend{
   478  						Config: &config.Config{
   479  							UptimePercentage: .8,
   480  						},
   481  						Uptimes: uptimes,
   482  					},
   483  				}
   484  
   485  				return &Block{
   486  					Block: &block.BanffProposalBlock{
   487  						ApricotProposalBlock: block.ApricotProposalBlock{
   488  							Tx: &txs.Tx{
   489  								Unsigned: &txs.RewardValidatorTx{
   490  									TxID: stakerTxID,
   491  								},
   492  							},
   493  						},
   494  					},
   495  					manager: manager,
   496  				}
   497  			},
   498  			expectedPreferenceType: &block.BanffAbortBlock{},
   499  		},
   500  	}
   501  
   502  	for _, tt := range tests {
   503  		t.Run(tt.name, func(t *testing.T) {
   504  			ctrl := gomock.NewController(t)
   505  			require := require.New(t)
   506  
   507  			blk := tt.blkF(ctrl)
   508  			options, err := blk.Options(context.Background())
   509  			require.NoError(err)
   510  			require.IsType(tt.expectedPreferenceType, options[0].(*Block).Block)
   511  		})
   512  	}
   513  }