Skip to content

Commit

Permalink
post-Canyon receipt-root deposit tx hashing fix
Browse files Browse the repository at this point in the history
  • Loading branch information
roberto-bayardo committed Oct 11, 2023
1 parent 1296a2c commit 0329e31
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 38 deletions.
38 changes: 22 additions & 16 deletions core/rawdb/accessors_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -705,23 +705,30 @@ func TestReadLogs(t *testing.T) {

body := &types.Body{Transactions: types.Transactions{tx1, tx2}}

// Create the two receipts to manage afterwards
// Create the three receipts to manage afterwards
depositNonce := uint64(math.MaxUint64)
receipt1 := &types.Receipt{
depositReceipt := types.Receipt{
Status: types.ReceiptStatusFailed,
CumulativeGasUsed: 1,
Logs: []*types.Log{
{Address: common.BytesToAddress([]byte{0x11})},
{Address: common.BytesToAddress([]byte{0x01, 0x11})},
},
TxHash: tx1.Hash(),
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
GasUsed: 111111,
DepositNonce: &depositNonce,
TxHash: tx1.Hash(),
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
GasUsed: 111111,
DepositNonce: &depositNonce,
DepositReceiptVersion: nil,
}
receipt1.Bloom = types.CreateBloom(types.Receipts{receipt1})
depositReceipt.Bloom = types.CreateBloom(types.Receipts{&depositReceipt})

receipt2 := &types.Receipt{
// versionedDepositReceipt is same as depositReceipt only it has the Canyon DepositReceiptVersion
versionedDepositReceipt := depositReceipt
receiptVersion := types.CanyonDepositReceiptVersion
versionedDepositReceipt.DepositReceiptVersion = &receiptVersion
versionedDepositReceipt.Bloom = types.CreateBloom(types.Receipts{&versionedDepositReceipt})

receipt := types.Receipt{
PostState: common.Hash{2}.Bytes(),
CumulativeGasUsed: 2,
Logs: []*types.Log{
Expand All @@ -732,8 +739,8 @@ func TestReadLogs(t *testing.T) {
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
GasUsed: 222222,
}
receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2})
receipts := []*types.Receipt{receipt1, receipt2}
receipt.Bloom = types.CreateBloom(types.Receipts{&receipt})
receipts := []*types.Receipt{&receipt, &depositReceipt, &versionedDepositReceipt}

hash := common.BytesToHash([]byte{0x03, 0x14})
// Check that no receipt entries are in a pristine database
Expand All @@ -750,14 +757,13 @@ func TestReadLogs(t *testing.T) {
if len(logs) == 0 {
t.Fatalf("no logs returned")
}
if have, want := len(logs), 2; have != want {
if have, want := len(logs), 3; have != want {
t.Fatalf("unexpected number of logs returned, have %d want %d", have, want)
}
if have, want := len(logs[0]), 2; have != want {
t.Fatalf("unexpected number of logs[0] returned, have %d want %d", have, want)
}
if have, want := len(logs[1]), 2; have != want {
t.Fatalf("unexpected number of logs[1] returned, have %d want %d", have, want)
for i := range logs {
if have, want := len(logs[i]), 2; have != want {
t.Fatalf("unexpected number of logs[%d] returned, have %d want %d", i, have, want)
}
}

for i, pr := range receipts {
Expand Down
10 changes: 8 additions & 2 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,15 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta
receipt.GasUsed = result.UsedGas

if msg.IsDepositTx && config.IsOptimismRegolith(evm.Context.Time) {
// The actual nonce for deposit transactions is only recorded from Regolith onwards.
// Before the Regolith fork the DepositNonce must remain nil
// The actual nonce for deposit transactions is only recorded from Regolith onwards and
// otherwise must be nil.
receipt.DepositNonce = &nonce
// The DepositReceiptVersion for deposit transactions is only recorded from Canyon onwards
// and otherwise must be nil.
if config.IsOptimismCanyon(evm.Context.Time) {
receipt.DepositReceiptVersion = new(uint64)
*receipt.DepositReceiptVersion = types.CanyonDepositReceiptVersion
}
}
if tx.Type() == types.BlobTxType {
receipt.BlobGasUsed = uint64(len(tx.BlobHashes()) * params.BlobTxBlobGasPerBlob)
Expand Down
41 changes: 36 additions & 5 deletions core/types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ const (

// ReceiptStatusSuccessful is the status code of a transaction if execution succeeded.
ReceiptStatusSuccessful = uint64(1)

// The version number for post-canyon deposit receipts.
CanyonDepositReceiptVersion = uint64(1)
)

// Receipt represents the results of a transaction.
Expand All @@ -70,6 +73,10 @@ type Receipt struct {
// DepositNonce was introduced in Regolith to store the actual nonce used by deposit transactions
// The state transition process ensures this is only set for Regolith deposit transactions.
DepositNonce *uint64 `json:"depositNonce,omitempty"`
// DepositReceiptVersion was introduced in Canyon to indicate an update to how receipt hashes
// should be computed when set. The state transition process ensures this is only set for
// post-Canyon deposit transactions.
DepositReceiptVersion *uint64 `json:"depositReceiptVersion,omitempty"`

// Inclusion information: These fields provide information about the inclusion of the
// transaction corresponding to this receipt.
Expand Down Expand Up @@ -111,14 +118,18 @@ type receiptRLP struct {
Logs []*Log
}

type depositReceiptRlp struct {
type depositReceiptRLP struct {
PostStateOrStatus []byte
CumulativeGasUsed uint64
Bloom Bloom
Logs []*Log
// DepositNonce was introduced in Regolith to store the actual nonce used by deposit transactions.
// Must be nil for any transactions prior to Regolith or that aren't deposit transactions.
DepositNonce *uint64 `rlp:"optional"`
// Receipt hash post-Regolith but pre-Canyon inadvertently did not include the above
// DepositNonce. Post Canyon, receipts will have a non-empty DepositReceiptVersion indicating
// which post-Canyon receipt hash function to invoke.
DepositReceiptVersion *uint64 `rlp:"optional"`
}

// storedReceiptRLP is the storage encoding of a receipt.
Expand All @@ -129,6 +140,10 @@ type storedReceiptRLP struct {
// DepositNonce was introduced in Regolith to store the actual nonce used by deposit transactions.
// Must be nil for any transactions prior to Regolith or that aren't deposit transactions.
DepositNonce *uint64 `rlp:"optional"`
// Receipt hash post-Regolith but pre-Canyon inadvertently did not include the above
// DepositNonce. Post Canyon, receipts will have a non-empty DepositReceiptVersion indicating
// which post-Canyon receipt hash function to invoke.
DepositReceiptVersion *uint64 `rlp:"optional"`
}

// LegacyOptimismStoredReceiptRLP is the pre bedrock storage encoding of a
Expand Down Expand Up @@ -234,7 +249,7 @@ func (r *Receipt) encodeTyped(data *receiptRLP, w *bytes.Buffer) error {
w.WriteByte(r.Type)
switch r.Type {
case DepositTxType:
withNonce := depositReceiptRlp{data.PostStateOrStatus, data.CumulativeGasUsed, data.Bloom, data.Logs, r.DepositNonce}
withNonce := &depositReceiptRLP{data.PostStateOrStatus, data.CumulativeGasUsed, data.Bloom, data.Logs, r.DepositNonce, r.DepositReceiptVersion}
return rlp.Encode(w, withNonce)
default:
return rlp.Encode(w, data)
Expand Down Expand Up @@ -315,13 +330,14 @@ func (r *Receipt) decodeTyped(b []byte) error {
r.Type = b[0]
return r.setFromRLP(data)
case DepositTxType:
var data depositReceiptRlp
var data depositReceiptRLP
err := rlp.DecodeBytes(b[1:], &data)
if err != nil {
return err
}
r.Type = b[0]
r.DepositNonce = data.DepositNonce
r.DepositReceiptVersion = data.DepositReceiptVersion
return r.setFromRLP(receiptRLP{data.PostStateOrStatus, data.CumulativeGasUsed, data.Bloom, data.Logs})
default:
return ErrTxTypeNotSupported
Expand Down Expand Up @@ -388,6 +404,9 @@ func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error {
w.ListEnd(logList)
if r.DepositNonce != nil {
w.WriteUint64(*r.DepositNonce)
if r.DepositReceiptVersion != nil {
w.WriteUint64(*r.DepositReceiptVersion)
}
}
w.ListEnd(outerList)
return w.Flush()
Expand Down Expand Up @@ -451,6 +470,7 @@ func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
if stored.DepositNonce != nil {
r.DepositNonce = stored.DepositNonce
r.DepositReceiptVersion = stored.DepositReceiptVersion
}
return nil
}
Expand All @@ -461,7 +481,10 @@ type Receipts []*Receipt
// Len returns the number of receipts in this list.
func (rs Receipts) Len() int { return len(rs) }

// EncodeIndex encodes the i'th receipt to w.
// EncodeIndex encodes the i'th receipt to w. For DepositTxType receipts with non-nil DepositNonce
// but nil DepositReceiptVersion, the output will differ than calling r.MarshalBinary(); this
// behavior difference should not be changed to preserve backwards compatibility of receipt-root
// hash computation.
func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
r := rs[i]
data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}
Expand All @@ -471,8 +494,16 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
}
w.WriteByte(r.Type)
switch r.Type {
case AccessListTxType, DynamicFeeTxType, BlobTxType, DepositTxType:
case AccessListTxType, DynamicFeeTxType, BlobTxType:
rlp.Encode(w, data)
case DepositTxType:
if r.DepositReceiptVersion != nil {
// post-canyon receipt hash computation update
depositData := &depositReceiptRLP{data.PostStateOrStatus, data.CumulativeGasUsed, r.Bloom, r.Logs, r.DepositNonce, r.DepositReceiptVersion}
rlp.Encode(w, depositData)
} else {
rlp.Encode(w, data)
}
default:
// For unsupported types, write nothing. Since this is for
// DeriveSha, the error will be caught matching the derived hash
Expand Down
Loading

0 comments on commit 0329e31

Please sign in to comment.