1// Copyright (c) 2014-2017 The btcsuite developers
2// Copyright (c) 2015-2017 The Decred developers
3// Use of this source code is governed by an ISC
4// license that can be found in the LICENSE file.
5
6package rpcclient
7
8import (
9	"bytes"
10	"encoding/base64"
11	"encoding/hex"
12	"encoding/json"
13	"fmt"
14
15	"github.com/btcsuite/btcd/btcjson"
16	"github.com/btcsuite/btcd/chaincfg/chainhash"
17	"github.com/btcsuite/btcd/wire"
18	"github.com/btcsuite/btcutil"
19)
20
21// FutureDebugLevelResult is a future promise to deliver the result of a
22// DebugLevelAsync RPC invocation (or an applicable error).
23type FutureDebugLevelResult chan *response
24
25// Receive waits for the response promised by the future and returns the result
26// of setting the debug logging level to the passed level specification or the
27// list of of the available subsystems for the special keyword 'show'.
28func (r FutureDebugLevelResult) Receive() (string, error) {
29	res, err := receiveFuture(r)
30	if err != nil {
31		return "", err
32	}
33
34	// Unmashal the result as a string.
35	var result string
36	err = json.Unmarshal(res, &result)
37	if err != nil {
38		return "", err
39	}
40	return result, nil
41}
42
43// DebugLevelAsync returns an instance of a type that can be used to get the
44// result of the RPC at some future time by invoking the Receive function on
45// the returned instance.
46//
47// See DebugLevel for the blocking version and more details.
48//
49// NOTE: This is a btcd extension.
50func (c *Client) DebugLevelAsync(levelSpec string) FutureDebugLevelResult {
51	cmd := btcjson.NewDebugLevelCmd(levelSpec)
52	return c.sendCmd(cmd)
53}
54
55// DebugLevel dynamically sets the debug logging level to the passed level
56// specification.
57//
58// The levelspec can be either a debug level or of the form:
59// 	<subsystem>=<level>,<subsystem2>=<level2>,...
60//
61// Additionally, the special keyword 'show' can be used to get a list of the
62// available subsystems.
63//
64// NOTE: This is a btcd extension.
65func (c *Client) DebugLevel(levelSpec string) (string, error) {
66	return c.DebugLevelAsync(levelSpec).Receive()
67}
68
69// FutureCreateEncryptedWalletResult is a future promise to deliver the error
70// result of a CreateEncryptedWalletAsync RPC invocation.
71type FutureCreateEncryptedWalletResult chan *response
72
73// Receive waits for and returns the error response promised by the future.
74func (r FutureCreateEncryptedWalletResult) Receive() error {
75	_, err := receiveFuture(r)
76	return err
77}
78
79// CreateEncryptedWalletAsync returns an instance of a type that can be used to
80// get the result of the RPC at some future time by invoking the Receive
81// function on the returned instance.
82//
83// See CreateEncryptedWallet for the blocking version and more details.
84//
85// NOTE: This is a btcwallet extension.
86func (c *Client) CreateEncryptedWalletAsync(passphrase string) FutureCreateEncryptedWalletResult {
87	cmd := btcjson.NewCreateEncryptedWalletCmd(passphrase)
88	return c.sendCmd(cmd)
89}
90
91// CreateEncryptedWallet requests the creation of an encrypted wallet.  Wallets
92// managed by btcwallet are only written to disk with encrypted private keys,
93// and generating wallets on the fly is impossible as it requires user input for
94// the encryption passphrase.  This RPC specifies the passphrase and instructs
95// the wallet creation.  This may error if a wallet is already opened, or the
96// new wallet cannot be written to disk.
97//
98// NOTE: This is a btcwallet extension.
99func (c *Client) CreateEncryptedWallet(passphrase string) error {
100	return c.CreateEncryptedWalletAsync(passphrase).Receive()
101}
102
103// FutureListAddressTransactionsResult is a future promise to deliver the result
104// of a ListAddressTransactionsAsync RPC invocation (or an applicable error).
105type FutureListAddressTransactionsResult chan *response
106
107// Receive waits for the response promised by the future and returns information
108// about all transactions associated with the provided addresses.
109func (r FutureListAddressTransactionsResult) Receive() ([]btcjson.ListTransactionsResult, error) {
110	res, err := receiveFuture(r)
111	if err != nil {
112		return nil, err
113	}
114
115	// Unmarshal the result as an array of listtransactions objects.
116	var transactions []btcjson.ListTransactionsResult
117	err = json.Unmarshal(res, &transactions)
118	if err != nil {
119		return nil, err
120	}
121	return transactions, nil
122}
123
124// ListAddressTransactionsAsync returns an instance of a type that can be used
125// to get the result of the RPC at some future time by invoking the Receive
126// function on the returned instance.
127//
128// See ListAddressTransactions for the blocking version and more details.
129//
130// NOTE: This is a btcd extension.
131func (c *Client) ListAddressTransactionsAsync(addresses []btcutil.Address, account string) FutureListAddressTransactionsResult {
132	// Convert addresses to strings.
133	addrs := make([]string, 0, len(addresses))
134	for _, addr := range addresses {
135		addrs = append(addrs, addr.EncodeAddress())
136	}
137	cmd := btcjson.NewListAddressTransactionsCmd(addrs, &account)
138	return c.sendCmd(cmd)
139}
140
141// ListAddressTransactions returns information about all transactions associated
142// with the provided addresses.
143//
144// NOTE: This is a btcwallet extension.
145func (c *Client) ListAddressTransactions(addresses []btcutil.Address, account string) ([]btcjson.ListTransactionsResult, error) {
146	return c.ListAddressTransactionsAsync(addresses, account).Receive()
147}
148
149// FutureGetBestBlockResult is a future promise to deliver the result of a
150// GetBestBlockAsync RPC invocation (or an applicable error).
151type FutureGetBestBlockResult chan *response
152
153// Receive waits for the response promised by the future and returns the hash
154// and height of the block in the longest (best) chain.
155func (r FutureGetBestBlockResult) Receive() (*chainhash.Hash, int32, error) {
156	res, err := receiveFuture(r)
157	if err != nil {
158		return nil, 0, err
159	}
160
161	// Unmarshal result as a getbestblock result object.
162	var bestBlock btcjson.GetBestBlockResult
163	err = json.Unmarshal(res, &bestBlock)
164	if err != nil {
165		return nil, 0, err
166	}
167
168	// Convert to hash from string.
169	hash, err := chainhash.NewHashFromStr(bestBlock.Hash)
170	if err != nil {
171		return nil, 0, err
172	}
173
174	return hash, bestBlock.Height, nil
175}
176
177// GetBestBlockAsync returns an instance of a type that can be used to get the
178// result of the RPC at some future time by invoking the Receive function on the
179// returned instance.
180//
181// See GetBestBlock for the blocking version and more details.
182//
183// NOTE: This is a btcd extension.
184func (c *Client) GetBestBlockAsync() FutureGetBestBlockResult {
185	cmd := btcjson.NewGetBestBlockCmd()
186	return c.sendCmd(cmd)
187}
188
189// GetBestBlock returns the hash and height of the block in the longest (best)
190// chain.
191//
192// NOTE: This is a btcd extension.
193func (c *Client) GetBestBlock() (*chainhash.Hash, int32, error) {
194	return c.GetBestBlockAsync().Receive()
195}
196
197// FutureGetCurrentNetResult is a future promise to deliver the result of a
198// GetCurrentNetAsync RPC invocation (or an applicable error).
199type FutureGetCurrentNetResult chan *response
200
201// Receive waits for the response promised by the future and returns the network
202// the server is running on.
203func (r FutureGetCurrentNetResult) Receive() (wire.BitcoinNet, error) {
204	res, err := receiveFuture(r)
205	if err != nil {
206		return 0, err
207	}
208
209	// Unmarshal result as an int64.
210	var net int64
211	err = json.Unmarshal(res, &net)
212	if err != nil {
213		return 0, err
214	}
215
216	return wire.BitcoinNet(net), nil
217}
218
219// GetCurrentNetAsync returns an instance of a type that can be used to get the
220// result of the RPC at some future time by invoking the Receive function on the
221// returned instance.
222//
223// See GetCurrentNet for the blocking version and more details.
224//
225// NOTE: This is a btcd extension.
226func (c *Client) GetCurrentNetAsync() FutureGetCurrentNetResult {
227	cmd := btcjson.NewGetCurrentNetCmd()
228	return c.sendCmd(cmd)
229}
230
231// GetCurrentNet returns the network the server is running on.
232//
233// NOTE: This is a btcd extension.
234func (c *Client) GetCurrentNet() (wire.BitcoinNet, error) {
235	return c.GetCurrentNetAsync().Receive()
236}
237
238// FutureGetHeadersResult is a future promise to deliver the result of a
239// getheaders RPC invocation (or an applicable error).
240//
241// NOTE: This is a btcsuite extension ported from
242// github.com/decred/dcrrpcclient.
243type FutureGetHeadersResult chan *response
244
245// Receive waits for the response promised by the future and returns the
246// getheaders result.
247//
248// NOTE: This is a btcsuite extension ported from
249// github.com/decred/dcrrpcclient.
250func (r FutureGetHeadersResult) Receive() ([]wire.BlockHeader, error) {
251	res, err := receiveFuture(r)
252	if err != nil {
253		return nil, err
254	}
255
256	// Unmarshal result as a slice of strings.
257	var result []string
258	err = json.Unmarshal(res, &result)
259	if err != nil {
260		return nil, err
261	}
262
263	// Deserialize the []string into []wire.BlockHeader.
264	headers := make([]wire.BlockHeader, len(result))
265	for i, headerHex := range result {
266		serialized, err := hex.DecodeString(headerHex)
267		if err != nil {
268			return nil, err
269		}
270		err = headers[i].Deserialize(bytes.NewReader(serialized))
271		if err != nil {
272			return nil, err
273		}
274	}
275	return headers, nil
276}
277
278// GetHeadersAsync returns an instance of a type that can be used to get the result
279// of the RPC at some future time by invoking the Receive function on the returned instance.
280//
281// See GetHeaders for the blocking version and more details.
282//
283// NOTE: This is a btcsuite extension ported from
284// github.com/decred/dcrrpcclient.
285func (c *Client) GetHeadersAsync(blockLocators []chainhash.Hash, hashStop *chainhash.Hash) FutureGetHeadersResult {
286	locators := make([]string, len(blockLocators))
287	for i := range blockLocators {
288		locators[i] = blockLocators[i].String()
289	}
290	hash := ""
291	if hashStop != nil {
292		hash = hashStop.String()
293	}
294	cmd := btcjson.NewGetHeadersCmd(locators, hash)
295	return c.sendCmd(cmd)
296}
297
298// GetHeaders mimics the wire protocol getheaders and headers messages by
299// returning all headers on the main chain after the first known block in the
300// locators, up until a block hash matches hashStop.
301//
302// NOTE: This is a btcsuite extension ported from
303// github.com/decred/dcrrpcclient.
304func (c *Client) GetHeaders(blockLocators []chainhash.Hash, hashStop *chainhash.Hash) ([]wire.BlockHeader, error) {
305	return c.GetHeadersAsync(blockLocators, hashStop).Receive()
306}
307
308// FutureExportWatchingWalletResult is a future promise to deliver the result of
309// an ExportWatchingWalletAsync RPC invocation (or an applicable error).
310type FutureExportWatchingWalletResult chan *response
311
312// Receive waits for the response promised by the future and returns the
313// exported wallet.
314func (r FutureExportWatchingWalletResult) Receive() ([]byte, []byte, error) {
315	res, err := receiveFuture(r)
316	if err != nil {
317		return nil, nil, err
318	}
319
320	// Unmarshal result as a JSON object.
321	var obj map[string]interface{}
322	err = json.Unmarshal(res, &obj)
323	if err != nil {
324		return nil, nil, err
325	}
326
327	// Check for the wallet and tx string fields in the object.
328	base64Wallet, ok := obj["wallet"].(string)
329	if !ok {
330		return nil, nil, fmt.Errorf("unexpected response type for "+
331			"exportwatchingwallet 'wallet' field: %T\n",
332			obj["wallet"])
333	}
334	base64TxStore, ok := obj["tx"].(string)
335	if !ok {
336		return nil, nil, fmt.Errorf("unexpected response type for "+
337			"exportwatchingwallet 'tx' field: %T\n",
338			obj["tx"])
339	}
340
341	walletBytes, err := base64.StdEncoding.DecodeString(base64Wallet)
342	if err != nil {
343		return nil, nil, err
344	}
345
346	txStoreBytes, err := base64.StdEncoding.DecodeString(base64TxStore)
347	if err != nil {
348		return nil, nil, err
349	}
350
351	return walletBytes, txStoreBytes, nil
352
353}
354
355// ExportWatchingWalletAsync returns an instance of a type that can be used to
356// get the result of the RPC at some future time by invoking the Receive
357// function on the returned instance.
358//
359// See ExportWatchingWallet for the blocking version and more details.
360//
361// NOTE: This is a btcwallet extension.
362func (c *Client) ExportWatchingWalletAsync(account string) FutureExportWatchingWalletResult {
363	cmd := btcjson.NewExportWatchingWalletCmd(&account, btcjson.Bool(true))
364	return c.sendCmd(cmd)
365}
366
367// ExportWatchingWallet returns the raw bytes for a watching-only version of
368// wallet.bin and tx.bin, respectively, for the specified account that can be
369// used by btcwallet to enable a wallet which does not have the private keys
370// necessary to spend funds.
371//
372// NOTE: This is a btcwallet extension.
373func (c *Client) ExportWatchingWallet(account string) ([]byte, []byte, error) {
374	return c.ExportWatchingWalletAsync(account).Receive()
375}
376
377// FutureSessionResult is a future promise to deliver the result of a
378// SessionAsync RPC invocation (or an applicable error).
379type FutureSessionResult chan *response
380
381// Receive waits for the response promised by the future and returns the
382// session result.
383func (r FutureSessionResult) Receive() (*btcjson.SessionResult, error) {
384	res, err := receiveFuture(r)
385	if err != nil {
386		return nil, err
387	}
388
389	// Unmarshal result as a session result object.
390	var session btcjson.SessionResult
391	err = json.Unmarshal(res, &session)
392	if err != nil {
393		return nil, err
394	}
395
396	return &session, nil
397}
398
399// SessionAsync returns an instance of a type that can be used to get the result
400// of the RPC at some future time by invoking the Receive function on the
401// returned instance.
402//
403// See Session for the blocking version and more details.
404//
405// NOTE: This is a btcsuite extension.
406func (c *Client) SessionAsync() FutureSessionResult {
407	// Not supported in HTTP POST mode.
408	if c.config.HTTPPostMode {
409		return newFutureError(ErrWebsocketsRequired)
410	}
411
412	cmd := btcjson.NewSessionCmd()
413	return c.sendCmd(cmd)
414}
415
416// Session returns details regarding a websocket client's current connection.
417//
418// This RPC requires the client to be running in websocket mode.
419//
420// NOTE: This is a btcsuite extension.
421func (c *Client) Session() (*btcjson.SessionResult, error) {
422	return c.SessionAsync().Receive()
423}
424
425// FutureVersionResult is a future promise to deliver the result of a version
426// RPC invocation (or an applicable error).
427//
428// NOTE: This is a btcsuite extension ported from
429// github.com/decred/dcrrpcclient.
430type FutureVersionResult chan *response
431
432// Receive waits for the response promised by the future and returns the version
433// result.
434//
435// NOTE: This is a btcsuite extension ported from
436// github.com/decred/dcrrpcclient.
437func (r FutureVersionResult) Receive() (map[string]btcjson.VersionResult,
438	error) {
439	res, err := receiveFuture(r)
440	if err != nil {
441		return nil, err
442	}
443
444	// Unmarshal result as a version result object.
445	var vr map[string]btcjson.VersionResult
446	err = json.Unmarshal(res, &vr)
447	if err != nil {
448		return nil, err
449	}
450
451	return vr, nil
452}
453
454// VersionAsync returns an instance of a type that can be used to get the result
455// of the RPC at some future time by invoking the Receive function on the
456// returned instance.
457//
458// See Version for the blocking version and more details.
459//
460// NOTE: This is a btcsuite extension ported from
461// github.com/decred/dcrrpcclient.
462func (c *Client) VersionAsync() FutureVersionResult {
463	cmd := btcjson.NewVersionCmd()
464	return c.sendCmd(cmd)
465}
466
467// Version returns information about the server's JSON-RPC API versions.
468//
469// NOTE: This is a btcsuite extension ported from
470// github.com/decred/dcrrpcclient.
471func (c *Client) Version() (map[string]btcjson.VersionResult, error) {
472	return c.VersionAsync().Receive()
473}
474