Skip to content

Commit

Permalink
Merge branch 'master' into parse-abi-errors
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuacolvin0 authored Feb 5, 2024
2 parents 7469252 + 17468a8 commit 17e4fd0
Show file tree
Hide file tree
Showing 11 changed files with 283 additions and 37 deletions.
41 changes: 36 additions & 5 deletions arbnode/delayed.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import (
"bytes"
"context"
"errors"
"fmt"
"math/big"
"sort"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
Expand All @@ -28,13 +30,15 @@ var messageDeliveredID common.Hash
var inboxMessageDeliveredID common.Hash
var inboxMessageFromOriginID common.Hash
var l2MessageFromOriginCallABI abi.Method
var delayedInboxAccsCallABI abi.Method

func init() {
parsedIBridgeABI, err := bridgegen.IBridgeMetaData.GetAbi()
if err != nil {
panic(err)
}
messageDeliveredID = parsedIBridgeABI.Events["MessageDelivered"].ID
delayedInboxAccsCallABI = parsedIBridgeABI.Methods["delayedInboxAccs"]

parsedIMessageProviderABI, err := bridgegen.IDelayedMessageProviderMetaData.GetAbi()
if err != nil {
Expand Down Expand Up @@ -95,12 +99,39 @@ func (b *DelayedBridge) GetMessageCount(ctx context.Context, blockNumber *big.In
return bigRes.Uint64(), nil
}

func (b *DelayedBridge) GetAccumulator(ctx context.Context, sequenceNumber uint64, blockNumber *big.Int) (common.Hash, error) {
opts := &bind.CallOpts{
Context: ctx,
BlockNumber: blockNumber,
// Uses blockHash if nonzero, otherwise uses blockNumber
func (b *DelayedBridge) GetAccumulator(ctx context.Context, sequenceNumber uint64, blockNumber *big.Int, blockHash common.Hash) (common.Hash, error) {
calldata := append([]byte{}, delayedInboxAccsCallABI.ID...)
inputs, err := delayedInboxAccsCallABI.Inputs.Pack(arbmath.UintToBig(sequenceNumber))
if err != nil {
return common.Hash{}, err
}
calldata = append(calldata, inputs...)
msg := ethereum.CallMsg{
To: &b.address,
Data: calldata,
}
var result hexutil.Bytes
if blockHash != (common.Hash{}) {
result, err = b.client.CallContractAtHash(ctx, msg, blockHash)
} else {
result, err = b.client.CallContract(ctx, msg, blockNumber)
}
if err != nil {
return common.Hash{}, err
}
values, err := delayedInboxAccsCallABI.Outputs.Unpack(result)
if err != nil {
return common.Hash{}, err
}
if len(values) != 1 {
return common.Hash{}, fmt.Errorf("expected 1 return value from %v, got %v", delayedInboxAccsCallABI.Name, len(values))
}
hash, ok := values[0].([32]byte)
if !ok {
return common.Hash{}, fmt.Errorf("expected [32]uint8 return value from %v, got %T", delayedInboxAccsCallABI.Name, values[0])
}
return b.con.DelayedInboxAccs(opts, new(big.Int).SetUint64(sequenceNumber))
return hash, nil
}

type DelayedInboxMessage struct {
Expand Down
10 changes: 7 additions & 3 deletions arbnode/delayed_sequencer.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,20 @@ func (d *DelayedSequencer) sequenceWithoutLockout(ctx context.Context, lastBlock
}

var finalized uint64
var finalizedHash common.Hash
if config.UseMergeFinality && headerreader.HeaderIndicatesFinalitySupport(lastBlockHeader) {
var header *types.Header
var err error
if config.RequireFullFinality {
finalized, err = d.l1Reader.LatestFinalizedBlockNr(ctx)
header, err = d.l1Reader.LatestFinalizedBlockHeader(ctx)
} else {
finalized, err = d.l1Reader.LatestSafeBlockNr(ctx)
header, err = d.l1Reader.LatestSafeBlockHeader(ctx)
}
if err != nil {
return err
}
finalized = header.Number.Uint64()
finalizedHash = header.Hash()
} else {
currentNum := lastBlockHeader.Number.Int64()
if currentNum < config.FinalizeDistance {
Expand Down Expand Up @@ -167,7 +171,7 @@ func (d *DelayedSequencer) sequenceWithoutLockout(ctx context.Context, lastBlock

// Sequence the delayed messages, if any
if len(messages) > 0 {
delayedBridgeAcc, err := d.bridge.GetAccumulator(ctx, pos-1, new(big.Int).SetUint64(finalized))
delayedBridgeAcc, err := d.bridge.GetAccumulator(ctx, pos-1, new(big.Int).SetUint64(finalized), finalizedHash)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion arbnode/inbox_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ func (r *InboxReader) run(ctx context.Context, hadError bool) error {
}
if checkingDelayedCount > 0 {
checkingDelayedSeqNum := checkingDelayedCount - 1
l1DelayedAcc, err := r.delayedBridge.GetAccumulator(ctx, checkingDelayedSeqNum, currentHeight)
l1DelayedAcc, err := r.delayedBridge.GetAccumulator(ctx, checkingDelayedSeqNum, currentHeight, common.Hash{})
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions arbutil/wait_for_l1.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type L1Interface interface {
ethereum.TransactionReader
TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error)
BlockNumber(ctx context.Context) (uint64, error)
CallContractAtHash(ctx context.Context, msg ethereum.CallMsg, blockHash common.Hash) ([]byte, error)
PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error)
ChainID(ctx context.Context) (*big.Int, error)
Client() rpc.ClientInterface
Expand Down
67 changes: 39 additions & 28 deletions cmd/conf/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,44 @@ package conf
import (
"time"

"github.com/ethereum/go-ethereum/log"
"github.com/spf13/pflag"
)

type InitConfig struct {
Force bool `koanf:"force"`
Url string `koanf:"url"`
DownloadPath string `koanf:"download-path"`
DownloadPoll time.Duration `koanf:"download-poll"`
DevInit bool `koanf:"dev-init"`
DevInitAddress string `koanf:"dev-init-address"`
DevInitBlockNum uint64 `koanf:"dev-init-blocknum"`
Empty bool `koanf:"empty"`
AccountsPerSync uint `koanf:"accounts-per-sync"`
ImportFile string `koanf:"import-file"`
ThenQuit bool `koanf:"then-quit"`
Prune string `koanf:"prune"`
PruneBloomSize uint64 `koanf:"prune-bloom-size"`
ResetToMessage int64 `koanf:"reset-to-message"`
Force bool `koanf:"force"`
Url string `koanf:"url"`
DownloadPath string `koanf:"download-path"`
DownloadPoll time.Duration `koanf:"download-poll"`
DevInit bool `koanf:"dev-init"`
DevInitAddress string `koanf:"dev-init-address"`
DevInitBlockNum uint64 `koanf:"dev-init-blocknum"`
Empty bool `koanf:"empty"`
AccountsPerSync uint `koanf:"accounts-per-sync"`
ImportFile string `koanf:"import-file"`
ThenQuit bool `koanf:"then-quit"`
Prune string `koanf:"prune"`
PruneBloomSize uint64 `koanf:"prune-bloom-size"`
ResetToMessage int64 `koanf:"reset-to-message"`
RecreateMissingStateFrom uint64 `koanf:"recreate-missing-state-from"`
}

var InitConfigDefault = InitConfig{
Force: false,
Url: "",
DownloadPath: "/tmp/",
DownloadPoll: time.Minute,
DevInit: false,
DevInitAddress: "",
DevInitBlockNum: 0,
Empty: false,
ImportFile: "",
AccountsPerSync: 100000,
ThenQuit: false,
Prune: "",
PruneBloomSize: 2048,
ResetToMessage: -1,
Force: false,
Url: "",
DownloadPath: "/tmp/",
DownloadPoll: time.Minute,
DevInit: false,
DevInitAddress: "",
DevInitBlockNum: 0,
Empty: false,
ImportFile: "",
AccountsPerSync: 100000,
ThenQuit: false,
Prune: "",
PruneBloomSize: 2048,
ResetToMessage: -1,
RecreateMissingStateFrom: 0, // 0 = disabled
}

func InitConfigAddOptions(prefix string, f *pflag.FlagSet) {
Expand All @@ -55,4 +58,12 @@ func InitConfigAddOptions(prefix string, f *pflag.FlagSet) {
f.String(prefix+".prune", InitConfigDefault.Prune, "pruning for a given use: \"full\" for full nodes serving RPC requests, or \"validator\" for validators")
f.Uint64(prefix+".prune-bloom-size", InitConfigDefault.PruneBloomSize, "the amount of memory in megabytes to use for the pruning bloom filter (higher values prune better)")
f.Int64(prefix+".reset-to-message", InitConfigDefault.ResetToMessage, "forces a reset to an old message height. Also set max-reorg-resequence-depth=0 to force re-reading messages")
f.Uint64(prefix+".recreate-missing-state-from", InitConfigDefault.RecreateMissingStateFrom, "block number to start recreating missing states from (0 = disabled)")
}

func (c *InitConfig) Validate() error {
if c.Force && c.RecreateMissingStateFrom > 0 {
log.Warn("force init enabled, recreate-missing-state-from will have no effect")
}
return nil
}
8 changes: 8 additions & 0 deletions cmd/nitro/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/offchainlabs/nitro/cmd/conf"
"github.com/offchainlabs/nitro/cmd/ipfshelper"
"github.com/offchainlabs/nitro/cmd/pruning"
"github.com/offchainlabs/nitro/cmd/staterecovery"
"github.com/offchainlabs/nitro/cmd/util"
"github.com/offchainlabs/nitro/execution/gethexec"
"github.com/offchainlabs/nitro/statetransfer"
Expand Down Expand Up @@ -183,6 +184,13 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo
if err != nil {
return chainDb, l2BlockChain, err
}
if config.Init.RecreateMissingStateFrom > 0 {
err = staterecovery.RecreateMissingStates(chainDb, l2BlockChain, cacheConfig, config.Init.RecreateMissingStateFrom)
if err != nil {
return chainDb, l2BlockChain, fmt.Errorf("failed to recreate missing states: %w", err)
}
}

return chainDb, l2BlockChain, nil
}
readOnlyDb.Close()
Expand Down
6 changes: 6 additions & 0 deletions cmd/nitro/nitro.go
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,12 @@ func (c *NodeConfig) CanReload(new *NodeConfig) error {
}

func (c *NodeConfig) Validate() error {
if c.Init.RecreateMissingStateFrom > 0 && !c.Execution.Caching.Archive {
return errors.New("recreate-missing-state-from enabled for a non-archive node")
}
if err := c.Init.Validate(); err != nil {
return err
}
if err := c.ParentChain.Validate(); err != nil {
return err
}
Expand Down
88 changes: 88 additions & 0 deletions cmd/staterecovery/staterecovery.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package staterecovery

import (
"fmt"
"time"

"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
)

func RecreateMissingStates(chainDb ethdb.Database, bc *core.BlockChain, cacheConfig *core.CacheConfig, startBlock uint64) error {
start := time.Now()
currentHeader := bc.CurrentBlock()
if currentHeader == nil {
return fmt.Errorf("current header is nil")
}
target := currentHeader.Number.Uint64()
current := startBlock
genesis := bc.Config().ArbitrumChainParams.GenesisBlockNum
if current < genesis+1 {
current = genesis + 1
log.Warn("recreate-missing-states-from before genesis+1, starting from genesis+1", "configured", startBlock, "override", current)
}
previousBlock := bc.GetBlockByNumber(current - 1)
if previousBlock == nil {
return fmt.Errorf("start block parent is missing, parent block number: %d", current-1)
}
hashConfig := *hashdb.Defaults
hashConfig.CleanCacheSize = cacheConfig.TrieCleanLimit
trieConfig := &trie.Config{
Preimages: false,
HashDB: &hashConfig,
}
database := state.NewDatabaseWithConfig(chainDb, trieConfig)
defer database.TrieDB().Close()
previousState, err := state.New(previousBlock.Root(), database, nil)
if err != nil {
return fmt.Errorf("state of start block parent is missing: %w", err)
}
// we don't need to reference states with `trie.Database.Reference` here, because:
// * either the state nodes will be read from disk and then cached in cleans cache
// * or they will be recreated, saved to disk and then also cached in cleans cache
logged := time.Unix(0, 0)
recreated := 0
for {
currentBlock := bc.GetBlockByNumber(current)
if currentBlock == nil {
break
}
if time.Since(logged) > 1*time.Minute {
log.Info("Recreating missing states", "block", current, "target", target, "remaining", int64(target)-int64(current), "elapsed", time.Since(start), "recreated", recreated)
logged = time.Now()
}
currentState, err := state.New(currentBlock.Root(), database, nil)
if err != nil {
_, _, _, err := bc.Processor().Process(currentBlock, previousState, vm.Config{})
if err != nil {
return fmt.Errorf("processing block %d failed: %w", current, err)
}
root, err := previousState.Commit(current, bc.Config().IsEIP158(currentBlock.Number()))
if err != nil {
return fmt.Errorf("StateDB commit failed, number %d root %v: %w", current, currentBlock.Root(), err)
}
if root.Cmp(currentBlock.Root()) != 0 {
return fmt.Errorf("reached different state root after processing block %d, have %v, want %v", current, root, currentBlock.Root())
}
// commit to disk
err = database.TrieDB().Commit(root, false)
if err != nil {
return fmt.Errorf("TrieDB commit failed, number %d root %v: %w", current, root, err)
}
currentState, err = state.New(currentBlock.Root(), database, nil)
if err != nil {
return fmt.Errorf("state reset after block %d failed: %w", current, err)
}
recreated++
}
current++
previousState = currentState
}
log.Info("Finished recreating missing states", "elapsed", time.Since(start), "recreated", recreated)
return nil
}
1 change: 1 addition & 0 deletions execution/gethexec/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ var DefaultCachingConfig = CachingConfig{
MaxAmountOfGasToSkipStateSaving: 0,
}

// TODO remove stack from parameters as it is no longer needed here
func DefaultCacheConfigFor(stack *node.Node, cachingConfig *CachingConfig) *core.CacheConfig {
baseConf := ethconfig.Defaults
if cachingConfig.Archive {
Expand Down
1 change: 1 addition & 0 deletions precompiles/precompile.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@ func Precompiles() map[addr]ArbosPrecompile {
ArbOwnerPublic.methodsByName["GetInfraFeeAccount"].arbosVersion = 5
ArbOwnerPublic.methodsByName["RectifyChainOwner"].arbosVersion = 11
ArbOwnerPublic.methodsByName["GetBrotliCompressionLevel"].arbosVersion = 20
ArbOwnerPublic.methodsByName["GetScheduledUpgrade"].arbosVersion = 20

ArbRetryableImpl := &ArbRetryableTx{Address: types.ArbRetryableTxAddress}
ArbRetryable := insert(MakePrecompile(templates.ArbRetryableTxMetaData, ArbRetryableImpl))
Expand Down
Loading

0 comments on commit 17e4fd0

Please sign in to comment.