diff --git a/core/blockchain.go b/core/blockchain.go index a28bfacde0..70de55a541 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -838,6 +838,9 @@ func (bc *BlockChain) Stop() { for _, offset := range []uint64{0, 1, TriesInMemory - 1} { if number := bc.CurrentBlock().NumberU64(); number > offset { recent := bc.GetBlockByNumber(number - offset) + if recent.Root() == (common.Hash{}) { + continue + } log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root()) if err := triedb.Commit(recent.Root(), true, nil); err != nil { diff --git a/core/types/arb_types.go b/core/types/arb_types.go index 30f621c772..2ea4541336 100644 --- a/core/types/arb_types.go +++ b/core/types/arb_types.go @@ -422,7 +422,8 @@ func (info HeaderInfo) UpdateHeaderWithInfo(header *Header) { } func DeserializeHeaderExtraInformation(header *Header) (HeaderInfo, error) { - if header.Number.Sign() == 0 || len(header.Extra) == 0 { + if header.BaseFee == nil || header.BaseFee.Sign() == 0 || len(header.Extra) == 0 { + // imported blocks have no base fee // The genesis block doesn't have an ArbOS encoded extra field return HeaderInfo{}, nil } diff --git a/core/types/arbitrum_legacy_tx.go b/core/types/arbitrum_legacy_tx.go index f498a18f92..7a71d688b8 100644 --- a/core/types/arbitrum_legacy_tx.go +++ b/core/types/arbitrum_legacy_tx.go @@ -1,126 +1,46 @@ package types import ( - "math/big" + "bytes" + "errors" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rlp" ) -// Data as received from Arb1 chain -type ArbitrumLegacyTransactionResult struct { - BlockHash *common.Hash `json:"blockHash"` - BlockNumber *hexutil.Big `json:"blockNumber"` - From common.Address `json:"from"` - Gas hexutil.Uint64 `json:"gas"` - GasPrice *hexutil.Big `json:"gasPrice"` - Hash common.Hash `json:"hash"` - Input hexutil.Bytes `json:"input"` - Nonce hexutil.Uint64 `json:"nonce"` - To *common.Address `json:"to"` - TransactionIndex *hexutil.Uint64 `json:"transactionIndex"` - Value *hexutil.Big `json:"value"` - V *hexutil.Big `json:"v"` - R *hexutil.Big `json:"r"` - S *hexutil.Big `json:"s"` - - // Arbitrum Specific Fields - L1SeqNum *hexutil.Big `json:"l1SequenceNumber"` - ParentRequestId *common.Hash `json:"parentRequestId"` - IndexInParent *hexutil.Big `json:"indexInParent"` - ArbType hexutil.Uint64 `json:"arbType"` - ArbSubType *hexutil.Uint64 `json:"arbSubType"` - L1BlockNumber *hexutil.Big `json:"l1BlockNumber"` -} - type ArbitrumLegacyTxData struct { - Gas uint64 - GasPrice *big.Int - Hash common.Hash // Hash cannot be locally computed from other fields - Data []byte - Nonce uint64 - To *common.Address `rlp:"nil"` // nil means contract creation - Value *big.Int - V, R, S *big.Int + LegacyTx + HashOverride common.Hash // Hash cannot be locally computed from other fields + EffectiveGasPrice uint64 + L1BlockNumber uint64 } -func (tx *ArbitrumLegacyTxData) copy() TxData { - cpy := &ArbitrumLegacyTxData{ - Nonce: tx.Nonce, - To: copyAddressPtr(tx.To), - Data: common.CopyBytes(tx.Data), - Gas: tx.Gas, - Hash: tx.Hash, - // These are initialized below. - Value: new(big.Int), - GasPrice: new(big.Int), - V: new(big.Int), - R: new(big.Int), - S: new(big.Int), - } - if tx.Value != nil { - cpy.Value.Set(tx.Value) - } - if tx.GasPrice != nil { - cpy.GasPrice.Set(tx.GasPrice) +func NewArbitrumLegacyTx(origTx *Transaction, hashOverride common.Hash, effectiveGas uint64, l1Block uint64) (*Transaction, error) { + if origTx.Type() != LegacyTxType { + return nil, errors.New("attempt to arbitrum-wrap non-legacy transaction") } - if tx.V != nil { - cpy.V.Set(tx.V) + legacyPtr := origTx.GetInner().(*LegacyTx) + inner := ArbitrumLegacyTxData{ + LegacyTx: *legacyPtr, + HashOverride: hashOverride, + EffectiveGasPrice: effectiveGas, + L1BlockNumber: l1Block, } - if tx.R != nil { - cpy.R.Set(tx.R) - } - if tx.S != nil { - cpy.S.Set(tx.S) - } - return cpy + return NewTx(&inner), nil } -func ArbitrumLegacyFromTransactionResult(result ArbitrumLegacyTransactionResult) *Transaction { - gas := uint64(result.Gas) - nonce := uint64(result.Nonce) - gasPrice := (*big.Int)(result.GasPrice) - value := (*big.Int)(result.Value) - v := (*big.Int)(result.V) - r := (*big.Int)(result.R) - s := (*big.Int)(result.S) - hash := common.Hash(result.Hash) - to := copyAddressPtr(result.To) - var data []byte = result.Input - arblegacy := ArbitrumLegacyTxData{ - Gas: gas, - GasPrice: gasPrice, - Hash: hash, - Data: data, - Nonce: nonce, - To: to, - Value: value, - V: v, - R: r, - S: s, +func (tx *ArbitrumLegacyTxData) copy() TxData { + legacyCopy := tx.LegacyTx.copy().(*LegacyTx) + return &ArbitrumLegacyTxData{ + LegacyTx: *legacyCopy, + HashOverride: tx.HashOverride, + EffectiveGasPrice: tx.EffectiveGasPrice, + L1BlockNumber: tx.L1BlockNumber, } - return NewTx(&arblegacy) } -// accessors for innerTx. -func (tx *ArbitrumLegacyTxData) txType() byte { return ArbitrumLegacyTxType } -func (tx *ArbitrumLegacyTxData) chainID() *big.Int { return deriveChainId(tx.V) } -func (tx *ArbitrumLegacyTxData) accessList() AccessList { return nil } -func (tx *ArbitrumLegacyTxData) data() []byte { return tx.Data } -func (tx *ArbitrumLegacyTxData) gas() uint64 { return tx.Gas } -func (tx *ArbitrumLegacyTxData) gasPrice() *big.Int { return tx.GasPrice } -func (tx *ArbitrumLegacyTxData) gasTipCap() *big.Int { return tx.GasPrice } -func (tx *ArbitrumLegacyTxData) gasFeeCap() *big.Int { return tx.GasPrice } -func (tx *ArbitrumLegacyTxData) value() *big.Int { return tx.Value } -func (tx *ArbitrumLegacyTxData) nonce() uint64 { return tx.Nonce } -func (tx *ArbitrumLegacyTxData) to() *common.Address { return tx.To } - -func (tx *ArbitrumLegacyTxData) isFake() bool { return false } - -func (tx *ArbitrumLegacyTxData) rawSignatureValues() (v, r, s *big.Int) { - return tx.V, tx.R, tx.S -} +func (tx *ArbitrumLegacyTxData) txType() byte { return ArbitrumLegacyTxType } -func (tx *ArbitrumLegacyTxData) setSignatureValues(chainID, v, r, s *big.Int) { - tx.V, tx.R, tx.S = v, r, s +func (tx *ArbitrumLegacyTxData) EncodeOnlyLegacyInto(w *bytes.Buffer) { + rlp.Encode(w, tx.LegacyTx) } diff --git a/core/types/arbitrum_signer.go b/core/types/arbitrum_signer.go index bf5027a4c7..e329f5ae54 100644 --- a/core/types/arbitrum_signer.go +++ b/core/types/arbitrum_signer.go @@ -32,6 +32,10 @@ func (s arbitrumSigner) Sender(tx *Transaction) (common.Address, error) { return inner.From, nil case *ArbitrumSubmitRetryableTx: return inner.From, nil + case *ArbitrumLegacyTxData: + legacyData := tx.inner.(*ArbitrumLegacyTxData) + fakeTx := NewTx(&legacyData.LegacyTx) + return s.Signer.Sender(fakeTx) default: return s.Signer.Sender(tx) } @@ -56,6 +60,10 @@ func (s arbitrumSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *b return bigZero, bigZero, bigZero, nil case *ArbitrumSubmitRetryableTx: return bigZero, bigZero, bigZero, nil + case *ArbitrumLegacyTxData: + legacyData := tx.inner.(*ArbitrumLegacyTxData) + fakeTx := NewTx(&legacyData.LegacyTx) + return s.Signer.SignatureValues(fakeTx, sig) default: return s.Signer.SignatureValues(tx, sig) } @@ -64,5 +72,9 @@ func (s arbitrumSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *b // Hash returns the hash to be signed by the sender. // It does not uniquely identify the transaction. func (s arbitrumSigner) Hash(tx *Transaction) common.Hash { + if legacyData, isArbLegacy := tx.inner.(*ArbitrumLegacyTxData); isArbLegacy { + fakeTx := NewTx(&legacyData.LegacyTx) + return s.Signer.Hash(fakeTx) + } return s.Signer.Hash(tx) } diff --git a/core/types/receipt.go b/core/types/receipt.go index f87aeccd30..3b58034161 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -36,6 +36,7 @@ import ( var ( receiptStatusFailedRLP = []byte{} receiptStatusSuccessfulRLP = []byte{0x01} + receiptRootArbitrumLegacy = []byte{0x00} ) var errShortTypedReceipt = errors.New("typed receipt too short") @@ -52,7 +53,7 @@ const ( type Receipt struct { // Arbitrum Implementation fields GasUsedForL1 uint64 `json:"gasUsedForL1"` - + // Consensus fields: These fields are defined by the Yellow Paper Type uint8 `json:"type,omitempty"` PostState []byte `json:"root"` @@ -103,6 +104,16 @@ type storedReceiptRLP struct { Logs []*LogForStorage } +type arbLegacyStoredReceiptRLP struct { + PostStateOrStatus []byte + CumulativeGasUsed uint64 + GasUsed uint64 + L1GasUsed uint64 + Status uint64 + ContractAddress common.Address + Logs []*LogForStorage +} + // v4StoredReceiptRLP is the storage encoding of a receipt used in database version 4. type v4StoredReceiptRLP struct { PostStateOrStatus []byte @@ -283,9 +294,18 @@ type ReceiptForStorage Receipt func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error { w := rlp.NewEncoderBuffer(_w) outerList := w.List() - w.WriteBytes((*Receipt)(r).statusEncoding()) - w.WriteUint64(r.CumulativeGasUsed) - w.WriteUint64(r.GasUsedForL1) + if r.Type == ArbitrumLegacyTxType { + w.WriteBytes(receiptRootArbitrumLegacy) + w.WriteUint64(r.CumulativeGasUsed) + w.WriteUint64(r.GasUsed) + w.WriteUint64(r.GasUsedForL1) + w.WriteUint64(r.Status) + rlp.Encode(w, r.ContractAddress) + } else { + w.WriteBytes((*Receipt)(r).statusEncoding()) + w.WriteUint64(r.CumulativeGasUsed) + w.WriteUint64(r.GasUsedForL1) + } logList := w.List() for _, log := range r.Logs { if err := rlp.Encode(w, log); err != nil { @@ -311,12 +331,39 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { if err := decodeStoredReceiptRLP(r, blob); err == nil { return nil } + if err := decodeArbitrumLegacyStoredReceiptRLP(r, blob); err == nil { + return nil + } if err := decodeV3StoredReceiptRLP(r, blob); err == nil { return nil } return decodeV4StoredReceiptRLP(r, blob) } +func decodeArbitrumLegacyStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { + var stored arbLegacyStoredReceiptRLP + if err := rlp.DecodeBytes(blob, &stored); err != nil { + return err + } + if !bytes.Equal(stored.PostStateOrStatus, receiptRootArbitrumLegacy) { + return errors.New("not arbitrum legacy Tx") + } + r.Type = ArbitrumLegacyTxType + (*Receipt)(r).PostState = receiptRootArbitrumLegacy + r.Status = stored.Status + r.CumulativeGasUsed = stored.CumulativeGasUsed + r.GasUsed = stored.GasUsed + r.GasUsedForL1 = stored.L1GasUsed + r.ContractAddress = stored.ContractAddress + r.Logs = make([]*Log, len(stored.Logs)) + for i, log := range stored.Logs { + r.Logs[i] = (*Log)(log) + } + r.Bloom = CreateBloom(Receipts{(*Receipt)(r)}) + + return nil +} + func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { var stored storedReceiptRLP if err := rlp.DecodeBytes(blob, &stored); err != nil { @@ -388,7 +435,7 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) { r := rs[i] data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs} switch r.Type { - case LegacyTxType: + case LegacyTxType, ArbitrumLegacyTxType: rlp.Encode(w, data) default: w.WriteByte(r.Type) @@ -415,17 +462,19 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu rs[i].BlockNumber = new(big.Int).SetUint64(number) rs[i].TransactionIndex = uint(i) - // The contract address can be derived from the transaction itself - if txs[i].To() == nil { - // Deriving the signer is expensive, only do if it's actually needed - from, _ := Sender(signer, txs[i]) - rs[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce()) - } - // The used gas can be calculated based on previous r - if i == 0 { - rs[i].GasUsed = rs[i].CumulativeGasUsed - } else { - rs[i].GasUsed = rs[i].CumulativeGasUsed - rs[i-1].CumulativeGasUsed + if rs[i].Type != ArbitrumLegacyTxType { + // The contract address can be derived from the transaction itself + if txs[i].To() == nil { + // Deriving the signer is expensive, only do if it's actually needed + from, _ := Sender(signer, txs[i]) + rs[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce()) + } + // The used gas can be calculated based on previous r + if i == 0 { + rs[i].GasUsed = rs[i].CumulativeGasUsed + } else { + rs[i].GasUsed = rs[i].CumulativeGasUsed - rs[i-1].CumulativeGasUsed + } } // The derived log fields can simply be set from the block and transaction for j := 0; j < len(rs[i].Logs); j++ { diff --git a/core/types/transaction.go b/core/types/transaction.go index 9a989cce91..23ce4f7d36 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -415,7 +415,7 @@ func (tx *Transaction) Hash() common.Hash { if tx.Type() == LegacyTxType { h = rlpHash(tx.inner) } else if tx.Type() == ArbitrumLegacyTxType { - h = tx.inner.(*ArbitrumLegacyTxData).Hash + h = tx.inner.(*ArbitrumLegacyTxData).HashOverride } else { h = prefixedRlpHash(tx.Type(), tx.inner) } @@ -460,6 +460,9 @@ func (s Transactions) EncodeIndex(i int, w *bytes.Buffer) { tx := s[i] if tx.Type() == LegacyTxType { rlp.Encode(w, tx.inner) + } else if tx.Type() == ArbitrumLegacyTxType { + arbData := tx.inner.(*ArbitrumLegacyTxData) + arbData.EncodeOnlyLegacyInto(w) } else { tx.encodeTyped(w) } diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go index 262d2d84b8..d24f338861 100644 --- a/core/types/transaction_marshalling.go +++ b/core/types/transaction_marshalling.go @@ -60,8 +60,10 @@ type txJSON struct { RetryData *hexutil.Bytes `json:"retryData,omitempty"` // SubmitRetryable Beneficiary *common.Address `json:"beneficiary,omitempty"` // SubmitRetryable MaxSubmissionFee *hexutil.Big `json:"maxSubmissionFee,omitempty"` // SubmitRetryable + EffectiveGasPrice *hexutil.Uint64 `json:"effectiveGasPrice,omitempty"` // ArbLegacy + L1BlockNumber *hexutil.Uint64 `json:"l1BlockNumber,omitempty"` // ArbLegacy - // Only used for encoding: + // Only used for encoding - and for ArbLegacy Hash common.Hash `json:"hash"` } @@ -130,6 +132,8 @@ func (t *Transaction) MarshalJSON() ([]byte, error) { enc.V = (*hexutil.Big)(tx.V) enc.R = (*hexutil.Big)(tx.R) enc.S = (*hexutil.Big)(tx.S) + enc.EffectiveGasPrice = (*hexutil.Uint64)(&tx.EffectiveGasPrice) + enc.L1BlockNumber = (*hexutil.Uint64)(&tx.L1BlockNumber) case *ArbitrumInternalTx: enc.ChainID = (*hexutil.Big)(tx.ChainId) enc.Data = (*hexutil.Bytes)(&tx.Data) @@ -356,41 +360,59 @@ func (t *Transaction) UnmarshalJSON(input []byte) error { } case ArbitrumLegacyTxType: - if dec.Gas == nil { - return errors.New("missing required field 'gas' in txdata") - } - if dec.GasPrice == nil { - return errors.New("missing required field 'gasPrice' in txdata") - } - if dec.Data == nil { - return errors.New("missing required field 'input' in transaction") + var itx LegacyTx + if dec.To != nil { + itx.To = dec.To } if dec.Nonce == nil { return errors.New("missing required field 'nonce' in transaction") } + itx.Nonce = uint64(*dec.Nonce) + if dec.GasPrice == nil { + return errors.New("missing required field 'gasPrice' in transaction") + } + itx.GasPrice = (*big.Int)(dec.GasPrice) + if dec.Gas == nil { + return errors.New("missing required field 'gas' in transaction") + } + itx.Gas = uint64(*dec.Gas) if dec.Value == nil { return errors.New("missing required field 'value' in transaction") } + itx.Value = (*big.Int)(dec.Value) + if dec.Data == nil { + return errors.New("missing required field 'input' in transaction") + } + itx.Data = *dec.Data if dec.V == nil { return errors.New("missing required field 'v' in transaction") } + itx.V = (*big.Int)(dec.V) if dec.R == nil { return errors.New("missing required field 'r' in transaction") } + itx.R = (*big.Int)(dec.R) if dec.S == nil { return errors.New("missing required field 's' in transaction") } + itx.S = (*big.Int)(dec.S) + withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 + if withSignature { + if err := sanityCheckSignature(itx.V, itx.R, itx.S, true); err != nil { + return err + } + } + if dec.EffectiveGasPrice == nil { + return errors.New("missing required field 'EffectiveGasPrice' in transaction") + } + if dec.L1BlockNumber == nil { + return errors.New("missing required field 'L1BlockNumber' in transaction") + } inner = &ArbitrumLegacyTxData{ - Gas: uint64(*dec.Gas), - GasPrice: (*big.Int)(dec.GasPrice), - Hash: dec.Hash, - Data: *dec.Data, - Nonce: uint64(*dec.Nonce), - To: dec.To, - Value: (*big.Int)(dec.Value), - V: (*big.Int)(dec.V), - R: (*big.Int)(dec.R), - S: (*big.Int)(dec.S), + LegacyTx: itx, + HashOverride: dec.Hash, + EffectiveGasPrice: uint64(*dec.EffectiveGasPrice), + L1BlockNumber: uint64(*dec.L1BlockNumber), } case ArbitrumInternalTxType: diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 41da9a3084..6abbed2903 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1289,14 +1289,14 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param } fields["uncles"] = uncleHashes - if config.IsArbitrum() { - fillArbitrumHeaderInfo(block.Header(), fields) + if config.IsArbitrumNitro(block.Header().Number) { + fillArbitrumNitroHeaderInfo(block.Header(), fields) } return fields, nil } -func fillArbitrumHeaderInfo(header *types.Header, fields map[string]interface{}) { +func fillArbitrumNitroHeaderInfo(header *types.Header, fields map[string]interface{}) { info, err := types.DeserializeHeaderExtraInformation(header) if err != nil { log.Error("Expected header to contain arbitrum data", "blockHash", header.Hash()) @@ -1312,8 +1312,8 @@ func fillArbitrumHeaderInfo(header *types.Header, fields map[string]interface{}) func (s *PublicBlockChainAPI) rpcMarshalHeader(ctx context.Context, header *types.Header) map[string]interface{} { fields := RPCMarshalHeader(header) fields["totalDifficulty"] = (*hexutil.Big)(s.b.GetTd(ctx, header.Hash())) - if s.b.ChainConfig().IsArbitrum() { - fillArbitrumHeaderInfo(header, fields) + if s.b.ChainConfig().IsArbitrumNitro(header.Number) { + fillArbitrumNitroHeaderInfo(header, fields) } return fields } @@ -1758,7 +1758,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha fields["effectiveGasPrice"] = hexutil.Uint64(gasPrice.Uint64()) } // Assign receipt status or post state. - if len(receipt.PostState) > 0 { + if len(receipt.PostState) > 0 && tx.Type() != types.ArbitrumLegacyTxType { fields["root"] = hexutil.Bytes(receipt.PostState) } else { fields["status"] = hexutil.Uint(receipt.Status) @@ -1777,12 +1777,23 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha if err != nil { return nil, err } - fields["effectiveGasPrice"] = hexutil.Uint64(header.BaseFee.Uint64()) - info, err := types.DeserializeHeaderExtraInformation(header) - if err != nil { - log.Error("Expected header to contain arbitrum data", "blockHash", blockHash) + if s.b.ChainConfig().IsArbitrumNitro(header.Number) { + fields["effectiveGasPrice"] = hexutil.Uint64(header.BaseFee.Uint64()) + info, err := types.DeserializeHeaderExtraInformation(header) + if err != nil { + log.Error("Expected header to contain arbitrum data", "blockHash", blockHash) + } else { + fields["l1BlockNumber"] = hexutil.Uint64(info.L1BlockNumber) + } } else { - fields["l1BlockNumber"] = hexutil.Uint64(info.L1BlockNumber) + inner := tx.GetInner() + arbTx, ok := inner.(*types.ArbitrumLegacyTxData) + if !ok { + log.Error("Expected transaction to contain arbitrum data", "txHash", tx.Hash()) + } else { + fields["effectiveGasPrice"] = hexutil.Uint64(arbTx.EffectiveGasPrice) + fields["l1BlockNumber"] = hexutil.Uint64(arbTx.L1BlockNumber) + } } } return fields, nil diff --git a/params/config.go b/params/config.go index 38d9274ad9..1dcfd5212c 100644 --- a/params/config.go +++ b/params/config.go @@ -520,6 +520,9 @@ func (c *ChainConfig) IsBerlin(num *big.Int) bool { // IsLondon returns whether num is either equal to the London fork block or greater. func (c *ChainConfig) IsLondon(num *big.Int) bool { + if c.IsArbitrum() { + return isForked(new(big.Int).SetUint64(c.ArbitrumChainParams.GenesisBlockNum), num) + } return isForked(c.LondonBlock, num) } diff --git a/params/config_arbitrum.go b/params/config_arbitrum.go index bd1bd894b7..4a1ca9ed29 100644 --- a/params/config_arbitrum.go +++ b/params/config_arbitrum.go @@ -35,6 +35,10 @@ func (c *ChainConfig) IsArbitrum() bool { return c.ArbitrumChainParams.EnableArbOS } +func (c *ChainConfig) IsArbitrumNitro(num *big.Int) bool { + return c.IsArbitrum() && isForked(new(big.Int).SetUint64(c.ArbitrumChainParams.GenesisBlockNum), num) +} + func (c *ChainConfig) DebugMode() bool { return c.ArbitrumChainParams.AllowDebugPrecompiles } @@ -92,6 +96,17 @@ func ArbitrumRollupGoerliTestnetParams() ArbitrumChainParams { } } +func ArbitrumRinkebyTestParams() ArbitrumChainParams { + return ArbitrumChainParams{ + EnableArbOS: true, + AllowDebugPrecompiles: false, + DataAvailabilityCommittee: false, + InitialArbOSVersion: 3, + // Not used - has init data + InitialChainOwner: common.Address{}, + } +} + func ArbitrumDevTestParams() ArbitrumChainParams { return ArbitrumChainParams{ EnableArbOS: true, @@ -282,6 +297,31 @@ func ArbitrumAnytrustGoerliTestnetChainConfig() *ChainConfig { } } +func ArbitrumRinkebyTestnetChainConfig() *ChainConfig { + return &ChainConfig{ + ChainID: big.NewInt(421611), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: nil, + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.Hash{}, + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArbitrumChainParams: ArbitrumRinkebyTestParams(), + Clique: &CliqueConfig{ + Period: 0, + Epoch: 0, + }, + } +} + var ArbitrumSupportedChainConfigs = []*ChainConfig{ ArbitrumOneChainConfig(), ArbitrumNovaChainConfig(), @@ -289,4 +329,5 @@ var ArbitrumSupportedChainConfigs = []*ChainConfig{ ArbitrumDevTestChainConfig(), ArbitrumDevTestDASChainConfig(), ArbitrumAnytrustGoerliTestnetChainConfig(), + ArbitrumRinkebyTestnetChainConfig(), } diff --git a/trie/database.go b/trie/database.go index 4167031a58..6cd41e0c80 100644 --- a/trie/database.go +++ b/trie/database.go @@ -685,6 +685,11 @@ func (db *Database) Cap(limit common.StorageSize) error { // Note, this method is a non-synchronized mutator. It is unsafe to call this // concurrently with other mutators. func (db *Database) Commit(node common.Hash, report bool, callback func(common.Hash)) error { + if node == (common.Hash{}) { + // There's no data to commit in this node + return nil + } + // Create a database batch to flush persistent data out. It is important that // outside code doesn't see an inconsistent state (referenced data removed from // memory cache during commit but not yet in persistent storage). This is ensured