Skip to content

Commit df38480

Browse files
piersykarlb
authored andcommitted
Handle migrated celo transactions (#150)
Introduces support for encoding/decoding legacy celo transaction types. That covers: Type 0 - The legacy transaction has been updated and now has the celo specific fields added Type 124 - The v1 of the dynamic fee tx. Additionally the transaction code has been updated to be less invasive to the op-geth repo by moving celo code into celo specific files and by removing celo specific methods added to the TxData interface. Additionally I reversed the EthCompatible boolean field since it's used to determine how to encode a legacy tx, and if unset (I.E. EthCompatible == false) then the legacy tx would be encoded as a celo legacy tx, which of course broke a lot of tests. So now the field is called CeloLegacy and if unset the tx will be encoded as an eth compatible transaction.
1 parent 7ec4340 commit df38480

25 files changed

+571
-177
lines changed

accounts/external/backend.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
215215
switch tx.Type() {
216216
case types.LegacyTxType, types.AccessListTxType:
217217
args.GasPrice = (*hexutil.Big)(tx.GasPrice())
218-
case types.DynamicFeeTxType, types.BlobTxType, types.CeloDynamicFeeTxType, types.CeloDenominatedTxType:
218+
case types.DynamicFeeTxType, types.BlobTxType, types.CeloDynamicFeeTxV2Type, types.CeloDenominatedTxType:
219219
args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
220220
args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
221221
default:

core/blockchain_celo_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func testNativeTransferWithFeeCurrency(t *testing.T, scheme string, feeCurrencyA
7777
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
7878
b.SetCoinbase(common.Address{1})
7979

80-
txdata := &types.CeloDynamicFeeTx{
80+
txdata := &types.CeloDynamicFeeTxV2{
8181
ChainID: gspec.Config.ChainID,
8282
Nonce: 0,
8383
To: &aa,

core/state_processor.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
182182
*receipt.DepositReceiptVersion = types.CanyonDepositReceiptVersion
183183
}
184184
}
185-
if tx.Type() == types.CeloDynamicFeeTxType {
185+
if tx.Type() == types.CeloDynamicFeeTxV2Type {
186186
alternativeBaseFee := evm.Context.BaseFee
187187
if tx.FeeCurrency() != nil {
188188
var err error

core/state_transition.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
217217
}
218218
// If baseFee provided, set gasPrice to effectiveGasPrice.
219219
if baseFee != nil {
220-
if tx.Type() == types.CeloDynamicFeeTxType {
220+
if tx.Type() == types.CeloDynamicFeeTxV2Type {
221221
var err error
222222
baseFee, err = exchange.ConvertCeloToCurrency(exchangeRates, msg.FeeCurrency, baseFee)
223223
if err != nil {

core/txpool/legacypool/celo_list_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
)
1212

1313
func txC(nonce int, feeCap int, tipCap int, gas int, currency *common.Address) *types.Transaction {
14-
return types.NewTx(&types.CeloDynamicFeeTx{
14+
return types.NewTx(&types.CeloDynamicFeeTxV2{
1515
GasFeeCap: big.NewInt(int64(feeCap)),
1616
GasTipCap: big.NewInt(int64(tipCap)),
1717
FeeCurrency: currency,

core/txpool/legacypool/legacypool.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ func New(config Config, chain BlockChain) *LegacyPool {
291291
// pool, specifically, whether it is a Legacy, AccessList or Dynamic transaction.
292292
func (pool *LegacyPool) Filter(tx *types.Transaction) bool {
293293
switch tx.Type() {
294-
case types.LegacyTxType, types.AccessListTxType, types.DynamicFeeTxType, types.CeloDynamicFeeTxType:
294+
case types.LegacyTxType, types.AccessListTxType, types.DynamicFeeTxType, types.CeloDynamicFeeTxV2Type:
295295
return true
296296
default:
297297
return false
@@ -645,7 +645,7 @@ func (pool *LegacyPool) validateTxBasics(tx *types.Transaction, local bool) erro
645645
types.LegacyTxType,
646646
types.AccessListTxType,
647647
types.DynamicFeeTxType,
648-
types.CeloDynamicFeeTxType),
648+
types.CeloDynamicFeeTxV2Type),
649649
MaxSize: txMaxSize,
650650
MinTip: pool.gasTip.Load().ToBig(),
651651
EffectiveGasCeil: pool.config.EffectiveGasCeil,

core/types/celo_denominated_tx.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"github.com/ethereum/go-ethereum/rlp"
99
)
1010

11+
const CeloDenominatedTxType = 0x7a
12+
1113
type CeloDenominatedTx struct {
1214
ChainID *big.Int
1315
Nonce uint64
@@ -115,7 +117,3 @@ func (tx *CeloDenominatedTx) encode(b *bytes.Buffer) error {
115117
func (tx *CeloDenominatedTx) decode(input []byte) error {
116118
return rlp.DecodeBytes(input, tx)
117119
}
118-
119-
func (tx *CeloDenominatedTx) feeCurrency() *common.Address { return tx.FeeCurrency }
120-
121-
func (tx *CeloDenominatedTx) maxFeeInFeeCurrency() *big.Int { return tx.MaxFeeInFeeCurrency }

core/types/celo_dynamic_fee_tx.go

+39-21
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
1-
// TODO: needs copyright header?
1+
// Copyright 2024 The Celo Authors
2+
// This file is part of the celo library.
3+
//
4+
// The celo library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The celo library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the celo library. If not, see <http://www.gnu.org/licenses/>.
216

317
package types
418

@@ -10,19 +24,21 @@ import (
1024
"github.com/ethereum/go-ethereum/rlp"
1125
)
1226

13-
// CeloDynamicFeeTx represents a CIP-64 transaction.
14-
type CeloDynamicFeeTx struct {
15-
ChainID *big.Int
16-
Nonce uint64
17-
GasTipCap *big.Int
18-
GasFeeCap *big.Int
19-
Gas uint64
20-
To *common.Address `rlp:"nil"` // nil means contract creation
21-
Value *big.Int
22-
Data []byte
23-
AccessList AccessList
27+
const CeloDynamicFeeTxType = 0x7c
2428

25-
FeeCurrency *common.Address `rlp:"nil"` // nil means native currency
29+
type CeloDynamicFeeTx struct {
30+
ChainID *big.Int
31+
Nonce uint64
32+
GasTipCap *big.Int
33+
GasFeeCap *big.Int
34+
Gas uint64
35+
FeeCurrency *common.Address `rlp:"nil"` // nil means native currency
36+
GatewayFeeRecipient *common.Address `rlp:"nil"` // nil means no gateway fee is paid
37+
GatewayFee *big.Int `rlp:"nil"`
38+
To *common.Address `rlp:"nil"` // nil means contract creation
39+
Value *big.Int
40+
Data []byte
41+
AccessList AccessList
2642

2743
// Signature values
2844
V *big.Int `json:"v" gencodec:"required"`
@@ -33,13 +49,15 @@ type CeloDynamicFeeTx struct {
3349
// copy creates a deep copy of the transaction data and initializes all fields.
3450
func (tx *CeloDynamicFeeTx) copy() TxData {
3551
cpy := &CeloDynamicFeeTx{
36-
Nonce: tx.Nonce,
37-
To: copyAddressPtr(tx.To),
38-
Data: common.CopyBytes(tx.Data),
39-
Gas: tx.Gas,
40-
FeeCurrency: copyAddressPtr(tx.FeeCurrency),
52+
Nonce: tx.Nonce,
53+
To: copyAddressPtr(tx.To),
54+
Data: common.CopyBytes(tx.Data),
55+
Gas: tx.Gas,
56+
FeeCurrency: copyAddressPtr(tx.FeeCurrency),
57+
GatewayFeeRecipient: copyAddressPtr(tx.GatewayFeeRecipient),
4158
// These are copied below.
4259
AccessList: make(AccessList, len(tx.AccessList)),
60+
GatewayFee: new(big.Int),
4361
Value: new(big.Int),
4462
ChainID: new(big.Int),
4563
GasTipCap: new(big.Int),
@@ -61,6 +79,9 @@ func (tx *CeloDynamicFeeTx) copy() TxData {
6179
if tx.GasFeeCap != nil {
6280
cpy.GasFeeCap.Set(tx.GasFeeCap)
6381
}
82+
if tx.GatewayFee != nil {
83+
cpy.GatewayFee.Set(tx.GatewayFee)
84+
}
6485
if tx.V != nil {
6586
cpy.V.Set(tx.V)
6687
}
@@ -113,6 +134,3 @@ func (tx *CeloDynamicFeeTx) encode(b *bytes.Buffer) error {
113134
func (tx *CeloDynamicFeeTx) decode(input []byte) error {
114135
return rlp.DecodeBytes(input, tx)
115136
}
116-
117-
func (tx *CeloDynamicFeeTx) feeCurrency() *common.Address { return tx.FeeCurrency }
118-
func (tx *CeloDynamicFeeTx) maxFeeInFeeCurrency() *big.Int { return nil }

core/types/celo_dynamic_fee_tx_v2.go

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// Copyright 2024 The Celo Authors
2+
// This file is part of the celo library.
3+
//
4+
// The celo library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The celo library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the celo library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package types
18+
19+
import (
20+
"bytes"
21+
"math/big"
22+
23+
"github.com/ethereum/go-ethereum/common"
24+
"github.com/ethereum/go-ethereum/rlp"
25+
)
26+
27+
const CeloDynamicFeeTxV2Type = 0x7b
28+
29+
// CeloDynamicFeeTxV2 represents a CIP-64 transaction.
30+
type CeloDynamicFeeTxV2 struct {
31+
ChainID *big.Int
32+
Nonce uint64
33+
GasTipCap *big.Int
34+
GasFeeCap *big.Int
35+
Gas uint64
36+
To *common.Address `rlp:"nil"` // nil means contract creation
37+
Value *big.Int
38+
Data []byte
39+
AccessList AccessList
40+
41+
FeeCurrency *common.Address `rlp:"nil"` // nil means native currency
42+
43+
// Signature values
44+
V *big.Int `json:"v" gencodec:"required"`
45+
R *big.Int `json:"r" gencodec:"required"`
46+
S *big.Int `json:"s" gencodec:"required"`
47+
}
48+
49+
// copy creates a deep copy of the transaction data and initializes all fields.
50+
func (tx *CeloDynamicFeeTxV2) copy() TxData {
51+
cpy := &CeloDynamicFeeTxV2{
52+
Nonce: tx.Nonce,
53+
To: copyAddressPtr(tx.To),
54+
Data: common.CopyBytes(tx.Data),
55+
Gas: tx.Gas,
56+
FeeCurrency: copyAddressPtr(tx.FeeCurrency),
57+
// These are copied below.
58+
AccessList: make(AccessList, len(tx.AccessList)),
59+
Value: new(big.Int),
60+
ChainID: new(big.Int),
61+
GasTipCap: new(big.Int),
62+
GasFeeCap: new(big.Int),
63+
V: new(big.Int),
64+
R: new(big.Int),
65+
S: new(big.Int),
66+
}
67+
copy(cpy.AccessList, tx.AccessList)
68+
if tx.Value != nil {
69+
cpy.Value.Set(tx.Value)
70+
}
71+
if tx.ChainID != nil {
72+
cpy.ChainID.Set(tx.ChainID)
73+
}
74+
if tx.GasTipCap != nil {
75+
cpy.GasTipCap.Set(tx.GasTipCap)
76+
}
77+
if tx.GasFeeCap != nil {
78+
cpy.GasFeeCap.Set(tx.GasFeeCap)
79+
}
80+
if tx.V != nil {
81+
cpy.V.Set(tx.V)
82+
}
83+
if tx.R != nil {
84+
cpy.R.Set(tx.R)
85+
}
86+
if tx.S != nil {
87+
cpy.S.Set(tx.S)
88+
}
89+
return cpy
90+
}
91+
92+
// accessors for innerTx.
93+
func (tx *CeloDynamicFeeTxV2) txType() byte { return CeloDynamicFeeTxV2Type }
94+
func (tx *CeloDynamicFeeTxV2) chainID() *big.Int { return tx.ChainID }
95+
func (tx *CeloDynamicFeeTxV2) accessList() AccessList { return tx.AccessList }
96+
func (tx *CeloDynamicFeeTxV2) data() []byte { return tx.Data }
97+
func (tx *CeloDynamicFeeTxV2) gas() uint64 { return tx.Gas }
98+
func (tx *CeloDynamicFeeTxV2) gasFeeCap() *big.Int { return tx.GasFeeCap }
99+
func (tx *CeloDynamicFeeTxV2) gasTipCap() *big.Int { return tx.GasTipCap }
100+
func (tx *CeloDynamicFeeTxV2) gasPrice() *big.Int { return tx.GasFeeCap }
101+
func (tx *CeloDynamicFeeTxV2) value() *big.Int { return tx.Value }
102+
func (tx *CeloDynamicFeeTxV2) nonce() uint64 { return tx.Nonce }
103+
func (tx *CeloDynamicFeeTxV2) to() *common.Address { return tx.To }
104+
func (tx *CeloDynamicFeeTxV2) isSystemTx() bool { return false }
105+
106+
func (tx *CeloDynamicFeeTxV2) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
107+
if baseFee == nil {
108+
return dst.Set(tx.GasFeeCap)
109+
}
110+
tip := dst.Sub(tx.GasFeeCap, baseFee)
111+
if tip.Cmp(tx.GasTipCap) > 0 {
112+
tip.Set(tx.GasTipCap)
113+
}
114+
return tip.Add(tip, baseFee)
115+
}
116+
117+
func (tx *CeloDynamicFeeTxV2) rawSignatureValues() (v, r, s *big.Int) {
118+
return tx.V, tx.R, tx.S
119+
}
120+
121+
func (tx *CeloDynamicFeeTxV2) setSignatureValues(chainID, v, r, s *big.Int) {
122+
tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s
123+
}
124+
125+
func (tx *CeloDynamicFeeTxV2) encode(b *bytes.Buffer) error {
126+
return rlp.Encode(b, tx)
127+
}
128+
129+
func (tx *CeloDynamicFeeTxV2) decode(input []byte) error {
130+
return rlp.DecodeBytes(input, tx)
131+
}

core/types/celo_transaction.go

+44-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,48 @@
1+
// Copyright 2024 The Celo Authors
2+
// This file is part of the celo library.
3+
//
4+
// The celo library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The celo library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the celo library. If not, see <http://www.gnu.org/licenses/>.
16+
117
package types
218

319
import (
20+
"math/big"
21+
22+
"github.com/ethereum/go-ethereum/common"
423
"github.com/ethereum/go-ethereum/common/exchange"
524
)
625

26+
// FeeCurrency returns the fee currency of the transaction if there is one.
27+
func (tx *Transaction) FeeCurrency() *common.Address {
28+
var feeCurrency *common.Address
29+
switch t := tx.inner.(type) {
30+
case *CeloDynamicFeeTxV2:
31+
feeCurrency = t.FeeCurrency
32+
}
33+
return feeCurrency
34+
}
35+
36+
// MaxFeeInFeeCurrency returns the maximum fee in the fee currency of the transaction if there is one.
37+
func (tx *Transaction) MaxFeeInFeeCurrency() *big.Int {
38+
var maxFeeInFeeCurrency *big.Int
39+
switch t := tx.inner.(type) {
40+
case *CeloDenominatedTx:
41+
maxFeeInFeeCurrency = t.MaxFeeInFeeCurrency
42+
}
43+
return maxFeeInFeeCurrency
44+
}
45+
746
// CompareWithRates compares the effective gas price of two transactions according to the exchange rates and
847
// the base fees in the transactions currencies.
948
func CompareWithRates(a, b *Transaction, ratesAndFees *exchange.RatesAndFees) int {
@@ -17,23 +56,23 @@ func CompareWithRates(a, b *Transaction, ratesAndFees *exchange.RatesAndFees) in
1756
}
1857
rates := ratesAndFees.Rates
1958
if ratesAndFees.HasBaseFee() {
20-
tipA := a.EffectiveGasTipValue(ratesAndFees.GetBaseFeeIn(a.inner.feeCurrency()))
21-
tipB := b.EffectiveGasTipValue(ratesAndFees.GetBaseFeeIn(b.inner.feeCurrency()))
22-
c, _ := exchange.CompareValue(rates, tipA, a.inner.feeCurrency(), tipB, b.inner.feeCurrency())
59+
tipA := a.EffectiveGasTipValue(ratesAndFees.GetBaseFeeIn(a.FeeCurrency()))
60+
tipB := b.EffectiveGasTipValue(ratesAndFees.GetBaseFeeIn(b.FeeCurrency()))
61+
c, _ := exchange.CompareValue(rates, tipA, a.FeeCurrency(), tipB, b.FeeCurrency())
2362
return c
2463
}
2564

2665
// Compare fee caps if baseFee is not specified or effective tips are equal
2766
feeA := a.inner.gasFeeCap()
2867
feeB := b.inner.gasFeeCap()
29-
c, _ := exchange.CompareValue(rates, feeA, a.inner.feeCurrency(), feeB, b.inner.feeCurrency())
68+
c, _ := exchange.CompareValue(rates, feeA, a.FeeCurrency(), feeB, b.FeeCurrency())
3069
if c != 0 {
3170
return c
3271
}
3372

3473
// Compare tips if effective tips and fee caps are equal
3574
tipCapA := a.inner.gasTipCap()
3675
tipCapB := b.inner.gasTipCap()
37-
c, _ = exchange.CompareValue(rates, tipCapA, a.inner.feeCurrency(), tipCapB, b.inner.feeCurrency())
76+
c, _ = exchange.CompareValue(rates, tipCapA, a.FeeCurrency(), tipCapB, b.FeeCurrency())
3877
return c
3978
}

0 commit comments

Comments
 (0)