Skip to content

Commit

Permalink
test(evm): backend tests with test network and real txs (#2045)
Browse files Browse the repository at this point in the history
* test(evm): backend tests with test network and real txs

* chore: changelog update
  • Loading branch information
onikonychev authored Sep 21, 2024
1 parent 0929266 commit 5214349
Show file tree
Hide file tree
Showing 28 changed files with 830 additions and 564 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#2031](https://github.com/NibiruChain/nibiru/pull/2031) - fix(evm): debug calls with custom tracer and tracer options
- [#2039](https://github.com/NibiruChain/nibiru/pull/2039) - refactor(rpc-backend): remove unnecessary interface code
- [#2039](https://github.com/NibiruChain/nibiru/pull/2039) - refactor(rpc-backend): Remove mocks from eth/rpc/backend, partially completing [nibiru#2037](https://github.com/NibiruChain/nibiru/issue/2037).
- [#2045](https://github.com/NibiruChain/nibiru/pull/2045) - test(evm): backend tests with test network and real txs

#### Dapp modules: perp, spot, oracle, etc

Expand Down
8 changes: 4 additions & 4 deletions eth/chain_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ func IsValidChainID(chainID string) bool {
return nibiruEvmChainId.MatchString(chainID)
}

// ParseEthChainID parses a string chain identifier's epoch to an
// Ethereum-compatible chain-id in *big.Int format.
// ParseEthChainID parses a string chain identifier's
// to an Ethereum-compatible chain-id in *big.Int format.
//
// This function uses Nibiru's map of chain IDs defined in Nibiru/app/appconst
// rather than the regex of EIP155, which is implemented by
// ParseEthChainIDStrict.
func ParseEthChainID(chainID string) (*big.Int, error) {
return appconst.GetEthChainID(chainID), nil
func ParseEthChainID(chainID string) *big.Int {
return appconst.GetEthChainID(chainID)
}

// ParseEthChainIDStrict parses a string chain identifier's epoch to an
Expand Down
10 changes: 2 additions & 8 deletions eth/eip712/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,7 @@ func decodeAminoSignDoc(signDocBytes []byte) (apitypes.TypedData, error) {
return apitypes.TypedData{}, err
}

chainID, err := eth.ParseEthChainID(aminoDoc.ChainID)
if err != nil {
return apitypes.TypedData{}, errors.New("invalid chain ID passed as argument")
}
chainID := eth.ParseEthChainID(aminoDoc.ChainID)

typedData, err := WrapTxToTypedData(
chainID.Uint64(),
Expand Down Expand Up @@ -167,10 +164,7 @@ func decodeProtobufSignDoc(signDocBytes []byte) (apitypes.TypedData, error) {

signerInfo := authInfo.SignerInfos[0]

chainID, err := eth.ParseEthChainID(signDoc.ChainId)
if err != nil {
return apitypes.TypedData{}, fmt.Errorf("invalid chain ID passed as argument: %w", err)
}
chainID := eth.ParseEthChainID(signDoc.ChainId)

stdFee := &legacytx.StdFee{
Amount: authInfo.Fee.Amount,
Expand Down
11 changes: 2 additions & 9 deletions eth/eip712/encoding_legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,7 @@ func legacyDecodeAminoSignDoc(signDocBytes []byte) (apitypes.TypedData, error) {
feeDelegation := &FeeDelegationOptions{
FeePayer: feePayer,
}

chainID, err := eth.ParseEthChainID(aminoDoc.ChainID)
if err != nil {
return apitypes.TypedData{}, errors.New("invalid chain ID passed as argument")
}
chainID := eth.ParseEthChainID(aminoDoc.ChainID)

typedData, err := LegacyWrapTxToTypedData(
protoCodec,
Expand Down Expand Up @@ -165,10 +161,7 @@ func legacyDecodeProtobufSignDoc(signDocBytes []byte) (apitypes.TypedData, error

signerInfo := authInfo.SignerInfos[0]

chainID, err := eth.ParseEthChainID(signDoc.ChainId)
if err != nil {
return apitypes.TypedData{}, fmt.Errorf("invalid chain ID passed as argument: %w", err)
}
chainID := eth.ParseEthChainID(signDoc.ChainId)

stdFee := &legacytx.StdFee{
Amount: authInfo.Fee.Amount,
Expand Down
22 changes: 11 additions & 11 deletions eth/rpc/backend/account_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/ethereum/go-ethereum/common"
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"

Expand All @@ -22,7 +22,7 @@ import (

// GetCode returns the contract code at the given address and block number.
func (b *Backend) GetCode(
address common.Address, blockNrOrHash rpc.BlockNumberOrHash,
address gethcommon.Address, blockNrOrHash rpc.BlockNumberOrHash,
) (hexutil.Bytes, error) {
blockNum, err := b.BlockNumberFromTendermint(blockNrOrHash)
if err != nil {
Expand All @@ -43,7 +43,7 @@ func (b *Backend) GetCode(

// GetProof returns an account object with proof and any storage proofs
func (b *Backend) GetProof(
address common.Address,
address gethcommon.Address,
storageKeys []string,
blockNrOrHash rpc.BlockNumberOrHash,
) (*rpc.AccountResult, error) {
Expand Down Expand Up @@ -81,7 +81,7 @@ func (b *Backend) GetProof(
storageProofs := make([]rpc.StorageResult, len(storageKeys))

for i, key := range storageKeys {
hexKey := common.HexToHash(key)
hexKey := gethcommon.HexToHash(key)
valueBz, proof, err := b.queryClient.GetProof(clientCtx, evm.StoreKey, evm.StateKey(address, hexKey.Bytes()))
if err != nil {
return nil, err
Expand All @@ -105,7 +105,7 @@ func (b *Backend) GetProof(
}

// query account proofs
accountKey := authtypes.AddressStoreKey(sdk.AccAddress(address.Bytes()))
accountKey := authtypes.AddressStoreKey(address.Bytes())
_, proof, err := b.queryClient.GetProof(clientCtx, authtypes.StoreKey, accountKey)
if err != nil {
return nil, err
Expand All @@ -120,18 +120,18 @@ func (b *Backend) GetProof(
Address: address,
AccountProof: GetHexProofs(proof),
Balance: (*hexutil.Big)(balance.BigInt()),
CodeHash: common.HexToHash(res.CodeHash),
CodeHash: gethcommon.HexToHash(res.CodeHash),
Nonce: hexutil.Uint64(res.Nonce),
// NOTE: The StorageHash is blank. Consider whether this is useful in the
// future. Currently, all storage is handles by persistent and transient
// `sdk.KVStore` objects.
StorageHash: common.Hash{},
StorageHash: gethcommon.Hash{},
StorageProof: storageProofs,
}, nil
}

// GetStorageAt returns the contract storage at the given address, block number, and key.
func (b *Backend) GetStorageAt(address common.Address, key string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
func (b *Backend) GetStorageAt(address gethcommon.Address, key string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) {
blockNum, err := b.BlockNumberFromTendermint(blockNrOrHash)
if err != nil {
return nil, err
Expand All @@ -147,13 +147,13 @@ func (b *Backend) GetStorageAt(address common.Address, key string, blockNrOrHash
return nil, err
}

value := common.HexToHash(res.Value)
value := gethcommon.HexToHash(res.Value)
return value.Bytes(), nil
}

// GetBalance returns the provided account's balance up to the provided block number.
func (b *Backend) GetBalance(
address common.Address,
address gethcommon.Address,
blockNrOrHash rpc.BlockNumberOrHash,
) (*hexutil.Big, error) {
blockNum, err := b.BlockNumberFromTendermint(blockNrOrHash)
Expand Down Expand Up @@ -189,7 +189,7 @@ func (b *Backend) GetBalance(
}

// GetTransactionCount returns the number of transactions at the given address up to the given block number.
func (b *Backend) GetTransactionCount(address common.Address, blockNum rpc.BlockNumber) (*hexutil.Uint64, error) {
func (b *Backend) GetTransactionCount(address gethcommon.Address, blockNum rpc.BlockNumber) (*hexutil.Uint64, error) {
n := hexutil.Uint64(0)
bn, err := b.BlockNumber()
if err != nil {
Expand Down
201 changes: 201 additions & 0 deletions eth/rpc/backend/account_info_test.go
Original file line number Diff line number Diff line change
@@ -1 +1,202 @@
package backend_test

import (
"math/big"

gethcommon "github.com/ethereum/go-ethereum/common"
"golang.org/x/crypto/sha3"

"github.com/NibiruChain/nibiru/v2/x/evm/evmtest"

rpc "github.com/NibiruChain/nibiru/v2/eth/rpc"
)

func (s *BackendSuite) TestGetCode() {
testCases := []struct {
name string
contractAddr gethcommon.Address
blockNumber rpc.BlockNumber
codeFound bool
}{
{
name: "happy: valid contract address",
contractAddr: testContractAddress,
blockNumber: deployContractBlockNumber,
codeFound: true,
},
{
name: "sad: not a contract address",
contractAddr: s.fundedAccEthAddr,
blockNumber: deployContractBlockNumber,
codeFound: false,
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
code, err := s.backend.GetCode(
tc.contractAddr,
rpc.BlockNumberOrHash{
BlockNumber: &tc.blockNumber,
},
)
if !tc.codeFound {
s.Require().Nil(code)
return
}
s.Require().NoError(err)
s.Require().NotNil(code)
})
}
}

func (s *BackendSuite) TestGetProof() {
testCases := []struct {
name string
contractAddr gethcommon.Address
blockNumber rpc.BlockNumber
address gethcommon.Address
slot uint64
wantValue string
}{
{
name: "happy: balance of the contract deployer",
contractAddr: testContractAddress,
address: s.fundedAccEthAddr,
blockNumber: deployContractBlockNumber,
slot: 0, // _balances is the first slot in ERC20
wantValue: "0xd3c21bcecceda1000000", // = 1000000 * (10**18), initial supply
},
{
name: "sad: address which is not in contract storage",
contractAddr: s.fundedAccEthAddr,
address: recipient,
blockNumber: deployContractBlockNumber,
slot: 0,
wantValue: "0x0",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
proof, err := s.backend.GetProof(
tc.contractAddr,
[]string{generateStorageKey(tc.address, tc.slot)},
rpc.BlockNumberOrHash{
BlockNumber: &tc.blockNumber,
},
)
s.Require().NoError(err)
s.Require().NotNil(proof)
s.Require().Equal(tc.wantValue, proof.StorageProof[0].Value.String())
})
}
}

func (s *BackendSuite) TestGetStorageAt() {
testCases := []struct {
name string
contractAddr gethcommon.Address
blockNumber rpc.BlockNumber
address gethcommon.Address
slot uint64
wantValue string
}{
{
name: "happy: balance of the contract deployer",
contractAddr: testContractAddress,
address: s.fundedAccEthAddr,
blockNumber: deployContractBlockNumber,
// _balances is the first slot in ERC20
slot: 0,
// = 1000000 * (10**18), initial supply
wantValue: "0x00000000000000000000000000000000000000000000d3c21bcecceda1000000",
},
{
name: "sad: address which is not in contract storage",
contractAddr: s.fundedAccEthAddr,
address: recipient,
blockNumber: deployContractBlockNumber,
slot: 0,
wantValue: "0x0000000000000000000000000000000000000000000000000000000000000000",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
value, err := s.backend.GetStorageAt(
tc.contractAddr,
generateStorageKey(tc.address, tc.slot),
rpc.BlockNumberOrHash{
BlockNumber: &tc.blockNumber,
},
)
s.Require().NoError(err)
s.Require().NotNil(value)
s.Require().Equal(tc.wantValue, value.String())
})
}
}

func (s *BackendSuite) TestGetBalance() {
testCases := []struct {
name string
blockNumber rpc.BlockNumber
address gethcommon.Address
wantPositiveBalance bool
}{
{
name: "happy: funded account balance",
address: s.fundedAccEthAddr,
blockNumber: transferTxBlockNumber,
wantPositiveBalance: true,
},
{
name: "happy: recipient balance at block 1",
address: recipient,
blockNumber: rpc.NewBlockNumber(big.NewInt(1)),
wantPositiveBalance: false,
},
{
name: "happy: recipient balance after transfer",
address: recipient,
blockNumber: transferTxBlockNumber,
wantPositiveBalance: true,
},
{
name: "sad: not existing account",
address: evmtest.NewEthPrivAcc().EthAddr,
blockNumber: transferTxBlockNumber,
wantPositiveBalance: false,
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
balance, err := s.backend.GetBalance(
tc.address,
rpc.BlockNumberOrHash{
BlockNumber: &tc.blockNumber,
},
)
s.Require().NoError(err)
s.Require().NotNil(balance)
if tc.wantPositiveBalance {
s.Require().Greater(balance.ToInt().Int64(), int64(0))
} else {
s.Require().Equal(balance.ToInt().Int64(), int64(0))
}
})
}
}

// generateStorageKey produces the storage key from address and slot (order of the variable in solidity declaration)
func generateStorageKey(key gethcommon.Address, slot uint64) string {
// Prepare the key and slot as 32-byte values
keyBytes := gethcommon.LeftPadBytes(key.Bytes(), 32)
slotBytes := gethcommon.LeftPadBytes(new(big.Int).SetUint64(slot).Bytes(), 32)

// Concatenate key and slot
data := append(keyBytes, slotBytes...)

// Hash the data using Keccak256
hash := sha3.NewLegacyKeccak256()
hash.Write(data)
return gethcommon.BytesToHash(hash.Sum(nil)).Hex()
}
6 changes: 1 addition & 5 deletions eth/rpc/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,7 @@ func NewBackend(
allowUnprotectedTxs bool,
indexer eth.EVMTxIndexer,
) *Backend {
chainID, err := eth.ParseEthChainID(clientCtx.ChainID)
if err != nil {
panic(err)
}

chainID := eth.ParseEthChainID(clientCtx.ChainID)
appConf, err := config.GetConfig(ctx.Viper)
if err != nil {
panic(err)
Expand Down
Loading

0 comments on commit 5214349

Please sign in to comment.