Compare commits

...

42 commits

Author SHA1 Message Date
Roy Lee
04763e36f0 doc: update README.md 2022-10-31 21:21:45 -07:00
Roy Lee
ca56a420ee rpc: implement rescanblockchain 2022-10-31 19:53:16 -07:00
Roy Lee
67c9c48940 wallet: break recovery() to recovery() and rescanblockchain()
Now the recovery, which runs at startup, only scans for known
addresses that were generated and recorded by this wallet.

The coming rescanblockchain RPC implementation, which requires the
wallet to be unlocked, does account discovery.
2022-10-31 19:53:16 -07:00
Roy Lee
c6842ef6fb go mod: update lbcd to v0.22.118 2022-10-31 19:53:16 -07:00
Roy Lee
1fa143fa0e wallet: update passphrase user experience.
For users don't want to set/manage a passphrase a default passphrase
"passphrase" will be used during wallet creation.

At startup, the wallet tries to unlock itself using the default
passphrase, or a user provided one (using -p).

Users that prefer a passphrase can override the default one at wallet
creation time using -p, or use the walletpassphrase rpc when
the wallet is running. This will prevent the wallet from auto-unlock,
and preserve the lock-by-default behavior.
2022-10-31 19:53:16 -07:00
Roy Lee
3aca0be46a consolidate: refactor prompt related code 2022-09-29 00:06:51 -07:00
Roy Lee
dc7f1e88eb config: remove SigNet and SimNet related code 2022-09-28 23:42:21 -07:00
Roy Lee
be7892a2ae config: unify lbcd and lbcwallet RPC credentails
1. remove lbcdusername and lbcdpassword
2. rename (username, password) to (rpcuser, rpcpass)

Most users are confused with the lbcduser and username at first,
and choose to use the same credentials between lbcd and lbcwallet.
2022-09-28 23:29:20 -07:00
Roy Lee
7a4cd602bb config: remove noinitload related configs 2022-09-28 23:04:56 -07:00
Roy Lee
10d58126b5 config: remove spv related configs 2022-09-28 23:04:32 -07:00
Roy Lee
08e57e9ee3 config: remove gRPC related configs 2022-09-28 23:04:22 -07:00
Roy Lee
1917978ae8 consolidate: remove obsoleted files 2022-09-28 22:27:03 -07:00
Roy Lee
79da868c74 wallet: remove public passphrase prompt
1. Remove passphrase support for public keys.
2. Rename privPassphrase to passphrase to avoid confusion.

Note:

There has been a bug in the prompt, which prevents users from
specifying a custom public passphrase. So, most wallet databases
have been using the default password for the public keys, anyway.
2022-09-28 22:12:38 -07:00
Roy Lee
9c20f19d23 go mod: update lbcd version to include rpc commend updates 2022-09-28 11:48:23 -07:00
Roy Lee
e78e49d0f6 multi-account: update rpc help messages 2022-09-28 11:48:23 -07:00
Roy Lee
50950fb0d5 multi-account: update listaddresstransactions 2022-09-28 11:48:23 -07:00
Roy Lee
51e700e7d9 multi-account: update listsinceblock 2022-09-28 11:48:23 -07:00
Roy Lee
1edb90e0aa multi-account: update listalltransactions 2022-09-28 11:48:23 -07:00
Roy Lee
2e8dcc4312 multi-account: update listtransactions 2022-09-28 11:48:23 -07:00
Roy Lee
4acd03be8b multi-account: update listreceivedbyaddress 2022-09-28 11:48:23 -07:00
Roy Lee
b34aa61e4d multi-account: update listreceivedbyaccount 2022-09-28 11:48:23 -07:00
Roy Lee
93b33edbcd multi-account: update sendtoaddress 2022-09-28 11:48:23 -07:00
Roy Lee
e6a4b3d1fd multi-account: update gettransaction 2022-09-28 11:48:23 -07:00
Roy Lee
c42e8a0705 multi-account: update validateaddress 2022-09-28 11:48:23 -07:00
Roy Lee
03256c049b multi-account: update getreceivedbyaccount 2022-09-28 11:48:23 -07:00
Roy Lee
678379ce45 multi-account: update listaccounts 2022-09-28 11:48:23 -07:00
Roy Lee
64554cfbc0 multi-account: update sendfrom 2022-09-28 11:48:23 -07:00
Roy Lee
af2d35a604 multi-account: update sendmany 2022-09-28 11:48:23 -07:00
Roy Lee
43bbf0a089 multi-account: update getrawchangeaddress 2022-09-28 11:48:23 -07:00
Roy Lee
c0b263ece7 multi-account: update renameaccount 2022-09-28 11:48:23 -07:00
Roy Lee
531c461383 multi-account: update createNewAccount 2022-09-28 11:48:23 -07:00
Roy Lee
03dd33f8c9 multi-account: update getunconfirmedbalance 2022-09-28 11:48:23 -07:00
Roy Lee
09ec547ed6 multi-account: update getaccount 2022-09-28 11:48:23 -07:00
Roy Lee
608e4dddb5 multi-account: update getbalance 2022-09-28 11:48:23 -07:00
Roy Lee
71f8ba68a0 multi-account: update getaccountaddress 2022-09-28 11:48:23 -07:00
Roy Lee
6a610a8cdf multi-account: update getaddressbyaccount 2022-09-28 11:48:23 -07:00
Roy Lee
a02435bbf7 multi-account: update getnewaddress 2022-09-28 11:48:23 -07:00
Roy Lee
47e8dbda55 multi-account: set DefaultScope to KeyScopeBIP0044
By design, all scopes shared the same account name/number space.
2022-09-28 11:48:23 -07:00
Roy Lee
5a0c010688 multi-account: update sendPairs to take keyscope pointer 2022-09-28 11:48:23 -07:00
Roy Lee
00c29c3936 multi-account: add forEachKeyScope uitility iterator 2022-09-28 11:48:23 -07:00
Roy Lee
169abd446c multi-account: support BIP44 account discovery 2022-09-28 11:48:23 -07:00
Roy Lee
0410b7ce01 consolidate: merge internal/external branches 2022-09-28 11:48:23 -07:00
42 changed files with 1394 additions and 3383 deletions

527
CHANGES
View file

@ -1,527 +0,0 @@
============================================================================
User visible changes for lbcwallet
A wallet daemon for lbcd, written in Go
============================================================================
Changes in 0.7.0 (Mon Nov 23 2015)
- New features:
- Wallet will now detect network inactivity and reconnect to the lbcd
RPC server if the connection was lost (#320)
- Bug fixes:
- Removed data races in the RPC server (#292) and waddrmgr package
(#293)
- Corrected handling of lbcutil.AddressPubKey addresses when querying
for a ManagedAddress from the address manager (#313)
- Fixed signmessage and verifymessage algorithm to match the equivalent
algorithms used by Core (#324)
- Notable developer-related changes:
- Added support for AppVeyor continuous integration (#299)
- Take advantage of optimized zeroing from the Go 1.5 release (#286)
- Added IsError function to waddrmgr to check that an error is a
ManagerError and contains a matching error code (#289). Simplified
error handling in the wallet package and RPC server with this function
(#290).
- Switched to using a more space efficient data structure for the
wtxmgr CreditRecord type (#295)
- Incorporated latest updates to the votingpool package (#315)
- Miscellaneous:
- Updated websocket notification handlers to latest API required by
lbcd (#294)
- Enabled the logging subsystem of the rpcclient package (#328)
- Contributors (alphabetical order):
- Alex Yocom-Piatt
- cjepson
- Dave Collins
- John C. Vernaleo
- Josh Rickmar
- Rune T. Aune
Changes in 0.6.0 (Wed May 27 2015)
- New features:
- Add initial account support (#155):
- Add account names for each account number
- Create initial account with the "default" name
- Create new accounts using the createnewaccount RPC
- All accounts (with the exception of the imported account) may be
renamed using the renameaccount RPC
- RPC requests with an unspecified account that default to the unnamed
account in Bitcoin Core Wallet default to "default", the name of the
initial account
- Several RPCs with account parameters do not work with lbcwallet
accounts due to concerns over expectations of API compatibility with
Bitcoin Core Wallet. A new RPC API is being planned to rectify this
(#220).
- Store transactions, transaction history, and spend tracking in the
database (#217, #234)
- A full rescan is required when updating from previous wallet
versions to rebuild the transaction history
- Add utility (cmd/dropwtxmgr) to drop transaction history and force a
rescan (#234)
- Implement the help RPC to return single line usages of all wallet and
lbcd server requests as well as detailed usage for a single request
- Bug fixes:
- Handle chain reorgs by unconfirming transactions from removed blocks
(#248)
- Rollback all transaction history when none of the saved recently seen
block hashes are known to lbcd (#234, #281)
- Prevent the situation where the default account was renamed but cannot
be renamed back to "" or "default" by removing the special case naming
policy for the default account (#253)
- Create the initial account address if needed when calling the
getaccountaddress RPC (#238)
- Prevent listsinceblock RPC from including all listtransactions result
objects for all transactions since the genesis block (fix included in
#227)
- Add missing fields to listtransactions and gettransaction RPC results
(#265)
- Remove target confirmations limit on listsinceblock results (#266)
- Add JSON array to report errors creating input signature for
signrawtransaction RPC (#267)
- Use negative fees with listtransactions result types (#272)
- Prevent duplicate wallet lock attempt after timeout if explicitly
locked (#275)
- Use correct RPC server JSON-RPC error code for incorrect passphrases
with a walletpassphrase request (#284)
- Regressions:
- Inserting transactions and marking outputs as controlled by wallet in
the new transaction database is extremely slow compared to the previous
in-memory implementation. Later versions may improve this performance
regression by using write-ahead logging (WAL) and performing more
updates at a time under a single database transaction.
- Notable developer-related changes:
- Relicense all code to the lbcsuite developers (#258)
- Replace txstore package with wtxmgr, the walletdb-based transaction
store (#217, #234)
- Add Cursor API to walletdb for forwards and backwards iteration over
a bucket (included in #234)
- Factor out much of main's wallet.go into a wallet package (#213,
#276, #255)
- Convert RPC server and client to lbcjson v2 API (#233, #227)
- Help text and single line usages for the help RPC are pregenerated
from descriptions in the internal/rpchelp package and saved as
globals in main. Help text must be regenerated (using `go generate`)
each time the lbcjson struct tags change or the help definitions are
modified.
- Add additional features to the votingpool package:
- Implement StartWithdrawal API to begin an Open Transactions
withdrawal (#178)
- Add internal APIs to store withdrawal transactions in the wallet's
transaction database (#221)
- Addresses marked as used after appearing publicly on the blockchain or
in mempool; required for future single-use address support (#207)
- Modified waddrmgr APIs to use ForEach functions to iterate over
address strings and managed addresses to improve scability (#216)
- Move legacy directory under internal directory to prevent importing
of unmaintained packages (enforced since Go 1.5) (#285)
- Improve test coverage in the waddrmgr and wtxmgr packages (#239, #217)
- Contributors (alphabetical order):
- Dave Collins
- Guilherme Salgado
- Javed Khan
- Josh Rickmar
- Manan Patel
Changes in 0.5.1 (Fri Mar 06 2015)
- New features:
- Add flag (--createtemp) to create a temporary simnet wallet
- Bug fixes:
- Mark newly received transactions confirmed when the wallet is initially
created or opened with no addresses
- Notable developer-related changes:
- Refactor the address manager database upgrade paths for easier future
upgrades
- Private key zeroing functions consolidated into the internal zero package
and optimized
Changes in 0.5.0 (Tue Mar 03 2015)
- New features:
- Add a new address manager package (waddrmgr) to replace the previous
wallet/keystore package:
- BIP0032 hierarchical deterministic keys
- BIP0043/BIP0044 multi-account hierarchy
- Strong focus on security:
- Wallet master encryption keys protected by scrypt PBKDF
- NaCl-based secretbox cryptography (XSalsa20 and Poly1305)
- Mandatory encryption of private keys and P2SH redeeming scripts
- Optional encryption of public data, including extended public keys
and addresses
- Different crypto keys for redeeming scripts to mitigate cryptanalysis
- Hardened against memory scraping through the use of actively clearing
private material from memory when locked
- Different crypto keys used for public, private, and script data
- Ability for different passphrases for public and private data
- Multi-tier scalable key design to allow instant password changes
regardless of the number of addresses stored
- Import WIF keys
- Import pay-to-script-hash scripts for things such as multi-signature
transactions
- Ability to export a watching-only version which does not contain any
private key material
- Programmatically detectable errors, including encapsulation of errors
from packages it relies on
- Address synchronization capabilities
- Add a new namespaced database package (walletdb):
- Key/value store
- Namespace support
- Allows multiple packages to have their own area in the database without
worrying about conflicts
- Read-only and read-write transactions with both manual and managed modes
- Nested buckets
- Supports registration of backend databases
- Comprehensive test coverage
- Replace the createencryptedwallet RPC with a wizard-style prompt
(--create) to create a new walletdb-backed wallet file and import keys
from the old Armory wallet file (if any)
- Transaction creation changes:
- Drop default transaction fee to 0.00001 BTC per kB
- Use standard script flags provided by the txscript package for
transaction creation and sanity checking
- Randomize change output index
- Includes amounts (total spendable, total needed, and fee) in all
insufficient funds errors
- Add support for simnet, the private simulation test network
- Implement the following Bitcoin Core RPCs:
- listreceivedbyaddress (#53)
- lockunspent, listlockunspent (#50, #55)
- getreceivedbyaddress
- listreceivedbyaccount
- Reimplement lbcd RPCs which return the best block to use the block most
recently processed by wallet to avoid confirmation races:
- getbestblockhash
- getblockcount
- Perform clean shutdown on interrupt or when a stop RPC is received (#69)
- Throttle the number of connected HTTP POST and websocket client
connections (tunable using the rpcmaxclients and rpcmaxwebsockets config
options)
- Provide the ability to disable TLS when connecting to a localhost lbcd or
serving localhost clients
- Rescan improvements:
- Add a rescan notification for when the rescan has completed and no more
rescan notifications are expected (#99)
- Use the most recent partial sync height from a rescan progress
notification when a rescan is restarted after the lbcd connection is lost
- Force a rescan if the transaction store cannot be opened (due to a
missing file or if the deserialization failed)
- RPC compatibility improvements:
- Allow the use of the `*` account name to refer to all accounts
- Make the account parameter optional for the getbalance and
listalltransactions requests
- Add iswatchonly field to the validateaddress response result
- Check address equivalence in verifymessage by comparing pubkeys and pubkey
hashes rather than requiring the address being verified to be one
controlled by the wallet and using its private key for verification
- Bug fixes:
- Prevent an out-of-bounds panic when handling a gettransaction RPC.
- Prevent a panic on client disconnect (#110).
- Prevent double spending coins when creating multiple transactions at once
by serializing access to the transaction creation logic (#120)
- Mark unconfirmed transaction credits as spent when another unconfirmed
transaction spends one (#91)
- Exclude immature coinbase outputs from listunspent results (#103)
- Fix several data and logic races during sync with lbcd (#101)
- Avoid a memory issue from incorrect slice usage which caused both
duplicate and missing blocks in the transaction store when middle
inserting transactions from a new block
- Only spend P2PKH outputs when creating sendfrom/sendmany/sendtoaddress
transactions (#89)
- Return the correct UTXO set when fetching all wallet UTXOs by fixing an
incorrect slice append
- Remove a deadlock caused by filling the lbcd notification channel (#100)
- Avoid a confirmation race by using the most recently processed block in
RPC handlers, rather than using the most recently notified block by lbcd
- Marshal empty JSON arrays as `[]` instead of the JSON `null` by using
empty, non-nil Go slices
- Flush logs and run all deferred functions before main returns and the
process exits
- Sync temporary transaction store flat file before closing and renaming
- Accept hex strings with an odd number of characters
- Notable developer-related changes:
- Switch from the go.net websocket package to gorilla websockets
- Refactor the RPC server:
- Move several global variables to the rpcServer struct
- Dynamically look up appropriate handlers for the current lbcd connection
status and wallet sync state
- Begin creating websocket notifications by sending to one of many
notification channels in the rpcServer struct, which are in turn
marshalled and broadcast to each websocket client
- Separate the RPC client code into the chain package:
- Uses rpcclient for a lbcd websocket RPC client
- Converts all notification callbacks to typed messages sent over channels
- Uses an unbounded queue for waiting notifications
- Import a new voting pool package (votingpool):
- Create and fetch voting pools and series from a walletdb namespace
- Generate deposit addresses utilizing m-of-n multisig P2SH scripts
- Improve transaction creation readability by splitting a monolithic
function into several smaller ones
- Check and handle all errors in some way, or explicitly comment why a
particular error was left unchecked
- Simplify RPC error handling by wrapping specific errors in unique types to
create an appropriate lbcjson error before the response is marshalled
- Add a map of unspent outputs (keyed by outpoint) to the transaction store
for quick lookup of any UTXO and access to the full wallet UTXO set
without iterating over many transactions looking for unspent credits
- Modify several data structures and function signatures have been modified
to reduce the number of needed allocations and be more cache friendly
- Miscellaneous:
- Rewrite paths relative to the data directory when an alternate data
directory is provided on the command line
- Switch the websocket endpoint to `ws` to match lbcd
- Remove the getaddressbalance extension RPC to discourage address reuse and
encourage watching for expected payments by using listunspent
- Increase transaction creation performance by moving the sorting of
transaction outputs by their amount out of an inner loop
- Add additional logging to the transaction store:
- Log each transaction added to the store
- Log each previously unconfirmed transaction that is mined
- [debug] Log which previous outputs are marked spent by a newly inserted
debiting transaction
- [debug] Log each transaction that is removed in a rollback
- Only log rollbacks if transactions are reorged out of the old chain
- Save logs to network-specific directories
(e.g. ~/.lbcwallet/logs/testnet3) to match lbcd behavior (#114)
Changes in 0.4.0 (Sun May 25 2014)
- Implement the following standard bitcoin server RPC requests:
- signmessage (https://github.com/lbryio/lbcwallet/issues/58)
- verifymessage (https://github.com/lbryio/lbcwallet/issues/61)
- listunspent (https://github.com/lbryio/lbcwallet/issues/54)
- validateaddress (https://github.com/lbryio/lbcwallet/issues/60)
- addressmultisig (https://github.com/lbryio/lbcwallet/issues/37)
- createmultisig (https://github.com/lbryio/lbcwallet/issues/37)
- signrawtransaction (https://github.com/lbryio/lbcwallet/issues/59)
- Add authenticate extension RPC request to authenticate a websocket
session without requiring the use of the HTTP Authorization header
- Add lbcdusername and lbcdpassword options to allow separate
authentication credentials from wallet clients when authenticating to a
lbcd websocket RPC server
- Fix RPC response passthrough: JSON unmarshaling and marshaling is now
delayed until necessary and JSON result objects from lbcd are sent to
clients directly without an extra decode+encode that may change the
representation of large integer values
- Fix several websocket client connection issues:
- Disconnect clients are cleanly removed without hanging on any final
sends
- Set deadline for websocket client sends to prevent hanging on
misbehaving clients or clients with a bad connection
- Fix return result for dumprivkey by always padding the private key bytes
to a length of 32
- Fix rescan for transaction history for imported addresses
(https://github.com/lbryio/lbcwallet/issues/74)
- Fix listsinceblock request handler to consider the minimum confirmation
parameter (https://github.com/lbryio/lbcwallet/issues/80)
- Fix several RPC handlers which require an unlocked wallet to check
for an unlocked wallet before continuing
(https://github.com/lbryio/lbcwallet/issues/65)
- Fix handling for block rewards (coinbase transactions):
- Update listtransactions results to use "generate" category for
coinbase outputs
- Prevent inclusion of immature coinbase outputs for newly created
transactions
- Rewrite the transaction store to handle several issues regarding
transation malleability and performance issues
- The new transaction store is written to disk in a different format
then before, and upgrades will require a rescan to rebuild the
transaction history
- Improve rescan:
- Begin rescan with known UTXO set at start height
- Serialize executation of all rescan requests
- Merge waiting rescan jobs so all jobs can be handled with a single
rescan
- Support parially synced addresses in the keystore and incrementally
mark rescan progress. If a rescan is unable to continue (wallet
closes, lbcd disconnects, etc.) a new rescan can start at the last
synced chain height
- Notify (with an unsolicited notification) websocket clients of lbcd
connection state
- Improve logging:
- Log reason for disconnecting a websocket client
- Updates for lbcd websocket API changes
- Stability fixes, internal API changes, general code cleanup, and comment
corrections
Changes in 0.3.0 (Mon Feb 10 2014)
- Use correct hash algorithm for chained addresses (fixes a bug where
address chaining was still deterministic, but forked from Armory and
previous lbcwallet implementations)
- Change websocket endpoint to connect to lbcd 0.6.0-alpha
- Redo server implementation to serialize handling of client requests
- Redo account locking to greatly reduce lbcwallet lockups caused by
incorrect mutex usage
- Open all accounts, rather than just the default account, at startup
- Generate new addresses using pubkey chaining if keypool is depleted and
wallet is locked
- Make maximum keypool size a configuration option (keypoolsize)
- Add disallowfree configuration option (default false) to force adding
the minimum fee to all outbound transactions
- Implement the following standard bitcoin server RPC requests:
- getinfo (https://github.com/lbryio/lbcwallet/issues/63)
- getrawchangeaddress (https://github.com/lbryio/lbcwallet/issues/41)
- getreceivedbyaccount (https://github.com/lbryio/lbcwallet/issues/42)
- gettransaction (https://github.com/lbryio/lbcwallet/issues/44)
- keypoolrefill (https://github.com/lbryio/lbcwallet/issues/48)
- listsinceblock (https://github.com/lbryio/lbcwallet/issues/52)
- sendtoaddress (https://github.com/lbryio/lbcwallet/issues/56)
- Add empty (unimplemented) handlers for the following RPC requests so
requests are not passed down to lbcd:
- getblocktemplate
- getwork
- stop
- Add RPC extension request, exportwatchingwallet, to export an account
with a watching-only wallet from an account with a hot wallet that
may be used by a separate lbcwallet instance
- Require all account wallets to share the same passphrase
- Change walletlock and walletpassphrase RPC requests to lock or unlock
all account wallets
- Allow opening accounts with watching-only wallets
- Return txid for sendfrom RPC requests
(https://github.com/lbryio/lbcwallet/issues/64)
- Rescan imported private keys in background
(https://github.com/lbryio/lbcwallet/issues/34)
- Do not import duplicate private keys
(https://github.com/lbryio/lbcwallet/issues/35)
- Write all three account files for a new account, rather than just
the wallet (https://github.com/lbryio/lbcwallet/issues/30)
- Create any missing directories before writing autogenerated certificate
pair
- Fix rescanning of a new account's root address
- Fix error in the wallet file serialization causing duplicate address
encryption attempts
- Fix issue calculating eligible transaction inputs caused by a bad
confirmation check
- Fix file locking issue on Windows caused by not closing files before
renaming
- Fix typos in README file
Changes in 0.2.1 (Thu Jan 10 2014)
- Fix a mutex issue which caused lbcwallet to lockup on all
RPC requests needing to read or write an account
Changes in 0.2.0 (Thu Jan 09 2014)
- Enable mainnet support (disabled by default, use --mainnet to enable)
- Don't hardcode localhost lbcd connections. Instead, add a --connect
option to specify the hostname or address and port of a local or
remote lbcd instance
(https://github.com/lbryio/lbcwallet/issues/1)
- Remove --serverport port and replace with --listen. This option works
just like lbcd's --rpclisten and allows to specify the interfaces to
listen for RPC connections
- Require TLS and Basic HTTP authentication before wallet can be
controlled over RPC
- Refill keypool if wallet is unlocked and keypool is emptied
- Detect and rollback saved tx/utxo info after lbcd performs blockchain
reorganizations while lbcwallet was disconnected
- Add support for the following standard bitcoin JSON-RPC calls:
- dumpprivkey (https://github.com/lbryio/lbcwallet/issues/9)
- getaccount
- getaccountaddress
- importprivkey (https://github.com/lbryio/lbcwallet/issues/2)
- listtransactions (https://github.com/lbryio/lbcwallet/issues/12)
- Add several extension RPC calls for websocket connections:
- getaddressbalance: get the balance associated with a single address
- getunconfirmedbalance: get total balance for unconfirmed transactions
- listaddresstransactions: list transactions for a single address
(https://github.com/lbryio/lbcwallet/issues/27)
- listalltransactions: lists all transactions without specifying a range
- Make RPC extensions available only to websocket connections, with the
exception of createencryptedwallet
- Add dummy handlers for unimplemented wallet RPC calls
(https://github.com/lbryio/lbcwallet/issues/29)
- Add socks5/tor proxy support
- Calculate and add minimum transaction fee to created transactions
- Use OS-specific rename calls to provide atomic file renames which
can replace a currently-existing file
(https://github.com/lbryio/lbcwallet/issues/20)
- Move account files to a single directory per bitcoin network to
prevent a future scaling issue
(https://github.com/lbryio/lbcwallet/issues/16)
- Fix several data races and mutex mishandling
- Fix a bug where the RPC server hung on requests requiring lbcd
when a lbcd connection was never established
- Fix a bug where creating account files did not create all necessary
directories (https://github.com/lbryio/lbcwallet/issues/15)
- Fix a bug where '~' did not expand to a home or user directory
(https://github.com/lbryio/lbcwallet/issues/17)
- Fix a bug where returning account names as strings did not remove
trailing ending 0s
- Fix a bug where help usage was displayed twice using the -h or --help
flag
- Fix sample listening address in sample configuration file
- Update sample configuration file with all available options with
descriptions and defaults for each
Initial Release 0.1.0 (Wed Nov 13 2013)
- Initial release

180
README.md
View file

@ -1,66 +1,18 @@
# lbcwallet
lbcwallet is a daemon, which provides lbry wallet functionality for a
single user.
Public and private keys are derived using the hierarchical
deterministic format described by
[BIP0032](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki).
Unencrypted private keys are not supported and are never written to disk.
lbcwallet uses the `m/44'/<coin type>'/<account>'/<branch>/<address index>`
HD path for all derived addresses, as described by
[BIP0044](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki).
Due to the sensitive nature of public data in a BIP0032 wallet,
lbcwallet provides the option of encrypting not just private keys, but
public data as well. This is intended to thwart privacy risks where a
wallet file is compromised without exposing all current and future
addresses (public keys) managed by the wallet. While access to this
information would not allow an attacker to spend or steal coins, it
does mean they could track all transactions involving your addresses
and therefore know your exact balance. In a future release, public data
encryption will extend to transactions as well.
Wallet clients can use one of two RPC servers:
1. A legacy JSON-RPC server mostly compatible with Bitcoin Core
The JSON-RPC server exists to ease the migration of wallet applications
from Core, but complete compatibility is not guaranteed. Some portions of
the API (and especially accounts) have to work differently due to other
design decisions (mostly due to BIP0044). However, if you find a
compatibility issue and feel that it could be reasonably supported, please
report an issue. This server is enabled by default.
2. An experimental gRPC server
The gRPC server uses a new API built for lbcwallet, but the API is not
stabilized and the server is feature gated behind a config option
(`--experimentalrpclisten`). If you don't mind applications breaking due
to API changes, don't want to deal with issues of the legacy API, or need
notifications for changes to the wallet, this is the RPC server to use.
The gRPC server is documented [here](./rpc/documentation/README.md).
lbcwallet implements HD Wallet functionality which conforms to
[BIP0032](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki),
[BIP0043](https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki),
and [BIP0044](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki).
## Security
We take security seriously. Please contact [security](mailto:security@lbry.com) regarding any security issues.
Our PGP key is [here](https://lbry.com/faq/pgp-key) if you need it.
## Requirements
## Build from Source Code
- [Go](http://golang.org) 1.16 or newer.
- `lbcwallet` is not an SPV client and requires connecting to a local or remote
`lbcd` for asynchronous blockchain queries and notifications over websockets.
Full installation instructions can be found [here](https://github.com/lbryio/lbcd).
## To Build lbcwallet, lbcd, and lbcctl from Source
Install Go according to its [installation instructions](http://golang.org/doc/install).
Build `lbcwallet`
Requires [Go](http://golang.org) 1.19 or newer. Install Go according to its [installation instructions](http://golang.org/doc/install).
``` sh
git clone https://github.com/lbryio/lbcwallet
@ -68,114 +20,62 @@ cd lbcwallet
go build .
```
To make the quick start guide self-contained, here's how we can build the `lbcd` and `lbcctl`
## **lbcd** & **lbcwallet**
``` sh
git clone https://github.com/lbryio/lbcd
cd lbcd
`lbcwallet` is not an SPV client and requires connecting to a `lbcd` node for asynchronous blockchain queries and notifications over websockets.
# build lbcd
go build .
lbcwallet can serve wallet related RPCs and proxy lbcd RPCs to the assocated lbcd. It's sufficient for a user to connect just the **lbcwallet** instead of both.
# build lbcctl
go build ./cmd/lbcctl
``` mermaid
sequenceDiagram
actor C as lbcctl
participant W as lbcwallet (port: 9244)
participant D as lbcd (port: 9245)
rect rgb(200,200,200)
Note over C,W: lbcctl --wallet balance
C ->>+ W: getbalance
W -->>- C: response
end
rect rgb(200,200,200)
Note over C,D: lbcctl --wallet getblockcount (lbcd RPC service proxied by lbcwallet)
C ->>+ W: getblockcount
W ->>+ D: getblockcount
D -->>- W: response
W -->>- C: response
end
```
## Getting Started
The first time running the `lbcwallet` we need to create a new wallet.
Create a new wallet with a randomly generated seed or an existing one.
``` sh
./lbcwallet --create
lbcwallet --create
Do you have an existing wallet seed you want to use? (n/no/y/yes) [no]: no
Your wallet generation seed is: 3d005498ad5e9b7439b857249e328ec34e21845b7d1a7d2a5641d4050c02d0da
```
Start a local instance of `lbcd` and have the `lbcwallet` connecting to it.
The created wallet protects the seed with a default passphrase (`"passphrase"`), which can be override with `-p` option:
``` sh
# Start a lbcd with its RPC credentials
./lbcd --txindex --rpcuser=lbcduser --rpcpass=lbcdpass
# Start a lbcwallet with its RPC credentials along with the lbcd's RPC credentials
# The default lbcd instance to conect to is already localhost:9245 so we don't need to specify it explicitly here.
./lbcwallet --username=rpcuser --password=rpcpass --lbcdusername=lbcduser --lbcdpassword=lbcdpass # --rpcconnect=localhost:9245
#
# rpcuser/rpcpass lbcduser/lbcdpass
# lbcctl <-------------------> lbcwallet <--------------------> lbcd
# RPC port 9244 RPC port 9245
#
lbcwallet --create -p my-passphrase
```
If the `lbcd` and `lbcwallet` use the same RPC credentials, we can skip the `--lbcdusername` and `--lbcdpassword`
Start wallet server, and connect it to a lbcd instance.
``` sh
./lbcd --txindex --rpcuser=rpcuser --rpcpass=rpcpass
./lbcwallet --username=rpcuser --password=rpcpass
#
# rpcuser/rpcpass rpcuser/rpcpass
# lbcctl <-------------------> lbcwallet <--------------------> lbcd
# RPC port 9244 RPC port 9245
#
lbcwallet --rpcuser=rpcuser --rpcpass=rpcpass # --rpcconnect=localhost:9245
```
Note:
At startup, the wallet will try to unlock itself with the default passphrase (`passphrase`) or an user provided one (using `-p` option).
- `lbcd` and `lbcwallet` implements two disjoint sets of RPCs.
- `lbcd` serves RPC on port 9245 while `lbcwallet` on port 9244.
- `lbcwallet` can proxy non-wallet RPCs to its associated `lbcd`.
Examples of using `lbcctl` to interact with the setup via RPCs:
1. Calling non-wallet RPC directly on lbcd:
``` sh
./lbcctl --rpcuser=rpcuser --rpcpass=rpcpass getblockcount
#
# lbcctl <-- getblockcount() --> lbcd
# RPC port 9245 (handled)
#
```
2. Calling wallet RPC on lbcwallet (using `--wallet`)
``` sh
./lbcctl --rpcuser=rpcuser --rpcpass=rpcpass --wallet getbalance
#
# lbcctl <-- getbalance() --> lbcwallet
# RPC port 9244 (handled)
#
```
3. Calling non-wallet RPC on lbcwallet, which proxies it to lbcd:
``` sh
./lbcctl --rpcuser=rpcuser --rpcpass=rpcpass --wallet getblockcount
#
# lbcctl <-- getblockcount() --> lbcwallet <-- getblockcount() --> lbcd
# RPC port 9244 (proxied) RPC port 9245
#
```
## Default Network and RPC Ports
| Instance | mainnet | testet | regtest |
| ------------- | ------- | ------ | ------- |
| lbcd Network | 9246 | 19246 | 29246 |
| lbcd RPC | 9245 | 19245 | 29245 |
| lbcwallet RPC | 9244 | 19244 | 29244 |
Examples
If the passphrase does not match, the wallet remains locked. User can lock/unlock the wallet using `walletlock` and `walletpassphrase` RPCs.
``` sh
./lbcctl getblockcount # port 9245
./lbcctl --wallet getblockcount # port 9244
./lbcctl --testnet getblockcount # port 19245
./lbcctl --wallet --regtest getblockcount # port 29244
lbcwallet --rpcuser=rpcuser --rpcpass=rpcpass -p my_passphrase
```
## Contributing

View file

@ -31,26 +31,18 @@ type BlockFilterer struct {
// Params specifies the chain params of the current network.
Params *chaincfg.Params
// ExReverseFilter holds a reverse index mapping an external address to
// ReverseFilter holds a reverse index mapping an external address to
// the scoped index from which it was derived.
ExReverseFilter map[string]waddrmgr.ScopedIndex
// InReverseFilter holds a reverse index mapping an internal address to
// the scoped index from which it was derived.
InReverseFilter map[string]waddrmgr.ScopedIndex
ReverseFilter map[string]waddrmgr.ScopedIndex
// WathcedOutPoints is a global set of outpoints being tracked by the
// wallet. This allows the block filterer to check for spends from an
// outpoint we own.
WatchedOutPoints map[wire.OutPoint]btcutil.Address
// FoundExternal is a two-layer map recording the scope and index of
// FoundAddresses is a two-layer map recording the scope and index of
// external addresses found in a single block.
FoundExternal map[waddrmgr.KeyScope]map[uint32]struct{}
// FoundInternal is a two-layer map recording the scope and index of
// internal addresses found in a single block.
FoundInternal map[waddrmgr.KeyScope]map[uint32]struct{}
FoundAddresses map[waddrmgr.ScopedIndex]struct{}
// FoundOutPoints is a set of outpoints found in a single block whose
// address belongs to the wallet.
@ -71,31 +63,20 @@ func NewBlockFilterer(params *chaincfg.Params,
// Construct a reverse index by address string for the requested
// external addresses.
nExAddrs := len(req.ExternalAddrs)
exReverseFilter := make(map[string]waddrmgr.ScopedIndex, nExAddrs)
for scopedIndex, addr := range req.ExternalAddrs {
exReverseFilter[addr.EncodeAddress()] = scopedIndex
nAddrs := len(req.Addresses)
reverseFilter := make(map[string]waddrmgr.ScopedIndex, nAddrs)
for scopedIndex, addr := range req.Addresses {
reverseFilter[addr.EncodeAddress()] = scopedIndex
}
// Construct a reverse index by address string for the requested
// internal addresses.
nInAddrs := len(req.InternalAddrs)
inReverseFilter := make(map[string]waddrmgr.ScopedIndex, nInAddrs)
for scopedIndex, addr := range req.InternalAddrs {
inReverseFilter[addr.EncodeAddress()] = scopedIndex
}
foundExternal := make(map[waddrmgr.KeyScope]map[uint32]struct{})
foundInternal := make(map[waddrmgr.KeyScope]map[uint32]struct{})
foundAddresses := make(map[waddrmgr.ScopedIndex]struct{})
foundOutPoints := make(map[wire.OutPoint]btcutil.Address)
return &BlockFilterer{
Params: params,
ExReverseFilter: exReverseFilter,
InReverseFilter: inReverseFilter,
ReverseFilter: reverseFilter,
WatchedOutPoints: req.WatchedOutPoints,
FoundExternal: foundExternal,
FoundInternal: foundInternal,
FoundAddresses: foundAddresses,
FoundOutPoints: foundOutPoints,
}
}
@ -183,12 +164,8 @@ func (bf *BlockFilterer) FilterOutputAddrs(addrs []btcutil.Address) bool {
var isRelevant bool
for _, addr := range addrs {
addrStr := addr.EncodeAddress()
if scopedIndex, ok := bf.ExReverseFilter[addrStr]; ok {
bf.foundExternal(scopedIndex)
isRelevant = true
}
if scopedIndex, ok := bf.InReverseFilter[addrStr]; ok {
bf.foundInternal(scopedIndex)
if scopedIndex, ok := bf.ReverseFilter[addrStr]; ok {
bf.found(scopedIndex)
isRelevant = true
}
}
@ -196,22 +173,9 @@ func (bf *BlockFilterer) FilterOutputAddrs(addrs []btcutil.Address) bool {
return isRelevant
}
// foundExternal marks the scoped index as found within the block filterer's
// found marks the scoped index as found within the block filterer's
// FoundExternal map. If this the first index found for a particular scope, the
// scope's second layer map will be initialized before marking the index.
func (bf *BlockFilterer) foundExternal(scopedIndex waddrmgr.ScopedIndex) {
if _, ok := bf.FoundExternal[scopedIndex.Scope]; !ok {
bf.FoundExternal[scopedIndex.Scope] = make(map[uint32]struct{})
}
bf.FoundExternal[scopedIndex.Scope][scopedIndex.Index] = struct{}{}
}
// foundInternal marks the scoped index as found within the block filterer's
// FoundInternal map. If this the first index found for a particular scope, the
// scope's second layer map will be initialized before marking the index.
func (bf *BlockFilterer) foundInternal(scopedIndex waddrmgr.ScopedIndex) {
if _, ok := bf.FoundInternal[scopedIndex.Scope]; !ok {
bf.FoundInternal[scopedIndex.Scope] = make(map[uint32]struct{})
}
bf.FoundInternal[scopedIndex.Scope][scopedIndex.Index] = struct{}{}
func (bf *BlockFilterer) found(scopedIndex waddrmgr.ScopedIndex) {
bf.FoundAddresses[scopedIndex] = struct{}{}
}

View file

@ -282,7 +282,7 @@ func TestBlockFiltererOneInOneOut(t *testing.T) {
req := &chain.FilterBlocksRequest{
WatchedOutPoints: watchedOutPoints,
}
blockFilterer := chain.NewBlockFilterer(&chaincfg.SimNetParams, req)
blockFilterer := chain.NewBlockFilterer(&chaincfg.RegressionNetParams, req)
// Filter block 100000, which should find matches for the watched
// outpoints.

View file

@ -75,8 +75,7 @@ type (
// is also included to monitor for spends.
FilterBlocksRequest struct {
Blocks []wtxmgr.BlockMeta
ExternalAddrs map[waddrmgr.ScopedIndex]btcutil.Address
InternalAddrs map[waddrmgr.ScopedIndex]btcutil.Address
Addresses map[waddrmgr.ScopedIndex]btcutil.Address
WatchedOutPoints map[wire.OutPoint]btcutil.Address
}
@ -88,12 +87,11 @@ type (
// caller can reinitiate a request for the subsequent block after
// updating the addresses of interest.
FilterBlocksResponse struct {
BatchIndex uint32
BlockMeta wtxmgr.BlockMeta
FoundExternalAddrs map[waddrmgr.KeyScope]map[uint32]struct{}
FoundInternalAddrs map[waddrmgr.KeyScope]map[uint32]struct{}
FoundOutPoints map[wire.OutPoint]btcutil.Address
RelevantTxns []*wire.MsgTx
BatchIndex uint32
BlockMeta wtxmgr.BlockMeta
FoundAddresses map[waddrmgr.ScopedIndex]struct{}
FoundOutPoints map[wire.OutPoint]btcutil.Address
RelevantTxns []*wire.MsgTx
}
// BlockDisconnected is a notifcation that the block described by the

View file

@ -12,22 +12,12 @@ func buildFilterBlocksWatchList(req *FilterBlocksRequest) ([][]byte, error) {
// Construct a watch list containing the script addresses of all
// internal and external addresses that were requested, in addition to
// the set of outpoints currently being watched.
watchListSize := len(req.ExternalAddrs) +
len(req.InternalAddrs) +
watchListSize := len(req.Addresses) +
len(req.WatchedOutPoints)
watchList := make([][]byte, 0, watchListSize)
for _, addr := range req.ExternalAddrs {
p2shAddr, err := txscript.PayToAddrScript(addr)
if err != nil {
return nil, err
}
watchList = append(watchList, p2shAddr)
}
for _, addr := range req.InternalAddrs {
for _, addr := range req.Addresses {
p2shAddr, err := txscript.PayToAddrScript(addr)
if err != nil {
return nil, err

View file

@ -270,12 +270,11 @@ func (c *RPCClient) FilterBlocks(
// `BatchIndex` is returned so that the caller can compute the
// *next* block from which to begin again.
resp := &FilterBlocksResponse{
BatchIndex: uint32(i),
BlockMeta: blk,
FoundExternalAddrs: blockFilterer.FoundExternal,
FoundInternalAddrs: blockFilterer.FoundInternal,
FoundOutPoints: blockFilterer.FoundOutPoints,
RelevantTxns: blockFilterer.RelevantTxns,
BatchIndex: uint32(i),
BlockMeta: blk,
FoundAddresses: blockFilterer.FoundAddresses,
FoundOutPoints: blockFilterer.FoundOutPoints,
RelevantTxns: blockFilterer.RelevantTxns,
}
return resp, nil

View file

@ -1,117 +0,0 @@
// Copyright (c) 2015-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"bufio"
"fmt"
"os"
"path/filepath"
"time"
"github.com/jessevdk/go-flags"
btcutil "github.com/lbryio/lbcutil"
"github.com/lbryio/lbcwallet/wallet"
"github.com/lbryio/lbcwallet/walletdb"
_ "github.com/lbryio/lbcwallet/walletdb/bdb"
)
const defaultNet = "mainnet"
var (
datadir = btcutil.AppDataDir("lbcwallet", false)
)
// Flags.
var opts = struct {
Force bool `short:"f" description:"Force removal without prompt"`
DbPath string `long:"db" description:"Path to wallet database"`
DropLabels bool `long:"droplabels" description:"Drop transaction labels"`
Timeout time.Duration `long:"timeout" description:"Timeout value when opening the wallet database"`
}{
Force: false,
DbPath: filepath.Join(datadir, defaultNet, wallet.WalletDBName),
Timeout: wallet.DefaultDBTimeout,
}
func init() {
_, err := flags.Parse(&opts)
if err != nil {
os.Exit(1)
}
}
func yes(s string) bool {
switch s {
case "y", "Y", "yes", "Yes":
return true
default:
return false
}
}
func no(s string) bool {
switch s {
case "n", "N", "no", "No":
return true
default:
return false
}
}
func main() {
os.Exit(mainInt())
}
func mainInt() int {
fmt.Println("Database path:", opts.DbPath)
_, err := os.Stat(opts.DbPath)
if os.IsNotExist(err) {
fmt.Println("Database file does not exist")
return 1
}
for !opts.Force {
fmt.Print("Drop all lbcwallet transaction history? [y/N] ")
scanner := bufio.NewScanner(bufio.NewReader(os.Stdin))
if !scanner.Scan() {
// Exit on EOF.
return 0
}
err := scanner.Err()
if err != nil {
fmt.Println()
fmt.Println(err)
return 1
}
resp := scanner.Text()
if yes(resp) {
break
}
if no(resp) || resp == "" {
return 0
}
fmt.Println("Enter yes or no.")
}
db, err := walletdb.Open("bdb", opts.DbPath, true, opts.Timeout)
if err != nil {
fmt.Println("Failed to open database:", err)
return 1
}
defer db.Close()
fmt.Println("Dropping lbcwallet transaction history")
err = wallet.DropTransactionHistory(db, !opts.DropLabels)
if err != nil {
fmt.Println("Failed to drop and re-create namespace:", err)
return 1
}
return 0
}

View file

@ -1,368 +0,0 @@
// Copyright (c) 2015-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"golang.org/x/crypto/ssh/terminal"
"github.com/jessevdk/go-flags"
"github.com/lbryio/lbcd/btcjson"
"github.com/lbryio/lbcd/chaincfg/chainhash"
"github.com/lbryio/lbcd/rpcclient"
"github.com/lbryio/lbcd/txscript"
"github.com/lbryio/lbcd/wire"
btcutil "github.com/lbryio/lbcutil"
"github.com/lbryio/lbcwallet/internal/cfgutil"
"github.com/lbryio/lbcwallet/netparams"
"github.com/lbryio/lbcwallet/wallet/txauthor"
"github.com/lbryio/lbcwallet/wallet/txrules"
"github.com/lbryio/lbcwallet/wallet/txsizes"
)
var (
walletDataDirectory = btcutil.AppDataDir("lbcwallet", false)
newlineBytes = []byte{'\n'}
)
func fatalf(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, format, args...)
os.Stderr.Write(newlineBytes)
os.Exit(1)
}
func errContext(err error, context string) error {
return fmt.Errorf("%s: %v", context, err)
}
// Flags.
var opts = struct {
TestNet3 bool `long:"testnet" description:"Use the test bitcoin network (version 3)"`
SimNet bool `long:"simnet" description:"Use the simulation bitcoin network"`
RegTest bool `long:"regtest" description:"Use the regression test network"`
RPCConnect string `short:"c" long:"connect" description:"Hostname[:port] of wallet RPC server"`
RPCUsername string `short:"u" long:"rpcuser" description:"Wallet RPC username"`
RPCCertificateFile string `long:"cafile" description:"Wallet RPC TLS certificate"`
FeeRate *cfgutil.AmountFlag `long:"feerate" description:"Transaction fee per kilobyte"`
SourceAccount string `long:"sourceacct" description:"Account to sweep outputs from"`
DestinationAccount string `long:"destacct" description:"Account to send sweeped outputs to"`
RequiredConfirmations int64 `long:"minconf" description:"Required confirmations to include an output"`
}{
TestNet3: false,
SimNet: false,
RegTest: false,
RPCConnect: "localhost",
RPCUsername: "",
RPCCertificateFile: filepath.Join(walletDataDirectory, "rpc.cert"),
FeeRate: cfgutil.NewAmountFlag(txrules.DefaultRelayFeePerKb),
SourceAccount: "imported",
DestinationAccount: "default",
RequiredConfirmations: 1,
}
// Parse and validate flags.
func init() {
// Unset localhost defaults if certificate file can not be found.
certFileExists, err := cfgutil.FileExists(opts.RPCCertificateFile)
if err != nil {
fatalf("%v", err)
}
if !certFileExists {
opts.RPCConnect = ""
opts.RPCCertificateFile = ""
}
_, err = flags.Parse(&opts)
if err != nil {
os.Exit(1)
}
if opts.TestNet3 && opts.SimNet {
fatalf("Multiple bitcoin networks may not be used simultaneously")
}
var activeNet = &netparams.MainNetParams
if opts.TestNet3 {
activeNet = &netparams.TestNet3Params
} else if opts.SimNet {
activeNet = &netparams.SimNetParams
} else if opts.RegTest {
activeNet = &netparams.RegTestParams
}
if opts.RPCConnect == "" {
fatalf("RPC hostname[:port] is required")
}
rpcConnect, err := cfgutil.NormalizeAddress(opts.RPCConnect, activeNet.RPCServerPort)
if err != nil {
fatalf("Invalid RPC network address `%v`: %v", opts.RPCConnect, err)
}
opts.RPCConnect = rpcConnect
if opts.RPCUsername == "" {
fatalf("RPC username is required")
}
certFileExists, err = cfgutil.FileExists(opts.RPCCertificateFile)
if err != nil {
fatalf("%v", err)
}
if !certFileExists {
fatalf("RPC certificate file `%s` not found", opts.RPCCertificateFile)
}
if opts.FeeRate.Amount > 1e6 {
fatalf("Fee rate `%v/kB` is exceptionally high", opts.FeeRate.Amount)
}
if opts.FeeRate.Amount < 1e2 {
fatalf("Fee rate `%v/kB` is exceptionally low", opts.FeeRate.Amount)
}
if opts.SourceAccount == opts.DestinationAccount {
fatalf("Source and destination accounts should not be equal")
}
if opts.RequiredConfirmations < 0 {
fatalf("Required confirmations must be non-negative")
}
}
// noInputValue describes an error returned by the input source when no inputs
// were selected because each previous output value was zero. Callers of
// txauthor.NewUnsignedTransaction need not report these errors to the user.
type noInputValue struct {
}
func (noInputValue) Error() string { return "no input value" }
// makeInputSource creates an InputSource that creates inputs for every unspent
// output with non-zero output values. The target amount is ignored since every
// output is consumed. The InputSource does not return any previous output
// scripts as they are not needed for creating the unsinged transaction and are
// looked up again by the wallet during the call to signrawtransaction.
func makeInputSource(outputs []btcjson.ListUnspentResult) txauthor.InputSource {
var (
totalInputValue btcutil.Amount
inputs = make([]*wire.TxIn, 0, len(outputs))
inputValues = make([]btcutil.Amount, 0, len(outputs))
sourceErr error
)
for _, output := range outputs {
output := output
outputAmount, err := btcutil.NewAmount(output.Amount)
if err != nil {
sourceErr = fmt.Errorf(
"invalid amount `%v` in listunspent result",
output.Amount)
break
}
if outputAmount == 0 {
continue
}
if !saneOutputValue(outputAmount) {
sourceErr = fmt.Errorf(
"impossible output amount `%v` in listunspent result",
outputAmount)
break
}
totalInputValue += outputAmount
previousOutPoint, err := parseOutPoint(&output)
if err != nil {
sourceErr = fmt.Errorf(
"invalid data in listunspent result: %v",
err)
break
}
inputs = append(inputs, wire.NewTxIn(&previousOutPoint, nil, nil))
inputValues = append(inputValues, outputAmount)
}
if sourceErr == nil && totalInputValue == 0 {
sourceErr = noInputValue{}
}
return func(btcutil.Amount) (btcutil.Amount, []*wire.TxIn, []btcutil.Amount, [][]byte, error) {
return totalInputValue, inputs, inputValues, nil, sourceErr
}
}
// makeDestinationScriptSource creates a ChangeSource which is used to receive
// all correlated previous input value. A non-change address is created by this
// function.
func makeDestinationScriptSource(rpcClient *rpcclient.Client, accountName string) *txauthor.ChangeSource {
// GetNewAddress always returns a P2PKH address since it assumes
// BIP-0044.
newChangeScript := func() ([]byte, error) {
destinationAddress, err := rpcClient.GetNewAddress(accountName)
if err != nil {
return nil, err
}
return txscript.PayToAddrScript(destinationAddress)
}
return &txauthor.ChangeSource{
ScriptSize: txsizes.P2PKHPkScriptSize,
NewScript: newChangeScript,
}
}
func main() {
err := sweep()
if err != nil {
fatalf("%v", err)
}
}
func sweep() error {
rpcPassword, err := promptSecret("Wallet RPC password")
if err != nil {
return errContext(err, "failed to read RPC password")
}
// Open RPC client.
rpcCertificate, err := ioutil.ReadFile(opts.RPCCertificateFile)
if err != nil {
return errContext(err, "failed to read RPC certificate")
}
rpcClient, err := rpcclient.New(&rpcclient.ConnConfig{
Host: opts.RPCConnect,
User: opts.RPCUsername,
Pass: rpcPassword,
Certificates: rpcCertificate,
HTTPPostMode: true,
}, nil)
if err != nil {
return errContext(err, "failed to create RPC client")
}
defer rpcClient.Shutdown()
// Fetch all unspent outputs, ignore those not from the source
// account, and group by their destination address. Each grouping of
// outputs will be used as inputs for a single transaction sending to a
// new destination account address.
unspentOutputs, err := rpcClient.ListUnspent()
if err != nil {
return errContext(err, "failed to fetch unspent outputs")
}
sourceOutputs := make(map[string][]btcjson.ListUnspentResult)
for _, unspentOutput := range unspentOutputs {
if !unspentOutput.Spendable {
continue
}
if unspentOutput.Confirmations < opts.RequiredConfirmations {
continue
}
if unspentOutput.Account != opts.SourceAccount {
continue
}
sourceAddressOutputs := sourceOutputs[unspentOutput.Address]
sourceOutputs[unspentOutput.Address] = append(sourceAddressOutputs, unspentOutput)
}
var privatePassphrase string
if len(sourceOutputs) != 0 {
privatePassphrase, err = promptSecret("Wallet private passphrase")
if err != nil {
return errContext(err, "failed to read private passphrase")
}
}
var totalSwept btcutil.Amount
var numErrors int
var reportError = func(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, format, args...)
os.Stderr.Write(newlineBytes)
numErrors++
}
for _, previousOutputs := range sourceOutputs {
inputSource := makeInputSource(previousOutputs)
destinationSource := makeDestinationScriptSource(rpcClient, opts.DestinationAccount)
tx, err := txauthor.NewUnsignedTransaction(nil, opts.FeeRate.Amount,
inputSource, destinationSource)
if err != nil {
if err != (noInputValue{}) {
reportError("Failed to create unsigned transaction: %v", err)
}
continue
}
// Unlock the wallet, sign the transaction, and immediately lock.
err = rpcClient.WalletPassphrase(privatePassphrase, 60)
if err != nil {
reportError("Failed to unlock wallet: %v", err)
continue
}
signedTransaction, complete, err := rpcClient.SignRawTransaction(tx.Tx)
_ = rpcClient.WalletLock()
if err != nil {
reportError("Failed to sign transaction: %v", err)
continue
}
if !complete {
reportError("Failed to sign every input")
continue
}
// Publish the signed sweep transaction.
txHash, err := rpcClient.SendRawTransaction(signedTransaction, false)
if err != nil {
reportError("Failed to publish transaction: %v", err)
continue
}
outputAmount := btcutil.Amount(tx.Tx.TxOut[0].Value)
fmt.Printf("Swept %v to destination account with transaction %v\n",
outputAmount, txHash)
totalSwept += outputAmount
}
numPublished := len(sourceOutputs) - numErrors
transactionNoun := pickNoun(numErrors, "transaction", "transactions")
if numPublished != 0 {
fmt.Printf("Swept %v to destination account across %d %s\n",
totalSwept, numPublished, transactionNoun)
}
if numErrors > 0 {
return fmt.Errorf("failed to publish %d %s", numErrors,
transactionNoun)
}
return nil
}
func promptSecret(what string) (string, error) {
fmt.Printf("%s: ", what)
fd := int(os.Stdin.Fd())
input, err := terminal.ReadPassword(fd)
fmt.Println()
if err != nil {
return "", err
}
return string(input), nil
}
func saneOutputValue(amount btcutil.Amount) bool {
return amount >= 0 && amount <= btcutil.MaxSatoshi
}
func parseOutPoint(input *btcjson.ListUnspentResult) (wire.OutPoint, error) {
txHash, err := chainhash.NewHashFromStr(input.TxID)
if err != nil {
return wire.OutPoint{}, err
}
return wire.OutPoint{Hash: *txHash, Index: input.Vout}, nil
}
func pickNoun(n int, singularForm, pluralForm string) string {
if n == 1 {
return singularForm
}
return pluralForm
}

171
config.go
View file

@ -5,7 +5,6 @@
package main
import (
"encoding/hex"
"fmt"
"net"
"os"
@ -17,7 +16,6 @@ import (
"time"
flags "github.com/jessevdk/go-flags"
"github.com/lbryio/lbcd/chaincfg"
"github.com/lbryio/lbcd/version"
btcutil "github.com/lbryio/lbcutil"
"github.com/lbryio/lbcwallet/internal/cfgutil"
@ -33,6 +31,7 @@ const (
defaultLogFilename = "lbcwallet.log"
defaultRPCMaxClients = 10
defaultRPCMaxWebsockets = 25
defaultPassphrase = "password"
)
var (
@ -46,68 +45,40 @@ var (
type config struct {
// General application behavior
ConfigFile *cfgutil.ExplicitString `short:"C" long:"configfile" description:"Path to configuration file"`
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
Create bool `long:"create" description:"Create the wallet if it does not exist"`
CreateTemp bool `long:"createtemp" description:"Create a temporary simulation wallet (pass=password) in the data directory indicated; must call with --datadir"`
AppDataDir *cfgutil.ExplicitString `short:"A" long:"appdata" description:"Application data directory for wallet config, databases and logs"`
TestNet3 bool `long:"testnet" description:"Use the test Bitcoin network (version 3) (default client port: 19244, server port: 19245)"`
Regtest bool `long:"regtest" description:"Use the regression test network (default client port: 29244, server port: 29245)"`
SimNet bool `long:"simnet" description:"Use the simulation test network (default client port: 39244, server port: 39245)"`
SigNet bool `long:"signet" description:"Use the signet test network (default client port: 49244, server port: 49245)"`
SigNetChallenge string `long:"signetchallenge" description:"Connect to a custom signet network defined by this challenge instead of using the global default signet test network -- Can be specified multiple times"`
SigNetSeedNode []string `long:"signetseednode" description:"Specify a seed node for the signet network instead of using the global default signet network seed nodes"`
NoInitialLoad bool `long:"noinitialload" description:"Defer wallet creation/opening on startup and enable loading wallets over RPC"`
DebugLevel string `short:"d" long:"debuglevel" description:"Logging level {trace, debug, info, warn, error, critical}"`
LogDir string `long:"logdir" description:"Directory to log output."`
Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"`
DBTimeout time.Duration `long:"dbtimeout" description:"The timeout value to use when opening the wallet database."`
ConfigFile *cfgutil.ExplicitString `short:"C" long:"configfile" description:"Path to configuration file"`
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
Create bool `long:"create" description:"Create the wallet if it does not exist"`
CreateTemp bool `long:"createtemp" description:"Create a temporary simulation wallet (pass=password) in the data directory indicated; must call with --datadir"`
AppDataDir *cfgutil.ExplicitString `short:"A" long:"appdata" description:"Application data directory for wallet config, databases and logs"`
TestNet3 bool `long:"testnet" description:"Use the test Bitcoin network (version 3) (default client port: 19244, server port: 19245)"`
Regtest bool `long:"regtest" description:"Use the regression test network (default client port: 29244, server port: 29245)"`
DebugLevel string `short:"d" long:"debuglevel" description:"Logging level {trace, debug, info, warn, error, critical}"`
LogDir string `long:"logdir" description:"Directory to log output."`
Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"`
DBTimeout time.Duration `long:"dbtimeout" description:"The timeout value to use when opening the wallet database."`
// Wallet options
WalletPass string `long:"walletpass" default-mask:"-" description:"The public wallet password -- Only required if the wallet was created with one"`
// Passphrase options
Passphrase string `short:"p" long:"passphrase" default-mask:"-" description:"The wallet passphrase (default: \"passphrase\")"`
// RPC client options
RPCConnect string `short:"c" long:"rpcconnect" description:"Hostname/IP and port of lbcd RPC server to connect to (default localhost:9245, testnet: localhost:19245, regtest: localhost:29245 simnet: localhost:39245)"`
RPCConnect string `short:"c" long:"rpcconnect" description:"Hostname/IP and port of lbcd RPC server to connect to (default localhost:9245, testnet: localhost:19245, regtest: localhost:29245)"`
CAFile *cfgutil.ExplicitString `long:"cafile" description:"File containing root certificates to authenticate a TLS connections with lbcd"`
DisableClientTLS bool `long:"noclienttls" description:"Disable TLS for the RPC client"`
SkipVerify bool `long:"skipverify" description:"Skip verifying TLS for the RPC client"`
LbcdUsername string `long:"lbcdusername" description:"Username for lbcd authentication"`
LbcdPassword string `long:"lbcdpassword" default-mask:"-" description:"Password for lbcd authentication"`
Proxy string `long:"proxy" description:"Connect via SOCKS5 proxy (eg. 127.0.0.1:9050)"`
ProxyUser string `long:"proxyuser" description:"Username for proxy server"`
ProxyPass string `long:"proxypass" default-mask:"-" description:"Password for proxy server"`
// SPV client options
UseSPV bool `long:"usespv" description:"Enables the experimental use of SPV rather than RPC for chain synchronization"`
AddPeers []string `short:"a" long:"addpeer" description:"Add a peer to connect with at startup"`
ConnectPeers []string `long:"connect" description:"Connect only to the specified peers at startup"`
MaxPeers int `long:"maxpeers" description:"Max number of inbound and outbound peers"`
BanDuration time.Duration `long:"banduration" description:"How long to ban misbehaving peers. Valid time units are {s, m, h}. Minimum 1 second"`
BanThreshold uint32 `long:"banthreshold" description:"Maximum allowed ban score before disconnecting and banning misbehaving peers."`
// RPC server options
//
// The legacy server is still enabled by default (and eventually will be
// replaced with the experimental server) so prepare for that change by
// renaming the struct fields (but not the configuration options).
//
// Usernames can also be used for the consensus RPC client, so they
// aren't considered legacy.
RPCCert *cfgutil.ExplicitString `long:"rpccert" description:"File containing the certificate file"`
RPCKey *cfgutil.ExplicitString `long:"rpckey" description:"File containing the certificate key"`
OneTimeTLSKey bool `long:"onetimetlskey" description:"Generate a new TLS certpair at startup, but only write the certificate to disk"`
DisableServerTLS bool `long:"noservertls" description:"Disable TLS for the RPC server"`
LegacyRPCListeners []string `long:"rpclisten" description:"Listen for legacy RPC connections on this interface/port (default port: 9244, testnet: 19244, regtest: 29244, simnet: 29244)"`
LegacyRPCListeners []string `long:"rpclisten" description:"Listen for legacy RPC connections on this interface/port (default port: 9244, testnet: 19244, regtest: 29244)"`
LegacyRPCMaxClients int64 `long:"rpcmaxclients" description:"Max number of legacy RPC clients for standard connections"`
LegacyRPCMaxWebsockets int64 `long:"rpcmaxwebsockets" description:"Max number of legacy RPC websocket connections"`
Username string `short:"u" long:"username" description:"Username for legacy RPC and lbcd authentication (if lbcdusername is unset)"`
Password string `short:"P" long:"password" default-mask:"-" description:"Password for legacy RPC and lbcd authentication (if lbcdpassword is unset)"`
// EXPERIMENTAL RPC server options
//
// These options will change (and require changes to config files, etc.)
// when the new gRPC server is enabled.
ExperimentalRPCListeners []string `long:"experimentalrpclisten" description:"Listen for RPC connections on this interface/port"`
LegacyRPCMaxWebsockets int64 `long:"rpcmaxwebsockets" description:"Max number of RPC websocket connections"`
RPCUser string `short:"u" long:"rpcuser" description:"Username for RPC and lbcd authentication"`
RPCPass string `short:"P" long:"rpcpass" default-mask:"-" description:"Password for RPC and lbcd authentication"`
// Deprecated options
DataDir *cfgutil.ExplicitString `short:"b" long:"datadir" default-mask:"-" description:"DEPRECATED -- use appdata instead"`
@ -265,17 +236,14 @@ func loadConfig() (*config, []string, error) {
ConfigFile: cfgutil.NewExplicitString(defaultConfigFile),
AppDataDir: cfgutil.NewExplicitString(defaultAppDataDir),
LogDir: defaultLogDir,
WalletPass: wallet.InsecurePubPassphrase,
CAFile: cfgutil.NewExplicitString(""),
RPCKey: cfgutil.NewExplicitString(defaultRPCKeyFile),
RPCCert: cfgutil.NewExplicitString(defaultRPCCertFile),
LegacyRPCMaxClients: defaultRPCMaxClients,
LegacyRPCMaxWebsockets: defaultRPCMaxWebsockets,
DataDir: cfgutil.NewExplicitString(defaultAppDataDir),
UseSPV: false,
AddPeers: []string{},
ConnectPeers: []string{},
DBTimeout: wallet.DefaultDBTimeout,
Passphrase: defaultPassphrase,
}
// Pre-parse the command line options to see if an alternative config
@ -368,52 +336,8 @@ func loadConfig() (*config, []string, error) {
activeNet = &netparams.RegTestParams
numNets++
}
if cfg.SimNet {
activeNet = &netparams.SimNetParams
numNets++
}
if cfg.SigNet {
activeNet = &netparams.SigNetParams
numNets++
// Let the user overwrite the default signet parameters. The
// challenge defines the actual signet network to join and the
// seed nodes are needed for network discovery.
sigNetChallenge := chaincfg.DefaultSignetChallenge
sigNetSeeds := chaincfg.DefaultSignetDNSSeeds
if cfg.SigNetChallenge != "" {
challenge, err := hex.DecodeString(cfg.SigNetChallenge)
if err != nil {
str := "%s: Invalid signet challenge, hex " +
"decode failed: %v"
err := fmt.Errorf(str, funcName, err)
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr, usageMessage)
return nil, nil, err
}
sigNetChallenge = challenge
}
if len(cfg.SigNetSeedNode) > 0 {
sigNetSeeds = make(
[]chaincfg.DNSSeed, len(cfg.SigNetSeedNode),
)
for idx, seed := range cfg.SigNetSeedNode {
sigNetSeeds[idx] = chaincfg.DNSSeed{
Host: seed,
HasFiltering: false,
}
}
}
chainParams := chaincfg.CustomSignetParams(
sigNetChallenge, sigNetSeeds,
)
activeNet.Params = &chainParams
}
if numNets > 1 {
str := "%s: The testnet, signet and simnet params can't be " +
"used together -- choose one"
str := "%s: more than one networks has been specified"
err := fmt.Errorf(str, "loadConfig")
fmt.Fprintln(os.Stderr, err)
parser.WriteHelp(os.Stderr)
@ -454,9 +378,9 @@ func loadConfig() (*config, []string, error) {
}
// Exit if you try to use a simulation wallet on anything other than
// simnet, regtest or testnet3.
if !(cfg.Regtest || cfg.SimNet || cfg.TestNet3) {
errMsg += "for network other than simnet, regtest, or testnet3"
// regtest or testnet3.
if !(cfg.Regtest || cfg.TestNet3) {
errMsg += "for network other than regtest, or testnet3"
fmt.Fprintln(os.Stderr, errMsg)
os.Exit(0)
}
@ -579,12 +503,7 @@ func loadConfig() (*config, []string, error) {
}
}
// Only set default RPC listeners when there are no listeners set for
// the experimental RPC server. This is required to prevent the old RPC
// server from sharing listen addresses, since it is impossible to
// remove defaults from go-flags slice options without assigning
// specific behavior to a particular string.
if len(cfg.ExperimentalRPCListeners) == 0 && len(cfg.LegacyRPCListeners) == 0 {
if len(cfg.LegacyRPCListeners) == 0 {
addrs, err := net.LookupHost("localhost")
if err != nil {
return nil, nil, err
@ -605,36 +524,9 @@ func loadConfig() (*config, []string, error) {
"Invalid network address in legacy RPC listeners: %v\n", err)
return nil, nil, err
}
cfg.ExperimentalRPCListeners, err = cfgutil.NormalizeAddresses(
cfg.ExperimentalRPCListeners, activeNet.RPCServerPort)
if err != nil {
fmt.Fprintf(os.Stderr,
"Invalid network address in RPC listeners: %v\n", err)
return nil, nil, err
}
// Both RPC servers may not listen on the same interface/port.
if len(cfg.LegacyRPCListeners) > 0 && len(cfg.ExperimentalRPCListeners) > 0 {
seenAddresses := make(map[string]struct{}, len(cfg.LegacyRPCListeners))
for _, addr := range cfg.LegacyRPCListeners {
seenAddresses[addr] = struct{}{}
}
for _, addr := range cfg.ExperimentalRPCListeners {
_, seen := seenAddresses[addr]
if seen {
err := fmt.Errorf("address `%s` may not be "+
"used as a listener address for both "+
"RPC servers", addr)
fmt.Fprintln(os.Stderr, err)
return nil, nil, err
}
}
}
if cfg.DisableServerTLS {
allListeners := append(cfg.LegacyRPCListeners,
cfg.ExperimentalRPCListeners...)
for _, addr := range allListeners {
for _, addr := range cfg.LegacyRPCListeners {
_, _, err := net.SplitHostPort(addr)
if err != nil {
str := "%s: RPC listen interface '%s' is " +
@ -652,17 +544,6 @@ func loadConfig() (*config, []string, error) {
cfg.RPCCert.Value = cleanAndExpandPath(cfg.RPCCert.Value)
cfg.RPCKey.Value = cleanAndExpandPath(cfg.RPCKey.Value)
// If the lbcd username or password are unset, use the same auth as for
// the client. The two settings were previously shared for lbcd and
// client auth, so this avoids breaking backwards compatibility while
// allowing users to use different auth settings for lbcd and wallet.
if cfg.LbcdUsername == "" {
cfg.LbcdUsername = cfg.Username
}
if cfg.LbcdPassword == "" {
cfg.LbcdPassword = cfg.Password
}
// Warn about missing config file after the final command line parse
// succeeds. This prevents the warning on help messages and invalid
// options.

View file

@ -1,28 +0,0 @@
Dependency Commits
==================
lbcwallet 0.7.0 Alpha
----------------
bolt 1139dd23c5f9d1a28096b511959044b29364a3fe
cea5d3c1cc16eb07a1686c557db23004655ae11b
btclog 5005b7240f310ae8f01c7664a3954d280241eb2b
btcrpcclient b81555beeac8eda71e8150cc9d63631aaa756965
btcutil ff82dacded1c76d101bce55c394c03c0bbff69e8
fastsha256 302ad4db268b46f9ebda3078f6f7397f96047735
go-flags 6c288d648c1cc1befcb90cb5511dcacf64ae8e61
go-socks cfe8b59e565c1a5bd4e2005d77cd9aa8b2e14524
golangcrypto 53f62d9b43e87a6c56975cf862af7edf33a8d0df
seelog 313961b101eb55f65ae0f03ddd4e322731763b6c
websocket 31079b6807923eb23992c421b114992b95131b55
lbcwallet 0.1.0 Alpha
----------------
3108b944017b14a3c5863ed1401f1a2471907d84
btcec a97fd5fe2c670030f8d77dc13b9fa8401ef9f349
btcjson d20f958c92e1444d83215c3cf98d6eef41898dcb
btcscript f4a6449ad3b90d0c830bf2895b83ced8d5fb91e9
btcutil aa811871654079f5036d3692dcf6c66928d19447
btcwire dd41f7e91a682b7c1ceed633e12ece6ba7b6bc72
btcws 497f1770445677372557d70621782d921a5318e3
go-flags fa177a84d3b73bf7e4b79125b2a963bc134eff77
seelog 6b91ad56123bb473755caa213db2bde5422177bf

View file

@ -1,3 +0,0 @@
### Guides
[Rebuilding all transaction history with forced rescans](https://github.com/lbryio/lbcwallet/tree/master/docs/force_rescans.md)

View file

@ -1,80 +0,0 @@
# Rebuilding transaction history
It is unlikely, but possible and unfortunate, that transaction history in the
wallet database may not represent reality. This may be due to a programming
mistake or the transaction database becoming corrupted. Thankfully, all
transactions are publicly recorded on the blockchain, and transactions
necessary for a fully functional wallet can be recovered. This process is
called rescanning, and the following guide will demonstrate how to force such a
rescan.
Rescans are automatically performed each time the wallet syncs to the network.
These are used to "catch up" the wallet to the newest best block in the block
chain. For example, the following log messages at startup indicate that an
out-of-sync wallet started a rescan for all addresses and unspent outputs since
some block.
```
13:45:03 2015-04-13 [INF] WLLT: Started rescan from block 00000000001703b1a9dfd4865d587cd3f3cbb2f8e6ce9b44668e78ad8d4a7377 (height 205921) for 1 address
...
13:45:49 2015-04-13 [INF] WLLT: Finished rescan for 1 address (synced to block 0000000005cecab1013ecb1275a3e0c9623c4a497a57b6b6bf0fc1525aca1fbf, height 335146)
```
During the rescan, relevant transactions from previously unseen blocks are added
to the wallet database and spend tracking is updated accordingly. After the
rescan at startup finishes, a wallet is marked in sync with the chain.
When wallet is started without any transaction history, a rescan is performed
for all blocks since the creation date of the wallet's first address. There are
two situations when this holds true:
1. The wallet is newly created or was recreated from the seed
2. The transaction history is explicitly deleted
The second case is how a forced rescan is performed.
lbcwallet will not drop transaction history by itself, as this is something that
should not be necessary under normal wallet operation. However, a tool,
`dropwtxmgr`, is provided in the `cmd/dropwtxmgr` directory which may be used to
drop the wallet transaction manager (wtxmgr) history from a wallet database.
The tool may already be installed in your PATH, but if not, installing it is easy:
```
$ cd $GOPATH/src/github.com/lbryio/lbcwallet/cmd/dropwtxmgr
$ go get
```
Dropping transaction history given the default database location can be
performed by stopping wallet (to release the database) and running the tool,
answering yes to the prompt:
```
$ dropwtxmgr
Database path: /home/username/.lbcwallet/mainnet/wallet.db
Drop all lbcwallet transaction history? [y/N] y
Dropping wtxmgr namespace
```
If the wallet database is in another location or transaction history for a
different network (e.g. testnet or simnet) must be dropped, the full database
path may be specified:
```
$ dropwtxmgr --db ~/.lbcwallet/testnet/wallet.db
Database path: /home/username/.lbcwallet/testnet/wallet.db
Drop all lbcwallet transaction history? [y/N] y
Dropping wtxmgr namespace
```
After dropping transaction history, lbcwallet may be restarted and a full rescan
will be triggered to sync the wallet:
```
$ lbcwallet
14:05:31 2015-04-13 [INF] BTCW: No recorded transaction history -- needs full rescan
...
14:05:31 2015-04-13 [INF] WLLT: Started rescan from block 000000000000e37b0f99af2e434834123b5459e31e17937169ce81ed0cc4d61c (height 193191) for 1 address
...
14:07:06 2015-04-13 [INF] WLLT: Finished rescan for 1 address (synced to block 00000000049041b5bd7f8ac86c8f1d32065053aefbe8c31e25ed03ef015a725a, height 335482)
```

10
go.mod
View file

@ -4,18 +4,15 @@ require (
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792
github.com/davecgh/go-spew v1.1.1
github.com/golang/protobuf v1.5.2
github.com/jessevdk/go-flags v1.5.0
github.com/jrick/logrotate v1.0.0
github.com/lbryio/lbcd v0.22.102
github.com/lbryio/lbcutil v1.0.202-rc3
github.com/lbryio/lbcd v0.22.118
github.com/lbryio/lbcutil v1.0.202
github.com/lightningnetwork/lnd/clock v1.1.0
github.com/stretchr/testify v1.7.1
go.etcd.io/bbolt v1.3.6
golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2
golang.org/x/tools v0.1.10
google.golang.org/grpc v1.46.2
)
require (
@ -43,10 +40,7 @@ require (
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/genproto v0.0.0-20220524023933-508584e28198 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

43
go.sum
View file

@ -1,5 +1,4 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@ -17,7 +16,6 @@ github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqR
github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
@ -33,10 +31,6 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=
github.com/cockroachdb/datadriven v1.0.1-0.20211007161720-b558070c3be0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=
github.com/cockroachdb/datadriven v1.0.1-0.20220214170620-9913f5bc19b7/go.mod h1:hi0MtSY3AYDQNDi83kDkMH5/yqM/CsIrsOITkSoH7KI=
@ -74,9 +68,7 @@ github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZi
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
@ -89,7 +81,6 @@ github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TB
github.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NWD+xWo=
github.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA7//UooKNumH0=
github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
@ -121,9 +112,7 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
@ -135,7 +124,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
@ -144,7 +132,6 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@ -200,10 +187,10 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/lbryio/lbcd v0.22.102 h1:UaJ0Tpurf6y5ROqTRLXR0e1RxzdWEPRkCt6XOoRk7Mc=
github.com/lbryio/lbcd v0.22.102/go.mod h1:iwOnc+syXOeMsAqjHHjMWktni+cg28xOXIY/lN+YIWE=
github.com/lbryio/lbcutil v1.0.202-rc3 h1:J7zYnIj3iN/ndPYKqMKBukLaLM1GhCEaiaMOYIMdUCU=
github.com/lbryio/lbcutil v1.0.202-rc3/go.mod h1:LGPtVBBzh4cFXfLFb8ginlFcbA2QwumLNFd0yk/as2o=
github.com/lbryio/lbcd v0.22.118 h1:q3HAwCKdINJE2Tj5FrjmSfltSuiqSB5gnuSDAAQVt8A=
github.com/lbryio/lbcd v0.22.118/go.mod h1:YZ2Vi4khEheO7hllkWhDdScXmHhXCBzK4xIQcVDcozs=
github.com/lbryio/lbcutil v1.0.202 h1:L0aRMs2bdCUAicD8Xe4NmUEvevDDea3qkIpCSACnftI=
github.com/lbryio/lbcutil v1.0.202/go.mod h1:LGPtVBBzh4cFXfLFb8ginlFcbA2QwumLNFd0yk/as2o=
github.com/lightningnetwork/lnd/clock v1.1.0 h1:/yfVAwtPmdx45aQBoXQImeY7sOIEr7IXlImRMBOZ7GQ=
github.com/lightningnetwork/lnd/clock v1.1.0/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@ -247,7 +234,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
@ -298,7 +284,6 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -332,7 +317,6 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -343,18 +327,13 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y=
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -380,7 +359,6 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -402,7 +380,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -432,23 +409,15 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20220524023933-508584e28198 h1:a1g7i05I2vUwq5eYrmxBJy6rPbw/yo7WzzwPJmcC0P4=
google.golang.org/genproto v0.0.0-20220524023933-508584e28198/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ=
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -460,9 +429,6 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
@ -475,7 +441,6 @@ gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -21,14 +21,7 @@ import (
"golang.org/x/crypto/ssh/terminal"
)
// ProvideSeed is used to prompt for the wallet seed which maybe required during
// upgrades.
func ProvideSeed() func() ([]byte, error) {
return func() ([]byte, error) {
return provideSeed(bufio.NewReader(os.Stdin))
}
}
func provideSeed(reader *bufio.Reader) ([]byte, error) {
func promptSeed(reader *bufio.Reader) ([]byte, error) {
for {
fmt.Print("Enter existing wallet seed: ")
seedStr, err := reader.ReadString('\n')
@ -52,26 +45,6 @@ func provideSeed(reader *bufio.Reader) ([]byte, error) {
}
}
// ProvidePrivPassphrase is used to prompt for the private passphrase which
// maybe required during upgrades.
func ProvidePrivPassphrase() ([]byte, error) {
prompt := "Enter the private passphrase of your wallet: "
for {
fmt.Print(prompt)
pass, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return nil, err
}
fmt.Print("\n")
pass = bytes.TrimSpace(pass)
if len(pass) == 0 {
continue
}
return pass, nil
}
}
// promptList prompts the user with the given prefix, list of valid responses,
// and default list entry to use. The function will repeat the prompt to the
// user until they enter a valid response.
@ -147,10 +120,15 @@ func promptUnixTimestamp(reader *bufio.Reader, prefix string,
}
}
// promptPass prompts the user for a passphrase with the given prefix. The
// function will ask the user to confirm the passphrase and will repeat the
// prompts until they enter a matching response.
func promptPass(_ *bufio.Reader, prefix string, confirm bool) ([]byte, error) {
// promptPassphrase prompts the user for a passphrase with the given prefix.
// The function will ask the user to confirm the passphrase and will repeat
// the prompts until they enter a matching response.
func promptPassphrase(prefix string, confirm bool) ([]byte, error) {
pass := os.Getenv("LBCWALLET_PASSPHRASE")
if len(pass) > 0 {
return []byte(pass), nil
}
// Prompt the user until they enter a passphrase.
prompt := fmt.Sprintf("%s: ", prefix)
for {
@ -191,56 +169,11 @@ func birthday(reader *bufio.Reader) (time.Time, error) {
return promptUnixTimestamp(reader, prompt, "0")
}
// PrivatePass prompts the user for a private passphrase. The user is prompted
// for a new private passphrase. All prompts are repeated until the user
// enters a valid response.
func PrivatePass(reader *bufio.Reader) ([]byte, error) {
return promptPass(reader, "Enter the private "+
"passphrase for your new wallet", true)
}
// PublicPass prompts the user whether they want to add an additional layer of
// encryption to the wallet. When the user answers yes and there is already a
// public passphrase provided via the passed config, it prompts them whether or
// not to use that configured passphrase. It will also detect when the same
// passphrase is used for the private and public passphrase and prompt the user
// if they are sure they want to use the same passphrase for both. Finally, all
// prompts are repeated until the user enters a valid response.
func PublicPass(reader *bufio.Reader, privPass []byte,
defaultPubPassphrase, configPubPassphrase []byte) ([]byte, error) {
pubPass := defaultPubPassphrase
usePubPass, err := promptListBool(reader, "Do you want "+
"to add an additional layer of encryption for public "+
"data?", "no")
if err != nil {
return nil, err
}
if !usePubPass {
return pubPass, nil
}
if !bytes.Equal(configPubPassphrase, pubPass) {
useExisting, err := promptListBool(reader, "Use the "+
"existing configured public passphrase for encryption "+
"of public data?", "no")
if err != nil {
return nil, err
}
if useExisting {
return configPubPassphrase, nil
}
}
pubPass, err = provideSeed(reader)
if err != nil {
return nil, err
}
fmt.Println("NOTE: Use the --walletpass option to configure your " +
"public passphrase.")
return pubPass, nil
// Passphrase prompts the user for a passphrase.
// All prompts are repeated until the user enters a valid response.
func Passphrase(confirm bool) ([]byte, error) {
return promptPassphrase("Enter the passphrase "+
"for your new wallet", confirm)
}
// Seed prompts the user whether they want to use an existing wallet generation
@ -284,7 +217,7 @@ func Seed(reader *bufio.Reader) ([]byte, time.Time, error) {
return seed, bday, nil
}
seed, err := provideSeed(reader)
seed, err := promptSeed(reader)
if err != nil {
return nil, bday, err
}

View file

@ -1,30 +0,0 @@
// Copyright (c) 2015-2021 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package prompt
import (
"bufio"
"fmt"
)
func ProvideSeed() ([]byte, error) {
return nil, fmt.Errorf("prompt not supported in WebAssembly")
}
func ProvidePrivPassphrase() ([]byte, error) {
return nil, fmt.Errorf("prompt not supported in WebAssembly")
}
func PrivatePass(_ *bufio.Reader) ([]byte, error) {
return nil, fmt.Errorf("prompt not supported in WebAssembly")
}
func PublicPass(_ *bufio.Reader, _, _, _ []byte) ([]byte, error) {
return nil, fmt.Errorf("prompt not supported in WebAssembly")
}
func Seed(_ *bufio.Reader) ([]byte, error) {
return nil, fmt.Errorf("prompt not supported in WebAssembly")
}

View file

@ -10,79 +10,82 @@ package rpchelp
var helpDescsEnUS = map[string]string{
// AddMultisigAddressCmd help.
"addmultisigaddress--synopsis": "Generates and imports a multisig address and redeeming script to the 'imported' account.",
"addmultisigaddress-account": "DEPRECATED -- Unused (all imported addresses belong to the imported account)",
"addmultisigaddress-keys": "Pubkeys and/or pay-to-pubkey-hash addresses to partially control the multisig address",
"addmultisigaddress-nrequired": "The number of signatures required to redeem outputs paid to this address",
"addmultisigaddress--result0": "The imported pay-to-script-hash address",
"addmultisigaddress-account": "DEPRECATED -- Unused (all imported addresses belong to the imported account).",
"addmultisigaddress-keys": "Pubkeys and/or pay-to-pubkey-hash addresses to partially control the multisig address.",
"addmultisigaddress-nrequired": "The number of signatures required to redeem outputs paid to this address.",
"addmultisigaddress--result0": "The imported pay-to-script-hash address.",
// CreateMultisigCmd help.
"createmultisig--synopsis": "Generate a multisig address and redeem script.",
"createmultisig-keys": "Pubkeys and/or pay-to-pubkey-hash addresses to partially control the multisig address",
"createmultisig-nrequired": "The number of signatures required to redeem outputs paid to this address",
"createmultisig-keys": "Pubkeys and/or pay-to-pubkey-hash addresses to partially control the multisig address.",
"createmultisig-nrequired": "The number of signatures required to redeem outputs paid to this address.",
// CreateMultisigResult help.
"createmultisigresult-address": "The generated pay-to-script-hash address",
"createmultisigresult-redeemScript": "The script required to redeem outputs paid to the multisig address",
"createmultisigresult-address": "The generated pay-to-script-hash address.",
"createmultisigresult-redeemScript": "The script required to redeem outputs paid to the multisig address.",
// CreateNewAccountCmd help.
"createnewaccount--synopsis": "Creates a new account.",
"createnewaccount-account": "Account name.",
// DumpPrivKeyCmd help.
"dumpprivkey--synopsis": "Returns the private key in WIF encoding that controls some wallet address.",
"dumpprivkey-address": "The address to return a private key for",
"dumpprivkey--result0": "The WIF-encoded private key",
"dumpprivkey-address": "The address to return a private key for.",
"dumpprivkey--result0": "The WIF-encoded private key.",
// ExportWatchingWalletCmd help.
"exportwatchingwallet--synopsis": "Creates and returns a duplicate of the wallet database without any private keys to be used as a watching-only wallet.",
"exportwatchingwallet-account": "Unused (must be unset or \"*\").",
"exportwatchingwallet-download": "Unused.",
"exportwatchingwallet--result0": "The watching-only database encoded as a base64 string.",
// GetAccountCmd help.
"getaccount--synopsis": "DEPRECATED -- Lookup the account name that some wallet address belongs to.",
"getaccount-address": "The address to query the account for",
"getaccount--result0": "The name of the account that 'address' belongs to",
"getaccount--synopsis": "Lookup the account name that some wallet address belongs to.",
"getaccount-address": "The address to query the account for.",
"getaccount--result0": "The name of the account that 'address' belongs to.",
// GetAccountAddressCmd help.
"getaccountaddress--synopsis": "DEPRECATED -- Returns the most recent external payment address for an account that has not been seen publicly.\n" +
"getaccountaddress--synopsis": "Returns the most recent external payment address for an account that has not been seen publicly.\n" +
"A new address is generated for the account if the most recently generated address has been seen on the blockchain or in mempool.",
"getaccountaddress-account": "The account of the returned address",
"getaccountaddress--result0": "The unused address for 'account'",
"getaccountaddress-account": "The account of the returned address. Defaults to 'default'",
"getaccountaddress-addresstype": "Address type. Must be one of 'legacy', 'p2sh-segwit', or 'bech32'. Default to 'legacy'.",
"getaccountaddress--result0": "The unused address for 'account'.",
// GetAddressesByAccountCmd help.
"getaddressesbyaccount--synopsis": "DEPRECATED -- Returns all addresses strings controlled by a single account.",
"getaddressesbyaccount-account": "Account name to fetch addresses for",
"getaddressesbyaccount--result0": "All addresses controlled by 'account'",
"getaddressesbyaccount--synopsis": "Returns all addresses controlled by a single account.",
"getaddressesbyaccount-account": "Account name to fetch addresses for. Defaults to 'default'",
"getaddressesbyaccount-addresstype": "Address type filter. Must be one of 'legacy', 'p2sh-segwit', 'bech32', or '*'. Defaults to '*'.",
"getaddressesbyaccount--result0": "All addresses controlled by 'account' filtered by 'addresstype'.",
// GetBalanceCmd help.
"getbalance--synopsis": "Calculates and returns the balance of one or all accounts.",
"getbalance-minconf": "Minimum number of block confirmations required before an unspent output's value is included in the balance",
"getbalance-account": "DEPRECATED -- The account name to query the balance for, or \"*\" to consider all accounts (default=\"*\")",
"getbalance--condition0": "account != \"*\"",
"getbalance--condition1": "account = \"*\"",
"getbalance--result0": "The balance of 'account' valued in bitcoin",
"getbalance--result1": "The balance of all accounts valued in bitcoin",
"getbalance-minconf": "Minimum number of block confirmations required before an unspent output's value is included in the balance.",
"getbalance-account": "Account name or '*' for all accounts to query the balance for. Default to 'default'.",
"getbalance-addresstype": "Address type filter. Must be one of 'legacy', 'p2sh-segwit', 'bech32', or '*'. Default to '*'.",
"getbalance--result0": "The balance valued in LBC.",
// GetBestBlockCmd help.
"getbestblock--synopsis": "Returns the hash and height of the newest block in the best chain that wallet has finished syncing with.",
// GetBestBlockResult help.
"getbestblockresult-hash": "The hash of the block.",
"getbestblockresult-height": "The blockchain height of the block.",
// GetBestBlockHashCmd help.
"getbestblockhash--synopsis": "Returns the hash of the newest block in the best chain that wallet has finished syncing with.",
"getbestblockhash--result0": "The hash of the most recent synced-to block",
"getbestblockhash--result0": "The hash of the most recent synced-to block.",
// GetBlockCountCmd help.
"getblockcount--synopsis": "Returns the blockchain height of the newest block in the best chain that wallet has finished syncing with.",
"getblockcount--result0": "The blockchain height of the most recent synced-to block",
"getblockcount--result0": "The blockchain height of the most recent synced-to block.",
// GetInfoCmd help.
"getinfo--synopsis": "Returns a JSON object containing various state info.",
// InfoWalletResult help.
"infowalletresult-version": "The version of the server",
"infowalletresult-protocolversion": "The latest supported protocol version",
"infowalletresult-blocks": "The number of blocks processed",
"infowalletresult-timeoffset": "The time offset",
"infowalletresult-connections": "The number of connected peers",
"infowalletresult-proxy": "The proxy used by the server",
"infowalletresult-difficulty": "The current target difficulty",
"infowalletresult-testnet": "Whether or not server is using testnet",
"infowalletresult-relayfee": "The minimum relay fee for non-free transactions in LBC/KB",
"infowalletresult-errors": "Any current errors",
"infowalletresult-paytxfee": "The increment used each time more fee is required for an authored transaction",
"infowalletresult-balance": "The non-staked balance of all accounts calculated with one block confirmation",
"infowalletresult-staked": "The staked balance of all accounts calculated with one block confirmation",
"infowalletresult-walletversion": "The version of the address manager database",
"infowalletresult-unlocked_until": "Unset",
"infowalletresult-keypoolsize": "Unset",
"infowalletresult-keypoololdest": "Unset",
// GetUnconfirmedBalanceCmd help.
"getunconfirmedbalance--synopsis": "Calculates the unspent output value of all unmined transaction outputs.",
"getunconfirmedbalance-account": "The account name to query the unconfirmed balance for. Default to 'default'.",
"getunconfirmedbalance--result0": "Total amount of all unmined unspent outputs of the account valued in LBC.",
// GetAddressInfoCmd help.
"getaddressinfo--synopsis": "Generates and returns a new payment address.",
@ -95,7 +98,7 @@ var helpDescsEnUS = map[string]string{
"getaddressinforesult-timestamp": "The creation time of the key, if available, expressed in UNIX epoch time.",
"getaddressinforesult-hdkeypath": "The HD keypath, if the key is HD and available.",
"getaddressinforesult-hdseedid": "The Hash160 of the HD seed.",
"getaddressinforesult-address": "The address validated",
"getaddressinforesult-address": "The address validatedi.",
"getaddressinforesult-scriptPubKey": "The hex-encoded scriptPubKey generated by the address.",
"getaddressinforesult-desc": "A descriptor for spending coins sent to this address (only when solvable).",
"getaddressinforesult-isscript": "If the key is a script.",
@ -111,7 +114,7 @@ var helpDescsEnUS = map[string]string{
"getaddressinforesult-iscompressed": "If the pubkey is compressed.",
"getaddressinforesult-hdmasterfingerprint": "The fingerprint of the master key.",
"getaddressinforesult-labels": "Array of labels associated with the address. Currently limited to one label but returned.",
"embeddedaddressinfo-address": "The address validated",
"embeddedaddressinfo-address": "The address validated.",
"embeddedaddressinfo-scriptPubKey": "The hex-encoded scriptPubKey generated by the address.",
"embeddedaddressinfo-desc": "A descriptor for spending coins sent to this address (only when solvable).",
"embeddedaddressinfo-isscript": "If the key is a script.",
@ -130,249 +133,296 @@ var helpDescsEnUS = map[string]string{
// GetNewAddressCmd help.
"getnewaddress--synopsis": "Generates and returns a new payment address.",
"getnewaddress-account": "DEPRECATED -- Account name the new address will belong to (default=\"default\")",
"getnewaddress-addresstype": "Address type. Must be one of legacy / p2pkh, p2sh-p2wpkh / p2sh-p2wkh / p2sh-segwit, or p2wpkh / p2wkh / bech32",
"getnewaddress--result0": "The payment address",
"getnewaddress-account": "Account name the new address will belong to. Defaults to 'default'.",
"getnewaddress-addresstype": "Address type. Must be one of 'legacy', 'p2sh-segwit', or 'bech32'. Default to 'legacy'.",
"getnewaddress--result0": "The payment address.",
// GetRawChangeAddressCmd help.
"getrawchangeaddress--synopsis": "Generates and returns a new internal payment address for use as a change address in raw transactions.",
"getrawchangeaddress-account": "Account name the new internal address will belong to (default=\"default\")",
"getrawchangeaddress--result0": "The internal payment address",
"getrawchangeaddress--synopsis": "Generates and returns a new internal payment address for use as a change address in raw transactions.",
"getrawchangeaddress-account": "Account name the new internal address will belong to. Defaults to 'default'.",
"getrawchangeaddress-addresstype": "Address type. Must be one of 'legacy', 'p2sh-segwit', or 'bech32'. Default to 'legacy'.",
"getrawchangeaddress--result0": "The internal payment address.",
// GetReceivedByAccountCmd help.
"getreceivedbyaccount--synopsis": "DEPRECATED -- Returns the total amount received by addresses of some account, including spent outputs.",
"getreceivedbyaccount-account": "Account name to query total received amount for",
"getreceivedbyaccount-minconf": "Minimum number of block confirmations required before an output's value is included in the total",
"getreceivedbyaccount--result0": "The total received amount valued in bitcoin",
"getreceivedbyaccount--synopsis": "Returns the total amount received by addresses of some account, including spent outputs.",
"getreceivedbyaccount-account": "Account name to query total received amount for. Defaults to 'default'",
"getreceivedbyaccount-minconf": "Minimum number of block confirmations required before an output's value is included in the total. Defaults to 0",
"getreceivedbyaccount--result0": "The total received amount valued in LBC.",
// GetReceivedByAddressCmd help.
"getreceivedbyaddress--synopsis": "Returns the total amount received by a single address, including spent outputs.",
"getreceivedbyaddress-address": "Payment address which received outputs to include in total",
"getreceivedbyaddress-minconf": "Minimum number of block confirmations required before an output's value is included in the total",
"getreceivedbyaddress--result0": "The total received amount valued in bitcoin",
"getreceivedbyaddress-address": "Payment address which received outputs to include in total.",
"getreceivedbyaddress-minconf": "Minimum number of block confirmations required before an output's value is included in the total. Defaults to 1",
"getreceivedbyaddress--result0": "The total received amount valued in LBC.",
// GetTransactionCmd help.
"gettransaction--synopsis": "Returns a JSON object with details regarding a transaction relevant to this wallet.",
"gettransaction-txid": "Hash of the transaction to query",
"gettransaction-includewatchonly": "Also consider transactions involving watched addresses",
"gettransaction-txid": "Hash of the transaction to query.",
"gettransaction-includewatchonly": "Also consider transactions involving watched addresses.",
// GetTransactionResult help.
"gettransactionresult-amount": "The total amount this transaction credits to the wallet, valued in LBC.",
"gettransactionresult-fee": "The total input value minus the total output value, or 0 if 'txid' is not a sent transaction.",
"gettransactionresult-confirmations": "The number of block confirmations of the transaction.",
"gettransactionresult-generated": "Only present if transaction only input is a coinbase one.",
"gettransactionresult-blockhash": "The hash of the block this transaction is mined in, or the empty string if unmined.",
"gettransactionresult-blockindex": "Unset.",
"gettransactionresult-blocktime": "The Unix time of the block header this transaction is mined in, or 0 if unmined.",
"gettransactionresult-txid": "The transaction hash.",
"gettransactionresult-walletconflicts": "Unset.",
"gettransactionresult-time": "The earliest Unix time this transaction was known to exist.",
"gettransactionresult-timereceived": "The earliest Unix time this transaction was known to exist.",
"gettransactionresult-details": "Additional details for each recorded wallet credit and debit.",
"gettransactionresult-hex": "The transaction encoded as a hexadecimal string.",
// GetTransactionDetailsResult help.
"gettransactiondetailsresult-account": "The account pertaining to this transaction.",
"gettransactiondetailsresult-address": "The address an output was paid to, or the empty string if the output is nonstandard or this detail is regarding a transaction input.",
"gettransactiondetailsresult-category": `The kind of detail: "send" for sent transactions, "immature" for immature coinbase outputs, "generate" for mature coinbase outputs, or "recv" for all other received outputs.`,
"gettransactiondetailsresult-amount": "The amount of a received output.",
"gettransactiondetailsresult-fee": "The included fee for a sent transaction.",
"gettransactiondetailsresult-vout": "The transaction output index.",
"gettransactiondetailsresult-involveswatchonly": "Unset.",
// HelpCmd help.
"help--synopsis": "Returns a list of all commands or help for a specified command.",
"help-command": "The command to retrieve help for",
"help--condition0": "no command provided",
"help--condition1": "command specified",
"help--result0": "List of commands",
"help--result1": "Help for specified command",
// GetTransactionResult help.
"gettransactionresult-amount": "The total amount this transaction credits to the wallet, valued in bitcoin",
"gettransactionresult-fee": "The total input value minus the total output value, or 0 if 'txid' is not a sent transaction",
"gettransactionresult-confirmations": "The number of block confirmations of the transaction",
"gettransactionresult-blockhash": "The hash of the block this transaction is mined in, or the empty string if unmined",
"gettransactionresult-blockindex": "Unset",
"gettransactionresult-blocktime": "The Unix time of the block header this transaction is mined in, or 0 if unmined",
"gettransactionresult-txid": "The transaction hash",
"gettransactionresult-walletconflicts": "Unset",
"gettransactionresult-time": "The earliest Unix time this transaction was known to exist",
"gettransactionresult-timereceived": "The earliest Unix time this transaction was known to exist",
"gettransactionresult-details": "Additional details for each recorded wallet credit and debit",
"gettransactionresult-hex": "The transaction encoded as a hexadecimal string",
// GetTransactionDetailsResult help.
"gettransactiondetailsresult-account": "DEPRECATED -- Unset",
"gettransactiondetailsresult-address": "The address an output was paid to, or the empty string if the output is nonstandard or this detail is regarding a transaction input",
"gettransactiondetailsresult-category": `The kind of detail: "send" for sent transactions, "immature" for immature coinbase outputs, "generate" for mature coinbase outputs, or "recv" for all other received outputs`,
"gettransactiondetailsresult-amount": "The amount of a received output",
"gettransactiondetailsresult-fee": "The included fee for a sent transaction",
"gettransactiondetailsresult-vout": "The transaction output index",
"gettransactiondetailsresult-involveswatchonly": "Unset",
"help-command": "The command to retrieve help for.",
"help--condition0": "no command provided.",
"help--condition1": "command specified.",
"help--result0": "List of commands.",
"help--result1": "Help for specified command.",
// ImportPrivKeyCmd help.
"importprivkey--synopsis": "Imports a WIF-encoded private key to the 'imported' account.",
"importprivkey-privkey": "The WIF-encoded private key",
"importprivkey-label": "Unused (must be unset or 'imported')",
"importprivkey-rescan": "Rescan the blockchain (since the genesis block) for outputs controlled by the imported key",
"importprivkey-privkey": "The WIF-encoded private key.",
"importprivkey-label": "Unused (must be unset or 'imported').",
"importprivkey-rescan": "Rescan the blockchain (since the genesis block) for outputs controlled by the imported key.",
// InfoWalletResult help.
"infowalletresult-version": "The version of the server.",
"infowalletresult-protocolversion": "The latest supported protocol version.",
"infowalletresult-blocks": "The number of blocks processed.",
"infowalletresult-timeoffset": "The time offset.",
"infowalletresult-connections": "The number of connected peers.",
"infowalletresult-proxy": "The proxy used by the server.",
"infowalletresult-difficulty": "The current target difficulty.",
"infowalletresult-testnet": "Whether or not server is using testnet.",
"infowalletresult-relayfee": "The minimum relay fee for non-free transactions in LBC/KB.",
"infowalletresult-errors": "Any current errors.",
"infowalletresult-paytxfee": "The increment used each time more fee is required for an authored transaction.",
"infowalletresult-balance": "The non-staked balance of all accounts calculated with one block confirmation.",
"infowalletresult-staked": "The staked balance of all accounts calculated with one block confirmation.",
"infowalletresult-walletversion": "The version of the address manager database.",
"infowalletresult-unlocked_until": "Unset.",
"infowalletresult-keypoolsize": "Unset.",
"infowalletresult-keypoololdest": "Unset.",
// KeypoolRefillCmd help.
"keypoolrefill--synopsis": "DEPRECATED -- This request does nothing since no keypool is maintained.",
"keypoolrefill-newsize": "Unused",
"keypoolrefill-newsize": "Unused.",
// ListAccountsCmd help.
"listaccounts--synopsis": "DEPRECATED -- Returns a JSON object of all accounts and their balances.",
"listaccounts-minconf": "Minimum number of block confirmations required before an unspent output's value is included in the balance",
"listaccounts--result0--desc": "JSON object with account names as keys and bitcoin amounts as values",
"listaccounts--result0--key": "The account name",
"listaccounts--result0--value": "The account balance valued in bitcoin",
"listaccounts--synopsis": "Returns a JSON object of all accounts and their balances.",
"listaccounts-minconf": "Minimum number of block confirmations required before an unspent output's value is included in the balance.",
"listaccounts-addresstype": "Address type filter. Must be one of 'legacy', 'p2sh-segwit', 'bech32', or '*'. Defaults to '*'.",
"listaccounts--result0--desc": "JSON object with account names as keys and LBC amounts as values.",
"listaccounts--result0--key": "Account name",
"listaccounts--result0--value": "Total balance and each scope respectively, valued in LBC.",
// ListAddressTransactionsCmd help.
"listaddresstransactions--synopsis": "Returns a JSON array of objects containing verbose details for wallet transactions pertaining some addresses.",
"listaddresstransactions-addresses": "Addresses to filter transaction results by.",
"listaddresstransactions-account": "Account to filter transactions results by. Defaults to 'default'.",
// ListAllTransactionsCmd help.
"listalltransactions--synopsis": "Returns a JSON array of objects in the same format as 'listtransactions' without limiting the number of returned objects.",
"listalltransactions-account": "Account to filter transactions results by. Defaults to 'default'.",
// ListLockUnspentCmd help.
"listlockunspent--synopsis": "Returns a JSON array of outpoints marked as locked (with lockunspent) for this wallet session.",
// TransactionInput help.
"transactioninput-txid": "The transaction hash of the referenced output",
"transactioninput-vout": "The output index of the referenced output",
"transactioninput-txid": "The transaction hash of the referenced output.",
"transactioninput-vout": "The output index of the referenced output.",
// ListReceivedByAccountCmd help.
"listreceivedbyaccount--synopsis": "DEPRECATED -- Returns a JSON array of objects listing all accounts and the total amount received by each account.",
"listreceivedbyaccount-minconf": "Minimum number of block confirmations required before a transaction is considered",
"listreceivedbyaccount-includeempty": "Unused",
"listreceivedbyaccount-includewatchonly": "Unused",
"listreceivedbyaccount--synopsis": "Returns a JSON array of objects listing all accounts and the total amount received by each account.",
"listreceivedbyaccount-minconf": "Minimum number of block confirmations required before a transaction is considered.",
"listreceivedbyaccount-includeempty": "Unused.",
"listreceivedbyaccount-includewatchonly": "Unused.",
// ListReceivedByAccountResult help.
"listreceivedbyaccountresult-account": "The name of the account",
"listreceivedbyaccountresult-amount": "Total amount received by payment addresses of the account valued in bitcoin",
"listreceivedbyaccountresult-confirmations": "Number of block confirmations of the most recent transaction relevant to the account",
"listreceivedbyaccountresult-account": "Account name.",
"listreceivedbyaccountresult-amount": "Total amount received by payment addresses of the account valued in LBC.",
"listreceivedbyaccountresult-confirmations": "Number of block confirmations of the most recent transaction relevant to the account.",
// ListReceivedByAddressCmd help.
"listreceivedbyaddress--synopsis": "Returns a JSON array of objects listing wallet payment addresses and their total received amounts.",
"listreceivedbyaddress-minconf": "Minimum number of block confirmations required before a transaction is considered",
"listreceivedbyaddress-includeempty": "Unused",
"listreceivedbyaddress-includewatchonly": "Unused",
"listreceivedbyaddress-minconf": "Minimum number of block confirmations required before a transaction is considered.",
"listreceivedbyaddress-includeempty": "Unused.",
"listreceivedbyaddress-includewatchonly": "Unused.",
// ListReceivedByAddressResult help.
"listreceivedbyaddressresult-account": "DEPRECATED -- Unset",
"listreceivedbyaddressresult-address": "The payment address",
"listreceivedbyaddressresult-amount": "Total amount received by the payment address valued in bitcoin",
"listreceivedbyaddressresult-confirmations": "Number of block confirmations of the most recent transaction relevant to the address",
"listreceivedbyaddressresult-txids": "Transaction hashes of all transactions involving this address",
"listreceivedbyaddressresult-involvesWatchonly": "Unset",
"listreceivedbyaddressresult-address": "The payment address.",
"listreceivedbyaddressresult-amount": "Total amount received by the payment address valued in LBC.",
"listreceivedbyaddressresult-confirmations": "Number of block confirmations of the most recent transaction relevant to the address.",
"listreceivedbyaddressresult-txids": "Transaction hashes of all transactions involving this address.",
"listreceivedbyaddressresult-involvesWatchonly": "Unset.",
// ListSinceBlockCmd help.
"listsinceblock--synopsis": "Returns a JSON array of objects listing details of all wallet transactions after some block.",
"listsinceblock-blockhash": "Hash of the parent block of the first block to consider transactions from, or unset to list all transactions",
"listsinceblock-targetconfirmations": "Minimum number of block confirmations of the last block in the result object. Must be 1 or greater. Note: The transactions array in the result object is not affected by this parameter",
"listsinceblock-includewatchonly": "Unused",
"listsinceblock--condition0": "blockhash specified",
"listsinceblock--condition1": "no blockhash specified",
"listsinceblock--result0": "Lists all transactions, including unmined transactions, since the specified block",
"listsinceblock--result1": "Lists all transactions since the genesis block",
"listsinceblock-blockhash": "Hash of the parent block of the first block to consider transactions from, or unset to list all transactions.",
"listsinceblock-targetconfirmations": "Minimum number of block confirmations of the last block in the result object. Must be 1 or greater. Note: The transactions array in the result object is not affected by this parameter.",
"listsinceblock-includewatchonly": "Unused.",
"listsinceblock--condition0": "blockhash specified.",
"listsinceblock--condition1": "no blockhash specified.",
"listsinceblock--result0": "Lists all transactions, including unmined transactions, since the specified block.",
"listsinceblock--result1": "Lists all transactions since the genesis block.",
// ListSinceBlockResult help.
"listsinceblockresult-transactions": "JSON array of objects containing verbose details of the each transaction",
"listsinceblockresult-lastblock": "Hash of the latest-synced block to be used in later calls to listsinceblock",
// ListTransactionsResult help.
"listtransactionsresult-account": "DEPRECATED -- Unset",
"listtransactionsresult-address": "Payment address for a transaction output",
"listtransactionsresult-category": `The kind of transaction: "send" for sent transactions, "immature" for immature coinbase outputs, "generate" for mature coinbase outputs, or "recv" for all other received outputs. Note: A single output may be included multiple times under different categories`,
"listtransactionsresult-amount": "The value of the transaction output valued in bitcoin",
"listtransactionsresult-fee": "The total input value minus the total output value for sent transactions",
"listtransactionsresult-confirmations": "The number of block confirmations of the transaction",
"listtransactionsresult-generated": "Whether the transaction output is a coinbase output",
"listtransactionsresult-blockhash": "The hash of the block this transaction is mined in, or the empty string if unmined",
"listtransactionsresult-blockheight": "The block height containing the transaction.",
"listtransactionsresult-blockindex": "Unset",
"listtransactionsresult-blocktime": "The Unix time of the block header this transaction is mined in, or 0 if unmined",
"listtransactionsresult-label": "A comment for the address/transaction, if any",
"listtransactionsresult-txid": "The hash of the transaction",
"listtransactionsresult-vout": "The transaction output index",
"listtransactionsresult-walletconflicts": "Unset",
"listtransactionsresult-time": "The earliest Unix time this transaction was known to exist",
"listtransactionsresult-timereceived": "The earliest Unix time this transaction was known to exist",
"listtransactionsresult-involveswatchonly": "Unset",
"listtransactionsresult-comment": "Unset",
"listtransactionsresult-otheraccount": "Unset",
"listtransactionsresult-trusted": "Unset",
"listtransactionsresult-bip125-replaceable": "Unset",
"listtransactionsresult-abandoned": "Unset",
"listsinceblockresult-transactions": "JSON array of objects containing verbose details of the each transaction.",
"listsinceblockresult-lastblock": "Hash of the latest-synced block to be used in later calls to listsinceblock.",
// ListTransactionsCmd help.
"listtransactions--synopsis": "Returns a JSON array of objects containing verbose details for wallet transactions.",
"listtransactions-account": "DEPRECATED -- Unused (must be unset, \"default\" or \"*\")",
"listtransactions-count": "Maximum number of transactions to create results from",
"listtransactions-from": "Number of transactions to skip before results are created",
"listtransactions-includewatchonly": "Unused",
"listtransactions-account": "Account to filter transactions results by. Defaults to 'default'.",
"listtransactions-count": "Maximum number of transactions to create results from. Defaults to 10",
"listtransactions-from": "Number of transactions to skip before results are created.",
"listtransactions-includewatchonly": "Unused.",
// ListTransactionsResult help.
"listtransactionsresult-account": "The account name associated with the transaction.",
"listtransactionsresult-address": "Payment address for a transaction output.",
"listtransactionsresult-category": `The kind of transaction: "send" for sent transactions, "immature" for immature coinbase outputs, "generate" for mature coinbase outputs, or "recv" for all other received outputs. Note: A single output may be included multiple times under different categories`,
"listtransactionsresult-amount": "The value of the transaction output valued in LBC.",
"listtransactionsresult-fee": "The total input value minus the total output value for sent transactions.",
"listtransactionsresult-confirmations": "The number of block confirmations of the transaction.",
"listtransactionsresult-generated": "Whether the transaction output is a coinbase output.",
"listtransactionsresult-blockhash": "The hash of the block this transaction is mined in, or the empty string if unmined.",
"listtransactionsresult-blockheight": "The block height containing the transaction.",
"listtransactionsresult-blockindex": "Unset.",
"listtransactionsresult-blocktime": "The Unix time of the block header this transaction is mined in, or 0 if unmined.",
"listtransactionsresult-label": "A comment for the address/transaction, if any.",
"listtransactionsresult-txid": "The hash of the transaction.",
"listtransactionsresult-vout": "The transaction output index.",
"listtransactionsresult-walletconflicts": "Unset.",
"listtransactionsresult-time": "The earliest Unix time this transaction was known to exist.",
"listtransactionsresult-timereceived": "The earliest Unix time this transaction was known to exist.",
"listtransactionsresult-involveswatchonly": "Unset.",
"listtransactionsresult-comment": "Unset.",
"listtransactionsresult-otheraccount": "Unset.",
"listtransactionsresult-trusted": "Unset.",
"listtransactionsresult-bip125-replaceable": "Unset.",
"listtransactionsresult-abandoned": "Unset.",
// ListUnspentCmd help.
"listunspent--synopsis": "Returns a JSON array of objects representing unlocked unspent outputs controlled by wallet keys.",
"listunspent-minconf": "Minimum number of block confirmations required before a transaction output is considered",
"listunspent-maxconf": "Maximum number of block confirmations required before a transaction output is excluded",
"listunspent-addresses": "If set, limits the returned details to unspent outputs received by any of these payment addresses",
"listunspent-minconf": "Minimum number of block confirmations required before a transaction output is considered.",
"listunspent-maxconf": "Maximum number of block confirmations required before a transaction output is excluded.",
"listunspent-addresses": "If set, limits the returned details to unspent outputs received by any of these payment addresses.",
// ListUnspentResult help.
"listunspentresult-txid": "The transaction hash of the referenced output",
"listunspentresult-vout": "The output index of the referenced output",
"listunspentresult-address": "The payment address that received the output",
"listunspentresult-account": "The account associated with the receiving payment address",
"listunspentresult-scriptPubKey": "The output script encoded as a hexadecimal string",
"listunspentresult-redeemScript": "Unset",
"listunspentresult-amount": "The amount of the output valued in bitcoin",
"listunspentresult-confirmations": "The number of block confirmations of the transaction",
"listunspentresult-solvable": "Whether the output is solvable",
"listunspentresult-spendable": "Whether the output is entirely controlled by wallet keys/scripts (false for partially controlled multisig outputs or outputs to watch-only addresses)",
"listunspentresult-isstake": "Whether the output is staked",
"listunspentresult-txid": "The transaction hash of the referenced output.",
"listunspentresult-vout": "The output index of the referenced output.",
"listunspentresult-address": "The payment address that received the output.",
"listunspentresult-account": "The account associated with the receiving payment address.",
"listunspentresult-scriptPubKey": "The output script encoded as a hexadecimal string.",
"listunspentresult-redeemScript": "Unset.",
"listunspentresult-amount": "The amount of the output valued in LBC.",
"listunspentresult-confirmations": "The number of block confirmations of the transaction.",
"listunspentresult-solvable": "Whether the output is solvable.",
"listunspentresult-spendable": "Whether the output is entirely controlled by wallet keys/scripts (false for partially controlled multisig outputs or outputs to watch-only addresses).",
"listunspentresult-isstake": "Whether the output is staked.",
// LockUnspentCmd help.
"lockunspent--synopsis": "Locks or unlocks an unspent output.\n" +
"Locked outputs are not chosen for transaction inputs of authored transactions and are not included in 'listunspent' results.\n" +
"Locked outputs are volatile and are not saved across wallet restarts.\n" +
"If unlock is true and no transaction outputs are specified, all locked outputs are marked unlocked.",
"lockunspent-unlock": "True to unlock outputs, false to lock",
"lockunspent-transactions": "Transaction outputs to lock or unlock",
"lockunspent--result0": "The boolean 'true'",
"lockunspent-unlock": "True to unlock outputs, false to lock.",
"lockunspent-transactions": "Transaction outputs to lock or unlock.",
"lockunspent--result0": "The boolean 'true'.",
// RenameAccountCmd help.
"renameaccount--synopsis": "Renames an account.",
"renameaccount-oldaccount": "The old account name to rename.",
"renameaccount-newaccount": "The new name for the account.",
// RescanBlockchainCmd help.
"rescanblockchain--synopsis": "Renames an account.",
"rescanblockchain-startheight": "Block height where the rescan should start.",
"rescanblockchain-stopheight": "The last block height that should be scanned. If none is provided it will rescan up to the tip at return time of this call.",
// RescanblockchainResult help.
"rescanblockchainresult-start_height": "The block height where the rescan started (the requested height or 0)",
"rescanblockchainresult-stop_height": "The height of the last rescanned block.",
// SendFromCmd help.
"sendfrom--synopsis": "DEPRECATED -- Authors, signs, and sends a transaction that outputs some amount to a payment address.\n" +
"sendfrom--synopsis": "Authors, signs, and sends a transaction that outputs some amount to a payment address.\n" +
"A change output is automatically included to send extra output value back to the original account.",
"sendfrom-fromaccount": "Account to pick unspent outputs from",
"sendfrom-toaddress": "Address to pay",
"sendfrom-amount": "Amount to send to the payment address valued in bitcoin",
"sendfrom-minconf": "Minimum number of block confirmations required before a transaction output is eligible to be spent",
"sendfrom-comment": "Unused",
"sendfrom-commentto": "Unused",
"sendfrom--result0": "The transaction hash of the sent transaction",
"sendfrom-fromaccount": "Account to pick unspent outputs from.",
"sendfrom-toaddress": "Address to pay.",
"sendfrom-amount": "Amount to send to the payment address valued in LBC.",
"sendfrom-minconf": "Minimum number of block confirmations required before a transaction output is eligible to be spent.",
"sendfrom-addresstype": "Address type filter for UTXOs to spent from. Must be one of 'legacy', 'p2sh-segwit', 'bech32', or '*'. Defaults to '*'.",
"sendfrom-comment": "Unused.",
"sendfrom-commentto": "Unused.",
"sendfrom--result0": "The transaction hash of the sent transaction.",
// SendManyCmd help.
"sendmany--synopsis": "Authors, signs, and sends a transaction that outputs to many payment addresses.\n" +
"A change output is automatically included to send extra output value back to the original account.",
"sendmany-fromaccount": "DEPRECATED -- Account to pick unspent outputs from",
"sendmany-amounts": "Pairs of payment addresses and the output amount to pay each",
"sendmany-amounts--desc": "JSON object using payment addresses as keys and output amounts valued in bitcoin to send to each address",
"sendmany-amounts--key": "Address to pay",
"sendmany-amounts--value": "Amount to send to the payment address valued in bitcoin",
"sendmany-minconf": "Minimum number of block confirmations required before a transaction output is eligible to be spent",
"sendmany-comment": "Unused",
"sendmany--result0": "The transaction hash of the sent transaction",
"sendmany-fromaccount": "Account to pick unspent outputs from.",
"sendmany-amounts": "Pairs of payment addresses and the output amount to pay each.",
"sendmany-amounts--desc": "JSON object using payment addresses as keys and output amounts valued in LBC to send to each address.",
"sendmany-amounts--key": "Address to pay.",
"sendmany-amounts--value": "Amount to send to the payment address valued in LBC.",
"sendmany-minconf": "Minimum number of block confirmations required before a transaction output is eligible to be spent.",
"sendmany-addresstype": "Address type filter for UTXOs to spent from. Must be one of 'legacy', 'p2sh-segwit', 'bech32', or '*'. Defaults to '*'.",
"sendmany-comment": "Unused.",
"sendmany--result0": "The transaction hash of the sent transaction.",
// SendToAddressCmd help.
"sendtoaddress--synopsis": "Authors, signs, and sends a transaction that outputs some amount to a payment address.\n" +
"Unlike sendfrom, outputs are always chosen from the default account.\n" +
"A change output is automatically included to send extra output value back to the original account.",
"sendtoaddress-address": "Address to pay",
"sendtoaddress-amount": "Amount to send to the payment address valued in bitcoin",
"sendtoaddress-comment": "Unused",
"sendtoaddress-commentto": "Unused",
"sendtoaddress--result0": "The transaction hash of the sent transaction",
"sendtoaddress-address": "Address to pay.",
"sendtoaddress-amount": "Amount to send to the payment address valued in LBC.",
"sendtoaddress-addresstype": "Address type filter for UTXOs to spent from. Must be one of 'legacy', 'p2sh-segwit', 'bech32', or '*'. Defaults to '*'.",
"sendtoaddress-comment": "Unused.",
"sendtoaddress-commentto": "Unused.",
"sendtoaddress--result0": "The transaction hash of the sent transaction.",
// SetTxFeeCmd help.
"settxfee--synopsis": "Modify the increment used each time more fee is required for an authored transaction.",
"settxfee-amount": "The new fee increment valued in bitcoin",
"settxfee--result0": "The boolean 'true'",
"settxfee-amount": "The new fee increment valued in LBC.",
"settxfee--result0": "The boolean 'true'.",
// SignMessageCmd help.
"signmessage--synopsis": "Signs a message using the private key of a payment address.",
"signmessage-address": "Payment address of private key used to sign the message with",
"signmessage-message": "Message to sign",
"signmessage--result0": "The signed message encoded as a base64 string",
"signmessage-address": "Payment address of private key used to sign the message with.",
"signmessage-message": "Message to sign.",
"signmessage--result0": "The signed message encoded as a base64 string.",
// SignRawTransactionCmd help.
"signrawtransaction--synopsis": "Signs transaction inputs using private keys from this wallet and request.\n" +
"The valid flags options are ALL, NONE, SINGLE, ALL|ANYONECANPAY, NONE|ANYONECANPAY, and SINGLE|ANYONECANPAY.",
"signrawtransaction-rawtx": "Unsigned or partially unsigned transaction to sign encoded as a hexadecimal string",
"signrawtransaction-inputs": "Additional data regarding inputs that this wallet may not be tracking",
"signrawtransaction-privkeys": "Additional WIF-encoded private keys to use when creating signatures",
"signrawtransaction-flags": "Sighash flags",
"signrawtransaction-rawtx": "Unsigned or partially unsigned transaction to sign encoded as a hexadecimal string.",
"signrawtransaction-inputs": "Additional data regarding inputs that this wallet may not be tracking.",
"signrawtransaction-privkeys": "Additional WIF-encoded private keys to use when creating signatures.",
"signrawtransaction-flags": "Sighash flags.",
// SignRawTransactionResult help.
"signrawtransactionresult-hex": "The resulting transaction encoded as a hexadecimal string",
"signrawtransactionresult-complete": "Whether all input signatures have been created",
"signrawtransactionresult-errors": "Script verification errors (if exists)",
"signrawtransactionresult-hex": "The resulting transaction encoded as a hexadecimal string.",
"signrawtransactionresult-complete": "Whether all input signatures have been created.",
"signrawtransactionresult-errors": "Script verification errors (if exists).",
// SignRawTransactionError help.
"signrawtransactionerror-error": "Verification or signing error related to the input",
"signrawtransactionerror-sequence": "Script sequence number",
"signrawtransactionerror-scriptSig": "The hex-encoded signature script",
"signrawtransactionerror-txid": "The transaction hash of the referenced previous output",
"signrawtransactionerror-vout": "The output index of the referenced previous output",
"signrawtransactionerror-error": "Verification or signing error related to the input.",
"signrawtransactionerror-sequence": "Script sequence number.",
"signrawtransactionerror-scriptSig": "The hex-encoded signature script.",
"signrawtransactionerror-txid": "The transaction hash of the referenced previous output.",
"signrawtransactionerror-vout": "The output index of the referenced previous output.",
// ValidateAddressCmd help.
"validateaddress--synopsis": "Verify that an address is valid.\n" +
@ -381,74 +431,43 @@ var helpDescsEnUS = map[string]string{
"The following fields are only valid when address has an associated public key: pubkey, iscompressed.\n" +
"The following fields are only valid when address is a pay-to-script-hash address: addresses, hex, and script.\n" +
"If the address is a multisig address controlled by this wallet, the multisig fields will be left unset if the wallet is locked since the redeem script cannot be decrypted.",
"validateaddress-address": "Address to validate",
"validateaddress-address": "Address to validate.",
// ValidateAddressWalletResult help.
"validateaddresswalletresult-isvalid": "Whether or not the address is valid",
"validateaddresswalletresult-address": "The payment address (only when isvalid is true)",
"validateaddresswalletresult-ismine": "Whether this address is controlled by the wallet (only when isvalid is true)",
"validateaddresswalletresult-iswatchonly": "Unset",
"validateaddresswalletresult-isscript": "Whether the payment address is a pay-to-script-hash address (only when isvalid is true)",
"validateaddresswalletresult-pubkey": "The associated public key of the payment address, if any (only when isvalid is true)",
"validateaddresswalletresult-iscompressed": "Whether the address was created by hashing a compressed public key, if any (only when isvalid is true)",
"validateaddresswalletresult-account": "The account this payment address belongs to (only when isvalid is true)",
"validateaddresswalletresult-addresses": "All associated payment addresses of the script if address is a multisig address (only when isvalid is true)",
"validateaddresswalletresult-hex": "The redeem script ",
"validateaddresswalletresult-script": "The class of redeem script for a multisig address",
"validateaddresswalletresult-sigsrequired": "The number of required signatures to redeem outputs to the multisig address",
"validateaddresswalletresult-isvalid": "Whether or not the address is valid.",
"validateaddresswalletresult-address": "The payment address (only when isvalid is true).",
"validateaddresswalletresult-ismine": "Whether this address is controlled by the wallet (only when isvalid is true).",
"validateaddresswalletresult-iswatchonly": "Unset.",
"validateaddresswalletresult-isscript": "Whether the payment address is a pay-to-script-hash address (only when isvalid is true).",
"validateaddresswalletresult-pubkey": "The associated public key of the payment address, if any (only when isvalid is true).",
"validateaddresswalletresult-iscompressed": "Whether the address was created by hashing a compressed public key, if any (only when isvalid is true).",
"validateaddresswalletresult-account": "The account this payment address belongs to (only when isvalid is true).",
"validateaddresswalletresult-addresses": "All associated payment addresses of the script if address is a multisig address (only when isvalid is true).",
"validateaddresswalletresult-hex": "The redeem script .",
"validateaddresswalletresult-script": "The class of redeem script for a multisig address.",
"validateaddresswalletresult-sigsrequired": "The number of required signatures to redeem outputs to the multisig address.",
// VerifyMessageCmd help.
"verifymessage--synopsis": "Verify a message was signed with the associated private key of some address.",
"verifymessage-address": "Address used to sign message",
"verifymessage-signature": "The signature to verify",
"verifymessage-message": "The message to verify",
"verifymessage--result0": "Whether the message was signed with the private key of 'address'",
"verifymessage-address": "Address used to sign message.",
"verifymessage-signature": "The signature to verify.",
"verifymessage-message": "The message to verify.",
"verifymessage--result0": "Whether the message was signed with the private key of 'address'.",
// WalletIsLockedCmd help.
"walletislocked--synopsis": "Returns whether or not the wallet is locked.",
"walletislocked--result0": "Whether the wallet is locked.",
// WalletLockCmd help.
"walletlock--synopsis": "Lock the wallet.",
// WalletPassphraseCmd help.
"walletpassphrase--synopsis": "Unlock the wallet.",
"walletpassphrase-passphrase": "The wallet passphrase",
"walletpassphrase-timeout": "The number of seconds to wait before the wallet automatically locks",
"walletpassphrase-passphrase": "The wallet passphrase.",
"walletpassphrase-timeout": "The number of seconds to wait before the wallet automatically locks.",
// WalletPassphraseChangeCmd help.
"walletpassphrasechange--synopsis": "Change the wallet passphrase.",
"walletpassphrasechange-oldpassphrase": "The old wallet passphrase",
"walletpassphrasechange-newpassphrase": "The new wallet passphrase",
// CreateNewAccountCmd help.
"createnewaccount--synopsis": "Creates a new account.\n" +
"The wallet must be unlocked for this request to succeed.",
"createnewaccount-account": "Name of the new account",
// GetBestBlockCmd help.
"getbestblock--synopsis": "Returns the hash and height of the newest block in the best chain that wallet has finished syncing with.",
// GetBestBlockResult help.
"getbestblockresult-hash": "The hash of the block",
"getbestblockresult-height": "The blockchain height of the block",
// GetUnconfirmedBalanceCmd help.
"getunconfirmedbalance--synopsis": "Calculates the unspent output value of all unmined transaction outputs for an account.",
"getunconfirmedbalance-account": "The account to query the unconfirmed balance for (default=\"default\")",
"getunconfirmedbalance--result0": "Total amount of all unmined unspent outputs of the account valued in bitcoin.",
// ListAddressTransactionsCmd help.
"listaddresstransactions--synopsis": "Returns a JSON array of objects containing verbose details for wallet transactions pertaining some addresses.",
"listaddresstransactions-addresses": "Addresses to filter transaction results by",
"listaddresstransactions-account": "Unused (must be unset or \"*\")",
// ListAllTransactionsCmd help.
"listalltransactions--synopsis": "Returns a JSON array of objects in the same format as 'listtransactions' without limiting the number of returned objects.",
"listalltransactions-account": "Unused (must be unset or \"*\")",
// RenameAccountCmd help.
"renameaccount--synopsis": "Renames an account.",
"renameaccount-oldaccount": "The old account name to rename",
"renameaccount-newaccount": "The new name for the account",
// WalletIsLockedCmd help.
"walletislocked--synopsis": "Returns whether or not the wallet is locked.",
"walletislocked--result0": "Whether the wallet is locked",
"walletpassphrasechange-oldpassphrase": "The old wallet passphrase.",
"walletpassphrasechange-newpassphrase": "The new wallet passphrase.",
}

View file

@ -7,7 +7,9 @@
package rpchelp
import "github.com/lbryio/lbcd/btcjson"
import (
"github.com/lbryio/lbcd/btcjson"
)
// Common return types.
var (
@ -31,7 +33,7 @@ var Methods = []struct {
{"getaccountaddress", returnsString},
{"getaddressesbyaccount", returnsStringArray},
{"getaddressinfo", []interface{}{(*btcjson.GetAddressInfoResult)(nil)}},
{"getbalance", append(returnsNumber, returnsNumber[0])},
{"getbalance", returnsNumber},
{"getbestblockhash", returnsString},
{"getblockcount", returnsNumber},
{"getinfo", []interface{}{(*btcjson.InfoWalletResult)(nil)}},
@ -68,6 +70,7 @@ var Methods = []struct {
{"listaddresstransactions", returnsLTRArray},
{"listalltransactions", returnsLTRArray},
{"renameaccount", nil},
{"rescanblockchain", []interface{}{(*btcjson.RescanBlockchainResult)(nil)}},
{"walletislocked", returnsBool},
}

View file

@ -81,24 +81,21 @@ func walletMain() error {
return err
}
// Create and start chain RPC client so it's ready to connect to
// the wallet when loaded later.
if !cfg.NoInitialLoad {
go rpcClientConnectLoop(legacyRPCServer, loader)
}
go rpcClientConnectLoop(legacyRPCServer, loader)
loader.RunAfterLoad(func(w *wallet.Wallet) {
startWalletRPCServices(w, legacyRPCServer)
log.Infof("Unlocking wallet with the default or specified passphrase...")
err = w.Unlock([]byte(cfg.Passphrase), nil)
if err != nil {
log.Infof("Unable to unlock wallet: %v", err)
}
})
if !cfg.NoInitialLoad {
// Load the wallet database. It must have been created already
// or this will return an appropriate error.
_, err = loader.OpenExistingWallet([]byte(cfg.WalletPass), true)
if err != nil {
log.Error(err)
return err
}
_, err = loader.OpenExistingWallet()
if err != nil {
log.Error(err)
return err
}
// Add interrupt handlers to shutdown the various process components
@ -135,10 +132,7 @@ func walletMain() error {
// associated with the server for RPC passthrough and to enable additional
// methods.
func rpcClientConnectLoop(legacyRPCServer *legacyrpc.Server, loader *wallet.Loader) {
var certs []byte
if !cfg.UseSPV {
certs = readCAFile()
}
certs := readCAFile()
for {
var (
@ -225,7 +219,7 @@ func readCAFile() []byte {
func startChainRPC(certs []byte) (*chain.RPCClient, error) {
log.Infof("Attempting RPC client connection to %v", cfg.RPCConnect)
rpcc, err := chain.NewRPCClient(activeNet.Params, cfg.RPCConnect,
cfg.LbcdUsername, cfg.LbcdPassword, certs, cfg.DisableClientTLS,
cfg.RPCUser, cfg.RPCPass, certs, cfg.DisableClientTLS,
cfg.SkipVerify, 0)
if err != nil {
return nil, err

View file

@ -6,7 +6,6 @@ package netparams
import (
"github.com/lbryio/lbcd/chaincfg"
"github.com/lbryio/lbcd/wire"
)
// Params is used to group parameters for various networks such as the main
@ -42,31 +41,3 @@ var RegTestParams = Params{
RPCClientPort: "29245",
RPCServerPort: "29244",
}
// SimNetParams contains parameters specific to the simulation test network
// (wire.SimNet).
var SimNetParams = Params{
Params: &chaincfg.SimNetParams,
RPCClientPort: "39245",
RPCServerPort: "39244",
}
// SigNetParams contains parameters specific to the signet test network
// (wire.SigNet).
var SigNetParams = Params{
Params: &chaincfg.SigNetParams,
RPCClientPort: "49245",
RPCServerPort: "49244",
}
// SigNetWire is a helper function that either returns the given chain
// parameter's net value if the parameter represents a signet network or 0 if
// it's not. This is necessary because there can be custom signet networks that
// have a different net value.
func SigNetWire(params *chaincfg.Params) wire.BitcoinNet {
if params.Name == chaincfg.SigNetParams.Name {
return params.Net
}
return 0
}

View file

@ -16,6 +16,7 @@ import (
"sync"
"time"
"github.com/lbryio/lbcd/blockchain"
"github.com/lbryio/lbcd/btcec"
"github.com/lbryio/lbcd/btcjson"
"github.com/lbryio/lbcd/chaincfg"
@ -101,6 +102,7 @@ var rpcHandlers = map[string]struct {
"listunspent": {handler: listUnspent},
"lockunspent": {handler: lockUnspent},
"sendfrom": {handlerWithChain: sendFrom},
"rescanblockchain": {handlerWithChain: rescanBlockchain},
"sendmany": {handler: sendMany},
"sendtoaddress": {handler: sendToAddress},
"settxfee": {handler: setTxFee},
@ -394,16 +396,25 @@ func dumpPrivKey(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
// getAddressesByAccount handles a getaddressesbyaccount request by returning
// all addresses for an account, or an error if the requested account does
// not exist.
func getAddressesByAccount(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
// not exist. If addresstype is also specified, only those address types are
// returned.
func getAddressesByAccount(icmd interface{}, w *wallet.Wallet) (
interface{}, error) {
cmd := icmd.(*btcjson.GetAddressesByAccountCmd)
account, err := w.AccountNumber(waddrmgr.KeyScopeBIP0044, cmd.Account)
account, err := w.AccountNumber(*cmd.Account)
if err != nil {
return nil, err
}
addrs, err := w.AccountAddresses(account)
// Use specified scope, if provided.
scope, err := lookupKeyScope(cmd.AddressType)
if err != nil {
return nil, err
}
addrs, err := w.AccountAddresses(account, scope)
if err != nil {
return nil, err
}
@ -412,6 +423,7 @@ func getAddressesByAccount(icmd interface{}, w *wallet.Wallet) (interface{}, err
for i, a := range addrs {
addrStrs[i] = a.EncodeAddress()
}
return addrStrs, nil
}
@ -572,30 +584,22 @@ func getAddressInfo(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
func getBalance(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
cmd := icmd.(*btcjson.GetBalanceCmd)
var balance btcutil.Amount
var err error
accountName := "*"
if cmd.Account != nil {
accountName = *cmd.Account
if *cmd.Account == "*" {
balance, _, err := w.CalculateBalance(int32(*cmd.MinConf))
if err != nil {
return nil, err
}
return balance.ToBTC(), nil
}
if accountName == "*" {
balance, _, err = w.CalculateBalance(int32(*cmd.MinConf))
if err != nil {
return nil, err
}
} else {
var account uint32
account, err = w.AccountNumber(waddrmgr.KeyScopeBIP0044, accountName)
if err != nil {
return nil, err
}
bals, err := w.CalculateAccountBalances(account, int32(*cmd.MinConf))
if err != nil {
return nil, err
}
balance = bals.Spendable
account, err := w.AccountNumber(*cmd.Account)
if err != nil {
return nil, err
}
return balance.ToBTC(), nil
bals, err := w.CalculateAccountBalances(account, int32(*cmd.MinConf))
return bals.Spendable.ToBTC(), err
}
// getBestBlock handles a getbestblock request by returning a JSON object
@ -689,7 +693,7 @@ func getAccount(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
return nil, &ErrAddressNotInWallet
}
acctName, err := w.AccountName(waddrmgr.KeyScopeBIP0044, account)
acctName, err := w.AccountName(account)
if err != nil {
return nil, &ErrAccountNameNotFound
}
@ -705,16 +709,22 @@ func getAccount(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
func getAccountAddress(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
cmd := icmd.(*btcjson.GetAccountAddressCmd)
account, err := w.AccountNumber(waddrmgr.KeyScopeBIP0044, cmd.Account)
if err != nil {
return nil, err
}
addr, err := w.CurrentAddress(account, waddrmgr.KeyScopeBIP0044)
account, err := w.AccountNumber(*cmd.Account)
if err != nil {
return nil, err
}
return addr.EncodeAddress(), err
scope, err := lookupKeyScope(cmd.AddressType)
if err != nil {
return nil, err
}
addr, err := w.CurrentAddress(account, *scope)
if err != nil {
return nil, err
}
return addr.EncodeAddress(), nil
}
// getUnconfirmedBalance handles a getunconfirmedbalance extension request
@ -722,14 +732,11 @@ func getAccountAddress(icmd interface{}, w *wallet.Wallet) (interface{}, error)
func getUnconfirmedBalance(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
cmd := icmd.(*btcjson.GetUnconfirmedBalanceCmd)
acctName := defaultAccountName
if cmd.Account != nil {
acctName = *cmd.Account
}
account, err := w.AccountNumber(waddrmgr.KeyScopeBIP0044, acctName)
account, err := w.AccountNumber(*cmd.Account)
if err != nil {
return nil, err
}
bals, err := w.CalculateAccountBalances(account, 1)
if err != nil {
return nil, err
@ -787,6 +794,7 @@ func keypoolRefill(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
// returning a new account. If the last account has no transaction history
// as per BIP 0044 a new account cannot be created so an error will be returned.
func createNewAccount(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
cmd := icmd.(*btcjson.CreateNewAccountCmd)
// The wildcard * is reserved by the rpc server with the special meaning
@ -795,7 +803,11 @@ func createNewAccount(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
return nil, &ErrReservedAccountName
}
_, err := w.NextAccount(waddrmgr.KeyScopeBIP0044, cmd.Account)
fn := func(scope waddrmgr.KeyScope) error {
_, err := w.NextAccount(scope, cmd.Account)
return err
}
err := forEachKeyScope(fn)
if waddrmgr.IsError(err, waddrmgr.ErrLocked) {
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCWalletUnlockNeeded,
@ -803,12 +815,14 @@ func createNewAccount(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
"Enter the wallet passphrase with walletpassphrase to unlock",
}
}
return nil, err
return nil, nil
}
// renameAccount handles a renameaccount request by renaming an account.
// If the account does not exist an appropriate error will be returned.
func renameAccount(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
cmd := icmd.(*btcjson.RenameAccountCmd)
// The wildcard * is reserved by the rpc server with the special meaning
@ -818,11 +832,18 @@ func renameAccount(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
}
// Check that given account exists
account, err := w.AccountNumber(waddrmgr.KeyScopeBIP0044, cmd.OldAccount)
account, err := w.AccountNumber(cmd.OldAccount)
if err != nil {
return nil, err
}
return nil, w.RenameAccount(waddrmgr.KeyScopeBIP0044, account, cmd.NewAccount)
// Interate over all key scopes and rename the account.
fn := func(scope waddrmgr.KeyScope) error {
return w.RenameAccount(scope, account, cmd.NewAccount)
}
err = forEachKeyScope(fn)
return nil, err
}
func lookupKeyScope(kind *string) (*waddrmgr.KeyScope, error) {
@ -855,17 +876,17 @@ func getNewAddress(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
acctName = *cmd.Account
}
keyScope, err := lookupKeyScope(cmd.AddressType)
account, err := w.AccountNumber(acctName)
if err != nil {
return nil, err
}
account, err := w.AccountNumber(*keyScope, acctName)
scope, err := lookupKeyScope(cmd.AddressType)
if err != nil {
return nil, err
}
addr, err := w.NewAddress(account, *keyScope)
addr, err := w.NewAddress(account, *scope)
if err != nil {
return nil, err
}
@ -880,17 +901,24 @@ func getNewAddress(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
// Note: bitcoind allows specifying the account as an optional parameter,
// but ignores the parameter.
func getRawChangeAddress(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
cmd := icmd.(*btcjson.GetRawChangeAddressCmd)
acctName := defaultAccountName
if cmd.Account != nil {
acctName = *cmd.Account
}
account, err := w.AccountNumber(waddrmgr.KeyScopeBIP0044, acctName)
account, err := w.AccountNumber(*cmd.Account)
if err != nil {
return nil, err
}
addr, err := w.NewChangeAddress(account, waddrmgr.KeyScopeBIP0044)
// Use specified scope, if provided.
scope, err := lookupKeyScope(cmd.AddressType)
if err != nil {
return nil, err
}
if scope == nil {
scope = &waddrmgr.DefaultKeyScope
}
addr, err := w.NewChangeAddress(account, *scope)
if err != nil {
return nil, err
}
@ -902,27 +930,38 @@ func getRawChangeAddress(icmd interface{}, w *wallet.Wallet) (interface{}, error
// getReceivedByAccount handles a getreceivedbyaccount request by returning
// the total amount received by addresses of an account.
func getReceivedByAccount(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
cmd := icmd.(*btcjson.GetReceivedByAccountCmd)
account, err := w.AccountNumber(waddrmgr.KeyScopeBIP0044, cmd.Account)
account, err := w.AccountNumber(*cmd.Account)
if err != nil {
return nil, err
}
// TODO: This is more inefficient that it could be, but the entire
// algorithm is already dominated by reading every transaction in the
// wallet's history.
results, err := w.TotalReceivedForAccounts(
waddrmgr.KeyScopeBIP0044, int32(*cmd.MinConf),
)
if err != nil {
return nil, err
totalReceived := 0.0
var results []wallet.AccountTotalReceivedResult
fn := func(scope waddrmgr.KeyScope) error {
results, err = w.TotalReceivedForAccounts(
scope, int32(*cmd.MinConf))
if err != nil {
return err
}
acctIndex := int(account)
if account == waddrmgr.ImportedAddrAccount {
acctIndex = len(results) - 1
}
totalReceived += results[acctIndex].TotalReceived.ToBTC()
return nil
}
acctIndex := int(account)
if account == waddrmgr.ImportedAddrAccount {
acctIndex = len(results) - 1
}
return results[acctIndex].TotalReceived.ToBTC(), nil
err = forEachKeyScope(fn)
return totalReceived, err
}
// getReceivedByAddress handles a getreceivedbyaddress request by returning
@ -974,15 +1013,13 @@ func getTransaction(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
return nil, err
}
// TODO: Add a "generated" field to this result type. "generated":true
// is only added if the transaction is a coinbase.
ret := btcjson.GetTransactionResult{
TxID: cmd.Txid,
Hex: hex.EncodeToString(txBuf.Bytes()),
Time: details.Received.Unix(),
TimeReceived: details.Received.Unix(),
WalletConflicts: []string{}, // Not saved
//Generated: blockchain.IsCoinBaseTx(&details.MsgTx),
Generated: blockchain.IsCoinBaseTx(&details.MsgTx),
}
if details.Block.Height != -1 {
@ -1057,7 +1094,7 @@ func getTransaction(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
address = addr.EncodeAddress()
account, err := w.AccountOfAddress(addr)
if err == nil {
name, err := w.AccountName(waddrmgr.KeyScopeBIP0044, account)
name, err := w.AccountName(account)
if err == nil {
accountName = name
}
@ -1205,16 +1242,40 @@ func help(icmd interface{}, _ *wallet.Wallet, chainClient *chain.RPCClient) (int
func listAccounts(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
cmd := icmd.(*btcjson.ListAccountsCmd)
accountBalances := map[string]float64{}
results, err := w.AccountBalances(waddrmgr.KeyScopeBIP0044, int32(*cmd.MinConf))
accountBalances := map[string]map[string]float64{}
fn := func(scope waddrmgr.KeyScope) error {
results, err := w.AccountBalances(scope, int32(*cmd.MinConf))
if err != nil {
return err
}
for _, result := range results {
if accountBalances[result.AccountName] == nil {
accountBalances[result.AccountName] = map[string]float64{}
}
accountBalances[result.AccountName][scope.String()] += result.AccountBalance.ToBTC()
accountBalances[result.AccountName]["total"] += result.AccountBalance.ToBTC()
}
return nil
}
scope, err := lookupKeyScope(cmd.AddressType)
if err != nil {
return nil, err
}
for _, result := range results {
accountBalances[result.AccountName] = result.AccountBalance.ToBTC()
if scope != nil {
err = fn(*scope)
} else {
err = forEachKeyScope(fn)
}
if err != nil {
return nil, err
}
// Return the map. This will be marshaled into a JSON object.
return accountBalances, nil
return accountBalances, err
}
// listLockUnspent handles a listlockunspent request by returning an slice of
@ -1239,21 +1300,42 @@ func listLockUnspent(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
func listReceivedByAccount(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
cmd := icmd.(*btcjson.ListReceivedByAccountCmd)
results, err := w.TotalReceivedForAccounts(
waddrmgr.KeyScopeBIP0044, int32(*cmd.MinConf),
)
accounts := map[uint32]*wallet.AccountTotalReceivedResult{}
err := forEachKeyScope(func(scope waddrmgr.KeyScope) error {
results, err := w.TotalReceivedForAccounts(
scope, int32(*cmd.MinConf),
)
if err != nil {
return err
}
for _, result := range results {
account := accounts[result.AccountNumber]
if account == nil {
dup := result
account = &dup
accounts[result.AccountNumber] = account
}
account.TotalReceived += result.TotalReceived
if account.LastConfirmation < result.LastConfirmation {
account.LastConfirmation = result.LastConfirmation
}
}
return nil
})
if err != nil {
return nil, err
}
jsonResults := make([]btcjson.ListReceivedByAccountResult, 0, len(results))
for _, result := range results {
jsonResults = append(jsonResults, btcjson.ListReceivedByAccountResult{
Account: result.AccountName,
Amount: result.TotalReceived.ToBTC(),
Confirmations: uint64(result.LastConfirmation),
})
jsonResults := make([]btcjson.ListReceivedByAccountResult, 0, len(accounts))
for _, account := range accounts {
jsonResult := btcjson.ListReceivedByAccountResult{
Account: account.AccountName,
Amount: account.TotalReceived.ToBTC(),
Confirmations: uint64(account.LastConfirmation),
}
jsonResults = append(jsonResults, jsonResult)
}
return jsonResults, nil
}
@ -1353,7 +1435,6 @@ func listReceivedByAddress(icmd interface{}, w *wallet.Wallet) (interface{}, err
Amount: addrData.amount.ToBTC(),
Confirmations: uint64(addrData.confirmations),
TxIDs: addrData.tx,
Account: addrData.account,
}
idx++
}
@ -1386,7 +1467,7 @@ func listSinceBlock(icmd interface{}, w *wallet.Wallet, chainClient *chain.RPCCl
start = int32(block.Height) + 1
}
txInfoList, err := w.ListSinceBlock(start, -1, syncBlock.Height)
txInfoList, err := w.ListSinceBlock("*", start, -1, syncBlock.Height)
if err != nil {
return nil, err
}
@ -1407,23 +1488,10 @@ func listSinceBlock(icmd interface{}, w *wallet.Wallet, chainClient *chain.RPCCl
// listTransactions handles a listtransactions request by returning an
// array of maps with details of sent and recevied wallet transactions.
func listTransactions(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
cmd := icmd.(*btcjson.ListTransactionsCmd)
// TODO: ListTransactions does not currently understand the difference
// between transactions pertaining to one account from another. This
// will be resolved when wtxmgr is combined with the waddrmgr namespace.
if cmd.Account != nil && *cmd.Account != "*" && *cmd.Account != "default" {
// For now, don't bother trying to continue if the user
// specified an account, since this can't be (easily or
// efficiently) calculated.
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCWallet,
Message: "Transactions are not yet grouped by account",
}
}
return w.ListTransactions(*cmd.From, *cmd.Count)
return w.ListTransactions(*cmd.Account, *cmd.From, *cmd.Count)
}
// listAddressTransactions handles a listaddresstransactions request by
@ -1434,13 +1502,6 @@ func listTransactions(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
func listAddressTransactions(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
cmd := icmd.(*btcjson.ListAddressTransactionsCmd)
if cmd.Account != nil && *cmd.Account != "*" {
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCInvalidParameter,
Message: "Listing transactions for addresses may only be done for all accounts",
}
}
// Decode addresses.
hash160Map := make(map[string]struct{})
for _, addrStr := range cmd.Addresses {
@ -1451,7 +1512,7 @@ func listAddressTransactions(icmd interface{}, w *wallet.Wallet) (interface{}, e
hash160Map[string(addr.ScriptAddress())] = struct{}{}
}
return w.ListAddressTransactions(hash160Map)
return w.ListAddressTransactions(*cmd.Account, hash160Map)
}
// listAllTransactions handles a listalltransactions request by returning
@ -1459,16 +1520,10 @@ func listAddressTransactions(icmd interface{}, w *wallet.Wallet) (interface{}, e
// similar to ListTransactions, except it takes only a single optional
// argument for the account name and replies with all transactions.
func listAllTransactions(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
cmd := icmd.(*btcjson.ListAllTransactionsCmd)
if cmd.Account != nil && *cmd.Account != "*" {
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCInvalidParameter,
Message: "Listing all transactions may only be done for all accounts",
}
}
return w.ListAllTransactions()
return w.ListAllTransactions(*cmd.Account)
}
// listUnspent handles the listunspent command.
@ -1531,11 +1586,54 @@ func makeOutputs(pairs map[string]btcutil.Amount, chainParams *chaincfg.Params)
return outputs, nil
}
// rescanBlockchain handles a rescanblockhain RPC request.
func rescanBlockchain(icmd interface{}, w *wallet.Wallet,
chainClient *chain.RPCClient) (interface{}, error) {
cmd := icmd.(*btcjson.RescanBlockchainCmd)
_, bestHeight, err := chainClient.GetBestBlock()
if err != nil {
return nil, err
}
startHeight := *cmd.StartHeight
if startHeight < 0 || startHeight > bestHeight {
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCInvalidParameter,
Message: "Invalid start height",
}
}
// Scan to the best block if no stopHeight is specified.
stopHeight := bestHeight
if cmd.StopHeight != nil {
stopHeight = *cmd.StopHeight
}
if stopHeight < 0 || stopHeight > bestHeight {
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCInvalidParameter,
Message: "Invalid stop height",
}
}
startHeight, stopHeight, err = w.RescanBlockchain(chainClient,
startHeight, stopHeight)
if err != nil {
return nil, fmt.Errorf("rescanblockchain: %w", err)
}
ret := btcjson.RescanBlockchainResult{
StartHeight: startHeight,
StoptHeight: stopHeight,
}
return ret, nil
}
// sendPairs creates and sends payment transactions.
// It returns the transaction hash in string format upon success
// All errors are returned in btcjson.RPCError format
func sendPairs(w *wallet.Wallet, amounts map[string]btcutil.Amount,
keyScope waddrmgr.KeyScope, account uint32, minconf int32,
keyScope *waddrmgr.KeyScope, account uint32, minconf int32,
feeSatPerKb btcutil.Amount) (string, error) {
outputs, err := makeOutputs(amounts, w.ChainParams())
@ -1543,7 +1641,7 @@ func sendPairs(w *wallet.Wallet, amounts map[string]btcutil.Amount,
return "", err
}
tx, err := w.SendOutputs(
outputs, &keyScope, account, minconf, feeSatPerKb,
outputs, keyScope, account, minconf, feeSatPerKb,
wallet.CoinSelectionLargest, "",
)
if err != nil {
@ -1577,7 +1675,9 @@ func isNilOrEmpty(s *string) bool {
// address. Leftover inputs not sent to the payment address or a fee for
// the miner are sent back to a new address in the wallet. Upon success,
// the TxID for the created transaction is returned.
func sendFrom(icmd interface{}, w *wallet.Wallet, chainClient *chain.RPCClient) (interface{}, error) {
func sendFrom(icmd interface{}, w *wallet.Wallet,
chainClient *chain.RPCClient) (interface{}, error) {
cmd := icmd.(*btcjson.SendFromCmd)
// Transaction comments are not yet supported. Error instead of
@ -1589,9 +1689,7 @@ func sendFrom(icmd interface{}, w *wallet.Wallet, chainClient *chain.RPCClient)
}
}
account, err := w.AccountNumber(
waddrmgr.KeyScopeBIP0044, cmd.FromAccount,
)
account, err := w.AccountNumber(cmd.FromAccount)
if err != nil {
return nil, err
}
@ -1604,6 +1702,7 @@ func sendFrom(icmd interface{}, w *wallet.Wallet, chainClient *chain.RPCClient)
if minConf < 0 {
return nil, ErrNeedPositiveMinconf
}
// Create map of address and amount pairs.
amt, err := btcutil.NewAmount(cmd.Amount)
if err != nil {
@ -1613,7 +1712,13 @@ func sendFrom(icmd interface{}, w *wallet.Wallet, chainClient *chain.RPCClient)
cmd.ToAddress: amt,
}
return sendPairs(w, pairs, waddrmgr.KeyScopeBIP0044, account, minConf,
// Use specified scope, if provided.
scope, err := lookupKeyScope(cmd.AddressType)
if err != nil {
return nil, err
}
return sendPairs(w, pairs, scope, account, minConf,
txrules.DefaultRelayFeePerKb)
}
@ -1623,6 +1728,7 @@ func sendFrom(icmd interface{}, w *wallet.Wallet, chainClient *chain.RPCClient)
// or a fee for the miner are sent back to a new address in the wallet.
// Upon success, the TxID for the created transaction is returned.
func sendMany(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
cmd := icmd.(*btcjson.SendManyCmd)
// Transaction comments are not yet supported. Error instead of
@ -1634,7 +1740,7 @@ func sendMany(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
}
}
account, err := w.AccountNumber(waddrmgr.KeyScopeBIP0044, cmd.FromAccount)
account, err := w.AccountNumber(cmd.FromAccount)
if err != nil {
return nil, err
}
@ -1645,6 +1751,12 @@ func sendMany(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
return nil, ErrNeedPositiveMinconf
}
// Use specified scope, if provided.
scope, err := lookupKeyScope(cmd.AddressType)
if err != nil {
return nil, err
}
// Recreate address/amount pairs, using dcrutil.Amount.
pairs := make(map[string]btcutil.Amount, len(cmd.Amounts))
for k, v := range cmd.Amounts {
@ -1655,7 +1767,7 @@ func sendMany(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
pairs[k] = amt
}
return sendPairs(w, pairs, waddrmgr.KeyScopeBIP0044, account, minConf, txrules.DefaultRelayFeePerKb)
return sendPairs(w, pairs, scope, account, minConf, txrules.DefaultRelayFeePerKb)
}
// sendToAddress handles a sendtoaddress RPC request by creating a new
@ -1690,8 +1802,14 @@ func sendToAddress(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
cmd.Address: amt,
}
// Use specified scope, if provided.
scope, err := lookupKeyScope(cmd.AddressType)
if err != nil {
return nil, err
}
// sendtoaddress always spends from the default account, this matches bitcoind
return sendPairs(w, pairs, waddrmgr.KeyScopeBIP0044, waddrmgr.DefaultAccountNum, 1,
return sendPairs(w, pairs, scope, waddrmgr.DefaultAccountNum, 1,
txrules.DefaultRelayFeePerKb)
}
@ -1946,9 +2064,7 @@ func validateAddress(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
// The address lookup was successful which means there is further
// information about it available and it is "mine".
result.IsMine = true
acctName, err := w.AccountName(
waddrmgr.KeyScopeBIP0044, ainfo.InternalAccount(),
)
acctName, err := w.AccountName(ainfo.InternalAccount())
if err != nil {
return nil, &ErrAccountNameNotFound
}
@ -2082,7 +2198,7 @@ func walletPassphrase(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
func walletPassphraseChange(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
cmd := icmd.(*btcjson.WalletPassphraseChangeCmd)
err := w.ChangePrivatePassphrase([]byte(cmd.OldPassphrase),
err := w.ChangePassphrase([]byte(cmd.OldPassphrase),
[]byte(cmd.NewPassphrase))
if waddrmgr.IsError(err, waddrmgr.ErrWrongPassphrase) {
return nil, &btcjson.RPCError{
@ -2110,3 +2226,17 @@ func decodeHexStr(hexStr string) ([]byte, error) {
}
return decoded, nil
}
// forEachKeyScope calls the given function with each default key scopes
// breaking early on error.
func forEachKeyScope(fn func(scope waddrmgr.KeyScope) error) error {
for _, scope := range waddrmgr.DefaultKeyScopes {
err := fn(scope)
if err != nil {
return err
}
}
return nil
}

View file

@ -4,51 +4,52 @@ package legacyrpc
func helpDescsEnUS() map[string]string {
return map[string]string{
"addmultisigaddress": "addmultisigaddress nrequired [\"key\",...] (\"account\")\n\nGenerates and imports a multisig address and redeeming script to the 'imported' account.\n\nArguments:\n1. nrequired (numeric, required) The number of signatures required to redeem outputs paid to this address\n2. keys (array of string, required) Pubkeys and/or pay-to-pubkey-hash addresses to partially control the multisig address\n3. account (string, optional) DEPRECATED -- Unused (all imported addresses belong to the imported account)\n\nResult:\n\"value\" (string) The imported pay-to-script-hash address\n",
"createmultisig": "createmultisig nrequired [\"key\",...]\n\nGenerate a multisig address and redeem script.\n\nArguments:\n1. nrequired (numeric, required) The number of signatures required to redeem outputs paid to this address\n2. keys (array of string, required) Pubkeys and/or pay-to-pubkey-hash addresses to partially control the multisig address\n\nResult:\n{\n \"address\": \"value\", (string) The generated pay-to-script-hash address\n \"redeemScript\": \"value\", (string) The script required to redeem outputs paid to the multisig address\n} \n",
"dumpprivkey": "dumpprivkey \"address\"\n\nReturns the private key in WIF encoding that controls some wallet address.\n\nArguments:\n1. address (string, required) The address to return a private key for\n\nResult:\n\"value\" (string) The WIF-encoded private key\n",
"getaccount": "getaccount \"address\"\n\nDEPRECATED -- Lookup the account name that some wallet address belongs to.\n\nArguments:\n1. address (string, required) The address to query the account for\n\nResult:\n\"value\" (string) The name of the account that 'address' belongs to\n",
"getaccountaddress": "getaccountaddress \"account\"\n\nDEPRECATED -- Returns the most recent external payment address for an account that has not been seen publicly.\nA new address is generated for the account if the most recently generated address has been seen on the blockchain or in mempool.\n\nArguments:\n1. account (string, required) The account of the returned address\n\nResult:\n\"value\" (string) The unused address for 'account'\n",
"getaddressesbyaccount": "getaddressesbyaccount \"account\"\n\nDEPRECATED -- Returns all addresses strings controlled by a single account.\n\nArguments:\n1. account (string, required) Account name to fetch addresses for\n\nResult:\n[\"value\",...] (array of string) All addresses controlled by 'account'\n",
"getaddressinfo": "getaddressinfo \"address\"\n\nGenerates and returns a new payment address.\n\nArguments:\n1. address (string, required) The address to get the information of.\n\nResult:\n{\n \"address\": \"value\", (string) The address validated\n \"scriptPubKey\": \"value\", (string) The hex-encoded scriptPubKey generated by the address.\n \"desc\": \"value\", (string) A descriptor for spending coins sent to this address (only when solvable).\n \"isscript\": true|false, (boolean) If the key is a script.\n \"ischange\": true|false, (boolean) If the address was used for change output.\n \"iswitness\": true|false, (boolean) If the address is a witness address.\n \"witness_version\": n, (numeric) The version number of the witness program.\n \"witness_program\": \"value\", (string) The hex value of the witness program.\n \"script\": n, (numeric) The output script type. Only if isscript is true and the redeemscript is known. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash, witness_unknown.\n \"hex\": \"value\", (string) The redeemscript for the p2sh address.\n \"pubkeys\": [\"value\",...], (array of string) The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH).\n \"sigsrequired\": n, (numeric) The number of signatures required to spend multisig output (only if script is multisig).\n \"pubkey\": \"value\", (string) Array of pubkeys associated with the known redeemscript (only if script is multisig).\n \"iscompressed\": true|false, (boolean) If the pubkey is compressed.\n \"hdmasterfingerprint\": \"value\", (string) The fingerprint of the master key.\n \"labels\": [\"value\",...], (array of string) Array of labels associated with the address. Currently limited to one label but returned.\n \"ismine\": true|false, (boolean) If the address is yours.\n \"iswatchonly\": true|false, (boolean) If the address is watchonly.\n \"timestamp\": n, (numeric) The creation time of the key, if available, expressed in UNIX epoch time.\n \"hdkeypath\": \"value\", (string) The HD keypath, if the key is HD and available.\n \"hdseedid\": \"value\", (string) The Hash160 of the HD seed.\n \"embedded\": { (object) Information about the address embedded in P2SH or P2WSH, if relevant and known.\n \"address\": \"value\", (string) The address validated\n \"scriptPubKey\": \"value\", (string) The hex-encoded scriptPubKey generated by the address.\n \"desc\": \"value\", (string) A descriptor for spending coins sent to this address (only when solvable).\n \"isscript\": true|false, (boolean) If the key is a script.\n \"ischange\": true|false, (boolean) If the address was used for change output.\n \"iswitness\": true|false, (boolean) If the address is a witness address.\n \"witness_version\": n, (numeric) The version number of the witness program.\n \"witness_program\": \"value\", (string) The hex value of the witness program.\n \"script\": n, (numeric) The output script type. Only if isscript is true and the redeemscript is known. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash, witness_unknown.\n \"hex\": \"value\", (string) The redeemscript for the p2sh address.\n \"pubkeys\": [\"value\",...], (array of string) The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH).\n \"sigsrequired\": n, (numeric) The number of signatures required to spend multisig output (only if script is multisig).\n \"pubkey\": \"value\", (string) Array of pubkeys associated with the known redeemscript (only if script is multisig).\n \"iscompressed\": true|false, (boolean) If the pubkey is compressed.\n \"hdmasterfingerprint\": \"value\", (string) The fingerprint of the master key.\n \"labels\": [\"value\",...], (array of string) Array of labels associated with the address. Currently limited to one label but returned.\n }, \n} \n",
"getbalance": "getbalance (\"account\" minconf=1)\n\nCalculates and returns the balance of one or all accounts.\n\nArguments:\n1. account (string, optional) DEPRECATED -- The account name to query the balance for, or \"*\" to consider all accounts (default=\"*\")\n2. minconf (numeric, optional, default=1) Minimum number of block confirmations required before an unspent output's value is included in the balance\n\nResult (account != \"*\"):\nn.nnn (numeric) The balance of 'account' valued in bitcoin\n\nResult (account = \"*\"):\nn.nnn (numeric) The balance of all accounts valued in bitcoin\n",
"getbestblockhash": "getbestblockhash\n\nReturns the hash of the newest block in the best chain that wallet has finished syncing with.\n\nArguments:\nNone\n\nResult:\n\"value\" (string) The hash of the most recent synced-to block\n",
"getblockcount": "getblockcount\n\nReturns the blockchain height of the newest block in the best chain that wallet has finished syncing with.\n\nArguments:\nNone\n\nResult:\nn.nnn (numeric) The blockchain height of the most recent synced-to block\n",
"getinfo": "getinfo\n\nReturns a JSON object containing various state info.\n\nArguments:\nNone\n\nResult:\n{\n \"version\": n, (numeric) The version of the server\n \"protocolversion\": n, (numeric) The latest supported protocol version\n \"walletversion\": n, (numeric) The version of the address manager database\n \"balance\": n.nnn, (numeric) The non-staked balance of all accounts calculated with one block confirmation\n \"blocks\": n, (numeric) The number of blocks processed\n \"timeoffset\": n, (numeric) The time offset\n \"connections\": n, (numeric) The number of connected peers\n \"proxy\": \"value\", (string) The proxy used by the server\n \"difficulty\": n.nnn, (numeric) The current target difficulty\n \"testnet\": true|false, (boolean) Whether or not server is using testnet\n \"keypoololdest\": n, (numeric) Unset\n \"keypoolsize\": n, (numeric) Unset\n \"unlocked_until\": n, (numeric) Unset\n \"paytxfee\": n.nnn, (numeric) The increment used each time more fee is required for an authored transaction\n \"relayfee\": n.nnn, (numeric) The minimum relay fee for non-free transactions in LBC/KB\n \"errors\": \"value\", (string) Any current errors\n \"staked\": n.nnn, (numeric) The staked balance of all accounts calculated with one block confirmation\n} \n",
"getnewaddress": "getnewaddress (\"account\" \"addresstype\")\n\nGenerates and returns a new payment address.\n\nArguments:\n1. account (string, optional) DEPRECATED -- Account name the new address will belong to (default=\"default\")\n2. addresstype (string, optional) Address type. Must be one of legacy / p2pkh, p2sh-p2wpkh / p2sh-p2wkh / p2sh-segwit, or p2wpkh / p2wkh / bech32\n\nResult:\n\"value\" (string) The payment address\n",
"getrawchangeaddress": "getrawchangeaddress (\"account\")\n\nGenerates and returns a new internal payment address for use as a change address in raw transactions.\n\nArguments:\n1. account (string, optional) Account name the new internal address will belong to (default=\"default\")\n\nResult:\n\"value\" (string) The internal payment address\n",
"getreceivedbyaccount": "getreceivedbyaccount \"account\" (minconf=1)\n\nDEPRECATED -- Returns the total amount received by addresses of some account, including spent outputs.\n\nArguments:\n1. account (string, required) Account name to query total received amount for\n2. minconf (numeric, optional, default=1) Minimum number of block confirmations required before an output's value is included in the total\n\nResult:\nn.nnn (numeric) The total received amount valued in bitcoin\n",
"getreceivedbyaddress": "getreceivedbyaddress \"address\" (minconf=1)\n\nReturns the total amount received by a single address, including spent outputs.\n\nArguments:\n1. address (string, required) Payment address which received outputs to include in total\n2. minconf (numeric, optional, default=1) Minimum number of block confirmations required before an output's value is included in the total\n\nResult:\nn.nnn (numeric) The total received amount valued in bitcoin\n",
"gettransaction": "gettransaction \"txid\" (includewatchonly=false)\n\nReturns a JSON object with details regarding a transaction relevant to this wallet.\n\nArguments:\n1. txid (string, required) Hash of the transaction to query\n2. includewatchonly (boolean, optional, default=false) Also consider transactions involving watched addresses\n\nResult:\n{\n \"amount\": n.nnn, (numeric) The total amount this transaction credits to the wallet, valued in bitcoin\n \"fee\": n.nnn, (numeric) The total input value minus the total output value, or 0 if 'txid' is not a sent transaction\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined\n \"blockindex\": n, (numeric) Unset\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined\n \"txid\": \"value\", (string) The transaction hash\n \"walletconflicts\": [\"value\",...], (array of string) Unset\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"details\": [{ (array of object) Additional details for each recorded wallet credit and debit\n \"account\": \"value\", (string) DEPRECATED -- Unset\n \"address\": \"value\", (string) The address an output was paid to, or the empty string if the output is nonstandard or this detail is regarding a transaction input\n \"amount\": n.nnn, (numeric) The amount of a received output\n \"category\": \"value\", (string) The kind of detail: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs\n \"involveswatchonly\": true|false, (boolean) Unset\n \"fee\": n.nnn, (numeric) The included fee for a sent transaction\n \"vout\": n, (numeric) The transaction output index\n },...], \n \"hex\": \"value\", (string) The transaction encoded as a hexadecimal string\n} \n",
"help": "help (\"command\")\n\nReturns a list of all commands or help for a specified command.\n\nArguments:\n1. command (string, optional) The command to retrieve help for\n\nResult (no command provided):\n\"value\" (string) List of commands\n\nResult (command specified):\n\"value\" (string) Help for specified command\n",
"importprivkey": "importprivkey \"privkey\" (\"label\" rescan=true)\n\nImports a WIF-encoded private key to the 'imported' account.\n\nArguments:\n1. privkey (string, required) The WIF-encoded private key\n2. label (string, optional) Unused (must be unset or 'imported')\n3. rescan (boolean, optional, default=true) Rescan the blockchain (since the genesis block) for outputs controlled by the imported key\n\nResult:\nNothing\n",
"keypoolrefill": "keypoolrefill (newsize=100)\n\nDEPRECATED -- This request does nothing since no keypool is maintained.\n\nArguments:\n1. newsize (numeric, optional, default=100) Unused\n\nResult:\nNothing\n",
"listaccounts": "listaccounts (minconf=1)\n\nDEPRECATED -- Returns a JSON object of all accounts and their balances.\n\nArguments:\n1. minconf (numeric, optional, default=1) Minimum number of block confirmations required before an unspent output's value is included in the balance\n\nResult:\n{\n \"The account name\": The account balance valued in bitcoin, (object) JSON object with account names as keys and bitcoin amounts as values\n ...\n}\n",
"listlockunspent": "listlockunspent\n\nReturns a JSON array of outpoints marked as locked (with lockunspent) for this wallet session.\n\nArguments:\nNone\n\nResult:\n[{\n \"txid\": \"value\", (string) The transaction hash of the referenced output\n \"vout\": n, (numeric) The output index of the referenced output\n},...]\n",
"listreceivedbyaccount": "listreceivedbyaccount (minconf=1 includeempty=false includewatchonly=false)\n\nDEPRECATED -- Returns a JSON array of objects listing all accounts and the total amount received by each account.\n\nArguments:\n1. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction is considered\n2. includeempty (boolean, optional, default=false) Unused\n3. includewatchonly (boolean, optional, default=false) Unused\n\nResult:\n[{\n \"account\": \"value\", (string) The name of the account\n \"amount\": n.nnn, (numeric) Total amount received by payment addresses of the account valued in bitcoin\n \"confirmations\": n, (numeric) Number of block confirmations of the most recent transaction relevant to the account\n},...]\n",
"listreceivedbyaddress": "listreceivedbyaddress (minconf=1 includeempty=false includewatchonly=false)\n\nReturns a JSON array of objects listing wallet payment addresses and their total received amounts.\n\nArguments:\n1. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction is considered\n2. includeempty (boolean, optional, default=false) Unused\n3. includewatchonly (boolean, optional, default=false) Unused\n\nResult:\n[{\n \"account\": \"value\", (string) DEPRECATED -- Unset\n \"address\": \"value\", (string) The payment address\n \"amount\": n.nnn, (numeric) Total amount received by the payment address valued in bitcoin\n \"confirmations\": n, (numeric) Number of block confirmations of the most recent transaction relevant to the address\n \"txids\": [\"value\",...], (array of string) Transaction hashes of all transactions involving this address\n \"involvesWatchonly\": true|false, (boolean) Unset\n},...]\n",
"listsinceblock": "listsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\n\nReturns a JSON array of objects listing details of all wallet transactions after some block.\n\nArguments:\n1. blockhash (string, optional) Hash of the parent block of the first block to consider transactions from, or unset to list all transactions\n2. targetconfirmations (numeric, optional, default=1) Minimum number of block confirmations of the last block in the result object. Must be 1 or greater. Note: The transactions array in the result object is not affected by this parameter\n3. includewatchonly (boolean, optional, default=false) Unused\n\nResult:\n{\n \"transactions\": [{ (array of object) JSON array of objects containing verbose details of the each transaction\n \"abandoned\": true|false, (boolean) Unset\n \"account\": \"value\", (string) DEPRECATED -- Unset\n \"address\": \"value\", (string) Payment address for a transaction output\n \"amount\": n.nnn, (numeric) The value of the transaction output valued in bitcoin\n \"bip125-replaceable\": \"value\", (string) Unset\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined\n \"blockheight\": n, (numeric) The block height containing the transaction.\n \"blockindex\": n, (numeric) Unset\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined\n \"category\": \"value\", (string) The kind of transaction: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs. Note: A single output may be included multiple times under different categories\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"fee\": n.nnn, (numeric) The total input value minus the total output value for sent transactions\n \"generated\": true|false, (boolean) Whether the transaction output is a coinbase output\n \"involveswatchonly\": true|false, (boolean) Unset\n \"label\": \"value\", (string) A comment for the address/transaction, if any\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"trusted\": true|false, (boolean) Unset\n \"txid\": \"value\", (string) The hash of the transaction\n \"vout\": n, (numeric) The transaction output index\n \"walletconflicts\": [\"value\",...], (array of string) Unset\n \"comment\": \"value\", (string) Unset\n \"otheraccount\": \"value\", (string) Unset\n },...], \n \"lastblock\": \"value\", (string) Hash of the latest-synced block to be used in later calls to listsinceblock\n} \n",
"listtransactions": "listtransactions (\"account\" count=10 from=0 includewatchonly=false)\n\nReturns a JSON array of objects containing verbose details for wallet transactions.\n\nArguments:\n1. account (string, optional) DEPRECATED -- Unused (must be unset, \"default\" or \"*\")\n2. count (numeric, optional, default=10) Maximum number of transactions to create results from\n3. from (numeric, optional, default=0) Number of transactions to skip before results are created\n4. includewatchonly (boolean, optional, default=false) Unused\n\nResult:\n[{\n \"abandoned\": true|false, (boolean) Unset\n \"account\": \"value\", (string) DEPRECATED -- Unset\n \"address\": \"value\", (string) Payment address for a transaction output\n \"amount\": n.nnn, (numeric) The value of the transaction output valued in bitcoin\n \"bip125-replaceable\": \"value\", (string) Unset\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined\n \"blockheight\": n, (numeric) The block height containing the transaction.\n \"blockindex\": n, (numeric) Unset\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined\n \"category\": \"value\", (string) The kind of transaction: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs. Note: A single output may be included multiple times under different categories\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"fee\": n.nnn, (numeric) The total input value minus the total output value for sent transactions\n \"generated\": true|false, (boolean) Whether the transaction output is a coinbase output\n \"involveswatchonly\": true|false, (boolean) Unset\n \"label\": \"value\", (string) A comment for the address/transaction, if any\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"trusted\": true|false, (boolean) Unset\n \"txid\": \"value\", (string) The hash of the transaction\n \"vout\": n, (numeric) The transaction output index\n \"walletconflicts\": [\"value\",...], (array of string) Unset\n \"comment\": \"value\", (string) Unset\n \"otheraccount\": \"value\", (string) Unset\n},...]\n",
"listunspent": "listunspent (minconf=1 maxconf=9999999 [\"address\",...])\n\nReturns a JSON array of objects representing unlocked unspent outputs controlled by wallet keys.\n\nArguments:\n1. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction output is considered\n2. maxconf (numeric, optional, default=9999999) Maximum number of block confirmations required before a transaction output is excluded\n3. addresses (array of string, optional) If set, limits the returned details to unspent outputs received by any of these payment addresses\n\nResult:\n{\n \"txid\": \"value\", (string) The transaction hash of the referenced output\n \"vout\": n, (numeric) The output index of the referenced output\n \"address\": \"value\", (string) The payment address that received the output\n \"account\": \"value\", (string) The account associated with the receiving payment address\n \"scriptPubKey\": \"value\", (string) The output script encoded as a hexadecimal string\n \"redeemScript\": \"value\", (string) Unset\n \"amount\": n.nnn, (numeric) The amount of the output valued in bitcoin\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"solvable\": true|false, (boolean) Whether the output is solvable\n \"spendable\": true|false, (boolean) Whether the output is entirely controlled by wallet keys/scripts (false for partially controlled multisig outputs or outputs to watch-only addresses)\n \"isstake\": true|false, (boolean) Whether the output is staked\n} \n",
"lockunspent": "lockunspent unlock [{\"txid\":\"value\",\"vout\":n},...]\n\nLocks or unlocks an unspent output.\nLocked outputs are not chosen for transaction inputs of authored transactions and are not included in 'listunspent' results.\nLocked outputs are volatile and are not saved across wallet restarts.\nIf unlock is true and no transaction outputs are specified, all locked outputs are marked unlocked.\n\nArguments:\n1. unlock (boolean, required) True to unlock outputs, false to lock\n2. transactions (array of object, required) Transaction outputs to lock or unlock\n[{\n \"txid\": \"value\", (string) The transaction hash of the referenced output\n \"vout\": n, (numeric) The output index of the referenced output\n},...]\n\nResult:\ntrue|false (boolean) The boolean 'true'\n",
"sendfrom": "sendfrom \"fromaccount\" \"toaddress\" amount (minconf=1 \"comment\" \"commentto\")\n\nDEPRECATED -- Authors, signs, and sends a transaction that outputs some amount to a payment address.\nA change output is automatically included to send extra output value back to the original account.\n\nArguments:\n1. fromaccount (string, required) Account to pick unspent outputs from\n2. toaddress (string, required) Address to pay\n3. amount (numeric, required) Amount to send to the payment address valued in bitcoin\n4. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction output is eligible to be spent\n5. comment (string, optional) Unused\n6. commentto (string, optional) Unused\n\nResult:\n\"value\" (string) The transaction hash of the sent transaction\n",
"sendmany": "sendmany \"fromaccount\" {\"address\":amount,...} (minconf=1 \"comment\")\n\nAuthors, signs, and sends a transaction that outputs to many payment addresses.\nA change output is automatically included to send extra output value back to the original account.\n\nArguments:\n1. fromaccount (string, required) DEPRECATED -- Account to pick unspent outputs from\n2. amounts (object, required) Pairs of payment addresses and the output amount to pay each\n{\n \"Address to pay\": Amount to send to the payment address valued in bitcoin, (object) JSON object using payment addresses as keys and output amounts valued in bitcoin to send to each address\n ...\n}\n3. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction output is eligible to be spent\n4. comment (string, optional) Unused\n\nResult:\n\"value\" (string) The transaction hash of the sent transaction\n",
"sendtoaddress": "sendtoaddress \"address\" amount (\"comment\" \"commentto\")\n\nAuthors, signs, and sends a transaction that outputs some amount to a payment address.\nUnlike sendfrom, outputs are always chosen from the default account.\nA change output is automatically included to send extra output value back to the original account.\n\nArguments:\n1. address (string, required) Address to pay\n2. amount (numeric, required) Amount to send to the payment address valued in bitcoin\n3. comment (string, optional) Unused\n4. commentto (string, optional) Unused\n\nResult:\n\"value\" (string) The transaction hash of the sent transaction\n",
"settxfee": "settxfee amount\n\nModify the increment used each time more fee is required for an authored transaction.\n\nArguments:\n1. amount (numeric, required) The new fee increment valued in bitcoin\n\nResult:\ntrue|false (boolean) The boolean 'true'\n",
"signmessage": "signmessage \"address\" \"message\"\n\nSigns a message using the private key of a payment address.\n\nArguments:\n1. address (string, required) Payment address of private key used to sign the message with\n2. message (string, required) Message to sign\n\nResult:\n\"value\" (string) The signed message encoded as a base64 string\n",
"signrawtransaction": "signrawtransaction \"rawtx\" ([{\"txid\":\"value\",\"vout\":n,\"scriptpubkey\":\"value\",\"redeemscript\":\"value\"},...] [\"privkey\",...] flags=\"ALL\")\n\nSigns transaction inputs using private keys from this wallet and request.\nThe valid flags options are ALL, NONE, SINGLE, ALL|ANYONECANPAY, NONE|ANYONECANPAY, and SINGLE|ANYONECANPAY.\n\nArguments:\n1. rawtx (string, required) Unsigned or partially unsigned transaction to sign encoded as a hexadecimal string\n2. inputs (array of object, optional) Additional data regarding inputs that this wallet may not be tracking\n3. privkeys (array of string, optional) Additional WIF-encoded private keys to use when creating signatures\n4. flags (string, optional, default=\"ALL\") Sighash flags\n\nResult:\n{\n \"hex\": \"value\", (string) The resulting transaction encoded as a hexadecimal string\n \"complete\": true|false, (boolean) Whether all input signatures have been created\n \"errors\": [{ (array of object) Script verification errors (if exists)\n \"txid\": \"value\", (string) The transaction hash of the referenced previous output\n \"vout\": n, (numeric) The output index of the referenced previous output\n \"scriptSig\": \"value\", (string) The hex-encoded signature script\n \"sequence\": n, (numeric) Script sequence number\n \"error\": \"value\", (string) Verification or signing error related to the input\n },...], \n} \n",
"validateaddress": "validateaddress \"address\"\n\nVerify that an address is valid.\nExtra details are returned if the address is controlled by this wallet.\nThe following fields are valid only when the address is controlled by this wallet (ismine=true): isscript, pubkey, iscompressed, account, addresses, hex, script, and sigsrequired.\nThe following fields are only valid when address has an associated public key: pubkey, iscompressed.\nThe following fields are only valid when address is a pay-to-script-hash address: addresses, hex, and script.\nIf the address is a multisig address controlled by this wallet, the multisig fields will be left unset if the wallet is locked since the redeem script cannot be decrypted.\n\nArguments:\n1. address (string, required) Address to validate\n\nResult:\n{\n \"isvalid\": true|false, (boolean) Whether or not the address is valid\n \"address\": \"value\", (string) The payment address (only when isvalid is true)\n \"ismine\": true|false, (boolean) Whether this address is controlled by the wallet (only when isvalid is true)\n \"iswatchonly\": true|false, (boolean) Unset\n \"isscript\": true|false, (boolean) Whether the payment address is a pay-to-script-hash address (only when isvalid is true)\n \"pubkey\": \"value\", (string) The associated public key of the payment address, if any (only when isvalid is true)\n \"iscompressed\": true|false, (boolean) Whether the address was created by hashing a compressed public key, if any (only when isvalid is true)\n \"account\": \"value\", (string) The account this payment address belongs to (only when isvalid is true)\n \"addresses\": [\"value\",...], (array of string) All associated payment addresses of the script if address is a multisig address (only when isvalid is true)\n \"hex\": \"value\", (string) The redeem script \n \"script\": \"value\", (string) The class of redeem script for a multisig address\n \"sigsrequired\": n, (numeric) The number of required signatures to redeem outputs to the multisig address\n} \n",
"verifymessage": "verifymessage \"address\" \"signature\" \"message\"\n\nVerify a message was signed with the associated private key of some address.\n\nArguments:\n1. address (string, required) Address used to sign message\n2. signature (string, required) The signature to verify\n3. message (string, required) The message to verify\n\nResult:\ntrue|false (boolean) Whether the message was signed with the private key of 'address'\n",
"addmultisigaddress": "addmultisigaddress nrequired [\"key\",...] (\"account\")\n\nGenerates and imports a multisig address and redeeming script to the 'imported' account.\n\nArguments:\n1. nrequired (numeric, required) The number of signatures required to redeem outputs paid to this address.\n2. keys (array of string, required) Pubkeys and/or pay-to-pubkey-hash addresses to partially control the multisig address.\n3. account (string, optional) DEPRECATED -- Unused (all imported addresses belong to the imported account).\n\nResult:\n\"value\" (string) The imported pay-to-script-hash address.\n",
"createmultisig": "createmultisig nrequired [\"key\",...]\n\nGenerate a multisig address and redeem script.\n\nArguments:\n1. nrequired (numeric, required) The number of signatures required to redeem outputs paid to this address.\n2. keys (array of string, required) Pubkeys and/or pay-to-pubkey-hash addresses to partially control the multisig address.\n\nResult:\n{\n \"address\": \"value\", (string) The generated pay-to-script-hash address.\n \"redeemScript\": \"value\", (string) The script required to redeem outputs paid to the multisig address.\n} \n",
"dumpprivkey": "dumpprivkey \"address\"\n\nReturns the private key in WIF encoding that controls some wallet address.\n\nArguments:\n1. address (string, required) The address to return a private key for.\n\nResult:\n\"value\" (string) The WIF-encoded private key.\n",
"getaccount": "getaccount \"address\"\n\nLookup the account name that some wallet address belongs to.\n\nArguments:\n1. address (string, required) The address to query the account for.\n\nResult:\n\"value\" (string) The name of the account that 'address' belongs to.\n",
"getaccountaddress": "getaccountaddress (account=\"default\" addresstype=\"legacy\")\n\nReturns the most recent external payment address for an account that has not been seen publicly.\nA new address is generated for the account if the most recently generated address has been seen on the blockchain or in mempool.\n\nArguments:\n1. account (string, optional, default=\"default\") The account of the returned address. Defaults to 'default'\n2. addresstype (string, optional, default=\"legacy\") Address type. Must be one of 'legacy', 'p2sh-segwit', or 'bech32'. Default to 'legacy'.\n\nResult:\n\"value\" (string) The unused address for 'account'.\n",
"getaddressesbyaccount": "getaddressesbyaccount (account=\"default\" addresstype=\"*\")\n\nReturns all addresses controlled by a single account.\n\nArguments:\n1. account (string, optional, default=\"default\") Account name to fetch addresses for. Defaults to 'default'\n2. addresstype (string, optional, default=\"*\") Address type filter. Must be one of 'legacy', 'p2sh-segwit', 'bech32', or '*'. Defaults to '*'.\n\nResult:\n[\"value\",...] (array of string) All addresses controlled by 'account' filtered by 'addresstype'.\n",
"getaddressinfo": "getaddressinfo \"address\"\n\nGenerates and returns a new payment address.\n\nArguments:\n1. address (string, required) The address to get the information of.\n\nResult:\n{\n \"address\": \"value\", (string) The address validatedi.\n \"scriptPubKey\": \"value\", (string) The hex-encoded scriptPubKey generated by the address.\n \"desc\": \"value\", (string) A descriptor for spending coins sent to this address (only when solvable).\n \"isscript\": true|false, (boolean) If the key is a script.\n \"ischange\": true|false, (boolean) If the address was used for change output.\n \"iswitness\": true|false, (boolean) If the address is a witness address.\n \"witness_version\": n, (numeric) The version number of the witness program.\n \"witness_program\": \"value\", (string) The hex value of the witness program.\n \"script\": n, (numeric) The output script type. Only if isscript is true and the redeemscript is known. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash, witness_unknown.\n \"hex\": \"value\", (string) The redeemscript for the p2sh address.\n \"pubkeys\": [\"value\",...], (array of string) The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH).\n \"sigsrequired\": n, (numeric) The number of signatures required to spend multisig output (only if script is multisig).\n \"pubkey\": \"value\", (string) Array of pubkeys associated with the known redeemscript (only if script is multisig).\n \"iscompressed\": true|false, (boolean) If the pubkey is compressed.\n \"hdmasterfingerprint\": \"value\", (string) The fingerprint of the master key.\n \"labels\": [\"value\",...], (array of string) Array of labels associated with the address. Currently limited to one label but returned.\n \"ismine\": true|false, (boolean) If the address is yours.\n \"iswatchonly\": true|false, (boolean) If the address is watchonly.\n \"timestamp\": n, (numeric) The creation time of the key, if available, expressed in UNIX epoch time.\n \"hdkeypath\": \"value\", (string) The HD keypath, if the key is HD and available.\n \"hdseedid\": \"value\", (string) The Hash160 of the HD seed.\n \"embedded\": { (object) Information about the address embedded in P2SH or P2WSH, if relevant and known.\n \"address\": \"value\", (string) The address validated.\n \"scriptPubKey\": \"value\", (string) The hex-encoded scriptPubKey generated by the address.\n \"desc\": \"value\", (string) A descriptor for spending coins sent to this address (only when solvable).\n \"isscript\": true|false, (boolean) If the key is a script.\n \"ischange\": true|false, (boolean) If the address was used for change output.\n \"iswitness\": true|false, (boolean) If the address is a witness address.\n \"witness_version\": n, (numeric) The version number of the witness program.\n \"witness_program\": \"value\", (string) The hex value of the witness program.\n \"script\": n, (numeric) The output script type. Only if isscript is true and the redeemscript is known. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash, witness_unknown.\n \"hex\": \"value\", (string) The redeemscript for the p2sh address.\n \"pubkeys\": [\"value\",...], (array of string) The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH).\n \"sigsrequired\": n, (numeric) The number of signatures required to spend multisig output (only if script is multisig).\n \"pubkey\": \"value\", (string) Array of pubkeys associated with the known redeemscript (only if script is multisig).\n \"iscompressed\": true|false, (boolean) If the pubkey is compressed.\n \"hdmasterfingerprint\": \"value\", (string) The fingerprint of the master key.\n \"labels\": [\"value\",...], (array of string) Array of labels associated with the address. Currently limited to one label but returned.\n }, \n} \n",
"getbalance": "getbalance (account=\"default\" minconf=1 addresstype=\"*\")\n\nCalculates and returns the balance of one or all accounts.\n\nArguments:\n1. account (string, optional, default=\"default\") Account name or '*' for all accounts to query the balance for. Default to 'default'.\n2. minconf (numeric, optional, default=1) Minimum number of block confirmations required before an unspent output's value is included in the balance.\n3. addresstype (string, optional, default=\"*\") Address type filter. Must be one of 'legacy', 'p2sh-segwit', 'bech32', or '*'. Default to '*'.\n\nResult:\nn.nnn (numeric) The balance valued in LBC.\n",
"getbestblockhash": "getbestblockhash\n\nReturns the hash of the newest block in the best chain that wallet has finished syncing with.\n\nArguments:\nNone\n\nResult:\n\"value\" (string) The hash of the most recent synced-to block.\n",
"getblockcount": "getblockcount\n\nReturns the blockchain height of the newest block in the best chain that wallet has finished syncing with.\n\nArguments:\nNone\n\nResult:\nn.nnn (numeric) The blockchain height of the most recent synced-to block.\n",
"getinfo": "getinfo\n\nReturns a JSON object containing various state info.\n\nArguments:\nNone\n\nResult:\n{\n \"version\": n, (numeric) The version of the server.\n \"protocolversion\": n, (numeric) The latest supported protocol version.\n \"walletversion\": n, (numeric) The version of the address manager database.\n \"balance\": n.nnn, (numeric) The non-staked balance of all accounts calculated with one block confirmation.\n \"blocks\": n, (numeric) The number of blocks processed.\n \"timeoffset\": n, (numeric) The time offset.\n \"connections\": n, (numeric) The number of connected peers.\n \"proxy\": \"value\", (string) The proxy used by the server.\n \"difficulty\": n.nnn, (numeric) The current target difficulty.\n \"testnet\": true|false, (boolean) Whether or not server is using testnet.\n \"keypoololdest\": n, (numeric) Unset.\n \"keypoolsize\": n, (numeric) Unset.\n \"unlocked_until\": n, (numeric) Unset.\n \"paytxfee\": n.nnn, (numeric) The increment used each time more fee is required for an authored transaction.\n \"relayfee\": n.nnn, (numeric) The minimum relay fee for non-free transactions in LBC/KB.\n \"errors\": \"value\", (string) Any current errors.\n \"staked\": n.nnn, (numeric) The staked balance of all accounts calculated with one block confirmation.\n} \n",
"getnewaddress": "getnewaddress (account=\"default\" addresstype=\"legacy\")\n\nGenerates and returns a new payment address.\n\nArguments:\n1. account (string, optional, default=\"default\") Account name the new address will belong to. Defaults to 'default'.\n2. addresstype (string, optional, default=\"legacy\") Address type. Must be one of 'legacy', 'p2sh-segwit', or 'bech32'. Default to 'legacy'.\n\nResult:\n\"value\" (string) The payment address.\n",
"getrawchangeaddress": "getrawchangeaddress (account=\"default\" addresstype=\"legacy\")\n\nGenerates and returns a new internal payment address for use as a change address in raw transactions.\n\nArguments:\n1. account (string, optional, default=\"default\") Account name the new internal address will belong to. Defaults to 'default'.\n2. addresstype (string, optional, default=\"legacy\") Address type. Must be one of 'legacy', 'p2sh-segwit', or 'bech32'. Default to 'legacy'.\n\nResult:\n\"value\" (string) The internal payment address.\n",
"getreceivedbyaccount": "getreceivedbyaccount (account=\"default\" minconf=1)\n\nReturns the total amount received by addresses of some account, including spent outputs.\n\nArguments:\n1. account (string, optional, default=\"default\") Account name to query total received amount for. Defaults to 'default'\n2. minconf (numeric, optional, default=1) Minimum number of block confirmations required before an output's value is included in the total. Defaults to 0\n\nResult:\nn.nnn (numeric) The total received amount valued in LBC.\n",
"getreceivedbyaddress": "getreceivedbyaddress \"address\" (minconf=1)\n\nReturns the total amount received by a single address, including spent outputs.\n\nArguments:\n1. address (string, required) Payment address which received outputs to include in total.\n2. minconf (numeric, optional, default=1) Minimum number of block confirmations required before an output's value is included in the total. Defaults to 1\n\nResult:\nn.nnn (numeric) The total received amount valued in LBC.\n",
"gettransaction": "gettransaction \"txid\" (includewatchonly=false)\n\nReturns a JSON object with details regarding a transaction relevant to this wallet.\n\nArguments:\n1. txid (string, required) Hash of the transaction to query.\n2. includewatchonly (boolean, optional, default=false) Also consider transactions involving watched addresses.\n\nResult:\n{\n \"amount\": n.nnn, (numeric) The total amount this transaction credits to the wallet, valued in LBC.\n \"fee\": n.nnn, (numeric) The total input value minus the total output value, or 0 if 'txid' is not a sent transaction.\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction.\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined.\n \"blockindex\": n, (numeric) Unset.\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined.\n \"txid\": \"value\", (string) The transaction hash.\n \"walletconflicts\": [\"value\",...], (array of string) Unset.\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist.\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist.\n \"details\": [{ (array of object) Additional details for each recorded wallet credit and debit.\n \"account\": \"value\", (string) The account pertaining to this transaction.\n \"address\": \"value\", (string) The address an output was paid to, or the empty string if the output is nonstandard or this detail is regarding a transaction input.\n \"amount\": n.nnn, (numeric) The amount of a received output.\n \"category\": \"value\", (string) The kind of detail: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs.\n \"involveswatchonly\": true|false, (boolean) Unset.\n \"fee\": n.nnn, (numeric) The included fee for a sent transaction.\n \"vout\": n, (numeric) The transaction output index.\n },...], \n \"hex\": \"value\", (string) The transaction encoded as a hexadecimal string.\n \"generated\": true|false, (boolean) Only present if transaction only input is a coinbase one.\n} \n",
"help": "help (\"command\")\n\nReturns a list of all commands or help for a specified command.\n\nArguments:\n1. command (string, optional) The command to retrieve help for.\n\nResult (no command provided.):\n\"value\" (string) List of commands.\n\nResult (command specified.):\n\"value\" (string) Help for specified command.\n",
"importprivkey": "importprivkey \"privkey\" (\"label\" rescan=true)\n\nImports a WIF-encoded private key to the 'imported' account.\n\nArguments:\n1. privkey (string, required) The WIF-encoded private key.\n2. label (string, optional) Unused (must be unset or 'imported').\n3. rescan (boolean, optional, default=true) Rescan the blockchain (since the genesis block) for outputs controlled by the imported key.\n\nResult:\nNothing\n",
"keypoolrefill": "keypoolrefill (newsize=100)\n\nDEPRECATED -- This request does nothing since no keypool is maintained.\n\nArguments:\n1. newsize (numeric, optional, default=100) Unused.\n\nResult:\nNothing\n",
"listaccounts": "listaccounts (minconf=1 addresstype=\"*\")\n\nReturns a JSON object of all accounts and their balances.\n\nArguments:\n1. minconf (numeric, optional, default=1) Minimum number of block confirmations required before an unspent output's value is included in the balance.\n2. addresstype (string, optional, default=\"*\") Address type filter. Must be one of 'legacy', 'p2sh-segwit', 'bech32', or '*'. Defaults to '*'.\n\nResult:\n{\n \"Account name\": Total balance and each scope respectively, valued in LBC., (object) JSON object with account names as keys and LBC amounts as values.\n ...\n}\n",
"listlockunspent": "listlockunspent\n\nReturns a JSON array of outpoints marked as locked (with lockunspent) for this wallet session.\n\nArguments:\nNone\n\nResult:\n[{\n \"txid\": \"value\", (string) The transaction hash of the referenced output.\n \"vout\": n, (numeric) The output index of the referenced output.\n},...]\n",
"listreceivedbyaccount": "listreceivedbyaccount (minconf=1 includeempty=false includewatchonly=false)\n\nReturns a JSON array of objects listing all accounts and the total amount received by each account.\n\nArguments:\n1. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction is considered.\n2. includeempty (boolean, optional, default=false) Unused.\n3. includewatchonly (boolean, optional, default=false) Unused.\n\nResult:\n[{\n \"account\": \"value\", (string) Account name.\n \"amount\": n.nnn, (numeric) Total amount received by payment addresses of the account valued in LBC.\n \"confirmations\": n, (numeric) Number of block confirmations of the most recent transaction relevant to the account.\n},...]\n",
"listreceivedbyaddress": "listreceivedbyaddress (minconf=1 includeempty=false includewatchonly=false)\n\nReturns a JSON array of objects listing wallet payment addresses and their total received amounts.\n\nArguments:\n1. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction is considered.\n2. includeempty (boolean, optional, default=false) Unused.\n3. includewatchonly (boolean, optional, default=false) Unused.\n\nResult:\n[{\n \"address\": \"value\", (string) The payment address.\n \"amount\": n.nnn, (numeric) Total amount received by the payment address valued in LBC.\n \"confirmations\": n, (numeric) Number of block confirmations of the most recent transaction relevant to the address.\n \"txids\": [\"value\",...], (array of string) Transaction hashes of all transactions involving this address.\n \"involvesWatchonly\": true|false, (boolean) Unset.\n},...]\n",
"listsinceblock": "listsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\n\nReturns a JSON array of objects listing details of all wallet transactions after some block.\n\nArguments:\n1. blockhash (string, optional) Hash of the parent block of the first block to consider transactions from, or unset to list all transactions.\n2. targetconfirmations (numeric, optional, default=1) Minimum number of block confirmations of the last block in the result object. Must be 1 or greater. Note: The transactions array in the result object is not affected by this parameter.\n3. includewatchonly (boolean, optional, default=false) Unused.\n\nResult:\n{\n \"transactions\": [{ (array of object) JSON array of objects containing verbose details of the each transaction.\n \"abandoned\": true|false, (boolean) Unset.\n \"account\": \"value\", (string) The account name associated with the transaction.\n \"address\": \"value\", (string) Payment address for a transaction output.\n \"amount\": n.nnn, (numeric) The value of the transaction output valued in LBC.\n \"bip125-replaceable\": \"value\", (string) Unset.\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined.\n \"blockheight\": n, (numeric) The block height containing the transaction.\n \"blockindex\": n, (numeric) Unset.\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined.\n \"category\": \"value\", (string) The kind of transaction: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs. Note: A single output may be included multiple times under different categories\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction.\n \"fee\": n.nnn, (numeric) The total input value minus the total output value for sent transactions.\n \"generated\": true|false, (boolean) Whether the transaction output is a coinbase output.\n \"involveswatchonly\": true|false, (boolean) Unset.\n \"label\": \"value\", (string) A comment for the address/transaction, if any.\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist.\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist.\n \"trusted\": true|false, (boolean) Unset.\n \"txid\": \"value\", (string) The hash of the transaction.\n \"vout\": n, (numeric) The transaction output index.\n \"walletconflicts\": [\"value\",...], (array of string) Unset.\n \"comment\": \"value\", (string) Unset.\n \"otheraccount\": \"value\", (string) Unset.\n },...], \n \"lastblock\": \"value\", (string) Hash of the latest-synced block to be used in later calls to listsinceblock.\n} \n",
"listtransactions": "listtransactions (account=\"default\" count=10 from=0 includewatchonly=false)\n\nReturns a JSON array of objects containing verbose details for wallet transactions.\n\nArguments:\n1. account (string, optional, default=\"default\") Account to filter transactions results by. Defaults to 'default'.\n2. count (numeric, optional, default=10) Maximum number of transactions to create results from. Defaults to 10\n3. from (numeric, optional, default=0) Number of transactions to skip before results are created.\n4. includewatchonly (boolean, optional, default=false) Unused.\n\nResult:\n[{\n \"abandoned\": true|false, (boolean) Unset.\n \"account\": \"value\", (string) The account name associated with the transaction.\n \"address\": \"value\", (string) Payment address for a transaction output.\n \"amount\": n.nnn, (numeric) The value of the transaction output valued in LBC.\n \"bip125-replaceable\": \"value\", (string) Unset.\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined.\n \"blockheight\": n, (numeric) The block height containing the transaction.\n \"blockindex\": n, (numeric) Unset.\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined.\n \"category\": \"value\", (string) The kind of transaction: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs. Note: A single output may be included multiple times under different categories\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction.\n \"fee\": n.nnn, (numeric) The total input value minus the total output value for sent transactions.\n \"generated\": true|false, (boolean) Whether the transaction output is a coinbase output.\n \"involveswatchonly\": true|false, (boolean) Unset.\n \"label\": \"value\", (string) A comment for the address/transaction, if any.\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist.\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist.\n \"trusted\": true|false, (boolean) Unset.\n \"txid\": \"value\", (string) The hash of the transaction.\n \"vout\": n, (numeric) The transaction output index.\n \"walletconflicts\": [\"value\",...], (array of string) Unset.\n \"comment\": \"value\", (string) Unset.\n \"otheraccount\": \"value\", (string) Unset.\n},...]\n",
"listunspent": "listunspent (minconf=1 maxconf=9999999 [\"address\",...])\n\nReturns a JSON array of objects representing unlocked unspent outputs controlled by wallet keys.\n\nArguments:\n1. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction output is considered.\n2. maxconf (numeric, optional, default=9999999) Maximum number of block confirmations required before a transaction output is excluded.\n3. addresses (array of string, optional) If set, limits the returned details to unspent outputs received by any of these payment addresses.\n\nResult:\n{\n \"txid\": \"value\", (string) The transaction hash of the referenced output.\n \"vout\": n, (numeric) The output index of the referenced output.\n \"address\": \"value\", (string) The payment address that received the output.\n \"account\": \"value\", (string) The account associated with the receiving payment address.\n \"scriptPubKey\": \"value\", (string) The output script encoded as a hexadecimal string.\n \"redeemScript\": \"value\", (string) Unset.\n \"amount\": n.nnn, (numeric) The amount of the output valued in LBC.\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction.\n \"solvable\": true|false, (boolean) Whether the output is solvable.\n \"spendable\": true|false, (boolean) Whether the output is entirely controlled by wallet keys/scripts (false for partially controlled multisig outputs or outputs to watch-only addresses).\n \"isstake\": true|false, (boolean) Whether the output is staked.\n} \n",
"lockunspent": "lockunspent unlock [{\"txid\":\"value\",\"vout\":n},...]\n\nLocks or unlocks an unspent output.\nLocked outputs are not chosen for transaction inputs of authored transactions and are not included in 'listunspent' results.\nLocked outputs are volatile and are not saved across wallet restarts.\nIf unlock is true and no transaction outputs are specified, all locked outputs are marked unlocked.\n\nArguments:\n1. unlock (boolean, required) True to unlock outputs, false to lock.\n2. transactions (array of object, required) Transaction outputs to lock or unlock.\n[{\n \"txid\": \"value\", (string) The transaction hash of the referenced output.\n \"vout\": n, (numeric) The output index of the referenced output.\n},...]\n\nResult:\ntrue|false (boolean) The boolean 'true'.\n",
"sendfrom": "sendfrom \"fromaccount\" \"toaddress\" amount (minconf=1 addresstype=\"*\" \"comment\" \"commentto\")\n\nAuthors, signs, and sends a transaction that outputs some amount to a payment address.\nA change output is automatically included to send extra output value back to the original account.\n\nArguments:\n1. fromaccount (string, required) Account to pick unspent outputs from.\n2. toaddress (string, required) Address to pay.\n3. amount (numeric, required) Amount to send to the payment address valued in LBC.\n4. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction output is eligible to be spent.\n5. addresstype (string, optional, default=\"*\") Address type filter for UTXOs to spent from. Must be one of 'legacy', 'p2sh-segwit', 'bech32', or '*'. Defaults to '*'.\n6. comment (string, optional) Unused.\n7. commentto (string, optional) Unused.\n\nResult:\n\"value\" (string) The transaction hash of the sent transaction.\n",
"sendmany": "sendmany \"fromaccount\" {\"address\":amount,...} (minconf=1 addresstype=\"*\" \"comment\")\n\nAuthors, signs, and sends a transaction that outputs to many payment addresses.\nA change output is automatically included to send extra output value back to the original account.\n\nArguments:\n1. fromaccount (string, required) Account to pick unspent outputs from.\n2. amounts (object, required) Pairs of payment addresses and the output amount to pay each.\n{\n \"Address to pay.\": Amount to send to the payment address valued in LBC., (object) JSON object using payment addresses as keys and output amounts valued in LBC to send to each address.\n ...\n}\n3. minconf (numeric, optional, default=1) Minimum number of block confirmations required before a transaction output is eligible to be spent.\n4. addresstype (string, optional, default=\"*\") Address type filter for UTXOs to spent from. Must be one of 'legacy', 'p2sh-segwit', 'bech32', or '*'. Defaults to '*'.\n5. comment (string, optional) Unused.\n\nResult:\n\"value\" (string) The transaction hash of the sent transaction.\n",
"sendtoaddress": "sendtoaddress \"address\" amount (addresstype=\"*\" \"comment\" \"commentto\")\n\nAuthors, signs, and sends a transaction that outputs some amount to a payment address.\nUnlike sendfrom, outputs are always chosen from the default account.\nA change output is automatically included to send extra output value back to the original account.\n\nArguments:\n1. address (string, required) Address to pay.\n2. amount (numeric, required) Amount to send to the payment address valued in LBC.\n3. addresstype (string, optional, default=\"*\") Address type filter for UTXOs to spent from. Must be one of 'legacy', 'p2sh-segwit', 'bech32', or '*'. Defaults to '*'.\n4. comment (string, optional) Unused.\n5. commentto (string, optional) Unused.\n\nResult:\n\"value\" (string) The transaction hash of the sent transaction.\n",
"settxfee": "settxfee amount\n\nModify the increment used each time more fee is required for an authored transaction.\n\nArguments:\n1. amount (numeric, required) The new fee increment valued in LBC.\n\nResult:\ntrue|false (boolean) The boolean 'true'.\n",
"signmessage": "signmessage \"address\" \"message\"\n\nSigns a message using the private key of a payment address.\n\nArguments:\n1. address (string, required) Payment address of private key used to sign the message with.\n2. message (string, required) Message to sign.\n\nResult:\n\"value\" (string) The signed message encoded as a base64 string.\n",
"signrawtransaction": "signrawtransaction \"rawtx\" ([{\"txid\":\"value\",\"vout\":n,\"scriptpubkey\":\"value\",\"redeemscript\":\"value\"},...] [\"privkey\",...] flags=\"ALL\")\n\nSigns transaction inputs using private keys from this wallet and request.\nThe valid flags options are ALL, NONE, SINGLE, ALL|ANYONECANPAY, NONE|ANYONECANPAY, and SINGLE|ANYONECANPAY.\n\nArguments:\n1. rawtx (string, required) Unsigned or partially unsigned transaction to sign encoded as a hexadecimal string.\n2. inputs (array of object, optional) Additional data regarding inputs that this wallet may not be tracking.\n3. privkeys (array of string, optional) Additional WIF-encoded private keys to use when creating signatures.\n4. flags (string, optional, default=\"ALL\") Sighash flags.\n\nResult:\n{\n \"hex\": \"value\", (string) The resulting transaction encoded as a hexadecimal string.\n \"complete\": true|false, (boolean) Whether all input signatures have been created.\n \"errors\": [{ (array of object) Script verification errors (if exists).\n \"txid\": \"value\", (string) The transaction hash of the referenced previous output.\n \"vout\": n, (numeric) The output index of the referenced previous output.\n \"scriptSig\": \"value\", (string) The hex-encoded signature script.\n \"sequence\": n, (numeric) Script sequence number.\n \"error\": \"value\", (string) Verification or signing error related to the input.\n },...], \n} \n",
"validateaddress": "validateaddress \"address\"\n\nVerify that an address is valid.\nExtra details are returned if the address is controlled by this wallet.\nThe following fields are valid only when the address is controlled by this wallet (ismine=true): isscript, pubkey, iscompressed, account, addresses, hex, script, and sigsrequired.\nThe following fields are only valid when address has an associated public key: pubkey, iscompressed.\nThe following fields are only valid when address is a pay-to-script-hash address: addresses, hex, and script.\nIf the address is a multisig address controlled by this wallet, the multisig fields will be left unset if the wallet is locked since the redeem script cannot be decrypted.\n\nArguments:\n1. address (string, required) Address to validate.\n\nResult:\n{\n \"isvalid\": true|false, (boolean) Whether or not the address is valid.\n \"address\": \"value\", (string) The payment address (only when isvalid is true).\n \"ismine\": true|false, (boolean) Whether this address is controlled by the wallet (only when isvalid is true).\n \"iswatchonly\": true|false, (boolean) Unset.\n \"isscript\": true|false, (boolean) Whether the payment address is a pay-to-script-hash address (only when isvalid is true).\n \"pubkey\": \"value\", (string) The associated public key of the payment address, if any (only when isvalid is true).\n \"iscompressed\": true|false, (boolean) Whether the address was created by hashing a compressed public key, if any (only when isvalid is true).\n \"account\": \"value\", (string) The account this payment address belongs to (only when isvalid is true).\n \"addresses\": [\"value\",...], (array of string) All associated payment addresses of the script if address is a multisig address (only when isvalid is true).\n \"hex\": \"value\", (string) The redeem script .\n \"script\": \"value\", (string) The class of redeem script for a multisig address.\n \"sigsrequired\": n, (numeric) The number of required signatures to redeem outputs to the multisig address.\n} \n",
"verifymessage": "verifymessage \"address\" \"signature\" \"message\"\n\nVerify a message was signed with the associated private key of some address.\n\nArguments:\n1. address (string, required) Address used to sign message.\n2. signature (string, required) The signature to verify.\n3. message (string, required) The message to verify.\n\nResult:\ntrue|false (boolean) Whether the message was signed with the private key of 'address'.\n",
"walletlock": "walletlock\n\nLock the wallet.\n\nArguments:\nNone\n\nResult:\nNothing\n",
"walletpassphrase": "walletpassphrase \"passphrase\" timeout\n\nUnlock the wallet.\n\nArguments:\n1. passphrase (string, required) The wallet passphrase\n2. timeout (numeric, required) The number of seconds to wait before the wallet automatically locks\n\nResult:\nNothing\n",
"walletpassphrasechange": "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n\nChange the wallet passphrase.\n\nArguments:\n1. oldpassphrase (string, required) The old wallet passphrase\n2. newpassphrase (string, required) The new wallet passphrase\n\nResult:\nNothing\n",
"createnewaccount": "createnewaccount \"account\"\n\nCreates a new account.\nThe wallet must be unlocked for this request to succeed.\n\nArguments:\n1. account (string, required) Name of the new account\n\nResult:\nNothing\n",
"getbestblock": "getbestblock\n\nReturns the hash and height of the newest block in the best chain that wallet has finished syncing with.\n\nArguments:\nNone\n\nResult:\n{\n \"hash\": \"value\", (string) The hash of the block\n \"height\": n, (numeric) The blockchain height of the block\n} \n",
"getunconfirmedbalance": "getunconfirmedbalance (\"account\")\n\nCalculates the unspent output value of all unmined transaction outputs for an account.\n\nArguments:\n1. account (string, optional) The account to query the unconfirmed balance for (default=\"default\")\n\nResult:\nn.nnn (numeric) Total amount of all unmined unspent outputs of the account valued in bitcoin.\n",
"listaddresstransactions": "listaddresstransactions [\"address\",...] (\"account\")\n\nReturns a JSON array of objects containing verbose details for wallet transactions pertaining some addresses.\n\nArguments:\n1. addresses (array of string, required) Addresses to filter transaction results by\n2. account (string, optional) Unused (must be unset or \"*\")\n\nResult:\n[{\n \"abandoned\": true|false, (boolean) Unset\n \"account\": \"value\", (string) DEPRECATED -- Unset\n \"address\": \"value\", (string) Payment address for a transaction output\n \"amount\": n.nnn, (numeric) The value of the transaction output valued in bitcoin\n \"bip125-replaceable\": \"value\", (string) Unset\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined\n \"blockheight\": n, (numeric) The block height containing the transaction.\n \"blockindex\": n, (numeric) Unset\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined\n \"category\": \"value\", (string) The kind of transaction: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs. Note: A single output may be included multiple times under different categories\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"fee\": n.nnn, (numeric) The total input value minus the total output value for sent transactions\n \"generated\": true|false, (boolean) Whether the transaction output is a coinbase output\n \"involveswatchonly\": true|false, (boolean) Unset\n \"label\": \"value\", (string) A comment for the address/transaction, if any\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"trusted\": true|false, (boolean) Unset\n \"txid\": \"value\", (string) The hash of the transaction\n \"vout\": n, (numeric) The transaction output index\n \"walletconflicts\": [\"value\",...], (array of string) Unset\n \"comment\": \"value\", (string) Unset\n \"otheraccount\": \"value\", (string) Unset\n},...]\n",
"listalltransactions": "listalltransactions (\"account\")\n\nReturns a JSON array of objects in the same format as 'listtransactions' without limiting the number of returned objects.\n\nArguments:\n1. account (string, optional) Unused (must be unset or \"*\")\n\nResult:\n[{\n \"abandoned\": true|false, (boolean) Unset\n \"account\": \"value\", (string) DEPRECATED -- Unset\n \"address\": \"value\", (string) Payment address for a transaction output\n \"amount\": n.nnn, (numeric) The value of the transaction output valued in bitcoin\n \"bip125-replaceable\": \"value\", (string) Unset\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined\n \"blockheight\": n, (numeric) The block height containing the transaction.\n \"blockindex\": n, (numeric) Unset\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined\n \"category\": \"value\", (string) The kind of transaction: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs. Note: A single output may be included multiple times under different categories\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction\n \"fee\": n.nnn, (numeric) The total input value minus the total output value for sent transactions\n \"generated\": true|false, (boolean) Whether the transaction output is a coinbase output\n \"involveswatchonly\": true|false, (boolean) Unset\n \"label\": \"value\", (string) A comment for the address/transaction, if any\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist\n \"trusted\": true|false, (boolean) Unset\n \"txid\": \"value\", (string) The hash of the transaction\n \"vout\": n, (numeric) The transaction output index\n \"walletconflicts\": [\"value\",...], (array of string) Unset\n \"comment\": \"value\", (string) Unset\n \"otheraccount\": \"value\", (string) Unset\n},...]\n",
"renameaccount": "renameaccount \"oldaccount\" \"newaccount\"\n\nRenames an account.\n\nArguments:\n1. oldaccount (string, required) The old account name to rename\n2. newaccount (string, required) The new name for the account\n\nResult:\nNothing\n",
"walletislocked": "walletislocked\n\nReturns whether or not the wallet is locked.\n\nArguments:\nNone\n\nResult:\ntrue|false (boolean) Whether the wallet is locked\n",
"walletpassphrase": "walletpassphrase \"passphrase\" timeout\n\nUnlock the wallet.\n\nArguments:\n1. passphrase (string, required) The wallet passphrase.\n2. timeout (numeric, required) The number of seconds to wait before the wallet automatically locks.\n\nResult:\nNothing\n",
"walletpassphrasechange": "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n\nChange the wallet passphrase.\n\nArguments:\n1. oldpassphrase (string, required) The old wallet passphrase.\n2. newpassphrase (string, required) The new wallet passphrase.\n\nResult:\nNothing\n",
"createnewaccount": "createnewaccount \"account\"\n\nCreates a new account.\n\nArguments:\n1. account (string, required) Account name.\n\nResult:\nNothing\n",
"getbestblock": "getbestblock\n\nReturns the hash and height of the newest block in the best chain that wallet has finished syncing with.\n\nArguments:\nNone\n\nResult:\n{\n \"hash\": \"value\", (string) The hash of the block.\n \"height\": n, (numeric) The blockchain height of the block.\n} \n",
"getunconfirmedbalance": "getunconfirmedbalance (account=\"default\")\n\nCalculates the unspent output value of all unmined transaction outputs.\n\nArguments:\n1. account (string, optional, default=\"default\") The account name to query the unconfirmed balance for. Default to 'default'.\n\nResult:\nn.nnn (numeric) Total amount of all unmined unspent outputs of the account valued in LBC.\n",
"listaddresstransactions": "listaddresstransactions [\"address\",...] (account=\"default\")\n\nReturns a JSON array of objects containing verbose details for wallet transactions pertaining some addresses.\n\nArguments:\n1. addresses (array of string, required) Addresses to filter transaction results by.\n2. account (string, optional, default=\"default\") Account to filter transactions results by. Defaults to 'default'.\n\nResult:\n[{\n \"abandoned\": true|false, (boolean) Unset.\n \"account\": \"value\", (string) The account name associated with the transaction.\n \"address\": \"value\", (string) Payment address for a transaction output.\n \"amount\": n.nnn, (numeric) The value of the transaction output valued in LBC.\n \"bip125-replaceable\": \"value\", (string) Unset.\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined.\n \"blockheight\": n, (numeric) The block height containing the transaction.\n \"blockindex\": n, (numeric) Unset.\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined.\n \"category\": \"value\", (string) The kind of transaction: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs. Note: A single output may be included multiple times under different categories\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction.\n \"fee\": n.nnn, (numeric) The total input value minus the total output value for sent transactions.\n \"generated\": true|false, (boolean) Whether the transaction output is a coinbase output.\n \"involveswatchonly\": true|false, (boolean) Unset.\n \"label\": \"value\", (string) A comment for the address/transaction, if any.\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist.\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist.\n \"trusted\": true|false, (boolean) Unset.\n \"txid\": \"value\", (string) The hash of the transaction.\n \"vout\": n, (numeric) The transaction output index.\n \"walletconflicts\": [\"value\",...], (array of string) Unset.\n \"comment\": \"value\", (string) Unset.\n \"otheraccount\": \"value\", (string) Unset.\n},...]\n",
"listalltransactions": "listalltransactions (account=\"default\")\n\nReturns a JSON array of objects in the same format as 'listtransactions' without limiting the number of returned objects.\n\nArguments:\n1. account (string, optional, default=\"default\") Account to filter transactions results by. Defaults to 'default'.\n\nResult:\n[{\n \"abandoned\": true|false, (boolean) Unset.\n \"account\": \"value\", (string) The account name associated with the transaction.\n \"address\": \"value\", (string) Payment address for a transaction output.\n \"amount\": n.nnn, (numeric) The value of the transaction output valued in LBC.\n \"bip125-replaceable\": \"value\", (string) Unset.\n \"blockhash\": \"value\", (string) The hash of the block this transaction is mined in, or the empty string if unmined.\n \"blockheight\": n, (numeric) The block height containing the transaction.\n \"blockindex\": n, (numeric) Unset.\n \"blocktime\": n, (numeric) The Unix time of the block header this transaction is mined in, or 0 if unmined.\n \"category\": \"value\", (string) The kind of transaction: \"send\" for sent transactions, \"immature\" for immature coinbase outputs, \"generate\" for mature coinbase outputs, or \"recv\" for all other received outputs. Note: A single output may be included multiple times under different categories\n \"confirmations\": n, (numeric) The number of block confirmations of the transaction.\n \"fee\": n.nnn, (numeric) The total input value minus the total output value for sent transactions.\n \"generated\": true|false, (boolean) Whether the transaction output is a coinbase output.\n \"involveswatchonly\": true|false, (boolean) Unset.\n \"label\": \"value\", (string) A comment for the address/transaction, if any.\n \"time\": n, (numeric) The earliest Unix time this transaction was known to exist.\n \"timereceived\": n, (numeric) The earliest Unix time this transaction was known to exist.\n \"trusted\": true|false, (boolean) Unset.\n \"txid\": \"value\", (string) The hash of the transaction.\n \"vout\": n, (numeric) The transaction output index.\n \"walletconflicts\": [\"value\",...], (array of string) Unset.\n \"comment\": \"value\", (string) Unset.\n \"otheraccount\": \"value\", (string) Unset.\n},...]\n",
"renameaccount": "renameaccount \"oldaccount\" \"newaccount\"\n\nRenames an account.\n\nArguments:\n1. oldaccount (string, required) The old account name to rename.\n2. newaccount (string, required) The new name for the account.\n\nResult:\nNothing\n",
"rescanblockchain": "rescanblockchain (startheight=0 stopheight)\n\nRenames an account.\n\nArguments:\n1. startheight (numeric, optional, default=0) Block height where the rescan should start.\n2. stopheight (numeric, optional) The last block height that should be scanned. If none is provided it will rescan up to the tip at return time of this call.\n\nResult:\n{\n \"start_height\": n, (numeric) The block height where the rescan started (the requested height or 0)\n \"stop_height\": n, (numeric) The height of the last rescanned block.\n} \n",
"walletislocked": "walletislocked\n\nReturns whether or not the wallet is locked.\n\nArguments:\nNone\n\nResult:\ntrue|false (boolean) Whether the wallet is locked.\n",
}
}
@ -56,4 +57,4 @@ var localeHelpDescs = map[string]func() map[string]string{
"en_US": helpDescsEnUS,
}
var requestUsages = "addmultisigaddress nrequired [\"key\",...] (\"account\")\ncreatemultisig nrequired [\"key\",...]\ndumpprivkey \"address\"\ngetaccount \"address\"\ngetaccountaddress \"account\"\ngetaddressesbyaccount \"account\"\ngetaddressinfo \"address\"\ngetbalance (\"account\" minconf=1)\ngetbestblockhash\ngetblockcount\ngetinfo\ngetnewaddress (\"account\" \"addresstype\")\ngetrawchangeaddress (\"account\")\ngetreceivedbyaccount \"account\" (minconf=1)\ngetreceivedbyaddress \"address\" (minconf=1)\ngettransaction \"txid\" (includewatchonly=false)\nhelp (\"command\")\nimportprivkey \"privkey\" (\"label\" rescan=true)\nkeypoolrefill (newsize=100)\nlistaccounts (minconf=1)\nlistlockunspent\nlistreceivedbyaccount (minconf=1 includeempty=false includewatchonly=false)\nlistreceivedbyaddress (minconf=1 includeempty=false includewatchonly=false)\nlistsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\nlisttransactions (\"account\" count=10 from=0 includewatchonly=false)\nlistunspent (minconf=1 maxconf=9999999 [\"address\",...])\nlockunspent unlock [{\"txid\":\"value\",\"vout\":n},...]\nsendfrom \"fromaccount\" \"toaddress\" amount (minconf=1 \"comment\" \"commentto\")\nsendmany \"fromaccount\" {\"address\":amount,...} (minconf=1 \"comment\")\nsendtoaddress \"address\" amount (\"comment\" \"commentto\")\nsettxfee amount\nsignmessage \"address\" \"message\"\nsignrawtransaction \"rawtx\" ([{\"txid\":\"value\",\"vout\":n,\"scriptpubkey\":\"value\",\"redeemscript\":\"value\"},...] [\"privkey\",...] flags=\"ALL\")\nvalidateaddress \"address\"\nverifymessage \"address\" \"signature\" \"message\"\nwalletlock\nwalletpassphrase \"passphrase\" timeout\nwalletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\ncreatenewaccount \"account\"\ngetbestblock\ngetunconfirmedbalance (\"account\")\nlistaddresstransactions [\"address\",...] (\"account\")\nlistalltransactions (\"account\")\nrenameaccount \"oldaccount\" \"newaccount\"\nwalletislocked"
var requestUsages = "addmultisigaddress nrequired [\"key\",...] (\"account\")\ncreatemultisig nrequired [\"key\",...]\ndumpprivkey \"address\"\ngetaccount \"address\"\ngetaccountaddress (account=\"default\" addresstype=\"legacy\")\ngetaddressesbyaccount (account=\"default\" addresstype=\"*\")\ngetaddressinfo \"address\"\ngetbalance (account=\"default\" minconf=1 addresstype=\"*\")\ngetbestblockhash\ngetblockcount\ngetinfo\ngetnewaddress (account=\"default\" addresstype=\"legacy\")\ngetrawchangeaddress (account=\"default\" addresstype=\"legacy\")\ngetreceivedbyaccount (account=\"default\" minconf=1)\ngetreceivedbyaddress \"address\" (minconf=1)\ngettransaction \"txid\" (includewatchonly=false)\nhelp (\"command\")\nimportprivkey \"privkey\" (\"label\" rescan=true)\nkeypoolrefill (newsize=100)\nlistaccounts (minconf=1 addresstype=\"*\")\nlistlockunspent\nlistreceivedbyaccount (minconf=1 includeempty=false includewatchonly=false)\nlistreceivedbyaddress (minconf=1 includeempty=false includewatchonly=false)\nlistsinceblock (\"blockhash\" targetconfirmations=1 includewatchonly=false)\nlisttransactions (account=\"default\" count=10 from=0 includewatchonly=false)\nlistunspent (minconf=1 maxconf=9999999 [\"address\",...])\nlockunspent unlock [{\"txid\":\"value\",\"vout\":n},...]\nsendfrom \"fromaccount\" \"toaddress\" amount (minconf=1 addresstype=\"*\" \"comment\" \"commentto\")\nsendmany \"fromaccount\" {\"address\":amount,...} (minconf=1 addresstype=\"*\" \"comment\")\nsendtoaddress \"address\" amount (addresstype=\"*\" \"comment\" \"commentto\")\nsettxfee amount\nsignmessage \"address\" \"message\"\nsignrawtransaction \"rawtx\" ([{\"txid\":\"value\",\"vout\":n,\"scriptpubkey\":\"value\",\"redeemscript\":\"value\"},...] [\"privkey\",...] flags=\"ALL\")\nvalidateaddress \"address\"\nverifymessage \"address\" \"signature\" \"message\"\nwalletlock\nwalletpassphrase \"passphrase\" timeout\nwalletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\ncreatenewaccount \"account\"\ngetbestblock\ngetunconfirmedbalance (account=\"default\")\nlistaddresstransactions [\"address\",...] (account=\"default\")\nlistalltransactions (account=\"default\")\nrenameaccount \"oldaccount\" \"newaccount\"\nrescanblockchain (startheight=0 stopheight)\nwalletislocked"

View file

@ -126,8 +126,8 @@ func startRPCServers(walletLoader *wallet.Loader) (*legacyrpc.Server, error) {
}
if cfg.Username == "" || cfg.Password == "" {
log.Info("Legacy RPC server disabled (requires username and password)")
if cfg.RPCUser == "" || cfg.RPCPass == "" {
log.Info("RPC server disabled (requires rpcuser and rpcpass)")
} else if len(cfg.LegacyRPCListeners) != 0 {
listeners := makeListeners(cfg.LegacyRPCListeners, legacyListen)
if len(listeners) == 0 {
@ -135,8 +135,8 @@ func startRPCServers(walletLoader *wallet.Loader) (*legacyrpc.Server, error) {
return nil, err
}
opts := legacyrpc.Options{
Username: cfg.Username,
Password: cfg.Password,
Username: cfg.RPCUser,
Password: cfg.RPCPass,
MaxPOSTClients: cfg.LegacyRPCMaxClients,
MaxWebsocketClients: cfg.LegacyRPCMaxWebsockets,
}

View file

@ -4,12 +4,9 @@
; Bitcoin wallet settings
; ------------------------------------------------------------------------------
; Use testnet (cannot be used with simnet=1).
; Use testnet
; testnet=0
; Use simnet (cannot be used with testnet=1).
; simnet=0
; The directory to open and save wallet, transaction, and unspent transaction
; output files. Two directories, `mainnet` and `testnet` are used in this
; directory for mainnet and testnet wallets, respectively.
@ -88,15 +85,8 @@
; Username and password to authenticate to lbcd a RPC server and authenticate
; new client connections
; username=
; password=
; Alternative username and password for lbcd. If set, these will be used
; instead of the username and password set above for authentication to a
; lbcd RPC server.
; lbcdusername=
; lbcdpassword=
; rpcuser=
; rpcpass=
; ------------------------------------------------------------------------------
; Debug

View file

@ -29,9 +29,7 @@ var (
rootKey, _ = hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
pubPassphrase = []byte("_DJr{fL4H0O}*-0\n:V1izc)(6BomK")
privPassphrase = []byte("81lUHXnOMZ@?XXd7O9xyDIWIbXX-lj")
pubPassphrase2 = []byte("-0NV4P~VSJBWbunw}%<Z]fuGpbN[ZI")
passphrase = []byte("81lUHXnOMZ@?XXd7O9xyDIWIbXX-lj")
privPassphrase2 = []byte("~{<]08%6!-?2s<$(8$8:f(5[4/!/{Y")
// fastScrypt are parameters used throughout the tests to speed up the
@ -287,13 +285,13 @@ func setupManager(t *testing.T) (tearDownFunc func(), db walletdb.DB, mgr *Manag
return err
}
err = Create(
ns, rootKey, pubPassphrase, privPassphrase,
&chaincfg.MainNetParams, fastScrypt, time.Time{},
ns, rootKey, passphrase, &chaincfg.MainNetParams,
fastScrypt, time.Time{},
)
if err != nil {
return err
}
mgr, err = Open(ns, pubPassphrase, &chaincfg.MainNetParams)
mgr, err = Open(ns, &chaincfg.MainNetParams)
return err
})
if err != nil {

View file

@ -1,17 +0,0 @@
#!/bin/sh
# This script uses gocov to generate a test coverage report.
# The gocov tool my be obtained with the following command:
# go get github.com/axw/gocov/gocov
#
# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH.
# Check for gocov.
type gocov >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo >&2 "This script requires the gocov tool."
echo >&2 "You may obtain it with the following command:"
echo >&2 "go get github.com/axw/gocov/gocov"
exit 1
fi
gocov test | gocov report

View file

@ -34,7 +34,7 @@ var (
// ObtainUserInputFunc is a function that reads a user input and returns it as
// a byte stream. It is used to accept data required during upgrades, for e.g.
// wallet seed and private passphrase.
// wallet seed and passphrase.
type ObtainUserInputFunc func() ([]byte, error)
// maybeConvertDbError converts the passed error to a ManagerError with an

View file

@ -42,6 +42,9 @@ const (
// ImportedAddrAccountName is the name of the imported account.
ImportedAddrAccountName = "imported"
// AccountGapLimit is used for account discovery defined in BIP0044
AccountGapLimit = 20
// DefaultAccountNum is the number of the default account.
DefaultAccountNum = 0
@ -78,7 +81,7 @@ const (
InternalBranch uint32 = 1
// saltSize is the number of bytes of the salt used when hashing
// private passphrases.
// passphrases.
saltSize = 32
)
@ -110,11 +113,11 @@ type OpenCallbacks struct {
// from the user (or any other mechanism the caller deems fit).
ObtainSeed ObtainUserInputFunc
// ObtainPrivatePass is a callback function that is potentially invoked
// ObtainPassphrase is a callback function that is potentially invoked
// during upgrades. It is intended to be used to request the wallet
// private passphrase from the user (or any other mechanism the caller
// deems fit).
ObtainPrivatePass ObtainUserInputFunc
// passphrase from the user (or any other mechanism the caller deems
// fit).
ObtainPassphrase ObtainUserInputFunc
}
// DefaultScryptOptions is the default options used with scrypt.
@ -156,13 +159,8 @@ type accountInfo struct {
// The external branch is used for all addresses which are intended for
// external use.
nextExternalIndex uint32
lastExternalAddr ManagedAddress
// The internal branch is used for all adddresses which are only
// intended for internal wallet use such as change addresses.
nextInternalIndex uint32
lastInternalAddr ManagedAddress
nextIndex [2]uint32
lastAddr [2]ManagedAddress
// addrSchema serves as a way for an account to override its
// corresponding address schema with a custom one.
@ -375,11 +373,11 @@ type Manager struct {
cryptoKeyScriptEncrypted []byte
cryptoKeyScript EncryptorDecryptor
// privPassphraseSalt and hashedPrivPassphrase allow for the secure
// detection of a correct passphrase on manager unlock when the
// manager is already unlocked. The hash is zeroed each lock.
privPassphraseSalt [saltSize]byte
hashedPrivPassphrase [sha512.Size]byte
// pssphraseSalt and hashedPassphrase allow for the secure detection
// of a correct passphrase on manager unlock when the manager is already
// unlocked. The hash is zeroed each lock.
passphraseSalt [saltSize]byte
hashedPassphrase [sha512.Size]byte
}
// lock performs a best try effort to remove and zero all secret keys associated
@ -415,7 +413,7 @@ func (m *Manager) lock() {
m.masterKeyPriv.Zero()
// Zero the hashed passphrase.
zero.Bytea64(&m.hashedPrivPassphrase)
zero.Bytea64(&m.hashedPassphrase)
// NOTE: m.cryptoKeyPub is intentionally not cleared here as the address
// manager needs to be able to continue to read and decrypt public data
@ -827,13 +825,12 @@ func (m *Manager) ChainParams() *chaincfg.Params {
return m.chainParams
}
// ChangePassphrase changes either the public or private passphrase to the
// provided value depending on the private flag. The new passphrase keys are
// derived using the scrypt parameters in the options, so changing the
// passphrase may be used to bump the computational difficulty needed to brute
// force the passphrase.
// ChangePassphrase changes passphrase to the provided value. The new
// passphrase keys are derived using the scrypt parameters in the options, so
// changing the passphrase may be used to bump the computational difficulty
// needed to brute force the passphrase.
func (m *Manager) ChangePassphrase(ns walletdb.ReadWriteBucket, oldPassphrase,
newPassphrase []byte, private bool, config *ScryptOptions) error {
newPassphrase []byte, config *ScryptOptions) error {
m.mtx.Lock()
defer m.mtx.Unlock()
@ -844,13 +841,9 @@ func (m *Manager) ChangePassphrase(ns walletdb.ReadWriteBucket, oldPassphrase,
// cleared when done to avoid leaving a copy in memory.
var keyName string
secretKey := snacl.SecretKey{Key: &snacl.CryptoKey{}}
if private {
keyName = "private"
secretKey.Parameters = m.masterKeyPriv.Parameters
} else {
keyName = "public"
secretKey.Parameters = m.masterKeyPub.Parameters
}
keyName = "private"
secretKey.Parameters = m.masterKeyPriv.Parameters
if err := secretKey.DeriveKey(&oldPassphrase); err != nil {
if err == snacl.ErrInvalidPassword {
str := fmt.Sprintf("invalid passphrase for %s master "+
@ -872,112 +865,85 @@ func (m *Manager) ChangePassphrase(ns walletdb.ReadWriteBucket, oldPassphrase,
}
newKeyParams := newMasterKey.Marshal()
if private {
// Technically, the locked state could be checked here to only
// do the decrypts when the address manager is locked as the
// clear text keys are already available in memory when it is
// unlocked, but this is not a hot path, decryption is quite
// fast, and it's less cyclomatic complexity to simply decrypt
// in either case.
// Technically, the locked state could be checked here to only
// do the decrypts when the address manager is locked as the
// clear text keys are already available in memory when it is
// unlocked, but this is not a hot path, decryption is quite
// fast, and it's less cyclomatic complexity to simply decrypt
// in either case.
// Create a new salt that will be used for hashing the new
// passphrase each unlock.
var passphraseSalt [saltSize]byte
_, err := rand.Read(passphraseSalt[:])
if err != nil {
str := "failed to read random source for passhprase salt"
return managerError(ErrCrypto, str, err)
}
// Re-encrypt the crypto private key using the new master
// private key.
decPriv, err := secretKey.Decrypt(m.cryptoKeyPrivEncrypted)
if err != nil {
str := "failed to decrypt crypto private key"
return managerError(ErrCrypto, str, err)
}
encPriv, err := newMasterKey.Encrypt(decPriv)
zero.Bytes(decPriv)
if err != nil {
str := "failed to encrypt crypto private key"
return managerError(ErrCrypto, str, err)
}
// Re-encrypt the crypto script key using the new master
// private key.
decScript, err := secretKey.Decrypt(m.cryptoKeyScriptEncrypted)
if err != nil {
str := "failed to decrypt crypto script key"
return managerError(ErrCrypto, str, err)
}
encScript, err := newMasterKey.Encrypt(decScript)
zero.Bytes(decScript)
if err != nil {
str := "failed to encrypt crypto script key"
return managerError(ErrCrypto, str, err)
}
// When the manager is locked, ensure the new clear text master
// key is cleared from memory now that it is no longer needed.
// If unlocked, create the new passphrase hash with the new
// passphrase and salt.
var hashedPassphrase [sha512.Size]byte
if m.locked {
newMasterKey.Zero()
} else {
saltedPassphrase := append(passphraseSalt[:],
newPassphrase...)
hashedPassphrase = sha512.Sum512(saltedPassphrase)
zero.Bytes(saltedPassphrase)
}
// Save the new keys and params to the db in a single
// transaction.
err = putCryptoKeys(ns, nil, encPriv, encScript)
if err != nil {
return maybeConvertDbError(err)
}
err = putMasterKeyParams(ns, nil, newKeyParams)
if err != nil {
return maybeConvertDbError(err)
}
// Now that the db has been successfully updated, clear the old
// key and set the new one.
copy(m.cryptoKeyPrivEncrypted, encPriv)
copy(m.cryptoKeyScriptEncrypted, encScript)
m.masterKeyPriv.Zero() // Clear the old key.
m.masterKeyPriv = newMasterKey
m.privPassphraseSalt = passphraseSalt
m.hashedPrivPassphrase = hashedPassphrase
} else {
// Re-encrypt the crypto public key using the new master public
// key.
encryptedPub, err := newMasterKey.Encrypt(m.cryptoKeyPub.Bytes())
if err != nil {
str := "failed to encrypt crypto public key"
return managerError(ErrCrypto, str, err)
}
// Save the new keys and params to the the db in a single
// transaction.
err = putCryptoKeys(ns, encryptedPub, nil, nil)
if err != nil {
return maybeConvertDbError(err)
}
err = putMasterKeyParams(ns, newKeyParams, nil)
if err != nil {
return maybeConvertDbError(err)
}
// Now that the db has been successfully updated, clear the old
// key and set the new one.
m.masterKeyPub.Zero()
m.masterKeyPub = newMasterKey
// Create a new salt that will be used for hashing the new
// passphrase each unlock.
var passphraseSalt [saltSize]byte
_, err = rand.Read(passphraseSalt[:])
if err != nil {
str := "failed to read random source for passhprase salt"
return managerError(ErrCrypto, str, err)
}
// Re-encrypt the crypto private key using the new master
// private key.
decPriv, err := secretKey.Decrypt(m.cryptoKeyPrivEncrypted)
if err != nil {
str := "failed to decrypt crypto private key"
return managerError(ErrCrypto, str, err)
}
encPriv, err := newMasterKey.Encrypt(decPriv)
zero.Bytes(decPriv)
if err != nil {
str := "failed to encrypt crypto private key"
return managerError(ErrCrypto, str, err)
}
// Re-encrypt the crypto script key using the new master
// private key.
decScript, err := secretKey.Decrypt(m.cryptoKeyScriptEncrypted)
if err != nil {
str := "failed to decrypt crypto script key"
return managerError(ErrCrypto, str, err)
}
encScript, err := newMasterKey.Encrypt(decScript)
zero.Bytes(decScript)
if err != nil {
str := "failed to encrypt crypto script key"
return managerError(ErrCrypto, str, err)
}
// When the manager is locked, ensure the new clear text master
// key is cleared from memory now that it is no longer needed.
// If unlocked, create the new passphrase hash with the new
// passphrase and salt.
var hashedPassphrase [sha512.Size]byte
if m.locked {
newMasterKey.Zero()
} else {
saltedPassphrase := append(passphraseSalt[:],
newPassphrase...)
hashedPassphrase = sha512.Sum512(saltedPassphrase)
zero.Bytes(saltedPassphrase)
}
// Save the new keys and params to the db in a single
// transaction.
err = putCryptoKeys(ns, nil, encPriv, encScript)
if err != nil {
return maybeConvertDbError(err)
}
err = putMasterKeyParams(ns, nil, newKeyParams)
if err != nil {
return maybeConvertDbError(err)
}
// Now that the db has been successfully updated, clear the old
// key and set the new one.
copy(m.cryptoKeyPrivEncrypted, encPriv)
copy(m.cryptoKeyScriptEncrypted, encScript)
m.masterKeyPriv.Zero() // Clear the old key.
m.masterKeyPriv = newMasterKey
m.passphraseSalt = passphraseSalt
m.hashedPassphrase = hashedPassphrase
return nil
}
@ -1029,11 +995,10 @@ func (m *Manager) Unlock(ns walletdb.ReadBucket, passphrase []byte) error {
// Avoid actually unlocking if the manager is already unlocked
// and the passphrases match.
if !m.locked {
saltedPassphrase := append(m.privPassphraseSalt[:],
passphrase...)
saltedPassphrase := append(m.passphraseSalt[:], passphrase...)
hashedPassphrase := sha512.Sum512(saltedPassphrase)
zero.Bytes(saltedPassphrase)
if hashedPassphrase != m.hashedPrivPassphrase {
if hashedPassphrase != m.hashedPassphrase {
m.lock()
str := "invalid passphrase for master private key"
return managerError(ErrWrongPassphrase, str, nil)
@ -1128,8 +1093,8 @@ func (m *Manager) Unlock(ns walletdb.ReadBucket, passphrase []byte) error {
}
m.locked = false
saltedPassphrase := append(m.privPassphraseSalt[:], passphrase...)
m.hashedPrivPassphrase = sha512.Sum512(saltedPassphrase)
saltedPassphrase := append(m.passphraseSalt[:], passphrase...)
m.hashedPassphrase = sha512.Sum512(saltedPassphrase)
zero.Bytes(saltedPassphrase)
return nil
}
@ -1237,7 +1202,7 @@ func (m *Manager) Decrypt(keyType CryptoKeyType, in []byte) ([]byte, error) {
func newManager(chainParams *chaincfg.Params, masterKeyPub *snacl.SecretKey,
masterKeyPriv *snacl.SecretKey, cryptoKeyPub EncryptorDecryptor,
cryptoKeyPrivEncrypted, cryptoKeyScriptEncrypted []byte, syncInfo *syncState,
birthday time.Time, privPassphraseSalt [saltSize]byte,
birthday time.Time, passphraseSalt [saltSize]byte,
scopedManagers map[KeyScope]*ScopedKeyManager) *Manager {
m := &Manager{
@ -1252,7 +1217,7 @@ func newManager(chainParams *chaincfg.Params, masterKeyPub *snacl.SecretKey,
cryptoKeyPriv: &cryptoKey{},
cryptoKeyScriptEncrypted: cryptoKeyScriptEncrypted,
cryptoKeyScript: &cryptoKey{},
privPassphraseSalt: privPassphraseSalt,
passphraseSalt: passphraseSalt,
scopedManagers: scopedManagers,
externalAddrSchemas: make(map[AddressType][]KeyScope),
internalAddrSchemas: make(map[AddressType][]KeyScope),
@ -1363,10 +1328,9 @@ func checkBranchKeys(acctKey *hdkeychain.ExtendedKey) error {
}
// loadManager returns a new address manager that results from loading it from
// the passed opened database. The public passphrase is required to decrypt
// the public keys.
func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte,
chainParams *chaincfg.Params) (*Manager, error) {
// the passed opened database.
func loadManager(ns walletdb.ReadBucket, chainParams *chaincfg.Params) (
*Manager, error) {
// Verify the version is neither too old or too new.
version, err := fetchManagerVersion(ns)
@ -1383,7 +1347,7 @@ func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte,
}
// Load the master key params from the db.
masterKeyPubParams, masterKeyPrivParams, err := fetchMasterKeyParams(ns)
masterKeyPubParams, masterKeyparams, err := fetchMasterKeyParams(ns)
if err != nil {
return nil, maybeConvertDbError(err)
}
@ -1412,7 +1376,7 @@ func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte,
// Set the master private key params, but don't derive it now since the
// manager starts off locked.
var masterKeyPriv snacl.SecretKey
err = masterKeyPriv.Unmarshal(masterKeyPrivParams)
err = masterKeyPriv.Unmarshal(masterKeyparams)
if err != nil {
str := "failed to unmarshal master private key"
return nil, managerError(ErrCrypto, str, err)
@ -1425,6 +1389,8 @@ func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte,
str := "failed to unmarshal master public key"
return nil, managerError(ErrCrypto, str, err)
}
pubPassphrase := []byte("public") // Hardcoded salt.
if err := masterKeyPub.DeriveKey(&pubPassphrase); err != nil {
str := "invalid passphrase for master public key"
return nil, managerError(ErrWrongPassphrase, str, nil)
@ -1443,9 +1409,9 @@ func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte,
// Create the sync state struct.
syncInfo := newSyncState(startBlock, syncedTo)
// Generate private passphrase salt.
var privPassphraseSalt [saltSize]byte
_, err = rand.Read(privPassphraseSalt[:])
// Generate passphrase salt.
var passphraseSalt [saltSize]byte
_, err = rand.Read(passphraseSalt[:])
if err != nil {
str := "failed to read random source for passphrase salt"
return nil, managerError(ErrCrypto, str, err)
@ -1482,7 +1448,7 @@ func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte,
mgr := newManager(
chainParams, &masterKeyPub, &masterKeyPriv,
cryptoKeyPub, cryptoKeyPrivEnc, cryptoKeyScriptEnc, syncInfo,
birthday, privPassphraseSalt, scopedManagers)
birthday, passphraseSalt, scopedManagers)
for _, scopedManager := range scopedManagers {
scopedManager.rootManager = mgr
@ -1491,18 +1457,15 @@ func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte,
return mgr, nil
}
// Open loads an existing address manager from the given namespace. The public
// passphrase is required to decrypt the public keys used to protect the public
// information such as addresses. This is important since access to BIP0032
// extended keys means it is possible to generate all future addresses.
// Open loads an existing address manager from the given namespace.
//
// If a config structure is passed to the function, that configuration will
// override the defaults.
//
// A ManagerError with an error code of ErrNoExist will be returned if the
// passed manager does not exist in the specified namespace.
func Open(ns walletdb.ReadBucket, pubPassphrase []byte,
chainParams *chaincfg.Params) (*Manager, error) {
func Open(ns walletdb.ReadBucket, chainParams *chaincfg.Params) (
*Manager, error) {
// Return an error if the manager has NOT already been created in the
// given database namespace.
@ -1512,7 +1475,7 @@ func Open(ns walletdb.ReadBucket, pubPassphrase []byte,
return nil, managerError(ErrNoExist, str, nil)
}
return loadManager(ns, pubPassphrase, chainParams)
return loadManager(ns, chainParams)
}
// createManagerKeyScope creates a new key scoped for a target manager's scope.
@ -1532,7 +1495,7 @@ func createManagerKeyScope(ns walletdb.ReadWriteBucket,
// Derive the account key for the first account according our
// BIP0044-like derivation.
acctKeyPriv, err := deriveAccountKey(coinTypeKeyPriv, 0)
acctKeyPriv, err := deriveAccountKey(coinTypeKeyPriv, DefaultAccountNum)
if err != nil {
// The seed is unusable if the any of the children in the
// required hierarchy can't be derived due to invalid child.
@ -1624,12 +1587,10 @@ func createManagerKeyScope(ns walletdb.ReadWriteBucket,
// derived. This allows all chained addresses in the address manager
// to be recovered by using the same seed.
//
// All private and public keys and information are protected by secret
// keys derived from the provided private and public passphrases. The
// public passphrase is required on subsequent opens of the address
// manager, and the private passphrase is required to unlock the
// address manager in order to gain access to any private keys and
// information.
// All private keys and information are protected by secret keys derived
// from the provided passphrase.
// The passphrase is required to unlock the address manager in order to gain
// access to any private keys and information.
//
// If a config structure is passed to the function, that configuration
// will override the defaults.
@ -1638,9 +1599,8 @@ func createManagerKeyScope(ns walletdb.ReadWriteBucket,
// returned the address manager already exists in the specified
// namespace.
func Create(ns walletdb.ReadWriteBucket, rootKey *hdkeychain.ExtendedKey,
pubPassphrase, privPassphrase []byte,
chainParams *chaincfg.Params, config *ScryptOptions,
birthday time.Time) error {
passphrase []byte, chainParams *chaincfg.Params,
config *ScryptOptions, birthday time.Time) error {
// Return an error if the manager has already been created in
// the given database namespace.
@ -1649,9 +1609,9 @@ func Create(ns walletdb.ReadWriteBucket, rootKey *hdkeychain.ExtendedKey,
return managerError(ErrAlreadyExists, errAlreadyExists, nil)
}
// Ensure the private passphrase is not empty.
if len(privPassphrase) == 0 {
str := "private passphrase may not be empty"
// Ensure the passphrase is not empty.
if len(passphrase) == 0 {
str := "passphrase may not be empty"
return managerError(ErrEmptyPassphrase, str, nil)
}
@ -1666,6 +1626,7 @@ func Create(ns walletdb.ReadWriteBucket, rootKey *hdkeychain.ExtendedKey,
// Generate new master keys. These master keys are used to protect the
// crypto keys that will be generated next.
pubPassphrase := []byte("public") // Hardcoded salt.
masterKeyPub, err := newSecretKey(&pubPassphrase, config)
if err != nil {
str := "failed to master public key"
@ -1701,22 +1662,22 @@ func Create(ns walletdb.ReadWriteBucket, rootKey *hdkeychain.ExtendedKey,
pubParams := masterKeyPub.Marshal()
var privParams []byte
var params []byte
var masterKeyPriv *snacl.SecretKey
var cryptoKeyPrivEnc []byte
var cryptoKeyScriptEnc []byte
masterKeyPriv, err = newSecretKey(&privPassphrase, config)
masterKeyPriv, err = newSecretKey(&passphrase, config)
if err != nil {
str := "failed to master private key"
return managerError(ErrCrypto, str, err)
}
defer masterKeyPriv.Zero()
// Generate the private passphrase salt. This is used when
// hashing passwords to detect whether an unlock can be
// avoided when the manager is already unlocked.
var privPassphraseSalt [saltSize]byte
_, err = rand.Read(privPassphraseSalt[:])
// Generate the passphrase salt. This is used when hashing passwords
// to detect whether an unlock can be avoided when the manager is
// already unlocked.
var passphraseSalt [saltSize]byte
_, err = rand.Read(passphraseSalt[:])
if err != nil {
str := "failed to read random source for passphrase salt"
return managerError(ErrCrypto, str, err)
@ -1788,10 +1749,10 @@ func Create(ns walletdb.ReadWriteBucket, rootKey *hdkeychain.ExtendedKey,
return maybeConvertDbError(err)
}
privParams = masterKeyPriv.Marshal()
params = masterKeyPriv.Marshal()
// Save the master key params to the database.
err = putMasterKeyParams(ns, pubParams, privParams)
err = putMasterKeyParams(ns, pubParams, params)
if err != nil {
return maybeConvertDbError(err)
}

View file

@ -334,8 +334,8 @@ func testExternalAddresses(tc *testContext) bool {
err := walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
var err error
addrs, err = tc.manager.NextExternalAddresses(
ns, tc.internalAccount, 5,
addrs, err = tc.manager.NextAddresses(
ns, tc.internalAccount, ExternalBranch, 5,
)
return err
})
@ -371,8 +371,8 @@ func testExternalAddresses(tc *testContext) bool {
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey)
var err error
lastAddr, err = tc.manager.LastExternalAddress(
ns, tc.internalAccount,
lastAddr, err = tc.manager.LastAddress(
ns, tc.internalAccount, ExternalBranch,
)
return err
})
@ -431,7 +431,7 @@ func testExternalAddresses(tc *testContext) bool {
// private information is valid as well.
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey)
return tc.rootManager.Unlock(ns, privPassphrase)
return tc.rootManager.Unlock(ns, passphrase)
})
if err != nil {
tc.t.Errorf("Unlock: unexpected error: %v", err)
@ -463,7 +463,7 @@ func testInternalAddresses(tc *testContext) bool {
// private information is valid as well.
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey)
return tc.rootManager.Unlock(ns, privPassphrase)
return tc.rootManager.Unlock(ns, passphrase)
})
if err != nil {
tc.t.Errorf("Unlock: unexpected error: %v", err)
@ -478,8 +478,8 @@ func testInternalAddresses(tc *testContext) bool {
err := walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
var err error
addrs, err = tc.manager.NextInternalAddresses(
ns, tc.internalAccount, 5,
addrs, err = tc.manager.NextAddresses(
ns, tc.internalAccount, InternalBranch, 5,
)
return err
})
@ -515,8 +515,8 @@ func testInternalAddresses(tc *testContext) bool {
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey)
var err error
lastAddr, err = tc.manager.LastInternalAddress(
ns, tc.internalAccount,
lastAddr, err = tc.manager.LastAddress(
ns, tc.internalAccount, InternalBranch,
)
return err
})
@ -603,7 +603,7 @@ func testLocking(tc *testContext) bool {
// unexpected errors and the manager properly reports it is unlocked.
err = walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey)
return tc.rootManager.Unlock(ns, privPassphrase)
return tc.rootManager.Unlock(ns, passphrase)
})
if err != nil {
tc.t.Errorf("Unlock: unexpected error: %v", err)
@ -617,7 +617,7 @@ func testLocking(tc *testContext) bool {
// Unlocking the manager again is allowed.
err = walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey)
return tc.rootManager.Unlock(ns, privPassphrase)
return tc.rootManager.Unlock(ns, passphrase)
})
if err != nil {
tc.t.Errorf("Unlock: unexpected error: %v", err)
@ -696,7 +696,7 @@ func testImportPrivateKey(tc *testContext) bool {
// The manager must be unlocked to import a private key.
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey)
return tc.rootManager.Unlock(ns, privPassphrase)
return tc.rootManager.Unlock(ns, passphrase)
})
if err != nil {
tc.t.Errorf("Unlock: unexpected error: %v", err)
@ -913,7 +913,7 @@ func testImportScript(tc *testContext) bool {
// testing private data.
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey)
return tc.rootManager.Unlock(ns, privPassphrase)
return tc.rootManager.Unlock(ns, passphrase)
})
if err != nil {
tc.t.Errorf("Unlock: unexpected error: %v", err)
@ -1122,74 +1122,11 @@ func testMarkUsed(tc *testContext, doScript bool) bool {
func testChangePassphrase(tc *testContext) bool {
pfx := fmt.Sprintf("(%s) ", tc.caseName)
// Force an error when changing the passphrase due to failure to
// generate a new secret key by replacing the generation function one
// that intentionally errors.
testName := pfx + "ChangePassphrase (public) with invalid new secret key"
oldKeyGen := SetSecretKeyGen(failingSecretKeyGen)
testName := pfx + "ChangePassphrase with invalid old passphrase"
err := walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
return tc.rootManager.ChangePassphrase(
ns, pubPassphrase, pubPassphrase2, false, fastScrypt,
)
})
if !checkManagerError(tc.t, testName, err, ErrCrypto) {
return false
}
// Attempt to change public passphrase with invalid old passphrase.
testName = pfx + "ChangePassphrase (public) with invalid old passphrase"
SetSecretKeyGen(oldKeyGen)
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
return tc.rootManager.ChangePassphrase(
ns, []byte("bogus"), pubPassphrase2, false, fastScrypt,
)
})
if !checkManagerError(tc.t, testName, err, ErrWrongPassphrase) {
return false
}
// Change the public passphrase.
testName = pfx + "ChangePassphrase (public)"
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
return tc.rootManager.ChangePassphrase(
ns, pubPassphrase, pubPassphrase2, false, fastScrypt,
)
})
if err != nil {
tc.t.Errorf("%s: unexpected error: %v", testName, err)
return false
}
// Ensure the public passphrase was successfully changed. We do this by
// being able to re-derive the public key with the new passphrase.
secretKey := snacl.SecretKey{Key: &snacl.CryptoKey{}}
secretKey.Parameters = tc.rootManager.masterKeyPub.Parameters
if err := secretKey.DeriveKey(&pubPassphrase2); err != nil {
tc.t.Errorf("%s: passphrase does not match", testName)
return false
}
// Change the private passphrase back to what it was.
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
return tc.rootManager.ChangePassphrase(
ns, pubPassphrase2, pubPassphrase, false, fastScrypt,
)
})
if err != nil {
tc.t.Errorf("%s: unexpected error: %v", testName, err)
return false
}
testName = pfx + "ChangePassphrase (private) with invalid old passphrase"
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
return tc.rootManager.ChangePassphrase(
ns, []byte("bogus"), privPassphrase2, true, fastScrypt,
ns, []byte("bogus"), privPassphrase2, fastScrypt,
)
})
wantErrCode := ErrWrongPassphrase
@ -1197,12 +1134,11 @@ func testChangePassphrase(tc *testContext) bool {
return false
}
// Change the private passphrase.
testName = pfx + "ChangePassphrase (private)"
testName = pfx + "ChangePassphrase"
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
return tc.rootManager.ChangePassphrase(
ns, privPassphrase, privPassphrase2, true, fastScrypt,
ns, passphrase, privPassphrase2, fastScrypt,
)
})
if err != nil {
@ -1217,8 +1153,8 @@ func testChangePassphrase(tc *testContext) bool {
return tc.rootManager.Unlock(ns, privPassphrase2)
})
if err != nil {
tc.t.Errorf("%s: failed to unlock with new private "+
"passphrase: %v", testName, err)
tc.t.Errorf("%s: failed to unlock with new passphrase: %v",
testName, err)
return false
}
tc.unlocked = true
@ -1228,7 +1164,7 @@ func testChangePassphrase(tc *testContext) bool {
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
return tc.rootManager.ChangePassphrase(
ns, privPassphrase2, privPassphrase, true, fastScrypt,
ns, privPassphrase2, passphrase, fastScrypt,
)
})
if err != nil {
@ -1269,7 +1205,7 @@ func testNewAccount(tc *testContext) bool {
// to derive account keys
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
err := tc.rootManager.Unlock(ns, privPassphrase)
err := tc.rootManager.Unlock(ns, passphrase)
return err
})
if err != nil {
@ -1668,7 +1604,7 @@ func _TestManager(t *testing.T) {
{
name: "created with seed",
rootKey: rootKey,
privPassphrase: privPassphrase,
privPassphrase: passphrase,
},
}
@ -1688,7 +1624,7 @@ func testManagerCase(t *testing.T, caseName string,
// returned.
err := walletdb.View(db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey)
_, err := Open(ns, pubPassphrase, &chaincfg.MainNetParams)
_, err := Open(ns, &chaincfg.MainNetParams)
return err
})
if !checkManagerError(t, "Open non-existent", err, ErrNoExist) {
@ -1703,13 +1639,13 @@ func testManagerCase(t *testing.T, caseName string,
return err
}
err = Create(
ns, caseKey, pubPassphrase, casePrivPassphrase,
ns, caseKey, casePrivPassphrase,
&chaincfg.MainNetParams, fastScrypt, time.Time{},
)
if err != nil {
return err
}
mgr, err = Open(ns, pubPassphrase, &chaincfg.MainNetParams)
mgr, err = Open(ns, &chaincfg.MainNetParams)
if err != nil {
return err
}
@ -1729,7 +1665,7 @@ func testManagerCase(t *testing.T, caseName string,
err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
return Create(
ns, caseKey, pubPassphrase, casePrivPassphrase,
ns, caseKey, casePrivPassphrase,
&chaincfg.MainNetParams, fastScrypt, time.Time{},
)
})
@ -1762,7 +1698,7 @@ func testManagerCase(t *testing.T, caseName string,
err = walletdb.View(db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey)
var err error
mgr, err = Open(ns, pubPassphrase, &chaincfg.MainNetParams)
mgr, err = Open(ns, &chaincfg.MainNetParams)
return err
})
if err != nil {
@ -1845,7 +1781,7 @@ func TestManagerHigherVersion(t *testing.T) {
// should expect to see the error ErrUpgrade.
err = walletdb.View(db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey)
_, err := Open(ns, pubPassphrase, &chaincfg.MainNetParams)
_, err := Open(ns, &chaincfg.MainNetParams)
return err
})
if !checkManagerError(t, "Upgrade needed", err, ErrUpgrade) {
@ -1869,7 +1805,7 @@ func TestManagerHigherVersion(t *testing.T) {
// ErrUpgrade.
err = walletdb.View(db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey)
_, err := Open(ns, pubPassphrase, &chaincfg.MainNetParams)
_, err := Open(ns, &chaincfg.MainNetParams)
return err
})
if !checkManagerError(t, "Upgrade needed", err, ErrUpgrade) {
@ -1913,7 +1849,7 @@ func TestEncryptDecryptErrors(t *testing.T) {
// Unlock the manager for these tests
err = walletdb.View(db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey)
return mgr.Unlock(ns, privPassphrase)
return mgr.Unlock(ns, passphrase)
})
if err != nil {
t.Fatal("Attempted to unlock the manager, but failed:", err)
@ -1945,7 +1881,7 @@ func TestEncryptDecrypt(t *testing.T) {
// Make sure address manager is unlocked
err := walletdb.View(db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey)
return mgr.Unlock(ns, privPassphrase)
return mgr.Unlock(ns, passphrase)
})
if err != nil {
t.Fatal("Attempted to unlock the manager, but failed:", err)
@ -1993,19 +1929,19 @@ func TestScopedKeyManagerManagement(t *testing.T) {
return err
}
err = Create(
ns, rootKey, pubPassphrase, privPassphrase,
ns, rootKey, passphrase,
&chaincfg.MainNetParams, fastScrypt, time.Time{},
)
if err != nil {
return err
}
mgr, err = Open(ns, pubPassphrase, &chaincfg.MainNetParams)
mgr, err = Open(ns, &chaincfg.MainNetParams)
if err != nil {
return err
}
return mgr.Unlock(ns, privPassphrase)
return mgr.Unlock(ns, passphrase)
})
if err != nil {
t.Fatalf("create/open: unexpected error: %v", err)
@ -2032,8 +1968,8 @@ func TestScopedKeyManagerManagement(t *testing.T) {
t.Fatalf("unable to fetch scope %v: %v", scope, err)
}
externalAddr, err := sMgr.NextExternalAddresses(
ns, DefaultAccountNum, 1,
externalAddr, err := sMgr.NextAddresses(
ns, DefaultAccountNum, ExternalBranch, 1,
)
if err != nil {
t.Fatalf("unable to derive external addr: %v", err)
@ -2047,8 +1983,8 @@ func TestScopedKeyManagerManagement(t *testing.T) {
ScopeAddrMap[scope].ExternalAddrType)
}
internalAddr, err := sMgr.NextInternalAddresses(
ns, DefaultAccountNum, 1,
internalAddr, err := sMgr.NextAddresses(
ns, DefaultAccountNum, InternalBranch, 1,
)
if err != nil {
t.Fatalf("unable to derive internal addr: %v", err)
@ -2106,15 +2042,15 @@ func TestScopedKeyManagerManagement(t *testing.T) {
// We'll now create a new external address to ensure we
// retrieve the proper type.
externalAddr, err = scopedMgr.NextExternalAddresses(
ns, DefaultAccountNum, 1,
externalAddr, err = scopedMgr.NextAddresses(
ns, DefaultAccountNum, ExternalBranch, 1,
)
if err != nil {
t.Fatalf("unable to derive external addr: %v", err)
}
internalAddr, err = scopedMgr.NextInternalAddresses(
ns, DefaultAccountNum, 1,
internalAddr, err = scopedMgr.NextAddresses(
ns, DefaultAccountNum, InternalBranch, 1,
)
if err != nil {
t.Fatalf("unable to derive internal addr: %v", err)
@ -2152,12 +2088,12 @@ func TestScopedKeyManagerManagement(t *testing.T) {
err = walletdb.View(db, func(tx walletdb.ReadTx) error {
ns := tx.ReadBucket(waddrmgrNamespaceKey)
var err error
mgr, err = Open(ns, pubPassphrase, &chaincfg.MainNetParams)
mgr, err = Open(ns, &chaincfg.MainNetParams)
if err != nil {
return err
}
return mgr.Unlock(ns, privPassphrase)
return mgr.Unlock(ns, passphrase)
})
if err != nil {
t.Fatalf("open: unexpected error: %v", err)
@ -2177,8 +2113,8 @@ func TestScopedKeyManagerManagement(t *testing.T) {
err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
lastAddr, err = scopedMgr.LastExternalAddress(
ns, DefaultAccountNum,
lastAddr, err = scopedMgr.LastAddress(
ns, DefaultAccountNum, ExternalBranch,
)
if err != nil {
return err
@ -2243,19 +2179,19 @@ func TestRootHDKeyNeutering(t *testing.T) {
return err
}
err = Create(
ns, rootKey, pubPassphrase, privPassphrase,
ns, rootKey, passphrase,
&chaincfg.MainNetParams, fastScrypt, time.Time{},
)
if err != nil {
return err
}
mgr, err = Open(ns, pubPassphrase, &chaincfg.MainNetParams)
mgr, err = Open(ns, &chaincfg.MainNetParams)
if err != nil {
return err
}
return mgr.Unlock(ns, privPassphrase)
return mgr.Unlock(ns, passphrase)
})
if err != nil {
t.Fatalf("create/open: unexpected error: %v", err)
@ -2336,19 +2272,19 @@ func TestNewRawAccount(t *testing.T) {
return err
}
err = Create(
ns, rootKey, pubPassphrase, privPassphrase,
ns, rootKey, passphrase,
&chaincfg.MainNetParams, fastScrypt, time.Time{},
)
if err != nil {
return err
}
mgr, err = Open(ns, pubPassphrase, &chaincfg.MainNetParams)
mgr, err = Open(ns, &chaincfg.MainNetParams)
if err != nil {
return err
}
return mgr.Unlock(ns, privPassphrase)
return mgr.Unlock(ns, passphrase)
})
if err != nil {
t.Fatalf("create/open: unexpected error: %v", err)
@ -2384,8 +2320,8 @@ func testNewRawAccount(t *testing.T, _ *Manager, db walletdb.DB,
err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
addrs, err := scopedMgr.NextExternalAddresses(
ns, accountNum, 1,
addrs, err := scopedMgr.NextAddresses(
ns, accountNum, ExternalBranch, 1,
)
if err != nil {
return err
@ -2449,18 +2385,18 @@ func TestDeriveFromKeyPathCache(t *testing.T) {
return err
}
err = Create(
ns, rootKey, pubPassphrase, privPassphrase,
ns, rootKey, passphrase,
&chaincfg.MainNetParams, fastScrypt, time.Time{},
)
if err != nil {
return err
}
mgr, err = Open(ns, pubPassphrase, &chaincfg.MainNetParams)
mgr, err = Open(ns, &chaincfg.MainNetParams)
if err != nil {
return err
}
return mgr.Unlock(ns, privPassphrase)
return mgr.Unlock(ns, passphrase)
})
require.NoError(t, err, "create/open: unexpected error: %v", err)

View file

@ -318,14 +318,6 @@ func populateBirthdayBlock(ns walletdb.ReadWriteBucket) error {
genesisTimestamp =
chaincfg.RegressionNetParams.GenesisBlock.Header.Timestamp
case *chaincfg.SimNetParams.GenesisHash:
genesisTimestamp =
chaincfg.SimNetParams.GenesisBlock.Header.Timestamp
case *chaincfg.SigNetParams.GenesisHash:
genesisTimestamp =
chaincfg.SigNetParams.GenesisBlock.Header.Timestamp
default:
return fmt.Errorf("unknown genesis hash %v", genesisHash)
}

View file

@ -14,7 +14,6 @@ import (
btcutil "github.com/lbryio/lbcutil"
"github.com/lbryio/lbcutil/hdkeychain"
"github.com/lbryio/lbcwallet/internal/zero"
"github.com/lbryio/lbcwallet/netparams"
"github.com/lbryio/lbcwallet/walletdb"
)
@ -46,11 +45,6 @@ const (
// HDVersionTestNetBIP0084 is the HDVersion for BIP-0084 on the test
// network.
HDVersionTestNetBIP0084 HDVersion = 0x045f1cf6 // vpub
// HDVersionSimNetBIP0044 is the HDVersion for BIP-0044 on the
// simulation test network. There aren't any other versions defined for
// the simulation test network.
HDVersionSimNetBIP0044 HDVersion = 0x0420bd3a // spub
)
const (
@ -116,11 +110,15 @@ type KeyScope struct {
// identify a particular child key, when the account and branch can be inferred
// from context.
type ScopedIndex struct {
// Scope is the BIP44 account' used to derive the child key.
Scope KeyScope
Scope KeyScope
Account uint32
Branch uint32
Index uint32
}
// Index is the BIP44 address_index used to derive the child key.
Index uint32
func (i ScopedIndex) String() string {
return fmt.Sprintf("%s/%d'/%d/%d",
i.Scope, i.Account, i.Branch, i.Index)
}
// String returns a human readable version describing the keypath encapsulated
@ -164,6 +162,9 @@ var (
Coin: 140,
}
// Set Default Scope to BIP0044 (legacy address type).
DefaultKeyScope = KeyScopeBIP0044
// DefaultKeyScopes is the set of default key scopes that will be
// created by the root manager upon initial creation.
DefaultKeyScopes = []KeyScope{
@ -401,11 +402,13 @@ func (s *ScopedKeyManager) loadAccountInfo(ns walletdb.ReadBucket,
switch row := rowInterface.(type) {
case *dbDefaultAccountRow:
acctInfo = &accountInfo{
acctName: row.name,
acctType: row.acctType,
acctKeyEncrypted: row.privKeyEncrypted,
nextExternalIndex: row.nextExternalIndex,
nextInternalIndex: row.nextInternalIndex,
acctName: row.name,
acctType: row.acctType,
acctKeyEncrypted: row.privKeyEncrypted,
nextIndex: [2]uint32{
row.nextExternalIndex,
row.nextInternalIndex,
},
}
// Use the crypto public key to decrypt the account public
@ -437,49 +440,30 @@ func (s *ScopedKeyManager) loadAccountInfo(ns walletdb.ReadBucket,
return nil, managerError(ErrDatabase, str, nil)
}
// Derive and cache the managed address for the last external address.
branch, index := ExternalBranch, acctInfo.nextExternalIndex
if index > 0 {
index--
}
lastExtAddrPath := DerivationPath{
InternalAccount: account,
Account: acctInfo.acctKeyPub.ChildIndex(),
Branch: branch,
Index: index,
MasterKeyFingerprint: acctInfo.masterKeyFingerprint,
}
lastExtKey, err := s.deriveKey(acctInfo, branch, index, hasPrivateKey)
if err != nil {
return nil, err
}
lastExtAddr, err := s.keyToManaged(lastExtKey, lastExtAddrPath, acctInfo)
if err != nil {
return nil, err
}
acctInfo.lastExternalAddr = lastExtAddr
for branch := 0; branch < 2; branch++ {
// Derive and cache the managed address for the last internal address.
branch, index = InternalBranch, acctInfo.nextInternalIndex
if index > 0 {
index--
// Derive and cache the managed address for the last external address.
index := acctInfo.nextIndex[branch]
if index > 0 {
index--
}
lastAddrPath := DerivationPath{
InternalAccount: account,
Account: acctInfo.acctKeyPub.ChildIndex(),
Branch: uint32(branch),
Index: index,
MasterKeyFingerprint: acctInfo.masterKeyFingerprint,
}
lastKey, err := s.deriveKey(acctInfo, uint32(branch), index, hasPrivateKey)
if err != nil {
return nil, err
}
lastAddr, err := s.keyToManaged(lastKey, lastAddrPath, acctInfo)
if err != nil {
return nil, err
}
acctInfo.lastAddr[branch] = lastAddr
}
lastIntAddrPath := DerivationPath{
InternalAccount: account,
Account: acctInfo.acctKeyPub.ChildIndex(),
Branch: branch,
Index: index,
MasterKeyFingerprint: acctInfo.masterKeyFingerprint,
}
lastIntKey, err := s.deriveKey(acctInfo, branch, index, hasPrivateKey)
if err != nil {
return nil, err
}
lastIntAddr, err := s.keyToManaged(lastIntKey, lastIntAddrPath, acctInfo)
if err != nil {
return nil, err
}
acctInfo.lastInternalAddr = lastIntAddr
// Add it to the cache and return it when everything is successful.
s.acctInfo[account] = acctInfo
@ -516,8 +500,8 @@ func (s *ScopedKeyManager) AccountProperties(ns walletdb.ReadBucket,
return nil, err
}
props.AccountName = acctInfo.acctName
props.ExternalKeyCount = acctInfo.nextExternalIndex
props.InternalKeyCount = acctInfo.nextInternalIndex
props.ExternalKeyCount = acctInfo.nextIndex[ExternalBranch]
props.InternalKeyCount = acctInfo.nextIndex[InternalBranch]
props.AccountPubKey = acctInfo.acctKeyPub
props.MasterKeyFingerprint = acctInfo.masterKeyFingerprint
props.AddrSchema = acctInfo.addrSchema
@ -642,6 +626,14 @@ func (s *ScopedKeyManager) DeriveFromKeyPathCache(
return privKey, nil
}
func (s *ScopedKeyManager) DeriveFromExtKeys(kp DerivationPath,
derivedKey *hdkeychain.ExtendedKey,
addrType AddressType) (ManagedAddress, error) {
return newManagedAddressFromExtKey(
s, kp, derivedKey, addrType,
)
}
// DeriveFromKeyPath attempts to derive a maximal child key (under the BIP0044
// scheme) from a given key path. If key derivation isn't possible, then an
// error will be returned.
@ -938,7 +930,7 @@ func (s *ScopedKeyManager) accountAddrType(acctInfo *accountInfo,
//
// This function MUST be called with the manager lock held for writes.
func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket,
account uint32, numAddresses uint32, internal bool) ([]ManagedAddress, error) {
account uint32, branch uint32, numAddresses uint32) ([]ManagedAddress, error) {
// The next address can only be generated for accounts that have
// already been created.
@ -956,16 +948,12 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket,
// Choose the branch key and index depending on whether or not this is
// an internal address.
branchNum, nextIndex := ExternalBranch, acctInfo.nextExternalIndex
if internal {
branchNum = InternalBranch
nextIndex = acctInfo.nextInternalIndex
}
nextIndex := acctInfo.nextIndex[branch]
// Choose the appropriate type of address to derive since it's possible
// for a watch-only account to have a different schema from the
// manager's.
addrType := s.accountAddrType(acctInfo, internal)
addrType := s.accountAddrType(acctInfo, branch == InternalBranch)
// Ensure the requested number of addresses doesn't exceed the maximum
// allowed for this account.
@ -978,10 +966,9 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket,
}
// Derive the appropriate branch key and ensure it is zeroed when done.
branchKey, err := acctKey.Derive(branchNum)
branchKey, err := acctKey.Derive(branch)
if err != nil {
str := fmt.Sprintf("failed to derive extended key branch %d",
branchNum)
str := fmt.Sprintf("failed to derive extended key branch %d", branch)
return nil, managerError(ErrKeyChain, str, err)
}
defer branchKey.Zero() // Ensure branch key is zeroed when done.
@ -1021,7 +1008,7 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket,
derivationPath := DerivationPath{
InternalAccount: account,
Account: acctKey.ChildIndex(),
Branch: branchNum,
Branch: branch,
Index: nextIndex - 1,
}
@ -1035,15 +1022,13 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket,
if err != nil {
return nil, err
}
if internal {
addr.internal = true
}
addr.internal = branch == InternalBranch
managedAddr := addr
nextKey.Zero()
info := unlockDeriveInfo{
managedAddr: managedAddr,
branch: branchNum,
branch: branch,
index: nextIndex - 1,
}
addressInfo = append(addressInfo, &info)
@ -1113,13 +1098,8 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket,
// Set the last address and next address for tracking.
ma := addressInfo[len(addressInfo)-1].managedAddr
if internal {
acctInfo.nextInternalIndex = nextIndex
acctInfo.lastInternalAddr = ma
} else {
acctInfo.nextExternalIndex = nextIndex
acctInfo.lastExternalAddr = ma
}
acctInfo.nextIndex[branch] = nextIndex
acctInfo.lastAddr[branch] = ma
}
ns.Tx().OnCommit(onCommit)
@ -1134,11 +1114,28 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket,
//
// This function MUST be called with the manager lock held for writes.
func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket,
account uint32, lastIndex uint32, internal bool) error {
account uint32, branch uint32, lastIndex uint32) error {
// The next address can only be generated for accounts that have
// already been created.
acctInfo, err := s.loadAccountInfo(ns, account)
if err != nil {
err = s.newAccount(ns, account, fmt.Sprintf("act:%v", account))
if err != nil {
return err
}
for gapAccount := account - 1; gapAccount >= 0; gapAccount-- {
_, err = s.loadAccountInfo(ns, gapAccount)
if err == nil {
break
}
err = s.newAccount(ns, gapAccount, fmt.Sprintf("act:%v", gapAccount))
if err != nil {
return err
}
}
}
acctInfo, err = s.loadAccountInfo(ns, account)
if err != nil {
return err
}
@ -1152,16 +1149,12 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket,
// Choose the branch key and index depending on whether or not this is
// an internal address.
branchNum, nextIndex := ExternalBranch, acctInfo.nextExternalIndex
if internal {
branchNum = InternalBranch
nextIndex = acctInfo.nextInternalIndex
}
nextIndex := acctInfo.nextIndex[branch]
// Choose the appropriate type of address to derive since it's possible
// for a watch-only account to have a different schema from the
// manager's.
addrType := s.accountAddrType(acctInfo, internal)
addrType := s.accountAddrType(acctInfo, branch == InternalBranch)
// If the last index requested is already lower than the next index, we
// can return early.
@ -1179,10 +1172,10 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket,
}
// Derive the appropriate branch key and ensure it is zeroed when done.
branchKey, err := acctKey.Derive(branchNum)
branchKey, err := acctKey.Derive(branch)
if err != nil {
str := fmt.Sprintf("failed to derive extended key branch %d",
branchNum)
branch)
return managerError(ErrKeyChain, str, err)
}
defer branchKey.Zero() // Ensure branch key is zeroed when done.
@ -1224,7 +1217,7 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket,
derivationPath := DerivationPath{
InternalAccount: account,
Account: acctInfo.acctKeyPub.ChildIndex(),
Branch: branchNum,
Branch: branch,
Index: nextIndex - 1,
}
@ -1238,15 +1231,13 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket,
if err != nil {
return err
}
if internal {
addr.internal = true
}
addr.internal = branch == InternalBranch
managedAddr := addr
nextKey.Zero()
info := unlockDeriveInfo{
managedAddr: managedAddr,
branch: branchNum,
branch: branch,
index: nextIndex - 1,
}
addressInfo = append(addressInfo, &info)
@ -1302,21 +1293,16 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket,
// Set the last address and next address for tracking.
ma := addressInfo[len(addressInfo)-1].managedAddr
if internal {
acctInfo.nextInternalIndex = nextIndex
acctInfo.lastInternalAddr = ma
} else {
acctInfo.nextExternalIndex = nextIndex
acctInfo.lastExternalAddr = ma
}
acctInfo.nextIndex[branch] = nextIndex
acctInfo.lastAddr[branch] = ma
return nil
}
// NextExternalAddresses returns the specified number of next chained addresses
// NextAddresses returns the specified number of next chained addresses
// that are intended for external use from the address manager.
func (s *ScopedKeyManager) NextExternalAddresses(ns walletdb.ReadWriteBucket,
account uint32, numAddresses uint32) ([]ManagedAddress, error) {
func (s *ScopedKeyManager) NextAddresses(ns walletdb.ReadWriteBucket,
account uint32, branch uint32, numAddresses uint32) ([]ManagedAddress, error) {
// Enforce maximum account number.
if account > MaxAccountNum {
@ -1327,32 +1313,15 @@ func (s *ScopedKeyManager) NextExternalAddresses(ns walletdb.ReadWriteBucket,
s.mtx.Lock()
defer s.mtx.Unlock()
return s.nextAddresses(ns, account, numAddresses, false)
}
// NextInternalAddresses returns the specified number of next chained addresses
// that are intended for internal use such as change from the address manager.
func (s *ScopedKeyManager) NextInternalAddresses(ns walletdb.ReadWriteBucket,
account uint32, numAddresses uint32) ([]ManagedAddress, error) {
// Enforce maximum account number.
if account > MaxAccountNum {
err := managerError(ErrAccountNumTooHigh, errAcctTooHigh, nil)
return nil, err
}
s.mtx.Lock()
defer s.mtx.Unlock()
return s.nextAddresses(ns, account, numAddresses, true)
return s.nextAddresses(ns, account, branch, numAddresses)
}
// ExtendExternalAddresses ensures that all valid external keys through
// lastIndex are derived and stored in the wallet. This is used to ensure that
// wallet's persistent state catches up to a external child that was found
// during recovery.
func (s *ScopedKeyManager) ExtendExternalAddresses(ns walletdb.ReadWriteBucket,
account uint32, lastIndex uint32) error {
func (s *ScopedKeyManager) ExtendAddresses(ns walletdb.ReadWriteBucket,
account uint32, branch uint32, lastIndex uint32) error {
if account > MaxAccountNum {
err := managerError(ErrAccountNumTooHigh, errAcctTooHigh, nil)
@ -1362,28 +1331,10 @@ func (s *ScopedKeyManager) ExtendExternalAddresses(ns walletdb.ReadWriteBucket,
s.mtx.Lock()
defer s.mtx.Unlock()
return s.extendAddresses(ns, account, lastIndex, false)
return s.extendAddresses(ns, account, branch, lastIndex)
}
// ExtendInternalAddresses ensures that all valid internal keys through
// lastIndex are derived and stored in the wallet. This is used to ensure that
// wallet's persistent state catches up to an internal child that was found
// during recovery.
func (s *ScopedKeyManager) ExtendInternalAddresses(ns walletdb.ReadWriteBucket,
account uint32, lastIndex uint32) error {
if account > MaxAccountNum {
err := managerError(ErrAccountNumTooHigh, errAcctTooHigh, nil)
return err
}
s.mtx.Lock()
defer s.mtx.Unlock()
return s.extendAddresses(ns, account, lastIndex, true)
}
// LastExternalAddress returns the most recently requested chained external
// LastAddress returns the most recently requested chained external
// address from calling NextExternalAddress for the given account. The first
// external address for the account will be returned if none have been
// previously requested.
@ -1391,8 +1342,8 @@ func (s *ScopedKeyManager) ExtendInternalAddresses(ns walletdb.ReadWriteBucket,
// This function will return an error if the provided account number is greater
// than the MaxAccountNum constant or there is no account information for the
// passed account. Any other errors returned are generally unexpected.
func (s *ScopedKeyManager) LastExternalAddress(ns walletdb.ReadBucket,
account uint32) (ManagedAddress, error) {
func (s *ScopedKeyManager) LastAddress(ns walletdb.ReadBucket,
account, branch uint32) (ManagedAddress, error) {
// Enforce maximum account number.
if account > MaxAccountNum {
@ -1410,47 +1361,13 @@ func (s *ScopedKeyManager) LastExternalAddress(ns walletdb.ReadBucket,
return nil, err
}
if acctInfo.nextExternalIndex > 0 {
return acctInfo.lastExternalAddr, nil
if acctInfo.nextIndex[branch] > 0 {
return acctInfo.lastAddr[branch], nil
}
return nil, managerError(ErrAddressNotFound, "no previous external address", nil)
}
// LastInternalAddress returns the most recently requested chained internal
// address from calling NextInternalAddress for the given account. The first
// internal address for the account will be returned if none have been
// previously requested.
//
// This function will return an error if the provided account number is greater
// than the MaxAccountNum constant or there is no account information for the
// passed account. Any other errors returned are generally unexpected.
func (s *ScopedKeyManager) LastInternalAddress(ns walletdb.ReadBucket,
account uint32) (ManagedAddress, error) {
// Enforce maximum account number.
if account > MaxAccountNum {
err := managerError(ErrAccountNumTooHigh, errAcctTooHigh, nil)
return nil, err
}
s.mtx.Lock()
defer s.mtx.Unlock()
// Load account information for the passed account. It is typically
// cached, but if not it will be loaded from the database.
acctInfo, err := s.loadAccountInfo(ns, account)
if err != nil {
return nil, err
}
if acctInfo.nextInternalIndex > 0 {
return acctInfo.lastInternalAddr, nil
}
return nil, managerError(ErrAddressNotFound, "no previous internal address", nil)
}
// NewRawAccount creates a new account for the scoped manager. This method
// differs from the NewAccount method in that this method takes the account
// number *directly*, rather than taking a string name for the account, then
@ -1581,10 +1498,42 @@ func (s *ScopedKeyManager) newAccount(ns walletdb.ReadWriteBucket,
return err
}
lastAccount, err := fetchLastAccount(ns, &s.scope)
if account < lastAccount {
return nil
}
// Save last account metadata
return putLastAccount(ns, &s.scope, account)
}
func (s *ScopedKeyManager) DeriveAccountKey(ns walletdb.ReadWriteBucket,
account uint32) (*hdkeychain.ExtendedKey, error) {
_, coinTypePrivEnc, err := fetchCoinTypeKeys(ns, &s.scope)
if err != nil {
return nil, err
}
// Decrypt the cointype key.
serializedKeyPriv, err := s.rootManager.cryptoKeyPriv.Decrypt(coinTypePrivEnc)
if err != nil {
str := fmt.Sprintf("failed to decrypt cointype serialized private key")
return nil, managerError(ErrLocked, str, err)
}
defer zero.Bytes(serializedKeyPriv)
coinTypeKeyPriv, err := hdkeychain.NewKeyFromString(string(serializedKeyPriv))
if err != nil {
str := fmt.Sprintf("failed to create cointype extended private key")
return nil, managerError(ErrKeyChain, str, err)
}
defer coinTypeKeyPriv.Zero()
// Derive the account key using the cointype key
return deriveAccountKey(coinTypeKeyPriv, account)
}
// RenameAccount renames an account stored in the manager based on the given
// account number with the given name. If an account with the same name
// already exists, ErrDuplicateAccount will be returned.
@ -2214,9 +2163,7 @@ func (s *ScopedKeyManager) cloneKeyWithVersion(key *hdkeychain.ExtendedKey) (
return nil, fmt.Errorf("unsupported scope %v", s.scope)
}
case wire.TestNet, wire.TestNet3,
netparams.SigNetWire(s.rootManager.ChainParams()):
case wire.TestNet, wire.TestNet3:
switch s.scope {
case KeyScopeBIP0044:
version = HDVersionTestNetBIP0044
@ -2228,21 +2175,6 @@ func (s *ScopedKeyManager) cloneKeyWithVersion(key *hdkeychain.ExtendedKey) (
return nil, fmt.Errorf("unsupported scope %v", s.scope)
}
case wire.SimNet:
switch s.scope {
case KeyScopeBIP0044:
version = HDVersionSimNetBIP0044
// We use the mainnet versions for simnet keys when the keys
// belong to a key scope which simnet doesn't have a defined
// version for.
case KeyScopeBIP0049:
version = HDVersionMainNetBIP0049
case KeyScopeBIP0084:
version = HDVersionMainNetBIP0084
default:
return nil, fmt.Errorf("unsupported scope %v", s.scope)
}
default:
return nil, fmt.Errorf("unsupported net %v", net)
}

View file

@ -1,126 +0,0 @@
github.com/lbryio/lbcwallet/waddrmgr/db.go serializeBIP0044AccountRow 100.00% (19/19)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.lock 100.00% (12/12)
github.com/lbryio/lbcwallet/waddrmgr/db.go serializeScriptAddress 100.00% (10/10)
github.com/lbryio/lbcwallet/waddrmgr/db.go serializeImportedAddress 100.00% (10/10)
github.com/lbryio/lbcwallet/waddrmgr/db.go serializeAddressRow 100.00% (9/9)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.Address 100.00% (8/8)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.Lock 100.00% (8/8)
github.com/lbryio/lbcwallet/waddrmgr/address.go scriptAddress.Script 100.00% (7/7)
github.com/lbryio/lbcwallet/waddrmgr/db.go serializeAccountRow 100.00% (6/6)
github.com/lbryio/lbcwallet/waddrmgr/sync.go BlockIterator.Prev 100.00% (6/6)
github.com/lbryio/lbcwallet/waddrmgr/address.go zeroBigInt 100.00% (5/5)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.zeroSensitivePublicData 100.00% (5/5)
github.com/lbryio/lbcwallet/waddrmgr/address.go managedAddress.lock 100.00% (4/4)
github.com/lbryio/lbcwallet/waddrmgr/address.go scriptAddress.lock 100.00% (4/4)
github.com/lbryio/lbcwallet/waddrmgr/db.go serializeChainedAddress 100.00% (4/4)
github.com/lbryio/lbcwallet/waddrmgr/address.go managedAddress.ExportPrivKey 100.00% (4/4)
github.com/lbryio/lbcwallet/waddrmgr/manager.go fileExists 100.00% (4/4)
github.com/lbryio/lbcwallet/waddrmgr/error.go ManagerError.Error 100.00% (3/3)
github.com/lbryio/lbcwallet/waddrmgr/error.go ErrorCode.String 100.00% (3/3)
github.com/lbryio/lbcwallet/waddrmgr/address.go managedAddress.pubKeyBytes 100.00% (3/3)
github.com/lbryio/lbcwallet/waddrmgr/sync.go Manager.NewIterateRecentBlocks 100.00% (3/3)
github.com/lbryio/lbcwallet/waddrmgr/sync.go BlockIterator.BlockStamp 100.00% (3/3)
github.com/lbryio/lbcwallet/waddrmgr/db.go accountKey 100.00% (3/3)
github.com/lbryio/lbcwallet/waddrmgr/sync.go Manager.SyncedTo 100.00% (3/3)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.PutAccountInfo 100.00% (3/3)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.IsLocked 100.00% (3/3)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.PutImportedAddress 100.00% (3/3)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.ExistsAddress 100.00% (3/3)
github.com/lbryio/lbcwallet/waddrmgr/address.go zero 100.00% (2/2)
github.com/lbryio/lbcwallet/waddrmgr/address.go scriptAddress.Account 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/address.go scriptAddress.Compressed 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/sync.go newSyncState 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/address.go scriptAddress.Internal 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/manager.go cryptoKey.CopyBytes 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/manager.go newManager 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/address.go managedAddress.AddrHash 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/manager.go defaultNewSecretKey 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/address.go scriptAddress.Imported 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/address.go scriptAddress.AddrHash 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/address.go scriptAddress.Address 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/address.go managedAddress.Account 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/address.go managedAddress.Internal 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.Net 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/address.go managedAddress.ExportPubKey 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/error.go managerError 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/address.go managedAddress.PubKey 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/manager.go cryptoKey.Bytes 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/address.go managedAddress.Compressed 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/address.go managedAddress.Address 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/address.go managedAddress.Imported 100.00% (1/1)
github.com/lbryio/lbcwallet/waddrmgr/sync.go Manager.SetSyncedTo 93.94% (31/33)
github.com/lbryio/lbcwallet/waddrmgr/address.go managedAddress.PrivKey 91.67% (11/12)
github.com/lbryio/lbcwallet/waddrmgr/db.go deserializeBIP0044AccountRow 90.48% (19/21)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.keyToManaged 90.00% (9/10)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.FetchCryptoKeys 88.89% (16/18)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.Close 88.89% (8/9)
github.com/lbryio/lbcwallet/waddrmgr/address.go newManagedAddressWithoutPrivKey 87.50% (7/8)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.PutRecentBlocks 85.71% (12/14)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Open 85.71% (6/7)
github.com/lbryio/lbcwallet/waddrmgr/db.go deserializeScriptAddress 84.62% (11/13)
github.com/lbryio/lbcwallet/waddrmgr/db.go deserializeImportedAddress 84.62% (11/13)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.FetchRecentBlocks 84.62% (11/13)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.FetchMasterKeyParams 84.62% (11/13)
github.com/lbryio/lbcwallet/waddrmgr/db.go deserializeAddressRow 83.33% (10/12)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.loadAndCacheAddress 83.33% (10/12)
github.com/lbryio/lbcwallet/waddrmgr/address.go managedAddress.unlock 81.82% (9/11)
github.com/lbryio/lbcwallet/waddrmgr/address.go scriptAddress.unlock 81.82% (9/11)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.nextAddresses 80.00% (52/65)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.PutScriptAddress 80.00% (4/5)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.ChangePassphrase 79.10% (53/67)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.PutChainedAddress 78.26% (18/23)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.FetchSyncedTo 77.78% (7/9)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.PutStartBlock 77.78% (7/9)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.FetchStartBlock 77.78% (7/9)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.PutSyncedTo 77.78% (7/9)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.existsAddress 77.78% (7/9)
github.com/lbryio/lbcwallet/waddrmgr/db.go deserializeAccountRow 77.78% (7/9)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.ExportWatchingOnly 75.00% (12/16)
github.com/lbryio/lbcwallet/waddrmgr/address.go newManagedAddressFromExtKey 75.00% (12/16)
github.com/lbryio/lbcwallet/waddrmgr/address.go newManagedAddress 75.00% (9/12)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.PutWatchingOnly 75.00% (6/8)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.PutNumAccounts 75.00% (6/8)
github.com/lbryio/lbcwallet/waddrmgr/address.go newScriptAddress 75.00% (3/4)
github.com/lbryio/lbcwallet/waddrmgr/manager.go defaultNewCryptoKey 75.00% (3/4)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.chainAddressRowToManaged 75.00% (3/4)
github.com/lbryio/lbcwallet/waddrmgr/manager.go checkBranchKeys 75.00% (3/4)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.deriveKeyFromPath 75.00% (3/4)
github.com/lbryio/lbcwallet/waddrmgr/manager.go loadManager 72.55% (37/51)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.putAddress 71.43% (5/7)
github.com/lbryio/lbcwallet/waddrmgr/db.go deserializeChainedAddress 71.43% (5/7)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.deriveKey 69.23% (9/13)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.ImportScript 67.44% (29/43)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.Unlock 67.35% (33/49)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.FetchAddress 66.67% (10/15)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.importedAddressRowToManaged 66.67% (10/15)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.PutMasterKeyParams 66.67% (8/12)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.LastInternalAddress 66.67% (6/9)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.LastExternalAddress 66.67% (6/9)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.putAccountRow 66.67% (4/6)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.rowInterfaceToManaged 66.67% (4/6)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.NextExternalAddresses 66.67% (4/6)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.NextInternalAddresses 66.67% (4/6)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.FetchWatchingOnly 66.67% (4/6)
github.com/lbryio/lbcwallet/waddrmgr/sync.go syncState.iter 66.67% (2/3)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.DeletePrivateKeys 66.04% (35/53)
github.com/lbryio/lbcwallet/waddrmgr/db.go openOrCreateDB 66.04% (35/53)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.ImportPrivateKey 64.71% (33/51)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.PutCryptoKeys 64.71% (11/17)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.loadAccountInfo 62.96% (34/54)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.FetchAccountInfo 61.54% (8/13)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.scriptAddressRowToManaged 60.00% (3/5)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Create 58.59% (58/99)
github.com/lbryio/lbcwallet/waddrmgr/manager.go deriveAccountKey 53.85% (7/13)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerDB.Update 50.00% (4/8)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerDB.View 50.00% (4/8)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerDB.Close 50.00% (2/4)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerDB.CopyDB 45.45% (5/11)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.FetchAllAddresses 0.00% (0/20)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.AllActiveAddresses 0.00% (0/16)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerDB.WriteTo 0.00% (0/11)
github.com/lbryio/lbcwallet/waddrmgr/sync.go BlockIterator.Next 0.00% (0/6)
github.com/lbryio/lbcwallet/waddrmgr/db.go managerTx.FetchNumAccounts 0.00% (0/6)
github.com/lbryio/lbcwallet/waddrmgr/manager.go Manager.Export 0.00% (0/3)
github.com/lbryio/lbcwallet/waddrmgr ----------------------------------- 72.59% (1030/1419)

View file

@ -83,7 +83,7 @@ func TestTxToOutputsDryRun(t *testing.T) {
}
change := dryRunTx.Tx.TxOut[dryRunTx.ChangeIndex]
addresses, err := w.AccountAddresses(0)
addresses, err := w.AccountAddresses(0, nil)
if err != nil {
t.Fatalf("unable to get addresses: %v", err)
}
@ -100,7 +100,7 @@ func TestTxToOutputsDryRun(t *testing.T) {
}
change2 := dryRunTx2.Tx.TxOut[dryRunTx2.ChangeIndex]
addresses, err = w.AccountAddresses(0)
addresses, err = w.AccountAddresses(0, nil)
if err != nil {
t.Fatalf("unable to get addresses: %v", err)
}
@ -135,7 +135,7 @@ func TestTxToOutputsDryRun(t *testing.T) {
}
change3 := tx.Tx.TxOut[tx.ChangeIndex]
addresses, err = w.AccountAddresses(0)
addresses, err = w.AccountAddresses(0, nil)
if err != nil {
t.Fatalf("unable to get addresses: %v", err)
}

View file

@ -33,19 +33,18 @@ func testWallet(t *testing.T) (*Wallet, func()) {
t.Fatalf("unable to create seed: %v", err)
}
pubPass := []byte("hello")
privPass := []byte("world")
passphrase := []byte("hello world")
loader := NewLoader(
&chaincfg.TestNet3Params, dir, true, defaultDBTimeout, 250,
)
w, err := loader.CreateNewWallet(pubPass, privPass, seed, time.Now())
w, err := loader.CreateNewWallet(passphrase, seed, time.Now())
if err != nil {
t.Fatalf("unable to create wallet: %v", err)
}
chainClient := &mockChainClient{}
w.chainClient = chainClient
if err := w.Unlock(privPass, time.After(10*time.Minute)); err != nil {
if err := w.Unlock(passphrase, time.After(10*time.Minute)); err != nil {
t.Fatalf("unable to unlock wallet: %v", err)
}

View file

@ -9,7 +9,6 @@ import (
"github.com/lbryio/lbcd/wire"
btcutil "github.com/lbryio/lbcutil"
"github.com/lbryio/lbcutil/hdkeychain"
"github.com/lbryio/lbcwallet/netparams"
"github.com/lbryio/lbcwallet/waddrmgr"
"github.com/lbryio/lbcwallet/walletdb"
)
@ -38,8 +37,7 @@ func keyScopeFromPubKey(pubKey *hdkeychain.ExtendedKey,
// force the standard BIP-0049 derivation scheme (nested witness pubkeys
// everywhere), while a witness address type will force the standard
// BIP-0084 derivation scheme.
case waddrmgr.HDVersionMainNetBIP0044, waddrmgr.HDVersionTestNetBIP0044,
waddrmgr.HDVersionSimNetBIP0044:
case waddrmgr.HDVersionMainNetBIP0044, waddrmgr.HDVersionTestNetBIP0044:
if addrType == nil {
return waddrmgr.KeyScope{}, nil, errors.New("address " +
@ -109,20 +107,10 @@ func (w *Wallet) isPubKeyForNet(pubKey *hdkeychain.ExtendedKey) bool {
version == waddrmgr.HDVersionMainNetBIP0049 ||
version == waddrmgr.HDVersionMainNetBIP0084
case wire.TestNet, wire.TestNet3, netparams.SigNetWire(w.chainParams):
case wire.TestNet, wire.TestNet3:
return version == waddrmgr.HDVersionTestNetBIP0044 ||
version == waddrmgr.HDVersionTestNetBIP0049 ||
version == waddrmgr.HDVersionTestNetBIP0084
// For simnet, we'll also allow the mainnet versions since simnet
// doesn't have defined versions for some of our key scopes, and the
// mainnet versions are usually used as the default regardless of the
// network/key scope.
case wire.SimNet:
return version == waddrmgr.HDVersionSimNetBIP0044 ||
version == waddrmgr.HDVersionMainNetBIP0049 ||
version == waddrmgr.HDVersionMainNetBIP0084
default:
return false
}
@ -326,14 +314,14 @@ func (w *Wallet) ImportAccountDryRun(name string,
// attempt, we'll want to invalidate the cache for it.
defer manager.InvalidateAccountCache(accountProps.AccountNumber)
externalAddrs, err = manager.NextExternalAddresses(
ns, accountProps.AccountNumber, numAddrs,
externalAddrs, err = manager.NextAddresses(
ns, accountProps.AccountNumber, waddrmgr.ExternalBranch, numAddrs,
)
if err != nil {
return err
}
internalAddrs, err = manager.NextInternalAddresses(
ns, accountProps.AccountNumber, numAddrs,
internalAddrs, err = manager.NextAddresses(
ns, accountProps.AccountNumber, waddrmgr.InternalBranch, numAddrs,
)
if err != nil {
return err

View file

@ -14,8 +14,6 @@ import (
"github.com/lbryio/lbcd/chaincfg"
"github.com/lbryio/lbcutil/hdkeychain"
"github.com/lbryio/lbcwallet/internal/prompt"
"github.com/lbryio/lbcwallet/waddrmgr"
"github.com/lbryio/lbcwallet/walletdb"
)
@ -140,10 +138,10 @@ func (l *Loader) OnWalletCreated(fn func(walletdb.ReadWriteTx) error) {
l.walletCreated = fn
}
// CreateNewWallet creates a new wallet using the provided public and private
// passphrases. The seed is optional. If non-nil, addresses are derived from
// this seed. If nil, a secure random seed is generated.
func (l *Loader) CreateNewWallet(pubPassphrase, privPassphrase, seed []byte,
// CreateNewWallet creates a new wallet using the provided passphrase.
// The seed is optional. If non-nil, addresses are derived from this seed.
// If nil, a secure random seed is generated.
func (l *Loader) CreateNewWallet(passphrase, seed []byte,
bday time.Time) (*Wallet, error) {
var (
@ -168,20 +166,20 @@ func (l *Loader) CreateNewWallet(pubPassphrase, privPassphrase, seed []byte,
}
}
return l.createNewWallet(pubPassphrase, privPassphrase, rootKey, bday)
return l.createNewWallet(passphrase, rootKey, bday)
}
// CreateNewWalletExtendedKey creates a new wallet from an extended master root
// key using the provided public and private passphrases. The root key is
// optional. If non-nil, addresses are derived from this root key. If nil, a
// key using the provided passphrase. The root key is optional.
// If non-nil, addresses are derived from this root key. If nil, a
// secure random seed is generated and the root key is derived from that.
func (l *Loader) CreateNewWalletExtendedKey(pubPassphrase, privPassphrase []byte,
func (l *Loader) CreateNewWalletExtendedKey(passphrase []byte,
rootKey *hdkeychain.ExtendedKey, bday time.Time) (*Wallet, error) {
return l.createNewWallet(pubPassphrase, privPassphrase, rootKey, bday)
return l.createNewWallet(passphrase, rootKey, bday)
}
func (l *Loader) createNewWallet(pubPassphrase, privPassphrase []byte,
func (l *Loader) createNewWallet(passphrase []byte,
rootKey *hdkeychain.ExtendedKey, bday time.Time) (*Wallet, error) {
defer l.mu.Unlock()
@ -216,16 +214,15 @@ func (l *Loader) createNewWallet(pubPassphrase, privPassphrase []byte,
}
// Initialize the newly created database for the wallet before opening.
err = CreateWithCallback(
l.db, pubPassphrase, privPassphrase, rootKey,
l.chainParams, bday, l.walletCreated,
err = CreateWithCallback(l.db, passphrase, rootKey, l.chainParams,
bday, l.walletCreated,
)
if err != nil {
return nil, err
}
// Open the newly-created wallet.
w, err := Open(l.db, pubPassphrase, nil, l.chainParams, l.recoveryWindow)
w, err := Open(l.db, l.chainParams, l.recoveryWindow)
if err != nil {
return nil, err
}
@ -237,15 +234,10 @@ func (l *Loader) createNewWallet(pubPassphrase, privPassphrase []byte,
var errNoConsole = errors.New("db upgrade requires console access for additional input")
func noConsole() ([]byte, error) {
return nil, errNoConsole
}
// OpenExistingWallet opens the wallet from the loader's wallet database path
// and the public passphrase. If the loader is being called by a context where
// standard input prompts may be used during wallet upgrades, setting
// canConsolePrompt will enables these prompts.
func (l *Loader) OpenExistingWallet(pubPassphrase []byte, canConsolePrompt bool) (*Wallet, error) {
// OpenExistingWallet opens the wallet from the loader's wallet database path.
// If the loader is being called by a context where standard input prompts may
// be used during wallet upgrades, setting canConsolePrompt will enables these prompts.
func (l *Loader) OpenExistingWallet() (*Wallet, error) {
defer l.mu.Unlock()
l.mu.Lock()
@ -271,19 +263,7 @@ func (l *Loader) OpenExistingWallet(pubPassphrase []byte, canConsolePrompt bool)
}
}
var cbs *waddrmgr.OpenCallbacks
if canConsolePrompt {
cbs = &waddrmgr.OpenCallbacks{
ObtainSeed: prompt.ProvideSeed(),
ObtainPrivatePass: prompt.ProvidePrivPassphrase,
}
} else {
cbs = &waddrmgr.OpenCallbacks{
ObtainSeed: noConsole,
ObtainPrivatePass: noConsole,
}
}
w, err := Open(l.db, pubPassphrase, cbs, l.chainParams, l.recoveryWindow)
w, err := Open(l.db, l.chainParams, l.recoveryWindow)
if err != nil {
// If opening the wallet fails (e.g. because of wrong
// passphrase), we must close the backing database to

View file

@ -63,66 +63,45 @@ func (rm *RecoveryManager) Resurrect(ns walletdb.ReadBucket,
// First, for each scope that we are recovering, rederive all of the
// addresses up to the last found address known to each branch.
for keyScope, scopedMgr := range scopedMgrs {
// Load the current account properties for this scope, using the
// the default account number.
// TODO(conner): rescan for all created accounts if we allow
// users to use non-default address
scopeState := rm.state.StateForScope(keyScope)
acctProperties, err := scopedMgr.AccountProperties(
ns, waddrmgr.DefaultAccountNum,
)
lastAccount, err := scopedMgr.LastAccount(ns)
if err != nil {
return err
}
// Fetch the external key count, which bounds the indexes we
// will need to rederive.
externalCount := acctProperties.ExternalKeyCount
// Walk through all indexes through the last external key,
// deriving each address and adding it to the external branch
// recovery state's set of addresses to look for.
for i := uint32(0); i < externalCount; i++ {
keyPath := externalKeyPath(i)
addr, err := scopedMgr.DeriveFromKeyPath(ns, keyPath)
if err != nil && err != hdkeychain.ErrInvalidChild {
for accountIndex, accountState := range scopeState[:lastAccount+1] {
log.Infof("Resurrecting addresses for key scope %v, account %v", keyScope, accountIndex)
acctProperties, err := scopedMgr.AccountProperties(ns,
uint32(accountIndex))
if err != nil {
return err
} else if err == hdkeychain.ErrInvalidChild {
scopeState.ExternalBranch.MarkInvalidChild(i)
continue
}
scopeState.ExternalBranch.AddAddr(i, addr.Address())
}
// Fetch the internal key count, which bounds the indexes we
// will need to rederive.
internalCount := acctProperties.InternalKeyCount
// Walk through all indexes through the last internal key,
// deriving each address and adding it to the internal branch
// recovery state's set of addresses to look for.
for i := uint32(0); i < internalCount; i++ {
keyPath := internalKeyPath(i)
addr, err := scopedMgr.DeriveFromKeyPath(ns, keyPath)
if err != nil && err != hdkeychain.ErrInvalidChild {
return err
} else if err == hdkeychain.ErrInvalidChild {
scopeState.InternalBranch.MarkInvalidChild(i)
continue
// Fetch the key count, which bounds the indexes we
// will need to rederive.
counts := []uint32{
acctProperties.ExternalKeyCount,
acctProperties.InternalKeyCount,
}
scopeState.InternalBranch.AddAddr(i, addr.Address())
}
// The key counts will point to the next key that can be
// derived, so we subtract one to point to last known key. If
// the key count is zero, then no addresses have been found.
if externalCount > 0 {
scopeState.ExternalBranch.ReportFound(externalCount - 1)
}
if internalCount > 0 {
scopeState.InternalBranch.ReportFound(internalCount - 1)
for branchIndex, branchState := range accountState {
// Walk through all indexes through the last key,
// deriving each address and adding it to the branch
// recovery state's set of addresses to look for.
for addrIndex := uint32(0); addrIndex < counts[branchIndex]; addrIndex++ {
keyPath := keyPath(uint32(accountIndex), uint32(branchIndex), addrIndex)
addr, err := scopedMgr.DeriveFromKeyPath(ns, keyPath)
if err != nil && err != hdkeychain.ErrInvalidChild {
return err
} else if err == hdkeychain.ErrInvalidChild {
branchState.MarkInvalidChild(addrIndex)
continue
}
branchState.AddAddr(addrIndex, addr.Address())
}
}
}
}
@ -202,7 +181,7 @@ type RecoveryState struct {
// scopes maintains a map of each requested key scope to its active
// RecoveryState.
scopes map[waddrmgr.KeyScope]*ScopeRecoveryState
scopes map[waddrmgr.KeyScope]ScopeRecoveryState
// watchedOutPoints contains the set of all outpoints known to the
// wallet. This is updated iteratively as new outpoints are found during
@ -214,7 +193,7 @@ type RecoveryState struct {
// recoveryWindow. Each RecoveryState that is subsequently initialized for a
// particular key scope will receive the same recoveryWindow.
func NewRecoveryState(recoveryWindow uint32) *RecoveryState {
scopes := make(map[waddrmgr.KeyScope]*ScopeRecoveryState)
scopes := make(map[waddrmgr.KeyScope]ScopeRecoveryState)
return &RecoveryState{
recoveryWindow: recoveryWindow,
@ -227,18 +206,21 @@ func NewRecoveryState(recoveryWindow uint32) *RecoveryState {
// does not already exist, a new one will be generated with the RecoveryState's
// recoveryWindow.
func (rs *RecoveryState) StateForScope(
keyScope waddrmgr.KeyScope) *ScopeRecoveryState {
keyScope waddrmgr.KeyScope) ScopeRecoveryState {
// If the account recovery state already exists, return it.
if scopeState, ok := rs.scopes[keyScope]; ok {
return scopeState
scopeState, ok := rs.scopes[keyScope]
if !ok {
for i := 0; i < waddrmgr.AccountGapLimit; i++ {
accountState := []*BranchRecoveryState{
NewBranchRecoveryState(rs.recoveryWindow),
NewBranchRecoveryState(rs.recoveryWindow),
}
scopeState = append(scopeState, accountState)
}
rs.scopes[keyScope] = scopeState
}
// Otherwise, initialize the recovery state for this scope with the
// chosen recovery window.
rs.scopes[keyScope] = NewScopeRecoveryState(rs.recoveryWindow)
return rs.scopes[keyScope]
return scopeState
}
// WatchedOutPoints returns the global set of outpoints that are known to belong
@ -256,26 +238,11 @@ func (rs *RecoveryState) AddWatchedOutPoint(outPoint *wire.OutPoint,
}
// ScopeRecoveryState is used to manage the recovery of addresses generated
// under a particular BIP32 account. Each account tracks both an external and
// internal branch recovery state, both of which use the same recovery window.
type ScopeRecoveryState struct {
// ExternalBranch is the recovery state of addresses generated for
// external use, i.e. receiving addresses.
ExternalBranch *BranchRecoveryState
// under a BIP32 accounts. Each account tracks both an external and internal
// branch recovery state, both of which use the same recovery window.
type ScopeRecoveryState []AccountRecoveryState
// InternalBranch is the recovery state of addresses generated for
// internal use, i.e. change addresses.
InternalBranch *BranchRecoveryState
}
// NewScopeRecoveryState initializes an ScopeRecoveryState with the chosen
// recovery window.
func NewScopeRecoveryState(recoveryWindow uint32) *ScopeRecoveryState {
return &ScopeRecoveryState{
ExternalBranch: NewBranchRecoveryState(recoveryWindow),
InternalBranch: NewBranchRecoveryState(recoveryWindow),
}
}
type AccountRecoveryState []*BranchRecoveryState
// BranchRecoveryState maintains the required state in-order to properly
// recover addresses derived from a particular account's internal or external

View file

@ -35,16 +35,6 @@ import (
)
const (
// InsecurePubPassphrase is the default outer encryption passphrase used
// for public data (everything but private keys). Using a non-default
// public passphrase can prevent an attacker without the public
// passphrase from discovering all past and future wallet addresses if
// they gain access to the wallet database.
//
// NOTE: at time of writing, public encryption only applies to public
// data in the waddrmgr namespace. Transactions are not yet encrypted.
InsecurePubPassphrase = "public"
// recoveryBatchSize is the default number of blocks that will be
// scanned successively by the recovery manager, in the event that the
// wallet is started in recovery mode.
@ -98,8 +88,6 @@ const (
// complete wallet. It contains the Armory-style key store
// addresses and keys),
type Wallet struct {
publicPassphrase []byte
// Data stores
db walletdb.DB
Manager *waddrmgr.Manager
@ -133,7 +121,6 @@ type Wallet struct {
holdUnlockRequests chan chan heldUnlock
lockState chan bool
changePassphrase chan changePassphraseRequest
changePassphrases chan changePassphrasesRequest
NtfnServer *NotificationServer
@ -419,7 +406,7 @@ func (w *Wallet) syncWithChain(birthdayStamp *waddrmgr.BlockStamp) error {
// If the wallet requested an on-chain recovery of its funds, we'll do
// so now.
if w.recoveryWindow > 0 {
if err := w.recovery(chainClient, birthdayStamp); err != nil {
if err := w.Recovery(chainClient); err != nil {
return fmt.Errorf("unable to perform wallet recovery: "+
"%v", err)
}
@ -541,11 +528,10 @@ func (w *Wallet) syncWithChain(birthdayStamp *waddrmgr.BlockStamp) error {
}
// isDevEnv determines whether the wallet is currently under a local developer
// environment, e.g. simnet or regtest.
// environment, e.g. regtest.
func (w *Wallet) isDevEnv() bool {
switch uint32(w.ChainParams().Net) {
case uint32(chaincfg.RegressionNetParams.Net):
case uint32(chaincfg.SimNetParams.Net):
default:
return false
}
@ -651,13 +637,12 @@ func locateBirthdayBlock(chainClient chainConn,
return birthdayBlock, nil
}
// recovery attempts to recover any unspent outputs that pay to any of our
// Recovery attempts to recover any unspent outputs that pay to any of our
// addresses starting from our birthday, or the wallet's tip (if higher), which
// would indicate resuming a recovery after a restart.
func (w *Wallet) recovery(chainClient chain.Interface,
birthdayBlock *waddrmgr.BlockStamp) error {
func (w *Wallet) Recovery(chainClient chain.Interface) error {
log.Infof("RECOVERY MODE ENABLED -- rescanning for used addresses "+
log.Infof("Recovery for used addresses "+
"with recovery_window=%d", w.recoveryWindow)
// We'll initialize the recovery manager with a default batch size of
@ -666,16 +651,13 @@ func (w *Wallet) recovery(chainClient chain.Interface,
w.recoveryWindow, recoveryBatchSize, w.chainParams,
)
// In the event that this recovery is being resumed, we will need to
// repopulate all found addresses from the database. Ideally, for basic
// recovery, we would only do so for the default scopes, but due to a
// bug in which the wallet would create change addresses outside of the
// default scopes, it's necessary to attempt all registered key scopes.
scopedMgrs := make(map[waddrmgr.KeyScope]*waddrmgr.ScopedKeyManager)
for _, scopedMgr := range w.Manager.ActiveScopedKeyManagers() {
scopedMgrs[scopedMgr.Scope()] = scopedMgr
}
err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
var credits []wtxmgr.Credit
txMgrNS := tx.ReadBucket(wtxmgrNamespaceKey)
credits, err := w.TxStore.UnspentOutputs(txMgrNS)
if err != nil {
@ -688,32 +670,36 @@ func (w *Wallet) recovery(chainClient chain.Interface,
return err
}
// Fetch the best height from the backend to determine when we should
// stop.
_, bestHeight, err := chainClient.GetBestBlock()
if err != nil {
return err
return nil
}
func (w *Wallet) RescanBlockchain(chainClient chain.Interface,
startHeight int32, stopHeight int32) (int32, int32, error) {
log.Infof("Rescanning blockchain from block %d to %d "+
"with recovery_window=%d", startHeight, stopHeight,
w.recoveryWindow)
defer log.Infof("Rescan blockchain done")
recoveryMgr := NewRecoveryManager(
w.recoveryWindow, recoveryBatchSize, w.chainParams,
)
scopedMgrs := make(map[waddrmgr.KeyScope]*waddrmgr.ScopedKeyManager)
for _, scopedMgr := range w.Manager.ActiveScopedKeyManagers() {
scopedMgrs[scopedMgr.Scope()] = scopedMgr
}
// Now we can begin scanning the chain from the wallet's current tip to
// ensure we properly handle restarts. Since the recovery process itself
// acts as rescan, we'll also update our wallet's synced state along the
// way to reflect the blocks we process and prevent rescanning them
// later on.
//
// NOTE: We purposefully don't update our best height since we assume
// that a wallet rescan will be performed from the wallet's tip, which
// will be of bestHeight after completing the recovery process.
var blocks []*waddrmgr.BlockStamp
startHeight := w.Manager.SyncedTo().Height + 1
for height := startHeight; height <= bestHeight; height++ {
for height := startHeight; height <= stopHeight; height++ {
hash, err := chainClient.GetBlockHash(int64(height))
if err != nil {
return err
return startHeight, stopHeight, err
}
header, err := chainClient.GetBlockHeader(hash)
if err != nil {
return err
return startHeight, stopHeight, err
}
blocks = append(blocks, &waddrmgr.BlockStamp{
Hash: *hash,
@ -724,7 +710,7 @@ func (w *Wallet) recovery(chainClient chain.Interface,
// It's possible for us to run into blocks before our birthday
// if our birthday is after our reorg safe height, so we'll make
// sure to not add those to the batch.
if height >= birthdayBlock.Height {
if height >= startHeight {
recoveryMgr.AddToBlockBatch(
hash, height, header.Timestamp,
)
@ -735,38 +721,39 @@ func (w *Wallet) recovery(chainClient chain.Interface,
// the recovery batch size, so we can proceed to commit our
// state to disk.
recoveryBatch := recoveryMgr.BlockBatch()
if len(recoveryBatch) == recoveryBatchSize || height == bestHeight {
err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
for _, block := range blocks {
err := w.Manager.SetSyncedTo(ns, block)
if err != nil {
return err
}
}
return w.recoverScopedAddresses(
chainClient, tx, ns, recoveryBatch,
recoveryMgr.State(), scopedMgrs,
)
})
if err != nil {
return err
}
if len(recoveryBatch) > 0 {
log.Infof("Recovered addresses from blocks "+
"%d-%d", recoveryBatch[0].Height,
recoveryBatch[len(recoveryBatch)-1].Height)
}
// Clear the batch of all processed blocks to reuse the
// same memory for future batches.
blocks = blocks[:0]
recoveryMgr.ResetBlockBatch()
if len(recoveryBatch) != recoveryBatchSize && height != stopHeight {
continue
}
}
return nil
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
for scope, scopedMgr := range scopedMgrs {
scopeState := recoveryMgr.State().StateForScope(scope)
err = expandScopeHorizons(ns, scopedMgr, scopeState)
if err != nil {
return err
}
}
return w.recoverScopedAddresses(chainClient, tx, ns,
recoveryBatch, recoveryMgr.State(), scopedMgrs,
)
})
if err != nil {
return startHeight, stopHeight, err
}
if len(recoveryBatch) > 0 {
log.Infof("Recovered addresses from blocks "+
"%d-%d", recoveryBatch[0].Height,
recoveryBatch[len(recoveryBatch)-1].Height)
}
// Clear the batch of all processed blocks to reuse the
// same memory for future batches.
blocks = blocks[:0]
recoveryMgr.ResetBlockBatch()
}
return startHeight, stopHeight, nil
}
// recoverScopedAddresses scans a range of blocks in attempts to recover any
@ -795,16 +782,8 @@ func (w *Wallet) recoverScopedAddresses(
return nil
}
log.Infof("Scanning %d blocks for recoverable addresses", len(batch))
expandHorizons:
for scope, scopedMgr := range scopedMgrs {
scopeState := recoveryState.StateForScope(scope)
err := expandScopeHorizons(ns, scopedMgr, scopeState)
if err != nil {
return err
}
}
log.Infof("Scanning %d blocks for recoverable addresses", len(batch))
// With the internal and external horizons properly expanded, we now
// construct the filter blocks request. The request includes the range
@ -887,89 +866,60 @@ expandHorizons:
// persistent state of the wallet. If any invalid child keys are detected, the
// horizon will be properly extended such that our lookahead always includes the
// proper number of valid child keys.
func expandScopeHorizons(ns walletdb.ReadWriteBucket,
func expandScopeHorizons(
ns walletdb.ReadWriteBucket,
scopedMgr *waddrmgr.ScopedKeyManager,
scopeState *ScopeRecoveryState) error {
scopeState ScopeRecoveryState) error {
// Compute the current external horizon and the number of addresses we
// must derive to ensure we maintain a sufficient recovery window for
// the external branch.
exHorizon, exWindow := scopeState.ExternalBranch.ExtendHorizon()
count, childIndex := uint32(0), exHorizon
for count < exWindow {
keyPath := externalKeyPath(childIndex)
addr, err := scopedMgr.DeriveFromKeyPath(ns, keyPath)
switch {
case err == hdkeychain.ErrInvalidChild:
// Record the existence of an invalid child with the
// external branch's recovery state. This also
// increments the branch's horizon so that it accounts
// for this skipped child index.
scopeState.ExternalBranch.MarkInvalidChild(childIndex)
childIndex++
continue
case err != nil:
for accountIndex, accountState := range scopeState {
acctKey, err := scopedMgr.DeriveAccountKey(ns, uint32(accountIndex))
if err != nil {
return err
}
for branchIndex, branchState := range accountState {
exHorizon, exWindow := branchState.ExtendHorizon()
count, addrIndex := uint32(0), exHorizon
// Register the newly generated external address and child index
// with the external branch recovery state.
scopeState.ExternalBranch.AddAddr(childIndex, addr.Address())
branchKey, err := acctKey.Derive(uint32(branchIndex))
if err != nil {
return err
}
childIndex++
count++
}
for count < exWindow {
kp := keyPath(uint32(accountIndex), uint32(branchIndex), addrIndex)
indexKey, err := branchKey.Derive(addrIndex)
if err != nil {
return err
}
addrType := waddrmgr.ScopeAddrMap[scopedMgr.Scope()].ExternalAddrType
addr, err := scopedMgr.DeriveFromExtKeys(kp, indexKey, addrType)
switch {
case err == hdkeychain.ErrInvalidChild:
branchState.MarkInvalidChild(addrIndex)
addrIndex++
continue
// Compute the current internal horizon and the number of addresses we
// must derive to ensure we maintain a sufficient recovery window for
// the internal branch.
inHorizon, inWindow := scopeState.InternalBranch.ExtendHorizon()
count, childIndex = 0, inHorizon
for count < inWindow {
keyPath := internalKeyPath(childIndex)
addr, err := scopedMgr.DeriveFromKeyPath(ns, keyPath)
switch {
case err == hdkeychain.ErrInvalidChild:
// Record the existence of an invalid child with the
// internal branch's recovery state. This also
// increments the branch's horizon so that it accounts
// for this skipped child index.
scopeState.InternalBranch.MarkInvalidChild(childIndex)
childIndex++
continue
case err != nil:
return err
}
case err != nil:
return err
branchState.AddAddr(addrIndex, addr.Address())
addrIndex++
count++
}
}
// Register the newly generated internal address and child index
// with the internal branch recovery state.
scopeState.InternalBranch.AddAddr(childIndex, addr.Address())
childIndex++
count++
}
return nil
}
// externalKeyPath returns the relative external derivation path /0/0/index.
func externalKeyPath(index uint32) waddrmgr.DerivationPath {
// keyPath returns the relative derivation path /account/branch/index.
func keyPath(account, branch, index uint32) waddrmgr.DerivationPath {
return waddrmgr.DerivationPath{
InternalAccount: waddrmgr.DefaultAccountNum,
Account: waddrmgr.DefaultAccountNum,
Branch: waddrmgr.ExternalBranch,
Index: index,
}
}
// internalKeyPath returns the relative internal derivation path /0/1/index.
func internalKeyPath(index uint32) waddrmgr.DerivationPath {
return waddrmgr.DerivationPath{
InternalAccount: waddrmgr.DefaultAccountNum,
Account: waddrmgr.DefaultAccountNum,
Branch: waddrmgr.InternalBranch,
InternalAccount: account,
Account: account,
Branch: branch,
Index: index,
}
}
@ -982,28 +932,26 @@ func newFilterBlocksRequest(batch []wtxmgr.BlockMeta,
filterReq := &chain.FilterBlocksRequest{
Blocks: batch,
ExternalAddrs: make(map[waddrmgr.ScopedIndex]btcutil.Address),
InternalAddrs: make(map[waddrmgr.ScopedIndex]btcutil.Address),
Addresses: make(map[waddrmgr.ScopedIndex]btcutil.Address),
WatchedOutPoints: recoveryState.WatchedOutPoints(),
}
// Populate the external and internal addresses by merging the addresses
// sets belong to all currently tracked scopes.
// Populate the addresses by merging the addresses sets belong to all
// currently tracked scopes.
for scope := range scopedMgrs {
scopeState := recoveryState.StateForScope(scope)
for index, addr := range scopeState.ExternalBranch.Addrs() {
scopedIndex := waddrmgr.ScopedIndex{
Scope: scope,
Index: index,
for accountIndex, accountState := range scopeState {
for branchIndex, branchState := range accountState {
for addrIndex, addr := range branchState.Addrs() {
scopedIndex := waddrmgr.ScopedIndex{
Scope: scope,
Account: uint32(accountIndex),
Branch: uint32(branchIndex),
Index: addrIndex,
}
filterReq.Addresses[scopedIndex] = addr
}
}
filterReq.ExternalAddrs[scopedIndex] = addr
}
for index, addr := range scopeState.InternalBranch.Addrs() {
scopedIndex := waddrmgr.ScopedIndex{
Scope: scope,
Index: index,
}
filterReq.InternalAddrs[scopedIndex] = addr
}
}
@ -1018,85 +966,63 @@ func extendFoundAddresses(ns walletdb.ReadWriteBucket,
scopedMgrs map[waddrmgr.KeyScope]*waddrmgr.ScopedKeyManager,
recoveryState *RecoveryState) error {
// Mark all recovered external addresses as used. This will be done only
// for scopes that reported a non-zero number of external addresses in
// this block.
for scope, indexes := range filterResp.FoundExternalAddrs {
// First, report all external child indexes found for this
// scope. This ensures that the external last-found index will
// be updated to include the maximum child index seen thus far.
scopeState := recoveryState.StateForScope(scope)
for index := range indexes {
scopeState.ExternalBranch.ReportFound(index)
}
scopedMgr := scopedMgrs[scope]
// Mark all recovered addresses as used. This will be done only for
// scopes that reported a non-zero number of addresses in this block.
for index := range filterResp.FoundAddresses {
scopedMgr := scopedMgrs[index.Scope]
// First, report all child indexes found for this scope. This
// ensures that the last-found index will be updated to include
// the maximum child index seen thus far.
scopeState := recoveryState.StateForScope(index.Scope)
branchState := scopeState[index.Account][index.Branch]
branchState.ReportFound(index.Index)
// Now, with all found addresses reported, derive and extend all
// external addresses up to and including the current last found
// index for this scope.
exNextUnfound := scopeState.ExternalBranch.NextUnfound()
nextFound := branchState.NextUnfound()
exLastFound := exNextUnfound
if exLastFound > 0 {
exLastFound--
lastFound := nextFound
if lastFound > 0 {
lastFound--
}
err := scopedMgr.ExtendExternalAddresses(
ns, waddrmgr.DefaultAccountNum, exLastFound,
err := scopedMgr.ExtendAddresses(
ns, index.Account, index.Branch, lastFound,
)
if err != nil {
return err
}
// Finally, with the scope's addresses extended, we mark used
// the external addresses that were found in the block and
// belong to this scope.
for index := range indexes {
addr := scopeState.ExternalBranch.GetAddr(index)
err := scopedMgr.MarkUsed(ns, addr)
if err != nil {
return err
}
// the addresses that were found in the block and belong to
// this scope.
addr := branchState.GetAddr(index.Index)
err = scopedMgr.MarkUsed(ns, addr)
if err != nil {
return err
}
}
// Mark all recovered internal addresses as used. This will be done only
// for scopes that reported a non-zero number of internal addresses in
// this block.
for scope, indexes := range filterResp.FoundInternalAddrs {
// First, report all internal child indexes found for this
// scope. This ensures that the internal last-found index will
// be updated to include the maximum child index seen thus far.
scopeState := recoveryState.StateForScope(scope)
for index := range indexes {
scopeState.InternalBranch.ReportFound(index)
}
scopedMgr := scopedMgrs[scope]
// Now, with all found addresses reported, derive and extend all
// internal addresses up to and including the current last found
// index for this scope.
inNextUnfound := scopeState.InternalBranch.NextUnfound()
inLastFound := inNextUnfound
if inLastFound > 0 {
inLastFound--
}
err := scopedMgr.ExtendInternalAddresses(
ns, waddrmgr.DefaultAccountNum, inLastFound,
)
var lastAccount uint32
for _, scopedMgr := range scopedMgrs {
account, err := scopedMgr.LastAccount(ns)
if err != nil {
return err
}
if lastAccount < account {
lastAccount = account
}
}
// Finally, with the scope's addresses extended, we mark used
// the internal addresses that were found in the blockand belong
// to this scope.
for index := range indexes {
addr := scopeState.InternalBranch.GetAddr(index)
err := scopedMgr.MarkUsed(ns, addr)
// Make sure all scopes are extended to the same account.
for _, s := range scopedMgrs {
for gapAccount := lastAccount; gapAccount >= 0; gapAccount-- {
_, err := s.AccountProperties(ns, gapAccount)
// If the account exists, we can stop extending.
if err == nil {
break
}
err = s.NewRawAccount(ns, gapAccount)
if err != nil {
return err
}
@ -1112,23 +1038,12 @@ func logFilterBlocksResp(block wtxmgr.BlockMeta,
resp *chain.FilterBlocksResponse) {
// Log the number of external addresses found in this block.
var nFoundExternal int
for _, indexes := range resp.FoundExternalAddrs {
nFoundExternal += len(indexes)
}
if nFoundExternal > 0 {
log.Infof("Recovered %d external addrs at height=%d hash=%v",
nFoundExternal, block.Height, block.Hash)
}
var nFoundAddresses int
nFoundAddresses += len(resp.FoundAddresses)
// Log the number of internal addresses found in this block.
var nFoundInternal int
for _, indexes := range resp.FoundInternalAddrs {
nFoundInternal += len(indexes)
}
if nFoundInternal > 0 {
log.Infof("Recovered %d internal addrs at height=%d hash=%v",
nFoundInternal, block.Height, block.Hash)
if nFoundAddresses > 0 {
log.Infof("Recovered %d addrs at height=%d hash=%v",
nFoundAddresses, block.Height, block.Hash)
}
// Log the number of outpoints found in this block.
@ -1242,16 +1157,9 @@ type (
changePassphraseRequest struct {
old, new []byte
private bool
err chan error
}
changePassphrasesRequest struct {
publicOld, publicNew []byte
privateOld, privateNew []byte
err chan error
}
// heldUnlock is a tool to prevent the wallet from automatically
// locking after some timeout before an operation which needed
// the unlocked wallet has finished. Any acquired heldUnlock
@ -1290,32 +1198,13 @@ out:
err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
return w.Manager.ChangePassphrase(
addrmgrNs, req.old, req.new, req.private,
addrmgrNs, req.old, req.new,
&waddrmgr.DefaultScryptOptions,
)
})
req.err <- err
continue
case req := <-w.changePassphrases:
err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
err := w.Manager.ChangePassphrase(
addrmgrNs, req.publicOld, req.publicNew,
false, &waddrmgr.DefaultScryptOptions,
)
if err != nil {
return err
}
return w.Manager.ChangePassphrase(
addrmgrNs, req.privateOld, req.privateNew,
true, &waddrmgr.DefaultScryptOptions,
)
})
req.err <- err
continue
case req := <-w.holdUnlockRequests:
if w.Manager.IsLocked() {
close(req)
@ -1416,58 +1305,48 @@ func (c heldUnlock) release() {
c <- struct{}{}
}
// ChangePrivatePassphrase attempts to change the passphrase for a wallet from
// ChangePassphrase attempts to change the passphrase for a wallet from
// old to new. Changing the passphrase is synchronized with all other address
// manager locking and unlocking. The lock state will be the same as it was
// before the password change.
func (w *Wallet) ChangePrivatePassphrase(old, new []byte) error {
func (w *Wallet) ChangePassphrase(old, new []byte) error {
err := make(chan error, 1)
w.changePassphrase <- changePassphraseRequest{
old: old,
new: new,
private: true,
err: err,
}
return <-err
}
// ChangePublicPassphrase modifies the public passphrase of the wallet.
func (w *Wallet) ChangePublicPassphrase(old, new []byte) error {
err := make(chan error, 1)
w.changePassphrase <- changePassphraseRequest{
old: old,
new: new,
private: false,
err: err,
}
return <-err
}
// ChangePassphrases modifies the public and private passphrase of the wallet
// atomically.
func (w *Wallet) ChangePassphrases(publicOld, publicNew, privateOld,
privateNew []byte) error {
err := make(chan error, 1)
w.changePassphrases <- changePassphrasesRequest{
publicOld: publicOld,
publicNew: publicNew,
privateOld: privateOld,
privateNew: privateNew,
err: err,
old: old,
new: new,
err: err,
}
return <-err
}
// AccountAddresses returns the addresses for every created address for an
// account.
func (w *Wallet) AccountAddresses(account uint32) (addrs []btcutil.Address, err error) {
func (w *Wallet) AccountAddresses(account uint32, scope *waddrmgr.KeyScope) (
addrs []btcutil.Address, err error) {
// By default, append all addresses under this account.
fn := func(maddr waddrmgr.ManagedAddress) error {
addrs = append(addrs, maddr.Address())
return nil
}
// If scope is set, append only those have the address type under
// this scope.
if scope != nil {
addrSchema := waddrmgr.ScopeAddrMap[*scope]
fn = func(maddr waddrmgr.ManagedAddress) error {
if maddr.AddrType() == addrSchema.InternalAddrType ||
maddr.AddrType() == addrSchema.ExternalAddrType {
addrs = append(addrs, maddr.Address())
}
return nil
}
}
err = walletdb.View(w.db, func(tx walletdb.ReadTx) error {
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
return w.Manager.ForEachAccountAddress(addrmgrNs, account, func(maddr waddrmgr.ManagedAddress) error {
addrs = append(addrs, maddr.Address())
return nil
})
return w.Manager.ForEachAccountAddress(addrmgrNs, account, fn)
})
return
}
@ -1568,7 +1447,7 @@ func (w *Wallet) CurrentAddress(account uint32, scope waddrmgr.KeyScope) (btcuti
)
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
maddr, err := manager.LastExternalAddress(addrmgrNs, account)
maddr, err := manager.LastAddress(addrmgrNs, account, waddrmgr.ExternalBranch)
if err != nil {
// If no address exists yet, create the first external
// address.
@ -1743,7 +1622,12 @@ func (w *Wallet) AddressInfo(a btcutil.Address) (waddrmgr.ManagedAddress, error)
// AccountNumber returns the account number for an account name under a
// particular key scope.
func (w *Wallet) AccountNumber(scope waddrmgr.KeyScope, accountName string) (uint32, error) {
func (w *Wallet) AccountNumber(accountName string) (uint32, error) {
// By design, the same account number is shared across all scopes.
return w.accountNumber(waddrmgr.DefaultKeyScope, accountName)
}
func (w *Wallet) accountNumber(scope waddrmgr.KeyScope, accountName string) (uint32, error) {
manager, err := w.Manager.FetchScopedKeyManager(scope)
if err != nil {
return 0, err
@ -1752,7 +1636,6 @@ func (w *Wallet) AccountNumber(scope waddrmgr.KeyScope, accountName string) (uin
var account uint32
err = walletdb.View(w.db, func(tx walletdb.ReadTx) error {
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
var err error
account, err = manager.LookupAccount(addrmgrNs, accountName)
return err
})
@ -1760,7 +1643,12 @@ func (w *Wallet) AccountNumber(scope waddrmgr.KeyScope, accountName string) (uin
}
// AccountName returns the name of an account.
func (w *Wallet) AccountName(scope waddrmgr.KeyScope, accountNumber uint32) (string, error) {
func (w *Wallet) AccountName(accountNumber uint32) (string, error) {
// By design, the same account name is shared across all scopes.
return w.accountName(waddrmgr.DefaultKeyScope, accountNumber)
}
func (w *Wallet) accountName(scope waddrmgr.KeyScope, accountNumber uint32) (string, error) {
manager, err := w.Manager.FetchScopedKeyManager(scope)
if err != nil {
return "", err
@ -1769,7 +1657,6 @@ func (w *Wallet) AccountName(scope waddrmgr.KeyScope, accountNumber uint32) (str
var accountName string
err = walletdb.View(w.db, func(tx walletdb.ReadTx) error {
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
var err error
accountName, err = manager.AccountName(addrmgrNs, accountNumber)
return err
})
@ -1943,7 +1830,8 @@ func RecvCategory(details *wtxmgr.TxDetails, syncHeight int32, net *chaincfg.Par
// for a listtransactions RPC.
//
// TODO: This should be moved to the legacyrpc package.
func listTransactions(tx walletdb.ReadTx, details *wtxmgr.TxDetails, addrMgr *waddrmgr.Manager,
func listTransactions(accountName string, tx walletdb.ReadTx,
details *wtxmgr.TxDetails, addrMgr *waddrmgr.Manager,
syncHeight int32, net *chaincfg.Params) []btcjson.ListTransactionsResult {
addrmgrNs := tx.ReadBucket(waddrmgrNamespaceKey)
@ -2004,19 +1892,22 @@ outputs:
}
var address string
var accountName string
var name string
_, addrs, _, _ := txscript.ExtractPkScriptAddrs(output.PkScript, net)
if len(addrs) == 1 {
addr := addrs[0]
address = addr.EncodeAddress()
mgr, account, err := addrMgr.AddrAccount(addrmgrNs, addrs[0])
if err == nil {
accountName, err = mgr.AccountName(addrmgrNs, account)
name, err = mgr.AccountName(addrmgrNs, account)
if err != nil {
accountName = ""
name = ""
}
}
}
if accountName != "*" && accountName != name {
continue
}
amountF64 := btcutil.Amount(output.Value).ToBTC()
result := btcjson.ListTransactionsResult{
@ -2025,7 +1916,7 @@ outputs:
// BlockIndex
//
// Fields set below:
// Account (only for non-"send" categories)
// Account
// Category
// Amount
// Fee
@ -2052,13 +1943,14 @@ outputs:
// with debits are grouped under the send category.
if send || spentCredit {
result.Account = name
result.Category = "send"
result.Amount = -amountF64
result.Fee = &feeF64
results = append(results, result)
}
if isCredit {
result.Account = accountName
result.Account = name
result.Category = recvCat
result.Amount = amountF64
result.Fee = nil
@ -2071,7 +1963,7 @@ outputs:
// ListSinceBlock returns a slice of objects with details about transactions
// since the given block. If the block is -1 then all transactions are included.
// This is intended to be used for listsinceblock RPC replies.
func (w *Wallet) ListSinceBlock(start, end, syncHeight int32) ([]btcjson.ListTransactionsResult, error) {
func (w *Wallet) ListSinceBlock(accountName string, start, end, syncHeight int32) ([]btcjson.ListTransactionsResult, error) {
txList := []btcjson.ListTransactionsResult{}
err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
@ -2081,8 +1973,8 @@ func (w *Wallet) ListSinceBlock(start, end, syncHeight int32) ([]btcjson.ListTra
detail := detail
jsonResults := listTransactions(
tx, &detail, w.Manager, syncHeight,
w.chainParams,
accountName, tx, &detail, w.Manager,
syncHeight, w.chainParams,
)
txList = append(txList, jsonResults...)
}
@ -2097,7 +1989,7 @@ func (w *Wallet) ListSinceBlock(start, end, syncHeight int32) ([]btcjson.ListTra
// ListTransactions returns a slice of objects with details about a recorded
// transaction. This is intended to be used for listtransactions RPC
// replies.
func (w *Wallet) ListTransactions(from, count int) ([]btcjson.ListTransactionsResult, error) {
func (w *Wallet) ListTransactions(accountName string, from, count int) ([]btcjson.ListTransactionsResult, error) {
txList := []btcjson.ListTransactionsResult{}
err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
@ -2110,7 +2002,6 @@ func (w *Wallet) ListTransactions(from, count int) ([]btcjson.ListTransactionsRe
// Need to skip the first from transactions, and after those, only
// include the next count transactions.
skipped := 0
n := 0
rangeFn := func(details []wtxmgr.TxDetails) (bool, error) {
for _, detail := range details {
@ -2119,18 +2010,16 @@ func (w *Wallet) ListTransactions(from, count int) ([]btcjson.ListTransactionsRe
continue
}
n++
if n > count {
if len(txList) >= count {
txList = txList[:count]
return true, nil
}
jsonResults := listTransactions(tx, &detail,
w.Manager, syncBlock.Height, w.chainParams)
txList = append(txList, jsonResults...)
jsonResults := listTransactions(accountName,
tx, &detail, w.Manager,
syncBlock.Height, w.chainParams)
if len(jsonResults) > 0 {
n++
}
txList = append(txList, jsonResults...)
}
return false, nil
@ -2151,7 +2040,7 @@ func (w *Wallet) ListTransactions(from, count int) ([]btcjson.ListTransactionsRe
// ListAddressTransactions returns a slice of objects with details about
// recorded transactions to or from any address belonging to a set. This is
// intended to be used for listaddresstransactions RPC replies.
func (w *Wallet) ListAddressTransactions(pkHashes map[string]struct{}) ([]btcjson.ListTransactionsResult, error) {
func (w *Wallet) ListAddressTransactions(accountName string, pkHashes map[string]struct{}) ([]btcjson.ListTransactionsResult, error) {
txList := []btcjson.ListTransactionsResult{}
err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
@ -2180,8 +2069,9 @@ func (w *Wallet) ListAddressTransactions(pkHashes map[string]struct{}) ([]btcjso
continue
}
jsonResults := listTransactions(tx, detail,
w.Manager, syncBlock.Height, w.chainParams)
jsonResults := listTransactions(accountName,
tx, detail, w.Manager,
syncBlock.Height, w.chainParams)
txList = append(txList, jsonResults...)
continue loopDetails
}
@ -2197,7 +2087,7 @@ func (w *Wallet) ListAddressTransactions(pkHashes map[string]struct{}) ([]btcjso
// ListAllTransactions returns a slice of objects with details about a recorded
// transaction. This is intended to be used for listalltransactions RPC
// replies.
func (w *Wallet) ListAllTransactions() ([]btcjson.ListTransactionsResult, error) {
func (w *Wallet) ListAllTransactions(accountName string) ([]btcjson.ListTransactionsResult, error) {
txList := []btcjson.ListTransactionsResult{}
err := walletdb.View(w.db, func(tx walletdb.ReadTx) error {
txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
@ -2212,7 +2102,8 @@ func (w *Wallet) ListAllTransactions() ([]btcjson.ListTransactionsResult, error)
// unsorted, but it will process mined transactions in the
// reverse order they were marked mined.
for i := len(details) - 1; i >= 0; i-- {
jsonResults := listTransactions(tx, &details[i], w.Manager,
jsonResults := listTransactions(accountName,
tx, &details[i], w.Manager,
syncBlock.Height, w.chainParams)
txList = append(txList, jsonResults...)
}
@ -2954,7 +2845,7 @@ func (w *Wallet) newAddress(addrmgrNs walletdb.ReadWriteBucket, account uint32,
}
// Get next address from wallet.
addrs, err := manager.NextExternalAddresses(addrmgrNs, account, 1)
addrs, err := manager.NextAddresses(addrmgrNs, account, waddrmgr.ExternalBranch, 1)
if err != nil {
return nil, nil, err
}
@ -3012,7 +2903,7 @@ func (w *Wallet) newChangeAddress(addrmgrNs walletdb.ReadWriteBucket,
}
// Get next chained change address from wallet for account.
addrs, err := manager.NextInternalAddresses(addrmgrNs, account, 1)
addrs, err := manager.NextAddresses(addrmgrNs, account, waddrmgr.InternalBranch, 1)
if err != nil {
return nil, err
}
@ -3682,29 +3573,23 @@ func (w *Wallet) Database() walletdb.DB {
// CreateWithCallback is the same as Create with an added callback that will be
// called in the same transaction the wallet structure is initialized.
func CreateWithCallback(db walletdb.DB, pubPass, privPass []byte,
func CreateWithCallback(db walletdb.DB, privPass []byte,
rootKey *hdkeychain.ExtendedKey, params *chaincfg.Params,
birthday time.Time, cb func(walletdb.ReadWriteTx) error) error {
return create(
db, pubPass, privPass, rootKey, params, birthday, cb,
)
return create(db, privPass, rootKey, params, birthday, cb)
}
// Create creates an new wallet, writing it to an empty database. If the passed
// root key is non-nil, it is used. Otherwise, a secure random seed of the
// recommended length is generated.
func Create(db walletdb.DB, pubPass, privPass []byte,
rootKey *hdkeychain.ExtendedKey, params *chaincfg.Params,
birthday time.Time) error {
func Create(db walletdb.DB, privPass []byte, rootKey *hdkeychain.ExtendedKey,
params *chaincfg.Params, birthday time.Time) error {
return create(
db, pubPass, privPass, rootKey, params, birthday, nil,
)
return create(db, privPass, rootKey, params, birthday, nil)
}
func create(db walletdb.DB, pubPass, privPass []byte,
rootKey *hdkeychain.ExtendedKey, params *chaincfg.Params,
birthday time.Time,
func create(db walletdb.DB, privPass []byte, rootKey *hdkeychain.ExtendedKey,
params *chaincfg.Params, birthday time.Time,
cb func(walletdb.ReadWriteTx) error) error {
// If no root key was provided, we create one now from a random seed.
@ -3741,7 +3626,7 @@ func create(db walletdb.DB, pubPass, privPass []byte,
}
err = waddrmgr.Create(
addrmgrNs, rootKey, pubPass, privPass, params, nil,
addrmgrNs, rootKey, privPass, params, nil,
birthday,
)
if err != nil {
@ -3762,8 +3647,8 @@ func create(db walletdb.DB, pubPass, privPass []byte,
}
// Open loads an already-created wallet from the passed database and namespaces.
func Open(db walletdb.DB, pubPass []byte, cbs *waddrmgr.OpenCallbacks,
params *chaincfg.Params, recoveryWindow uint32) (*Wallet, error) {
func Open(db walletdb.DB, params *chaincfg.Params, recoveryWindow uint32) (
*Wallet, error) {
var (
addrMgr *waddrmgr.Manager
@ -3791,7 +3676,7 @@ func Open(db walletdb.DB, pubPass []byte, cbs *waddrmgr.OpenCallbacks,
return err
}
addrMgr, err = waddrmgr.Open(addrMgrBucket, pubPass, params)
addrMgr, err = waddrmgr.Open(addrMgrBucket, params)
if err != nil {
return err
}
@ -3809,7 +3694,6 @@ func Open(db walletdb.DB, pubPass []byte, cbs *waddrmgr.OpenCallbacks,
log.Infof("Opened wallet") // TODO: log balance? last sync height?
w := &Wallet{
publicPassphrase: pubPass,
db: db,
Manager: addrMgr,
TxStore: txMgr,
@ -3826,7 +3710,6 @@ func Open(db walletdb.DB, pubPass []byte, cbs *waddrmgr.OpenCallbacks,
holdUnlockRequests: make(chan chan heldUnlock),
lockState: make(chan bool),
changePassphrase: make(chan changePassphraseRequest),
changePassphrases: make(chan changePassphrasesRequest),
chainParams: params,
quit: make(chan struct{}),
}

View file

@ -1,7 +0,0 @@
#!/bin/sh
# This script uses go tool cover to generate a test coverage report.
go test -coverprofile=cov.out && go tool cover -func=cov.out && rm -f cov.out
echo "============================================================"
(cd bdb && go test -coverprofile=cov.out && go tool cover -func=cov.out && \
rm -f cov.out)

View file

@ -1,39 +0,0 @@
PASS
coverage: 100.0% of statements
ok github.com/lbryio/lbcwallet/walletdb 0.130s
github.com\lbryio\lbcwallet\walletdb\interface.go:190: RegisterDriver 100.0%
github.com\lbryio\lbcwallet\walletdb\interface.go:201: SupportedDrivers 100.0%
github.com\lbryio\lbcwallet\walletdb\interface.go:214: Create 100.0%
github.com\lbryio\lbcwallet\walletdb\interface.go:228: Open 100.0%
total: (statements) 100.0%
============================================================
PASS
coverage: 91.7% of statements
ok github.com/lbryio/lbcwallet/walletdb/bdb 0.149s
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:28: convertErr 76.9%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:74: Bucket 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:90: CreateBucket 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:103: CreateBucketIfNotExists 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:116: DeleteBucket 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:129: ForEach 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:136: Writable 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:145: Put 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:157: Get 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:166: Delete 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:185: RootBucket 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:193: Commit 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:201: Rollback 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:227: Begin 85.7%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:249: View 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:270: Update 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:294: Namespace 93.3%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:329: DeleteNamespace 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:339: WriteTo 0.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:348: Close 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:353: fileExists 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\db.go:364: openDB 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\driver.go:34: parseArgs 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\driver.go:50: openDBDriver 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\driver.go:60: createDBDriver 100.0%
github.com\lbryio\lbcwallet\walletdb\bdb\driver.go:69: init 66.7%
total: (statements) 91.7%

View file

@ -45,22 +45,10 @@ func createWallet(cfg *config) error {
activeNet.Params, dbDir, true, cfg.DBTimeout, 250,
)
// Start by prompting for the private passphrase.
// Start by prompting for the passphrase.
passphrase := []byte(cfg.Passphrase)
reader := bufio.NewReader(os.Stdin)
privPass, err := prompt.PrivatePass(reader)
if err != nil {
return err
}
// Ascertain the public passphrase. This will either be a value
// specified by the user or the default hard-coded public passphrase if
// the user does not want the additional public data encryption.
pubPass, err := prompt.PublicPass(reader, privPass,
[]byte(wallet.InsecurePubPassphrase), []byte(cfg.WalletPass))
if err != nil {
return err
}
// Ascertain the wallet generation seed. This will either be an
// automatically generated value the user has already confirmed or a
// value the user has entered which has already been validated.
@ -70,7 +58,7 @@ func createWallet(cfg *config) error {
}
fmt.Println("Creating the wallet...")
w, err := loader.CreateNewWallet(pubPass, privPass, seed, bday)
w, err := loader.CreateNewWallet(passphrase, seed, bday)
if err != nil {
return err
}
@ -88,9 +76,6 @@ func createSimulationWallet(cfg *config) error {
// Simulation wallet password is 'password'.
privPass := []byte("password")
// Public passphrase is the default.
pubPass := []byte(wallet.InsecurePubPassphrase)
netDir := networkDir(cfg.AppDataDir.Value, activeNet.Params)
// Create the wallet.
@ -105,7 +90,7 @@ func createSimulationWallet(cfg *config) error {
defer db.Close()
// Create the wallet.
err = wallet.Create(db, pubPass, privPass, nil, activeNet.Params, time.Now())
err = wallet.Create(db, privPass, nil, activeNet.Params, time.Now())
if err != nil {
return err
}