Skip to content

Commit

Permalink
Add json marshalling support for legacy celo txs (#217)
Browse files Browse the repository at this point in the history
Add json marshalling support for legacy celo txs
  • Loading branch information
piersy authored Sep 12, 2024
1 parent 1440796 commit 3ec26d7
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 1 deletion.
229 changes: 229 additions & 0 deletions core/types/celo_transaction_marshalling.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,74 @@ func celoTransactionMarshal(tx *Transaction) ([]byte, bool, error) {
enc.Hash = tx.Hash()
enc.Type = hexutil.Uint64(tx.Type())
switch itx := tx.inner.(type) {
case *LegacyTx:
if !itx.CeloLegacy {
return nil, false, nil
}
enc.Nonce = (*hexutil.Uint64)(&itx.Nonce)
enc.To = tx.To()
enc.Gas = (*hexutil.Uint64)(&itx.Gas)
enc.Value = (*hexutil.Big)(itx.Value)
enc.Input = (*hexutil.Bytes)(&itx.Data)
enc.V = (*hexutil.Big)(itx.V)
enc.R = (*hexutil.Big)(itx.R)
enc.S = (*hexutil.Big)(itx.S)
if tx.Protected() {
enc.ChainID = (*hexutil.Big)(tx.ChainId())
}
// Celo specific fields
enc.FeeCurrency = itx.FeeCurrency
enc.GatewayFee = (*hexutil.Big)(itx.GatewayFee)
enc.GatewayFeeRecipient = itx.GatewayFeeRecipient
enc.EthCompatible = new(bool)
case *CeloDynamicFeeTx:
enc.ChainID = (*hexutil.Big)(itx.ChainID)
enc.Nonce = (*hexutil.Uint64)(&itx.Nonce)
enc.To = tx.To()
enc.Gas = (*hexutil.Uint64)(&itx.Gas)
enc.MaxFeePerGas = (*hexutil.Big)(itx.GasFeeCap)
enc.MaxPriorityFeePerGas = (*hexutil.Big)(itx.GasTipCap)
enc.Value = (*hexutil.Big)(itx.Value)
enc.Input = (*hexutil.Bytes)(&itx.Data)
enc.AccessList = &itx.AccessList
enc.V = (*hexutil.Big)(itx.V)
enc.R = (*hexutil.Big)(itx.R)
enc.S = (*hexutil.Big)(itx.S)
// Celo specific fields
enc.FeeCurrency = itx.FeeCurrency
enc.GatewayFee = (*hexutil.Big)(itx.GatewayFee)
enc.GatewayFeeRecipient = itx.GatewayFeeRecipient
case *CeloDynamicFeeTxV2:
enc.ChainID = (*hexutil.Big)(itx.ChainID)
enc.Nonce = (*hexutil.Uint64)(&itx.Nonce)
enc.To = tx.To()
enc.Gas = (*hexutil.Uint64)(&itx.Gas)
enc.MaxFeePerGas = (*hexutil.Big)(itx.GasFeeCap)
enc.MaxPriorityFeePerGas = (*hexutil.Big)(itx.GasTipCap)
enc.Value = (*hexutil.Big)(itx.Value)
enc.Input = (*hexutil.Bytes)(&itx.Data)
enc.AccessList = &itx.AccessList
enc.V = (*hexutil.Big)(itx.V)
enc.R = (*hexutil.Big)(itx.R)
enc.S = (*hexutil.Big)(itx.S)
// Celo specific fields
enc.FeeCurrency = itx.FeeCurrency
case *CeloDenominatedTx:
enc.ChainID = (*hexutil.Big)(itx.ChainID)
enc.Nonce = (*hexutil.Uint64)(&itx.Nonce)
enc.To = tx.To()
enc.Gas = (*hexutil.Uint64)(&itx.Gas)
enc.MaxFeePerGas = (*hexutil.Big)(itx.GasFeeCap)
enc.MaxPriorityFeePerGas = (*hexutil.Big)(itx.GasTipCap)
enc.Value = (*hexutil.Big)(itx.Value)
enc.Input = (*hexutil.Bytes)(&itx.Data)
enc.AccessList = &itx.AccessList
enc.V = (*hexutil.Big)(itx.V)
enc.R = (*hexutil.Big)(itx.R)
enc.S = (*hexutil.Big)(itx.S)
// Celo specific fields
enc.FeeCurrency = itx.FeeCurrency
enc.MaxFeeInFeeCurrency = (*hexutil.Big)(itx.MaxFeeInFeeCurrency)
default:
return nil, false, nil
}
Expand All @@ -53,6 +107,121 @@ func celoTransactionMarshal(tx *Transaction) ([]byte, bool, error) {

func celoTransactionUnmarshal(dec txJSON, inner *TxData) (bool, error) {
switch dec.Type {
case LegacyTxType:
// EthCompatible is only set to false for celo legacy transactions, otherwise its not set. So not set means it is ethCompatible.
if dec.EthCompatible == nil {
return false, nil
}
var itx LegacyTx
*inner = &itx
if dec.Nonce == nil {
return true, errors.New("missing required field 'nonce' in transaction")
}
itx.Nonce = uint64(*dec.Nonce)
if dec.To != nil {
itx.To = dec.To
}
if dec.Gas == nil {
return true, errors.New("missing required field 'gas' in transaction")
}
itx.Gas = uint64(*dec.Gas)
if dec.GasPrice == nil {
return true, errors.New("missing required field 'gasPrice' in transaction")
}
itx.GasPrice = (*big.Int)(dec.GasPrice)
if dec.Value == nil {
return true, errors.New("missing required field 'value' in transaction")
}
itx.Value = (*big.Int)(dec.Value)
if dec.Input == nil {
return true, errors.New("missing required field 'input' in transaction")
}
itx.Data = *dec.Input

// signature R
if dec.R == nil {
return true, errors.New("missing required field 'r' in transaction")
}
itx.R = (*big.Int)(dec.R)
// signature S
if dec.S == nil {
return true, errors.New("missing required field 's' in transaction")
}
itx.S = (*big.Int)(dec.S)
// signature V
if dec.V == nil {
return true, errors.New("missing required field 'v' in transaction")
}
itx.V = (*big.Int)(dec.V)
if itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 {
if err := sanityCheckSignature(itx.V, itx.R, itx.S, true); err != nil {
return true, err
}
}
itx.CeloLegacy = true
itx.FeeCurrency = dec.FeeCurrency
itx.GatewayFeeRecipient = dec.GatewayFeeRecipient
itx.GatewayFee = (*big.Int)(dec.GatewayFee)

case CeloDynamicFeeTxType:
var itx CeloDynamicFeeTx
*inner = &itx
if dec.ChainID == nil {
return true, errors.New("missing required field 'chainId' in transaction")
}
itx.ChainID = (*big.Int)(dec.ChainID)
if dec.Nonce == nil {
return true, errors.New("missing required field 'nonce' in transaction")
}
itx.Nonce = uint64(*dec.Nonce)
if dec.To != nil {
itx.To = dec.To
}
if dec.Gas == nil {
return true, errors.New("missing required field 'gas' for txdata")
}
itx.Gas = uint64(*dec.Gas)
if dec.MaxPriorityFeePerGas == nil {
return true, errors.New("missing required field 'maxPriorityFeePerGas' for txdata")
}
itx.GasTipCap = (*big.Int)(dec.MaxPriorityFeePerGas)
if dec.MaxFeePerGas == nil {
return true, errors.New("missing required field 'maxFeePerGas' for txdata")
}
itx.GasFeeCap = (*big.Int)(dec.MaxFeePerGas)
if dec.Value == nil {
return true, errors.New("missing required field 'value' in transaction")
}
itx.Value = (*big.Int)(dec.Value)
if dec.Input == nil {
return true, errors.New("missing required field 'input' in transaction")
}
itx.Data = *dec.Input
if dec.V == nil {
return true, errors.New("missing required field 'v' in transaction")
}
if dec.AccessList != nil {
itx.AccessList = *dec.AccessList
}
itx.V = (*big.Int)(dec.V)
if dec.R == nil {
return true, errors.New("missing required field 'r' in transaction")
}
itx.R = (*big.Int)(dec.R)
if dec.S == nil {
return true, errors.New("missing required field 's' in transaction")
}
itx.S = (*big.Int)(dec.S)
withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0
if withSignature {
if err := sanityCheckSignature(itx.V, itx.R, itx.S, false); err != nil {
return true, err
}
}
// Celo specific fields
itx.FeeCurrency = dec.FeeCurrency
itx.GatewayFee = (*big.Int)(dec.GatewayFee)
itx.GatewayFeeRecipient = dec.GatewayFeeRecipient
case CeloDynamicFeeTxV2Type:
var itx CeloDynamicFeeTxV2
*inner = &itx
Expand Down Expand Up @@ -82,6 +251,63 @@ func celoTransactionUnmarshal(dec txJSON, inner *TxData) (bool, error) {
if dec.Value == nil {
return true, errors.New("missing required field 'value' in transaction")
}
itx.Value = (*big.Int)(dec.Value)
if dec.Input == nil {
return true, errors.New("missing required field 'input' in transaction")
}
itx.Data = *dec.Input
if dec.V == nil {
return true, errors.New("missing required field 'v' in transaction")
}
if dec.AccessList != nil {
itx.AccessList = *dec.AccessList
}
itx.V = (*big.Int)(dec.V)
if dec.R == nil {
return true, errors.New("missing required field 'r' in transaction")
}
itx.R = (*big.Int)(dec.R)
if dec.S == nil {
return true, errors.New("missing required field 's' in transaction")
}
itx.S = (*big.Int)(dec.S)
withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0
if withSignature {
if err := sanityCheckSignature(itx.V, itx.R, itx.S, false); err != nil {
return true, err
}
}
// Celo specific fields
itx.FeeCurrency = dec.FeeCurrency
case CeloDenominatedTxType:
var itx CeloDenominatedTx
*inner = &itx
if dec.ChainID == nil {
return true, errors.New("missing required field 'chainId' in transaction")
}
itx.ChainID = (*big.Int)(dec.ChainID)
if dec.Nonce == nil {
return true, errors.New("missing required field 'nonce' in transaction")
}
itx.Nonce = uint64(*dec.Nonce)
if dec.To != nil {
itx.To = dec.To
}
if dec.Gas == nil {
return true, errors.New("missing required field 'gas' for txdata")
}
itx.Gas = uint64(*dec.Gas)
if dec.MaxPriorityFeePerGas == nil {
return true, errors.New("missing required field 'maxPriorityFeePerGas' for txdata")
}
itx.GasTipCap = (*big.Int)(dec.MaxPriorityFeePerGas)
if dec.MaxFeePerGas == nil {
return true, errors.New("missing required field 'maxFeePerGas' for txdata")
}
itx.GasFeeCap = (*big.Int)(dec.MaxFeePerGas)
if dec.Value == nil {
return true, errors.New("missing required field 'value' in transaction")
}
itx.FeeCurrency = dec.FeeCurrency
itx.Value = (*big.Int)(dec.Value)
if dec.Input == nil {
Expand Down Expand Up @@ -109,6 +335,9 @@ func celoTransactionUnmarshal(dec txJSON, inner *TxData) (bool, error) {
return true, err
}
}
// Celo specific fields
itx.FeeCurrency = dec.FeeCurrency
itx.MaxFeeInFeeCurrency = (*big.Int)(dec.MaxFeeInFeeCurrency)
default:
return false, nil
}
Expand Down
6 changes: 5 additions & 1 deletion core/types/transaction_marshalling.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ type txJSON struct {
Hash common.Hash `json:"hash"`

// Celo specific fields
FeeCurrency *common.Address `json:"feeCurrency,omitempty"` // nil means native currency
FeeCurrency *common.Address `json:"feeCurrency,omitempty"` // nil means native currency
MaxFeeInFeeCurrency *hexutil.Big `json:"maxFeeInFeeCurrency,omitempty"`
EthCompatible *bool `json:"ethCompatible,omitempty"`
GatewayFee *hexutil.Big `json:"gatewayFee,omitempty"`
GatewayFeeRecipient *common.Address `json:"gatewayFeeRecipient,omitempty"`
}

// yParityValue returns the YParity value from JSON. For backwards-compatibility reasons,
Expand Down

0 comments on commit 3ec26d7

Please sign in to comment.