personal API (#420)

This commit is contained in:
noot 2020-08-13 13:14:48 -04:00 committed by GitHub
parent a2a5799d13
commit 1cb712fb16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 371 additions and 38 deletions

View File

@ -22,6 +22,7 @@ const (
func GetRPCAPIs(cliCtx context.CLIContext, keys []emintcrypto.PrivKeySecp256k1) []rpc.API { func GetRPCAPIs(cliCtx context.CLIContext, keys []emintcrypto.PrivKeySecp256k1) []rpc.API {
nonceLock := new(AddrLocker) nonceLock := new(AddrLocker)
backend := NewEthermintBackend(cliCtx) backend := NewEthermintBackend(cliCtx)
ethAPI := NewPublicEthAPI(cliCtx, backend, nonceLock, keys)
return []rpc.API{ return []rpc.API{
{ {
@ -33,13 +34,13 @@ func GetRPCAPIs(cliCtx context.CLIContext, keys []emintcrypto.PrivKeySecp256k1)
{ {
Namespace: EthNamespace, Namespace: EthNamespace,
Version: apiVersion, Version: apiVersion,
Service: NewPublicEthAPI(cliCtx, backend, nonceLock, keys), Service: ethAPI,
Public: true, Public: true,
}, },
{ {
Namespace: PersonalNamespace, Namespace: PersonalNamespace,
Version: apiVersion, Version: apiVersion,
Service: NewPersonalEthAPI(cliCtx, nonceLock), Service: NewPersonalEthAPI(cliCtx, ethAPI, nonceLock, keys),
Public: false, Public: false,
}, },
{ {

View File

@ -81,6 +81,10 @@ func registerRoutes(rs *lcd.RestServer) {
if err := s.RegisterName(api.Namespace, api.Service); err != nil { if err := s.RegisterName(api.Namespace, api.Service); err != nil {
panic(err) panic(err)
} }
} else if !api.Public { // TODO: how to handle private apis? should only accept local calls
if err := s.RegisterName(api.Namespace, api.Service); err != nil {
panic(err)
}
} }
} }

View File

@ -1,30 +1,184 @@
package rpc package rpc
import ( import (
"bytes"
"context" "context"
"fmt"
"log"
"os"
"sync"
"time"
sdkcontext "github.com/cosmos/cosmos-sdk/client/context" sdkcontext "github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdk "github.com/cosmos/cosmos-sdk/types"
emintcrypto "github.com/cosmos/ethermint/crypto"
params "github.com/cosmos/ethermint/rpc/args"
"github.com/spf13/viper"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
) )
// PersonalEthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec. // PersonalEthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec.
type PersonalEthAPI struct { type PersonalEthAPI struct {
cliCtx sdkcontext.CLIContext cliCtx sdkcontext.CLIContext
ethAPI *PublicEthAPI
nonceLock *AddrLocker nonceLock *AddrLocker
keys []emintcrypto.PrivKeySecp256k1
keyInfos []keyring.Info
keybaseLock sync.Mutex
} }
// NewPersonalEthAPI creates an instance of the public ETH Web3 API. // NewPersonalEthAPI creates an instance of the public ETH Web3 API.
func NewPersonalEthAPI(cliCtx sdkcontext.CLIContext, nonceLock *AddrLocker) *PersonalEthAPI { func NewPersonalEthAPI(cliCtx sdkcontext.CLIContext, ethAPI *PublicEthAPI, nonceLock *AddrLocker, keys []emintcrypto.PrivKeySecp256k1) *PersonalEthAPI {
return &PersonalEthAPI{ api := &PersonalEthAPI{
cliCtx: cliCtx, cliCtx: cliCtx,
ethAPI: ethAPI,
nonceLock: nonceLock, nonceLock: nonceLock,
keys: keys,
}
infos, err := api.getKeybaseInfo()
if err != nil {
return api
}
api.keyInfos = infos
return api
}
func (e *PersonalEthAPI) getKeybaseInfo() ([]keyring.Info, error) {
e.keybaseLock.Lock()
defer e.keybaseLock.Unlock()
if e.cliCtx.Keybase == nil {
keybase, err := keyring.NewKeyring(
sdk.KeyringServiceName(),
viper.GetString(flags.FlagKeyringBackend),
viper.GetString(flags.FlagHome),
e.cliCtx.Input,
emintcrypto.EthSecp256k1Options()...,
)
if err != nil {
return nil, err
}
e.cliCtx.Keybase = keybase
}
return e.cliCtx.Keybase.List()
}
// ImportRawKey stores the given hex encoded ECDSA key into the key directory,
// encrypting it with the passphrase.
// Currently, this is not implemented since the feature is not supported by the keyring.
func (e *PersonalEthAPI) ImportRawKey(privkey, password string) (common.Address, error) {
_, err := crypto.HexToECDSA(privkey)
if err != nil {
return common.Address{}, err
}
return common.Address{}, nil
}
// ListAccounts will return a list of addresses for accounts this node manages.
func (e *PersonalEthAPI) ListAccounts() ([]common.Address, error) {
addrs := []common.Address{}
for _, info := range e.keyInfos {
addressBytes := info.GetPubKey().Address().Bytes()
addrs = append(addrs, common.BytesToAddress(addressBytes))
}
return addrs, nil
}
// LockAccount will lock the account associated with the given address when it's unlocked.
// It removes the key corresponding to the given address from the API's local keys.
func (e *PersonalEthAPI) LockAccount(address common.Address) bool {
for i, key := range e.keys {
if !bytes.Equal(key.PubKey().Address().Bytes(), address.Bytes()) {
continue
}
tmp := make([]emintcrypto.PrivKeySecp256k1, len(e.keys)-1)
copy(tmp[:i], e.keys[:i])
copy(tmp[i:], e.keys[i+1:])
e.keys = tmp
return true
}
return false
}
// NewAccount will create a new account and returns the address for the new account.
func (e *PersonalEthAPI) NewAccount(password string) (common.Address, error) {
_, err := e.getKeybaseInfo()
if err != nil {
return common.Address{}, err
}
name := "key_" + time.Now().UTC().Format(time.RFC3339)
info, _, err := e.cliCtx.Keybase.CreateMnemonic(name, keyring.English, password, emintcrypto.EthSecp256k1)
if err != nil {
return common.Address{}, err
}
e.keyInfos = append(e.keyInfos, info)
addr := common.BytesToAddress(info.GetPubKey().Address().Bytes())
log.Printf("Your new key was generated\t\taddress=0x%x", addr)
log.Printf("Please backup your key file!\tpath=%s", os.Getenv("HOME")+"/.ethermintcli/"+name)
log.Println("Please remember your password!")
return addr, nil
}
// UnlockAccount will unlock the account associated with the given address with
// the given password for duration seconds. If duration is nil it will use a
// default of 300 seconds. It returns an indication if the account was unlocked.
// It exports the private key corresponding to the given address from the keyring and stores it in the API's local keys.
func (e *PersonalEthAPI) UnlockAccount(ctx context.Context, addr common.Address, password string, _ *uint64) (bool, error) {
// TODO: use duration
name := ""
for _, info := range e.keyInfos {
addressBytes := info.GetPubKey().Address().Bytes()
if bytes.Equal(addressBytes, addr[:]) {
name = info.GetName()
} }
} }
if name == "" {
return false, fmt.Errorf("cannot find key with given address")
}
// TODO: this only works on local keys
privKey, err := e.cliCtx.Keybase.ExportPrivateKeyObject(name, password)
if err != nil {
return false, err
}
emintKey, ok := privKey.(emintcrypto.PrivKeySecp256k1)
if !ok {
return false, fmt.Errorf("invalid private key type: %T", privKey)
}
e.keys = append(e.keys, emintKey)
return true, nil
}
// SendTransaction will create a transaction from the given arguments and
// tries to sign it with the key associated with args.To. If the given password isn't
// able to decrypt the key it fails.
func (e *PersonalEthAPI) SendTransaction(ctx context.Context, args params.SendTxArgs, passwd string) (common.Hash, error) {
return e.ethAPI.SendTransaction(args)
}
// Sign calculates an Ethereum ECDSA signature for: // Sign calculates an Ethereum ECDSA signature for:
// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message)) // keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))
// //
// Note, the produced signature conforms to the secp256k1 curve R, S and V values, // Note, the produced signature conforms to the secp256k1 curve R, S and V values,
// where the V value will be 27 or 28 for legacy reasons. // where the V value will be 27 or 28 for legacy reasons.
@ -33,5 +187,42 @@ func NewPersonalEthAPI(cliCtx sdkcontext.CLIContext, nonceLock *AddrLocker) *Per
// //
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign // https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
func (e *PersonalEthAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) { func (e *PersonalEthAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) {
return nil, nil key, ok := checkKeyInKeyring(e.keys, addr)
if !ok {
return nil, fmt.Errorf("cannot find key with given address")
}
sig, err := crypto.Sign(accounts.TextHash(data), key.ToECDSA())
if err != nil {
return nil, err
}
sig[crypto.RecoveryIDOffset] += 27 // transform V from 0/1 to 27/28
return sig, nil
}
// EcRecover returns the address for the account that was used to create the signature.
// Note, this function is compatible with eth_sign and personal_sign. As such it recovers
// the address of:
// hash = keccak256("\x19Ethereum Signed Message:\n"${message length}${message})
// addr = ecrecover(hash, signature)
//
// Note, the signature must conform to the secp256k1 curve R, S and V values, where
// the V value must be 27 or 28 for legacy reasons.
//
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecove
func (e *PersonalEthAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) {
if len(sig) != crypto.SignatureLength {
return common.Address{}, fmt.Errorf("signature must be %d bytes long", crypto.SignatureLength)
}
if sig[crypto.RecoveryIDOffset] != 27 && sig[crypto.RecoveryIDOffset] != 28 {
return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)")
}
sig[crypto.RecoveryIDOffset] -= 27 // Transform yellow paper V from 27/28 to 0/1
rpk, err := crypto.SigToPub(accounts.TextHash(data), sig)
if err != nil {
return common.Address{}, err
}
return crypto.PubkeyToAddress(*rpk), nil
} }

108
tests/personal_test.go Normal file
View File

@ -0,0 +1,108 @@
package tests
import (
"encoding/json"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/require"
)
func TestPersonal_ListAccounts(t *testing.T) {
rpcRes := call(t, "personal_listAccounts", []string{})
var res []hexutil.Bytes
err := json.Unmarshal(rpcRes.Result, &res)
require.NoError(t, err)
require.Equal(t, 1, len(res))
}
func TestPersonal_NewAccount(t *testing.T) {
rpcRes := call(t, "personal_newAccount", []string{""})
var addr common.Address
err := json.Unmarshal(rpcRes.Result, &addr)
require.NoError(t, err)
rpcRes = call(t, "personal_listAccounts", []string{})
var res []hexutil.Bytes
err = json.Unmarshal(rpcRes.Result, &res)
require.NoError(t, err)
require.Equal(t, 2, len(res))
}
func TestPersonal_Sign(t *testing.T) {
rpcRes := call(t, "personal_sign", []interface{}{hexutil.Bytes{0x88}, hexutil.Bytes(from), ""})
var res hexutil.Bytes
err := json.Unmarshal(rpcRes.Result, &res)
require.NoError(t, err)
require.Equal(t, 65, len(res))
// TODO: check that signature is same as with geth, requires importing a key
}
func TestPersonal_EcRecover(t *testing.T) {
data := hexutil.Bytes{0x88}
rpcRes := call(t, "personal_sign", []interface{}{data, hexutil.Bytes(from), ""})
var res hexutil.Bytes
err := json.Unmarshal(rpcRes.Result, &res)
require.NoError(t, err)
require.Equal(t, 65, len(res))
rpcRes = call(t, "personal_ecRecover", []interface{}{data, res})
var ecrecoverRes common.Address
err = json.Unmarshal(rpcRes.Result, &ecrecoverRes)
require.NoError(t, err)
require.Equal(t, from, ecrecoverRes[:])
}
func TestPersonal_UnlockAccount(t *testing.T) {
pswd := "nootwashere"
rpcRes := call(t, "personal_newAccount", []string{pswd})
var addr common.Address
err := json.Unmarshal(rpcRes.Result, &addr)
require.NoError(t, err)
// try to sign, should be locked
_, err = callWithError("personal_sign", []interface{}{hexutil.Bytes{0x88}, addr, ""})
require.NotNil(t, err)
rpcRes = call(t, "personal_unlockAccount", []interface{}{addr, ""})
var unlocked bool
err = json.Unmarshal(rpcRes.Result, &unlocked)
require.NoError(t, err)
require.True(t, unlocked)
// try to sign, should work now
rpcRes = call(t, "personal_sign", []interface{}{hexutil.Bytes{0x88}, addr, pswd})
var res hexutil.Bytes
err = json.Unmarshal(rpcRes.Result, &res)
require.NoError(t, err)
require.Equal(t, 65, len(res))
}
func TestPersonal_LockAccount(t *testing.T) {
pswd := "nootwashere"
rpcRes := call(t, "personal_newAccount", []string{pswd})
var addr common.Address
err := json.Unmarshal(rpcRes.Result, &addr)
require.NoError(t, err)
rpcRes = call(t, "personal_unlockAccount", []interface{}{addr, ""})
var unlocked bool
err = json.Unmarshal(rpcRes.Result, &unlocked)
require.NoError(t, err)
require.True(t, unlocked)
rpcRes = call(t, "personal_lockAccount", []interface{}{addr})
var locked bool
err = json.Unmarshal(rpcRes.Result, &locked)
require.NoError(t, err)
require.True(t, locked)
// try to sign, should be locked
_, err = callWithError("personal_sign", []interface{}{hexutil.Bytes{0x88}, addr, ""})
require.NotNil(t, err)
}

View File

@ -3,10 +3,7 @@
// To run these tests please first ensure you have the ethermintd running // To run these tests please first ensure you have the ethermintd running
// and have started the RPC service with `ethermintcli rest-server`. // and have started the RPC service with `ethermintcli rest-server`.
// //
// You can configure the desired ETHERMINT_NODE_HOST and ETHERMINT_INTEGRATION_TEST_MODE // You can configure the desired HOST and MODE as well
//
// to have it running
package tests package tests
import ( import (
@ -42,6 +39,7 @@ var (
HOST = os.Getenv("HOST") HOST = os.Getenv("HOST")
zeroString = "0x0" zeroString = "0x0"
from = []byte{}
) )
type Request struct { type Request struct {
@ -73,11 +71,33 @@ func TestMain(m *testing.M) {
HOST = "http://localhost:8545" HOST = "http://localhost:8545"
} }
var err error
from, err = getAddress()
if err != nil {
fmt.Printf("failed to get account: %s\n", err)
os.Exit(1)
}
// Start all tests // Start all tests
code := m.Run() code := m.Run()
os.Exit(code) os.Exit(code)
} }
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 { func createRequest(method string, params interface{}) Request {
return Request{ return Request{
Version: "2.0", Version: "2.0",
@ -109,6 +129,39 @@ func call(t *testing.T, method string, params interface{}) *Response {
return rpcRes return rpcRes
} }
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)
/* #nosec */
res, err := http.Post(HOST, "application/json", bytes.NewBuffer(req))
if err != nil {
return nil, err
}
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 // turns a 0x prefixed hex string to a big.Int
func hexToBigInt(t *testing.T, in string) *big.Int { func hexToBigInt(t *testing.T, in string) *big.Int {
s := in[2:] s := in[2:]
@ -240,7 +293,7 @@ func TestEth_coinbase(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
t.Logf("Got coinbase block proposer: %s\n", res.String()) t.Logf("Got coinbase block proposer: %s\n", res.String())
require.NotEqual(t, zeroAddress.String(), res.String(), "expected: %s got: %s\n", zeroAddress.String(), res.String()) require.NotEqual(t, zeroAddress.String(), res.String(), "expected: not %s got: %s\n", zeroAddress.String(), res.String())
} }
func TestEth_GetBalance(t *testing.T) { func TestEth_GetBalance(t *testing.T) {
@ -301,19 +354,7 @@ func TestEth_GetCode(t *testing.T) {
require.True(t, bytes.Equal(expectedRes, code), "expected: %X got: %X", expectedRes, code) require.True(t, bytes.Equal(expectedRes, code), "expected: %X got: %X", expectedRes, code)
} }
func getAddress(t *testing.T) []byte {
rpcRes := call(t, "eth_accounts", []string{})
var res []hexutil.Bytes
err := json.Unmarshal(rpcRes.Result, &res)
require.NoError(t, err)
return res[0]
}
func TestEth_SendTransaction_Transfer(t *testing.T) { func TestEth_SendTransaction_Transfer(t *testing.T) {
from := getAddress(t)
param := make([]map[string]string, 1) param := make([]map[string]string, 1)
param[0] = make(map[string]string) param[0] = make(map[string]string)
param[0]["from"] = "0x" + fmt.Sprintf("%x", from) param[0]["from"] = "0x" + fmt.Sprintf("%x", from)
@ -334,8 +375,6 @@ func TestEth_SendTransaction_Transfer(t *testing.T) {
} }
func TestEth_SendTransaction_ContractDeploy(t *testing.T) { func TestEth_SendTransaction_ContractDeploy(t *testing.T) {
from := getAddress(t)
param := make([]map[string]string, 1) param := make([]map[string]string, 1)
param[0] = make(map[string]string) param[0] = make(map[string]string)
param[0]["from"] = "0x" + fmt.Sprintf("%x", from) param[0]["from"] = "0x" + fmt.Sprintf("%x", from)
@ -422,7 +461,6 @@ func TestEth_GetFilterChanges_WrongID(t *testing.T) {
// sendTestTransaction sends a dummy transaction // sendTestTransaction sends a dummy transaction
func sendTestTransaction(t *testing.T) hexutil.Bytes { func sendTestTransaction(t *testing.T) hexutil.Bytes {
from := getAddress(t)
param := make([]map[string]string, 1) param := make([]map[string]string, 1)
param[0] = make(map[string]string) param[0] = make(map[string]string)
param[0]["from"] = "0x" + fmt.Sprintf("%x", from) param[0]["from"] = "0x" + fmt.Sprintf("%x", from)
@ -455,8 +493,6 @@ func TestEth_GetTransactionReceipt(t *testing.T) {
// deployTestContract deploys a contract that emits an event in the constructor // deployTestContract deploys a contract that emits an event in the constructor
func deployTestContract(t *testing.T) (hexutil.Bytes, map[string]interface{}) { func deployTestContract(t *testing.T) (hexutil.Bytes, map[string]interface{}) {
from := getAddress(t)
param := make([]map[string]string, 1) param := make([]map[string]string, 1)
param[0] = make(map[string]string) param[0] = make(map[string]string)
param[0]["from"] = "0x" + fmt.Sprintf("%x", from) param[0]["from"] = "0x" + fmt.Sprintf("%x", from)
@ -586,8 +622,6 @@ func deployTestContractWithFunction(t *testing.T) hexutil.Bytes {
bytecode := "0x608060405234801561001057600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a260d08061004d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063eb8ac92114602d575b600080fd5b606060048036036040811015604157600080fd5b8101908080359060200190929190803590602001909291905050506062565b005b8160008190555080827ff3ca124a697ba07e8c5e80bebcfcc48991fc16a63170e8a9206e30508960d00360405160405180910390a3505056fea265627a7a723158201d94d2187aaf3a6790527b615fcc40970febf0385fa6d72a2344848ebd0df3e964736f6c63430005110032" bytecode := "0x608060405234801561001057600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a260d08061004d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063eb8ac92114602d575b600080fd5b606060048036036040811015604157600080fd5b8101908080359060200190929190803590602001909291905050506062565b005b8160008190555080827ff3ca124a697ba07e8c5e80bebcfcc48991fc16a63170e8a9206e30508960d00360405160405180910390a3505056fea265627a7a723158201d94d2187aaf3a6790527b615fcc40970febf0385fa6d72a2344848ebd0df3e964736f6c63430005110032"
from := getAddress(t)
param := make([]map[string]string, 1) param := make([]map[string]string, 1)
param[0] = make(map[string]string) param[0] = make(map[string]string)
param[0]["from"] = "0x" + fmt.Sprintf("%x", from) param[0]["from"] = "0x" + fmt.Sprintf("%x", from)
@ -701,7 +735,6 @@ func TestEth_PendingTransactionFilter(t *testing.T) {
} }
func getNonce(t *testing.T) hexutil.Uint64 { func getNonce(t *testing.T) hexutil.Uint64 {
from := getAddress(t)
param := []interface{}{hexutil.Bytes(from), "latest"} param := []interface{}{hexutil.Bytes(from), "latest"}
rpcRes := call(t, "eth_getTransactionCount", param) rpcRes := call(t, "eth_getTransactionCount", param)
@ -712,7 +745,6 @@ func getNonce(t *testing.T) hexutil.Uint64 {
} }
func TestEth_EstimateGas(t *testing.T) { func TestEth_EstimateGas(t *testing.T) {
from := getAddress(t)
param := make([]map[string]string, 1) param := make([]map[string]string, 1)
param[0] = make(map[string]string) param[0] = make(map[string]string)
param[0]["from"] = "0x" + fmt.Sprintf("%x", from) param[0]["from"] = "0x" + fmt.Sprintf("%x", from)
@ -728,7 +760,6 @@ func TestEth_EstimateGas(t *testing.T) {
} }
func TestEth_EstimateGas_ContractDeployment(t *testing.T) { func TestEth_EstimateGas_ContractDeployment(t *testing.T) {
from := getAddress(t)
bytecode := "0x608060405234801561001057600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a260d08061004d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063eb8ac92114602d575b600080fd5b606060048036036040811015604157600080fd5b8101908080359060200190929190803590602001909291905050506062565b005b8160008190555080827ff3ca124a697ba07e8c5e80bebcfcc48991fc16a63170e8a9206e30508960d00360405160405180910390a3505056fea265627a7a723158201d94d2187aaf3a6790527b615fcc40970febf0385fa6d72a2344848ebd0df3e964736f6c63430005110032" bytecode := "0x608060405234801561001057600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a260d08061004d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063eb8ac92114602d575b600080fd5b606060048036036040811015604157600080fd5b8101908080359060200190929190803590602001909291905050506062565b005b8160008190555080827ff3ca124a697ba07e8c5e80bebcfcc48991fc16a63170e8a9206e30508960d00360405160405180910390a3505056fea265627a7a723158201d94d2187aaf3a6790527b615fcc40970febf0385fa6d72a2344848ebd0df3e964736f6c63430005110032"
param := make([]map[string]string, 1) param := make([]map[string]string, 1)
@ -742,7 +773,7 @@ func TestEth_EstimateGas_ContractDeployment(t *testing.T) {
err := json.Unmarshal(rpcRes.Result, &gas) err := json.Unmarshal(rpcRes.Result, &gas)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, hexutil.Uint64(0x1d46b), gas) require.Equal(t, hexutil.Uint64(0x1cab2), gas)
} }
func TestEth_ExportAccount(t *testing.T) { func TestEth_ExportAccount(t *testing.T) {
@ -773,12 +804,10 @@ func TestEth_ExportAccount_WithStorage(t *testing.T) {
// call function to set storage // call function to set storage
calldata := "0xeb8ac92100000000000000000000000000000000000000000000000000000000000000630000000000000000000000000000000000000000000000000000000000000000" calldata := "0xeb8ac92100000000000000000000000000000000000000000000000000000000000000630000000000000000000000000000000000000000000000000000000000000000"
from := getAddress(t)
param := make([]map[string]string, 1) param := make([]map[string]string, 1)
param[0] = make(map[string]string) param[0] = make(map[string]string)
param[0]["from"] = "0x" + fmt.Sprintf("%x", from) param[0]["from"] = "0x" + fmt.Sprintf("%x", from)
param[0]["to"] = addr param[0]["to"] = addr
//param[0]["value"] = "0x1"
param[0]["data"] = calldata param[0]["data"] = calldata
rpcRes := call(t, "eth_sendTransaction", param) rpcRes := call(t, "eth_sendTransaction", param)