Skip to content

Commit

Permalink
Merge pull request #2689 from jorgemmsilva/fix/nonce-evm-isc
Browse files Browse the repository at this point in the history
Fix: nonce evm isc sync
  • Loading branch information
jorgemmsilva authored Jul 10, 2023
2 parents cb8e687 + f1f6297 commit b585186
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 49 deletions.
65 changes: 29 additions & 36 deletions packages/chain/mempool/mempool.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import (
"github.com/iotaledger/wasp/packages/util/pipe"
"github.com/iotaledger/wasp/packages/vm/core/accounts"
"github.com/iotaledger/wasp/packages/vm/core/blocklog"
"github.com/iotaledger/wasp/packages/vm/core/evm/evmimpl"
"github.com/iotaledger/wasp/packages/vm/core/governance"
)

Expand Down Expand Up @@ -452,46 +453,39 @@ func (mpi *mempoolImpl) distSyncRequestReceivedCB(request isc.Request) {
}
}

func (mpi *mempoolImpl) nonce(account isc.AgentID) uint64 {
accountsState := accounts.NewStateAccess(mpi.chainHeadState)
evmState := evmimpl.NewStateAccess(mpi.chainHeadState)

if evmSender, ok := account.(*isc.EthereumAddressAgentID); ok {
return evmState.Nonce(evmSender.EthAddress())
}
return accountsState.Nonce(account)
}

func (mpi *mempoolImpl) shouldAddOffledgerRequest(req isc.OffLedgerRequest) error {
mpi.log.Debugf("trying to add to mempool, requestID: %s", req.ID().String())
if mpi.offLedgerPool.Has(isc.RequestRefFromRequest(req)) {
return fmt.Errorf("already in mempool")
}
if mpi.chainHeadState != nil {
requestID := req.ID()
// TODO check nonce instead
processed, err := blocklog.IsRequestProcessed(mpi.chainHeadState, requestID)
if err != nil {
panic(fmt.Errorf(
"cannot check if request.ID=%v is processed in the blocklog at state=%v: %w",
requestID,
mpi.chainHeadState,
err,
))
}
if processed {
return fmt.Errorf("already processed")
}
accountsState := accounts.NewStateAccess(mpi.chainHeadState)

if req.SenderAccount().Kind() == isc.AgentIDKindEthereumAddress { //nolint:revive // intentionally left empty
// TODO check ethereum nonce
} else {
accountNonce := accountsState.Nonce(req.SenderAccount())
if req.Nonce() < accountNonce {
return fmt.Errorf("bad nonce, expected: %d", accountNonce)
}
}
if mpi.chainHeadState == nil {
return fmt.Errorf("chainHeadState is nil")
}

governanceState := governance.NewStateAccess(mpi.chainHeadState)
// check user has on-chain balance
if !accountsState.AccountExists(req.SenderAccount()) {
// make an exception for gov calls (sender is chan owner and target is gov contract)
chainOwner := governanceState.ChainOwnerID()
isGovRequest := req.SenderAccount().Equals(chainOwner) && req.CallTarget().Contract == governance.Contract.Hname()
if !isGovRequest {
return fmt.Errorf("no funds on chain")
}
accountNonce := mpi.nonce(req.SenderAccount())
if req.Nonce() < accountNonce {
return fmt.Errorf("bad nonce, expected: %d", accountNonce)
}

governanceState := governance.NewStateAccess(mpi.chainHeadState)
// check user has on-chain balance
accountsState := accounts.NewStateAccess(mpi.chainHeadState)
if !accountsState.AccountExists(req.SenderAccount()) {
// make an exception for gov calls (sender is chan owner and target is gov contract)
chainOwner := governanceState.ChainOwnerID()
isGovRequest := req.SenderAccount().Equals(chainOwner) && req.CallTarget().Contract == governance.Contract.Hname()
if !isGovRequest {
return fmt.Errorf("no funds on chain")
}
}
return nil
Expand Down Expand Up @@ -556,7 +550,6 @@ func (mpi *mempoolImpl) refsToPropose() []*isc.RequestRef {

expectedAccountNonces := map[string]uint64{} // string is isc.AgentID.String()
requestsNonces := map[string][]reqRefNonce{} // string is isc.AgentID.String()
accountsState := accounts.NewStateAccess(mpi.chainHeadState)

mpi.offLedgerPool.Filter(func(request isc.OffLedgerRequest, ts time.Time) bool {
ref := isc.RequestRefFromRequest(request)
Expand All @@ -567,7 +560,7 @@ func (mpi *mempoolImpl) refsToPropose() []*isc.RequestRef {
_, ok := expectedAccountNonces[senderKey]
if !ok {
// get the current state nonce so we can detect gaps with it
expectedAccountNonces[senderKey] = accountsState.Nonce(request.SenderAccount())
expectedAccountNonces[senderKey] = mpi.nonce(request.SenderAccount())
}
requestsNonces[senderKey] = append(requestsNonces[senderKey], reqRefNonce{ref: ref, nonce: request.Nonce()})

Expand Down
5 changes: 5 additions & 0 deletions packages/solo/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,11 @@ func (ch *Chain) LatestBlock() state.Block {
}

func (ch *Chain) Nonce(agentID isc.AgentID) uint64 {
if evmAgentID, ok := agentID.(*isc.EthereumAddressAgentID); ok {
nonce, err := ch.EVM().TransactionCount(evmAgentID.EthAddress(), nil)
require.NoError(ch.Env.T, err)
return nonce
}
res, err := ch.CallView(accounts.Contract.Name, accounts.ViewGetAccountNonce.Name, accounts.ParamAgentID, agentID)
require.NoError(ch.Env.T, err)
return codec.MustDecodeUint64(res.Get(accounts.ParamAccountNonce))
Expand Down
2 changes: 1 addition & 1 deletion packages/testutil/testdbhash/TestStorageContract.hex
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0x574c6f091ebf2eb2110d557bb491976007d71882be47cb3baf5f81f6ea46243a
0xf3074fae5eb6e9c78e8a56b3a478c5140a419e812d789165dfefc66456c055b8
10 changes: 10 additions & 0 deletions packages/vm/core/accounts/nonce.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ func nonceKey(callerAgentID isc.AgentID) kv.Key {

// Nonce returns the "total request count" for an account (its the accountNonce that is expected in the next request)
func accountNonce(state kv.KVStoreReader, callerAgentID isc.AgentID) uint64 {
if callerAgentID.Kind() == isc.AgentIDKindEthereumAddress {
panic("to get EVM nonce, call EVM contract")
}
data := state.Get(nonceKey(callerAgentID))
if data == nil {
return 0
Expand All @@ -22,11 +25,18 @@ func accountNonce(state kv.KVStoreReader, callerAgentID isc.AgentID) uint64 {
}

func IncrementNonce(state kv.KVStore, callerAgentID isc.AgentID) {
if callerAgentID.Kind() == isc.AgentIDKindEthereumAddress {
// don't update EVM nonces
return
}
next := accountNonce(state, callerAgentID)
state.Set(nonceKey(callerAgentID), codec.EncodeUint64(next))
}

func CheckNonce(state kv.KVStoreReader, agentID isc.AgentID, nonce uint64) error {
if agentID.Kind() == isc.AgentIDKindEthereumAddress {
panic("to get EVM nonce, call EVM contract")
}
expected := accountNonce(state, agentID)
if nonce != expected {
return fmt.Errorf("Invalid nonce, expected %d, got %d", expected, nonce)
Expand Down
6 changes: 5 additions & 1 deletion packages/vm/core/evm/emulator/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,12 @@ func (s *StateDB) GetBalance(addr common.Address) *big.Int {
return s.l2Balance.Get(addr)
}

func GetNonce(s kv.KVStoreReader, addr common.Address) uint64 {
return codec.MustDecodeUint64(s.Get(accountNonceKey(addr)), 0)
}

func (s *StateDB) GetNonce(addr common.Address) uint64 {
return codec.MustDecodeUint64(s.kv.Get(accountNonceKey(addr)), 0)
return GetNonce(s.kv, addr)
}

func (s *StateDB) SetNonce(addr common.Address, n uint64) {
Expand Down
25 changes: 25 additions & 0 deletions packages/vm/core/evm/evmimpl/external.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package evmimpl

import (
"fmt"

"github.com/ethereum/go-ethereum/common"

"github.com/iotaledger/wasp/packages/kv"
"github.com/iotaledger/wasp/packages/kv/subrealm"
"github.com/iotaledger/wasp/packages/vm/core/evm/emulator"
)

func Nonce(state kv.KVStoreReader, addr common.Address) uint64 {
emulatorState := evmStateSubrealmR(state)
stateDBStore := subrealm.NewReadOnly(emulatorState, emulator.KeyStateDB)
return emulator.GetNonce(stateDBStore, addr)
}

func CheckNonce(state kv.KVStore, addr common.Address, nonce uint64) error {
expected := Nonce(state, addr)
if nonce != expected {
return fmt.Errorf("Invalid nonce, expected %d, got %d", expected, nonce)
}
return nil
}
4 changes: 4 additions & 0 deletions packages/vm/core/evm/evmimpl/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ func evmStateSubrealm(state kv.KVStore) kv.KVStore {
return subrealm.New(state, evm.KeyEVMState)
}

func evmStateSubrealmR(state kv.KVStoreReader) kv.KVStoreReader {
return subrealm.NewReadOnly(state, evm.KeyEVMState)
}

func iscMagicSubrealm(state kv.KVStore) kv.KVStore {
return subrealm.New(state, evm.KeyISCMagic)
}
Expand Down
22 changes: 22 additions & 0 deletions packages/vm/core/evm/evmimpl/stateaccess.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package evmimpl

import (
"github.com/ethereum/go-ethereum/common"

"github.com/iotaledger/wasp/packages/kv"
"github.com/iotaledger/wasp/packages/kv/subrealm"
"github.com/iotaledger/wasp/packages/vm/core/evm"
)

type StateAccess struct {
state kv.KVStoreReader
}

func NewStateAccess(store kv.KVStoreReader) *StateAccess {
contractState := subrealm.NewReadOnly(store, kv.Key(evm.Contract.Hname().Bytes()))
return &StateAccess{state: contractState}
}

func (sa *StateAccess) Nonce(addr common.Address) uint64 {
return Nonce(sa.state, addr)
}
2 changes: 1 addition & 1 deletion packages/vm/vmimpl/internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ func (vmctx *vmContext) MustSaveEvent(hContract isc.Hname, topic string, payload
vmctx.reqctx.requestEventIndex++
}

// updateOffLedgerRequestNonce updates stored nonce for off ledger requests
// updateOffLedgerRequestNonce updates stored nonce for ISC off ledger requests
func (vmctx *vmContext) updateOffLedgerRequestNonce() {
vmctx.callCore(accounts.Contract, func(s kv.KVStore) {
accounts.IncrementNonce(s, vmctx.reqctx.req.SenderAccount())
Expand Down
27 changes: 17 additions & 10 deletions packages/vm/vmimpl/skipreq.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"github.com/iotaledger/wasp/packages/kv"
"github.com/iotaledger/wasp/packages/vm/core/accounts"
"github.com/iotaledger/wasp/packages/vm/core/blocklog"
"github.com/iotaledger/wasp/packages/vm/core/evm"
"github.com/iotaledger/wasp/packages/vm/core/evm/evmimpl"
"github.com/iotaledger/wasp/packages/vm/core/governance"
"github.com/iotaledger/wasp/packages/vm/vmexceptions"
)
Expand Down Expand Up @@ -58,21 +60,26 @@ func (vmctx *vmContext) checkReasonRequestProcessed() error {

// checkReasonToSkipOffLedger checks reasons to skip off ledger request
func (vmctx *vmContext) checkReasonToSkipOffLedger() error {
// first checks if it is already in backlog
// TODO check nonce instead
if err := vmctx.checkReasonRequestProcessed(); err != nil {
return err
if vmctx.task.EstimateGasMode {
return nil
}

// skip ISC nonce check for EVM requests
senderAccount := vmctx.reqctx.req.SenderAccount()
if senderAccount.Kind() == isc.AgentIDKindEthereumAddress {
return nil
reqNonce := vmctx.reqctx.req.(isc.OffLedgerRequest).Nonce()
var nonceErr error

if evmAgentID, ok := senderAccount.(*isc.EthereumAddressAgentID); ok {
vmctx.callCore(evm.Contract, func(s kv.KVStore) {
nonceErr = evmimpl.CheckNonce(s, evmAgentID.EthAddress(), reqNonce)
})
return nonceErr
}

var nonceErr error
vmctx.callCore(accounts.Contract, func(s kv.KVStore) {
nonceErr = accounts.CheckNonce(s, senderAccount, vmctx.reqctx.req.(isc.OffLedgerRequest).Nonce())
nonceErr = accounts.CheckNonce(
s,
senderAccount,
reqNonce,
)
})
return nonceErr
}
Expand Down

0 comments on commit b585186

Please sign in to comment.