diff --git a/arbnode/execution/blockchain.go b/arbnode/execution/blockchain.go index 2ed0221b04..a4de72588a 100644 --- a/arbnode/execution/blockchain.go +++ b/arbnode/execution/blockchain.go @@ -20,6 +20,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbos" "github.com/offchainlabs/nitro/arbos/arbosState" + "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/gethhook" "github.com/offchainlabs/nitro/statetransfer" ) @@ -82,7 +83,7 @@ func DefaultCacheConfigFor(stack *node.Node, cachingConfig *CachingConfig) *core } } -func WriteOrTestGenblock(chainDb ethdb.Database, initData statetransfer.InitDataReader, chainConfig *params.ChainConfig, serializedChainConfig []byte, accountsPerSync uint) error { +func WriteOrTestGenblock(chainDb ethdb.Database, initData statetransfer.InitDataReader, chainConfig *params.ChainConfig, initMessage *arbostypes.ParsedInitMessage, accountsPerSync uint) error { EmptyHash := common.Hash{} prevHash := EmptyHash prevDifficulty := big.NewInt(0) @@ -103,7 +104,7 @@ func WriteOrTestGenblock(chainDb ethdb.Database, initData statetransfer.InitData } timestamp = prevHeader.Time } - stateRoot, err := arbosState.InitializeArbosInDatabase(chainDb, initData, chainConfig, serializedChainConfig, timestamp, accountsPerSync) + stateRoot, err := arbosState.InitializeArbosInDatabase(chainDb, initData, chainConfig, initMessage, timestamp, accountsPerSync) if err != nil { return err } @@ -170,8 +171,8 @@ func GetBlockChain(chainDb ethdb.Database, cacheConfig *core.CacheConfig, chainC return core.NewBlockChain(chainDb, cacheConfig, chainConfig, nil, nil, engine, vmConfig, shouldPreserveFalse, &txLookupLimit) } -func WriteOrTestBlockChain(chainDb ethdb.Database, cacheConfig *core.CacheConfig, initData statetransfer.InitDataReader, chainConfig *params.ChainConfig, serializedChainConfig []byte, txLookupLimit uint64, accountsPerSync uint) (*core.BlockChain, error) { - err := WriteOrTestGenblock(chainDb, initData, chainConfig, serializedChainConfig, accountsPerSync) +func WriteOrTestBlockChain(chainDb ethdb.Database, cacheConfig *core.CacheConfig, initData statetransfer.InitDataReader, chainConfig *params.ChainConfig, initMessage *arbostypes.ParsedInitMessage, txLookupLimit uint64, accountsPerSync uint) (*core.BlockChain, error) { + err := WriteOrTestGenblock(chainDb, initData, chainConfig, initMessage, accountsPerSync) if err != nil { return nil, err } diff --git a/arbnode/inbox_reader.go b/arbnode/inbox_reader.go index 19fee7aa3d..c83e8e4af4 100644 --- a/arbnode/inbox_reader.go +++ b/arbnode/inbox_reader.go @@ -140,17 +140,17 @@ func (r *InboxReader) Start(ctxIn context.Context) error { if err != nil { return err } - initChainId, initChainConfig, _, err := message.ParseInitMessage() + initMessage, err := message.ParseInitMessage() if err != nil { return err } chainConfig := r.tracker.txStreamer.chainConfig configChainId := chainConfig.ChainID - if initChainId.Cmp(configChainId) != 0 { - return fmt.Errorf("expected L2 chain ID %v but read L2 chain ID %v from init message in L1 inbox", configChainId, initChainId) + if initMessage.ChainId.Cmp(configChainId) != 0 { + return fmt.Errorf("expected L2 chain ID %v but read L2 chain ID %v from init message in L1 inbox", configChainId, initMessage.ChainId) } - if initChainConfig != nil { - if err := initChainConfig.CheckCompatible(chainConfig, chainConfig.ArbitrumChainParams.GenesisBlockNum, 0); err != nil { + if initMessage.ChainConfig != nil { + if err := initMessage.ChainConfig.CheckCompatible(chainConfig, chainConfig.ArbitrumChainParams.GenesisBlockNum, 0); err != nil { return fmt.Errorf("incompatible chain config read from init message in L1 inbox: %w", err) } } diff --git a/arbnode/inbox_test.go b/arbnode/inbox_test.go index 7ab300ef36..e68cee49ff 100644 --- a/arbnode/inbox_test.go +++ b/arbnode/inbox_test.go @@ -6,7 +6,6 @@ package arbnode import ( "context" "encoding/binary" - "encoding/json" "math/big" "math/rand" "testing" @@ -33,10 +32,6 @@ import ( func NewTransactionStreamerForTest(t *testing.T, ownerAddress common.Address) (*execution.ExecutionEngine, *TransactionStreamer, ethdb.Database, *core.BlockChain) { chainConfig := params.ArbitrumDevTestChainConfig() - serializedChainConfig, err := json.Marshal(chainConfig) - if err != nil { - Fail(t, err) - } initData := statetransfer.ArbosInitializationInfo{ Accounts: []statetransfer.AccountInitializationInfo{ @@ -51,7 +46,7 @@ func NewTransactionStreamerForTest(t *testing.T, ownerAddress common.Address) (* arbDb := rawdb.NewMemoryDatabase() initReader := statetransfer.NewMemoryInitDataReader(&initData) - bc, err := execution.WriteOrTestBlockChain(chainDb, nil, initReader, chainConfig, serializedChainConfig, ConfigDefaultL2Test().TxLookupLimit, 0) + bc, err := execution.WriteOrTestBlockChain(chainDb, nil, initReader, chainConfig, arbostypes.TestInitMessage, ConfigDefaultL2Test().TxLookupLimit, 0) if err != nil { Fail(t, err) diff --git a/arbnode/message_pruner.go b/arbnode/message_pruner.go index 2eacbc858a..1ba3886d8d 100644 --- a/arbnode/message_pruner.go +++ b/arbnode/message_pruner.go @@ -67,12 +67,12 @@ func (m *MessagePruner) prune(ctx context.Context) time.Duration { BlockNumber: big.NewInt(int64(rpc.FinalizedBlockNumber)), }) if err != nil { - log.Error("error getting latest confirmed node: %w", err) + log.Error("error getting latest confirmed node", "err", err) return m.config().MessagePruneInterval } nodeInfo, err := m.staker.Rollup().LookupNode(ctx, latestConfirmedNode) if err != nil { - log.Error("error getting latest confirmed node info: %w", err) + log.Error("error getting latest confirmed node info", "node", latestConfirmedNode, "err", err) return m.config().MessagePruneInterval } endBatchCount := nodeInfo.Assertion.AfterState.GlobalState.Batch @@ -81,7 +81,7 @@ func (m *MessagePruner) prune(ctx context.Context) time.Duration { } endBatchMetadata, err := m.inboxTracker.GetBatchMetadata(endBatchCount - 1) if err != nil { - log.Error("error getting last batch metadata: %w", err) + log.Error("error getting last batch metadata", "batch", endBatchCount-1, "err", err) return m.config().MessagePruneInterval } deleteOldMessageFromDB(endBatchCount, endBatchMetadata, m.inboxTracker.db, m.transactionStreamer.db) @@ -91,7 +91,7 @@ func (m *MessagePruner) prune(ctx context.Context) time.Duration { func deleteOldMessageFromDB(endBatchCount uint64, endBatchMetadata BatchMetadata, inboxTrackerDb ethdb.Database, transactionStreamerDb ethdb.Database) { prunedKeysRange, err := deleteFromLastPrunedUptoEndKey(inboxTrackerDb, sequencerBatchMetaPrefix, endBatchCount) if err != nil { - log.Error("error deleting batch metadata: %w", err) + log.Error("error deleting batch metadata", "err", err) return } if len(prunedKeysRange) > 0 { @@ -100,7 +100,7 @@ func deleteOldMessageFromDB(endBatchCount uint64, endBatchMetadata BatchMetadata prunedKeysRange, err = deleteFromLastPrunedUptoEndKey(transactionStreamerDb, messagePrefix, uint64(endBatchMetadata.MessageCount)) if err != nil { - log.Error("error deleting last batch messages: %w", err) + log.Error("error deleting last batch messages", "err", err) return } if len(prunedKeysRange) > 0 { @@ -109,7 +109,7 @@ func deleteOldMessageFromDB(endBatchCount uint64, endBatchMetadata BatchMetadata prunedKeysRange, err = deleteFromLastPrunedUptoEndKey(inboxTrackerDb, rlpDelayedMessagePrefix, endBatchMetadata.DelayedMessageCount) if err != nil { - log.Error("error deleting last batch delayed messages: %w", err) + log.Error("error deleting last batch delayed messages", "err", err) return } if len(prunedKeysRange) > 0 { diff --git a/arbos/arbosState/arbosstate.go b/arbos/arbosState/arbosstate.go index 333ad464ac..2bea8f7c54 100644 --- a/arbos/arbosState/arbosstate.go +++ b/arbos/arbosState/arbosstate.go @@ -4,7 +4,6 @@ package arbosState import ( - "encoding/json" "errors" "fmt" "math/big" @@ -19,6 +18,7 @@ import ( "github.com/offchainlabs/nitro/arbos/addressSet" "github.com/offchainlabs/nitro/arbos/addressTable" + "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbos/blockhash" "github.com/offchainlabs/nitro/arbos/burn" "github.com/offchainlabs/nitro/arbos/l1pricing" @@ -112,11 +112,7 @@ func NewArbosMemoryBackedArbOSState() (*ArbosState, *state.StateDB) { } burner := burn.NewSystemBurner(nil, false) chainConfig := params.ArbitrumDevTestChainConfig() - serializedChainConfig, err := json.Marshal(chainConfig) - if err != nil { - log.Crit("failed to serialize chain config", "error", err) - } - newState, err := InitializeArbosState(statedb, burner, chainConfig, serializedChainConfig) + newState, err := InitializeArbosState(statedb, burner, chainConfig, arbostypes.TestInitMessage) if err != nil { log.Crit("failed to open the ArbOS state", "error", err) } @@ -183,7 +179,7 @@ func getArbitrumOnlyGenesisPrecompiles(chainConfig *params.ChainConfig) []common // start running long-lived chains, every change to the storage format will require defining a new version and // providing upgrade code. -func InitializeArbosState(stateDB vm.StateDB, burner burn.Burner, chainConfig *params.ChainConfig, serializedChainConfig []byte) (*ArbosState, error) { +func InitializeArbosState(stateDB vm.StateDB, burner burn.Burner, chainConfig *params.ChainConfig, initMessage *arbostypes.ParsedInitMessage) (*ArbosState, error) { sto := storage.NewGeth(stateDB, burner) arbosVersion, err := sto.GetUint64ByUint64(uint64(versionOffset)) if err != nil { @@ -217,14 +213,14 @@ func InitializeArbosState(stateDB vm.StateDB, burner burn.Burner, chainConfig *p } _ = sto.SetByUint64(uint64(chainIdOffset), common.BigToHash(chainConfig.ChainID)) chainConfigStorage := sto.OpenStorageBackedBytes(chainConfigSubspace) - _ = chainConfigStorage.Set(serializedChainConfig) + _ = chainConfigStorage.Set(initMessage.SerializedChainConfig) _ = sto.SetUint64ByUint64(uint64(genesisBlockNumOffset), chainConfig.ArbitrumChainParams.GenesisBlockNum) initialRewardsRecipient := l1pricing.BatchPosterAddress if desiredArbosVersion >= 2 { initialRewardsRecipient = initialChainOwner } - _ = l1pricing.InitializeL1PricingState(sto.OpenSubStorage(l1PricingSubspace), initialRewardsRecipient) + _ = l1pricing.InitializeL1PricingState(sto.OpenSubStorage(l1PricingSubspace), initialRewardsRecipient, initMessage.InitialL1BaseFee) _ = l2pricing.InitializeL2PricingState(sto.OpenSubStorage(l2PricingSubspace)) _ = retryables.InitializeRetryableState(sto.OpenSubStorage(retryablesSubspace)) addressTable.Initialize(sto.OpenSubStorage(addressTableSubspace)) diff --git a/arbos/arbosState/initialization_test.go b/arbos/arbosState/initialization_test.go index 14ac8afa99..968f533e3e 100644 --- a/arbos/arbosState/initialization_test.go +++ b/arbos/arbosState/initialization_test.go @@ -12,8 +12,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" + "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbos/burn" "github.com/offchainlabs/nitro/statetransfer" "github.com/offchainlabs/nitro/util/testhelpers" @@ -60,11 +60,7 @@ func tryMarshalUnmarshal(input *statetransfer.ArbosInitializationInfo, t *testin initReader := statetransfer.NewMemoryInitDataReader(&initData) chainConfig := params.ArbitrumDevTestChainConfig() - serializedChainConfig, err := json.Marshal(chainConfig) - if err != nil { - log.Crit("failed to serialize chain config", "error", err) - } - stateroot, err := InitializeArbosInDatabase(raw, initReader, chainConfig, serializedChainConfig, 0, 0) + stateroot, err := InitializeArbosInDatabase(raw, initReader, chainConfig, arbostypes.TestInitMessage, 0, 0) Require(t, err) stateDb, err := state.New(stateroot, state.NewDatabase(raw), nil) diff --git a/arbos/arbosState/initialize.go b/arbos/arbosState/initialize.go index d30507ee81..e98ab08485 100644 --- a/arbos/arbosState/initialize.go +++ b/arbos/arbosState/initialize.go @@ -15,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" + "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbos/burn" "github.com/offchainlabs/nitro/arbos/l2pricing" "github.com/offchainlabs/nitro/arbos/retryables" @@ -49,7 +50,7 @@ func MakeGenesisBlock(parentHash common.Hash, blockNumber uint64, timestamp uint return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil)) } -func InitializeArbosInDatabase(db ethdb.Database, initData statetransfer.InitDataReader, chainConfig *params.ChainConfig, serializedChainConfig []byte, timestamp uint64, accountsPerSync uint) (common.Hash, error) { +func InitializeArbosInDatabase(db ethdb.Database, initData statetransfer.InitDataReader, chainConfig *params.ChainConfig, initMessage *arbostypes.ParsedInitMessage, timestamp uint64, accountsPerSync uint) (common.Hash, error) { stateDatabase := state.NewDatabase(db) statedb, err := state.New(common.Hash{}, stateDatabase, nil) if err != nil { @@ -73,7 +74,7 @@ func InitializeArbosInDatabase(db ethdb.Database, initData statetransfer.InitDat } burner := burn.NewSystemBurner(nil, false) - arbosState, err := InitializeArbosState(statedb, burner, chainConfig, serializedChainConfig) + arbosState, err := InitializeArbosState(statedb, burner, chainConfig, initMessage) if err != nil { log.Crit("failed to open the ArbOS state", "error", err) } diff --git a/arbos/arbostypes/incomingmessage.go b/arbos/arbostypes/incomingmessage.go index 9636bb095a..58186b7108 100644 --- a/arbos/arbostypes/incomingmessage.go +++ b/arbos/arbostypes/incomingmessage.go @@ -234,30 +234,60 @@ func ParseIncomingL1Message(rd io.Reader, batchFetcher FallibleBatchFetcher) (*L type FallibleBatchFetcher func(batchNum uint64) ([]byte, error) +type ParsedInitMessage struct { + ChainId *big.Int + InitialL1BaseFee *big.Int + + // These may be nil + ChainConfig *params.ChainConfig + SerializedChainConfig []byte +} + +// The initial L1 pricing basefee starts at 50 GWei unless set in the init message +var DefaultInitialL1BaseFee = big.NewInt(50 * params.GWei) + +var TestInitMessage = &ParsedInitMessage{ + ChainId: params.ArbitrumDevTestChainConfig().ChainID, + InitialL1BaseFee: DefaultInitialL1BaseFee, +} + // ParseInitMessage returns the chain id on success -func (msg *L1IncomingMessage) ParseInitMessage() (*big.Int, *params.ChainConfig, []byte, error) { +func (msg *L1IncomingMessage) ParseInitMessage() (*ParsedInitMessage, error) { if msg.Header.Kind != L1MessageType_Initialize { - return nil, nil, nil, fmt.Errorf("invalid init message kind %v", msg.Header.Kind) + return nil, fmt.Errorf("invalid init message kind %v", msg.Header.Kind) } + basefee := new(big.Int).Set(DefaultInitialL1BaseFee) var chainConfig params.ChainConfig var chainId *big.Int if len(msg.L2msg) == 32 { chainId = new(big.Int).SetBytes(msg.L2msg[:32]) - return chainId, nil, nil, nil + return &ParsedInitMessage{chainId, basefee, nil, nil}, nil } if len(msg.L2msg) > 32 { chainId = new(big.Int).SetBytes(msg.L2msg[:32]) version := msg.L2msg[32] - if version == 0 && len(msg.L2msg) > 33 { - serializedChainConfig := msg.L2msg[33:] - err := json.Unmarshal(serializedChainConfig, &chainConfig) + reader := bytes.NewReader(msg.L2msg[33:]) + switch version { + case 1: + var err error + basefee, err = util.Uint256FromReader(reader) + if err != nil { + return nil, err + } + fallthrough + case 0: + serializedChainConfig, err := io.ReadAll(reader) + if err != nil { + return nil, err + } + err = json.Unmarshal(serializedChainConfig, &chainConfig) if err != nil { - return nil, nil, nil, fmt.Errorf("failed to parse init message, err: %w, message data: %v", err, string(msg.L2msg)) + return nil, fmt.Errorf("failed to parse init message, err: %w, message data: %v", err, string(msg.L2msg)) } - return chainId, &chainConfig, serializedChainConfig, nil + return &ParsedInitMessage{chainId, basefee, &chainConfig, serializedChainConfig}, nil } } - return nil, nil, nil, fmt.Errorf("invalid init message data %v", string(msg.L2msg)) + return nil, fmt.Errorf("invalid init message data %v", string(msg.L2msg)) } func ParseBatchPostingReportMessageFields(rd io.Reader) (*big.Int, common.Address, common.Hash, uint64, *big.Int, error) { diff --git a/arbos/block_processor.go b/arbos/block_processor.go index 731f6a3357..9f208c4404 100644 --- a/arbos/block_processor.go +++ b/arbos/block_processor.go @@ -54,7 +54,7 @@ func (info *L1Info) L1BlockNumber() uint64 { return info.l1BlockNumber } -func createNewHeader(prevHeader *types.Header, l1info *L1Info, state *arbosState.ArbosState, chainConfig *params.ChainConfig, updateHeaderWithInfo bool) *types.Header { +func createNewHeader(prevHeader *types.Header, l1info *L1Info, state *arbosState.ArbosState, chainConfig *params.ChainConfig) *types.Header { l2Pricing := state.L2PricingState() baseFee, err := l2Pricing.BaseFeeWei() state.Restrict(err) @@ -96,30 +96,6 @@ func createNewHeader(prevHeader *types.Header, l1info *L1Info, state *arbosState Nonce: [8]byte{}, // Filled in later; post-merge Ethereum will require this to be zero BaseFee: baseFee, } - - if updateHeaderWithInfo { - var sendRoot common.Hash - var sendCount uint64 - var nextL1BlockNumber uint64 - var arbosVersion uint64 - - if blockNumber.Uint64() == chainConfig.ArbitrumChainParams.GenesisBlockNum { - arbosVersion = chainConfig.ArbitrumChainParams.InitialArbOSVersion - } else { - acc := state.SendMerkleAccumulator() - sendRoot, _ = acc.Root() - sendCount, _ = acc.Size() - nextL1BlockNumber, _ = state.Blockhashes().L1BlockNumber() - arbosVersion = state.ArbOSVersion() - } - arbitrumHeader := types.HeaderInfo{ - SendRoot: sendRoot, - SendCount: sendCount, - L1BlockNumber: nextL1BlockNumber, - ArbOSFormatVersion: arbosVersion, - } - arbitrumHeader.UpdateHeaderWithInfo(header) - } return header } @@ -213,7 +189,7 @@ func ProduceBlockAdvanced( l1Timestamp: l1Header.Timestamp, } - header := createNewHeader(lastBlockHeader, l1Info, state, chainConfig, false) + header := createNewHeader(lastBlockHeader, l1Info, state, chainConfig) signer := types.MakeSigner(chainConfig, header.Number) // Note: blockGasLeft will diverge from the actual gas left during execution in the event of invalid txs, // but it's only used as block-local representation limiting the amount of work done in a block. @@ -349,11 +325,15 @@ func ProduceBlockAdvanced( })() if tx.Type() == types.ArbitrumInternalTxType { + // ArbOS might have upgraded to a new version, so we need to refresh our state state, err = arbosState.OpenSystemArbosState(statedb, nil, true) if err != nil { return nil, nil, err } - header = createNewHeader(lastBlockHeader, l1Info, state, chainConfig, true) + // Update the ArbOS version in the header (if it changed) + extraInfo := types.DeserializeHeaderExtraInformation(header) + extraInfo.ArbOSFormatVersion = state.ArbOSVersion() + extraInfo.UpdateHeaderWithInfo(header) } // append the err, even if it is nil diff --git a/arbos/l1pricing/l1pricing.go b/arbos/l1pricing/l1pricing.go index 059d56858e..9772ac028b 100644 --- a/arbos/l1pricing/l1pricing.go +++ b/arbos/l1pricing/l1pricing.go @@ -74,7 +74,6 @@ const ( const ( InitialInertia = 10 InitialPerUnitReward = 10 - InitialPricePerUnitWei = 50 * params.GWei InitialPerBatchGasCostV6 = 100000 ) @@ -82,7 +81,7 @@ const ( var InitialEquilibrationUnitsV0 = arbmath.UintToBig(60 * params.TxDataNonZeroGasEIP2028 * 100000) var InitialEquilibrationUnitsV6 = arbmath.UintToBig(params.TxDataNonZeroGasEIP2028 * 10000000) -func InitializeL1PricingState(sto *storage.Storage, initialRewardsRecipient common.Address) error { +func InitializeL1PricingState(sto *storage.Storage, initialRewardsRecipient common.Address, initialL1BaseFee *big.Int) error { bptStorage := sto.OpenSubStorage(BatchPosterTableKey) if err := InitializeBatchPostersTable(bptStorage); err != nil { return err @@ -109,7 +108,7 @@ func InitializeL1PricingState(sto *storage.Storage, initialRewardsRecipient comm return err } pricePerUnit := sto.OpenStorageBackedBigInt(pricePerUnitOffset) - if err := pricePerUnit.SetByUint(InitialPricePerUnitWei); err != nil { + if err := pricePerUnit.SetSaturatingWithWarning(initialL1BaseFee, "initial L1 base fee (storing in price per unit)"); err != nil { return err } return nil diff --git a/arbos/l1pricing/l1pricing_test.go b/arbos/l1pricing/l1pricing_test.go index ec0ecc275e..b301c94257 100644 --- a/arbos/l1pricing/l1pricing_test.go +++ b/arbos/l1pricing/l1pricing_test.go @@ -4,18 +4,19 @@ package l1pricing import ( + "math/big" "testing" - am "github.com/offchainlabs/nitro/util/arbmath" - "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbos/burn" "github.com/offchainlabs/nitro/arbos/storage" ) func TestL1PriceUpdate(t *testing.T) { sto := storage.NewMemoryBacked(burn.NewSystemBurner(nil, false)) - err := InitializeL1PricingState(sto, common.Address{}) + initialPriceEstimate := big.NewInt(123 * params.GWei) + err := InitializeL1PricingState(sto, common.Address{}, initialPriceEstimate) Require(t, err) ps := OpenL1PricingState(sto) @@ -25,7 +26,6 @@ func TestL1PriceUpdate(t *testing.T) { Fail(t) } - initialPriceEstimate := am.UintToBig(InitialPricePerUnitWei) priceEstimate, err := ps.PricePerUnit() Require(t, err) if priceEstimate.Cmp(initialPriceEstimate) != 0 { diff --git a/arbos/util/util.go b/arbos/util/util.go index 1514d6d10d..4c0142aeb9 100644 --- a/arbos/util/util.go +++ b/arbos/util/util.go @@ -108,6 +108,14 @@ func HashFromReader(rd io.Reader) (common.Hash, error) { return common.BytesToHash(buf), nil } +func Uint256FromReader(rd io.Reader) (*big.Int, error) { + asHash, err := HashFromReader(rd) + if err != nil { + return nil, err + } + return asHash.Big(), nil +} + func HashToWriter(val common.Hash, wr io.Writer) error { _, err := wr.Write(val.Bytes()) return err diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index 114514b53a..c5faec4dd8 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -575,7 +575,7 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo if config.Init.ThenQuit { cacheConfig.SnapshotWait = true } - var serializedChainConfig []byte + var parsedInitMessage *arbostypes.ParsedInitMessage if config.Node.L1Reader.Enable { delayedBridge, err := arbnode.NewDelayedBridge(l1Client, rollupAddrs.Bridge, rollupAddrs.DeployedAt) if err != nil { @@ -596,30 +596,34 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo if initMessage == nil { return chainDb, nil, fmt.Errorf("failed to get init message while attempting to get serialized chain config") } - var initChainConfig *params.ChainConfig - var initChainId *big.Int - initChainId, initChainConfig, serializedChainConfig, err = initMessage.ParseInitMessage() + parsedInitMessage, err = initMessage.ParseInitMessage() if err != nil { return chainDb, nil, err } - if initChainId.Cmp(chainId) != 0 { - return chainDb, nil, fmt.Errorf("expected L2 chain ID %v but read L2 chain ID %v from init message in L1 inbox", chainId, initChainId) + if parsedInitMessage.ChainId.Cmp(chainId) != 0 { + return chainDb, nil, fmt.Errorf("expected L2 chain ID %v but read L2 chain ID %v from init message in L1 inbox", chainId, parsedInitMessage.ChainId) } - if initChainConfig != nil { - if err := initChainConfig.CheckCompatible(chainConfig, chainConfig.ArbitrumChainParams.GenesisBlockNum, 0); err != nil { + if parsedInitMessage.ChainConfig != nil { + if err := parsedInitMessage.ChainConfig.CheckCompatible(chainConfig, chainConfig.ArbitrumChainParams.GenesisBlockNum, 0); err != nil { return chainDb, nil, fmt.Errorf("incompatible chain config read from init message in L1 inbox: %w", err) } } - log.Info("Read serialized chain config from init message", "json", string(serializedChainConfig)) + log.Info("Read serialized chain config from init message", "json", string(parsedInitMessage.SerializedChainConfig)) } else { - serializedChainConfig, err = json.Marshal(chainConfig) + serializedChainConfig, err := json.Marshal(chainConfig) if err != nil { return chainDb, nil, err } - log.Warn("Serialized chain config as L1Reader is disabled and serialized chain config from init message is not available", "json", string(serializedChainConfig)) + parsedInitMessage = &arbostypes.ParsedInitMessage{ + ChainId: chainConfig.ChainID, + InitialL1BaseFee: arbostypes.DefaultInitialL1BaseFee, + ChainConfig: chainConfig, + SerializedChainConfig: serializedChainConfig, + } + log.Warn("Created fake init message as L1Reader is disabled and serialized chain config from init message is not available", "json", string(serializedChainConfig)) } - l2BlockChain, err = execution.WriteOrTestBlockChain(chainDb, cacheConfig, initDataReader, chainConfig, serializedChainConfig, config.Node.TxLookupLimit, config.Init.AccountsPerSync) + l2BlockChain, err = execution.WriteOrTestBlockChain(chainDb, cacheConfig, initDataReader, chainConfig, parsedInitMessage, config.Node.TxLookupLimit, config.Init.AccountsPerSync) if err != nil { return chainDb, nil, err } diff --git a/cmd/replay/main.go b/cmd/replay/main.go index 32e30cc892..501562d265 100644 --- a/cmd/replay/main.go +++ b/cmd/replay/main.go @@ -244,19 +244,20 @@ func main() { message := readMessage(false) - chainId, chainConfig, serializedChainConfig, err := message.Message.ParseInitMessage() + initMessage, err := message.Message.ParseInitMessage() if err != nil { panic(err) } + chainConfig := initMessage.ChainConfig if chainConfig == nil { log.Info("No chain config in the init message. Falling back to hardcoded chain config.") - chainConfig, err = chaininfo.GetChainConfig(chainId, "", 0, []string{}, "") + chainConfig, err = chaininfo.GetChainConfig(initMessage.ChainId, "", 0, []string{}, "") if err != nil { panic(err) } } - _, err = arbosState.InitializeArbosState(statedb, burn.NewSystemBurner(nil, false), chainConfig, serializedChainConfig) + _, err = arbosState.InitializeArbosState(statedb, burn.NewSystemBurner(nil, false), chainConfig, initMessage) if err != nil { panic(fmt.Sprintf("Error initializing ArbOS: %v", err.Error())) } diff --git a/system_tests/common_test.go b/system_tests/common_test.go index d74ae2c481..7c9b6b1191 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -18,6 +18,7 @@ import ( "github.com/offchainlabs/nitro/validator/server_api" "github.com/offchainlabs/nitro/validator/valnode" + "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbos/util" "github.com/offchainlabs/nitro/arbstate" "github.com/offchainlabs/nitro/blsSignatures" @@ -432,9 +433,24 @@ func createTestL1BlockChainWithConfig(t *testing.T, l1info info, stackConfig *no return l1info, l1Client, l1backend, stack } +func getInitMessage(ctx context.Context, t *testing.T, l1client client, addresses *chaininfo.RollupAddresses) *arbostypes.ParsedInitMessage { + bridge, err := arbnode.NewDelayedBridge(l1client, addresses.Bridge, addresses.DeployedAt) + Require(t, err) + deployedAtBig := arbmath.UintToBig(addresses.DeployedAt) + messages, err := bridge.LookupMessagesInRange(ctx, deployedAtBig, deployedAtBig, nil) + Require(t, err) + if len(messages) == 0 { + Fatal(t, "No delayed messages found at rollup creation block") + } + initMessage, err := messages[0].Message.ParseInitMessage() + Require(t, err, "Failed to parse rollup init message") + + return initMessage +} + func DeployOnTestL1( t *testing.T, ctx context.Context, l1info info, l1client client, chainConfig *params.ChainConfig, -) *chaininfo.RollupAddresses { +) (*chaininfo.RollupAddresses, *arbostypes.ParsedInitMessage) { l1info.GenerateAccount("RollupOwner") l1info.GenerateAccount("Sequencer") l1info.GenerateAccount("User") @@ -462,17 +478,18 @@ func DeployOnTestL1( l1info.SetContract("Bridge", addresses.Bridge) l1info.SetContract("SequencerInbox", addresses.SequencerInbox) l1info.SetContract("Inbox", addresses.Inbox) - return addresses + initMessage := getInitMessage(ctx, t, l1client, addresses) + return addresses, initMessage } func createL2BlockChain( t *testing.T, l2info *BlockchainTestInfo, dataDir string, chainConfig *params.ChainConfig, ) (*BlockchainTestInfo, *node.Node, ethdb.Database, ethdb.Database, *core.BlockChain) { - return createL2BlockChainWithStackConfig(t, l2info, dataDir, chainConfig, nil) + return createL2BlockChainWithStackConfig(t, l2info, dataDir, chainConfig, nil, nil) } func createL2BlockChainWithStackConfig( - t *testing.T, l2info *BlockchainTestInfo, dataDir string, chainConfig *params.ChainConfig, stackConfig *node.Config, + t *testing.T, l2info *BlockchainTestInfo, dataDir string, chainConfig *params.ChainConfig, initMessage *arbostypes.ParsedInitMessage, stackConfig *node.Config, ) (*BlockchainTestInfo, *node.Node, ethdb.Database, ethdb.Database, *core.BlockChain) { if l2info == nil { l2info = NewArbTestInfo(t, chainConfig.ChainID) @@ -493,9 +510,17 @@ func createL2BlockChainWithStackConfig( Require(t, err) initReader := statetransfer.NewMemoryInitDataReader(&l2info.ArbInitData) - serializedChainConfig, err := json.Marshal(chainConfig) - Require(t, err) - blockchain, err := execution.WriteOrTestBlockChain(chainDb, nil, initReader, chainConfig, serializedChainConfig, arbnode.ConfigDefaultL2Test().TxLookupLimit, 0) + if initMessage == nil { + serializedChainConfig, err := json.Marshal(chainConfig) + Require(t, err) + initMessage = &arbostypes.ParsedInitMessage{ + ChainId: chainConfig.ChainID, + InitialL1BaseFee: arbostypes.DefaultInitialL1BaseFee, + ChainConfig: chainConfig, + SerializedChainConfig: serializedChainConfig, + } + } + blockchain, err := execution.WriteOrTestBlockChain(chainDb, nil, initReader, chainConfig, initMessage, arbnode.ConfigDefaultL2Test().TxLookupLimit, 0) Require(t, err) return l2info, stack, chainDb, arbDb, blockchain @@ -561,8 +586,8 @@ func createTestNodeOnL1WithConfigImpl( if l2info == nil { l2info = NewArbTestInfo(t, chainConfig.ChainID) } - _, l2stack, l2chainDb, l2arbDb, l2blockchain = createL2BlockChainWithStackConfig(t, l2info, "", chainConfig, stackConfig) - addresses := DeployOnTestL1(t, ctx, l1info, l1client, chainConfig) + addresses, initMessage := DeployOnTestL1(t, ctx, l1info, l1client, chainConfig) + _, l2stack, l2chainDb, l2arbDb, l2blockchain = createL2BlockChainWithStackConfig(t, l2info, "", chainConfig, initMessage, stackConfig) var sequencerTxOptsPtr *bind.TransactOpts var dataSigner signature.DataSignerFunc if isSequencer { @@ -712,11 +737,8 @@ func Create2ndNodeWithConfig( dataSigner := signature.DataSignerFromPrivateKey(l1info.GetInfoWithPrivKey("Sequencer").PrivateKey) txOpts := l1info.GetDefaultTransactOpts("Sequencer", ctx) chainConfig := first.Execution.ArbInterface.BlockChain().Config() - serializedChainConfig, err := json.Marshal(chainConfig) - if err != nil { - Fatal(t, err) - } - l2blockchain, err := execution.WriteOrTestBlockChain(l2chainDb, nil, initReader, chainConfig, serializedChainConfig, arbnode.ConfigDefaultL2Test().TxLookupLimit, 0) + initMessage := getInitMessage(ctx, t, l1client, first.DeployInfo) + l2blockchain, err := execution.WriteOrTestBlockChain(l2chainDb, nil, initReader, chainConfig, initMessage, arbnode.ConfigDefaultL2Test().TxLookupLimit, 0) Require(t, err) AddDefaultValNode(t, ctx, nodeConfig, true) diff --git a/system_tests/das_test.go b/system_tests/das_test.go index 42711b1172..d813253670 100644 --- a/system_tests/das_test.go +++ b/system_tests/das_test.go @@ -110,7 +110,7 @@ func TestDASRekey(t *testing.T) { l1info, l1client, _, l1stack := createTestL1BlockChain(t, nil) defer requireClose(t, l1stack) feedErrChan := make(chan error, 10) - addresses := DeployOnTestL1(t, ctx, l1info, l1client, chainConfig) + addresses, initMessage := DeployOnTestL1(t, ctx, l1info, l1client, chainConfig) // Setup DAS servers dasDataDir := t.TempDir() @@ -126,7 +126,7 @@ func TestDASRekey(t *testing.T) { authorizeDASKeyset(t, ctx, pubkeyA, l1info, l1client) // Setup L2 chain - _, l2stackA, l2chainDb, l2arbDb, l2blockchain := createL2BlockChain(t, l2info, nodeDir, chainConfig) + _, l2stackA, l2chainDb, l2arbDb, l2blockchain := createL2BlockChainWithStackConfig(t, l2info, nodeDir, chainConfig, initMessage, nil) l2info.GenerateAccount("User2") // Setup DAS config @@ -238,7 +238,7 @@ func TestDASComplexConfigAndRestMirror(t *testing.T) { l1Reader.Start(ctx) defer l1Reader.StopAndWait() feedErrChan := make(chan error, 10) - addresses := DeployOnTestL1(t, ctx, l1info, l1client, chainConfig) + addresses, initMessage := DeployOnTestL1(t, ctx, l1info, l1client, chainConfig) keyDir, fileDataDir, dbDataDir := t.TempDir(), t.TempDir(), t.TempDir() pubkey, _, err := das.GenerateAndStoreKeys(keyDir) @@ -304,7 +304,7 @@ func TestDASComplexConfigAndRestMirror(t *testing.T) { Require(t, err) // Setup L2 chain - l2info, l2stackA, l2chainDb, l2arbDb, l2blockchain := createL2BlockChain(t, nil, "", chainConfig) + l2info, l2stackA, l2chainDb, l2arbDb, l2blockchain := createL2BlockChainWithStackConfig(t, nil, "", chainConfig, initMessage, nil) l2info.GenerateAccount("User2") sequencerTxOpts := l1info.GetDefaultTransactOpts("Sequencer", ctx) diff --git a/system_tests/estimation_test.go b/system_tests/estimation_test.go index c1aade3942..2a416ad179 100644 --- a/system_tests/estimation_test.go +++ b/system_tests/estimation_test.go @@ -13,7 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" - "github.com/offchainlabs/nitro/arbos/l1pricing" + "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/solgen/go/mocksgen" "github.com/offchainlabs/nitro/solgen/go/node_interfacegen" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" @@ -134,7 +134,7 @@ func TestComponentEstimate(t *testing.T) { l2info, node, client := CreateTestL2(t, ctx) defer node.StopAndWait() - l1BaseFee := big.NewInt(l1pricing.InitialPricePerUnitWei) + l1BaseFee := new(big.Int).Set(arbostypes.DefaultInitialL1BaseFee) l2BaseFee := GetBaseFee(t, client, ctx) colors.PrintGrey("l1 basefee ", l1BaseFee) diff --git a/system_tests/fees_test.go b/system_tests/fees_test.go index 70074265a0..bdd998357e 100644 --- a/system_tests/fees_test.go +++ b/system_tests/fees_test.go @@ -90,12 +90,14 @@ func TestSequencerFeePaid(t *testing.T) { txSize := compressedTxSize(t, tx) l1GasBought := arbmath.BigDiv(l1Charge, l1Estimate).Uint64() - l1GasActual := txSize * params.TxDataNonZeroGasEIP2028 + l1ChargeExpected := arbmath.BigMulByUint(l1Estimate, txSize*params.TxDataNonZeroGasEIP2028) + // L1 gas can only be charged in terms of L2 gas, so subtract off any rounding error from the expected value + l1ChargeExpected.Sub(l1ChargeExpected, new(big.Int).Mod(l1ChargeExpected, l2info.GasPrice)) colors.PrintBlue("bytes ", l1GasBought/params.TxDataNonZeroGasEIP2028, txSize) - if l1GasBought != l1GasActual { - Fatal(t, "the sequencer's future revenue does not match its costs", l1GasBought, l1GasActual) + if !arbmath.BigEquals(l1Charge, l1ChargeExpected) { + Fatal(t, "the sequencer's future revenue does not match its costs", l1Charge, l1ChargeExpected) } return networkRevenue, tipPaidToNet } diff --git a/system_tests/full_challenge_impl_test.go b/system_tests/full_challenge_impl_test.go index 34d4a51928..26e2d4a64e 100644 --- a/system_tests/full_challenge_impl_test.go +++ b/system_tests/full_challenge_impl_test.go @@ -245,7 +245,7 @@ func RunChallengeTest(t *testing.T, asserterIsCorrect bool) { configByValidationNode(t, conf, valStack) fatalErrChan := make(chan error, 10) - asserterRollupAddresses := DeployOnTestL1(t, ctx, l1Info, l1Backend, chainConfig) + asserterRollupAddresses, initMessage := DeployOnTestL1(t, ctx, l1Info, l1Backend, chainConfig) deployerTxOpts := l1Info.GetDefaultTransactOpts("deployer", ctx) sequencerTxOpts := l1Info.GetDefaultTransactOpts("sequencer", ctx) @@ -255,7 +255,7 @@ func RunChallengeTest(t *testing.T, asserterIsCorrect bool) { asserterBridgeAddr, asserterSeqInbox, asserterSeqInboxAddr := setupSequencerInboxStub(ctx, t, l1Info, l1Backend, chainConfig) challengerBridgeAddr, challengerSeqInbox, challengerSeqInboxAddr := setupSequencerInboxStub(ctx, t, l1Info, l1Backend, chainConfig) - asserterL2Info, asserterL2Stack, asserterL2ChainDb, asserterL2ArbDb, asserterL2Blockchain := createL2BlockChain(t, nil, "", chainConfig) + asserterL2Info, asserterL2Stack, asserterL2ChainDb, asserterL2ArbDb, asserterL2Blockchain := createL2BlockChainWithStackConfig(t, nil, "", chainConfig, initMessage, nil) asserterRollupAddresses.Bridge = asserterBridgeAddr asserterRollupAddresses.SequencerInbox = asserterSeqInboxAddr asserterL2, err := arbnode.CreateNode(ctx, asserterL2Stack, asserterL2ChainDb, asserterL2ArbDb, NewFetcherFromConfig(conf), asserterL2Blockchain, l1Backend, asserterRollupAddresses, nil, nil, nil, fatalErrChan) @@ -263,7 +263,7 @@ func RunChallengeTest(t *testing.T, asserterIsCorrect bool) { err = asserterL2.Start(ctx) Require(t, err) - challengerL2Info, challengerL2Stack, challengerL2ChainDb, challengerL2ArbDb, challengerL2Blockchain := createL2BlockChain(t, nil, "", chainConfig) + challengerL2Info, challengerL2Stack, challengerL2ChainDb, challengerL2ArbDb, challengerL2Blockchain := createL2BlockChainWithStackConfig(t, nil, "", chainConfig, initMessage, nil) challengerRollupAddresses := *asserterRollupAddresses challengerRollupAddresses.Bridge = challengerBridgeAddr challengerRollupAddresses.SequencerInbox = challengerSeqInboxAddr diff --git a/system_tests/precompile_fuzz_test.go b/system_tests/precompile_fuzz_test.go index b5680cbb74..8ab133cf58 100644 --- a/system_tests/precompile_fuzz_test.go +++ b/system_tests/precompile_fuzz_test.go @@ -4,7 +4,6 @@ package arbtest import ( - "encoding/json" "math/big" "testing" @@ -13,9 +12,9 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbos/arbosState" + "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbos/burn" "github.com/offchainlabs/nitro/gethhook" "github.com/offchainlabs/nitro/precompiles" @@ -34,11 +33,7 @@ func FuzzPrecompiles(f *testing.F) { } burner := burn.NewSystemBurner(nil, false) chainConfig := params.ArbitrumDevTestChainConfig() - serializedChainConfig, err := json.Marshal(chainConfig) - if err != nil { - log.Crit("failed to serialize chain config", "error", err) - } - _, err = arbosState.InitializeArbosState(sdb, burner, chainConfig, serializedChainConfig) + _, err = arbosState.InitializeArbosState(sdb, burner, chainConfig, arbostypes.TestInitMessage) if err != nil { panic(err) } @@ -59,7 +54,7 @@ func FuzzPrecompiles(f *testing.F) { GasLimit: fuzzGas, BaseFee: common.Big1, } - evm := vm.NewEVM(blockContext, txContext, sdb, params.ArbitrumDevTestChainConfig(), vm.Config{}) + evm := vm.NewEVM(blockContext, txContext, sdb, chainConfig, vm.Config{}) // Pick a precompile address based on the first byte of the input var addr common.Address diff --git a/system_tests/state_fuzz_test.go b/system_tests/state_fuzz_test.go index 4d46ffc930..a8209499df 100644 --- a/system_tests/state_fuzz_test.go +++ b/system_tests/state_fuzz_test.go @@ -127,11 +127,17 @@ func FuzzStateTransition(f *testing.F) { if err != nil { panic(err) } + initMessage := &arbostypes.ParsedInitMessage{ + ChainId: chainConfig.ChainID, + InitialL1BaseFee: arbostypes.DefaultInitialL1BaseFee, + ChainConfig: chainConfig, + SerializedChainConfig: serializedChainConfig, + } stateRoot, err := arbosState.InitializeArbosInDatabase( chainDb, statetransfer.NewMemoryInitDataReader(&statetransfer.ArbosInitializationInfo{}), chainConfig, - serializedChainConfig, + initMessage, 0, 0, )