Skip to content

Commit

Permalink
Merge pull request ethereum#37 from OffchainLabs/merge-nitro-v2.3.4
Browse files Browse the repository at this point in the history
Merge nitro v2.3.4 (rc3)
  • Loading branch information
rachel-bousfield authored Apr 9, 2024
2 parents d43aafe + 874a123 commit 2bef373
Show file tree
Hide file tree
Showing 15 changed files with 270 additions and 159 deletions.
22 changes: 21 additions & 1 deletion accounts/abi/bind/bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,21 @@ func isKeyWord(arg string) bool {
return true
}

func duplicates(methods map[string]abi.Method) map[string]bool {
var (
identifiers = make(map[string]bool)
dups = make(map[string]bool)
)
for _, method := range methods {
identifiers, dups := identifiers, dups
if identifiers[method.RawName] {
dups[method.RawName] = true
}
identifiers[method.RawName] = true
}
return dups
}

// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
// to be used as is in client code, but rather as an intermediate struct which
// enforces compile time type safety and naming convention opposed to having to
Expand Down Expand Up @@ -121,6 +136,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
callIdentifiers = make(map[string]bool)
transactIdentifiers = make(map[string]bool)
eventIdentifiers = make(map[string]bool)
dups = duplicates(evmABI.Methods)
)

for _, input := range evmABI.Constructor.Inputs {
Expand All @@ -132,12 +148,16 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
for _, original := range evmABI.Methods {
// Normalize the method for capital cases and non-anonymous inputs/outputs
normalized := original
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
// Ensure there is no duplicated identifier
var identifiers = callIdentifiers
if !original.IsConstant() {
identifiers = transactIdentifiers
}
name := original.RawName
if dups[original.RawName] {
name = fmt.Sprintf("%s%x", original.RawName, original.ID)
}
normalizedName := methodNormalizer[lang](alias(aliases, name))
// Name shouldn't start with a digit. It will make the generated code invalid.
if len(normalizedName) > 0 && unicode.IsDigit(rune(normalizedName[0])) {
normalizedName = fmt.Sprintf("M%s", normalizedName)
Expand Down
4 changes: 2 additions & 2 deletions accounts/abi/bind/bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1486,7 +1486,7 @@ var bindTests = []struct {
}
}
}()
contract.Foo(auth, big.NewInt(1), big.NewInt(2))
contract.Foo04bc52f8(auth, big.NewInt(1), big.NewInt(2))
sim.Commit()
select {
case n := <-resCh:
Expand All @@ -1497,7 +1497,7 @@ var bindTests = []struct {
t.Fatalf("Wait bar0 event timeout")
}
contract.Foo0(auth, big.NewInt(1))
contract.Foo2fbebd38(auth, big.NewInt(1))
sim.Commit()
select {
case n := <-resCh:
Expand Down
92 changes: 81 additions & 11 deletions arbitrum/apibackend.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/trie"

"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
Expand All @@ -22,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/filters"
Expand All @@ -32,6 +35,13 @@ import (
"github.com/ethereum/go-ethereum/rpc"
)

var (
liveStatesReferencedCounter = metrics.NewRegisteredCounter("arb/apibackend/states/live/referenced", nil)
liveStatesDereferencedCounter = metrics.NewRegisteredCounter("arb/apibackend/states/live/dereferenced", nil)
recreatedStatesReferencedCounter = metrics.NewRegisteredCounter("arb/apibackend/states/recreated/referenced", nil)
recreatedStatesDereferencedCounter = metrics.NewRegisteredCounter("arb/apibackend/states/recreated/dereferenced", nil)
)

type APIBackend struct {
b *Backend

Expand Down Expand Up @@ -145,7 +155,7 @@ func (a *APIBackend) GetAPIs(filterSystem *filters.FilterSystem) []rpc.API {
}

func (a *APIBackend) BlockChain() *core.BlockChain {
return a.b.arb.BlockChain()
return a.b.BlockChain()
}

func (a *APIBackend) GetArbitrumNode() interface{} {
Expand Down Expand Up @@ -444,21 +454,75 @@ func (a *APIBackend) stateAndHeaderFromHeader(ctx context.Context, header *types
return nil, header, types.ErrUseFallback
}
bc := a.BlockChain()
stateFor := func(header *types.Header) (*state.StateDB, error) {
return bc.StateAt(header.Root)
stateFor := func(db state.Database, snapshots *snapshot.Tree) func(header *types.Header) (*state.StateDB, StateReleaseFunc, error) {
return func(header *types.Header) (*state.StateDB, StateReleaseFunc, error) {
if header.Root != (common.Hash{}) {
// Try referencing the root, if it isn't in dirties cache then Reference will have no effect
db.TrieDB().Reference(header.Root, common.Hash{})
}
statedb, err := state.New(header.Root, db, snapshots)
if err != nil {
return nil, nil, err
}
if header.Root != (common.Hash{}) {
headerRoot := header.Root
return statedb, func() { db.TrieDB().Dereference(headerRoot) }, nil
}
return statedb, NoopStateRelease, nil
}
}
state, lastHeader, err := FindLastAvailableState(ctx, bc, stateFor, header, nil, a.b.config.MaxRecreateStateDepth)
liveState, liveStateRelease, err := stateFor(bc.StateCache(), bc.Snapshots())(header)
if err == nil {
liveStatesReferencedCounter.Inc(1)
liveState.SetArbFinalizer(func(*state.ArbitrumExtraData) {
liveStateRelease()
liveStatesDereferencedCounter.Inc(1)
})
return liveState, header, nil
}
// else err != nil => we don't need to call liveStateRelease

// Create an ephemeral trie.Database for isolating the live one
// note: triedb cleans cache is disabled in trie.HashDefaults
// note: only states committed to diskdb can be found as we're creating new triedb
// note: snapshots are not used here
ephemeral := state.NewDatabaseWithConfig(a.ChainDb(), trie.HashDefaults)
lastState, lastHeader, lastStateRelease, err := FindLastAvailableState(ctx, bc, stateFor(ephemeral, nil), header, nil, a.b.config.MaxRecreateStateDepth)
if err != nil {
return nil, nil, err
}
// make sure that we haven't found the state in diskdb
if lastHeader == header {
return state, header, nil
}
state, err = AdvanceStateUpToBlock(ctx, bc, state, header, lastHeader, nil)
liveStatesReferencedCounter.Inc(1)
lastState.SetArbFinalizer(func(*state.ArbitrumExtraData) {
lastStateRelease()
liveStatesDereferencedCounter.Inc(1)
})
return lastState, header, nil
}
defer lastStateRelease()
targetBlock := bc.GetBlockByNumber(header.Number.Uint64())
if targetBlock == nil {
return nil, nil, errors.New("target block not found")
}
lastBlock := bc.GetBlockByNumber(lastHeader.Number.Uint64())
if lastBlock == nil {
return nil, nil, errors.New("last block not found")
}
reexec := uint64(0)
checkLive := false
preferDisk := false // preferDisk is ignored in this case
statedb, release, err := eth.NewArbEthereum(a.b.arb.BlockChain(), a.ChainDb()).StateAtBlock(ctx, targetBlock, reexec, lastState, lastBlock, checkLive, preferDisk)
if err != nil {
return nil, nil, err
return nil, nil, fmt.Errorf("failed to recreate state: %w", err)
}
return state, header, err
// we are setting finalizer instead of returning a StateReleaseFunc to avoid changing ethapi.Backend interface to minimize diff to upstream
recreatedStatesReferencedCounter.Inc(1)
statedb.SetArbFinalizer(func(*state.ArbitrumExtraData) {
release()
recreatedStatesDereferencedCounter.Inc(1)
})
return statedb, header, err
}

func (a *APIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
Expand All @@ -468,6 +532,12 @@ func (a *APIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.Bloc

func (a *APIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
header, err := a.HeaderByNumberOrHash(ctx, blockNrOrHash)
hash, ishash := blockNrOrHash.Hash()
bc := a.BlockChain()
// check if we are not trying to get recent state that is not yet triedb referenced or committed in Blockchain.writeBlockWithState
if ishash && header.Number.Cmp(bc.CurrentBlock().Number) > 0 && bc.GetCanonicalHash(header.Number.Uint64()) != hash {
return nil, nil, errors.New("requested block ahead of current block and the hash is not currently canonical")
}
return a.stateAndHeaderFromHeader(ctx, header, err)
}

Expand All @@ -476,7 +546,7 @@ func (a *APIBackend) StateAtBlock(ctx context.Context, block *types.Block, reexe
return nil, nil, types.ErrUseFallback
}
// DEV: This assumes that `StateAtBlock` only accesses the blockchain and chainDb fields
return eth.NewArbEthereum(a.b.arb.BlockChain(), a.ChainDb()).StateAtBlock(ctx, block, reexec, base, checkLive, preferDisk)
return eth.NewArbEthereum(a.b.arb.BlockChain(), a.ChainDb()).StateAtBlock(ctx, block, reexec, base, nil, checkLive, preferDisk)
}

func (a *APIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
Expand Down Expand Up @@ -607,7 +677,7 @@ func (a *APIBackend) ChainConfig() *params.ChainConfig {
}

func (a *APIBackend) Engine() consensus.Engine {
return a.BlockChain().Engine()
return a.b.Engine()
}

func (b *APIBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
Expand Down
32 changes: 18 additions & 14 deletions arbitrum/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package arbitrum
import (
"context"

"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/arbitrum_types"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/types"
Expand All @@ -12,6 +14,7 @@ import (
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/shutdowncheck"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"
)

type Backend struct {
Expand All @@ -32,6 +35,8 @@ type Backend struct {
chanTxs chan *types.Transaction
chanClose chan struct{} //close coroutine
chanNewBlock chan struct{} //create new L2 block unless empty

filterSystem *filters.FilterSystem
}

func NewBackend(stack *node.Node, config *Config, chainDb ethdb.Database, publisher ArbInterface, filterConfig filters.Config) (*Backend, *filters.FilterSystem, error) {
Expand Down Expand Up @@ -64,15 +69,22 @@ func NewBackend(stack *node.Node, config *Config, chainDb ethdb.Database, publis
if err != nil {
return nil, nil, err
}
backend.filterSystem = filterSystem
return backend, filterSystem, nil
}

func (b *Backend) APIBackend() *APIBackend {
return b.apiBackend
}

func (b *Backend) ChainDb() ethdb.Database {
return b.chainDb
func (b *Backend) AccountManager() *accounts.Manager { return b.stack.AccountManager() }
func (b *Backend) APIBackend() *APIBackend { return b.apiBackend }
func (b *Backend) APIs() []rpc.API { return b.apiBackend.GetAPIs(b.filterSystem) }
func (b *Backend) ArbInterface() ArbInterface { return b.arb }
func (b *Backend) BlockChain() *core.BlockChain { return b.arb.BlockChain() }
func (b *Backend) BloomIndexer() *core.ChainIndexer { return b.bloomIndexer }
func (b *Backend) ChainDb() ethdb.Database { return b.chainDb }
func (b *Backend) Engine() consensus.Engine { return b.arb.BlockChain().Engine() }
func (b *Backend) Stack() *node.Node { return b.stack }

func (b *Backend) ResetWithGenesisBlock(gb *types.Block) {
b.arb.BlockChain().ResetWithGenesisBlock(gb)
}

func (b *Backend) EnqueueL2Message(ctx context.Context, tx *types.Transaction, options *arbitrum_types.ConditionalOptions) error {
Expand All @@ -83,14 +95,6 @@ func (b *Backend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscri
return b.scope.Track(b.txFeed.Subscribe(ch))
}

func (b *Backend) Stack() *node.Node {
return b.stack
}

func (b *Backend) ArbInterface() ArbInterface {
return b.arb
}

// TODO: this is used when registering backend as lifecycle in stack
func (b *Backend) Start() error {
b.startBloomHandlers(b.config.BloomBitsBlocks)
Expand Down
7 changes: 6 additions & 1 deletion arbitrum/recordingdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,12 @@ func (r *RecordingDatabase) PreimagesFromRecording(chainContextIf core.ChainCont
}

func (r *RecordingDatabase) GetOrRecreateState(ctx context.Context, header *types.Header, logFunc StateBuildingLogFunction) (*state.StateDB, error) {
state, currentHeader, err := FindLastAvailableState(ctx, r.bc, r.StateFor, header, logFunc, -1)
stateFor := func(header *types.Header) (*state.StateDB, StateReleaseFunc, error) {
state, err := r.StateFor(header)
// we don't use the release functor pattern here yet
return state, NoopStateRelease, err
}
state, currentHeader, _, err := FindLastAvailableState(ctx, r.bc, stateFor, header, logFunc, -1)
if err != nil {
return nil, err
}
Expand Down
24 changes: 15 additions & 9 deletions arbitrum/recreatestate.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,58 +9,64 @@ import (
"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/eth/tracers"
"github.com/pkg/errors"
)

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

type StateReleaseFunc tracers.StateReleaseFunc

var NoopStateRelease StateReleaseFunc = func() {}

type StateBuildingLogFunction func(targetHeader, header *types.Header, hasState bool)
type StateForHeaderFunction func(header *types.Header) (*state.StateDB, error)
type StateForHeaderFunction func(header *types.Header) (*state.StateDB, StateReleaseFunc, 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) {
func FindLastAvailableState(ctx context.Context, bc *core.BlockChain, stateFor StateForHeaderFunction, targetHeader *types.Header, logFunc StateBuildingLogFunction, maxDepthInL2Gas int64) (*state.StateDB, *types.Header, StateReleaseFunc, error) {
genesis := bc.Config().ArbitrumChainParams.GenesisBlockNum
currentHeader := targetHeader
var state *state.StateDB
var err error
var l2GasUsed uint64
release := NoopStateRelease
for ctx.Err() == nil {
lastHeader := currentHeader
state, err = stateFor(currentHeader)
state, release, 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())
return nil, lastHeader, nil, 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
return nil, lastHeader, nil, ErrDepthLimitExceeded
}
} else if maxDepthInL2Gas != InfiniteMaxRecreateStateDepth {
return nil, lastHeader, err
return nil, lastHeader, nil, 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))
return nil, lastHeader, nil, 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 nil, lastHeader, nil, fmt.Errorf("chain doesn't contain parent of block %d hash %v", lastHeader.Number, lastHeader.Hash())
}
}
return state, currentHeader, ctx.Err()
return state, currentHeader, release, 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) {
Expand Down
Loading

0 comments on commit 2bef373

Please sign in to comment.