Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: copy evm & feemarket types to sdk-go #265

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@
.chain_cookie
.exchange_cookie
coverage.out
.anima
.vscode

4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ copy-chain-types:
rm -rf chain/stream/types/*test.go rm -rf chain/stream/types/*gw.go
cp ../injective-core/injective-chain/types/*.go chain/types
rm -rf chain/types/*test.go rm -rf chain/types/*gw.go
cp ../injective-core/injective-chain/modules/evm/types/*.go chain/evm/types
rm -rf chain/evm/types/*test.go rm -rf chain/evm/types/*gw.go
cp ../injective-core/injective-chain/modules/feemarket/types/*.go chain/feemarket/types
rm -rf chain/feemarket/types/*test.go rm -rf chain/feemarket/types/*gw.go

@echo "👉 Replace injective-core/injective-chain/modules with sdk-go/chain"
@echo "👉 Replace injective-core/injective-chain/codec with sdk-go/chain/codec"
Expand Down
55 changes: 55 additions & 0 deletions chain/evm/types/access_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package types

import (
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
)

// AccessList is an EIP-2930 access list that represents the slice of
// the protobuf AccessTuples.
type AccessList []AccessTuple

// NewAccessList creates a new protobuf-compatible AccessList from an ethereum
// core AccessList type
func NewAccessList(ethAccessList *ethtypes.AccessList) AccessList {
if ethAccessList == nil {
return nil
}

al := AccessList{}
for _, tuple := range *ethAccessList {
storageKeys := make([]string, len(tuple.StorageKeys))

for i := range tuple.StorageKeys {
storageKeys[i] = tuple.StorageKeys[i].String()
}

al = append(al, AccessTuple{
Address: tuple.Address.String(),
StorageKeys: storageKeys,
})
}

return al
}

// ToEthAccessList is an utility function to convert the protobuf compatible
// AccessList to eth core AccessList from go-ethereum
func (al AccessList) ToEthAccessList() *ethtypes.AccessList {
var ethAccessList ethtypes.AccessList

for _, tuple := range al {
storageKeys := make([]common.Hash, len(tuple.StorageKeys))

for i := range tuple.StorageKeys {
storageKeys[i] = common.HexToHash(tuple.StorageKeys[i])
}

ethAccessList = append(ethAccessList, ethtypes.AccessTuple{
Address: common.HexToAddress(tuple.Address),
StorageKeys: storageKeys,
})
}

return &ethAccessList
}
249 changes: 249 additions & 0 deletions chain/evm/types/access_list_tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
package types

import (
"math/big"

errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
errortypes "github.com/cosmos/cosmos-sdk/types/errors"

"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"

"github.com/InjectiveLabs/sdk-go/chain/types"
)

func NewAccessListTx(tx *ethtypes.Transaction) (*AccessListTx, error) {
txData := &AccessListTx{
Nonce: tx.Nonce(),
Data: tx.Data(),
GasLimit: tx.Gas(),
}

v, r, s := tx.RawSignatureValues()
if to := tx.To(); to != nil {
txData.To = to.Hex()
}

if tx.Value() != nil {
amountInt, err := types.SafeNewIntFromBigInt(tx.Value())
if err != nil {
return nil, err
}
txData.Amount = &amountInt
}

if tx.GasPrice() != nil {
gasPriceInt, err := types.SafeNewIntFromBigInt(tx.GasPrice())
if err != nil {
return nil, err
}
txData.GasPrice = &gasPriceInt
}

if tx.AccessList() != nil {
al := tx.AccessList()
txData.Accesses = NewAccessList(&al)
}

txData.SetSignatureValues(tx.ChainId(), v, r, s)
return txData, nil
}

// TxType returns the tx type
func (tx *AccessListTx) TxType() uint8 {
return ethtypes.AccessListTxType
}

// Copy returns an instance with the same field values
func (tx *AccessListTx) Copy() TxData {
return &AccessListTx{
ChainID: tx.ChainID,
Nonce: tx.Nonce,
GasPrice: tx.GasPrice,
GasLimit: tx.GasLimit,
To: tx.To,
Amount: tx.Amount,
Data: common.CopyBytes(tx.Data),
Accesses: tx.Accesses,
V: common.CopyBytes(tx.V),
R: common.CopyBytes(tx.R),
S: common.CopyBytes(tx.S),
}
Comment on lines +60 to +72
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Ensure deep copy of the Accesses field in the Copy method

In the Copy() method, the Accesses field is a pointer to an AccessList. Assigning it directly could lead to shared mutable state between the original and the copied transaction. To prevent unintended side effects, consider performing a deep copy of the Accesses field.

You can modify the Copy() method as follows to create a deep copy of the Accesses:

func (tx *AccessListTx) Copy() TxData {
	var accesses *AccessList
	if tx.Accesses != nil {
		accessesCopy := tx.Accesses.Copy() // Implement a Copy method for AccessList
		accesses = &accessesCopy
	}
	return &AccessListTx{
		ChainID:  tx.ChainID,
		Nonce:    tx.Nonce,
		GasPrice: tx.GasPrice,
		GasLimit: tx.GasLimit,
		To:       tx.To,
		Amount:   tx.Amount,
		Data:     common.CopyBytes(tx.Data),
		Accesses: accesses,
		V:        common.CopyBytes(tx.V),
		R:        common.CopyBytes(tx.R),
		S:        common.CopyBytes(tx.S),
	}
}

Ensure that the AccessList type has a Copy() method implemented to facilitate deep copying.

}

// GetChainID returns the chain id field from the AccessListTx
func (tx *AccessListTx) GetChainID() *big.Int {
if tx.ChainID == nil {
return nil
}

return tx.ChainID.BigInt()
}

// GetAccessList returns the AccessList field.
func (tx *AccessListTx) GetAccessList() ethtypes.AccessList {
if tx.Accesses == nil {
return nil
}
return *tx.Accesses.ToEthAccessList()
}

// GetData returns the a copy of the input data bytes.
func (tx *AccessListTx) GetData() []byte {
return common.CopyBytes(tx.Data)
}

// GetGas returns the gas limit.
func (tx *AccessListTx) GetGas() uint64 {
return tx.GasLimit
}

// GetGasPrice returns the gas price field.
func (tx *AccessListTx) GetGasPrice() *big.Int {
if tx.GasPrice == nil {
return nil
}
return tx.GasPrice.BigInt()
}

// GetGasTipCap returns the gas price field.
func (tx *AccessListTx) GetGasTipCap() *big.Int {
return tx.GetGasPrice()
}

// GetGasFeeCap returns the gas price field.
func (tx *AccessListTx) GetGasFeeCap() *big.Int {
return tx.GetGasPrice()
}

// GetValue returns the tx amount.
func (tx *AccessListTx) GetValue() *big.Int {
if tx.Amount == nil {
return nil
}

return tx.Amount.BigInt()
}

// GetNonce returns the account sequence for the transaction.
func (tx *AccessListTx) GetNonce() uint64 { return tx.Nonce }

// GetTo returns the pointer to the recipient address.
func (tx *AccessListTx) GetTo() *common.Address {
if tx.To == "" {
return nil
}
to := common.HexToAddress(tx.To)
return &to
}

// AsEthereumData returns an AccessListTx transaction tx from the proto-formatted
// TxData defined on the Cosmos EVM.
func (tx *AccessListTx) AsEthereumData() ethtypes.TxData {
v, r, s := tx.GetRawSignatureValues()
return &ethtypes.AccessListTx{
ChainID: tx.GetChainID(),
Nonce: tx.GetNonce(),
GasPrice: tx.GetGasPrice(),
Gas: tx.GetGas(),
To: tx.GetTo(),
Value: tx.GetValue(),
Data: tx.GetData(),
AccessList: tx.GetAccessList(),
V: v,
R: r,
S: s,
}
}

// GetRawSignatureValues returns the V, R, S signature values of the transaction.
// The return values should not be modified by the caller.
func (tx *AccessListTx) GetRawSignatureValues() (v, r, s *big.Int) {
return rawSignatureValues(tx.V, tx.R, tx.S)
}

// SetSignatureValues sets the signature values to the transaction.
func (tx *AccessListTx) SetSignatureValues(chainID, v, r, s *big.Int) {
if v != nil {
tx.V = v.Bytes()
}
if r != nil {
tx.R = r.Bytes()
}
if s != nil {
tx.S = s.Bytes()
}
if chainID != nil {
chainIDInt := sdkmath.NewIntFromBigInt(chainID)
tx.ChainID = &chainIDInt
}
}

// Validate performs a stateless validation of the tx fields.
func (tx AccessListTx) Validate() error {
gasPrice := tx.GetGasPrice()
if gasPrice == nil {
return errorsmod.Wrap(ErrInvalidGasPrice, "cannot be nil")
}
if !types.IsValidInt256(gasPrice) {
return errorsmod.Wrap(ErrInvalidGasPrice, "out of bound")
}

if gasPrice.Sign() == -1 {
return errorsmod.Wrapf(ErrInvalidGasPrice, "gas price cannot be negative %s", gasPrice)
}

amount := tx.GetValue()
// Amount can be 0
if amount != nil && amount.Sign() == -1 {
return errorsmod.Wrapf(ErrInvalidAmount, "amount cannot be negative %s", amount)
}
if !types.IsValidInt256(amount) {
return errorsmod.Wrap(ErrInvalidAmount, "out of bound")
}
Comment on lines +202 to +204
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handle potential nil amount when validating

In the Validate() method, you are calling IsValidInt256(amount) without checking if amount is nil. If amount is nil, this could cause a nil pointer dereference. Consider adding a check for amount != nil before validating.

Modify the code to include a nil check:

if amount != nil {
	if !types.IsValidInt256(amount) {
		return errorsmod.Wrap(ErrInvalidAmount, "out of bound")
	}
} else {
	// Optional: Decide if a nil amount is acceptable or should return an error
}

This ensures that the validation only proceeds when amount is not nil.


if !types.IsValidInt256(tx.Fee()) {
return errorsmod.Wrap(ErrInvalidGasFee, "out of bound")
}

if tx.To != "" {
if err := types.ValidateAddress(tx.To); err != nil {
return errorsmod.Wrap(err, "invalid to address")
}
}

if tx.GetChainID() == nil {
return errorsmod.Wrap(
errortypes.ErrInvalidChainID,
"chain ID must be present on AccessList txs",
)
}

return nil
}
Comment on lines +184 to +224
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation for GasLimit to prevent zero or excessively high values

The Validate() method does not currently check whether GasLimit is within acceptable ranges. It's important to ensure that GasLimit is greater than zero to prevent transactions with an invalid gas limit.

Consider adding the following validation:

if tx.GasLimit == 0 {
	return errorsmod.Wrap(ErrInvalidGasLimit, "gas limit cannot be zero")
}
// Optional: Set an upper limit if required
const MaxGasLimit = 10000000 // Example maximum value
if tx.GasLimit > MaxGasLimit {
	return errorsmod.Wrapf(ErrInvalidGasLimit, "gas limit exceeds maximum allowed: %d", tx.GasLimit)
}

Adjust MaxGasLimit to the appropriate value based on your network's specifications.


// Fee returns gasprice * gaslimit.
func (tx AccessListTx) Fee() *big.Int {
return fee(tx.GetGasPrice(), tx.GetGas())
}

// Cost returns amount + gasprice * gaslimit.
func (tx AccessListTx) Cost() *big.Int {
return cost(tx.Fee(), tx.GetValue())
}

// EffectiveGasPrice is the same as GasPrice for AccessListTx
func (tx AccessListTx) EffectiveGasPrice(_ *big.Int) *big.Int {
return tx.GetGasPrice()
}

// EffectiveFee is the same as Fee for AccessListTx
func (tx AccessListTx) EffectiveFee(_ *big.Int) *big.Int {
return tx.Fee()
}

// EffectiveCost is the same as Cost for AccessListTx
func (tx AccessListTx) EffectiveCost(_ *big.Int) *big.Int {
return tx.Cost()
}
Loading
Loading