Skip to content

Commit

Permalink
Merge branch 'master' into merge-v1.12.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Tristan-Wilson authored Aug 22, 2023
2 parents 66163ca + 88b1bd3 commit c905292
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 50 deletions.
26 changes: 21 additions & 5 deletions arbitrum/apibackend.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ func (a *APIBackend) RPCEVMTimeout() time.Duration {
}

func (a *APIBackend) UnprotectedAllowed() bool {
return true // TODO: is that true?
return a.b.config.TxAllowUnprotected
}

// Blockchain API
Expand Down Expand Up @@ -415,7 +415,7 @@ func (a *APIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.
return nil, errors.New("invalid arguments; neither block nor hash specified")
}

func (a *APIBackend) stateAndHeaderFromHeader(header *types.Header, err error) (*state.StateDB, *types.Header, error) {
func (a *APIBackend) stateAndHeaderFromHeader(ctx context.Context, header *types.Header, err error) (*state.StateDB, *types.Header, error) {
if err != nil {
return nil, header, err
}
Expand All @@ -425,16 +425,32 @@ func (a *APIBackend) stateAndHeaderFromHeader(header *types.Header, err error) (
if !a.blockChain().Config().IsArbitrumNitro(header.Number) {
return nil, header, types.ErrUseFallback
}
state, err := a.blockChain().StateAt(header.Root)
bc := a.blockChain()
stateFor := func(header *types.Header) (*state.StateDB, error) {
return bc.StateAt(header.Root)
}
state, lastHeader, err := FindLastAvailableState(ctx, bc, stateFor, header, nil, a.b.config.MaxRecreateStateDepth)
if err != nil {
return nil, nil, err
}
if lastHeader == header {
return state, header, nil
}
state, err = AdvanceStateUpToBlock(ctx, bc, state, header, lastHeader, nil)
if err != nil {
return nil, nil, err
}
return state, header, err
}

func (a *APIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
return a.stateAndHeaderFromHeader(a.HeaderByNumber(ctx, number))
header, err := a.HeaderByNumber(ctx, number)
return a.stateAndHeaderFromHeader(ctx, header, err)
}

func (a *APIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
return a.stateAndHeaderFromHeader(a.HeaderByNumberOrHash(ctx, blockNrOrHash))
header, err := a.HeaderByNumberOrHash(ctx, blockNrOrHash)
return a.stateAndHeaderFromHeader(ctx, header, err)
}

func (a *APIBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, release tracers.StateReleaseFunc, err error) {
Expand Down
19 changes: 16 additions & 3 deletions arbitrum/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ type Config struct {
// send-transction variants. The unit is ether.
RPCTxFeeCap float64 `koanf:"tx-fee-cap"`

TxAllowUnprotected bool `koanf:"tx-allow-unprotected"`

// RPCEVMTimeout is the global timeout for eth-call.
RPCEVMTimeout time.Duration `koanf:"evm-timeout"`

Expand All @@ -34,6 +36,7 @@ type Config struct {

ClassicRedirect string `koanf:"classic-redirect"`
ClassicRedirectTimeout time.Duration `koanf:"classic-redirect-timeout"`
MaxRecreateStateDepth int64 `koanf:"max-recreate-state-depth"`
}

type ArbDebugConfig struct {
Expand All @@ -44,6 +47,7 @@ type ArbDebugConfig struct {
func ConfigAddOptions(prefix string, f *flag.FlagSet) {
f.Uint64(prefix+".gas-cap", DefaultConfig.RPCGasCap, "cap on computation gas that can be used in eth_call/estimateGas (0=infinite)")
f.Float64(prefix+".tx-fee-cap", DefaultConfig.RPCTxFeeCap, "cap on transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap)")
f.Bool(prefix+".tx-allow-unprotected", DefaultConfig.TxAllowUnprotected, "allow transactions that aren't EIP-155 replay protected to be submitted over the RPC")
f.Duration(prefix+".evm-timeout", DefaultConfig.RPCEVMTimeout, "timeout used for eth_call (0=infinite)")
f.Uint64(prefix+".bloom-bits-blocks", DefaultConfig.BloomBitsBlocks, "number of blocks a single bloom bit section vector holds")
f.Uint64(prefix+".bloom-confirms", DefaultConfig.BloomConfirms, "number of confirmation blocks before a bloom section is considered final")
Expand All @@ -52,22 +56,31 @@ func ConfigAddOptions(prefix string, f *flag.FlagSet) {
f.Duration(prefix+".classic-redirect-timeout", DefaultConfig.ClassicRedirectTimeout, "timeout for forwarded classic requests, where 0 = no timeout")
f.Int(prefix+".filter-log-cache-size", DefaultConfig.FilterLogCacheSize, "log filter system maximum number of cached blocks")
f.Duration(prefix+".filter-timeout", DefaultConfig.FilterTimeout, "log filter system maximum time filters stay active")

f.Int64(prefix+".max-recreate-state-depth", DefaultConfig.MaxRecreateStateDepth, "maximum depth for recreating state, measured in l2 gas (0=don't recreate state, -1=infinite, -2=use default value for archive or non-archive node (whichever is configured))")
arbDebug := DefaultConfig.ArbDebug
f.Uint64(prefix+".arbdebug.block-range-bound", arbDebug.BlockRangeBound, "bounds the number of blocks arbdebug calls may return")
f.Uint64(prefix+".arbdebug.timeout-queue-bound", arbDebug.TimeoutQueueBound, "bounds the length of timeout queues arbdebug calls may return")
}

const (
DefaultArchiveNodeMaxRecreateStateDepth = 30 * 1000 * 1000
DefaultNonArchiveNodeMaxRecreateStateDepth = 0 // don't recreate state
UninitializedMaxRecreateStateDepth = -2
InfiniteMaxRecreateStateDepth = -1
)

var DefaultConfig = Config{
RPCGasCap: ethconfig.Defaults.RPCGasCap, // 50,000,000
RPCTxFeeCap: ethconfig.Defaults.RPCTxFeeCap, // 1 ether
RPCGasCap: ethconfig.Defaults.RPCGasCap, // 50,000,000
RPCTxFeeCap: ethconfig.Defaults.RPCTxFeeCap, // 1 ether
TxAllowUnprotected: true,
RPCEVMTimeout: ethconfig.Defaults.RPCEVMTimeout, // 5 seconds
BloomBitsBlocks: params.BloomBitsBlocks * 4, // we generally have smaller blocks
BloomConfirms: params.BloomConfirms,
FilterLogCacheSize: 32,
FilterTimeout: 5 * time.Minute,
FeeHistoryMaxBlockCount: 1024,
ClassicRedirect: "",
MaxRecreateStateDepth: UninitializedMaxRecreateStateDepth, // default value should be set for depending on node type (archive / non-archive)
ArbDebug: ArbDebugConfig{
BlockRangeBound: 256,
TimeoutQueueBound: 512,
Expand Down
54 changes: 12 additions & 42 deletions arbitrum/recordingdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
Expand Down Expand Up @@ -254,8 +253,6 @@ func (r *RecordingDatabase) addStateVerify(statedb *state.StateDB, expected comm
return nil
}

type StateBuildingLogFunction func(targetHeader, header *types.Header, hasState bool)

func (r *RecordingDatabase) PrepareRecording(ctx context.Context, lastBlockHeader *types.Header, logFunc StateBuildingLogFunction) (*state.StateDB, core.ChainContext, *RecordingKV, error) {
_, err := r.GetOrRecreateState(ctx, lastBlockHeader, logFunc)
if err != nil {
Expand Down Expand Up @@ -305,56 +302,29 @@ func (r *RecordingDatabase) PreimagesFromRecording(chainContextIf core.ChainCont
}

func (r *RecordingDatabase) GetOrRecreateState(ctx context.Context, header *types.Header, logFunc StateBuildingLogFunction) (*state.StateDB, error) {
stateDb, err := r.StateFor(header)
if err == nil {
return stateDb, nil
state, currentHeader, err := FindLastAvailableState(ctx, r.bc, r.StateFor, header, logFunc, -1)
if err != nil {
return nil, err
}
returnedBlockNumber := header.Number.Uint64()
genesis := r.bc.Config().ArbitrumChainParams.GenesisBlockNum
currentHeader := header
var lastRoot common.Hash
for ctx.Err() == nil {
if logFunc != nil {
logFunc(header, currentHeader, false)
}
if currentHeader.Number.Uint64() <= genesis {
return nil, fmt.Errorf("moved beyond genesis looking for state looking for %d, genesis %d, err %w", returnedBlockNumber, genesis, err)
}
lastHeader := currentHeader
currentHeader = r.bc.GetHeader(currentHeader.ParentHash, currentHeader.Number.Uint64()-1)
if currentHeader == nil {
return nil, fmt.Errorf("chain doesn't contain parent of block %d hash %v (expected parent hash %v)", lastHeader.Number, lastHeader.Hash(), lastHeader.ParentHash)
}
stateDb, err = r.StateFor(currentHeader)
if err == nil {
lastRoot = currentHeader.Root
break
}
if currentHeader == header {
return state, nil
}
lastRoot := currentHeader.Root
defer func() {
if (lastRoot != common.Hash{}) {
r.dereferenceRoot(lastRoot)
}
}()
blockToRecreate := currentHeader.Number.Uint64() + 1
prevHash := currentHeader.Hash()
returnedBlockNumber := header.Number.Uint64()
for ctx.Err() == nil {
block := r.bc.GetBlockByNumber(blockToRecreate)
if block == nil {
return nil, fmt.Errorf("block not found while recreating: %d", blockToRecreate)
}
if block.ParentHash() != prevHash {
return nil, fmt.Errorf("reorg detected: number %d expectedPrev: %v foundPrev: %v", blockToRecreate, prevHash, block.ParentHash())
}
prevHash = block.Hash()
if logFunc != nil {
logFunc(header, block.Header(), true)
}
_, _, _, err := r.bc.Processor().Process(block, stateDb, vm.Config{})
state, block, err := AdvanceStateByBlock(ctx, r.bc, state, header, blockToRecreate, prevHash, logFunc)
if err != nil {
return nil, fmt.Errorf("failed recreating state for block %d : %w", blockToRecreate, err)
return nil, err
}
err = r.addStateVerify(stateDb, block.Root())
prevHash = block.Hash()
err = r.addStateVerify(state, block.Root())
if err != nil {
return nil, fmt.Errorf("failed committing state for block %d : %w", blockToRecreate, err)
}
Expand All @@ -366,7 +336,7 @@ func (r *RecordingDatabase) GetOrRecreateState(ctx context.Context, header *type
}
// don't dereference this one
lastRoot = common.Hash{}
return stateDb, nil
return state, nil
}
blockToRecreate++
}
Expand Down
103 changes: 103 additions & 0 deletions arbitrum/recreatestate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package arbitrum

import (
"context"
"fmt"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/pkg/errors"
)

var (
ErrDepthLimitExceeded = errors.New("state recreation l2 gas depth limit exceeded")
)

type StateBuildingLogFunction func(targetHeader, header *types.Header, hasState bool)
type StateForHeaderFunction func(header *types.Header) (*state.StateDB, error)

// finds last available state and header checking it first for targetHeader then looking backwards
// if maxDepthInL2Gas is positive, it constitutes a limit for cumulative l2 gas used of the traversed blocks
// else if maxDepthInL2Gas is -1, the traversal depth is not limited
// otherwise only targetHeader state is checked and no search is performed
func FindLastAvailableState(ctx context.Context, bc *core.BlockChain, stateFor StateForHeaderFunction, targetHeader *types.Header, logFunc StateBuildingLogFunction, maxDepthInL2Gas int64) (*state.StateDB, *types.Header, error) {
genesis := bc.Config().ArbitrumChainParams.GenesisBlockNum
currentHeader := targetHeader
var state *state.StateDB
var err error
var l2GasUsed uint64
for ctx.Err() == nil {
lastHeader := currentHeader
state, err = stateFor(currentHeader)
if err == nil {
break
}
if maxDepthInL2Gas > 0 {
receipts := bc.GetReceiptsByHash(currentHeader.Hash())
if receipts == nil {
return nil, lastHeader, fmt.Errorf("failed to get receipts for hash %v", currentHeader.Hash())
}
for _, receipt := range receipts {
l2GasUsed += receipt.GasUsed - receipt.GasUsedForL1
}
if l2GasUsed > uint64(maxDepthInL2Gas) {
return nil, lastHeader, ErrDepthLimitExceeded
}
} else if maxDepthInL2Gas != InfiniteMaxRecreateStateDepth {
return nil, lastHeader, err
}
if logFunc != nil {
logFunc(targetHeader, currentHeader, false)
}
if currentHeader.Number.Uint64() <= genesis {
return nil, lastHeader, errors.Wrap(err, fmt.Sprintf("moved beyond genesis looking for state %d, genesis %d", targetHeader.Number.Uint64(), genesis))
}
currentHeader = bc.GetHeader(currentHeader.ParentHash, currentHeader.Number.Uint64()-1)
if currentHeader == nil {
return nil, lastHeader, fmt.Errorf("chain doesn't contain parent of block %d hash %v", lastHeader.Number, lastHeader.Hash())
}
}
return state, currentHeader, ctx.Err()
}

func AdvanceStateByBlock(ctx context.Context, bc *core.BlockChain, state *state.StateDB, targetHeader *types.Header, blockToRecreate uint64, prevBlockHash common.Hash, logFunc StateBuildingLogFunction) (*state.StateDB, *types.Block, error) {
block := bc.GetBlockByNumber(blockToRecreate)
if block == nil {
return nil, nil, fmt.Errorf("block not found while recreating: %d", blockToRecreate)
}
if block.ParentHash() != prevBlockHash {
return nil, nil, fmt.Errorf("reorg detected: number %d expectedPrev: %v foundPrev: %v", blockToRecreate, prevBlockHash, block.ParentHash())
}
if logFunc != nil {
logFunc(targetHeader, block.Header(), true)
}
_, _, _, err := bc.Processor().Process(block, state, vm.Config{})
if err != nil {
return nil, nil, fmt.Errorf("failed recreating state for block %d : %w", blockToRecreate, err)
}
return state, block, nil
}

func AdvanceStateUpToBlock(ctx context.Context, bc *core.BlockChain, state *state.StateDB, targetHeader *types.Header, lastAvailableHeader *types.Header, logFunc StateBuildingLogFunction) (*state.StateDB, error) {
returnedBlockNumber := targetHeader.Number.Uint64()
blockToRecreate := lastAvailableHeader.Number.Uint64() + 1
prevHash := lastAvailableHeader.Hash()
for ctx.Err() == nil {
state, block, err := AdvanceStateByBlock(ctx, bc, state, targetHeader, blockToRecreate, prevHash, logFunc)
if err != nil {
return nil, err
}
prevHash = block.Hash()
if blockToRecreate >= returnedBlockNumber {
if block.Hash() != targetHeader.Hash() {
return nil, fmt.Errorf("blockHash doesn't match when recreating number: %d expected: %v got: %v", blockToRecreate, targetHeader.Hash(), block.Hash())
}
return state, nil
}
blockToRecreate++
}
return nil, ctx.Err()
}

0 comments on commit c905292

Please sign in to comment.