From ca8bbd6cf5ffc8fe89e527cb606f5508e1cb4581 Mon Sep 17 00:00:00 2001 From: Mark Tyneway Date: Tue, 29 Nov 2022 18:03:16 -0800 Subject: [PATCH] core/types: add legacy receipt deserialization (#28) Receipts in `l2geth` are stored with L1 fee data to prevent the need of having an archive node for historical L1 fee data. https://github.com/ethereum-optimism/optimism/blob/91a80d287237032a47d68d8809b1dc67ad15293f/l2geth/core/types/receipt.go#L247 This ports the deserialization of the legacy `l2geth` style serialization to `op-geth`. This is required for nodes that upgrade using the db migration tool. --- core/types/receipt.go | 111 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/core/types/receipt.go b/core/types/receipt.go index b37bb6f2c7..d651dc6142 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -108,6 +108,72 @@ type storedReceiptRLP struct { Logs []*Log } +// LegacyOptimismStoredReceiptRLP is the pre bedrock storage encoding of a +// receipt. It will only exist in the database if it was migrated using the +// migration tool. Nodes that sync using snap-sync will not have any of these +// entries. +type LegacyOptimismStoredReceiptRLP struct { + PostStateOrStatus []byte + CumulativeGasUsed uint64 + Logs []*LogForStorage + L1GasUsed *big.Int + L1GasPrice *big.Int + L1Fee *big.Int + FeeScalar string +} + +// LogForStorage is a wrapper around a Log that handles +// backward compatibility with prior storage formats. +type LogForStorage Log + +// EncodeRLP implements rlp.Encoder. +func (l *LogForStorage) EncodeRLP(w io.Writer) error { + rl := rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data} + return rlp.Encode(w, &rl) +} + +type legacyRlpStorageLog struct { + Address common.Address + Topics []common.Hash + Data []byte + BlockNumber uint64 + TxHash common.Hash + TxIndex uint + BlockHash common.Hash + Index uint +} + +// DecodeRLP implements rlp.Decoder. +// +// Note some redundant fields(e.g. block number, tx hash etc) will be assembled later. +func (l *LogForStorage) DecodeRLP(s *rlp.Stream) error { + blob, err := s.Raw() + if err != nil { + return err + } + var dec rlpLog + err = rlp.DecodeBytes(blob, &dec) + if err == nil { + *l = LogForStorage{ + Address: dec.Address, + Topics: dec.Topics, + Data: dec.Data, + } + } else { + // Try to decode log with previous definition. + var dec legacyRlpStorageLog + err = rlp.DecodeBytes(blob, &dec) + if err == nil { + *l = LogForStorage{ + Address: dec.Address, + Topics: dec.Topics, + Data: dec.Data, + } + } + } + return err +} + // NewReceipt creates a barebone transaction receipt, copying the init fields. // Deprecated: create receipts using a struct literal instead. func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt { @@ -283,8 +349,51 @@ func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error { // DecodeRLP implements rlp.Decoder, and loads both consensus and implementation // fields of a receipt from an RLP stream. func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { + // Retrieve the entire receipt blob as we need to try multiple decoders + blob, err := s.Raw() + if err != nil { + return err + } + // First try to decode the latest receipt database format, try the pre-bedrock Optimism legacy format otherwise. + if err := decodeStoredReceiptRLP(r, blob); err == nil { + return nil + } + return decodeLegacyOptimismReceiptRLP(r, blob) +} + +func decodeLegacyOptimismReceiptRLP(r *ReceiptForStorage, blob []byte) error { + var stored LegacyOptimismStoredReceiptRLP + if err := rlp.DecodeBytes(blob, &stored); err != nil { + return err + } + if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil { + return err + } + r.CumulativeGasUsed = stored.CumulativeGasUsed + r.Logs = make([]*Log, len(stored.Logs)) + for i, log := range stored.Logs { + r.Logs[i] = (*Log)(log) + } + r.Bloom = CreateBloom(Receipts{(*Receipt)(r)}) + // UsingOVM + scalar := new(big.Float) + if stored.FeeScalar != "" { + var ok bool + scalar, ok = scalar.SetString(stored.FeeScalar) + if !ok { + return errors.New("cannot parse fee scalar") + } + } + r.L1GasUsed = stored.L1GasUsed + r.L1GasPrice = stored.L1GasPrice + r.L1Fee = stored.L1Fee + r.FeeScalar = scalar + return nil +} + +func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { var stored storedReceiptRLP - if err := s.Decode(&stored); err != nil { + if err := rlp.DecodeBytes(blob, &stored); err != nil { return err } if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil {