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 }