From 7aaac7129ef4b2ddcf75a59f9eb8a2acccba17bd Mon Sep 17 00:00:00 2001 From: Dustin Xie Date: Thu, 11 Apr 2024 16:59:04 -0700 Subject: [PATCH 1/2] [action] add execution.To() method --- action/execution.go | 14 ++++++++++++++ action/execution_test.go | 17 +++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/action/execution.go b/action/execution.go index 197cc65614..6eeb3d85d2 100644 --- a/action/execution.go +++ b/action/execution.go @@ -91,6 +91,20 @@ func NewExecutionWithAccessList( }, nil } +// To returns the contract address pointer +// nil indicates a contract-creation transaction +func (ex *Execution) To() *common.Address { + if ex.contract == EmptyAddress { + return nil + } + addr, err := address.FromString(ex.contract) + if err != nil { + panic(err) + } + evmAddr := common.BytesToAddress(addr.Bytes()) + return &evmAddr +} + // Contract returns a contract address func (ex *Execution) Contract() string { return ex.contract } diff --git a/action/execution_test.go b/action/execution_test.go index c381bb2974..4620e0c14c 100644 --- a/action/execution_test.go +++ b/action/execution_test.go @@ -15,6 +15,7 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/require" + "github.com/iotexproject/iotex-address/address" "github.com/iotexproject/iotex-core/test/identityset" ) @@ -75,6 +76,22 @@ func TestExecutionSanityCheck(t *testing.T) { require.Contains(ex.SanityCheck().Error(), "error when validating contract's address") }) + t.Run("Empty contract address", func(t *testing.T) { + ex, err := NewExecution( + EmptyAddress, + uint64(1), + big.NewInt(0), + uint64(0), + big.NewInt(0), + []byte{}, + ) + require.NoError(err) + require.Nil(ex.To()) + addr, _ := address.FromBytes(_c1.Bytes()) + ex.contract = addr.String() + require.Equal(_c1, *ex.To()) + }) + t.Run("Negative gas price", func(t *testing.T) { ex, err := NewExecution(identityset.Address(29).String(), uint64(1), big.NewInt(100), uint64(0), big.NewInt(-1), []byte{}) require.NoError(err) From 0f4c4418e9cfbce8d55b7e5da29ab77211b8b1ac Mon Sep 17 00:00:00 2001 From: Dustin Xie Date: Wed, 10 Apr 2024 17:48:58 -0700 Subject: [PATCH 2/2] [action] add EvmTransaction to represent actions that run in EVM (#4227) --- action/evm_transaction.go | 73 +++++++++++++++++++++++ action/execution.go | 3 +- action/protocol/execution/evm/evm.go | 35 ++++------- action/protocol/execution/evm/evm_test.go | 4 +- action/protocol/execution/protocol.go | 4 +- action/protocol/poll/consortium.go | 2 +- action/protocol/poll/staking_committee.go | 2 +- 7 files changed, 94 insertions(+), 29 deletions(-) create mode 100644 action/evm_transaction.go diff --git a/action/evm_transaction.go b/action/evm_transaction.go new file mode 100644 index 0000000000..8264448e67 --- /dev/null +++ b/action/evm_transaction.go @@ -0,0 +1,73 @@ +// Copyright (c) 2024 IoTeX Foundation +// 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 License 2.0 that can be found in the LICENSE file. + +package action + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +type ( + // EvmTransaction represents an action to be executed by EVM protocol + // as of now 3 types of transactions are supported: + // 1. Legacy transaction + // 2. EIP-2930 access list transaction + // 3. EIP-4844 shard blob transaction + EvmTransaction struct { + inner TxData + } + + TxData interface { + Nonce() uint64 + GasLimit() uint64 + GasPrice() *big.Int + Amount() *big.Int + To() *common.Address + Data() []byte + AccessList() types.AccessList + } +) + +func NewEvmTx(a Action) *EvmTransaction { + tx := new(EvmTransaction) + switch act := a.(type) { + case *Execution: + tx.inner = act + default: + panic("unsupported action type") + } + return tx +} + +func (tx *EvmTransaction) Nonce() uint64 { + return tx.inner.Nonce() +} + +func (tx *EvmTransaction) Gas() uint64 { + return tx.inner.GasLimit() +} + +func (tx *EvmTransaction) GasPrice() *big.Int { + return tx.inner.GasPrice() +} + +func (tx *EvmTransaction) Value() *big.Int { + return tx.inner.Amount() +} + +func (tx *EvmTransaction) To() *common.Address { + return tx.inner.To() +} + +func (tx *EvmTransaction) Data() []byte { + return tx.inner.Data() +} + +func (tx *EvmTransaction) AccessList() types.AccessList { + return tx.inner.AccessList() +} diff --git a/action/execution.go b/action/execution.go index 6eeb3d85d2..f7b993180d 100644 --- a/action/execution.go +++ b/action/execution.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019 IoTeX Foundation +// Copyright (c) 2024 IoTeX Foundation // 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 License 2.0 that can be found in the LICENSE file. @@ -33,6 +33,7 @@ const ( var ( _ hasDestination = (*Execution)(nil) _ EthCompatibleAction = (*Execution)(nil) + _ TxData = (*Execution)(nil) ) // Execution defines the struct of account-based contract execution diff --git a/action/protocol/execution/evm/evm.go b/action/protocol/execution/evm/evm.go index 929ac09092..4fd7105fa5 100644 --- a/action/protocol/execution/evm/evm.go +++ b/action/protocol/execution/evm/evm.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019 IoTeX Foundation +// Copyright (c) 2024 IoTeX Foundation // 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 License 2.0 that can be found in the LICENSE file. @@ -108,7 +108,7 @@ type ( // newParams creates a new context for use in the EVM. func newParams( ctx context.Context, - execution *action.Execution, + execution *action.EvmTransaction, stateDB *StateDBAdapter, ) (*Params, error) { var ( @@ -121,21 +121,11 @@ func newParams( executorAddr = common.BytesToAddress(actionCtx.Caller.Bytes()) getBlockHash = helperCtx.GetBlockHash - vmConfig vm.Config - contractAddrPointer *common.Address - getHashFn vm.GetHashFunc + vmConfig vm.Config + getHashFn vm.GetHashFunc ) - if dest := execution.Contract(); dest != action.EmptyAddress { - contract, err := address.FromString(execution.Contract()) - if err != nil { - return nil, errors.Wrapf(err, "failed to decode contract address %s", dest) - } - contractAddr := common.BytesToAddress(contract.Bytes()) - contractAddrPointer = &contractAddr - } - - gasLimit := execution.GasLimit() + gasLimit := execution.Gas() // Reset gas limit to the system wide action gas limit cap if it's greater than it if blkCtx.BlockHeight > 0 && featureCtx.SystemWideActionGasLimit && gasLimit > _preAleutianActionGasLimit { gasLimit = _preAleutianActionGasLimit @@ -200,8 +190,8 @@ func newParams( GasPrice: execution.GasPrice(), }, execution.Nonce(), - execution.Amount(), - contractAddrPointer, + execution.Value(), + execution.To(), gasLimit, execution.Data(), execution.AccessList(), @@ -237,7 +227,7 @@ func securityDeposit(ps *Params, stateDB vm.StateDB, gasLimit uint64) error { func ExecuteContract( ctx context.Context, sm protocol.StateManager, - execution *action.Execution, + execution *action.EvmTransaction, ) ([]byte, *action.Receipt, error) { ctx, span := tracer.NewSpan(ctx, "evm.ExecuteContract") defer span.End() @@ -332,16 +322,17 @@ func ExecuteContract( return retval, receipt, nil } -func processSGD(ctx context.Context, sm protocol.StateManager, execution *action.Execution, consumedGas uint64, sgd SGDRegistry, +func processSGD(ctx context.Context, sm protocol.StateManager, execution *action.EvmTransaction, consumedGas uint64, sgd SGDRegistry, ) (address.Address, uint64, error) { - if execution.Contract() == action.EmptyAddress { + if execution.To() == nil { return nil, 0, nil } height, err := sm.Height() if err != nil { return nil, 0, err } - receiver, percentage, ok, err := sgd.CheckContract(ctx, execution.Contract(), height-1) + contract, _ := address.FromBytes((*execution.To())[:]) + receiver, percentage, ok, err := sgd.CheckContract(ctx, contract.String(), height-1) if err != nil || !ok { return nil, 0, err } @@ -680,6 +671,6 @@ func SimulateExecution( return ExecuteContract( ctx, sm, - ex, + action.NewEvmTx(ex), ) } diff --git a/action/protocol/execution/evm/evm_test.go b/action/protocol/execution/evm/evm_test.go index f995bfbab4..8865bd244c 100644 --- a/action/protocol/execution/evm/evm_test.go +++ b/action/protocol/execution/evm/evm_test.go @@ -72,7 +72,7 @@ func TestExecuteContractFailure(t *testing.T) { }, Sgd: nil, }) - retval, receipt, err := ExecuteContract(ctx, sm, e) + retval, receipt, err := ExecuteContract(ctx, sm, action.NewEvmTx(e)) require.Nil(t, retval) require.Nil(t, receipt) require.Error(t, err) @@ -302,7 +302,7 @@ func TestConstantinople(t *testing.T) { }) stateDB, err := prepareStateDB(fCtx, sm) require.NoError(err) - ps, err := newParams(fCtx, ex, stateDB) + ps, err := newParams(fCtx, action.NewEvmTx(ex), stateDB) require.NoError(err) evm := vm.NewEVM(ps.context, ps.txCtx, stateDB, ps.chainConfig, ps.evmConfig) diff --git a/action/protocol/execution/protocol.go b/action/protocol/execution/protocol.go index ba5264ea21..f6d77295e9 100644 --- a/action/protocol/execution/protocol.go +++ b/action/protocol/execution/protocol.go @@ -1,4 +1,4 @@ -// Copyright (c) 2019 IoTeX Foundation +// Copyright (c) 2024 IoTeX Foundation // 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 License 2.0 that can be found in the LICENSE file. @@ -74,7 +74,7 @@ func (p *Protocol) Handle(ctx context.Context, act action.Action, sm protocol.St DepositGasFunc: p.depositGas, Sgd: p.sgdRegistry, }) - _, receipt, err := evm.ExecuteContract(ctx, sm, exec) + _, receipt, err := evm.ExecuteContract(ctx, sm, action.NewEvmTx(exec)) if err != nil { return nil, errors.Wrap(err, "failed to execute contract") diff --git a/action/protocol/poll/consortium.go b/action/protocol/poll/consortium.go index 7fe54d61b6..7f85cb73af 100644 --- a/action/protocol/poll/consortium.go +++ b/action/protocol/poll/consortium.go @@ -145,7 +145,7 @@ func (cc *consortiumCommittee) CreateGenesisStates(ctx context.Context, sm proto _, receipt, err := evm.ExecuteContract( ctx, sm, - execution, + action.NewEvmTx(execution), ) if err != nil { return err diff --git a/action/protocol/poll/staking_committee.go b/action/protocol/poll/staking_committee.go index fb97d01c84..c93e2ed3f9 100644 --- a/action/protocol/poll/staking_committee.go +++ b/action/protocol/poll/staking_committee.go @@ -152,7 +152,7 @@ func (sc *stakingCommittee) CreateGenesisStates(ctx context.Context, sm protocol _, receipt, err := evm.ExecuteContract( ctx, sm, - execution, + action.NewEvmTx(execution), ) if err != nil { return err