diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index b0e3061330..614711249b 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -845,7 +845,7 @@ func (p *DataPoster) sendTx(ctx context.Context, prevTx *storage.QueuedTransacti return fmt.Errorf("couldn't get preceding tx in DataPoster to check if should send tx with nonce %d: %w", newTx.FullTx.Nonce(), err) } if precedingTx != nil && // precedingTx == nil -> the actual preceding tx was already confirmed - precedingTx.FullTx.Type() != newTx.FullTx.Type() { + (precedingTx.FullTx.Type() != newTx.FullTx.Type() || !precedingTx.Sent) { latestBlockNumber, err := p.client.BlockNumber(ctx) if err != nil { return fmt.Errorf("couldn't get block number in DataPoster to check if should send tx with nonce %d: %w", newTx.FullTx.Nonce(), err) @@ -857,7 +857,7 @@ func (p *DataPoster) sendTx(ctx context.Context, prevTx *storage.QueuedTransacti } if precedingTx.FullTx.Nonce() > reorgResistantNonce { - log.Info("DataPoster is holding off on sending a transaction of different type to the previous transaction until the previous transaction has been included in a reorg resistant block (it remains queued and will be retried)", "nonce", newTx.FullTx.Nonce(), "prevType", precedingTx.FullTx.Type(), "type", newTx.FullTx.Type()) + log.Info("DataPoster is avoiding creating a mempool nonce gap (the tx remains queued and will be retried)", "nonce", newTx.FullTx.Nonce(), "prevType", precedingTx.FullTx.Type(), "type", newTx.FullTx.Type(), "prevSent", precedingTx.Sent) return nil } } diff --git a/arbnode/inbox_test.go b/arbnode/inbox_test.go index e979979dea..5c879743a4 100644 --- a/arbnode/inbox_test.go +++ b/arbnode/inbox_test.go @@ -233,7 +233,7 @@ func TestTransactionStreamer(t *testing.T) { Fail(t, "error getting block state", err) } haveBalance := state.GetBalance(acct) - if balance.Cmp(haveBalance) != 0 { + if balance.Cmp(haveBalance.ToBig()) != 0 { t.Error("unexpected balance for account", acct, "; expected", balance, "got", haveBalance) } } diff --git a/arbos/arbosState/arbosstate.go b/arbos/arbosState/arbosstate.go index b753797822..e84c350538 100644 --- a/arbos/arbosState/arbosstate.go +++ b/arbos/arbosState/arbosstate.go @@ -36,24 +36,26 @@ import ( // persisted beyond the end of the test.) type ArbosState struct { - arbosVersion uint64 // version of the ArbOS storage format and semantics - upgradeVersion storage.StorageBackedUint64 // version we're planning to upgrade to, or 0 if not planning to upgrade - upgradeTimestamp storage.StorageBackedUint64 // when to do the planned upgrade - networkFeeAccount storage.StorageBackedAddress - l1PricingState *l1pricing.L1PricingState - l2PricingState *l2pricing.L2PricingState - retryableState *retryables.RetryableState - addressTable *addressTable.AddressTable - chainOwners *addressSet.AddressSet - sendMerkle *merkleAccumulator.MerkleAccumulator - blockhashes *blockhash.Blockhashes - chainId storage.StorageBackedBigInt - chainConfig storage.StorageBackedBytes - genesisBlockNum storage.StorageBackedUint64 - infraFeeAccount storage.StorageBackedAddress - brotliCompressionLevel storage.StorageBackedUint64 // brotli compression level used for pricing - backingStorage *storage.Storage - Burner burn.Burner + arbosVersion uint64 // version of the ArbOS storage format and semantics + maxArbosVersionSupported uint64 // maximum ArbOS version supported by this code + maxDebugArbosVersionSupported uint64 // maximum ArbOS version supported by this code in debug mode + upgradeVersion storage.StorageBackedUint64 // version we're planning to upgrade to, or 0 if not planning to upgrade + upgradeTimestamp storage.StorageBackedUint64 // when to do the planned upgrade + networkFeeAccount storage.StorageBackedAddress + l1PricingState *l1pricing.L1PricingState + l2PricingState *l2pricing.L2PricingState + retryableState *retryables.RetryableState + addressTable *addressTable.AddressTable + chainOwners *addressSet.AddressSet + sendMerkle *merkleAccumulator.MerkleAccumulator + blockhashes *blockhash.Blockhashes + chainId storage.StorageBackedBigInt + chainConfig storage.StorageBackedBytes + genesisBlockNum storage.StorageBackedUint64 + infraFeeAccount storage.StorageBackedAddress + brotliCompressionLevel storage.StorageBackedUint64 // brotli compression level used for pricing + backingStorage *storage.Storage + Burner burn.Burner } var ErrUninitializedArbOS = errors.New("ArbOS uninitialized") @@ -70,6 +72,8 @@ func OpenArbosState(stateDB vm.StateDB, burner burn.Burner) (*ArbosState, error) } return &ArbosState{ arbosVersion, + 20, + 20, backingStorage.OpenStorageBackedUint64(uint64(upgradeVersionOffset)), backingStorage.OpenStorageBackedUint64(uint64(upgradeTimestampOffset)), backingStorage.OpenStorageBackedAddress(uint64(networkFeeAccountOffset)), @@ -299,7 +303,7 @@ func (state *ArbosState) UpgradeArbosVersion( case 10: ensure(state.l1PricingState.SetL1FeesAvailable(stateDB.GetBalance( l1pricing.L1PricerFundsPoolAddress, - ))) + ).ToBig())) case 11: // Update the PerBatchGasCost to a more accurate value compared to the old v6 default. ensure(state.l1PricingState.SetPerBatchGasCost(l1pricing.InitialPerBatchGasCostV12)) @@ -413,6 +417,14 @@ func (state *ArbosState) RetryableState() *retryables.RetryableState { return state.retryableState } +func (state *ArbosState) MaxArbosVersionSupported() uint64 { + return state.maxArbosVersionSupported +} + +func (state *ArbosState) MaxDebugArbosVersionSupported() uint64 { + return state.maxDebugArbosVersionSupported +} + func (state *ArbosState) L1PricingState() *l1pricing.L1PricingState { return state.l1PricingState } diff --git a/arbos/arbosState/initialization_test.go b/arbos/arbosState/initialization_test.go index 3de1fc5d38..0ef9cea4c5 100644 --- a/arbos/arbosState/initialization_test.go +++ b/arbos/arbosState/initialization_test.go @@ -151,7 +151,7 @@ func checkAccounts(db *state.StateDB, arbState *ArbosState, accts []statetransfe if db.GetNonce(addr) != acct.Nonce { t.Fatal() } - if db.GetBalance(addr).Cmp(acct.EthBalance) != 0 { + if db.GetBalance(addr).ToBig().Cmp(acct.EthBalance) != 0 { t.Fatal() } if acct.ContractInfo != nil { diff --git a/arbos/arbosState/initialize.go b/arbos/arbosState/initialize.go index 56d8172ee8..486c6ae33a 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/holiman/uint256" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbos/burn" "github.com/offchainlabs/nitro/arbos/l2pricing" @@ -142,7 +143,7 @@ func InitializeArbosInDatabase(db ethdb.Database, initData statetransfer.InitDat if err != nil { return common.Hash{}, err } - statedb.SetBalance(account.Addr, account.EthBalance) + statedb.SetBalance(account.Addr, uint256.MustFromBig(account.EthBalance)) statedb.SetNonce(account.Addr, account.Nonce) if account.ContractInfo != nil { statedb.SetCode(account.Addr, account.ContractInfo.Code) @@ -173,7 +174,7 @@ func initializeRetryables(statedb *state.StateDB, rs *retryables.RetryableState, return err } if r.Timeout <= currentTimestamp { - statedb.AddBalance(r.Beneficiary, r.Callvalue) + statedb.AddBalance(r.Beneficiary, uint256.MustFromBig(r.Callvalue)) continue } retryablesList = append(retryablesList, r) @@ -192,7 +193,7 @@ func initializeRetryables(statedb *state.StateDB, rs *retryables.RetryableState, addr := r.To to = &addr } - statedb.AddBalance(retryables.RetryableEscrowAddress(r.Id), r.Callvalue) + statedb.AddBalance(retryables.RetryableEscrowAddress(r.Id), uint256.MustFromBig(r.Callvalue)) _, err := rs.CreateRetryable(r.Id, r.Timeout, r.From, to, r.Callvalue, r.Beneficiary, r.Calldata) if err != nil { return err diff --git a/arbos/l1pricing/l1PricingOldVersions.go b/arbos/l1pricing/l1PricingOldVersions.go index 5c6b6ab7d6..821d743e7d 100644 --- a/arbos/l1pricing/l1PricingOldVersions.go +++ b/arbos/l1pricing/l1PricingOldVersions.go @@ -4,12 +4,13 @@ package l1pricing import ( + "math" + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/offchainlabs/nitro/arbos/util" am "github.com/offchainlabs/nitro/util/arbmath" - "math" - "math/big" ) func (ps *L1PricingState) _preversion10_UpdateForBatchPosterSpending( @@ -105,8 +106,8 @@ func (ps *L1PricingState) _preversion10_UpdateForBatchPosterSpending( // pay rewards, as much as possible paymentForRewards := am.BigMulByUint(am.UintToBig(perUnitReward), unitsAllocated) availableFunds := statedb.GetBalance(L1PricerFundsPoolAddress) - if am.BigLessThan(availableFunds, paymentForRewards) { - paymentForRewards = availableFunds + if am.BigLessThan(availableFunds.ToBig(), paymentForRewards) { + paymentForRewards = availableFunds.ToBig() } fundsDueForRewards = am.BigSub(fundsDueForRewards, paymentForRewards) if err := ps.SetFundsDueForRewards(fundsDueForRewards); err != nil { @@ -130,8 +131,8 @@ func (ps *L1PricingState) _preversion10_UpdateForBatchPosterSpending( return err } balanceToTransfer := balanceDueToPoster - if am.BigLessThan(availableFunds, balanceToTransfer) { - balanceToTransfer = availableFunds + if am.BigLessThan(availableFunds.ToBig(), balanceToTransfer) { + balanceToTransfer = availableFunds.ToBig() } if balanceToTransfer.Sign() > 0 { addrToPay, err := posterState.PayTo() @@ -166,7 +167,7 @@ func (ps *L1PricingState) _preversion10_UpdateForBatchPosterSpending( if err != nil { return err } - surplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress), am.BigAdd(totalFundsDue, fundsDueForRewards)) + surplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress).ToBig(), am.BigAdd(totalFundsDue, fundsDueForRewards)) inertia, err := ps.Inertia() if err != nil { @@ -230,7 +231,7 @@ func (ps *L1PricingState) _preVersion2_UpdateForBatchPosterSpending( if err != nil { return err } - oldSurplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress), am.BigAdd(totalFundsDue, fundsDueForRewards)) + oldSurplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress).ToBig(), am.BigAdd(totalFundsDue, fundsDueForRewards)) // compute allocation fraction -- will allocate updateTimeDelta/timeDelta fraction of units and funds to this update lastUpdateTime, err := ps.LastUpdateTime() @@ -280,7 +281,7 @@ func (ps *L1PricingState) _preVersion2_UpdateForBatchPosterSpending( // allocate funds to this update collectedSinceUpdate := statedb.GetBalance(L1PricerFundsPoolAddress) - availableFunds := am.BigDivByUint(am.BigMulByUint(collectedSinceUpdate, allocationNumerator), allocationDenominator) + availableFunds := am.BigDivByUint(am.BigMulByUint(collectedSinceUpdate.ToBig(), allocationNumerator), allocationDenominator) // pay rewards, as much as possible paymentForRewards := am.BigMulByUint(am.UintToBig(perUnitReward), unitsAllocated) @@ -356,7 +357,7 @@ func (ps *L1PricingState) _preVersion2_UpdateForBatchPosterSpending( if err != nil { return err } - surplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress), am.BigAdd(totalFundsDue, fundsDueForRewards)) + surplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress).ToBig(), am.BigAdd(totalFundsDue, fundsDueForRewards)) inertia, err := ps.Inertia() if err != nil { diff --git a/arbos/l1pricing_test.go b/arbos/l1pricing_test.go index b23c1747ab..6e2b1b7eec 100644 --- a/arbos/l1pricing_test.go +++ b/arbos/l1pricing_test.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" + "github.com/holiman/uint256" "github.com/offchainlabs/nitro/arbos/arbosState" "github.com/offchainlabs/nitro/arbos/l1pricing" "github.com/offchainlabs/nitro/arbos/util" @@ -171,7 +172,7 @@ func _testL1PricingFundsDue(t *testing.T, testParams *l1PricingTest, expectedRes // create some fake collection balanceAdded := big.NewInt(int64(testParams.fundsCollectedPerSecond * 3)) unitsAdded := testParams.unitsPerSecond * 3 - evm.StateDB.AddBalance(l1pricing.L1PricerFundsPoolAddress, balanceAdded) + evm.StateDB.AddBalance(l1pricing.L1PricerFundsPoolAddress, uint256.MustFromBig(balanceAdded)) err = l1p.SetL1FeesAvailable(balanceAdded) Require(t, err) err = l1p.SetUnitsSinceUpdate(unitsAdded) @@ -187,7 +188,7 @@ func _testL1PricingFundsDue(t *testing.T, testParams *l1PricingTest, expectedRes ) Require(t, err) rewardRecipientBalance := evm.StateDB.GetBalance(rewardAddress) - if !arbmath.BigEquals(rewardRecipientBalance, expectedResults.rewardRecipientBalance) { + if !arbmath.BigEquals(rewardRecipientBalance.ToBig(), expectedResults.rewardRecipientBalance) { Fail(t, rewardRecipientBalance, expectedResults.rewardRecipientBalance) } unitsRemaining, err := l1p.UnitsSinceUpdate() @@ -196,16 +197,16 @@ func _testL1PricingFundsDue(t *testing.T, testParams *l1PricingTest, expectedRes Fail(t, unitsRemaining, expectedResults.unitsRemaining) } fundsReceived := evm.StateDB.GetBalance(firstPayTo) - if !arbmath.BigEquals(fundsReceived, expectedResults.fundsReceived) { + if !arbmath.BigEquals(fundsReceived.ToBig(), expectedResults.fundsReceived) { Fail(t, fundsReceived, expectedResults.fundsReceived) } fundsStillHeld := evm.StateDB.GetBalance(l1pricing.L1PricerFundsPoolAddress) - if !arbmath.BigEquals(fundsStillHeld, expectedResults.fundsStillHeld) { + if !arbmath.BigEquals(fundsStillHeld.ToBig(), expectedResults.fundsStillHeld) { Fail(t, fundsStillHeld, expectedResults.fundsStillHeld) } fundsAvail, err := l1p.L1FeesAvailable() Require(t, err) - if fundsStillHeld.Cmp(fundsAvail) != 0 { + if fundsStillHeld.ToBig().Cmp(fundsAvail) != 0 { Fail(t, fundsStillHeld, fundsAvail) } } diff --git a/arbos/retryables/retryable.go b/arbos/retryables/retryable.go index 6984e41904..e1cfe48bcf 100644 --- a/arbos/retryables/retryable.go +++ b/arbos/retryables/retryable.go @@ -145,7 +145,7 @@ func (rs *RetryableState) DeleteRetryable(id common.Hash, evm *vm.EVM, scenario escrowAddress := RetryableEscrowAddress(id) beneficiaryAddress := common.BytesToAddress(beneficiary[:]) amount := evm.StateDB.GetBalance(escrowAddress) - err = util.TransferBalance(&escrowAddress, &beneficiaryAddress, amount, evm, scenario, "escrow") + err = util.TransferBalance(&escrowAddress, &beneficiaryAddress, amount.ToBig(), evm, scenario, "escrow") if err != nil { return false, err } diff --git a/arbos/tx_processor.go b/arbos/tx_processor.go index 06ea51bcb9..2eab598b09 100644 --- a/arbos/tx_processor.go +++ b/arbos/tx_processor.go @@ -8,6 +8,7 @@ import ( "fmt" "math/big" + "github.com/holiman/uint256" "github.com/offchainlabs/nitro/arbos/l1pricing" "github.com/offchainlabs/nitro/arbos/util" @@ -145,7 +146,7 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r // This transfer is necessary because we don't actually invoke the EVM. // Since MintBalance already called AddBalance on `from`, // we don't have EIP-161 concerns around not touching `from`. - core.Transfer(evm.StateDB, from, *to, value) + core.Transfer(evm.StateDB, from, *to, uint256.MustFromBig(value)) return true, 0, nil, nil case *types.ArbitrumInternalTx: defer (startTracer())() @@ -174,7 +175,7 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r // check that the user has enough balance to pay for the max submission fee balanceAfterMint := evm.StateDB.GetBalance(tx.From) - if balanceAfterMint.Cmp(tx.MaxSubmissionFee) < 0 { + if balanceAfterMint.ToBig().Cmp(tx.MaxSubmissionFee) < 0 { err := fmt.Errorf( "insufficient funds for max submission fee: address %v have %v want %v", tx.From, balanceAfterMint, tx.MaxSubmissionFee, @@ -258,7 +259,7 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r maxGasCost := arbmath.BigMulByUint(tx.GasFeeCap, usergas) maxFeePerGasTooLow := arbmath.BigLessThan(tx.GasFeeCap, effectiveBaseFee) - if arbmath.BigLessThan(balance, maxGasCost) || usergas < params.TxGas || maxFeePerGasTooLow { + if arbmath.BigLessThan(balance.ToBig(), maxGasCost) || usergas < params.TxGas || maxFeePerGasTooLow { // User either specified too low of a gas fee cap, didn't have enough balance to pay for gas, // or the specified gas limit is below the minimum transaction gas cost. // Either way, attempt to refund the gas costs, since we're not doing the auto-redeem. diff --git a/arbos/util/tracing.go b/arbos/util/tracing.go index e4cde0f42b..49b82d6d64 100644 --- a/arbos/util/tracing.go +++ b/arbos/util/tracing.go @@ -42,7 +42,7 @@ func NewTracingInfo(evm *vm.EVM, from, to common.Address, scenario TracingScenar return &TracingInfo{ Tracer: evm.Config.Tracer, Scenario: scenario, - Contract: vm.NewContract(addressHolder{to}, addressHolder{from}, big.NewInt(0), 0), + Contract: vm.NewContract(addressHolder{to}, addressHolder{from}, uint256.NewInt(0), 0), Depth: evm.Depth(), } } @@ -79,7 +79,7 @@ func (info *TracingInfo) MockCall(input []byte, gas uint64, from, to common.Addr tracer := info.Tracer depth := info.Depth - contract := vm.NewContract(addressHolder{to}, addressHolder{from}, amount, gas) + contract := vm.NewContract(addressHolder{to}, addressHolder{from}, uint256.MustFromBig(amount), gas) scope := &vm.ScopeContext{ Memory: TracingMemoryFromBytes(input), diff --git a/arbos/util/transfer.go b/arbos/util/transfer.go index dd6a0807db..e293ef13c3 100644 --- a/arbos/util/transfer.go +++ b/arbos/util/transfer.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/log" + "github.com/holiman/uint256" "github.com/offchainlabs/nitro/util/arbmath" ) @@ -29,17 +30,17 @@ func TransferBalance( } if from != nil { balance := evm.StateDB.GetBalance(*from) - if arbmath.BigLessThan(balance, amount) { + if arbmath.BigLessThan(balance.ToBig(), amount) { return fmt.Errorf("%w: addr %v have %v want %v", vm.ErrInsufficientBalance, *from, balance, amount) } - evm.StateDB.SubBalance(*from, amount) + evm.StateDB.SubBalance(*from, uint256.MustFromBig(amount)) if evm.Context.ArbOSVersion >= 30 { // ensure the from account is "touched" for EIP-161 - evm.StateDB.AddBalance(*from, common.Big0) + evm.StateDB.AddBalance(*from, &uint256.Int{}) } } if to != nil { - evm.StateDB.AddBalance(*to, amount) + evm.StateDB.AddBalance(*to, uint256.MustFromBig(amount)) } if tracer := evm.Config.Tracer; tracer != nil { if evm.Depth() != 0 && scenario != TracingDuringEVM { @@ -63,7 +64,7 @@ func TransferBalance( info := &TracingInfo{ Tracer: evm.Config.Tracer, Scenario: scenario, - Contract: vm.NewContract(addressHolder{*to}, addressHolder{*from}, big.NewInt(0), 0), + Contract: vm.NewContract(addressHolder{*to}, addressHolder{*from}, uint256.NewInt(0), 0), Depth: evm.Depth(), } info.MockCall([]byte{}, 0, *from, *to, amount) diff --git a/cmd/daserver/daserver.go b/cmd/daserver/daserver.go index 07481651b2..3e96412648 100644 --- a/cmd/daserver/daserver.go +++ b/cmd/daserver/daserver.go @@ -7,12 +7,15 @@ import ( "context" "errors" "fmt" + "io" "net/http" "os" "os/signal" "syscall" "time" + "golang.org/x/exp/slog" + koanfjson "github.com/knadh/koanf/parsers/json" flag "github.com/spf13/pflag" @@ -182,14 +185,14 @@ func startup() error { confighelpers.PrintErrorAndExit(errors.New("please specify at least one of --enable-rest or --enable-rpc"), printSampleUsage) } - logFormat, err := genericconf.ParseLogType(serverConfig.LogType) + handler, err := genericconf.HandlerFromLogType(serverConfig.LogType, io.Writer(os.Stderr)) if err != nil { flag.Usage() - panic(fmt.Sprintf("Error parsing log type: %v", err)) + return fmt.Errorf("error parsing log type when creating handler: %w", err) } - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, logFormat)) - glogger.Verbosity(log.Lvl(serverConfig.LogLevel)) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler(handler) + glogger.Verbosity(slog.Level(serverConfig.LogLevel)) + log.SetDefault(log.NewLogger(glogger)) if err := startMetrics(serverConfig); err != nil { return err diff --git a/cmd/deploy/deploy.go b/cmd/deploy/deploy.go index 1c8b858106..d8c0aeeac4 100644 --- a/cmd/deploy/deploy.go +++ b/cmd/deploy/deploy.go @@ -8,6 +8,7 @@ import ( "encoding/json" "flag" "fmt" + "io" "math/big" "os" "strings" @@ -30,9 +31,10 @@ import ( ) func main() { - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) glogger.Verbosity(log.LvlDebug) - log.Root().SetHandler(glogger) + log.SetDefault(log.NewLogger(glogger)) log.Info("deploying rollup") ctx := context.Background() diff --git a/cmd/genericconf/config.go b/cmd/genericconf/config.go index 50aafbe223..06e1fcd12d 100644 --- a/cmd/genericconf/config.go +++ b/cmd/genericconf/config.go @@ -5,11 +5,13 @@ package genericconf import ( "errors" + "io" "time" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" flag "github.com/spf13/pflag" + "golang.org/x/exp/slog" ) type ConfConfig struct { @@ -63,11 +65,11 @@ var DefaultS3Config = S3Config{ SecretKey: "", } -func ParseLogType(logType string) (log.Format, error) { +func HandlerFromLogType(logType string, output io.Writer) (slog.Handler, error) { if logType == "plaintext" { - return log.TerminalFormat(false), nil + return log.NewTerminalHandler(output, false), nil } else if logType == "json" { - return log.JSONFormat(), nil + return log.JSONHandler(output), nil } return nil, errors.New("invalid log type") } diff --git a/cmd/genericconf/filehandler_test.go b/cmd/genericconf/filehandler_test.go index 7ea0668229..daa9ed397c 100644 --- a/cmd/genericconf/filehandler_test.go +++ b/cmd/genericconf/filehandler_test.go @@ -72,9 +72,10 @@ func testFileHandler(t *testing.T, testCompressed bool) { config.MaxSize = 1 config.Compress = testCompressed config.File = testFile - fileHandler := globalFileHandlerFactory.newHandler(log.JSONFormat(), &config, testFile) - defer func() { testhelpers.RequireImpl(t, globalFileHandlerFactory.close()) }() - log.Root().SetHandler(fileHandler) + handler, err := HandlerFromLogType("json", globalFileLoggerFactory.newFileWriter(&config, testFile)) + defer func() { testhelpers.RequireImpl(t, globalFileLoggerFactory.close()) }() + testhelpers.RequireImpl(t, err) + log.SetDefault(log.NewLogger(handler)) expected := []string{"dead", "beef", "ate", "bad", "beef"} for _, e := range expected { log.Warn(e) diff --git a/cmd/genericconf/logging.go b/cmd/genericconf/logging.go index a50dfa3190..d77071a0bf 100644 --- a/cmd/genericconf/logging.go +++ b/cmd/genericconf/logging.go @@ -4,22 +4,47 @@ import ( "context" "flag" "fmt" + "io" "os" + "sync" "github.com/ethereum/go-ethereum/log" + "golang.org/x/exp/slog" "gopkg.in/natefinch/lumberjack.v2" ) -var globalFileHandlerFactory = fileHandlerFactory{} +var globalFileLoggerFactory = fileLoggerFactory{} -type fileHandlerFactory struct { - writer *lumberjack.Logger - records chan *log.Record - cancel context.CancelFunc +type fileLoggerFactory struct { + // writerMutex is to avoid parallel writes to the file-logger + writerMutex sync.Mutex + writer *lumberjack.Logger + + cancel context.CancelFunc + + // writeStartPing and writeDonePing are used to simulate sending of data via a buffered channel + // when Write is called and receiving it on another go-routine to write it to the io.Writer. + writeStartPing chan struct{} + writeDonePing chan struct{} +} + +// Write is essentially a wrapper for filewriter or lumberjack.Logger's Write method to implement +// config.BufSize functionality, data is dropped when l.writeStartPing channel (of size config.BuffSize) is full +func (l *fileLoggerFactory) Write(p []byte) (n int, err error) { + select { + case l.writeStartPing <- struct{}{}: + // Write data to the filelogger + l.writerMutex.Lock() + _, _ = l.writer.Write(p) + l.writerMutex.Unlock() + l.writeDonePing <- struct{}{} + default: + } + return len(p), nil } -// newHandler is not threadsafe -func (l *fileHandlerFactory) newHandler(logFormat log.Format, config *FileLoggingConfig, filename string) log.Handler { +// newFileWriter is not threadsafe +func (l *fileLoggerFactory) newFileWriter(config *FileLoggingConfig, filename string) io.Writer { l.close() l.writer = &lumberjack.Logger{ Filename: filename, @@ -28,40 +53,29 @@ func (l *fileHandlerFactory) newHandler(logFormat log.Format, config *FileLoggin MaxAge: config.MaxAge, Compress: config.Compress, } - // capture copy of the pointer - writer := l.writer - // lumberjack.Logger already locks on Write, no need for SyncHandler proxy which is used in StreamHandler - unsafeStreamHandler := log.LazyHandler(log.FuncHandler(func(r *log.Record) error { - _, err := writer.Write(logFormat.Format(r)) - return err - })) - l.records = make(chan *log.Record, config.BufSize) + l.writeStartPing = make(chan struct{}, config.BufSize) + l.writeDonePing = make(chan struct{}, config.BufSize) // capture copy - records := l.records + writeStartPing := l.writeStartPing + writeDonePing := l.writeDonePing var consumerCtx context.Context consumerCtx, l.cancel = context.WithCancel(context.Background()) go func() { + // writeStartPing channel signals Write operations to correctly implement config.BufSize functionality for { select { - case r := <-records: - _ = unsafeStreamHandler.Log(r) + case <-writeStartPing: + <-writeDonePing case <-consumerCtx.Done(): return } } }() - return log.FuncHandler(func(r *log.Record) error { - select { - case records <- r: - return nil - default: - return fmt.Errorf("Buffer overflow, dropping record") - } - }) + return l } // close is not threadsafe -func (l *fileHandlerFactory) close() error { +func (l *fileLoggerFactory) close() error { if l.cancel != nil { l.cancel() l.cancel = nil @@ -76,28 +90,29 @@ func (l *fileHandlerFactory) close() error { } // initLog is not threadsafe -func InitLog(logType string, logLevel log.Lvl, fileLoggingConfig *FileLoggingConfig, pathResolver func(string) string) error { - logFormat, err := ParseLogType(logType) - if err != nil { - flag.Usage() - return fmt.Errorf("error parsing log type: %w", err) - } +func InitLog(logType string, logLevel slog.Level, fileLoggingConfig *FileLoggingConfig, pathResolver func(string) string) error { var glogger *log.GlogHandler // always close previous instance of file logger - if err := globalFileHandlerFactory.close(); err != nil { + if err := globalFileLoggerFactory.close(); err != nil { return fmt.Errorf("failed to close file writer: %w", err) } + var output io.Writer if fileLoggingConfig.Enable { - glogger = log.NewGlogHandler( - log.MultiHandler( - log.StreamHandler(os.Stderr, logFormat), - // on overflow records are dropped silently as MultiHandler ignores errors - globalFileHandlerFactory.newHandler(logFormat, fileLoggingConfig, pathResolver(fileLoggingConfig.File)), - )) + output = io.MultiWriter( + io.Writer(os.Stderr), + // on overflow writeStartPing are dropped silently + globalFileLoggerFactory.newFileWriter(fileLoggingConfig, pathResolver(fileLoggingConfig.File)), + ) } else { - glogger = log.NewGlogHandler(log.StreamHandler(os.Stderr, logFormat)) + output = io.Writer(os.Stderr) + } + handler, err := HandlerFromLogType(logType, output) + if err != nil { + flag.Usage() + return fmt.Errorf("error parsing log type when creating handler: %w", err) } + glogger = log.NewGlogHandler(handler) glogger.Verbosity(logLevel) - log.Root().SetHandler(glogger) + log.SetDefault(log.NewLogger(glogger)) return nil } diff --git a/cmd/nitro-val/nitro_val.go b/cmd/nitro-val/nitro_val.go index 3671c7ea8d..4e543f7953 100644 --- a/cmd/nitro-val/nitro_val.go +++ b/cmd/nitro-val/nitro_val.go @@ -22,6 +22,7 @@ import ( "github.com/offchainlabs/nitro/cmd/util/confighelpers" _ "github.com/offchainlabs/nitro/execution/nodeInterface" "github.com/offchainlabs/nitro/validator/valnode" + "golang.org/x/exp/slog" ) func printSampleUsage(name string) { @@ -89,7 +90,7 @@ func mainImpl() int { } } - err = genericconf.InitLog(nodeConfig.LogType, log.Lvl(nodeConfig.LogLevel), &nodeConfig.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) + err = genericconf.InitLog(nodeConfig.LogType, slog.Level(nodeConfig.LogLevel), &nodeConfig.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) if err != nil { fmt.Fprintf(os.Stderr, "Error initializing logging: %v\n", err) return 1 @@ -108,7 +109,7 @@ func mainImpl() int { liveNodeConfig := genericconf.NewLiveConfig[*ValidationNodeConfig](args, nodeConfig, ParseNode) liveNodeConfig.SetOnReloadHook(func(oldCfg *ValidationNodeConfig, newCfg *ValidationNodeConfig) error { - return genericconf.InitLog(newCfg.LogType, log.Lvl(newCfg.LogLevel), &newCfg.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) + return genericconf.InitLog(newCfg.LogType, slog.Level(newCfg.LogLevel), &newCfg.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) }) valnode.EnsureValidationExposedViaAuthRPC(&stackConf) diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index 6ebfec3bb1..a45ec054a1 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -155,6 +155,17 @@ func validateBlockChain(blockChain *core.BlockChain, chainConfig *params.ChainCo return fmt.Errorf("invalid chain config, not compatible with previous: %w", err) } } + // Make sure we don't allow accidentally downgrading ArbOS + if chainConfig.DebugMode() { + if currentArbosState.ArbOSVersion() > currentArbosState.MaxDebugArbosVersionSupported() { + return fmt.Errorf("attempted to launch node in debug mode with ArbOS version %v on ArbOS state with version %v", currentArbosState.MaxDebugArbosVersionSupported(), currentArbosState.ArbOSVersion()) + } + } else { + if currentArbosState.ArbOSVersion() > currentArbosState.MaxArbosVersionSupported() { + return fmt.Errorf("attempted to launch node with ArbOS version %v on ArbOS state with version %v", currentArbosState.MaxArbosVersionSupported(), currentArbosState.ArbOSVersion()) + } + + } return nil } diff --git a/cmd/nitro/nitro.go b/cmd/nitro/nitro.go index 79ecd51ac2..df0feca8ee 100644 --- a/cmd/nitro/nitro.go +++ b/cmd/nitro/nitro.go @@ -63,6 +63,7 @@ import ( "github.com/offchainlabs/nitro/util/signature" "github.com/offchainlabs/nitro/validator/server_common" "github.com/offchainlabs/nitro/validator/valnode" + "golang.org/x/exp/slog" ) func printSampleUsage(name string) { @@ -207,7 +208,7 @@ func mainImpl() int { } stackConf.JWTSecret = filename } - err = genericconf.InitLog(nodeConfig.LogType, log.Lvl(nodeConfig.LogLevel), &nodeConfig.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) + err = genericconf.InitLog(nodeConfig.LogType, slog.Level(nodeConfig.LogLevel), &nodeConfig.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) if err != nil { fmt.Fprintf(os.Stderr, "Error initializing logging: %v\n", err) return 1 @@ -599,7 +600,7 @@ func mainImpl() int { } liveNodeConfig.SetOnReloadHook(func(oldCfg *NodeConfig, newCfg *NodeConfig) error { - if err := genericconf.InitLog(newCfg.LogType, log.Lvl(newCfg.LogLevel), &newCfg.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)); err != nil { + if err := genericconf.InitLog(newCfg.LogType, slog.Level(newCfg.LogLevel), &newCfg.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)); err != nil { return fmt.Errorf("failed to re-init logging: %w", err) } return currentNode.OnConfigReload(&oldCfg.Node, &newCfg.Node) diff --git a/cmd/relay/relay.go b/cmd/relay/relay.go index 40f4f26eec..5a7499e691 100644 --- a/cmd/relay/relay.go +++ b/cmd/relay/relay.go @@ -6,6 +6,7 @@ package main import ( "context" "fmt" + "io" "os" "os/signal" "syscall" @@ -19,6 +20,7 @@ import ( "github.com/offchainlabs/nitro/cmd/genericconf" "github.com/offchainlabs/nitro/cmd/util/confighelpers" "github.com/offchainlabs/nitro/relay" + "golang.org/x/exp/slog" ) func main() { @@ -62,14 +64,14 @@ func startup() error { confighelpers.PrintErrorAndExit(err, printSampleUsage) } - logFormat, err := genericconf.ParseLogType(relayConfig.LogType) + handler, err := genericconf.HandlerFromLogType(relayConfig.LogType, io.Writer(os.Stderr)) if err != nil { flag.Usage() - panic(fmt.Sprintf("Error parsing log type: %v", err)) + return fmt.Errorf("error parsing log type when creating handler: %w", err) } - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, logFormat)) - glogger.Verbosity(log.Lvl(relayConfig.LogLevel)) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler(handler) + glogger.Verbosity(slog.Level(relayConfig.LogLevel)) + log.SetDefault(log.NewLogger(glogger)) vcsRevision, _, vcsTime := confighelpers.GetVersion() log.Info("Running Arbitrum nitro relay", "revision", vcsRevision, "vcs.time", vcsTime) diff --git a/cmd/replay/main.go b/cmd/replay/main.go index 5369495324..3348d0b431 100644 --- a/cmd/replay/main.go +++ b/cmd/replay/main.go @@ -9,6 +9,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "io" "os" "github.com/ethereum/go-ethereum/common" @@ -172,9 +173,10 @@ func main() { wavmio.StubInit() gethhook.RequireHookedGeth() - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.LvlError) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) + glogger.Verbosity(log.LevelError) + log.SetDefault(log.NewLogger(glogger)) populateEcdsaCaches() diff --git a/das/aggregator_test.go b/das/aggregator_test.go index 776af3975b..ef8ef5327a 100644 --- a/das/aggregator_test.go +++ b/das/aggregator_test.go @@ -8,6 +8,7 @@ import ( "context" "errors" "fmt" + "io" "math/rand" "os" "strconv" @@ -158,9 +159,10 @@ func min(a, b int) int { } func enableLogging() { - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.LvlTrace) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) + glogger.Verbosity(log.LevelTrace) + log.SetDefault(log.NewLogger(glogger)) } func testConfigurableStorageFailures(t *testing.T, shouldFailAggregation bool) { diff --git a/execution/gethexec/tx_pre_checker.go b/execution/gethexec/tx_pre_checker.go index cff8b04d32..1a48d75fda 100644 --- a/execution/gethexec/tx_pre_checker.go +++ b/execution/gethexec/tx_pre_checker.go @@ -187,7 +187,7 @@ func PreCheckTx(bc *core.BlockChain, chainConfig *params.ChainConfig, header *ty } balance := statedb.GetBalance(sender) cost := tx.Cost() - if arbmath.BigLessThan(balance, cost) { + if arbmath.BigLessThan(balance.ToBig(), cost) { return fmt.Errorf("%w: address %v have %v want %v", core.ErrInsufficientFunds, sender, balance, cost) } if config.Strictness >= TxPreCheckerStrictnessFullValidation && tx.Nonce() > stateNonce { diff --git a/execution/nodeInterface/virtual-contracts.go b/execution/nodeInterface/virtual-contracts.go index 3a863e31b5..d72ad0da8e 100644 --- a/execution/nodeInterface/virtual-contracts.go +++ b/execution/nodeInterface/virtual-contracts.go @@ -88,7 +88,7 @@ func init() { return msg, nil, nil } - evm, vmError := backend.GetEVM(ctx, msg, statedb, header, &vm.Config{NoBaseFee: true}, blockCtx) + evm := backend.GetEVM(ctx, msg, statedb, header, &vm.Config{NoBaseFee: true}, blockCtx) go func() { <-ctx.Done() evm.Cancel() @@ -110,7 +110,7 @@ func init() { ReturnData: output, ScheduledTxes: nil, } - return msg, res, vmError() + return msg, res, statedb.Error() } return msg, nil, nil } diff --git a/gethhook/geth-hook.go b/gethhook/geth-hook.go index 08b96b384f..82a98830aa 100644 --- a/gethhook/geth-hook.go +++ b/gethhook/geth-hook.go @@ -65,6 +65,15 @@ func init() { vm.PrecompiledAddressesArbitrum = append(vm.PrecompiledAddressesArbitrum, addr) } + for addr, precompile := range vm.PrecompiledContractsArbitrum { + vm.PrecompiledContractsArbOS30[addr] = precompile + vm.PrecompiledAddressesArbOS30 = append(vm.PrecompiledAddressesArbOS30, addr) + } + for addr, precompile := range vm.PrecompiledContractsP256Verify { + vm.PrecompiledContractsArbOS30[addr] = precompile + vm.PrecompiledAddressesArbOS30 = append(vm.PrecompiledAddressesArbOS30, addr) + } + core.RenderRPCError = func(data []byte) error { if len(data) < 4 { return nil diff --git a/gethhook/geth_test.go b/gethhook/geth_test.go index 6274a54119..99bfa4ae1c 100644 --- a/gethhook/geth_test.go +++ b/gethhook/geth_test.go @@ -110,7 +110,7 @@ func TestEthDepositMessage(t *testing.T) { RunMessagesThroughAPI(t, [][]byte{serialized, serialized2}, statedb) - balanceAfter := statedb.GetBalance(addr) + balanceAfter := statedb.GetBalance(addr).ToBig() if balanceAfter.Cmp(new(big.Int).Add(balance.Big(), balance2.Big())) != 0 { Fail(t) } diff --git a/go-ethereum b/go-ethereum index daccadb06c..cc9e427d63 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit daccadb06c7bd9ad7e86c74f33ea39d897f0ece4 +Subproject commit cc9e427d63c377677b97cdb60af89859bd9c48cd diff --git a/go.mod b/go.mod index e48d99f489..652c5ed02d 100644 --- a/go.mod +++ b/go.mod @@ -21,12 +21,12 @@ require ( github.com/codeclysm/extract/v3 v3.0.2 github.com/dgraph-io/badger/v4 v4.2.0 github.com/enescakir/emoji v1.0.0 - github.com/ethereum/go-ethereum v1.10.26 + github.com/ethereum/go-ethereum v1.13.14 github.com/fatih/structtag v1.2.0 github.com/gdamore/tcell/v2 v2.6.0 github.com/google/go-cmp v0.6.0 github.com/hashicorp/golang-lru/v2 v2.0.2 - github.com/holiman/uint256 v1.2.3 + github.com/holiman/uint256 v1.2.4 github.com/ipfs/go-cid v0.4.1 github.com/ipfs/go-libipfs v0.6.2 github.com/ipfs/interface-go-ipfs-core v0.11.0 @@ -41,9 +41,10 @@ require ( github.com/spf13/pflag v1.0.5 github.com/wealdtech/go-merkletree v1.0.0 golang.org/x/crypto v0.21.0 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa golang.org/x/sys v0.18.0 golang.org/x/term v0.18.0 - golang.org/x/tools v0.13.0 + golang.org/x/tools v0.15.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) @@ -73,7 +74,7 @@ require ( github.com/aws/smithy-go v1.15.0 // indirect github.com/benbjohnson/clock v1.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.7.0 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect @@ -90,6 +91,7 @@ require ( github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/cskr/pubsub v1.0.2 // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect @@ -109,6 +111,7 @@ require ( github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gammazero/deque v0.2.1 // indirect + github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect github.com/gdamore/encoding v1.0.0 // indirect github.com/getsentry/sentry-go v0.18.0 // indirect github.com/go-logr/logr v1.2.3 // indirect @@ -280,10 +283,9 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect go4.org v0.0.0-20200411211856-f5505b9728dd // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/mod v0.12.0 // indirect + golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.22.0 // indirect - golang.org/x/sync v0.3.0 // indirect + golang.org/x/sync v0.5.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect @@ -314,9 +316,8 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect - github.com/go-ole/go-ole v1.2.5 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-redis/redis/v8 v8.11.4 - github.com/go-stack/stack v1.8.1 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/uuid v1.3.1 github.com/gorilla/websocket v1.5.0 // indirect diff --git a/go.sum b/go.sum index 484805a064..72d78ba497 100644 --- a/go.sum +++ b/go.sum @@ -171,8 +171,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= -github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= @@ -274,6 +274,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHH github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 h1:HVTnpeuvF6Owjd5mniCL8DEXo7uYXdQEmOP4FJbV5tg= github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= @@ -380,6 +382,8 @@ github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZ github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/tcell/v2 v2.6.0 h1:OKbluoP9VYmJwZwq/iLb4BxwKcwGthaa1YNBJIyCySg= @@ -414,8 +418,9 @@ github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= -github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= @@ -429,8 +434,6 @@ github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyL github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= @@ -537,6 +540,7 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= @@ -646,8 +650,8 @@ github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZ github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= -github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= @@ -1812,8 +1816,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1838,8 +1842,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180406214816-61147c48b25b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1922,8 +1926,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2020,6 +2024,7 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2112,8 +2117,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/precompiles/ArbGasInfo.go b/precompiles/ArbGasInfo.go index cb0045c49f..25801109c7 100644 --- a/precompiles/ArbGasInfo.go +++ b/precompiles/ArbGasInfo.go @@ -187,7 +187,7 @@ func (con ArbGasInfo) GetGasBacklog(c ctx, evm mech) (uint64, error) { return c.State.L2PricingState().GasBacklog() } -// GetPricingInertia gets the L2 basefee in response to backlogged gas +// GetPricingInertia gets how slowly ArbOS updates the L2 basefee in response to backlogged gas func (con ArbGasInfo) GetPricingInertia(c ctx, evm mech) (uint64, error) { return c.State.L2PricingState().PricingInertia() } @@ -197,6 +197,7 @@ func (con ArbGasInfo) GetGasBacklogTolerance(c ctx, evm mech) (uint64, error) { return c.State.L2PricingState().BacklogTolerance() } +// GetL1PricingSurplus gets the surplus of funds for L1 batch posting payments (may be negative) func (con ArbGasInfo) GetL1PricingSurplus(c ctx, evm mech) (*big.Int, error) { if c.State.ArbOSVersion() < 10 { return con._preversion10_GetL1PricingSurplus(c, evm) @@ -217,37 +218,45 @@ func (con ArbGasInfo) _preversion10_GetL1PricingSurplus(c ctx, evm mech) (*big.I } haveFunds := evm.StateDB.GetBalance(l1pricing.L1PricerFundsPoolAddress) needFunds := arbmath.BigAdd(fundsDueForRefunds, fundsDueForRewards) - return arbmath.BigSub(haveFunds, needFunds), nil + return arbmath.BigSub(haveFunds.ToBig(), needFunds), nil } +// GetPerBatchGasCharge gets the base charge (in L1 gas) attributed to each data batch in the calldata pricer func (con ArbGasInfo) GetPerBatchGasCharge(c ctx, evm mech) (int64, error) { return c.State.L1PricingState().PerBatchGasCost() } +// GetAmortizedCostCapBips gets the cost amortization cap in basis points func (con ArbGasInfo) GetAmortizedCostCapBips(c ctx, evm mech) (uint64, error) { return c.State.L1PricingState().AmortizedCostCapBips() } +// GetL1FeesAvailable gets the available funds from L1 fees func (con ArbGasInfo) GetL1FeesAvailable(c ctx, evm mech) (huge, error) { return c.State.L1PricingState().L1FeesAvailable() } +// GetL1PricingEquilibrationUnits gets the equilibration units parameter for L1 price adjustment algorithm func (con ArbGasInfo) GetL1PricingEquilibrationUnits(c ctx, evm mech) (*big.Int, error) { return c.State.L1PricingState().EquilibrationUnits() } +// GetLastL1PricingUpdateTime gets the last time the L1 calldata pricer was updated func (con ArbGasInfo) GetLastL1PricingUpdateTime(c ctx, evm mech) (uint64, error) { return c.State.L1PricingState().LastUpdateTime() } +// GetL1PricingFundsDueForRewards gets the amount of L1 calldata payments due for rewards (per the L1 reward rate) func (con ArbGasInfo) GetL1PricingFundsDueForRewards(c ctx, evm mech) (*big.Int, error) { return c.State.L1PricingState().FundsDueForRewards() } +// GetL1PricingUnitsSinceUpdate gets the amount of L1 calldata posted since the last update func (con ArbGasInfo) GetL1PricingUnitsSinceUpdate(c ctx, evm mech) (uint64, error) { return c.State.L1PricingState().UnitsSinceUpdate() } +// GetLastL1PricingSurplus gets the L1 pricing surplus as of the last update (may be negative) func (con ArbGasInfo) GetLastL1PricingSurplus(c ctx, evm mech) (*big.Int, error) { return c.State.L1PricingState().LastSurplus() } diff --git a/precompiles/ArbInfo.go b/precompiles/ArbInfo.go index a260f7e7a4..9f8cf34532 100644 --- a/precompiles/ArbInfo.go +++ b/precompiles/ArbInfo.go @@ -18,7 +18,7 @@ func (con ArbInfo) GetBalance(c ctx, evm mech, account addr) (huge, error) { if err := c.Burn(params.BalanceGasEIP1884); err != nil { return nil, err } - return evm.StateDB.GetBalance(account), nil + return evm.StateDB.GetBalance(account).ToBig(), nil } // GetCode retrieves a contract's deployed code diff --git a/precompiles/ArbOwner.go b/precompiles/ArbOwner.go index 166768940b..f718a99f3e 100644 --- a/precompiles/ArbOwner.go +++ b/precompiles/ArbOwner.go @@ -153,7 +153,7 @@ func (con ArbOwner) ReleaseL1PricerSurplusFunds(c ctx, evm mech, maxWeiToRelease if err != nil { return nil, err } - weiToTransfer := new(big.Int).Sub(balance, recognized) + weiToTransfer := new(big.Int).Sub(balance.ToBig(), recognized) if weiToTransfer.Sign() < 0 { return common.Big0, nil } diff --git a/precompiles/ArbOwner_test.go b/precompiles/ArbOwner_test.go index ab128a8cb2..1f8c7ae4cd 100644 --- a/precompiles/ArbOwner_test.go +++ b/precompiles/ArbOwner_test.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" "github.com/offchainlabs/nitro/arbos/arbosState" "github.com/offchainlabs/nitro/arbos/burn" @@ -113,7 +114,7 @@ func TestArbOwner(t *testing.T) { Fail(t, avail) } deposited := big.NewInt(1000000) - evm.StateDB.AddBalance(l1pricing.L1PricerFundsPoolAddress, deposited) + evm.StateDB.AddBalance(l1pricing.L1PricerFundsPoolAddress, uint256.MustFromBig(deposited)) avail, err = gasInfo.GetL1FeesAvailable(callCtx, evm) Require(t, err) if avail.Sign() != 0 { diff --git a/precompiles/precompile_test.go b/precompiles/precompile_test.go index 975856bced..376bfd7161 100644 --- a/precompiles/precompile_test.go +++ b/precompiles/precompile_test.go @@ -5,6 +5,7 @@ package precompiles import ( "fmt" + "io" "math/big" "os" "testing" @@ -181,9 +182,10 @@ func TestEventCosts(t *testing.T) { func TestPrecompilesPerArbosVersion(t *testing.T) { // Set up a logger in case log.Crit is called by Precompiles() - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.LvlWarn) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) + glogger.Verbosity(log.LevelWarn) + log.SetDefault(log.NewLogger(glogger)) expectedNewMethodsPerArbosVersion := map[uint64]int{ 0: 89, diff --git a/staker/challenge_test.go b/staker/challenge_test.go index c21ebcdecd..f74e18b63d 100644 --- a/staker/challenge_test.go +++ b/staker/challenge_test.go @@ -5,6 +5,7 @@ package staker import ( "context" + "io" "math/big" "os" "path" @@ -116,9 +117,10 @@ func runChallengeTest( testTimeout bool, maxInboxMessage uint64, ) { - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.LvlDebug) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) + glogger.Verbosity(log.LevelDebug) + log.SetDefault(log.NewLogger(glogger)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/system_tests/common_test.go b/system_tests/common_test.go index af9cac2801..8d783c4564 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -8,6 +8,7 @@ import ( "context" "encoding/hex" "encoding/json" + "io" "math/big" "net" "os" @@ -67,6 +68,7 @@ import ( "github.com/offchainlabs/nitro/solgen/go/upgrade_executorgen" "github.com/offchainlabs/nitro/statetransfer" "github.com/offchainlabs/nitro/util/testhelpers" + "golang.org/x/exp/slog" ) type info = *BlockchainTestInfo @@ -641,7 +643,8 @@ func createTestL1BlockChainWithConfig(t *testing.T, l1info info, stackConfig *no nodeConf := ethconfig.Defaults nodeConf.NetworkId = chainConfig.ChainID.Uint64() - l1Genesis := core.DeveloperGenesisBlock(15_000_000, l1info.GetAddress("Faucet")) + faucetAddr := l1info.GetAddress("Faucet") + l1Genesis := core.DeveloperGenesisBlock(15_000_000, &faucetAddr) infoGenesis := l1info.GetGenesisAlloc() for acct, info := range infoGenesis { l1Genesis.Alloc[acct] = info @@ -1165,13 +1168,14 @@ func deploySimple( func TestMain(m *testing.M) { logLevelEnv := os.Getenv("TEST_LOGLEVEL") if logLevelEnv != "" { - logLevel, err := strconv.ParseUint(logLevelEnv, 10, 32) - if err != nil || logLevel > uint64(log.LvlTrace) { + logLevel, err := strconv.ParseInt(logLevelEnv, 10, 32) + if err != nil || logLevel > int64(log.LevelCrit) { log.Warn("TEST_LOGLEVEL exists but out of bound, ignoring", "logLevel", logLevelEnv, "max", log.LvlTrace) } - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.Lvl(logLevel)) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) + glogger.Verbosity(slog.Level(logLevel)) + log.SetDefault(log.NewLogger(glogger)) } code := m.Run() os.Exit(code) diff --git a/system_tests/conditionaltx_test.go b/system_tests/conditionaltx_test.go index 438e42d37e..5099fc6c01 100644 --- a/system_tests/conditionaltx_test.go +++ b/system_tests/conditionaltx_test.go @@ -101,7 +101,7 @@ func getOptions(address common.Address, rootHash common.Hash, slotValueMap map[c } func getFulfillableBlockTimeLimits(t *testing.T, blockNumber uint64, timestamp uint64) []*arbitrum_types.ConditionalOptions { - future := math.HexOrDecimal64(timestamp + 40) + future := math.HexOrDecimal64(timestamp + 70) past := math.HexOrDecimal64(timestamp - 1) futureBlockNumber := math.HexOrDecimal64(blockNumber + 1000) currentBlockNumber := math.HexOrDecimal64(blockNumber) diff --git a/system_tests/das_test.go b/system_tests/das_test.go index c4a3c453d8..bb09cc9880 100644 --- a/system_tests/das_test.go +++ b/system_tests/das_test.go @@ -7,6 +7,7 @@ import ( "context" "encoding/base64" "encoding/json" + "io" "math/big" "net" "net/http" @@ -32,6 +33,7 @@ import ( "github.com/offchainlabs/nitro/solgen/go/precompilesgen" "github.com/offchainlabs/nitro/util/headerreader" "github.com/offchainlabs/nitro/util/signature" + "golang.org/x/exp/slog" ) func startLocalDASServer( @@ -356,9 +358,10 @@ func TestDASComplexConfigAndRestMirror(t *testing.T) { } func enableLogging(logLvl int) { - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.Lvl(logLvl)) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) + glogger.Verbosity(slog.Level(logLvl)) + log.SetDefault(log.NewLogger(glogger)) } func initTest(t *testing.T) { diff --git a/system_tests/debugapi_test.go b/system_tests/debugapi_test.go index 52a6bb25c4..30a2bee03e 100644 --- a/system_tests/debugapi_test.go +++ b/system_tests/debugapi_test.go @@ -2,15 +2,15 @@ package arbtest import ( "context" - "github.com/ethereum/go-ethereum/eth/tracers" + "encoding/json" "testing" - "encoding/json" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/rpc" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" ) @@ -34,7 +34,7 @@ func TestDebugAPI(t *testing.T) { err = l2rpc.CallContext(ctx, &badBlocks, "debug_getBadBlocks") Require(t, err) - var dumpIt state.IteratorDump + var dumpIt state.Dump err = l2rpc.CallContext(ctx, &dumpIt, "debug_accountRange", rpc.LatestBlockNumber, hexutil.Bytes{}, 10, true, true, false) Require(t, err) err = l2rpc.CallContext(ctx, &dumpIt, "debug_accountRange", rpc.PendingBlockNumber, hexutil.Bytes{}, 10, true, true, false) diff --git a/system_tests/estimation_test.go b/system_tests/estimation_test.go index 6f47c14f1f..e7f00ca94e 100644 --- a/system_tests/estimation_test.go +++ b/system_tests/estimation_test.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/gasestimator" "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/solgen/go/mocksgen" @@ -285,7 +286,7 @@ func TestComponentEstimate(t *testing.T) { l2Used := receipt.GasUsed - receipt.GasUsedForL1 colors.PrintMint("True ", receipt.GasUsed, " - ", receipt.GasUsedForL1, " = ", l2Used) - if l2Estimate != l2Used { + if float64(l2Estimate-l2Used) > float64(gasEstimateForL1+l2Used)*gasestimator.EstimateGasErrorRatio { Fatal(t, l2Estimate, l2Used) } } diff --git a/system_tests/full_challenge_impl_test.go b/system_tests/full_challenge_impl_test.go index af790c9a17..eec274a915 100644 --- a/system_tests/full_challenge_impl_test.go +++ b/system_tests/full_challenge_impl_test.go @@ -249,9 +249,10 @@ func createL2Nodes(t *testing.T, ctx context.Context, conf *arbnode.Config, chai } func RunChallengeTest(t *testing.T, asserterIsCorrect bool, useStubs bool, challengeMsgIdx int64) { - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) glogger.Verbosity(log.LvlInfo) - log.Root().SetHandler(glogger) + log.SetDefault(log.NewLogger(glogger)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/system_tests/retryable_test.go b/system_tests/retryable_test.go index f7c7cec54c..1abf9f2162 100644 --- a/system_tests/retryable_test.go +++ b/system_tests/retryable_test.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/gasestimator" "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbnode" "github.com/offchainlabs/nitro/arbos" @@ -161,8 +162,12 @@ func TestSubmitRetryableImmediateSuccess(t *testing.T) { Require(t, err, "failed to estimate retryable submission") estimate := tx.Gas() expectedEstimate := params.TxGas + params.TxDataNonZeroGasEIP2028*4 - if estimate != expectedEstimate { - t.Errorf("estimated retryable ticket at %v gas but expected %v", estimate, expectedEstimate) + if float64(estimate) > float64(expectedEstimate)*(1+gasestimator.EstimateGasErrorRatio) { + t.Errorf("estimated retryable ticket at %v gas but expected %v, with error margin of %v", + estimate, + expectedEstimate, + gasestimator.EstimateGasErrorRatio, + ) } // submit & auto redeem the retryable using the gas estimate diff --git a/system_tests/seqinbox_test.go b/system_tests/seqinbox_test.go index 81dd2ad0d7..1b2701c2df 100644 --- a/system_tests/seqinbox_test.go +++ b/system_tests/seqinbox_test.go @@ -171,7 +171,7 @@ func testSequencerInboxReaderImpl(t *testing.T, validator bool) { var blockStates []blockTestState blockStates = append(blockStates, blockTestState{ balances: map[common.Address]*big.Int{ - ownerAddress: startOwnerBalance, + ownerAddress: startOwnerBalance.ToBig(), }, nonces: map[common.Address]uint64{ ownerAddress: startOwnerNonce, @@ -392,7 +392,7 @@ func testSequencerInboxReaderImpl(t *testing.T, validator bool) { } if batchCount.Cmp(big.NewInt(int64(len(blockStates)))) == 0 { break - } else if i >= 100 { + } else if i >= 140 { Fatal(t, "timed out waiting for l1 batch count update; have", batchCount, "want", len(blockStates)-1) } time.Sleep(10 * time.Millisecond) @@ -433,7 +433,7 @@ func testSequencerInboxReaderImpl(t *testing.T, validator bool) { Require(t, err) for acct, expectedBalance := range state.balances { haveBalance := stateDb.GetBalance(acct) - if expectedBalance.Cmp(haveBalance) < 0 { + if expectedBalance.Cmp(haveBalance.ToBig()) < 0 { Fatal(t, "unexpected balance for account", acct, "; expected", expectedBalance, "got", haveBalance) } } diff --git a/system_tests/triedb_race_test.go b/system_tests/triedb_race_test.go index 6d9415df83..9f14f08898 100644 --- a/system_tests/triedb_race_test.go +++ b/system_tests/triedb_race_test.go @@ -14,7 +14,7 @@ import ( ) func TestTrieDBCommitRace(t *testing.T) { - _ = testhelpers.InitTestLog(t, log.LvlError) + _ = testhelpers.InitTestLog(t, log.LevelError) ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/util/testhelpers/testhelpers.go b/util/testhelpers/testhelpers.go index bccc269171..eafd0eda7e 100644 --- a/util/testhelpers/testhelpers.go +++ b/util/testhelpers/testhelpers.go @@ -4,7 +4,9 @@ package testhelpers import ( + "context" "crypto/rand" + "io" "os" "regexp" "sync" @@ -13,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/util/colors" + "golang.org/x/exp/slog" ) // Fail a test should an error occur @@ -43,19 +46,29 @@ func RandomAddress() common.Address { } type LogHandler struct { - mutex sync.Mutex - t *testing.T - records []log.Record - streamHandler log.Handler + mutex sync.Mutex + t *testing.T + records []slog.Record + terminalHandler *log.TerminalHandler } -func (h *LogHandler) Log(record *log.Record) error { - if err := h.streamHandler.Log(record); err != nil { +func (h *LogHandler) Enabled(_ context.Context, level slog.Level) bool { + return h.terminalHandler.Enabled(context.Background(), level) +} +func (h *LogHandler) WithGroup(name string) slog.Handler { + return h.terminalHandler.WithGroup(name) +} +func (h *LogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + return h.terminalHandler.WithAttrs(attrs) +} + +func (h *LogHandler) Handle(_ context.Context, record slog.Record) error { + if err := h.terminalHandler.Handle(context.Background(), record); err != nil { return err } h.mutex.Lock() defer h.mutex.Unlock() - h.records = append(h.records, *record) + h.records = append(h.records, record) return nil } @@ -65,7 +78,7 @@ func (h *LogHandler) WasLogged(pattern string) bool { h.mutex.Lock() defer h.mutex.Unlock() for _, record := range h.records { - if re.MatchString(record.Msg) { + if re.MatchString(record.Message) { return true } } @@ -74,16 +87,16 @@ func (h *LogHandler) WasLogged(pattern string) bool { func newLogHandler(t *testing.T) *LogHandler { return &LogHandler{ - t: t, - records: make([]log.Record, 0), - streamHandler: log.StreamHandler(os.Stderr, log.TerminalFormat(false)), + t: t, + records: make([]slog.Record, 0), + terminalHandler: log.NewTerminalHandler(io.Writer(os.Stderr), false), } } -func InitTestLog(t *testing.T, level log.Lvl) *LogHandler { +func InitTestLog(t *testing.T, level slog.Level) *LogHandler { handler := newLogHandler(t) glogger := log.NewGlogHandler(handler) glogger.Verbosity(level) - log.Root().SetHandler(glogger) + log.SetDefault(log.NewLogger(glogger)) return handler } diff --git a/validator/server_common/machine_locator.go b/validator/server_common/machine_locator.go index 4c25448dda..c8b4d9a165 100644 --- a/validator/server_common/machine_locator.go +++ b/validator/server_common/machine_locator.go @@ -8,21 +8,20 @@ import ( "strings" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" ) type MachineLocator struct { - rootPath string - latest common.Hash + rootPath string + latest common.Hash + moduleRoots []common.Hash } var ErrMachineNotFound = errors.New("machine not found") func NewMachineLocator(rootPath string) (*MachineLocator, error) { - var places []string - - if rootPath != "" { - places = append(places, rootPath) - } else { + dirs := []string{rootPath} + if rootPath == "" { // Check the project dir: /arbnode/node.go => ../../target/machines _, thisFile, _, ok := runtime.Caller(0) if !ok { @@ -30,7 +29,7 @@ func NewMachineLocator(rootPath string) (*MachineLocator, error) { } projectDir := filepath.Dir(filepath.Dir(filepath.Dir(thisFile))) projectPath := filepath.Join(filepath.Join(projectDir, "target"), "machines") - places = append(places, projectPath) + dirs = append(dirs, projectPath) // Check the working directory: ./machines and ./target/machines workDir, err := os.Getwd() @@ -39,8 +38,8 @@ func NewMachineLocator(rootPath string) (*MachineLocator, error) { } workPath1 := filepath.Join(workDir, "machines") workPath2 := filepath.Join(filepath.Join(workDir, "target"), "machines") - places = append(places, workPath1) - places = append(places, workPath2) + dirs = append(dirs, workPath1) + dirs = append(dirs, workPath2) // Check above the executable: => ../../machines execfile, err := os.Executable() @@ -48,22 +47,59 @@ func NewMachineLocator(rootPath string) (*MachineLocator, error) { return nil, err } execPath := filepath.Join(filepath.Dir(filepath.Dir(execfile)), "machines") - places = append(places, execPath) + dirs = append(dirs, execPath) } - for _, place := range places { - if _, err := os.Stat(place); err == nil { - var latestModuleRoot common.Hash - latestModuleRootPath := filepath.Join(place, "latest", "module-root.txt") - fileBytes, err := os.ReadFile(latestModuleRootPath) - if err == nil { - s := strings.TrimSpace(string(fileBytes)) - latestModuleRoot = common.HexToHash(s) + var ( + moduleRoots = make(map[common.Hash]bool) + latestModuleRoot common.Hash + ) + + for _, dir := range dirs { + fInfo, err := os.Stat(dir) + if err != nil { + log.Warn("Getting file info", "error", err) + continue + } + if !fInfo.IsDir() { + // Skip files that are not directories. + continue + } + files, err := os.ReadDir(dir) + if err != nil { + log.Warn("Reading directory", "dir", dir, "error", err) + } + for _, file := range files { + mrFile := filepath.Join(dir, file.Name(), "module-root.txt") + if _, err := os.Stat(mrFile); errors.Is(err, os.ErrNotExist) { + // Skip if module-roots file does not exist. + continue + } + mrContent, err := os.ReadFile(mrFile) + if err != nil { + log.Warn("Reading module roots file", "file path", mrFile, "error", err) + continue + } + moduleRoot := common.HexToHash(strings.TrimSpace(string(mrContent))) + if file.Name() != "latest" && file.Name() != moduleRoot.Hex() { + continue + } + moduleRoots[moduleRoot] = true + if file.Name() == "latest" { + latestModuleRoot = moduleRoot + rootPath = dir } - return &MachineLocator{place, latestModuleRoot}, nil } } - return nil, ErrMachineNotFound + var roots []common.Hash + for k := range moduleRoots { + roots = append(roots, k) + } + return &MachineLocator{ + rootPath: rootPath, + latest: latestModuleRoot, + moduleRoots: roots, + }, nil } func (l MachineLocator) GetMachinePath(moduleRoot common.Hash) string { @@ -81,3 +117,7 @@ func (l MachineLocator) LatestWasmModuleRoot() common.Hash { func (l MachineLocator) RootPath() string { return l.rootPath } + +func (l MachineLocator) ModuleRoots() []common.Hash { + return l.moduleRoots +} diff --git a/validator/server_common/machine_locator_test.go b/validator/server_common/machine_locator_test.go new file mode 100644 index 0000000000..7c1575871c --- /dev/null +++ b/validator/server_common/machine_locator_test.go @@ -0,0 +1,36 @@ +package server_common + +import ( + "sort" + "testing" + + "github.com/google/go-cmp/cmp" +) + +var ( + wantLatestModuleRoot = "0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a" + wantModuleRoots = []string{ + "0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4", + "0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4", + "0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a", + } +) + +func TestNewMachineLocator(t *testing.T) { + ml, err := NewMachineLocator("testdata") + if err != nil { + t.Fatalf("Error creating new machine locator: %v", err) + } + if ml.latest.Hex() != wantLatestModuleRoot { + t.Errorf("NewMachineLocator() got latestModuleRoot: %v, want: %v", ml.latest, wantLatestModuleRoot) + } + var got []string + for _, s := range ml.ModuleRoots() { + got = append(got, s.Hex()) + } + sort.Strings(got) + sort.Strings(wantModuleRoots) + if diff := cmp.Diff(got, wantModuleRoots); diff != "" { + t.Errorf("NewMachineLocator() unexpected diff (-want +got):\n%s", diff) + } +} diff --git a/validator/server_common/testdata/0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4/module-root.txt b/validator/server_common/testdata/0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4/module-root.txt new file mode 100644 index 0000000000..067f2db9f5 --- /dev/null +++ b/validator/server_common/testdata/0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4/module-root.txt @@ -0,0 +1 @@ +0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4 diff --git a/validator/server_common/testdata/0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4/module-root.txt b/validator/server_common/testdata/0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4/module-root.txt new file mode 100644 index 0000000000..ad3a905ab7 --- /dev/null +++ b/validator/server_common/testdata/0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4/module-root.txt @@ -0,0 +1 @@ +0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4 diff --git a/validator/server_common/testdata/0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a/module-root.txt b/validator/server_common/testdata/0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a/module-root.txt new file mode 100644 index 0000000000..1a359ae1cd --- /dev/null +++ b/validator/server_common/testdata/0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a/module-root.txt @@ -0,0 +1 @@ +0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a diff --git a/validator/server_common/testdata/latest b/validator/server_common/testdata/latest new file mode 120000 index 0000000000..42d98792a0 --- /dev/null +++ b/validator/server_common/testdata/latest @@ -0,0 +1 @@ +0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a \ No newline at end of file