mirror of
https://github.com/status-im/op-geth.git
synced 2025-02-12 14:56:23 +00:00
feat(core/types,internal/ethapi): In regolith, record the actual nonce used for deposit transactions
The nonce is available as DepositNonce on the receipt and is used to calculate the correct ContractAddress.
This commit is contained in:
parent
2864f90ac0
commit
bc0cca3223
@ -103,6 +103,11 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, gp *GasPool
|
||||
txContext := NewEVMTxContext(msg)
|
||||
evm.Reset(txContext, statedb)
|
||||
|
||||
nonce := tx.Nonce()
|
||||
if msg.IsDepositTx() && config.IsOptimismRegolith(evm.Context.Time) {
|
||||
nonce = statedb.GetNonce(msg.From())
|
||||
}
|
||||
|
||||
// Apply the transaction to the current state (included in the env).
|
||||
result, err := ApplyMessage(evm, msg, gp)
|
||||
if err != nil {
|
||||
@ -129,9 +134,15 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, gp *GasPool
|
||||
receipt.TxHash = tx.Hash()
|
||||
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
|
||||
receipt.DepositNonce = &nonce
|
||||
}
|
||||
|
||||
// If the transaction created a contract, store the creation address in the receipt.
|
||||
if msg.To() == nil {
|
||||
receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
|
||||
receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, nonce)
|
||||
}
|
||||
|
||||
// Set the receipt logs and create the bloom filter.
|
||||
|
@ -72,6 +72,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||
L1GasUsed *hexutil.Big `json:"l1GasUsed,omitempty"`
|
||||
L1Fee *hexutil.Big `json:"l1Fee,omitempty"`
|
||||
FeeScalar *big.Float `json:"l1FeeScalar,omitempty"`
|
||||
DepositNonce *hexutil.Uint64 `json:"depositNonce,omitempty"`
|
||||
}
|
||||
var dec Receipt
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
@ -130,5 +131,8 @@ func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||
if dec.FeeScalar != nil {
|
||||
r.FeeScalar = dec.FeeScalar
|
||||
}
|
||||
if dec.DepositNonce != nil {
|
||||
r.DepositNonce = (*uint64)(dec.DepositNonce)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -63,6 +63,9 @@ type Receipt struct {
|
||||
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
|
||||
ContractAddress common.Address `json:"contractAddress"`
|
||||
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
||||
// 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"`
|
||||
|
||||
// Inclusion information: These fields provide information about the inclusion of the
|
||||
// transaction corresponding to this receipt.
|
||||
@ -101,11 +104,24 @@ type receiptRLP struct {
|
||||
Logs []*Log
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
// storedReceiptRLP is the storage encoding of a receipt.
|
||||
type storedReceiptRLP struct {
|
||||
PostStateOrStatus []byte
|
||||
CumulativeGasUsed uint64
|
||||
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"`
|
||||
}
|
||||
|
||||
// LegacyOptimismStoredReceiptRLP is the pre bedrock storage encoding of a
|
||||
@ -209,7 +225,13 @@ func (r *Receipt) EncodeRLP(w io.Writer) error {
|
||||
// encodeTyped writes the canonical encoding of a typed receipt to w.
|
||||
func (r *Receipt) encodeTyped(data *receiptRLP, w *bytes.Buffer) error {
|
||||
w.WriteByte(r.Type)
|
||||
return rlp.Encode(w, data)
|
||||
switch r.Type {
|
||||
case DepositTxType:
|
||||
withNonce := depositReceiptRlp{data.PostStateOrStatus, data.CumulativeGasUsed, data.Bloom, data.Logs, r.DepositNonce}
|
||||
return rlp.Encode(w, withNonce)
|
||||
default:
|
||||
return rlp.Encode(w, data)
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalBinary returns the consensus encoding of the receipt.
|
||||
@ -271,7 +293,7 @@ func (r *Receipt) decodeTyped(b []byte) error {
|
||||
return errShortTypedReceipt
|
||||
}
|
||||
switch b[0] {
|
||||
case DynamicFeeTxType, AccessListTxType, DepositTxType:
|
||||
case DynamicFeeTxType, AccessListTxType:
|
||||
var data receiptRLP
|
||||
err := rlp.DecodeBytes(b[1:], &data)
|
||||
if err != nil {
|
||||
@ -279,6 +301,15 @@ func (r *Receipt) decodeTyped(b []byte) error {
|
||||
}
|
||||
r.Type = b[0]
|
||||
return r.setFromRLP(data)
|
||||
case DepositTxType:
|
||||
var data depositReceiptRlp
|
||||
err := rlp.DecodeBytes(b[1:], &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Type = b[0]
|
||||
r.DepositNonce = data.DepositNonce
|
||||
return r.setFromRLP(receiptRLP{data.PostStateOrStatus, data.CumulativeGasUsed, data.Bloom, data.Logs})
|
||||
default:
|
||||
return ErrTxTypeNotSupported
|
||||
}
|
||||
@ -342,6 +373,9 @@ func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error {
|
||||
}
|
||||
}
|
||||
w.ListEnd(logList)
|
||||
if r.DepositNonce != nil {
|
||||
w.WriteUint64(*r.DepositNonce)
|
||||
}
|
||||
w.ListEnd(outerList)
|
||||
return w.Flush()
|
||||
}
|
||||
@ -402,7 +436,9 @@ func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
|
||||
r.CumulativeGasUsed = stored.CumulativeGasUsed
|
||||
r.Logs = stored.Logs
|
||||
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
|
||||
|
||||
if stored.DepositNonce != nil {
|
||||
r.DepositNonce = stored.DepositNonce
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -458,7 +494,11 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu
|
||||
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())
|
||||
nonce := txs[i].Nonce()
|
||||
if rs[i].DepositNonce != nil {
|
||||
nonce = *rs[i].DepositNonce
|
||||
}
|
||||
rs[i].ContractAddress = crypto.CreateAddress(from, nonce)
|
||||
}
|
||||
// The used gas can be calculated based on previous r
|
||||
if i == 0 {
|
||||
|
@ -18,6 +18,7 @@ package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
@ -27,6 +28,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -80,6 +82,42 @@ var (
|
||||
},
|
||||
Type: DynamicFeeTxType,
|
||||
}
|
||||
depositReceiptNoNonce = &Receipt{
|
||||
Status: ReceiptStatusFailed,
|
||||
CumulativeGasUsed: 1,
|
||||
Logs: []*Log{
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
},
|
||||
Type: DepositTxType,
|
||||
}
|
||||
nonce = uint64(1234)
|
||||
depositReceiptWithNonce = &Receipt{
|
||||
Status: ReceiptStatusFailed,
|
||||
CumulativeGasUsed: 1,
|
||||
DepositNonce: &nonce,
|
||||
Logs: []*Log{
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
},
|
||||
Type: DepositTxType,
|
||||
}
|
||||
)
|
||||
|
||||
func TestDecodeEmptyTypedReceipt(t *testing.T) {
|
||||
@ -117,7 +155,12 @@ func TestDeriveFields(t *testing.T) {
|
||||
Gas: 3,
|
||||
GasPrice: big.NewInt(3),
|
||||
}),
|
||||
NewTx(&DepositTx{
|
||||
Value: big.NewInt(3),
|
||||
Gas: 4,
|
||||
}),
|
||||
}
|
||||
depNonce := uint64(7)
|
||||
// Create the corresponding receipts
|
||||
receipts := Receipts{
|
||||
&Receipt{
|
||||
@ -154,6 +197,26 @@ func TestDeriveFields(t *testing.T) {
|
||||
ContractAddress: common.BytesToAddress([]byte{0x03, 0x33, 0x33}),
|
||||
GasUsed: 3,
|
||||
},
|
||||
&Receipt{
|
||||
Type: DepositTxType,
|
||||
PostState: common.Hash{3}.Bytes(),
|
||||
CumulativeGasUsed: 10,
|
||||
Logs: []*Log{
|
||||
{Address: common.BytesToAddress([]byte{0x33})},
|
||||
{Address: common.BytesToAddress([]byte{0x03, 0x33})},
|
||||
},
|
||||
TxHash: txs[3].Hash(),
|
||||
ContractAddress: common.BytesToAddress([]byte{0x03, 0x33, 0x33}),
|
||||
GasUsed: 4,
|
||||
DepositNonce: &depNonce,
|
||||
},
|
||||
}
|
||||
|
||||
nonces := []uint64{
|
||||
txs[0].Nonce(),
|
||||
txs[1].Nonce(),
|
||||
txs[2].Nonce(),
|
||||
*receipts[3].DepositNonce, // Deposit tx should use deposit nonce
|
||||
}
|
||||
// Clear all the computed fields and re-derive them
|
||||
number := big.NewInt(1)
|
||||
@ -190,7 +253,7 @@ func TestDeriveFields(t *testing.T) {
|
||||
t.Errorf("receipts[%d].ContractAddress = %s, want %s", i, receipts[i].ContractAddress.String(), (common.Address{}).String())
|
||||
}
|
||||
from, _ := Sender(signer, txs[i])
|
||||
contractAddress := crypto.CreateAddress(from, txs[i].Nonce())
|
||||
contractAddress := crypto.CreateAddress(from, nonces[i])
|
||||
if txs[i].To() == nil && receipts[i].ContractAddress != contractAddress {
|
||||
t.Errorf("receipts[%d].ContractAddress = %s, want %s", i, receipts[i].ContractAddress.String(), contractAddress.String())
|
||||
}
|
||||
@ -342,6 +405,38 @@ func TestReceiptUnmarshalBinary(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBedrockDepositReceiptUnchanged(t *testing.T) {
|
||||
expectedRlp := common.FromHex("7EF90156A003000000000000000000000000000000000000000000000000000000000000000AB9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F0D7940000000000000000000000000000000000000033C001D7940000000000000000000000000000000000000333C002")
|
||||
// Deposit receipt with no nonce
|
||||
receipt := &Receipt{
|
||||
Type: DepositTxType,
|
||||
PostState: common.Hash{3}.Bytes(),
|
||||
CumulativeGasUsed: 10,
|
||||
Logs: []*Log{
|
||||
{Address: common.BytesToAddress([]byte{0x33}), Data: []byte{1}, Topics: []common.Hash{}},
|
||||
{Address: common.BytesToAddress([]byte{0x03, 0x33}), Data: []byte{2}, Topics: []common.Hash{}},
|
||||
},
|
||||
TxHash: common.Hash{},
|
||||
ContractAddress: common.BytesToAddress([]byte{0x03, 0x33, 0x33}),
|
||||
GasUsed: 4,
|
||||
}
|
||||
|
||||
rlp, err := receipt.MarshalBinary()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedRlp, rlp)
|
||||
|
||||
// Consensus values should be unchanged after reparsing
|
||||
parsed := new(Receipt)
|
||||
err = parsed.UnmarshalBinary(rlp)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, receipt.Status, parsed.Status)
|
||||
require.Equal(t, receipt.CumulativeGasUsed, parsed.CumulativeGasUsed)
|
||||
require.Equal(t, receipt.Bloom, parsed.Bloom)
|
||||
require.EqualValues(t, receipt.Logs, parsed.Logs)
|
||||
// And still shouldn't have a nonce
|
||||
require.Nil(t, parsed.DepositNonce)
|
||||
}
|
||||
|
||||
func clearComputedFieldsOnReceipts(t *testing.T, receipts Receipts) {
|
||||
t.Helper()
|
||||
|
||||
@ -380,3 +475,64 @@ func clearComputedFieldsOnLog(t *testing.T, log *Log) {
|
||||
log.TxIndex = math.MaxUint32
|
||||
log.Index = math.MaxUint32
|
||||
}
|
||||
|
||||
func TestRoundTripReceipt(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rcpt *Receipt
|
||||
}{
|
||||
{name: "Legacy", rcpt: legacyReceipt},
|
||||
{name: "AccessList", rcpt: accessListReceipt},
|
||||
{name: "EIP1559", rcpt: eip1559Receipt},
|
||||
{name: "DepositNoNonce", rcpt: depositReceiptNoNonce},
|
||||
{name: "DepositWithNonce", rcpt: depositReceiptWithNonce},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
data, err := test.rcpt.MarshalBinary()
|
||||
require.NoError(t, err)
|
||||
|
||||
d := &Receipt{}
|
||||
err = d.UnmarshalBinary(data)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.rcpt, d)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("%sRejectExtraData", test.name), func(t *testing.T) {
|
||||
data, err := test.rcpt.MarshalBinary()
|
||||
require.NoError(t, err)
|
||||
data = append(data, 1, 2, 3, 4)
|
||||
d := &Receipt{}
|
||||
err = d.UnmarshalBinary(data)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundTripReceiptForStorage(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rcpt *Receipt
|
||||
}{
|
||||
{name: "Legacy", rcpt: legacyReceipt},
|
||||
{name: "AccessList", rcpt: accessListReceipt},
|
||||
{name: "EIP1559", rcpt: eip1559Receipt},
|
||||
{name: "DepositNoNonce", rcpt: depositReceiptNoNonce},
|
||||
{name: "DepositWithNonce", rcpt: depositReceiptWithNonce},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
data, err := rlp.EncodeToBytes((*ReceiptForStorage)(test.rcpt))
|
||||
require.NoError(t, err)
|
||||
|
||||
d := &ReceiptForStorage{}
|
||||
err = rlp.DecodeBytes(data, d)
|
||||
require.NoError(t, err)
|
||||
// Only check the stored fields - the others are derived later
|
||||
require.Equal(t, test.rcpt.Status, d.Status)
|
||||
require.Equal(t, test.rcpt.CumulativeGasUsed, d.CumulativeGasUsed)
|
||||
require.Equal(t, test.rcpt.Logs, d.Logs)
|
||||
require.Equal(t, test.rcpt.DepositNonce, d.DepositNonce)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1826,6 +1826,9 @@ func (s *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.
|
||||
fields["l1Fee"] = (*hexutil.Big)(receipt.L1Fee)
|
||||
fields["l1FeeScalar"] = receipt.FeeScalar.String()
|
||||
}
|
||||
if s.b.ChainConfig().Optimism != nil && tx.IsDepositTx() && receipt.DepositNonce != nil {
|
||||
fields["depositNonce"] = hexutil.Uint64(*receipt.DepositNonce)
|
||||
}
|
||||
// Assign the effective gas price paid
|
||||
if !s.b.ChainConfig().IsLondon(bigblock) {
|
||||
fields["effectiveGasPrice"] = hexutil.Uint64(tx.GasPrice().Uint64())
|
||||
|
Loading…
x
Reference in New Issue
Block a user