diff --git a/core/error.go b/core/error.go index daef2a474b78d..5503a6e6f2ef8 100644 --- a/core/error.go +++ b/core/error.go @@ -37,6 +37,10 @@ var ( // next one expected based on the local chain. ErrNonceTooHigh = errors.New("nonce too high") + // ErrNonceMax is returned if the nonce of a transaction sender account has + // maximum allowed value and would become invalid if incremented. + ErrNonceMax = errors.New("nonce has max value") + ErrNotXDPoS = errors.New("XDPoS not found in config") ErrNotFoundM1 = errors.New("list M1 not found ") diff --git a/core/state_transition.go b/core/state_transition.go index 3c581d2d9b237..3e1120ceb9866 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -197,16 +197,19 @@ func (st *StateTransition) buyGas() error { } func (st *StateTransition) preCheck() error { - msg := st.msg - sender := st.from() - // Make sure this transaction's nonce is correct - if msg.CheckNonce() { - nonce := st.state.GetNonce(sender.Address()) - if nonce < msg.Nonce() { - return ErrNonceTooHigh - } else if nonce > msg.Nonce() { - return ErrNonceTooLow + if st.msg.CheckNonce() { + // Make sure this transaction's nonce is correct. + stNonce := st.state.GetNonce(st.from().Address()) + if msgNonce := st.msg.Nonce(); stNonce < msgNonce { + return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooHigh, + st.msg.From().Hex(), msgNonce, stNonce) + } else if stNonce > msgNonce { + return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow, + st.msg.From().Hex(), msgNonce, stNonce) + } else if stNonce+1 < stNonce { + return fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax, + st.msg.From().Hex(), stNonce) } } return st.buyGas() diff --git a/core/vm/errors.go b/core/vm/errors.go index 236e22568b585..a1e8adf49ef0a 100644 --- a/core/vm/errors.go +++ b/core/vm/errors.go @@ -34,6 +34,7 @@ var ( ErrWriteProtection = errors.New("write protection") ErrReturnDataOutOfBounds = errors.New("return data out of bounds") ErrGasUintOverflow = errors.New("gas uint64 overflow") + ErrNonceUintOverflow = errors.New("nonce uint64 overflow") // errStopToken is an internal token indicating interpreter loop termination, // never returned to outside callers. diff --git a/core/vm/evm.go b/core/vm/evm.go index dc08607bb3d1c..18b132b1809b1 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -443,6 +443,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, return nil, common.Address{}, gas, ErrInsufficientBalance } nonce := evm.StateDB.GetNonce(caller.Address()) + if nonce+1 < nonce { + return nil, common.Address{}, gas, ErrNonceUintOverflow + } evm.StateDB.SetNonce(caller.Address(), nonce+1) // We add this to the access list _before_ taking a snapshot. Even if the creation fails, // the access-list change should not be rolled back