1 package wallet
2 3 import (
4 "bytes"
5 "encoding/base64"
6 "encoding/hex"
7 js "encoding/json"
8 "errors"
9 "fmt"
10 "sync"
11 "time"
12 13 "github.com/p9c/p9/pkg/amt"
14 "github.com/p9c/p9/pkg/btcaddr"
15 "github.com/p9c/p9/pkg/chaincfg"
16 17 "github.com/p9c/p9/pkg/btcjson"
18 "github.com/p9c/p9/pkg/chainclient"
19 "github.com/p9c/p9/pkg/chainhash"
20 "github.com/p9c/p9/pkg/ecc"
21 "github.com/p9c/p9/pkg/interrupt"
22 "github.com/p9c/p9/pkg/rpcclient"
23 "github.com/p9c/p9/pkg/txrules"
24 "github.com/p9c/p9/pkg/txscript"
25 "github.com/p9c/p9/pkg/util"
26 "github.com/p9c/p9/pkg/waddrmgr"
27 "github.com/p9c/p9/pkg/wire"
28 "github.com/p9c/p9/pkg/wtxmgr"
29 )
30 31 // // confirmed checks whether a transaction at height txHeight has met minconf
32 // // confirmations for a blockchain at height curHeight.
33 // func confirmed(// minconf, txHeight, curHeight int32) bool {
34 // return confirms(txHeight, curHeight) >= minconf
35 // }
36 37 // Confirms returns the number of confirmations for a transaction in a block at height txHeight (or -1 for an
38 // unconfirmed tx) given the chain height curHeight.
39 func Confirms(txHeight, curHeight int32) int32 {
40 switch {
41 case txHeight == -1, txHeight > curHeight:
42 return 0
43 default:
44 return curHeight - txHeight + 1
45 }
46 }
47 48 // var RPCHandlers = map[string]struct {
49 // Handler RequestHandler
50 // HandlerWithChain RequestHandlerChainRequired
51 // // Function variables cannot be compared against anything but nil, so
52 // // use a boolean to record whether help generation is necessary. This
53 // // is used by the tests to ensure that help can be generated for every
54 // // implemented method.
55 // //
56 // // A single map and this bool is here is used rather than several maps
57 // // for the unimplemented handlers so every method has exactly one
58 // // handler function.
59 // //
60 // // The Return field returns a new channel of the type returned by this function. This makes it possible to
61 // // use this for callers to receive a response in the `cpc` library which implements the functions as channel pipes
62 // NoHelp bool
63 // Params interface{}
64 // Return func() interface{}
65 // }{
66 // // Reference implementation wallet methods (implemented)
67 // "addmultisigaddress": {
68 // Handler: AddMultiSigAddress,
69 // Params: make(chan btcjson.AddMultisigAddressCmd),
70 // Return: func() interface{} { return make(chan AddMultiSigAddressRes) },
71 // },
72 // "createmultisig": {
73 // Handler: CreateMultiSig,
74 // Params: make(chan btcjson.CreateMultisigCmd),
75 // Return: func() interface{} { return make(chan CreateMultiSigRes) },
76 // },
77 // "dumpprivkey": {
78 // Handler: DumpPrivKey,
79 // Params: make(chan btcjson.DumpPrivKeyCmd),
80 // Return: func() interface{} { return make(chan DumpPrivKeyRes) },
81 // },
82 // "getaccount": {
83 // Handler: GetAccount,
84 // Params: make(chan btcjson.GetAccountCmd),
85 // Return: func() interface{} { return make(chan GetAccountRes) },
86 // },
87 // "getaccountaddress": {
88 // Handler: GetAccountAddress,
89 // Params: make(chan btcjson.GetAccountAddressCmd),
90 // Return: func() interface{} { return make(chan GetAccountAddressRes) },
91 // },
92 // "getaddressesbyaccount": {
93 // Handler: GetAddressesByAccount,
94 // Params: make(chan btcjson.GetAddressesByAccountCmd),
95 // Return: func() interface{} { return make(chan GetAddressesByAccountRes) },
96 // },
97 // "getbalance": {
98 // Handler: GetBalance,
99 // Params: make(chan btcjson.GetBalanceCmd),
100 // Return: func() interface{} { return make(chan GetBalanceRes) },
101 // },
102 // "getbestblockhash": {
103 // Handler: GetBestBlockHash,
104 // Return: func() interface{} { return make(chan GetBestBlockHashRes) },
105 // },
106 // "getblockcount": {
107 // Handler: GetBlockCount,
108 // Return: func() interface{} { return make(chan GetBlockCountRes) },
109 // },
110 // "getinfo": {
111 // HandlerWithChain: GetInfo,
112 // Return: func() interface{} { return make(chan GetInfoRes) },
113 // },
114 // "getnewaddress": {
115 // Handler: GetNewAddress,
116 // Params: make(chan btcjson.GetNewAddressCmd),
117 // Return: func() interface{} { return make(chan GetNewAddressRes) },
118 // },
119 // "getrawchangeaddress": {
120 // Handler: GetRawChangeAddress,
121 // Params: make(chan btcjson.GetRawChangeAddressCmd),
122 // Return: func() interface{} { return make(chan GetRawChangeAddressRes) },
123 // },
124 // "getreceivedbyaccount": {
125 // Handler: GetReceivedByAccount,
126 // Params: make(chan btcjson.GetReceivedByAccountCmd),
127 // Return: func() interface{} { return make(chan GetReceivedByAccountRes) },
128 // },
129 // "getreceivedbyaddress": {
130 // Handler: GetReceivedByAddress,
131 // Params: make(chan btcjson.GetReceivedByAddressCmd),
132 // Return: func() interface{} { return make(chan GetReceivedByAddressRes) },
133 // },
134 // "gettransaction": {
135 // Handler: GetTransaction,
136 // Params: make(chan btcjson.GetTransactionCmd),
137 // Return: func() interface{} { return make(chan GetTransactionRes) },
138 // },
139 // "help": {
140 // Handler: HelpNoChainRPC,
141 // HandlerWithChain: HelpWithChainRPC,
142 // Params: make(chan btcjson.HelpCmd),
143 // Return: func() interface{} { return make(chan HelpNoChainRPCRes) },
144 // },
145 // "importprivkey": {
146 // Handler: ImportPrivKey,
147 // Params: make(chan btcjson.ImportPrivKeyCmd),
148 // Return: func() interface{} { return make(chan ImportPrivKeyRes) },
149 // },
150 // "keypoolrefill": {
151 // Handler: KeypoolRefill,
152 // Params: qu.T(),
153 // Return: func() interface{} { return make(chan KeypoolRefillRes) },
154 // },
155 // "listaccounts": {
156 // Handler: ListAccounts,
157 // Params: make(chan btcjson.ListAccountsCmd),
158 // Return: func() interface{} { return make(chan ListAccountsRes) },
159 // },
160 // "listlockunspent": {
161 // Handler: ListLockUnspent,
162 // Params: qu.T(),
163 // Return: func() interface{} { return make(chan ListLockUnspentRes) },
164 // },
165 // "listreceivedbyaccount": {
166 // Handler: ListReceivedByAccount,
167 // Params: make(chan btcjson.ListReceivedByAccountCmd),
168 // Return: func() interface{} { return make(chan ListReceivedByAccountRes) },
169 // },
170 // "listreceivedbyaddress": {
171 // Handler: ListReceivedByAddress,
172 // Params: make(chan btcjson.ListReceivedByAddressCmd),
173 // Return: func() interface{} { return make(chan ListReceivedByAddressRes) },
174 // },
175 // "listsinceblock": {
176 // HandlerWithChain: ListSinceBlock,
177 // Params: make(chan btcjson.ListSinceBlockCmd),
178 // Return: func() interface{} { return make(chan ListSinceBlockRes) },
179 // },
180 // "listtransactions": {
181 // Handler: ListTransactions,
182 // Params: make(chan btcjson.ListTransactionsCmd),
183 // Return: func() interface{} { return make(chan ListTransactionsRes) },
184 // },
185 // "listunspent": {
186 // Handler: ListUnspent,
187 // Params: make(chan btcjson.ListUnspentCmd),
188 // Return: func() interface{} { return make(chan ListUnspentRes) },
189 // },
190 // "lockunspent": {
191 // Handler: LockUnspent,
192 // Params: make(chan btcjson.LockUnspentCmd),
193 // Return: func() interface{} { return make(chan LockUnspentRes) },
194 // },
195 // "sendfrom": {
196 // HandlerWithChain: SendFrom,
197 // Params: make(chan btcjson.SendFromCmd),
198 // Return: func() interface{} { return make(chan SendFromRes) },
199 // },
200 // "sendmany": {
201 // Handler: SendMany,
202 // Params: make(chan btcjson.SendManyCmd),
203 // Return: func() interface{} { return make(chan SendManyRes) },
204 // },
205 // "sendtoaddress": {
206 // Handler: SendToAddress,
207 // Params: make(chan btcjson.SendToAddressCmd),
208 // Return: func() interface{} { return make(chan SendToAddressRes) },
209 // },
210 // "settxfee": {
211 // Handler: SetTxFee,
212 // Params: make(chan btcjson.SetTxFeeCmd),
213 // Return: func() interface{} { return make(chan SetTxFeeRes) },
214 // },
215 // "signmessage": {
216 // Handler: SignMessage,
217 // Params: make(chan btcjson.SignMessageCmd),
218 // Return: func() interface{} { return make(chan SignMessageRes) },
219 // },
220 // "signrawtransaction": {
221 // HandlerWithChain: SignRawTransaction,
222 // Params: make(chan btcjson.SignRawTransactionCmd),
223 // Return: func() interface{} { return make(chan SignRawTransactionRes) },
224 // },
225 // "validateaddress": {
226 // Handler: ValidateAddress,
227 // Params: make(chan btcjson.ValidateAddressCmd),
228 // Return: func() interface{} { return make(chan ValidateAddressRes) },
229 // },
230 // "verifymessage": {
231 // Handler: VerifyMessage,
232 // Params: make(chan btcjson.VerifyMessageCmd),
233 // Return: func() interface{} { return make(chan VerifyMessageRes) },
234 // },
235 // "walletlock": {
236 // Handler: WalletLock,
237 // Params: qu.T(),
238 // Return: func() interface{} { return make(chan WalletLockRes) },
239 // },
240 // "walletpassphrase": {
241 // Handler: WalletPassphrase,
242 // Params: make(chan btcjson.WalletPassphraseCmd),
243 // Return: func() interface{} { return make(chan WalletPassphraseRes) },
244 // },
245 // "walletpassphrasechange": {
246 // Handler: WalletPassphraseChange,
247 // Params: make(chan btcjson.WalletPassphraseChangeCmd),
248 // Return: func() interface{} { return make(chan WalletPassphraseChangeRes) },
249 // },
250 // // Reference implementation methods (still unimplemented)
251 // "backupwallet": {Handler: Unimplemented, NoHelp: true},
252 // "dumpwallet": {Handler: Unimplemented, NoHelp: true},
253 // "getwalletinfo": {Handler: Unimplemented, NoHelp: true},
254 // "importwallet": {Handler: Unimplemented, NoHelp: true},
255 // "listaddressgroupings": {Handler: Unimplemented, NoHelp: true},
256 // // Reference methods which can't be implemented by btcwallet due to
257 // // design decision differences
258 // "encryptwallet": {Handler: Unsupported, NoHelp: true},
259 // "move": {Handler: Unsupported, NoHelp: true},
260 // "setaccount": {Handler: Unsupported, NoHelp: true},
261 // // Extensions to the reference client JSON-RPC API
262 // "createnewaccount": {
263 // Handler: CreateNewAccount,
264 // Params: make(chan btcjson.CreateNewAccountCmd),
265 // Return: func() interface{} { return make(chan CreateNewAccountRes) },
266 // },
267 // "getbestblock": {
268 // Handler: GetBestBlock,
269 // Params: qu.T(),
270 // Return: func() interface{} { return make(chan GetBestBlockRes) },
271 // },
272 // // This was an extension but the reference implementation added it as
273 // // well, but with a different API (no account parameter). It's listed
274 // // here because it hasn't been update to use the reference
275 // // implemenation's API.
276 // "getunconfirmedbalance": {
277 // Handler: GetUnconfirmedBalance,
278 // Params: make(chan btcjson.GetUnconfirmedBalanceCmd),
279 // Return: func() interface{} { return make(chan GetUnconfirmedBalanceRes) },
280 // },
281 // "listaddresstransactions": {
282 // Handler: ListAddressTransactions,
283 // Params: make(chan btcjson.ListAddressTransactionsCmd),
284 // Return: func() interface{} { return make(chan ListAddressTransactionsRes) },
285 // },
286 // "listalltransactions": {
287 // Handler: ListAllTransactions,
288 // Params: make(chan btcjson.ListAllTransactionsCmd),
289 // Return: func() interface{} { return make(chan ListAllTransactionsRes) },
290 // },
291 // "renameaccount": {
292 // Handler: RenameAccount,
293 // Params: make(chan btcjson.RenameAccountCmd),
294 // Return: func() interface{} { return make(chan RenameAccountRes) },
295 // },
296 // "walletislocked": {
297 // Handler: WalletIsLocked,
298 // Params: qu.T(),
299 // Return: func() interface{} { return make(chan WalletIsLockedRes) },
300 // },
301 // "dropwallethistory": {
302 // Handler: HandleDropWalletHistory,
303 // Params: qu.T(),
304 // Return: func() interface{} { return make(chan DropWalletHistoryRes) },
305 // },
306 // }
307 308 // Unimplemented handles an Unimplemented RPC request with the
309 // appropiate error.
310 func Unimplemented(interface{}, *Wallet) (interface{}, error) {
311 return nil, &btcjson.RPCError{
312 Code: btcjson.ErrRPCUnimplemented,
313 Message: "Method unimplemented",
314 }
315 }
316 317 // Unsupported handles a standard bitcoind RPC request which is Unsupported by btcwallet due to design differences.
318 func Unsupported(interface{}, *Wallet) (interface{}, error) {
319 return nil, &btcjson.RPCError{
320 Code: -1,
321 Message: "Request unsupported by wallet",
322 }
323 }
324 325 // LazyHandler is a closure over a requestHandler or passthrough request with the RPC server's wallet and chain server
326 // variables as part of the closure context.
327 type LazyHandler func() (interface{}, *btcjson.RPCError)
328 329 // LazyApplyHandler looks up the best request handler func for the method, returning a closure that will execute it with
330 // the (required) wallet and (optional) consensus RPC server. If no handlers are found and the chainClient is not nil,
331 // the returned handler performs RPC passthrough.
332 func LazyApplyHandler(request *btcjson.Request, w *Wallet, chainClient chainclient.Interface) LazyHandler {
333 handlerData, ok := RPCHandlers[request.Method]
334 D.Ln("LazyApplyHandler >>> >>> >>>", ok, handlerData.Handler != nil, w != nil, chainClient != nil)
335 if ok && handlerData.Handler != nil && w != nil && chainClient != nil {
336 D.Ln("found handler for call")
337 // D.S(request)
338 return func() (interface{}, *btcjson.RPCError) {
339 cmd, e := btcjson.UnmarshalCmd(request)
340 if e != nil {
341 return nil, btcjson.ErrRPCInvalidRequest
342 }
343 switch client := chainClient.(type) {
344 case *chainclient.RPCClient:
345 D.Ln("client is a chain.RPCClient")
346 var resp interface{}
347 if resp, e = handlerData.Handler(cmd, w, client); E.Chk(e) {
348 return nil, JSONError(e)
349 }
350 D.Ln("handler call succeeded")
351 return resp, nil
352 default:
353 D.Ln("client is unknown")
354 return nil, &btcjson.RPCError{
355 Code: -1,
356 Message: "Chain RPC is inactive",
357 }
358 }
359 }
360 }
361 D.Ln("failed to find handler for call")
362 // I.Ln("handler", handlerData.Handler, "wallet", w)
363 if ok && handlerData.Handler != nil && w != nil {
364 D.Ln("handling", request.Method)
365 return func() (interface{}, *btcjson.RPCError) {
366 cmd, e := btcjson.UnmarshalCmd(request)
367 if e != nil {
368 return nil, btcjson.ErrRPCInvalidRequest
369 }
370 var resp interface{}
371 if resp, e = handlerData.Handler(cmd, w); E.Chk(e) {
372 return nil, JSONError(e)
373 }
374 return resp, nil
375 }
376 }
377 // Fallback to RPC passthrough
378 return func() (interface{}, *btcjson.RPCError) {
379 I.Ln("passing to node", request.Method)
380 if chainClient == nil {
381 return nil, &btcjson.RPCError{
382 Code: -1,
383 Message: "Chain RPC is inactive",
384 }
385 }
386 switch client := chainClient.(type) {
387 case *chainclient.RPCClient:
388 resp, e := client.RawRequest(
389 request.Method,
390 request.Params,
391 )
392 if e != nil {
393 return nil, JSONError(e)
394 }
395 return &resp, nil
396 default:
397 return nil, &btcjson.RPCError{
398 Code: -1,
399 Message: "Chain RPC is inactive",
400 }
401 }
402 }
403 }
404 405 // MakeResponse makes the JSON-RPC response struct for the result and error returned by a requestHandler. The returned
406 // response is not ready for marshaling and sending off to a client, but must be
407 func MakeResponse(id, result interface{}, e error) btcjson.Response {
408 idPtr := IDPointer(id)
409 if e != nil {
410 return btcjson.Response{
411 ID: idPtr,
412 Error: JSONError(e),
413 }
414 }
415 var resultBytes []byte
416 resultBytes, e = js.Marshal(result)
417 if e != nil {
418 return btcjson.Response{
419 ID: idPtr,
420 Error: &btcjson.RPCError{
421 Code: btcjson.ErrRPCInternal.Code,
422 Message: "Unexpected error marshalling result",
423 },
424 }
425 }
426 return btcjson.Response{
427 ID: idPtr,
428 Result: resultBytes,
429 }
430 }
431 432 // JSONError creates a JSON-RPC error from the Go error.
433 func JSONError(e error) *btcjson.RPCError {
434 if e == nil {
435 return nil
436 }
437 code := btcjson.ErrRPCWallet
438 switch e := e.(type) {
439 case btcjson.RPCError:
440 return &e
441 case *btcjson.RPCError:
442 return e
443 case DeserializationError:
444 code = btcjson.ErrRPCDeserialization
445 case InvalidParameterError:
446 code = btcjson.ErrRPCInvalidParameter
447 case ParseError:
448 code = btcjson.ErrRPCParse.Code
449 case waddrmgr.ManagerError:
450 switch e.ErrorCode {
451 case waddrmgr.ErrWrongPassphrase:
452 code = btcjson.ErrRPCWalletPassphraseIncorrect
453 }
454 }
455 return &btcjson.RPCError{
456 Code: code,
457 Message: e.Error(),
458 }
459 }
460 461 // MakeMultiSigScript is a helper function to combine common logic for AddMultiSig and CreateMultiSig.
462 func MakeMultiSigScript(w *Wallet, keys []string, nRequired int) ([]byte, error) {
463 keysesPrecious := make([]*btcaddr.PubKey, len(keys))
464 // The address list will made up either of addresses (pubkey hash), for which we need to look up the keys in
465 // wallet, straight pubkeys, or a mixture of the two.
466 for i, a := range keys {
467 // try to parse as pubkey address
468 a, e := DecodeAddress(a, w.ChainParams())
469 if e != nil {
470 return nil, e
471 }
472 switch addr := a.(type) {
473 case *btcaddr.PubKey:
474 keysesPrecious[i] = addr
475 default:
476 pubKey, e := w.PubKeyForAddress(addr)
477 if e != nil {
478 return nil, e
479 }
480 pubKeyAddr, e := btcaddr.NewPubKey(
481 pubKey.SerializeCompressed(), w.ChainParams(),
482 )
483 if e != nil {
484 return nil, e
485 }
486 keysesPrecious[i] = pubKeyAddr
487 }
488 }
489 return txscript.MultiSigScript(keysesPrecious, nRequired)
490 }
491 492 // AddMultiSigAddress handles an addmultisigaddress request by adding a
493 // multisig address to the given wallet.
494 func AddMultiSigAddress(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (
495 interface{},
496 error,
497 ) {
498 var msg string
499 cmd, ok := icmd.(*btcjson.AddMultisigAddressCmd)
500 // cmd, ok := icmd.(*btcjson.ListTransactionsCmd)
501 if !ok {
502 var h string
503 h = HelpDescsEnUS()["addmultisigaddress"]
504 msg += h
505 return nil, &btcjson.RPCError{
506 Code: btcjson.ErrRPCInvalidParameter,
507 Message: msg,
508 // "invalid subcommand for addnode",
509 }
510 }
511 // If an account is specified, ensure that is the imported account.
512 if cmd.Account != nil && *cmd.Account != waddrmgr.ImportedAddrAccountName {
513 return nil, &ErrNotImportedAccount
514 }
515 secp256k1Addrs := make([]btcaddr.Address, len(cmd.Keys))
516 for i, k := range cmd.Keys {
517 addr, e := DecodeAddress(k, w.ChainParams())
518 if e != nil {
519 return nil, ParseError{e}
520 }
521 secp256k1Addrs[i] = addr
522 }
523 script, e := w.MakeMultiSigScript(secp256k1Addrs, cmd.NRequired)
524 if e != nil {
525 return nil, e
526 }
527 p2shAddr, e := w.ImportP2SHRedeemScript(script)
528 if e != nil {
529 return nil, e
530 }
531 return p2shAddr.EncodeAddress(), nil
532 }
533 534 // CreateMultiSig handles an createmultisig request by returning a multisig address for the given inputs.
535 func CreateMultiSig(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (interface{}, error) {
536 var msg string
537 cmd, ok := icmd.(*btcjson.CreateMultisigCmd)
538 if !ok {
539 var h string
540 h = HelpDescsEnUS()["createmultisig"]
541 msg += h
542 return nil, &btcjson.RPCError{
543 Code: btcjson.ErrRPCInvalidParameter,
544 Message: msg,
545 // "invalid subcommand for addnode",
546 }
547 }
548 script, e := MakeMultiSigScript(w, cmd.Keys, cmd.NRequired)
549 if e != nil {
550 return nil, ParseError{e}
551 }
552 address, e := btcaddr.NewScriptHash(script, w.ChainParams())
553 if e != nil {
554 // above is a valid script, shouldn't happen.
555 return nil, e
556 }
557 return btcjson.CreateMultiSigResult{
558 Address: address.EncodeAddress(),
559 RedeemScript: hex.EncodeToString(script),
560 }, nil
561 }
562 563 // DumpPrivKey handles a dumpprivkey request with the private key for a single address, or an appropriate error if the
564 // wallet is locked.
565 func DumpPrivKey(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (interface{}, error) {
566 var msg string
567 cmd, ok := icmd.(*btcjson.DumpPrivKeyCmd)
568 if !ok {
569 msg = HelpDescsEnUS()["dumpprivkey"]
570 return nil, &btcjson.RPCError{
571 Code: btcjson.ErrRPCInvalidParameter,
572 Message: msg,
573 // "invalid subcommand for addnode",
574 }
575 }
576 addr, e := DecodeAddress(cmd.Address, w.ChainParams())
577 if e != nil {
578 return nil, e
579 }
580 key, e := w.DumpWIFPrivateKey(addr)
581 if waddrmgr.IsError(e, waddrmgr.ErrLocked) {
582 // Address was found, but the private key isn't accessible.
583 return nil, &ErrWalletUnlockNeeded
584 }
585 return key, e
586 }
587 588 // // dumpWallet handles a dumpwallet request by returning all private
589 // // keys in a wallet, or an appropiate error if the wallet is locked.
590 // // TODO: finish this to match bitcoind by writing the dump to a file.
591 // func dumpWallet(// icmd interface{}, w *wallet.Wallet) (interface{}, error) {
592 // keys, e := w.DumpPrivKeys()
593 // if waddrmgr.IsError(err, waddrmgr.ErrLocked) {
594 // return nil, &ErrWalletUnlockNeeded
595 // }
596 // return keys, err
597 // }
598 599 // GetAddressesByAccount handles a getaddressesbyaccount request by returning
600 // all addresses for an account, or an error if the requested account does not
601 // exist.
602 func GetAddressesByAccount(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (
603 interface{},
604 error,
605 ) {
606 cmd, ok := icmd.(*btcjson.GetAddressesByAccountCmd)
607 if !ok {
608 return nil, &btcjson.RPCError{
609 Code: btcjson.ErrRPCInvalidParameter,
610 Message: HelpDescsEnUS()["dumpprivkey"],
611 // "invalid subcommand for addnode",
612 }
613 }
614 account, e := w.AccountNumber(waddrmgr.KeyScopeBIP0044, cmd.Account)
615 if e != nil {
616 return nil, e
617 }
618 addrs, e := w.AccountAddresses(account)
619 if e != nil {
620 return nil, e
621 }
622 addrStrs := make([]string, len(addrs))
623 for i, a := range addrs {
624 addrStrs[i] = a.EncodeAddress()
625 }
626 return addrStrs, nil
627 }
628 629 // GetBalance handles a getbalance request by returning the balance for an
630 // account (wallet), or an error if the requested account does not exist.
631 func GetBalance(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (interface{}, error) {
632 cmd, ok := icmd.(*btcjson.GetBalanceCmd)
633 if !ok {
634 return nil, &btcjson.RPCError{
635 Code: btcjson.ErrRPCInvalidParameter,
636 Message: HelpDescsEnUS()["getbalance"],
637 // "invalid subcommand for addnode",
638 }
639 }
640 var balance amt.Amount
641 var e error
642 accountName := "*"
643 if cmd.Account != nil {
644 accountName = *cmd.Account
645 }
646 if accountName == "*" {
647 balance, e = w.CalculateBalance(int32(*cmd.MinConf))
648 if e != nil {
649 return nil, e
650 }
651 } else {
652 var account uint32
653 account, e = w.AccountNumber(waddrmgr.KeyScopeBIP0044, accountName)
654 if e != nil {
655 return nil, e
656 }
657 bals, e := w.CalculateAccountBalances(account, int32(*cmd.MinConf))
658 if e != nil {
659 return nil, e
660 }
661 balance = bals.Spendable
662 }
663 return balance.ToDUO(), nil
664 }
665 666 // GetBestBlock handles a getbestblock request by returning a JSON object with
667 // the height and hash of the most recently processed block.
668 func GetBestBlock(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (interface{}, error) {
669 blk := w.Manager.SyncedTo()
670 result := &btcjson.GetBestBlockResult{
671 Hash: blk.Hash.String(),
672 Height: blk.Height,
673 }
674 return result, nil
675 }
676 677 // GetBestBlockHash handles a getbestblockhash request by returning the hash of
678 // the most recently processed block.
679 func GetBestBlockHash(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (interface{}, error) {
680 blk := w.Manager.SyncedTo()
681 return blk.Hash.String(), nil
682 }
683 684 // GetBlockCount handles a getblockcount request by returning the chain height
685 // of the most recently processed block.
686 func GetBlockCount(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (interface{}, error) {
687 blk := w.Manager.SyncedTo()
688 return blk.Height, nil
689 }
690 691 // GetInfo handles a getinfo request by returning the a structure containing
692 // information about the current state of btcwallet. exist.
693 func GetInfo(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (interface{}, error) {
694 if len(chainClient) < 1 || chainClient[0] == nil {
695 return nil, &btcjson.RPCError{
696 Code: btcjson.ErrRPCNoChain,
697 Message: "there is currently no chain client to get this response",
698 }
699 }
700 // Call down to pod for all of the information in this command known by them.
701 var info *btcjson.InfoWalletResult
702 var e error
703 info, e = chainClient[0].GetInfo()
704 if e != nil {
705 return nil, e
706 }
707 var bal amt.Amount
708 bal, e = w.CalculateBalance(1)
709 if e != nil {
710 return nil, e
711 }
712 // TODO(davec): This should probably have a database version as opposed
713 // to using the manager version.
714 info.WalletVersion = int32(waddrmgr.LatestMgrVersion)
715 info.Balance = bal.ToDUO()
716 info.PaytxFee = float64(txrules.DefaultRelayFeePerKb)
717 // We don't set the following since they don't make much sense in the wallet architecture:
718 //
719 // - unlocked_until
720 // - errors
721 return info, nil
722 }
723 724 func DecodeAddress(s string, params *chaincfg.Params) (btcaddr.Address, error) {
725 addr, e := btcaddr.Decode(s, params)
726 if e != nil {
727 msg := fmt.Sprintf("Invalid address %q: decode failed with %#q", s, e)
728 return nil, &btcjson.RPCError{
729 Code: btcjson.ErrRPCInvalidAddressOrKey,
730 Message: msg,
731 }
732 }
733 if !addr.IsForNet(params) {
734 msg := fmt.Sprintf(
735 "Invalid address %q: not intended for use on %s",
736 addr, params.Name,
737 )
738 return nil, &btcjson.RPCError{
739 Code: btcjson.ErrRPCInvalidAddressOrKey,
740 Message: msg,
741 }
742 }
743 return addr, nil
744 }
745 746 // GetAccount handles a getaccount request by returning the account name
747 // associated with a single address.
748 func GetAccount(
749 icmd interface{}, w *Wallet,
750 chainClient ...*chainclient.RPCClient,
751 ) (interface{}, error) {
752 cmd, ok := icmd.(*btcjson.GetAccountCmd)
753 if !ok {
754 return nil, &btcjson.RPCError{
755 Code: btcjson.ErrRPCInvalidParameter,
756 Message: HelpDescsEnUS()["getaccount"],
757 // "invalid subcommand for addnode",
758 }
759 }
760 addr, e := DecodeAddress(cmd.Address, w.ChainParams())
761 if e != nil {
762 return nil, e
763 }
764 // Fetch the associated account
765 account, e := w.AccountOfAddress(addr)
766 if e != nil {
767 return nil, &ErrAddressNotInWallet
768 }
769 acctName, e := w.AccountName(waddrmgr.KeyScopeBIP0044, account)
770 if e != nil {
771 return nil, &ErrAccountNameNotFound
772 }
773 return acctName, nil
774 }
775 776 // GetAccountAddress handles a getaccountaddress by returning the most
777 // recently-created chained address that has not yet been used (does not yet
778 // appear in the blockchain, or any tx that has arrived in the pod mempool).
779 //
780 // If the most recently-requested address has been used, a new address (the next
781 // chained address in the keypool) is used. This can fail if the keypool runs
782 // out (and will return json.ErrRPCWalletKeypoolRanOut if that happens).
783 func GetAccountAddress(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (interface{}, error) {
784 cmd, ok := icmd.(*btcjson.GetAccountAddressCmd)
785 if !ok {
786 return nil, &btcjson.RPCError{
787 Code: btcjson.ErrRPCInvalidParameter,
788 Message: HelpDescsEnUS()["getaccountaddress"],
789 // "invalid subcommand for addnode",
790 }
791 }
792 account, e := w.AccountNumber(waddrmgr.KeyScopeBIP0044, cmd.Account)
793 if e != nil {
794 return nil, e
795 }
796 addr, e := w.CurrentAddress(account, waddrmgr.KeyScopeBIP0044)
797 if e != nil {
798 return nil, e
799 }
800 return addr.EncodeAddress(), e
801 }
802 803 // GetUnconfirmedBalance handles a getunconfirmedbalance extension request by
804 // returning the current unconfirmed balance of an account.
805 func GetUnconfirmedBalance(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (
806 interface{},
807 error,
808 ) {
809 cmd, ok := icmd.(*btcjson.GetUnconfirmedBalanceCmd)
810 if !ok {
811 return nil, &btcjson.RPCError{
812 Code: btcjson.ErrRPCInvalidParameter,
813 Message: HelpDescsEnUS()["getunconfirmedbalance"],
814 // "invalid subcommand for addnode",
815 }
816 }
817 acctName := "default"
818 if cmd.Account != nil {
819 acctName = *cmd.Account
820 }
821 account, e := w.AccountNumber(waddrmgr.KeyScopeBIP0044, acctName)
822 if e != nil {
823 return nil, e
824 }
825 bals, e := w.CalculateAccountBalances(account, 1)
826 if e != nil {
827 return nil, e
828 }
829 return (bals.Total - bals.Spendable).ToDUO(), nil
830 }
831 832 // ImportPrivKey handles an importprivkey request by parsing a WIF-encoded
833 // private key and adding it to an account.
834 func ImportPrivKey(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (interface{}, error) {
835 cmd, ok := icmd.(*btcjson.ImportPrivKeyCmd)
836 if !ok {
837 return nil, &btcjson.RPCError{
838 Code: btcjson.ErrRPCInvalidParameter,
839 Message: HelpDescsEnUS()["importprivkey"],
840 // "invalid subcommand for addnode",
841 }
842 }
843 // Ensure that private keys are only imported to the correct account.
844 //
845 // Yes, Label is the account name.
846 if cmd.Label != nil && *cmd.Label != waddrmgr.ImportedAddrAccountName {
847 return nil, &ErrNotImportedAccount
848 }
849 wif, e := util.DecodeWIF(cmd.PrivKey)
850 if e != nil {
851 return nil, &btcjson.RPCError{
852 Code: btcjson.ErrRPCInvalidAddressOrKey,
853 Message: "WIF decode failed: " + e.Error(),
854 }
855 }
856 if !wif.IsForNet(w.ChainParams()) {
857 return nil, &btcjson.RPCError{
858 Code: btcjson.ErrRPCInvalidAddressOrKey,
859 Message: "Key is not intended for " + w.ChainParams().Name,
860 }
861 }
862 // Import the private key, handling any errors.
863 _, e = w.ImportPrivateKey(waddrmgr.KeyScopeBIP0044, wif, nil, *cmd.Rescan)
864 switch {
865 case waddrmgr.IsError(e, waddrmgr.ErrDuplicateAddress):
866 // Do not return duplicate key errors to the client.
867 return nil, nil
868 case waddrmgr.IsError(e, waddrmgr.ErrLocked):
869 return nil, &ErrWalletUnlockNeeded
870 }
871 return nil, e
872 }
873 874 // KeypoolRefill handles the keypoolrefill command. Since we handle the keypool automatically this does nothing since
875 // refilling is never manually required.
876 func KeypoolRefill(
877 icmd interface{}, w *Wallet,
878 chainClient ...*chainclient.RPCClient,
879 ) (interface{}, error) {
880 return nil, nil
881 }
882 883 // CreateNewAccount handles a createnewaccount request by creating and returning a new account. If the last account has
884 // no transaction history as per BIP 0044 a new account cannot be created so an error will be returned.
885 func CreateNewAccount(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (interface{}, error) {
886 cmd, ok := icmd.(*btcjson.CreateNewAccountCmd)
887 if !ok {
888 return nil, &btcjson.RPCError{
889 Code: btcjson.ErrRPCInvalidParameter,
890 Message: HelpDescsEnUS()["createnewaccount"],
891 // "invalid subcommand for addnode",
892 }
893 }
894 // The wildcard * is reserved by the rpc server with the special meaning of "all
895 // accounts", so disallow naming accounts to this string.
896 if cmd.Account == "*" {
897 return nil, &ErrReservedAccountName
898 }
899 _, e := w.NextAccount(waddrmgr.KeyScopeBIP0044, cmd.Account)
900 if waddrmgr.IsError(e, waddrmgr.ErrLocked) {
901 return nil, &btcjson.RPCError{
902 Code: btcjson.ErrRPCWalletUnlockNeeded,
903 Message: "Creating an account requires the wallet to be unlocked. " +
904 "Enter the wallet passphrase with walletpassphrase to unlock",
905 }
906 }
907 return nil, e
908 }
909 910 // RenameAccount handles a renameaccount request by renaming an account. If the
911 // account does not exist an appropiate error will be returned.
912 func RenameAccount(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (interface{}, error) {
913 cmd, ok := icmd.(*btcjson.RenameAccountCmd)
914 if !ok {
915 return nil, &btcjson.RPCError{
916 Code: btcjson.ErrRPCInvalidParameter,
917 Message: HelpDescsEnUS()["renameaccount"],
918 // "invalid subcommand for addnode",
919 }
920 }
921 // The wildcard * is reserved by the rpc server with the special meaning of "all
922 // accounts", so disallow naming accounts to this string.
923 if cmd.NewAccount == "*" {
924 return nil, &ErrReservedAccountName
925 }
926 // Chk that given account exists
927 account, e := w.AccountNumber(waddrmgr.KeyScopeBIP0044, cmd.OldAccount)
928 if e != nil {
929 return nil, e
930 }
931 return nil, w.RenameAccount(waddrmgr.KeyScopeBIP0044, account, cmd.NewAccount)
932 }
933 934 // GetNewAddress handles a getnewaddress request by returning a new address for
935 // an account. If the account does not exist an appropiate error is returned.
936 //
937 // TODO: Follow BIP 0044 and warn if number of unused addresses exceeds the gap limit.
938 func GetNewAddress(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (interface{}, error) {
939 cmd, ok := icmd.(*btcjson.GetNewAddressCmd)
940 if !ok {
941 return nil, &btcjson.RPCError{
942 Code: btcjson.ErrRPCInvalidParameter,
943 Message: HelpDescsEnUS()["getnewaddress"],
944 // "invalid subcommand for addnode",
945 }
946 }
947 acctName := "default"
948 if cmd.Account != nil {
949 acctName = *cmd.Account
950 }
951 account, e := w.AccountNumber(waddrmgr.KeyScopeBIP0044, acctName)
952 if e != nil {
953 return nil, e
954 }
955 addr, e := w.NewAddress(account, waddrmgr.KeyScopeBIP0044, false)
956 if e != nil {
957 return nil, e
958 }
959 // Return the new payment address string.
960 return addr.EncodeAddress(), nil
961 }
962 963 // GetRawChangeAddress handles a getrawchangeaddress request by creating and
964 // returning a new change address for an account.
965 //
966 // Note: bitcoind allows specifying the account as an optional parameter, but
967 // ignores the parameter.
968 func GetRawChangeAddress(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (
969 interface{},
970 error,
971 ) {
972 cmd, ok := icmd.(*btcjson.GetRawChangeAddressCmd)
973 if !ok {
974 return nil, &btcjson.RPCError{
975 Code: btcjson.ErrRPCInvalidParameter,
976 Message: HelpDescsEnUS()["getrawchangeaddress"],
977 // "invalid subcommand for addnode",
978 }
979 }
980 acctName := "default"
981 if cmd.Account != nil {
982 acctName = *cmd.Account
983 }
984 account, e := w.AccountNumber(waddrmgr.KeyScopeBIP0044, acctName)
985 if e != nil {
986 return nil, e
987 }
988 addr, e := w.NewChangeAddress(account, waddrmgr.KeyScopeBIP0044)
989 if e != nil {
990 return nil, e
991 }
992 // Return the new payment address string.
993 return addr.EncodeAddress(), nil
994 }
995 996 // GetReceivedByAccount handles a getreceivedbyaccount request by returning the
997 // total amount received by addresses of an account.
998 func GetReceivedByAccount(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (
999 ii interface{},
1000 e error,
1001 ) {
1002 cmd, ok := icmd.(*btcjson.GetReceivedByAccountCmd)
1003 if !ok {
1004 return nil, &btcjson.RPCError{
1005 Code: btcjson.ErrRPCInvalidParameter,
1006 Message: HelpDescsEnUS()["getreceivedbyaccount"],
1007 // "invalid subcommand for addnode",
1008 }
1009 }
1010 var account uint32
1011 account, e = w.AccountNumber(waddrmgr.KeyScopeBIP0044, cmd.Account)
1012 if e != nil {
1013 return nil, e
1014 }
1015 // TODO: This is more inefficient that it could be, but the entire algorithm is
1016 // already dominated by reading every transaction in the wallet's history.
1017 var results []AccountTotalReceivedResult
1018 results, e = w.TotalReceivedForAccounts(
1019 waddrmgr.KeyScopeBIP0044, int32(*cmd.MinConf),
1020 )
1021 if e != nil {
1022 return nil, e
1023 }
1024 acctIndex := int(account)
1025 if account == waddrmgr.ImportedAddrAccount {
1026 acctIndex = len(results) - 1
1027 }
1028 return results[acctIndex].TotalReceived.ToDUO(), nil
1029 }
1030 1031 // GetReceivedByAddress handles a getreceivedbyaddress request by returning the total amount received by a single
1032 // address.
1033 func GetReceivedByAddress(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (
1034 interface{},
1035 error,
1036 ) {
1037 cmd, ok := icmd.(*btcjson.GetReceivedByAddressCmd)
1038 if !ok {
1039 return nil, &btcjson.RPCError{
1040 Code: btcjson.ErrRPCInvalidParameter,
1041 Message: HelpDescsEnUS()["getreceivedbyaddress"],
1042 // "invalid subcommand for addnode",
1043 }
1044 }
1045 addr, e := DecodeAddress(cmd.Address, w.ChainParams())
1046 if e != nil {
1047 return nil, e
1048 }
1049 total, e := w.TotalReceivedForAddr(addr, int32(*cmd.MinConf))
1050 if e != nil {
1051 return nil, e
1052 }
1053 return total.ToDUO(), nil
1054 }
1055 1056 // GetTransaction handles a gettransaction request by returning details about a single transaction saved by wallet.
1057 func GetTransaction(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (interface{}, error) {
1058 cmd, ok := icmd.(*btcjson.GetTransactionCmd)
1059 if !ok {
1060 return nil, &btcjson.RPCError{
1061 Code: btcjson.ErrRPCInvalidParameter,
1062 Message: HelpDescsEnUS()["gettransaction"],
1063 // "invalid subcommand for addnode",
1064 }
1065 }
1066 txHash, e := chainhash.NewHashFromStr(cmd.Txid)
1067 if e != nil {
1068 return nil, &btcjson.RPCError{
1069 Code: btcjson.ErrRPCDecodeHexString,
1070 Message: "Transaction hash string decode failed: " + e.Error(),
1071 }
1072 }
1073 details, e := ExposeUnstableAPI(w).TxDetails(txHash)
1074 if e != nil {
1075 return nil, e
1076 }
1077 if details == nil {
1078 return nil, &ErrNoTransactionInfo
1079 }
1080 syncBlock := w.Manager.SyncedTo()
1081 // TODO: The serialized transaction is already in the DB, so
1082 // reserializing can be avoided here.
1083 var txBuf bytes.Buffer
1084 txBuf.Grow(details.MsgTx.SerializeSize())
1085 e = details.MsgTx.Serialize(&txBuf)
1086 if e != nil {
1087 return nil, e
1088 }
1089 // TODO: Add a "generated" field to this result type. "generated":true
1090 // is only added if the transaction is a coinbase.
1091 ret := btcjson.GetTransactionResult{
1092 TxID: cmd.Txid,
1093 Hex: hex.EncodeToString(txBuf.Bytes()),
1094 Time: details.Received.Unix(),
1095 TimeReceived: details.Received.Unix(),
1096 WalletConflicts: []string{}, // Not saved
1097 // Generated: blockchain.IsCoinBaseTx(&details.MsgTx),
1098 }
1099 if details.Block.Height != -1 {
1100 ret.BlockHash = details.Block.Hash.String()
1101 ret.BlockTime = details.Block.Time.Unix()
1102 ret.Confirmations = int64(Confirms(details.Block.Height, syncBlock.Height))
1103 }
1104 var (
1105 debitTotal amt.Amount
1106 creditTotal amt.Amount // Excludes change
1107 fee amt.Amount
1108 feeF64 float64
1109 )
1110 for _, deb := range details.Debits {
1111 debitTotal += deb.Amount
1112 }
1113 for _, cred := range details.Credits {
1114 if !cred.Change {
1115 creditTotal += cred.Amount
1116 }
1117 }
1118 // Fee can only be determined if every input is a debit.
1119 if len(details.Debits) == len(details.MsgTx.TxIn) {
1120 var outputTotal amt.Amount
1121 for _, output := range details.MsgTx.TxOut {
1122 outputTotal += amt.Amount(output.Value)
1123 }
1124 fee = debitTotal - outputTotal
1125 feeF64 = fee.ToDUO()
1126 }
1127 if len(details.Debits) == 0 {
1128 // Credits must be set later, but since we know the full length
1129 // of the details slice, allocate it with the correct cap.
1130 ret.Details = make([]btcjson.GetTransactionDetailsResult, 0, len(details.Credits))
1131 } else {
1132 ret.Details = make([]btcjson.GetTransactionDetailsResult, 1, len(details.Credits)+1)
1133 ret.Details[0] = btcjson.GetTransactionDetailsResult{
1134 // Fields left zeroed:
1135 // InvolvesWatchOnly
1136 // Account
1137 // Address
1138 // VOut
1139 //
1140 // TODO(jrick): Address and VOut should always be set,
1141 // but we're doing the wrong thing here by not matching
1142 // core. Instead, gettransaction should only be adding
1143 // details for transaction outputs, just like
1144 // listtransactions (but using the short result format).
1145 Category: "send",
1146 Amount: (-debitTotal).ToDUO(), // negative since it is a send
1147 Fee: &feeF64,
1148 }
1149 ret.Fee = feeF64
1150 }
1151 credCat := RecvCategory(details, syncBlock.Height, w.ChainParams()).String()
1152 for _, cred := range details.Credits {
1153 // Change is ignored.
1154 if cred.Change {
1155 continue
1156 }
1157 var address string
1158 var accountName string
1159 var addrs []btcaddr.Address
1160 _, addrs, _, e = txscript.ExtractPkScriptAddrs(
1161 details.MsgTx.TxOut[cred.Index].PkScript, w.ChainParams(),
1162 )
1163 if e == nil && len(addrs) == 1 {
1164 addr := addrs[0]
1165 address = addr.EncodeAddress()
1166 account, e := w.AccountOfAddress(addr)
1167 if e == nil {
1168 name, e := w.AccountName(waddrmgr.KeyScopeBIP0044, account)
1169 if e == nil {
1170 accountName = name
1171 }
1172 }
1173 }
1174 ret.Details = append(
1175 ret.Details, btcjson.GetTransactionDetailsResult{
1176 // Fields left zeroed:
1177 // InvolvesWatchOnly
1178 // Fee
1179 Account: accountName,
1180 Address: address,
1181 Category: credCat,
1182 Amount: cred.Amount.ToDUO(),
1183 Vout: cred.Index,
1184 },
1185 )
1186 }
1187 ret.Amount = creditTotal.ToDUO()
1188 return ret, nil
1189 }
1190 1191 func HandleDropWalletHistory(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (
1192 out interface{}, e error,
1193 ) {
1194 D.Ln("dropping wallet history")
1195 if e = DropWalletHistory(w, w.PodConfig); E.Chk(e) {
1196 }
1197 D.Ln("dropped wallet history")
1198 // go func() {
1199 // rwt, e := w.Database().BeginReadWriteTx()
1200 // if e != nil {
1201 // L.Script // }
1202 // ns := rwt.ReadWriteBucket([]byte("waddrmgr"))
1203 // w.Manager.SetSyncedTo(ns, nil)
1204 // if e = rwt.Commit(); E.Chk(e) {
1205 // }
1206 // }()
1207 defer interrupt.RequestRestart()
1208 return nil, e
1209 }
1210 1211 // These generators create the following global variables in this package:
1212 //
1213 // var localeHelpDescs map[string]func() map[string]string
1214 // var requestUsages string
1215 //
1216 // localeHelpDescs maps from locale strings (e.g. "en_US") to a function that builds a map of help texts for each RPC
1217 // server method. This prevents help text maps for every locale map from being rooted and created during init. Instead,
1218 // the appropiate function is looked up when help text is first needed using the current locale and saved to the global
1219 // below for futher reuse.
1220 //
1221 // requestUsages contains single line usages for every supported request, separated by newlines. It is set during init.
1222 // These usages are used for all locales.
1223 //
1224 1225 var HelpDescs map[string]string
1226 var HelpDescsMutex sync.Mutex // Help may execute concurrently, so synchronize access.
1227 1228 // HelpWithChainRPC handles the help request when the RPC server has been associated with a consensus RPC client. The
1229 // additional RPC client is used to include help messages for methods implemented by the consensus server via RPC
1230 // passthrough.
1231 func HelpWithChainRPC(
1232 icmd interface{}, w *Wallet,
1233 chainClient ...*chainclient.RPCClient,
1234 ) (interface{}, error) {
1235 return Help(icmd, w, chainClient[0])
1236 }
1237 1238 // HelpNoChainRPC handles the help request when the RPC server has not been associated with a consensus RPC client. No
1239 // help messages are included for passthrough requests.
1240 func HelpNoChainRPC(
1241 icmd interface{}, w *Wallet,
1242 chainClient ...*chainclient.RPCClient,
1243 ) (interface{}, error) {
1244 return Help(icmd, w, nil)
1245 }
1246 1247 // Help handles the Help request by returning one line usage of all available methods, or full Help for a specific
1248 // method. The chainClient is optional, and this is simply a helper function for the HelpNoChainRPC and HelpWithChainRPC
1249 // handlers.
1250 func Help(icmd interface{}, w *Wallet, chainClient *chainclient.RPCClient) (interface{}, error) {
1251 cmd := icmd.(*btcjson.HelpCmd)
1252 // pod returns different help messages depending on the kind of connection the client is using. Only methods
1253 // availble to HTTP POST clients are available to be used by wallet clients, even though wallet itself is a
1254 // websocket client to pod. Therefore, create a POST client as needed.
1255 //
1256 // Returns nil if chainClient is currently nil or there is an error creating the client.
1257 //
1258 // This is hacky and is probably better handled by exposing help usage texts in a non-internal pod package.
1259 postClient := func() *rpcclient.Client {
1260 if chainClient == nil {
1261 return nil
1262 }
1263 c, e := chainClient.POSTClient()
1264 if e != nil {
1265 return nil
1266 }
1267 return c
1268 }
1269 if cmd.Command == nil || *cmd.Command == "" {
1270 // Prepend chain server usage if it is available.
1271 usages := RequestUsages
1272 client := postClient()
1273 if client != nil {
1274 rawChainUsage, e := client.RawRequest("help", nil)
1275 var chainUsage string
1276 if e == nil {
1277 _ = js.Unmarshal(rawChainUsage, &chainUsage)
1278 }
1279 if chainUsage != "" {
1280 usages = "Chain server usage:\n\n" + chainUsage + "\n\n" +
1281 "Wallet server usage (overrides chain requests):\n\n" +
1282 RequestUsages
1283 }
1284 }
1285 return usages, nil
1286 }
1287 defer HelpDescsMutex.Unlock()
1288 HelpDescsMutex.Lock()
1289 if HelpDescs == nil {
1290 // TODO: Allow other locales to be set via config or determine this from environment variables. For now,
1291 // hardcode US English.
1292 HelpDescs = LocaleHelpDescs["en_US"]()
1293 }
1294 helpText, ok := HelpDescs[*cmd.Command]
1295 if ok {
1296 return helpText, nil
1297 }
1298 // Return the chain server's detailed help if possible.
1299 var chainHelp string
1300 client := postClient()
1301 if client != nil {
1302 param := make([]byte, len(*cmd.Command)+2)
1303 param[0] = '"'
1304 copy(param[1:], *cmd.Command)
1305 param[len(param)-1] = '"'
1306 rawChainHelp, e := client.RawRequest("help", []js.RawMessage{param})
1307 if e == nil {
1308 _ = js.Unmarshal(rawChainHelp, &chainHelp)
1309 }
1310 }
1311 if chainHelp != "" {
1312 return chainHelp, nil
1313 }
1314 return nil, &btcjson.RPCError{
1315 Code: btcjson.ErrRPCInvalidParameter,
1316 Message: fmt.Sprintf("No help for method '%s'", *cmd.Command),
1317 }
1318 }
1319 1320 // ListAccounts handles a listaccounts request by returning a map of account names to their balances.
1321 func ListAccounts(
1322 icmd interface{}, w *Wallet,
1323 chainClient ...*chainclient.RPCClient,
1324 ) (interface{}, error) {
1325 cmd, ok := icmd.(*btcjson.ListAccountsCmd)
1326 if !ok {
1327 return nil, &btcjson.RPCError{
1328 Code: btcjson.ErrRPCInvalidParameter,
1329 Message: HelpDescsEnUS()["listaccounts"],
1330 // "invalid subcommand for addnode",
1331 }
1332 }
1333 accountBalances := map[string]float64{}
1334 results, e := w.AccountBalances(waddrmgr.KeyScopeBIP0044, int32(*cmd.MinConf))
1335 if e != nil {
1336 return nil, e
1337 }
1338 for _, result := range results {
1339 accountBalances[result.AccountName] = result.AccountBalance.ToDUO()
1340 }
1341 // Return the map. This will be marshaled into a JSON object.
1342 return accountBalances, nil
1343 }
1344 1345 // ListLockUnspent handles a listlockunspent request by returning an slice of all locked outpoints.
1346 func ListLockUnspent(
1347 icmd interface{}, w *Wallet,
1348 chainClient ...*chainclient.RPCClient,
1349 ) (interface{}, error) {
1350 return w.LockedOutpoints(), nil
1351 }
1352 1353 // ListReceivedByAccount handles a listreceivedbyaccount request by returning a slice of objects, each one containing:
1354 //
1355 // "account": the receiving account;
1356 //
1357 // "amount": total amount received by the account;
1358 //
1359 // "confirmations": number of confirmations of the most recent transaction.
1360 //
1361 // It takes two parameters:
1362 //
1363 // "minconf": minimum number of confirmations to consider a transaction - default: one;
1364 //
1365 // "includeempty": whether or not to include addresses that have no transactions - default: false.
1366 func ListReceivedByAccount(
1367 icmd interface{}, w *Wallet,
1368 chainClient ...*chainclient.RPCClient,
1369 ) (interface{}, error) {
1370 cmd, ok := icmd.(*btcjson.ListReceivedByAccountCmd)
1371 if !ok {
1372 return nil, &btcjson.RPCError{
1373 Code: btcjson.ErrRPCInvalidParameter,
1374 Message: HelpDescsEnUS()["listreceivedbyaccount"],
1375 // "invalid subcommand for addnode",
1376 }
1377 }
1378 results, e := w.TotalReceivedForAccounts(
1379 waddrmgr.KeyScopeBIP0044, int32(*cmd.MinConf),
1380 )
1381 if e != nil {
1382 return nil, e
1383 }
1384 jsonResults := make([]btcjson.ListReceivedByAccountResult, 0, len(results))
1385 for _, result := range results {
1386 jsonResults = append(
1387 jsonResults, btcjson.ListReceivedByAccountResult{
1388 Account: result.AccountName,
1389 Amount: result.TotalReceived.ToDUO(),
1390 Confirmations: uint64(result.LastConfirmation),
1391 },
1392 )
1393 }
1394 return jsonResults, nil
1395 }
1396 1397 // ListReceivedByAddress handles a listreceivedbyaddress request by returning
1398 // a slice of objects, each one containing:
1399 //
1400 // "account": the account of the receiving address;
1401 //
1402 // "address": the receiving address;
1403 //
1404 // "amount": total amount received by the address;
1405 //
1406 // "confirmations": number of confirmations of the most recent transaction.
1407 //
1408 // It takes two parameters:
1409 //
1410 // "minconf": minimum number of confirmations to consider a transaction - default: one;
1411 //
1412 // "includeempty": whether or not to include addresses that have no transactions - default: false.
1413 func ListReceivedByAddress(
1414 icmd interface{}, w *Wallet,
1415 chainClient ...*chainclient.RPCClient,
1416 ) (interface{}, error) {
1417 cmd, ok := icmd.(*btcjson.ListReceivedByAddressCmd)
1418 if !ok {
1419 return nil, &btcjson.RPCError{
1420 Code: btcjson.ErrRPCInvalidParameter,
1421 Message: HelpDescsEnUS()["listreceivedbyaddress"],
1422 // "invalid subcommand for addnode",
1423 }
1424 }
1425 // Intermediate data for each address.
1426 type AddrData struct {
1427 // Total amount received.
1428 amount amt.Amount
1429 // Number of confirmations of the last transaction.
1430 confirmations int32
1431 // Merkles of transactions which include an output paying to the address
1432 tx []string
1433 // Account which the address belongs to account string
1434 }
1435 syncBlock := w.Manager.SyncedTo()
1436 // Intermediate data for all addresses.
1437 allAddrData := make(map[string]AddrData)
1438 // Create an AddrData entry for each active address in the account. Otherwise we'll just get addresses from
1439 // transactions later.
1440 sortedAddrs, e := w.SortedActivePaymentAddresses()
1441 if e != nil {
1442 return nil, e
1443 }
1444 for _, address := range sortedAddrs {
1445 // There might be duplicates, just overwrite them.
1446 allAddrData[address] = AddrData{}
1447 }
1448 minConf := *cmd.MinConf
1449 var endHeight int32
1450 if minConf == 0 {
1451 endHeight = -1
1452 } else {
1453 endHeight = syncBlock.Height - int32(minConf) + 1
1454 }
1455 e = ExposeUnstableAPI(w).RangeTransactions(
1456 0, endHeight, func(details []wtxmgr.TxDetails) (bool, error) {
1457 confirmations := Confirms(details[0].Block.Height, syncBlock.Height)
1458 for _, tx := range details {
1459 for _, cred := range tx.Credits {
1460 pkScript := tx.MsgTx.TxOut[cred.Index].PkScript
1461 var addrs []btcaddr.Address
1462 _, addrs, _, e = txscript.ExtractPkScriptAddrs(
1463 pkScript, w.ChainParams(),
1464 )
1465 if e != nil {
1466 // Non standard script, skip.
1467 continue
1468 }
1469 for _, addr := range addrs {
1470 addrStr := addr.EncodeAddress()
1471 addrData, ok := allAddrData[addrStr]
1472 if ok {
1473 addrData.amount += cred.Amount
1474 // Always overwrite confirmations with newer ones.
1475 addrData.confirmations = confirmations
1476 } else {
1477 addrData = AddrData{
1478 amount: cred.Amount,
1479 confirmations: confirmations,
1480 }
1481 }
1482 addrData.tx = append(addrData.tx, tx.Hash.String())
1483 allAddrData[addrStr] = addrData
1484 }
1485 }
1486 }
1487 return false, nil
1488 },
1489 )
1490 if e != nil {
1491 return nil, e
1492 }
1493 // Massage address data into output format.
1494 numAddresses := len(allAddrData)
1495 ret := make([]btcjson.ListReceivedByAddressResult, numAddresses)
1496 idx := 0
1497 for address, addrData := range allAddrData {
1498 ret[idx] = btcjson.ListReceivedByAddressResult{
1499 Address: address,
1500 Amount: addrData.amount.ToDUO(),
1501 Confirmations: uint64(addrData.confirmations),
1502 TxIDs: addrData.tx,
1503 }
1504 idx++
1505 }
1506 return ret, nil
1507 }
1508 1509 // ListSinceBlock handles a listsinceblock request by returning an array of maps with details of sent and received
1510 // wallet transactions since the given block.
1511 func ListSinceBlock(
1512 icmd interface{}, w *Wallet,
1513 cc ...*chainclient.RPCClient,
1514 ) (interface{}, error) {
1515 if len(cc) < 1 || cc[0] == nil {
1516 return nil, &btcjson.RPCError{
1517 Code: btcjson.ErrRPCNoChain,
1518 Message: "there is currently no chain client to get this response",
1519 }
1520 }
1521 chainClient := cc[0]
1522 cmd, ok := icmd.(*btcjson.ListSinceBlockCmd)
1523 if !ok {
1524 return nil, &btcjson.RPCError{
1525 Code: btcjson.ErrRPCInvalidParameter,
1526 Message: HelpDescsEnUS()["listsinceblock"],
1527 // "invalid subcommand for addnode",
1528 }
1529 }
1530 syncBlock := w.Manager.SyncedTo()
1531 targetConf := int64(*cmd.TargetConfirmations)
1532 // For the result we need the block hash for the last block counted in the blockchain due to confirmations. We send
1533 // this off now so that it can arrive asynchronously while we figure out the rest.
1534 gbh := chainClient.GetBlockHashAsync(int64(syncBlock.Height) + 1 - targetConf)
1535 var start int32
1536 if cmd.BlockHash != nil {
1537 hash, e := chainhash.NewHashFromStr(*cmd.BlockHash)
1538 if e != nil {
1539 return nil, DeserializationError{e}
1540 }
1541 block, e := chainClient.GetBlockVerboseTx(hash)
1542 if e != nil {
1543 return nil, e
1544 }
1545 start = int32(block.Height) + 1
1546 }
1547 txInfoList, e := w.ListSinceBlock(start, -1, syncBlock.Height)
1548 if e != nil {
1549 return nil, e
1550 }
1551 // Done with work, get the response.
1552 blockHash, e := gbh.Receive()
1553 if e != nil {
1554 return nil, e
1555 }
1556 res := btcjson.ListSinceBlockResult{
1557 Transactions: txInfoList,
1558 LastBlock: blockHash.String(),
1559 }
1560 return res, nil
1561 }
1562 1563 // ListTransactions handles a listtransactions request by returning an array of maps with details of sent and recevied
1564 // wallet transactions.
1565 func ListTransactions(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (
1566 txs interface{},
1567 e error,
1568 ) {
1569 // D.S(icmd)
1570 // D.Ln("ListTransactions")
1571 if len(chainClient) < 1 || chainClient[0] == nil {
1572 return nil, &btcjson.RPCError{
1573 Code: btcjson.ErrRPCNoChain,
1574 Message: "there is currently no chain client to get this response",
1575 }
1576 }
1577 cmd, ok := icmd.(*btcjson.ListTransactionsCmd)
1578 if !ok { // || cmd.From == nil || cmd.Count == nil || cmd.Account != nil {
1579 E.Ln(
1580 "invalid parameter ok",
1581 !ok,
1582 "from",
1583 cmd.From == nil,
1584 "count",
1585 cmd.Count == nil,
1586 "account",
1587 cmd.Account != nil,
1588 )
1589 return nil, &btcjson.RPCError{
1590 Code: btcjson.ErrRPCInvalidParameter,
1591 Message: HelpDescsEnUS()["listtransactions"],
1592 }
1593 }
1594 // // TODO: ListTransactions does not currently understand the difference
1595 // // between transactions pertaining to one account from another. This
1596 // // will be resolved when wtxmgr is combined with the waddrmgr namespace.
1597 // if *cmd.Account != "*" {
1598 // // For now, don't bother trying to continue if the user specified an account, since this can't be (easily or
1599 // // efficiently) calculated.
1600 // E.Ln("you must use * for account, as transactions are not yet grouped by account")
1601 // return nil, &btcjson.RPCError{
1602 // Code: btcjson.ErrRPCWallet,
1603 // Message: "Transactions are not yet grouped by account",
1604 // }
1605 // }
1606 txs, e = w.ListTransactions(*cmd.From, *cmd.Count)
1607 return txs, e
1608 }
1609 1610 // ListAddressTransactions handles a listaddresstransactions request by returning an array of maps with details of spent
1611 // and received wallet transactions.
1612 //
1613 // The form of the reply is identical to listtransactions, but the array elements are limited to transaction details
1614 // which are about the addresess included in the request.
1615 func ListAddressTransactions(
1616 icmd interface{}, w *Wallet,
1617 chainClient ...*chainclient.RPCClient,
1618 ) (interface{}, error) {
1619 cmd, ok := icmd.(*btcjson.ListAddressTransactionsCmd)
1620 if !ok {
1621 return nil, &btcjson.RPCError{
1622 Code: btcjson.ErrRPCInvalidParameter,
1623 Message: HelpDescsEnUS()["listaddresstransactions"],
1624 // "invalid subcommand for addnode",
1625 }
1626 }
1627 if cmd.Account != nil && *cmd.Account != "*" {
1628 return nil, &btcjson.RPCError{
1629 Code: btcjson.ErrRPCInvalidParameter,
1630 Message: "Listing transactions for addresses may only be done for all accounts",
1631 }
1632 }
1633 // Decode addresses.
1634 hash160Map := make(map[string]struct{})
1635 for _, addrStr := range cmd.Addresses {
1636 addr, e := DecodeAddress(addrStr, w.ChainParams())
1637 if e != nil {
1638 return nil, e
1639 }
1640 hash160Map[string(addr.ScriptAddress())] = struct{}{}
1641 }
1642 return w.ListAddressTransactions(hash160Map)
1643 }
1644 1645 // ListAllTransactions handles a listalltransactions request by returning a map with details of sent and received wallet
1646 // transactions. This is similar to ListTransactions, except it takes only a single optional argument for the account
1647 // name and replies with all transactions.
1648 func ListAllTransactions(
1649 icmd interface{}, w *Wallet,
1650 chainClient ...*chainclient.RPCClient,
1651 ) (interface{}, error) {
1652 cmd, ok := icmd.(*btcjson.ListAllTransactionsCmd)
1653 if !ok {
1654 return nil, &btcjson.RPCError{
1655 Code: btcjson.ErrRPCInvalidParameter,
1656 Message: HelpDescsEnUS()["listalltransactions"],
1657 // "invalid subcommand for addnode",
1658 }
1659 }
1660 if cmd.Account != nil && *cmd.Account != "*" {
1661 return nil, &btcjson.RPCError{
1662 Code: btcjson.ErrRPCInvalidParameter,
1663 Message: "Listing all transactions may only be done for all accounts",
1664 }
1665 }
1666 return w.ListAllTransactions()
1667 }
1668 1669 // ListUnspent handles the listunspent command.
1670 func ListUnspent(
1671 icmd interface{}, w *Wallet,
1672 chainClient ...*chainclient.RPCClient,
1673 ) (interface{}, error) {
1674 cmd, ok := icmd.(*btcjson.ListUnspentCmd)
1675 if !ok {
1676 return nil, &btcjson.RPCError{
1677 Code: btcjson.ErrRPCInvalidParameter,
1678 Message: HelpDescsEnUS()["listunspent"],
1679 // "invalid subcommand for addnode",
1680 }
1681 }
1682 var addresses map[string]struct{}
1683 if cmd.Addresses != nil {
1684 addresses = make(map[string]struct{})
1685 // confirm that all of them are good:
1686 for _, as := range *cmd.Addresses {
1687 a, e := DecodeAddress(as, w.ChainParams())
1688 if e != nil {
1689 return nil, e
1690 }
1691 addresses[a.EncodeAddress()] = struct{}{}
1692 }
1693 }
1694 return w.ListUnspent(int32(*cmd.MinConf), int32(*cmd.MaxConf), addresses)
1695 }
1696 1697 // LockUnspent handles the lockunspent command.
1698 func LockUnspent(
1699 icmd interface{}, w *Wallet,
1700 chainClient ...*chainclient.RPCClient,
1701 ) (interface{}, error) {
1702 cmd, ok := icmd.(*btcjson.LockUnspentCmd)
1703 if !ok {
1704 return nil, &btcjson.RPCError{
1705 Code: btcjson.ErrRPCInvalidParameter,
1706 Message: HelpDescsEnUS()["lockunspent"],
1707 // "invalid subcommand for addnode",
1708 }
1709 }
1710 switch {
1711 case cmd.Unlock && len(cmd.Transactions) == 0:
1712 w.ResetLockedOutpoints()
1713 default:
1714 for _, input := range cmd.Transactions {
1715 txHash, e := chainhash.NewHashFromStr(input.Txid)
1716 if e != nil {
1717 return nil, ParseError{e}
1718 }
1719 op := wire.OutPoint{Hash: *txHash, Index: input.Vout}
1720 if cmd.Unlock {
1721 w.UnlockOutpoint(op)
1722 } else {
1723 w.LockOutpoint(op)
1724 }
1725 }
1726 }
1727 return true, nil
1728 }
1729 1730 // MakeOutputs creates a slice of transaction outputs from a pair of address strings to amounts. This is used to create
1731 // the outputs to include in newly created transactions from a JSON object describing the output destinations and
1732 // amounts.
1733 func MakeOutputs(pairs map[string]amt.Amount, chainParams *chaincfg.Params) ([]*wire.TxOut, error) {
1734 outputs := make([]*wire.TxOut, 0, len(pairs))
1735 for addrStr, amt := range pairs {
1736 addr, e := btcaddr.Decode(addrStr, chainParams)
1737 if e != nil {
1738 return nil, fmt.Errorf("cannot decode address: %s", e)
1739 }
1740 pkScript, e := txscript.PayToAddrScript(addr)
1741 if e != nil {
1742 return nil, fmt.Errorf("cannot create txout script: %s", e)
1743 }
1744 outputs = append(outputs, wire.NewTxOut(int64(amt), pkScript))
1745 }
1746 return outputs, nil
1747 }
1748 1749 // SendPairs creates and sends payment transactions. It returns the transaction hash in string format upon success All
1750 // errors are returned in json.RPCError format
1751 func SendPairs(
1752 w *Wallet, amounts map[string]amt.Amount,
1753 account uint32, minconf int32, feeSatPerKb amt.Amount,
1754 ) (string, error) {
1755 outputs, e := MakeOutputs(amounts, w.ChainParams())
1756 if e != nil {
1757 return "", e
1758 }
1759 var txHash *chainhash.Hash
1760 txHash, e = w.SendOutputs(outputs, account, minconf, feeSatPerKb)
1761 if e != nil {
1762 if e == txrules.ErrAmountNegative {
1763 return "", ErrNeedPositiveAmount
1764 }
1765 if waddrmgr.IsError(e, waddrmgr.ErrLocked) {
1766 return "", &ErrWalletUnlockNeeded
1767 }
1768 switch e.(type) {
1769 case btcjson.RPCError:
1770 return "", e
1771 }
1772 return "", &btcjson.RPCError{
1773 Code: btcjson.ErrRPCInternal.Code,
1774 Message: e.Error(),
1775 }
1776 }
1777 txHashStr := txHash.String()
1778 I.Ln("successfully sent transaction", txHashStr)
1779 return txHashStr, nil
1780 }
1781 func IsNilOrEmpty(s *string) bool {
1782 return s == nil || *s == ""
1783 }
1784 1785 // SendFrom handles a sendfrom RPC request by creating a new transaction spending unspent transaction outputs for a
1786 // wallet to another payment address. Leftover inputs not sent to the payment address or a fee for the miner are sent
1787 // back to a new address in the wallet. Upon success, the TxID for the created transaction is returned.
1788 func SendFrom(icmd interface{}, w *Wallet, chainClient *chainclient.RPCClient) (interface{}, error) {
1789 cmd, ok := icmd.(*btcjson.SendFromCmd)
1790 if !ok {
1791 return nil, &btcjson.RPCError{
1792 Code: btcjson.ErrRPCInvalidParameter,
1793 Message: HelpDescsEnUS()["sendfrom"],
1794 // "invalid subcommand for addnode",
1795 }
1796 }
1797 // Transaction comments are not yet supported. ScriptError instead of pretending to save them.
1798 if !IsNilOrEmpty(cmd.Comment) || !IsNilOrEmpty(cmd.CommentTo) {
1799 return nil, &btcjson.RPCError{
1800 Code: btcjson.ErrRPCUnimplemented,
1801 Message: "Transaction comments are not yet supported",
1802 }
1803 }
1804 account, e := w.AccountNumber(
1805 waddrmgr.KeyScopeBIP0044, cmd.FromAccount,
1806 )
1807 if e != nil {
1808 return nil, e
1809 }
1810 // Chk that signed integer parameters are positive.
1811 if cmd.Amount < 0 {
1812 return nil, ErrNeedPositiveAmount
1813 }
1814 minConf := int32(*cmd.MinConf)
1815 if minConf < 0 {
1816 return nil, ErrNeedPositiveMinconf
1817 }
1818 // Create map of address and amount pairs.
1819 amount, e := amt.NewAmount(cmd.Amount)
1820 if e != nil {
1821 return nil, e
1822 }
1823 pairs := map[string]amt.Amount{
1824 cmd.ToAddress: amount,
1825 }
1826 return SendPairs(
1827 w, pairs, account, minConf,
1828 txrules.DefaultRelayFeePerKb,
1829 )
1830 }
1831 1832 // SendMany handles a sendmany RPC request by creating a new transaction spending unspent transaction outputs for a
1833 // wallet to any number of payment addresses.
1834 //
1835 // Leftover inputs not sent to the payment address or a fee for the miner are sent back to a new address in the wallet.
1836 // Upon success, the TxID for the created transaction is returned.
1837 func SendMany(
1838 icmd interface{}, w *Wallet,
1839 chainClient ...*chainclient.RPCClient,
1840 ) (interface{}, error) {
1841 cmd, ok := icmd.(*btcjson.SendManyCmd)
1842 if !ok {
1843 return nil, &btcjson.RPCError{
1844 Code: btcjson.ErrRPCInvalidParameter,
1845 Message: HelpDescsEnUS()["sendmany"],
1846 // "invalid subcommand for addnode",
1847 }
1848 }
1849 // Transaction comments are not yet supported. ScriptError instead of pretending to save them.
1850 if !IsNilOrEmpty(cmd.Comment) {
1851 return nil, &btcjson.RPCError{
1852 Code: btcjson.ErrRPCUnimplemented,
1853 Message: "Transaction comments are not yet supported",
1854 }
1855 }
1856 account, e := w.AccountNumber(waddrmgr.KeyScopeBIP0044, cmd.FromAccount)
1857 if e != nil {
1858 return nil, e
1859 }
1860 // Chk that minconf is positive.
1861 minConf := int32(*cmd.MinConf)
1862 if minConf < 0 {
1863 return nil, ErrNeedPositiveMinconf
1864 }
1865 // Recreate address/amount pairs, using dcrutil.Amount.
1866 pairs := make(map[string]amt.Amount, len(cmd.Amounts))
1867 for k, v := range cmd.Amounts {
1868 amt, e := amt.NewAmount(v)
1869 if e != nil {
1870 return nil, e
1871 }
1872 pairs[k] = amt
1873 }
1874 return SendPairs(w, pairs, account, minConf, txrules.DefaultRelayFeePerKb)
1875 }
1876 1877 // SendToAddress handles a sendtoaddress RPC request by creating a new transaction spending unspent transaction outputs
1878 // for a wallet to another payment address.
1879 //
1880 // Leftover inputs not sent to the payment address or a fee for the miner are sent back to a new address in the wallet.
1881 // Upon success, the TxID for the created transaction is returned.
1882 func SendToAddress(
1883 icmd interface{}, w *Wallet,
1884 chainClient ...*chainclient.RPCClient,
1885 ) (interface{}, error) {
1886 cmd, ok := icmd.(*btcjson.SendToAddressCmd)
1887 if !ok {
1888 return nil, &btcjson.RPCError{
1889 Code: btcjson.ErrRPCInvalidParameter,
1890 Message: HelpDescsEnUS()["sendtoaddress"],
1891 // "invalid subcommand for addnode",
1892 }
1893 }
1894 // Transaction comments are not yet supported. ScriptError instead of
1895 // pretending to save them.
1896 if !IsNilOrEmpty(cmd.Comment) || !IsNilOrEmpty(cmd.CommentTo) {
1897 return nil, &btcjson.RPCError{
1898 Code: btcjson.ErrRPCUnimplemented,
1899 Message: "Transaction comments are not yet supported",
1900 }
1901 }
1902 amount, e := amt.NewAmount(cmd.Amount)
1903 if e != nil {
1904 D.Ln(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", e)
1905 return nil, e
1906 }
1907 // Chk that signed integer parameters are positive.
1908 if amount < 0 {
1909 D.Ln(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> need positive amount")
1910 return nil, ErrNeedPositiveAmount
1911 }
1912 // Mock up map of address and amount pairs.
1913 pairs := map[string]amt.Amount{
1914 cmd.Address: amount,
1915 }
1916 // sendtoaddress always spends from the default account, this matches bitcoind
1917 return SendPairs(
1918 w, pairs, waddrmgr.DefaultAccountNum, 1,
1919 txrules.DefaultRelayFeePerKb,
1920 )
1921 }
1922 1923 // SetTxFee sets the transaction fee per kilobyte added to transactions.
1924 func SetTxFee(
1925 icmd interface{}, w *Wallet,
1926 chainClient ...*chainclient.RPCClient,
1927 ) (interface{}, error) {
1928 cmd, ok := icmd.(*btcjson.SetTxFeeCmd)
1929 if !ok {
1930 return nil, &btcjson.RPCError{
1931 Code: btcjson.ErrRPCInvalidParameter,
1932 Message: HelpDescsEnUS()["settxfee"],
1933 // "invalid subcommand for addnode",
1934 }
1935 }
1936 // Chk that amount is not negative.
1937 if cmd.Amount < 0 {
1938 return nil, ErrNeedPositiveAmount
1939 }
1940 // A boolean true result is returned upon success.
1941 return true, nil
1942 }
1943 1944 // SignMessage signs the given message with the private key for the given address
1945 func SignMessage(
1946 icmd interface{}, w *Wallet,
1947 chainClient ...*chainclient.RPCClient,
1948 ) (interface{}, error) {
1949 cmd, ok := icmd.(*btcjson.SignMessageCmd)
1950 if !ok {
1951 return nil, &btcjson.RPCError{
1952 Code: btcjson.ErrRPCInvalidParameter,
1953 Message: HelpDescsEnUS()["signmessage"],
1954 // "invalid subcommand for addnode",
1955 }
1956 }
1957 addr, e := DecodeAddress(cmd.Address, w.ChainParams())
1958 if e != nil {
1959 return nil, e
1960 }
1961 privKey, e := w.PrivKeyForAddress(addr)
1962 if e != nil {
1963 return nil, e
1964 }
1965 var buf bytes.Buffer
1966 e = wire.WriteVarString(&buf, 0, "Bitcoin Signed Message:\n")
1967 if e != nil {
1968 D.Ln(e)
1969 }
1970 e = wire.WriteVarString(&buf, 0, cmd.Message)
1971 if e != nil {
1972 D.Ln(e)
1973 }
1974 messageHash := chainhash.DoubleHashB(buf.Bytes())
1975 sigbytes, e := ecc.SignCompact(
1976 ecc.S256(), privKey,
1977 messageHash, true,
1978 )
1979 if e != nil {
1980 return nil, e
1981 }
1982 return base64.StdEncoding.EncodeToString(sigbytes), nil
1983 }
1984 1985 // SignRawTransaction handles the signrawtransaction command.
1986 func SignRawTransaction(
1987 icmd interface{}, w *Wallet,
1988 cc ...*chainclient.RPCClient,
1989 ) (interface{}, error) {
1990 if len(cc) < 1 || cc[0] == nil {
1991 return nil, &btcjson.RPCError{
1992 Code: btcjson.ErrRPCNoChain,
1993 Message: "there is currently no chain client to get this response",
1994 }
1995 }
1996 chainClient := cc[0]
1997 cmd, ok := icmd.(*btcjson.SignRawTransactionCmd)
1998 if !ok {
1999 return nil, &btcjson.RPCError{
2000 Code: btcjson.ErrRPCInvalidParameter,
2001 Message: HelpDescsEnUS()["signrawtransaction"],
2002 // "invalid subcommand for addnode",
2003 }
2004 }
2005 serializedTx, e := DecodeHexStr(cmd.RawTx)
2006 if e != nil {
2007 return nil, e
2008 }
2009 var tx wire.MsgTx
2010 e = tx.Deserialize(bytes.NewBuffer(serializedTx))
2011 if e != nil {
2012 e = errors.New("TX decode failed")
2013 return nil, DeserializationError{e}
2014 }
2015 var hashType txscript.SigHashType
2016 switch *cmd.Flags {
2017 case "ALL":
2018 hashType = txscript.SigHashAll
2019 case "NONE":
2020 hashType = txscript.SigHashNone
2021 case "SINGLE":
2022 hashType = txscript.SigHashSingle
2023 case "ALL|ANYONECANPAY":
2024 hashType = txscript.SigHashAll | txscript.SigHashAnyOneCanPay
2025 case "NONE|ANYONECANPAY":
2026 hashType = txscript.SigHashNone | txscript.SigHashAnyOneCanPay
2027 case "SINGLE|ANYONECANPAY":
2028 hashType = txscript.SigHashSingle | txscript.SigHashAnyOneCanPay
2029 default:
2030 e = errors.New("invalid sighash parameter")
2031 return nil, InvalidParameterError{e}
2032 }
2033 // TODO: really we probably should look these up with pod anyway to
2034 // make sure that they match the blockchain if present.
2035 inputs := make(map[wire.OutPoint][]byte)
2036 scripts := make(map[string][]byte)
2037 var cmdInputs []btcjson.RawTxInput
2038 if cmd.Inputs != nil {
2039 cmdInputs = *cmd.Inputs
2040 }
2041 for _, rti := range cmdInputs {
2042 var inputHash *chainhash.Hash
2043 inputHash, e = chainhash.NewHashFromStr(rti.Txid)
2044 if e != nil {
2045 return nil, DeserializationError{e}
2046 }
2047 var script []byte
2048 script, e = DecodeHexStr(rti.ScriptPubKey)
2049 if e != nil {
2050 return nil, e
2051 }
2052 // redeemScript is only actually used iff the user provided private keys. In which case, it is used to get the
2053 // scripts for signing. If the user did not provide keys then we always get scripts from the wallet.
2054 //
2055 // Empty strings are ok for this one and hex.DecodeString will DTRT.
2056 if cmd.PrivKeys != nil && len(*cmd.PrivKeys) != 0 {
2057 var redeemScript []byte
2058 redeemScript, e = DecodeHexStr(rti.RedeemScript)
2059 if e != nil {
2060 return nil, e
2061 }
2062 var addr *btcaddr.ScriptHash
2063 addr, e = btcaddr.NewScriptHash(
2064 redeemScript,
2065 w.ChainParams(),
2066 )
2067 if e != nil {
2068 return nil, DeserializationError{e}
2069 }
2070 scripts[addr.String()] = redeemScript
2071 }
2072 inputs[wire.OutPoint{
2073 Hash: *inputHash,
2074 Index: rti.Vout,
2075 }] = script
2076 }
2077 // Now we go and look for any inputs that we were not provided by querying pod with getrawtransaction. We queue up a
2078 // bunch of async requests and will wait for replies after we have checked the rest of the arguments.
2079 requested := make(map[wire.OutPoint]rpcclient.FutureGetTxOutResult)
2080 for _, txIn := range tx.TxIn {
2081 // Did we get this outpoint from the arguments?
2082 if _, ok := inputs[txIn.PreviousOutPoint]; ok {
2083 continue
2084 }
2085 // Asynchronously request the output script.
2086 requested[txIn.PreviousOutPoint] = chainClient.GetTxOutAsync(
2087 &txIn.PreviousOutPoint.Hash, txIn.PreviousOutPoint.Index,
2088 true,
2089 )
2090 }
2091 // Parse list of private keys, if present. If there are any keys here they are the keys that we may use for signing.
2092 // If empty we will use any keys known to us already.
2093 var keys map[string]*util.WIF
2094 if cmd.PrivKeys != nil {
2095 keys = make(map[string]*util.WIF)
2096 for _, key := range *cmd.PrivKeys {
2097 var wif *util.WIF
2098 wif, e = util.DecodeWIF(key)
2099 if e != nil {
2100 return nil, DeserializationError{e}
2101 }
2102 if !wif.IsForNet(w.ChainParams()) {
2103 s := "key network doesn't match wallet's"
2104 return nil, DeserializationError{errors.New(s)}
2105 }
2106 var addr *btcaddr.PubKey
2107 addr, e = btcaddr.NewPubKey(
2108 wif.SerializePubKey(),
2109 w.ChainParams(),
2110 )
2111 if e != nil {
2112 return nil, DeserializationError{e}
2113 }
2114 keys[addr.EncodeAddress()] = wif
2115 }
2116 }
2117 // We have checked the rest of the args. now we can collect the async txs.
2118 //
2119 // TODO: If we don't mind the possibility of wasting work we could move waiting to the following loop and be
2120 // slightly more asynchronous.
2121 for outPoint, resp := range requested {
2122 var result *btcjson.GetTxOutResult
2123 result, e = resp.Receive()
2124 if e != nil {
2125 return nil, e
2126 }
2127 var script []byte
2128 if script, e = hex.DecodeString(result.ScriptPubKey.Hex); E.Chk(e) {
2129 return nil, e
2130 }
2131 inputs[outPoint] = script
2132 }
2133 // All args collected. Now we can sign all the inputs that we can. `complete' denotes that we successfully signed
2134 // all outputs and that all scripts will run to completion. This is returned as part of the reply.
2135 var signErrs []SignatureError
2136 signErrs, e = w.SignTransaction(&tx, hashType, inputs, keys, scripts)
2137 if e != nil {
2138 return nil, e
2139 }
2140 var buf bytes.Buffer
2141 buf.Grow(tx.SerializeSize())
2142 // All returned errors (not OOM, which panics) encountered during bytes.Buffer writes are unexpected.
2143 if e = tx.Serialize(&buf); E.Chk(e) {
2144 panic(e)
2145 }
2146 signErrors := make([]btcjson.SignRawTransactionError, 0, len(signErrs))
2147 for _, ee := range signErrs {
2148 input := tx.TxIn[ee.InputIndex]
2149 signErrors = append(
2150 signErrors, btcjson.SignRawTransactionError{
2151 TxID: input.PreviousOutPoint.Hash.String(),
2152 Vout: input.PreviousOutPoint.Index,
2153 ScriptSig: hex.EncodeToString(input.SignatureScript),
2154 Sequence: input.Sequence,
2155 Error: e.Error(),
2156 },
2157 )
2158 }
2159 return btcjson.SignRawTransactionResult{
2160 Hex: hex.EncodeToString(buf.Bytes()),
2161 Complete: len(signErrors) == 0,
2162 Errors: signErrors,
2163 }, nil
2164 }
2165 2166 // ValidateAddress handles the validateaddress command.
2167 func ValidateAddress(icmd interface{}, w *Wallet, chainClient ...*chainclient.RPCClient) (interface{}, error) {
2168 cmd, ok := icmd.(*btcjson.ValidateAddressCmd)
2169 if !ok {
2170 return nil, &btcjson.RPCError{
2171 Code: btcjson.ErrRPCInvalidParameter,
2172 Message: HelpDescsEnUS()["validateaddress"],
2173 // "invalid subcommand for addnode",
2174 }
2175 }
2176 result := btcjson.ValidateAddressWalletResult{}
2177 addr, e := DecodeAddress(cmd.Address, w.ChainParams())
2178 if e != nil {
2179 // Use result zero value (IsValid=false).
2180 return result, nil
2181 }
2182 // We could put whether or not the address is a script here, by checking the type of "addr", however, the reference
2183 // implementation only puts that information if the script is "ismine", and we follow that behaviour.
2184 result.Address = addr.EncodeAddress()
2185 result.IsValid = true
2186 ainfo, e := w.AddressInfo(addr)
2187 if e != nil {
2188 if waddrmgr.IsError(e, waddrmgr.ErrAddressNotFound) {
2189 // No additional information available about the address.
2190 return result, nil
2191 }
2192 return nil, e
2193 }
2194 // The address lookup was successful which means there is further information about it available and it is "mine".
2195 result.IsMine = true
2196 acctName, e := w.AccountName(waddrmgr.KeyScopeBIP0044, ainfo.Account())
2197 if e != nil {
2198 return nil, &ErrAccountNameNotFound
2199 }
2200 result.Account = acctName
2201 switch ma := ainfo.(type) {
2202 case waddrmgr.ManagedPubKeyAddress:
2203 result.IsCompressed = ma.Compressed()
2204 result.PubKey = ma.ExportPubKey()
2205 case waddrmgr.ManagedScriptAddress:
2206 result.IsScript = true
2207 // The script is only available if the manager is unlocked, so just break out now if there is an error.
2208 script, e := ma.Script()
2209 if e != nil {
2210 break
2211 }
2212 result.Hex = hex.EncodeToString(script)
2213 // This typically shouldn't fail unless an invalid script was imported.
2214 //
2215 // However, if it fails for any reason, there is no further information available, so just set the script type a
2216 // non-standard and break out now.
2217 class, addrs, reqSigs, e := txscript.ExtractPkScriptAddrs(
2218 script, w.ChainParams(),
2219 )
2220 if e != nil {
2221 result.Script = txscript.NonStandardTy.String()
2222 break
2223 }
2224 addrStrings := make([]string, len(addrs))
2225 for i, a := range addrs {
2226 addrStrings[i] = a.EncodeAddress()
2227 }
2228 result.Addresses = addrStrings
2229 // Multi-signature scripts also provide the number of required
2230 // signatures.
2231 result.Script = class.String()
2232 if class == txscript.MultiSigTy {
2233 result.SigsRequired = int32(reqSigs)
2234 }
2235 }
2236 return result, nil
2237 }
2238 2239 // VerifyMessage handles the verifymessage command by verifying the provided compact signature for the given address and
2240 // message.
2241 func VerifyMessage(
2242 icmd interface{}, w *Wallet,
2243 chainClient ...*chainclient.RPCClient,
2244 ) (interface{}, error) {
2245 cmd, ok := icmd.(*btcjson.VerifyMessageCmd)
2246 if !ok {
2247 return nil, &btcjson.RPCError{
2248 Code: btcjson.ErrRPCInvalidParameter,
2249 Message: HelpDescsEnUS()["verifymessage"],
2250 // "invalid subcommand for addnode",
2251 }
2252 }
2253 addr, e := DecodeAddress(cmd.Address, w.ChainParams())
2254 if e != nil {
2255 return nil, e
2256 }
2257 // decode base64 signature
2258 sig, e := base64.StdEncoding.DecodeString(cmd.Signature)
2259 if e != nil {
2260 return nil, e
2261 }
2262 // Validate the signature - this just shows that it was valid at all. we will compare it with the key next.
2263 var buf bytes.Buffer
2264 e = wire.WriteVarString(&buf, 0, "Parallelcoin Signed Message:\n")
2265 if e != nil {
2266 D.Ln(e)
2267 }
2268 e = wire.WriteVarString(&buf, 0, cmd.Message)
2269 if e != nil {
2270 D.Ln(e)
2271 }
2272 expectedMessageHash := chainhash.DoubleHashB(buf.Bytes())
2273 pk, wasCompressed, e := ecc.RecoverCompact(
2274 ecc.S256(), sig,
2275 expectedMessageHash,
2276 )
2277 if e != nil {
2278 return nil, e
2279 }
2280 var serializedPubKey []byte
2281 if wasCompressed {
2282 serializedPubKey = pk.SerializeCompressed()
2283 } else {
2284 serializedPubKey = pk.SerializeUncompressed()
2285 }
2286 // Verify that the signed-by address matches the given address
2287 switch checkAddr := addr.(type) {
2288 case *btcaddr.PubKeyHash: // ok
2289 return bytes.Equal(btcaddr.Hash160(serializedPubKey), checkAddr.Hash160()[:]), nil
2290 case *btcaddr.PubKey: // ok
2291 return string(serializedPubKey) == checkAddr.String(), nil
2292 default:
2293 return nil, errors.New("address type not supported")
2294 }
2295 }
2296 2297 // WalletIsLocked handles the walletislocked extension request by returning the current lock state (false for unlocked,
2298 // true for locked) of an account.
2299 func WalletIsLocked(
2300 icmd interface{}, w *Wallet,
2301 chainClient ...*chainclient.RPCClient,
2302 ) (interface{}, error) {
2303 return w.Locked(), nil
2304 }
2305 2306 // WalletLock handles a walletlock request by locking the all account wallets, returning an error if any wallet is not
2307 // encrypted (for example, a watching-only wallet).
2308 func WalletLock(
2309 icmd interface{}, w *Wallet,
2310 chainClient ...*chainclient.RPCClient,
2311 ) (interface{}, error) {
2312 w.Lock()
2313 return nil, nil
2314 }
2315 2316 // WalletPassphrase responds to the walletpassphrase request by unlocking the wallet. The decryption key is saved in the
2317 // wallet until timeout seconds expires, after which the wallet is locked.
2318 func WalletPassphrase(
2319 icmd interface{}, w *Wallet,
2320 chainClient ...*chainclient.RPCClient,
2321 ) (interface{}, error) {
2322 cmd, ok := icmd.(*btcjson.WalletPassphraseCmd)
2323 if !ok {
2324 return nil, &btcjson.RPCError{
2325 Code: btcjson.ErrRPCInvalidParameter,
2326 Message: HelpDescsEnUS()["walletpassphrase"],
2327 // "invalid subcommand for addnode",
2328 }
2329 }
2330 timeout := time.Second * time.Duration(cmd.Timeout)
2331 var unlockAfter <-chan time.Time
2332 if timeout != 0 {
2333 unlockAfter = time.After(timeout)
2334 }
2335 e := w.Unlock([]byte(cmd.Passphrase), unlockAfter)
2336 return nil, e
2337 }
2338 2339 // WalletPassphraseChange responds to the walletpassphrasechange request by unlocking all accounts with the provided old
2340 // passphrase, and re-encrypting each private key with an AES key derived from the new passphrase.
2341 //
2342 // If the old passphrase is correct and the passphrase is changed, all wallets will be immediately locked.
2343 func WalletPassphraseChange(
2344 icmd interface{}, w *Wallet,
2345 chainClient ...*chainclient.RPCClient,
2346 ) (interface{}, error) {
2347 cmd, ok := icmd.(*btcjson.WalletPassphraseChangeCmd)
2348 if !ok {
2349 return nil, &btcjson.RPCError{
2350 Code: btcjson.ErrRPCInvalidParameter,
2351 Message: HelpDescsEnUS()["walletpassphrasechange"],
2352 // "invalid subcommand for addnode",
2353 }
2354 }
2355 e := w.ChangePrivatePassphrase(
2356 []byte(cmd.OldPassphrase),
2357 []byte(cmd.NewPassphrase),
2358 )
2359 if waddrmgr.IsError(e, waddrmgr.ErrWrongPassphrase) {
2360 return nil, &btcjson.RPCError{
2361 Code: btcjson.ErrRPCWalletPassphraseIncorrect,
2362 Message: "Incorrect passphrase",
2363 }
2364 }
2365 return nil, e
2366 }
2367 2368 // DecodeHexStr decodes the hex encoding of a string, possibly prepending a leading '0' character if there is an odd
2369 // number of bytes in the hex string. This is to prevent an error for an invalid hex string when using an odd number of
2370 // bytes when calling hex.Decode.
2371 func DecodeHexStr(hexStr string) ([]byte, error) {
2372 if len(hexStr)%2 != 0 {
2373 hexStr = "0" + hexStr
2374 }
2375 decoded, e := hex.DecodeString(hexStr)
2376 if e != nil {
2377 return nil, &btcjson.RPCError{
2378 Code: btcjson.ErrRPCDecodeHexString,
2379 Message: "Hex string decode failed: " + e.Error(),
2380 }
2381 }
2382 return decoded, nil
2383 }
2384