test deployer flows

This commit is contained in:
Dror Tirosh 2024-07-03 18:50:20 +03:00
parent fcf91d4525
commit 65daa36d1f
3 changed files with 142 additions and 15 deletions

View file

@ -177,8 +177,9 @@ func CheckNonceRip7560(tx *types.Rip7560AccountAbstractionTx, st *state.StateDB)
func ApplyRip7560ValidationPhases(chainConfig *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, cfg vm.Config) (*ValidationPhaseResult, error) { func ApplyRip7560ValidationPhases(chainConfig *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, cfg vm.Config) (*ValidationPhaseResult, error) {
blockContext := NewEVMBlockContext(header, bc, author) blockContext := NewEVMBlockContext(header, bc, author)
sender := tx.Rip7560TransactionData().Sender
txContext := vm.TxContext{ txContext := vm.TxContext{
Origin: *tx.Rip7560TransactionData().Sender, Origin: *sender,
GasPrice: tx.GasFeeCap(), GasPrice: tx.GasFeeCap(),
} }
evm := vm.NewEVM(blockContext, txContext, statedb, chainConfig, cfg) evm := vm.NewEVM(blockContext, txContext, statedb, chainConfig, cfg)
@ -186,16 +187,25 @@ func ApplyRip7560ValidationPhases(chainConfig *params.ChainConfig, bc ChainConte
deployerMsg := prepareDeployerMessage(tx, chainConfig) deployerMsg := prepareDeployerMessage(tx, chainConfig)
var deploymentUsedGas uint64 var deploymentUsedGas uint64
if deployerMsg != nil { if deployerMsg != nil {
resultDeployer, err := ApplyMessage(evm, deployerMsg, gp) var err error
var resultDeployer *ExecutionResult
if statedb.GetCodeSize(*sender) != 0 {
err = errors.New("sender already deployed")
} else {
resultDeployer, err = ApplyMessage(evm, deployerMsg, gp)
}
if err == nil && resultDeployer != nil {
err = resultDeployer.Err
deploymentUsedGas = resultDeployer.UsedGas
}
if err == nil && statedb.GetCodeSize(*sender) == 0 {
err = errors.New("sender not deployed")
}
if err != nil { if err != nil {
return nil, err // TODO: bubble up the inner error message to the user, if possible
return nil, fmt.Errorf("account deployment failed: %v", err)
} }
statedb.IntermediateRoot(true) statedb.IntermediateRoot(true)
if resultDeployer.Failed() {
// TODO: bubble up the inner error message to the user, if possible
return nil, errors.New("account deployment failed - invalid transaction")
}
deploymentUsedGas = resultDeployer.UsedGas
} }
/*** Account Validation Frame ***/ /*** Account Validation Frame ***/
@ -338,19 +348,18 @@ func ApplyRip7560ExecutionPhase(config *params.ChainConfig, vpr *ValidationPhase
func prepareDeployerMessage(baseTx *types.Transaction, config *params.ChainConfig) *Message { func prepareDeployerMessage(baseTx *types.Transaction, config *params.ChainConfig) *Message {
tx := baseTx.Rip7560TransactionData() tx := baseTx.Rip7560TransactionData()
if len(tx.DeployerData) < 20 { if tx.Deployer == nil {
return nil return nil
} }
var deployerAddress common.Address = [20]byte(tx.DeployerData[0:20])
return &Message{ return &Message{
From: config.DeployerCallerAddress, From: config.DeployerCallerAddress,
To: &deployerAddress, To: tx.Deployer,
Value: big.NewInt(0), Value: big.NewInt(0),
GasLimit: tx.ValidationGas, GasLimit: tx.ValidationGas,
GasPrice: tx.GasFeeCap, GasPrice: tx.GasFeeCap,
GasFeeCap: tx.GasFeeCap, GasFeeCap: tx.GasFeeCap,
GasTipCap: tx.GasTipCap, GasTipCap: tx.GasTipCap,
Data: tx.DeployerData[20:], Data: tx.DeployerData,
AccessList: make(types.AccessList, 0), AccessList: make(types.AccessList, 0),
SkipAccountChecks: true, SkipAccountChecks: true,
IsRip7560Frame: true, IsRip7560Frame: true,

View file

@ -0,0 +1,88 @@
package rip7560
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"math/big"
"testing"
)
var DEPLOYER = common.HexToAddress("0xddddddddddeeeeeeeeeeddddddddddeeeeeeeeee")
func TestValidationFailure_deployerRevert(t *testing.T) {
handleTransaction(newTestContextBuilder(t).
withCode(DEFAULT_SENDER, []byte{}, DEFAULT_BALANCE).
withCode(DEPLOYER.Hex(), revertWithData([]byte{}), 0),
types.Rip7560AccountAbstractionTx{
Deployer: &DEPLOYER,
ValidationGas: 1000000000,
GasFeeCap: big.NewInt(1000000000),
}, "account deployment failed: execution reverted")
}
func TestValidationFailure_deployerOOG(t *testing.T) {
handleTransaction(newTestContextBuilder(t).
withCode(DEFAULT_SENDER, []byte{}, DEFAULT_BALANCE).
withCode(DEPLOYER.Hex(), revertWithData([]byte{}), 0),
types.Rip7560AccountAbstractionTx{
Deployer: &DEPLOYER,
ValidationGas: 1,
GasFeeCap: big.NewInt(1000000000),
}, "account deployment failed: out of gas")
}
func TestValidationFailure_senderNotDeployed(t *testing.T) {
handleTransaction(newTestContextBuilder(t).
withCode(DEFAULT_SENDER, []byte{}, DEFAULT_BALANCE).
withCode(DEPLOYER.Hex(), returnWithData([]byte{}), 0),
types.Rip7560AccountAbstractionTx{
Deployer: &DEPLOYER,
ValidationGas: 1000000000,
GasFeeCap: big.NewInt(1000000000),
}, "account deployment failed: sender not deployed")
}
func TestValidationFailure_senderAlreadyDeployed(t *testing.T) {
accountCode := revertWithData([]byte{})
deployerCode := create2(accountCode)
sender := create2_addr(DEPLOYER, accountCode)
handleTransaction(newTestContextBuilder(t).
withCode(sender.Hex(), accountCode, DEFAULT_BALANCE).
withCode(DEPLOYER.Hex(), deployerCode, 0),
types.Rip7560AccountAbstractionTx{
Sender: &sender,
Deployer: &DEPLOYER,
ValidationGas: 1000000000,
GasFeeCap: big.NewInt(1000000000),
}, "account deployment failed: sender already deployed")
}
func TestValidationFailure_senderReverts(t *testing.T) {
accountCode := revertWithData([]byte{})
deployerCode := createCode(create2(accountCode), returnWithData([]byte{}))
sender := create2_addr(DEPLOYER, accountCode)
handleTransaction(newTestContextBuilder(t).
withCode(sender.Hex(), []byte{}, DEFAULT_BALANCE).
withCode(DEPLOYER.Hex(), deployerCode, 0),
types.Rip7560AccountAbstractionTx{
Sender: &sender,
Deployer: &DEPLOYER,
ValidationGas: 1000000000,
GasFeeCap: big.NewInt(1000000000),
}, "execution reverted")
}
func TestValidation_deployer_ok(t *testing.T) {
accountCode := createAccountCode()
deployerCode := createCode(create2(accountCode), returnWithData([]byte{}))
sender := create2_addr(DEPLOYER, accountCode)
handleTransaction(newTestContextBuilder(t).
withCode(sender.Hex(), []byte{}, DEFAULT_BALANCE).
withCode(DEPLOYER.Hex(), deployerCode, 0),
types.Rip7560AccountAbstractionTx{
Sender: &sender,
Deployer: &DEPLOYER,
ValidationGas: 1000000000,
GasFeeCap: big.NewInt(1000000000),
}, "ok")
}

View file

@ -8,6 +8,7 @@ import (
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/status-im/keycard-go/hexutils" "github.com/status-im/keycard-go/hexutils"
"math/big" "math/big"
@ -45,6 +46,31 @@ func newTestContextBuilder(t *testing.T) *testContextBuilder {
} }
} }
// return a contract code that will deploy the given code
func create2_contract(deployedCode []byte) []byte {
return returnWithData(deployedCode)
}
// return the generated address when deploying the given code
func create2_addr(deployer common.Address, deployedCode []byte) common.Address {
contractCode := create2_contract(deployedCode)
data := createCode(0xff, deployer.Bytes(), common.Hash{}, crypto.Keccak256(contractCode))
return common.BytesToAddress(crypto.Keccak256(data))
}
// generate code to call create2
// note: parameter is the deployed code, not the full contract code
// always use zero value and zero salt.
func create2(deployedCode []byte) []byte {
contractCode := create2_contract(deployedCode)
return createCode(
copyToMemory(contractCode, 0),
push(0), push(len(contractCode)), push(0), push(0), vm.CREATE2,
)
}
func (tb *testContextBuilder) build() *testContext { func (tb *testContextBuilder) build() *testContext {
genesis := core.DeveloperGenesisBlock(10_000_000, &common.Address{}) genesis := core.DeveloperGenesisBlock(10_000_000, &common.Address{})
genesis.Timestamp = 100 genesis.Timestamp = 100
@ -69,7 +95,6 @@ func (tt *testContextBuilder) withAccount(addr string, balance int64) *testConte
tt.genesisAlloc[common.HexToAddress(addr)] = types.Account{Balance: big.NewInt(balance)} tt.genesisAlloc[common.HexToAddress(addr)] = types.Account{Balance: big.NewInt(balance)}
return tt return tt
} }
func (tt *testContextBuilder) withCode(addr string, code []byte, balance int64) *testContextBuilder { func (tt *testContextBuilder) withCode(addr string, code []byte, balance int64) *testContextBuilder {
if len(code) == 0 { if len(code) == 0 {
tt.genesisAlloc[common.HexToAddress(addr)] = types.Account{ tt.genesisAlloc[common.HexToAddress(addr)] = types.Account{
@ -113,8 +138,9 @@ func copyToMemory(data []byte, offset uint) []byte {
} }
if len(data) > 0 { if len(data) > 0 {
PUSHn := byte(int(vm.PUSH0) + len(data)) //push data up, as EVM is big-endian
ret = append(ret, createCode(PUSHn, data, vm.PUSH2, uint16(offset), vm.MSTORE)...) v := common.RightPadBytes(data, 32)
ret = append(ret, createCode(vm.PUSH32, v, vm.PUSH2, uint16(offset), vm.MSTORE)...)
} }
return ret return ret
} }
@ -160,6 +186,10 @@ func createCode(items ...interface{}) []byte {
panic(fmt.Errorf("int defaults to int8 (byte). use int16, etc: %v", v)) panic(fmt.Errorf("int defaults to int8 (byte). use int16, etc: %v", v))
} }
buffer.WriteByte(byte(v)) buffer.WriteByte(byte(v))
case common.Hash:
buffer.Write(v.Bytes())
case common.Address:
buffer.Write(v.Bytes())
default: default:
// should be a compile-time error... // should be a compile-time error...
panic(fmt.Errorf("unsupported type: %T", v)) panic(fmt.Errorf("unsupported type: %T", v))