From 41258f532eafa634ba389ab60ca37a1774e3b08e Mon Sep 17 00:00:00 2001 From: Chen Chen <34592639+envestcc@users.noreply.github.com> Date: Thu, 4 Jul 2024 21:36:02 +0800 Subject: [PATCH] [e2etest] add more test (#4322) --- action/rlp_tx_test.go | 132 ++++++++++-- action/signedaction.go | 41 ++++ blockchain/integrity/integrity_test.go | 47 ++++- e2etest/contract_staking_v2_test.go | 275 +++++++++++++++++++++++++ e2etest/e2etest.go | 33 +++ e2etest/expect.go | 4 +- e2etest/native_staking_test.go | 189 ++++++++++++++--- e2etest/rewarding_test.go | 73 +++++++ e2etest/staking_contract_v2_bytecode | 1 + 9 files changed, 744 insertions(+), 51 deletions(-) create mode 100644 e2etest/contract_staking_v2_test.go create mode 100644 e2etest/staking_contract_v2_bytecode diff --git a/action/rlp_tx_test.go b/action/rlp_tx_test.go index 0b6b42aedd..e2bcddb192 100644 --- a/action/rlp_tx_test.go +++ b/action/rlp_tx_test.go @@ -3,6 +3,7 @@ package action import ( "bytes" "context" + "crypto/ecdsa" "encoding/hex" "math/big" "strings" @@ -314,6 +315,21 @@ var ( "04830579b50e01602c2015c24e72fbc48bca1cca1e601b119ca73abe2e0b5bd61fcb7874567e091030d6b644f927445d80e00b3f9ca0c566c21c30615e94c343da", "8d38efe45794d7fceea10b2262c23c12245959db", }, + { + "rewardingClaimWithAddress", + "f9014905830186a082520894a576c141e5659137ddda4223d209d4744b2106be80b8e4d804b87c0000000000000000000000000000000000000000000000000000002e90edd000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000029696f313433617638383078307863653474737939737877723861766870687135736768756d37376374000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008224c6a01490de878ee13ee6976064082f75bbbfd7da0294a151b966d5ddf45dac6838e2a0104fdcc42607f54a8d1a5ad7f2e7fbbd3f8e7f56efb58ebff638ac8485509332", + 5, + 21000, + "100000", + "0", + "0xA576C141e5659137ddDa4223d209d4744b2106BE", + _evmNetworkID, + iotextypes.Encoding_ETHEREUM_EIP155, + 228, + "2b34c548e0b8d027124ead244331c65a92fd7716ce4dac42c7bb5b342d21e240", + "04bc3a3123a0d72e1e622ec1a51087ef3b15a9d6db0f924c0fd8b4958653ff7608194321d1fd90c0c949b05b6b911d8d7e9aaadbe497e696367c19780a016ce440", + "fff810c667050c7e4263a39e796af8d5e74a1b55", + }, { "rewardingDeposit", "f8c6016482520894a576c141e5659137ddda4223d209d4744b2106be80b86427852a6b0000000000000000000000000000000000000000000000000000000000000065000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000008224c6a013b7679dbabcb0f97b93942436f5072cca3c7fe43451a8fedcdf3c84c1344e1da02af4cc67594c0200b59f4e30ba149af15e546acbfc69fa31f14e8788ab063d85", @@ -374,7 +390,6 @@ var ( "04dc4c548c3a478278a6a09ffa8b5c4b384368e49654b35a6961ee8288fc889cdc39e9f8194e41abdbfac248ef9dc3f37b131a36ee2c052d974c21c1d2cd56730b", "1e14d5373e1af9cc77f0032ad2cd0fba8be5ea2e", }, - // TODO: add StakeMigrate test { "unprotected", deterministicDeploymentTx, @@ -390,23 +405,66 @@ var ( "040a98b1acb38ed9cd8d0e8f1f03b1588bae140586f8a8049197b65013a3c17690151ae422e3fdfb26be2e6a4465b1f9cf5c26a5635109929a0d0a11734124d50a", "3fab184622dc19b6109349b94811493bf2a45362", }, - // TODO: endorseCandidate, intentToRevokeEndorsement, revokeEndorsement - // also add util functions to generate the test data - // { - // "endorseCandidate", - // "f8487885e8d4a510008252089404c22afae6a03438b8fed74cb1cf441168df3f1280a4284ab8110000000000000000000000000000000000000000000000000000000000000011808080", - // 120, - // 21000, - // "1000000000000", - // "0", - // "0x04C22AfaE6a03438b8FED74cb1Cf441168DF3F12", - // _evmNetworkID, - // iotextypes.Encoding_ETHEREUM_UNPROTECTED, - // 36, - // "d190061a42223c40e755eef500c9c8bbec071556368032777c18d8f023b0f2e3", - // "04bc3a3123a0d72e1e622ec1a51087ef3b15a9d6db0f924c0fd8b4958653ff7608194321d1fd90c0c949b05b6b911d8d7e9aaadbe497e696367c19780a016ce440", - // "fff810c667050c7e4263a39e796af8d5e74a1b55", - // }, + { + "migrateStake", + "f887788227108252089404c22afae6a03438b8fed74cb1cf441168df3f1280a4c53b10a4000000000000000000000000000000000000000000000000000000000000000a8224c5a046500c934b4295c246d395a18ab78c4755033a5b85f0743938956ff0b98f8f16a05b758049c1e8ccb3154c53e2ec22a4bad65465aed0be6e352aaa53cfec6f6906", + 120, + 21000, + "10000", + "0", + "0x04C22AfaE6a03438b8FED74cb1Cf441168DF3F12", + _evmNetworkID, + iotextypes.Encoding_ETHEREUM_EIP155, + 36, + "2cb96b971adc556d5deb3f1fbad0b480983545c154a5b9401670ff33dd1f68ce", + "04bc3a3123a0d72e1e622ec1a51087ef3b15a9d6db0f924c0fd8b4958653ff7608194321d1fd90c0c949b05b6b911d8d7e9aaadbe497e696367c19780a016ce440", + "fff810c667050c7e4263a39e796af8d5e74a1b55", + }, + { + "endorseCandidate", + "f887788227108252089404c22afae6a03438b8fed74cb1cf441168df3f1280a4284ab81100000000000000000000000000000000000000000000000000000000000000028224c5a01d0c6982474992866529fb72032c629e697ab612d75cba4b07c0137f24081e92a007c4d43d162aa7297cb9f89cf6f1cadfc5fd57703b25f2a67c152b1c32cb09c0", + 120, + 21000, + "10000", + "0", + "0x04C22AfaE6a03438b8FED74cb1Cf441168DF3F12", + _evmNetworkID, + iotextypes.Encoding_ETHEREUM_EIP155, + 36, + "15cd6fec8876ca308d85cce80b26ea9b28c93f57d6823674c4d37efc34248d22", + "04bc3a3123a0d72e1e622ec1a51087ef3b15a9d6db0f924c0fd8b4958653ff7608194321d1fd90c0c949b05b6b911d8d7e9aaadbe497e696367c19780a016ce440", + "fff810c667050c7e4263a39e796af8d5e74a1b55", + }, + { + "intentToRevokeEndorsement", + "f887788227108252089404c22afae6a03438b8fed74cb1cf441168df3f1280a485edc03c00000000000000000000000000000000000000000000000000000000000000028224c6a0d07751b74f3a2359a4f12f164ae24146e19aa3db2bda6a9e9eee5ecd67234384a053ea6a1c98fe89b0f065b0173cbeddf1a03b121a6e3c5d3b35dd91641f08baae", + 120, + 21000, + "10000", + "0", + "0x04C22AfaE6a03438b8FED74cb1Cf441168DF3F12", + _evmNetworkID, + iotextypes.Encoding_ETHEREUM_EIP155, + 36, + "c30f7fdacfaba5d7c78963824579793409c3fed1bc5ab67735d128ef2c91eb59", + "04bc3a3123a0d72e1e622ec1a51087ef3b15a9d6db0f924c0fd8b4958653ff7608194321d1fd90c0c949b05b6b911d8d7e9aaadbe497e696367c19780a016ce440", + "fff810c667050c7e4263a39e796af8d5e74a1b55", + }, + { + "revokeEndorsement", + "f887788227108252089404c22afae6a03438b8fed74cb1cf441168df3f1280a4120f99ad00000000000000000000000000000000000000000000000000000000000000028224c6a0cbb3cb584e956ada942864d242a15409409bfeb9a428f247a50f5c185cae3c8aa0469d24184060efa8585afe2122a3082d3b61a78e2b1263a750bacf465040d4e1", + 120, + 21000, + "10000", + "0", + "0x04C22AfaE6a03438b8FED74cb1Cf441168DF3F12", + _evmNetworkID, + iotextypes.Encoding_ETHEREUM_EIP155, + 36, + "0a3270daa16fc706fc11dd64ca6e15ed67ccdc40dd1747d1d1001edb18ceaef4", + "04bc3a3123a0d72e1e622ec1a51087ef3b15a9d6db0f924c0fd8b4958653ff7608194321d1fd90c0c949b05b6b911d8d7e9aaadbe497e696367c19780a016ce440", + "fff810c667050c7e4263a39e796af8d5e74a1b55", + }, } ) @@ -1014,10 +1072,10 @@ func convertToNativeProto(tx *types.Transaction, actType string) *iotextypes.Act return elp.Proto() case "stakeCreate", "stakeAddDeposit", "changeCandidate", "unstake", "withdrawStake", "restake", "transferStake", "candidateRegister", "candidateUpdate", "candidateActivate", "candidateEndorsement", "candidateTransferOwnership", - "endorseCandidate", "intentToRevokeEndorsement", "revokeEndorsement": + "endorseCandidate", "intentToRevokeEndorsement", "revokeEndorsement", "migrateStake": elp, _ := elpBuilder.BuildStakingAction(tx) return elp.Proto() - case "rewardingClaim", "rewardingDeposit": + case "rewardingClaim", "rewardingClaimWithAddress", "rewardingDeposit": elp, _ := elpBuilder.BuildRewardingAction(tx) return elp.Proto() default: @@ -1106,3 +1164,37 @@ func TestIssue3944(t *testing.T) { r.Equal("9415", v.String()) // this is the correct V value corresponding to chainID = 4690 r.Equal(hash, tx1.Hash().Hex()) } + +// generateRLPTestData generates RLP test data for testing +// for example to generate for migrate stake action, use the following code snippet: +// +// act, err := NewMigrateStake(120, 10, 21000, big.NewInt(10000)) +// test, err := generateRLPTestData("migrateStake", _evmNetworkID, iotextypes.Encoding_ETHEREUM_EIP155, identityset.PrivateKey(1), act) +// fmt.Sprintf("test=%+v", test) +func generateRLPTestData(actType string, chainID uint32, encoding iotextypes.Encoding, sk iotexcrypto.PrivateKey, act EthCompatibleAction) (*rlpTest, error) { + tx, err := act.ToEthTx(chainID) + if err != nil { + return nil, err + } + signer := types.NewEIP2930Signer(big.NewInt(int64(chainID))) + signedTx, err := types.SignTx(tx, signer, sk.EcdsaPrivateKey().(*ecdsa.PrivateKey)) + raw, err := signedTx.MarshalBinary() + if err != nil { + return nil, err + } + return &rlpTest{ + actType: actType, + raw: hex.EncodeToString(raw), + nonce: tx.Nonce(), + limit: tx.Gas(), + price: tx.GasPrice().String(), + amount: tx.Value().String(), + to: tx.To().Hex(), + chainID: uint32(tx.ChainId().Int64()), + encoding: encoding, + dataLen: len(signedTx.Data()), + hash: signedTx.Hash().Hex(), + pubkey: hex.EncodeToString(sk.PublicKey().Bytes()), + pkhash: hex.EncodeToString(sk.PublicKey().Hash()), + }, nil +} diff --git a/action/signedaction.go b/action/signedaction.go index ab1ee3f36f..1bad27a90d 100644 --- a/action/signedaction.go +++ b/action/signedaction.go @@ -10,6 +10,7 @@ import ( "math/big" "github.com/iotexproject/go-pkgs/crypto" + "github.com/iotexproject/iotex-address/address" "github.com/pkg/errors" ) @@ -470,3 +471,43 @@ func SignedMigrateStake( } return selp, nil } + +func SignedClaimRewardLegacy( + nonce uint64, + gasLimit uint64, + gasPrice *big.Int, + senderPriKey crypto.PrivateKey, + amount *big.Int, + payload []byte, + options ...SignedActionOption, +) (*SealedEnvelope, error) { + return SignedClaimReward(nonce, gasLimit, gasPrice, senderPriKey, amount, payload, nil, options...) +} + +func SignedClaimReward( + nonce uint64, + gasLimit uint64, + gasPrice *big.Int, + senderPriKey crypto.PrivateKey, + amount *big.Int, + payload []byte, + address address.Address, + options ...SignedActionOption, +) (*SealedEnvelope, error) { + b := &ClaimFromRewardingFundBuilder{} + act := b.SetAmount(amount).SetData(payload).SetAddress(address).Build() + bd := &EnvelopeBuilder{} + bd = bd.SetNonce(nonce). + SetGasPrice(gasPrice). + SetGasLimit(gasLimit). + SetAction(&act) + for _, opt := range options { + opt(bd) + } + elp := bd.Build() + selp, err := Sign(elp, senderPriKey) + if err != nil { + return nil, errors.Wrapf(err, "failed to sign candidate transfer ownership %v", elp) + } + return selp, nil +} diff --git a/blockchain/integrity/integrity_test.go b/blockchain/integrity/integrity_test.go index cf21cb4dc3..397916ce64 100644 --- a/blockchain/integrity/integrity_test.go +++ b/blockchain/integrity/integrity_test.go @@ -53,6 +53,7 @@ import ( "github.com/iotexproject/iotex-core/state/factory" "github.com/iotexproject/iotex-core/test/identityset" "github.com/iotexproject/iotex-core/test/mock/mock_blockcreationsubscriber" + "github.com/iotexproject/iotex-core/test/mock/mock_poll" "github.com/iotexproject/iotex-core/testutil" ) @@ -999,6 +1000,23 @@ func TestBlockchainHardForkFeatures(t *testing.T) { ctx := context.Background() bc, sf, dao, ap, err := createChain(cfg, true) require.NoError(err) + sk, err := iotexcrypto.HexStringToPrivateKey(cfg.Chain.ProducerPrivKey) + require.NoError(err) + producer := sk.PublicKey().Address() + ctrl := gomock.NewController(t) + pp := mock_poll.NewMockProtocol(ctrl) + pp.EXPECT().CreateGenesisStates(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + pp.EXPECT().Candidates(gomock.Any(), gomock.Any()).Return([]*state.Candidate{ + &state.Candidate{ + Address: producer.String(), + RewardAddress: producer.String(), + }, + }, nil).AnyTimes() + pp.EXPECT().Register(gomock.Any()).DoAndReturn(func(reg *protocol.Registry) error { + return reg.Register("poll", pp) + }).AnyTimes() + pp.EXPECT().Validate(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + require.NoError(sf.Register(pp)) require.NoError(bc.Start(ctx)) defer func() { require.NoError(bc.Stop(ctx)) @@ -1215,7 +1233,7 @@ func TestBlockchainHardForkFeatures(t *testing.T) { require.Equal(v.b, a.Balance.String()) } - // Add block 4 -- test the UseTxContainer flag + // Add block 4 -- test the UseTxContainer and AddClaimRewardAddress flag var ( txs [2]*types.Transaction contractHash hash.Hash256 @@ -1266,11 +1284,23 @@ func TestBlockchainHardForkFeatures(t *testing.T) { require.EqualValues(iotextypes.Encoding_TX_CONTAINER, selp.Encoding()) require.NoError(ap.Add(ctx, selp)) } + claim := (&action.ClaimFromRewardingFundBuilder{}). + SetAmount(big.NewInt(200000000000)). + SetAddress(producer).Build() + elp = (&action.EnvelopeBuilder{}).SetNonce(6). + SetChainID(cfg.Chain.ID). + SetGasPrice(minGas). + SetGasLimit(100000). + SetAction(&claim).Build() + tsf2, err = action.Sign(elp, priKey0) + require.NoError(err) + require.NoError(ap.Add(ctx, tsf2)) + blockTime = blockTime.Add(time.Second) blk3, err := bc.MintNewBlock(blockTime) require.NoError(err) require.EqualValues(4, blk3.Height()) - require.Equal(3, len(blk3.Body.Actions)) + require.Equal(4, len(blk3.Body.Actions)) require.NoError(bc.CommitBlock(blk3)) // verify contract execution @@ -1286,6 +1316,12 @@ func TestBlockchainHardForkFeatures(t *testing.T) { require.Equal(_sarTopic, logs[0].Topics[1][:]) require.True(blk3.Header.LogsBloomfilter().Exist(_sarTopic)) + // verify claim reward + a, err := accountutil.AccountState(ctx, sf, producer) + require.NoError(err) + require.EqualValues(1, a.AccountType()) + require.Equal("200000000000", a.Balance.String()) + // commit 4 blocks to a new chain testTriePath2, err := testutil.PathOfTempFile("trie") require.NoError(err) @@ -1307,6 +1343,7 @@ func TestBlockchainHardForkFeatures(t *testing.T) { cfg.Chain.IndexDBPath = testIndexPath2 bc2, sf2, dao2, _, err := createChain(cfg, false) require.NoError(err) + require.NoError(sf2.Register(pp)) require.NoError(bc2.Start(ctx)) defer func() { require.NoError(bc2.Stop(ctx)) @@ -1350,6 +1387,12 @@ func TestBlockchainHardForkFeatures(t *testing.T) { tl, err = dao2.TransactionLogs(2) require.NoError(err) require.Equal(4, len(tl.Logs)) + + // verify claim reward + a, err = accountutil.AccountState(ctx, sf2, producer) + require.NoError(err) + require.EqualValues(1, a.AccountType()) + require.Equal("200000000000", a.Balance.String()) } func TestConstantinople(t *testing.T) { diff --git a/e2etest/contract_staking_v2_test.go b/e2etest/contract_staking_v2_test.go new file mode 100644 index 0000000000..dc6b690ec9 --- /dev/null +++ b/e2etest/contract_staking_v2_test.go @@ -0,0 +1,275 @@ +package e2etest + +import ( + "context" + _ "embed" + "encoding/hex" + "math" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/iotexproject/iotex-proto/golang/iotexapi" + "github.com/iotexproject/iotex-proto/golang/iotextypes" + + "github.com/iotexproject/iotex-core/action" + "github.com/iotexproject/iotex-core/action/protocol/staking" + "github.com/iotexproject/iotex-core/config" + "github.com/iotexproject/iotex-core/pkg/unit" + "github.com/iotexproject/iotex-core/test/identityset" +) + +var ( + //go:embed staking_contract_v2_bytecode + stakingContractV2Bytecode string + stakingContractV2ABI = staking.StakingContractABI + stakingContractV2Address = "io1dkqh5mu9djfas3xyrmzdv9frsmmytel4mp7a64" +) + +func TestContractStakingV2(t *testing.T) { + require := require.New(t) + contractAddress := stakingContractV2Address + cfg := initCfg(require) + cfg.Genesis.UpernavikBlockHeight = 1 + cfg.Genesis.SystemStakingContractV2Address = contractAddress + cfg.Genesis.SystemStakingContractV2Height = 1 + cfg.DardanellesUpgrade.BlockInterval = time.Second * 8640 + cfg.Plugins[config.GatewayPlugin] = nil + test := newE2ETest(t, cfg) + defer test.teardown() + + var ( + successExpect = &basicActionExpect{nil, uint64(iotextypes.ReceiptStatus_Success), ""} + chainID = test.cfg.Chain.ID + contractCreator = 1 + stakerID = 2 + beneficiaryID = 10 + stakeAmount = unit.ConvertIotxToRau(10000) + registerAmount = unit.ConvertIotxToRau(1200000) + stakeTime = time.Now() + unlockTime = stakeTime.Add(time.Hour) + candOwnerID = 3 + candOwnerID2 = 4 + blocksPerDay = 24 * time.Hour / cfg.DardanellesUpgrade.BlockInterval + stakeDurationBlocks = big.NewInt(int64(blocksPerDay)) + minAmount = unit.ConvertIotxToRau(1000) + + tmpVotes = big.NewInt(0) + tmpBalance = big.NewInt(0) + ) + bytecode, err := hex.DecodeString(stakingContractV2Bytecode) + require.NoError(err) + mustCallData := func(m string, args ...any) []byte { + data, err := abiCall(staking.StakingContractABI, m, args...) + require.NoError(err) + return data + } + genTransferActions := func(n int) []*actionWithTime { + acts := make([]*actionWithTime, n) + for i := 0; i < n; i++ { + acts[i] = &actionWithTime{mustNoErr(action.SignedTransfer(identityset.Address(1).String(), identityset.PrivateKey(2), test.nonceMgr.pop(identityset.Address(2).String()), unit.ConvertIotxToRau(1), nil, gasLimit, gasPrice, action.WithChainID(chainID))), time.Now()} + } + return acts + } + test.run([]*testcase{ + { + name: "deploy staking contract", + preActs: []*actionWithTime{ + {mustNoErr(action.SignedCandidateRegister(test.nonceMgr.pop(identityset.Address(candOwnerID).String()), "cand1", identityset.Address(1).String(), identityset.Address(1).String(), identityset.Address(candOwnerID).String(), registerAmount.String(), 1, true, nil, gasLimit, gasPrice, identityset.PrivateKey(candOwnerID), action.WithChainID(chainID))), time.Now()}, + }, + act: &actionWithTime{mustNoErr(action.SignedExecution("", identityset.PrivateKey(contractCreator), test.nonceMgr.pop(identityset.Address(contractCreator).String()), big.NewInt(0), gasLimit, gasPrice, append(bytecode, mustCallData("", minAmount, common.BytesToAddress(identityset.Address(beneficiaryID).Bytes()))...), action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, &executionExpect{contractAddress}}, + }, + { + name: "stake", + preFunc: func(e *e2etest) { + candidate, err := e.getCandidateByName("cand1") + require.NoError(err) + _, ok := tmpVotes.SetString(candidate.TotalWeightedVotes, 10) + require.True(ok) + }, + act: &actionWithTime{mustNoErr(action.SignedExecution(contractAddress, identityset.PrivateKey(stakerID), test.nonceMgr.pop(identityset.Address(stakerID).String()), stakeAmount, gasLimit, gasPrice, mustCallData("stake(uint256,address)", stakeDurationBlocks, common.BytesToAddress(identityset.Address(candOwnerID).Bytes())), action.WithChainID(chainID))), stakeTime}, + expect: []actionExpect{successExpect, + &bucketExpect{&iotextypes.VoteBucket{Index: 1, ContractAddress: contractAddress, Owner: identityset.Address(stakerID).String(), CandidateAddress: identityset.Address(candOwnerID).String(), StakedDuration: uint32(stakeDurationBlocks.Uint64() / uint64(blocksPerDay)), StakedDurationBlockNumber: stakeDurationBlocks.Uint64(), CreateTime: timestamppb.New(time.Time{}), StakeStartTime: timestamppb.New(time.Time{}), StakeStartBlockHeight: 3, CreateBlockHeight: 3, UnstakeStartTime: timestamppb.New(time.Time{}), UnstakeStartBlockHeight: uint64(math.MaxUint64), StakedAmount: stakeAmount.String(), AutoStake: true}}, + &candidateExpect{"cand1", &iotextypes.CandidateV2{OwnerAddress: identityset.Address(candOwnerID).String(), OperatorAddress: identityset.Address(1).String(), RewardAddress: identityset.Address(1).String(), Name: "cand1", TotalWeightedVotes: "1256001586604779503009155", SelfStakingTokens: registerAmount.String(), SelfStakeBucketIdx: 0}}, + &functionExpect{func(test *e2etest, act *action.SealedEnvelope, receipt *action.Receipt, err error) { + candidate, err := test.getCandidateByName("cand1") + require.NoError(err) + deltaVotes := staking.CalculateVoteWeight(test.cfg.Genesis.VoteWeightCalConsts, &staking.VoteBucket{AutoStake: true, StakedDuration: time.Duration(stakeDurationBlocks.Uint64()/uint64(blocksPerDay)*24) * (time.Hour), StakedAmount: stakeAmount}, false) + require.Equal(tmpVotes.Add(tmpVotes, deltaVotes).String(), candidate.TotalWeightedVotes) + }}, + }, + }, + { + name: "unlock", + act: &actionWithTime{mustNoErr(action.SignedExecution(contractAddress, identityset.PrivateKey(stakerID), test.nonceMgr.pop(identityset.Address(stakerID).String()), big.NewInt(0), gasLimit, gasPrice, mustCallData("unlock(uint256)", big.NewInt(1)), action.WithChainID(chainID))), unlockTime}, + expect: []actionExpect{successExpect, + &bucketExpect{&iotextypes.VoteBucket{Index: 1, ContractAddress: contractAddress, Owner: identityset.Address(stakerID).String(), CandidateAddress: identityset.Address(candOwnerID).String(), StakedDuration: uint32(stakeDurationBlocks.Uint64() / uint64(blocksPerDay)), StakedDurationBlockNumber: stakeDurationBlocks.Uint64(), CreateTime: timestamppb.New(time.Time{}), StakeStartTime: timestamppb.New(time.Time{}), StakeStartBlockHeight: 4, CreateBlockHeight: 3, UnstakeStartTime: timestamppb.New(time.Time{}), UnstakeStartBlockHeight: math.MaxUint64, StakedAmount: stakeAmount.String(), AutoStake: false}}, + &functionExpect{func(test *e2etest, act *action.SealedEnvelope, receipt *action.Receipt, err error) { + candidate, err := test.getCandidateByName("cand1") + require.NoError(err) + lockedStakeVotes := staking.CalculateVoteWeight(test.cfg.Genesis.VoteWeightCalConsts, &staking.VoteBucket{AutoStake: true, StakedDuration: time.Duration(stakeDurationBlocks.Uint64()/uint64(blocksPerDay)*24) * (time.Hour), StakedAmount: stakeAmount}, false) + unlockedVotes := staking.CalculateVoteWeight(test.cfg.Genesis.VoteWeightCalConsts, &staking.VoteBucket{AutoStake: false, StakedDuration: time.Duration(stakeDurationBlocks.Uint64()/uint64(blocksPerDay)*24) * (time.Hour), StakedAmount: stakeAmount}, false) + tmpVotes.Sub(tmpVotes, lockedStakeVotes) + tmpVotes.Add(tmpVotes, unlockedVotes) + require.Equal(tmpVotes.String(), candidate.TotalWeightedVotes) + }}, + }, + }, + { + name: "lock", + act: &actionWithTime{mustNoErr(action.SignedExecution(contractAddress, identityset.PrivateKey(stakerID), test.nonceMgr.pop(identityset.Address(stakerID).String()), big.NewInt(0), gasLimit, gasPrice, mustCallData("lock(uint256,uint256)", big.NewInt(1), big.NewInt(0).Mul(big.NewInt(2), stakeDurationBlocks)), action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, + &bucketExpect{&iotextypes.VoteBucket{Index: 1, ContractAddress: contractAddress, Owner: identityset.Address(stakerID).String(), CandidateAddress: identityset.Address(candOwnerID).String(), StakedDuration: uint32(stakeDurationBlocks.Uint64()/uint64(blocksPerDay)) * 2, StakedDurationBlockNumber: stakeDurationBlocks.Uint64() * 2, CreateTime: timestamppb.New(time.Time{}), StakeStartTime: timestamppb.New(time.Time{}), StakeStartBlockHeight: 3, CreateBlockHeight: 3, UnstakeStartTime: timestamppb.New(time.Time{}), UnstakeStartBlockHeight: math.MaxUint64, StakedAmount: stakeAmount.String(), AutoStake: true}}, + &functionExpect{func(test *e2etest, act *action.SealedEnvelope, receipt *action.Receipt, err error) { + candidate, err := test.getCandidateByName("cand1") + require.NoError(err) + preStakeVotes := staking.CalculateVoteWeight(test.cfg.Genesis.VoteWeightCalConsts, &staking.VoteBucket{AutoStake: false, StakedDuration: time.Duration(stakeDurationBlocks.Uint64()/uint64(blocksPerDay)*24) * (time.Hour), StakedAmount: stakeAmount}, false) + postVotes := staking.CalculateVoteWeight(test.cfg.Genesis.VoteWeightCalConsts, &staking.VoteBucket{AutoStake: true, StakedDuration: time.Duration(2*stakeDurationBlocks.Uint64()/uint64(blocksPerDay)*24) * (time.Hour), StakedAmount: stakeAmount}, false) + tmpVotes.Sub(tmpVotes, preStakeVotes) + tmpVotes.Add(tmpVotes, postVotes) + require.Equal(tmpVotes.String(), candidate.TotalWeightedVotes) + }}, + }, + }, + { + name: "unstake", + preActs: append([]*actionWithTime{ + {mustNoErr(action.SignedExecution(contractAddress, identityset.PrivateKey(stakerID), test.nonceMgr.pop(identityset.Address(stakerID).String()), big.NewInt(0), gasLimit, gasPrice, mustCallData("unlock(uint256)", big.NewInt(1)), action.WithChainID(chainID))), unlockTime}, + }, genTransferActions(20)...), + act: &actionWithTime{mustNoErr(action.SignedExecution(contractAddress, identityset.PrivateKey(stakerID), test.nonceMgr.pop(identityset.Address(stakerID).String()), big.NewInt(0), gasLimit, gasPrice, mustCallData("unstake(uint256)", big.NewInt(1)), action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, + &bucketExpect{&iotextypes.VoteBucket{Index: 1, ContractAddress: contractAddress, Owner: identityset.Address(stakerID).String(), CandidateAddress: identityset.Address(candOwnerID).String(), StakedDuration: uint32(2 * stakeDurationBlocks.Uint64() / uint64(blocksPerDay)), StakedDurationBlockNumber: stakeDurationBlocks.Uint64() * 2, CreateTime: timestamppb.New(time.Time{}), StakeStartTime: timestamppb.New(time.Time{}), StakeStartBlockHeight: 6, CreateBlockHeight: 3, UnstakeStartTime: timestamppb.New(time.Time{}), UnstakeStartBlockHeight: 27, StakedAmount: stakeAmount.String(), AutoStake: false}}, + &functionExpect{func(test *e2etest, act *action.SealedEnvelope, receipt *action.Receipt, err error) { + candidate, err := test.getCandidateByName("cand1") + require.NoError(err) + preStakeVotes := staking.CalculateVoteWeight(test.cfg.Genesis.VoteWeightCalConsts, &staking.VoteBucket{AutoStake: true, StakedDuration: time.Duration(2*stakeDurationBlocks.Uint64()/uint64(blocksPerDay)*24) * (time.Hour), StakedAmount: stakeAmount}, false) + tmpVotes.Sub(tmpVotes, preStakeVotes) + require.Equal(tmpVotes.String(), candidate.TotalWeightedVotes) + }}, + }, + }, + { + name: "withdraw", + preFunc: func(e *e2etest) { + acc, err := e.api.GetAccount(context.Background(), &iotexapi.GetAccountRequest{Address: identityset.Address(beneficiaryID).String()}) + require.NoError(err) + _, ok := tmpBalance.SetString(acc.AccountMeta.Balance, 10) + require.True(ok) + }, + preActs: genTransferActions(30), + act: &actionWithTime{mustNoErr(action.SignedExecution(contractAddress, identityset.PrivateKey(stakerID), test.nonceMgr.pop(identityset.Address(stakerID).String()), big.NewInt(0), gasLimit, gasPrice, mustCallData("withdraw(uint256,address)", big.NewInt(1), common.BytesToAddress(identityset.Address(beneficiaryID).Bytes())), action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, + &noBucketExpect{1, contractAddress}, + &functionExpect{func(test *e2etest, act *action.SealedEnvelope, receipt *action.Receipt, err error) { + acc, err := test.api.GetAccount(context.Background(), &iotexapi.GetAccountRequest{Address: identityset.Address(beneficiaryID).String()}) + require.NoError(err) + tmpBalance.Add(tmpBalance, stakeAmount) + require.Equal(tmpBalance.String(), acc.AccountMeta.Balance) + }}, + }, + }, + { + name: "change candidate", + preActs: []*actionWithTime{ + {mustNoErr(action.SignedCandidateRegister(test.nonceMgr.pop(identityset.Address(candOwnerID2).String()), "cand2", identityset.Address(2).String(), identityset.Address(2).String(), identityset.Address(candOwnerID2).String(), registerAmount.String(), 1, true, nil, gasLimit, gasPrice, identityset.PrivateKey(candOwnerID2), action.WithChainID(chainID))), time.Now()}, + {mustNoErr(action.SignedExecution(contractAddress, identityset.PrivateKey(stakerID), test.nonceMgr.pop(identityset.Address(stakerID).String()), stakeAmount, gasLimit, gasPrice, mustCallData("stake(uint256,address)", stakeDurationBlocks, common.BytesToAddress(identityset.Address(candOwnerID).Bytes())), action.WithChainID(chainID))), stakeTime}, + }, + act: &actionWithTime{mustNoErr(action.SignedExecution(contractAddress, identityset.PrivateKey(stakerID), test.nonceMgr.pop(identityset.Address(stakerID).String()), big.NewInt(0), gasLimit, gasPrice, mustCallData("changeDelegate(uint256,address)", big.NewInt(2), common.BytesToAddress(identityset.Address(candOwnerID2).Bytes())), action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, + &bucketExpect{&iotextypes.VoteBucket{Index: 2, ContractAddress: contractAddress, Owner: identityset.Address(stakerID).String(), CandidateAddress: identityset.Address(candOwnerID2).String(), StakedDuration: uint32(stakeDurationBlocks.Uint64() / uint64(blocksPerDay)), StakedDurationBlockNumber: stakeDurationBlocks.Uint64(), CreateTime: timestamppb.New(time.Time{}), StakeStartTime: timestamppb.New(time.Time{}), StakeStartBlockHeight: 60, CreateBlockHeight: 60, UnstakeStartTime: timestamppb.New(time.Time{}), UnstakeStartBlockHeight: math.MaxUint64, StakedAmount: stakeAmount.String(), AutoStake: true}}, + &functionExpect{func(test *e2etest, act *action.SealedEnvelope, receipt *action.Receipt, err error) { + candidate, err := test.getCandidateByName("cand1") + require.NoError(err) + require.Equal(tmpVotes.String(), candidate.TotalWeightedVotes) + }}, + }, + }, + { + name: "batch stake", + preFunc: func(e *e2etest) { + candidate, err := e.getCandidateByName("cand2") + require.NoError(err) + _, ok := tmpVotes.SetString(candidate.TotalWeightedVotes, 10) + require.True(ok) + }, + act: &actionWithTime{mustNoErr(action.SignedExecution(contractAddress, identityset.PrivateKey(stakerID), test.nonceMgr.pop(identityset.Address(stakerID).String()), big.NewInt(0).Mul(big.NewInt(10), stakeAmount), gasLimit, gasPrice, mustCallData("stake(uint256,uint256,address,uint256)", stakeAmount, stakeDurationBlocks, common.BytesToAddress(identityset.Address(candOwnerID2).Bytes()), big.NewInt(10)), action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, + &bucketExpect{&iotextypes.VoteBucket{Index: 3, ContractAddress: contractAddress, Owner: identityset.Address(stakerID).String(), CandidateAddress: identityset.Address(candOwnerID2).String(), StakedDuration: uint32(stakeDurationBlocks.Uint64() / uint64(blocksPerDay)), StakedDurationBlockNumber: stakeDurationBlocks.Uint64(), CreateTime: timestamppb.New(time.Time{}), StakeStartTime: timestamppb.New(time.Time{}), StakeStartBlockHeight: 62, CreateBlockHeight: 62, UnstakeStartTime: timestamppb.New(time.Time{}), UnstakeStartBlockHeight: math.MaxUint64, StakedAmount: stakeAmount.String(), AutoStake: true}}, + &bucketExpect{&iotextypes.VoteBucket{Index: 12, ContractAddress: contractAddress, Owner: identityset.Address(stakerID).String(), CandidateAddress: identityset.Address(candOwnerID2).String(), StakedDuration: uint32(stakeDurationBlocks.Uint64() / uint64(blocksPerDay)), StakedDurationBlockNumber: stakeDurationBlocks.Uint64(), CreateTime: timestamppb.New(time.Time{}), StakeStartTime: timestamppb.New(time.Time{}), StakeStartBlockHeight: 62, CreateBlockHeight: 62, UnstakeStartTime: timestamppb.New(time.Time{}), UnstakeStartBlockHeight: math.MaxUint64, StakedAmount: stakeAmount.String(), AutoStake: true}}, + &functionExpect{func(test *e2etest, act *action.SealedEnvelope, receipt *action.Receipt, err error) { + candidate, err := test.getCandidateByName("cand2") + require.NoError(err) + deltaVotes := staking.CalculateVoteWeight(test.cfg.Genesis.VoteWeightCalConsts, &staking.VoteBucket{AutoStake: true, StakedDuration: time.Duration(stakeDurationBlocks.Uint64()/uint64(blocksPerDay)*24) * (time.Hour), StakedAmount: stakeAmount}, false) + tmpVotes.Add(tmpVotes, deltaVotes.Mul(deltaVotes, big.NewInt(10))) + require.Equal(tmpVotes.String(), candidate.TotalWeightedVotes) + }}, + }, + }, + { + name: "merge", + act: &actionWithTime{mustNoErr(action.SignedExecution(contractAddress, identityset.PrivateKey(stakerID), test.nonceMgr.pop(identityset.Address(stakerID).String()), big.NewInt(0), gasLimit, gasPrice, mustCallData("merge(uint256[],uint256)", []*big.Int{big.NewInt(3), big.NewInt(4), big.NewInt(5)}, stakeDurationBlocks), action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, + &bucketExpect{&iotextypes.VoteBucket{Index: 3, ContractAddress: contractAddress, Owner: identityset.Address(stakerID).String(), CandidateAddress: identityset.Address(candOwnerID2).String(), StakedDuration: uint32(stakeDurationBlocks.Uint64() / uint64(blocksPerDay)), StakedDurationBlockNumber: stakeDurationBlocks.Uint64(), CreateTime: timestamppb.New(time.Time{}), StakeStartTime: timestamppb.New(time.Time{}), StakeStartBlockHeight: 62, CreateBlockHeight: 62, UnstakeStartTime: timestamppb.New(time.Time{}), UnstakeStartBlockHeight: math.MaxUint64, StakedAmount: big.NewInt(0).Mul(stakeAmount, big.NewInt(3)).String(), AutoStake: true}}, + &noBucketExpect{4, contractAddress}, &noBucketExpect{5, contractAddress}, + &functionExpect{func(test *e2etest, act *action.SealedEnvelope, receipt *action.Receipt, err error) { + candidate, err := test.getCandidateByName("cand2") + require.NoError(err) + subVotes := staking.CalculateVoteWeight(test.cfg.Genesis.VoteWeightCalConsts, &staking.VoteBucket{AutoStake: true, StakedDuration: time.Duration(stakeDurationBlocks.Uint64()/uint64(blocksPerDay)*24) * (time.Hour), StakedAmount: stakeAmount}, false) + addVotes := staking.CalculateVoteWeight(test.cfg.Genesis.VoteWeightCalConsts, &staking.VoteBucket{AutoStake: true, StakedDuration: time.Duration(stakeDurationBlocks.Uint64()/uint64(blocksPerDay)*24) * (time.Hour), StakedAmount: big.NewInt(0).Mul(stakeAmount, big.NewInt(3))}, false) + tmpVotes.Sub(tmpVotes, subVotes.Mul(subVotes, big.NewInt(3))) + tmpVotes.Add(tmpVotes, addVotes) + require.Equal(tmpVotes.String(), candidate.TotalWeightedVotes) + }}, + }, + }, + { + name: "expand", + act: &actionWithTime{mustNoErr(action.SignedExecution(contractAddress, identityset.PrivateKey(stakerID), test.nonceMgr.pop(identityset.Address(stakerID).String()), stakeAmount, gasLimit, gasPrice, mustCallData("expandBucket(uint256,uint256)", big.NewInt(3), big.NewInt(0).Mul(stakeDurationBlocks, big.NewInt(2))), action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, + &bucketExpect{&iotextypes.VoteBucket{Index: 3, ContractAddress: contractAddress, Owner: identityset.Address(stakerID).String(), CandidateAddress: identityset.Address(candOwnerID2).String(), StakedDuration: uint32(stakeDurationBlocks.Uint64()/uint64(blocksPerDay)) * 2, StakedDurationBlockNumber: stakeDurationBlocks.Uint64() * 2, CreateTime: timestamppb.New(time.Time{}), StakeStartTime: timestamppb.New(time.Time{}), StakeStartBlockHeight: 62, CreateBlockHeight: 62, UnstakeStartTime: timestamppb.New(time.Time{}), UnstakeStartBlockHeight: math.MaxUint64, StakedAmount: big.NewInt(0).Mul(stakeAmount, big.NewInt(4)).String(), AutoStake: true}}, + }, + }, + { + name: "donate", + preFunc: func(e *e2etest) { + resp, err := test.api.GetAccount(context.Background(), &iotexapi.GetAccountRequest{Address: identityset.Address(beneficiaryID).String()}) + require.NoError(err) + _, ok := tmpBalance.SetString(resp.AccountMeta.Balance, 10) + require.True(ok) + }, + act: &actionWithTime{mustNoErr(action.SignedExecution(contractAddress, identityset.PrivateKey(stakerID), test.nonceMgr.pop(identityset.Address(stakerID).String()), big.NewInt(0), gasLimit, gasPrice, mustCallData("donate(uint256,uint256)", big.NewInt(3), stakeAmount), action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, + &bucketExpect{&iotextypes.VoteBucket{Index: 3, ContractAddress: contractAddress, Owner: identityset.Address(stakerID).String(), CandidateAddress: identityset.Address(candOwnerID2).String(), StakedDuration: uint32(stakeDurationBlocks.Uint64()/uint64(blocksPerDay)) * 2, StakedDurationBlockNumber: stakeDurationBlocks.Uint64() * 2, CreateTime: timestamppb.New(time.Time{}), StakeStartTime: timestamppb.New(time.Time{}), StakeStartBlockHeight: 62, CreateBlockHeight: 62, UnstakeStartTime: timestamppb.New(time.Time{}), UnstakeStartBlockHeight: math.MaxUint64, StakedAmount: big.NewInt(0).Mul(stakeAmount, big.NewInt(3)).String(), AutoStake: true}}, + &functionExpect{func(test *e2etest, act *action.SealedEnvelope, receipt *action.Receipt, err error) { + resp, err := test.api.GetAccount(context.Background(), &iotexapi.GetAccountRequest{Address: identityset.Address(beneficiaryID).String()}) + require.NoError(err) + tmpBalance.Add(tmpBalance, stakeAmount) + require.Equal(tmpBalance.String(), resp.AccountMeta.Balance) + }}, + }, + }, + }) +} + +func methodSignToID(sign string) []byte { + hash := crypto.Keccak256Hash([]byte(sign)) + return hash.Bytes()[:4] +} + +func abiCall(_abi abi.ABI, methodSign string, args ...interface{}) ([]byte, error) { + if methodSign == "" { + return _abi.Pack("", args...) + } + m, err := _abi.MethodById(methodSignToID(methodSign)) + if err != nil { + return nil, err + } + return _abi.Pack(m.Name, args...) +} diff --git a/e2etest/e2etest.go b/e2etest/e2etest.go index 940453a982..7bbc8df4dd 100644 --- a/e2etest/e2etest.go +++ b/e2etest/e2etest.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + "google.golang.org/protobuf/proto" "github.com/iotexproject/iotex-proto/golang/iotexapi" "github.com/iotexproject/iotex-proto/golang/iotextypes" @@ -125,6 +126,38 @@ func (e *e2etest) withTest(t *testing.T) *e2etest { } } +func (e *e2etest) getCandidateByName(name string) (*iotextypes.CandidateV2, error) { + methodName, err := proto.Marshal(&iotexapi.ReadStakingDataMethod{ + Method: iotexapi.ReadStakingDataMethod_CANDIDATE_BY_NAME, + }) + if err != nil { + return nil, err + } + arg, err := proto.Marshal(&iotexapi.ReadStakingDataRequest{ + Request: &iotexapi.ReadStakingDataRequest_CandidateByName_{ + CandidateByName: &iotexapi.ReadStakingDataRequest_CandidateByName{ + CandName: name, + }, + }, + }) + if err != nil { + return nil, err + } + resp, err := e.api.ReadState(context.Background(), &iotexapi.ReadStateRequest{ + ProtocolID: []byte("staking"), + MethodName: methodName, + Arguments: [][]byte{arg}, + }) + if err != nil { + return nil, err + } + candidate := &iotextypes.CandidateV2{} + if err = proto.Unmarshal(resp.GetData(), candidate); err != nil { + return nil, err + } + return candidate, nil +} + func addOneTx(ctx context.Context, ap actpool.ActPool, bc blockchain.Blockchain, tx *actionWithTime) (*action.SealedEnvelope, *action.Receipt, error) { if err := ap.Add(ctx, tx.act); err != nil { return tx.act, nil, err diff --git a/e2etest/expect.go b/e2etest/expect.go index 593610e1b5..1f5e924054 100644 --- a/e2etest/expect.go +++ b/e2etest/expect.go @@ -69,7 +69,7 @@ func (be *basicActionExpect) expect(test *e2etest, act *action.SealedEnvelope, r require.Nil(receipt) return } - require.Equal(be.status, receipt.Status) + require.Equalf(be.status, receipt.Status, "revert msg: %s", receipt.ExecutionRevertMsg()) require.Equal(be.executionRevertMsg, receipt.ExecutionRevertMsg()) } @@ -150,7 +150,7 @@ func (be *bucketExpect) expect(test *e2etest, act *action.SealedEnvelope, receip idx := slices.IndexFunc(vbs.Buckets, func(vb *iotextypes.VoteBucket) bool { return vb.ContractAddress == be.bucket.ContractAddress }) - require.Greater(idx, -1) + require.Greaterf(idx, -1, "bucket not found, index: %d, contract: %s", be.bucket.Index, be.bucket.ContractAddress) require.EqualValues(be.bucket.String(), vbs.Buckets[idx].String()) } diff --git a/e2etest/native_staking_test.go b/e2etest/native_staking_test.go index c0e29da4e3..f66fb53c9b 100644 --- a/e2etest/native_staking_test.go +++ b/e2etest/native_staking_test.go @@ -52,8 +52,6 @@ var ( vote, _ = new(big.Int).SetString("100000000000000000000", 10) autoStakeVote, _ = new(big.Int).SetString("103801784016923925869", 10) initBalance, _ = new(big.Int).SetString("100000000000000000000000000", 10) - - stakingContractBytecode, _ = hex.DecodeString(`60a060405234801562000010575f80fd5b5060405162002f9a38038062002f9a83398101604081905262000033916200013a565b60405180604001604052806009815260200168109d58dad95d13919560ba1b815250604051806040016040528060038152602001621092d560ea1b815250815f908162000081919062000214565b50600162000090828262000214565b505050620000ad620000a7620000e560201b60201c565b620000e9565b6006805460ff60a01b19169055608091909152600980546001600160a01b0319166001600160a01b03909216919091179055620002e0565b3390565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f80604083850312156200014c575f80fd5b825160208401519092506001600160a01b03811681146200016b575f80fd5b809150509250929050565b634e487b7160e01b5f52604160045260245ffd5b600181811c908216806200019f57607f821691505b602082108103620001be57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156200020f57805f5260205f20601f840160051c81016020851015620001eb5750805b601f840160051c820191505b818110156200020c575f8155600101620001f7565b50505b505050565b81516001600160401b0381111562000230576200023062000176565b62000248816200024184546200018a565b84620001c4565b602080601f8311600181146200027e575f8415620002665750858301515b5f19600386901b1c1916600185901b178555620002d8565b5f85815260208120601f198616915b82811015620002ae578886015182559484019460019091019084016200028d565b5085821015620002cc57878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b608051612c9a620003005f395f818161071f0152611bd80152612c9a5ff3fe60806040526004361061025e575f3560e01c8063715018a61161013f578063b88d4fde116100b3578063ddbcb5fa11610078578063ddbcb5fa1461070e578063e449f34114610741578063e985e9c514610760578063eec7ee73146107a7578063f0b56b5d146107ba578063f2fde38b146107ce575f80fd5b8063b88d4fde1461068a578063b8f4bd7b146106a9578063bbe33ea5146106c8578063c87b56dd146106db578063d0949f99146106fa575f80fd5b80638da5cb5b116101045780638da5cb5b146105e857806393b6ef591461060557806395d89b411461062457806398ca3b7614610638578063a22cb46514610657578063b1724b4614610676575f80fd5b8063715018a6146105865780637acb77571461059a5780638456cb59146105ad57806384ac79b0146105c1578063863e76db146105d4575f80fd5b806338af3eed116101d65780635ceb8b5b1161019b5780635ceb8b5b146104cb5780635d36598f146104ea5780636198e339146105095780636352211e1461052857806370a0823114610547578063711563d414610566575f80fd5b806338af3eed146103f65780633f4ba83a1461041557806342842e0e14610429578063431cd92a146104485780635c975abb146104ad575f80fd5b8063095ea7b311610227578063095ea7b31461033c5780630cdd53f61461035b5780630f5b2ca51461037a5780631338736f1461039957806323b872dd146103b85780632e17de78146103d7575f80fd5b8062f714ce1461026257806301ffc9a71461028357806303459b16146102b757806306fdde03146102e4578063081812fc14610305575b5f80fd5b34801561026d575f80fd5b5061028161027c3660046124c4565b6107ed565b005b34801561028e575f80fd5b506102a261029d366004612507565b61080e565b60405190151581526020015b60405180910390f35b3480156102c2575f80fd5b506102d66102d1366004612522565b61085f565b6040519081526020016102ae565b3480156102ef575f80fd5b506102f8610883565b6040516102ae9190612586565b348015610310575f80fd5b5061032461031f366004612522565b610912565b6040516001600160a01b0390911681526020016102ae565b348015610347575f80fd5b50610281610356366004612598565b610937565b348015610366575f80fd5b506102816103753660046125c2565b610a4b565b348015610385575f80fd5b506102816103943660046124c4565b610b12565b3480156103a4575f80fd5b506102816103b33660046125c2565b610b28565b3480156103c3575f80fd5b506102816103d23660046125e2565b610b43565b3480156103e2575f80fd5b506102816103f1366004612522565b610b74565b348015610401575f80fd5b50600954610324906001600160a01b031681565b348015610420575f80fd5b50610281610b88565b348015610434575f80fd5b506102816104433660046125e2565b610b9a565b348015610453575f80fd5b50610467610462366004612522565b610bb4565b6040516102ae9190815181526020808301519082015260408083015190820152606080830151908201526080918201516001600160a01b03169181019190915260a00190565b3480156104b8575f80fd5b50600654600160a01b900460ff166102a2565b3480156104d6575f80fd5b506102816104e5366004612668565b610c4a565b3480156104f5575f80fd5b506102816105043660046126b0565b610c95565b348015610514575f80fd5b50610281610523366004612522565b610cd0565b348015610533575f80fd5b50610324610542366004612522565b610ce1565b348015610552575f80fd5b506102d66105613660046126ef565b610d40565b61057961057436600461270a565b610dc4565b6040516102ae9190612746565b348015610591575f80fd5b50610281610e91565b6102d66105a83660046124c4565b610ea2565b3480156105b8575f80fd5b50610281610ed1565b6102816105cf3660046125c2565b610ee1565b3480156105df575f80fd5b506102d6600a81565b3480156105f3575f80fd5b506006546001600160a01b0316610324565b348015610610575f80fd5b506102d661061f366004612522565b610f9e565b34801561062f575f80fd5b506102f8610fdc565b348015610643575f80fd5b50610281610652366004612789565b610feb565b348015610662575f80fd5b506102816106713660046127dc565b611027565b348015610681575f80fd5b506102d6611032565b348015610695575f80fd5b506102816106a4366004612851565b61104d565b3480156106b4575f80fd5b506102816106c3366004612789565b61107f565b6102816106d6366004612668565b6110bb565b3480156106e6575f80fd5b506102f86106f5366004612522565b6111ee565b348015610705575f80fd5b506102d65f1981565b348015610719575f80fd5b506102d67f000000000000000000000000000000000000000000000000000000000000000081565b34801561074c575f80fd5b5061028161075b3660046126b0565b61125d565b34801561076b575f80fd5b506102a261077a36600461290e565b6001600160a01b039182165f90815260056020908152604080832093909416825291909152205460ff1690565b6105796107b536600461293a565b611298565b3480156107c5575f80fd5b506102d6611382565b3480156107d9575f80fd5b506102816107e83660046126ef565b61138e565b6107f5611404565b816107ff81611451565b610809838361148b565b505050565b5f6001600160e01b031982166380ac58cd60e01b148061083e57506001600160e01b03198216635b5e139f60e01b145b8061085957506301ffc9a760e01b6001600160e01b03198316145b92915050565b5f6108698261151f565b5f828152600860205260409020600301546108599061157d565b60605f8054610891906129fb565b80601f01602080910402602001604051908101604052809291908181526020018280546108bd906129fb565b80156109085780601f106108df57610100808354040283529160200191610908565b820191905f5260205f20905b8154815290600101906020018083116108eb57829003601f168201915b5050505050905090565b5f61091c8261151f565b505f908152600460205260409020546001600160a01b031690565b5f61094182610ce1565b9050806001600160a01b0316836001600160a01b0316036109b35760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b60648201526084015b60405180910390fd5b336001600160a01b03821614806109cf57506109cf813361077a565b610a415760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c00000060648201526084016109aa565b61080983836115d0565b81610a5581611451565b5f8381526008602052604090206003810154610a709061163d565b8054831180610a7d575082155b15610a9b576040516359b9300b60e11b815260040160405180910390fd5b82815f015f828254610aad9190612a47565b9091555050600954610ac8906001600160a01b03168461165e565b6009546040518481526001600160a01b039091169085907f1b606d34afacd55873aba0fd274841a10c63e18455f2dffebad2fc60a36b2c839060200160405180910390a350505050565b610b1a611404565b610b2482826116ce565b5050565b610b30611404565b610b3981611775565b610b2482826117c1565b610b4d3382611851565b610b695760405162461bcd60e51b81526004016109aa90612a5a565b6108098383836118cd565b610b7c611404565b610b8581611a3c565b50565b610b90611acd565b610b98611b27565b565b61080983838360405180602001604052805f81525061104d565b610bea6040518060a001604052805f81526020015f81526020015f81526020015f81526020015f6001600160a01b031681525090565b610bf38261151f565b505f90815260086020908152604091829020825160a08101845281548152600182015492810192909252600281015492820192909252600382015460608201526004909101546001600160a01b0316608082015290565b610c52611404565b610c5b81611775565b5f5b82811015610c8f57610c87848483818110610c7a57610c7a612aa7565b90506020020135836117c1565b600101610c5d565b50505050565b610c9d611404565b5f5b8181101561080957610cc8838383818110610cbc57610cbc612aa7565b90506020020135611b7c565b600101610c9f565b610cd8611404565b610b8581611b7c565b5f818152600260205260408120546001600160a01b0316806108595760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b60448201526064016109aa565b5f6001600160a01b038216610da95760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b60648201526084016109aa565b506001600160a01b03165f9081526003602052604090205490565b6060610dce611404565b610dd785611bd6565b610de084611775565b34610deb8387612abb565b14610e09576040516359b9300b60e11b815260040160405180910390fd5b8167ffffffffffffffff811115610e2257610e2261280c565b604051908082528060200260200182016040528015610e4b578160200160208202803683370190505b5090505f5b82811015610e8857610e63868686611c17565b828281518110610e7557610e75612aa7565b6020908102919091010152600101610e50565b50949350505050565b610e99611acd565b610b985f611cff565b5f610eab611404565b34610eb581611bd6565b610ebe84611775565b610ec9818585611c17565b949350505050565b610ed9611acd565b610b98611d50565b610ee9611404565b81610ef381611451565b610efc82611775565b5f8381526008602052604090206002810154610f1790611d93565b8060010154831015610f3c5760405163257a8d4b60e11b815260040160405180910390fd5b34815f015f828254610f4e9190612ad2565b9091555050600181018390558054604080519182526020820185905285917fd29e04160a74f0dbab5e7b82ef0392d86d11ac2939e5883eb3353be4cfedb83e91015b60405180910390a250505050565b5f610fa88261151f565b5f8281526008602052604090206003810154610fc39061163d565b610fd581600201548260010154611db4565b9392505050565b606060018054610891906129fb565b610ff3611404565b5f5b82811015610c8f5761101f84848381811061101257611012612aa7565b90506020020135836116ce565b600101610ff5565b610b24338383611deb565b61103f600a61016d612abb565b61104a906003612abb565b81565b6110573383611851565b6110735760405162461bcd60e51b81526004016109aa90612a5a565b610c8f84848484611eb8565b611087611404565b5f5b82811015610c8f576110b38484838181106110a6576110a6612aa7565b905060200201358361148b565b600101611089565b6110c3611404565b600182116110e457604051637e878a5f60e11b815260040160405180910390fd5b6110ed81611775565b345f80845b80156111e5575f190186868281811061110d5761110d612aa7565b90506020020135925061111f83611451565b5f838152600860205260409020600381015490925061113d9061163d565b61114f82600201548360010154611db4565b85101561116f5760405163257a8d4b60e11b815260040160405180910390fd5b815461117b9085612ad2565b935080156111915761118c83611eeb565b6110f2565b5f196002830155838255600182018590556040517fb3f4c8ca702dbbd32d9a25ce17b1942a5060284d9d69fc4fcac8fb0397891b12906111d8908990899088908a90612ae5565b60405180910390a16110f2565b50505050505050565b60606111f98261151f565b5f61120e60408051602081019091525f815290565b90505f81511161122c5760405180602001604052805f815250610fd5565b8061123684611f8a565b604051602001611247929190612b29565b6040516020818303038152906040529392505050565b611265611404565b5f5b818110156108095761129083838381811061128457611284612aa7565b90506020020135611a3c565b600101611267565b60606112a2611404565b6112ab84611bd6565b6112b483611775565b348251856112c29190612abb565b146112e0576040516359b9300b60e11b815260040160405180910390fd5b815167ffffffffffffffff8111156112fa576112fa61280c565b604051908082528060200260200182016040528015611323578160200160208202803683370190505b5090505f5b825181101561137a57611355858585848151811061134857611348612aa7565b6020026020010151611c17565b82828151811061136757611367612aa7565b6020908102919091010152600101611328565b509392505050565b61104a600a6003612abb565b611396611acd565b6001600160a01b0381166113fb5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016109aa565b610b8581611cff565b600654600160a01b900460ff1615610b985760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064016109aa565b61145a81610ce1565b6001600160a01b0316336001600160a01b031614610b8557604051630da4973960e31b815260040160405180910390fd5b8161149581611451565b5f83815260086020526040902060038101546114b09061157d565b156114ce57604051634e395b8560e01b815260040160405180910390fd5b6114d784611eeb565b6114e483825f015461165e565b6040516001600160a01b0384169085907fd964a27d45f595739c13d8b1160b57491050cacf3a2e5602207277d6228f64ee905f90a350505050565b5f818152600260205260409020546001600160a01b0316610b855760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b60448201526064016109aa565b5f5f19820361159f57604051636263fd5f60e11b815260040160405180910390fd5b5f6115ac600a6003612abb565b6115b69084612ad2565b90504381116115c757505f92915050565b43900392915050565b5f81815260046020526040902080546001600160a01b0319166001600160a01b038416908117909155819061160482610ce1565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b5f198114610b85576040516343febe2b60e01b815260040160405180910390fd5b5f826001600160a01b0316826040515f6040518083038185875af1925050503d805f81146116a7576040519150601f19603f3d011682016040523d82523d5f602084013e6116ac565b606091505b505090508061080957604051639a7058e160e01b815260040160405180910390fd5b816116d881611451565b5f83815260086020526040902060038101546116f39061163d565b60048101546001600160a01b0380851691160361172357604051637e878a5f60e11b815260040160405180910390fd5b6004810180546001600160a01b0319166001600160a01b03851690811790915560405190815284907f6f08c7e76d830d5f3d0a18fd27f4d8c0049b24a8689ddb39625e0864d894a9c190602001610f90565b611780600a82612b57565b1515806117a35750611795600a61016d612abb565b6117a0906003612abb565b81115b15610b855760405163257a8d4b60e11b815260040160405180910390fd5b816117cb81611451565b5f83815260086020526040902060038101546117e69061163d565b6117f881600201548260010154611db4565b8310156118185760405163257a8d4b60e11b815260040160405180910390fd5b5f19600282015560405183815284907f907fece23ce39fbcbceb71e515043fe29408353fbb393b25b35eb8a70a4bad0b90602001610f90565b5f8061185c83610ce1565b9050806001600160a01b0316846001600160a01b031614806118a257506001600160a01b038082165f9081526005602090815260408083209388168352929052205460ff165b80610ec95750836001600160a01b03166118bb84610912565b6001600160a01b031614949350505050565b826001600160a01b03166118e082610ce1565b6001600160a01b0316146119065760405162461bcd60e51b81526004016109aa90612b76565b6001600160a01b0382166119685760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b60648201526084016109aa565b611975838383600161201a565b826001600160a01b031661198882610ce1565b6001600160a01b0316146119ae5760405162461bcd60e51b81526004016109aa90612b76565b5f81815260046020908152604080832080546001600160a01b03199081169091556001600160a01b038781168086526003855283862080545f1901905590871680865283862080546001019055868652600290945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b80611a4681611451565b5f8281526008602052604090206003810154611a619061163d565b611a7381600201548260010154611db4565b15611a9157604051634e395b8560e01b815260040160405180910390fd5b4360028201819055600382015560405183907f11725367022c3ff288940f4b5473aa61c2da6a24af7363a1128ee2401e8983b2905f90a2505050565b6006546001600160a01b03163314610b985760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016109aa565b611b2f612026565b6006805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b80611b8681611451565b5f8281526008602052604090206002810154611ba190611d93565b43600282015560405183907ff27b6ce5b2f5e68ddb2fd95a8a909d4ecf1daaac270935fff052feacb24f1842905f90a2505050565b7f0000000000000000000000000000000000000000000000000000000000000000811015610b85576040516359b9300b60e11b815260040160405180910390fd5b5f80611c2560075460010190565b60078190556040805160a08101825287815260208082018881525f19838501818152606085019182526001600160a01b038a8116608087019081525f8981526008909652969094209451855591516001850155905160028401555160038301559151600490910180546001600160a01b031916919092161790559050611cab3382612076565b604080516001600160a01b03851681526020810187905290810185905281907f17700ceb1658b18206f427c1578048e87504106b14ec69e9b4586d9a95174a329060600160405180910390a2949350505050565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b611d58611404565b6006805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258611b5f3390565b5f198114610b855760405163da808c7560e01b815260040160405180910390fd5b5f5f198303611dc4575080610859565b5f611dcf8385612ad2565b9050438111611de1575f915050610859565b4390039392505050565b816001600160a01b0316836001600160a01b031603611e4c5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c65720000000000000060448201526064016109aa565b6001600160a01b038381165f81815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b611ec38484846118cd565b611ecf8484848461208f565b610c8f5760405162461bcd60e51b81526004016109aa90612bbb565b5f611ef582610ce1565b9050611f04815f84600161201a565b611f0d82610ce1565b5f83815260046020908152604080832080546001600160a01b03199081169091556001600160a01b0385168085526003845282852080545f190190558785526002909352818420805490911690555192935084927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b60605f611f968361218c565b60010190505f8167ffffffffffffffff811115611fb557611fb561280c565b6040519080825280601f01601f191660200182016040528015611fdf576020820181803683370190505b5090508181016020015b5f19016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a8504945084611fe957509392505050565b610c8f84848484612263565b600654600160a01b900460ff16610b985760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b60448201526064016109aa565b610b24828260405180602001604052805f8152506122e9565b5f6001600160a01b0384163b1561218157604051630a85bd0160e11b81526001600160a01b0385169063150b7a02906120d2903390899088908890600401612c0d565b6020604051808303815f875af192505050801561210c575060408051601f3d908101601f1916820190925261210991810190612c49565b60015b612167573d808015612139576040519150601f19603f3d011682016040523d82523d5f602084013e61213e565b606091505b5080515f0361215f5760405162461bcd60e51b81526004016109aa90612bbb565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610ec9565b506001949350505050565b5f8072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106121ca5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef810000000083106121f6576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061221457662386f26fc10000830492506010015b6305f5e100831061222c576305f5e100830492506008015b612710831061224057612710830492506004015b60648310612252576064830492506002015b600a83106108595760010192915050565b6001811115610c8f576001600160a01b038416156122a8576001600160a01b0384165f90815260036020526040812080548392906122a2908490612a47565b90915550505b6001600160a01b03831615610c8f576001600160a01b0383165f90815260036020526040812080548392906122de908490612ad2565b909155505050505050565b6122f3838361231b565b6122ff5f84848461208f565b6108095760405162461bcd60e51b81526004016109aa90612bbb565b6001600160a01b0382166123715760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f206164647265737360448201526064016109aa565b5f818152600260205260409020546001600160a01b0316156123d55760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016109aa565b6123e25f8383600161201a565b5f818152600260205260409020546001600160a01b0316156124465760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016109aa565b6001600160a01b0382165f81815260036020908152604080832080546001019055848352600290915280822080546001600160a01b0319168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6001600160a01b0381168114610b85575f80fd5b5f80604083850312156124d5575f80fd5b8235915060208301356124e7816124b0565b809150509250929050565b6001600160e01b031981168114610b85575f80fd5b5f60208284031215612517575f80fd5b8135610fd5816124f2565b5f60208284031215612532575f80fd5b5035919050565b5f5b8381101561255357818101518382015260200161253b565b50505f910152565b5f8151808452612572816020860160208601612539565b601f01601f19169290920160200192915050565b602081525f610fd5602083018461255b565b5f80604083850312156125a9575f80fd5b82356125b4816124b0565b946020939093013593505050565b5f80604083850312156125d3575f80fd5b50508035926020909101359150565b5f805f606084860312156125f4575f80fd5b83356125ff816124b0565b9250602084013561260f816124b0565b929592945050506040919091013590565b5f8083601f840112612630575f80fd5b50813567ffffffffffffffff811115612647575f80fd5b6020830191508360208260051b8501011115612661575f80fd5b9250929050565b5f805f6040848603121561267a575f80fd5b833567ffffffffffffffff811115612690575f80fd5b61269c86828701612620565b909790965060209590950135949350505050565b5f80602083850312156126c1575f80fd5b823567ffffffffffffffff8111156126d7575f80fd5b6126e385828601612620565b90969095509350505050565b5f602082840312156126ff575f80fd5b8135610fd5816124b0565b5f805f806080858703121561271d575f80fd5b84359350602085013592506040850135612736816124b0565b9396929550929360600135925050565b602080825282518282018190525f9190848201906040850190845b8181101561277d57835183529284019291840191600101612761565b50909695505050505050565b5f805f6040848603121561279b575f80fd5b833567ffffffffffffffff8111156127b1575f80fd5b6127bd86828701612620565b90945092505060208401356127d1816124b0565b809150509250925092565b5f80604083850312156127ed575f80fd5b82356127f8816124b0565b9150602083013580151581146124e7575f80fd5b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff811182821017156128495761284961280c565b604052919050565b5f805f8060808587031215612864575f80fd5b843561286f816124b0565b9350602085810135612880816124b0565b935060408601359250606086013567ffffffffffffffff808211156128a3575f80fd5b818801915088601f8301126128b6575f80fd5b8135818111156128c8576128c861280c565b6128da601f8201601f19168501612820565b915080825289848285010111156128ef575f80fd5b80848401858401375f8482840101525080935050505092959194509250565b5f806040838503121561291f575f80fd5b823561292a816124b0565b915060208301356124e7816124b0565b5f805f6060848603121561294c575f80fd5b833592506020808501359250604085013567ffffffffffffffff80821115612972575f80fd5b818701915087601f830112612985575f80fd5b8135818111156129975761299761280c565b8060051b91506129a8848301612820565b818152918301840191848101908a8411156129c1575f80fd5b938501935b838510156129eb57843592506129db836124b0565b82825293850193908501906129c6565b8096505050505050509250925092565b600181811c90821680612a0f57607f821691505b602082108103612a2d57634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561085957610859612a33565b6020808252602d908201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560408201526c1c881bdc88185c1c1c9bdd9959609a1b606082015260800190565b634e487b7160e01b5f52603260045260245ffd5b808202811582820484141761085957610859612a33565b8082018082111561085957610859612a33565b606080825281018490525f6001600160fb1b03851115612b03575f80fd5b8460051b8087608085013760208301949094525060408101919091520160800192915050565b5f8351612b3a818460208801612539565b835190830190612b4e818360208801612539565b01949350505050565b5f82612b7157634e487b7160e01b5f52601260045260245ffd5b500690565b60208082526025908201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060408201526437bbb732b960d91b606082015260800190565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b6001600160a01b03858116825284166020820152604081018390526080606082018190525f90612c3f9083018461255b565b9695505050505050565b5f60208284031215612c59575f80fd5b8151610fd5816124f256fea2646970667358221220205b2909c5a9d941c62e75ad22db4e210223e35d30466fe9ac16d3f20eeb406b64736f6c63430008160033`) ) var ( @@ -595,28 +593,13 @@ func checkAccountState( func TestCandidateTransferOwnership(t *testing.T) { require := require.New(t) - initCfg := func() config.Config { - cfg := deepcopy.Copy(config.Default).(config.Config) - initDBPaths(require, &cfg) - cfg.ActPool.MinGasPriceStr = "0" - cfg.Chain.TrieDBPatchFile = "" - cfg.Consensus.Scheme = config.NOOPScheme - cfg.Chain.EnableAsyncIndexWrite = false - cfg.Genesis.InitBalanceMap[identityset.Address(1).String()] = "100000000000000000000000000" - cfg.Genesis.InitBalanceMap[identityset.Address(2).String()] = "100000000000000000000000000" - cfg.Genesis.EndorsementWithdrawWaitingBlocks = 10 - cfg.Genesis.TsunamiBlockHeight = 1 - cfg.Genesis.UpernavikBlockHeight = 2 // enable CandidateIdentifiedByOwner feature - normalizeGenesisHeights(&cfg) - return cfg - } registerAmount, _ := big.NewInt(0).SetString("1200000000000000000000000", 10) gasLimit = uint64(10000000) gasPrice = big.NewInt(1) t.Run("transfer candidate ownership", func(t *testing.T) { - test := newE2ETest(t, initCfg()) + test := newE2ETest(t, initCfg(require)) defer test.teardown() oldOwnerID := 1 newOwnerID := 2 @@ -630,6 +613,20 @@ func TestCandidateTransferOwnership(t *testing.T) { act: &actionWithTime{mustNoErr(action.SignedCandidateTransferOwnership(test.nonceMgr.pop(identityset.Address(oldOwnerID).String()), identityset.Address(newOwnerID).String(), nil, gasLimit, gasPrice, identityset.PrivateKey(oldOwnerID), action.WithChainID(chainID))), time.Now()}, expect: []actionExpect{successExpect, &candidateExpect{"cand1", &iotextypes.CandidateV2{Name: "cand1", OperatorAddress: identityset.Address(1).String(), RewardAddress: identityset.Address(1).String(), TotalWeightedVotes: "1245621408203087110422302", SelfStakingTokens: "0", OwnerAddress: identityset.Address(newOwnerID).String(), SelfStakeBucketIdx: math.MaxUint64}}}, }, + { + name: "cannot transfer from non-owner", + act: &actionWithTime{mustNoErr(action.SignedCandidateTransferOwnership(test.nonceMgr.pop(identityset.Address(4).String()), identityset.Address(oldOwnerID).String(), nil, gasLimit, gasPrice, identityset.PrivateKey(4), action.WithChainID(chainID))), + time.Now(), + }, + expect: []actionExpect{&basicActionExpect{nil, uint64(iotextypes.ReceiptStatus_ErrCandidateNotExist), ""}}, + }, + { + name: "cannot transfer to self", + act: &actionWithTime{mustNoErr(action.SignedCandidateTransferOwnership(test.nonceMgr.pop(identityset.Address(newOwnerID).String()), identityset.Address(newOwnerID).String(), nil, gasLimit, gasPrice, identityset.PrivateKey(newOwnerID), action.WithChainID(chainID))), + time.Now(), + }, + expect: []actionExpect{&basicActionExpect{nil, uint64(iotextypes.ReceiptStatus_ErrUnauthorizedOperator), ""}}, + }, { name: "transfer back to old owner", act: &actionWithTime{mustNoErr(action.SignedCandidateTransferOwnership(test.nonceMgr.pop(identityset.Address(newOwnerID).String()), identityset.Address(oldOwnerID).String(), nil, gasLimit, gasPrice, identityset.PrivateKey(newOwnerID), action.WithChainID(chainID))), time.Now()}, @@ -637,8 +634,29 @@ func TestCandidateTransferOwnership(t *testing.T) { }, }) }) + t.Run("transfer endorsed candidate", func(t *testing.T) { + test := newE2ETest(t, initCfg(require)) + defer test.teardown() + oldOwnerID := 1 + newOwnerID := 2 + stakerID := 3 + chainID := test.cfg.Chain.ID + test.run([]*testcase{ + { + name: "success to transfer candidate ownership", + preActs: []*actionWithTime{ + {mustNoErr(action.SignedCandidateRegister(test.nonceMgr.pop(identityset.Address(oldOwnerID).String()), "cand1", identityset.Address(1).String(), identityset.Address(1).String(), identityset.Address(oldOwnerID).String(), registerAmount.String(), 1, true, nil, gasLimit, gasPrice, identityset.PrivateKey(oldOwnerID), action.WithChainID(chainID))), time.Now()}, + {mustNoErr(action.SignedCreateStake(test.nonceMgr.pop(identityset.Address(stakerID).String()), "cand1", registerAmount.String(), 1, true, nil, gasLimit, gasPrice, identityset.PrivateKey(stakerID), action.WithChainID(chainID))), time.Now()}, + {mustNoErr(action.SignedCandidateEndorsement(test.nonceMgr.pop(identityset.Address(stakerID).String()), 1, action.CandidateEndorsementOpEndorse, gasLimit, gasPrice, identityset.PrivateKey(stakerID), action.WithChainID(chainID))), time.Now()}, + {mustNoErr(action.SignedCandidateActivate(test.nonceMgr.pop(identityset.Address(oldOwnerID).String()), 1, gasLimit, gasPrice, identityset.PrivateKey(oldOwnerID), action.WithChainID(chainID))), time.Now()}, + }, + act: &actionWithTime{mustNoErr(action.SignedCandidateTransferOwnership(test.nonceMgr.pop(identityset.Address(oldOwnerID).String()), identityset.Address(newOwnerID).String(), nil, gasLimit, gasPrice, identityset.PrivateKey(oldOwnerID), action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, &candidateExpect{"cand1", &iotextypes.CandidateV2{Name: "cand1", OperatorAddress: identityset.Address(1).String(), RewardAddress: identityset.Address(1).String(), TotalWeightedVotes: "2491242816406174220844604", SelfStakingTokens: registerAmount.String(), OwnerAddress: identityset.Address(newOwnerID).String(), SelfStakeBucketIdx: 1}}}, + }, + }) + }) t.Run("candidate activate after transfer candidate ownership", func(t *testing.T) { - test := newE2ETest(t, initCfg()) + test := newE2ETest(t, initCfg(require)) defer test.teardown() oldOwnerID := 1 @@ -667,7 +685,7 @@ func TestCandidateTransferOwnership(t *testing.T) { }) }) t.Run("candidate endorsement after transfer candidate ownership", func(t *testing.T) { - test := newE2ETest(t, initCfg()) + test := newE2ETest(t, initCfg(require)) defer test.teardown() oldOwnerID := 1 @@ -690,7 +708,7 @@ func TestCandidateTransferOwnership(t *testing.T) { }) }) t.Run("candidate register after transfer candidate ownership", func(t *testing.T) { - test := newE2ETest(t, initCfg()) + test := newE2ETest(t, initCfg(require)) defer test.teardown() oldOwnerID := 1 @@ -726,7 +744,7 @@ func TestCandidateTransferOwnership(t *testing.T) { }) }) t.Run("candidate update after transfer candidate ownership", func(t *testing.T) { - test := newE2ETest(t, initCfg()) + test := newE2ETest(t, initCfg(require)) defer test.teardown() oldOwnerID := 1 @@ -751,7 +769,7 @@ func TestCandidateTransferOwnership(t *testing.T) { }) }) t.Run("stake change candidate after transfer candidate ownership", func(t *testing.T) { - test := newE2ETest(t, initCfg()) + test := newE2ETest(t, initCfg(require)) defer test.teardown() oldOwnerID := 1 @@ -780,7 +798,7 @@ func TestCandidateTransferOwnership(t *testing.T) { }) }) t.Run("stake create after transfer candidate ownership", func(t *testing.T) { - test := newE2ETest(t, initCfg()) + test := newE2ETest(t, initCfg(require)) defer test.teardown() oldOwnerID := 1 @@ -806,7 +824,7 @@ func TestCandidateTransferOwnership(t *testing.T) { }) t.Run("migrate stake", func(t *testing.T) { contractAddress := "io1dkqh5mu9djfas3xyrmzdv9frsmmytel4mp7a64" - cfg := initCfg() + cfg := initCfg(require) cfg.Genesis.SystemStakingContractV2Address = contractAddress cfg.Genesis.SystemStakingContractV2Height = 1 cfg.DardanellesUpgrade.BlockInterval = time.Second * 8640 @@ -825,8 +843,10 @@ func TestCandidateTransferOwnership(t *testing.T) { h := identityset.Address(1).String() t.Logf("address 1: %v\n", h) minAmount, _ := big.NewInt(0).SetString("1000000000000000000000", 10) // 1000 IOTX + bytecode, err := hex.DecodeString(stakingContractV2Bytecode) + require.NoError(err) deployCode, err := staking.StakingContractABI.Constructor.Inputs.Pack(minAmount, common.BytesToAddress(identityset.Address(stakerID).Bytes())) - deployCode = append(stakingContractBytecode, deployCode...) + deployCode = append(bytecode, deployCode...) require.NoError(err) test.run([]*testcase{ { @@ -1006,7 +1026,7 @@ func TestCandidateTransferOwnership(t *testing.T) { }) }) t.Run("new endorsement", func(t *testing.T) { - cfg := initCfg() + cfg := initCfg(require) cfg.Genesis.UpernavikBlockHeight = 6 cfg.Genesis.EndorsementWithdrawWaitingBlocks = 5 test := newE2ETest(t, cfg) @@ -1143,6 +1163,121 @@ func TestCandidateTransferOwnership(t *testing.T) { }, }) }) + t.Run("revise_endorsement", func(t *testing.T) { + cfg := initCfg(require) + cfg.Genesis.UpernavikBlockHeight = 25 + cfg.Genesis.EndorsementWithdrawWaitingBlocks = 5 + test := newE2ETest(t, cfg) + defer test.teardown() + + var ( + candOwnerID = 1 + stakerID = 2 + candOwnerID2 = 3 + candOwnerID3 = 4 + chainID = test.cfg.Chain.ID + stakeTime = time.Now() + ) + test.run([]*testcase{ + { + name: "deposit after endorsement expired", + preActs: []*actionWithTime{ + {mustNoErr(action.SignedCandidateRegister(test.nonceMgr.pop(identityset.Address(candOwnerID).String()), "cand1", identityset.Address(1).String(), identityset.Address(1).String(), identityset.Address(candOwnerID).String(), registerAmount.String(), 1, true, nil, gasLimit, gasPrice, identityset.PrivateKey(candOwnerID), action.WithChainID(test.cfg.Chain.ID))), time.Now()}, + {mustNoErr(action.SignedCreateStake(test.nonceMgr.pop(identityset.Address(stakerID).String()), "cand1", registerAmount.String(), 91, true, nil, gasLimit, gasPrice, identityset.PrivateKey(stakerID), action.WithChainID(test.cfg.Chain.ID))), stakeTime}, + {mustNoErr(action.SignedCandidateEndorsementLegacy(test.nonceMgr.pop(identityset.Address(stakerID).String()), 1, true, gasLimit, gasPrice, identityset.PrivateKey(stakerID), action.WithChainID(test.cfg.Chain.ID))), time.Now()}, + {mustNoErr(action.SignedCandidateActivate(test.nonceMgr.pop(identityset.Address(candOwnerID).String()), 1, gasLimit, gasPrice, identityset.PrivateKey(candOwnerID), action.WithChainID(test.cfg.Chain.ID))), time.Now()}, + {mustNoErr(action.SignedCandidateEndorsementLegacy(test.nonceMgr.pop(identityset.Address(stakerID).String()), 1, false, gasLimit, gasPrice, identityset.PrivateKey(stakerID), action.WithChainID(test.cfg.Chain.ID))), time.Now()}, + {mustNoErr(action.SignedTransfer(identityset.Address(1).String(), identityset.PrivateKey(2), test.nonceMgr.pop(identityset.Address(2).String()), unit.ConvertIotxToRau(1), nil, gasLimit, gasPrice, action.WithChainID(chainID))), time.Now()}, + {mustNoErr(action.SignedTransfer(identityset.Address(1).String(), identityset.PrivateKey(2), test.nonceMgr.pop(identityset.Address(2).String()), unit.ConvertIotxToRau(1), nil, gasLimit, gasPrice, action.WithChainID(chainID))), time.Now()}, + {mustNoErr(action.SignedTransfer(identityset.Address(1).String(), identityset.PrivateKey(2), test.nonceMgr.pop(identityset.Address(2).String()), unit.ConvertIotxToRau(1), nil, gasLimit, gasPrice, action.WithChainID(chainID))), time.Now()}, + {mustNoErr(action.SignedTransfer(identityset.Address(1).String(), identityset.PrivateKey(2), test.nonceMgr.pop(identityset.Address(2).String()), unit.ConvertIotxToRau(1), nil, gasLimit, gasPrice, action.WithChainID(chainID))), time.Now()}, + {mustNoErr(action.SignedTransfer(identityset.Address(1).String(), identityset.PrivateKey(2), test.nonceMgr.pop(identityset.Address(2).String()), unit.ConvertIotxToRau(1), nil, gasLimit, gasPrice, action.WithChainID(chainID))), time.Now()}, + }, + act: &actionWithTime{mustNoErr(action.SignedDepositToStake(test.nonceMgr.pop(identityset.Address(stakerID).String()), 1, unit.ConvertIotxToRau(10000000).String(), nil, gasLimit, gasPrice, identityset.PrivateKey(stakerID), action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, + &candidateExpect{"cand1", &iotextypes.CandidateV2{Name: "cand1", OperatorAddress: identityset.Address(1).String(), RewardAddress: identityset.Address(1).String(), TotalWeightedVotes: "15734989908573124317570090", SelfStakingTokens: "0", OwnerAddress: identityset.Address(candOwnerID).String(), SelfStakeBucketIdx: math.MaxUint64}}, + &bucketExpect{&iotextypes.VoteBucket{Index: 1, EndorsementExpireBlockHeight: 10, CandidateAddress: identityset.Address(candOwnerID).String(), StakedAmount: "11200000000000000000000000", AutoStake: true, StakedDuration: 91, Owner: identityset.Address(stakerID).String(), CreateTime: timestamppb.New(stakeTime), StakeStartTime: timestamppb.New(stakeTime), UnstakeStartTime: ×tamppb.Timestamp{}}}, + }, + }, + { + name: "wrong votes after change delegate", + preActs: []*actionWithTime{ + {mustNoErr(action.SignedCandidateRegister(test.nonceMgr.pop(identityset.Address(candOwnerID2).String()), "cand2", identityset.Address(3).String(), identityset.Address(3).String(), identityset.Address(candOwnerID2).String(), registerAmount.String(), 1, true, nil, gasLimit, gasPrice, identityset.PrivateKey(candOwnerID2), action.WithChainID(test.cfg.Chain.ID))), time.Now()}, + {mustNoErr(action.SignedChangeCandidate(test.nonceMgr.pop(identityset.Address(candOwnerID).String()), "cand2", 0, nil, gasLimit, gasPrice, identityset.PrivateKey(candOwnerID), action.WithChainID(chainID))), time.Now()}, + }, + act: &actionWithTime{mustNoErr(action.SignedChangeCandidate(test.nonceMgr.pop(identityset.Address(stakerID).String()), "cand2", 1, nil, gasLimit, gasPrice, identityset.PrivateKey(stakerID), action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, + &candidateExpect{"cand1", &iotextypes.CandidateV2{Name: "cand1", OperatorAddress: identityset.Address(1).String(), RewardAddress: identityset.Address(1).String(), TotalWeightedVotes: "92550969839127272820178", SelfStakingTokens: "0", OwnerAddress: identityset.Address(candOwnerID).String(), SelfStakeBucketIdx: math.MaxUint64}}, + }, + }, + { + name: "endorsement expired", + preActs: []*actionWithTime{ + {mustNoErr(action.SignedCandidateRegister(test.nonceMgr.pop(identityset.Address(candOwnerID3).String()), "cand3", identityset.Address(4).String(), identityset.Address(4).String(), identityset.Address(candOwnerID3).String(), registerAmount.String(), 1, true, nil, gasLimit, gasPrice, identityset.PrivateKey(candOwnerID3), action.WithChainID(test.cfg.Chain.ID))), time.Now()}, + {mustNoErr(action.SignedCreateStake(test.nonceMgr.pop(identityset.Address(stakerID).String()), "cand3", registerAmount.String(), 91, true, nil, gasLimit, gasPrice, identityset.PrivateKey(stakerID), action.WithChainID(test.cfg.Chain.ID))), stakeTime}, + {mustNoErr(action.SignedCandidateEndorsementLegacy(test.nonceMgr.pop(identityset.Address(stakerID).String()), 4, true, gasLimit, gasPrice, identityset.PrivateKey(stakerID), action.WithChainID(test.cfg.Chain.ID))), time.Now()}, + {mustNoErr(action.SignedCandidateActivate(test.nonceMgr.pop(identityset.Address(candOwnerID3).String()), 4, gasLimit, gasPrice, identityset.PrivateKey(candOwnerID3), action.WithChainID(test.cfg.Chain.ID))), time.Now()}, + {mustNoErr(action.SignedCandidateEndorsementLegacy(test.nonceMgr.pop(identityset.Address(stakerID).String()), 4, false, gasLimit, gasPrice, identityset.PrivateKey(stakerID), action.WithChainID(test.cfg.Chain.ID))), time.Now()}, + {mustNoErr(action.SignedTransfer(identityset.Address(1).String(), identityset.PrivateKey(2), test.nonceMgr.pop(identityset.Address(2).String()), unit.ConvertIotxToRau(1), nil, gasLimit, gasPrice, action.WithChainID(chainID))), time.Now()}, + {mustNoErr(action.SignedTransfer(identityset.Address(1).String(), identityset.PrivateKey(2), test.nonceMgr.pop(identityset.Address(2).String()), unit.ConvertIotxToRau(1), nil, gasLimit, gasPrice, action.WithChainID(chainID))), time.Now()}, + {mustNoErr(action.SignedTransfer(identityset.Address(1).String(), identityset.PrivateKey(2), test.nonceMgr.pop(identityset.Address(2).String()), unit.ConvertIotxToRau(1), nil, gasLimit, gasPrice, action.WithChainID(chainID))), time.Now()}, + {mustNoErr(action.SignedTransfer(identityset.Address(1).String(), identityset.PrivateKey(2), test.nonceMgr.pop(identityset.Address(2).String()), unit.ConvertIotxToRau(1), nil, gasLimit, gasPrice, action.WithChainID(chainID))), time.Now()}, + }, + act: &actionWithTime{mustNoErr(action.SignedTransfer(identityset.Address(1).String(), identityset.PrivateKey(2), test.nonceMgr.pop(identityset.Address(2).String()), unit.ConvertIotxToRau(1), nil, gasLimit, gasPrice, action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, + &candidateExpect{"cand3", &iotextypes.CandidateV2{Name: "cand3", OperatorAddress: identityset.Address(4).String(), RewardAddress: identityset.Address(4).String(), TotalWeightedVotes: "2880688542027669019063296", SelfStakingTokens: "0", OwnerAddress: identityset.Address(candOwnerID3).String(), SelfStakeBucketIdx: math.MaxUint64}}, + &bucketExpect{&iotextypes.VoteBucket{Index: 4, EndorsementExpireBlockHeight: 24, CandidateAddress: identityset.Address(candOwnerID3).String(), StakedAmount: "1200000000000000000000000", AutoStake: true, StakedDuration: 91, Owner: identityset.Address(stakerID).String(), CreateTime: timestamppb.New(stakeTime), StakeStartTime: timestamppb.New(stakeTime), UnstakeStartTime: ×tamppb.Timestamp{}}}, + }, + }, + { + name: "revise at upernavik block height", + act: &actionWithTime{mustNoErr(action.SignedTransfer(identityset.Address(1).String(), identityset.PrivateKey(2), test.nonceMgr.pop(identityset.Address(2).String()), unit.ConvertIotxToRau(1), nil, gasLimit, gasPrice, action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, + &bucketExpect{&iotextypes.VoteBucket{Index: 1, EndorsementExpireBlockHeight: 10, CandidateAddress: identityset.Address(candOwnerID2).String(), StakedAmount: "11200000000000000000000000", AutoStake: true, StakedDuration: 91, Owner: identityset.Address(stakerID).String(), CreateTime: timestamppb.New(stakeTime), StakeStartTime: timestamppb.New(stakeTime), UnstakeStartTime: ×tamppb.Timestamp{}}}, + &bucketExpect{&iotextypes.VoteBucket{Index: 4, EndorsementExpireBlockHeight: 0, CandidateAddress: identityset.Address(candOwnerID3).String(), StakedAmount: "1200000000000000000000000", AutoStake: true, StakedDuration: 91, Owner: identityset.Address(stakerID).String(), CreateTime: timestamppb.New(stakeTime), StakeStartTime: timestamppb.New(stakeTime), UnstakeStartTime: ×tamppb.Timestamp{}}}, + &candidateExpect{"cand1", &iotextypes.CandidateV2{Name: "cand1", OperatorAddress: identityset.Address(1).String(), RewardAddress: identityset.Address(1).String(), TotalWeightedVotes: "0", SelfStakingTokens: "0", OwnerAddress: identityset.Address(candOwnerID).String(), SelfStakeBucketIdx: math.MaxUint64}}, + }, + }, + }) + }) +} + +func initCfg(r *require.Assertions) config.Config { + cfg := deepcopy.Copy(config.Default).(config.Config) + initDBPaths(r, &cfg) + + cfg.ActPool.MinGasPriceStr = "0" + cfg.Chain.TrieDBPatchFile = "" + cfg.Consensus.Scheme = config.NOOPScheme + cfg.Chain.EnableAsyncIndexWrite = false + cfg.Genesis.InitBalanceMap[identityset.Address(1).String()] = "100000000000000000000000000" + cfg.Genesis.InitBalanceMap[identityset.Address(2).String()] = "100000000000000000000000000" + cfg.Genesis.EndorsementWithdrawWaitingBlocks = 10 + cfg.Genesis.PacificBlockHeight = 1 + cfg.Genesis.AleutianBlockHeight = 1 + cfg.Genesis.BeringBlockHeight = 1 + cfg.Genesis.CookBlockHeight = 1 + cfg.Genesis.DardanellesBlockHeight = 1 + cfg.Genesis.DaytonaBlockHeight = 1 + cfg.Genesis.EasterBlockHeight = 1 + cfg.Genesis.FbkMigrationBlockHeight = 1 + cfg.Genesis.FairbankBlockHeight = 1 + cfg.Genesis.GreenlandBlockHeight = 1 + cfg.Genesis.HawaiiBlockHeight = 1 + cfg.Genesis.IcelandBlockHeight = 1 + cfg.Genesis.JutlandBlockHeight = 1 + cfg.Genesis.KamchatkaBlockHeight = 1 + cfg.Genesis.LordHoweBlockHeight = 1 + cfg.Genesis.MidwayBlockHeight = 1 + cfg.Genesis.NewfoundlandBlockHeight = 1 + cfg.Genesis.OkhotskBlockHeight = 1 + cfg.Genesis.PalauBlockHeight = 1 + cfg.Genesis.QuebecBlockHeight = 1 + cfg.Genesis.RedseaBlockHeight = 1 + cfg.Genesis.SumatraBlockHeight = 1 + cfg.Genesis.TsunamiBlockHeight = 1 + cfg.Genesis.UpernavikBlockHeight = 2 // enable CandidateIdentifiedByOwner feature + return cfg } func TestCandidateOwnerCollision(t *testing.T) { diff --git a/e2etest/rewarding_test.go b/e2etest/rewarding_test.go index fe07f045d9..08df16ccac 100644 --- a/e2etest/rewarding_test.go +++ b/e2etest/rewarding_test.go @@ -32,6 +32,7 @@ import ( "github.com/iotexproject/iotex-core/config" "github.com/iotexproject/iotex-core/pkg/log" "github.com/iotexproject/iotex-core/pkg/probe" + "github.com/iotexproject/iotex-core/pkg/unit" "github.com/iotexproject/iotex-core/pkg/util/fileutil" "github.com/iotexproject/iotex-core/server/itx" "github.com/iotexproject/iotex-core/state/factory" @@ -471,6 +472,78 @@ func TestBlockEpochReward(t *testing.T) { } } +func TestClaimReward(t *testing.T) { + t.Skip("") + require := require.New(t) + // set config + cfg := initCfg(require) + producerSK, err := crypto.HexStringToPrivateKey(cfg.Chain.ProducerPrivKey) + cfg.Genesis.TsunamiBlockHeight = 1 + cfg.Genesis.UpernavikBlockHeight = 10 + cfg.Genesis.InitBalanceMap[producerSK.PublicKey().Address().String()] = "100000000000000000000000000" + cfg.Plugins[config.GatewayPlugin] = struct{}{} + normalizeGenesisHeights(&cfg) + // new e2e test + test := newE2ETest(t, cfg) + defer test.teardown() + chainID := cfg.Chain.ID + gasPrice := big.NewInt(1) + require.NoError(err) + genTransferActions := func(n int) []*actionWithTime { + acts := make([]*actionWithTime, n) + for i := 0; i < n; i++ { + acts[i] = &actionWithTime{mustNoErr(action.SignedTransfer(identityset.Address(1).String(), identityset.PrivateKey(2), test.nonceMgr.pop(identityset.Address(2).String()), unit.ConvertIotxToRau(1), nil, gasLimit, gasPrice, action.WithChainID(chainID))), time.Now()} + } + return acts + } + callerBalance := big.NewInt(0) + producerBalance := big.NewInt(0) + // run test + test.run([]*testcase{ + { + name: "v1 claimreward before UpernavikBlockHeight", + preActs: genTransferActions(5), + act: &actionWithTime{mustNoErr(action.SignedClaimRewardLegacy(test.nonceMgr.pop(producerSK.PublicKey().Address().String()), gasLimit, gasPrice, producerSK, unit.ConvertIotxToRau(1), nil, action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect}, + }, + { + name: "v2 claimreward before UpernavikBlockHeight", + act: &actionWithTime{mustNoErr(action.SignedClaimReward(test.nonceMgr[(identityset.Address(1).String())], gasLimit, gasPrice, identityset.PrivateKey(1), unit.ConvertIotxToRau(1), nil, producerSK.PublicKey().Address(), action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{&basicActionExpect{err: errReceiptNotFound}}, + }, + { + name: "v1 claimreward after UpernavikBlockHeight", + preActs: genTransferActions(5), + act: &actionWithTime{mustNoErr(action.SignedClaimRewardLegacy(test.nonceMgr.pop(producerSK.PublicKey().Address().String()), gasLimit, gasPrice, producerSK, unit.ConvertIotxToRau(1), nil, action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect}, + }, + { + name: "v2 claimreward after UpernavikBlockHeight", + preFunc: func(e *e2etest) { + resp, err := e.api.GetAccount(context.Background(), &iotexapi.GetAccountRequest{Address: identityset.Address(1).String()}) + require.NoError(err) + callerBalance, _ = big.NewInt(0).SetString(resp.GetAccountMeta().Balance, 10) + resp, err = e.api.GetAccount(context.Background(), &iotexapi.GetAccountRequest{Address: producerSK.PublicKey().Address().String()}) + require.NoError(err) + producerBalance, _ = big.NewInt(0).SetString(resp.GetAccountMeta().Balance, 10) + }, + act: &actionWithTime{mustNoErr(action.SignedClaimReward(test.nonceMgr.pop(identityset.Address(1).String()), gasLimit, gasPrice, identityset.PrivateKey(1), unit.ConvertIotxToRau(1), nil, producerSK.PublicKey().Address(), action.WithChainID(chainID))), time.Now()}, + expect: []actionExpect{successExpect, &functionExpect{func(test *e2etest, act *action.SealedEnvelope, receipt *action.Receipt, err error) { + // caller balance sub action gas fee + resp, err := test.api.GetAccount(context.Background(), &iotexapi.GetAccountRequest{Address: identityset.Address(1).String()}) + require.NoError(err) + callerBalance.Sub(callerBalance, big.NewInt(0).Mul(big.NewInt(int64(receipt.GasConsumed)), gasPrice)) + require.Equal(callerBalance.String(), resp.GetAccountMeta().Balance) + // producer balance received 1 IOTX + resp, err = test.api.GetAccount(context.Background(), &iotexapi.GetAccountRequest{Address: producerSK.PublicKey().Address().String()}) + require.NoError(err) + producerBalance.Add(producerBalance, unit.ConvertIotxToRau(1)) + require.Equal(producerBalance.String(), resp.GetAccountMeta().Balance) + }}}, + }, + }) +} + func injectClaim( t *testing.T, wg *sync.WaitGroup, diff --git a/e2etest/staking_contract_v2_bytecode b/e2etest/staking_contract_v2_bytecode new file mode 100644 index 0000000000..6689c2992f --- /dev/null +++ b/e2etest/staking_contract_v2_bytecode @@ -0,0 +1 @@ +60a060405234801562000010575f80fd5b5060405162002f9a38038062002f9a83398101604081905262000033916200013a565b60405180604001604052806009815260200168109d58dad95d13919560ba1b815250604051806040016040528060038152602001621092d560ea1b815250815f908162000081919062000214565b50600162000090828262000214565b505050620000ad620000a7620000e560201b60201c565b620000e9565b6006805460ff60a01b19169055608091909152600980546001600160a01b0319166001600160a01b03909216919091179055620002e0565b3390565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f80604083850312156200014c575f80fd5b825160208401519092506001600160a01b03811681146200016b575f80fd5b809150509250929050565b634e487b7160e01b5f52604160045260245ffd5b600181811c908216806200019f57607f821691505b602082108103620001be57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f8211156200020f57805f5260205f20601f840160051c81016020851015620001eb5750805b601f840160051c820191505b818110156200020c575f8155600101620001f7565b50505b505050565b81516001600160401b0381111562000230576200023062000176565b62000248816200024184546200018a565b84620001c4565b602080601f8311600181146200027e575f8415620002665750858301515b5f19600386901b1c1916600185901b178555620002d8565b5f85815260208120601f198616915b82811015620002ae578886015182559484019460019091019084016200028d565b5085821015620002cc57878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b608051612c9a620003005f395f818161071f0152611bd80152612c9a5ff3fe60806040526004361061025e575f3560e01c8063715018a61161013f578063b88d4fde116100b3578063ddbcb5fa11610078578063ddbcb5fa1461070e578063e449f34114610741578063e985e9c514610760578063eec7ee73146107a7578063f0b56b5d146107ba578063f2fde38b146107ce575f80fd5b8063b88d4fde1461068a578063b8f4bd7b146106a9578063bbe33ea5146106c8578063c87b56dd146106db578063d0949f99146106fa575f80fd5b80638da5cb5b116101045780638da5cb5b146105e857806393b6ef591461060557806395d89b411461062457806398ca3b7614610638578063a22cb46514610657578063b1724b4614610676575f80fd5b8063715018a6146105865780637acb77571461059a5780638456cb59146105ad57806384ac79b0146105c1578063863e76db146105d4575f80fd5b806338af3eed116101d65780635ceb8b5b1161019b5780635ceb8b5b146104cb5780635d36598f146104ea5780636198e339146105095780636352211e1461052857806370a0823114610547578063711563d414610566575f80fd5b806338af3eed146103f65780633f4ba83a1461041557806342842e0e14610429578063431cd92a146104485780635c975abb146104ad575f80fd5b8063095ea7b311610227578063095ea7b31461033c5780630cdd53f61461035b5780630f5b2ca51461037a5780631338736f1461039957806323b872dd146103b85780632e17de78146103d7575f80fd5b8062f714ce1461026257806301ffc9a71461028357806303459b16146102b757806306fdde03146102e4578063081812fc14610305575b5f80fd5b34801561026d575f80fd5b5061028161027c3660046124c4565b6107ed565b005b34801561028e575f80fd5b506102a261029d366004612507565b61080e565b60405190151581526020015b60405180910390f35b3480156102c2575f80fd5b506102d66102d1366004612522565b61085f565b6040519081526020016102ae565b3480156102ef575f80fd5b506102f8610883565b6040516102ae9190612586565b348015610310575f80fd5b5061032461031f366004612522565b610912565b6040516001600160a01b0390911681526020016102ae565b348015610347575f80fd5b50610281610356366004612598565b610937565b348015610366575f80fd5b506102816103753660046125c2565b610a4b565b348015610385575f80fd5b506102816103943660046124c4565b610b12565b3480156103a4575f80fd5b506102816103b33660046125c2565b610b28565b3480156103c3575f80fd5b506102816103d23660046125e2565b610b43565b3480156103e2575f80fd5b506102816103f1366004612522565b610b74565b348015610401575f80fd5b50600954610324906001600160a01b031681565b348015610420575f80fd5b50610281610b88565b348015610434575f80fd5b506102816104433660046125e2565b610b9a565b348015610453575f80fd5b50610467610462366004612522565b610bb4565b6040516102ae9190815181526020808301519082015260408083015190820152606080830151908201526080918201516001600160a01b03169181019190915260a00190565b3480156104b8575f80fd5b50600654600160a01b900460ff166102a2565b3480156104d6575f80fd5b506102816104e5366004612668565b610c4a565b3480156104f5575f80fd5b506102816105043660046126b0565b610c95565b348015610514575f80fd5b50610281610523366004612522565b610cd0565b348015610533575f80fd5b50610324610542366004612522565b610ce1565b348015610552575f80fd5b506102d66105613660046126ef565b610d40565b61057961057436600461270a565b610dc4565b6040516102ae9190612746565b348015610591575f80fd5b50610281610e91565b6102d66105a83660046124c4565b610ea2565b3480156105b8575f80fd5b50610281610ed1565b6102816105cf3660046125c2565b610ee1565b3480156105df575f80fd5b506102d6600a81565b3480156105f3575f80fd5b506006546001600160a01b0316610324565b348015610610575f80fd5b506102d661061f366004612522565b610f9e565b34801561062f575f80fd5b506102f8610fdc565b348015610643575f80fd5b50610281610652366004612789565b610feb565b348015610662575f80fd5b506102816106713660046127dc565b611027565b348015610681575f80fd5b506102d6611032565b348015610695575f80fd5b506102816106a4366004612851565b61104d565b3480156106b4575f80fd5b506102816106c3366004612789565b61107f565b6102816106d6366004612668565b6110bb565b3480156106e6575f80fd5b506102f86106f5366004612522565b6111ee565b348015610705575f80fd5b506102d65f1981565b348015610719575f80fd5b506102d67f000000000000000000000000000000000000000000000000000000000000000081565b34801561074c575f80fd5b5061028161075b3660046126b0565b61125d565b34801561076b575f80fd5b506102a261077a36600461290e565b6001600160a01b039182165f90815260056020908152604080832093909416825291909152205460ff1690565b6105796107b536600461293a565b611298565b3480156107c5575f80fd5b506102d6611382565b3480156107d9575f80fd5b506102816107e83660046126ef565b61138e565b6107f5611404565b816107ff81611451565b610809838361148b565b505050565b5f6001600160e01b031982166380ac58cd60e01b148061083e57506001600160e01b03198216635b5e139f60e01b145b8061085957506301ffc9a760e01b6001600160e01b03198316145b92915050565b5f6108698261151f565b5f828152600860205260409020600301546108599061157d565b60605f8054610891906129fb565b80601f01602080910402602001604051908101604052809291908181526020018280546108bd906129fb565b80156109085780601f106108df57610100808354040283529160200191610908565b820191905f5260205f20905b8154815290600101906020018083116108eb57829003601f168201915b5050505050905090565b5f61091c8261151f565b505f908152600460205260409020546001600160a01b031690565b5f61094182610ce1565b9050806001600160a01b0316836001600160a01b0316036109b35760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b60648201526084015b60405180910390fd5b336001600160a01b03821614806109cf57506109cf813361077a565b610a415760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c00000060648201526084016109aa565b61080983836115d0565b81610a5581611451565b5f8381526008602052604090206003810154610a709061163d565b8054831180610a7d575082155b15610a9b576040516359b9300b60e11b815260040160405180910390fd5b82815f015f828254610aad9190612a47565b9091555050600954610ac8906001600160a01b03168461165e565b6009546040518481526001600160a01b039091169085907f1b606d34afacd55873aba0fd274841a10c63e18455f2dffebad2fc60a36b2c839060200160405180910390a350505050565b610b1a611404565b610b2482826116ce565b5050565b610b30611404565b610b3981611775565b610b2482826117c1565b610b4d3382611851565b610b695760405162461bcd60e51b81526004016109aa90612a5a565b6108098383836118cd565b610b7c611404565b610b8581611a3c565b50565b610b90611acd565b610b98611b27565b565b61080983838360405180602001604052805f81525061104d565b610bea6040518060a001604052805f81526020015f81526020015f81526020015f81526020015f6001600160a01b031681525090565b610bf38261151f565b505f90815260086020908152604091829020825160a08101845281548152600182015492810192909252600281015492820192909252600382015460608201526004909101546001600160a01b0316608082015290565b610c52611404565b610c5b81611775565b5f5b82811015610c8f57610c87848483818110610c7a57610c7a612aa7565b90506020020135836117c1565b600101610c5d565b50505050565b610c9d611404565b5f5b8181101561080957610cc8838383818110610cbc57610cbc612aa7565b90506020020135611b7c565b600101610c9f565b610cd8611404565b610b8581611b7c565b5f818152600260205260408120546001600160a01b0316806108595760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b60448201526064016109aa565b5f6001600160a01b038216610da95760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b60648201526084016109aa565b506001600160a01b03165f9081526003602052604090205490565b6060610dce611404565b610dd785611bd6565b610de084611775565b34610deb8387612abb565b14610e09576040516359b9300b60e11b815260040160405180910390fd5b8167ffffffffffffffff811115610e2257610e2261280c565b604051908082528060200260200182016040528015610e4b578160200160208202803683370190505b5090505f5b82811015610e8857610e63868686611c17565b828281518110610e7557610e75612aa7565b6020908102919091010152600101610e50565b50949350505050565b610e99611acd565b610b985f611cff565b5f610eab611404565b34610eb581611bd6565b610ebe84611775565b610ec9818585611c17565b949350505050565b610ed9611acd565b610b98611d50565b610ee9611404565b81610ef381611451565b610efc82611775565b5f8381526008602052604090206002810154610f1790611d93565b8060010154831015610f3c5760405163257a8d4b60e11b815260040160405180910390fd5b34815f015f828254610f4e9190612ad2565b9091555050600181018390558054604080519182526020820185905285917fd29e04160a74f0dbab5e7b82ef0392d86d11ac2939e5883eb3353be4cfedb83e91015b60405180910390a250505050565b5f610fa88261151f565b5f8281526008602052604090206003810154610fc39061163d565b610fd581600201548260010154611db4565b9392505050565b606060018054610891906129fb565b610ff3611404565b5f5b82811015610c8f5761101f84848381811061101257611012612aa7565b90506020020135836116ce565b600101610ff5565b610b24338383611deb565b61103f600a61016d612abb565b61104a906003612abb565b81565b6110573383611851565b6110735760405162461bcd60e51b81526004016109aa90612a5a565b610c8f84848484611eb8565b611087611404565b5f5b82811015610c8f576110b38484838181106110a6576110a6612aa7565b905060200201358361148b565b600101611089565b6110c3611404565b600182116110e457604051637e878a5f60e11b815260040160405180910390fd5b6110ed81611775565b345f80845b80156111e5575f190186868281811061110d5761110d612aa7565b90506020020135925061111f83611451565b5f838152600860205260409020600381015490925061113d9061163d565b61114f82600201548360010154611db4565b85101561116f5760405163257a8d4b60e11b815260040160405180910390fd5b815461117b9085612ad2565b935080156111915761118c83611eeb565b6110f2565b5f196002830155838255600182018590556040517fb3f4c8ca702dbbd32d9a25ce17b1942a5060284d9d69fc4fcac8fb0397891b12906111d8908990899088908a90612ae5565b60405180910390a16110f2565b50505050505050565b60606111f98261151f565b5f61120e60408051602081019091525f815290565b90505f81511161122c5760405180602001604052805f815250610fd5565b8061123684611f8a565b604051602001611247929190612b29565b6040516020818303038152906040529392505050565b611265611404565b5f5b818110156108095761129083838381811061128457611284612aa7565b90506020020135611a3c565b600101611267565b60606112a2611404565b6112ab84611bd6565b6112b483611775565b348251856112c29190612abb565b146112e0576040516359b9300b60e11b815260040160405180910390fd5b815167ffffffffffffffff8111156112fa576112fa61280c565b604051908082528060200260200182016040528015611323578160200160208202803683370190505b5090505f5b825181101561137a57611355858585848151811061134857611348612aa7565b6020026020010151611c17565b82828151811061136757611367612aa7565b6020908102919091010152600101611328565b509392505050565b61104a600a6003612abb565b611396611acd565b6001600160a01b0381166113fb5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016109aa565b610b8581611cff565b600654600160a01b900460ff1615610b985760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064016109aa565b61145a81610ce1565b6001600160a01b0316336001600160a01b031614610b8557604051630da4973960e31b815260040160405180910390fd5b8161149581611451565b5f83815260086020526040902060038101546114b09061157d565b156114ce57604051634e395b8560e01b815260040160405180910390fd5b6114d784611eeb565b6114e483825f015461165e565b6040516001600160a01b0384169085907fd964a27d45f595739c13d8b1160b57491050cacf3a2e5602207277d6228f64ee905f90a350505050565b5f818152600260205260409020546001600160a01b0316610b855760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b60448201526064016109aa565b5f5f19820361159f57604051636263fd5f60e11b815260040160405180910390fd5b5f6115ac600a6003612abb565b6115b69084612ad2565b90504381116115c757505f92915050565b43900392915050565b5f81815260046020526040902080546001600160a01b0319166001600160a01b038416908117909155819061160482610ce1565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b5f198114610b85576040516343febe2b60e01b815260040160405180910390fd5b5f826001600160a01b0316826040515f6040518083038185875af1925050503d805f81146116a7576040519150601f19603f3d011682016040523d82523d5f602084013e6116ac565b606091505b505090508061080957604051639a7058e160e01b815260040160405180910390fd5b816116d881611451565b5f83815260086020526040902060038101546116f39061163d565b60048101546001600160a01b0380851691160361172357604051637e878a5f60e11b815260040160405180910390fd5b6004810180546001600160a01b0319166001600160a01b03851690811790915560405190815284907f6f08c7e76d830d5f3d0a18fd27f4d8c0049b24a8689ddb39625e0864d894a9c190602001610f90565b611780600a82612b57565b1515806117a35750611795600a61016d612abb565b6117a0906003612abb565b81115b15610b855760405163257a8d4b60e11b815260040160405180910390fd5b816117cb81611451565b5f83815260086020526040902060038101546117e69061163d565b6117f881600201548260010154611db4565b8310156118185760405163257a8d4b60e11b815260040160405180910390fd5b5f19600282015560405183815284907f907fece23ce39fbcbceb71e515043fe29408353fbb393b25b35eb8a70a4bad0b90602001610f90565b5f8061185c83610ce1565b9050806001600160a01b0316846001600160a01b031614806118a257506001600160a01b038082165f9081526005602090815260408083209388168352929052205460ff165b80610ec95750836001600160a01b03166118bb84610912565b6001600160a01b031614949350505050565b826001600160a01b03166118e082610ce1565b6001600160a01b0316146119065760405162461bcd60e51b81526004016109aa90612b76565b6001600160a01b0382166119685760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b60648201526084016109aa565b611975838383600161201a565b826001600160a01b031661198882610ce1565b6001600160a01b0316146119ae5760405162461bcd60e51b81526004016109aa90612b76565b5f81815260046020908152604080832080546001600160a01b03199081169091556001600160a01b038781168086526003855283862080545f1901905590871680865283862080546001019055868652600290945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b80611a4681611451565b5f8281526008602052604090206003810154611a619061163d565b611a7381600201548260010154611db4565b15611a9157604051634e395b8560e01b815260040160405180910390fd5b4360028201819055600382015560405183907f11725367022c3ff288940f4b5473aa61c2da6a24af7363a1128ee2401e8983b2905f90a2505050565b6006546001600160a01b03163314610b985760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016109aa565b611b2f612026565b6006805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b80611b8681611451565b5f8281526008602052604090206002810154611ba190611d93565b43600282015560405183907ff27b6ce5b2f5e68ddb2fd95a8a909d4ecf1daaac270935fff052feacb24f1842905f90a2505050565b7f0000000000000000000000000000000000000000000000000000000000000000811015610b85576040516359b9300b60e11b815260040160405180910390fd5b5f80611c2560075460010190565b60078190556040805160a08101825287815260208082018881525f19838501818152606085019182526001600160a01b038a8116608087019081525f8981526008909652969094209451855591516001850155905160028401555160038301559151600490910180546001600160a01b031916919092161790559050611cab3382612076565b604080516001600160a01b03851681526020810187905290810185905281907f17700ceb1658b18206f427c1578048e87504106b14ec69e9b4586d9a95174a329060600160405180910390a2949350505050565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b611d58611404565b6006805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258611b5f3390565b5f198114610b855760405163da808c7560e01b815260040160405180910390fd5b5f5f198303611dc4575080610859565b5f611dcf8385612ad2565b9050438111611de1575f915050610859565b4390039392505050565b816001600160a01b0316836001600160a01b031603611e4c5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c65720000000000000060448201526064016109aa565b6001600160a01b038381165f81815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b611ec38484846118cd565b611ecf8484848461208f565b610c8f5760405162461bcd60e51b81526004016109aa90612bbb565b5f611ef582610ce1565b9050611f04815f84600161201a565b611f0d82610ce1565b5f83815260046020908152604080832080546001600160a01b03199081169091556001600160a01b0385168085526003845282852080545f190190558785526002909352818420805490911690555192935084927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b60605f611f968361218c565b60010190505f8167ffffffffffffffff811115611fb557611fb561280c565b6040519080825280601f01601f191660200182016040528015611fdf576020820181803683370190505b5090508181016020015b5f19016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a8504945084611fe957509392505050565b610c8f84848484612263565b600654600160a01b900460ff16610b985760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b60448201526064016109aa565b610b24828260405180602001604052805f8152506122e9565b5f6001600160a01b0384163b1561218157604051630a85bd0160e11b81526001600160a01b0385169063150b7a02906120d2903390899088908890600401612c0d565b6020604051808303815f875af192505050801561210c575060408051601f3d908101601f1916820190925261210991810190612c49565b60015b612167573d808015612139576040519150601f19603f3d011682016040523d82523d5f602084013e61213e565b606091505b5080515f0361215f5760405162461bcd60e51b81526004016109aa90612bbb565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610ec9565b506001949350505050565b5f8072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106121ca5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef810000000083106121f6576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061221457662386f26fc10000830492506010015b6305f5e100831061222c576305f5e100830492506008015b612710831061224057612710830492506004015b60648310612252576064830492506002015b600a83106108595760010192915050565b6001811115610c8f576001600160a01b038416156122a8576001600160a01b0384165f90815260036020526040812080548392906122a2908490612a47565b90915550505b6001600160a01b03831615610c8f576001600160a01b0383165f90815260036020526040812080548392906122de908490612ad2565b909155505050505050565b6122f3838361231b565b6122ff5f84848461208f565b6108095760405162461bcd60e51b81526004016109aa90612bbb565b6001600160a01b0382166123715760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f206164647265737360448201526064016109aa565b5f818152600260205260409020546001600160a01b0316156123d55760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016109aa565b6123e25f8383600161201a565b5f818152600260205260409020546001600160a01b0316156124465760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016109aa565b6001600160a01b0382165f81815260036020908152604080832080546001019055848352600290915280822080546001600160a01b0319168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6001600160a01b0381168114610b85575f80fd5b5f80604083850312156124d5575f80fd5b8235915060208301356124e7816124b0565b809150509250929050565b6001600160e01b031981168114610b85575f80fd5b5f60208284031215612517575f80fd5b8135610fd5816124f2565b5f60208284031215612532575f80fd5b5035919050565b5f5b8381101561255357818101518382015260200161253b565b50505f910152565b5f8151808452612572816020860160208601612539565b601f01601f19169290920160200192915050565b602081525f610fd5602083018461255b565b5f80604083850312156125a9575f80fd5b82356125b4816124b0565b946020939093013593505050565b5f80604083850312156125d3575f80fd5b50508035926020909101359150565b5f805f606084860312156125f4575f80fd5b83356125ff816124b0565b9250602084013561260f816124b0565b929592945050506040919091013590565b5f8083601f840112612630575f80fd5b50813567ffffffffffffffff811115612647575f80fd5b6020830191508360208260051b8501011115612661575f80fd5b9250929050565b5f805f6040848603121561267a575f80fd5b833567ffffffffffffffff811115612690575f80fd5b61269c86828701612620565b909790965060209590950135949350505050565b5f80602083850312156126c1575f80fd5b823567ffffffffffffffff8111156126d7575f80fd5b6126e385828601612620565b90969095509350505050565b5f602082840312156126ff575f80fd5b8135610fd5816124b0565b5f805f806080858703121561271d575f80fd5b84359350602085013592506040850135612736816124b0565b9396929550929360600135925050565b602080825282518282018190525f9190848201906040850190845b8181101561277d57835183529284019291840191600101612761565b50909695505050505050565b5f805f6040848603121561279b575f80fd5b833567ffffffffffffffff8111156127b1575f80fd5b6127bd86828701612620565b90945092505060208401356127d1816124b0565b809150509250925092565b5f80604083850312156127ed575f80fd5b82356127f8816124b0565b9150602083013580151581146124e7575f80fd5b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff811182821017156128495761284961280c565b604052919050565b5f805f8060808587031215612864575f80fd5b843561286f816124b0565b9350602085810135612880816124b0565b935060408601359250606086013567ffffffffffffffff808211156128a3575f80fd5b818801915088601f8301126128b6575f80fd5b8135818111156128c8576128c861280c565b6128da601f8201601f19168501612820565b915080825289848285010111156128ef575f80fd5b80848401858401375f8482840101525080935050505092959194509250565b5f806040838503121561291f575f80fd5b823561292a816124b0565b915060208301356124e7816124b0565b5f805f6060848603121561294c575f80fd5b833592506020808501359250604085013567ffffffffffffffff80821115612972575f80fd5b818701915087601f830112612985575f80fd5b8135818111156129975761299761280c565b8060051b91506129a8848301612820565b818152918301840191848101908a8411156129c1575f80fd5b938501935b838510156129eb57843592506129db836124b0565b82825293850193908501906129c6565b8096505050505050509250925092565b600181811c90821680612a0f57607f821691505b602082108103612a2d57634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561085957610859612a33565b6020808252602d908201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560408201526c1c881bdc88185c1c1c9bdd9959609a1b606082015260800190565b634e487b7160e01b5f52603260045260245ffd5b808202811582820484141761085957610859612a33565b8082018082111561085957610859612a33565b606080825281018490525f6001600160fb1b03851115612b03575f80fd5b8460051b8087608085013760208301949094525060408101919091520160800192915050565b5f8351612b3a818460208801612539565b835190830190612b4e818360208801612539565b01949350505050565b5f82612b7157634e487b7160e01b5f52601260045260245ffd5b500690565b60208082526025908201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060408201526437bbb732b960d91b606082015260800190565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b6001600160a01b03858116825284166020820152604081018390526080606082018190525f90612c3f9083018461255b565b9695505050505050565b5f60208284031215612c59575f80fd5b8151610fd5816124f256fea2646970667358221220205b2909c5a9d941c62e75ad22db4e210223e35d30466fe9ac16d3f20eeb406b64736f6c63430008160033 \ No newline at end of file