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

test(evm): eth api integration test suite #1887

Merged
merged 31 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
faa464b
test(evm): eth api integration test suite
onikonychev May 18, 2024
5d9b532
test(evm): more integration tests for eth api
onikonychev May 20, 2024
fbf929d
feat: implemented basic evm tx methods
onikonychev May 20, 2024
0489f85
chore: lint
onikonychev May 20, 2024
d44aa45
chore: lint
onikonychev May 20, 2024
1c5f574
chore: lint
onikonychev May 20, 2024
ec9beb1
chore: extracted evm chain_id to appconst
onikonychev May 20, 2024
a3b7b6b
fix: tests
onikonychev May 20, 2024
e110347
Merge branch 'ud/eth' into on/evm-query-tests
Unique-Divine May 21, 2024
14aae36
Merge branch 'ud/eth' into on/evm-query-tests
Unique-Divine May 21, 2024
4213a19
chore: resolve conflicts
onikonychev May 22, 2024
79516f5
fix: tests
onikonychev May 22, 2024
91d9e4c
fix: lint
onikonychev May 22, 2024
a6a5409
chore: cleanup
onikonychev May 22, 2024
e38b9a5
Merge branch 'on/evm-tx' into on/evm-query-tests
onikonychev May 22, 2024
48d32e2
Merge branch 'ud/eth' into on/evm-query-tests
Unique-Divine May 22, 2024
9001ade
Merge branch 'on/evm-query-tests' of https://github.com/NibiruChain/n…
Unique-Divine May 22, 2024
a7731b7
refactor: merge
Unique-Divine May 22, 2024
dfd49a8
fix: eth chain ID test
Unique-Divine May 22, 2024
f8d2b3a
fix(eth): chain ID
Unique-Divine May 22, 2024
6290748
linter fix
Unique-Divine May 22, 2024
0b4931a
Merge branch 'ud/eth' of github.com:NibiruChain/nibiru into on/evm-qu…
onikonychev May 23, 2024
f5b61ce
test(evm): integration tests for tx and smart contract
onikonychev May 23, 2024
789e143
fix: lint
onikonychev May 23, 2024
4043354
chore(evm): disabled json rpc by default
onikonychev May 23, 2024
cc674fb
Merge branch 'ud/eth' of github.com:NibiruChain/nibiru into on/evm-qu…
onikonychev May 27, 2024
068b8c5
Merge branch 'ud/eth' into on/evm-query-tests
Unique-Divine May 27, 2024
0b35117
refactor: merge conflicts
Unique-Divine May 27, 2024
7ab7445
test(rpcapi): make fixtures consistent
Unique-Divine May 27, 2024
67df3cc
fix(evm): integration test fixture setup
onikonychev May 27, 2024
00edd09
chore: linter
Unique-Divine May 27, 2024
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
2 changes: 1 addition & 1 deletion app/server/config/server_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ func GetAPINamespaces() []string {
// DefaultJSONRPCConfig returns an EVM config with the JSON-RPC API enabled by default
func DefaultJSONRPCConfig() *JSONRPCConfig {
return &JSONRPCConfig{
Enable: true,
Enable: false,
API: GetDefaultAPINamespaces(),
Address: DefaultJSONRPCAddress,
WsAddress: DefaultJSONRPCWsAddress,
Expand Down
4 changes: 4 additions & 0 deletions contrib/scripts/localnet.sh
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ $BINARY config # Prints config.
echo_info "config/app.toml: Enabling API server"
sed -i $SEDOPTION '/\[api\]/,+3 s/enable = false/enable = true/' $CHAIN_DIR/config/app.toml

# Enable JSON RPC Server
echo_info "config/app.toml: Enabling JSON API server"
sed -i $SEDOPTION '/\[json\-rpc\]/,+3 s/enable = false/enable = true/' $CHAIN_DIR/config/app.toml

# Enable Swagger Docs
echo_info "config/app.toml: Enabling Swagger Docs"
sed -i $SEDOPTION 's/swagger = false/swagger = true/' $CHAIN_DIR/config/app.toml
Expand Down
318 changes: 318 additions & 0 deletions eth/rpc/rpcapi/eth_api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
package rpcapi_test

import (
"context"
"crypto/ecdsa"
"math/big"
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum"
ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/params"

"github.com/NibiruChain/nibiru/app/appconst"
nibiCommon "github.com/NibiruChain/nibiru/x/common"
"github.com/NibiruChain/nibiru/x/common/denoms"
"github.com/NibiruChain/nibiru/x/evm/evmtest"

"github.com/stretchr/testify/suite"

"github.com/NibiruChain/nibiru/app"
"github.com/NibiruChain/nibiru/x/common/testutil"
testutilcli "github.com/NibiruChain/nibiru/x/common/testutil/cli"
"github.com/NibiruChain/nibiru/x/common/testutil/genesis"
"github.com/NibiruChain/nibiru/x/common/testutil/testapp"
)

type IntegrationSuite struct {
suite.Suite
cfg testutilcli.Config
network *testutilcli.Network

ethClient *ethclient.Client

fundedAccPrivateKey *ecdsa.PrivateKey
fundedAccEthAddr ethCommon.Address
fundedAccNibiAddr sdk.AccAddress

contractData evmtest.CompiledEvmContract
}

func TestSuite_IntegrationSuite_RunAll(t *testing.T) {
suite.Run(t, new(IntegrationSuite))
}

// SetupSuite initialize network
func (s *IntegrationSuite) SetupSuite() {
testutil.BeforeIntegrationSuite(s.T())
testapp.EnsureNibiruPrefix()

genState := genesis.NewTestGenesisState(app.MakeEncodingConfig())
homeDir := s.T().TempDir()
s.cfg = testutilcli.BuildNetworkConfig(genState)
network, err := testutilcli.New(s.T(), homeDir, s.cfg)
s.Require().NoError(err)

s.network = network
s.ethClient = network.Validators[0].JSONRPCClient

s.contractData = evmtest.SmartContract_FunToken.Load(s.T())

testAccPrivateKey, _ := crypto.GenerateKey()
s.fundedAccPrivateKey = testAccPrivateKey
s.fundedAccEthAddr = crypto.PubkeyToAddress(testAccPrivateKey.PublicKey)
s.fundedAccNibiAddr = evmtest.EthAddrToNibiruAddr(s.fundedAccEthAddr)

val := s.network.Validators[0]

funds := sdk.NewCoins(sdk.NewInt64Coin(denoms.NIBI, 100_000_000)) // 10 NIBI
s.NoError(testutilcli.FillWalletFromValidator(s.fundedAccNibiAddr, funds, val, denoms.NIBI))
s.NoError(s.network.WaitForNextBlock())
}

// Test_ChainID EVM method: eth_chainId
func (s *IntegrationSuite) Test_ChainID() {
ethChainID, err := s.ethClient.ChainID(context.Background())
s.NoError(err)
s.Equal(appconst.ETH_CHAIN_ID_DEFAULT, ethChainID.Int64())
}

// Test_BlockNumber EVM method: eth_blockNumber
func (s *IntegrationSuite) Test_BlockNumber() {
networkBlockNumber, err := s.network.LatestHeight()
s.NoError(err)

ethBlockNumber, err := s.ethClient.BlockNumber(context.Background())
s.NoError(err)
s.Equal(networkBlockNumber, int64(ethBlockNumber))
}

// Test_BlockByNumber EVM method: eth_getBlockByNumber
func (s *IntegrationSuite) Test_BlockByNumber() {
networkBlockNumber, err := s.network.LatestHeight()
s.NoError(err)

ethBlock, err := s.ethClient.BlockByNumber(context.Background(), big.NewInt(networkBlockNumber))
s.NoError(err)

// TODO: add more checks about the eth block
s.NotNil(ethBlock)
}

// Test_BalanceAt EVM method: eth_getBalance
func (s *IntegrationSuite) Test_BalanceAt() {
testAccEthAddr := ethCommon.BytesToAddress(testutilcli.NewAccount(s.network, "new-user"))

// New user balance should be 0
balance, err := s.ethClient.BalanceAt(context.Background(), testAccEthAddr, nil)
s.NoError(err)
s.NotNil(balance)
s.Equal(int64(0), balance.Int64())

// Funded account balance should be > 0
balance, err = s.ethClient.BalanceAt(context.Background(), s.fundedAccEthAddr, nil)
s.NoError(err)
s.NotNil(balance)
s.Greater(balance.Int64(), int64(0))
}

// Test_StorageAt EVM method: eth_getStorageAt
func (s *IntegrationSuite) Test_StorageAt() {
storage, err := s.ethClient.StorageAt(
context.Background(), s.fundedAccEthAddr, ethCommon.Hash{}, nil,
)
s.NoError(err)
// TODO: add more checks
s.NotNil(storage)
}

// Test_PendingStorageAt EVM method: eth_getStorageAt | pending
func (s *IntegrationSuite) Test_PendingStorageAt() {
storage, err := s.ethClient.PendingStorageAt(
context.Background(), s.fundedAccEthAddr, ethCommon.Hash{},
)
s.NoError(err)

// TODO: add more checks
s.NotNil(storage)
}

// Test_CodeAt EVM method: eth_getCode
func (s *IntegrationSuite) Test_CodeAt() {
code, err := s.ethClient.CodeAt(context.Background(), s.fundedAccEthAddr, nil)
s.NoError(err)

// TODO: add more checks
s.NotNil(code)
}

// Test_PendingCodeAt EVM method: eth_getCode
func (s *IntegrationSuite) Test_PendingCodeAt() {
code, err := s.ethClient.PendingCodeAt(context.Background(), s.fundedAccEthAddr)
s.NoError(err)

// TODO: add more checks
s.NotNil(code)
}

// Test_EstimateGas EVM method: eth_estimateGas
func (s *IntegrationSuite) Test_EstimateGas() {
testAccEthAddr := ethCommon.BytesToAddress(testutilcli.NewAccount(s.network, "new-user"))
gasLimit := uint64(21000)
msg := ethereum.CallMsg{
From: s.fundedAccEthAddr,
To: &testAccEthAddr,
Gas: gasLimit,
Value: big.NewInt(1),
}
gasEstimated, err := s.ethClient.EstimateGas(context.Background(), msg)
s.NoError(err)
s.Equal(gasEstimated, gasLimit)
}

// Test_SuggestGasPrice EVM method: eth_gasPrice
func (s *IntegrationSuite) Test_SuggestGasPrice() {
// TODO: the backend method is stubbed to 0
_, err := s.ethClient.SuggestGasPrice(context.Background())
s.NoError(err)
}

// Test_SimpleTransferTransaction EVM method: eth_sendRawTransaction
func (s *IntegrationSuite) Test_SimpleTransferTransaction() {
chainID, err := s.ethClient.ChainID(context.Background())
s.NoError(err)
nonce, err := s.ethClient.PendingNonceAt(context.Background(), s.fundedAccEthAddr)
s.NoError(err)

senderBalanceBefore, err := s.ethClient.BalanceAt(
context.Background(), s.fundedAccEthAddr, nil,
)
s.NoError(err)
recipientAddr := ethCommon.BytesToAddress(testutilcli.NewAccount(s.network, "recipient"))
recipientBalanceBefore, err := s.ethClient.BalanceAt(context.Background(), recipientAddr, nil)
s.NoError(err)
s.Equal(int64(0), recipientBalanceBefore.Int64())

amountToSend := big.NewInt(1000)

signer := types.LatestSignerForChainID(chainID)
tx, err := types.SignNewTx(
s.fundedAccPrivateKey,
signer,
&types.LegacyTx{
Nonce: nonce,
To: &recipientAddr,
Value: amountToSend,
Gas: params.TxGas,
GasPrice: big.NewInt(1),
})
s.NoError(err)
err = s.ethClient.SendTransaction(context.Background(), tx)
s.NoError(err)
s.NoError(s.network.WaitForNextBlock())

senderAmountAfter, err := s.ethClient.BalanceAt(context.Background(), s.fundedAccEthAddr, nil)
s.NoError(err)

expectedSenderBalance := senderBalanceBefore.Sub(senderBalanceBefore, amountToSend)
expectedSenderBalance = expectedSenderBalance.Sub(senderBalanceBefore, big.NewInt(int64(params.TxGas)))

s.Equal(expectedSenderBalance.Int64(), senderAmountAfter.Int64())

recipientBalanceAfter, err := s.ethClient.BalanceAt(context.Background(), recipientAddr, nil)
s.NoError(err)
s.Equal(amountToSend.Int64(), recipientBalanceAfter.Int64())
}

// Test_SmartContract includes contract deployment, query, execution
func (s *IntegrationSuite) Test_SmartContract() {
chainID, err := s.ethClient.ChainID(context.Background())
s.NoError(err)
nonce, err := s.ethClient.NonceAt(context.Background(), s.fundedAccEthAddr, nil)
s.NoError(err)

// Deploying contract
signer := types.LatestSignerForChainID(chainID)
txData := s.contractData.Bytecode
tx, err := types.SignNewTx(
s.fundedAccPrivateKey,
signer,
&types.LegacyTx{
Nonce: nonce,
Gas: 1_500_000,
GasPrice: big.NewInt(1),
Data: txData,
})
s.NoError(err)
err = s.ethClient.SendTransaction(context.Background(), tx)
s.NoError(err)
s.NoError(s.network.WaitForNextBlock())
hash := tx.Hash()
receipt, err := s.ethClient.TransactionReceipt(context.Background(), hash)
s.NoError(err)
contractAddress := receipt.ContractAddress

// Querying contract: owner's balance should be 1000_000 tokens
ownerInitialBalance := (&big.Int{}).Mul(big.NewInt(1000_000), nibiCommon.TO_ATTO)
s.assertERC20Balance(contractAddress, s.fundedAccEthAddr, ownerInitialBalance)

// Querying contract: recipient balance should be 0
recipientAddr := ethCommon.BytesToAddress(testutilcli.NewAccount(s.network, "contract_recipient"))
s.assertERC20Balance(contractAddress, recipientAddr, big.NewInt(0))

// Execute contract: send 1000 anibi to recipient
sendAmount := (&big.Int{}).Mul(big.NewInt(1000), nibiCommon.TO_ATTO)
input, err := s.contractData.ABI.Pack("transfer", recipientAddr, sendAmount)
s.NoError(err)
nonce, err = s.ethClient.NonceAt(context.Background(), s.fundedAccEthAddr, nil)
s.NoError(err)
tx, err = types.SignNewTx(
s.fundedAccPrivateKey,
signer,
&types.LegacyTx{
Nonce: nonce,
To: &contractAddress,
Gas: 1_500_000,
GasPrice: big.NewInt(1),
Data: input,
})
s.NoError(err)
err = s.ethClient.SendTransaction(context.Background(), tx)
s.NoError(err)
s.NoError(s.network.WaitForNextBlock())

// Querying contract: owner's balance should be 999_000 tokens
ownerBalance := (&big.Int{}).Mul(big.NewInt(999_000), nibiCommon.TO_ATTO)
s.assertERC20Balance(contractAddress, s.fundedAccEthAddr, ownerBalance)

// Querying contract: recipient balance should be 1000 tokens
recipientBalance := (&big.Int{}).Mul(big.NewInt(1000), nibiCommon.TO_ATTO)
s.assertERC20Balance(contractAddress, recipientAddr, recipientBalance)
}

func (s *IntegrationSuite) TearDownSuite() {
s.T().Log("tearing down integration test suite")
s.network.Cleanup()
}

func (s *IntegrationSuite) assertERC20Balance(
contractAddress ethCommon.Address,
userAddress ethCommon.Address,
expectedBalance *big.Int,
) {
input, err := s.contractData.ABI.Pack("balanceOf", userAddress)
s.NoError(err)
msg := ethereum.CallMsg{
From: s.fundedAccEthAddr,
To: &contractAddress,
Data: input,
}
recipientBalanceBeforeBytes, err := s.ethClient.CallContract(context.Background(), msg, nil)
s.NoError(err)
balance := new(big.Int).SetBytes(recipientBalanceBeforeBytes)
s.Equal(expectedBalance.String(), balance.String())
}
1 change: 0 additions & 1 deletion geth
Submodule geth deleted from 7fb652
2 changes: 1 addition & 1 deletion gosdk/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func CreateBlockchain(t *testing.T) (nibiru Blockchain, err error) {
}

val := network.Validators[0]
AbsorbServerConfig(cfg, val.AppConfig)
AbsorbServerConfig(cfg, &val.AppConfig.Config)
AbsorbTmConfig(cfg, val.Ctx.Config)

grpcConn, err := ConnectGrpcToVal(val)
Expand Down
9 changes: 8 additions & 1 deletion x/common/constants.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package common

import sdk "github.com/cosmos/cosmos-sdk/types"
import (
"math/big"

sdk "github.com/cosmos/cosmos-sdk/types"
)

const (
TreasuryPoolModuleAccount = "treasury_pool"
Expand All @@ -10,6 +14,9 @@ const (
NIBIRU_TEAM = "nibi1l8dxzwz9d4peazcqjclnkj2mhvtj7mpnkqx85mg0ndrlhwrnh7gskkzg0v"
)

// TO_ATTO eth multiplier
var TO_ATTO = big.NewInt(1e18)

func NibiruTeamAddr() sdk.AccAddress {
return sdk.MustAccAddressFromBech32(NIBIRU_TEAM)
}
Loading
Loading