Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

post-Canyon receipt-root deposit tx hashing fix #152

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 39 additions & 22 deletions core/rawdb/accessors_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -702,38 +702,56 @@ func TestReadLogs(t *testing.T) {
// Create a live block since we need metadata to reconstruct the receipt
tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
tx2 := types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil)
tx3 := types.NewTransaction(3, common.HexToAddress("0x3"), big.NewInt(3), 3, big.NewInt(3), nil)

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

// 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{
PostState: common.Hash{2}.Bytes(),
receiptVersion := types.CanyonDepositReceiptVersion
versionedDepositReceipt := types.Receipt{
Status: types.ReceiptStatusFailed,
CumulativeGasUsed: 2,
Logs: []*types.Log{
{Address: common.BytesToAddress([]byte{0x22})},
{Address: common.BytesToAddress([]byte{0x02, 0x22})},
{Address: common.BytesToAddress([]byte{0x01, 0x11})},
},
TxHash: tx2.Hash(),
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
GasUsed: 222222,
TxHash: tx2.Hash(),
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
GasUsed: 222222,
DepositNonce: &depositNonce,
DepositReceiptVersion: &receiptVersion,
}
receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2})
receipts := []*types.Receipt{receipt1, receipt2}
versionedDepositReceipt.Bloom = types.CreateBloom(types.Receipts{&versionedDepositReceipt})

receipt := types.Receipt{
PostState: common.Hash{3}.Bytes(),
CumulativeGasUsed: 3,
Logs: []*types.Log{
{Address: common.BytesToAddress([]byte{0x33})},
{Address: common.BytesToAddress([]byte{0x03, 0x33})},
},
TxHash: tx3.Hash(),
ContractAddress: common.BytesToAddress([]byte{0x03, 0x33, 0x33}),
GasUsed: 333333,
}
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 +768,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