2015-07-07 03:54:22 +03:00
|
|
|
// Copyright 2015 The go-ethereum Authors
|
2015-07-22 19:48:40 +03:00
|
|
|
// This file is part of the go-ethereum library.
|
2015-07-07 03:54:22 +03:00
|
|
|
//
|
2015-07-23 19:35:11 +03:00
|
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
2015-07-07 03:54:22 +03:00
|
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
2015-07-22 19:48:40 +03:00
|
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
2015-07-07 03:54:22 +03:00
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2015-07-22 19:48:40 +03:00
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2015-07-07 03:54:22 +03:00
|
|
|
// GNU Lesser General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Lesser General Public License
|
2015-07-22 19:48:40 +03:00
|
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
2015-07-07 03:54:22 +03:00
|
|
|
|
2015-06-08 11:23:54 +03:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"math/big"
|
|
|
|
|
2015-07-10 12:35:15 +03:00
|
|
|
"fmt"
|
|
|
|
|
2015-06-08 11:23:54 +03:00
|
|
|
"github.com/ethereum/go-ethereum/common"
|
2015-10-27 00:24:09 +03:00
|
|
|
"github.com/ethereum/go-ethereum/common/natspec"
|
2015-06-24 14:53:37 +03:00
|
|
|
"github.com/ethereum/go-ethereum/eth"
|
2015-08-16 00:51:31 +03:00
|
|
|
"github.com/ethereum/go-ethereum/rlp"
|
2015-06-08 11:23:54 +03:00
|
|
|
"github.com/ethereum/go-ethereum/rpc/codec"
|
|
|
|
"github.com/ethereum/go-ethereum/rpc/shared"
|
|
|
|
"github.com/ethereum/go-ethereum/xeth"
|
2015-06-24 14:53:37 +03:00
|
|
|
"gopkg.in/fatih/set.v0"
|
2015-06-08 11:23:54 +03:00
|
|
|
)
|
|
|
|
|
2015-06-08 11:23:54 +03:00
|
|
|
const (
|
|
|
|
EthApiVersion = "1.0"
|
|
|
|
)
|
|
|
|
|
2015-06-08 11:23:54 +03:00
|
|
|
// eth api provider
|
|
|
|
// See https://github.com/ethereum/wiki/wiki/JSON-RPC
|
2015-06-09 17:06:51 +03:00
|
|
|
type ethApi struct {
|
2015-06-24 14:53:37 +03:00
|
|
|
xeth *xeth.XEth
|
|
|
|
ethereum *eth.Ethereum
|
|
|
|
methods map[string]ethhandler
|
|
|
|
codec codec.ApiCoder
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// eth callback handler
|
2015-06-09 17:06:51 +03:00
|
|
|
type ethhandler func(*ethApi, *shared.Request) (interface{}, error)
|
2015-06-08 11:23:54 +03:00
|
|
|
|
|
|
|
var (
|
|
|
|
ethMapping = map[string]ethhandler{
|
2015-06-16 16:40:24 +03:00
|
|
|
"eth_accounts": (*ethApi).Accounts,
|
|
|
|
"eth_blockNumber": (*ethApi).BlockNumber,
|
|
|
|
"eth_getBalance": (*ethApi).GetBalance,
|
|
|
|
"eth_protocolVersion": (*ethApi).ProtocolVersion,
|
|
|
|
"eth_coinbase": (*ethApi).Coinbase,
|
|
|
|
"eth_mining": (*ethApi).IsMining,
|
2015-09-09 19:02:54 +03:00
|
|
|
"eth_syncing": (*ethApi).IsSyncing,
|
2015-06-16 16:40:24 +03:00
|
|
|
"eth_gasPrice": (*ethApi).GasPrice,
|
|
|
|
"eth_getStorage": (*ethApi).GetStorage,
|
|
|
|
"eth_storageAt": (*ethApi).GetStorage,
|
|
|
|
"eth_getStorageAt": (*ethApi).GetStorageAt,
|
|
|
|
"eth_getTransactionCount": (*ethApi).GetTransactionCount,
|
|
|
|
"eth_getBlockTransactionCountByHash": (*ethApi).GetBlockTransactionCountByHash,
|
|
|
|
"eth_getBlockTransactionCountByNumber": (*ethApi).GetBlockTransactionCountByNumber,
|
|
|
|
"eth_getUncleCountByBlockHash": (*ethApi).GetUncleCountByBlockHash,
|
|
|
|
"eth_getUncleCountByBlockNumber": (*ethApi).GetUncleCountByBlockNumber,
|
|
|
|
"eth_getData": (*ethApi).GetData,
|
|
|
|
"eth_getCode": (*ethApi).GetData,
|
2015-10-27 00:24:09 +03:00
|
|
|
"eth_getNatSpec": (*ethApi).GetNatSpec,
|
2015-06-16 16:40:24 +03:00
|
|
|
"eth_sign": (*ethApi).Sign,
|
2015-08-16 00:51:31 +03:00
|
|
|
"eth_sendRawTransaction": (*ethApi).SubmitTransaction,
|
|
|
|
"eth_submitTransaction": (*ethApi).SubmitTransaction,
|
2015-06-16 16:40:24 +03:00
|
|
|
"eth_sendTransaction": (*ethApi).SendTransaction,
|
2015-08-16 00:51:31 +03:00
|
|
|
"eth_signTransaction": (*ethApi).SignTransaction,
|
2015-06-16 16:40:24 +03:00
|
|
|
"eth_transact": (*ethApi).SendTransaction,
|
|
|
|
"eth_estimateGas": (*ethApi).EstimateGas,
|
|
|
|
"eth_call": (*ethApi).Call,
|
|
|
|
"eth_flush": (*ethApi).Flush,
|
|
|
|
"eth_getBlockByHash": (*ethApi).GetBlockByHash,
|
|
|
|
"eth_getBlockByNumber": (*ethApi).GetBlockByNumber,
|
|
|
|
"eth_getTransactionByHash": (*ethApi).GetTransactionByHash,
|
|
|
|
"eth_getTransactionByBlockNumberAndIndex": (*ethApi).GetTransactionByBlockNumberAndIndex,
|
|
|
|
"eth_getTransactionByBlockHashAndIndex": (*ethApi).GetTransactionByBlockHashAndIndex,
|
|
|
|
"eth_getUncleByBlockHashAndIndex": (*ethApi).GetUncleByBlockHashAndIndex,
|
|
|
|
"eth_getUncleByBlockNumberAndIndex": (*ethApi).GetUncleByBlockNumberAndIndex,
|
|
|
|
"eth_getCompilers": (*ethApi).GetCompilers,
|
|
|
|
"eth_compileSolidity": (*ethApi).CompileSolidity,
|
|
|
|
"eth_newFilter": (*ethApi).NewFilter,
|
|
|
|
"eth_newBlockFilter": (*ethApi).NewBlockFilter,
|
|
|
|
"eth_newPendingTransactionFilter": (*ethApi).NewPendingTransactionFilter,
|
|
|
|
"eth_uninstallFilter": (*ethApi).UninstallFilter,
|
|
|
|
"eth_getFilterChanges": (*ethApi).GetFilterChanges,
|
|
|
|
"eth_getFilterLogs": (*ethApi).GetFilterLogs,
|
|
|
|
"eth_getLogs": (*ethApi).GetLogs,
|
|
|
|
"eth_hashrate": (*ethApi).Hashrate,
|
|
|
|
"eth_getWork": (*ethApi).GetWork,
|
|
|
|
"eth_submitWork": (*ethApi).SubmitWork,
|
2015-08-06 12:58:14 +03:00
|
|
|
"eth_submitHashrate": (*ethApi).SubmitHashrate,
|
2015-06-24 15:56:53 +03:00
|
|
|
"eth_resend": (*ethApi).Resend,
|
2015-06-24 14:53:37 +03:00
|
|
|
"eth_pendingTransactions": (*ethApi).PendingTransactions,
|
2015-07-03 19:20:07 +03:00
|
|
|
"eth_getTransactionReceipt": (*ethApi).GetTransactionReceipt,
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
// create new ethApi instance
|
2015-06-24 14:53:37 +03:00
|
|
|
func NewEthApi(xeth *xeth.XEth, eth *eth.Ethereum, codec codec.Codec) *ethApi {
|
|
|
|
return ðApi{xeth, eth, ethMapping, codec.New(nil)}
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// collection with supported methods
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) Methods() []string {
|
2015-06-08 11:23:54 +03:00
|
|
|
methods := make([]string, len(self.methods))
|
|
|
|
i := 0
|
|
|
|
for k := range self.methods {
|
|
|
|
methods[i] = k
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
return methods
|
|
|
|
}
|
|
|
|
|
|
|
|
// Execute given request
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) Execute(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
if callback, ok := self.methods[req.Method]; ok {
|
|
|
|
return callback(self, req)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, shared.NewNotImplementedError(req.Method)
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) Name() string {
|
2015-06-22 13:47:32 +03:00
|
|
|
return shared.EthApiName
|
2015-06-08 13:43:58 +03:00
|
|
|
}
|
|
|
|
|
2015-06-08 11:23:54 +03:00
|
|
|
func (self *ethApi) ApiVersion() string {
|
|
|
|
return EthApiVersion
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) Accounts(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
return self.xeth.Accounts(), nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) Hashrate(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
return newHexNum(self.xeth.HashRate()), nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) BlockNumber(req *shared.Request) (interface{}, error) {
|
2015-06-16 16:40:24 +03:00
|
|
|
num := self.xeth.CurrentBlock().Number()
|
|
|
|
return newHexNum(num.Bytes()), nil
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetBalance(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(GetBalanceArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return self.xeth.AtStateNum(args.BlockNumber).BalanceAt(args.Address), nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) ProtocolVersion(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
return self.xeth.EthVersion(), nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) Coinbase(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
return newHexData(self.xeth.Coinbase()), nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) IsMining(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
return self.xeth.IsMining(), nil
|
|
|
|
}
|
|
|
|
|
2015-09-09 19:02:54 +03:00
|
|
|
func (self *ethApi) IsSyncing(req *shared.Request) (interface{}, error) {
|
2015-10-13 12:04:25 +03:00
|
|
|
origin, current, height := self.ethereum.Downloader().Progress()
|
2015-09-09 19:02:54 +03:00
|
|
|
if current < height {
|
|
|
|
return map[string]interface{}{
|
2015-09-11 18:41:42 +03:00
|
|
|
"startingBlock": newHexNum(big.NewInt(int64(origin)).Bytes()),
|
|
|
|
"currentBlock": newHexNum(big.NewInt(int64(current)).Bytes()),
|
|
|
|
"highestBlock": newHexNum(big.NewInt(int64(height)).Bytes()),
|
2015-09-09 19:02:54 +03:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GasPrice(req *shared.Request) (interface{}, error) {
|
2015-06-12 15:11:10 +03:00
|
|
|
return newHexNum(self.xeth.DefaultGasPrice().Bytes()), nil
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetStorage(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(GetStorageArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return self.xeth.AtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage(), nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetStorageAt(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(GetStorageAtArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return self.xeth.AtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key), nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetTransactionCount(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(GetTxCountArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
count := self.xeth.AtStateNum(args.BlockNumber).TxCountAt(args.Address)
|
2015-09-23 00:59:26 +03:00
|
|
|
return fmt.Sprintf("%#x", count), nil
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetBlockTransactionCountByHash(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(HashArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
2015-09-23 00:59:26 +03:00
|
|
|
block := self.xeth.EthBlockByHash(args.Hash)
|
2015-06-08 11:23:54 +03:00
|
|
|
if block == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2015-09-23 00:59:26 +03:00
|
|
|
return fmt.Sprintf("%#x", len(block.Transactions())), nil
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetBlockTransactionCountByNumber(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(BlockNumArg)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
2015-09-23 00:59:26 +03:00
|
|
|
block := self.xeth.EthBlockByNumber(args.BlockNumber)
|
2015-06-08 11:23:54 +03:00
|
|
|
if block == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2015-09-23 00:59:26 +03:00
|
|
|
return fmt.Sprintf("%#x", len(block.Transactions())), nil
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetUncleCountByBlockHash(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(HashArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
2015-09-23 00:59:26 +03:00
|
|
|
block := self.xeth.EthBlockByHash(args.Hash)
|
2015-09-07 20:43:01 +03:00
|
|
|
if block == nil {
|
2015-06-08 11:23:54 +03:00
|
|
|
return nil, nil
|
|
|
|
}
|
2015-09-23 00:59:26 +03:00
|
|
|
return fmt.Sprintf("%#x", len(block.Uncles())), nil
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetUncleCountByBlockNumber(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(BlockNumArg)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
2015-09-23 00:59:26 +03:00
|
|
|
block := self.xeth.EthBlockByNumber(args.BlockNumber)
|
2015-09-07 20:43:01 +03:00
|
|
|
if block == nil {
|
2015-06-08 11:23:54 +03:00
|
|
|
return nil, nil
|
|
|
|
}
|
2015-09-23 00:59:26 +03:00
|
|
|
return fmt.Sprintf("%#x", len(block.Uncles())), nil
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetData(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(GetDataArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
v := self.xeth.AtStateNum(args.BlockNumber).CodeAtBytes(args.Address)
|
|
|
|
return newHexData(v), nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) Sign(req *shared.Request) (interface{}, error) {
|
|
|
|
args := new(NewSigArgs)
|
2015-06-08 11:23:54 +03:00
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
v, err := self.xeth.Sign(args.From, args.Data, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return v, nil
|
|
|
|
}
|
|
|
|
|
2015-08-16 00:51:31 +03:00
|
|
|
func (self *ethApi) SubmitTransaction(req *shared.Request) (interface{}, error) {
|
2015-06-16 19:28:10 +03:00
|
|
|
args := new(NewDataArgs)
|
2015-06-15 01:07:03 +03:00
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
2015-06-15 17:07:32 +03:00
|
|
|
v, err := self.xeth.PushTx(args.Data)
|
2015-06-15 01:07:03 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return v, nil
|
|
|
|
}
|
|
|
|
|
2015-08-16 00:51:31 +03:00
|
|
|
// JsonTransaction is returned as response by the JSON RPC. It contains the
|
|
|
|
// signed RLP encoded transaction as Raw and the signed transaction object as Tx.
|
|
|
|
type JsonTransaction struct {
|
|
|
|
Raw string `json:"raw"`
|
|
|
|
Tx *tx `json:"tx"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *ethApi) SignTransaction(req *shared.Request) (interface{}, error) {
|
|
|
|
args := new(NewTxArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
// nonce may be nil ("guess" mode)
|
|
|
|
var nonce string
|
|
|
|
if args.Nonce != nil {
|
|
|
|
nonce = args.Nonce.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
var gas, price string
|
|
|
|
if args.Gas != nil {
|
|
|
|
gas = args.Gas.String()
|
|
|
|
}
|
|
|
|
if args.GasPrice != nil {
|
|
|
|
price = args.GasPrice.String()
|
|
|
|
}
|
|
|
|
tx, err := self.xeth.SignTransaction(args.From, args.To, nonce, args.Value.String(), gas, price, args.Data)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := rlp.EncodeToBytes(tx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return JsonTransaction{"0x" + common.Bytes2Hex(data), newTx(tx)}, nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) SendTransaction(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(NewTxArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
// nonce may be nil ("guess" mode)
|
|
|
|
var nonce string
|
|
|
|
if args.Nonce != nil {
|
|
|
|
nonce = args.Nonce.String()
|
|
|
|
}
|
|
|
|
|
2015-06-15 18:21:08 +03:00
|
|
|
var gas, price string
|
|
|
|
if args.Gas != nil {
|
|
|
|
gas = args.Gas.String()
|
|
|
|
}
|
|
|
|
if args.GasPrice != nil {
|
|
|
|
price = args.GasPrice.String()
|
|
|
|
}
|
|
|
|
v, err := self.xeth.Transact(args.From, args.To, nonce, args.Value.String(), gas, price, args.Data)
|
2015-06-08 11:23:54 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return v, nil
|
|
|
|
}
|
|
|
|
|
2015-10-27 00:24:09 +03:00
|
|
|
func (self *ethApi) GetNatSpec(req *shared.Request) (interface{}, error) {
|
|
|
|
args := new(NewTxArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
var jsontx = fmt.Sprintf(`{"params":[{"to":"%s","data": "%s"}]}`, args.To, args.Data)
|
|
|
|
notice := natspec.GetNotice(self.xeth, jsontx, self.ethereum.HTTPClient())
|
|
|
|
|
|
|
|
return notice, nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) EstimateGas(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
_, gas, err := self.doCall(req.Params)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO unwrap the parent method's ToHex call
|
|
|
|
if len(gas) == 0 {
|
|
|
|
return newHexNum(0), nil
|
|
|
|
} else {
|
2015-07-27 14:44:37 +03:00
|
|
|
return newHexNum(common.String2Big(gas)), err
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) Call(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
v, _, err := self.doCall(req.Params)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO unwrap the parent method's ToHex call
|
|
|
|
if v == "0x0" {
|
|
|
|
return newHexData([]byte{}), nil
|
|
|
|
} else {
|
|
|
|
return newHexData(common.FromHex(v)), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) Flush(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
return nil, shared.NewNotImplementedError(req.Method)
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) doCall(params json.RawMessage) (string, string, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(CallArgs)
|
|
|
|
if err := self.codec.Decode(params, &args); err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return self.xeth.AtStateNum(args.BlockNumber).Call(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetBlockByHash(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(GetBlockByHashArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
block := self.xeth.EthBlockByHash(args.BlockHash)
|
2015-09-23 00:59:26 +03:00
|
|
|
if block == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2015-09-07 20:43:01 +03:00
|
|
|
return NewBlockRes(block, self.xeth.Td(block.Hash()), args.IncludeTxs), nil
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetBlockByNumber(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(GetBlockByNumberArgs)
|
|
|
|
if err := json.Unmarshal(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
block := self.xeth.EthBlockByNumber(args.BlockNumber)
|
2015-09-23 00:59:26 +03:00
|
|
|
if block == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2015-09-07 20:43:01 +03:00
|
|
|
return NewBlockRes(block, self.xeth.Td(block.Hash()), args.IncludeTxs), nil
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetTransactionByHash(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(HashArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash)
|
|
|
|
if tx != nil {
|
|
|
|
v := NewTransactionRes(tx)
|
|
|
|
// if the blockhash is 0, assume this is a pending transaction
|
|
|
|
if bytes.Compare(bhash.Bytes(), bytes.Repeat([]byte{0}, 32)) != 0 {
|
|
|
|
v.BlockHash = newHexData(bhash)
|
|
|
|
v.BlockNumber = newHexNum(bnum)
|
|
|
|
v.TxIndex = newHexNum(txi)
|
|
|
|
}
|
|
|
|
return v, nil
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetTransactionByBlockHashAndIndex(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(HashIndexArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
2015-09-07 20:43:01 +03:00
|
|
|
raw := self.xeth.EthBlockByHash(args.Hash)
|
2015-09-23 00:59:26 +03:00
|
|
|
if raw == nil {
|
2015-06-08 11:23:54 +03:00
|
|
|
return nil, nil
|
|
|
|
}
|
2015-09-23 00:59:26 +03:00
|
|
|
block := NewBlockRes(raw, self.xeth.Td(raw.Hash()), true)
|
2015-09-07 20:43:01 +03:00
|
|
|
if args.Index >= int64(len(block.Transactions)) || args.Index < 0 {
|
2015-06-08 11:23:54 +03:00
|
|
|
return nil, nil
|
|
|
|
} else {
|
2015-09-07 20:43:01 +03:00
|
|
|
return block.Transactions[args.Index], nil
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetTransactionByBlockNumberAndIndex(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(BlockNumIndexArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
2015-09-07 20:43:01 +03:00
|
|
|
raw := self.xeth.EthBlockByNumber(args.BlockNumber)
|
2015-09-23 00:59:26 +03:00
|
|
|
if raw == nil {
|
2015-06-08 11:23:54 +03:00
|
|
|
return nil, nil
|
|
|
|
}
|
2015-09-23 00:59:26 +03:00
|
|
|
block := NewBlockRes(raw, self.xeth.Td(raw.Hash()), true)
|
2015-09-07 20:43:01 +03:00
|
|
|
if args.Index >= int64(len(block.Transactions)) || args.Index < 0 {
|
2015-06-08 11:23:54 +03:00
|
|
|
// return NewValidationError("Index", "does not exist")
|
|
|
|
return nil, nil
|
|
|
|
}
|
2015-09-07 20:43:01 +03:00
|
|
|
return block.Transactions[args.Index], nil
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetUncleByBlockHashAndIndex(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(HashIndexArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
2015-09-07 20:43:01 +03:00
|
|
|
raw := self.xeth.EthBlockByHash(args.Hash)
|
2015-09-23 00:59:26 +03:00
|
|
|
if raw == nil {
|
2015-06-08 11:23:54 +03:00
|
|
|
return nil, nil
|
|
|
|
}
|
2015-09-23 00:59:26 +03:00
|
|
|
block := NewBlockRes(raw, self.xeth.Td(raw.Hash()), false)
|
2015-09-07 20:43:01 +03:00
|
|
|
if args.Index >= int64(len(block.Uncles)) || args.Index < 0 {
|
2015-06-08 11:23:54 +03:00
|
|
|
// return NewValidationError("Index", "does not exist")
|
|
|
|
return nil, nil
|
|
|
|
}
|
2015-09-07 20:43:01 +03:00
|
|
|
return block.Uncles[args.Index], nil
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetUncleByBlockNumberAndIndex(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(BlockNumIndexArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
2015-09-07 20:43:01 +03:00
|
|
|
raw := self.xeth.EthBlockByNumber(args.BlockNumber)
|
2015-09-23 00:59:26 +03:00
|
|
|
if raw == nil {
|
2015-06-08 11:23:54 +03:00
|
|
|
return nil, nil
|
|
|
|
}
|
2015-09-23 00:59:26 +03:00
|
|
|
block := NewBlockRes(raw, self.xeth.Td(raw.Hash()), true)
|
2015-09-07 20:43:01 +03:00
|
|
|
if args.Index >= int64(len(block.Uncles)) || args.Index < 0 {
|
2015-06-08 11:23:54 +03:00
|
|
|
return nil, nil
|
|
|
|
} else {
|
2015-09-07 20:43:01 +03:00
|
|
|
return block.Uncles[args.Index], nil
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetCompilers(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
var lang string
|
|
|
|
if solc, _ := self.xeth.Solc(); solc != nil {
|
|
|
|
lang = "Solidity"
|
|
|
|
}
|
|
|
|
c := []string{lang}
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) CompileSolidity(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
solc, _ := self.xeth.Solc()
|
|
|
|
if solc == nil {
|
|
|
|
return nil, shared.NewNotAvailableError(req.Method, "solc (solidity compiler) not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
args := new(SourceArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
contracts, err := solc.Compile(args.Source)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return contracts, nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) NewFilter(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(BlockFilterArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
id := self.xeth.NewLogFilter(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics)
|
|
|
|
return newHexNum(big.NewInt(int64(id)).Bytes()), nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) NewBlockFilter(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
return newHexNum(self.xeth.NewBlockFilter()), nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) NewPendingTransactionFilter(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
return newHexNum(self.xeth.NewTransactionFilter()), nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) UninstallFilter(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(FilterIdArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
return self.xeth.UninstallFilter(args.Id), nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetFilterChanges(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(FilterIdArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
switch self.xeth.GetFilterType(args.Id) {
|
|
|
|
case xeth.BlockFilterTy:
|
|
|
|
return NewHashesRes(self.xeth.BlockFilterChanged(args.Id)), nil
|
|
|
|
case xeth.TransactionFilterTy:
|
|
|
|
return NewHashesRes(self.xeth.TransactionFilterChanged(args.Id)), nil
|
|
|
|
case xeth.LogFilterTy:
|
|
|
|
return NewLogsRes(self.xeth.LogFilterChanged(args.Id)), nil
|
|
|
|
default:
|
|
|
|
return []string{}, nil // reply empty string slice
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetFilterLogs(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(FilterIdArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return NewLogsRes(self.xeth.Logs(args.Id)), nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetLogs(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(BlockFilterArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
return NewLogsRes(self.xeth.AllLogs(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics)), nil
|
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) GetWork(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
self.xeth.SetMining(true, 0)
|
2015-08-26 13:46:50 +03:00
|
|
|
ret, err := self.xeth.RemoteMining().GetWork()
|
|
|
|
if err != nil {
|
2015-08-28 04:42:01 +03:00
|
|
|
return nil, shared.NewNotReadyError("mining work")
|
2015-08-26 13:46:50 +03:00
|
|
|
} else {
|
|
|
|
return ret, nil
|
|
|
|
}
|
2015-06-08 11:23:54 +03:00
|
|
|
}
|
|
|
|
|
2015-06-09 17:06:51 +03:00
|
|
|
func (self *ethApi) SubmitWork(req *shared.Request) (interface{}, error) {
|
2015-06-08 11:23:54 +03:00
|
|
|
args := new(SubmitWorkArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
return self.xeth.RemoteMining().SubmitWork(args.Nonce, common.HexToHash(args.Digest), common.HexToHash(args.Header)), nil
|
|
|
|
}
|
2015-06-24 14:53:37 +03:00
|
|
|
|
2015-08-06 12:58:14 +03:00
|
|
|
func (self *ethApi) SubmitHashrate(req *shared.Request) (interface{}, error) {
|
|
|
|
args := new(SubmitHashRateArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
2015-08-17 16:09:30 +03:00
|
|
|
return false, shared.NewDecodeParamError(err.Error())
|
2015-08-06 12:58:14 +03:00
|
|
|
}
|
|
|
|
self.xeth.RemoteMining().SubmitHashrate(common.HexToHash(args.Id), args.Rate)
|
2015-08-17 16:09:30 +03:00
|
|
|
return true, nil
|
2015-08-06 12:58:14 +03:00
|
|
|
}
|
|
|
|
|
2015-06-24 15:56:53 +03:00
|
|
|
func (self *ethApi) Resend(req *shared.Request) (interface{}, error) {
|
|
|
|
args := new(ResendArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
2015-07-10 12:35:15 +03:00
|
|
|
from := common.HexToAddress(args.Tx.From)
|
2015-06-24 15:56:53 +03:00
|
|
|
|
2015-07-10 12:35:15 +03:00
|
|
|
pending := self.ethereum.TxPool().GetTransactions()
|
|
|
|
for _, p := range pending {
|
|
|
|
if pFrom, err := p.From(); err == nil && pFrom == from && p.SigHash() == args.Tx.tx.SigHash() {
|
|
|
|
self.ethereum.TxPool().RemoveTx(common.HexToHash(args.Tx.Hash))
|
|
|
|
return self.xeth.Transact(args.Tx.From, args.Tx.To, args.Tx.Nonce, args.Tx.Value, args.GasLimit, args.GasPrice, args.Tx.Data)
|
|
|
|
}
|
|
|
|
}
|
2015-06-24 15:56:53 +03:00
|
|
|
|
2015-07-10 12:35:15 +03:00
|
|
|
return nil, fmt.Errorf("Transaction %s not found", args.Tx.Hash)
|
2015-06-24 15:56:53 +03:00
|
|
|
}
|
|
|
|
|
2015-06-24 14:53:37 +03:00
|
|
|
func (self *ethApi) PendingTransactions(req *shared.Request) (interface{}, error) {
|
|
|
|
txs := self.ethereum.TxPool().GetTransactions()
|
|
|
|
|
2015-06-29 12:13:28 +03:00
|
|
|
// grab the accounts from the account manager. This will help with determining which
|
2015-06-24 14:53:37 +03:00
|
|
|
// transactions should be returned.
|
|
|
|
accounts, err := self.ethereum.AccountManager().Accounts()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the accouns to a new set
|
|
|
|
accountSet := set.New()
|
|
|
|
for _, account := range accounts {
|
|
|
|
accountSet.Add(account.Address)
|
|
|
|
}
|
|
|
|
|
|
|
|
var ltxs []*tx
|
|
|
|
for _, tx := range txs {
|
|
|
|
if from, _ := tx.From(); accountSet.Has(from) {
|
|
|
|
ltxs = append(ltxs, newTx(tx))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ltxs, nil
|
|
|
|
}
|
2015-07-03 19:20:07 +03:00
|
|
|
|
|
|
|
func (self *ethApi) GetTransactionReceipt(req *shared.Request) (interface{}, error) {
|
|
|
|
args := new(HashArgs)
|
|
|
|
if err := self.codec.Decode(req.Params, &args); err != nil {
|
|
|
|
return nil, shared.NewDecodeParamError(err.Error())
|
|
|
|
}
|
|
|
|
|
2015-07-04 18:24:52 +03:00
|
|
|
txhash := common.BytesToHash(common.FromHex(args.Hash))
|
2015-07-04 20:03:37 +03:00
|
|
|
tx, bhash, bnum, txi := self.xeth.EthTransactionByHash(args.Hash)
|
2015-07-04 18:24:52 +03:00
|
|
|
rec := self.xeth.GetTxReceipt(txhash)
|
2015-07-03 19:20:07 +03:00
|
|
|
// We could have an error of "not found". Should disambiguate
|
|
|
|
// if err != nil {
|
|
|
|
// return err, nil
|
|
|
|
// }
|
2015-07-04 20:03:37 +03:00
|
|
|
if rec != nil && tx != nil {
|
2015-07-03 19:20:07 +03:00
|
|
|
v := NewReceiptRes(rec)
|
2015-07-04 20:03:37 +03:00
|
|
|
v.BlockHash = newHexData(bhash)
|
|
|
|
v.BlockNumber = newHexNum(bnum)
|
|
|
|
v.TransactionIndex = newHexNum(txi)
|
2015-07-03 19:20:07 +03:00
|
|
|
return v, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
}
|