github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/rewarding/protocol_test.go (about) 1 // Copyright (c) 2019 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package rewarding 7 8 import ( 9 "context" 10 "math/big" 11 "testing" 12 13 "github.com/golang/mock/gomock" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 17 "github.com/iotexproject/go-pkgs/hash" 18 "github.com/iotexproject/iotex-address/address" 19 "github.com/iotexproject/iotex-proto/golang/iotextypes" 20 21 "github.com/iotexproject/iotex-core/action" 22 "github.com/iotexproject/iotex-core/action/protocol" 23 "github.com/iotexproject/iotex-core/action/protocol/account" 24 "github.com/iotexproject/iotex-core/action/protocol/poll" 25 "github.com/iotexproject/iotex-core/action/protocol/rolldpos" 26 "github.com/iotexproject/iotex-core/blockchain/genesis" 27 "github.com/iotexproject/iotex-core/db/batch" 28 "github.com/iotexproject/iotex-core/pkg/unit" 29 "github.com/iotexproject/iotex-core/state" 30 "github.com/iotexproject/iotex-core/test/identityset" 31 "github.com/iotexproject/iotex-core/test/mock/mock_chainmanager" 32 "github.com/iotexproject/iotex-core/test/mock/mock_poll" 33 "github.com/iotexproject/iotex-core/testutil/testdb" 34 ) 35 36 func TestValidateExtension(t *testing.T) { 37 r := require.New(t) 38 39 g := genesis.Default.Rewarding 40 r.NoError(validateFoundationBonusExtension(g)) 41 42 last := g.FoundationBonusP2StartEpoch 43 g.FoundationBonusP2StartEpoch = g.FoundationBonusLastEpoch - 1 44 r.Equal(errInvalidEpoch, validateFoundationBonusExtension(g)) 45 g.FoundationBonusP2StartEpoch = last 46 47 last = g.FoundationBonusP2EndEpoch 48 g.FoundationBonusP2EndEpoch = g.FoundationBonusP2StartEpoch - 1 49 r.Equal(errInvalidEpoch, validateFoundationBonusExtension(g)) 50 g.FoundationBonusP2EndEpoch = last 51 } 52 53 func testProtocol(t *testing.T, test func(*testing.T, context.Context, protocol.StateManager, *Protocol), withExempt bool) { 54 ctrl := gomock.NewController(t) 55 56 registry := protocol.NewRegistry() 57 sm := testdb.NewMockStateManager(ctrl) 58 59 g := genesis.Default 60 // Create a test account with 1000 token 61 g.InitBalanceMap[identityset.Address(28).String()] = "1000" 62 g.Rewarding.InitBalanceStr = "0" 63 g.Rewarding.ExemptAddrStrsFromEpochReward = []string{} 64 g.Rewarding.BlockRewardStr = "10" 65 g.Rewarding.EpochRewardStr = "100" 66 g.Rewarding.NumDelegatesForEpochReward = 4 67 g.Rewarding.FoundationBonusStr = "5" 68 g.Rewarding.NumDelegatesForFoundationBonus = 5 69 g.Rewarding.FoundationBonusLastEpoch = 365 70 g.Rewarding.ProductivityThreshold = 50 71 // Initialize the protocol 72 if withExempt { 73 g.Rewarding.ExemptAddrStrsFromEpochReward = []string{ 74 identityset.Address(31).String(), 75 } 76 g.Rewarding.NumDelegatesForEpochReward = 10 77 } 78 rp := rolldpos.NewProtocol( 79 g.NumCandidateDelegates, 80 g.NumDelegates, 81 g.NumSubEpochs, 82 rolldpos.EnableDardanellesSubEpoch(g.DardanellesBlockHeight, g.DardanellesNumSubEpochs), 83 ) 84 p := NewProtocol(g.Rewarding) 85 candidates := []*state.Candidate{ 86 { 87 Address: identityset.Address(27).String(), 88 Votes: unit.ConvertIotxToRau(4000000), 89 RewardAddress: identityset.Address(0).String(), 90 }, 91 { 92 Address: identityset.Address(28).String(), 93 Votes: unit.ConvertIotxToRau(3000000), 94 RewardAddress: identityset.Address(28).String(), 95 }, 96 { 97 Address: identityset.Address(29).String(), 98 Votes: unit.ConvertIotxToRau(2000000), 99 RewardAddress: identityset.Address(29).String(), 100 }, 101 { 102 Address: identityset.Address(30).String(), 103 Votes: unit.ConvertIotxToRau(1000000), 104 RewardAddress: identityset.Address(30).String(), 105 }, 106 { 107 Address: identityset.Address(31).String(), 108 Votes: unit.ConvertIotxToRau(500000), 109 RewardAddress: identityset.Address(31).String(), 110 }, 111 { 112 Address: identityset.Address(32).String(), 113 Votes: unit.ConvertIotxToRau(500000), 114 RewardAddress: identityset.Address(32).String(), 115 }, 116 } 117 abps := []*state.Candidate{ 118 { 119 Address: identityset.Address(27).String(), 120 Votes: unit.ConvertIotxToRau(4000000), 121 RewardAddress: identityset.Address(0).String(), 122 }, 123 { 124 Address: identityset.Address(28).String(), 125 Votes: unit.ConvertIotxToRau(3000000), 126 RewardAddress: identityset.Address(28).String(), 127 }, 128 { 129 Address: identityset.Address(29).String(), 130 Votes: unit.ConvertIotxToRau(2000000), 131 RewardAddress: identityset.Address(29).String(), 132 }, 133 { 134 Address: identityset.Address(30).String(), 135 Votes: unit.ConvertIotxToRau(1000000), 136 RewardAddress: identityset.Address(30).String(), 137 }, 138 { 139 Address: identityset.Address(31).String(), 140 Votes: unit.ConvertIotxToRau(500000), 141 RewardAddress: identityset.Address(31).String(), 142 }, 143 } 144 pp := mock_poll.NewMockProtocol(ctrl) 145 pp.EXPECT().Candidates(gomock.Any(), gomock.Any()).Return(candidates, nil).AnyTimes() 146 pp.EXPECT().Delegates(gomock.Any(), gomock.Any()).Return(abps, nil).AnyTimes() 147 pp.EXPECT().Register(gomock.Any()).DoAndReturn(func(reg *protocol.Registry) error { 148 return reg.Register("poll", pp) 149 }).AnyTimes() 150 pp.EXPECT().CalculateUnproductiveDelegates(gomock.Any(), gomock.Any()).Return( 151 []string{ 152 identityset.Address(29).String(), 153 identityset.Address(31).String(), 154 }, nil, 155 ).AnyTimes() 156 require.NoError(t, rp.Register(registry)) 157 require.NoError(t, pp.Register(registry)) 158 require.NoError(t, p.Register(registry)) 159 160 ctx := protocol.WithBlockCtx( 161 context.Background(), 162 protocol.BlockCtx{ 163 BlockHeight: 0, 164 }, 165 ) 166 ctx = genesis.WithGenesisContext(ctx, g) 167 ctx = protocol.WithFeatureCtx(ctx) 168 ap := account.NewProtocol(DepositGas) 169 require.NoError(t, ap.Register(registry)) 170 require.NoError(t, ap.CreateGenesisStates(ctx, sm)) 171 require.NoError(t, p.CreateGenesisStates(ctx, sm)) 172 173 ctx = protocol.WithBlockCtx( 174 ctx, protocol.BlockCtx{ 175 Producer: identityset.Address(27), 176 BlockHeight: genesis.Default.NumDelegates * genesis.Default.NumSubEpochs, 177 }, 178 ) 179 ctx = protocol.WithActionCtx( 180 ctx, protocol.ActionCtx{ 181 Caller: identityset.Address(28), 182 }, 183 ) 184 ctx = protocol.WithBlockchainCtx( 185 protocol.WithRegistry(ctx, registry), protocol.BlockchainCtx{ 186 Tip: protocol.TipInfo{ 187 Height: 20, 188 }, 189 }, 190 ) 191 blockReward, err := p.BlockReward(ctx, sm) 192 require.NoError(t, err) 193 assert.Equal(t, big.NewInt(10), blockReward) 194 epochReward, err := p.EpochReward(ctx, sm) 195 require.NoError(t, err) 196 assert.Equal(t, big.NewInt(100), epochReward) 197 fb, err := p.FoundationBonus(ctx, sm) 198 require.NoError(t, err) 199 assert.Equal(t, big.NewInt(5), fb) 200 ndffb, err := p.NumDelegatesForFoundationBonus(ctx, sm) 201 require.NoError(t, err) 202 assert.Equal(t, uint64(5), ndffb) 203 fble, err := p.FoundationBonusLastEpoch(ctx, sm) 204 require.NoError(t, err) 205 assert.Equal(t, uint64(365), fble) 206 pt, err := p.ProductivityThreshold(ctx, sm) 207 require.NoError(t, err) 208 assert.Equal(t, uint64(50), pt) 209 210 totalBalance, _, err := p.TotalBalance(ctx, sm) 211 require.NoError(t, err) 212 assert.Equal(t, big.NewInt(0), totalBalance) 213 availableBalance, _, err := p.AvailableBalance(ctx, sm) 214 require.NoError(t, err) 215 assert.Equal(t, big.NewInt(0), availableBalance) 216 217 test(t, ctx, sm, p) 218 } 219 220 func TestProtocol_Validate(t *testing.T) { 221 g := genesis.Default 222 g.NewfoundlandBlockHeight = 0 223 p := NewProtocol(g.Rewarding) 224 act := createGrantRewardAction(0, uint64(0)).Action() 225 ctx := protocol.WithBlockCtx( 226 context.Background(), 227 protocol.BlockCtx{ 228 Producer: identityset.Address(0), 229 BlockHeight: genesis.Default.NumDelegates * genesis.Default.NumSubEpochs, 230 }, 231 ) 232 ctx = genesis.WithGenesisContext( 233 ctx, 234 genesis.Genesis{}, 235 ) 236 ctx = protocol.WithActionCtx( 237 protocol.WithFeatureCtx(ctx), 238 protocol.ActionCtx{ 239 Caller: identityset.Address(0), 240 GasPrice: big.NewInt(0), 241 }, 242 ) 243 require.NoError(t, p.Validate(ctx, act, nil)) 244 ctx = protocol.WithActionCtx( 245 ctx, 246 protocol.ActionCtx{ 247 Caller: identityset.Address(1), 248 GasPrice: big.NewInt(0), 249 }, 250 ) 251 require.Error(t, p.Validate(ctx, act, nil)) 252 ctx = protocol.WithActionCtx( 253 ctx, 254 protocol.ActionCtx{ 255 Caller: identityset.Address(0), 256 GasPrice: big.NewInt(1), 257 }, 258 ) 259 require.Error(t, p.Validate(ctx, act, nil)) 260 require.Error(t, p.Validate(ctx, act, nil)) 261 ctx = protocol.WithActionCtx( 262 ctx, 263 protocol.ActionCtx{ 264 Caller: identityset.Address(0), 265 GasPrice: big.NewInt(0), 266 IntrinsicGas: 1, 267 }, 268 ) 269 require.Error(t, p.Validate(ctx, act, nil)) 270 } 271 272 func TestProtocol_Handle(t *testing.T) { 273 ctrl := gomock.NewController(t) 274 275 g := genesis.Default 276 registry := protocol.NewRegistry() 277 sm := mock_chainmanager.NewMockStateManager(ctrl) 278 cb := batch.NewCachedBatch() 279 sm.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn( 280 func(account interface{}, opts ...protocol.StateOption) (uint64, error) { 281 cfg, err := protocol.CreateStateConfig(opts...) 282 if err != nil { 283 return 0, err 284 } 285 val, err := cb.Get("state", cfg.Key) 286 if err != nil { 287 return 0, state.ErrStateNotExist 288 } 289 return 0, state.Deserialize(account, val) 290 }).AnyTimes() 291 sm.EXPECT().PutState(gomock.Any(), gomock.Any()).DoAndReturn( 292 func(account interface{}, opts ...protocol.StateOption) (uint64, error) { 293 cfg, err := protocol.CreateStateConfig(opts...) 294 if err != nil { 295 return 0, err 296 } 297 ss, err := state.Serialize(account) 298 if err != nil { 299 return 0, err 300 } 301 cb.Put("state", cfg.Key, ss, "failed to put state") 302 return 0, nil 303 }).AnyTimes() 304 sm.EXPECT().Snapshot().Return(1).AnyTimes() 305 sm.EXPECT().Revert(gomock.Any()).Return(nil).AnyTimes() 306 307 g.Rewarding.InitBalanceStr = "1000000" 308 g.Rewarding.BlockRewardStr = "10" 309 g.Rewarding.EpochRewardStr = "100" 310 g.Rewarding.NumDelegatesForEpochReward = 10 311 g.Rewarding.ExemptAddrStrsFromEpochReward = []string{} 312 g.Rewarding.FoundationBonusStr = "5" 313 g.Rewarding.NumDelegatesForFoundationBonus = 5 314 g.Rewarding.FoundationBonusLastEpoch = 0 315 g.Rewarding.ProductivityThreshold = 50 316 // Create a test account with 1000000 token 317 g.InitBalanceMap[identityset.Address(0).String()] = "1000000" 318 g.NumSubEpochs = 15 319 rp := rolldpos.NewProtocol( 320 g.NumCandidateDelegates, 321 g.NumDelegates, 322 g.NumSubEpochs, 323 rolldpos.EnableDardanellesSubEpoch(g.DardanellesBlockHeight, g.DardanellesNumSubEpochs), 324 ) 325 require.Equal(t, g.FairbankBlockHeight, rp.GetEpochHeight(g.FoundationBonusP2StartEpoch)) 326 require.Equal(t, g.FoundationBonusP2StartEpoch, rp.GetEpochNum(g.FairbankBlockHeight)) 327 require.Equal(t, g.FoundationBonusP2EndEpoch, g.FoundationBonusP2StartEpoch+24*365) 328 require.NoError(t, rp.Register(registry)) 329 pp := poll.NewLifeLongDelegatesProtocol(g.Delegates) 330 require.NoError(t, pp.Register(registry)) 331 p := NewProtocol(g.Rewarding) 332 require.NoError(t, p.Register(registry)) 333 // Test for ForceRegister 334 require.NoError(t, p.ForceRegister(registry)) 335 336 // address package also defined protocol address, make sure they match 337 require.Equal(t, p.addr.Bytes(), address.RewardingProtocolAddrHash[:]) 338 rwdAddr, err := address.FromString(address.RewardingProtocol) 339 require.NoError(t, err) 340 require.Equal(t, p.addr.Bytes(), rwdAddr.Bytes()) 341 342 ctx := protocol.WithBlockCtx( 343 context.Background(), 344 protocol.BlockCtx{ 345 BlockHeight: 0, 346 }, 347 ) 348 349 ctx = genesis.WithGenesisContext(protocol.WithRegistry(ctx, registry), g) 350 ctx = protocol.WithFeatureCtx(ctx) 351 ap := account.NewProtocol(DepositGas) 352 require.NoError(t, ap.Register(registry)) 353 require.NoError(t, ap.CreateGenesisStates(ctx, sm)) 354 require.NoError(t, p.CreateGenesisStates(ctx, sm)) 355 356 ctx = protocol.WithBlockCtx( 357 ctx, 358 protocol.BlockCtx{ 359 Producer: identityset.Address(0), 360 BlockHeight: genesis.Default.NumDelegates * genesis.Default.NumSubEpochs, 361 }, 362 ) 363 ctx = protocol.WithActionCtx( 364 ctx, 365 protocol.ActionCtx{ 366 Caller: identityset.Address(0), 367 GasPrice: big.NewInt(0), 368 Nonce: 1, 369 }, 370 ) 371 372 // Deposit 373 db := action.DepositToRewardingFundBuilder{} 374 deposit := db.SetAmount(big.NewInt(1000000)).Build() 375 eb1 := action.EnvelopeBuilder{} 376 e1 := eb1.SetNonce(1). 377 SetGasPrice(big.NewInt(0)). 378 SetGasLimit(deposit.GasLimit()). 379 SetAction(&deposit). 380 Build() 381 se1, err := action.Sign(e1, identityset.PrivateKey(0)) 382 require.NoError(t, err) 383 384 _, err = p.Handle(ctx, se1.Action(), sm) 385 require.NoError(t, err) 386 balance, _, err := p.TotalBalance(ctx, sm) 387 require.NoError(t, err) 388 assert.Equal(t, big.NewInt(2000000), balance) 389 390 // Grant 391 // Test for createGrantRewardAction 392 e2 := createGrantRewardAction(0, uint64(0)) 393 se2, err := action.Sign(e2, identityset.PrivateKey(0)) 394 require.NoError(t, err) 395 ctx = protocol.WithActionCtx( 396 ctx, 397 protocol.ActionCtx{ 398 Caller: identityset.Address(0), 399 GasPrice: big.NewInt(0), 400 Nonce: 0, 401 }, 402 ) 403 receipt, err := p.Handle(ctx, se2.Action(), sm) 404 require.NoError(t, err) 405 assert.Equal(t, uint64(iotextypes.ReceiptStatus_Success), receipt.Status) 406 assert.Equal(t, 1, len(receipt.Logs())) 407 ctx = protocol.WithActionCtx( 408 ctx, 409 protocol.ActionCtx{ 410 Caller: identityset.Address(0), 411 GasPrice: big.NewInt(0), 412 Nonce: 0, 413 }, 414 ) 415 // Grant the block reward again should fail 416 receipt, err = p.Handle(ctx, se2.Action(), sm) 417 require.NoError(t, err) 418 assert.Equal(t, uint64(iotextypes.ReceiptStatus_Failure), receipt.Status) 419 420 // Claim 421 claimBuilder := action.ClaimFromRewardingFundBuilder{} 422 claim := claimBuilder.SetAmount(big.NewInt(1000000)).Build() 423 eb3 := action.EnvelopeBuilder{} 424 e3 := eb3.SetNonce(4). 425 SetGasPrice(big.NewInt(0)). 426 SetGasLimit(claim.GasLimit()). 427 SetAction(&claim). 428 Build() 429 se3, err := action.Sign(e3, identityset.PrivateKey(0)) 430 require.NoError(t, err) 431 ctx = protocol.WithActionCtx( 432 ctx, 433 protocol.ActionCtx{ 434 Caller: identityset.Address(0), 435 GasPrice: big.NewInt(0), 436 Nonce: 2, 437 }, 438 ) 439 _, err = p.Handle(ctx, se3.Action(), sm) 440 require.NoError(t, err) 441 balance, _, err = p.TotalBalance(ctx, sm) 442 require.NoError(t, err) 443 assert.Equal(t, big.NewInt(1000000), balance) 444 445 // Test CreatePreStates 446 ctx = protocol.WithBlockCtx( 447 ctx, 448 protocol.BlockCtx{ 449 BlockHeight: 1816201, 450 }, 451 ) 452 require.NoError(t, p.CreatePreStates(ctx, sm)) 453 blockReward, err := p.BlockReward(ctx, sm) 454 require.NoError(t, err) 455 assert.Equal(t, big.NewInt(8000000000000000000), blockReward) 456 457 ctx = protocol.WithBlockCtx( 458 ctx, 459 protocol.BlockCtx{ 460 BlockHeight: 864001, 461 }, 462 ) 463 require.NoError(t, p.CreatePreStates(ctx, sm)) 464 BlockReward, err := p.BlockReward(ctx, sm) 465 require.NoError(t, err) 466 assert.Equal(t, big.NewInt(8000000000000000000), BlockReward) 467 468 // Test for CreatePostSystemActions 469 grants, err := p.CreatePostSystemActions(ctx, sm) 470 require.NoError(t, err) 471 require.NotNil(t, grants) 472 473 // Test for ReadState 474 testMethods := []struct { 475 input string 476 expect []byte 477 }{ 478 { 479 input: "AvailableBalance", 480 expect: []byte{49, 57, 57, 57, 57, 57, 48}, 481 }, 482 { 483 input: "TotalBalance", 484 expect: []byte{49, 48, 48, 48, 48, 48, 48}, 485 }, 486 { 487 input: "UnclaimedBalance", 488 expect: []byte{48}, 489 }, 490 } 491 492 for _, ts := range testMethods { 493 494 if ts.input == "UnclaimedBalance" { 495 UnclaimedBalance, _, err := p.ReadState(ctx, sm, []byte(ts.input), nil) 496 require.Nil(t, UnclaimedBalance) 497 require.Error(t, err) 498 499 arg1 := []byte("io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqd39ym7") 500 arg2 := []byte("io1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqd39ym8") 501 UnclaimedBalance, _, err = p.ReadState(ctx, sm, []byte(ts.input), arg1, arg2) 502 require.Nil(t, UnclaimedBalance) 503 require.Error(t, err) 504 505 UnclaimedBalance, _, err = p.ReadState(ctx, sm, []byte(ts.input), arg1) 506 require.Equal(t, ts.expect, UnclaimedBalance) 507 require.NoError(t, err) 508 continue 509 } 510 511 output, _, err := p.ReadState(ctx, sm, []byte(ts.input), nil) 512 require.NoError(t, err) 513 require.Equal(t, ts.expect, output) 514 } 515 516 // Test for deleteState 517 sm.EXPECT().DelState(gomock.Any()).DoAndReturn(func(addrHash hash.Hash160) error { 518 cb.Delete("state", addrHash[:], "failed to delete state") 519 return nil 520 }).AnyTimes() 521 } 522 523 func TestStateCheckLegacy(t *testing.T) { 524 require := require.New(t) 525 526 ctrl := gomock.NewController(t) 527 sm := testdb.NewMockStateManager(ctrl) 528 p := NewProtocol(genesis.Default.Rewarding) 529 chainCtx := genesis.WithGenesisContext( 530 context.Background(), 531 genesis.Genesis{ 532 Blockchain: genesis.Blockchain{GreenlandBlockHeight: 3}, 533 }, 534 ) 535 ctx := protocol.WithBlockCtx(chainCtx, protocol.BlockCtx{ 536 BlockHeight: 2, 537 }) 538 ctx = protocol.WithFeatureCtx(ctx) 539 540 tests := []struct { 541 before, add *big.Int 542 v1 [2]bool 543 }{ 544 { 545 big.NewInt(100), big.NewInt(20), [2]bool{true, true}, 546 }, 547 { 548 big.NewInt(120), big.NewInt(30), [2]bool{true, false}, 549 }, 550 } 551 552 // put V1 value 553 addr := identityset.Address(1) 554 acc := rewardAccount{ 555 balance: tests[0].before, 556 } 557 accKey := append(_adminKey, addr.Bytes()...) 558 require.NoError(p.putState(ctx, sm, accKey, &acc)) 559 560 for useV2 := 0; useV2 < 2; useV2++ { 561 if useV2 == 0 { 562 require.False(useV2Storage(ctx)) 563 } else { 564 require.True(useV2Storage(ctx)) 565 } 566 for i := 0; i < 2; i++ { 567 _, v1, err := p.stateCheckLegacy(ctx, sm, accKey, &acc) 568 require.Equal(tests[useV2].v1[i], v1) 569 require.NoError(err) 570 if i == 0 { 571 require.Equal(tests[useV2].before, acc.balance) 572 } else { 573 require.Equal(tests[useV2].before.Add(tests[useV2].before, tests[useV2].add), acc.balance) 574 } 575 if i == 0 { 576 // grant to existing addr 577 require.NoError(p.grantToAccount(ctx, sm, addr, tests[useV2].add)) 578 // grant to new addr 579 newAddr := identityset.Address(4 + useV2) 580 require.NoError(p.grantToAccount(ctx, sm, newAddr, tests[useV2].add)) 581 _, err = p.state(ctx, sm, append(_adminKey, newAddr.Bytes()...), &acc) 582 require.NoError(err) 583 require.Equal(acc.balance, tests[useV2].add) 584 } 585 } 586 587 if useV2 == 0 { 588 // switch to test V2 589 ctx = protocol.WithBlockCtx(chainCtx, protocol.BlockCtx{ 590 BlockHeight: 3, 591 }) 592 ctx = protocol.WithFeatureCtx(ctx) 593 } 594 } 595 596 // verify V1 deleted 597 _, err := p.stateV1(sm, accKey, &acc) 598 require.Equal(state.ErrStateNotExist, err) 599 _, err = p.stateV2(sm, accKey, &acc) 600 require.NoError(err) 601 require.Equal(tests[1].before, acc.balance) 602 } 603 604 func TestMigrateValue(t *testing.T) { 605 r := require.New(t) 606 607 a1 := admin{ 608 blockReward: big.NewInt(10), 609 epochReward: big.NewInt(100), 610 numDelegatesForEpochReward: 10, 611 foundationBonus: big.NewInt(5), 612 numDelegatesForFoundationBonus: 5, 613 foundationBonusLastEpoch: 365, 614 productivityThreshold: 50, 615 } 616 f1 := fund{ 617 totalBalance: new(big.Int), 618 unclaimedBalance: new(big.Int), 619 } 620 e1 := exempt{ 621 []address.Address{identityset.Address(31)}, 622 } 623 g := genesis.Default 624 625 testProtocol(t, func(t *testing.T, ctx context.Context, sm protocol.StateManager, p *Protocol) { 626 // verify v1 state 627 a := admin{} 628 _, err := p.stateV1(sm, _adminKey, &a) 629 r.NoError(err) 630 r.Equal(a1, a) 631 632 f := fund{} 633 _, err = p.stateV1(sm, _fundKey, &f) 634 r.NoError(err) 635 r.Equal(f1, f) 636 637 e := exempt{} 638 _, err = p.stateV1(sm, _exemptKey, &e) 639 r.NoError(err) 640 r.Equal(e1, e) 641 642 // use numSubEpochs = 15 643 rp := rolldpos.NewProtocol( 644 g.NumCandidateDelegates, 645 g.NumDelegates, 646 15, 647 rolldpos.EnableDardanellesSubEpoch(g.DardanellesBlockHeight, g.DardanellesNumSubEpochs), 648 ) 649 reg, ok := protocol.GetRegistry(ctx) 650 r.True(ok) 651 r.NoError(rp.ForceRegister(reg)) 652 653 for _, v := range []struct { 654 height, lastEpoch uint64 655 }{ 656 {g.GreenlandBlockHeight, a1.foundationBonusLastEpoch}, 657 {1641601, g.FoundationBonusP2EndEpoch}, 658 {g.KamchatkaBlockHeight, 30473}, 659 } { 660 fCtx := ctx 661 if v.height == 1641601 { 662 // test the case where newStartEpoch < cfg.FoundationBonusP2StartEpoch 663 g1 := g 664 g1.GreenlandBlockHeight = v.height - 720 665 g1.KamchatkaBlockHeight = v.height 666 fCtx = genesis.WithGenesisContext(ctx, g1) 667 } 668 blkCtx := protocol.MustGetBlockCtx(ctx) 669 blkCtx.BlockHeight = v.height 670 fCtx = protocol.WithFeatureCtx(protocol.WithBlockCtx(fCtx, blkCtx)) 671 r.NoError(p.CreatePreStates(fCtx, sm)) 672 673 // verify v1 is deleted 674 _, err = p.stateV1(sm, _adminKey, &a) 675 r.Equal(state.ErrStateNotExist, err) 676 _, err = p.stateV1(sm, _fundKey, &f) 677 r.Equal(state.ErrStateNotExist, err) 678 _, err = p.stateV1(sm, _exemptKey, &e) 679 r.Equal(state.ErrStateNotExist, err) 680 681 // verify v2 exist 682 _, err = p.stateV2(sm, _adminKey, &a) 683 r.NoError(err) 684 _, err = p.stateV2(sm, _fundKey, &f) 685 r.NoError(err) 686 r.Equal(f1, f) 687 _, err = p.stateV2(sm, _exemptKey, &e) 688 r.NoError(err) 689 r.Equal(e1, e) 690 691 switch v.height { 692 case g.GreenlandBlockHeight: 693 r.Equal(a1, a) 694 // test migrate with no data 695 r.NoError(p.migrateValueGreenland(ctx, sm)) 696 default: 697 r.Equal(v.lastEpoch, a.foundationBonusLastEpoch) 698 r.True(a.grantFoundationBonus(v.lastEpoch)) 699 r.False(a.grantFoundationBonus(v.lastEpoch + 1)) 700 a.foundationBonusLastEpoch = a1.foundationBonusLastEpoch 701 r.Equal(a1, a) 702 a.foundationBonusLastEpoch = v.lastEpoch 703 // test migrate with no data 704 r.NoError(p.migrateValueGreenland(ctx, sm)) 705 } 706 } 707 }, true) 708 }