-
Notifications
You must be signed in to change notification settings - Fork 46
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
base: master
Are you sure you want to change the base?
Changes from 1 commit
a074c61
e0c8441
afaf354
6a088d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,6 @@ | |
.chain_cookie | ||
.exchange_cookie | ||
coverage.out | ||
.anima | ||
.vscode | ||
|
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 ðAccessList | ||
} |
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), | ||
} | ||
} | ||
|
||
// 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 ðtypes.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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle potential nil In the 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 |
||
|
||
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add validation for The 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 |
||
|
||
// 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() | ||
} |
There was a problem hiding this comment.
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 theCopy
methodIn the
Copy()
method, theAccesses
field is a pointer to anAccessList
. 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 theAccesses
field.You can modify the
Copy()
method as follows to create a deep copy of theAccesses
:Ensure that the
AccessList
type has aCopy()
method implemented to facilitate deep copying.