github.com/iotexproject/iotex-core@v1.14.1-rc1/e2etest/native_staking_test.go (about) 1 package e2etest 2 3 import ( 4 "context" 5 "math/big" 6 "testing" 7 "time" 8 9 "github.com/pkg/errors" 10 "github.com/stretchr/testify/require" 11 12 "github.com/iotexproject/go-pkgs/hash" 13 "github.com/iotexproject/iotex-address/address" 14 "github.com/iotexproject/iotex-proto/golang/iotextypes" 15 16 "github.com/iotexproject/iotex-core/action" 17 "github.com/iotexproject/iotex-core/action/protocol" 18 accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util" 19 "github.com/iotexproject/iotex-core/action/protocol/staking" 20 "github.com/iotexproject/iotex-core/actpool" 21 "github.com/iotexproject/iotex-core/blockchain" 22 "github.com/iotexproject/iotex-core/blockchain/block" 23 "github.com/iotexproject/iotex-core/blockchain/genesis" 24 "github.com/iotexproject/iotex-core/config" 25 "github.com/iotexproject/iotex-core/pkg/util/byteutil" 26 "github.com/iotexproject/iotex-core/server/itx" 27 "github.com/iotexproject/iotex-core/state" 28 "github.com/iotexproject/iotex-core/test/identityset" 29 "github.com/iotexproject/iotex-core/testutil" 30 ) 31 32 const ( 33 // TODO: to be removed 34 _const = byte(iota) 35 _bucket 36 _voterIndex 37 _candIndex 38 _stakingNameSpace = "Staking" 39 _candidateNameSpace = "Candidate" 40 41 candidate1Name = "candidate1" 42 candidate2Name = "candidate2" 43 candidate3Name = "candidate3" 44 ) 45 46 var ( 47 selfStake, _ = new(big.Int).SetString("1200000000000000000000000", 10) 48 cand1Votes, _ = new(big.Int).SetString("1635067133824581908640994", 10) 49 vote, _ = new(big.Int).SetString("100000000000000000000", 10) 50 autoStakeVote, _ = new(big.Int).SetString("103801784016923925869", 10) 51 initBalance, _ = new(big.Int).SetString("100000000000000000000000000", 10) 52 ) 53 54 var ( 55 gasPrice = big.NewInt(0) 56 gasLimit = uint64(1000000) 57 ) 58 59 func TestNativeStaking(t *testing.T) { 60 require := require.New(t) 61 62 testInitCands := []genesis.BootstrapCandidate{ 63 { 64 OwnerAddress: identityset.Address(22).String(), 65 OperatorAddress: identityset.Address(23).String(), 66 RewardAddress: identityset.Address(23).String(), 67 Name: "test1", 68 SelfStakingTokens: selfStake.String(), 69 }, 70 { 71 OwnerAddress: identityset.Address(24).String(), 72 OperatorAddress: identityset.Address(25).String(), 73 RewardAddress: identityset.Address(25).String(), 74 Name: "test2", 75 SelfStakingTokens: selfStake.String(), 76 }, 77 } 78 79 testNativeStaking := func(cfg config.Config, t *testing.T) { 80 ctx := context.Background() 81 82 // Create a new blockchain 83 svr, err := itx.NewServer(cfg) 84 require.NoError(err) 85 require.NoError(svr.Start(ctx)) 86 defer func() { 87 require.NoError(svr.Stop(ctx)) 88 }() 89 90 chainID := cfg.Chain.ID 91 bc := svr.ChainService(chainID).Blockchain() 92 sf := svr.ChainService(chainID).StateFactory() 93 ap := svr.ChainService(chainID).ActionPool() 94 require.NotNil(bc) 95 prtcl, ok := svr.ChainService(chainID).Registry().Find("staking") 96 require.True(ok) 97 stkPrtcl := prtcl.(*staking.Protocol) 98 99 require.True(cfg.Genesis.IsFbkMigration(1)) 100 101 // Create two candidates 102 cand1Addr := identityset.Address(0) 103 cand1PriKey := identityset.PrivateKey(0) 104 105 cand2Addr := identityset.Address(1) 106 cand2PriKey := identityset.PrivateKey(1) 107 108 // create non-stake candidate 109 cand3Addr := identityset.Address(4) 110 cand3PriKey := identityset.PrivateKey(4) 111 112 fixedTime := time.Unix(cfg.Genesis.Timestamp, 0) 113 addOneTx := func(tx *action.SealedEnvelope, err error) (*action.SealedEnvelope, *action.Receipt, error) { 114 if err != nil { 115 return tx, nil, err 116 } 117 if err := ap.Add(ctx, tx); err != nil { 118 return tx, nil, err 119 } 120 blk, err := createAndCommitBlock(bc, ap, fixedTime) 121 if err != nil { 122 return tx, nil, err 123 } 124 h, err := tx.Hash() 125 if err != nil { 126 return tx, nil, err 127 } 128 for _, r := range blk.Receipts { 129 if r.ActionHash == h { 130 return tx, r, nil 131 } 132 } 133 return tx, nil, errors.Errorf("failed to find receipt for %x", h) 134 } 135 136 register1, r1, err := addOneTx(action.SignedCandidateRegister(1, candidate1Name, cand1Addr.String(), cand1Addr.String(), 137 cand1Addr.String(), selfStake.String(), 91, true, nil, gasLimit, gasPrice, cand1PriKey)) 138 require.NoError(err) 139 register2, _, err := addOneTx(action.SignedCandidateRegister(1, candidate2Name, cand2Addr.String(), cand2Addr.String(), 140 cand2Addr.String(), selfStake.String(), 1, false, nil, gasLimit, gasPrice, cand2PriKey)) 141 require.NoError(err) 142 // check candidate state 143 require.NoError(checkCandidateState(sf, candidate1Name, cand1Addr.String(), selfStake, cand1Votes, cand1Addr)) 144 require.NoError(checkCandidateState(sf, candidate2Name, cand2Addr.String(), selfStake, selfStake, cand2Addr)) 145 146 // check candidate account state 147 require.NoError(checkAccountState(cfg, sf, register1, true, initBalance, cand1Addr)) 148 require.NoError(checkAccountState(cfg, sf, register2, true, initBalance, cand2Addr)) 149 150 // get self-stake index from receipts 151 require.EqualValues(iotextypes.ReceiptStatus_Success, r1.Status) 152 logs := r1.Logs() 153 require.Equal(3, len(logs[0].Topics)) 154 require.Equal(hash.BytesToHash256([]byte(staking.HandleCandidateRegister)), logs[0].Topics[0]) 155 selfstakeIndex1 := byteutil.BytesToUint64BigEndian(logs[0].Topics[1][24:]) 156 require.Equal(hash.BytesToHash256(cand1Addr.Bytes()), logs[0].Topics[2]) 157 158 // create two stakes from two voters 159 voter1Addr := identityset.Address(2) 160 voter1PriKey := identityset.PrivateKey(2) 161 162 voter2Addr := identityset.Address(3) 163 voter2PriKey := identityset.PrivateKey(3) 164 165 cs1, r1, err := addOneTx(action.SignedCreateStake(1, candidate1Name, vote.String(), 1, false, 166 nil, gasLimit, gasPrice, voter1PriKey)) 167 require.NoError(err) 168 cs2, r2, err := addOneTx(action.SignedCreateStake(1, candidate1Name, vote.String(), 1, false, 169 nil, gasLimit, gasPrice, voter2PriKey)) 170 require.NoError(err) 171 172 // check candidate state 173 expectedVotes := big.NewInt(0).Add(cand1Votes, big.NewInt(0).Mul(vote, big.NewInt(2))) 174 require.NoError(checkCandidateState(sf, candidate1Name, cand1Addr.String(), selfStake, expectedVotes, cand1Addr)) 175 176 // check voter account state 177 require.NoError(checkAccountState(cfg, sf, cs1, false, initBalance, voter1Addr)) 178 require.NoError(checkAccountState(cfg, sf, cs2, false, initBalance, voter2Addr)) 179 180 // get bucket index from receipts 181 require.EqualValues(iotextypes.ReceiptStatus_Success, r1.Status) 182 logs = r1.Logs() 183 require.Equal(3, len(logs[0].Topics)) 184 require.Equal(hash.BytesToHash256([]byte(staking.HandleCreateStake)), logs[0].Topics[0]) 185 require.Equal(hash.BytesToHash256(cand1Addr.Bytes()), logs[0].Topics[2]) 186 voter1BucketIndex := byteutil.BytesToUint64BigEndian(logs[0].Topics[1][24:]) 187 188 require.EqualValues(iotextypes.ReceiptStatus_Success, r2.Status) 189 logs = r2.Logs() 190 require.Equal(3, len(logs[0].Topics)) 191 require.Equal(hash.BytesToHash256([]byte(staking.HandleCreateStake)), logs[0].Topics[0]) 192 require.Equal(hash.BytesToHash256(cand1Addr.Bytes()), logs[0].Topics[2]) 193 voter2BucketIndex := byteutil.BytesToUint64BigEndian(logs[0].Topics[1][24:]) 194 195 // change candidate 196 _, rc, err := addOneTx(action.SignedChangeCandidate(2, candidate2Name, voter2BucketIndex, nil, 197 gasLimit, gasPrice, voter2PriKey)) 198 require.NoError(err) 199 200 require.EqualValues(iotextypes.ReceiptStatus_Success, rc.Status) 201 logs = rc.Logs() 202 require.Equal(4, len(logs[0].Topics)) 203 require.Equal(hash.BytesToHash256([]byte(staking.HandleChangeCandidate)), logs[0].Topics[0]) 204 require.Equal(voter2BucketIndex, byteutil.BytesToUint64BigEndian(logs[0].Topics[1][24:])) 205 require.Equal(hash.BytesToHash256(cand1Addr.Bytes()), logs[0].Topics[2]) 206 require.Equal(hash.BytesToHash256(cand2Addr.Bytes()), logs[0].Topics[3]) 207 208 // check candidate state 209 expectedVotes = big.NewInt(0).Add(cand1Votes, vote) 210 require.NoError(checkCandidateState(sf, candidate1Name, cand1Addr.String(), selfStake, expectedVotes, cand1Addr)) 211 expectedVotes = big.NewInt(0).Add(selfStake, vote) 212 require.NoError(checkCandidateState(sf, candidate2Name, cand2Addr.String(), selfStake, expectedVotes, cand2Addr)) 213 214 // transfer stake 215 _, rt, err := addOneTx(action.SignedTransferStake(2, voter2Addr.String(), voter1BucketIndex, nil, gasLimit, gasPrice, voter1PriKey)) 216 require.NoError(err) 217 218 require.EqualValues(iotextypes.ReceiptStatus_Success, rt.Status) 219 logs = rt.Logs() 220 require.Equal(4, len(logs[0].Topics)) 221 require.Equal(hash.BytesToHash256([]byte(staking.HandleTransferStake)), logs[0].Topics[0]) 222 require.Equal(voter1BucketIndex, byteutil.BytesToUint64BigEndian(logs[0].Topics[1][24:])) 223 require.Equal(hash.BytesToHash256(voter2Addr.Bytes()), logs[0].Topics[2]) 224 require.Equal(hash.BytesToHash256(cand1Addr.Bytes()), logs[0].Topics[3]) 225 226 // check buckets 227 var bis staking.BucketIndices 228 _, err = sf.State(&bis, protocol.NamespaceOption(_stakingNameSpace), 229 protocol.KeyOption(staking.AddrKeyWithPrefix(voter1Addr, _voterIndex))) 230 require.Error(err) 231 require.Equal(state.ErrStateNotExist, errors.Cause(err)) 232 233 _, err = sf.State(&bis, protocol.NamespaceOption(_stakingNameSpace), 234 protocol.KeyOption(staking.AddrKeyWithPrefix(voter2Addr, _voterIndex))) 235 require.NoError(err) 236 require.Equal(2, len(bis)) 237 require.Equal(voter2BucketIndex, bis[0]) 238 require.Equal(voter1BucketIndex, bis[1]) 239 240 // deposit to stake 241 ds, rd, err := addOneTx(action.SignedDepositToStake(3, voter2BucketIndex, vote.String(), nil, gasLimit, gasPrice, voter2PriKey)) 242 require.NoError(err) 243 244 require.EqualValues(iotextypes.ReceiptStatus_ErrInvalidBucketType, rd.Status) 245 logs = rd.Logs() 246 require.Equal(4, len(logs[0].Topics)) 247 require.Equal(hash.BytesToHash256([]byte(staking.HandleDepositToStake)), logs[0].Topics[0]) 248 require.Equal(voter2BucketIndex, byteutil.BytesToUint64BigEndian(logs[0].Topics[1][24:])) 249 require.Equal(hash.BytesToHash256(voter2Addr.Bytes()), logs[0].Topics[2]) 250 require.Equal(hash.BytesToHash256(cand2Addr.Bytes()), logs[0].Topics[3]) 251 252 // restake 253 _, rr, err := addOneTx(action.SignedRestake(4, voter2BucketIndex, 1, true, nil, 254 gasLimit, gasPrice, voter2PriKey)) 255 require.NoError(err) 256 257 require.EqualValues(iotextypes.ReceiptStatus_Success, rr.Status) 258 logs = rr.Logs() 259 require.Equal(3, len(logs[0].Topics)) 260 require.Equal(hash.BytesToHash256([]byte(staking.HandleRestake)), logs[0].Topics[0]) 261 require.Equal(voter2BucketIndex, byteutil.BytesToUint64BigEndian(logs[0].Topics[1][24:])) 262 require.Equal(hash.BytesToHash256(cand2Addr.Bytes()), logs[0].Topics[2]) 263 264 // check candidate state 265 expectedVotes = big.NewInt(0).Add(selfStake, autoStakeVote) 266 require.NoError(checkCandidateState(sf, candidate2Name, cand2Addr.String(), selfStake, expectedVotes, cand2Addr)) 267 268 // deposit to stake again 269 ds, rd, err = addOneTx(action.SignedDepositToStake(5, voter2BucketIndex, vote.String(), nil, gasLimit, gasPrice, voter2PriKey)) 270 require.NoError(err) 271 272 // check voter account state 273 require.NoError(checkAccountState(cfg, sf, ds, false, big.NewInt(0).Sub(initBalance, vote), voter2Addr)) 274 275 // unstake voter stake 276 _, ru, err := addOneTx(action.SignedReclaimStake(false, 6, voter1BucketIndex, nil, gasLimit, gasPrice, voter2PriKey)) 277 require.NoError(err) 278 279 require.Equal(uint64(iotextypes.ReceiptStatus_ErrUnstakeBeforeMaturity), ru.Status) 280 logs = ru.Logs() 281 require.Equal(3, len(logs[0].Topics)) 282 require.Equal(hash.BytesToHash256([]byte(staking.HandleUnstake)), logs[0].Topics[0]) 283 require.Equal(voter1BucketIndex, byteutil.BytesToUint64BigEndian(logs[0].Topics[1][24:])) 284 require.Equal(hash.BytesToHash256(cand1Addr.Bytes()), logs[0].Topics[2]) 285 286 unstakeTime := fixedTime.Add(time.Duration(1) * 24 * time.Hour) 287 addOneTx = func(tx *action.SealedEnvelope, err error) (*action.SealedEnvelope, *action.Receipt, error) { 288 if err != nil { 289 return tx, nil, err 290 } 291 if err := ap.Add(ctx, tx); err != nil { 292 return tx, nil, err 293 } 294 blk, err := createAndCommitBlock(bc, ap, unstakeTime) 295 if err != nil { 296 return tx, nil, err 297 } 298 h, err := tx.Hash() 299 if err != nil { 300 return tx, nil, err 301 } 302 for _, r := range blk.Receipts { 303 if r.ActionHash == h { 304 return tx, r, nil 305 } 306 } 307 return tx, nil, errors.Errorf("failed to find receipt for %x", h) 308 } 309 310 // unstake with correct timestamp 311 _, ru, err = addOneTx(action.SignedReclaimStake(false, 7, voter1BucketIndex, nil, gasLimit, gasPrice, voter2PriKey)) 312 require.NoError(err) 313 314 require.Equal(uint64(iotextypes.ReceiptStatus_Success), ru.Status) 315 logs = ru.Logs() 316 require.Equal(3, len(logs[0].Topics)) 317 require.Equal(hash.BytesToHash256([]byte(staking.HandleUnstake)), logs[0].Topics[0]) 318 require.Equal(voter1BucketIndex, byteutil.BytesToUint64BigEndian(logs[0].Topics[1][24:])) 319 require.Equal(hash.BytesToHash256(cand1Addr.Bytes()), logs[0].Topics[2]) 320 321 // check candidate state 322 require.NoError(checkCandidateState(sf, candidate1Name, cand1Addr.String(), selfStake, cand1Votes, cand1Addr)) 323 324 // unstake self stake 325 _, ru, err = addOneTx(action.SignedReclaimStake(false, 2, selfstakeIndex1, nil, gasLimit, gasPrice, cand1PriKey)) 326 require.NoError(err) 327 328 require.EqualValues(iotextypes.ReceiptStatus_ErrInvalidBucketType, ru.Status) 329 logs = ru.Logs() 330 require.Equal(3, len(logs[0].Topics)) 331 require.Equal(hash.BytesToHash256([]byte(staking.HandleUnstake)), logs[0].Topics[0]) 332 require.Equal(selfstakeIndex1, byteutil.BytesToUint64BigEndian(logs[0].Topics[1][24:])) 333 require.Equal(hash.BytesToHash256(cand1Addr.Bytes()), logs[0].Topics[2]) 334 335 // check candidate state 336 require.NoError(checkCandidateState(sf, candidate1Name, cand1Addr.String(), selfStake, cand1Votes, cand1Addr)) 337 338 // withdraw stake 339 ws, rw, err := addOneTx(action.SignedReclaimStake(true, 3, selfstakeIndex1, nil, gasLimit, gasPrice, cand1PriKey)) 340 require.NoError(err) 341 342 require.EqualValues(iotextypes.ReceiptStatus_ErrWithdrawBeforeUnstake, rw.Status) 343 logs = rw.Logs() 344 require.Equal(3, len(logs[0].Topics)) 345 require.Equal(hash.BytesToHash256([]byte(staking.HandleWithdrawStake)), logs[0].Topics[0]) 346 require.Equal(selfstakeIndex1, byteutil.BytesToUint64BigEndian(logs[0].Topics[1][24:])) 347 require.Equal(hash.BytesToHash256(cand1Addr.Bytes()), logs[0].Topics[2]) 348 349 // withdraw with correct timestamp 350 unstakeTime = unstakeTime.Add(cfg.Genesis.WithdrawWaitingPeriod) 351 ws, rw, err = addOneTx(action.SignedReclaimStake(true, 4, selfstakeIndex1, nil, gasLimit, gasPrice, cand1PriKey)) 352 require.NoError(err) 353 354 require.EqualValues(iotextypes.ReceiptStatus_ErrWithdrawBeforeUnstake, rw.Status) 355 logs = rw.Logs() 356 require.Equal(3, len(logs[0].Topics)) 357 require.Equal(hash.BytesToHash256([]byte(staking.HandleWithdrawStake)), logs[0].Topics[0]) 358 require.Equal(selfstakeIndex1, byteutil.BytesToUint64BigEndian(logs[0].Topics[1][24:])) 359 require.Equal(hash.BytesToHash256(cand1Addr.Bytes()), logs[0].Topics[2]) 360 361 // check buckets 362 _, err = sf.State(&bis, protocol.NamespaceOption(_stakingNameSpace), 363 protocol.KeyOption(staking.AddrKeyWithPrefix(cand1Addr, _voterIndex))) 364 require.NoError(err) 365 require.Equal(1, len(bis)) 366 367 _, err = sf.State(&bis, protocol.NamespaceOption(_stakingNameSpace), 368 protocol.KeyOption(staking.AddrKeyWithPrefix(cand1Addr, _candIndex))) 369 require.NoError(err) 370 require.Equal(2, len(bis)) 371 372 // check candidate account state 373 require.NoError(checkAccountState(cfg, sf, ws, true, big.NewInt(0).Sub(initBalance, selfStake), cand1Addr)) 374 375 // register without stake 376 register3, r3, err := addOneTx(action.SignedCandidateRegister(1, candidate3Name, cand3Addr.String(), cand3Addr.String(), 377 cand3Addr.String(), "0", 1, false, nil, gasLimit, gasPrice, cand3PriKey)) 378 require.NoError(err) 379 require.EqualValues(iotextypes.ReceiptStatus_Success, r3.Status) 380 require.NoError(checkCandidateState(sf, candidate3Name, cand3Addr.String(), big.NewInt(0), big.NewInt(0), cand3Addr)) 381 require.NoError(checkAccountState(cfg, sf, register3, true, initBalance, cand3Addr)) 382 383 ctx, err = bc.Context(ctx) 384 require.NoError(err) 385 ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{ 386 BlockHeight: bc.TipHeight() + 1, 387 }) 388 ctx = protocol.WithFeatureCtx(ctx) 389 cands, err := stkPrtcl.ActiveCandidates(ctx, sf, 0) 390 require.NoError(err) 391 require.Equal(4, len(cands)) 392 for _, cand := range cands { 393 t.Logf("\ncandidate=%+v, %+v\n", string(cand.CanName), cand.Votes.String()) 394 } 395 // stake bucket 396 _, cr3, err := addOneTx(action.SignedCreateStake(3, candidate3Name, selfStake.String(), 1, false, 397 nil, gasLimit, gasPrice, voter1PriKey)) 398 require.NoError(err) 399 require.EqualValues(iotextypes.ReceiptStatus_Success, cr3.Status) 400 logs = cr3.Logs() 401 require.Equal(3, len(logs[0].Topics)) 402 require.Equal(hash.BytesToHash256([]byte(staking.HandleCreateStake)), logs[0].Topics[0]) 403 endorseBucketIndex := byteutil.BytesToUint64BigEndian(logs[0].Topics[1][24:]) 404 t.Logf("endorseBucketIndex=%+v", endorseBucketIndex) 405 // endorse bucket 406 _, esr, err := addOneTx(action.SignedCandidateEndorsement(4, endorseBucketIndex, true, gasLimit, gasPrice, voter1PriKey)) 407 require.NoError(err) 408 require.NoError(err) 409 require.EqualValues(iotextypes.ReceiptStatus_Success, esr.Status) 410 // candidate self stake 411 _, cssr, err := addOneTx(action.SignedCandidateActivate(2, endorseBucketIndex, gasLimit, gasPrice, cand3PriKey)) 412 require.NoError(err) 413 require.EqualValues(iotextypes.ReceiptStatus_Success, cssr.Status) 414 ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{ 415 BlockHeight: bc.TipHeight() + 1, 416 }) 417 ctx = protocol.WithFeatureCtx(ctx) 418 cands, err = stkPrtcl.ActiveCandidates(ctx, sf, 0) 419 require.NoError(err) 420 require.Equal(5, len(cands)) 421 for _, cand := range cands { 422 t.Logf("\ncandidate=%+v, %+v\n", string(cand.CanName), cand.Votes.String()) 423 } 424 // unendorse bucket 425 _, esr, err = addOneTx(action.SignedCandidateEndorsement(5, endorseBucketIndex, false, gasLimit, gasPrice, voter1PriKey)) 426 require.NoError(err) 427 require.EqualValues(iotextypes.ReceiptStatus_Success, esr.Status) 428 ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{ 429 BlockHeight: bc.TipHeight() + 1, 430 }) 431 cands, err = stkPrtcl.ActiveCandidates(ctx, sf, 0) 432 require.NoError(err) 433 require.Equal(5, len(cands)) 434 t.Run("endorsement is withdrawing, candidate can also be chosen as delegate", func(t *testing.T) { 435 ctx, err = bc.Context(ctx) 436 require.NoError(err) 437 ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{ 438 BlockHeight: bc.TipHeight(), 439 }) 440 ctx = protocol.WithFeatureCtx(ctx) 441 cands, err = stkPrtcl.ActiveCandidates(ctx, sf, 0) 442 require.NoError(err) 443 require.Equal(5, len(cands)) 444 }) 445 t.Run("endorsement is expired, candidate can not be chosen as delegate any more", func(t *testing.T) { 446 ctx, err = bc.Context(ctx) 447 require.NoError(err) 448 jumpBlocks(bc, int(cfg.Genesis.EndorsementWithdrawWaitingBlocks), require) 449 ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{ 450 BlockHeight: bc.TipHeight(), 451 }) 452 ctx = protocol.WithFeatureCtx(ctx) 453 cands, err = stkPrtcl.ActiveCandidates(ctx, sf, 0) 454 require.NoError(err) 455 require.Equal(4, len(cands)) 456 }) 457 } 458 459 cfg := config.Default 460 testTriePath, err := testutil.PathOfTempFile("trie") 461 require.NoError(err) 462 testDBPath, err := testutil.PathOfTempFile("db") 463 require.NoError(err) 464 testIndexPath, err := testutil.PathOfTempFile("index") 465 require.NoError(err) 466 testSystemLogPath, err := testutil.PathOfTempFile("systemlog") 467 require.NoError(err) 468 testSGDIndexPath, err := testutil.PathOfTempFile("sgdindex") 469 require.NoError(err) 470 defer func() { 471 testutil.CleanupPath(testTriePath) 472 testutil.CleanupPath(testDBPath) 473 testutil.CleanupPath(testIndexPath) 474 testutil.CleanupPath(testSystemLogPath) 475 testutil.CleanupPath(testSGDIndexPath) 476 // clear the gateway 477 delete(cfg.Plugins, config.GatewayPlugin) 478 }() 479 480 cfg.ActPool.MinGasPriceStr = "0" 481 cfg.Chain.TrieDBPatchFile = "" 482 cfg.Chain.TrieDBPath = testTriePath 483 cfg.Chain.ChainDBPath = testDBPath 484 cfg.Chain.IndexDBPath = testIndexPath 485 cfg.Chain.ContractStakingIndexDBPath = testIndexPath 486 cfg.Chain.SGDIndexDBPath = testSGDIndexPath 487 cfg.System.SystemLogDBPath = testSystemLogPath 488 cfg.Consensus.Scheme = config.NOOPScheme 489 cfg.Chain.EnableAsyncIndexWrite = false 490 cfg.Genesis.BootstrapCandidates = testInitCands 491 cfg.Genesis.FbkMigrationBlockHeight = 1 492 cfg.Genesis.TsunamiBlockHeight = 0 493 cfg.Genesis.EndorsementWithdrawWaitingBlocks = 10 494 495 t.Run("test native staking", func(t *testing.T) { 496 testNativeStaking(cfg, t) 497 }) 498 } 499 500 func checkCandidateState( 501 sr protocol.StateReader, 502 expectedName, 503 expectedOwnerAddr string, 504 expectedSelfStake, 505 expectedVotes *big.Int, 506 candidateAddr address.Address, 507 ) error { 508 var cand staking.Candidate 509 if _, err := sr.State(&cand, protocol.NamespaceOption(_candidateNameSpace), protocol.KeyOption(candidateAddr.Bytes())); err != nil { 510 return err 511 } 512 if expectedName != cand.Name { 513 return errors.New("name does not match") 514 } 515 if expectedOwnerAddr != cand.Owner.String() { 516 return errors.New("Owner address does not match") 517 } 518 if expectedSelfStake.Cmp(cand.SelfStake) != 0 { 519 return errors.New("self stake does not match") 520 } 521 if expectedVotes.Cmp(cand.Votes) != 0 { 522 return errors.New("votes does not match") 523 } 524 return nil 525 } 526 527 func checkAccountState( 528 cfg config.Config, 529 sr protocol.StateReader, 530 act *action.SealedEnvelope, 531 registrationFee bool, 532 expectedBalance *big.Int, 533 accountAddr address.Address, 534 ) error { 535 cost, err := act.Cost() 536 if err != nil { 537 return err 538 } 539 if registrationFee { 540 regFee, ok := new(big.Int).SetString(cfg.Genesis.RegistrationConsts.Fee, 10) 541 if !ok { 542 return errors.New("failed to set genesis registration fee") 543 } 544 cost.Add(cost, regFee) 545 } 546 acct1, err := accountutil.LoadAccount(sr, accountAddr) 547 if err != nil { 548 return err 549 } 550 if expectedBalance.Cmp(cost.Add(cost, acct1.Balance)) != 0 { 551 return errors.New("balance does not match") 552 } 553 return nil 554 } 555 556 func createAndCommitBlock(bc blockchain.Blockchain, ap actpool.ActPool, blkTime time.Time) (*block.Block, error) { 557 blk, err := bc.MintNewBlock(blkTime) 558 if err != nil { 559 return nil, err 560 } 561 if err := bc.CommitBlock(blk); err != nil { 562 return nil, err 563 } 564 ap.Reset() 565 return blk, nil 566 }