Skip to content

Commit

Permalink
Celo Denominated tx type and receipt
Browse files Browse the repository at this point in the history
  • Loading branch information
hbandura committed Apr 9, 2024
1 parent bd5863f commit 9d4c4e6
Show file tree
Hide file tree
Showing 15 changed files with 356 additions and 19 deletions.
2 changes: 1 addition & 1 deletion accounts/external/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
switch tx.Type() {
case types.LegacyTxType, types.AccessListTxType:
args.GasPrice = (*hexutil.Big)(tx.GasPrice())
case types.DynamicFeeTxType, types.CeloDynamicFeeTxType:
case types.DynamicFeeTxType, types.CeloDynamicFeeTxType, types.CeloDenominatedTxType:
args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
default:
Expand Down
5 changes: 3 additions & 2 deletions core/txpool/legacypool/legacypool.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ func New(config Config, chain BlockChain) *LegacyPool {
// pool, specifically, whether it is a Legacy, AccessList or Dynamic transaction.
func (pool *LegacyPool) Filter(tx *types.Transaction) bool {
switch tx.Type() {
case types.LegacyTxType, types.AccessListTxType, types.DynamicFeeTxType, types.CeloDynamicFeeTxType:
case types.LegacyTxType, types.AccessListTxType, types.DynamicFeeTxType, types.CeloDynamicFeeTxType, types.CeloDenominatedTxType:
return true
default:
return false
Expand Down Expand Up @@ -626,7 +626,8 @@ func (pool *LegacyPool) validateTxBasics(tx *types.Transaction, local bool) erro
types.LegacyTxType,
types.AccessListTxType,
types.DynamicFeeTxType,
types.CeloDynamicFeeTxType),
types.CeloDynamicFeeTxType,
types.CeloDenominatedTxType),
MaxSize: txMaxSize,
MinTip: pool.gasTip.Load(),
}
Expand Down
120 changes: 120 additions & 0 deletions core/types/celo_denominated_tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package types

import (
"bytes"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
)

type CeloDenominatedTx struct {
ChainID *big.Int
Nonce uint64
GasTipCap *big.Int
GasFeeCap *big.Int
Gas uint64
To *common.Address `rlp:"nil"` // nil means contract creation
Value *big.Int
Data []byte
AccessList AccessList

FeeCurrency *common.Address `rlp:"nil"` // nil means native currency
MaxFeeInFeeCurrency *big.Int

// Signature values
V *big.Int `json:"v" gencodec:"required"`
R *big.Int `json:"r" gencodec:"required"`
S *big.Int `json:"s" gencodec:"required"`
}

// copy creates a deep copy of the transaction data and initializes all fields.
func (tx *CeloDenominatedTx) copy() TxData {
cpy := &CeloDenominatedTx{
Nonce: tx.Nonce,
To: copyAddressPtr(tx.To),
Data: common.CopyBytes(tx.Data),
Gas: tx.Gas,
FeeCurrency: copyAddressPtr(tx.FeeCurrency),
// These are copied below.
AccessList: make(AccessList, len(tx.AccessList)),
Value: new(big.Int),
ChainID: new(big.Int),
GasTipCap: new(big.Int),
GasFeeCap: new(big.Int),
V: new(big.Int),
R: new(big.Int),
S: new(big.Int),
}
if tx.MaxFeeInFeeCurrency != nil {
cpy.MaxFeeInFeeCurrency = new(big.Int).Set(tx.MaxFeeInFeeCurrency)
}
copy(cpy.AccessList, tx.AccessList)
if tx.Value != nil {
cpy.Value.Set(tx.Value)
}
if tx.ChainID != nil {
cpy.ChainID.Set(tx.ChainID)
}
if tx.GasTipCap != nil {
cpy.GasTipCap.Set(tx.GasTipCap)
}
if tx.GasFeeCap != nil {
cpy.GasFeeCap.Set(tx.GasFeeCap)
}
if tx.V != nil {
cpy.V.Set(tx.V)
}
if tx.R != nil {
cpy.R.Set(tx.R)
}
if tx.S != nil {
cpy.S.Set(tx.S)
}
return cpy
}

// accessors for innerTx.
func (tx *CeloDenominatedTx) txType() byte { return CeloDenominatedTxType }
func (tx *CeloDenominatedTx) chainID() *big.Int { return tx.ChainID }
func (tx *CeloDenominatedTx) accessList() AccessList { return tx.AccessList }
func (tx *CeloDenominatedTx) data() []byte { return tx.Data }
func (tx *CeloDenominatedTx) gas() uint64 { return tx.Gas }
func (tx *CeloDenominatedTx) gasFeeCap() *big.Int { return tx.GasFeeCap }
func (tx *CeloDenominatedTx) gasTipCap() *big.Int { return tx.GasTipCap }
func (tx *CeloDenominatedTx) gasPrice() *big.Int { return tx.GasFeeCap }
func (tx *CeloDenominatedTx) value() *big.Int { return tx.Value }
func (tx *CeloDenominatedTx) nonce() uint64 { return tx.Nonce }
func (tx *CeloDenominatedTx) to() *common.Address { return tx.To }
func (tx *CeloDenominatedTx) isSystemTx() bool { return false }

func (tx *CeloDenominatedTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
if baseFee == nil {
return dst.Set(tx.GasFeeCap)
}
tip := dst.Sub(tx.GasFeeCap, baseFee)
if tip.Cmp(tx.GasTipCap) > 0 {
tip.Set(tx.GasTipCap)
}
return tip.Add(tip, baseFee)
}

func (tx *CeloDenominatedTx) rawSignatureValues() (v, r, s *big.Int) {
return tx.V, tx.R, tx.S
}

func (tx *CeloDenominatedTx) setSignatureValues(chainID, v, r, s *big.Int) {
tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s
}

func (tx *CeloDenominatedTx) encode(b *bytes.Buffer) error {
return rlp.Encode(b, tx)
}

func (tx *CeloDenominatedTx) decode(input []byte) error {
return rlp.DecodeBytes(input, tx)
}

func (tx *CeloDenominatedTx) feeCurrency() *common.Address { return tx.FeeCurrency }

func (tx *CeloDenominatedTx) maxFeeInFeeCurrency() *big.Int { return tx.MaxFeeInFeeCurrency }
3 changes: 2 additions & 1 deletion core/types/celo_dynamic_fee_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,5 @@ func (tx *CeloDynamicFeeTx) decode(input []byte) error {
return rlp.DecodeBytes(input, tx)
}

func (tx *CeloDynamicFeeTx) feeCurrency() *common.Address { return tx.FeeCurrency }
func (tx *CeloDynamicFeeTx) feeCurrency() *common.Address { return tx.FeeCurrency }
func (tx *CeloDynamicFeeTx) maxFeeInFeeCurrency() *big.Int { return nil }
26 changes: 22 additions & 4 deletions core/types/celo_transaction_signing.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func NewCel2Signer(chainId *big.Int) Signer {
}

func (s cel2Signer) Sender(tx *Transaction) (common.Address, error) {
if tx.Type() != CeloDynamicFeeTxType {
if tx.Type() != CeloDynamicFeeTxType && tx.Type() != CeloDenominatedTxType {
return s.londonSigner.Sender(tx)
}
V, R, S := tx.RawSignatureValues()
Expand All @@ -55,13 +55,14 @@ func (s cel2Signer) Equal(s2 Signer) bool {
}

func (s cel2Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
txdata, ok := tx.inner.(*CeloDynamicFeeTx)
if !ok {
if tx.Type() != CeloDynamicFeeTxType && tx.Type() != CeloDenominatedTxType {
return s.londonSigner.SignatureValues(tx, sig)
}

// Check that chain ID of tx matches the signer. We also accept ID zero here,
// because it indicates that the chain ID was not specified in the tx.
if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 {
chainID := tx.inner.chainID()
if chainID.Sign() != 0 && chainID.Cmp(s.chainId) != 0 {
return nil, nil, nil, ErrInvalidChainId
}
R, S, _ = decodeSignature(sig)
Expand All @@ -88,5 +89,22 @@ func (s cel2Signer) Hash(tx *Transaction) common.Hash {
tx.FeeCurrency(),
})
}
if tx.Type() == CeloDenominatedTxType {
return prefixedRlpHash(
tx.Type(),
[]interface{}{
s.chainId,
tx.Nonce(),
tx.GasTipCap(),
tx.GasFeeCap(),
tx.Gas(),
tx.To(),
tx.Value(),
tx.Data(),
tx.AccessList(),
tx.FeeCurrency(),
tx.MaxFeeInFeeCurrency(),
})
}
return s.londonSigner.Hash(tx)
}
3 changes: 2 additions & 1 deletion core/types/deposit_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,5 @@ func (tx *DepositTx) decode(input []byte) error {
return rlp.DecodeBytes(input, tx)
}

func (tx *DepositTx) feeCurrency() *common.Address { return nil }
func (tx *DepositTx) feeCurrency() *common.Address { return nil }
func (tx *DepositTx) maxFeeInFeeCurrency() *big.Int { return nil }
70 changes: 69 additions & 1 deletion core/types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ type Receipt struct {
// The BaseFee is stored in fee currency for fee currency txs. We need
// this field to calculate the EffectiveGasPrice for fee currency txs.
BaseFee *big.Int `json:"baseFee,omitempty"`
// The FeeInFeeCurrency is stored in CELO denominated txs. Its value is
// exactly what the tx fee was in the FeeCurrency of the tx.
FeeInFeeCurrency *big.Int `json:"feeInFeeCurrency,omitempty"`
}

type receiptMarshaling struct {
Expand Down Expand Up @@ -148,6 +151,15 @@ type celoDynamicReceiptRLP struct {
BaseFee *big.Int
}

type celoDenominatedReceiptRLP struct {
PostStateOrStatus []byte
CumulativeGasUsed uint64
Bloom Bloom
Logs []*Log
// FeeInFeeCurrency was introduced as mandatory in Cel2 ONLY for the CeloDenominatedFeeTxs
FeeInFeeCurrency *big.Int
}

// storedReceiptRLP is the storage encoding of a receipt.
type storedReceiptRLP struct {
PostStateOrStatus []byte
Expand All @@ -170,6 +182,14 @@ type celoDynamicFeeStoredReceiptRLP struct {
BaseFee *big.Int
}

type celoDenominatedStoredReceiptRLP struct {
CeloDenominatedReceiptMarker string // Marker to distinguish this from storedReceiptRLP
PostStateOrStatus []byte
CumulativeGasUsed uint64
Logs []*Log
FeeInFeeCurrency *big.Int
}

// 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
Expand Down Expand Up @@ -275,6 +295,9 @@ func (r *Receipt) encodeTyped(data *receiptRLP, w *bytes.Buffer) error {
case CeloDynamicFeeTxType:
withBaseFee := &celoDynamicReceiptRLP{data.PostStateOrStatus, data.CumulativeGasUsed, data.Bloom, data.Logs, r.BaseFee}
return rlp.Encode(w, withBaseFee)
case CeloDenominatedTxType:
withFeeInFeeCurrency := &celoDenominatedReceiptRLP{data.PostStateOrStatus, data.CumulativeGasUsed, data.Bloom, data.Logs, r.FeeInFeeCurrency}
return rlp.Encode(w, withFeeInFeeCurrency)
case DepositTxType:
withNonce := &depositReceiptRLP{data.PostStateOrStatus, data.CumulativeGasUsed, data.Bloom, data.Logs, r.DepositNonce, r.DepositReceiptVersion}
return rlp.Encode(w, withNonce)
Expand Down Expand Up @@ -365,6 +388,15 @@ func (r *Receipt) decodeTyped(b []byte) error {
r.Type = b[0]
r.BaseFee = data.BaseFee
return r.setFromRLP(receiptRLP{data.PostStateOrStatus, data.CumulativeGasUsed, data.Bloom, data.Logs})
case CeloDenominatedTxType:
var data celoDenominatedReceiptRLP
err := rlp.DecodeBytes(b[1:], &data)
if err != nil {
return err
}
r.Type = b[0]
r.FeeInFeeCurrency = data.FeeInFeeCurrency
return r.setFromRLP(receiptRLP{data.PostStateOrStatus, data.CumulativeGasUsed, data.Bloom, data.Logs})
case DepositTxType:
var data depositReceiptRLP
err := rlp.DecodeBytes(b[1:], &data)
Expand Down Expand Up @@ -434,6 +466,10 @@ func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error {
listIndex := w.List()
w.ListEnd(listIndex)
}
if r.Type == CeloDenominatedTxType {
// Mark receipt as CeloDenominated receipt by starting with an empty string
w.WriteString("")
}
w.WriteBytes((*Receipt)(r).statusEncoding())
w.WriteUint64(r.CumulativeGasUsed)
logList := w.List()
Expand All @@ -452,6 +488,9 @@ func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error {
if r.Type == CeloDynamicFeeTxType {
w.WriteBigInt(r.BaseFee)
}
if r.Type == CeloDenominatedTxType {
w.WriteBigInt(r.FeeInFeeCurrency)
}
w.ListEnd(outerList)
return w.Flush()
}
Expand All @@ -460,12 +499,20 @@ func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error {
// To distinguish these receipts from the very similar normal receipts, an
// empty list is added as the first element of the RLP-serialized struct.
func isCeloDynamicFeeReceipt(blob []byte) bool {
return isFirstElementA(blob, 0xc0) // an empty short list
}

func isCeloDenominatedFeeReceipt(blob []byte) bool {
return isFirstElementA(blob, 0x80) // an empty short string
}

func isFirstElementA(blob []byte, value byte) bool {
listHeaderSize := 1 // Length of the list header representing the struct in bytes
if blob[0] > 0xf7 {
listHeaderSize += int(blob[0]) - 0xf7
}
firstListElement := blob[listHeaderSize] // First byte of first list element
return firstListElement == 0xc0
return firstListElement == value
}

// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation
Expand All @@ -480,6 +527,9 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
if isCeloDynamicFeeReceipt(blob) {
return decodeStoredCeloDynamicFeeReceiptRLP(r, blob)
}
if isCeloDenominatedFeeReceipt(blob) {
return decodeStoredCeloDenominatedReceiptRLP(r, blob)
}
if err := decodeStoredReceiptRLP(r, blob); err == nil {
return nil
}
Expand Down Expand Up @@ -531,6 +581,21 @@ func decodeStoredCeloDynamicFeeReceiptRLP(r *ReceiptForStorage, blob []byte) err
return nil
}

func decodeStoredCeloDenominatedReceiptRLP(r *ReceiptForStorage, blob []byte) error {
var stored celoDenominatedStoredReceiptRLP
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 = stored.Logs
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
r.FeeInFeeCurrency = stored.FeeInFeeCurrency
return nil
}

func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
var stored storedReceiptRLP
if err := rlp.DecodeBytes(blob, &stored); err != nil {
Expand Down Expand Up @@ -573,6 +638,9 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
case CeloDynamicFeeTxType:
celoDynamicData := &celoDynamicReceiptRLP{data.PostStateOrStatus, data.CumulativeGasUsed, data.Bloom, data.Logs, r.BaseFee}
rlp.Encode(w, celoDynamicData)
case CeloDenominatedTxType:
celoDenominatedData := &celoDenominatedReceiptRLP{data.PostStateOrStatus, data.CumulativeGasUsed, data.Bloom, data.Logs, r.FeeInFeeCurrency}
rlp.Encode(w, celoDenominatedData)
case DepositTxType:
if r.DepositReceiptVersion != nil {
// post-canyon receipt hash computation update
Expand Down
Loading

0 comments on commit 9d4c4e6

Please sign in to comment.