From 99f3ffb85cb95587ebd5d4f55318deebb3995ad8 Mon Sep 17 00:00:00 2001 From: zhi Date: Thu, 19 May 2022 18:49:08 -0700 Subject: [PATCH 01/11] set balance via SubBalance and AddBalance --- action/protocol/account/protocol.go | 4 ++- action/protocol/account/transfer.go | 6 ++-- action/protocol/account/transfer_test.go | 10 +++--- action/protocol/account/util/util.go | 4 ++- .../protocol/execution/evm/contract_test.go | 7 ++-- .../execution/evm/evmstatedbadapter.go | 12 +++++-- action/protocol/rewarding/fund.go | 25 ++------------ action/protocol/rewarding/reward.go | 4 ++- action/protocol/staking/handlers.go | 6 ++-- action/protocol/staking/handlers_test.go | 8 ++++- actpool/actpool_test.go | 34 +++++++++---------- e2etest/bigint_test.go | 14 ++++---- state/account.go | 25 ++++++++++++-- state/account_test.go | 4 +-- state/factory/factory_test.go | 12 +++---- 15 files changed, 100 insertions(+), 75 deletions(-) diff --git a/action/protocol/account/protocol.go b/action/protocol/account/protocol.go index 60e27a0ccc..470a399d48 100644 --- a/action/protocol/account/protocol.go +++ b/action/protocol/account/protocol.go @@ -119,7 +119,9 @@ func createAccount(sm protocol.StateManager, encodedAddr string, init *big.Int) case nil: return errors.Errorf("failed to create account %s", encodedAddr) case state.ErrStateNotExist: - account.Balance = init + if err := account.AddBalance(init); err != nil { + return errors.Wrapf(err, "failed to add balance %s", init) + } account.VotingWeight = big.NewInt(0) if _, err := sm.PutState(account, protocol.LegacyKeyOption(addrHash)); err != nil { return errors.Wrapf(err, "failed to put state for account %x", addrHash) diff --git a/action/protocol/account/transfer.go b/action/protocol/account/transfer.go index e78d855748..b8aa6aec12 100644 --- a/action/protocol/account/transfer.go +++ b/action/protocol/account/transfer.go @@ -38,7 +38,7 @@ func (p *Protocol) handleTransfer(ctx context.Context, act action.Action, sm pro } gasFee := big.NewInt(0).Mul(tsf.GasPrice(), big.NewInt(0).SetUint64(actionCtx.IntrinsicGas)) - if big.NewInt(0).Add(tsf.Amount(), gasFee).Cmp(sender.Balance) == 1 { + if !sender.HasSufficientBalance(big.NewInt(0).Add(tsf.Amount(), gasFee)) { return nil, errors.Wrapf( state.ErrNotEnoughBalance, "sender %s balance %s, required amount %s", @@ -119,7 +119,9 @@ func (p *Protocol) handleTransfer(ctx context.Context, act action.Action, sm pro if err != nil { return nil, errors.Wrapf(err, "failed to load or create the account of recipient %s", tsf.Recipient()) } - recipient.AddBalance(tsf.Amount()) + if err := recipient.AddBalance(tsf.Amount()); err != nil { + return nil, errors.Wrapf(err, "failed to add balance %s", tsf.Amount()) + } // put updated recipient's state to trie if err := accountutil.StoreAccount(sm, recipientAddr, recipient); err != nil { return nil, errors.Wrap(err, "failed to update pending account changes to trie") diff --git a/action/protocol/account/transfer_test.go b/action/protocol/account/transfer_test.go index fe93964b44..f20c90def7 100644 --- a/action/protocol/account/transfer_test.go +++ b/action/protocol/account/transfer_test.go @@ -78,9 +78,9 @@ func TestProtocol_HandleTransfer(t *testing.T) { alfa := identityset.Address(28) bravo := identityset.Address(29) charlie := identityset.Address(30) - require.NoError(accountutil.StoreAccount(sm, alfa, &state.Account{ - Balance: big.NewInt(50005), - })) + acct1 := &state.Account{} + require.NoError(acct1.AddBalance(big.NewInt(50005))) + require.NoError(accountutil.StoreAccount(sm, alfa, acct1)) require.NoError(accountutil.StoreAccount(sm, charlie, &state.Account{ CodeHash: []byte("codeHash"), })) @@ -156,13 +156,13 @@ func TestProtocol_HandleTransfer(t *testing.T) { require.NoError(err) newRecipient, err := accountutil.AccountState(sm, addr) require.NoError(err) - recipient.AddBalance(v.amount) + require.NoError(recipient.AddBalance(v.amount)) require.Equal(recipient.Balance, newRecipient.Balance) } // verify sender balance/nonce newSender, err := accountutil.AccountState(sm, v.caller) require.NoError(err) - sender.SubBalance(gasFee) + require.NoError(sender.SubBalance(gasFee)) require.Equal(sender.Balance, newSender.Balance) require.Equal(v.nonce, newSender.Nonce) diff --git a/action/protocol/account/util/util.go b/action/protocol/account/util/util.go index 437a6d190e..6be4c1a1f2 100644 --- a/action/protocol/account/util/util.go +++ b/action/protocol/account/util/util.go @@ -40,7 +40,9 @@ func LoadOrCreateAccount(sm protocol.StateManager, addr address.Address) (*state return &account, nil } if errors.Cause(err) == state.ErrStateNotExist { - account.Balance = big.NewInt(0) + if err := account.AddBalance(big.NewInt(0)); err != nil { + return nil, err + } account.VotingWeight = big.NewInt(0) if _, err := sm.PutState(account, protocol.LegacyKeyOption(addrHash)); err != nil { return nil, errors.Wrapf(err, "failed to put state for account %x", addrHash) diff --git a/action/protocol/execution/evm/contract_test.go b/action/protocol/execution/evm/contract_test.go index 78580bd8ef..07f06becbf 100644 --- a/action/protocol/execution/evm/contract_test.go +++ b/action/protocol/execution/evm/contract_test.go @@ -252,9 +252,8 @@ func TestSnapshot(t *testing.T) { testfunc := func(enableAsync bool) { sm, err := initMockStateManager(ctrl) require.NoError(err) - s := &state.Account{ - Balance: big.NewInt(5), - } + s := &state.Account{} + require.NoError(s.AddBalance(big.NewInt(5))) _c1, err := newContract( hash.BytesToHash160(identityset.Address(28).Bytes()), s, @@ -264,7 +263,7 @@ func TestSnapshot(t *testing.T) { require.NoError(err) require.NoError(_c1.SetState(_k2b, _v2[:])) _c2 := _c1.Snapshot() - _c1.SelfState().AddBalance(big.NewInt(7)) + require.NoError(_c1.SelfState().AddBalance(big.NewInt(7))) require.NoError(_c1.SetState(_k1b, _v1[:])) require.Equal(big.NewInt(12), _c1.SelfState().Balance) require.Equal(big.NewInt(5), _c2.SelfState().Balance) diff --git a/action/protocol/execution/evm/evmstatedbadapter.go b/action/protocol/execution/evm/evmstatedbadapter.go index 7fdfc5311b..dc4dc8691b 100644 --- a/action/protocol/execution/evm/evmstatedbadapter.go +++ b/action/protocol/execution/evm/evmstatedbadapter.go @@ -239,7 +239,11 @@ func (stateDB *StateDBAdapter) AddBalance(evmAddr common.Address, amount *big.In return } } - state.AddBalance(amount) + if err := state.AddBalance(amount); err != nil { + log.L().Error("failed to add balance", zap.Error(err), zap.String("amount", amount.String())) + stateDB.logError(err) + return + } if err := accountutil.StoreAccount(stateDB.sm, addr, state); err != nil { log.L().Error("Failed to update pending account changes to trie.", zap.Error(err)) stateDB.logError(err) @@ -365,8 +369,10 @@ func (stateDB *StateDBAdapter) Suicide(evmAddr common.Address) bool { return false } // clears the account balance - s.Balance = nil - s.Balance = big.NewInt(0) + if err := s.SubBalance(s.Balance); err != nil { + log.L().Debug("failed to clear balance", zap.Error(err), zap.String("address", addr.String())) + return false + } addrHash := hash.BytesToHash160(evmAddr.Bytes()) if _, err := stateDB.sm.PutState(s, protocol.LegacyKeyOption(addrHash)); err != nil { log.L().Error("Failed to kill contract.", zap.Error(err)) diff --git a/action/protocol/rewarding/fund.go b/action/protocol/rewarding/fund.go index 4b321a86cd..ff4e185062 100644 --- a/action/protocol/rewarding/fund.go +++ b/action/protocol/rewarding/fund.go @@ -65,18 +65,14 @@ func (p *Protocol) Deposit( transactionLogType iotextypes.TransactionLogType, ) (*action.TransactionLog, error) { actionCtx := protocol.MustGetActionCtx(ctx) - if err := p.assertAmount(amount); err != nil { - return nil, err - } - if err := p.assertEnoughBalance(actionCtx, sm, amount); err != nil { - return nil, err - } // Subtract balance from caller acc, err := accountutil.LoadAccount(sm, actionCtx.Caller) if err != nil { return nil, err } - acc.Balance = big.NewInt(0).Sub(acc.Balance, amount) + if err := acc.SubBalance(amount); err != nil { + return nil, err + } if err := accountutil.StoreAccount(sm, actionCtx.Caller, acc); err != nil { return nil, err } @@ -124,21 +120,6 @@ func (p *Protocol) AvailableBalance( return f.unclaimedBalance, height, nil } -func (p *Protocol) assertEnoughBalance( - actionCtx protocol.ActionCtx, - sm protocol.StateReader, - amount *big.Int, -) error { - acc, err := accountutil.LoadAccount(sm, actionCtx.Caller) - if err != nil { - return err - } - if acc.Balance.Cmp(amount) < 0 { - return errors.New("balance is not enough for donation") - } - return nil -} - // DepositGas deposits gas into the rewarding fund func DepositGas(ctx context.Context, sm protocol.StateManager, amount *big.Int) (*action.TransactionLog, error) { // If the gas fee is 0, return immediately diff --git a/action/protocol/rewarding/reward.go b/action/protocol/rewarding/reward.go index 5640c9f0a9..be8b6fb690 100644 --- a/action/protocol/rewarding/reward.go +++ b/action/protocol/rewarding/reward.go @@ -394,7 +394,9 @@ func (p *Protocol) claimFromAccount(ctx context.Context, sm protocol.StateManage if err != nil { return err } - primAcc.Balance = big.NewInt(0).Add(primAcc.Balance, amount) + if err := primAcc.AddBalance(amount); err != nil { + return err + } return accountutil.StoreAccount(sm, addr, primAcc) } diff --git a/action/protocol/staking/handlers.go b/action/protocol/staking/handlers.go index 2c15ac6bda..1f9f1e8045 100644 --- a/action/protocol/staking/handlers.go +++ b/action/protocol/staking/handlers.go @@ -254,7 +254,9 @@ func (p *Protocol) handleWithdrawStake(ctx context.Context, act *action.Withdraw } // update withdrawer balance - withdrawer.AddBalance(bucket.StakedAmount) + if err := withdrawer.AddBalance(bucket.StakedAmount); err != nil { + return log, nil, errors.Wrapf(err, "failed to add balance %s", bucket.StakedAmount) + } // put updated withdrawer's account state to trie if err := accountutil.StoreAccount(csm.SM(), actionCtx.Caller, withdrawer); err != nil { return log, nil, errors.Wrapf(err, "failed to store account %s", actionCtx.Caller.String()) @@ -815,7 +817,7 @@ func fetchCaller(ctx context.Context, csm CandidateStateManager, amount *big.Int } gasFee := big.NewInt(0).Mul(actionCtx.GasPrice, big.NewInt(0).SetUint64(actionCtx.IntrinsicGas)) // check caller's balance - if gasFee.Add(amount, gasFee).Cmp(caller.Balance) == 1 { + if !caller.HasSufficientBalance(new(big.Int).Add(amount, gasFee)) { return nil, &handleError{ err: errors.Wrapf(state.ErrNotEnoughBalance, "caller %s balance not enough", actionCtx.Caller.String()), failureStatus: iotextypes.ReceiptStatus_ErrNotEnoughBalance, diff --git a/action/protocol/staking/handlers_test.go b/action/protocol/staking/handlers_test.go index 5b29076dd7..2de4aea00e 100644 --- a/action/protocol/staking/handlers_test.go +++ b/action/protocol/staking/handlers_test.go @@ -2660,7 +2660,12 @@ func setupAccount(sm protocol.StateManager, addr address.Address, balance int64) if err != nil { return err } - account.Balance = unit.ConvertIotxToRau(balance) + if err := account.SubBalance(account.Balance); err != nil { + return err + } + if err := account.AddBalance(unit.ConvertIotxToRau(balance)); err != nil { + return err + } return accountutil.StoreAccount(sm, addr, account) } @@ -2671,6 +2676,7 @@ func depositGas(ctx context.Context, sm protocol.StateManager, gasFee *big.Int) if err != nil { return nil, err } + // TODO: replace with SubBalance, and then change `Balance` to a function acc.Balance = big.NewInt(0).Sub(acc.Balance, gasFee) return nil, accountutil.StoreAccount(sm, actionCtx.Caller, acc) } diff --git a/actpool/actpool_test.go b/actpool/actpool_test.go index 8a6ab1a765..af75c9c4a2 100644 --- a/actpool/actpool_test.go +++ b/actpool/actpool_test.go @@ -135,9 +135,9 @@ func TestActPool_AddActs(t *testing.T) { opt(cfg) } if bytes.Equal(cfg.Key, identityset.Address(28).Bytes()) { - acct.Balance = big.NewInt(100) + require.NoError(acct.AddBalance(big.NewInt(100))) } else { - acct.Balance = big.NewInt(10) + require.NoError(acct.AddBalance(big.NewInt(10))) } return 0, nil }).AnyTimes() @@ -332,9 +332,9 @@ func TestActPool_PickActs(t *testing.T) { opt(cfg) } if bytes.Equal(cfg.Key, identityset.Address(28).Bytes()) { - acct.Balance = big.NewInt(100) + require.NoError(acct.AddBalance(big.NewInt(100))) } else { - acct.Balance = big.NewInt(10) + require.NoError(acct.AddBalance(big.NewInt(10))) } return 0, nil }).AnyTimes() @@ -395,7 +395,7 @@ func TestActPool_removeConfirmedActs(t *testing.T) { acct, ok := account.(*state.Account) require.True(ok) acct.Nonce = 0 - acct.Balance = big.NewInt(100000000000000000) + require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil }).Times(8) @@ -410,7 +410,7 @@ func TestActPool_removeConfirmedActs(t *testing.T) { acct, ok := account.(*state.Account) require.True(ok) acct.Nonce = 4 - acct.Balance = big.NewInt(100000000000000000) + require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil }).Times(1) @@ -441,19 +441,19 @@ func TestActPool_Reset(t *testing.T) { } switch { case bytes.Equal(cfg.Key, identityset.Address(28).Bytes()): - acct.Balance = new(big.Int).Set(balances[0]) + require.NoError(acct.AddBalance(new(big.Int).Set(balances[0]))) acct.Nonce = nonces[0] case bytes.Equal(cfg.Key, identityset.Address(29).Bytes()): - acct.Balance = new(big.Int).Set(balances[1]) + require.NoError(acct.AddBalance(new(big.Int).Set(balances[1]))) acct.Nonce = nonces[1] case bytes.Equal(cfg.Key, identityset.Address(30).Bytes()): - acct.Balance = new(big.Int).Set(balances[2]) + require.NoError(acct.AddBalance(new(big.Int).Set(balances[2]))) acct.Nonce = nonces[2] case bytes.Equal(cfg.Key, identityset.Address(31).Bytes()): - acct.Balance = new(big.Int).Set(balances[3]) + require.NoError(acct.AddBalance(new(big.Int).Set(balances[3]))) acct.Nonce = nonces[3] case bytes.Equal(cfg.Key, identityset.Address(32).Bytes()): - acct.Balance = new(big.Int).Set(balances[4]) + require.NoError(acct.AddBalance(new(big.Int).Set(balances[4]))) acct.Nonce = nonces[4] } return 0, nil @@ -796,7 +796,7 @@ func TestActPool_removeInvalidActs(t *testing.T) { acct, ok := account.(*state.Account) require.True(ok) acct.Nonce = 0 - acct.Balance = big.NewInt(100000000000000000) + require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil }).Times(8) @@ -841,7 +841,7 @@ func TestActPool_GetPendingNonce(t *testing.T) { acct, ok := account.(*state.Account) require.True(ok) acct.Nonce = 0 - acct.Balance = big.NewInt(100000000000000000) + require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil }).Times(10) @@ -889,7 +889,7 @@ func TestActPool_GetUnconfirmedActs(t *testing.T) { acct, ok := account.(*state.Account) require.True(ok) acct.Nonce = 0 - acct.Balance = big.NewInt(100000000000000000) + require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil }).Times(10) @@ -989,7 +989,7 @@ func TestActPool_GetSize(t *testing.T) { acct, ok := account.(*state.Account) require.True(ok) acct.Nonce = 0 - acct.Balance = big.NewInt(100000000000000000) + require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil }).Times(8) @@ -1003,7 +1003,7 @@ func TestActPool_GetSize(t *testing.T) { acct, ok := account.(*state.Account) require.True(ok) acct.Nonce = 4 - acct.Balance = big.NewInt(100000000000000000) + require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil }).Times(1) @@ -1046,7 +1046,7 @@ func TestActPool_SpeedUpAction(t *testing.T) { for _, opt := range opts { opt(cfg) } - acct.Balance = big.NewInt(10000000) + require.NoError(acct.AddBalance(big.NewInt(10000000))) return 0, nil }).AnyTimes() diff --git a/e2etest/bigint_test.go b/e2etest/bigint_test.go index 978eff251e..fde6834996 100644 --- a/e2etest/bigint_test.go +++ b/e2etest/bigint_test.go @@ -50,8 +50,8 @@ func TestTransfer_Negative(t *testing.T) { r.NoError(err) blk, err := prepareTransfer(bc, sf, ap, r) r.NoError(err) - r.Equal(2, len(blk.Actions)) - r.Error(bc.ValidateBlock(blk)) + r.Equal(1, len(blk.Actions)) + r.NoError(bc.ValidateBlock(blk)) state, err := accountutil.AccountState(sf, addr) r.NoError(err) r.Equal(0, state.Balance.Cmp(stateBeforeTransfer.Balance)) @@ -69,8 +69,8 @@ func TestAction_Negative(t *testing.T) { blk, err := prepareAction(bc, sf, ap, r) r.NoError(err) r.NotNil(blk) - r.Equal(2, len(blk.Actions)) - r.Error(bc.ValidateBlock(blk)) + r.Equal(1, len(blk.Actions)) + r.NoError(bc.ValidateBlock(blk)) state, err := accountutil.AccountState(sf, addr) r.NoError(err) r.Equal(0, state.Balance.Cmp(stateBeforeTransfer.Balance)) @@ -88,8 +88,10 @@ func prepareBlockchain(ctx context.Context, _executor string, r *require.Asserti r.NoError(rp.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) r.NoError(err) + genericValidator := protocol.NewGenericValidator(sf, accountutil.AccountState) ap, err := actpool.NewActPool(sf, cfg.ActPool) r.NoError(err) + ap.AddActionEnvelopeValidators(genericValidator) dao := blockdao.NewBlockDAOInMemForTest([]blockdao.BlockIndexer{sf}) bc := blockchain.NewBlockchain( cfg, @@ -97,7 +99,7 @@ func prepareBlockchain(ctx context.Context, _executor string, r *require.Asserti factory.NewMinter(sf, ap), blockchain.BlockValidatorOption(block.NewValidator( sf, - protocol.NewGenericValidator(sf, accountutil.AccountState), + genericValidator, )), ) r.NotNil(bc) @@ -141,7 +143,7 @@ func prepare(bc blockchain.Blockchain, sf factory.Factory, ap actpool.ActPool, e r.NoError(err) selp, err := action.Sign(elp, priKey) r.NoError(err) - r.NoError(ap.Add(context.Background(), selp)) + r.Error(ap.Add(context.Background(), selp)) blk, err := bc.MintNewBlock(testutil.TimestampNow()) r.NoError(err) // when validate/commit a blk, the workingset and receipts of blk should be nil diff --git a/state/account.go b/state/account.go index dfd519b61c..5e6c756e5d 100644 --- a/state/account.go +++ b/state/account.go @@ -19,6 +19,8 @@ import ( var ( // ErrNotEnoughBalance is the error that the balance is not enough ErrNotEnoughBalance = errors.New("not enough balance") + // ErrInvalidAmount is the error that the amount to add is negative + ErrInvalidAmount = errors.New("invalid amount") // ErrAccountCollision is the error that the account already exists ErrAccountCollision = errors.New("account already exists") ) @@ -93,13 +95,32 @@ func (st *Account) Deserialize(buf []byte) error { return nil } +// HasSufficientBalance returns true if balance is larger than amount +func (st *Account) HasSufficientBalance(amount *big.Int) bool { + if amount == nil { + return true + } + return amount.Cmp(st.Balance) <= 0 +} + // AddBalance adds balance for account state -func (st *Account) AddBalance(amount *big.Int) { - st.Balance.Add(st.Balance, amount) +func (st *Account) AddBalance(amount *big.Int) error { + if amount == nil || amount.Sign() < 0 { + return errors.Wrapf(ErrInvalidAmount, "amount %s shouldn't be negative", amount.String()) + } + if st.Balance != nil { + st.Balance = new(big.Int).Add(st.Balance, amount) + } else { + st.Balance = new(big.Int).Set(amount) + } + return nil } // SubBalance subtracts balance for account state func (st *Account) SubBalance(amount *big.Int) error { + if amount == nil || amount.Cmp(big.NewInt(0)) < 0 { + return errors.Wrapf(ErrInvalidAmount, "amount %s shouldn't be negative", amount.String()) + } // make sure there's enough fund to spend if amount.Cmp(st.Balance) == 1 { return ErrNotEnoughBalance diff --git a/state/account_test.go b/state/account_test.go index a9eecc794f..c53032e41e 100644 --- a/state/account_test.go +++ b/state/account_test.go @@ -55,7 +55,7 @@ func TestBalance(t *testing.T) { state := &Account{Balance: big.NewInt(20)} // Add 10 to the balance - state.AddBalance(big.NewInt(10)) + require.NoError(state.AddBalance(big.NewInt(10))) // Balance should == 30 now require.Equal(0, state.Balance.Cmp(big.NewInt(30))) // Sub 40 to the balance @@ -71,7 +71,7 @@ func TestClone(t *testing.T) { account := ss.Clone() require.Equal(big.NewInt(200), account.Balance) - account.AddBalance(big.NewInt(100)) + require.NoError(account.AddBalance(big.NewInt(100))) require.Equal(big.NewInt(200), ss.Balance) require.Equal(big.NewInt(200+100), account.Balance) } diff --git a/state/factory/factory_test.go b/state/factory/factory_test.go index df5046569f..88416a481c 100644 --- a/state/factory/factory_test.go +++ b/state/factory/factory_test.go @@ -130,7 +130,7 @@ func testRevert(ws *workingSet, t *testing.T) { s0 := ws.Snapshot() require.Equal(1, s0) - s.Balance.Add(s.Balance, big.NewInt(5)) + require.NoError(s.AddBalance(big.NewInt(5))) require.Equal(big.NewInt(10), s.Balance) _, err = ws.PutState(s, protocol.LegacyKeyOption(sHash)) require.NoError(err) @@ -151,7 +151,7 @@ func testSDBRevert(ws *workingSet, t *testing.T) { s0 := ws.Snapshot() require.Equal(1, s0) - s.Balance.Add(s.Balance, big.NewInt(5)) + require.NoError(s.AddBalance(big.NewInt(5))) require.Equal(big.NewInt(10), s.Balance) _, err = ws.PutState(s, protocol.LegacyKeyOption(sHash)) require.NoError(err) @@ -177,13 +177,13 @@ func testSnapshot(ws *workingSet, t *testing.T) { require.Equal(big.NewInt(5), s.Balance) s0 := ws.Snapshot() require.Zero(s0) - s.Balance.Add(s.Balance, big.NewInt(5)) + require.NoError(s.AddBalance(big.NewInt(5))) require.Equal(big.NewInt(10), s.Balance) _, err = ws.PutState(s, protocol.LegacyKeyOption(sHash)) require.NoError(err) s1 := ws.Snapshot() require.Equal(1, s1) - s.Balance.Add(s.Balance, big.NewInt(5)) + require.NoError(s.AddBalance(big.NewInt(5))) require.Equal(big.NewInt(15), s.Balance) _, err = ws.PutState(s, protocol.LegacyKeyOption(sHash)) require.NoError(err) @@ -193,7 +193,7 @@ func testSnapshot(ws *workingSet, t *testing.T) { require.Equal(big.NewInt(7), s.Balance) s2 := ws.Snapshot() require.Equal(2, s2) - s.AddBalance(big.NewInt(6)) + require.NoError(s.AddBalance(big.NewInt(6))) require.Equal(big.NewInt(13), s.Balance) _, err = ws.PutState(s, protocol.LegacyKeyOption(tHash)) require.NoError(err) @@ -1207,7 +1207,7 @@ func testCachedBatch(ws *workingSet, t *testing.T) { // test PutState() hashA := hash.BytesToHash160(identityset.Address(28).Bytes()) accountA := state.EmptyAccount() - accountA.Balance = big.NewInt(70) + require.NoError(accountA.AddBalance(big.NewInt(70))) _, err := ws.PutState(accountA, protocol.LegacyKeyOption(hashA)) require.NoError(err) From 8598056df90dab9ad629f84a01ba3e3fbe7d486c Mon Sep 17 00:00:00 2001 From: zhi Date: Fri, 20 May 2022 14:39:01 -0700 Subject: [PATCH 02/11] create account with NewAccount and NewEmptyAccount --- action/protocol/account/protocol.go | 5 +- action/protocol/account/protocol_test.go | 2 +- action/protocol/account/transfer_test.go | 8 +- action/protocol/account/util/util.go | 73 +++++++++---------- .../protocol/execution/evm/contract_test.go | 4 +- action/protocol/generic_validator_test.go | 5 +- action/protocol/poll/util.go | 3 +- blockchain/block/validator_test.go | 4 +- blockchain/integrity/integrity_test.go | 8 +- e2etest/staking_test.go | 6 +- state/account.go | 73 +++++++++++++------ state/factory/factory_test.go | 27 ++++--- .../internal/client/client_test.go | 2 +- 13 files changed, 124 insertions(+), 96 deletions(-) diff --git a/action/protocol/account/protocol.go b/action/protocol/account/protocol.go index 470a399d48..30b535b323 100644 --- a/action/protocol/account/protocol.go +++ b/action/protocol/account/protocol.go @@ -108,13 +108,13 @@ func (p *Protocol) Name() string { } func createAccount(sm protocol.StateManager, encodedAddr string, init *big.Int) error { - var account state.Account + account := state.NewEmptyAccount() addr, err := address.FromString(encodedAddr) if err != nil { return errors.Wrap(err, "failed to get address public key hash from encoded address") } addrHash := hash.BytesToHash160(addr.Bytes()) - _, err = sm.State(&account, protocol.LegacyKeyOption(addrHash)) + _, err = sm.State(account, protocol.LegacyKeyOption(addrHash)) switch errors.Cause(err) { case nil: return errors.Errorf("failed to create account %s", encodedAddr) @@ -122,7 +122,6 @@ func createAccount(sm protocol.StateManager, encodedAddr string, init *big.Int) if err := account.AddBalance(init); err != nil { return errors.Wrapf(err, "failed to add balance %s", init) } - account.VotingWeight = big.NewInt(0) if _, err := sm.PutState(account, protocol.LegacyKeyOption(addrHash)); err != nil { return errors.Wrapf(err, "failed to put state for account %x", addrHash) } diff --git a/action/protocol/account/protocol_test.go b/action/protocol/account/protocol_test.go index 1e245c9de0..b199065b0c 100644 --- a/action/protocol/account/protocol_test.go +++ b/action/protocol/account/protocol_test.go @@ -61,7 +61,7 @@ func TestLoadOrCreateAccountState(t *testing.T) { addrv1 := identityset.Address(27) s, err := accountutil.LoadAccount(sm, addrv1) require.NoError(err) - require.Equal(s.Balance, state.EmptyAccount().Balance) + require.Equal(s.Balance, big.NewInt(0)) // create account require.NoError(createAccount(sm, addrv1.String(), big.NewInt(5))) diff --git a/action/protocol/account/transfer_test.go b/action/protocol/account/transfer_test.go index f20c90def7..7ceeeb5811 100644 --- a/action/protocol/account/transfer_test.go +++ b/action/protocol/account/transfer_test.go @@ -78,12 +78,12 @@ func TestProtocol_HandleTransfer(t *testing.T) { alfa := identityset.Address(28) bravo := identityset.Address(29) charlie := identityset.Address(30) - acct1 := &state.Account{} + acct1 := state.NewEmptyAccount() require.NoError(acct1.AddBalance(big.NewInt(50005))) require.NoError(accountutil.StoreAccount(sm, alfa, acct1)) - require.NoError(accountutil.StoreAccount(sm, charlie, &state.Account{ - CodeHash: []byte("codeHash"), - })) + acct2 := state.NewEmptyAccount() + acct2.CodeHash = []byte("codeHash") + require.NoError(accountutil.StoreAccount(sm, charlie, acct2)) tests := []struct { caller address.Address diff --git a/action/protocol/account/util/util.go b/action/protocol/account/util/util.go index 6be4c1a1f2..8cdfa0d078 100644 --- a/action/protocol/account/util/util.go +++ b/action/protocol/account/util/util.go @@ -7,8 +7,6 @@ package accountutil import ( - "math/big" - "github.com/pkg/errors" "github.com/iotexproject/go-pkgs/hash" @@ -30,44 +28,45 @@ func SetNonce(i noncer, state *state.Account) { } // LoadOrCreateAccount either loads an account state or creates an account state -func LoadOrCreateAccount(sm protocol.StateManager, addr address.Address) (*state.Account, error) { +func LoadOrCreateAccount(sm protocol.StateManager, addr address.Address, opts ...state.AccountCreationOption) (*state.Account, error) { var ( - account state.Account + account = state.NewEmptyAccount() addrHash = hash.BytesToHash160(addr.Bytes()) ) - _, err := sm.State(&account, protocol.LegacyKeyOption(addrHash)) - if err == nil { - return &account, nil - } - if errors.Cause(err) == state.ErrStateNotExist { - if err := account.AddBalance(big.NewInt(0)); err != nil { - return nil, err + _, err := sm.State(account, protocol.LegacyKeyOption(addrHash)) + switch errors.Cause(err) { + case nil: + return account, nil + case state.ErrStateNotExist: + account, err := state.NewAccount(opts...) + if err != nil { + return nil, errors.Wrapf(err, "failed to create state account for %x", addrHash) } - account.VotingWeight = big.NewInt(0) if _, err := sm.PutState(account, protocol.LegacyKeyOption(addrHash)); err != nil { return nil, errors.Wrapf(err, "failed to put state for account %x", addrHash) } - return &account, nil + return account, nil + default: + return nil, err } - return nil, err } // LoadAccount loads an account state by address.Address -func LoadAccount(sr protocol.StateReader, addr address.Address) (*state.Account, error) { - return LoadAccountByHash160(sr, hash.BytesToHash160(addr.Bytes())) +func LoadAccount(sr protocol.StateReader, addr address.Address, opts ...state.AccountCreationOption) (*state.Account, error) { + return LoadAccountByHash160(sr, hash.BytesToHash160(addr.Bytes()), opts...) } // LoadAccountByHash160 loads an account state by 20-byte address -func LoadAccountByHash160(sr protocol.StateReader, addrHash hash.Hash160) (*state.Account, error) { - var account state.Account - if _, err := sr.State(&account, protocol.LegacyKeyOption(addrHash)); err != nil { - if errors.Cause(err) == state.ErrStateNotExist { - account = state.EmptyAccount() - return &account, nil - } +func LoadAccountByHash160(sr protocol.StateReader, addrHash hash.Hash160, opts ...state.AccountCreationOption) (*state.Account, error) { + account := state.NewEmptyAccount() + switch _, err := sr.State(account, protocol.LegacyKeyOption(addrHash)); errors.Cause(err) { + case state.ErrStateNotExist: + return state.NewAccount(opts...) + case nil: + return account, nil + default: return nil, err } - return &account, nil } // StoreAccount puts updated account state to trie @@ -79,12 +78,12 @@ func StoreAccount(sm protocol.StateManager, addr address.Address, account *state // Recorded tests if an account has been actually stored func Recorded(sr protocol.StateReader, addr address.Address) (bool, error) { - var account state.Account - _, err := sr.State(&account, protocol.LegacyKeyOption(hash.BytesToHash160(addr.Bytes()))) - if err == nil { + account := state.NewEmptyAccount() + _, err := sr.State(account, protocol.LegacyKeyOption(hash.BytesToHash160(addr.Bytes()))) + switch errors.Cause(err) { + case nil: return true, nil - } - if errors.Cause(err) == state.ErrStateNotExist { + case state.ErrStateNotExist: return false, nil } return false, err @@ -99,14 +98,14 @@ func AccountState(sr protocol.StateReader, addr address.Address) (*state.Account // AccountStateWithHeight returns the confirmed account state on the chain with what height the state is read from. func AccountStateWithHeight(sr protocol.StateReader, addr address.Address) (*state.Account, uint64, error) { pkHash := hash.BytesToHash160(addr.Bytes()) - var account state.Account - h, err := sr.State(&account, protocol.LegacyKeyOption(pkHash)) - if err != nil { - if errors.Cause(err) == state.ErrStateNotExist { - account = state.EmptyAccount() - return &account, h, nil - } + account := state.NewEmptyAccount() + h, err := sr.State(account, protocol.LegacyKeyOption(pkHash)) + switch errors.Cause(err) { + case nil: + fallthrough + case state.ErrStateNotExist: + return account, h, nil + default: return nil, h, errors.Wrapf(err, "error when loading state of %x", pkHash) } - return &account, h, nil } diff --git a/action/protocol/execution/evm/contract_test.go b/action/protocol/execution/evm/contract_test.go index 07f06becbf..9046612f8d 100644 --- a/action/protocol/execution/evm/contract_test.go +++ b/action/protocol/execution/evm/contract_test.go @@ -101,7 +101,7 @@ func TestLoadStoreCommit(t *testing.T) { ctrl := gomock.NewController(t) sm, err := initMockStateManager(ctrl) require.NoError(err) - cntr1, err := newContract(hash.BytesToHash160(_c1[:]), &state.Account{}, sm, enableAsync) + cntr1, err := newContract(hash.BytesToHash160(_c1[:]), state.NewEmptyAccount(), sm, enableAsync) require.NoError(err) tests := []cntrTest{ @@ -252,7 +252,7 @@ func TestSnapshot(t *testing.T) { testfunc := func(enableAsync bool) { sm, err := initMockStateManager(ctrl) require.NoError(err) - s := &state.Account{} + s := state.NewEmptyAccount() require.NoError(s.AddBalance(big.NewInt(5))) _c1, err := newContract( hash.BytesToHash160(identityset.Address(28).Bytes()), diff --git a/action/protocol/generic_validator_test.go b/action/protocol/generic_validator_test.go index 9814754785..192993bc21 100644 --- a/action/protocol/generic_validator_test.go +++ b/action/protocol/generic_validator_test.go @@ -67,7 +67,10 @@ func TestActionProtoAndGenericValidator(t *testing.T) { if strings.EqualFold(eAddr.String(), addr.String()) { return nil, errors.New("MockChainManager nonce error") } - return &state.Account{Nonce: 2}, nil + acct := state.NewEmptyAccount() + acct.Nonce = 2 + + return acct, nil }) data, err := hex.DecodeString("") require.NoError(err) diff --git a/action/protocol/poll/util.go b/action/protocol/poll/util.go index 4096d38376..04f72ebcee 100644 --- a/action/protocol/poll/util.go +++ b/action/protocol/poll/util.go @@ -173,11 +173,10 @@ func setCandidates( if err != nil { return errors.Wrapf(err, "failed to decode delegate address %s", candidate.Address) } - delegate, err := accountutil.LoadOrCreateAccount(sm, addr) + delegate, err := accountutil.LoadOrCreateAccount(sm, addr, state.DelegateCandidateOption()) if err != nil { return errors.Wrapf(err, "failed to load or create the account for delegate %s", candidate.Address) } - delegate.IsCandidate = true if loadCandidatesLegacy { if err := candidatesutil.LoadAndAddCandidates(sm, height, candidate.Address); err != nil { return err diff --git a/blockchain/block/validator_test.go b/blockchain/block/validator_test.go index ed4920a75b..bf024ffd7e 100644 --- a/blockchain/block/validator_test.go +++ b/blockchain/block/validator_test.go @@ -39,7 +39,9 @@ func TestValidator(t *testing.T) { if strings.EqualFold(eAddr.String(), addr.String()) { return nil, errors.New("MockChainManager nonce error") } - return &state.Account{Nonce: 2}, nil + account := state.NewEmptyAccount() + account.Nonce = 2 + return account, nil }) tsf1, err := action.SignedTransfer(identityset.Address(28).String(), identityset.PrivateKey(27), 1, big.NewInt(20), []byte{}, 100000, big.NewInt(10)) diff --git a/blockchain/integrity/integrity_test.go b/blockchain/integrity/integrity_test.go index 07ee66cc1c..2be772e3e8 100644 --- a/blockchain/integrity/integrity_test.go +++ b/blockchain/integrity/integrity_test.go @@ -191,10 +191,10 @@ func addTestingConstantinopleBlocks(bc blockchain.Blockchain, dao blockdao.Block // Add block 8 // test store out of gas var ( - caller state.Account + caller = state.NewEmptyAccount() callerAddr = hash.BytesToHash160(identityset.Address(27).Bytes()) ) - _, err = sf.State(&caller, protocol.LegacyKeyOption(callerAddr)) + _, err = sf.State(caller, protocol.LegacyKeyOption(callerAddr)) if err != nil { return err } @@ -216,7 +216,7 @@ func addTestingConstantinopleBlocks(bc blockchain.Blockchain, dao blockdao.Block // Add block 9 // test store out of gas - _, err = sf.State(&caller, protocol.LegacyKeyOption(callerAddr)) + _, err = sf.State(caller, protocol.LegacyKeyOption(callerAddr)) if err != nil { return err } @@ -236,7 +236,7 @@ func addTestingConstantinopleBlocks(bc blockchain.Blockchain, dao blockdao.Block } } - _, err = sf.State(&caller, protocol.LegacyKeyOption(callerAddr)) + _, err = sf.State(caller, protocol.LegacyKeyOption(callerAddr)) if err != nil { return err } diff --git a/e2etest/staking_test.go b/e2etest/staking_test.go index afbd16c8fc..9e68bf641d 100644 --- a/e2etest/staking_test.go +++ b/e2etest/staking_test.go @@ -58,8 +58,8 @@ func TestStakingContract(t *testing.T) { require.NotNil(registry) admin := identityset.PrivateKey(26) state0 := hash.BytesToHash160(identityset.Address(26).Bytes()) - var s state.Account - _, err = sf.State(&s, protocol.LegacyKeyOption(state0)) + s := state.NewEmptyAccount() + _, err = sf.State(s, protocol.LegacyKeyOption(state0)) require.NoError(err) require.Equal(unit.ConvertIotxToRau(100000000), s.Balance) @@ -101,7 +101,7 @@ func TestStakingContract(t *testing.T) { require.NoError(bc.CommitBlock(blk)) state0 = hash.BytesToHash160(identityset.Address(i).Bytes()) - _, err = sf.State(&s, protocol.LegacyKeyOption(state0)) + _, err = sf.State(s, protocol.LegacyKeyOption(state0)) require.NoError(err) require.Equal(unit.ConvertIotxToRau(100000000-int64(numBucket)*200), s.Balance) } diff --git a/state/account.go b/state/account.go index 5e6c756e5d..033e3f2919 100644 --- a/state/account.go +++ b/state/account.go @@ -25,18 +25,31 @@ var ( ErrAccountCollision = errors.New("account already exists") ) -// Account is the canonical representation of an account. -type Account struct { - // 0 is reserved from actions in genesis block and coinbase transfers nonces - // other actions' nonces start from 1 - Nonce uint64 - Balance *big.Int - Root hash.Hash256 // storage trie root for contract account - CodeHash []byte // hash of the smart contract byte-code for contract account - IsCandidate bool - VotingWeight *big.Int +// DelegateCandidateOption is an option to create a delegate candidate account +func DelegateCandidateOption() AccountCreationOption { + return func(account *Account) error { + account.isCandidate = true + return nil + } } +type ( + // AccountCreationOption is to create new account with specific settings + AccountCreationOption func(*Account) error + + // Account is the canonical representation of an account. + Account struct { + // 0 is reserved from actions in genesis block and coinbase transfers nonces + // other actions' nonces start from 1 + Nonce uint64 + Balance *big.Int + Root hash.Hash256 // storage trie root for contract account + CodeHash []byte // hash of the smart contract byte-code for contract account + isCandidate bool + votingWeight *big.Int + } +) + // ToProto converts to protobuf's Account func (st *Account) ToProto() *accountpb.Account { acPb := &accountpb.Account{} @@ -48,9 +61,9 @@ func (st *Account) ToProto() *accountpb.Account { copy(acPb.Root, st.Root[:]) acPb.CodeHash = make([]byte, len(st.CodeHash)) copy(acPb.CodeHash, st.CodeHash) - acPb.IsCandidate = st.IsCandidate - if st.VotingWeight != nil { - acPb.VotingWeight = st.VotingWeight.Bytes() + acPb.IsCandidate = st.isCandidate + if st.votingWeight != nil { + acPb.VotingWeight = st.votingWeight.Bytes() } return acPb } @@ -78,10 +91,10 @@ func (st *Account) FromProto(acPb *accountpb.Account) { st.CodeHash = make([]byte, len(acPb.CodeHash)) copy(st.CodeHash, acPb.CodeHash) } - st.IsCandidate = acPb.IsCandidate - st.VotingWeight = big.NewInt(0) + st.isCandidate = acPb.IsCandidate + st.votingWeight = big.NewInt(0) if acPb.VotingWeight != nil { - st.VotingWeight.SetBytes(acPb.VotingWeight) + st.votingWeight.SetBytes(acPb.VotingWeight) } } @@ -139,9 +152,9 @@ func (st *Account) Clone() *Account { s := *st s.Balance = nil s.Balance = new(big.Int).Set(st.Balance) - s.VotingWeight = nil - if st.VotingWeight != nil { - s.VotingWeight = new(big.Int).Set(st.VotingWeight) + s.votingWeight = nil + if st.votingWeight != nil { + s.votingWeight = new(big.Int).Set(st.votingWeight) } if st.CodeHash != nil { s.CodeHash = nil @@ -151,10 +164,24 @@ func (st *Account) Clone() *Account { return &s } -// EmptyAccount returns an empty account -func EmptyAccount() Account { - return Account{ +// NewEmptyAccount returns an empty account +func NewEmptyAccount() *Account { + return &Account{ + Balance: big.NewInt(0), + votingWeight: big.NewInt(0), + } +} + +// NewAccount creates a new account with options +func NewAccount(opts ...AccountCreationOption) (*Account, error) { + account := &Account{ Balance: big.NewInt(0), - VotingWeight: big.NewInt(0), + votingWeight: big.NewInt(0), + } + for _, opt := range opts { + if err := opt(account); err != nil { + return nil, errors.Wrap(err, "failed to apply account creation option") + } } + return account, nil } diff --git a/state/factory/factory_test.go b/state/factory/factory_test.go index 88416a481c..5be54290a1 100644 --- a/state/factory/factory_test.go +++ b/state/factory/factory_test.go @@ -468,13 +468,13 @@ func testState(sf Factory, t *testing.T) { require.NoError(t, sf.PutBlock(ctx, &blk)) //test AccountState() & State() - var testAccount state.Account + testAccount := state.NewEmptyAccount() accountA, err := accountutil.AccountState(sf, a) require.NoError(t, err) sHash := hash.BytesToHash160(identityset.Address(28).Bytes()) - _, err = sf.State(&testAccount, protocol.LegacyKeyOption(sHash)) + _, err = sf.State(testAccount, protocol.LegacyKeyOption(sHash)) require.NoError(t, err) - require.Equal(t, accountA, &testAccount) + require.Equal(t, accountA, testAccount) require.Equal(t, big.NewInt(90), accountA.Balance) } @@ -631,7 +631,7 @@ func testFactoryStates(sf Factory, t *testing.T) { require.Equal(t, 3, iter.Size()) accounts := make([]*state.Account, 0) for i := 0; i < iter.Size(); i++ { - c := &state.Account{} + c := state.NewEmptyAccount() err = iter.Next(c) if err != nil { continue @@ -650,7 +650,7 @@ func testFactoryStates(sf Factory, t *testing.T) { require.Equal(t, 3, iter.Size()) accounts = make([]*state.Account, 0) for i := 0; i < iter.Size(); i++ { - c := &state.Account{} + c := state.NewEmptyAccount() err = iter.Next(c) if err != nil { continue @@ -669,7 +669,7 @@ func testFactoryStates(sf Factory, t *testing.T) { require.Equal(t, 3, iter.Size()) accounts = make([]*state.Account, 0) for i := 0; i < iter.Size(); i++ { - c := &state.Account{} + c := state.NewEmptyAccount() err = iter.Next(c) if err != nil { continue @@ -690,7 +690,7 @@ func testFactoryStates(sf Factory, t *testing.T) { require.Equal(t, 1, iter.Size()) accounts = make([]*state.Account, 0) for i := 0; i < iter.Size(); i++ { - c := &state.Account{} + c := state.NewEmptyAccount() require.NoError(t, iter.Next(c)) accounts = append(accounts, c) } @@ -1206,14 +1206,14 @@ func testCachedBatch(ws *workingSet, t *testing.T) { // test PutState() hashA := hash.BytesToHash160(identityset.Address(28).Bytes()) - accountA := state.EmptyAccount() + accountA := state.NewEmptyAccount() require.NoError(accountA.AddBalance(big.NewInt(70))) _, err := ws.PutState(accountA, protocol.LegacyKeyOption(hashA)) require.NoError(err) // test State() - testAccount := state.EmptyAccount() - _, err = ws.State(&testAccount, protocol.LegacyKeyOption(hashA)) + testAccount := state.NewEmptyAccount() + _, err = ws.State(testAccount, protocol.LegacyKeyOption(hashA)) require.NoError(err) require.Equal(accountA, testAccount) @@ -1222,7 +1222,7 @@ func testCachedBatch(ws *workingSet, t *testing.T) { require.NoError(err) // can't state account "alfa" anymore - _, err = ws.State(&testAccount, protocol.LegacyKeyOption(hashA)) + _, err = ws.State(testAccount, protocol.LegacyKeyOption(hashA)) require.Error(err) } @@ -1355,9 +1355,8 @@ func TestStateDBPatch(t *testing.T) { func TestDeleteAndPutSameKey(t *testing.T) { testDeleteAndPutSameKey := func(t *testing.T, ws *workingSet) { key := hash.Hash160b([]byte("test")) - acc := state.Account{ - Nonce: 1, - } + acc := state.NewEmptyAccount() + acc.Nonce = 1 _, err := ws.PutState(acc, protocol.LegacyKeyOption(key)) require.NoError(t, err) _, err = ws.DelState(protocol.LegacyKeyOption(key)) diff --git a/tools/actioninjector.v2/internal/client/client_test.go b/tools/actioninjector.v2/internal/client/client_test.go index 4705a93516..4513003862 100644 --- a/tools/actioninjector.v2/internal/client/client_test.go +++ b/tools/actioninjector.v2/internal/client/client_test.go @@ -56,7 +56,7 @@ func TestClient(t *testing.T) { ap := mock_actpool.NewMockActPool(mockCtrl) sf.EXPECT().State(gomock.Any(), gomock.Any()).Do(func(accountState *state.Account, _ protocol.StateOption) { - *accountState = state.EmptyAccount() + accountState = state.NewEmptyAccount() }) sf.EXPECT().Height().Return(uint64(10), nil).AnyTimes() bc.EXPECT().Genesis().Return(cfg.Genesis).AnyTimes() From 478d264b5eb59ccec73beb27dd9075e61891a740 Mon Sep 17 00:00:00 2001 From: zhi Date: Sat, 21 May 2022 22:34:01 -0700 Subject: [PATCH 03/11] refactor state.nonce --- action/protocol/account/protocol_test.go | 2 +- action/protocol/account/transfer_test.go | 4 +-- action/protocol/account/util/util.go | 6 +++-- .../execution/evm/evmstatedbadapter.go | 21 +++++++++------ action/protocol/execution/protocol_test.go | 7 +++-- action/protocol/generic_validator.go | 2 +- action/protocol/generic_validator_test.go | 4 ++- action/protocol/rewarding/protocol.go | 6 +++-- action/protocol/staking/handlers_test.go | 20 +++++++------- action/protocol/staking/protocol.go | 6 +++-- actpool/actpool.go | 18 ++++++------- actpool/actpool_test.go | 27 ++++++++----------- actpool/actqueue.go | 2 +- actpool/actqueue_test.go | 2 +- api/coreservice.go | 8 +++--- api/grpcserver_integrity_test.go | 4 +-- blockchain/block/validator_test.go | 2 +- blockchain/integrity/integrity_test.go | 4 +-- e2etest/local_test.go | 8 +++--- state/account.go | 17 +++++++++--- state/account_test.go | 6 ++--- state/factory/factory_test.go | 6 ++--- state/factory/workingset.go | 5 ++-- 23 files changed, 102 insertions(+), 85 deletions(-) diff --git a/action/protocol/account/protocol_test.go b/action/protocol/account/protocol_test.go index b199065b0c..81c1b4907f 100644 --- a/action/protocol/account/protocol_test.go +++ b/action/protocol/account/protocol_test.go @@ -68,7 +68,7 @@ func TestLoadOrCreateAccountState(t *testing.T) { s, err = accountutil.LoadAccount(sm, addrv1) require.NoError(err) require.Equal("5", s.Balance.String()) - require.Equal(uint64(0x0), s.Nonce) + require.Equal(uint64(0x1), s.PendingNonce()) } func TestProtocol_Initialize(t *testing.T) { diff --git a/action/protocol/account/transfer_test.go b/action/protocol/account/transfer_test.go index 7ceeeb5811..432b739870 100644 --- a/action/protocol/account/transfer_test.go +++ b/action/protocol/account/transfer_test.go @@ -143,7 +143,7 @@ func TestProtocol_HandleTransfer(t *testing.T) { newSender, err := accountutil.AccountState(sm, v.caller) require.NoError(err) require.Equal(sender.Balance, newSender.Balance) - require.Equal(sender.Nonce, newSender.Nonce) + require.Equal(sender.PendingNonce(), newSender.PendingNonce()) continue } require.Equal(v.status, receipt.Status) @@ -164,7 +164,7 @@ func TestProtocol_HandleTransfer(t *testing.T) { require.NoError(err) require.NoError(sender.SubBalance(gasFee)) require.Equal(sender.Balance, newSender.Balance) - require.Equal(v.nonce, newSender.Nonce) + require.Equal(v.nonce+1, newSender.PendingNonce()) // verify transaction log tLog := block.ReceiptTransactionLog(receipt) diff --git a/action/protocol/account/util/util.go b/action/protocol/account/util/util.go index 8cdfa0d078..dde480f0b7 100644 --- a/action/protocol/account/util/util.go +++ b/action/protocol/account/util/util.go @@ -22,8 +22,10 @@ type noncer interface { // SetNonce sets nonce for account func SetNonce(i noncer, state *state.Account) { - if i.Nonce() > state.Nonce { - state.Nonce = i.Nonce() + if i.Nonce() >= state.PendingNonce() { + if err := state.SetNonce(i.Nonce()); err != nil { + panic("invalid nonce") + } } } diff --git a/action/protocol/execution/evm/evmstatedbadapter.go b/action/protocol/execution/evm/evmstatedbadapter.go index dc4dc8691b..73001dcd80 100644 --- a/action/protocol/execution/evm/evmstatedbadapter.go +++ b/action/protocol/execution/evm/evmstatedbadapter.go @@ -282,22 +282,25 @@ func (stateDB *StateDBAdapter) GetNonce(evmAddr common.Address) uint64 { log.L().Error("Failed to convert evm address.", zap.Error(err)) return 0 } - nonce := uint64(0) + pendingNonce := uint64(1) state, err := stateDB.AccountState(addr.String()) if err != nil { log.L().Error("Failed to get nonce.", zap.Error(err)) // stateDB.logError(err) } else { - nonce = state.Nonce + pendingNonce = state.PendingNonce() } - if stateDB.usePendingNonce { - nonce++ + if !stateDB.usePendingNonce { + if pendingNonce == 0 { + panic("invalid pending nonce") + } + pendingNonce-- } log.L().Debug("Called GetNonce.", zap.String("address", addr.String()), - zap.Uint64("nonce", nonce)) + zap.Uint64("pendingNonce", pendingNonce)) - return nonce + return pendingNonce } // SetNonce sets the nonce of account @@ -322,7 +325,9 @@ func (stateDB *StateDBAdapter) SetNonce(evmAddr common.Address, nonce uint64) { log.L().Debug("Called SetNonce.", zap.String("address", addr.String()), zap.Uint64("nonce", nonce)) - s.Nonce = nonce + if err := s.SetNonce(nonce); err != nil { + return + } if err := accountutil.StoreAccount(stateDB.sm, addr, s); err != nil { log.L().Error("Failed to set nonce.", zap.Error(err)) stateDB.logError(err) @@ -472,7 +477,7 @@ func (stateDB *StateDBAdapter) Empty(evmAddr common.Address) bool { return true } // TODO: delete hash.ZeroHash256 - return s.Nonce == 0 && + return s.PendingNonce() == 1 && s.Balance.Sign() == 0 && (len(s.CodeHash) == 0 || bytes.Equal(s.CodeHash, hash.ZeroHash256[:])) } diff --git a/action/protocol/execution/protocol_test.go b/action/protocol/execution/protocol_test.go index 40ba0e3a3d..2fa0c48a81 100644 --- a/action/protocol/execution/protocol_test.go +++ b/action/protocol/execution/protocol_test.go @@ -265,7 +265,7 @@ func readExecution( } exec, err := action.NewExecutionWithAccessList( contractAddr, - state.Nonce+1, + state.PendingNonce(), ecfg.Amount(), ecfg.GasLimit(), ecfg.GasPrice(), @@ -299,7 +299,7 @@ func runExecutions( hashes := []hash.Hash256{} for i, ecfg := range ecfgs { log.S().Info(ecfg.Comment) - var nonce uint64 + nonce := uint64(1) var ok bool executor := ecfg.Executor() if nonce, ok = nonces[executor.String()]; !ok { @@ -307,9 +307,8 @@ func runExecutions( if err != nil { return nil, nil, err } - nonce = state.Nonce + nonce = state.PendingNonce() } - nonce = nonce + 1 nonces[executor.String()] = nonce exec, err := action.NewExecutionWithAccessList( contractAddrs[i], diff --git a/action/protocol/generic_validator.go b/action/protocol/generic_validator.go index 2d457c2924..549a005981 100644 --- a/action/protocol/generic_validator.go +++ b/action/protocol/generic_validator.go @@ -59,7 +59,7 @@ func (v *GenericValidator) Validate(_ context.Context, selp action.SealedEnvelop return errors.Wrapf(err, "invalid state of account %s", caller.String()) } - pendingNonce := confirmedState.Nonce + 1 + pendingNonce := confirmedState.PendingNonce() if selp.Nonce() > 0 && pendingNonce > selp.Nonce() { return action.ErrNonceTooLow } diff --git a/action/protocol/generic_validator_test.go b/action/protocol/generic_validator_test.go index 192993bc21..7f751b6dad 100644 --- a/action/protocol/generic_validator_test.go +++ b/action/protocol/generic_validator_test.go @@ -68,7 +68,9 @@ func TestActionProtoAndGenericValidator(t *testing.T) { return nil, errors.New("MockChainManager nonce error") } acct := state.NewEmptyAccount() - acct.Nonce = 2 + if err := acct.SetNonce(2); err != nil { + return nil, err + } return acct, nil }) diff --git a/action/protocol/rewarding/protocol.go b/action/protocol/rewarding/protocol.go index dea92ee31c..6cf7b486c2 100644 --- a/action/protocol/rewarding/protocol.go +++ b/action/protocol/rewarding/protocol.go @@ -416,8 +416,10 @@ func (p *Protocol) increaseNonce(sm protocol.StateManager, addr address.Address, return err } // TODO: this check shouldn't be necessary - if nonce > acc.Nonce { - acc.Nonce = nonce + if nonce >= acc.PendingNonce() { + if err := acc.SetNonce(nonce); err != nil { + return err + } } return accountutil.StoreAccount(sm, addr, acc) } diff --git a/action/protocol/staking/handlers_test.go b/action/protocol/staking/handlers_test.go index 2de4aea00e..f9fdd3f852 100644 --- a/action/protocol/staking/handlers_test.go +++ b/action/protocol/staking/handlers_test.go @@ -232,7 +232,7 @@ func TestProtocol_HandleCreateStake(t *testing.T) { actCost, err := act.Cost() require.NoError(err) require.Equal(unit.ConvertIotxToRau(test.initBalance), big.NewInt(0).Add(caller.Balance, actCost)) - require.Equal(test.nonce, caller.Nonce) + require.Equal(test.nonce+1, caller.PendingNonce()) } } } @@ -593,7 +593,7 @@ func TestProtocol_HandleCandidateRegister(t *testing.T) { require.NoError(err) total := big.NewInt(0) require.Equal(unit.ConvertIotxToRau(test.initBalance), total.Add(total, caller.Balance).Add(total, actCost).Add(total, p.config.RegistrationConsts.Fee)) - require.Equal(test.nonce, caller.Nonce) + require.Equal(test.nonce+1, caller.PendingNonce()) } } } @@ -909,7 +909,7 @@ func TestProtocol_HandleCandidateUpdate(t *testing.T) { require.NoError(err) total := big.NewInt(0) require.Equal(unit.ConvertIotxToRau(test.initBalance), total.Add(total, caller.Balance).Add(total, actCost).Add(total, cuCost).Add(total, p.config.RegistrationConsts.Fee)) - require.Equal(test.nonce, caller.Nonce) + require.Equal(test.nonce+1, caller.PendingNonce()) } } } @@ -1137,7 +1137,7 @@ func TestProtocol_HandleUnstake(t *testing.T) { require.NoError(err) actCost, err := act.Cost() require.NoError(err) - require.Equal(nonce, caller.Nonce) + require.Equal(nonce+1, caller.PendingNonce()) total := big.NewInt(0) require.Equal(unit.ConvertIotxToRau(test.initBalance), total.Add(total, caller.Balance).Add(total, actCost).Add(total, createCost)) } @@ -1353,7 +1353,7 @@ func TestProtocol_HandleWithdrawStake(t *testing.T) { require.NoError(err) withdrawCost, err := withdraw.Cost() require.NoError(err) - require.EqualValues(3, caller.Nonce) + require.Equal(uint64(4), caller.PendingNonce()) total := big.NewInt(0) withdrawAmount, ok := new(big.Int).SetString(test.amount, 10) require.True(ok) @@ -1658,7 +1658,7 @@ func TestProtocol_HandleChangeCandidate(t *testing.T) { require.NoError(err) actCost, err := act.Cost() require.NoError(err) - require.Equal(test.nonce, caller.Nonce) + require.Equal(test.nonce+1, caller.PendingNonce()) total := big.NewInt(0) require.Equal(unit.ConvertIotxToRau(test.initBalance), total.Add(total, caller.Balance).Add(total, actCost).Add(total, createCost)) } @@ -1859,7 +1859,7 @@ func TestProtocol_HandleTransferStake(t *testing.T) { require.NoError(err) actCost, err := act.Cost() require.NoError(err) - require.Equal(test.nonce, caller.Nonce) + require.Equal(test.nonce+1, caller.PendingNonce()) total := big.NewInt(0) require.Equal(unit.ConvertIotxToRau(test.initBalance), total.Add(total, caller.Balance).Add(total, actCost).Add(total, createCost)) } @@ -2092,7 +2092,7 @@ func TestProtocol_HandleConsignmentTransfer(t *testing.T) { require.NoError(err) actCost, err := act.Cost() require.NoError(err) - require.Equal(uint64(1), caller.Nonce) + require.Equal(uint64(2), caller.PendingNonce()) total := big.NewInt(0) require.Equal(unit.ConvertIotxToRau(initBalance), total.Add(total, caller.Balance).Add(total, actCost)) } @@ -2358,7 +2358,7 @@ func TestProtocol_HandleRestake(t *testing.T) { require.NoError(err) actCost, err := act.Cost() require.NoError(err) - require.Equal(test.nonce, caller.Nonce) + require.Equal(test.nonce+1, caller.PendingNonce()) total := big.NewInt(0) require.Equal(unit.ConvertIotxToRau(test.initBalance), total.Add(total, caller.Balance).Add(total, actCost).Add(total, createCost)) } @@ -2580,7 +2580,7 @@ func TestProtocol_HandleDepositToStake(t *testing.T) { require.NoError(err) actCost, err := act.Cost() require.NoError(err) - require.Equal(test.nonce, caller.Nonce) + require.Equal(test.nonce+1, caller.PendingNonce()) total := big.NewInt(0) require.Equal(unit.ConvertIotxToRau(test.initBalance), total.Add(total, caller.Balance).Add(total, actCost).Add(total, createCost)) } diff --git a/action/protocol/staking/protocol.go b/action/protocol/staking/protocol.go index d00fcb5406..4ded146d59 100644 --- a/action/protocol/staking/protocol.go +++ b/action/protocol/staking/protocol.go @@ -522,8 +522,10 @@ func (p *Protocol) settleAction( return nil, err } // TODO: this check shouldn't be necessary - if actionCtx.Nonce > acc.Nonce { - acc.Nonce = actionCtx.Nonce + if actionCtx.Nonce >= acc.PendingNonce() { + if err := acc.SetNonce(actionCtx.Nonce); err != nil { + return nil, err + } } if err := accountutil.StoreAccount(sm, actionCtx.Caller, acc); err != nil { return nil, errors.Wrap(err, "failed to update nonce") diff --git a/actpool/actpool.go b/actpool/actpool.go index 0fb18fe902..df9cda12cf 100644 --- a/actpool/actpool.go +++ b/actpool/actpool.go @@ -250,7 +250,7 @@ func (ap *actPool) GetPendingNonce(addr string) (uint64, error) { if err != nil { return 0, err } - return confirmedState.Nonce + 1, err + return confirmedState.PendingNonce(), err } // GetUnconfirmedActs returns unconfirmed actions in pool given an account address @@ -368,8 +368,8 @@ func (ap *actPool) enqueueAction(ctx context.Context, addr address.Address, act _actpoolMtc.WithLabelValues("failedToGetNonce").Inc() return errors.Wrapf(err, "failed to get sender's nonce for action %x", actHash) } - confirmedNonce := confirmedState.Nonce - if actNonce <= confirmedNonce { + pendingNonce := confirmedState.PendingNonce() + if actNonce < pendingNonce { return action.ErrNonceTooLow } sender := addr.String() @@ -379,15 +379,15 @@ func (ap *actPool) enqueueAction(ctx context.Context, addr address.Address, act queue = NewActQueue(ap, sender, WithTimeOut(ap.cfg.ActionExpiry)) ap.accountActs[sender] = queue // Initialize pending nonce and balance for new account - queue.SetPendingNonce(confirmedNonce + 1) + queue.SetPendingNonce(pendingNonce) queue.SetPendingBalance(confirmedState.Balance) } - if actNonce-confirmedNonce >= ap.cfg.MaxNumActsPerAcct+1 { + if actNonce-pendingNonce >= ap.cfg.MaxNumActsPerAcct { // Nonce exceeds current range log.L().Debug("Rejecting action because nonce is too large.", log.Hex("hash", actHash[:]), - zap.Uint64("startNonce", confirmedNonce+1), + zap.Uint64("startNonce", pendingNonce), zap.Uint64("actNonce", actNonce)) _actpoolMtc.WithLabelValues("nonceTooLarge").Inc() return action.ErrNonceTooHigh @@ -452,7 +452,7 @@ func (ap *actPool) removeConfirmedActs() { log.L().Error("Error when removing confirmed actions", zap.Error(err)) return } - pendingNonce := confirmedState.Nonce + 1 + pendingNonce := confirmedState.PendingNonce() // Remove all actions that are committed to new block acts := queue.FilterNonce(pendingNonce) ap.removeInvalidActs(acts) @@ -529,9 +529,7 @@ func (ap *actPool) reset() { queue.SetPendingBalance(state.Balance) // Reset pending nonce and remove invalid actions for each account - confirmedNonce := state.Nonce - pendingNonce := confirmedNonce + 1 - queue.SetPendingNonce(pendingNonce) + queue.SetPendingNonce(state.PendingNonce()) ap.updateAccount(from) } } diff --git a/actpool/actpool_test.go b/actpool/actpool_test.go index af75c9c4a2..01fb2b1644 100644 --- a/actpool/actpool_test.go +++ b/actpool/actpool_test.go @@ -129,7 +129,6 @@ func TestActPool_AddActs(t *testing.T) { sf.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn(func(account interface{}, opts ...protocol.StateOption) (uint64, error) { acct, ok := account.(*state.Account) require.True(ok) - acct.Nonce = 0 cfg := &protocol.StateConfig{} for _, opt := range opts { opt(cfg) @@ -326,7 +325,6 @@ func TestActPool_PickActs(t *testing.T) { sf.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn(func(account interface{}, opts ...protocol.StateOption) (uint64, error) { acct, ok := account.(*state.Account) require.True(ok) - acct.Nonce = 0 cfg := &protocol.StateConfig{} for _, opt := range opts { opt(cfg) @@ -394,7 +392,6 @@ func TestActPool_removeConfirmedActs(t *testing.T) { sf.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn(func(account interface{}, opts ...protocol.StateOption) (uint64, error) { acct, ok := account.(*state.Account) require.True(ok) - acct.Nonce = 0 require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil @@ -409,7 +406,7 @@ func TestActPool_removeConfirmedActs(t *testing.T) { sf.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn(func(account interface{}, opts ...protocol.StateOption) (uint64, error) { acct, ok := account.(*state.Account) require.True(ok) - acct.Nonce = 4 + require.NoError(acct.SetNonce(4)) require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil @@ -442,19 +439,19 @@ func TestActPool_Reset(t *testing.T) { switch { case bytes.Equal(cfg.Key, identityset.Address(28).Bytes()): require.NoError(acct.AddBalance(new(big.Int).Set(balances[0]))) - acct.Nonce = nonces[0] + require.NoError(acct.SetNonce(nonces[0])) case bytes.Equal(cfg.Key, identityset.Address(29).Bytes()): require.NoError(acct.AddBalance(new(big.Int).Set(balances[1]))) - acct.Nonce = nonces[1] + require.NoError(acct.SetNonce(nonces[1])) case bytes.Equal(cfg.Key, identityset.Address(30).Bytes()): require.NoError(acct.AddBalance(new(big.Int).Set(balances[2]))) - acct.Nonce = nonces[2] + require.NoError(acct.SetNonce(nonces[2])) case bytes.Equal(cfg.Key, identityset.Address(31).Bytes()): require.NoError(acct.AddBalance(new(big.Int).Set(balances[3]))) - acct.Nonce = nonces[3] + require.NoError(acct.SetNonce(nonces[3])) case bytes.Equal(cfg.Key, identityset.Address(32).Bytes()): require.NoError(acct.AddBalance(new(big.Int).Set(balances[4]))) - acct.Nonce = nonces[4] + require.NoError(acct.SetNonce(nonces[4])) } return 0, nil }).AnyTimes() @@ -795,7 +792,6 @@ func TestActPool_removeInvalidActs(t *testing.T) { sf.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn(func(account interface{}, opts ...protocol.StateOption) (uint64, error) { acct, ok := account.(*state.Account) require.True(ok) - acct.Nonce = 0 require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil @@ -840,7 +836,6 @@ func TestActPool_GetPendingNonce(t *testing.T) { sf.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn(func(account interface{}, opts ...protocol.StateOption) (uint64, error) { acct, ok := account.(*state.Account) require.True(ok) - acct.Nonce = 0 require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil @@ -888,7 +883,6 @@ func TestActPool_GetUnconfirmedActs(t *testing.T) { sf.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn(func(account interface{}, opts ...protocol.StateOption) (uint64, error) { acct, ok := account.(*state.Account) require.True(ok) - acct.Nonce = 0 require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil @@ -988,7 +982,6 @@ func TestActPool_GetSize(t *testing.T) { sf.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn(func(account interface{}, opts ...protocol.StateOption) (uint64, error) { acct, ok := account.(*state.Account) require.True(ok) - acct.Nonce = 0 require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil @@ -1002,7 +995,7 @@ func TestActPool_GetSize(t *testing.T) { sf.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn(func(account interface{}, opts ...protocol.StateOption) (uint64, error) { acct, ok := account.(*state.Account) require.True(ok) - acct.Nonce = 4 + require.NoError(acct.SetNonce(4)) require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil @@ -1041,7 +1034,6 @@ func TestActPool_SpeedUpAction(t *testing.T) { sf.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn(func(account interface{}, opts ...protocol.StateOption) (uint64, error) { acct, ok := account.(*state.Account) require.True(ok) - acct.Nonce = 0 cfg := &protocol.StateConfig{} for _, opt := range opts { opt(cfg) @@ -1118,7 +1110,10 @@ func (ap *actPool) getPendingNonce(addr string) (uint64, error) { return 0, err } committedState, err := accountutil.AccountState(ap.sf, _addr1) - return committedState.Nonce + 1, err + if err != nil { + return 0, err + } + return committedState.PendingNonce(), nil } // Helper function to return the correct pending balance just in case of empty queue diff --git a/actpool/actqueue.go b/actpool/actqueue.go index 7600b25893..074924a184 100644 --- a/actpool/actqueue.go +++ b/actpool/actqueue.go @@ -265,7 +265,7 @@ func (q *actQueue) PendingActs() []action.SealedEnvelope { log.L().Error("Error when getting the nonce", zap.String("address", q.address), zap.Error(err)) return nil } - nonce := confirmedState.Nonce + 1 + nonce := confirmedState.PendingNonce() for ; ; nonce++ { if _, exist := q.items[nonce]; !exist { break diff --git a/actpool/actqueue_test.go b/actpool/actqueue_test.go index 74e515826f..effd589d14 100644 --- a/actpool/actqueue_test.go +++ b/actpool/actqueue_test.go @@ -129,7 +129,7 @@ func TestActQueuePendingActs(t *testing.T) { cfg := config.Default sf := mock_chainmanager.NewMockStateReader(ctrl) sf.EXPECT().State(gomock.Any(), gomock.Any()).Do(func(accountState *state.Account, _ protocol.StateOption) { - accountState.Nonce = uint64(1) + require.NoError(accountState.SetNonce(accountState.PendingNonce())) }).Return(uint64(0), nil).Times(1) ap, err := NewActPool(sf, cfg.ActPool, EnableExperimentalActions()) require.NoError(err) diff --git a/api/coreservice.go b/api/coreservice.go index 815ec411d4..de8cc5e608 100644 --- a/api/coreservice.go +++ b/api/coreservice.go @@ -268,10 +268,10 @@ func (core *coreService) Account(addr address.Address) (*iotextypes.AccountMeta, if err != nil { return nil, nil, status.Error(codes.NotFound, err.Error()) } + // TODO: deprecate nonce field in account meta accountMeta := &iotextypes.AccountMeta{ Address: addrStr, Balance: state.Balance.String(), - Nonce: state.Nonce, PendingNonce: pendingNonce, NumActions: numActions, IsContract: state.IsContract(), @@ -460,7 +460,7 @@ func (core *coreService) ReadContract(ctx context.Context, callerAddr address.Ad if ctx, err = core.bc.Context(ctx); err != nil { return "", nil, err } - sc.SetNonce(state.Nonce + 1) + sc.SetNonce(state.PendingNonce()) blockGasLimit := core.bc.Genesis().BlockGasLimit if sc.GasLimit() == 0 || blockGasLimit < sc.GasLimit() { sc.SetGasLimit(blockGasLimit) @@ -1370,7 +1370,7 @@ func (core *coreService) EstimateExecutionGasConsumption(ctx context.Context, sc if err != nil { return 0, status.Error(codes.InvalidArgument, err.Error()) } - sc.SetNonce(state.Nonce + 1) + sc.SetNonce(state.PendingNonce()) sc.SetGasPrice(big.NewInt(0)) blockGasLimit := core.bc.Genesis().BlockGasLimit sc.SetGasLimit(blockGasLimit) @@ -1566,7 +1566,7 @@ func (core *coreService) SimulateExecution(ctx context.Context, addr address.Add return nil, nil, err } // TODO (liuhaai): Use original nonce and gas limit properly - exec.SetNonce(state.Nonce + 1) + exec.SetNonce(state.PendingNonce()) if err != nil { return nil, nil, err } diff --git a/api/grpcserver_integrity_test.go b/api/grpcserver_integrity_test.go index 5f260803a0..d85d6aaca7 100644 --- a/api/grpcserver_integrity_test.go +++ b/api/grpcserver_integrity_test.go @@ -141,7 +141,7 @@ var ( {identityset.Address(30).String(), "io1d4c5lp4ea4754wy439g2t99ue7wryu5r2lslh2", "3", - 8, + 0, 9, 9, }, @@ -149,7 +149,7 @@ var ( identityset.Address(27).String(), "io1mflp9m6hcgm2qcghchsdqj3z3eccrnekx9p0ms", "9999999999999999999999898950", - 5, + 0, 6, 6, }, diff --git a/blockchain/block/validator_test.go b/blockchain/block/validator_test.go index bf024ffd7e..e890c28fe4 100644 --- a/blockchain/block/validator_test.go +++ b/blockchain/block/validator_test.go @@ -40,7 +40,7 @@ func TestValidator(t *testing.T) { return nil, errors.New("MockChainManager nonce error") } account := state.NewEmptyAccount() - account.Nonce = 2 + require.NoError(account.SetNonce(2)) return account, nil }) diff --git a/blockchain/integrity/integrity_test.go b/blockchain/integrity/integrity_test.go index 2be772e3e8..be3bdd903c 100644 --- a/blockchain/integrity/integrity_test.go +++ b/blockchain/integrity/integrity_test.go @@ -1228,7 +1228,7 @@ func TestLoadBlockchainfromDB(t *testing.T) { require.NoError(err) act, err := accountutil.AccountState(sf, addr) require.NoError(err) - require.Equal(uint64(0), act.Nonce) + require.Equal(uint64(1), act.PendingNonce()) require.Equal(big.NewInt(0), act.Balance) _, gateway := cfg.Plugins[config.GatewayPlugin] @@ -1462,7 +1462,7 @@ func TestBlockchain_AccountState(t *testing.T) { require.NotNil(bc) s, err := accountutil.AccountState(sf, identityset.Address(0)) require.NoError(err) - require.Equal(uint64(0), s.Nonce) + require.Equal(uint64(1), s.PendingNonce()) require.Equal(big.NewInt(100), s.Balance) require.Equal(hash.ZeroHash256, s.Root) require.Equal([]byte(nil), s.CodeHash) diff --git a/e2etest/local_test.go b/e2etest/local_test.go index b5ca659a6a..c05d7527e1 100644 --- a/e2etest/local_test.go +++ b/e2etest/local_test.go @@ -191,7 +191,7 @@ func TestLocalCommit(t *testing.T) { // transfer 1 // C --> A s, _ = accountutil.AccountState(sf, identityset.Address(30)) - tsf1, err := action.SignedTransfer(identityset.Address(28).String(), identityset.PrivateKey(30), s.Nonce+1, big.NewInt(1), []byte{}, 100000, big.NewInt(0)) + tsf1, err := action.SignedTransfer(identityset.Address(28).String(), identityset.PrivateKey(30), s.PendingNonce(), big.NewInt(1), []byte{}, 100000, big.NewInt(0)) require.NoError(err) require.NoError(ap2.Add(context.Background(), tsf1)) @@ -212,7 +212,7 @@ func TestLocalCommit(t *testing.T) { // transfer 2 // F --> D s, _ = accountutil.AccountState(sf, identityset.Address(33)) - tsf2, err := action.SignedTransfer(identityset.Address(31).String(), identityset.PrivateKey(33), s.Nonce+1, big.NewInt(1), []byte{}, 100000, big.NewInt(0)) + tsf2, err := action.SignedTransfer(identityset.Address(31).String(), identityset.PrivateKey(33), s.PendingNonce(), big.NewInt(1), []byte{}, 100000, big.NewInt(0)) require.NoError(err) require.NoError(ap2.Add(context.Background(), tsf2)) @@ -233,7 +233,7 @@ func TestLocalCommit(t *testing.T) { // transfer 3 // B --> B s, _ = accountutil.AccountState(sf, identityset.Address(29)) - tsf3, err := action.SignedTransfer(identityset.Address(29).String(), identityset.PrivateKey(29), s.Nonce+1, big.NewInt(1), []byte{}, 100000, big.NewInt(0)) + tsf3, err := action.SignedTransfer(identityset.Address(29).String(), identityset.PrivateKey(29), s.PendingNonce(), big.NewInt(1), []byte{}, 100000, big.NewInt(0)) require.NoError(err) require.NoError(ap2.Add(context.Background(), tsf3)) @@ -254,7 +254,7 @@ func TestLocalCommit(t *testing.T) { // transfer 4 // test --> E s, _ = accountutil.AccountState(sf, identityset.Address(27)) - tsf4, err := action.SignedTransfer(identityset.Address(32).String(), identityset.PrivateKey(27), s.Nonce+1, big.NewInt(1), []byte{}, 100000, big.NewInt(0)) + tsf4, err := action.SignedTransfer(identityset.Address(32).String(), identityset.PrivateKey(27), s.PendingNonce(), big.NewInt(1), []byte{}, 100000, big.NewInt(0)) require.NoError(err) require.NoError(ap2.Add(context.Background(), tsf4)) diff --git a/state/account.go b/state/account.go index 033e3f2919..d5a5981fbc 100644 --- a/state/account.go +++ b/state/account.go @@ -41,7 +41,7 @@ type ( Account struct { // 0 is reserved from actions in genesis block and coinbase transfers nonces // other actions' nonces start from 1 - Nonce uint64 + nonce uint64 Balance *big.Int Root hash.Hash256 // storage trie root for contract account CodeHash []byte // hash of the smart contract byte-code for contract account @@ -53,7 +53,7 @@ type ( // ToProto converts to protobuf's Account func (st *Account) ToProto() *accountpb.Account { acPb := &accountpb.Account{} - acPb.Nonce = st.Nonce + acPb.Nonce = st.nonce if st.Balance != nil { acPb.Balance = st.Balance.String() } @@ -75,7 +75,7 @@ func (st Account) Serialize() ([]byte, error) { // FromProto converts from protobuf's Account func (st *Account) FromProto(acPb *accountpb.Account) { - st.Nonce = acPb.Nonce + st.nonce = acPb.Nonce if acPb.Balance == "" { st.Balance = big.NewInt(0) } else { @@ -108,6 +108,17 @@ func (st *Account) Deserialize(buf []byte) error { return nil } +// SetNonce sets the nonce of the account +func (st *Account) SetNonce(nonce uint64) error { + st.nonce = nonce + return nil +} + +// PendingNonce returns the pending nonce of the account +func (st *Account) PendingNonce() uint64 { + return st.nonce + 1 +} + // HasSufficientBalance returns true if balance is larger than amount func (st *Account) HasSufficientBalance(amount *big.Int) bool { if amount == nil { diff --git a/state/account_test.go b/state/account_test.go index c53032e41e..11d80acffd 100644 --- a/state/account_test.go +++ b/state/account_test.go @@ -20,7 +20,7 @@ import ( func TestEncodeDecode(t *testing.T) { require := require.New(t) s1 := Account{ - Nonce: 0x10, + nonce: 0x10, Balance: big.NewInt(20000000), CodeHash: []byte("testing codehash"), } @@ -32,7 +32,7 @@ func TestEncodeDecode(t *testing.T) { s2 := Account{} require.NoError(s2.Deserialize(ss)) require.Equal(big.NewInt(20000000), s2.Balance) - require.Equal(uint64(0x10), s2.Nonce) + require.Equal(uint64(0x11), s2.PendingNonce()) require.Equal(hash.ZeroHash256, s2.Root) require.Equal([]byte("testing codehash"), s2.CodeHash) } @@ -65,7 +65,7 @@ func TestBalance(t *testing.T) { func TestClone(t *testing.T) { require := require.New(t) ss := &Account{ - Nonce: 0x10, + nonce: 0x10, Balance: big.NewInt(200), } account := ss.Clone() diff --git a/state/factory/factory_test.go b/state/factory/factory_test.go index 5be54290a1..b65d51d4dd 100644 --- a/state/factory/factory_test.go +++ b/state/factory/factory_test.go @@ -782,7 +782,7 @@ func testNonce(sf Factory, t *testing.T) { require.NoError(t, err) state, err := accountutil.AccountState(sf, a) require.NoError(t, err) - require.Equal(t, uint64(0), state.Nonce) + require.Equal(t, uint64(1), state.PendingNonce()) tx, err = action.NewTransfer(1, big.NewInt(2), b, nil, uint64(20000), big.NewInt(0)) require.NoError(t, err) @@ -802,7 +802,7 @@ func testNonce(sf Factory, t *testing.T) { require.NoError(t, sf.PutBlock(ctx, &blk)) state, err = accountutil.AccountState(sf, a) require.NoError(t, err) - require.Equal(t, uint64(1), state.Nonce) + require.Equal(t, uint64(2), state.PendingNonce()) } func TestLoadStoreHeight(t *testing.T) { @@ -1356,7 +1356,7 @@ func TestDeleteAndPutSameKey(t *testing.T) { testDeleteAndPutSameKey := func(t *testing.T, ws *workingSet) { key := hash.Hash160b([]byte("test")) acc := state.NewEmptyAccount() - acc.Nonce = 1 + require.NoError(t, acc.SetNonce(1)) _, err := ws.PutState(acc, protocol.LegacyKeyOption(key)) require.NoError(t, err) _, err = ws.DelState(protocol.LegacyKeyOption(key)) diff --git a/state/factory/workingset.go b/state/factory/workingset.go index 150ca110ab..bf22a08f2b 100644 --- a/state/factory/workingset.go +++ b/state/factory/workingset.go @@ -346,15 +346,16 @@ func (ws *workingSet) validateNonce(blk *block.Block) error { } receivedNonces := receivedNonces sort.Slice(receivedNonces, func(i, j int) bool { return receivedNonces[i] < receivedNonces[j] }) + pendingNonce := confirmedState.PendingNonce() for i, nonce := range receivedNonces { - if nonce != confirmedState.Nonce+uint64(i+1) { + if nonce != pendingNonce+uint64(i) { return errors.Wrapf( action.ErrNonceTooHigh, "the %d nonce %d of address %s (confirmed nonce %d) is not continuously increasing", i, nonce, srcAddr, - confirmedState.Nonce, + pendingNonce, ) } } From b397bcb137c29a34a370e92021da2b91082344cc Mon Sep 17 00:00:00 2001 From: zhi Date: Sun, 22 May 2022 21:12:36 -0700 Subject: [PATCH 04/11] skip update nonce and gas for system actions --- action/const.go | 1 + action/protocol/context.go | 2 + action/protocol/generic_validator.go | 13 +++-- action/protocol/generic_validator_test.go | 19 +++++- action/protocol/rewarding/protocol.go | 70 +++++++++++++++-------- action/protocol/staking/protocol.go | 7 +-- 6 files changed, 79 insertions(+), 33 deletions(-) diff --git a/action/const.go b/action/const.go index 16dcbdfab9..52b6958190 100644 --- a/action/const.go +++ b/action/const.go @@ -16,6 +16,7 @@ var ( ErrChainID = errors.New("invalid chainID") ErrExistedInPool = errors.New("known transaction") ErrReplaceUnderpriced = errors.New("replacement transaction underpriced") + ErrSystemActionNonce = errors.New("invalid system action nonce") ErrNonceTooLow = errors.New("nonce too low") ErrUnderpriced = errors.New("transaction underpriced") ErrNegativeValue = errors.New("negative value") diff --git a/action/protocol/context.go b/action/protocol/context.go index 9121e028e6..6189daf5cb 100644 --- a/action/protocol/context.go +++ b/action/protocol/context.go @@ -105,6 +105,7 @@ type ( RevertLog bool TolerateLegacyAddress bool ValidateRewardProtocol bool + SkipUpdateForSystemAction bool } // FeatureWithHeightCtx provides feature check functions. @@ -234,6 +235,7 @@ func WithFeatureCtx(ctx context.Context) context.Context { RevertLog: g.IsMidway(height), TolerateLegacyAddress: !g.IsNewfoundland(height), ValidateRewardProtocol: g.IsNewfoundland(height), + SkipUpdateForSystemAction: g.IsToBeEnabled(height), }, ) } diff --git a/action/protocol/generic_validator.go b/action/protocol/generic_validator.go index 549a005981..36014a1965 100644 --- a/action/protocol/generic_validator.go +++ b/action/protocol/generic_validator.go @@ -58,10 +58,15 @@ func (v *GenericValidator) Validate(_ context.Context, selp action.SealedEnvelop if err != nil { return errors.Wrapf(err, "invalid state of account %s", caller.String()) } - - pendingNonce := confirmedState.PendingNonce() - if selp.Nonce() > 0 && pendingNonce > selp.Nonce() { - return action.ErrNonceTooLow + if action.IsSystemAction(selp) { + if selp.Nonce() != 0 { + return action.ErrSystemActionNonce + } + } else { + if confirmedState.PendingNonce() > selp.Nonce() { + return action.ErrNonceTooLow + } } + return selp.Action().SanityCheck() } diff --git a/action/protocol/generic_validator_test.go b/action/protocol/generic_validator_test.go index 7f751b6dad..518bdf05b1 100644 --- a/action/protocol/generic_validator_test.go +++ b/action/protocol/generic_validator_test.go @@ -77,11 +77,12 @@ func TestActionProtoAndGenericValidator(t *testing.T) { data, err := hex.DecodeString("") require.NoError(err) t.Run("normal", func(t *testing.T) { - v, err := action.NewExecution("", 0, big.NewInt(10), uint64(10), big.NewInt(10), data) + v, err := action.NewExecution("", 3, big.NewInt(10), uint64(10), big.NewInt(10), data) require.NoError(err) bd := &action.EnvelopeBuilder{} elp := bd.SetGasPrice(big.NewInt(10)). SetGasLimit(uint64(100000)). + SetNonce(3). SetAction(v).Build() selp, err := action.Sign(elp, identityset.PrivateKey(28)) require.NoError(err) @@ -119,6 +120,22 @@ func TestActionProtoAndGenericValidator(t *testing.T) { require.Error(err) require.Contains(err.Error(), "invalid state of account") }) + t.Run("invalid system action nonce", func(t *testing.T) { + gr := action.GrantReward{} + gr.SetNonce(1) + bd := &action.EnvelopeBuilder{} + elp := bd.SetGasPrice(big.NewInt(10)). + SetNonce(1). + SetGasLimit(uint64(100000)). + SetAction(&gr).Build() + selp, err := action.Sign(elp, identityset.PrivateKey(28)) + require.NoError(err) + nselp := action.SealedEnvelope{} + require.NoError(nselp.LoadProto(selp.Proto())) + err = valid.Validate(ctx, nselp) + require.Error(err) + require.Equal(action.ErrSystemActionNonce, errors.Cause(err)) + }) t.Run("nonce too low", func(t *testing.T) { v, err := action.NewExecution("", 1, big.NewInt(10), uint64(10), big.NewInt(10), data) require.NoError(err) diff --git a/action/protocol/rewarding/protocol.go b/action/protocol/rewarding/protocol.go index 6cf7b486c2..264b9da2fa 100644 --- a/action/protocol/rewarding/protocol.go +++ b/action/protocol/rewarding/protocol.go @@ -214,17 +214,17 @@ func (p *Protocol) Handle( rlog, err := p.Deposit(ctx, sm, act.Amount(), iotextypes.TransactionLogType_DEPOSIT_TO_REWARDING_FUND) if err != nil { log.L().Debug("Error when handling rewarding action", zap.Error(err)) - return p.settleAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Failure), si, nil) + return p.settleUserAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Failure), si, nil) } - return p.settleAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, nil, rlog) + return p.settleUserAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, nil, rlog) case *action.ClaimFromRewardingFund: si := sm.Snapshot() rlog, err := p.Claim(ctx, sm, act.Amount()) if err != nil { log.L().Debug("Error when handling rewarding action", zap.Error(err)) - return p.settleAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Failure), si, nil) + return p.settleUserAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Failure), si, nil) } - return p.settleAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, nil, rlog) + return p.settleUserAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, nil, rlog) case *action.GrantReward: switch act.RewardType() { case action.BlockReward: @@ -232,20 +232,20 @@ func (p *Protocol) Handle( rewardLog, err := p.GrantBlockReward(ctx, sm) if err != nil { log.L().Debug("Error when handling rewarding action", zap.Error(err)) - return p.settleAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Failure), si, nil) + return p.settleSystemAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Failure), si, nil) } if rewardLog == nil { - return p.settleAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, nil) + return p.settleSystemAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, nil) } - return p.settleAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, []*action.Log{rewardLog}) + return p.settleSystemAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, []*action.Log{rewardLog}) case action.EpochReward: si := sm.Snapshot() rewardLogs, err := p.GrantEpochReward(ctx, sm) if err != nil { log.L().Debug("Error when handling rewarding action", zap.Error(err)) - return p.settleAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Failure), si, nil) + return p.settleSystemAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Failure), si, nil) } - return p.settleAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, rewardLogs) + return p.settleSystemAction(ctx, sm, uint64(iotextypes.ReceiptStatus_Success), si, rewardLogs) } } return nil, nil @@ -381,11 +381,34 @@ func (p *Protocol) deleteStateV2(sm protocol.StateManager, key []byte) error { return err } +func (p *Protocol) settleSystemAction( + ctx context.Context, + sm protocol.StateManager, + status uint64, + si int, + logs []*action.Log, + tLogs ...*action.TransactionLog, +) (*action.Receipt, error) { + return p.settleAction(ctx, sm, status, si, true, logs, tLogs...) +} + +func (p *Protocol) settleUserAction( + ctx context.Context, + sm protocol.StateManager, + status uint64, + si int, + logs []*action.Log, + tLogs ...*action.TransactionLog, +) (*action.Receipt, error) { + return p.settleAction(ctx, sm, status, si, false, logs, tLogs...) +} + func (p *Protocol) settleAction( ctx context.Context, sm protocol.StateManager, status uint64, si int, + isSystemAction bool, logs []*action.Log, tLogs ...*action.TransactionLog, ) (*action.Receipt, error) { @@ -396,29 +419,30 @@ func (p *Protocol) settleAction( return nil, err } } - gasFee := big.NewInt(0).Mul(actionCtx.GasPrice, big.NewInt(0).SetUint64(actionCtx.IntrinsicGas)) - depositLog, err := DepositGas(ctx, sm, gasFee) - if err != nil { - return nil, err - } - if depositLog != nil { - tLogs = append(tLogs, depositLog) - } - if err := p.increaseNonce(sm, actionCtx.Caller, actionCtx.Nonce); err != nil { - return nil, err + if !isSystemAction || !protocol.MustGetFeatureCtx(ctx).SkipUpdateForSystemAction { + gasFee := big.NewInt(0).Mul(actionCtx.GasPrice, big.NewInt(0).SetUint64(actionCtx.IntrinsicGas)) + depositLog, err := DepositGas(ctx, sm, gasFee) + if err != nil { + return nil, err + } + if depositLog != nil { + tLogs = append(tLogs, depositLog) + } + if err := p.increaseNonce(sm, actionCtx.Caller, actionCtx.Nonce, isSystemAction); err != nil { + return nil, err + } } return p.createReceipt(status, blkCtx.BlockHeight, actionCtx.ActionHash, actionCtx.IntrinsicGas, logs, tLogs...), nil } -func (p *Protocol) increaseNonce(sm protocol.StateManager, addr address.Address, nonce uint64) error { +func (p *Protocol) increaseNonce(sm protocol.StateManager, addr address.Address, nonce uint64, isSystemAction bool) error { acc, err := accountutil.LoadOrCreateAccount(sm, addr) if err != nil { return err } - // TODO: this check shouldn't be necessary - if nonce >= acc.PendingNonce() { + if !isSystemAction || nonce != 0 { if err := acc.SetNonce(nonce); err != nil { - return err + return errors.Wrapf(err, "invalid nonce %d", nonce) } } return accountutil.StoreAccount(sm, addr, acc) diff --git a/action/protocol/staking/protocol.go b/action/protocol/staking/protocol.go index 4ded146d59..784eec6de4 100644 --- a/action/protocol/staking/protocol.go +++ b/action/protocol/staking/protocol.go @@ -521,11 +521,8 @@ func (p *Protocol) settleAction( if err != nil { return nil, err } - // TODO: this check shouldn't be necessary - if actionCtx.Nonce >= acc.PendingNonce() { - if err := acc.SetNonce(actionCtx.Nonce); err != nil { - return nil, err - } + if err := acc.SetNonce(actionCtx.Nonce); err != nil { + return nil, errors.Wrap(err, "failed to set nonce") } if err := accountutil.StoreAccount(sm, actionCtx.Caller, acc); err != nil { return nil, errors.Wrap(err, "failed to update nonce") From 878c5831745ba364149f4ead547d868e08a67b98 Mon Sep 17 00:00:00 2001 From: zhi Date: Thu, 19 May 2022 18:49:08 -0700 Subject: [PATCH 05/11] check nonce value --- action/protocol/account/transfer.go | 8 +- action/protocol/account/util/util.go | 13 --- action/protocol/generic_validator_test.go | 3 + action/protocol/rewarding/protocol_test.go | 31 +++++- action/protocol/staking/handlers_test.go | 118 +++++++++++++-------- actpool/actpool_test.go | 28 +++-- state/account.go | 5 + state/factory/factory_test.go | 6 +- 8 files changed, 137 insertions(+), 75 deletions(-) diff --git a/action/protocol/account/transfer.go b/action/protocol/account/transfer.go index b8aa6aec12..00fa152e74 100644 --- a/action/protocol/account/transfer.go +++ b/action/protocol/account/transfer.go @@ -80,7 +80,9 @@ func (p *Protocol) handleTransfer(ctx context.Context, act action.Action, sm pro } if recipientAcct.IsContract() { // update sender Nonce - accountutil.SetNonce(tsf, sender) + if err := sender.SetNonce(tsf.Nonce()); err != nil { + return nil, errors.Wrapf(err, "failed to update pending nonce of sender %s", actionCtx.Caller.String()) + } // put updated sender's state to trie if err := accountutil.StoreAccount(sm, actionCtx.Caller, sender); err != nil { return nil, errors.Wrap(err, "failed to update pending account changes to trie") @@ -109,7 +111,9 @@ func (p *Protocol) handleTransfer(ctx context.Context, act action.Action, sm pro return nil, errors.Wrapf(err, "failed to update the Balance of sender %s", actionCtx.Caller.String()) } // update sender Nonce - accountutil.SetNonce(tsf, sender) + if err := sender.SetNonce(tsf.Nonce()); err != nil { + return nil, errors.Wrapf(err, "failed to update pending nonce of sender %s", actionCtx.Caller.String()) + } // put updated sender's state to trie if err := accountutil.StoreAccount(sm, actionCtx.Caller, sender); err != nil { return nil, errors.Wrap(err, "failed to update pending account changes to trie") diff --git a/action/protocol/account/util/util.go b/action/protocol/account/util/util.go index dde480f0b7..d71c5d9ee5 100644 --- a/action/protocol/account/util/util.go +++ b/action/protocol/account/util/util.go @@ -16,19 +16,6 @@ import ( "github.com/iotexproject/iotex-core/state" ) -type noncer interface { - Nonce() uint64 -} - -// SetNonce sets nonce for account -func SetNonce(i noncer, state *state.Account) { - if i.Nonce() >= state.PendingNonce() { - if err := state.SetNonce(i.Nonce()); err != nil { - panic("invalid nonce") - } - } -} - // LoadOrCreateAccount either loads an account state or creates an account state func LoadOrCreateAccount(sm protocol.StateManager, addr address.Address, opts ...state.AccountCreationOption) (*state.Account, error) { var ( diff --git a/action/protocol/generic_validator_test.go b/action/protocol/generic_validator_test.go index 518bdf05b1..6b117de4b1 100644 --- a/action/protocol/generic_validator_test.go +++ b/action/protocol/generic_validator_test.go @@ -68,6 +68,9 @@ func TestActionProtoAndGenericValidator(t *testing.T) { return nil, errors.New("MockChainManager nonce error") } acct := state.NewEmptyAccount() + if err := acct.SetNonce(1); err != nil { + return nil, err + } if err := acct.SetNonce(2); err != nil { return nil, err } diff --git a/action/protocol/rewarding/protocol_test.go b/action/protocol/rewarding/protocol_test.go index 4a2658c190..1b89025d43 100644 --- a/action/protocol/rewarding/protocol_test.go +++ b/action/protocol/rewarding/protocol_test.go @@ -364,6 +364,7 @@ func TestProtocol_Handle(t *testing.T) { protocol.ActionCtx{ Caller: identityset.Address(0), GasPrice: big.NewInt(0), + Nonce: 1, }, ) @@ -371,7 +372,7 @@ func TestProtocol_Handle(t *testing.T) { db := action.DepositToRewardingFundBuilder{} deposit := db.SetAmount(big.NewInt(1000000)).Build() eb1 := action.EnvelopeBuilder{} - e1 := eb1.SetNonce(0). + e1 := eb1.SetNonce(1). SetGasPrice(big.NewInt(0)). SetGasLimit(deposit.GasLimit()). SetAction(&deposit). @@ -390,11 +391,26 @@ func TestProtocol_Handle(t *testing.T) { e2 := createGrantRewardAction(0, uint64(0)) se2, err := action.Sign(e2, identityset.PrivateKey(0)) require.NoError(t, err) - + ctx = protocol.WithActionCtx( + ctx, + protocol.ActionCtx{ + Caller: identityset.Address(0), + GasPrice: big.NewInt(0), + Nonce: 0, + }, + ) receipt, err := p.Handle(ctx, se2.Action(), sm) require.NoError(t, err) assert.Equal(t, uint64(iotextypes.ReceiptStatus_Success), receipt.Status) assert.Equal(t, 1, len(receipt.Logs())) + ctx = protocol.WithActionCtx( + ctx, + protocol.ActionCtx{ + Caller: identityset.Address(0), + GasPrice: big.NewInt(0), + Nonce: 0, + }, + ) // Grant the block reward again should fail receipt, err = p.Handle(ctx, se2.Action(), sm) require.NoError(t, err) @@ -404,14 +420,21 @@ func TestProtocol_Handle(t *testing.T) { claimBuilder := action.ClaimFromRewardingFundBuilder{} claim := claimBuilder.SetAmount(big.NewInt(1000000)).Build() eb3 := action.EnvelopeBuilder{} - e3 := eb3.SetNonce(0). + e3 := eb3.SetNonce(4). SetGasPrice(big.NewInt(0)). SetGasLimit(claim.GasLimit()). SetAction(&claim). Build() se3, err := action.Sign(e3, identityset.PrivateKey(0)) require.NoError(t, err) - + ctx = protocol.WithActionCtx( + ctx, + protocol.ActionCtx{ + Caller: identityset.Address(0), + GasPrice: big.NewInt(0), + Nonce: 2, + }, + ) _, err = p.Handle(ctx, se3.Action(), sm) require.NoError(t, err) balance, _, err = p.TotalBalance(ctx, sm) diff --git a/action/protocol/staking/handlers_test.go b/action/protocol/staking/handlers_test.go index f9fdd3f852..0440cd2d0d 100644 --- a/action/protocol/staking/handlers_test.go +++ b/action/protocol/staking/handlers_test.go @@ -128,7 +128,7 @@ func TestProtocol_HandleCreateStake(t *testing.T) { false, big.NewInt(unit.Qev), 10000, - 1, + 2, 1, time.Now(), 10000, @@ -143,7 +143,7 @@ func TestProtocol_HandleCreateStake(t *testing.T) { false, big.NewInt(unit.Qev), 10000, - 1, + 2, 1, time.Now(), 10000, @@ -158,7 +158,7 @@ func TestProtocol_HandleCreateStake(t *testing.T) { false, big.NewInt(unit.Qev), 10000, - 1, + 2, 1, time.Now(), 10000, @@ -266,7 +266,7 @@ func TestProtocol_HandleCandidateRegister(t *testing.T) { { 1200000, identityset.Address(27), - uint64(10), + 1, "test", identityset.Address(28).String(), identityset.Address(29).String(), @@ -287,7 +287,7 @@ func TestProtocol_HandleCandidateRegister(t *testing.T) { { 1201000, identityset.Address(27), - uint64(10), + 1, "test", identityset.Address(28).String(), identityset.Address(29).String(), @@ -308,7 +308,7 @@ func TestProtocol_HandleCandidateRegister(t *testing.T) { { 1201000, identityset.Address(27), - uint64(10), + 2, "test", identityset.Address(28).String(), identityset.Address(29).String(), @@ -329,7 +329,7 @@ func TestProtocol_HandleCandidateRegister(t *testing.T) { { 1201000, identityset.Address(27), - uint64(10), + 1, "test", identityset.Address(28).String(), identityset.Address(29).String(), @@ -350,7 +350,7 @@ func TestProtocol_HandleCandidateRegister(t *testing.T) { { 1201000, identityset.Address(27), - uint64(10), + 1, "!invalid", identityset.Address(28).String(), identityset.Address(29).String(), @@ -371,7 +371,7 @@ func TestProtocol_HandleCandidateRegister(t *testing.T) { { 1201000, identityset.Address(27), - uint64(10), + 1, "test", identityset.Address(28).String(), identityset.Address(29).String(), @@ -392,7 +392,7 @@ func TestProtocol_HandleCandidateRegister(t *testing.T) { { 1201000, identityset.Address(27), - uint64(10), + 2, "test2", identityset.Address(10).String(), identityset.Address(10).String(), @@ -411,9 +411,10 @@ func TestProtocol_HandleCandidateRegister(t *testing.T) { // act.OwnerAddress() is not nil,existing candidate, collide with existing name,this case cannot happen,b/c if ownerExist,it will return ReceiptStatus_ErrCandidateAlreadyExist // act.OwnerAddress() is not nil,existing candidate, collide with existing operator,this case cannot happen,b/c if ownerExist,it will return ReceiptStatus_ErrCandidateAlreadyExist // act.OwnerAddress() is not nil,new candidate, collide with existing name - {1201000, + { + 1201000, identityset.Address(27), - uint64(10), + 3, "test", identityset.Address(10).String(), identityset.Address(10).String(), @@ -431,9 +432,10 @@ func TestProtocol_HandleCandidateRegister(t *testing.T) { iotextypes.ReceiptStatus_ErrCandidateConflict, }, // act.OwnerAddress() is not nil,new candidate, collide with existing operator - {1201000, + { + 1201000, identityset.Address(27), - uint64(10), + 4, "test2", identityset.Address(28).String(), identityset.Address(10).String(), @@ -451,9 +453,10 @@ func TestProtocol_HandleCandidateRegister(t *testing.T) { iotextypes.ReceiptStatus_ErrCandidateConflict, }, // act.OwnerAddress() is nil,existing owner, but selfstake is not 0 - {1201000, + { + 1201000, identityset.Address(30), - uint64(10), + 1, "test2", identityset.Address(28).String(), identityset.Address(10).String(), @@ -473,9 +476,10 @@ func TestProtocol_HandleCandidateRegister(t *testing.T) { // act.OwnerAddress() is nil,existing candidate, collide with existing name,this case cannot happen,b/c if ownerExist,it will return ReceiptStatus_ErrCandidateAlreadyExist // act.OwnerAddress() is nil,existing candidate, collide with existing operator,this case cannot happen,b/c if ownerExist,it will return ReceiptStatus_ErrCandidateAlreadyExist // act.OwnerAddress() is nil,new candidate, collide with existing name - {1201000, + { + 1201000, identityset.Address(21), - uint64(10), + 1, "test", identityset.Address(28).String(), identityset.Address(10).String(), @@ -493,9 +497,10 @@ func TestProtocol_HandleCandidateRegister(t *testing.T) { iotextypes.ReceiptStatus_ErrCandidateConflict, }, // act.OwnerAddress() is nil,new candidate, collide with existing operator - {1201000, + { + 1201000, identityset.Address(21), - uint64(10), + 2, "test2", identityset.Address(28).String(), identityset.Address(10).String(), @@ -629,7 +634,7 @@ func TestProtocol_HandleCandidateUpdate(t *testing.T) { { 1200101, identityset.Address(27), - uint64(10), + 1, "test", identityset.Address(28).String(), identityset.Address(29).String(), @@ -653,7 +658,7 @@ func TestProtocol_HandleCandidateUpdate(t *testing.T) { { 1000, identityset.Address(27), - uint64(10), + 1, "test", identityset.Address(28).String(), identityset.Address(29).String(), @@ -677,7 +682,7 @@ func TestProtocol_HandleCandidateUpdate(t *testing.T) { { 1201000, identityset.Address(27), - uint64(10), + 1, "test", identityset.Address(28).String(), identityset.Address(29).String(), @@ -701,7 +706,7 @@ func TestProtocol_HandleCandidateUpdate(t *testing.T) { { 1201000, identityset.Address(27), - uint64(10), + 1, "test", identityset.Address(29).String(), identityset.Address(30).String(), @@ -725,7 +730,7 @@ func TestProtocol_HandleCandidateUpdate(t *testing.T) { { 1201000, identityset.Address(27), - uint64(10), + 1, "test", identityset.Address(27).String(), identityset.Address(29).String(), @@ -749,7 +754,7 @@ func TestProtocol_HandleCandidateUpdate(t *testing.T) { { 1201000, identityset.Address(27), - uint64(10), + 1, "test", identityset.Address(27).String(), identityset.Address(29).String(), @@ -842,14 +847,14 @@ func TestProtocol_HandleCandidateUpdate(t *testing.T) { _, err = p.Handle(ctx, act, sm) require.NoError(err) - cu, err := action.NewCandidateUpdate(test.nonce, test.updateName, test.updateOperator, test.updateReward, test.gasLimit, test.gasPrice) + cu, err := action.NewCandidateUpdate(test.nonce+1, test.updateName, test.updateOperator, test.updateReward, test.gasLimit, test.gasPrice) require.NoError(err) intrinsic, _ = cu.IntrinsicGas() ctx = protocol.WithActionCtx(ctx, protocol.ActionCtx{ Caller: test.caller, GasPrice: test.gasPrice, IntrinsicGas: intrinsic, - Nonce: test.nonce, + Nonce: test.nonce + 1, }) ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{ BlockHeight: 1, @@ -909,7 +914,7 @@ func TestProtocol_HandleCandidateUpdate(t *testing.T) { require.NoError(err) total := big.NewInt(0) require.Equal(unit.ConvertIotxToRau(test.initBalance), total.Add(total, caller.Balance).Add(total, actCost).Add(total, cuCost).Add(total, p.config.RegistrationConsts.Fee)) - require.Equal(test.nonce+1, caller.PendingNonce()) + require.Equal(test.nonce+2, caller.PendingNonce()) } } } @@ -1077,15 +1082,22 @@ func TestProtocol_HandleUnstake(t *testing.T) { var createCost *big.Int ctx, createCost = initCreateStake(t, sm, test.caller, test.initBalance, big.NewInt(unit.Qev), gasLimit, nonce, blkHeight, test.blkTimestamp, gasLimit, p, candidate, test.amount, test.autoStake) - act, err := action.NewUnstake(nonce, test.index, - nil, gasLimit, gasPrice) + act, err := action.NewUnstake(nonce+1, test.index, nil, gasLimit, gasPrice) require.NoError(err) if test.blkTimestamp != test.ctxTimestamp { blkCtx := protocol.MustGetBlockCtx(ctx) blkCtx.BlockTimeStamp = test.ctxTimestamp ctx = protocol.WithBlockCtx(ctx, blkCtx) } + intrinsicGas, err := act.IntrinsicGas() + require.NoError(err) ctx = protocol.WithFeatureCtx(protocol.WithFeatureWithHeightCtx(ctx)) + ctx = protocol.WithActionCtx(ctx, protocol.ActionCtx{ + Caller: test.caller, + GasPrice: gasPrice, + IntrinsicGas: intrinsicGas, + Nonce: nonce + 1, + }) var r *action.Receipt if test.clear { csm, err := NewCandidateStateManager(sm, false) @@ -1137,7 +1149,7 @@ func TestProtocol_HandleUnstake(t *testing.T) { require.NoError(err) actCost, err := act.Cost() require.NoError(err) - require.Equal(nonce+1, caller.PendingNonce()) + require.Equal(nonce+2, caller.PendingNonce()) total := big.NewInt(0) require.Equal(unit.ConvertIotxToRau(test.initBalance), total.Add(total, caller.Balance).Add(total, actCost).Add(total, createCost)) } @@ -1174,7 +1186,7 @@ func TestProtocol_HandleUnstake(t *testing.T) { {restake, false, iotextypes.ReceiptStatus_ErrNotEnoughBalance}, } - for _, v := range unstakedBucketTests { + for i, v := range unstakedBucketTests { greenland := genesis.Default if v.greenland { blkCtx := protocol.MustGetBlockCtx(ctx) @@ -1182,6 +1194,12 @@ func TestProtocol_HandleUnstake(t *testing.T) { } ctx = genesis.WithGenesisContext(ctx, greenland) ctx = protocol.WithFeatureCtx(protocol.WithFeatureWithHeightCtx(ctx)) + ctx = protocol.WithFeatureCtx(protocol.WithFeatureWithHeightCtx(ctx)) + ctx = protocol.WithActionCtx(ctx, protocol.ActionCtx{ + Caller: callerAddr, + GasPrice: gasPrice, + Nonce: nonce + 2 + uint64(i), + }) _, err = p.Start(ctx, sm) require.NoError(err) r, err := p.Handle(ctx, v.act, sm) @@ -1581,7 +1599,7 @@ func TestProtocol_HandleChangeCandidate(t *testing.T) { // candidate vote self,index 1 _, createCost := initCreateStake(t, sm, candidate.Owner, test.initBalance, big.NewInt(unit.Qev), test.gasLimit, test.nonce, test.blkHeight, test.blkTimestamp, test.blkGasLimit, p, candidate, test.amount, false) - act, err := action.NewChangeCandidate(test.nonce, test.candidateName, test.index, nil, test.gasLimit, test.gasPrice) + act, err := action.NewChangeCandidate(test.nonce+1, test.candidateName, test.index, nil, test.gasLimit, test.gasPrice) require.NoError(err) intrinsic, err := act.IntrinsicGas() require.NoError(err) @@ -1589,7 +1607,7 @@ func TestProtocol_HandleChangeCandidate(t *testing.T) { Caller: test.caller, GasPrice: test.gasPrice, IntrinsicGas: intrinsic, - Nonce: test.nonce, + Nonce: test.nonce + 1, }) ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{ BlockHeight: test.blkHeight, @@ -1658,7 +1676,7 @@ func TestProtocol_HandleChangeCandidate(t *testing.T) { require.NoError(err) actCost, err := act.Cost() require.NoError(err) - require.Equal(test.nonce+1, caller.PendingNonce()) + require.Equal(test.nonce+2, caller.PendingNonce()) total := big.NewInt(0) require.Equal(unit.ConvertIotxToRau(test.initBalance), total.Add(total, caller.Balance).Add(total, actCost).Add(total, createCost)) } @@ -1798,7 +1816,7 @@ func TestProtocol_HandleTransferStake(t *testing.T) { require.NoError(setupAccount(sm, identityset.Address(1), 1)) } - act, err := action.NewTransferStake(test.nonce, test.to.String(), test.index, nil, test.gasLimit, test.gasPrice) + act, err := action.NewTransferStake(test.nonce+1, test.to.String(), test.index, nil, test.gasLimit, test.gasPrice) require.NoError(err) intrinsic, err := act.IntrinsicGas() require.NoError(err) @@ -1807,7 +1825,7 @@ func TestProtocol_HandleTransferStake(t *testing.T) { Caller: test.caller, GasPrice: test.gasPrice, IntrinsicGas: intrinsic, - Nonce: test.nonce, + Nonce: test.nonce + 1, }) ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{ BlockHeight: test.blkHeight, @@ -1859,7 +1877,7 @@ func TestProtocol_HandleTransferStake(t *testing.T) { require.NoError(err) actCost, err := act.Cost() require.NoError(err) - require.Equal(test.nonce+1, caller.PendingNonce()) + require.Equal(test.nonce+2, caller.PendingNonce()) total := big.NewInt(0) require.Equal(unit.ConvertIotxToRau(test.initBalance), total.Add(total, caller.Balance).Add(total, actCost).Add(total, createCost)) } @@ -2284,15 +2302,19 @@ func TestProtocol_HandleRestake(t *testing.T) { for _, test := range tests { sm, p, candidate, candidate2 := initAll(t, ctrl) csr := newCandidateStateReader(sm) - ctx, createCost := initCreateStake(t, sm, candidate2.Owner, test.initBalance, big.NewInt(unit.Qev), 10000, 1, 1, time.Now(), 10000, p, candidate2, test.amount, test.autoStake) + ctx, createCost := initCreateStake(t, sm, candidate2.Owner, test.initBalance, big.NewInt(unit.Qev), 10000, test.nonce, 1, time.Now(), 10000, p, candidate2, test.amount, test.autoStake) if test.newAccount { require.NoError(setupAccount(sm, test.caller, test.initBalance)) } else { candidate = candidate2 } + nonce := test.nonce + if test.caller.String() == candidate2.Owner.String() { + nonce++ + } - act, err := action.NewRestake(test.nonce, test.index, test.duration, test.autoStake, nil, test.gasLimit, test.gasPrice) + act, err := action.NewRestake(nonce, test.index, test.duration, test.autoStake, nil, test.gasLimit, test.gasPrice) require.NoError(err) intrinsic, err := act.IntrinsicGas() require.NoError(err) @@ -2300,7 +2322,7 @@ func TestProtocol_HandleRestake(t *testing.T) { Caller: test.caller, GasPrice: test.gasPrice, IntrinsicGas: intrinsic, - Nonce: test.nonce, + Nonce: nonce, }) ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{ BlockHeight: 1, @@ -2358,7 +2380,7 @@ func TestProtocol_HandleRestake(t *testing.T) { require.NoError(err) actCost, err := act.Cost() require.NoError(err) - require.Equal(test.nonce+1, caller.PendingNonce()) + require.Equal(test.nonce+2, caller.PendingNonce()) total := big.NewInt(0) require.Equal(unit.ConvertIotxToRau(test.initBalance), total.Add(total, caller.Balance).Add(total, actCost).Add(total, createCost)) } @@ -2504,7 +2526,11 @@ func TestProtocol_HandleDepositToStake(t *testing.T) { require.NoError(setupAccount(sm, test.caller, test.initBalance)) } - act, err := action.NewDepositToStake(test.nonce, test.index, test.amount, nil, test.gasLimit, test.gasPrice) + nonce := test.nonce + if test.caller.String() == candidate.Owner.String() { + nonce++ + } + act, err := action.NewDepositToStake(nonce, test.index, test.amount, nil, test.gasLimit, test.gasPrice) require.NoError(err) intrinsic, err := act.IntrinsicGas() require.NoError(err) @@ -2512,7 +2538,7 @@ func TestProtocol_HandleDepositToStake(t *testing.T) { Caller: test.caller, GasPrice: test.gasPrice, IntrinsicGas: intrinsic, - Nonce: test.nonce, + Nonce: nonce, }) ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{ BlockHeight: 1, @@ -2580,7 +2606,7 @@ func TestProtocol_HandleDepositToStake(t *testing.T) { require.NoError(err) actCost, err := act.Cost() require.NoError(err) - require.Equal(test.nonce+1, caller.PendingNonce()) + require.Equal(test.nonce+2, caller.PendingNonce()) total := big.NewInt(0) require.Equal(unit.ConvertIotxToRau(test.initBalance), total.Add(total, caller.Balance).Add(total, actCost).Add(total, createCost)) } diff --git a/actpool/actpool_test.go b/actpool/actpool_test.go index 01fb2b1644..65e5160571 100644 --- a/actpool/actpool_test.go +++ b/actpool/actpool_test.go @@ -406,7 +406,9 @@ func TestActPool_removeConfirmedActs(t *testing.T) { sf.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn(func(account interface{}, opts ...protocol.StateOption) (uint64, error) { acct, ok := account.(*state.Account) require.True(ok) - require.NoError(acct.SetNonce(4)) + for i := uint64(1); i <= 4; i++ { + require.NoError(acct.SetNonce(i)) + } require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil @@ -439,19 +441,29 @@ func TestActPool_Reset(t *testing.T) { switch { case bytes.Equal(cfg.Key, identityset.Address(28).Bytes()): require.NoError(acct.AddBalance(new(big.Int).Set(balances[0]))) - require.NoError(acct.SetNonce(nonces[0])) + for i := uint64(1); i <= nonces[0]; i++ { + require.NoError(acct.SetNonce(i)) + } case bytes.Equal(cfg.Key, identityset.Address(29).Bytes()): require.NoError(acct.AddBalance(new(big.Int).Set(balances[1]))) - require.NoError(acct.SetNonce(nonces[1])) + for i := uint64(1); i <= nonces[1]; i++ { + require.NoError(acct.SetNonce(i)) + } case bytes.Equal(cfg.Key, identityset.Address(30).Bytes()): require.NoError(acct.AddBalance(new(big.Int).Set(balances[2]))) - require.NoError(acct.SetNonce(nonces[2])) + for i := uint64(1); i <= nonces[2]; i++ { + require.NoError(acct.SetNonce(i)) + } case bytes.Equal(cfg.Key, identityset.Address(31).Bytes()): require.NoError(acct.AddBalance(new(big.Int).Set(balances[3]))) - require.NoError(acct.SetNonce(nonces[3])) + for i := uint64(1); i <= nonces[3]; i++ { + require.NoError(acct.SetNonce(i)) + } case bytes.Equal(cfg.Key, identityset.Address(32).Bytes()): require.NoError(acct.AddBalance(new(big.Int).Set(balances[4]))) - require.NoError(acct.SetNonce(nonces[4])) + for i := uint64(1); i <= nonces[4]; i++ { + require.NoError(acct.SetNonce(i)) + } } return 0, nil }).AnyTimes() @@ -995,7 +1007,9 @@ func TestActPool_GetSize(t *testing.T) { sf.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn(func(account interface{}, opts ...protocol.StateOption) (uint64, error) { acct, ok := account.(*state.Account) require.True(ok) - require.NoError(acct.SetNonce(4)) + for i := uint64(1); i <= 4; i++ { + require.NoError(acct.SetNonce(i)) + } require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil diff --git a/state/account.go b/state/account.go index d5a5981fbc..0bf0cc3194 100644 --- a/state/account.go +++ b/state/account.go @@ -23,6 +23,8 @@ var ( ErrInvalidAmount = errors.New("invalid amount") // ErrAccountCollision is the error that the account already exists ErrAccountCollision = errors.New("account already exists") + // ErrInvalidNonce is the error that the nonce to set is invalid + ErrInvalidNonce = errors.New("invalid nonce") ) // DelegateCandidateOption is an option to create a delegate candidate account @@ -110,6 +112,9 @@ func (st *Account) Deserialize(buf []byte) error { // SetNonce sets the nonce of the account func (st *Account) SetNonce(nonce uint64) error { + if nonce != st.nonce+1 { + return errors.Wrapf(ErrInvalidNonce, "actual value %d, %d expected", nonce, st.nonce+1) + } st.nonce = nonce return nil } diff --git a/state/factory/factory_test.go b/state/factory/factory_test.go index b65d51d4dd..feb8c7db22 100644 --- a/state/factory/factory_test.go +++ b/state/factory/factory_test.go @@ -447,7 +447,7 @@ func testState(sf Factory, t *testing.T) { tsf, err := action.NewTransfer(1, big.NewInt(10), identityset.Address(31).String(), nil, uint64(20000), big.NewInt(0)) require.NoError(t, err) bd := &action.EnvelopeBuilder{} - elp := bd.SetAction(tsf).SetGasLimit(20000).Build() + elp := bd.SetAction(tsf).SetGasLimit(20000).SetNonce(1).Build() selp, err := action.Sign(elp, priKeyA) require.NoError(t, err) ctx = protocol.WithBlockCtx( @@ -511,7 +511,7 @@ func testHistoryState(sf Factory, t *testing.T, statetx, archive bool) { tsf, err := action.NewTransfer(1, big.NewInt(10), b.String(), nil, uint64(20000), big.NewInt(0)) require.NoError(t, err) bd := &action.EnvelopeBuilder{} - elp := bd.SetAction(tsf).SetGasLimit(20000).Build() + elp := bd.SetAction(tsf).SetGasLimit(20000).SetNonce(1).Build() selp, err := action.Sign(elp, priKeyA) require.NoError(t, err) ctx = protocol.WithBlockCtx( @@ -595,7 +595,7 @@ func testFactoryStates(sf Factory, t *testing.T) { tsf, err := action.NewTransfer(1, big.NewInt(10), b, nil, uint64(20000), big.NewInt(0)) require.NoError(t, err) bd := &action.EnvelopeBuilder{} - elp := bd.SetAction(tsf).SetGasLimit(20000).Build() + elp := bd.SetAction(tsf).SetGasLimit(20000).SetNonce(1).Build() selp, err := action.Sign(elp, priKeyA) require.NoError(t, err) ctx = protocol.WithBlockCtx( From 2bae9700065822d6979892ffd95f26e4b7cde8e8 Mon Sep 17 00:00:00 2001 From: zhi Date: Thu, 12 May 2022 19:51:07 -0700 Subject: [PATCH 06/11] account type --- .../protocol/account/accountpb/account.pb.go | 104 +++++++++++++++--- .../protocol/account/accountpb/account.proto | 10 +- action/protocol/account/protocol.go | 12 +- action/protocol/account/protocol_test.go | 2 +- action/protocol/account/transfer.go | 22 ++-- action/protocol/context.go | 2 + action/protocol/execution/evm/evm.go | 3 + .../execution/evm/evmstatedbadapter.go | 60 +++++++--- .../protocol/poll/governance_protocol_test.go | 1 + .../protocol/poll/lifelong_protocol_test.go | 1 + .../protocol/poll/staking_committee_test.go | 1 + action/protocol/poll/util.go | 6 +- action/protocol/rewarding/fund.go | 7 +- action/protocol/rewarding/protocol.go | 10 +- action/protocol/rewarding/reward.go | 7 +- action/protocol/staking/handlers.go | 7 +- action/protocol/staking/handlers_test.go | 51 +++++---- action/protocol/staking/protocol.go | 6 +- go.mod | 2 +- go.sum | 4 +- state/account.go | 51 ++++++++- state/factory/factory_test.go | 4 +- 22 files changed, 283 insertions(+), 90 deletions(-) diff --git a/action/protocol/account/accountpb/account.pb.go b/action/protocol/account/accountpb/account.pb.go index 47cb4051b1..762b79fa8c 100644 --- a/action/protocol/account/accountpb/account.pb.go +++ b/action/protocol/account/accountpb/account.pb.go @@ -1,4 +1,4 @@ -// Copyright (c) 2018 IoTeX +// Copyright (c) 2022 IoTeX // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache @@ -9,8 +9,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.23.0 -// protoc v3.12.4 +// protoc-gen-go v1.26.0 +// protoc v3.20.1 // source: account.proto package accountpb @@ -29,18 +29,65 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type AccountType int32 + +const ( + AccountType_DEFAULT AccountType = 0 + AccountType_ZERO_NONCE AccountType = 1 +) + +// Enum value maps for AccountType. +var ( + AccountType_name = map[int32]string{ + 0: "DEFAULT", + 1: "ZERO_NONCE", + } + AccountType_value = map[string]int32{ + "DEFAULT": 0, + "ZERO_NONCE": 1, + } +) + +func (x AccountType) Enum() *AccountType { + p := new(AccountType) + *p = x + return p +} + +func (x AccountType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (AccountType) Descriptor() protoreflect.EnumDescriptor { + return file_account_proto_enumTypes[0].Descriptor() +} + +func (AccountType) Type() protoreflect.EnumType { + return &file_account_proto_enumTypes[0] +} + +func (x AccountType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use AccountType.Descriptor instead. +func (AccountType) EnumDescriptor() ([]byte, []int) { + return file_account_proto_rawDescGZIP(), []int{0} +} + type Account struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // used by state-based model - Nonce uint64 `protobuf:"varint,1,opt,name=nonce,proto3" json:"nonce,omitempty"` - Balance string `protobuf:"bytes,2,opt,name=balance,proto3" json:"balance,omitempty"` - Root []byte `protobuf:"bytes,3,opt,name=root,proto3" json:"root,omitempty"` - CodeHash []byte `protobuf:"bytes,4,opt,name=codeHash,proto3" json:"codeHash,omitempty"` - IsCandidate bool `protobuf:"varint,5,opt,name=isCandidate,proto3" json:"isCandidate,omitempty"` - VotingWeight []byte `protobuf:"bytes,6,opt,name=votingWeight,proto3" json:"votingWeight,omitempty"` + Nonce uint64 `protobuf:"varint,1,opt,name=nonce,proto3" json:"nonce,omitempty"` + Balance string `protobuf:"bytes,2,opt,name=balance,proto3" json:"balance,omitempty"` + Root []byte `protobuf:"bytes,3,opt,name=root,proto3" json:"root,omitempty"` + CodeHash []byte `protobuf:"bytes,4,opt,name=codeHash,proto3" json:"codeHash,omitempty"` + IsCandidate bool `protobuf:"varint,5,opt,name=isCandidate,proto3" json:"isCandidate,omitempty"` + VotingWeight []byte `protobuf:"bytes,6,opt,name=votingWeight,proto3" json:"votingWeight,omitempty"` + Type AccountType `protobuf:"varint,7,opt,name=type,proto3,enum=accountpb.AccountType" json:"type,omitempty"` } func (x *Account) Reset() { @@ -117,11 +164,18 @@ func (x *Account) GetVotingWeight() []byte { return nil } +func (x *Account) GetType() AccountType { + if x != nil { + return x.Type + } + return AccountType_DEFAULT +} + var File_account_proto protoreflect.FileDescriptor var file_account_proto_rawDesc = []byte{ 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, - 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x70, 0x62, 0x22, 0xaf, 0x01, 0x0a, 0x07, 0x41, + 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x70, 0x62, 0x22, 0xdb, 0x01, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, @@ -132,7 +186,17 @@ var file_account_proto_rawDesc = []byte{ 0x69, 0x64, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x76, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, - 0x76, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x62, 0x06, 0x70, 0x72, + 0x76, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2a, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x61, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a, 0x2a, 0x0a, 0x0b, 0x41, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, + 0x4c, 0x54, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x4e, 0x4f, 0x4e, + 0x43, 0x45, 0x10, 0x01, 0x42, 0x46, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x69, 0x6f, 0x74, 0x65, 0x78, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, + 0x69, 0x6f, 0x74, 0x65, 0x78, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } @@ -148,16 +212,19 @@ func file_account_proto_rawDescGZIP() []byte { return file_account_proto_rawDescData } +var file_account_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_account_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_account_proto_goTypes = []interface{}{ - (*Account)(nil), // 0: accountpb.Account + (AccountType)(0), // 0: accountpb.AccountType + (*Account)(nil), // 1: accountpb.Account } var file_account_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 0, // 0: accountpb.Account.type:type_name -> accountpb.AccountType + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name } func init() { file_account_proto_init() } @@ -184,13 +251,14 @@ func file_account_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_account_proto_rawDesc, - NumEnums: 0, + NumEnums: 1, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_account_proto_goTypes, DependencyIndexes: file_account_proto_depIdxs, + EnumInfos: file_account_proto_enumTypes, MessageInfos: file_account_proto_msgTypes, }.Build() File_account_proto = out.File diff --git a/action/protocol/account/accountpb/account.proto b/action/protocol/account/accountpb/account.proto index 9d4dff064f..aedfb4d458 100644 --- a/action/protocol/account/accountpb/account.proto +++ b/action/protocol/account/accountpb/account.proto @@ -1,4 +1,4 @@ -// Copyright (c) 2018 IoTeX +// Copyright (c) 2022 IoTeX // This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no // warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent // permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache @@ -8,6 +8,12 @@ // protoc --go_out=plugins=grpc:. *.proto syntax = "proto3"; package accountpb; +option go_package = "github.com/iotexproject/iotex-core/action/protocol/account/accountpb"; + +enum AccountType { + DEFAULT = 0; + ZERO_NONCE = 1; +} message Account { // used by state-based model @@ -17,5 +23,5 @@ message Account { bytes codeHash = 4; bool isCandidate = 5; bytes votingWeight = 6; + AccountType type = 7; } - diff --git a/action/protocol/account/protocol.go b/action/protocol/account/protocol.go index 30b535b323..e0460794f8 100644 --- a/action/protocol/account/protocol.go +++ b/action/protocol/account/protocol.go @@ -107,7 +107,7 @@ func (p *Protocol) Name() string { return protocolID } -func createAccount(sm protocol.StateManager, encodedAddr string, init *big.Int) error { +func createAccount(sm protocol.StateManager, encodedAddr string, init *big.Int, opts ...state.AccountCreationOption) error { account := state.NewEmptyAccount() addr, err := address.FromString(encodedAddr) if err != nil { @@ -119,6 +119,10 @@ func createAccount(sm protocol.StateManager, encodedAddr string, init *big.Int) case nil: return errors.Errorf("failed to create account %s", encodedAddr) case state.ErrStateNotExist: + account, err := state.NewAccount(opts...) + if err != nil { + return err + } if err := account.AddBalance(init); err != nil { return errors.Wrapf(err, "failed to add balance %s", init) } @@ -144,8 +148,12 @@ func (p *Protocol) CreateGenesisStates(ctx context.Context, sm protocol.StateMan if err := p.assertAmounts(amounts); err != nil { return err } + opts := []state.AccountCreationOption{} + if protocol.MustGetFeatureCtx(ctx).CreateZeroNonceAccount { + opts = append(opts, state.ZeroNonceAccountTypeOption()) + } for i, addr := range addrs { - if err := createAccount(sm, addr.String(), amounts[i]); err != nil { + if err := createAccount(sm, addr.String(), amounts[i], opts...); err != nil { return err } } diff --git a/action/protocol/account/protocol_test.go b/action/protocol/account/protocol_test.go index 81c1b4907f..9f81554062 100644 --- a/action/protocol/account/protocol_test.go +++ b/action/protocol/account/protocol_test.go @@ -109,7 +109,7 @@ func TestProtocol_Initialize(t *testing.T) { ctx := protocol.WithBlockCtx(context.Background(), protocol.BlockCtx{ BlockHeight: 0, }) - ctx = genesis.WithGenesisContext(ctx, ge) + ctx = protocol.WithFeatureCtx(genesis.WithGenesisContext(ctx, ge)) p := NewProtocol(rewarding.DepositGas) require.NoError( p.CreateGenesisStates( diff --git a/action/protocol/account/transfer.go b/action/protocol/account/transfer.go index 00fa152e74..bda9adde13 100644 --- a/action/protocol/account/transfer.go +++ b/action/protocol/account/transfer.go @@ -25,14 +25,17 @@ const TransferSizeLimit = 32 * 1024 // handleTransfer handles a transfer func (p *Protocol) handleTransfer(ctx context.Context, act action.Action, sm protocol.StateManager) (*action.Receipt, error) { - actionCtx := protocol.MustGetActionCtx(ctx) - blkCtx := protocol.MustGetBlockCtx(ctx) tsf, ok := act.(*action.Transfer) if !ok { return nil, nil } + accountCreationOpts := []state.AccountCreationOption{} + if protocol.MustGetFeatureCtx(ctx).CreateZeroNonceAccount { + accountCreationOpts = append(accountCreationOpts, state.ZeroNonceAccountTypeOption()) + } + actionCtx := protocol.MustGetActionCtx(ctx) // check sender - sender, err := accountutil.LoadOrCreateAccount(sm, actionCtx.Caller) + sender, err := accountutil.LoadOrCreateAccount(sm, actionCtx.Caller, accountCreationOpts...) if err != nil { return nil, errors.Wrapf(err, "failed to load or create the account of sender %s", actionCtx.Caller.String()) } @@ -74,11 +77,14 @@ func (p *Protocol) handleTransfer(ctx context.Context, act action.Action, sm pro if err != nil { return nil, errors.Wrapf(err, "failed to decode recipient address %s", tsf.Recipient()) } - recipientAcct, err := accountutil.LoadAccount(sm, recipientAddr) - if err != nil { - return nil, errors.Wrapf(err, "failed to load address %s", tsf.Recipient()) + recipientAcct, err := accountutil.LoadAccount(sm, recipientAddr, accountCreationOpts...) + if !fCtx.TolerateLegacyAddress { + if err != nil { + return nil, errors.Wrapf(err, "failed to load address %s", tsf.Recipient()) + } } - if recipientAcct.IsContract() { + blkCtx := protocol.MustGetBlockCtx(ctx) + if err == nil && recipientAcct.IsContract() { // update sender Nonce if err := sender.SetNonce(tsf.Nonce()); err != nil { return nil, errors.Wrapf(err, "failed to update pending nonce of sender %s", actionCtx.Caller.String()) @@ -119,7 +125,7 @@ func (p *Protocol) handleTransfer(ctx context.Context, act action.Action, sm pro return nil, errors.Wrap(err, "failed to update pending account changes to trie") } // check recipient - recipient, err := accountutil.LoadOrCreateAccount(sm, recipientAddr) + recipient, err := accountutil.LoadOrCreateAccount(sm, recipientAddr, accountCreationOpts...) if err != nil { return nil, errors.Wrapf(err, "failed to load or create the account of recipient %s", tsf.Recipient()) } diff --git a/action/protocol/context.go b/action/protocol/context.go index 6189daf5cb..bad3468769 100644 --- a/action/protocol/context.go +++ b/action/protocol/context.go @@ -105,6 +105,7 @@ type ( RevertLog bool TolerateLegacyAddress bool ValidateRewardProtocol bool + CreateZeroNonceAccount bool SkipUpdateForSystemAction bool } @@ -235,6 +236,7 @@ func WithFeatureCtx(ctx context.Context) context.Context { RevertLog: g.IsMidway(height), TolerateLegacyAddress: !g.IsNewfoundland(height), ValidateRewardProtocol: g.IsNewfoundland(height), + CreateZeroNonceAccount: g.IsToBeEnabled(height), SkipUpdateForSystemAction: g.IsToBeEnabled(height), }, ) diff --git a/action/protocol/execution/evm/evm.go b/action/protocol/execution/evm/evm.go index 1ab9cdf2fe..61f317f9cb 100644 --- a/action/protocol/execution/evm/evm.go +++ b/action/protocol/execution/evm/evm.go @@ -290,6 +290,9 @@ func prepareStateDB(ctx context.Context, sm protocol.StateManager) *StateDBAdapt blkCtx := protocol.MustGetBlockCtx(ctx) featureCtx := protocol.MustGetFeatureCtx(ctx) opts := []StateDBAdapterOption{} + if featureCtx.CreateZeroNonceAccount { + opts = append(opts, ZeroNonceAccountOption()) + } if featureCtx.UsePendingNonceOption { opts = append(opts, SortCachedContractsOption(), UsePendingNonceOption()) } diff --git a/action/protocol/execution/evm/evmstatedbadapter.go b/action/protocol/execution/evm/evmstatedbadapter.go index 73001dcd80..3f32367e25 100644 --- a/action/protocol/execution/evm/evmstatedbadapter.go +++ b/action/protocol/execution/evm/evmstatedbadapter.go @@ -70,6 +70,7 @@ type ( asyncContractTrie bool sortCachedContracts bool usePendingNonce bool + zeroNonceAccount bool fixSnapshotOrder bool revertLog bool } @@ -110,6 +111,14 @@ func UsePendingNonceOption() StateDBAdapterOption { } } +// ZeroNonceAccountOption set fixPendingNonce as true +func ZeroNonceAccountOption() StateDBAdapterOption { + return func(adapter *StateDBAdapter) error { + adapter.zeroNonceAccount = true + return nil + } +} + // FixSnapshotOrderOption set fixSnapshotOrder as true func FixSnapshotOrderOption() StateDBAdapterOption { return func(adapter *StateDBAdapter) error { @@ -169,6 +178,13 @@ func (stateDB *StateDBAdapter) Error() error { return stateDB.err } +func (stateDB *StateDBAdapter) accountCreationOpts() []state.AccountCreationOption { + if stateDB.zeroNonceAccount { + return []state.AccountCreationOption{state.ZeroNonceAccountTypeOption()} + } + return nil +} + // CreateAccount creates an account in iotx blockchain func (stateDB *StateDBAdapter) CreateAccount(evmAddr common.Address) { addr, err := address.FromBytes(evmAddr.Bytes()) @@ -176,7 +192,7 @@ func (stateDB *StateDBAdapter) CreateAccount(evmAddr common.Address) { log.L().Error("Failed to convert evm address.", zap.Error(err)) return } - _, err = accountutil.LoadOrCreateAccount(stateDB.sm, addr) + _, err = accountutil.LoadOrCreateAccount(stateDB.sm, addr, stateDB.accountCreationOpts()...) if err != nil { log.L().Error("Failed to create account.", zap.Error(err)) stateDB.logError(err) @@ -232,7 +248,7 @@ func (stateDB *StateDBAdapter) AddBalance(evmAddr common.Address, amount *big.In if contract, ok := stateDB.cachedContract[addrHash]; ok { state = contract.SelfState() } else { - state, err = accountutil.LoadOrCreateAccount(stateDB.sm, addr) + state, err = accountutil.LoadOrCreateAccount(stateDB.sm, addr, stateDB.accountCreationOpts()...) if err != nil { log.L().Error("Failed to add balance.", log.Hex("addrHash", evmAddr[:])) stateDB.logError(err) @@ -267,12 +283,20 @@ func (stateDB *StateDBAdapter) GetBalance(evmAddr common.Address) *big.Int { return state.Balance } -// InitNonce returns the init nonce of an account -func (stateDB *StateDBAdapter) InitNonce() uint64 { - if stateDB.usePendingNonce { - return 1 +// IsNewAccount returns true if this is a new account +func (stateDB *StateDBAdapter) IsNewAccount(evmAddr common.Address) bool { + addr, err := address.FromBytes(evmAddr.Bytes()) + if err != nil { + log.L().Error("Failed to convert evm address.", zap.Error(err)) + return false + } + state, err := stateDB.AccountState(addr.String()) + if err != nil { + log.L().Error("failed to load account.", zap.Error(err), zap.String("address", addr.String())) + return false } - return 0 + + return state.IsNewbieAccount() } // GetNonce gets the nonce of account @@ -282,7 +306,12 @@ func (stateDB *StateDBAdapter) GetNonce(evmAddr common.Address) uint64 { log.L().Error("Failed to convert evm address.", zap.Error(err)) return 0 } - pendingNonce := uint64(1) + var pendingNonce uint64 + if stateDB.zeroNonceAccount { + pendingNonce = uint64(0) + } else { + pendingNonce = uint64(1) + } state, err := stateDB.AccountState(addr.String()) if err != nil { log.L().Error("Failed to get nonce.", zap.Error(err)) @@ -326,10 +355,11 @@ func (stateDB *StateDBAdapter) SetNonce(evmAddr common.Address, nonce uint64) { zap.String("address", addr.String()), zap.Uint64("nonce", nonce)) if err := s.SetNonce(nonce); err != nil { - return + log.L().Error("Failed to set nonce.", zap.Error(err)) + stateDB.logError(err) } if err := accountutil.StoreAccount(stateDB.sm, addr, s); err != nil { - log.L().Error("Failed to set nonce.", zap.Error(err)) + log.L().Error("Failed to store account.", zap.Error(err)) stateDB.logError(err) } } @@ -477,7 +507,7 @@ func (stateDB *StateDBAdapter) Empty(evmAddr common.Address) bool { return true } // TODO: delete hash.ZeroHash256 - return s.PendingNonce() == 1 && + return s.IsNewbieAccount() && s.Balance.Sign() == 0 && (len(s.CodeHash) == 0 || bytes.Equal(s.CodeHash, hash.ZeroHash256[:])) } @@ -740,7 +770,7 @@ func (stateDB *StateDBAdapter) AccountState(encodedAddr string) (*state.Account, if contract, ok := stateDB.cachedContract[addrHash]; ok { return contract.SelfState(), nil } - return accountutil.LoadAccountByHash160(stateDB.sm, addrHash) + return accountutil.LoadAccountByHash160(stateDB.sm, addrHash, stateDB.accountCreationOpts()...) } //====================================== @@ -755,7 +785,7 @@ func (stateDB *StateDBAdapter) GetCodeHash(evmAddr common.Address) common.Hash { copy(codeHash[:], contract.SelfState().CodeHash) return codeHash } - account, err := accountutil.LoadAccountByHash160(stateDB.sm, addr) + account, err := accountutil.LoadAccountByHash160(stateDB.sm, addr, stateDB.accountCreationOpts()...) if err != nil { log.L().Error("Failed to get code hash.", zap.Error(err)) // TODO (zhi) not all err should be logged @@ -777,7 +807,7 @@ func (stateDB *StateDBAdapter) GetCode(evmAddr common.Address) []byte { } return code } - account, err := accountutil.LoadAccountByHash160(stateDB.sm, addr) + account, err := accountutil.LoadAccountByHash160(stateDB.sm, addr, stateDB.accountCreationOpts()...) if err != nil { log.L().Error("Failed to load account state for address.", log.Hex("addrHash", addr[:])) return nil @@ -947,7 +977,7 @@ func (stateDB *StateDBAdapter) getContract(addr hash.Hash160) (Contract, error) } func (stateDB *StateDBAdapter) getNewContract(addr hash.Hash160) (Contract, error) { - account, err := accountutil.LoadAccountByHash160(stateDB.sm, addr) + account, err := accountutil.LoadAccountByHash160(stateDB.sm, addr, stateDB.accountCreationOpts()...) if err != nil { return nil, errors.Wrapf(err, "failed to load account state for address %x", addr) } diff --git a/action/protocol/poll/governance_protocol_test.go b/action/protocol/poll/governance_protocol_test.go index 1b701dd724..c986e1a94e 100644 --- a/action/protocol/poll/governance_protocol_test.go +++ b/action/protocol/poll/governance_protocol_test.go @@ -68,6 +68,7 @@ func initConstruct(ctrl *gomock.Controller) (Protocol, context.Context, protocol ctx, protocol.ActionCtx{}, ) + ctx = protocol.WithFeatureCtx(ctx) sm := mock_chainmanager.NewMockStateManager(ctrl) committee := mock_committee.NewMockCommittee(ctrl) diff --git a/action/protocol/poll/lifelong_protocol_test.go b/action/protocol/poll/lifelong_protocol_test.go index ec2285c0be..9bfce501ac 100644 --- a/action/protocol/poll/lifelong_protocol_test.go +++ b/action/protocol/poll/lifelong_protocol_test.go @@ -82,6 +82,7 @@ func TestCreateGenesisStates_WithLifeLong(t *testing.T) { require.NoError(err) ctx = protocol.WithFeatureWithHeightCtx(ctx) + ctx = protocol.WithFeatureCtx(ctx) require.NoError(p.CreateGenesisStates(ctx, sm)) } diff --git a/action/protocol/poll/staking_committee_test.go b/action/protocol/poll/staking_committee_test.go index a3434019af..fc2084bfbd 100644 --- a/action/protocol/poll/staking_committee_test.go +++ b/action/protocol/poll/staking_committee_test.go @@ -62,6 +62,7 @@ func initConstructStakingCommittee(ctrl *gomock.Controller) (Protocol, context.C Caller: producer, }, ) + ctx = protocol.WithFeatureCtx(ctx) sm := mock_chainmanager.NewMockStateManager(ctrl) committee := mock_committee.NewMockCommittee(ctrl) diff --git a/action/protocol/poll/util.go b/action/protocol/poll/util.go index 04f72ebcee..12d9b442a8 100644 --- a/action/protocol/poll/util.go +++ b/action/protocol/poll/util.go @@ -168,12 +168,16 @@ func setCandidates( return errors.New("put poll result height should be epoch start height") } loadCandidatesLegacy := featureCtx.LoadCandidatesLegacy(height) + accountCreationOpts := []state.AccountCreationOption{state.DelegateCandidateOption()} + if protocol.MustGetFeatureCtx(ctx).CreateZeroNonceAccount { + accountCreationOpts = append(accountCreationOpts, state.ZeroNonceAccountTypeOption()) + } for _, candidate := range candidates { addr, err := address.FromString(candidate.Address) if err != nil { return errors.Wrapf(err, "failed to decode delegate address %s", candidate.Address) } - delegate, err := accountutil.LoadOrCreateAccount(sm, addr, state.DelegateCandidateOption()) + delegate, err := accountutil.LoadOrCreateAccount(sm, addr, accountCreationOpts...) if err != nil { return errors.Wrapf(err, "failed to load or create the account for delegate %s", candidate.Address) } diff --git a/action/protocol/rewarding/fund.go b/action/protocol/rewarding/fund.go index ff4e185062..e10226ae5f 100644 --- a/action/protocol/rewarding/fund.go +++ b/action/protocol/rewarding/fund.go @@ -20,6 +20,7 @@ import ( "github.com/iotexproject/iotex-core/action/protocol" accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util" "github.com/iotexproject/iotex-core/action/protocol/rewarding/rewardingpb" + "github.com/iotexproject/iotex-core/state" ) // fund stores the balance of the rewarding fund. The difference between total and available balance should be @@ -65,8 +66,12 @@ func (p *Protocol) Deposit( transactionLogType iotextypes.TransactionLogType, ) (*action.TransactionLog, error) { actionCtx := protocol.MustGetActionCtx(ctx) + accountCreationOpts := []state.AccountCreationOption{} + if protocol.MustGetFeatureCtx(ctx).CreateZeroNonceAccount { + accountCreationOpts = append(accountCreationOpts, state.ZeroNonceAccountTypeOption()) + } // Subtract balance from caller - acc, err := accountutil.LoadAccount(sm, actionCtx.Caller) + acc, err := accountutil.LoadAccount(sm, actionCtx.Caller, accountCreationOpts...) if err != nil { return nil, err } diff --git a/action/protocol/rewarding/protocol.go b/action/protocol/rewarding/protocol.go index 264b9da2fa..9082bfc931 100644 --- a/action/protocol/rewarding/protocol.go +++ b/action/protocol/rewarding/protocol.go @@ -428,15 +428,19 @@ func (p *Protocol) settleAction( if depositLog != nil { tLogs = append(tLogs, depositLog) } - if err := p.increaseNonce(sm, actionCtx.Caller, actionCtx.Nonce, isSystemAction); err != nil { + if err := p.increaseNonce(ctx, sm, actionCtx.Caller, actionCtx.Nonce, isSystemAction); err != nil { return nil, err } } return p.createReceipt(status, blkCtx.BlockHeight, actionCtx.ActionHash, actionCtx.IntrinsicGas, logs, tLogs...), nil } -func (p *Protocol) increaseNonce(sm protocol.StateManager, addr address.Address, nonce uint64, isSystemAction bool) error { - acc, err := accountutil.LoadOrCreateAccount(sm, addr) +func (p *Protocol) increaseNonce(ctx context.Context, sm protocol.StateManager, addr address.Address, nonce uint64, isSystemAction bool) error { + accountCreationOpts := []state.AccountCreationOption{} + if protocol.MustGetFeatureCtx(ctx).CreateZeroNonceAccount { + accountCreationOpts = append(accountCreationOpts, state.ZeroNonceAccountTypeOption()) + } + acc, err := accountutil.LoadOrCreateAccount(sm, addr, accountCreationOpts...) if err != nil { return err } diff --git a/action/protocol/rewarding/reward.go b/action/protocol/rewarding/reward.go index be8b6fb690..7411a8fe91 100644 --- a/action/protocol/rewarding/reward.go +++ b/action/protocol/rewarding/reward.go @@ -388,9 +388,12 @@ func (p *Protocol) claimFromAccount(ctx context.Context, sm protocol.StateManage return err } } - + accountCreationOpts := []state.AccountCreationOption{} + if protocol.MustGetFeatureCtx(ctx).CreateZeroNonceAccount { + accountCreationOpts = append(accountCreationOpts, state.ZeroNonceAccountTypeOption()) + } // Update primary account - primAcc, err := accountutil.LoadOrCreateAccount(sm, addr) + primAcc, err := accountutil.LoadOrCreateAccount(sm, addr, accountCreationOpts...) if err != nil { return err } diff --git a/action/protocol/staking/handlers.go b/action/protocol/staking/handlers.go index 1f9f1e8045..9f3a7ce963 100644 --- a/action/protocol/staking/handlers.go +++ b/action/protocol/staking/handlers.go @@ -807,8 +807,11 @@ func (p *Protocol) fetchBucket( func fetchCaller(ctx context.Context, csm CandidateStateManager, amount *big.Int) (*state.Account, ReceiptError) { actionCtx := protocol.MustGetActionCtx(ctx) - - caller, err := accountutil.LoadAccount(csm.SM(), actionCtx.Caller) + accountCreationOpts := []state.AccountCreationOption{} + if protocol.MustGetFeatureCtx(ctx).CreateZeroNonceAccount { + accountCreationOpts = append(accountCreationOpts, state.ZeroNonceAccountTypeOption()) + } + caller, err := accountutil.LoadAccount(csm.SM(), actionCtx.Caller, accountCreationOpts...) if err != nil { return nil, &handleError{ err: errors.Wrapf(err, "failed to load the account of caller %s", actionCtx.Caller.String()), diff --git a/action/protocol/staking/handlers_test.go b/action/protocol/staking/handlers_test.go index 0440cd2d0d..69eee1e093 100644 --- a/action/protocol/staking/handlers_test.go +++ b/action/protocol/staking/handlers_test.go @@ -1084,13 +1084,13 @@ func TestProtocol_HandleUnstake(t *testing.T) { ctx, createCost = initCreateStake(t, sm, test.caller, test.initBalance, big.NewInt(unit.Qev), gasLimit, nonce, blkHeight, test.blkTimestamp, gasLimit, p, candidate, test.amount, test.autoStake) act, err := action.NewUnstake(nonce+1, test.index, nil, gasLimit, gasPrice) require.NoError(err) + intrinsicGas, err := act.IntrinsicGas() + require.NoError(err) if test.blkTimestamp != test.ctxTimestamp { blkCtx := protocol.MustGetBlockCtx(ctx) blkCtx.BlockTimeStamp = test.ctxTimestamp ctx = protocol.WithBlockCtx(ctx, blkCtx) } - intrinsicGas, err := act.IntrinsicGas() - require.NoError(err) ctx = protocol.WithFeatureCtx(protocol.WithFeatureWithHeightCtx(ctx)) ctx = protocol.WithActionCtx(ctx, protocol.ActionCtx{ Caller: test.caller, @@ -1160,13 +1160,13 @@ func TestProtocol_HandleUnstake(t *testing.T) { require.NoError(err) require.True(vb.isUnstaked()) - unstake, err := action.NewUnstake(nonce+1, 0, nil, gasLimit, gasPrice) + unstake, err := action.NewUnstake(nonce+2, 0, nil, gasLimit, gasPrice) require.NoError(err) - changeCand, err := action.NewChangeCandidate(nonce+1, candidate2.Name, 0, nil, gasLimit, gasPrice) + changeCand, err := action.NewChangeCandidate(nonce+2, candidate2.Name, 0, nil, gasLimit, gasPrice) require.NoError(err) - deposit, err := action.NewDepositToStake(nonce+1, 0, "10000", nil, gasLimit, gasPrice) + deposit, err := action.NewDepositToStake(nonce+2, 0, "10000", nil, gasLimit, gasPrice) require.NoError(err) - restake, err := action.NewRestake(nonce+1, 0, 0, false, nil, gasLimit, gasPrice) + restake, err := action.NewRestake(nonce+2, 0, 0, false, nil, gasLimit, gasPrice) require.NoError(err) unstakedBucketTests := []struct { @@ -1185,13 +1185,15 @@ func TestProtocol_HandleUnstake(t *testing.T) { // restake an unstaked bucket is allowed pre-Greenland {restake, false, iotextypes.ReceiptStatus_ErrNotEnoughBalance}, } - for i, v := range unstakedBucketTests { greenland := genesis.Default if v.greenland { blkCtx := protocol.MustGetBlockCtx(ctx) greenland.GreenlandBlockHeight = blkCtx.BlockHeight } + actCtx := protocol.MustGetActionCtx(ctx) + actCtx.Nonce = nonce + 2 + uint64(i) + ctx = protocol.WithActionCtx(ctx, actCtx) ctx = genesis.WithGenesisContext(ctx, greenland) ctx = protocol.WithFeatureCtx(protocol.WithFeatureWithHeightCtx(ctx)) ctx = protocol.WithFeatureCtx(protocol.WithFeatureWithHeightCtx(ctx)) @@ -1719,7 +1721,7 @@ func TestProtocol_HandleTransferStake(t *testing.T) { 0, big.NewInt(unit.Qev), 1000000000, - 1, + 2, 1, time.Now(), 10000, @@ -1775,7 +1777,7 @@ func TestProtocol_HandleTransferStake(t *testing.T) { 0, big.NewInt(unit.Qev), 10000, - 1, + 2, 1, time.Now(), 10000, @@ -1794,7 +1796,7 @@ func TestProtocol_HandleTransferStake(t *testing.T) { 0, big.NewInt(unit.Qev), 10000, - 1, + 2, genesis.Default.HawaiiBlockHeight, time.Now(), 10000, @@ -1809,14 +1811,21 @@ func TestProtocol_HandleTransferStake(t *testing.T) { for _, test := range tests { sm, p, candi, candidate2 := initAll(t, ctrl) csr := newCandidateStateReader(sm) + nonce := uint64(1) + if test.caller.String() == candidate2.Owner.String() { + nonce++ + } ctx, createCost := initCreateStake(t, sm, candidate2.Owner, test.initBalance, big.NewInt(unit.Qev), 10000, 1, 1, time.Now(), 10000, p, candidate2, test.amount, false) if test.init { initCreateStake(t, sm, candi.Owner, test.initBalance, test.gasPrice, test.gasLimit, test.nonce, test.blkHeight, test.blkTimestamp, test.blkGasLimit, p, candi, test.amount, false) + if test.caller.String() == candi.Owner.String() { + nonce++ + } } else { require.NoError(setupAccount(sm, identityset.Address(1), 1)) } - act, err := action.NewTransferStake(test.nonce+1, test.to.String(), test.index, nil, test.gasLimit, test.gasPrice) + act, err := action.NewTransferStake(nonce, test.to.String(), test.index, nil, test.gasLimit, test.gasPrice) require.NoError(err) intrinsic, err := act.IntrinsicGas() require.NoError(err) @@ -1825,7 +1834,7 @@ func TestProtocol_HandleTransferStake(t *testing.T) { Caller: test.caller, GasPrice: test.gasPrice, IntrinsicGas: intrinsic, - Nonce: test.nonce + 1, + Nonce: nonce, }) ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{ BlockHeight: test.blkHeight, @@ -1877,7 +1886,7 @@ func TestProtocol_HandleTransferStake(t *testing.T) { require.NoError(err) actCost, err := act.Cost() require.NoError(err) - require.Equal(test.nonce+2, caller.PendingNonce()) + require.Equal(test.nonce+1, caller.PendingNonce()) total := big.NewInt(0) require.Equal(unit.ConvertIotxToRau(test.initBalance), total.Add(total, caller.Balance).Add(total, actCost).Add(total, createCost)) } @@ -2425,7 +2434,7 @@ func TestProtocol_HandleDepositToStake(t *testing.T) { 0, big.NewInt(unit.Qev), 10000, - 1, + 2, 1, time.Now(), 10000, @@ -2485,7 +2494,7 @@ func TestProtocol_HandleDepositToStake(t *testing.T) { 0, big.NewInt(unit.Qev), 10000, - 1, + 2, 1, time.Now(), 10000, @@ -2505,7 +2514,7 @@ func TestProtocol_HandleDepositToStake(t *testing.T) { 0, big.NewInt(unit.Qev), 10000, - 1, + 2, 1, time.Now(), 10000, @@ -2526,11 +2535,7 @@ func TestProtocol_HandleDepositToStake(t *testing.T) { require.NoError(setupAccount(sm, test.caller, test.initBalance)) } - nonce := test.nonce - if test.caller.String() == candidate.Owner.String() { - nonce++ - } - act, err := action.NewDepositToStake(nonce, test.index, test.amount, nil, test.gasLimit, test.gasPrice) + act, err := action.NewDepositToStake(test.nonce, test.index, test.amount, nil, test.gasLimit, test.gasPrice) require.NoError(err) intrinsic, err := act.IntrinsicGas() require.NoError(err) @@ -2538,7 +2543,7 @@ func TestProtocol_HandleDepositToStake(t *testing.T) { Caller: test.caller, GasPrice: test.gasPrice, IntrinsicGas: intrinsic, - Nonce: nonce, + Nonce: test.nonce, }) ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{ BlockHeight: 1, @@ -2606,7 +2611,7 @@ func TestProtocol_HandleDepositToStake(t *testing.T) { require.NoError(err) actCost, err := act.Cost() require.NoError(err) - require.Equal(test.nonce+2, caller.PendingNonce()) + require.Equal(test.nonce+1, caller.PendingNonce()) total := big.NewInt(0) require.Equal(unit.ConvertIotxToRau(test.initBalance), total.Add(total, caller.Balance).Add(total, actCost).Add(total, createCost)) } diff --git a/action/protocol/staking/protocol.go b/action/protocol/staking/protocol.go index 784eec6de4..dfa8c86d7b 100644 --- a/action/protocol/staking/protocol.go +++ b/action/protocol/staking/protocol.go @@ -517,7 +517,11 @@ func (p *Protocol) settleAction( if err != nil { return nil, errors.Wrap(err, "failed to deposit gas") } - acc, err := accountutil.LoadAccount(sm, actionCtx.Caller) + accountCreationOpts := []state.AccountCreationOption{} + if protocol.MustGetFeatureCtx(ctx).CreateZeroNonceAccount { + accountCreationOpts = append(accountCreationOpts, state.ZeroNonceAccountTypeOption()) + } + acc, err := accountutil.LoadAccount(sm, actionCtx.Caller, accountCreationOpts...) if err != nil { return nil, err } diff --git a/go.mod b/go.mod index 073442b341..5cae8afb25 100644 --- a/go.mod +++ b/go.mod @@ -184,6 +184,6 @@ require ( gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) -replace github.com/ethereum/go-ethereum => github.com/iotexproject/go-ethereum v0.4.0 +replace github.com/ethereum/go-ethereum => github.com/iotexproject/go-ethereum v1.7.4-0.20220515212948-4dae4279da68 replace golang.org/x/xerrors => golang.org/x/xerrors v0.0.0-20190212162355-a5947ffaace3 diff --git a/go.sum b/go.sum index 08fc6e57af..3df956ea5f 100644 --- a/go.sum +++ b/go.sum @@ -438,8 +438,8 @@ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19y github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= -github.com/iotexproject/go-ethereum v0.4.0 h1:3GX+vZTI6KeazPabaw3oyLpPl1dOuyJyqqGpWspi1Gs= -github.com/iotexproject/go-ethereum v0.4.0/go.mod h1:pJNuIUYfX5+JKzSD/BTdNsvJSZ1TJqmz0dVyXMAbf6M= +github.com/iotexproject/go-ethereum v1.7.4-0.20220515212948-4dae4279da68 h1:UGqAXDT1euYeb6hQvXXKRP49f5sC4Dv/cW/CjvOC4Hc= +github.com/iotexproject/go-ethereum v1.7.4-0.20220515212948-4dae4279da68/go.mod h1:pJNuIUYfX5+JKzSD/BTdNsvJSZ1TJqmz0dVyXMAbf6M= github.com/iotexproject/go-fsm v1.0.0 h1:Zrg9JnNDUZg4Anpj6oa0Tk4+sXbHTpJzI0v5/Cj5N6A= github.com/iotexproject/go-fsm v1.0.0/go.mod h1:t3aYXtCCcQxyS7oduQZyuUpPnVI4ddFTwbAagHN7fT0= github.com/iotexproject/go-p2p v0.3.3 h1:qDGwaHu1oYG93PIcJAnY5Hl2iK0GoeAQewB0QtvhOXs= diff --git a/state/account.go b/state/account.go index 0bf0cc3194..053556c3be 100644 --- a/state/account.go +++ b/state/account.go @@ -25,8 +25,18 @@ var ( ErrAccountCollision = errors.New("account already exists") // ErrInvalidNonce is the error that the nonce to set is invalid ErrInvalidNonce = errors.New("invalid nonce") + // ErrUnknownAccountType is the error that the account type is unknown + ErrUnknownAccountType = errors.New("unknown account type") ) +// ZeroNonceAccountTypeOption is an option to create account with new account type +func ZeroNonceAccountTypeOption() AccountCreationOption { + return func(account *Account) error { + account.accountType = 1 + return nil + } +} + // DelegateCandidateOption is an option to create a delegate candidate account func DelegateCandidateOption() AccountCreationOption { return func(account *Account) error { @@ -41,7 +51,7 @@ type ( // Account is the canonical representation of an account. Account struct { - // 0 is reserved from actions in genesis block and coinbase transfers nonces + // for Type 0, nonce 0 is reserved from actions in genesis block and coinbase transfers nonces // other actions' nonces start from 1 nonce uint64 Balance *big.Int @@ -49,6 +59,7 @@ type ( CodeHash []byte // hash of the smart contract byte-code for contract account isCandidate bool votingWeight *big.Int + accountType int32 } ) @@ -56,6 +67,10 @@ type ( func (st *Account) ToProto() *accountpb.Account { acPb := &accountpb.Account{} acPb.Nonce = st.nonce + if _, ok := accountpb.AccountType_name[st.accountType]; !ok { + panic("unknown account type") + } + acPb.Type = accountpb.AccountType(st.accountType) if st.Balance != nil { acPb.Balance = st.Balance.String() } @@ -78,6 +93,7 @@ func (st Account) Serialize() ([]byte, error) { // FromProto converts from protobuf's Account func (st *Account) FromProto(acPb *accountpb.Account) { st.nonce = acPb.Nonce + st.accountType = int32(acPb.Type.Number()) if acPb.Balance == "" { st.Balance = big.NewInt(0) } else { @@ -110,18 +126,39 @@ func (st *Account) Deserialize(buf []byte) error { return nil } -// SetNonce sets the nonce of the account +// IsNewbieAccount returns true if the account has not sent any actions +func (st *Account) IsNewbieAccount() bool { + return st.nonce == 0 +} + +// SetNonce sets the nonce according to type func (st *Account) SetNonce(nonce uint64) error { - if nonce != st.nonce+1 { - return errors.Wrapf(ErrInvalidNonce, "actual value %d, %d expected", nonce, st.nonce+1) + switch st.accountType { + case 1: + if nonce != st.nonce { + return errors.Wrapf(ErrInvalidNonce, "actual value %d, %d expected", nonce, st.nonce) + } + st.nonce = nonce + 1 + case 0: + if nonce != st.nonce+1 { + return errors.Wrapf(ErrInvalidNonce, "actual value %d, %d expected", nonce, st.nonce+1) + } + st.nonce = nonce + default: + return errors.Wrapf(ErrUnknownAccountType, "account type %d", st.accountType) } - st.nonce = nonce + return nil } // PendingNonce returns the pending nonce of the account func (st *Account) PendingNonce() uint64 { - return st.nonce + 1 + switch st.accountType { + case 1: + return st.nonce + default: // 0 + return st.nonce + 1 + } } // HasSufficientBalance returns true if balance is larger than amount @@ -168,6 +205,8 @@ func (st *Account) Clone() *Account { s := *st s.Balance = nil s.Balance = new(big.Int).Set(st.Balance) + s.accountType = st.accountType + s.nonce = st.nonce s.votingWeight = nil if st.votingWeight != nil { s.votingWeight = new(big.Int).Set(st.votingWeight) diff --git a/state/factory/factory_test.go b/state/factory/factory_test.go index feb8c7db22..0bdb45ee52 100644 --- a/state/factory/factory_test.go +++ b/state/factory/factory_test.go @@ -1361,9 +1361,9 @@ func TestDeleteAndPutSameKey(t *testing.T) { require.NoError(t, err) _, err = ws.DelState(protocol.LegacyKeyOption(key)) require.NoError(t, err) - _, err = ws.State(&acc, protocol.LegacyKeyOption(key)) + _, err = ws.State(acc, protocol.LegacyKeyOption(key)) require.Equal(t, state.ErrStateNotExist, errors.Cause(err)) - _, err = ws.State(&acc, protocol.LegacyKeyOption(hash.Hash160b([]byte("other")))) + _, err = ws.State(acc, protocol.LegacyKeyOption(hash.Hash160b([]byte("other")))) require.Equal(t, state.ErrStateNotExist, errors.Cause(err)) } ctx := genesis.WithGenesisContext( From 524115eedbb078edecbbbea5899d7ca5a71d1e9d Mon Sep 17 00:00:00 2001 From: zhi Date: Tue, 14 Jun 2022 18:00:40 -0700 Subject: [PATCH 07/11] read state with context --- action/protocol/account/protocol.go | 6 +- action/protocol/account/protocol_test.go | 2 +- action/protocol/account/transfer.go | 21 ++- action/protocol/account/transfer_test.go | 19 ++- action/protocol/account/util/util.go | 33 +++- action/protocol/context.go | 104 ++++++------ .../protocol/execution/evm/contract_test.go | 9 +- action/protocol/execution/evm/evm.go | 20 ++- action/protocol/execution/evm/evm_test.go | 3 +- .../execution/evm/evmstatedbadapter.go | 99 +++++------ .../execution/evm/evmstatedbadapter_test.go | 112 +++++++++---- action/protocol/execution/protocol_test.go | 18 +- action/protocol/generic_validator.go | 19 ++- action/protocol/generic_validator_test.go | 24 ++- action/protocol/poll/util.go | 11 +- action/protocol/rewarding/fund.go | 4 +- action/protocol/rewarding/protocol.go | 22 ++- action/protocol/rewarding/reward.go | 4 +- action/protocol/staking/handlers.go | 4 +- action/protocol/staking/handlers_test.go | 3 +- action/protocol/staking/protocol.go | 6 +- actpool/actpool.go | 28 +++- actpool/actpool_test.go | 154 ++++++++++-------- actpool/actqueue.go | 7 +- actpool/actqueue_test.go | 10 +- api/coreservice.go | 13 +- api/grpcserver_integrity_test.go | 2 +- api/serverV2_integrity_test.go | 6 +- blockchain/block/validator_test.go | 7 +- blockchain/integrity/benchmark_test.go | 2 +- blockchain/integrity/integrity_test.go | 80 ++++----- blocksync/blocksync_test.go | 14 +- blocksync/buffer_test.go | 4 +- chainservice/builder.go | 2 +- consensus/scheme/rolldpos/rolldpos_test.go | 2 +- .../scheme/rolldpos/roundcalculator_test.go | 2 +- e2etest/bigint_test.go | 12 +- e2etest/local_test.go | 22 +-- e2etest/local_transfer_test.go | 14 +- e2etest/rewarding_test.go | 17 +- e2etest/staking_test.go | 2 +- gasstation/gasstattion_test.go | 4 +- state/account.go | 44 ++--- state/account_test.go | 38 ++++- state/factory/factory_test.go | 54 +++--- state/factory/workingset.go | 6 +- .../internal/client/client_test.go | 1 - 47 files changed, 649 insertions(+), 441 deletions(-) diff --git a/action/protocol/account/protocol.go b/action/protocol/account/protocol.go index e0460794f8..036f23afb7 100644 --- a/action/protocol/account/protocol.go +++ b/action/protocol/account/protocol.go @@ -108,7 +108,7 @@ func (p *Protocol) Name() string { } func createAccount(sm protocol.StateManager, encodedAddr string, init *big.Int, opts ...state.AccountCreationOption) error { - account := state.NewEmptyAccount() + account := &state.Account{} addr, err := address.FromString(encodedAddr) if err != nil { return errors.Wrap(err, "failed to get address public key hash from encoded address") @@ -149,8 +149,8 @@ func (p *Protocol) CreateGenesisStates(ctx context.Context, sm protocol.StateMan return err } opts := []state.AccountCreationOption{} - if protocol.MustGetFeatureCtx(ctx).CreateZeroNonceAccount { - opts = append(opts, state.ZeroNonceAccountTypeOption()) + if protocol.MustGetFeatureCtx(ctx).CreateLegacyNonceAccount { + opts = append(opts, state.LegacyNonceAccountTypeOption()) } for i, addr := range addrs { if err := createAccount(sm, addr.String(), amounts[i], opts...); err != nil { diff --git a/action/protocol/account/protocol_test.go b/action/protocol/account/protocol_test.go index 9f81554062..1501c41c4f 100644 --- a/action/protocol/account/protocol_test.go +++ b/action/protocol/account/protocol_test.go @@ -64,7 +64,7 @@ func TestLoadOrCreateAccountState(t *testing.T) { require.Equal(s.Balance, big.NewInt(0)) // create account - require.NoError(createAccount(sm, addrv1.String(), big.NewInt(5))) + require.NoError(createAccount(sm, addrv1.String(), big.NewInt(5), state.LegacyNonceAccountTypeOption())) s, err = accountutil.LoadAccount(sm, addrv1) require.NoError(err) require.Equal("5", s.Balance.String()) diff --git a/action/protocol/account/transfer.go b/action/protocol/account/transfer.go index bda9adde13..1ed65969a8 100644 --- a/action/protocol/account/transfer.go +++ b/action/protocol/account/transfer.go @@ -30,8 +30,8 @@ func (p *Protocol) handleTransfer(ctx context.Context, act action.Action, sm pro return nil, nil } accountCreationOpts := []state.AccountCreationOption{} - if protocol.MustGetFeatureCtx(ctx).CreateZeroNonceAccount { - accountCreationOpts = append(accountCreationOpts, state.ZeroNonceAccountTypeOption()) + if protocol.MustGetFeatureCtx(ctx).CreateLegacyNonceAccount { + accountCreationOpts = append(accountCreationOpts, state.LegacyNonceAccountTypeOption()) } actionCtx := protocol.MustGetActionCtx(ctx) // check sender @@ -83,11 +83,14 @@ func (p *Protocol) handleTransfer(ctx context.Context, act action.Action, sm pro return nil, errors.Wrapf(err, "failed to load address %s", tsf.Recipient()) } } + fixNonce := protocol.MustGetFeatureCtx(ctx).FixGasAndNonceUpdate blkCtx := protocol.MustGetBlockCtx(ctx) if err == nil && recipientAcct.IsContract() { - // update sender Nonce - if err := sender.SetNonce(tsf.Nonce()); err != nil { - return nil, errors.Wrapf(err, "failed to update pending nonce of sender %s", actionCtx.Caller.String()) + if fixNonce || tsf.Nonce() != 0 { + // update sender Nonce + if err := sender.SetPendingNonce(tsf.Nonce() + 1); err != nil { + return nil, errors.Wrapf(err, "failed to update pending nonce of sender %s", actionCtx.Caller.String()) + } } // put updated sender's state to trie if err := accountutil.StoreAccount(sm, actionCtx.Caller, sender); err != nil { @@ -116,9 +119,11 @@ func (p *Protocol) handleTransfer(ctx context.Context, act action.Action, sm pro if err := sender.SubBalance(tsf.Amount()); err != nil { return nil, errors.Wrapf(err, "failed to update the Balance of sender %s", actionCtx.Caller.String()) } - // update sender Nonce - if err := sender.SetNonce(tsf.Nonce()); err != nil { - return nil, errors.Wrapf(err, "failed to update pending nonce of sender %s", actionCtx.Caller.String()) + if fixNonce || tsf.Nonce() != 0 { + // update sender Nonce + if err := sender.SetPendingNonce(tsf.Nonce() + 1); err != nil { + return nil, errors.Wrapf(err, "failed to update pending nonce of sender %s", actionCtx.Caller.String()) + } } // put updated sender's state to trie if err := accountutil.StoreAccount(sm, actionCtx.Caller, sender); err != nil { diff --git a/action/protocol/account/transfer_test.go b/action/protocol/account/transfer_test.go index 432b739870..665bd07ada 100644 --- a/action/protocol/account/transfer_test.go +++ b/action/protocol/account/transfer_test.go @@ -78,10 +78,12 @@ func TestProtocol_HandleTransfer(t *testing.T) { alfa := identityset.Address(28) bravo := identityset.Address(29) charlie := identityset.Address(30) - acct1 := state.NewEmptyAccount() + acct1, err := state.NewAccount(state.LegacyNonceAccountTypeOption()) + require.NoError(err) require.NoError(acct1.AddBalance(big.NewInt(50005))) require.NoError(accountutil.StoreAccount(sm, alfa, acct1)) - acct2 := state.NewEmptyAccount() + acct2, err := state.NewAccount() + require.NoError(err) acct2.CodeHash = []byte("codeHash") require.NoError(accountutil.StoreAccount(sm, charlie, acct2)) @@ -116,9 +118,10 @@ func TestProtocol_HandleTransfer(t *testing.T) { gas, err := tsf.IntrinsicGas() require.NoError(err) - ctx = protocol.WithActionCtx(chainCtx, protocol.ActionCtx{ + ctx := protocol.WithActionCtx(chainCtx, protocol.ActionCtx{ Caller: v.caller, IntrinsicGas: gas, + Nonce: v.nonce, }) ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{ BlockHeight: 1, @@ -126,11 +129,11 @@ func TestProtocol_HandleTransfer(t *testing.T) { GasLimit: testutil.TestGasLimit, }) - sender, err := accountutil.AccountState(sm, v.caller) + sender, err := accountutil.AccountState(ctx, sm, v.caller) require.NoError(err) addr, err := address.FromString(v.recipient) require.NoError(err) - recipient, err := accountutil.AccountState(sm, addr) + recipient, err := accountutil.AccountState(ctx, sm, addr) require.NoError(err) gasFee := new(big.Int).Mul(v.gasPrice, new(big.Int).SetUint64(gas)) @@ -140,7 +143,7 @@ func TestProtocol_HandleTransfer(t *testing.T) { if err != nil { require.Nil(receipt) // sender balance/nonce remains the same in case of error - newSender, err := accountutil.AccountState(sm, v.caller) + newSender, err := accountutil.AccountState(ctx, sm, v.caller) require.NoError(err) require.Equal(sender.Balance, newSender.Balance) require.Equal(sender.PendingNonce(), newSender.PendingNonce()) @@ -154,13 +157,13 @@ func TestProtocol_HandleTransfer(t *testing.T) { // verify recipient addr, err := address.FromString(v.recipient) require.NoError(err) - newRecipient, err := accountutil.AccountState(sm, addr) + newRecipient, err := accountutil.AccountState(ctx, sm, addr) require.NoError(err) require.NoError(recipient.AddBalance(v.amount)) require.Equal(recipient.Balance, newRecipient.Balance) } // verify sender balance/nonce - newSender, err := accountutil.AccountState(sm, v.caller) + newSender, err := accountutil.AccountState(ctx, sm, v.caller) require.NoError(err) require.NoError(sender.SubBalance(gasFee)) require.Equal(sender.Balance, newSender.Balance) diff --git a/action/protocol/account/util/util.go b/action/protocol/account/util/util.go index d71c5d9ee5..4114290016 100644 --- a/action/protocol/account/util/util.go +++ b/action/protocol/account/util/util.go @@ -7,6 +7,8 @@ package accountutil import ( + "context" + "github.com/pkg/errors" "github.com/iotexproject/go-pkgs/hash" @@ -19,7 +21,7 @@ import ( // LoadOrCreateAccount either loads an account state or creates an account state func LoadOrCreateAccount(sm protocol.StateManager, addr address.Address, opts ...state.AccountCreationOption) (*state.Account, error) { var ( - account = state.NewEmptyAccount() + account = &state.Account{} addrHash = hash.BytesToHash160(addr.Bytes()) ) _, err := sm.State(account, protocol.LegacyKeyOption(addrHash)) @@ -47,7 +49,7 @@ func LoadAccount(sr protocol.StateReader, addr address.Address, opts ...state.Ac // LoadAccountByHash160 loads an account state by 20-byte address func LoadAccountByHash160(sr protocol.StateReader, addrHash hash.Hash160, opts ...state.AccountCreationOption) (*state.Account, error) { - account := state.NewEmptyAccount() + account := &state.Account{} switch _, err := sr.State(account, protocol.LegacyKeyOption(addrHash)); errors.Cause(err) { case state.ErrStateNotExist: return state.NewAccount(opts...) @@ -67,7 +69,7 @@ func StoreAccount(sm protocol.StateManager, addr address.Address, account *state // Recorded tests if an account has been actually stored func Recorded(sr protocol.StateReader, addr address.Address) (bool, error) { - account := state.NewEmptyAccount() + account := &state.Account{} _, err := sr.State(account, protocol.LegacyKeyOption(hash.BytesToHash160(addr.Bytes()))) switch errors.Cause(err) { case nil: @@ -79,21 +81,34 @@ func Recorded(sr protocol.StateReader, addr address.Address) (bool, error) { } // AccountState returns the confirmed account state on the chain -func AccountState(sr protocol.StateReader, addr address.Address) (*state.Account, error) { - a, _, err := AccountStateWithHeight(sr, addr) +func AccountState(ctx context.Context, sr protocol.StateReader, addr address.Address) (*state.Account, error) { + a, _, err := AccountStateWithHeight(ctx, sr, addr) return a, err } // AccountStateWithHeight returns the confirmed account state on the chain with what height the state is read from. -func AccountStateWithHeight(sr protocol.StateReader, addr address.Address) (*state.Account, uint64, error) { +func AccountStateWithHeight(ctx context.Context, sr protocol.StateReader, addr address.Address) (*state.Account, uint64, error) { pkHash := hash.BytesToHash160(addr.Bytes()) - account := state.NewEmptyAccount() + account := &state.Account{} h, err := sr.State(account, protocol.LegacyKeyOption(pkHash)) switch errors.Cause(err) { case nil: - fallthrough - case state.ErrStateNotExist: return account, h, nil + case state.ErrStateNotExist: + tip, err := sr.Height() + if err != nil { + return nil, 0, err + } + ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{ + BlockHeight: tip + 1, + }) + ctx = protocol.WithFeatureCtx(ctx) + var opts []state.AccountCreationOption + if protocol.MustGetFeatureCtx(ctx).CreateLegacyNonceAccount { + opts = append(opts, state.LegacyNonceAccountTypeOption()) + } + account, err = state.NewAccount(opts...) + return account, h, err default: return nil, h, errors.Wrapf(err, "error when loading state of %x", pkHash) } diff --git a/action/protocol/context.go b/action/protocol/context.go index bad3468769..085f6c71fb 100644 --- a/action/protocol/context.go +++ b/action/protocol/context.go @@ -81,32 +81,32 @@ type ( // FeatureCtx provides features information. FeatureCtx struct { - FixDoubleChargeGas bool - SystemWideActionGasLimit bool - NotFixTopicCopyBug bool - SetRevertMessageToReceipt bool - FixGetHashFnHeight bool - UsePendingNonceOption bool - AsyncContractTrie bool - AddOutOfGasToTransactionLog bool - AddChainIDToConfig bool - UseV2Storage bool - CannotUnstakeAgain bool - SkipStakingIndexer bool - ReturnFetchError bool - CannotTranferToSelf bool - NewStakingReceiptFormat bool - UpdateBlockMeta bool - CurrentEpochProductivity bool - FixSnapshotOrder bool - AllowCorrectDefaultChainID bool - CorrectGetHashFn bool - CorrectTxLogIndex bool - RevertLog bool - TolerateLegacyAddress bool - ValidateRewardProtocol bool - CreateZeroNonceAccount bool - SkipUpdateForSystemAction bool + FixDoubleChargeGas bool + SystemWideActionGasLimit bool + NotFixTopicCopyBug bool + SetRevertMessageToReceipt bool + FixGetHashFnHeight bool + FixSortCacheContractsAndUsePendingNonce bool + AsyncContractTrie bool + AddOutOfGasToTransactionLog bool + AddChainIDToConfig bool + UseV2Storage bool + CannotUnstakeAgain bool + SkipStakingIndexer bool + ReturnFetchError bool + CannotTranferToSelf bool + NewStakingReceiptFormat bool + UpdateBlockMeta bool + CurrentEpochProductivity bool + FixSnapshotOrder bool + AllowCorrectDefaultChainID bool + CorrectGetHashFn bool + CorrectTxLogIndex bool + RevertLog bool + TolerateLegacyAddress bool + ValidateRewardProtocol bool + CreateLegacyNonceAccount bool + FixGasAndNonceUpdate bool } // FeatureWithHeightCtx provides feature check functions. @@ -212,32 +212,32 @@ func WithFeatureCtx(ctx context.Context) context.Context { ctx, featureContextKey{}, FeatureCtx{ - FixDoubleChargeGas: g.IsPacific(height), - SystemWideActionGasLimit: !g.IsAleutian(height), - NotFixTopicCopyBug: !g.IsAleutian(height), - SetRevertMessageToReceipt: g.IsHawaii(height), - FixGetHashFnHeight: g.IsHawaii(height), - UsePendingNonceOption: g.IsHawaii(height), - AsyncContractTrie: g.IsGreenland(height), - AddOutOfGasToTransactionLog: !g.IsGreenland(height), - AddChainIDToConfig: g.IsIceland(height), - UseV2Storage: g.IsGreenland(height), - CannotUnstakeAgain: g.IsGreenland(height), - SkipStakingIndexer: !g.IsFairbank(height), - ReturnFetchError: !g.IsGreenland(height), - CannotTranferToSelf: g.IsHawaii(height), - NewStakingReceiptFormat: g.IsFbkMigration(height), - UpdateBlockMeta: g.IsGreenland(height), - CurrentEpochProductivity: g.IsGreenland(height), - FixSnapshotOrder: g.IsKamchatka(height), - AllowCorrectDefaultChainID: g.IsMidway(height), - CorrectGetHashFn: g.IsMidway(height), - CorrectTxLogIndex: g.IsMidway(height), - RevertLog: g.IsMidway(height), - TolerateLegacyAddress: !g.IsNewfoundland(height), - ValidateRewardProtocol: g.IsNewfoundland(height), - CreateZeroNonceAccount: g.IsToBeEnabled(height), - SkipUpdateForSystemAction: g.IsToBeEnabled(height), + FixDoubleChargeGas: g.IsPacific(height), + SystemWideActionGasLimit: !g.IsAleutian(height), + NotFixTopicCopyBug: !g.IsAleutian(height), + SetRevertMessageToReceipt: g.IsHawaii(height), + FixGetHashFnHeight: g.IsHawaii(height), + FixSortCacheContractsAndUsePendingNonce: g.IsHawaii(height), + AsyncContractTrie: g.IsGreenland(height), + AddOutOfGasToTransactionLog: !g.IsGreenland(height), + AddChainIDToConfig: g.IsIceland(height), + UseV2Storage: g.IsGreenland(height), + CannotUnstakeAgain: g.IsGreenland(height), + SkipStakingIndexer: !g.IsFairbank(height), + ReturnFetchError: !g.IsGreenland(height), + CannotTranferToSelf: g.IsHawaii(height), + NewStakingReceiptFormat: g.IsFbkMigration(height), + UpdateBlockMeta: g.IsGreenland(height), + CurrentEpochProductivity: g.IsGreenland(height), + FixSnapshotOrder: g.IsKamchatka(height), + AllowCorrectDefaultChainID: g.IsMidway(height), + CorrectGetHashFn: g.IsMidway(height), + CorrectTxLogIndex: g.IsMidway(height), + RevertLog: g.IsMidway(height), + TolerateLegacyAddress: !g.IsNewfoundland(height), + ValidateRewardProtocol: g.IsNewfoundland(height), + CreateLegacyNonceAccount: !g.IsToBeEnabled(height), + FixGasAndNonceUpdate: g.IsToBeEnabled(height), }, ) } diff --git a/action/protocol/execution/evm/contract_test.go b/action/protocol/execution/evm/contract_test.go index 9046612f8d..8cf45f7416 100644 --- a/action/protocol/execution/evm/contract_test.go +++ b/action/protocol/execution/evm/contract_test.go @@ -67,7 +67,8 @@ func TestCreateContract(t *testing.T) { addr := identityset.Address(28) _, err = accountutil.LoadOrCreateAccount(sm, addr) require.NoError(err) - stateDB := NewStateDBAdapter(sm, 0, hash.ZeroHash256, NotFixTopicCopyBugOption()) + stateDB, err := NewStateDBAdapter(sm, 0, hash.ZeroHash256, NotFixTopicCopyBugOption()) + require.NoError(err) contract := addr.Bytes() var evmContract common.Address @@ -101,7 +102,8 @@ func TestLoadStoreCommit(t *testing.T) { ctrl := gomock.NewController(t) sm, err := initMockStateManager(ctrl) require.NoError(err) - cntr1, err := newContract(hash.BytesToHash160(_c1[:]), state.NewEmptyAccount(), sm, enableAsync) + acct := &state.Account{} + cntr1, err := newContract(hash.BytesToHash160(_c1[:]), acct, sm, enableAsync) require.NoError(err) tests := []cntrTest{ @@ -252,7 +254,8 @@ func TestSnapshot(t *testing.T) { testfunc := func(enableAsync bool) { sm, err := initMockStateManager(ctrl) require.NoError(err) - s := state.NewEmptyAccount() + s, err := state.NewAccount() + require.NoError(err) require.NoError(s.AddBalance(big.NewInt(5))) _c1, err := newContract( hash.BytesToHash160(identityset.Address(28).Bytes()), diff --git a/action/protocol/execution/evm/evm.go b/action/protocol/execution/evm/evm.go index 61f317f9cb..e9c6be8fe0 100644 --- a/action/protocol/execution/evm/evm.go +++ b/action/protocol/execution/evm/evm.go @@ -199,7 +199,10 @@ func ExecuteContract( blkCtx := protocol.MustGetBlockCtx(ctx) g := genesis.MustExtractGenesisContext(ctx) featureCtx := protocol.MustGetFeatureCtx(ctx) - stateDB := prepareStateDB(ctx, sm) + stateDB, err := prepareStateDB(ctx, sm) + if err != nil { + return nil, nil, err + } ps, err := newParams(ctx, execution, stateDB, getBlockHash) if err != nil { return nil, nil, err @@ -280,21 +283,24 @@ func ReadContractStorage( BlockHeight: bcCtx.Tip.Height + 1, }, )) - stateDB := prepareStateDB(ctx, sm) + stateDB, err := prepareStateDB(ctx, sm) + if err != nil { + return nil, err + } res := stateDB.GetState(common.BytesToAddress(contract.Bytes()), common.BytesToHash(key)) return res[:], nil } -func prepareStateDB(ctx context.Context, sm protocol.StateManager) *StateDBAdapter { +func prepareStateDB(ctx context.Context, sm protocol.StateManager) (*StateDBAdapter, error) { actionCtx := protocol.MustGetActionCtx(ctx) blkCtx := protocol.MustGetBlockCtx(ctx) featureCtx := protocol.MustGetFeatureCtx(ctx) opts := []StateDBAdapterOption{} - if featureCtx.CreateZeroNonceAccount { - opts = append(opts, ZeroNonceAccountOption()) + if featureCtx.CreateLegacyNonceAccount { + opts = append(opts, LegacyNonceAccountOption()) } - if featureCtx.UsePendingNonceOption { - opts = append(opts, SortCachedContractsOption(), UsePendingNonceOption()) + if !featureCtx.FixSortCacheContractsAndUsePendingNonce { + opts = append(opts, DisableSortCachedContractsOption(), UseConfirmedNonceOption()) } if featureCtx.NotFixTopicCopyBug { opts = append(opts, NotFixTopicCopyBugOption()) diff --git a/action/protocol/execution/evm/evm_test.go b/action/protocol/execution/evm/evm_test.go index 53e2c54667..8db5696525 100644 --- a/action/protocol/execution/evm/evm_test.go +++ b/action/protocol/execution/evm/evm_test.go @@ -214,7 +214,8 @@ func TestConstantinople(t *testing.T) { if g.IsKamchatka(e.height) { opt = append(opt, FixSnapshotOrderOption()) } - stateDB := NewStateDBAdapter(sm, e.height, hash.ZeroHash256, opt...) + stateDB, err := NewStateDBAdapter(sm, e.height, hash.ZeroHash256, opt...) + require.NoError(err) fCtx := protocol.WithBlockCtx(ctx, protocol.BlockCtx{ Producer: identityset.Address(27), diff --git a/action/protocol/execution/evm/evmstatedbadapter.go b/action/protocol/execution/evm/evmstatedbadapter.go index 3f32367e25..a7833c6770 100644 --- a/action/protocol/execution/evm/evmstatedbadapter.go +++ b/action/protocol/execution/evm/evmstatedbadapter.go @@ -49,40 +49,40 @@ type ( // StateDBAdapter represents the state db adapter for evm to access iotx blockchain StateDBAdapter struct { - sm protocol.StateManager - logs []*action.Log - transactionLogs []*action.TransactionLog - err error - blockHeight uint64 - executionHash hash.Hash256 - refund uint64 - cachedContract contractMap - contractSnapshot map[int]contractMap // snapshots of contracts - suicided deleteAccount // account/contract calling Suicide - suicideSnapshot map[int]deleteAccount // snapshots of suicide accounts - preimages preimageMap - preimageSnapshot map[int]preimageMap - accessList *accessList // per-transaction access list - accessListSnapshot map[int]*accessList - logsSnapshot map[int]int // logs is an array, save len(logs) at time of snapshot suffices - txLogsSnapshot map[int]int - notFixTopicCopyBug bool - asyncContractTrie bool - sortCachedContracts bool - usePendingNonce bool - zeroNonceAccount bool - fixSnapshotOrder bool - revertLog bool + sm protocol.StateManager + logs []*action.Log + transactionLogs []*action.TransactionLog + err error + blockHeight uint64 + executionHash hash.Hash256 + refund uint64 + cachedContract contractMap + contractSnapshot map[int]contractMap // snapshots of contracts + suicided deleteAccount // account/contract calling Suicide + suicideSnapshot map[int]deleteAccount // snapshots of suicide accounts + preimages preimageMap + preimageSnapshot map[int]preimageMap + accessList *accessList // per-transaction access list + accessListSnapshot map[int]*accessList + logsSnapshot map[int]int // logs is an array, save len(logs) at time of snapshot suffices + txLogsSnapshot map[int]int + notFixTopicCopyBug bool + asyncContractTrie bool + disableSortCachedContracts bool + useConfirmedNonce bool + legacyNonceAccount bool + fixSnapshotOrder bool + revertLog bool } ) // StateDBAdapterOption set StateDBAdapter construction param type StateDBAdapterOption func(*StateDBAdapter) error -// SortCachedContractsOption set sort cached contracts as true -func SortCachedContractsOption() StateDBAdapterOption { +// DisableSortCachedContractsOption set disable sort cached contracts as true +func DisableSortCachedContractsOption() StateDBAdapterOption { return func(adapter *StateDBAdapter) error { - adapter.sortCachedContracts = true + adapter.disableSortCachedContracts = true return nil } } @@ -103,18 +103,18 @@ func AsyncContractTrieOption() StateDBAdapterOption { } } -// UsePendingNonceOption set usePendingNonce as true -func UsePendingNonceOption() StateDBAdapterOption { +// UseConfirmedNonceOption set usePendingNonce as true +func UseConfirmedNonceOption() StateDBAdapterOption { return func(adapter *StateDBAdapter) error { - adapter.usePendingNonce = true + adapter.useConfirmedNonce = true return nil } } -// ZeroNonceAccountOption set fixPendingNonce as true -func ZeroNonceAccountOption() StateDBAdapterOption { +// LegacyNonceAccountOption set legacyNonceAccount as true +func LegacyNonceAccountOption() StateDBAdapterOption { return func(adapter *StateDBAdapter) error { - adapter.zeroNonceAccount = true + adapter.legacyNonceAccount = true return nil } } @@ -141,7 +141,7 @@ func NewStateDBAdapter( blockHeight uint64, executionHash hash.Hash256, opts ...StateDBAdapterOption, -) *StateDBAdapter { +) (*StateDBAdapter, error) { s := &StateDBAdapter{ sm: sm, logs: []*action.Log{}, @@ -161,10 +161,13 @@ func NewStateDBAdapter( } for _, opt := range opts { if err := opt(s); err != nil { - log.L().Panic("failed to execute stateDB creation option") + return nil, errors.Wrap(err, "failed to execute stateDB creation option") } } - return s + if !s.legacyNonceAccount && s.useConfirmedNonce { + return nil, errors.New("invalid parameter combination") + } + return s, nil } func (stateDB *StateDBAdapter) logError(err error) { @@ -179,8 +182,8 @@ func (stateDB *StateDBAdapter) Error() error { } func (stateDB *StateDBAdapter) accountCreationOpts() []state.AccountCreationOption { - if stateDB.zeroNonceAccount { - return []state.AccountCreationOption{state.ZeroNonceAccountTypeOption()} + if stateDB.legacyNonceAccount { + return []state.AccountCreationOption{state.LegacyNonceAccountTypeOption()} } return nil } @@ -307,10 +310,10 @@ func (stateDB *StateDBAdapter) GetNonce(evmAddr common.Address) uint64 { return 0 } var pendingNonce uint64 - if stateDB.zeroNonceAccount { - pendingNonce = uint64(0) - } else { + if stateDB.legacyNonceAccount { pendingNonce = uint64(1) + } else { + pendingNonce = uint64(0) } state, err := stateDB.AccountState(addr.String()) if err != nil { @@ -319,7 +322,7 @@ func (stateDB *StateDBAdapter) GetNonce(evmAddr common.Address) uint64 { } else { pendingNonce = state.PendingNonce() } - if !stateDB.usePendingNonce { + if stateDB.useConfirmedNonce { if pendingNonce == 0 { panic("invalid pending nonce") } @@ -345,7 +348,7 @@ func (stateDB *StateDBAdapter) SetNonce(evmAddr common.Address, nonce uint64) { // stateDB.logError(err) return } - if stateDB.usePendingNonce { + if !stateDB.useConfirmedNonce { if nonce == 0 { panic("invalid nonce zero") } @@ -354,9 +357,11 @@ func (stateDB *StateDBAdapter) SetNonce(evmAddr common.Address, nonce uint64) { log.L().Debug("Called SetNonce.", zap.String("address", addr.String()), zap.Uint64("nonce", nonce)) - if err := s.SetNonce(nonce); err != nil { - log.L().Error("Failed to set nonce.", zap.Error(err)) - stateDB.logError(err) + if !s.IsNewbieAccount() || s.AccountType() != 0 || nonce != 0 { + if err := s.SetPendingNonce(nonce + 1); err != nil { + log.L().Panic("Failed to set nonce.", zap.Error(err), zap.String("addr", addr.Hex()), zap.Uint64("pendingNonce", s.PendingNonce()), zap.Uint64("nonce", nonce), zap.String("execution", hex.EncodeToString(stateDB.executionHash[:]))) + stateDB.logError(err) + } } if err := accountutil.StoreAccount(stateDB.sm, addr, s); err != nil { log.L().Error("Failed to store account.", zap.Error(err)) @@ -613,7 +618,7 @@ func (stateDB *StateDBAdapter) cachedContractAddrs() []hash.Hash160 { for addr := range stateDB.cachedContract { addrs = append(addrs, addr) } - if stateDB.sortCachedContracts { + if !stateDB.disableSortCachedContracts { sort.Slice(addrs, func(i, j int) bool { return bytes.Compare(addrs[i][:], addrs[j][:]) < 0 }) } return addrs diff --git a/action/protocol/execution/evm/evmstatedbadapter_test.go b/action/protocol/execution/evm/evmstatedbadapter_test.go index 203857f391..3a10eba4fc 100644 --- a/action/protocol/execution/evm/evmstatedbadapter_test.go +++ b/action/protocol/execution/evm/evmstatedbadapter_test.go @@ -91,13 +91,14 @@ func TestAddBalance(t *testing.T) { sm, err := initMockStateManager(ctrl) require.NoError(err) addr := common.HexToAddress("02ae2a956d21e8d481c3a69e146633470cf625ec") - stateDB := NewStateDBAdapter( + stateDB, err := NewStateDBAdapter( sm, 1, hash.ZeroHash256, NotFixTopicCopyBugOption(), FixSnapshotOrderOption(), ) + require.NoError(err) addAmount := big.NewInt(40000) stateDB.AddBalance(addr, addAmount) amount := stateDB.GetBalance(addr) @@ -113,13 +114,14 @@ func TestRefundAPIs(t *testing.T) { sm, err := initMockStateManager(ctrl) require.NoError(err) - stateDB := NewStateDBAdapter( + stateDB, err := NewStateDBAdapter( sm, 1, hash.ZeroHash256, NotFixTopicCopyBugOption(), FixSnapshotOrderOption(), ) + require.NoError(err) require.Zero(stateDB.GetRefund()) refund := uint64(1024) stateDB.AddRefund(refund) @@ -133,13 +135,14 @@ func TestEmptyAndCode(t *testing.T) { sm, err := initMockStateManager(ctrl) require.NoError(err) addr := common.HexToAddress("02ae2a956d21e8d481c3a69e146633470cf625ec") - stateDB := NewStateDBAdapter( + stateDB, err := NewStateDBAdapter( sm, 1, hash.ZeroHash256, NotFixTopicCopyBugOption(), FixSnapshotOrderOption(), ) + require.NoError(err) require.True(stateDB.Empty(addr)) stateDB.CreateAccount(addr) require.True(stateDB.Empty(addr)) @@ -169,13 +172,14 @@ func TestForEachStorage(t *testing.T) { sm, err := initMockStateManager(ctrl) require.NoError(err) addr := common.HexToAddress("02ae2a956d21e8d481c3a69e146633470cf625ec") - stateDB := NewStateDBAdapter( + stateDB, err := NewStateDBAdapter( sm, 1, hash.ZeroHash256, NotFixTopicCopyBugOption(), FixSnapshotOrderOption(), ) + require.NoError(err) stateDB.CreateAccount(addr) for k, v := range kvs { stateDB.SetState(addr, k, v) @@ -197,15 +201,14 @@ func TestReadContractStorage(t *testing.T) { sm, err := initMockStateManager(ctrl) require.NoError(err) addr := common.HexToAddress("02ae2a956d21e8d481c3a69e146633470cf625ec") - stateDB := NewStateDBAdapter( + stateDB, err := NewStateDBAdapter( sm, 1, hash.ZeroHash256, AsyncContractTrieOption(), - SortCachedContractsOption(), - UsePendingNonceOption(), FixSnapshotOrderOption(), ) + require.NoError(err) stateDB.CreateAccount(addr) kvs := map[common.Hash]common.Hash{ common.HexToHash("0123456701234567012345670123456701234567012345670123456701234560"): common.HexToHash("0123456701234567012345670123456701234567012345670123456701234560"), @@ -240,17 +243,60 @@ func TestNonce(t *testing.T) { require := require.New(t) ctrl := gomock.NewController(t) - sm, err := initMockStateManager(ctrl) - require.NoError(err) addr := common.HexToAddress("02ae2a956d21e8d481c3a69e146633470cf625ec") - opt := []StateDBAdapterOption{ - NotFixTopicCopyBugOption(), - FixSnapshotOrderOption(), - } - stateDB := NewStateDBAdapter(sm, 1, hash.ZeroHash256, opt...) - require.Equal(uint64(0), stateDB.GetNonce(addr)) - stateDB.SetNonce(addr, 1) - require.Equal(uint64(1), stateDB.GetNonce(addr)) + t.Run("legacy nonce account with confirmed nonce", func(t *testing.T) { + sm, err := initMockStateManager(ctrl) + require.NoError(err) + opt := []StateDBAdapterOption{ + NotFixTopicCopyBugOption(), + FixSnapshotOrderOption(), + LegacyNonceAccountOption(), + UseConfirmedNonceOption(), + } + stateDB, err := NewStateDBAdapter(sm, 1, hash.ZeroHash256, opt...) + require.NoError(err) + require.Equal(uint64(0), stateDB.GetNonce(addr)) + stateDB.SetNonce(addr, 1) + require.Equal(uint64(1), stateDB.GetNonce(addr)) + }) + t.Run("legacy nonce account with pending nonce", func(t *testing.T) { + sm, err := initMockStateManager(ctrl) + require.NoError(err) + opt := []StateDBAdapterOption{ + NotFixTopicCopyBugOption(), + FixSnapshotOrderOption(), + LegacyNonceAccountOption(), + } + stateDB, err := NewStateDBAdapter(sm, 1, hash.ZeroHash256, opt...) + require.NoError(err) + require.Equal(uint64(1), stateDB.GetNonce(addr)) + stateDB.SetNonce(addr, 2) + require.Equal(uint64(2), stateDB.GetNonce(addr)) + }) + t.Run("zero nonce account with confirmed nonce", func(t *testing.T) { + sm, err := initMockStateManager(ctrl) + require.NoError(err) + opt := []StateDBAdapterOption{ + NotFixTopicCopyBugOption(), + FixSnapshotOrderOption(), + UseConfirmedNonceOption(), + } + _, err = NewStateDBAdapter(sm, 1, hash.ZeroHash256, opt...) + require.Error(err) + }) + t.Run("zero nonce account with pending nonce", func(t *testing.T) { + sm, err := initMockStateManager(ctrl) + require.NoError(err) + opt := []StateDBAdapterOption{ + NotFixTopicCopyBugOption(), + FixSnapshotOrderOption(), + } + stateDB, err := NewStateDBAdapter(sm, 1, hash.ZeroHash256, opt...) + require.NoError(err) + require.Equal(uint64(0), stateDB.GetNonce(addr)) + stateDB.SetNonce(addr, 1) + require.Equal(uint64(1), stateDB.GetNonce(addr)) + }) } var tests = []stateDBTest{ @@ -363,7 +409,8 @@ func TestSnapshotRevertAndCommit(t *testing.T) { if revertLog { opt = append(opt, RevertLogOption()) } - stateDB := NewStateDBAdapter(sm, 1, hash.ZeroHash256, opt...) + stateDB, err := NewStateDBAdapter(sm, 1, hash.ZeroHash256, opt...) + require.NoError(err) for i, test := range tests { // add balance @@ -625,7 +672,8 @@ func TestClearSnapshots(t *testing.T) { if fixSnapshotOrder { opts = append(opts, FixSnapshotOrderOption()) } - stateDB := NewStateDBAdapter(sm, 1, hash.ZeroHash256, opts...) + stateDB, err := NewStateDBAdapter(sm, 1, hash.ZeroHash256, opts...) + require.NoError(err) for i, test := range tests { // add balance @@ -701,13 +749,14 @@ func TestGetCommittedState(t *testing.T) { sm, err := initMockStateManager(ctrl) require.NoError(err) - stateDB := NewStateDBAdapter( + stateDB, err := NewStateDBAdapter( sm, 1, hash.ZeroHash256, NotFixTopicCopyBugOption(), FixSnapshotOrderOption(), ) + require.NoError(err) stateDB.SetState(_c1, _k1, _v1) // _k2 does not exist @@ -739,13 +788,14 @@ func TestGetBalanceOnError(t *testing.T) { for _, err := range errs { sm.EXPECT().State(gomock.Any(), gomock.Any()).Return(uint64(0), err).Times(1) addr := common.HexToAddress("test address") - stateDB := NewStateDBAdapter( + stateDB, err := NewStateDBAdapter( sm, 1, hash.ZeroHash256, NotFixTopicCopyBugOption(), FixSnapshotOrderOption(), ) + assert.NoError(t, err) amount := stateDB.GetBalance(addr) assert.Equal(t, big.NewInt(0), amount) } @@ -757,13 +807,14 @@ func TestPreimage(t *testing.T) { sm, err := initMockStateManager(ctrl) require.NoError(err) - stateDB := NewStateDBAdapter( + stateDB, err := NewStateDBAdapter( sm, 1, hash.ZeroHash256, NotFixTopicCopyBugOption(), FixSnapshotOrderOption(), ) + require.NoError(err) stateDB.AddPreimage(common.BytesToHash(_v1[:]), []byte("cat")) stateDB.AddPreimage(common.BytesToHash(_v2[:]), []byte("dog")) @@ -785,6 +836,7 @@ func TestPreimage(t *testing.T) { } func TestSortMap(t *testing.T) { + require := require.New(t) uniqueSlice := func(slice []string) bool { for _, v := range slice[1:] { if v != slice[0] { @@ -799,7 +851,8 @@ func TestSortMap(t *testing.T) { NotFixTopicCopyBugOption(), FixSnapshotOrderOption(), ) - stateDB := NewStateDBAdapter(sm, 1, hash.ZeroHash256, opts...) + stateDB, err := NewStateDBAdapter(sm, 1, hash.ZeroHash256, opts...) + require.NoError(err) size := 10 for i := 0; i < size; i++ { @@ -812,13 +865,13 @@ func TestSortMap(t *testing.T) { for i := 0; i < size; i++ { stateDB.RevertToSnapshot(sn) s := "" - if stateDB.sortCachedContracts { - for _, addr := range stateDB.cachedContractAddrs() { - c := stateDB.cachedContract[addr] + if stateDB.disableSortCachedContracts { + for _, c := range stateDB.cachedContract { s += string(c.SelfState().Root[:]) } } else { - for _, c := range stateDB.cachedContract { + for _, addr := range stateDB.cachedContractAddrs() { + c := stateDB.cachedContract[addr] s += string(c.SelfState().Root[:]) } } @@ -827,16 +880,15 @@ func TestSortMap(t *testing.T) { } return uniqueSlice(caches) } - require := require.New(t) ctrl := gomock.NewController(t) sm, err := initMockStateManager(ctrl) require.NoError(err) t.Run("before fix sort map", func(t *testing.T) { - require.False(testFunc(t, sm)) + require.False(testFunc(t, sm, DisableSortCachedContractsOption())) }) t.Run("after fix sort map", func(t *testing.T) { - require.True(testFunc(t, sm, SortCachedContractsOption())) + require.True(testFunc(t, sm)) }) } diff --git a/action/protocol/execution/protocol_test.go b/action/protocol/execution/protocol_test.go index 2fa0c48a81..0c255f1479 100644 --- a/action/protocol/execution/protocol_test.go +++ b/action/protocol/execution/protocol_test.go @@ -39,6 +39,7 @@ import ( "github.com/iotexproject/iotex-core/blockchain" "github.com/iotexproject/iotex-core/blockchain/block" "github.com/iotexproject/iotex-core/blockchain/blockdao" + "github.com/iotexproject/iotex-core/blockchain/genesis" "github.com/iotexproject/iotex-core/blockindex" "github.com/iotexproject/iotex-core/config" "github.com/iotexproject/iotex-core/db" @@ -259,7 +260,7 @@ func readExecution( contractAddr string, ) ([]byte, *action.Receipt, error) { log.S().Info(ecfg.Comment) - state, err := accountutil.AccountState(sf, ecfg.Executor()) + state, err := accountutil.AccountState(genesis.WithGenesisContext(context.Background(), bc.Genesis()), sf, ecfg.Executor()) if err != nil { return nil, nil, err } @@ -303,7 +304,7 @@ func runExecutions( var ok bool executor := ecfg.Executor() if nonce, ok = nonces[executor.String()]; !ok { - state, err := accountutil.AccountState(sf, executor) + state, err := accountutil.AccountState(genesis.WithGenesisContext(context.Background(), bc.Genesis()), sf, executor) if err != nil { return nil, nil, err } @@ -422,7 +423,7 @@ func (sct *SmartContractTest) prepareBlockchain( sf, err = factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) } r.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) r.NoError(err) // create indexer indexer, err := blockindex.NewIndexer(db.NewMemKVStore(), cfg.Genesis.Hash()) @@ -508,7 +509,7 @@ func (sct *SmartContractTest) run(r *require.Assertions) { defer func() { r.NoError(bc.Stop(ctx)) }() - + ctx = genesis.WithGenesisContext(context.Background(), bc.Genesis()) // deploy smart contract contractAddresses := sct.deployContracts(bc, sf, dao, ap, r) if len(contractAddresses) == 0 { @@ -568,7 +569,7 @@ func (sct *SmartContractTest) run(r *require.Assertions) { } addr, err := address.FromString(account) r.NoError(err) - state, err := accountutil.AccountState(sf, addr) + state, err := accountutil.AccountState(ctx, sf, addr) r.NoError(err) r.Equal( 0, @@ -605,7 +606,6 @@ func TestProtocol_Handle(t *testing.T) { log.S().Info("Test EVM") require := require.New(t) - ctx := context.Background() cfg := config.Default defer func() { delete(cfg.Plugins, config.GatewayPlugin) @@ -631,6 +631,8 @@ func TestProtocol_Handle(t *testing.T) { cfg.Genesis.EnableGravityChainVoting = false cfg.ActPool.MinGasPriceStr = "0" cfg.Genesis.InitBalanceMap[identityset.Address(27).String()] = unit.ConvertIotxToRau(1000000000).String() + ctx := genesis.WithGenesisContext(context.Background(), cfg.Genesis) + registry := protocol.NewRegistry() acc := account.NewProtocol(rewarding.DepositGas) require.NoError(acc.Register(registry)) @@ -639,7 +641,7 @@ func TestProtocol_Handle(t *testing.T) { // create state factory sf, err := factory.NewStateDB(cfg, factory.CachedStateDBOption(), factory.RegistryStateDBOption(registry)) require.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NoError(err) // create indexer cfg.DB.DbPath = cfg.Chain.IndexDBPath @@ -692,7 +694,7 @@ func TestProtocol_Handle(t *testing.T) { require.NoError(err) // test IsContract - state, err := accountutil.AccountState(sf, contract) + state, err := accountutil.AccountState(ctx, sf, contract) require.NoError(err) require.True(state.IsContract()) diff --git a/action/protocol/generic_validator.go b/action/protocol/generic_validator.go index 36014a1965..863e2e5ab9 100644 --- a/action/protocol/generic_validator.go +++ b/action/protocol/generic_validator.go @@ -19,7 +19,7 @@ import ( type ( // AccountState defines a function to return the account state of a given address - AccountState func(StateReader, address.Address) (*state.Account, error) + AccountState func(context.Context, StateReader, address.Address) (*state.Account, error) // GenericValidator is the validator for generic action verification GenericValidator struct { accountState AccountState @@ -36,7 +36,7 @@ func NewGenericValidator(sr StateReader, accountState AccountState) *GenericVali } // Validate validates a generic action -func (v *GenericValidator) Validate(_ context.Context, selp action.SealedEnvelope) error { +func (v *GenericValidator) Validate(ctx context.Context, selp action.SealedEnvelope) error { intrinsicGas, err := selp.IntrinsicGas() if err != nil { return err @@ -54,17 +54,20 @@ func (v *GenericValidator) Validate(_ context.Context, selp action.SealedEnvelop return errors.New("failed to get address") } // Reject action if nonce is too low - confirmedState, err := v.accountState(v.sr, caller) - if err != nil { - return errors.Wrapf(err, "invalid state of account %s", caller.String()) - } if action.IsSystemAction(selp) { if selp.Nonce() != 0 { return action.ErrSystemActionNonce } } else { - if confirmedState.PendingNonce() > selp.Nonce() { - return action.ErrNonceTooLow + featureCtx, ok := GetFeatureCtx(ctx) + if ok && featureCtx.FixGasAndNonceUpdate || selp.Nonce() != 0 { + confirmedState, err := v.accountState(ctx, v.sr, caller) + if err != nil { + return errors.Wrapf(err, "invalid state of account %s", caller.String()) + } + if confirmedState.PendingNonce() > selp.Nonce() { + return action.ErrNonceTooLow + } } } diff --git a/action/protocol/generic_validator_test.go b/action/protocol/generic_validator_test.go index 6b117de4b1..c0b20f6c57 100644 --- a/action/protocol/generic_validator_test.go +++ b/action/protocol/generic_validator_test.go @@ -59,19 +59,25 @@ func TestActionProtoAndGenericValidator(t *testing.T) { }, ) - ctx = genesis.WithGenesisContext(ctx, config.Default.Genesis) + ctx = WithFeatureCtx(genesis.WithGenesisContext(ctx, config.Default.Genesis)) - valid := NewGenericValidator(nil, func(sr StateReader, addr address.Address) (*state.Account, error) { + valid := NewGenericValidator(nil, func(_ context.Context, sr StateReader, addr address.Address) (*state.Account, error) { pk := identityset.PrivateKey(27).PublicKey() eAddr := pk.Address() if strings.EqualFold(eAddr.String(), addr.String()) { return nil, errors.New("MockChainManager nonce error") } - acct := state.NewEmptyAccount() - if err := acct.SetNonce(1); err != nil { + acct, err := state.NewAccount() + if err != nil { return nil, err } - if err := acct.SetNonce(2); err != nil { + if err := acct.SetPendingNonce(1); err != nil { + return nil, err + } + if err := acct.SetPendingNonce(2); err != nil { + return nil, err + } + if err := acct.SetPendingNonce(3); err != nil { return nil, err } @@ -109,11 +115,12 @@ func TestActionProtoAndGenericValidator(t *testing.T) { require.Contains(err.Error(), action.ErrIntrinsicGas.Error()) }) t.Run("state error", func(t *testing.T) { - v, err := action.NewExecution("", 0, big.NewInt(10), uint64(10), big.NewInt(10), data) + v, err := action.NewExecution("", 1, big.NewInt(10), uint64(10), big.NewInt(10), data) require.NoError(err) bd := &action.EnvelopeBuilder{} elp := bd.SetGasPrice(big.NewInt(10)). SetGasLimit(uint64(100000)). + SetNonce(1). SetAction(v).Build() selp, err := action.Sign(elp, identityset.PrivateKey(27)) require.NoError(err) @@ -133,10 +140,9 @@ func TestActionProtoAndGenericValidator(t *testing.T) { SetAction(&gr).Build() selp, err := action.Sign(elp, identityset.PrivateKey(28)) require.NoError(err) - nselp := action.SealedEnvelope{} - require.NoError(nselp.LoadProto(selp.Proto())) + nselp, err := (&action.Deserializer{}).SetEvmNetworkID(_evmNetworkID).ActionToSealedEnvelope(selp.Proto()) + require.NoError(err) err = valid.Validate(ctx, nselp) - require.Error(err) require.Equal(action.ErrSystemActionNonce, errors.Cause(err)) }) t.Run("nonce too low", func(t *testing.T) { diff --git a/action/protocol/poll/util.go b/action/protocol/poll/util.go index 12d9b442a8..18b8676a83 100644 --- a/action/protocol/poll/util.go +++ b/action/protocol/poll/util.go @@ -168,9 +168,11 @@ func setCandidates( return errors.New("put poll result height should be epoch start height") } loadCandidatesLegacy := featureCtx.LoadCandidatesLegacy(height) - accountCreationOpts := []state.AccountCreationOption{state.DelegateCandidateOption()} - if protocol.MustGetFeatureCtx(ctx).CreateZeroNonceAccount { - accountCreationOpts = append(accountCreationOpts, state.ZeroNonceAccountTypeOption()) + accountCreationOpts := []state.AccountCreationOption{} + if protocol.MustGetFeatureCtx(ctx).CreateLegacyNonceAccount { + accountCreationOpts = append(accountCreationOpts, state.LegacyNonceAccountTypeOption()) + } else { + accountCreationOpts = append(accountCreationOpts, state.DelegateCandidateOption()) } for _, candidate := range candidates { addr, err := address.FromString(candidate.Address) @@ -181,6 +183,9 @@ func setCandidates( if err != nil { return errors.Wrapf(err, "failed to load or create the account for delegate %s", candidate.Address) } + if protocol.MustGetFeatureCtx(ctx).CreateLegacyNonceAccount { + delegate.MarkAsCandidate() + } if loadCandidatesLegacy { if err := candidatesutil.LoadAndAddCandidates(sm, height, candidate.Address); err != nil { return err diff --git a/action/protocol/rewarding/fund.go b/action/protocol/rewarding/fund.go index e10226ae5f..4b42c03131 100644 --- a/action/protocol/rewarding/fund.go +++ b/action/protocol/rewarding/fund.go @@ -67,8 +67,8 @@ func (p *Protocol) Deposit( ) (*action.TransactionLog, error) { actionCtx := protocol.MustGetActionCtx(ctx) accountCreationOpts := []state.AccountCreationOption{} - if protocol.MustGetFeatureCtx(ctx).CreateZeroNonceAccount { - accountCreationOpts = append(accountCreationOpts, state.ZeroNonceAccountTypeOption()) + if protocol.MustGetFeatureCtx(ctx).CreateLegacyNonceAccount { + accountCreationOpts = append(accountCreationOpts, state.LegacyNonceAccountTypeOption()) } // Subtract balance from caller acc, err := accountutil.LoadAccount(sm, actionCtx.Caller, accountCreationOpts...) diff --git a/action/protocol/rewarding/protocol.go b/action/protocol/rewarding/protocol.go index 9082bfc931..15df94adc4 100644 --- a/action/protocol/rewarding/protocol.go +++ b/action/protocol/rewarding/protocol.go @@ -419,7 +419,8 @@ func (p *Protocol) settleAction( return nil, err } } - if !isSystemAction || !protocol.MustGetFeatureCtx(ctx).SkipUpdateForSystemAction { + skipUpdateForSystemAction := protocol.MustGetFeatureCtx(ctx).FixGasAndNonceUpdate + if !isSystemAction || !skipUpdateForSystemAction { gasFee := big.NewInt(0).Mul(actionCtx.GasPrice, big.NewInt(0).SetUint64(actionCtx.IntrinsicGas)) depositLog, err := DepositGas(ctx, sm, gasFee) if err != nil { @@ -428,24 +429,31 @@ func (p *Protocol) settleAction( if depositLog != nil { tLogs = append(tLogs, depositLog) } - if err := p.increaseNonce(ctx, sm, actionCtx.Caller, actionCtx.Nonce, isSystemAction); err != nil { + if err := p.increaseNonce( + ctx, + sm, + actionCtx.Caller, + actionCtx.Nonce, + !skipUpdateForSystemAction && actionCtx.Nonce == 0, + ); err != nil { return nil, err } } + return p.createReceipt(status, blkCtx.BlockHeight, actionCtx.ActionHash, actionCtx.IntrinsicGas, logs, tLogs...), nil } -func (p *Protocol) increaseNonce(ctx context.Context, sm protocol.StateManager, addr address.Address, nonce uint64, isSystemAction bool) error { +func (p *Protocol) increaseNonce(ctx context.Context, sm protocol.StateManager, addr address.Address, nonce uint64, skipSetNonce bool) error { accountCreationOpts := []state.AccountCreationOption{} - if protocol.MustGetFeatureCtx(ctx).CreateZeroNonceAccount { - accountCreationOpts = append(accountCreationOpts, state.ZeroNonceAccountTypeOption()) + if protocol.MustGetFeatureCtx(ctx).CreateLegacyNonceAccount { + accountCreationOpts = append(accountCreationOpts, state.LegacyNonceAccountTypeOption()) } acc, err := accountutil.LoadOrCreateAccount(sm, addr, accountCreationOpts...) if err != nil { return err } - if !isSystemAction || nonce != 0 { - if err := acc.SetNonce(nonce); err != nil { + if !skipSetNonce { + if err := acc.SetPendingNonce(nonce + 1); err != nil { return errors.Wrapf(err, "invalid nonce %d", nonce) } } diff --git a/action/protocol/rewarding/reward.go b/action/protocol/rewarding/reward.go index 7411a8fe91..2e22bbcd87 100644 --- a/action/protocol/rewarding/reward.go +++ b/action/protocol/rewarding/reward.go @@ -389,8 +389,8 @@ func (p *Protocol) claimFromAccount(ctx context.Context, sm protocol.StateManage } } accountCreationOpts := []state.AccountCreationOption{} - if protocol.MustGetFeatureCtx(ctx).CreateZeroNonceAccount { - accountCreationOpts = append(accountCreationOpts, state.ZeroNonceAccountTypeOption()) + if protocol.MustGetFeatureCtx(ctx).CreateLegacyNonceAccount { + accountCreationOpts = append(accountCreationOpts, state.LegacyNonceAccountTypeOption()) } // Update primary account primAcc, err := accountutil.LoadOrCreateAccount(sm, addr, accountCreationOpts...) diff --git a/action/protocol/staking/handlers.go b/action/protocol/staking/handlers.go index 9f3a7ce963..9a9ad805b0 100644 --- a/action/protocol/staking/handlers.go +++ b/action/protocol/staking/handlers.go @@ -808,8 +808,8 @@ func (p *Protocol) fetchBucket( func fetchCaller(ctx context.Context, csm CandidateStateManager, amount *big.Int) (*state.Account, ReceiptError) { actionCtx := protocol.MustGetActionCtx(ctx) accountCreationOpts := []state.AccountCreationOption{} - if protocol.MustGetFeatureCtx(ctx).CreateZeroNonceAccount { - accountCreationOpts = append(accountCreationOpts, state.ZeroNonceAccountTypeOption()) + if protocol.MustGetFeatureCtx(ctx).CreateLegacyNonceAccount { + accountCreationOpts = append(accountCreationOpts, state.LegacyNonceAccountTypeOption()) } caller, err := accountutil.LoadAccount(csm.SM(), actionCtx.Caller, accountCreationOpts...) if err != nil { diff --git a/action/protocol/staking/handlers_test.go b/action/protocol/staking/handlers_test.go index 69eee1e093..8f5231c0db 100644 --- a/action/protocol/staking/handlers_test.go +++ b/action/protocol/staking/handlers_test.go @@ -27,6 +27,7 @@ import ( accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util" "github.com/iotexproject/iotex-core/blockchain/genesis" "github.com/iotexproject/iotex-core/pkg/unit" + "github.com/iotexproject/iotex-core/state" "github.com/iotexproject/iotex-core/test/identityset" "github.com/iotexproject/iotex-core/testutil/testdb" ) @@ -2687,7 +2688,7 @@ func setupAccount(sm protocol.StateManager, addr address.Address, balance int64) if balance < 0 { return errors.New("balance cannot be negative") } - account, err := accountutil.LoadOrCreateAccount(sm, addr) + account, err := accountutil.LoadOrCreateAccount(sm, addr, state.LegacyNonceAccountTypeOption()) if err != nil { return err } diff --git a/action/protocol/staking/protocol.go b/action/protocol/staking/protocol.go index dfa8c86d7b..69c10fa304 100644 --- a/action/protocol/staking/protocol.go +++ b/action/protocol/staking/protocol.go @@ -518,14 +518,14 @@ func (p *Protocol) settleAction( return nil, errors.Wrap(err, "failed to deposit gas") } accountCreationOpts := []state.AccountCreationOption{} - if protocol.MustGetFeatureCtx(ctx).CreateZeroNonceAccount { - accountCreationOpts = append(accountCreationOpts, state.ZeroNonceAccountTypeOption()) + if protocol.MustGetFeatureCtx(ctx).CreateLegacyNonceAccount { + accountCreationOpts = append(accountCreationOpts, state.LegacyNonceAccountTypeOption()) } acc, err := accountutil.LoadAccount(sm, actionCtx.Caller, accountCreationOpts...) if err != nil { return nil, err } - if err := acc.SetNonce(actionCtx.Nonce); err != nil { + if err := acc.SetPendingNonce(actionCtx.Nonce + 1); err != nil { return nil, errors.Wrap(err, "failed to set nonce") } if err := accountutil.StoreAccount(sm, actionCtx.Caller, acc); err != nil { diff --git a/actpool/actpool.go b/actpool/actpool.go index df9cda12cf..9d3018ec3a 100644 --- a/actpool/actpool.go +++ b/actpool/actpool.go @@ -24,6 +24,7 @@ import ( "github.com/iotexproject/iotex-core/action/protocol" accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util" "github.com/iotexproject/iotex-core/blockchain/block" + "github.com/iotexproject/iotex-core/blockchain/genesis" "github.com/iotexproject/iotex-core/config" "github.com/iotexproject/iotex-core/pkg/log" "github.com/iotexproject/iotex-core/pkg/prometheustimer" @@ -94,6 +95,7 @@ func EnableExperimentalActions() Option { type actPool struct { mutex sync.RWMutex cfg config.ActPool + g genesis.Genesis sf protocol.StateReader accountActs map[string]ActQueue accountDesActs map[string]map[hash.Hash256]action.SealedEnvelope @@ -106,7 +108,7 @@ type actPool struct { } // NewActPool constructs a new actpool -func NewActPool(sf protocol.StateReader, cfg config.ActPool, opts ...Option) (ActPool, error) { +func NewActPool(g genesis.Genesis, sf protocol.StateReader, cfg config.ActPool, opts ...Option) (ActPool, error) { if sf == nil { return nil, errors.New("Try to attach a nil state reader") } @@ -118,6 +120,7 @@ func NewActPool(sf protocol.StateReader, cfg config.ActPool, opts ...Option) (Ac ap := &actPool{ cfg: cfg, + g: g, sf: sf, senderBlackList: senderBlackList, accountActs: make(map[string]ActQueue), @@ -177,9 +180,10 @@ func (ap *actPool) PendingActionMap() map[string][]action.SealedEnvelope { // Remove the actions that are already timeout ap.reset() + ctx := ap.context(context.Background()) actionMap := make(map[string][]action.SealedEnvelope) for from, queue := range ap.accountActs { - actionMap[from] = append(actionMap[from], queue.PendingActs()...) + actionMap[from] = append(actionMap[from], queue.PendingActs(ctx)...) } return actionMap } @@ -188,7 +192,7 @@ func (ap *actPool) Add(ctx context.Context, act action.SealedEnvelope) error { ap.mutex.Lock() defer ap.mutex.Unlock() - ctx, span := tracer.NewSpan(ctx, "actPool.Add") + ctx, span := tracer.NewSpan(ap.context(ctx), "actPool.Add") defer span.End() // Reject action if pool space is full @@ -246,7 +250,8 @@ func (ap *actPool) GetPendingNonce(addr string) (uint64, error) { if queue, ok := ap.accountActs[addr]; ok { return queue.PendingNonce(), nil } - confirmedState, err := accountutil.AccountState(ap.sf, addrStr) + ctx := ap.context(context.Background()) + confirmedState, err := accountutil.AccountState(ctx, ap.sf, addrStr) if err != nil { return 0, err } @@ -363,7 +368,7 @@ func (ap *actPool) validate(ctx context.Context, selp action.SealedEnvelope) err func (ap *actPool) enqueueAction(ctx context.Context, addr address.Address, act action.SealedEnvelope, actHash hash.Hash256, actNonce uint64) error { span := tracer.SpanFromContext(ctx) defer span.End() - confirmedState, err := accountutil.AccountState(ap.sf, addr) + confirmedState, err := accountutil.AccountState(ctx, ap.sf, addr) if err != nil { _actpoolMtc.WithLabelValues("failedToGetNonce").Inc() return errors.Wrapf(err, "failed to get sender's nonce for action %x", actHash) @@ -444,10 +449,10 @@ func (ap *actPool) enqueueAction(ctx context.Context, addr address.Address, act } // removeConfirmedActs removes processed (committed to block) actions from pool -func (ap *actPool) removeConfirmedActs() { +func (ap *actPool) removeConfirmedActs(ctx context.Context) { for from, queue := range ap.accountActs { addr, _ := address.FromString(from) - confirmedState, err := accountutil.AccountState(ap.sf, addr) + confirmedState, err := accountutil.AccountState(ctx, ap.sf, addr) if err != nil { log.L().Error("Error when removing confirmed actions", zap.Error(err)) return @@ -512,16 +517,21 @@ func (ap *actPool) updateAccount(sender string) { } } +func (ap *actPool) context(ctx context.Context) context.Context { + return genesis.WithGenesisContext(ctx, ap.g) +} + func (ap *actPool) reset() { timer := ap.timerFactory.NewTimer("reset") defer timer.End() + ctx := ap.context(context.Background()) // Remove confirmed actions in actpool - ap.removeConfirmedActs() + ap.removeConfirmedActs(ctx) for from, queue := range ap.accountActs { // Reset pending balance for each account addr, _ := address.FromString(from) - state, err := accountutil.AccountState(ap.sf, addr) + state, err := accountutil.AccountState(ctx, ap.sf, addr) if err != nil { log.L().Error("Error when resetting actpool state.", zap.Error(err)) return diff --git a/actpool/actpool_test.go b/actpool/actpool_test.go index 65e5160571..539fa89a85 100644 --- a/actpool/actpool_test.go +++ b/actpool/actpool_test.go @@ -64,25 +64,25 @@ func TestActPool_NewActPool(t *testing.T) { cfg := config.Default //error caused by nil blockchain - _, err := NewActPool(nil, cfg.ActPool, nil) + _, err := NewActPool(cfg.Genesis, nil, cfg.ActPool, nil) require.Error(err) // all good opt := EnableExperimentalActions() require.Panics(func() { blockchain.NewBlockchain(cfg, nil, nil, nil) }, "option is nil") sf := mock_chainmanager.NewMockStateReader(ctrl) - act, err := NewActPool(sf, cfg.ActPool, opt) + act, err := NewActPool(cfg.Genesis, sf, cfg.ActPool, opt) require.NoError(err) require.NotNil(act) // panic caused by option is nil - require.Panics(func() { NewActPool(sf, cfg.ActPool, nil) }, "option is nil") + require.Panics(func() { NewActPool(cfg.Genesis, sf, cfg.ActPool, nil) }, "option is nil") // error caused by option opt2 := func(pool *actPool) error { return errors.New("test error") } - _, err = NewActPool(sf, cfg.ActPool, opt2) + _, err = NewActPool(cfg.Genesis, sf, cfg.ActPool, opt2) require.Error(err) // test AddAction nil @@ -106,7 +106,7 @@ func TestValidate(t *testing.T) { mockError := errors.New("mock error") sev.EXPECT().Validate(gomock.Any(), gomock.Any()).Return(mockError).Times(1) apConfig := getActPoolCfg() - Ap, err := NewActPool(sf, apConfig, EnableExperimentalActions()) + Ap, err := NewActPool(cfg.Genesis, sf, apConfig, EnableExperimentalActions()) require.NoError(err) ap, ok := Ap.(*actPool) require.True(ok) @@ -140,9 +140,10 @@ func TestActPool_AddActs(t *testing.T) { } return 0, nil }).AnyTimes() + sf.EXPECT().Height().Return(uint64(1), nil).AnyTimes() // Create actpool apConfig := getActPoolCfg() - Ap, err := NewActPool(sf, apConfig, EnableExperimentalActions()) + Ap, err := NewActPool(genesis.Default, sf, apConfig, EnableExperimentalActions()) require.NoError(err) ap, ok := Ap.(*actPool) require.True(ok) @@ -165,7 +166,7 @@ func TestActPool_AddActs(t *testing.T) { tsf8, err := action.SignedTransfer(_addr2, _priKey2, uint64(4), big.NewInt(5), []byte{}, uint64(100000), big.NewInt(0)) require.NoError(err) - ctx := context.Background() + ctx := genesis.WithGenesisContext(context.Background(), config.Default.Genesis) require.NoError(ap.Add(ctx, tsf1)) require.NoError(ap.Add(ctx, tsf2)) require.NoError(ap.Add(ctx, tsf3)) @@ -202,7 +203,7 @@ func TestActPool_AddActs(t *testing.T) { require.Error(ap.Add(ctx, tsf1)) require.Error(ap.Add(ctx, tsf4)) // Case III: Pool space/gas space is full - Ap2, err := NewActPool(sf, apConfig, EnableExperimentalActions()) + Ap2, err := NewActPool(genesis.Default, sf, apConfig, EnableExperimentalActions()) require.NoError(err) ap2, ok := Ap2.(*actPool) require.True(ok) @@ -218,7 +219,7 @@ func TestActPool_AddActs(t *testing.T) { err = ap2.Add(ctx, tsf4) require.Equal(action.ErrTxPoolOverflow, errors.Cause(err)) - Ap3, err := NewActPool(sf, apConfig) + Ap3, err := NewActPool(genesis.Default, sf, apConfig) require.NoError(err) ap3, ok := Ap3.(*actPool) require.True(ok) @@ -293,9 +294,10 @@ func TestActPool_PickActs(t *testing.T) { ctrl := gomock.NewController(t) require := require.New(t) sf := mock_chainmanager.NewMockStateReader(ctrl) + ctx := genesis.WithGenesisContext(context.Background(), config.Default.Genesis) createActPool := func(cfg config.ActPool) (*actPool, []action.SealedEnvelope, []action.SealedEnvelope, []action.SealedEnvelope) { // Create actpool - Ap, err := NewActPool(sf, cfg, EnableExperimentalActions()) + Ap, err := NewActPool(genesis.Default, sf, cfg, EnableExperimentalActions()) require.NoError(err) ap, ok := Ap.(*actPool) require.True(ok) @@ -336,17 +338,18 @@ func TestActPool_PickActs(t *testing.T) { } return 0, nil }).AnyTimes() - require.NoError(ap.Add(context.Background(), tsf1)) - require.NoError(ap.Add(context.Background(), tsf2)) - require.NoError(ap.Add(context.Background(), tsf3)) - require.NoError(ap.Add(context.Background(), tsf4)) - require.Equal(action.ErrInsufficientFunds, errors.Cause(ap.Add(context.Background(), tsf5))) - require.Error(ap.Add(context.Background(), tsf6)) - - require.Error(ap.Add(context.Background(), tsf7)) - require.NoError(ap.Add(context.Background(), tsf8)) - require.NoError(ap.Add(context.Background(), tsf9)) - require.NoError(ap.Add(context.Background(), tsf10)) + sf.EXPECT().Height().Return(uint64(1), nil).AnyTimes() + require.NoError(ap.Add(ctx, tsf1)) + require.NoError(ap.Add(ctx, tsf2)) + require.NoError(ap.Add(ctx, tsf3)) + require.NoError(ap.Add(ctx, tsf4)) + require.Equal(action.ErrInsufficientFunds, errors.Cause(ap.Add(ctx, tsf5))) + require.Error(ap.Add(ctx, tsf6)) + + require.Error(ap.Add(ctx, tsf7)) + require.NoError(ap.Add(ctx, tsf8)) + require.NoError(ap.Add(ctx, tsf9)) + require.NoError(ap.Add(ctx, tsf10)) return ap, []action.SealedEnvelope{tsf1, tsf2, tsf3, tsf4}, []action.SealedEnvelope{}, []action.SealedEnvelope{} } @@ -374,7 +377,7 @@ func TestActPool_removeConfirmedActs(t *testing.T) { sf := mock_chainmanager.NewMockStateReader(ctrl) // Create actpool apConfig := getActPoolCfg() - Ap, err := NewActPool(sf, apConfig, EnableExperimentalActions()) + Ap, err := NewActPool(genesis.Default, sf, apConfig, EnableExperimentalActions()) require.NoError(err) ap, ok := Ap.(*actPool) require.True(ok) @@ -396,10 +399,12 @@ func TestActPool_removeConfirmedActs(t *testing.T) { return 0, nil }).Times(8) - require.NoError(ap.Add(context.Background(), tsf1)) - require.NoError(ap.Add(context.Background(), tsf2)) - require.NoError(ap.Add(context.Background(), tsf3)) - require.NoError(ap.Add(context.Background(), tsf4)) + sf.EXPECT().Height().Return(uint64(1), nil).AnyTimes() + ctx := genesis.WithGenesisContext(context.Background(), config.Default.Genesis) + require.NoError(ap.Add(ctx, tsf1)) + require.NoError(ap.Add(ctx, tsf2)) + require.NoError(ap.Add(ctx, tsf3)) + require.NoError(ap.Add(ctx, tsf4)) require.Equal(4, len(ap.allActions)) require.NotNil(ap.accountActs[_addr1]) @@ -407,13 +412,13 @@ func TestActPool_removeConfirmedActs(t *testing.T) { acct, ok := account.(*state.Account) require.True(ok) for i := uint64(1); i <= 4; i++ { - require.NoError(acct.SetNonce(i)) + require.NoError(acct.SetPendingNonce(i + 1)) } require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil }).Times(1) - ap.removeConfirmedActs() + ap.removeConfirmedActs(ctx) require.Equal(0, len(ap.allActions)) require.Nil(ap.accountActs[_addr1]) } @@ -442,39 +447,40 @@ func TestActPool_Reset(t *testing.T) { case bytes.Equal(cfg.Key, identityset.Address(28).Bytes()): require.NoError(acct.AddBalance(new(big.Int).Set(balances[0]))) for i := uint64(1); i <= nonces[0]; i++ { - require.NoError(acct.SetNonce(i)) + require.NoError(acct.SetPendingNonce(i + 1)) } case bytes.Equal(cfg.Key, identityset.Address(29).Bytes()): require.NoError(acct.AddBalance(new(big.Int).Set(balances[1]))) for i := uint64(1); i <= nonces[1]; i++ { - require.NoError(acct.SetNonce(i)) + require.NoError(acct.SetPendingNonce(i + 1)) } case bytes.Equal(cfg.Key, identityset.Address(30).Bytes()): require.NoError(acct.AddBalance(new(big.Int).Set(balances[2]))) for i := uint64(1); i <= nonces[2]; i++ { - require.NoError(acct.SetNonce(i)) + require.NoError(acct.SetPendingNonce(i + 1)) } case bytes.Equal(cfg.Key, identityset.Address(31).Bytes()): require.NoError(acct.AddBalance(new(big.Int).Set(balances[3]))) for i := uint64(1); i <= nonces[3]; i++ { - require.NoError(acct.SetNonce(i)) + require.NoError(acct.SetPendingNonce(i + 1)) } case bytes.Equal(cfg.Key, identityset.Address(32).Bytes()): require.NoError(acct.AddBalance(new(big.Int).Set(balances[4]))) for i := uint64(1); i <= nonces[4]; i++ { - require.NoError(acct.SetNonce(i)) + require.NoError(acct.SetPendingNonce(i + 1)) } } return 0, nil }).AnyTimes() + sf.EXPECT().Height().Return(uint64(1), nil).AnyTimes() apConfig := getActPoolCfg() - Ap1, err := NewActPool(sf, apConfig, EnableExperimentalActions()) + Ap1, err := NewActPool(genesis.Default, sf, apConfig, EnableExperimentalActions()) require.NoError(err) ap1, ok := Ap1.(*actPool) require.True(ok) ap1.AddActionEnvelopeValidators(protocol.NewGenericValidator(sf, accountutil.AccountState)) - Ap2, err := NewActPool(sf, apConfig, EnableExperimentalActions()) + Ap2, err := NewActPool(genesis.Default, sf, apConfig, EnableExperimentalActions()) require.NoError(err) ap2, ok := Ap2.(*actPool) require.True(ok) @@ -500,7 +506,7 @@ func TestActPool_Reset(t *testing.T) { tsf9, err := action.SignedTransfer(_addr1, _priKey3, uint64(4), big.NewInt(100), []byte{}, uint64(20000), big.NewInt(0)) require.NoError(err) - ctx := context.Background() + ctx := genesis.WithGenesisContext(context.Background(), config.Default.Genesis) require.NoError(ap1.Add(ctx, tsf1)) require.NoError(ap1.Add(ctx, tsf2)) err = ap1.Add(ctx, tsf3) @@ -787,7 +793,7 @@ func TestActPool_removeInvalidActs(t *testing.T) { sf := mock_chainmanager.NewMockStateReader(ctrl) // Create actpool apConfig := getActPoolCfg() - Ap, err := NewActPool(sf, apConfig, EnableExperimentalActions()) + Ap, err := NewActPool(genesis.Default, sf, apConfig, EnableExperimentalActions()) require.NoError(err) ap, ok := Ap.(*actPool) require.True(ok) @@ -808,10 +814,12 @@ func TestActPool_removeInvalidActs(t *testing.T) { return 0, nil }).Times(8) - require.NoError(ap.Add(context.Background(), tsf1)) - require.NoError(ap.Add(context.Background(), tsf2)) - require.NoError(ap.Add(context.Background(), tsf3)) - require.NoError(ap.Add(context.Background(), tsf4)) + sf.EXPECT().Height().Return(uint64(1), nil).AnyTimes() + ctx := genesis.WithGenesisContext(context.Background(), config.Default.Genesis) + require.NoError(ap.Add(ctx, tsf1)) + require.NoError(ap.Add(ctx, tsf2)) + require.NoError(ap.Add(ctx, tsf3)) + require.NoError(ap.Add(ctx, tsf4)) hash1, err := tsf1.Hash() require.NoError(err) @@ -831,7 +839,7 @@ func TestActPool_GetPendingNonce(t *testing.T) { sf := mock_chainmanager.NewMockStateReader(ctrl) // Create actpool apConfig := getActPoolCfg() - Ap, err := NewActPool(sf, apConfig, EnableExperimentalActions()) + Ap, err := NewActPool(genesis.Default, sf, apConfig, EnableExperimentalActions()) require.NoError(err) ap, ok := Ap.(*actPool) require.True(ok) @@ -852,10 +860,12 @@ func TestActPool_GetPendingNonce(t *testing.T) { return 0, nil }).Times(10) + sf.EXPECT().Height().Return(uint64(1), nil).AnyTimes() - require.NoError(ap.Add(context.Background(), tsf1)) - require.NoError(ap.Add(context.Background(), tsf3)) - require.NoError(ap.Add(context.Background(), tsf4)) + ctx := genesis.WithGenesisContext(context.Background(), config.Default.Genesis) + require.NoError(ap.Add(ctx, tsf1)) + require.NoError(ap.Add(ctx, tsf3)) + require.NoError(ap.Add(ctx, tsf4)) nonce, err := ap.GetPendingNonce(_addr2) require.NoError(err) @@ -865,7 +875,7 @@ func TestActPool_GetPendingNonce(t *testing.T) { require.NoError(err) require.Equal(uint64(2), nonce) - require.NoError(ap.Add(context.Background(), tsf2)) + require.NoError(ap.Add(ctx, tsf2)) nonce, err = ap.GetPendingNonce(_addr1) require.NoError(err) require.Equal(uint64(5), nonce) @@ -877,7 +887,7 @@ func TestActPool_GetUnconfirmedActs(t *testing.T) { sf := mock_chainmanager.NewMockStateReader(ctrl) // Create actpool apConfig := getActPoolCfg() - Ap, err := NewActPool(sf, apConfig, EnableExperimentalActions()) + Ap, err := NewActPool(genesis.Default, sf, apConfig, EnableExperimentalActions()) require.NoError(err) ap, ok := Ap.(*actPool) require.True(ok) @@ -899,10 +909,12 @@ func TestActPool_GetUnconfirmedActs(t *testing.T) { return 0, nil }).Times(10) - require.NoError(ap.Add(context.Background(), tsf1)) - require.NoError(ap.Add(context.Background(), tsf3)) - require.NoError(ap.Add(context.Background(), tsf4)) - require.NoError(ap.Add(context.Background(), tsf5)) + sf.EXPECT().Height().Return(uint64(1), nil).AnyTimes() + ctx := genesis.WithGenesisContext(context.Background(), config.Default.Genesis) + require.NoError(ap.Add(ctx, tsf1)) + require.NoError(ap.Add(ctx, tsf3)) + require.NoError(ap.Add(ctx, tsf4)) + require.NoError(ap.Add(ctx, tsf5)) acts := ap.GetUnconfirmedActs(_addr3) require.Equal([]action.SealedEnvelope(nil), acts) @@ -926,7 +938,7 @@ func TestActPool_GetActionByHash(t *testing.T) { sf := mock_chainmanager.NewMockStateReader(ctrl) // Create actpool apConfig := getActPoolCfg() - Ap, err := NewActPool(sf, apConfig, EnableExperimentalActions()) + Ap, err := NewActPool(genesis.Default, sf, apConfig, EnableExperimentalActions()) require.NoError(err) ap, ok := Ap.(*actPool) require.True(ok) @@ -960,7 +972,7 @@ func TestActPool_GetCapacity(t *testing.T) { sf := mock_chainmanager.NewMockStateReader(ctrl) // Create actpool apConfig := getActPoolCfg() - Ap, err := NewActPool(sf, apConfig, EnableExperimentalActions()) + Ap, err := NewActPool(genesis.Default, sf, apConfig, EnableExperimentalActions()) require.NoError(err) ap, ok := Ap.(*actPool) require.True(ok) @@ -974,7 +986,7 @@ func TestActPool_GetSize(t *testing.T) { sf := mock_chainmanager.NewMockStateReader(ctrl) // Create actpool apConfig := getActPoolCfg() - Ap, err := NewActPool(sf, apConfig, EnableExperimentalActions()) + Ap, err := NewActPool(genesis.Default, sf, apConfig, EnableExperimentalActions()) require.NoError(err) ap, ok := Ap.(*actPool) require.True(ok) @@ -998,23 +1010,25 @@ func TestActPool_GetSize(t *testing.T) { return 0, nil }).Times(8) - require.NoError(ap.Add(context.Background(), tsf1)) - require.NoError(ap.Add(context.Background(), tsf2)) - require.NoError(ap.Add(context.Background(), tsf3)) - require.NoError(ap.Add(context.Background(), tsf4)) + sf.EXPECT().Height().Return(uint64(1), nil).AnyTimes() + ctx := genesis.WithGenesisContext(context.Background(), config.Default.Genesis) + require.NoError(ap.Add(ctx, tsf1)) + require.NoError(ap.Add(ctx, tsf2)) + require.NoError(ap.Add(ctx, tsf3)) + require.NoError(ap.Add(ctx, tsf4)) require.Equal(uint64(4), ap.GetSize()) require.Equal(uint64(40000), ap.GetGasSize()) sf.EXPECT().State(gomock.Any(), gomock.Any()).DoAndReturn(func(account interface{}, opts ...protocol.StateOption) (uint64, error) { acct, ok := account.(*state.Account) require.True(ok) for i := uint64(1); i <= 4; i++ { - require.NoError(acct.SetNonce(i)) + require.NoError(acct.SetPendingNonce(i + 1)) } require.NoError(acct.AddBalance(big.NewInt(100000000000000000))) return 0, nil }).Times(1) - ap.removeConfirmedActs() + ap.removeConfirmedActs(ctx) require.Equal(uint64(0), ap.GetSize()) require.Equal(uint64(0), ap.GetGasSize()) } @@ -1024,7 +1038,7 @@ func TestActPool_AddActionNotEnoughGasPrice(t *testing.T) { sf := mock_chainmanager.NewMockStateReader(ctrl) apConfig := config.Default.ActPool - ap, err := NewActPool(sf, apConfig, EnableExperimentalActions()) + ap, err := NewActPool(genesis.Default, sf, apConfig, EnableExperimentalActions()) require.NoError(t, err) tsf, err := action.SignedTransfer( identityset.Address(0).String(), @@ -1038,6 +1052,7 @@ func TestActPool_AddActionNotEnoughGasPrice(t *testing.T) { require.NoError(t, err) ctx := protocol.WithBlockchainCtx(context.Background(), protocol.BlockchainCtx{}) + ctx = genesis.WithGenesisContext(ctx, config.Default.Genesis) require.Error(t, ap.Add(ctx, tsf)) } @@ -1055,10 +1070,11 @@ func TestActPool_SpeedUpAction(t *testing.T) { require.NoError(acct.AddBalance(big.NewInt(10000000))) return 0, nil }).AnyTimes() + sf.EXPECT().Height().Return(uint64(1), nil).AnyTimes() // Create actpool apConfig := getActPoolCfg() - Ap, err := NewActPool(sf, apConfig, EnableExperimentalActions()) + Ap, err := NewActPool(genesis.Default, sf, apConfig, EnableExperimentalActions()) require.NoError(err) ap, ok := Ap.(*actPool) require.True(ok) @@ -1072,7 +1088,7 @@ func TestActPool_SpeedUpAction(t *testing.T) { require.NoError(err) // A send action tsf1 with nonce 1, B send action tsf2 with nonce 1 - ctx := context.Background() + ctx := genesis.WithGenesisContext(context.Background(), config.Default.Genesis) require.NoError(ap.Add(ctx, tsf1)) require.NoError(ap.Add(ctx, tsf2)) @@ -1123,7 +1139,11 @@ func (ap *actPool) getPendingNonce(addr string) (uint64, error) { if err != nil { return 0, err } - committedState, err := accountutil.AccountState(ap.sf, _addr1) + committedState, err := accountutil.AccountState( + genesis.WithGenesisContext(context.Background(), config.Default.Genesis), + ap.sf, + _addr1, + ) if err != nil { return 0, err } @@ -1139,7 +1159,11 @@ func (ap *actPool) getPendingBalance(addr string) (*big.Int, error) { if err != nil { return nil, err } - state, err := accountutil.AccountState(ap.sf, _addr1) + state, err := accountutil.AccountState( + genesis.WithGenesisContext(context.Background(), config.Default.Genesis), + ap.sf, + _addr1, + ) if err != nil { return nil, err } diff --git a/actpool/actqueue.go b/actpool/actqueue.go index 074924a184..c72af9cbb8 100644 --- a/actpool/actqueue.go +++ b/actpool/actqueue.go @@ -8,6 +8,7 @@ package actpool import ( "container/heap" + "context" "math/big" "sort" "time" @@ -68,7 +69,7 @@ type ActQueue interface { PendingBalance() *big.Int Len() int Empty() bool - PendingActs() []action.SealedEnvelope + PendingActs(context.Context) []action.SealedEnvelope AllActs() []action.SealedEnvelope } @@ -250,7 +251,7 @@ func (q *actQueue) Empty() bool { } // PendingActs creates a consecutive nonce-sorted slice of actions -func (q *actQueue) PendingActs() []action.SealedEnvelope { +func (q *actQueue) PendingActs(ctx context.Context) []action.SealedEnvelope { if q.Len() == 0 { return nil } @@ -260,7 +261,7 @@ func (q *actQueue) PendingActs() []action.SealedEnvelope { log.L().Error("Error when getting the address", zap.String("address", q.address), zap.Error(err)) return nil } - confirmedState, err := accountutil.AccountState(q.ap.sf, addr) + confirmedState, err := accountutil.AccountState(ctx, q.ap.sf, addr) if err != nil { log.L().Error("Error when getting the nonce", zap.String("address", q.address), zap.Error(err)) return nil diff --git a/actpool/actqueue_test.go b/actpool/actqueue_test.go index effd589d14..3e52da2b5e 100644 --- a/actpool/actqueue_test.go +++ b/actpool/actqueue_test.go @@ -7,6 +7,7 @@ package actpool import ( "container/heap" + "context" "fmt" "math/big" "math/rand" @@ -20,6 +21,7 @@ import ( "github.com/iotexproject/iotex-core/action" "github.com/iotexproject/iotex-core/action/protocol" + "github.com/iotexproject/iotex-core/blockchain/genesis" "github.com/iotexproject/iotex-core/config" "github.com/iotexproject/iotex-core/state" "github.com/iotexproject/iotex-core/test/identityset" @@ -129,9 +131,11 @@ func TestActQueuePendingActs(t *testing.T) { cfg := config.Default sf := mock_chainmanager.NewMockStateReader(ctrl) sf.EXPECT().State(gomock.Any(), gomock.Any()).Do(func(accountState *state.Account, _ protocol.StateOption) { - require.NoError(accountState.SetNonce(accountState.PendingNonce())) + require.NoError(accountState.SetPendingNonce(accountState.PendingNonce() + 1)) }).Return(uint64(0), nil).Times(1) - ap, err := NewActPool(sf, cfg.ActPool, EnableExperimentalActions()) + sf.EXPECT().Height().Return(uint64(1), nil).AnyTimes() + ctx := genesis.WithGenesisContext(context.Background(), config.Default.Genesis) + ap, err := NewActPool(cfg.Genesis, sf, cfg.ActPool, EnableExperimentalActions()) require.NoError(err) q := NewActQueue(ap.(*actPool), identityset.Address(0).String()).(*actQueue) tsf1, err := action.SignedTransfer(_addr2, _priKey1, 2, big.NewInt(100), nil, uint64(0), big.NewInt(0)) @@ -150,7 +154,7 @@ func TestActQueuePendingActs(t *testing.T) { require.NoError(q.Put(tsf4)) require.NoError(q.Put(tsf5)) q.pendingNonce = 4 - actions := q.PendingActs() + actions := q.PendingActs(ctx) require.Equal([]action.SealedEnvelope{tsf1, tsf2}, actions) } diff --git a/api/coreservice.go b/api/coreservice.go index de8cc5e608..dabd54f099 100644 --- a/api/coreservice.go +++ b/api/coreservice.go @@ -251,7 +251,8 @@ func (core *coreService) Account(addr address.Address) (*iotextypes.AccountMeta, return core.getProtocolAccount(ctx, addrStr) } span.AddEvent("accountutil.AccountStateWithHeight") - state, tipHeight, err := accountutil.AccountStateWithHeight(core.sf, addr) + ctx = genesis.WithGenesisContext(ctx, core.bc.Genesis()) + state, tipHeight, err := accountutil.AccountStateWithHeight(ctx, core.sf, addr) if err != nil { return nil, nil, status.Error(codes.NotFound, err.Error()) } @@ -395,7 +396,7 @@ func (core *coreService) SendAction(ctx context.Context, in *iotextypes.Action) } // Add to local actpool - ctx = protocol.WithRegistry(ctx, core.registry) + ctx = genesis.WithGenesisContext(protocol.WithRegistry(ctx, core.registry), core.bc.Genesis()) hash, err := selp.Hash() if err != nil { return "", err @@ -453,7 +454,8 @@ func (core *coreService) ReadContract(ctx context.Context, callerAddr address.Ad return res.Data, res.Receipt, nil } } - state, err := accountutil.AccountState(core.sf, callerAddr) + ctx = genesis.WithGenesisContext(ctx, core.bc.Genesis()) + state, err := accountutil.AccountState(ctx, core.sf, callerAddr) if err != nil { return "", nil, status.Error(codes.InvalidArgument, err.Error()) } @@ -1366,7 +1368,7 @@ func (core *coreService) EstimateGasForNonExecution(actType action.Action) (uint // EstimateExecutionGasConsumption estimate gas consumption for execution action func (core *coreService) EstimateExecutionGasConsumption(ctx context.Context, sc *action.Execution, callerAddr address.Address) (uint64, error) { - state, err := accountutil.AccountState(core.sf, callerAddr) + state, err := accountutil.AccountState(genesis.WithGenesisContext(ctx, core.bc.Genesis()), core.sf, callerAddr) if err != nil { return 0, status.Error(codes.InvalidArgument, err.Error()) } @@ -1557,7 +1559,8 @@ func (core *coreService) ReceiveBlock(blk *block.Block) error { } func (core *coreService) SimulateExecution(ctx context.Context, addr address.Address, exec *action.Execution) ([]byte, *action.Receipt, error) { - state, err := accountutil.AccountState(core.sf, addr) + ctx = genesis.WithGenesisContext(ctx, core.bc.Genesis()) + state, err := accountutil.AccountState(ctx, core.sf, addr) if err != nil { return nil, nil, err } diff --git a/api/grpcserver_integrity_test.go b/api/grpcserver_integrity_test.go index d85d6aaca7..fa2e18b6c3 100644 --- a/api/grpcserver_integrity_test.go +++ b/api/grpcserver_integrity_test.go @@ -2372,7 +2372,7 @@ func TestGrpcServer_GetEvmTransfersByBlockHeightIntegrity(t *testing.T) { func TestGrpcServer_GetActPoolActionsIntegrity(t *testing.T) { require := require.New(t) cfg := newConfig() - ctx := context.Background() + ctx := genesis.WithGenesisContext(context.Background(), cfg.Genesis) svr, _, _, _, _, actPool, bfIndexFile, err := createServerV2(cfg, false) require.NoError(err) diff --git a/api/serverV2_integrity_test.go b/api/serverV2_integrity_test.go index e580e05e82..180daed27a 100644 --- a/api/serverV2_integrity_test.go +++ b/api/serverV2_integrity_test.go @@ -285,7 +285,7 @@ func setupChain(cfg config.Config) (blockchain.Blockchain, blockdao.BlockDAO, bl if err != nil { return nil, nil, nil, nil, nil, nil, nil, "", err } - ap, err := setupActPool(sf, cfg.ActPool) + ap, err := setupActPool(cfg.Genesis, sf, cfg.ActPool) if err != nil { return nil, nil, nil, nil, nil, nil, nil, "", err } @@ -354,8 +354,8 @@ func setupChain(cfg config.Config) (blockchain.Blockchain, blockdao.BlockDAO, bl return bc, dao, indexer, bfIndexer, sf, ap, registry, testPath, nil } -func setupActPool(sf factory.Factory, cfg config.ActPool) (actpool.ActPool, error) { - ap, err := actpool.NewActPool(sf, cfg, actpool.EnableExperimentalActions()) +func setupActPool(g genesis.Genesis, sf factory.Factory, cfg config.ActPool) (actpool.ActPool, error) { + ap, err := actpool.NewActPool(g, sf, cfg, actpool.EnableExperimentalActions()) if err != nil { return nil, err } diff --git a/blockchain/block/validator_test.go b/blockchain/block/validator_test.go index e890c28fe4..8ddf9ff387 100644 --- a/blockchain/block/validator_test.go +++ b/blockchain/block/validator_test.go @@ -33,14 +33,15 @@ func TestValidator(t *testing.T) { v := NewValidator(nil) require.NoError(v.Validate(ctx, blk)) - valid := protocol.NewGenericValidator(nil, func(sr protocol.StateReader, addr address.Address) (*state.Account, error) { + valid := protocol.NewGenericValidator(nil, func(ctx context.Context, sr protocol.StateReader, addr address.Address) (*state.Account, error) { pk := identityset.PrivateKey(27).PublicKey() eAddr := pk.Address() if strings.EqualFold(eAddr.String(), addr.String()) { return nil, errors.New("MockChainManager nonce error") } - account := state.NewEmptyAccount() - require.NoError(account.SetNonce(2)) + account, err := state.NewAccount() + require.NoError(err) + require.NoError(account.SetPendingNonce(3)) return account, nil }) diff --git a/blockchain/integrity/benchmark_test.go b/blockchain/integrity/benchmark_test.go index eaf0d2a62d..cdba4e2e27 100644 --- a/blockchain/integrity/benchmark_test.go +++ b/blockchain/integrity/benchmark_test.go @@ -232,7 +232,7 @@ func newChainInDB() (blockchain.Blockchain, actpool.ActPool, error) { return nil, nil, err } - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) if err != nil { return nil, nil, err } diff --git a/blockchain/integrity/integrity_test.go b/blockchain/integrity/integrity_test.go index be3bdd903c..40f6096395 100644 --- a/blockchain/integrity/integrity_test.go +++ b/blockchain/integrity/integrity_test.go @@ -191,7 +191,7 @@ func addTestingConstantinopleBlocks(bc blockchain.Blockchain, dao blockdao.Block // Add block 8 // test store out of gas var ( - caller = state.NewEmptyAccount() + caller = &state.Account{} callerAddr = hash.BytesToHash160(identityset.Address(27).Bytes()) ) _, err = sf.State(caller, protocol.LegacyKeyOption(callerAddr)) @@ -245,7 +245,7 @@ func addTestingConstantinopleBlocks(bc blockchain.Blockchain, dao blockdao.Block } func addTestingTsfBlocks(cfg config.Config, bc blockchain.Blockchain, dao blockdao.BlockDAO, ap actpool.ActPool) error { - ctx := context.Background() + ctx := genesis.WithGenesisContext(context.Background(), cfg.Genesis) addOneTsf := func(recipientAddr string, senderPriKey iotexcrypto.PrivateKey, nonce uint64, amount *big.Int, payload []byte, gasLimit uint64, gasPrice *big.Int) error { tx, err := action.SignedTransfer(recipientAddr, senderPriKey, nonce, amount, payload, gasLimit, gasPrice) if err != nil { @@ -318,7 +318,7 @@ func addTestingTsfBlocks(cfg config.Config, bc blockchain.Blockchain, dao blockd if err != nil { return err } - if err := ap.Add(context.Background(), ex1); err != nil { + if err := ap.Add(ctx, ex1); err != nil { return err } _deployHash, err = ex1.Hash() @@ -481,7 +481,7 @@ func TestCreateBlockchain(t *testing.T) { require.NoError(rp.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NoError(err) dao := blockdao.NewBlockDAOInMemForTest([]blockdao.BlockIndexer{sf}) bc := blockchain.NewBlockchain( @@ -532,7 +532,7 @@ func TestGetBlockHash(t *testing.T) { require.NoError(rp.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NoError(err) dao := blockdao.NewBlockDAOInMemForTest([]blockdao.BlockIndexer{sf}) bc := blockchain.NewBlockchain( @@ -577,11 +577,12 @@ func addTestingGetBlockHash(t *testing.T, g genesis.Genesis, bc blockchain.Block } } */ + ctx := genesis.WithGenesisContext(context.Background(), g) data, _ := hex.DecodeString("6080604052348015600f57600080fd5b5060de8061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063ee82ac5e14602d575b600080fd5b605660048036036020811015604157600080fd5b8101908080359060200190929190505050606c565b6040518082815260200191505060405180910390f35b60008082409050807f2d93f7749862d33969fb261757410b48065a1bc86a56da5c47820bd063e2338260405160405180910390a28091505091905056fea265627a7a723158200a258cd08ea99ee11aa68c78b6d2bf7ea912615a1e64a81b90a2abca2dd59cfa64736f6c634300050c0032") ex1, err := action.SignedExecution(action.EmptyAddress, priKey0, 1, big.NewInt(0), 500000, big.NewInt(testutil.TestGasPriceInt64), data) require.NoError(err) - require.NoError(ap.Add(context.Background(), ex1)) + require.NoError(ap.Add(ctx, ex1)) _deployHash, err = ex1.Hash() require.NoError(err) blk, err := bc.MintNewBlock(testutil.TimestampNow()) @@ -603,7 +604,7 @@ func addTestingGetBlockHash(t *testing.T, g genesis.Genesis, bc blockchain.Block return hash.ZeroHash256, err } blockTime = blockTime.Add(time.Second) - if err := ap.Add(context.Background(), ex1); err != nil { + if err := ap.Add(ctx, ex1); err != nil { return hash.ZeroHash256, err } blk, err = bc.MintNewBlock(blockTime) @@ -680,11 +681,11 @@ func addTestingGetBlockHash(t *testing.T, g genesis.Genesis, bc blockchain.Block } func TestBlockchain_MintNewBlock(t *testing.T) { - ctx := context.Background() cfg := config.Default cfg.Genesis.BlockGasLimit = uint64(100000) cfg.Genesis.EnableGravityChainVoting = false cfg.ActPool.MinGasPriceStr = "0" + ctx := genesis.WithGenesisContext(context.Background(), cfg.Genesis) registry := protocol.NewRegistry() acc := account.NewProtocol(rewarding.DepositGas) require.NoError(t, acc.Register(registry)) @@ -692,7 +693,7 @@ func TestBlockchain_MintNewBlock(t *testing.T) { require.NoError(t, rp.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(t, err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NoError(t, err) dao := blockdao.NewBlockDAOInMemForTest([]blockdao.BlockIndexer{sf}) bc := blockchain.NewBlockchain( @@ -733,7 +734,7 @@ func TestBlockchain_MintNewBlock(t *testing.T) { SetGasPrice(big.NewInt(10)).Build() selp1, err := action.Sign(elp1, identityset.PrivateKey(0)) require.NoError(t, err) - require.NoError(t, ap.Add(context.Background(), selp1)) + require.NoError(t, ap.Add(ctx, selp1)) // This execution should not be included in block because block is out of gas elp2 := bd.SetAction(execution). SetNonce(2). @@ -741,7 +742,7 @@ func TestBlockchain_MintNewBlock(t *testing.T) { SetGasPrice(big.NewInt(10)).Build() selp2, err := action.Sign(elp2, identityset.PrivateKey(0)) require.NoError(t, err) - require.NoError(t, ap.Add(context.Background(), selp2)) + require.NoError(t, ap.Add(ctx, selp2)) blk, err := bc.MintNewBlock(testutil.TimestampNow()) require.NoError(t, err) @@ -755,16 +756,16 @@ func TestBlockchain_MintNewBlock(t *testing.T) { } func TestBlockchain_MintNewBlock_PopAccount(t *testing.T) { - ctx := context.Background() cfg := config.Default cfg.Genesis.EnableGravityChainVoting = false cfg.ActPool.MinGasPriceStr = "0" + ctx := genesis.WithGenesisContext(context.Background(), cfg.Genesis) registry := protocol.NewRegistry() acc := account.NewProtocol(rewarding.DepositGas) require.NoError(t, acc.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(t, err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NoError(t, err) dao := blockdao.NewBlockDAOInMemForTest([]blockdao.BlockIndexer{sf}) bc := blockchain.NewBlockchain( @@ -801,12 +802,12 @@ func TestBlockchain_MintNewBlock_PopAccount(t *testing.T) { tsf, err := action.SignedTransfer(addr1, priKey0, i+7, big.NewInt(2), bytes, 19000, big.NewInt(testutil.TestGasPriceInt64)) require.NoError(t, err) - require.NoError(t, ap.Add(context.Background(), tsf)) + require.NoError(t, ap.Add(ctx, tsf)) } transfer1, err := action.SignedTransfer(addr1, priKey3, 7, big.NewInt(2), []byte{}, 10000, big.NewInt(testutil.TestGasPriceInt64)) require.NoError(t, err) - require.NoError(t, ap.Add(context.Background(), transfer1)) + require.NoError(t, ap.Add(ctx, transfer1)) blk, err := bc.MintNewBlock(testutil.TimestampNow()) require.NoError(t, err) @@ -854,7 +855,7 @@ func TestConstantinople(t *testing.T) { // Create a blockchain from scratch sf, err := factory.NewFactory(cfg, factory.DefaultTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NoError(err) acc := account.NewProtocol(rewarding.DepositGas) require.NoError(acc.Register(registry)) @@ -1088,13 +1089,13 @@ func TestConstantinople(t *testing.T) { func TestLoadBlockchainfromDB(t *testing.T) { require := require.New(t) testValidateBlockchain := func(cfg config.Config, t *testing.T) { - ctx := context.Background() + ctx := genesis.WithGenesisContext(context.Background(), cfg.Genesis) registry := protocol.NewRegistry() // Create a blockchain from scratch sf, err := factory.NewFactory(cfg, factory.DefaultTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NoError(err) acc := account.NewProtocol(rewarding.DepositGas) require.NoError(acc.Register(registry)) @@ -1226,7 +1227,7 @@ func TestLoadBlockchainfromDB(t *testing.T) { // valid but unused address should return empty account addr, err := address.FromString("io1066kus4vlyvk0ljql39fzwqw0k22h7j8wmef3n") require.NoError(err) - act, err := accountutil.AccountState(sf, addr) + act, err := accountutil.AccountState(ctx, sf, addr) require.NoError(err) require.Equal(uint64(1), act.PendingNonce()) require.Equal(big.NewInt(0), act.Balance) @@ -1405,7 +1406,7 @@ func TestBlockchainInitialCandidate(t *testing.T) { registry := protocol.NewRegistry() sf, err := factory.NewFactory(cfg, factory.DefaultTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NoError(err) accountProtocol := account.NewProtocol(rewarding.DepositGas) require.NoError(accountProtocol.Register(registry)) @@ -1449,18 +1450,19 @@ func TestBlockchain_AccountState(t *testing.T) { // disable account-based testing // create chain cfg.Genesis.InitBalanceMap[identityset.Address(0).String()] = "100" + ctx := genesis.WithGenesisContext(context.Background(), cfg.Genesis) registry := protocol.NewRegistry() acc := account.NewProtocol(rewarding.DepositGas) require.NoError(acc.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NoError(err) dao := blockdao.NewBlockDAOInMemForTest([]blockdao.BlockIndexer{sf}) bc := blockchain.NewBlockchain(cfg, dao, factory.NewMinter(sf, ap)) require.NoError(bc.Start(context.Background())) require.NotNil(bc) - s, err := accountutil.AccountState(sf, identityset.Address(0)) + s, err := accountutil.AccountState(ctx, sf, identityset.Address(0)) require.NoError(err) require.Equal(uint64(1), s.PendingNonce()) require.Equal(big.NewInt(100), s.Balance) @@ -1497,7 +1499,7 @@ func TestBlocks(t *testing.T) { require.NoError(acc.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NoError(err) dbcfg := cfg.DB dbcfg.DbPath = cfg.Chain.ChainDBPath @@ -1570,7 +1572,7 @@ func TestActions(t *testing.T) { sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NoError(err) dbcfg := cfg.DB dbcfg.DbPath = cfg.Chain.ChainDBPath @@ -1633,7 +1635,7 @@ func TestBlockchain_AddRemoveSubscriber(t *testing.T) { registry := protocol.NewRegistry() sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) req.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) req.NoError(err) dao := blockdao.NewBlockDAOInMemForTest([]blockdao.BlockIndexer{sf}) bc := blockchain.NewBlockchain(cfg, dao, factory.NewMinter(sf, ap)) @@ -1658,11 +1660,12 @@ func testHistoryForAccount(t *testing.T, statetx bool) { a := identityset.Address(28) priKeyA := identityset.PrivateKey(28) b := identityset.Address(29) + ctx := genesis.WithGenesisContext(context.Background(), bc.Genesis()) // check the original balance a and b before transfer - AccountA, err := accountutil.AccountState(sf, a) + AccountA, err := accountutil.AccountState(ctx, sf, a) require.NoError(err) - AccountB, err := accountutil.AccountState(sf, b) + AccountB, err := accountutil.AccountState(ctx, sf, b) require.NoError(err) require.Equal(big.NewInt(100), AccountA.Balance) require.Equal(big.NewInt(100), AccountB.Balance) @@ -1679,23 +1682,23 @@ func testHistoryForAccount(t *testing.T, statetx bool) { require.NoError(bc.CommitBlock(blk)) // check balances after transfer - AccountA, err = accountutil.AccountState(sf, a) + AccountA, err = accountutil.AccountState(ctx, sf, a) require.NoError(err) - AccountB, err = accountutil.AccountState(sf, b) + AccountB, err = accountutil.AccountState(ctx, sf, b) require.NoError(err) require.Equal(big.NewInt(90), AccountA.Balance) require.Equal(big.NewInt(110), AccountB.Balance) // check history account's balance if statetx { - _, err = accountutil.AccountState(factory.NewHistoryStateReader(sf, bc.TipHeight()-1), a) + _, err = accountutil.AccountState(ctx, factory.NewHistoryStateReader(sf, bc.TipHeight()-1), a) require.Equal(factory.ErrNotSupported, errors.Cause(err)) - _, err = accountutil.AccountState(factory.NewHistoryStateReader(sf, bc.TipHeight()-1), b) + _, err = accountutil.AccountState(ctx, factory.NewHistoryStateReader(sf, bc.TipHeight()-1), b) require.Equal(factory.ErrNotSupported, errors.Cause(err)) } else { - AccountA, err = accountutil.AccountState(factory.NewHistoryStateReader(sf, bc.TipHeight()-1), a) + AccountA, err = accountutil.AccountState(ctx, factory.NewHistoryStateReader(sf, bc.TipHeight()-1), a) require.NoError(err) - AccountB, err = accountutil.AccountState(factory.NewHistoryStateReader(sf, bc.TipHeight()-1), b) + AccountB, err = accountutil.AccountState(ctx, factory.NewHistoryStateReader(sf, bc.TipHeight()-1), b) require.NoError(err) require.Equal(big.NewInt(100), AccountA.Balance) require.Equal(big.NewInt(100), AccountB.Balance) @@ -1710,13 +1713,14 @@ func TestHistoryForContract(t *testing.T) { func testHistoryForContract(t *testing.T, statetx bool) { require := require.New(t) bc, sf, _, dao, ap := newChain(t, statetx) + ctx := genesis.WithGenesisContext(context.Background(), bc.Genesis()) genesisAccount := identityset.Address(27).String() // deploy and get contract address contract := deployXrc20(bc, dao, ap, t) contractAddr, err := address.FromString(contract) require.NoError(err) - account, err := accountutil.AccountState(sf, contractAddr) + account, err := accountutil.AccountState(ctx, sf, contractAddr) require.NoError(err) // check the original balance balance := BalanceOfContract(contract, genesisAccount, sf, t, account.Root) @@ -1725,7 +1729,7 @@ func testHistoryForContract(t *testing.T, statetx bool) { require.Equal(expect, balance) // make a transfer for contract makeTransfer(contract, bc, ap, t) - account, err = accountutil.AccountState(sf, contractAddr) + account, err = accountutil.AccountState(ctx, sf, contractAddr) require.NoError(err) // check the balance after transfer balance = BalanceOfContract(contract, genesisAccount, sf, t, account.Root) @@ -1735,11 +1739,11 @@ func testHistoryForContract(t *testing.T, statetx bool) { // check the the original balance again if statetx { - _, err = accountutil.AccountState(factory.NewHistoryStateReader(sf, bc.TipHeight()-1), contractAddr) + _, err = accountutil.AccountState(ctx, factory.NewHistoryStateReader(sf, bc.TipHeight()-1), contractAddr) require.True(errors.Cause(err) == factory.ErrNotSupported) } else { sr := factory.NewHistoryStateReader(sf, bc.TipHeight()-1) - account, err = accountutil.AccountState(sr, contractAddr) + account, err = accountutil.AccountState(ctx, sr, contractAddr) require.NoError(err) balance = BalanceOfContract(contract, genesisAccount, sr, t, account.Root) expect, ok = new(big.Int).SetString("2000000000000000000000000000", 10) @@ -1842,7 +1846,7 @@ func newChain(t *testing.T, stateTX bool) (blockchain.Blockchain, factory.Factor sf, err = factory.NewFactory(cfg, factory.PrecreatedTrieDBOption(kv), factory.RegistryOption(registry)) require.NoError(err) } - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NoError(err) acc := account.NewProtocol(rewarding.DepositGas) require.NoError(acc.Register(registry)) diff --git a/blocksync/blocksync_test.go b/blocksync/blocksync_test.go index a788046d65..3e33a68a6c 100644 --- a/blocksync/blocksync_test.go +++ b/blocksync/blocksync_test.go @@ -179,7 +179,7 @@ func TestBlockSyncerProcessBlockTipHeight(t *testing.T) { require.NoError(rp.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool, actpool.EnableExperimentalActions()) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool, actpool.EnableExperimentalActions()) require.NotNil(ap) require.NoError(err) ap.AddActionEnvelopeValidators(protocol.NewGenericValidator(sf, accountutil.AccountState)) @@ -241,7 +241,7 @@ func TestBlockSyncerProcessBlockOutOfOrder(t *testing.T) { require.NoError(rp.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap1, err := actpool.NewActPool(sf, cfg.ActPool, actpool.EnableExperimentalActions()) + ap1, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool, actpool.EnableExperimentalActions()) require.NotNil(ap1) require.NoError(err) ap1.AddActionEnvelopeValidators(protocol.NewGenericValidator(sf, accountutil.AccountState)) @@ -265,7 +265,7 @@ func TestBlockSyncerProcessBlockOutOfOrder(t *testing.T) { require.NoError(rp.Register(registry2)) sf2, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry2)) require.NoError(err) - ap2, err := actpool.NewActPool(sf2, cfg.ActPool, actpool.EnableExperimentalActions()) + ap2, err := actpool.NewActPool(cfg.Genesis, sf2, cfg.ActPool, actpool.EnableExperimentalActions()) require.NotNil(ap2) require.NoError(err) ap2.AddActionEnvelopeValidators(protocol.NewGenericValidator(sf2, accountutil.AccountState)) @@ -336,7 +336,7 @@ func TestBlockSyncerProcessBlock(t *testing.T) { require.NoError(rolldposProtocol.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap1, err := actpool.NewActPool(sf, cfg.ActPool) + ap1, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NotNil(ap1) require.NoError(err) ap1.AddActionEnvelopeValidators(protocol.NewGenericValidator(sf, accountutil.AccountState)) @@ -359,7 +359,7 @@ func TestBlockSyncerProcessBlock(t *testing.T) { require.NoError(rolldposProtocol.Register(registry2)) sf2, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry2)) require.NoError(err) - ap2, err := actpool.NewActPool(sf2, cfg.ActPool, actpool.EnableExperimentalActions()) + ap2, err := actpool.NewActPool(cfg.Genesis, sf2, cfg.ActPool, actpool.EnableExperimentalActions()) require.NotNil(ap2) require.NoError(err) ap2.AddActionEnvelopeValidators(protocol.NewGenericValidator(sf2, accountutil.AccountState)) @@ -423,7 +423,7 @@ func TestBlockSyncerSync(t *testing.T) { require.NoError(rp.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool, actpool.EnableExperimentalActions()) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool, actpool.EnableExperimentalActions()) require.NotNil(ap) require.NoError(err) ap.AddActionEnvelopeValidators(protocol.NewGenericValidator(sf, accountutil.AccountState)) @@ -508,7 +508,7 @@ func TestBlockSyncerPeerBlockList(t *testing.T) { require.NoError(rp.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool, actpool.EnableExperimentalActions()) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool, actpool.EnableExperimentalActions()) require.NotNil(ap) require.NoError(err) ap.AddActionEnvelopeValidators(protocol.NewGenericValidator(sf, accountutil.AccountState)) diff --git a/blocksync/buffer_test.go b/blocksync/buffer_test.go index b9e159e1ac..cae44c0e7c 100644 --- a/blocksync/buffer_test.go +++ b/blocksync/buffer_test.go @@ -42,7 +42,7 @@ func TestBlockBufferFlush(t *testing.T) { require.NoError(rp.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool, actpool.EnableExperimentalActions()) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool, actpool.EnableExperimentalActions()) require.NotNil(ap) require.NoError(err) ap.AddActionEnvelopeValidators(protocol.NewGenericValidator(sf, accountutil.AccountState)) @@ -133,7 +133,7 @@ func TestBlockBufferGetBlocksIntervalsToSync(t *testing.T) { require.NoError(rp.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool, actpool.EnableExperimentalActions()) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool, actpool.EnableExperimentalActions()) require.NotNil(ap) require.NoError(err) dao := blockdao.NewBlockDAOInMemForTest([]blockdao.BlockIndexer{sf}) diff --git a/chainservice/builder.go b/chainservice/builder.go index 8c704983ee..e346b5e197 100644 --- a/chainservice/builder.go +++ b/chainservice/builder.go @@ -218,7 +218,7 @@ func (builder *Builder) createElectionCommittee() (committee.Committee, error) { func (builder *Builder) buildActionPool() error { if builder.cs.actpool == nil { - ac, err := actpool.NewActPool(builder.cs.factory, builder.cfg.ActPool) + ac, err := actpool.NewActPool(builder.cfg.Genesis, builder.cs.factory, builder.cfg.ActPool) if err != nil { return errors.Wrap(err, "failed to create actpool") } diff --git a/consensus/scheme/rolldpos/rolldpos_test.go b/consensus/scheme/rolldpos/rolldpos_test.go index c0450e8363..047f88a43c 100644 --- a/consensus/scheme/rolldpos/rolldpos_test.go +++ b/consensus/scheme/rolldpos/rolldpos_test.go @@ -411,7 +411,7 @@ func TestRollDPoSConsensus(t *testing.T) { protocol.WithRegistry(ctx, registry), cfg.Genesis, ))) - actPool, err := actpool.NewActPool(sf, cfg.ActPool, actpool.EnableExperimentalActions()) + actPool, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool, actpool.EnableExperimentalActions()) require.NoError(t, err) require.NoError(t, err) acc := account.NewProtocol(rewarding.DepositGas) diff --git a/consensus/scheme/rolldpos/roundcalculator_test.go b/consensus/scheme/rolldpos/roundcalculator_test.go index 72cada0d85..4959ad8328 100644 --- a/consensus/scheme/rolldpos/roundcalculator_test.go +++ b/consensus/scheme/rolldpos/roundcalculator_test.go @@ -182,7 +182,7 @@ func makeChain(t *testing.T) (blockchain.Blockchain, factory.Factory, actpool.Ac registry := protocol.NewRegistry() sf, err := factory.NewFactory(cfg, factory.DefaultTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NoError(err) dbcfg := cfg.DB dbcfg.DbPath = cfg.Chain.ChainDBPath diff --git a/e2etest/bigint_test.go b/e2etest/bigint_test.go index fde6834996..0bf82de6e4 100644 --- a/e2etest/bigint_test.go +++ b/e2etest/bigint_test.go @@ -44,15 +44,16 @@ func TestTransfer_Negative(t *testing.T) { ctx := context.Background() bc, sf, ap := prepareBlockchain(ctx, _executor, r) defer r.NoError(bc.Stop(ctx)) + ctx = genesis.WithGenesisContext(ctx, bc.Genesis()) addr, err := address.FromString(_executor) r.NoError(err) - stateBeforeTransfer, err := accountutil.AccountState(sf, addr) + stateBeforeTransfer, err := accountutil.AccountState(ctx, sf, addr) r.NoError(err) blk, err := prepareTransfer(bc, sf, ap, r) r.NoError(err) r.Equal(1, len(blk.Actions)) r.NoError(bc.ValidateBlock(blk)) - state, err := accountutil.AccountState(sf, addr) + state, err := accountutil.AccountState(ctx, sf, addr) r.NoError(err) r.Equal(0, state.Balance.Cmp(stateBeforeTransfer.Balance)) } @@ -64,14 +65,15 @@ func TestAction_Negative(t *testing.T) { defer r.NoError(bc.Stop(ctx)) addr, err := address.FromString(_executor) r.NoError(err) - stateBeforeTransfer, err := accountutil.AccountState(sf, addr) + ctx = genesis.WithGenesisContext(ctx, bc.Genesis()) + stateBeforeTransfer, err := accountutil.AccountState(ctx, sf, addr) r.NoError(err) blk, err := prepareAction(bc, sf, ap, r) r.NoError(err) r.NotNil(blk) r.Equal(1, len(blk.Actions)) r.NoError(bc.ValidateBlock(blk)) - state, err := accountutil.AccountState(sf, addr) + state, err := accountutil.AccountState(ctx, sf, addr) r.NoError(err) r.Equal(0, state.Balance.Cmp(stateBeforeTransfer.Balance)) } @@ -89,7 +91,7 @@ func prepareBlockchain(ctx context.Context, _executor string, r *require.Asserti sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) r.NoError(err) genericValidator := protocol.NewGenericValidator(sf, accountutil.AccountState) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) r.NoError(err) ap.AddActionEnvelopeValidators(genericValidator) dao := blockdao.NewBlockDAOInMemForTest([]blockdao.BlockIndexer{sf}) diff --git a/e2etest/local_test.go b/e2etest/local_test.go index c05d7527e1..ec5cd15d17 100644 --- a/e2etest/local_test.go +++ b/e2etest/local_test.go @@ -71,7 +71,7 @@ func TestLocalCommit(t *testing.T) { }() // create server - ctx := context.Background() + ctx := genesis.WithGenesisContext(context.Background(), cfg.Genesis) svr, err := itx.NewServer(cfg) require.NoError(err) require.NoError(svr.Start(ctx)) @@ -86,7 +86,7 @@ func TestLocalCommit(t *testing.T) { require.NotNil(sf) require.NotNil(ap) - i27State, err := accountutil.AccountState(sf, identityset.Address(27)) + i27State, err := accountutil.AccountState(ctx, sf, identityset.Address(27)) require.NoError(err) require.NoError(addTestingTsfBlocks(bc, ap)) require.EqualValues(5, bc.TipHeight()) @@ -104,12 +104,12 @@ func TestLocalCommit(t *testing.T) { {32, "100"}, {33, "5242883"}, } { - s, err := accountutil.AccountState(sf, identityset.Address(v.addrIndex)) + s, err := accountutil.AccountState(ctx, sf, identityset.Address(v.addrIndex)) require.NoError(err) require.Equal(v.balance, s.Balance.String()) change.Add(change, s.Balance) } - s, err := accountutil.AccountState(sf, identityset.Address(27)) + s, err := accountutil.AccountState(ctx, sf, identityset.Address(27)) require.NoError(err) change.Add(change, s.Balance) change.Sub(change, i27State.Balance) @@ -157,7 +157,7 @@ func TestLocalCommit(t *testing.T) { registry := protocol.NewRegistry() sf2, err := factory.NewStateDB(cfg, factory.CachedStateDBOption(), factory.RegistryStateDBOption(registry)) require.NoError(err) - ap2, err := actpool.NewActPool(sf2, cfg.ActPool) + ap2, err := actpool.NewActPool(cfg.Genesis, sf2, cfg.ActPool) require.NoError(err) dbcfg := cfg.DB dbcfg.DbPath = cfg.Chain.ChainDBPath @@ -190,7 +190,7 @@ func TestLocalCommit(t *testing.T) { // transfer 1 // C --> A - s, _ = accountutil.AccountState(sf, identityset.Address(30)) + s, _ = accountutil.AccountState(ctx, sf, identityset.Address(30)) tsf1, err := action.SignedTransfer(identityset.Address(28).String(), identityset.PrivateKey(30), s.PendingNonce(), big.NewInt(1), []byte{}, 100000, big.NewInt(0)) require.NoError(err) @@ -211,7 +211,7 @@ func TestLocalCommit(t *testing.T) { // transfer 2 // F --> D - s, _ = accountutil.AccountState(sf, identityset.Address(33)) + s, _ = accountutil.AccountState(ctx, sf, identityset.Address(33)) tsf2, err := action.SignedTransfer(identityset.Address(31).String(), identityset.PrivateKey(33), s.PendingNonce(), big.NewInt(1), []byte{}, 100000, big.NewInt(0)) require.NoError(err) @@ -232,7 +232,7 @@ func TestLocalCommit(t *testing.T) { // transfer 3 // B --> B - s, _ = accountutil.AccountState(sf, identityset.Address(29)) + s, _ = accountutil.AccountState(ctx, sf, identityset.Address(29)) tsf3, err := action.SignedTransfer(identityset.Address(29).String(), identityset.PrivateKey(29), s.PendingNonce(), big.NewInt(1), []byte{}, 100000, big.NewInt(0)) require.NoError(err) @@ -253,7 +253,7 @@ func TestLocalCommit(t *testing.T) { // transfer 4 // test --> E - s, _ = accountutil.AccountState(sf, identityset.Address(27)) + s, _ = accountutil.AccountState(ctx, sf, identityset.Address(27)) tsf4, err := action.SignedTransfer(identityset.Address(32).String(), identityset.PrivateKey(27), s.PendingNonce(), big.NewInt(1), []byte{}, 100000, big.NewInt(0)) require.NoError(err) @@ -296,12 +296,12 @@ func TestLocalCommit(t *testing.T) { {32, "101"}, {33, "5242882"}, } { - s, err = accountutil.AccountState(sf, identityset.Address(v.addrIndex)) + s, err = accountutil.AccountState(ctx, sf, identityset.Address(v.addrIndex)) require.NoError(err) require.Equal(v.balance, s.Balance.String()) change.Add(change, s.Balance) } - s, err = accountutil.AccountState(sf, identityset.Address(27)) + s, err = accountutil.AccountState(ctx, sf, identityset.Address(27)) require.NoError(err) change.Add(change, s.Balance) change.Sub(change, i27State.Balance) diff --git a/e2etest/local_transfer_test.go b/e2etest/local_transfer_test.go index 5488414a5d..acc948f6bd 100644 --- a/e2etest/local_transfer_test.go +++ b/e2etest/local_transfer_test.go @@ -34,6 +34,7 @@ import ( "github.com/iotexproject/iotex-core/actpool" "github.com/iotexproject/iotex-core/blockchain" "github.com/iotexproject/iotex-core/blockchain/blockdao" + "github.com/iotexproject/iotex-core/blockchain/genesis" "github.com/iotexproject/iotex-core/config" "github.com/iotexproject/iotex-core/pkg/probe" "github.com/iotexproject/iotex-core/pkg/unit" @@ -348,7 +349,7 @@ func TestLocalTransfer(t *testing.T) { require.NoError(err) // Create and start probe server - ctx := context.Background() + ctx := genesis.WithGenesisContext(context.Background(), cfg.Genesis) probeSvr := probe.New(7788) require.NoError(probeSvr.Start(ctx)) @@ -418,7 +419,7 @@ func TestLocalTransfer(t *testing.T) { senderAddr1, err := address.FromString(senderAddr) require.NoError(err) - newSenderState, _ := accountutil.AccountState(sf, senderAddr1) + newSenderState, _ := accountutil.AccountState(ctx, sf, senderAddr1) minusAmount := big.NewInt(0).Sub(tsfTest.senderBalance, tsfTest.amount) gasUnitPayloadConsumed := big.NewInt(0).Mul(big.NewInt(int64(action.TransferPayloadGas)), big.NewInt(int64(len(tsfTest.payload)))) @@ -430,7 +431,7 @@ func TestLocalTransfer(t *testing.T) { recvAddr1, err := address.FromString(recvAddr) require.NoError(err) - newRecvState, err := accountutil.AccountState(sf, recvAddr1) + newRecvState, err := accountutil.AccountState(ctx, sf, recvAddr1) require.NoError(err) expectedRecvrBalance := big.NewInt(0) if tsfTest.recvAcntState == AcntNotRegistered { @@ -480,7 +481,7 @@ func TestLocalTransfer(t *testing.T) { if tsfTest.senderAcntState == AcntCreate || tsfTest.senderAcntState == AcntExist { senderAddr1, err := address.FromString(senderAddr) require.NoError(err) - newSenderState, _ := accountutil.AccountState(sf, senderAddr1) + newSenderState, _ := accountutil.AccountState(ctx, sf, senderAddr1) require.Equal(tsfTest.senderBalance.String(), newSenderState.Balance.String()) } @@ -543,7 +544,8 @@ func initStateKeyAddr( return nil, "", errors.New("failed to get address") } retAddr = addr.String() - existState, err := accountutil.AccountState(sf, addr) + ctx := genesis.WithGenesisContext(context.Background(), bc.Genesis()) + existState, err := accountutil.AccountState(ctx, sf, addr) if err != nil { return nil, "", err } @@ -647,7 +649,7 @@ func TestEnforceChainID(t *testing.T) { require.NoError(acc.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NoError(err) blkMemDao := blockdao.NewBlockDAOInMemForTest([]blockdao.BlockIndexer{sf}) bc := blockchain.NewBlockchain( diff --git a/e2etest/rewarding_test.go b/e2etest/rewarding_test.go index 9154a1808e..55bdf05eef 100644 --- a/e2etest/rewarding_test.go +++ b/e2etest/rewarding_test.go @@ -264,13 +264,14 @@ func TestBlockEpochReward(t *testing.T) { rewardAddr := identityset.Address(i + numNodes) rewardAddrStr := identityset.Address(i + numNodes).String() exptUnclaimed[rewardAddrStr] = big.NewInt(0) - initState, err := accountutil.AccountState(sfs[i], rewardAddr) + ctx := genesis.WithGenesisContext(context.Background(), configs[i].Genesis) + initState, err := accountutil.AccountState(ctx, sfs[i], rewardAddr) require.NoError(t, err) initBalances[rewardAddrStr] = initState.Balance operatorAddr := identityset.Address(i) operatorAddrStr := identityset.Address(i).String() - initState, err = accountutil.AccountState(sfs[i], operatorAddr) + initState, err = accountutil.AccountState(ctx, sfs[i], operatorAddr) require.NoError(t, err) initBalances[operatorAddrStr] = initState.Balance @@ -456,7 +457,11 @@ func TestBlockEpochReward(t *testing.T) { //Check Reward address balance rewardAddr := identityset.Address(i + numNodes) rewardAddrStr := rewardAddr.String() - endState, err := accountutil.AccountState(sfs[0], rewardAddr) + endState, err := accountutil.AccountState( + genesis.WithGenesisContext(context.Background(), configs[0].Genesis), + sfs[0], + rewardAddr, + ) require.NoError(t, err) fmt.Println("Server ", i, " ", rewardAddrStr, " Closing Balance ", endState.Balance.String()) expectBalance := big.NewInt(0).Add(initBalances[rewardAddrStr], claimedAmount[rewardAddrStr]) @@ -466,7 +471,11 @@ func TestBlockEpochReward(t *testing.T) { //Make sure the non-reward addresses have not received money operatorAddr := identityset.Address(i) operatorAddrStr := identityset.Address(i).String() - operatorState, err := accountutil.AccountState(sfs[i], operatorAddr) + operatorState, err := accountutil.AccountState( + genesis.WithGenesisContext(context.Background(), configs[i].Genesis), + sfs[i], + operatorAddr, + ) require.NoError(t, err) require.Equal(t, initBalances[operatorAddrStr], operatorState.Balance) } diff --git a/e2etest/staking_test.go b/e2etest/staking_test.go index 9e68bf641d..46f5236cda 100644 --- a/e2etest/staking_test.go +++ b/e2etest/staking_test.go @@ -58,7 +58,7 @@ func TestStakingContract(t *testing.T) { require.NotNil(registry) admin := identityset.PrivateKey(26) state0 := hash.BytesToHash160(identityset.Address(26).Bytes()) - s := state.NewEmptyAccount() + s := &state.Account{} _, err = sf.State(s, protocol.LegacyKeyOption(state0)) require.NoError(err) require.Equal(unit.ConvertIotxToRau(100000000), s.Balance) diff --git a/gasstation/gasstattion_test.go b/gasstation/gasstattion_test.go index 30bf3295af..f55882be76 100644 --- a/gasstation/gasstattion_test.go +++ b/gasstation/gasstattion_test.go @@ -48,7 +48,7 @@ func TestSuggestGasPriceForUserAction(t *testing.T) { require.NoError(t, rp.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(t, err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NoError(t, err) blkMemDao := blockdao.NewBlockDAOInMemForTest([]blockdao.BlockIndexer{sf}) bc := blockchain.NewBlockchain( @@ -124,7 +124,7 @@ func TestSuggestGasPriceForSystemAction(t *testing.T) { require.NoError(t, rp.Register(registry)) sf, err := factory.NewFactory(cfg, factory.InMemTrieOption(), factory.RegistryOption(registry)) require.NoError(t, err) - ap, err := actpool.NewActPool(sf, cfg.ActPool) + ap, err := actpool.NewActPool(cfg.Genesis, sf, cfg.ActPool) require.NoError(t, err) blkMemDao := blockdao.NewBlockDAOInMemForTest([]blockdao.BlockIndexer{sf}) bc := blockchain.NewBlockchain( diff --git a/state/account.go b/state/account.go index 053556c3be..388d6dda33 100644 --- a/state/account.go +++ b/state/account.go @@ -29,10 +29,10 @@ var ( ErrUnknownAccountType = errors.New("unknown account type") ) -// ZeroNonceAccountTypeOption is an option to create account with new account type -func ZeroNonceAccountTypeOption() AccountCreationOption { +// LegacyNonceAccountTypeOption is an option to create account with new account type +func LegacyNonceAccountTypeOption() AccountCreationOption { return func(account *Account) error { - account.accountType = 1 + account.accountType = 0 return nil } } @@ -93,6 +93,9 @@ func (st Account) Serialize() ([]byte, error) { // FromProto converts from protobuf's Account func (st *Account) FromProto(acPb *accountpb.Account) { st.nonce = acPb.Nonce + if _, ok := accountpb.AccountType_name[int32(acPb.Type.Number())]; !ok { + panic("unknown account type") + } st.accountType = int32(acPb.Type.Number()) if acPb.Balance == "" { st.Balance = big.NewInt(0) @@ -131,19 +134,24 @@ func (st *Account) IsNewbieAccount() bool { return st.nonce == 0 } -// SetNonce sets the nonce according to type -func (st *Account) SetNonce(nonce uint64) error { +// AccountType returns the account type +func (st *Account) AccountType() int32 { + return st.accountType +} + +// SetPendingNonce sets the pending nonce +func (st *Account) SetPendingNonce(nonce uint64) error { switch st.accountType { case 1: - if nonce != st.nonce { - return errors.Wrapf(ErrInvalidNonce, "actual value %d, %d expected", nonce, st.nonce) - } - st.nonce = nonce + 1 - case 0: if nonce != st.nonce+1 { return errors.Wrapf(ErrInvalidNonce, "actual value %d, %d expected", nonce, st.nonce+1) } - st.nonce = nonce + st.nonce++ + case 0: + if nonce != st.nonce+2 { + return errors.Wrapf(ErrInvalidNonce, "actual value %d, %d expected", nonce, st.nonce+2) + } + st.nonce++ default: return errors.Wrapf(ErrUnknownAccountType, "account type %d", st.accountType) } @@ -161,6 +169,11 @@ func (st *Account) PendingNonce() uint64 { } } +// MarkAsCandidate marks the account as a candidate +func (st *Account) MarkAsCandidate() { + st.isCandidate = true +} + // HasSufficientBalance returns true if balance is larger than amount func (st *Account) HasSufficientBalance(amount *big.Int) bool { if amount == nil { @@ -219,19 +232,12 @@ func (st *Account) Clone() *Account { return &s } -// NewEmptyAccount returns an empty account -func NewEmptyAccount() *Account { - return &Account{ - Balance: big.NewInt(0), - votingWeight: big.NewInt(0), - } -} - // NewAccount creates a new account with options func NewAccount(opts ...AccountCreationOption) (*Account, error) { account := &Account{ Balance: big.NewInt(0), votingWeight: big.NewInt(0), + accountType: 1, } for _, opt := range opts { if err := opt(account); err != nil { diff --git a/state/account_test.go b/state/account_test.go index 11d80acffd..73e164e716 100644 --- a/state/account_test.go +++ b/state/account_test.go @@ -17,24 +17,48 @@ import ( "github.com/stretchr/testify/require" ) +func TestNonce(t *testing.T) { + require := require.New(t) + t.Run("legacy account type", func(t *testing.T) { + acct, err := NewAccount(LegacyNonceAccountTypeOption()) + require.NoError(err) + require.Equal(uint64(1), acct.PendingNonce()) + require.Error(acct.SetPendingNonce(0)) + require.Error(acct.SetPendingNonce(1)) + require.Error(acct.SetPendingNonce(3)) + require.NoError(acct.SetPendingNonce(2)) + require.Equal(uint64(2), acct.PendingNonce()) + }) + t.Run("zero nonce account type", func(t *testing.T) { + acct, err := NewAccount() + require.NoError(err) + require.Equal(uint64(0), acct.PendingNonce()) + require.Error(acct.SetPendingNonce(2)) + require.NoError(acct.SetPendingNonce(1)) + require.Equal(uint64(1), acct.PendingNonce()) + }) +} + func TestEncodeDecode(t *testing.T) { require := require.New(t) s1 := Account{ - nonce: 0x10, - Balance: big.NewInt(20000000), - CodeHash: []byte("testing codehash"), + accountType: 1, + nonce: 0x10, + Balance: big.NewInt(20000000), + CodeHash: []byte("testing codehash"), } ss, err := s1.Serialize() require.NoError(err) require.NotEmpty(ss) - require.Equal(64, len(ss)) + require.Equal(66, len(ss)) s2 := Account{} require.NoError(s2.Deserialize(ss)) - require.Equal(big.NewInt(20000000), s2.Balance) - require.Equal(uint64(0x11), s2.PendingNonce()) + require.Equal(s1.accountType, s2.accountType) + require.Equal(s1.Balance, s2.Balance) + require.Equal(s1.nonce, s2.nonce) require.Equal(hash.ZeroHash256, s2.Root) - require.Equal([]byte("testing codehash"), s2.CodeHash) + require.Equal(s1.CodeHash, s2.CodeHash) } func TestProto(t *testing.T) { diff --git a/state/factory/factory_test.go b/state/factory/factory_test.go index 0bdb45ee52..499bd670c6 100644 --- a/state/factory/factory_test.go +++ b/state/factory/factory_test.go @@ -468,8 +468,8 @@ func testState(sf Factory, t *testing.T) { require.NoError(t, sf.PutBlock(ctx, &blk)) //test AccountState() & State() - testAccount := state.NewEmptyAccount() - accountA, err := accountutil.AccountState(sf, a) + testAccount := &state.Account{} + accountA, err := accountutil.AccountState(ctx, sf, a) require.NoError(t, err) sHash := hash.BytesToHash160(identityset.Address(28).Bytes()) _, err = sf.State(testAccount, protocol.LegacyKeyOption(sHash)) @@ -502,9 +502,9 @@ func testHistoryState(sf Factory, t *testing.T, statetx, archive bool) { defer func() { require.NoError(t, sf.Stop(ctx)) }() - accountA, err := accountutil.AccountState(sf, a) + accountA, err := accountutil.AccountState(ctx, sf, a) require.NoError(t, err) - accountB, err := accountutil.AccountState(sf, b) + accountB, err := accountutil.AccountState(ctx, sf, b) require.NoError(t, err) require.Equal(t, big.NewInt(100), accountA.Balance) require.Equal(t, big.NewInt(0), accountB.Balance) @@ -535,9 +535,9 @@ func testHistoryState(sf Factory, t *testing.T, statetx, archive bool) { require.NoError(t, sf.PutBlock(ctx, &blk)) // check latest balance - accountA, err = accountutil.AccountState(sf, a) + accountA, err = accountutil.AccountState(ctx, sf, a) require.NoError(t, err) - accountB, err = accountutil.AccountState(sf, b) + accountB, err = accountutil.AccountState(ctx, sf, b) require.NoError(t, err) require.Equal(t, big.NewInt(90), accountA.Balance) require.Equal(t, big.NewInt(10), accountB.Balance) @@ -545,20 +545,20 @@ func testHistoryState(sf Factory, t *testing.T, statetx, archive bool) { // check archive data if statetx { // statetx not support archive mode - _, err = accountutil.AccountState(NewHistoryStateReader(sf, 0), a) + _, err = accountutil.AccountState(ctx, NewHistoryStateReader(sf, 0), a) require.Equal(t, ErrNotSupported, errors.Cause(err)) - _, err = accountutil.AccountState(NewHistoryStateReader(sf, 0), b) + _, err = accountutil.AccountState(ctx, NewHistoryStateReader(sf, 0), b) require.Equal(t, ErrNotSupported, errors.Cause(err)) } else { if !archive { - _, err = accountutil.AccountState(NewHistoryStateReader(sf, 0), a) + _, err = accountutil.AccountState(ctx, NewHistoryStateReader(sf, 0), a) require.Equal(t, ErrNoArchiveData, errors.Cause(err)) - _, err = accountutil.AccountState(NewHistoryStateReader(sf, 0), b) + _, err = accountutil.AccountState(ctx, NewHistoryStateReader(sf, 0), b) require.Equal(t, ErrNoArchiveData, errors.Cause(err)) } else { - accountA, err = accountutil.AccountState(NewHistoryStateReader(sf, 0), a) + accountA, err = accountutil.AccountState(ctx, NewHistoryStateReader(sf, 0), a) require.NoError(t, err) - accountB, err = accountutil.AccountState(NewHistoryStateReader(sf, 0), b) + accountB, err = accountutil.AccountState(ctx, NewHistoryStateReader(sf, 0), b) require.NoError(t, err) require.Equal(t, big.NewInt(100), accountA.Balance) require.Equal(t, big.NewInt(0), accountB.Balance) @@ -631,7 +631,7 @@ func testFactoryStates(sf Factory, t *testing.T) { require.Equal(t, 3, iter.Size()) accounts := make([]*state.Account, 0) for i := 0; i < iter.Size(); i++ { - c := state.NewEmptyAccount() + c := &state.Account{} err = iter.Next(c) if err != nil { continue @@ -650,7 +650,7 @@ func testFactoryStates(sf Factory, t *testing.T) { require.Equal(t, 3, iter.Size()) accounts = make([]*state.Account, 0) for i := 0; i < iter.Size(); i++ { - c := state.NewEmptyAccount() + c := &state.Account{} err = iter.Next(c) if err != nil { continue @@ -669,7 +669,7 @@ func testFactoryStates(sf Factory, t *testing.T) { require.Equal(t, 3, iter.Size()) accounts = make([]*state.Account, 0) for i := 0; i < iter.Size(); i++ { - c := state.NewEmptyAccount() + c := &state.Account{} err = iter.Next(c) if err != nil { continue @@ -690,7 +690,7 @@ func testFactoryStates(sf Factory, t *testing.T) { require.Equal(t, 1, iter.Size()) accounts = make([]*state.Account, 0) for i := 0; i < iter.Size(); i++ { - c := state.NewEmptyAccount() + c := &state.Account{} require.NoError(t, iter.Next(c)) accounts = append(accounts, c) } @@ -780,7 +780,7 @@ func testNonce(sf Factory, t *testing.T) { }) _, err = ws.runAction(ctx, selp) require.NoError(t, err) - state, err := accountutil.AccountState(sf, a) + state, err := accountutil.AccountState(ctx, sf, a) require.NoError(t, err) require.Equal(t, uint64(1), state.PendingNonce()) @@ -800,7 +800,7 @@ func testNonce(sf Factory, t *testing.T) { require.NoError(t, err) require.NoError(t, sf.PutBlock(ctx, &blk)) - state, err = accountutil.AccountState(sf, a) + state, err = accountutil.AccountState(ctx, sf, a) require.NoError(t, err) require.Equal(t, uint64(2), state.PendingNonce()) } @@ -1206,13 +1206,14 @@ func testCachedBatch(ws *workingSet, t *testing.T) { // test PutState() hashA := hash.BytesToHash160(identityset.Address(28).Bytes()) - accountA := state.NewEmptyAccount() + accountA, err := state.NewAccount() + require.NoError(err) require.NoError(accountA.AddBalance(big.NewInt(70))) - _, err := ws.PutState(accountA, protocol.LegacyKeyOption(hashA)) + _, err = ws.PutState(accountA, protocol.LegacyKeyOption(hashA)) require.NoError(err) // test State() - testAccount := state.NewEmptyAccount() + testAccount := &state.Account{} _, err = ws.State(testAccount, protocol.LegacyKeyOption(hashA)) require.NoError(err) require.Equal(accountA, testAccount) @@ -1355,9 +1356,11 @@ func TestStateDBPatch(t *testing.T) { func TestDeleteAndPutSameKey(t *testing.T) { testDeleteAndPutSameKey := func(t *testing.T, ws *workingSet) { key := hash.Hash160b([]byte("test")) - acc := state.NewEmptyAccount() - require.NoError(t, acc.SetNonce(1)) - _, err := ws.PutState(acc, protocol.LegacyKeyOption(key)) + acc, err := state.NewAccount() + require.NoError(t, err) + require.NoError(t, acc.SetPendingNonce(1)) + require.NoError(t, acc.SetPendingNonce(2)) + _, err = ws.PutState(acc, protocol.LegacyKeyOption(key)) require.NoError(t, err) _, err = ws.DelState(protocol.LegacyKeyOption(key)) require.NoError(t, err) @@ -1379,6 +1382,7 @@ func TestDeleteAndPutSameKey(t *testing.T) { }) t.Run("stateTx", func(t *testing.T) { sdb, err := NewStateDB(config.Default, InMemStateDBOption()) + require.NoError(t, err) ws, err := sdb.(workingSetCreator).newWorkingSet(ctx, 0) require.NoError(t, err) testDeleteAndPutSameKey(t, ws) @@ -1677,7 +1681,7 @@ func benchState(sf Factory, b *testing.B) { if err != nil { b.Fatal(err) } - _, err = accountutil.AccountState(sf, addr) + _, err = accountutil.AccountState(zctx, sf, addr) if err != nil { b.Fatal(err) } diff --git a/state/factory/workingset.go b/state/factory/workingset.go index bf22a08f2b..95965177b3 100644 --- a/state/factory/workingset.go +++ b/state/factory/workingset.go @@ -323,7 +323,7 @@ func (ws *workingSet) CreateGenesisStates(ctx context.Context) error { return ws.finalize() } -func (ws *workingSet) validateNonce(blk *block.Block) error { +func (ws *workingSet) validateNonce(ctx context.Context, blk *block.Block) error { accountNonceMap := make(map[string][]uint64) for _, selp := range blk.Actions { caller := selp.SenderAddress() @@ -340,7 +340,7 @@ func (ws *workingSet) validateNonce(blk *block.Block) error { // Verify each account's Nonce for srcAddr, receivedNonces := range accountNonceMap { addr, _ := address.FromString(srcAddr) - confirmedState, err := accountutil.AccountState(ws, addr) + confirmedState, err := accountutil.AccountState(ctx, ws, addr) if err != nil { return errors.Wrapf(err, "failed to get the confirmed nonce of address %s", srcAddr) } @@ -516,7 +516,7 @@ func updateReceiptIndex(receipts []*action.Receipt) { } func (ws *workingSet) ValidateBlock(ctx context.Context, blk *block.Block) error { - if err := ws.validateNonce(blk); err != nil { + if err := ws.validateNonce(ctx, blk); err != nil { return errors.Wrap(err, "failed to validate nonce") } if err := ws.process(ctx, blk.RunnableActions().Actions()); err != nil { diff --git a/tools/actioninjector.v2/internal/client/client_test.go b/tools/actioninjector.v2/internal/client/client_test.go index 4513003862..575b3937d3 100644 --- a/tools/actioninjector.v2/internal/client/client_test.go +++ b/tools/actioninjector.v2/internal/client/client_test.go @@ -56,7 +56,6 @@ func TestClient(t *testing.T) { ap := mock_actpool.NewMockActPool(mockCtrl) sf.EXPECT().State(gomock.Any(), gomock.Any()).Do(func(accountState *state.Account, _ protocol.StateOption) { - accountState = state.NewEmptyAccount() }) sf.EXPECT().Height().Return(uint64(10), nil).AnyTimes() bc.EXPECT().Genesis().Return(cfg.Genesis).AnyTimes() From 79aeaeee9619e298eaf01734f7d183697833c0a3 Mon Sep 17 00:00:00 2001 From: zhi Date: Mon, 27 Jun 2022 09:53:36 -0700 Subject: [PATCH 08/11] address comments --- action/protocol/account/transfer.go | 8 +++----- state/account.go | 2 -- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/action/protocol/account/transfer.go b/action/protocol/account/transfer.go index 1ed65969a8..929088c83d 100644 --- a/action/protocol/account/transfer.go +++ b/action/protocol/account/transfer.go @@ -78,14 +78,12 @@ func (p *Protocol) handleTransfer(ctx context.Context, act action.Action, sm pro return nil, errors.Wrapf(err, "failed to decode recipient address %s", tsf.Recipient()) } recipientAcct, err := accountutil.LoadAccount(sm, recipientAddr, accountCreationOpts...) - if !fCtx.TolerateLegacyAddress { - if err != nil { - return nil, errors.Wrapf(err, "failed to load address %s", tsf.Recipient()) - } + if err != nil { + return nil, errors.Wrapf(err, "failed to load address %s", tsf.Recipient()) } fixNonce := protocol.MustGetFeatureCtx(ctx).FixGasAndNonceUpdate blkCtx := protocol.MustGetBlockCtx(ctx) - if err == nil && recipientAcct.IsContract() { + if recipientAcct.IsContract() { if fixNonce || tsf.Nonce() != 0 { // update sender Nonce if err := sender.SetPendingNonce(tsf.Nonce() + 1); err != nil { diff --git a/state/account.go b/state/account.go index 388d6dda33..ddb9fc90f9 100644 --- a/state/account.go +++ b/state/account.go @@ -218,8 +218,6 @@ func (st *Account) Clone() *Account { s := *st s.Balance = nil s.Balance = new(big.Int).Set(st.Balance) - s.accountType = st.accountType - s.nonce = st.nonce s.votingWeight = nil if st.votingWeight != nil { s.votingWeight = new(big.Int).Set(st.votingWeight) From 59509d1fff1f72830a5dd7a92512d10fd048b7fa Mon Sep 17 00:00:00 2001 From: zhi Date: Mon, 27 Jun 2022 22:48:07 -0700 Subject: [PATCH 09/11] fix unit test --- .../execution/testdata-london/basic-token.json | 4 ++-- .../execution/testdata-london/datacopy.json | 6 +++--- .../execution/testdata-london/gas-test.json | 4 ++-- .../execution/testdata-london/mapping-delete.json | 2 +- .../execution/testdata-london/maxtime.json | 4 ++-- .../execution/testdata-london/reentry-attack.json | 14 +++++++------- .../testdata-london/remove-from-array.json | 2 +- .../execution/testdata-london/storage-test.json | 4 ++-- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/action/protocol/execution/testdata-london/basic-token.json b/action/protocol/execution/testdata-london/basic-token.json index 39ae2a2d1a..f59be8685a 100644 --- a/action/protocol/execution/testdata-london/basic-token.json +++ b/action/protocol/execution/testdata-london/basic-token.json @@ -26,7 +26,7 @@ "rawGasLimit": 1000000, "rawGasPrice": "0", "rawAccessList": [{ - "address": "06abb2ca03834bd4f62c610ca7627e3dd12d9a97", + "address": "675f1057F81e9e768e33faddbd5609C09F4c0a5C", "storageKeys": [ "cd7bfe84e3fe161aeb958aa899cea602147a8edb25bcc4cc49f4753fa3410c17" ] @@ -42,7 +42,7 @@ "rawGasLimit": 1000000, "rawGasPrice": "0", "rawAccessList": [{ - "address": "06abb2ca03834bd4f62c610ca7627e3dd12d9a97", + "address": "675f1057F81e9e768e33faddbd5609C09F4c0a5C", "storageKeys": [ "cd7bfe84e3fe161aeb958aa899cea602147a8edb25bcc4cc49f4753fa3410c17" ] diff --git a/action/protocol/execution/testdata-london/datacopy.json b/action/protocol/execution/testdata-london/datacopy.json index 8fd715248a..862f1e42d3 100644 --- a/action/protocol/execution/testdata-london/datacopy.json +++ b/action/protocol/execution/testdata-london/datacopy.json @@ -30,9 +30,9 @@ "expectedStatus": 1, "comment": "the data of return is [0x11, 0x22, 0x33]", "expectedBlockInfos" : { - "txRootHash" : "3672945ca662bea4dd977e799374c5ce36c0c3e9ecbe98f1655f33439bbfe40c", - "stateRootHash" : "31d9f2c50c45877029ed9afa24e2052bfc36232e9b0d220adfb0f90ec941d86c", - "receiptRootHash" : "596f5fd6f5931b252cff495050859df5faffd86ca960d0222b907a31bc1f75ae" + "txRootHash" : "3d03a074dee9d9edb3dbd2bc30cf854c768e863ecf053c491fd71b2b1092c202", + "stateRootHash" : "3595fffd9f80f99887e3108d84f3558e2691ab9dc1c73f6279e3da5735d02afa", + "receiptRootHash" : "5182918a42865f1ebddb18ad846b9657c49592e4252a9f4234f224bfda05b738" } }] } diff --git a/action/protocol/execution/testdata-london/gas-test.json b/action/protocol/execution/testdata-london/gas-test.json index 361cd44673..7b6fa44944 100644 --- a/action/protocol/execution/testdata-london/gas-test.json +++ b/action/protocol/execution/testdata-london/gas-test.json @@ -67,7 +67,7 @@ "rawGasLimit": 6000000, "rawGasPrice": "0", "rawAccessList": [{ - "address": "06abb2ca03834bd4f62c610ca7627e3dd12d9a97", + "address": "675f1057F81e9e768e33faddbd5609C09F4c0a5C", "storageKeys": [ "0000000000000000000000000000000000000000000000000000000000000000", "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563", @@ -333,7 +333,7 @@ "rawGasLimit": 6000000, "rawGasPrice": "0", "rawAccessList": [{ - "address": "06abb2ca03834bd4f62c610ca7627e3dd12d9a97", + "address": "675f1057F81e9e768e33faddbd5609C09F4c0a5C", "storageKeys": [ "0000000000000000000000000000000000000000000000000000000000000000", "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563", diff --git a/action/protocol/execution/testdata-london/mapping-delete.json b/action/protocol/execution/testdata-london/mapping-delete.json index 7eeada4857..209b39a609 100644 --- a/action/protocol/execution/testdata-london/mapping-delete.json +++ b/action/protocol/execution/testdata-london/mapping-delete.json @@ -15,7 +15,7 @@ "rawGasLimit": 5000000, "rawGasPrice": "0", "rawAccessList": [{ - "address": "06abb2ca03834bd4f62c610ca7627e3dd12d9a97", + "address": "675f1057F81e9e768e33faddbd5609C09F4c0a5C", "storageKeys": [ "ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5", "ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb6", diff --git a/action/protocol/execution/testdata-london/maxtime.json b/action/protocol/execution/testdata-london/maxtime.json index c133254959..fe18122157 100644 --- a/action/protocol/execution/testdata-london/maxtime.json +++ b/action/protocol/execution/testdata-london/maxtime.json @@ -25,7 +25,7 @@ "rawGasLimit": 1000000, "rawGasPrice": "1", "rawAccessList": [{ - "address": "06abb2ca03834bd4f62c610ca7627e3dd12d9a97", + "address": "675f1057F81e9e768e33faddbd5609C09F4c0a5C", "storageKeys": [ "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000001", @@ -93,7 +93,7 @@ "rawGasLimit": 5000000, "rawGasPrice": "1", "rawAccessList": [{ - "address": "06abb2ca03834bd4f62c610ca7627e3dd12d9a97", + "address": "675f1057F81e9e768e33faddbd5609C09F4c0a5C", "storageKeys": [ "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000001", diff --git a/action/protocol/execution/testdata-london/reentry-attack.json b/action/protocol/execution/testdata-london/reentry-attack.json index 29adcc7324..82995b566f 100644 --- a/action/protocol/execution/testdata-london/reentry-attack.json +++ b/action/protocol/execution/testdata-london/reentry-attack.json @@ -27,16 +27,16 @@ "rawGasLimit": 5000000, "rawGasPrice": "0", "rawAccessList": [{ - "address": "67ec2fd251e5563c112b2534590ae74d23e6df19", + "address": "06Abb2Ca03834bd4f62C610CA7627E3dD12D9a97", "storageKeys": [ "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000002" ] },{ - "address": "06abb2ca03834bd4f62c610ca7627e3dd12d9a97", + "address": "675f1057F81e9e768e33faddbd5609C09F4c0a5C", "storageKeys": [ - "809fddd0e44ad4227f52c39fc29ccd10b3c1d7ca546bc63db1a7bdde63de0cee" + "a13f8e0b4dc774404729f73c84108ed3304902e8d516606ec7c44c0bf6ad9f74" ] }], "rawExpectedGasConsumed": 228673, @@ -52,14 +52,14 @@ "rawGasLimit": 1000000, "rawGasPrice": "0", "rawAccessList": [{ - "address": "67ec2fd251e5563c112b2534590ae74d23e6df19", + "address": "06Abb2Ca03834bd4f62C610CA7627E3dD12D9a97", "storageKeys": [ "0000000000000000000000000000000000000000000000000000000000000000" ] },{ - "address": "06abb2ca03834bd4f62c610ca7627e3dd12d9a97", + "address": "675f1057F81e9e768e33faddbd5609C09F4c0a5C", "storageKeys": [ - "809fddd0e44ad4227f52c39fc29ccd10b3c1d7ca546bc63db1a7bdde63de0cee" + "a13f8e0b4dc774404729f73c84108ed3304902e8d516606ec7c44c0bf6ad9f74" ] }], "rawExpectedGasConsumed": 81795, @@ -71,7 +71,7 @@ "account": "", "rawBalance": "2000000000000000000" },{ - "account": "io1q64m9jsrsd9afa3vvyx2wcn78hgjmx5h0y0kh6", + "account": "io1va03q4lcr608dr3nltwm64sfcz05czjuycsqgn", "rawBalance": "3000000000000000000" }], "comment": "attack" diff --git a/action/protocol/execution/testdata-london/remove-from-array.json b/action/protocol/execution/testdata-london/remove-from-array.json index 6ea867c6d1..4be95cabd6 100644 --- a/action/protocol/execution/testdata-london/remove-from-array.json +++ b/action/protocol/execution/testdata-london/remove-from-array.json @@ -26,7 +26,7 @@ "rawGasLimit": 1000000, "rawGasPrice": "0", "rawAccessList": [{ - "address": "06abb2ca03834bd4f62c610ca7627e3dd12d9a97", + "address": "675f1057F81e9e768e33faddbd5609C09F4c0a5C", "storageKeys": [ "0000000000000000000000000000000000000000000000000000000000000000", "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563", diff --git a/action/protocol/execution/testdata-london/storage-test.json b/action/protocol/execution/testdata-london/storage-test.json index ad29218d18..0ee3eac03b 100644 --- a/action/protocol/execution/testdata-london/storage-test.json +++ b/action/protocol/execution/testdata-london/storage-test.json @@ -26,7 +26,7 @@ "rawGasLimit": 6000000, "rawGasPrice": "0", "rawAccessList": [{ - "address": "06abb2ca03834bd4f62c610ca7627e3dd12d9a97", + "address": "675f1057F81e9e768e33faddbd5609C09F4c0a5C", "storageKeys": [ "0000000000000000000000000000000000000000000000000000000000000000", "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563", @@ -265,7 +265,7 @@ "rawGasLimit": 6000000, "rawGasPrice": "0", "rawAccessList": [{ - "address": "06abb2ca03834bd4f62c610ca7627e3dd12d9a97", + "address": "675f1057F81e9e768e33faddbd5609C09F4c0a5C", "storageKeys": [ "0000000000000000000000000000000000000000000000000000000000000000", "290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e57f", From 877cf1a3747a115acc81541c970efac2876a1230 Mon Sep 17 00:00:00 2001 From: zhi Date: Tue, 28 Jun 2022 14:33:35 -0700 Subject: [PATCH 10/11] address comment --- api/coreservice.go | 5 +++-- state/factory/workingset.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/api/coreservice.go b/api/coreservice.go index dabd54f099..7bd8cf1703 100644 --- a/api/coreservice.go +++ b/api/coreservice.go @@ -396,7 +396,7 @@ func (core *coreService) SendAction(ctx context.Context, in *iotextypes.Action) } // Add to local actpool - ctx = genesis.WithGenesisContext(protocol.WithRegistry(ctx, core.registry), core.bc.Genesis()) + ctx = protocol.WithRegistry(ctx, core.registry) hash, err := selp.Hash() if err != nil { return "", err @@ -1368,7 +1368,8 @@ func (core *coreService) EstimateGasForNonExecution(actType action.Action) (uint // EstimateExecutionGasConsumption estimate gas consumption for execution action func (core *coreService) EstimateExecutionGasConsumption(ctx context.Context, sc *action.Execution, callerAddr address.Address) (uint64, error) { - state, err := accountutil.AccountState(genesis.WithGenesisContext(ctx, core.bc.Genesis()), core.sf, callerAddr) + ctx = genesis.WithGenesisContext(ctx, core.bc.Genesis()) + state, err := accountutil.AccountState(ctx, core.sf, callerAddr) if err != nil { return 0, status.Error(codes.InvalidArgument, err.Error()) } diff --git a/state/factory/workingset.go b/state/factory/workingset.go index 95965177b3..379902a4d8 100644 --- a/state/factory/workingset.go +++ b/state/factory/workingset.go @@ -351,7 +351,7 @@ func (ws *workingSet) validateNonce(ctx context.Context, blk *block.Block) error if nonce != pendingNonce+uint64(i) { return errors.Wrapf( action.ErrNonceTooHigh, - "the %d nonce %d of address %s (confirmed nonce %d) is not continuously increasing", + "the %d nonce %d of address %s (init pending nonce %d) is not continuously increasing", i, nonce, srcAddr, From 50c36f91192c7221d3c084a025a9dc04e44ca2c1 Mon Sep 17 00:00:00 2001 From: zhi Date: Thu, 30 Jun 2022 17:57:40 -0700 Subject: [PATCH 11/11] address comment --- state/account.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/state/account.go b/state/account.go index ddb9fc90f9..8b128caea9 100644 --- a/state/account.go +++ b/state/account.go @@ -164,8 +164,10 @@ func (st *Account) PendingNonce() uint64 { switch st.accountType { case 1: return st.nonce - default: // 0 + case 0: return st.nonce + 1 + default: + panic(errors.Wrapf(ErrUnknownAccountType, "account type %d", st.accountType)) } }