accounts/abi/backends: change SimulatedBackend to interface

This commit is contained in:
Felix Lange 2023-12-01 23:44:23 +01:00 committed by Marius van der Wijden
parent cb549c9455
commit 71eec44d7b
2 changed files with 84 additions and 25 deletions

View file

@ -19,8 +19,12 @@ package backends
import ( import (
"context" "context"
"math" "math"
"math/big"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/eth/catalyst"
@ -34,9 +38,51 @@ import (
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
) )
var _ bind.ContractBackend = (*SimulatedBackend)(nil) var _ bind.ContractBackend = (*simBackend)(nil)
type SimulatedBackend struct { type SimChainManagement interface {
// Commit seals a block and moves the chain forward to a new empty block.
Commit() common.Hash
// Rollback un-sends previously added transactions.
Rollback()
// Fork sets the head to a new block, which is based on the provided parentHash.
Fork(ctx context.Context, parentHash common.Hash) error
// AdjustTime changes the block timestamp.
AdjustTime(adjustment time.Duration) error
// Close closes the backend. You need to call this to clean up resources.
Close() error
}
// TODO: these methods should have their own interface in the ethereum package.
type SimChainExtras interface {
BlockNumber(context.Context) (uint64, error)
ChainID(context.Context) (*big.Int, error)
}
// SimulatedBackend all interfaces in the ethereum package, but is based on a
// simulated blockchain. It is intended for testing purposes.
type SimulatedBackend interface {
SimChainManagement
// The backend implements all interfaces in the ethereum package.
ethereum.ChainReader
ethereum.ChainStateReader
ethereum.ContractCaller
ethereum.GasEstimator
ethereum.LogFilterer
ethereum.GasPricer
ethereum.PendingStateReader
ethereum.PendingContractCaller
ethereum.TransactionReader
ethereum.TransactionSender
SimChainExtras
}
type simBackend struct {
eth *eth.Ethereum eth *eth.Ethereum
*catalyst.SimulatedBeacon *catalyst.SimulatedBeacon
*ethclient.Client *ethclient.Client
@ -45,7 +91,7 @@ type SimulatedBackend struct {
// NewSimulatedBackend creates a new binding backend using a simulated blockchain // NewSimulatedBackend creates a new binding backend using a simulated blockchain
// for testing purposes. // for testing purposes.
// A simulated backend always uses chainID 1337. // A simulated backend always uses chainID 1337.
func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend { func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) SimulatedBackend {
// Setup the node object // Setup the node object
nodeConf := node.DefaultConfig nodeConf := node.DefaultConfig
nodeConf.DataDir = "" nodeConf.DataDir = ""
@ -76,7 +122,7 @@ func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBac
// NewSimWithNode sets up a simulated backend on an existing node // NewSimWithNode sets up a simulated backend on an existing node
// this allows users to do persistent simulations. // this allows users to do persistent simulations.
// The provided node must not be started and will be started by NewSimWithNode // The provided node must not be started and will be started by NewSimWithNode
func NewSimWithNode(stack *node.Node, conf *eth.Config, blockPeriod uint64) (*SimulatedBackend, error) { func NewSimWithNode(stack *node.Node, conf *eth.Config, blockPeriod uint64) (SimulatedBackend, error) {
backend, err := eth.New(stack, conf) backend, err := eth.New(stack, conf)
if err != nil { if err != nil {
return nil, err return nil, err
@ -105,14 +151,14 @@ func NewSimWithNode(stack *node.Node, conf *eth.Config, blockPeriod uint64) (*Si
return nil, err return nil, err
} }
return &SimulatedBackend{ return &simBackend{
eth: backend, eth: backend,
SimulatedBeacon: beacon, SimulatedBeacon: beacon,
Client: ethclient.NewClient(stack.Attach()), Client: ethclient.NewClient(stack.Attach()),
}, nil }, nil
} }
func (n *SimulatedBackend) Close() error { func (n *simBackend) Close() error {
if n.Client != nil { if n.Client != nil {
n.Client.Close() n.Client.Close()
n.Client = nil n.Client = nil

View file

@ -36,7 +36,7 @@ var (
testAddr = crypto.PubkeyToAddress(testKey.PublicKey) testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
) )
func simTestBackend(testAddr common.Address) *SimulatedBackend { func simTestBackend(testAddr common.Address) SimulatedBackend {
return NewSimulatedBackend( return NewSimulatedBackend(
core.GenesisAlloc{ core.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000000000)}, testAddr: {Balance: big.NewInt(10000000000000000)},
@ -44,12 +44,7 @@ func simTestBackend(testAddr common.Address) *SimulatedBackend {
) )
} }
func (sim *SimulatedBackend) currentBlock() *types.Block { func newTx(sim SimulatedBackend, key *ecdsa.PrivateKey) (*types.Transaction, error) {
current, _ := sim.BlockByNumber(context.Background(), nil)
return current
}
func (sim *SimulatedBackend) newTx(key *ecdsa.PrivateKey) (*types.Transaction, error) {
// create a signed transaction to send // create a signed transaction to send
head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1)) gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
@ -113,7 +108,7 @@ func TestSendTransaction(t *testing.T) {
defer sim.Close() defer sim.Close()
bgCtx := context.Background() bgCtx := context.Background()
signedTx, err := sim.newTx(testKey) signedTx, err := newTx(sim, testKey)
if err != nil { if err != nil {
t.Errorf("could not create transaction: %v", err) t.Errorf("could not create transaction: %v", err)
} }
@ -148,25 +143,35 @@ func TestFork(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
ctx := context.Background()
// 1. // 1.
parent := sim.currentBlock() parent, _ := sim.HeaderByNumber(ctx, nil)
// 2. // 2.
n := int(rand.Int31n(21)) n := int(rand.Int31n(21))
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
sim.Commit() sim.Commit()
} }
// 3. // 3.
if sim.currentBlock().Number().Uint64() != uint64(n) { b, _ := sim.BlockNumber(ctx)
if b != uint64(n) {
t.Error("wrong chain length") t.Error("wrong chain length")
} }
// 4. // 4.
sim.Fork(context.Background(), parent.Hash()) sim.Fork(ctx, parent.Hash())
// 5. // 5.
for i := 0; i < n+1; i++ { for i := 0; i < n+1; i++ {
sim.Commit() sim.Commit()
} }
// 6. // 6.
if sim.currentBlock().Number().Uint64() != uint64(n+1) { b, _ = sim.BlockNumber(ctx)
if b != uint64(n+1) {
t.Error("wrong chain length") t.Error("wrong chain length")
} }
} }
@ -185,24 +190,30 @@ func TestForkResendTx(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey) testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr) sim := simTestBackend(testAddr)
defer sim.Close() defer sim.Close()
// 1. // 1.
parent := sim.currentBlock() ctx := context.Background()
parent, _ := sim.HeaderByNumber(ctx, nil)
// 2. // 2.
tx, err := sim.newTx(testKey) tx, err := newTx(sim, testKey)
if err != nil { if err != nil {
t.Fatalf("could not create transaction: %v", err) t.Fatalf("could not create transaction: %v", err)
} }
sim.SendTransaction(context.Background(), tx) sim.SendTransaction(context.Background(), tx)
sim.Commit() sim.Commit()
// 3. // 3.
receipt, _ := sim.TransactionReceipt(context.Background(), tx.Hash()) receipt, _ := sim.TransactionReceipt(context.Background(), tx.Hash())
if h := receipt.BlockNumber.Uint64(); h != 1 { if h := receipt.BlockNumber.Uint64(); h != 1 {
t.Errorf("TX included in wrong block: %d", h) t.Errorf("TX included in wrong block: %d", h)
} }
// 4. // 4.
if err := sim.Fork(context.Background(), parent.Hash()); err != nil { if err := sim.Fork(context.Background(), parent.Hash()); err != nil {
t.Errorf("forking: %v", err) t.Errorf("forking: %v", err)
} }
// 5. // 5.
sim.Commit() sim.Commit()
if err := sim.SendTransaction(context.Background(), tx); err != nil { if err := sim.SendTransaction(context.Background(), tx); err != nil {
@ -223,7 +234,8 @@ func TestCommitReturnValue(t *testing.T) {
// Test if Commit returns the correct block hash // Test if Commit returns the correct block hash
h1 := sim.Commit() h1 := sim.Commit()
if h1 != sim.currentBlock().Hash() { cur, _ := sim.HeaderByNumber(context.Background(), nil)
if h1 != cur.Hash() {
t.Error("Commit did not return the hash of the last block.") t.Error("Commit did not return the hash of the last block.")
} }
@ -262,14 +274,15 @@ func TestAdjustTimeAfterFork(t *testing.T) {
defer sim.Close() defer sim.Close()
sim.Commit() // h1 sim.Commit() // h1
h1 := sim.currentBlock().Hash() h1, _ := sim.HeaderByNumber(context.Background(), nil)
sim.Commit() // h2 sim.Commit() // h2
sim.Fork(context.Background(), h1) sim.Fork(context.Background(), h1.Hash())
sim.AdjustTime(1 * time.Second) sim.AdjustTime(1 * time.Second)
sim.Commit() sim.Commit()
head := sim.currentBlock() head, _ := sim.HeaderByNumber(context.Background(), nil)
if head.Number() == common.Big2 && head.ParentHash() != h1 { if head.Number.Uint64() == 2 && head.ParentHash != h1.Hash() {
t.Errorf("failed to build block on fork") t.Errorf("failed to build block on fork")
} }
} }