afc09f9d59
* avoid pending tx get confirmed when sleep * fix install-tparse which need go >= 1.18 * fix strings.Cut in https://github.com/tharsis/ethermint/runs/6611646254?check_suite_focus=true * for more info, https://dev.to/hgsgtk/go-118-new-function-cut-added-to-stringsbytes-package-5c2f * Revert "fix install-tparse which need go >= 1.18" This reverts commit 5e39c2d7351ebbd6b789df214f2396e6c5dfb28e. Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
300 lines
7.9 KiB
Go
300 lines
7.9 KiB
Go
package rpc
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math/big"
|
|
"net/http"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
"github.com/pkg/errors"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type Request struct {
|
|
Version string `json:"jsonrpc"`
|
|
Method string `json:"method"`
|
|
Params interface{} `json:"params"`
|
|
ID int `json:"id"`
|
|
}
|
|
|
|
type Error struct {
|
|
Code int `json:"code"`
|
|
Message string `json:"message"`
|
|
Data interface{} `json:"data,omitempty"`
|
|
}
|
|
|
|
type Response struct {
|
|
Error *Error `json:"error"`
|
|
ID int `json:"id"`
|
|
Result json.RawMessage `json:"result,omitempty"`
|
|
}
|
|
|
|
var HOST = os.Getenv("HOST")
|
|
|
|
func GetAddress() ([]byte, error) {
|
|
rpcRes, err := CallWithError("eth_accounts", []string{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var res []hexutil.Bytes
|
|
err = json.Unmarshal(rpcRes.Result, &res)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return res[0], nil
|
|
}
|
|
|
|
func CreateRequest(method string, params interface{}) Request {
|
|
return Request{
|
|
Version: "2.0",
|
|
Method: method,
|
|
Params: params,
|
|
ID: 1,
|
|
}
|
|
}
|
|
|
|
func CallWithSleep(t *testing.T, method string, params interface{}, sleep time.Duration) *Response {
|
|
req, err := json.Marshal(CreateRequest(method, params))
|
|
require.NoError(t, err)
|
|
|
|
var rpcRes *Response
|
|
if sleep > 0 {
|
|
time.Sleep(sleep)
|
|
}
|
|
|
|
httpReq, err := http.NewRequestWithContext(context.Background(), "POST", HOST, bytes.NewBuffer(req))
|
|
if err != nil {
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
httpReq.Header.Set("Content-Type", "application/json")
|
|
client := &http.Client{}
|
|
res, err := client.Do(httpReq)
|
|
if err != nil {
|
|
require.NoError(t, errors.Wrap(err, "Could not perform request"))
|
|
}
|
|
|
|
decoder := json.NewDecoder(res.Body)
|
|
rpcRes = new(Response)
|
|
err = decoder.Decode(&rpcRes)
|
|
require.NoError(t, err)
|
|
|
|
err = res.Body.Close()
|
|
require.NoError(t, err)
|
|
require.Nil(t, rpcRes.Error)
|
|
|
|
return rpcRes
|
|
}
|
|
|
|
func Call(t *testing.T, method string, params interface{}) *Response {
|
|
return CallWithSleep(t, method, params, time.Second)
|
|
}
|
|
|
|
func CallWithError(method string, params interface{}) (*Response, error) {
|
|
req, err := json.Marshal(CreateRequest(method, params))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var rpcRes *Response
|
|
time.Sleep(1 * time.Second)
|
|
|
|
httpReq, err := http.NewRequestWithContext(context.Background(), "POST", HOST, bytes.NewBuffer(req))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
httpReq.Header.Set("Content-Type", "application/json")
|
|
client := &http.Client{}
|
|
res, err := client.Do(httpReq)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "Could not perform request")
|
|
}
|
|
|
|
decoder := json.NewDecoder(res.Body)
|
|
rpcRes = new(Response)
|
|
err = decoder.Decode(&rpcRes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = res.Body.Close()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if rpcRes.Error != nil {
|
|
return nil, fmt.Errorf(rpcRes.Error.Message)
|
|
}
|
|
|
|
return rpcRes, nil
|
|
}
|
|
|
|
// turns a 0x prefixed hex string to a big.Int
|
|
func HexToBigInt(t *testing.T, in string) *big.Int {
|
|
s := in[2:]
|
|
b, err := hex.DecodeString(s)
|
|
require.NoError(t, err)
|
|
return big.NewInt(0).SetBytes(b)
|
|
}
|
|
|
|
// sendTestTransaction sends a dummy transaction
|
|
func SendTestTransaction(t *testing.T, addr []byte) hexutil.Bytes {
|
|
param := make([]map[string]string, 1)
|
|
param[0] = make(map[string]string)
|
|
param[0]["from"] = "0x" + fmt.Sprintf("%x", addr)
|
|
param[0]["to"] = "0x1122334455667788990011223344556677889900"
|
|
param[0]["value"] = "0x1"
|
|
|
|
rpcRes := Call(t, "personal_unlockAccount", []interface{}{param[0]["from"], ""})
|
|
require.Nil(t, rpcRes.Error)
|
|
|
|
rpcRes = Call(t, "eth_sendTransaction", param)
|
|
require.Nil(t, rpcRes.Error)
|
|
|
|
var hash hexutil.Bytes
|
|
err := json.Unmarshal(rpcRes.Result, &hash)
|
|
require.NoError(t, err)
|
|
return hash
|
|
}
|
|
|
|
// deployTestContract deploys a contract that emits an event in the constructor
|
|
func DeployTestContract(t *testing.T, addr []byte) (hexutil.Bytes, map[string]interface{}) {
|
|
param := make([]map[string]string, 1)
|
|
param[0] = make(map[string]string)
|
|
param[0]["from"] = "0x" + fmt.Sprintf("%x", addr)
|
|
param[0]["data"] = "0x6080604052348015600f57600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a2603580604b6000396000f3fe6080604052600080fdfea165627a7a723058206cab665f0f557620554bb45adf266708d2bd349b8a4314bdff205ee8440e3c240029"
|
|
param[0]["gas"] = "0x200000"
|
|
|
|
rpcRes := Call(t, "personal_unlockAccount", []interface{}{param[0]["from"], ""})
|
|
require.Nil(t, rpcRes.Error)
|
|
|
|
rpcRes = Call(t, "eth_sendTransaction", param)
|
|
require.Nil(t, rpcRes.Error)
|
|
|
|
var hash hexutil.Bytes
|
|
err := json.Unmarshal(rpcRes.Result, &hash)
|
|
require.NoError(t, err)
|
|
|
|
receipt := WaitForReceipt(t, hash)
|
|
require.NotNil(t, receipt, "transaction failed")
|
|
require.Equal(t, "0x1", receipt["status"].(string))
|
|
|
|
return hash, receipt
|
|
}
|
|
|
|
func DeployTestContractWithFunction(t *testing.T, addr []byte) hexutil.Bytes {
|
|
// pragma solidity ^0.5.1;
|
|
|
|
// contract Test {
|
|
// event Hello(uint256 indexed world);
|
|
// event TestEvent(uint256 indexed a, uint256 indexed b);
|
|
|
|
// uint256 myStorage;
|
|
|
|
// constructor() public {
|
|
// emit Hello(17);
|
|
// }
|
|
|
|
// function test(uint256 a, uint256 b) public {
|
|
// myStorage = a;
|
|
// emit TestEvent(a, b);
|
|
// }
|
|
// }
|
|
|
|
bytecode := "0x608060405234801561001057600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a260d08061004d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063eb8ac92114602d575b600080fd5b606060048036036040811015604157600080fd5b8101908080359060200190929190803590602001909291905050506062565b005b8160008190555080827ff3ca124a697ba07e8c5e80bebcfcc48991fc16a63170e8a9206e30508960d00360405160405180910390a3505056fea265627a7a723158201d94d2187aaf3a6790527b615fcc40970febf0385fa6d72a2344848ebd0df3e964736f6c63430005110032"
|
|
|
|
param := make([]map[string]string, 1)
|
|
param[0] = make(map[string]string)
|
|
param[0]["from"] = "0x" + fmt.Sprintf("%x", addr)
|
|
param[0]["data"] = bytecode
|
|
param[0]["gas"] = "0x200000"
|
|
|
|
rpcRes := Call(t, "personal_unlockAccount", []interface{}{param[0]["from"], ""})
|
|
require.Nil(t, rpcRes.Error)
|
|
|
|
rpcRes = Call(t, "eth_sendTransaction", param)
|
|
|
|
var hash hexutil.Bytes
|
|
err := json.Unmarshal(rpcRes.Result, &hash)
|
|
require.NoError(t, err)
|
|
|
|
receipt := WaitForReceipt(t, hash)
|
|
require.NotNil(t, receipt, "transaction failed")
|
|
require.Equal(t, "0x1", receipt["status"].(string))
|
|
|
|
return hash
|
|
}
|
|
|
|
func GetTransactionReceipt(t *testing.T, hash hexutil.Bytes) map[string]interface{} {
|
|
param := []string{hash.String()}
|
|
rpcRes := Call(t, "eth_getTransactionReceipt", param)
|
|
|
|
receipt := make(map[string]interface{})
|
|
err := json.Unmarshal(rpcRes.Result, &receipt)
|
|
require.NoError(t, err)
|
|
|
|
return receipt
|
|
}
|
|
|
|
func WaitForReceipt(t *testing.T, hash hexutil.Bytes) map[string]interface{} {
|
|
for i := 0; i < 12; i++ {
|
|
receipt := GetTransactionReceipt(t, hash)
|
|
if receipt != nil {
|
|
return receipt
|
|
}
|
|
|
|
time.Sleep(time.Second)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func GetNonce(t *testing.T, block string) hexutil.Uint64 {
|
|
from, err := GetAddress()
|
|
require.NoError(t, err)
|
|
|
|
param := []interface{}{hexutil.Bytes(from), block}
|
|
rpcRes := Call(t, "eth_getTransactionCount", param)
|
|
|
|
var nonce hexutil.Uint64
|
|
err = json.Unmarshal(rpcRes.Result, &nonce)
|
|
require.NoError(t, err)
|
|
return nonce
|
|
}
|
|
|
|
func GetGasPrice(t *testing.T) string {
|
|
gasRes := Call(t, "eth_gasPrice", []interface{}{})
|
|
|
|
var gasPrice string
|
|
err := json.Unmarshal(gasRes.Result, &gasPrice)
|
|
require.NoError(t, err)
|
|
return gasPrice
|
|
}
|
|
|
|
func UnlockAllAccounts(t *testing.T) {
|
|
var accts []common.Address
|
|
rpcRes := Call(t, "eth_accounts", []map[string]string{})
|
|
err := json.Unmarshal(rpcRes.Result, &accts)
|
|
require.NoError(t, err)
|
|
|
|
for _, acct := range accts {
|
|
t.Logf("account: %v", acct)
|
|
rpcRes = Call(t, "personal_unlockAccount", []interface{}{acct, ""})
|
|
var unlocked bool
|
|
err = json.Unmarshal(rpcRes.Result, &unlocked)
|
|
require.NoError(t, err)
|
|
require.True(t, unlocked)
|
|
}
|
|
}
|