Files
zeekayandHanzo Dev f2d18226a0 Convert BlackholeAddr to a geth address where one is wanted
constants.BlackholeAddr is a plain [20]byte now, so constants no longer imports
geth. Go assigns an unnamed [20]byte to common.Address implicitly, so every call
site still compiled, but one of them changed behavior: fmt %v prints
[1 0 0 ...] for an array and 0x0100000000000000000000000000000000000000 for a
common.Address, so the invalid-coinbase error in syntacticVerify lost its hex
form. Writing common.Address(constants.BlackholeAddr) restores the message and
names the type at the boundary; the conversion is free.

Six sites take the conversion: the etherbase in initializeChain, the simulated
backend etherbase, the coinbase error text, the module address in the registerer
test, and the two SetCoinbase calls. The last two sit in files carrying
//go:build ignore, so the compiler does not check them.

Two sites compare bytes and stay as they are, the registerer against a module
address and syntacticVerify against the header coinbase.

go.mod is untouched. The requirement still reads constants v1.6.2 and the bump
waits on the tag.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-06 00:28:51 -07:00

286 lines
8.3 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Copyright 2023 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package simulated
import (
"errors"
"math/big"
"time"
"github.com/luxfi/constants"
"github.com/luxfi/coreth/consensus/dummy"
"github.com/luxfi/coreth/core"
"github.com/luxfi/coreth/eth"
"github.com/luxfi/coreth/eth/ethconfig"
"github.com/luxfi/coreth/ethclient"
"github.com/luxfi/coreth/interfaces"
"github.com/luxfi/coreth/node"
"github.com/luxfi/coreth/params"
"github.com/luxfi/coreth/rpc"
ethereum "github.com/luxfi/geth"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/core/rawdb"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/timer/mockable"
)
var _ eth.PushGossiper = (*fakePushGossiper)(nil)
type fakePushGossiper struct{}
func (*fakePushGossiper) Add(*types.Transaction) {}
// Client exposes the methods provided by the Ethereum RPC client.
type Client interface {
ethereum.BlockNumberReader
ethereum.ChainReader
ethereum.ChainStateReader
ethereum.ContractCaller
ethereum.GasEstimator
ethereum.GasPricer
ethereum.GasPricer1559
ethereum.FeeHistoryReader
ethereum.LogFilterer
interfaces.AcceptedStateReader
interfaces.AcceptedContractCaller
ethereum.TransactionReader
ethereum.TransactionSender
ethereum.ChainIDReader
}
// simClient wraps ethclient. This exists to prevent extracting ethclient.Client
// from the Client interface returned by Backend.
type simClient struct {
*ethclient.Client
}
// Backend is a simulated blockchain. You can use it to test your contracts or
// other code that interacts with the Ethereum chain.
type Backend struct {
eth *eth.Ethereum
client simClient
clock *mockable.Clock
server *rpc.Server
}
// NewBackend creates a new simulated blockchain that can be used as a backend for
// contract bindings in unit tests.
//
// A simulated backend always uses chainID 1337.
func NewBackend(alloc types.GenesisAlloc, options ...func(nodeConf *node.Config, ethConf *ethconfig.Config)) *Backend {
// Copy TestChainConfig properly, preserving extras from the original config
chainConfigCopy := params.Copy(params.TestChainConfig)
// Re-associate extras with the new pointer (Copy loses the association)
chainConfig := params.WithExtra(chainConfigCopy, params.GetExtra(params.TestChainConfig))
chainConfig.ChainID = big.NewInt(1337)
// Create the default configurations for the outer node shell and the Ethereum
// service to mutate with the options afterwards
nodeConf := node.DefaultConfig
ethConf := ethconfig.DefaultConfig
ethConf.Genesis = &core.Genesis{
Config: chainConfig,
Alloc: alloc,
}
ethConf.AllowUnfinalizedQueries = true
ethConf.Miner.Etherbase = common.Address(constants.BlackholeAddr)
ethConf.Miner.TestOnlyAllowDuplicateBlocks = true
ethConf.TxPool.NoLocals = true
for _, option := range options {
option(&nodeConf, &ethConf)
}
// Assemble the Ethereum stack to run the chain with
stack, err := node.New(&nodeConf)
if err != nil {
panic(err) // this should never happen
}
sim, err := newWithNode(stack, &ethConf, 0)
if err != nil {
panic(err) // this should never happen
}
return sim
}
// newWithNode sets up a simulated backend on an existing node. The provided node
// must not be started and will be started by this method.
func newWithNode(stack *node.Node, conf *eth.Config, blockPeriod uint64) (*Backend, error) {
chaindb := rawdb.NewMemoryDatabase()
clock := &mockable.Clock{}
clock.Set(time.Unix(0, 0))
engine := dummy.NewFakerWithModeAndClock(
dummy.Mode{ModeSkipCoinbase: true}, clock,
)
backend, err := eth.New(
stack, conf, &fakePushGossiper{}, chaindb, eth.Settings{}, common.Hash{},
engine, clock,
)
if err != nil {
return nil, err
}
server := rpc.NewServer(0)
for _, api := range backend.APIs() {
if err := server.RegisterName(api.Namespace, api.Service); err != nil {
return nil, err
}
}
return &Backend{
eth: backend,
client: simClient{ethclient.NewClient(rpc.DialInProc(server))},
clock: clock,
server: server,
}, nil
}
// Close shuts down the simBackend.
// The simulated backend can't be used afterwards.
func (n *Backend) Close() error {
if n.client.Client != nil {
n.client.Close()
}
n.server.Stop()
return nil
}
// Commit seals a block and moves the chain forward to a new empty block.
func (n *Backend) Commit(accept bool) common.Hash {
hash, err := n.buildBlock(accept, 10)
if err != nil {
panic(err)
}
return hash
}
func (n *Backend) buildBlock(accept bool, gap uint64) (common.Hash, error) {
chain := n.eth.BlockChain()
parent := chain.CurrentBlock()
if err := n.eth.TxPool().Sync(); err != nil {
return common.Hash{}, err
}
n.clock.Set(time.Unix(int64(parent.Time+gap), 0))
block, err := n.eth.Miner().GenerateBlock(nil)
if err != nil {
return common.Hash{}, err
}
if err := chain.InsertBlock(block); err != nil {
return common.Hash{}, err
}
if accept {
if err := n.acceptAncestors(block); err != nil {
return common.Hash{}, err
}
chain.DrainAcceptorQueue()
}
return block.Hash(), nil
}
func (n *Backend) acceptAncestors(block *types.Block) error {
chain := n.eth.BlockChain()
lastAccepted := chain.LastConsensusAcceptedBlock()
// Accept all ancestors of the block
toAccept := []*types.Block{block}
for block.ParentHash() != lastAccepted.Hash() {
block = chain.GetBlockByHash(block.ParentHash())
toAccept = append(toAccept, block)
if block.NumberU64() < lastAccepted.NumberU64() {
return errors.New("last accepted must be an ancestor of the block to accept")
}
}
for i := len(toAccept) - 1; i >= 0; i-- {
if err := chain.Accept(toAccept[i]); err != nil {
return err
}
}
return nil
}
// Rollback removes all pending transactions, reverting to the last committed state.
func (n *Backend) Rollback() {
// Flush all transactions from the transaction pools
maxUint256 := new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 256), common.Big1)
original := n.eth.TxPool().GasTip()
n.eth.TxPool().SetGasTip(maxUint256)
n.eth.TxPool().SetGasTip(original)
}
// Fork creates a side-chain that can be used to simulate reorgs.
//
// This function should be called with the ancestor block where the new side
// chain should be started. Transactions (old and new) can then be applied on
// top and Commit-ed.
//
// Note, the side-chain will only become canonical (and trigger the events) when
// it becomes longer. Until then CallContract will still operate on the current
// canonical chain.
//
// There is a % chance that the side chain becomes canonical at the same length
// to simulate live network behavior.
func (n *Backend) Fork(parentHash common.Hash) error {
chain := n.eth.BlockChain()
if chain.CurrentBlock().Hash() == parentHash {
return nil
}
parent := chain.GetBlockByHash(parentHash)
if parent == nil {
return errors.New("parent block not found")
}
ch := make(chan core.NewTxPoolReorgEvent, 1)
sub := n.eth.TxPool().SubscribeNewReorgEvent(ch)
defer sub.Unsubscribe()
if err := n.eth.BlockChain().SetPreference(parent); err != nil {
return err
}
for {
select {
case reorg := <-ch:
// Wait for tx pool to reorg, then flush the tx pool
if reorg.Head.Hash() == parent.Hash() {
n.Rollback()
return nil
}
case <-time.After(2 * time.Second):
return errors.New("fork not accepted")
}
}
}
// AdjustTime changes the block timestamp and creates a new block.
// It can only be called on empty blocks.
func (n *Backend) AdjustTime(adjustment time.Duration) error {
_, err := n.buildBlock(false, uint64(adjustment))
return err
}
// Client returns a client that accesses the simulated chain.
func (n *Backend) Client() Client {
return n.client
}