From 063c4c5b8d153af8bbec2085a4662b29abda8a53 Mon Sep 17 00:00:00 2001 From: dcb9 Date: Sat, 29 Sep 2018 00:23:22 +0800 Subject: [PATCH] Add eth_getBalance, update eth_estimateGas --- README.md | 6 +- pkg/eth/rpc_types.go | 15 +++++ pkg/qtum/jsonrpc.go | 1 + pkg/qtum/method.go | 21 +++++++ pkg/qtum/rpc_types.go | 93 ++++++++++++++++++++++++++++++ pkg/transformer/eth_call.go | 6 +- pkg/transformer/eth_estimateGas.go | 25 ++++++-- pkg/transformer/eth_getBalance.go | 59 +++++++++++++++++++ pkg/transformer/eth_getCode.go | 6 +- pkg/transformer/transformer.go | 6 +- 10 files changed, 225 insertions(+), 13 deletions(-) create mode 100644 pkg/transformer/eth_getBalance.go diff --git a/README.md b/README.md index 419384f9..72b439ff 100644 --- a/README.md +++ b/README.md @@ -691,10 +691,14 @@ curl --header 'Content-Type: application/json' --data \ - tags, "pending" and "earliest", are unsupported - eth_accounts - eth_getCode +- eth_newFilter - eth_newBlockFilter - eth_getFilterChanges - - only support filters created with `eth_newBlockFilter` - eth_uninstallFilter +- eth_getFilterLogs +- eth_getBlockByNumber +- eth_estimateGas +- eth_getBalance ## Known issues diff --git a/pkg/eth/rpc_types.go b/pkg/eth/rpc_types.go index 4edf60fe..a29f56fe 100644 --- a/pkg/eth/rpc_types.go +++ b/pkg/eth/rpc_types.go @@ -417,3 +417,18 @@ func (r *NewFilterRequest) UnmarshalJSON(data []byte) error { } type NewFilterResponse string + +// ========== eth_getBalance ============= // + +type GetBalanceRequest struct { + Address string + Block json.RawMessage +} + +func (r *GetBalanceRequest) UnmarshalJSON(data []byte) error { + tmp := []interface{}{&r.Address, &r.Block} + + return json.Unmarshal(data, &tmp) +} + +type GetBalanceResponse string diff --git a/pkg/qtum/jsonrpc.go b/pkg/qtum/jsonrpc.go index ab33f412..af26ff9a 100644 --- a/pkg/qtum/jsonrpc.go +++ b/pkg/qtum/jsonrpc.go @@ -29,6 +29,7 @@ const ( MethodGetAddressesByAccount = "getaddressesbyaccount" MethodGetAccountInfo = "getaccountinfo" MethodGenerate = "generate" + MethodListUnspent = "listunspent" ) type JSONRPCRequest struct { diff --git a/pkg/qtum/method.go b/pkg/qtum/method.go index 952c81b9..963b77fb 100644 --- a/pkg/qtum/method.go +++ b/pkg/qtum/method.go @@ -96,3 +96,24 @@ func (m *Method) SearchLogs(req *SearchLogsRequest) (receipts SearchLogsResponse } return } + +func (m *Method) CallContract(req *CallContractRequest) (resp *CallContractResponse, err error) { + if err := m.Request(MethodCallContract, req, &resp); err != nil { + return nil, err + } + return +} + +func (m *Method) GetAccountInfo(req *GetAccountInfoRequest) (resp *GetAccountInfoResponse, err error) { + if err := m.Request(MethodGetAccountInfo, req, &resp); err != nil { + return nil, err + } + return +} + +func (m *Method) ListUnspent(req *ListUnspentRequest) (resp *ListUnspentResponse, err error) { + if err := m.Request(MethodListUnspent, req, &resp); err != nil { + return nil, err + } + return +} diff --git a/pkg/qtum/rpc_types.go b/pkg/qtum/rpc_types.go index 0d1fbc92..61ed7c88 100644 --- a/pkg/qtum/rpc_types.go +++ b/pkg/qtum/rpc_types.go @@ -868,3 +868,96 @@ func (r *GetBlockRequest) MarshalJSON() ([]byte, error) { verbosity, }) } + +// ========== ListUnspent ============= // +type ( + + /* + Arguments: + 1. minconf (numeric, optional, default=1) The minimum confirmations to filter + 2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter + 3. "addresses" (string) A json array of qtum addresses to filter + [ + "address" (string) qtum address + ,... + ] + 4. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend + See description of "safe" attribute below. + 5. query_options (json, optional) JSON with query options + { + "minimumAmount" (numeric or string, default=0) Minimum value of each UTXO in QTUM + "maximumAmount" (numeric or string, default=unlimited) Maximum value of each UTXO in QTUM + "maximumCount" (numeric or string, default=unlimited) Maximum number of UTXOs + "minimumSumAmount" (numeric or string, default=unlimited) Minimum sum value of all UTXOs in QTUM + } + */ + ListUnspentRequest struct { + MinConf int + MaxConf int + Addresses []string + } + + /* + [ (array of json object) + { + "txid" : "txid", (string) the transaction id + "vout" : n, (numeric) the vout value + "address" : "address", (string) the qtum address + "account" : "account", (string) DEPRECATED. The associated account, or "" for the default account + "scriptPubKey" : "key", (string) the script key + "amount" : x.xxx, (numeric) the transaction output amount in QTUM + "confirmations" : n, (numeric) The number of confirmations + "redeemScript" : n (string) The redeemScript if scriptPubKey is P2SH + "spendable" : xxx, (bool) Whether we have the private keys to spend this output + "solvable" : xxx, (bool) Whether we know how to spend this output, ignoring the lack of keys + "safe" : xxx (bool) Whether this output is considered safe to spend. Unconfirmed transactions + from outside keys and unconfirmed replacement transactions are considered unsafe + and are not eligible for spending by fundrawtransaction and sendtoaddress. + } + ,... + ] + + [ + { + "txid": "a8d97ae8bb819cd4aa98ed2ddaef4969783aee845461a9ea5a88184ad58f44fe", + "vout": 2, + "address": "qUbxboqjBRp96j3La8D1RYkyqx5uQbJPoW", + "account": "", + "scriptPubKey": "210299d391f528b9edd07284c7e23df8415232a8ce41531cf460a390ce32b4efd112ac", + "amount": 15007.10682200, + "confirmations": 532, + "spendable": true, + "solvable": true, + "safe": true + } + ] + */ + ListUnspentResponse []struct { + Txid string `json:"txid"` + Vout int `json:"vout"` + Address string `json:"address"` + Account string `json:"account"` + ScriptPubKey string `json:"scriptPubKey"` + Amount float64 `json:"amount"` + Confirmations int `json:"confirmations"` + Spendable bool `json:"spendable"` + Solvable bool `json:"solvable"` + Safe bool `json:"safe"` + } +) + +func NewListUnspentRequest(addresses ...string) *ListUnspentRequest { + return &ListUnspentRequest{ + MinConf: 1, + MaxConf: 99999999, + Addresses: addresses, + } +} + +func (r *ListUnspentRequest) MarshalJSON() ([]byte, error) { + return json.Marshal([]interface{}{ + r.MinConf, + r.MaxConf, + r.Addresses, + }) +} diff --git a/pkg/transformer/eth_call.go b/pkg/transformer/eth_call.go index eb13615a..5b665b4f 100644 --- a/pkg/transformer/eth_call.go +++ b/pkg/transformer/eth_call.go @@ -26,13 +26,13 @@ func (p *ProxyETHCall) Request(rawreq *eth.JSONRPCRequest) (interface{}, error) func (p *ProxyETHCall) request(ethreq *eth.CallRequest) (*eth.CallResponse, error) { // eth req -> qtum req - params, err := p.ToRequest(ethreq) + qtumreq, err := p.ToRequest(ethreq) if err != nil { return nil, err } - var qtumresp *qtum.CallContractResponse - if err = p.Qtum.Request(qtum.MethodCallContract, params, &qtumresp); err != nil { + qtumresp, err := p.CallContract(qtumreq) + if err != nil { return nil, err } diff --git a/pkg/transformer/eth_estimateGas.go b/pkg/transformer/eth_estimateGas.go index c74a5383..4f1abb6c 100644 --- a/pkg/transformer/eth_estimateGas.go +++ b/pkg/transformer/eth_estimateGas.go @@ -3,11 +3,12 @@ package transformer import ( "github.com/dcb9/janus/pkg/eth" "github.com/dcb9/janus/pkg/qtum" + "github.com/ethereum/go-ethereum/common/hexutil" ) // ProxyETHEstimateGas implements ETHProxy type ProxyETHEstimateGas struct { - *qtum.Qtum + *ProxyETHCall } func (p *ProxyETHEstimateGas) Method() string { @@ -15,10 +16,26 @@ func (p *ProxyETHEstimateGas) Method() string { } func (p *ProxyETHEstimateGas) Request(rawreq *eth.JSONRPCRequest) (interface{}, error) { - return p.request() + var ethreq eth.CallRequest + if err := unmarshalRequest(rawreq.Params, ðreq); err != nil { + return nil, err + } + + // eth req -> qtum req + qtumreq, err := p.ToRequest(ðreq) + if err != nil { + return nil, err + } + + qtumresp, err := p.CallContract(qtumreq) + if err != nil { + return nil, err + } + + return p.toResp(qtumresp) } -func (p *ProxyETHEstimateGas) request() (*eth.EstimateGasResponse, error) { - gas := eth.EstimateGasResponse("0x500000") +func (p *ProxyETHEstimateGas) toResp(qtumresp *qtum.CallContractResponse) (*eth.EstimateGasResponse, error) { + gas := eth.EstimateGasResponse(hexutil.EncodeUint64(uint64(qtumresp.ExecutionResult.GasUsed))) return &gas, nil } diff --git a/pkg/transformer/eth_getBalance.go b/pkg/transformer/eth_getBalance.go new file mode 100644 index 00000000..ac804123 --- /dev/null +++ b/pkg/transformer/eth_getBalance.go @@ -0,0 +1,59 @@ +package transformer + +import ( + "github.com/dcb9/janus/pkg/eth" + "github.com/dcb9/janus/pkg/qtum" + "github.com/dcb9/janus/pkg/utils" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// ProxyETHGetBalance implements ETHProxy +type ProxyETHGetBalance struct { + *qtum.Qtum +} + +func (p *ProxyETHGetBalance) Method() string { + return "eth_getBalance" +} + +func (p *ProxyETHGetBalance) Request(rawreq *eth.JSONRPCRequest) (interface{}, error) { + var req eth.GetBalanceRequest + if err := unmarshalRequest(rawreq.Params, &req); err != nil { + return nil, err + } + + addr := utils.RemoveHexPrefix(req.Address) + { + // is address a contract or an account? + qtumreq := qtum.GetAccountInfoRequest(addr) + qtumresp, err := p.GetAccountInfo(&qtumreq) + + // the address is a contract + if err == nil { + // the unit of the balance Satoshi + return hexutil.EncodeUint64(uint64(qtumresp.Balance)), nil + } + } + + { + // try account + base58Addr, err := p.FromHexAddress(addr) + if err != nil { + return nil, err + } + + qtumreq := qtum.NewListUnspentRequest(base58Addr) + qtumresp, err := p.ListUnspent(qtumreq) + if err != nil { + return nil, err + } + + balance := float64(0) + for _, utxo := range *qtumresp { + balance += utxo.Amount + } + + // 1 QTUM = 10 ^ 8 Satoshi + return hexutil.EncodeUint64(uint64(balance * 10e8)), nil + } +} diff --git a/pkg/transformer/eth_getCode.go b/pkg/transformer/eth_getCode.go index 854065cf..40138c69 100644 --- a/pkg/transformer/eth_getCode.go +++ b/pkg/transformer/eth_getCode.go @@ -27,9 +27,9 @@ func (p *ProxyETHGetCode) Request(rawreq *eth.JSONRPCRequest) (interface{}, erro func (p *ProxyETHGetCode) request(ethreq *eth.GetCodeRequest) (eth.GetCodeResponse, error) { qtumreq := qtum.GetAccountInfoRequest(utils.RemoveHexPrefix(ethreq.Address)) - var qtumresp qtum.GetAccountInfoResponse - if err := p.Qtum.Request(qtum.MethodGetAccountInfo, &qtumreq, &qtumresp); err != nil { - return "", nil + qtumresp, err := p.GetAccountInfo(&qtumreq) + if err != nil { + return "", err } // qtum res -> eth res diff --git a/pkg/transformer/transformer.go b/pkg/transformer/transformer.go index 614f2315..dd43a18b 100644 --- a/pkg/transformer/transformer.go +++ b/pkg/transformer/transformer.go @@ -76,9 +76,10 @@ func (t *Transformer) getProxy(rpcReq *eth.JSONRPCRequest) (ETHProxy, error) { func DefaultProxies(qtumRPCClient *qtum.Qtum) []ETHProxy { filter := eth.NewFilterSimulator() getFilterChanges := &ProxyETHGetFilterChanges{Qtum: qtumRPCClient, filter: filter} + ethCall := &ProxyETHCall{Qtum: qtumRPCClient} return []ETHProxy{ - &ProxyETHCall{Qtum: qtumRPCClient}, + ethCall, &ProxyETHPersonalUnlockAccount{}, &ProxyETHBlockNumber{Qtum: qtumRPCClient}, &ProxyETHNetVersion{Qtum: qtumRPCClient}, @@ -95,8 +96,9 @@ func DefaultProxies(qtumRPCClient *qtum.Qtum) []ETHProxy { &ProxyETHGetFilterLogs{ProxyETHGetFilterChanges: getFilterChanges}, &ProxyETHUninstallFilter{Qtum: qtumRPCClient, filter: filter}, - &ProxyETHEstimateGas{Qtum: qtumRPCClient}, + &ProxyETHEstimateGas{ProxyETHCall: ethCall}, &ProxyETHGetBlockByNumber{Qtum: qtumRPCClient}, + &ProxyETHGetBalance{Qtum: qtumRPCClient}, } }