mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
Test ApplyRip7560ValidationPhases (#5)
* initial UT for ApplyRip7560ValidationPhases test ./tests/rip7560/ * refactor test context * add some error tests * returnData helper * github actions for rip7560 tests * update path * refactor test utils * refactor textContextBuilder * remove prestate json. * pr comments
This commit is contained in:
parent
960cd7f64e
commit
507f4724a7
4 changed files with 263 additions and 2 deletions
4
.github/workflows/go.yml
vendored
4
.github/workflows/go.yml
vendored
|
|
@ -2,9 +2,9 @@ name: i386 linux tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [ disabled ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [ disabled ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
|
||||||
34
.github/workflows/rip7560test.yml
vendored
Normal file
34
.github/workflows/rip7560test.yml
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
name: rip7560-test
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Run RIP-7560 tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Check out code into the Go module directory
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.22.1
|
||||||
|
|
||||||
|
- name: Cache Go dependencies
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
|
- name: Get dependencies
|
||||||
|
run: go mod download
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: go test -v ./tests/rip7560
|
||||||
140
tests/rip7560/rip7560TestUtils.go
Normal file
140
tests/rip7560/rip7560TestUtils.go
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
package rip7560
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/status-im/keycard-go/hexutils"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const DEFAULT_SENDER = "0x1111111111222222222233333333334444444444"
|
||||||
|
|
||||||
|
type testContext struct {
|
||||||
|
genesisAlloc types.GenesisAlloc
|
||||||
|
t *testing.T
|
||||||
|
chainContext *ethapi.ChainContext
|
||||||
|
chainConfig *params.ChainConfig
|
||||||
|
gaspool *core.GasPool
|
||||||
|
genesis *core.Genesis
|
||||||
|
genesisBlock *types.Block
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestContext(t *testing.T) *testContext {
|
||||||
|
return newTestContextBuilder(t).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
type testContextBuilder struct {
|
||||||
|
t *testing.T
|
||||||
|
chainConfig *params.ChainConfig
|
||||||
|
genesisAlloc types.GenesisAlloc
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestContextBuilder(t *testing.T) *testContextBuilder {
|
||||||
|
genesisAlloc := types.GenesisAlloc{}
|
||||||
|
|
||||||
|
chainConfig := params.AllDevChainProtocolChanges
|
||||||
|
// probably bug in geth..
|
||||||
|
chainConfig.PragueTime = chainConfig.CancunTime
|
||||||
|
|
||||||
|
return &testContextBuilder{
|
||||||
|
t: t,
|
||||||
|
chainConfig: chainConfig,
|
||||||
|
genesisAlloc: genesisAlloc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tb *testContextBuilder) build() *testContext {
|
||||||
|
genesis := &core.Genesis{
|
||||||
|
Config: params.AllDevChainProtocolChanges,
|
||||||
|
Alloc: tb.genesisAlloc,
|
||||||
|
}
|
||||||
|
genesisBlock := genesis.ToBlock()
|
||||||
|
gaspool := new(core.GasPool).AddGas(genesisBlock.GasLimit())
|
||||||
|
|
||||||
|
//TODO: fill some mock backend...
|
||||||
|
var backend ethapi.Backend
|
||||||
|
|
||||||
|
return &testContext{
|
||||||
|
t: tb.t,
|
||||||
|
genesisAlloc: tb.genesisAlloc,
|
||||||
|
chainContext: ethapi.NewChainContext(context.TODO(), backend),
|
||||||
|
chainConfig: tb.chainConfig,
|
||||||
|
genesis: genesis,
|
||||||
|
genesisBlock: genesisBlock,
|
||||||
|
gaspool: gaspool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add EOA account with balance
|
||||||
|
func (tt *testContextBuilder) withAccount(addr string, balance int64) *testContextBuilder {
|
||||||
|
tt.genesisAlloc[common.HexToAddress(addr)] = types.Account{Balance: big.NewInt(balance)}
|
||||||
|
return tt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tt *testContextBuilder) withCode(addr string, code []byte, balance int64) *testContextBuilder {
|
||||||
|
if len(code) == 0 {
|
||||||
|
tt.genesisAlloc[common.HexToAddress(addr)] = types.Account{
|
||||||
|
Balance: big.NewInt(balance),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tt.genesisAlloc[common.HexToAddress(addr)] = types.Account{
|
||||||
|
Code: code,
|
||||||
|
Balance: big.NewInt(balance),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tt
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate the code to return the given byte array (up to 32 bytes)
|
||||||
|
func returnData(data []byte) []byte {
|
||||||
|
//couldn't get geth to support PUSH0 ...
|
||||||
|
datalen := len(data)
|
||||||
|
if datalen == 0 {
|
||||||
|
data = []byte{0}
|
||||||
|
}
|
||||||
|
if datalen > 32 {
|
||||||
|
panic(fmt.Errorf("data length is too big %v", data))
|
||||||
|
}
|
||||||
|
|
||||||
|
PUSHn := byte(int(vm.PUSH0) + datalen)
|
||||||
|
ret := createCode(PUSHn, data, vm.PUSH1, 0, vm.MSTORE, vm.PUSH1, 32, vm.PUSH1, 0, vm.RETURN)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// create EVM code from OpCode, byte and []bytes
|
||||||
|
func createCode(items ...interface{}) []byte {
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
|
||||||
|
for _, item := range items {
|
||||||
|
switch v := item.(type) {
|
||||||
|
case string:
|
||||||
|
buffer.Write(hexutils.HexToBytes(v))
|
||||||
|
case vm.OpCode:
|
||||||
|
buffer.WriteByte(byte(v))
|
||||||
|
case byte:
|
||||||
|
buffer.WriteByte(v)
|
||||||
|
case []byte:
|
||||||
|
buffer.Write(v)
|
||||||
|
case int8:
|
||||||
|
buffer.WriteByte(byte(v))
|
||||||
|
case int:
|
||||||
|
if v >= 256 {
|
||||||
|
panic(fmt.Errorf("int defaults to int8 (byte). int16, etc: %v", v))
|
||||||
|
}
|
||||||
|
buffer.WriteByte(byte(v))
|
||||||
|
default:
|
||||||
|
// should be a compile-time error...
|
||||||
|
panic(fmt.Errorf("unsupported type: %T", v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.Bytes()
|
||||||
|
}
|
||||||
87
tests/rip7560/validation_test.go
Normal file
87
tests/rip7560/validation_test.go
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
package rip7560
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/tests"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidation_OOG(t *testing.T) {
|
||||||
|
magic := big.NewInt(0xbf45c166)
|
||||||
|
magic.Lsh(magic, 256-32)
|
||||||
|
|
||||||
|
validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, returnData(magic.Bytes()), 0), types.Rip7560AccountAbstractionTx{
|
||||||
|
ValidationGas: uint64(1),
|
||||||
|
GasFeeCap: big.NewInt(1000000000),
|
||||||
|
}, "out of gas")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidation_ok(t *testing.T) {
|
||||||
|
magic := big.NewInt(0xbf45c166)
|
||||||
|
magic.Lsh(magic, 256-32)
|
||||||
|
|
||||||
|
validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, returnData(magic.Bytes()), 0), types.Rip7560AccountAbstractionTx{
|
||||||
|
ValidationGas: uint64(1000000000),
|
||||||
|
GasFeeCap: big.NewInt(1000000000),
|
||||||
|
}, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidation_account_revert(t *testing.T) {
|
||||||
|
validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER,
|
||||||
|
createCode(vm.PUSH1, 0, vm.DUP1, vm.REVERT), 0), types.Rip7560AccountAbstractionTx{
|
||||||
|
ValidationGas: uint64(1000000000),
|
||||||
|
GasFeeCap: big.NewInt(1000000000),
|
||||||
|
}, "execution reverted")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidation_account_no_return_value(t *testing.T) {
|
||||||
|
validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER, []byte{
|
||||||
|
byte(vm.PUSH1), 0, byte(vm.DUP1), byte(vm.RETURN),
|
||||||
|
}, 0), types.Rip7560AccountAbstractionTx{
|
||||||
|
ValidationGas: uint64(1000000000),
|
||||||
|
GasFeeCap: big.NewInt(1000000000),
|
||||||
|
}, "invalid account return data length")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidation_account_wrong_return_value(t *testing.T) {
|
||||||
|
validatePhase(newTestContextBuilder(t).withCode(DEFAULT_SENDER,
|
||||||
|
returnData(createCode(1)),
|
||||||
|
0), types.Rip7560AccountAbstractionTx{
|
||||||
|
ValidationGas: uint64(1000000000),
|
||||||
|
GasFeeCap: big.NewInt(1000000000),
|
||||||
|
}, "account did not return correct MAGIC_VALUE")
|
||||||
|
}
|
||||||
|
|
||||||
|
func validatePhase(tb *testContextBuilder, aatx types.Rip7560AccountAbstractionTx, expectedErr string) {
|
||||||
|
t := tb.build()
|
||||||
|
if aatx.Sender == nil {
|
||||||
|
//pre-deployed sender account
|
||||||
|
Sender := common.HexToAddress(DEFAULT_SENDER)
|
||||||
|
aatx.Sender = &Sender
|
||||||
|
}
|
||||||
|
tx := types.NewTx(&aatx)
|
||||||
|
|
||||||
|
var state = tests.MakePreState(rawdb.NewMemoryDatabase(), t.genesisAlloc, false, rawdb.HashScheme)
|
||||||
|
defer state.Close()
|
||||||
|
|
||||||
|
_, err := core.ApplyRip7560ValidationPhases(t.chainConfig, t.chainContext, &common.Address{}, t.gaspool, state.StateDB, t.genesisBlock.Header(), tx, vm.Config{})
|
||||||
|
// err string or empty if nil
|
||||||
|
errStr := ""
|
||||||
|
if err != nil {
|
||||||
|
errStr = err.Error()
|
||||||
|
}
|
||||||
|
assert.Equal(t.t, expectedErr, errStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
//test failure on non-rip7560
|
||||||
|
|
||||||
|
//IntrinsicGas: for validation frame, should return the max possible gas.
|
||||||
|
// - execution should be "free" (and refund the excess)
|
||||||
|
// geth increment nonce before "call" our validation frame. (in ApplyMessage)
|
||||||
Loading…
Reference in a new issue