2021-06-11 13:38:51 +00:00
|
|
|
package types
|
|
|
|
|
|
|
|
import (
|
|
|
|
"math/big"
|
|
|
|
|
2021-06-30 15:28:38 +00:00
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2021-06-11 13:38:51 +00:00
|
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
2021-06-30 15:28:38 +00:00
|
|
|
|
2021-06-11 13:38:51 +00:00
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
2021-06-30 15:28:38 +00:00
|
|
|
|
2021-06-22 10:49:18 +00:00
|
|
|
"github.com/tharsis/ethermint/types"
|
2021-06-11 13:38:51 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// var _ ethtypes.TxData = &TxData{}
|
|
|
|
|
|
|
|
func newTxData(
|
|
|
|
chainID *big.Int, nonce uint64, to *common.Address, amount *big.Int,
|
|
|
|
gasLimit uint64, gasPrice *big.Int, input []byte, accesses *ethtypes.AccessList,
|
|
|
|
) *TxData {
|
|
|
|
txData := &TxData{
|
|
|
|
Nonce: nonce,
|
|
|
|
GasLimit: gasLimit,
|
|
|
|
}
|
|
|
|
|
2021-06-21 14:17:31 +00:00
|
|
|
txData.Input = common.CopyBytes(input)
|
2021-06-11 13:38:51 +00:00
|
|
|
|
|
|
|
if to != nil {
|
|
|
|
txData.To = to.Hex()
|
|
|
|
}
|
|
|
|
|
|
|
|
if accesses != nil {
|
|
|
|
txData.Accesses = NewAccessList(accesses)
|
|
|
|
|
|
|
|
// NOTE: we don't populate chain id on LegacyTx type
|
|
|
|
if chainID != nil {
|
2021-06-30 15:28:38 +00:00
|
|
|
txData.ChainID = sdk.NewIntFromBigInt(chainID)
|
2021-06-11 13:38:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if amount != nil {
|
2021-06-30 15:28:38 +00:00
|
|
|
txData.Amount = sdk.NewIntFromBigInt(amount)
|
2021-06-11 13:38:51 +00:00
|
|
|
}
|
|
|
|
if gasPrice != nil {
|
2021-06-30 15:28:38 +00:00
|
|
|
txData.GasPrice = sdk.NewIntFromBigInt(gasPrice)
|
2021-06-11 13:38:51 +00:00
|
|
|
}
|
|
|
|
return txData
|
|
|
|
}
|
|
|
|
|
2021-06-30 15:28:38 +00:00
|
|
|
// Type returns the tx type
|
|
|
|
func (data TxData) Type() uint8 {
|
|
|
|
return data.txType()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (data *TxData) txType() uint8 {
|
2021-06-11 13:38:51 +00:00
|
|
|
if data.Accesses == nil {
|
|
|
|
return ethtypes.LegacyTxType
|
|
|
|
}
|
|
|
|
return ethtypes.AccessListTxType
|
|
|
|
}
|
|
|
|
|
|
|
|
func (data *TxData) chainID() *big.Int {
|
|
|
|
if data.txType() == ethtypes.LegacyTxType {
|
|
|
|
v, _, _ := data.rawSignatureValues()
|
|
|
|
return DeriveChainID(v)
|
|
|
|
}
|
|
|
|
|
2021-06-30 15:28:38 +00:00
|
|
|
return data.ChainID.BigInt()
|
2021-06-11 13:38:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (data *TxData) accessList() ethtypes.AccessList {
|
|
|
|
if data.Accesses == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return *data.Accesses.ToEthAccessList()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (data *TxData) data() []byte {
|
|
|
|
return common.CopyBytes(data.Input)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (data *TxData) gas() uint64 {
|
|
|
|
return data.GasLimit
|
|
|
|
}
|
|
|
|
|
|
|
|
func (data *TxData) gasPrice() *big.Int {
|
2021-06-30 15:28:38 +00:00
|
|
|
return data.GasPrice.BigInt()
|
2021-06-11 13:38:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (data *TxData) amount() *big.Int {
|
2021-06-30 15:28:38 +00:00
|
|
|
return data.Amount.BigInt()
|
2021-06-11 13:38:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (data *TxData) nonce() uint64 { return data.Nonce }
|
|
|
|
|
|
|
|
func (data *TxData) to() *common.Address {
|
|
|
|
if data.To == "" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
to := common.HexToAddress(data.To)
|
|
|
|
return &to
|
|
|
|
}
|
|
|
|
|
|
|
|
// AsEthereumData returns an AccessListTx transaction data from the proto-formatted
|
|
|
|
// TxData defined on the Cosmos EVM.
|
|
|
|
func (data *TxData) AsEthereumData() ethtypes.TxData {
|
|
|
|
v, r, s := data.rawSignatureValues()
|
|
|
|
if data.Accesses == nil {
|
|
|
|
return ðtypes.LegacyTx{
|
|
|
|
Nonce: data.nonce(),
|
|
|
|
GasPrice: data.gasPrice(),
|
|
|
|
Gas: data.gas(),
|
|
|
|
To: data.to(),
|
|
|
|
Value: data.amount(),
|
|
|
|
Data: data.data(),
|
|
|
|
V: v,
|
|
|
|
R: r,
|
|
|
|
S: s,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ðtypes.AccessListTx{
|
|
|
|
ChainID: data.chainID(),
|
|
|
|
Nonce: data.nonce(),
|
|
|
|
GasPrice: data.gasPrice(),
|
|
|
|
Gas: data.gas(),
|
|
|
|
To: data.to(),
|
|
|
|
Value: data.amount(),
|
|
|
|
Data: data.data(),
|
|
|
|
AccessList: data.accessList(),
|
|
|
|
V: v,
|
|
|
|
R: r,
|
|
|
|
S: s,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// rawSignatureValues returns the V, R, S signature values of the transaction.
|
|
|
|
// The return values should not be modified by the caller.
|
|
|
|
func (data *TxData) rawSignatureValues() (v, r, s *big.Int) {
|
|
|
|
if len(data.V) > 0 {
|
|
|
|
v = new(big.Int).SetBytes(data.V)
|
|
|
|
}
|
|
|
|
if len(data.R) > 0 {
|
|
|
|
r = new(big.Int).SetBytes(data.R)
|
|
|
|
}
|
|
|
|
if len(data.S) > 0 {
|
|
|
|
s = new(big.Int).SetBytes(data.S)
|
|
|
|
}
|
|
|
|
return v, r, s
|
|
|
|
}
|
|
|
|
|
|
|
|
func (data *TxData) setSignatureValues(chainID, v, r, s *big.Int) {
|
|
|
|
if v != nil {
|
|
|
|
data.V = v.Bytes()
|
|
|
|
}
|
|
|
|
if r != nil {
|
|
|
|
data.R = r.Bytes()
|
|
|
|
}
|
|
|
|
if s != nil {
|
|
|
|
data.S = s.Bytes()
|
|
|
|
}
|
|
|
|
if data.txType() == ethtypes.AccessListTxType && chainID != nil {
|
2021-06-30 15:28:38 +00:00
|
|
|
data.ChainID = sdk.NewIntFromBigInt(chainID)
|
2021-06-11 13:38:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate performs a basic validation of the tx data fields.
|
|
|
|
func (data TxData) Validate() error {
|
|
|
|
gasPrice := data.gasPrice()
|
|
|
|
if gasPrice == nil {
|
|
|
|
return sdkerrors.Wrap(ErrInvalidGasPrice, "cannot be nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
if gasPrice.Sign() == -1 {
|
|
|
|
return sdkerrors.Wrapf(ErrInvalidGasPrice, "gas price cannot be negative %s", gasPrice)
|
|
|
|
}
|
|
|
|
|
|
|
|
amount := data.amount()
|
|
|
|
// Amount can be 0
|
2021-06-14 12:42:34 +00:00
|
|
|
if amount != nil && amount.Sign() == -1 {
|
2021-06-11 13:38:51 +00:00
|
|
|
return sdkerrors.Wrapf(ErrInvalidAmount, "amount cannot be negative %s", amount)
|
|
|
|
}
|
|
|
|
|
|
|
|
if data.To != "" {
|
|
|
|
if err := types.ValidateAddress(data.To); err != nil {
|
|
|
|
return sdkerrors.Wrap(err, "invalid to address")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if data.txType() == ethtypes.AccessListTxType && data.chainID() == nil {
|
|
|
|
return sdkerrors.Wrap(
|
|
|
|
sdkerrors.ErrInvalidChainID,
|
|
|
|
"chain ID must be present on AccessList txs",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeriveChainID derives the chain id from the given v parameter
|
|
|
|
func DeriveChainID(v *big.Int) *big.Int {
|
|
|
|
if v == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.BitLen() <= 64 {
|
|
|
|
v := v.Uint64()
|
|
|
|
if v == 27 || v == 28 {
|
|
|
|
return new(big.Int)
|
|
|
|
}
|
|
|
|
return new(big.Int).SetUint64((v - 35) / 2)
|
|
|
|
}
|
|
|
|
v = new(big.Int).Sub(v, big.NewInt(35))
|
|
|
|
return v.Div(v, big.NewInt(2))
|
|
|
|
}
|