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 (
"context"
"math"
"math/big"
"time"
"github.com/ethereum/go-ethereum"
"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/eth"
"github.com/ethereum/go-ethereum/eth/catalyst"
@ -34,9 +38,51 @@ import (
"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
*catalyst.SimulatedBeacon
*ethclient.Client
@ -45,7 +91,7 @@ type SimulatedBackend struct {
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
// for testing purposes.
// 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
nodeConf := node.DefaultConfig
nodeConf.DataDir = ""
@ -76,7 +122,7 @@ func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBac
// NewSimWithNode sets up a simulated backend on an existing node
// this allows users to do persistent simulations.
// 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)
if err != nil {
return nil, err
@ -105,14 +151,14 @@ func NewSimWithNode(stack *node.Node, conf *eth.Config, blockPeriod uint64) (*Si
return nil, err
}
return &SimulatedBackend{
return &simBackend{
eth: backend,
SimulatedBeacon: beacon,
Client: ethclient.NewClient(stack.Attach()),
}, nil
}
func (n *SimulatedBackend) Close() error {
func (n *simBackend) Close() error {
if n.Client != nil {
n.Client.Close()
n.Client = nil

View file

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