Compare commits
69 commits
Author | SHA1 | Date | |
---|---|---|---|
|
04763e36f0 | ||
|
ca56a420ee | ||
|
67c9c48940 | ||
|
c6842ef6fb | ||
|
1fa143fa0e | ||
|
3aca0be46a | ||
|
dc7f1e88eb | ||
|
be7892a2ae | ||
|
7a4cd602bb | ||
|
10d58126b5 | ||
|
08e57e9ee3 | ||
|
1917978ae8 | ||
|
79da868c74 | ||
|
9c20f19d23 | ||
|
e78e49d0f6 | ||
|
50950fb0d5 | ||
|
51e700e7d9 | ||
|
1edb90e0aa | ||
|
2e8dcc4312 | ||
|
4acd03be8b | ||
|
b34aa61e4d | ||
|
93b33edbcd | ||
|
e6a4b3d1fd | ||
|
c42e8a0705 | ||
|
03256c049b | ||
|
678379ce45 | ||
|
64554cfbc0 | ||
|
af2d35a604 | ||
|
43bbf0a089 | ||
|
c0b263ece7 | ||
|
531c461383 | ||
|
03dd33f8c9 | ||
|
09ec547ed6 | ||
|
608e4dddb5 | ||
|
71f8ba68a0 | ||
|
6a610a8cdf | ||
|
a02435bbf7 | ||
|
47e8dbda55 | ||
|
5a0c010688 | ||
|
00c29c3936 | ||
|
169abd446c | ||
|
0410b7ce01 | ||
|
de408d4133 | ||
|
2b0d245b1f | ||
|
efb8e0b699 | ||
|
e0cb7b7a91 | ||
|
f27ea6094e | ||
|
78b8743ced | ||
|
1f05a9858b | ||
|
79bcb8365c | ||
|
b7013bf5e1 | ||
|
aecb972d75 | ||
|
f4ba8a01d1 | ||
|
af82460075 | ||
|
40f7d7cefe | ||
|
d9d257a9f6 | ||
|
92acdcbfca | ||
|
893a820c65 | ||
|
46f4df5a64 | ||
|
610ec20ea8 | ||
|
0c2b21840d | ||
|
3e45c66f34 | ||
|
fd92031e27 | ||
|
eff01c9e63 | ||
|
2428b381da | ||
|
2e95c1427f | ||
|
13a42b37ce | ||
|
18b605c6e1 | ||
|
c21be77e05 |
82 changed files with 1972 additions and 15531 deletions
57
.github/workflows/golangci-lint.yml
vendored
57
.github/workflows/golangci-lint.yml
vendored
|
@ -1,57 +0,0 @@
|
|||
name: golangci-lint
|
||||
|
||||
env:
|
||||
# go needs absolute directories, using the $HOME variable doesn't work here.
|
||||
GOCACHE: /home/runner/work/go/pkg/build
|
||||
GOPATH: /home/runner/work/go
|
||||
GO_VERSION: '^1.18.2'
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
branches:
|
||||
- "*"
|
||||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: setup go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '${{ env.GO_VERSION }}'
|
||||
|
||||
- name: checkout source
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: compile code
|
||||
run: go install -v ./...
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
|
||||
version: latest
|
||||
|
||||
# Optional: working directory, useful for monorepos
|
||||
# working-directory: somedir
|
||||
|
||||
# Optional: golangci-lint command line arguments.
|
||||
# args: --issues-exit-code=0
|
||||
|
||||
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
||||
# only-new-issues: true
|
||||
|
||||
# Optional: if set to true then the action will use pre-installed Go.
|
||||
skip-go-installation: true
|
||||
|
||||
# Optional: if set to true then the action don't cache or restore ~/go/pkg.
|
||||
# skip-pkg-cache: true
|
||||
|
||||
# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
|
||||
# skip-build-cache: true
|
20
.github/workflows/main.yml
vendored
20
.github/workflows/main.yml
vendored
|
@ -26,32 +26,32 @@ env:
|
|||
GOCACHE: /home/runner/work/go/pkg/build
|
||||
GOPATH: /home/runner/work/go
|
||||
|
||||
GO_VERSION: '^1.18.2'
|
||||
GO_VERSION: '^1.19'
|
||||
|
||||
jobs:
|
||||
########################
|
||||
# Format, compileation and lint check
|
||||
########################
|
||||
golangci:
|
||||
name: lint
|
||||
lint-check:
|
||||
name: Format, compilation and lint check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: git checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: setup go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '${{ env.GO_VERSION }}'
|
||||
|
||||
- name: checkout source
|
||||
uses: actions/checkout@v2
|
||||
- name: run format
|
||||
run: make fmt
|
||||
|
||||
- name: compile code
|
||||
run: go install -v ./...
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: latest
|
||||
skip-go-installation: true
|
||||
- name: run lint
|
||||
run: make lint
|
||||
|
||||
########################
|
||||
# run unit tests
|
||||
|
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
@ -28,7 +28,7 @@ jobs:
|
|||
name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18.2
|
||||
go-version: 1.19
|
||||
|
||||
# Login against a Docker registry except on PR
|
||||
# https://github.com/docker/login-action
|
||||
|
|
|
@ -103,7 +103,7 @@ linters:
|
|||
# - noctx
|
||||
# - nolintlint
|
||||
# - prealloc
|
||||
- rowserrcheck
|
||||
# - rowserrcheck # Disabled due to generic, see https://github.com/golangci/golangci-lint/issues/2649
|
||||
# - revive
|
||||
# - scopelint
|
||||
# - staticcheck
|
||||
|
|
527
CHANGES
527
CHANGES
|
@ -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
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
ARG ARCH=amd64
|
||||
|
||||
FROM golang:1.18.2 AS build-container
|
||||
FROM golang:1.19 AS build-container
|
||||
|
||||
ARG ARCH
|
||||
|
||||
|
|
4
Makefile
4
Makefile
|
@ -9,8 +9,8 @@ GO_BIN := ${GOPATH}/bin
|
|||
LINT_BIN := ${GO_BIN}/golangci-lint
|
||||
GOACC_BIN := $(GO_BIN)/go-acc
|
||||
|
||||
LINT_COMMIT := v1.42.1
|
||||
GOACC_COMMIT := v0.2.6
|
||||
LINT_COMMIT := v1.48
|
||||
GOACC_COMMIT := v0.2.8
|
||||
|
||||
DEPGET := go install
|
||||
GOBUILD := go build -v
|
||||
|
|
180
README.md
180
README.md
|
@ -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
|
||||
|
|
|
@ -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{}{}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -25,7 +25,9 @@ func BackEnds() []string {
|
|||
}
|
||||
|
||||
// Interface allows more than one backing blockchain source, such as a
|
||||
// RPC chain server, or an SPV library, as long as we write a driver for
|
||||
//
|
||||
// RPC chain server, or an SPV library, as long as we write a driver for
|
||||
//
|
||||
// it.
|
||||
type Interface interface {
|
||||
Start() error
|
||||
|
@ -73,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
|
||||
}
|
||||
|
||||
|
@ -86,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
|
||||
|
|
|
@ -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
|
||||
|
|
11
chain/rpc.go
11
chain/rpc.go
|
@ -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
|
||||
|
|
|
@ -66,7 +66,8 @@ func pipe(c1, c2 *conn) (*conn, *conn) {
|
|||
// returns the root of the tree.
|
||||
//
|
||||
// This function was copied from:
|
||||
// https://github.com/lbryio/lbcd/blob/36a96f6a0025b6aeaebe4106821c2d46ee4be8d4/blockchain/fullblocktests/generate.go#L303
|
||||
//
|
||||
// https://github.com/lbryio/lbcd/blob/36a96f6a0025b6aeaebe4106821c2d46ee4be8d4/blockchain/fullblocktests/generate.go#L303
|
||||
func calcMerkleRoot(txns []*wire.MsgTx) chainhash.Hash {
|
||||
if len(txns) == 0 {
|
||||
return chainhash.Hash{}
|
||||
|
@ -86,7 +87,8 @@ func calcMerkleRoot(txns []*wire.MsgTx) chainhash.Hash {
|
|||
// with the solution. False is returned if no solution exists.
|
||||
//
|
||||
// This function was copied from:
|
||||
// https://github.com/lbryio/lbcd/blob/36a96f6a0025b6aeaebe4106821c2d46ee4be8d4/blockchain/fullblocktests/generate.go#L324
|
||||
//
|
||||
// https://github.com/lbryio/lbcd/blob/36a96f6a0025b6aeaebe4106821c2d46ee4be8d4/blockchain/fullblocktests/generate.go#L324
|
||||
func solveBlock(header *wire.BlockHeader) bool {
|
||||
// sbResult is used by the solver goroutines to send results.
|
||||
type sbResult struct {
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
202
config.go
202
config.go
|
@ -5,7 +5,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
@ -17,11 +16,9 @@ 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"
|
||||
"github.com/lbryio/lbcwallet/internal/legacy/keystore"
|
||||
"github.com/lbryio/lbcwallet/netparams"
|
||||
"github.com/lbryio/lbcwallet/wallet"
|
||||
)
|
||||
|
@ -34,6 +31,7 @@ const (
|
|||
defaultLogFilename = "lbcwallet.log"
|
||||
defaultRPCMaxClients = 10
|
||||
defaultRPCMaxWebsockets = 25
|
||||
defaultPassphrase = "password"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -47,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 -- NOTE: This is only allowed if the RPC client is connecting to localhost"`
|
||||
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 -- NOTE: This is only allowed if the RPC server is bound to localhost"`
|
||||
LegacyRPCListeners []string `long:"rpclisten" description:"Listen for legacy RPC connections on this interface/port (default port: 9244, testnet: 19244, regtest: 29244, simnet: 29244)"`
|
||||
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)"`
|
||||
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"`
|
||||
|
@ -251,10 +221,10 @@ func parseAndSetDebugLevels(debugLevel string) error {
|
|||
// line options.
|
||||
//
|
||||
// The configuration proceeds as follows:
|
||||
// 1) Start with a default config with sane settings
|
||||
// 2) Pre-parse the command line to check for an alternative config file
|
||||
// 3) Load configuration file overwriting defaults with any specified options
|
||||
// 4) Parse CLI options and overwrite/add any specified options
|
||||
// 1. Start with a default config with sane settings
|
||||
// 2. Pre-parse the command line to check for an alternative config file
|
||||
// 3. Load configuration file overwriting defaults with any specified options
|
||||
// 4. Parse CLI options and overwrite/add any specified options
|
||||
//
|
||||
// The above results in lbcwallet functioning properly without any config
|
||||
// settings while still allowing the user to override settings with config files
|
||||
|
@ -266,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
|
||||
|
@ -369,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)
|
||||
|
@ -455,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)
|
||||
}
|
||||
|
@ -527,22 +450,6 @@ func loadConfig() (*config, []string, error) {
|
|||
|
||||
// Created successfully, so exit now with success.
|
||||
os.Exit(0)
|
||||
} else if !dbFileExists && !cfg.NoInitialLoad {
|
||||
keystorePath := filepath.Join(netDir, keystore.Filename)
|
||||
keystoreExists, err := cfgutil.FileExists(keystorePath)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return nil, nil, err
|
||||
}
|
||||
if !keystoreExists {
|
||||
err = fmt.Errorf("the wallet does not exist, run with " +
|
||||
"the --create option to initialize and create it")
|
||||
} else {
|
||||
err = fmt.Errorf("the wallet is in legacy format, run " +
|
||||
"with the --create option to import it")
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
localhostListeners := map[string]struct{}{
|
||||
|
@ -596,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
|
||||
|
@ -622,38 +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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only allow server TLS to be disabled if the RPC server is bound to
|
||||
// localhost addresses.
|
||||
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 " +
|
||||
|
@ -671,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.
|
||||
|
|
28
deps.txt
28
deps.txt
|
@ -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
|
|
@ -1,3 +0,0 @@
|
|||
### Guides
|
||||
|
||||
[Rebuilding all transaction history with forced rescans](https://github.com/lbryio/lbcwallet/tree/master/docs/force_rescans.md)
|
|
@ -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)
|
||||
|
||||
```
|
27
go.mod
27
go.mod
|
@ -4,58 +4,43 @@ 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
|
||||
google.golang.org/grpc v1.46.2
|
||||
golang.org/x/tools v0.1.10
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/DataDog/zstd v1.5.2 // indirect
|
||||
github.com/aead/siphash v1.0.1 // indirect
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect
|
||||
github.com/btcsuite/winsvc v1.0.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cockroachdb/errors v1.9.0 // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect
|
||||
github.com/cockroachdb/pebble v0.0.0-20220523221036-bb2c1501ac23 // indirect
|
||||
github.com/cockroachdb/redact v1.1.3 // indirect
|
||||
github.com/codahale/hdrhistogram v0.9.0 // indirect
|
||||
github.com/decred/dcrd/lru v1.1.1 // indirect
|
||||
github.com/felixge/fgprof v0.9.2 // indirect
|
||||
github.com/getsentry/sentry-go v0.13.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/pprof v0.0.0-20220520215854-d04f2422c8a1 // indirect
|
||||
github.com/kkdai/bstream v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.15.4 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20220517141722-cf486979b281 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.1 // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.22.4 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.5.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf // indirect
|
||||
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
|
||||
google.golang.org/genproto v0.0.0-20220524023933-508584e28198 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
||||
|
@ -65,4 +50,4 @@ require (
|
|||
// version.
|
||||
replace github.com/onsi/ginkgo => github.com/onsi/ginkgo v1.14.2
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
|
90
go.sum
90
go.sum
|
@ -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=
|
||||
|
@ -26,22 +24,13 @@ github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JG
|
|||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||
github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk=
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
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=
|
||||
|
@ -71,8 +60,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/lru v1.1.1 h1:kWFDaW0OWx6AD6Ki342c+JPmHbiVdE6rK81pT3fuo/Y=
|
||||
github.com/decred/dcrd/lru v1.1.1/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
|
||||
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
|
@ -81,15 +68,11 @@ 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=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/felixge/fgprof v0.9.2 h1:tAMHtWMyl6E0BimjVbFt7fieU6FpjttsZN7j0wT5blc=
|
||||
github.com/felixge/fgprof v0.9.2/go.mod h1:+VNi+ZXtHIQ6wIw6bUT8nXQRefQflWECoFyRealT5sg=
|
||||
github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
|
@ -98,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=
|
||||
|
@ -106,8 +88,6 @@ github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6
|
|||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
|
@ -132,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=
|
||||
|
@ -146,26 +124,19 @@ 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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
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=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
||||
github.com/google/pprof v0.0.0-20220520215854-d04f2422c8a1 h1:K4bn56FHdjFCfjSo3wWaD6rJL8r9yvmmncJNMhdkKrw=
|
||||
github.com/google/pprof v0.0.0-20220520215854-d04f2422c8a1/go.mod h1:gSuNB+gJaOiQKLEZ+q+PK9Mq3SOzhRcw2GsGS/FhYDk=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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=
|
||||
github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
|
||||
github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
|
||||
|
@ -216,19 +187,12 @@ 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.100 h1:j2WDptbw6KPVqQXvUX/SXDr9Ndr8/C6yLe4z3wstgzM=
|
||||
github.com/lbryio/lbcd v0.22.100/go.mod h1:iwOnc+syXOeMsAqjHHjMWktni+cg28xOXIY/lN+YIWE=
|
||||
github.com/lbryio/lbcd v0.22.101 h1:gGzkb5w6fJMlWzbl5pqwPTZLCyRkLCmgSvpcoPl72qQ=
|
||||
github.com/lbryio/lbcd v0.22.101/go.mod h1:iwOnc+syXOeMsAqjHHjMWktni+cg28xOXIY/lN+YIWE=
|
||||
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/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/lufia/plan9stats v0.0.0-20220517141722-cf486979b281 h1:aczX6NMOtt6L4YT0fQvKkDK6LZEtdOso9sUH89V1+P0=
|
||||
github.com/lufia/plan9stats v0.0.0-20220517141722-cf486979b281/go.mod h1:lc+czkgO/8F7puNki5jk8QyujbfK1LOT7Wl0ON2hxyk=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
|
@ -269,11 +233,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c h1:NRoLoZvkBTKvR5gQLgA3e0hqjkY9u1wm+iOL45VN/qI=
|
||||
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
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=
|
||||
|
@ -282,8 +242,6 @@ github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo
|
|||
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shirou/gopsutil/v3 v3.22.4 h1:srAQaiX6jX/cYL6q29aE0m8lOskT9CurZ9N61YR3yoI=
|
||||
github.com/shirou/gopsutil/v3 v3.22.4/go.mod h1:D01hZJ4pVHPpCTZ3m3T2+wDF2YAGfd+H4ifUguaQzHM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
|
@ -303,12 +261,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/tklauser/numcpus v0.5.0 h1:ooe7gN0fg6myJ0EKoTAf5hebTZrH52px3New/D9iJ+A=
|
||||
github.com/tklauser/numcpus v0.5.0/go.mod h1:OGzpTxpcIMNGYQdit2BYL1pvk/dSOaJWjKoflh+RQjo=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
|
@ -330,11 +282,8 @@ github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZ
|
|||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
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=
|
||||
|
@ -362,11 +311,12 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
|||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
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=
|
||||
|
@ -377,19 +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-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
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=
|
||||
|
@ -406,19 +350,15 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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-20201204225414-ed752295db88/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=
|
||||
|
@ -429,9 +369,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210909193231-528a39cd75f3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
@ -442,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=
|
||||
|
@ -460,32 +397,27 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK
|
|||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
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=
|
||||
|
@ -497,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=
|
||||
|
@ -512,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=
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,18 +0,0 @@
|
|||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !windows && !plan9
|
||||
// +build !windows,!plan9
|
||||
|
||||
package rename
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Atomic provides an atomic file rename. newpath is replaced if it
|
||||
// already exists.
|
||||
func Atomic(oldpath, newpath string) error {
|
||||
return os.Rename(oldpath, newpath)
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
// The following is adapted from goleveldb
|
||||
// (https://github.com/syndtr/goleveldb) under the following license:
|
||||
//
|
||||
// Copyright 2012 Suryandaru Triandana <syndtr@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package rename
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procMoveFileExW = modkernel32.NewProc("MoveFileExW")
|
||||
)
|
||||
|
||||
const (
|
||||
_MOVEFILE_REPLACE_EXISTING = 1
|
||||
)
|
||||
|
||||
func moveFileEx(from *uint16, to *uint16, flags uint32) error {
|
||||
r1, _, e1 := syscall.Syscall(procMoveFileExW.Addr(), 3,
|
||||
uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)),
|
||||
uintptr(flags))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
return error(e1)
|
||||
} else {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Atomic provides an atomic file rename. newpath is replaced if it
|
||||
// already exists.
|
||||
func Atomic(oldpath, newpath string) error {
|
||||
from, err := syscall.UTF16PtrFromString(oldpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to, err := syscall.UTF16PtrFromString(newpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return moveFileEx(from, to, _MOVEFILE_REPLACE_EXISTING)
|
||||
}
|
|
@ -13,17 +13,15 @@ import (
|
|||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcutil/hdkeychain"
|
||||
"github.com/lbryio/lbcwallet/internal/legacy/keystore"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
// ProvideSeed is used to prompt for the wallet seed which maybe required during
|
||||
// upgrades.
|
||||
func ProvideSeed() ([]byte, error) {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
func promptSeed(reader *bufio.Reader) ([]byte, error) {
|
||||
for {
|
||||
fmt.Print("Enter existing wallet seed: ")
|
||||
seedStr, err := reader.ReadString('\n')
|
||||
|
@ -47,26 +45,6 @@ func ProvideSeed() ([]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.
|
||||
|
@ -116,10 +94,41 @@ func promptListBool(reader *bufio.Reader, prefix string,
|
|||
return response == "yes" || response == "y", nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// promptBirhdayUnixTimeStamp prompts the user a Unix timestamp in second.
|
||||
func promptUnixTimestamp(reader *bufio.Reader, prefix string,
|
||||
defaultEntry string) (time.Time, error) { // nolint:unparam
|
||||
|
||||
prompt := fmt.Sprintf("%s [%s]: ", prefix, defaultEntry)
|
||||
|
||||
for {
|
||||
fmt.Print(prompt)
|
||||
reply, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
reply = strings.TrimSpace(strings.ToLower(reply))
|
||||
if reply == "" {
|
||||
reply = defaultEntry
|
||||
}
|
||||
ts, err := strconv.ParseInt(reply, 10, 64)
|
||||
if err != nil {
|
||||
fmt.Print(prompt)
|
||||
continue
|
||||
}
|
||||
|
||||
return time.Unix(ts, 0), nil
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
@ -154,110 +163,17 @@ func promptPass(_ *bufio.Reader, prefix string, confirm bool) ([]byte, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// PrivatePass prompts the user for a private passphrase with varying behavior
|
||||
// depending on whether the passed legacy keystore exists. When it does, the
|
||||
// user is prompted for the existing passphrase which is then used to unlock it.
|
||||
// On the other hand, when the legacy keystore is nil, 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, legacyKeyStore *keystore.Store) ([]byte, error) {
|
||||
// When there is not an existing legacy wallet, simply prompt the user
|
||||
// for a new private passphase and return it.
|
||||
if legacyKeyStore == nil {
|
||||
return promptPass(reader, "Enter the private "+
|
||||
"passphrase for your new wallet", true)
|
||||
}
|
||||
|
||||
// At this point, there is an existing legacy wallet, so prompt the user
|
||||
// for the existing private passphrase and ensure it properly unlocks
|
||||
// the legacy wallet so all of the addresses can later be imported.
|
||||
fmt.Println("You have an existing legacy wallet. All addresses from " +
|
||||
"your existing legacy wallet will be imported into the new " +
|
||||
"wallet format.")
|
||||
for {
|
||||
privPass, err := promptPass(reader, "Enter the private "+
|
||||
"passphrase for your existing wallet", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Keep prompting the user until the passphrase is correct.
|
||||
if err := legacyKeyStore.Unlock(privPass); err != nil {
|
||||
if err == keystore.ErrWrongPassphrase {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return privPass, nil
|
||||
}
|
||||
func birthday(reader *bufio.Reader) (time.Time, error) {
|
||||
prompt := "Enter the birthday of the seed in Unix timestamp " +
|
||||
"(the walllet will scan the chain from this time)"
|
||||
return promptUnixTimestamp(reader, prompt, "0")
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
pubPass, err = promptPass(reader, "Enter the public "+
|
||||
"passphrase for your new wallet", true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if bytes.Equal(pubPass, privPass) {
|
||||
useSamePass, err := promptListBool(reader,
|
||||
"Are you sure want to use the same passphrase "+
|
||||
"for public and private data?", "no")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if useSamePass {
|
||||
break
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -265,34 +181,31 @@ func PublicPass(reader *bufio.Reader, privPass []byte,
|
|||
// the user along with prompting them for confirmation. When the user answers
|
||||
// yes, a the user is prompted for it. All prompts are repeated until the user
|
||||
// enters a valid response.
|
||||
func Seed(reader *bufio.Reader) ([]byte, error) {
|
||||
func Seed(reader *bufio.Reader) ([]byte, time.Time, error) {
|
||||
bday := time.Now()
|
||||
// Ascertain the wallet generation seed.
|
||||
useUserSeed, err := promptListBool(reader, "Do you have an "+
|
||||
"existing wallet seed you want to use?", "no")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, bday, err
|
||||
}
|
||||
if !useUserSeed {
|
||||
seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, bday, err
|
||||
}
|
||||
|
||||
fmt.Println("Your wallet generation seed is:")
|
||||
fmt.Printf("%x\n", seed)
|
||||
fmt.Println("IMPORTANT: Keep the seed in a safe place as you\n" +
|
||||
"will NOT be able to restore your wallet without it.")
|
||||
fmt.Println("Please keep in mind that anyone who has access\n" +
|
||||
"to the seed can also restore your wallet thereby\n" +
|
||||
"giving them access to all your funds, so it is\n" +
|
||||
"imperative that you keep it in a secure location.")
|
||||
fmt.Printf("Your wallet generation seed is: %x\n", seed)
|
||||
|
||||
fmt.Printf("\nIMPORTANT: Keep the seed in a safe place as you " +
|
||||
"will NOT be able to restore your wallet without it.\n\n")
|
||||
|
||||
for {
|
||||
fmt.Print(`Once you have stored the seed in a safe ` +
|
||||
`and secure location, enter "OK" to continue: `)
|
||||
confirmSeed, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, bday, err
|
||||
}
|
||||
confirmSeed = strings.TrimSpace(confirmSeed)
|
||||
confirmSeed = strings.Trim(confirmSeed, `"`)
|
||||
|
@ -301,28 +214,18 @@ func Seed(reader *bufio.Reader) ([]byte, error) {
|
|||
}
|
||||
}
|
||||
|
||||
return seed, nil
|
||||
return seed, bday, nil
|
||||
}
|
||||
|
||||
for {
|
||||
fmt.Print("Enter existing wallet seed: ")
|
||||
seedStr, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seedStr = strings.TrimSpace(strings.ToLower(seedStr))
|
||||
|
||||
seed, err := hex.DecodeString(seedStr)
|
||||
if err != nil || len(seed) < hdkeychain.MinSeedBytes ||
|
||||
len(seed) > hdkeychain.MaxSeedBytes {
|
||||
|
||||
fmt.Printf("Invalid seed specified. Must be a "+
|
||||
"hexadecimal value that is at least %d bits and "+
|
||||
"at most %d bits\n", hdkeychain.MinSeedBytes*8,
|
||||
hdkeychain.MaxSeedBytes*8)
|
||||
continue
|
||||
}
|
||||
|
||||
return seed, nil
|
||||
seed, err := promptSeed(reader)
|
||||
if err != nil {
|
||||
return nil, bday, err
|
||||
}
|
||||
|
||||
bday, err = birthday(reader)
|
||||
if err != nil {
|
||||
return nil, bday, err
|
||||
}
|
||||
|
||||
return seed, bday, nil
|
||||
}
|
||||
|
|
|
@ -1,32 +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"
|
||||
|
||||
"github.com/lbryio/lbcwallet/internal/legacy/keystore"
|
||||
)
|
||||
|
||||
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, _ *keystore.Store) ([]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")
|
||||
}
|
|
@ -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,80 +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",
|
||||
|
||||
// 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",
|
||||
|
||||
// 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.",
|
||||
}
|
||||
|
|
|
@ -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)}},
|
||||
|
@ -63,12 +65,12 @@ var Methods = []struct {
|
|||
{"walletpassphrase", nil},
|
||||
{"walletpassphrasechange", nil},
|
||||
{"createnewaccount", nil},
|
||||
{"exportwatchingwallet", returnsString},
|
||||
{"getbestblock", []interface{}{(*btcjson.GetBestBlockResult)(nil)}},
|
||||
{"getunconfirmedbalance", returnsNumber},
|
||||
{"listaddresstransactions", returnsLTRArray},
|
||||
{"listalltransactions", returnsLTRArray},
|
||||
{"renameaccount", nil},
|
||||
{"rescanblockchain", []interface{}{(*btcjson.RescanBlockchainResult)(nil)}},
|
||||
{"walletislocked", returnsBool},
|
||||
}
|
||||
|
||||
|
|
43
lbcwallet.go
43
lbcwallet.go
|
@ -75,30 +75,27 @@ func walletMain() error {
|
|||
// Create and start HTTP server to serve wallet client connections.
|
||||
// This will be updated with the wallet and chain server RPC client
|
||||
// created below after each is created.
|
||||
rpcs, legacyRPCServer, err := startRPCServers(loader)
|
||||
legacyRPCServer, err := startRPCServers(loader)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to create RPC servers: %v", err)
|
||||
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, rpcs, legacyRPCServer)
|
||||
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
|
||||
|
@ -110,15 +107,6 @@ func walletMain() error {
|
|||
log.Errorf("Failed to close wallet: %v", err)
|
||||
}
|
||||
})
|
||||
if rpcs != nil {
|
||||
addInterruptHandler(func() {
|
||||
// TODO: Does this need to wait for the grpc server to
|
||||
// finish up any requests?
|
||||
log.Warn("Stopping RPC server...")
|
||||
rpcs.Stop()
|
||||
log.Info("RPC server shutdown")
|
||||
})
|
||||
}
|
||||
if legacyRPCServer != nil {
|
||||
addInterruptHandler(func() {
|
||||
log.Warn("Stopping legacy RPC server...")
|
||||
|
@ -144,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 (
|
||||
|
@ -234,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
|
||||
|
|
2
log.go
2
log.go
|
@ -15,7 +15,6 @@ import (
|
|||
"github.com/lbryio/lbcd/rpcclient"
|
||||
"github.com/lbryio/lbcwallet/chain"
|
||||
"github.com/lbryio/lbcwallet/rpc/legacyrpc"
|
||||
"github.com/lbryio/lbcwallet/rpc/rpcserver"
|
||||
"github.com/lbryio/lbcwallet/wallet"
|
||||
"github.com/lbryio/lbcwallet/wtxmgr"
|
||||
)
|
||||
|
@ -67,7 +66,6 @@ func init() {
|
|||
wtxmgr.UseLogger(txmgrLog)
|
||||
chain.UseLogger(chainLog)
|
||||
rpcclient.UseLogger(chainLog)
|
||||
rpcserver.UseLogger(grpcLog)
|
||||
legacyrpc.UseLogger(legacyRPCLog)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -18,7 +17,8 @@ type Params struct {
|
|||
}
|
||||
|
||||
// MainNetParams contains parameters specific running lbcwallet and
|
||||
// on the main network (wire.MainNet).
|
||||
//
|
||||
// on the main network (wire.MainNet).
|
||||
var MainNetParams = Params{
|
||||
Params: &chaincfg.MainNetParams,
|
||||
RPCClientPort: "9245",
|
||||
|
@ -26,7 +26,8 @@ var MainNetParams = Params{
|
|||
}
|
||||
|
||||
// TestNet3Params contains parameters specific running lbcwallet and
|
||||
// on the test network (version 3) (wire.TestNet3).
|
||||
//
|
||||
// on the test network (version 3) (wire.TestNet3).
|
||||
var TestNet3Params = Params{
|
||||
Params: &chaincfg.TestNet3Params,
|
||||
RPCClientPort: "19245",
|
||||
|
@ -40,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
|
||||
}
|
||||
|
|
319
rpc/api.proto
319
rpc/api.proto
|
@ -1,319 +0,0 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package walletrpc;
|
||||
|
||||
service VersionService {
|
||||
rpc Version (VersionRequest) returns (VersionResponse);
|
||||
}
|
||||
|
||||
message VersionRequest {}
|
||||
message VersionResponse {
|
||||
string version_string = 1;
|
||||
uint32 major = 2;
|
||||
uint32 minor = 3;
|
||||
uint32 patch = 4;
|
||||
string prerelease = 5;
|
||||
string build_metadata = 6;
|
||||
}
|
||||
|
||||
service WalletService {
|
||||
// Queries
|
||||
rpc Ping (PingRequest) returns (PingResponse);
|
||||
rpc Network (NetworkRequest) returns (NetworkResponse);
|
||||
rpc AccountNumber (AccountNumberRequest) returns (AccountNumberResponse);
|
||||
rpc Accounts (AccountsRequest) returns (AccountsResponse);
|
||||
rpc Balance (BalanceRequest) returns (BalanceResponse);
|
||||
rpc GetTransactions (GetTransactionsRequest) returns (GetTransactionsResponse);
|
||||
|
||||
// Notifications
|
||||
rpc TransactionNotifications (TransactionNotificationsRequest) returns (stream TransactionNotificationsResponse);
|
||||
rpc SpentnessNotifications (SpentnessNotificationsRequest) returns (stream SpentnessNotificationsResponse);
|
||||
rpc AccountNotifications (AccountNotificationsRequest) returns (stream AccountNotificationsResponse);
|
||||
|
||||
// Control
|
||||
rpc ChangePassphrase (ChangePassphraseRequest) returns (ChangePassphraseResponse);
|
||||
rpc RenameAccount (RenameAccountRequest) returns (RenameAccountResponse);
|
||||
rpc NextAccount (NextAccountRequest) returns (NextAccountResponse);
|
||||
rpc NextAddress (NextAddressRequest) returns (NextAddressResponse);
|
||||
rpc ImportPrivateKey (ImportPrivateKeyRequest) returns (ImportPrivateKeyResponse);
|
||||
rpc FundTransaction (FundTransactionRequest) returns (FundTransactionResponse);
|
||||
rpc SignTransaction (SignTransactionRequest) returns (SignTransactionResponse);
|
||||
rpc PublishTransaction (PublishTransactionRequest) returns (PublishTransactionResponse);
|
||||
}
|
||||
|
||||
service WalletLoaderService {
|
||||
rpc WalletExists (WalletExistsRequest) returns (WalletExistsResponse);
|
||||
rpc CreateWallet (CreateWalletRequest) returns (CreateWalletResponse);
|
||||
rpc OpenWallet (OpenWalletRequest) returns (OpenWalletResponse);
|
||||
rpc CloseWallet (CloseWalletRequest) returns (CloseWalletResponse);
|
||||
rpc StartConsensusRpc (StartConsensusRpcRequest) returns (StartConsensusRpcResponse);
|
||||
}
|
||||
|
||||
message TransactionDetails {
|
||||
message Input {
|
||||
uint32 index = 1;
|
||||
uint32 previous_account = 2;
|
||||
int64 previous_amount = 3;
|
||||
}
|
||||
message Output {
|
||||
uint32 index = 1;
|
||||
uint32 account = 2;
|
||||
bool internal = 3;
|
||||
}
|
||||
bytes hash = 1;
|
||||
bytes transaction = 2;
|
||||
repeated Input debits = 3;
|
||||
repeated Output credits = 4;
|
||||
int64 fee = 5;
|
||||
int64 timestamp = 6; // May be earlier than a block timestamp, but never later.
|
||||
}
|
||||
|
||||
message BlockDetails {
|
||||
bytes hash = 1;
|
||||
int32 height = 2;
|
||||
int64 timestamp = 3;
|
||||
repeated TransactionDetails transactions = 4;
|
||||
}
|
||||
|
||||
message AccountBalance {
|
||||
uint32 account = 1;
|
||||
int64 total_balance = 2;
|
||||
}
|
||||
|
||||
message PingRequest {}
|
||||
message PingResponse {}
|
||||
|
||||
message NetworkRequest {}
|
||||
message NetworkResponse {
|
||||
uint32 active_network = 1;
|
||||
}
|
||||
|
||||
message AccountNumberRequest {
|
||||
string account_name = 1;
|
||||
}
|
||||
message AccountNumberResponse {
|
||||
uint32 account_number = 1;
|
||||
}
|
||||
|
||||
message AccountsRequest {}
|
||||
message AccountsResponse {
|
||||
message Account {
|
||||
uint32 account_number = 1;
|
||||
string account_name = 2;
|
||||
int64 total_balance = 3;
|
||||
uint32 external_key_count = 4;
|
||||
uint32 internal_key_count = 5;
|
||||
uint32 imported_key_count = 6;
|
||||
}
|
||||
repeated Account accounts = 1;
|
||||
bytes current_block_hash = 2;
|
||||
int32 current_block_height = 3;
|
||||
}
|
||||
|
||||
message RenameAccountRequest {
|
||||
uint32 account_number = 1;
|
||||
string new_name = 2;
|
||||
}
|
||||
message RenameAccountResponse {}
|
||||
|
||||
message NextAccountRequest {
|
||||
bytes passphrase = 1;
|
||||
string account_name = 2;
|
||||
}
|
||||
message NextAccountResponse {
|
||||
uint32 account_number = 1;
|
||||
}
|
||||
|
||||
message NextAddressRequest {
|
||||
uint32 account = 1;
|
||||
enum Kind {
|
||||
BIP0044_EXTERNAL = 0;
|
||||
BIP0044_INTERNAL = 1;
|
||||
}
|
||||
Kind kind = 2;
|
||||
}
|
||||
message NextAddressResponse {
|
||||
string address = 1;
|
||||
}
|
||||
|
||||
message ImportPrivateKeyRequest {
|
||||
bytes passphrase = 1;
|
||||
uint32 account = 2;
|
||||
string private_key_wif = 3;
|
||||
bool rescan = 4;
|
||||
}
|
||||
message ImportPrivateKeyResponse {
|
||||
}
|
||||
|
||||
message BalanceRequest {
|
||||
uint32 account_number = 1;
|
||||
int32 required_confirmations = 2;
|
||||
}
|
||||
message BalanceResponse {
|
||||
int64 total = 1;
|
||||
int64 spendable = 2;
|
||||
int64 immature_reward = 3;
|
||||
}
|
||||
|
||||
message GetTransactionsRequest {
|
||||
// Optionally specify the starting block from which to begin including all transactions.
|
||||
// Either the starting block hash or height may be specified, but not both.
|
||||
// If a block height is specified and is negative, the absolute value becomes the number of
|
||||
// last blocks to include. That is, given a current chain height of 1000 and a starting block
|
||||
// height of -3, transaction notifications will be created for blocks 998, 999, and 1000.
|
||||
// If both options are excluded, transaction results are created for transactions since the
|
||||
// genesis block.
|
||||
bytes starting_block_hash = 1;
|
||||
sint32 starting_block_height = 2;
|
||||
|
||||
// Optionally specify the last block that transaction results may appear in.
|
||||
// Either the ending block hash or height may be specified, but not both.
|
||||
// If both are excluded, transaction results are created for all transactions
|
||||
// through the best block, and include all unmined transactions.
|
||||
bytes ending_block_hash = 3;
|
||||
int32 ending_block_height = 4;
|
||||
|
||||
// Include at least this many of the newest transactions if they exist.
|
||||
// Cannot be used when the ending block hash is specified.
|
||||
//
|
||||
// TODO: remove until spec adds it back in some way.
|
||||
int32 minimum_recent_transactions = 5;
|
||||
|
||||
// TODO: limit max number of txs?
|
||||
}
|
||||
message GetTransactionsResponse {
|
||||
repeated BlockDetails mined_transactions = 1;
|
||||
repeated TransactionDetails unmined_transactions = 2;
|
||||
}
|
||||
|
||||
message ChangePassphraseRequest {
|
||||
enum Key {
|
||||
PRIVATE = 0;
|
||||
PUBLIC = 1;
|
||||
}
|
||||
Key key = 1;
|
||||
bytes old_passphrase = 2;
|
||||
bytes new_passphrase = 3;
|
||||
}
|
||||
message ChangePassphraseResponse {}
|
||||
|
||||
message FundTransactionRequest {
|
||||
uint32 account = 1;
|
||||
int64 target_amount = 2;
|
||||
int32 required_confirmations = 3;
|
||||
bool include_immature_coinbases = 4;
|
||||
bool include_change_script = 5;
|
||||
bool include_stakes = 6;
|
||||
}
|
||||
message FundTransactionResponse {
|
||||
message PreviousOutput {
|
||||
bytes transaction_hash = 1;
|
||||
uint32 output_index = 2;
|
||||
int64 amount = 3;
|
||||
bytes pk_script = 4;
|
||||
int64 receive_time = 5;
|
||||
bool from_coinbase = 6;
|
||||
}
|
||||
repeated PreviousOutput selected_outputs = 1;
|
||||
int64 total_amount = 2;
|
||||
bytes change_pk_script = 3;
|
||||
}
|
||||
|
||||
message SignTransactionRequest {
|
||||
bytes passphrase = 1;
|
||||
|
||||
bytes serialized_transaction = 2;
|
||||
|
||||
// If no indexes are specified, signatures scripts will be added for
|
||||
// every input. If any input indexes are specified, only those inputs
|
||||
// will be signed. Rather than returning an incompletely signed
|
||||
// transaction if any of the inputs to be signed can not be, the RPC
|
||||
// immediately errors.
|
||||
repeated uint32 input_indexes = 3;
|
||||
}
|
||||
message SignTransactionResponse {
|
||||
bytes transaction = 1;
|
||||
repeated uint32 unsigned_input_indexes = 2;
|
||||
}
|
||||
|
||||
message PublishTransactionRequest {
|
||||
bytes signed_transaction = 1;
|
||||
}
|
||||
message PublishTransactionResponse {}
|
||||
|
||||
message TransactionNotificationsRequest {}
|
||||
message TransactionNotificationsResponse {
|
||||
// Sorted by increasing height. This is a repeated field so many new blocks
|
||||
// in a new best chain can be notified at once during a reorganize.
|
||||
repeated BlockDetails attached_blocks = 1;
|
||||
|
||||
// If there was a chain reorganize, there may have been blocks with wallet
|
||||
// transactions that are no longer in the best chain. These are those
|
||||
// block's hashes.
|
||||
repeated bytes detached_blocks = 2;
|
||||
|
||||
// Any new unmined transactions are included here. These unmined transactions
|
||||
// refer to the current best chain, so transactions from detached blocks may
|
||||
// be moved to mempool and included here if they are not mined or double spent
|
||||
// in the new chain. Additonally, if no new blocks were attached but a relevant
|
||||
// unmined transaction is seen by the wallet, it will be reported here.
|
||||
repeated TransactionDetails unmined_transactions = 3;
|
||||
|
||||
// Instead of notifying all of the removed unmined transactions,
|
||||
// just send all of the current hashes.
|
||||
repeated bytes unmined_transaction_hashes = 4;
|
||||
}
|
||||
|
||||
message SpentnessNotificationsRequest {
|
||||
uint32 account = 1;
|
||||
bool no_notify_unspent = 2;
|
||||
bool no_notify_spent = 3;
|
||||
}
|
||||
|
||||
message SpentnessNotificationsResponse {
|
||||
bytes transaction_hash = 1;
|
||||
uint32 output_index = 2;
|
||||
message Spender {
|
||||
bytes transaction_hash = 1;
|
||||
uint32 input_index = 2;
|
||||
}
|
||||
Spender spender = 3;
|
||||
}
|
||||
|
||||
message AccountNotificationsRequest {}
|
||||
message AccountNotificationsResponse {
|
||||
uint32 account_number = 1;
|
||||
string account_name = 2;
|
||||
uint32 external_key_count = 3;
|
||||
uint32 internal_key_count = 4;
|
||||
uint32 imported_key_count = 5;
|
||||
}
|
||||
|
||||
message CreateWalletRequest {
|
||||
bytes public_passphrase = 1;
|
||||
bytes private_passphrase = 2;
|
||||
bytes seed = 3;
|
||||
}
|
||||
message CreateWalletResponse {}
|
||||
|
||||
message OpenWalletRequest {
|
||||
bytes public_passphrase = 1;
|
||||
}
|
||||
message OpenWalletResponse {}
|
||||
|
||||
message CloseWalletRequest {}
|
||||
message CloseWalletResponse {}
|
||||
|
||||
message WalletExistsRequest {}
|
||||
message WalletExistsResponse {
|
||||
bool exists = 1;
|
||||
}
|
||||
|
||||
message StartConsensusRpcRequest {
|
||||
string network_address = 1;
|
||||
string username = 2;
|
||||
bytes password = 3;
|
||||
bytes certificate = 4;
|
||||
bool skipverify = 5;
|
||||
}
|
||||
message StartConsensusRpcResponse {}
|
|
@ -1,16 +0,0 @@
|
|||
# RPC Documentation
|
||||
|
||||
This project provides a [gRPC](http://www.grpc.io/) server for Remote Procedure
|
||||
Call (RPC) access from other processes. This is intended to be the primary
|
||||
means by which users, through other client programs, interact with the wallet.
|
||||
|
||||
These documents cover the documentation for both consumers of the server and
|
||||
developers who must make changes or additions to the API and server
|
||||
implementation:
|
||||
|
||||
- [API specification](./api.md)
|
||||
- [Client usage](./clientusage.md)
|
||||
- [Making API changes](./serverchanges.md)
|
||||
|
||||
A legacy RPC server based on the JSON-RPC API of Bitcoin Core's wallet is also
|
||||
available, but documenting its usage is out of scope for these documents.
|
|
@ -1,999 +0,0 @@
|
|||
# RPC API Specification
|
||||
|
||||
Version: 2.0.1
|
||||
=======
|
||||
|
||||
**Note:** This document assumes the reader is familiar with gRPC concepts.
|
||||
Refer to the [gRPC Concepts documentation](http://www.grpc.io/docs/guides/concepts.html)
|
||||
for any unfamiliar terms.
|
||||
|
||||
**Note:** The naming style used for autogenerated identifiers may differ
|
||||
depending on the language being used. This document follows the naming style
|
||||
used by Google in their Protocol Buffers and gRPC documentation as well as this
|
||||
project's `.proto` files. That is, CamelCase is used for services, methods, and
|
||||
messages, lower_snake_case for message fields, and SCREAMING_SNAKE_CASE for
|
||||
enums.
|
||||
|
||||
**Note:** The entierty of the RPC API is currently considered unstable and may
|
||||
change anytime. Stability will be gradually added based on correctness,
|
||||
perceived usefulness and ease-of-use over alternatives, and user feedback.
|
||||
|
||||
This document is the authoritative source on the RPC API's definitions and
|
||||
semantics. Any divergence from this document is an implementation error. API
|
||||
fixes and additions require a version increase according to the rules of
|
||||
[Semantic Versioning 2.0.0](http://semver.org/).
|
||||
|
||||
Only optional proto3 message fields are used (the `required` keyword is never
|
||||
used in the `.proto` file). If a message field must be set to something other
|
||||
than the default value, or any other values are invalid, the error must occur in
|
||||
the application's message handling. This prevents accidentally introducing
|
||||
parsing errors if a previously optional field is missing or a new required field
|
||||
is added.
|
||||
|
||||
Functionality is grouped into gRPC services. Depending on what functions are
|
||||
currently callable, different services will be running. As an example, the
|
||||
server may be running without a loaded wallet, in which case the Wallet service
|
||||
is not running and the Loader service must be used to create a new or load an
|
||||
existing wallet.
|
||||
|
||||
- [`VersionService`](#versionservice)
|
||||
- [`LoaderService`](#loaderservice)
|
||||
- [`WalletService`](#walletservice)
|
||||
|
||||
## `VersionService`
|
||||
|
||||
The `VersionService` service provides the caller with versioning information
|
||||
regarding the RPC server. It has no dependencies and is always running.
|
||||
|
||||
**Methods:**
|
||||
|
||||
- [`Version`](#version)
|
||||
|
||||
### Methods
|
||||
|
||||
#### `Version`
|
||||
|
||||
The `Version` method returns the RPC server version. Versioning follows the
|
||||
rules of Semantic Versioning (SemVer) 2.0.0.
|
||||
|
||||
**Request:** `VersionRequest`
|
||||
|
||||
**Response:** `VersionResponse`
|
||||
|
||||
- `string version_string`: The version encoded as a string.
|
||||
|
||||
- `uint32 major`: The SemVer major version number.
|
||||
|
||||
- `uint32 minor`: The SemVer minor version number.
|
||||
|
||||
- `uint32 patch`: The SemVer patch version number.
|
||||
|
||||
- `string prerelease`: The SemVer pre-release version identifier, if any.
|
||||
|
||||
- `string build_metadata`: Extra SemVer build metadata, if any.
|
||||
|
||||
**Expected errors:** None
|
||||
|
||||
**Stability:** Stable
|
||||
|
||||
## `LoaderService`
|
||||
|
||||
The `LoaderService` service provides the caller with functions related to the
|
||||
management of the wallet and its connection to the Bitcoin network. It has no
|
||||
dependencies and is always running.
|
||||
|
||||
**Methods:**
|
||||
|
||||
- [`WalletExists`](#walletexists)
|
||||
- [`CreateWallet`](#createwallet)
|
||||
- [`OpenWallet`](#openwallet)
|
||||
- [`CloseWallet`](#closewallet)
|
||||
- [`StartConsensusRpc`](#startconsensusrpc)
|
||||
|
||||
**Shared messages:**
|
||||
|
||||
- [`BlockDetails`](#blockdetails)
|
||||
- [`TransactionDetails`](#transactiondetails)
|
||||
|
||||
### Methods
|
||||
|
||||
#### `WalletExists`
|
||||
|
||||
The `WalletExists` method returns whether a file at the wallet database's file
|
||||
path exists. Clients that must load wallets with this service are expected to
|
||||
call this RPC to query whether `OpenWallet` should be used to open an existing
|
||||
wallet, or `CreateWallet` to create a new wallet.
|
||||
|
||||
**Request:** `WalletExistsRequest`
|
||||
|
||||
**Response:** `WalletExistsResponse`
|
||||
|
||||
- `bool exists`: Whether the wallet file exists.
|
||||
|
||||
**Expected errors:** None
|
||||
|
||||
**Stability:** Unstable
|
||||
|
||||
___
|
||||
|
||||
#### `CreateWallet`
|
||||
|
||||
The `CreateWallet` method is used to create a wallet that is protected by two
|
||||
levels of encryption: the public passphrase (for data that is made public on the
|
||||
blockchain) and the private passphrase (for private keys). Since the seed is
|
||||
not saved in the wallet database and clients should make their users backup the
|
||||
seed, it needs to be passed as part of the request.
|
||||
|
||||
After creating a wallet, the `WalletService` service begins running.
|
||||
|
||||
**Request:** `CreateWalletRequest`
|
||||
|
||||
- `bytes public_passphrase`: The passphrase used for the outer wallet
|
||||
encryption. This passphrase protects data that is made public on the
|
||||
blockchain. If this passphrase has zero length, an insecure default is used
|
||||
instead.
|
||||
|
||||
- `bytes private_passphrase`: The passphrase used for the inner wallet
|
||||
encryption. This is the passphrase used for data that must always remain
|
||||
private, such as private keys. The length of this field must not be zero.
|
||||
|
||||
- `bytes seed`: The BIP0032 seed used to derive all wallet keys. The length of
|
||||
this field must be between 16 and 64 bytes, inclusive.
|
||||
|
||||
**Response:** `CreateWalletReponse`
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `FailedPrecondition`: The wallet is currently open.
|
||||
|
||||
- `AlreadyExists`: A file already exists at the wallet database file path.
|
||||
|
||||
- `InvalidArgument`: A private passphrase was not included in the request, or
|
||||
the seed is of incorrect length.
|
||||
|
||||
**Stability:** Unstable: There needs to be a way to recover all keys and
|
||||
transactions of a wallet being recovered by its seed. It is unclear whether
|
||||
it should be part of this method or a `WalletService` method.
|
||||
|
||||
___
|
||||
|
||||
#### `OpenWallet`
|
||||
|
||||
The `OpenWallet` method is used to open an existing wallet database. If the
|
||||
wallet is protected by a public passphrase, it can not be successfully opened if
|
||||
the public passphrase parameter is missing or incorrect.
|
||||
|
||||
After opening a wallet, the `WalletService` service begins running.
|
||||
|
||||
**Request:** `OpenWalletRequest`
|
||||
|
||||
- `bytes public_passphrase`: The passphrase used for the outer wallet
|
||||
encryption. This passphrase protects data that is made public on the
|
||||
blockchain. If this passphrase has zero length, an insecure default is used
|
||||
instead.
|
||||
|
||||
**Response:** `OpenWalletResponse`
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `FailedPrecondition`: The wallet is currently open.
|
||||
|
||||
- `NotFound`: The wallet database file does not exist.
|
||||
|
||||
- `InvalidArgument`: The public encryption passphrase was missing or incorrect.
|
||||
|
||||
**Stability:** Unstable
|
||||
|
||||
___
|
||||
|
||||
#### `CloseWallet`
|
||||
|
||||
The `CloseWallet` method is used to cleanly stop all wallet operations on a
|
||||
loaded wallet and close the database. After closing, the `WalletService`
|
||||
service will remain running but any operations that require the database will be
|
||||
unusable.
|
||||
|
||||
**Request:** `CloseWalletRequest`
|
||||
|
||||
**Response:** `CloseWalletResponse`
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `FailedPrecondition`: The wallet is not currently open.
|
||||
|
||||
**Stability:** Unstable: It would be preferable to stop the `WalletService`
|
||||
after closing, but there does not appear to be any way to do so currently. It
|
||||
may also be a good idea to limit under what conditions a wallet can be closed,
|
||||
such as only closing wallets loaded by `LoaderService` and/or using a secret
|
||||
to authenticate the operation.
|
||||
|
||||
___
|
||||
|
||||
#### `StartConsensusRpc`
|
||||
|
||||
The `StartConsensusRpc` method is used to provide clients the ability to dynamically
|
||||
start the RPC client. This RPC client is used for wallet syncing and
|
||||
publishing transactions to the Bitcoin network.
|
||||
|
||||
**Request:** `StartConsensusRpcRequest`
|
||||
|
||||
- `string network_address`: The host/IP and optional port of the RPC server to
|
||||
connect to. IP addresses may be IPv4 or IPv6. If the port is missing, a
|
||||
default port is chosen corresponding to the default RPC port of the
|
||||
active Bitcoin network.
|
||||
|
||||
- `string username`: The RPC username required to authenticate to the RPC
|
||||
server.
|
||||
|
||||
- `bytes password`: The RPC password required to authenticate to the RPC server.
|
||||
|
||||
- `bytes certificate`: The consensus RPC server's TLS certificate. If this
|
||||
field has zero length and the network address describes a loopback connection
|
||||
(`localhost`, `127.0.0.1`, or `::1`) TLS will be disabled.
|
||||
|
||||
**Response:** `StartConsensusRpcResponse`
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `FailedPrecondition`: A consensus RPC client is already active.
|
||||
|
||||
- `InvalidArgument`: The network address is ill-formatted or does not contain a
|
||||
valid IP address.
|
||||
|
||||
- `NotFound`: The consensus RPC server is unreachable. This condition may not
|
||||
return `Unavailable` as that refers to `LoaderService` itself being
|
||||
unavailable.
|
||||
|
||||
- `InvalidArgument`: The username, password, or certificate are invalid. This
|
||||
condition may not be return `Unauthenticated` as that refers to the client not
|
||||
having the credentials to call this method.
|
||||
|
||||
**Stability:** Unstable: It is unknown if the consensus RPC client will remain
|
||||
used after the project gains SPV support.
|
||||
|
||||
## `WalletService`
|
||||
|
||||
The WalletService service provides RPCs for the wallet itself. The service
|
||||
depends on a loaded wallet and does not run when the wallet has not been created
|
||||
or opened yet.
|
||||
|
||||
The service provides the following methods:
|
||||
|
||||
- [RPC API Specification](#rpc-api-specification)
|
||||
- [Version: 2.0.1](#version-201)
|
||||
- [`VersionService`](#versionservice)
|
||||
- [Methods](#methods)
|
||||
- [`Version`](#version)
|
||||
- [`LoaderService`](#loaderservice)
|
||||
- [Methods](#methods-1)
|
||||
- [`WalletExists`](#walletexists)
|
||||
- [`CreateWallet`](#createwallet)
|
||||
- [`OpenWallet`](#openwallet)
|
||||
- [`CloseWallet`](#closewallet)
|
||||
- [`StartConsensusRpc`](#startconsensusrpc)
|
||||
- [`WalletService`](#walletservice)
|
||||
- [`Ping`](#ping)
|
||||
- [`Network`](#network)
|
||||
- [`AccountNumber`](#accountnumber)
|
||||
- [`Accounts`](#accounts)
|
||||
- [`Balance`](#balance)
|
||||
- [`GetTransactions`](#gettransactions)
|
||||
- [`ChangePassphrase`](#changepassphrase)
|
||||
- [`RenameAccount`](#renameaccount)
|
||||
- [`NextAccount`](#nextaccount)
|
||||
- [`NextAddress`](#nextaddress)
|
||||
- [`ImportPrivateKey`](#importprivatekey)
|
||||
- [`FundTransaction`](#fundtransaction)
|
||||
- [`SignTransaction`](#signtransaction)
|
||||
- [`PublishTransaction`](#publishtransaction)
|
||||
- [`TransactionNotifications`](#transactionnotifications)
|
||||
- [`SpentnessNotifications`](#spentnessnotifications)
|
||||
- [`AccountNotifications`](#accountnotifications)
|
||||
- [Shared messages](#shared-messages)
|
||||
- [`BlockDetails`](#blockdetails)
|
||||
- [`TransactionDetails`](#transactiondetails)
|
||||
|
||||
#### `Ping`
|
||||
|
||||
The `Ping` method checks whether the service is active.
|
||||
|
||||
**Request:** `PingRequest`
|
||||
|
||||
**Response:** `PingResponse`
|
||||
|
||||
**Expected errors:** None
|
||||
|
||||
**Stability:** Unstable: This may be moved to another service as it does not
|
||||
depend on the wallet.
|
||||
|
||||
___
|
||||
|
||||
#### `Network`
|
||||
|
||||
The `Network` method returns the network identifier constant describing the
|
||||
server's active network.
|
||||
|
||||
**Request:** `NetworkRequest`
|
||||
|
||||
**Response:** `NetworkResponse`
|
||||
|
||||
- `uint32 active_network`: The network identifier.
|
||||
|
||||
**Expected errors:** None
|
||||
|
||||
**Stability:** Unstable: This may be moved to another service as it does not
|
||||
depend on the wallet.
|
||||
|
||||
___
|
||||
|
||||
#### `AccountNumber`
|
||||
|
||||
The `AccountNumber` method looks up a BIP0044 account number by an account's
|
||||
unique name.
|
||||
|
||||
**Request:** `AccountNumberRequest`
|
||||
|
||||
- `string account_name`: The name of the account being queried.
|
||||
|
||||
**Response:** `AccountNumberResponse`
|
||||
|
||||
- `uint32 account_number`: The BIP0044 account number.
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `Aborted`: The wallet database is closed.
|
||||
|
||||
- `NotFound`: No accounts exist by the name in the request.
|
||||
|
||||
**Stability:** Unstable
|
||||
|
||||
___
|
||||
|
||||
#### `Accounts`
|
||||
|
||||
The `Accounts` method returns the current properties of all accounts managed in
|
||||
the wallet.
|
||||
|
||||
**Request:** `AccountsRequest`
|
||||
|
||||
**Response:** `AccountsResponse`
|
||||
|
||||
- `repeated Account accounts`: Account properties grouped into `Account` nested
|
||||
message types, one per account, ordered by increasing account numbers.
|
||||
|
||||
**Nested message:** `Account`
|
||||
|
||||
- `uint32 account_number`: The BIP0044 account number.
|
||||
|
||||
- `string account_name`: The name of the account.
|
||||
|
||||
- `int64 total_balance`: The total (zero-conf and immature) balance, counted
|
||||
in Satoshis.
|
||||
|
||||
- `uint32 external_key_count`: The number of derived keys in the external
|
||||
key chain.
|
||||
|
||||
- `uint32 internal_key_count`: The number of derived keys in the internal
|
||||
key chain.
|
||||
|
||||
- `uint32 imported_key_count`: The number of imported keys.
|
||||
|
||||
- `bytes current_block_hash`: The hash of the block wallet is considered to
|
||||
be synced with.
|
||||
|
||||
- `int32 current_block_height`: The height of the block wallet is considered
|
||||
to be synced with.
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `Aborted`: The wallet database is closed.
|
||||
|
||||
**Stability:** Unstable
|
||||
|
||||
___
|
||||
|
||||
#### `Balance`
|
||||
|
||||
The `Balance` method queries the wallet for an account's balance. Balances are
|
||||
returned as combination of total, spendable (by consensus and request policy),
|
||||
and unspendable immature coinbase balances.
|
||||
|
||||
**Request:** `BalanceRequest`
|
||||
|
||||
- `uint32 account_number`: The account number to query.
|
||||
|
||||
- `int32 required_confirmations`: The number of confirmations required before an
|
||||
unspent transaction output's value is included in the spendable balance. This
|
||||
may not be negative.
|
||||
|
||||
**Response:** `BalanceResponse`
|
||||
|
||||
- `int64 total`: The total (zero-conf and immature) balance, counted in
|
||||
Satoshis.
|
||||
|
||||
- `int64 spendable`: The spendable balance, given some number of required
|
||||
confirmations, counted in Satoshis. This equals the total balance when the
|
||||
required number of confirmations is zero and there are no immature coinbase
|
||||
outputs.
|
||||
|
||||
- `int64 immature_reward`: The total value of all immature coinbase outputs,
|
||||
counted in Satoshis.
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `InvalidArgument`: The required number of confirmations is negative.
|
||||
|
||||
- `Aborted`: The wallet database is closed.
|
||||
|
||||
- `NotFound`: The account does not exist.
|
||||
|
||||
**Stability:** Unstable: It may prove useful to modify this RPC to query
|
||||
multiple accounts together.
|
||||
|
||||
___
|
||||
|
||||
#### `GetTransactions`
|
||||
|
||||
The `GetTransactions` method queries the wallet for relevant transactions. The
|
||||
query set may be specified using a block range, inclusive, with the heights or
|
||||
hashes of the minimum and maximum block. Transaction results are grouped
|
||||
grouped by the block they are mined in, or grouped together with other unmined
|
||||
transactions.
|
||||
|
||||
**Request:** `GetTransactionsRequest`
|
||||
|
||||
- `bytes starting_block_hash`: The block hash of the block to begin including
|
||||
transactions from. If this field is set to the default, the
|
||||
`starting_block_height` field is used instead. If changed, the byte array
|
||||
must have length 32 and `starting_block_height` must be zero.
|
||||
|
||||
- `sint32 starting_block_height`: The block height to begin including
|
||||
transactions from. If this field is non-zero, `starting_block_hash` must be
|
||||
set to its default value to avoid ambiguity. If positive, the field is
|
||||
interpreted as a block height. If negative, the height is subtracted from the
|
||||
block wallet considers itself in sync with.
|
||||
|
||||
- `bytes ending_block_hash`: The block hash of the last block to include
|
||||
transactions from. If this default is set to the default, the
|
||||
`ending_block_height` field is used instead. If changed, the byte array must
|
||||
have length 32 and `ending_block_height` must be zero.
|
||||
|
||||
- `int32 ending_block_height`: The block height of the last block to include
|
||||
transactions from. If non-zero, the `ending_block_hash` field must be set to
|
||||
its default value to avoid ambiguity. If both this field and
|
||||
`ending_block_hash` are set to their default values, no upper block limit is
|
||||
used and transactions through the best block and all unmined transactions are
|
||||
included.
|
||||
|
||||
**Response:** `GetTransactionsResponse`
|
||||
|
||||
- `repeated BlockDetails mined_transactions`: All mined transactions, organized
|
||||
by blocks in the order they appear in the blockchain.
|
||||
|
||||
The `BlockDetails` message is used by other methods and is documented
|
||||
[here](#blockdetails).
|
||||
|
||||
- `repeated TransactionDetails unmined_transactions`: All unmined transactions.
|
||||
The ordering is unspecified.
|
||||
|
||||
The `TransactionDetails` message is used by other methods and is documented
|
||||
[here](#transactiondetails).
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `InvalidArgument`: A non-default block hash field did not have the correct length.
|
||||
|
||||
- `Aborted`: The wallet database is closed.
|
||||
|
||||
- `NotFound`: A block, specified by its height or hash, is unknown to the
|
||||
wallet.
|
||||
|
||||
**Stability:** Unstable
|
||||
|
||||
- There is currently no way to get only unmined transactions due to the way
|
||||
the block range is specified.
|
||||
|
||||
- It would be useful to ignore the block range and return some minimum number of
|
||||
the most recent transaction, but it is unclear if that should be added to this
|
||||
method's request object, or to make a new method.
|
||||
|
||||
- A specified ordering (such as dependency order) for all returned unmined
|
||||
transactions would be useful.
|
||||
|
||||
___
|
||||
|
||||
#### `ChangePassphrase`
|
||||
|
||||
The `ChangePassphrase` method requests a change to either the public (outer) or
|
||||
private (inner) encryption passphrases.
|
||||
|
||||
**Request:** `ChangePassphraseRequest`
|
||||
|
||||
- `Key key`: The key being changed.
|
||||
|
||||
**Nested enum:** `Key`
|
||||
|
||||
- `PRIVATE`: The request specifies to change the private (inner) encryption
|
||||
passphrase.
|
||||
|
||||
- `PUBLIC`: The request specifies to change the public (outer) encryption
|
||||
passphrase.
|
||||
|
||||
- `bytes old_passphrase`: The current passphrase for the encryption key. This
|
||||
is the value being modified. If the public passphrase is being modified and
|
||||
this value is the default value, an insecure default is used instead.
|
||||
|
||||
- `bytes new_passphrase`: The replacement passphrase. This field may only have
|
||||
zero length if the public passphrase is being changed, in which case an
|
||||
insecure default will be used instead.
|
||||
|
||||
**Response:** `ChangePassphraseResponse`
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `InvalidArgument`: A zero length passphrase was specified when changing the
|
||||
private passphrase, or the old passphrase was incorrect.
|
||||
|
||||
- `Aborted`: The wallet database is closed.
|
||||
|
||||
**Stability:** Unstable
|
||||
|
||||
___
|
||||
|
||||
#### `RenameAccount`
|
||||
|
||||
The `RenameAccount` method requests a change to an account's name property.
|
||||
|
||||
**Request:** `RenameAccountRequest`
|
||||
|
||||
- `uint32 account_number`: The number of the account being modified.
|
||||
|
||||
- `string new_name`: The new name for the account.
|
||||
|
||||
**Response:** `RenameAccountResponse`
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `Aborted`: The wallet database is closed.
|
||||
|
||||
- `InvalidArgument`: The new account name is a reserved name.
|
||||
|
||||
- `NotFound`: The account does not exist.
|
||||
|
||||
- `AlreadyExists`: An account by the same name already exists.
|
||||
|
||||
**Stability:** Unstable: There should be a way to specify a starting block or
|
||||
time to begin the rescan at. Additionally, since the client is expected to be
|
||||
able to do asynchronous RPC, it may be useful for the response to block on the
|
||||
rescan finishing before returning.
|
||||
|
||||
___
|
||||
|
||||
#### `NextAccount`
|
||||
|
||||
The `NextAccount` method generates the next BIP0044 account for the wallet.
|
||||
|
||||
**Request:** `NextAccountRequest`
|
||||
|
||||
- `bytes passphrase`: The private passphrase required to derive the next
|
||||
account's key.
|
||||
|
||||
- `string account_name`: The name to give the new account.
|
||||
|
||||
**Response:** `NextAccountResponse`
|
||||
|
||||
- `uint32 account_number`: The number of the newly-created account.
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `Aborted`: The wallet database is closed.
|
||||
|
||||
- `InvalidArgument`: The private passphrase is incorrect.
|
||||
|
||||
- `InvalidArgument`: The new account name is a reserved name.
|
||||
|
||||
- `AlreadyExists`: An account by the same name already exists.
|
||||
|
||||
**Stability:** Unstable
|
||||
|
||||
___
|
||||
|
||||
#### `NextAddress`
|
||||
|
||||
The `NextAddress` method generates the next deterministic address for the
|
||||
wallet.
|
||||
|
||||
**Request:** `NextAddressRequest`
|
||||
|
||||
- `uint32 account`: The number of the account to derive the next address for.
|
||||
|
||||
- `Kind kind`: The type of address to generate.
|
||||
|
||||
**Nested enum:** `Kind`
|
||||
|
||||
- `BIP0044_EXTERNAL`: The request specifies to generate the next address for
|
||||
the account's BIP0044 external key chain.
|
||||
|
||||
- `BIP0044_INTERNAL`: The request specifies to generate the next address for
|
||||
the account's BIP0044 internal key chain.
|
||||
|
||||
**Response:** `NextAddressResponse`
|
||||
|
||||
- `string address`: The payment address string.
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `Aborted`: The wallet database is closed.
|
||||
|
||||
- `NotFound`: The account does not exist.
|
||||
|
||||
**Stability:** Unstable
|
||||
|
||||
___
|
||||
|
||||
#### `ImportPrivateKey`
|
||||
|
||||
The `ImportPrivateKey` method imports a private key in Wallet Import Format
|
||||
(WIF) encoding to a wallet account. A rescan may optionally be started to
|
||||
search for transactions involving the private key's associated payment address.
|
||||
|
||||
**Request:** `ImportPrivateKeyRequest`
|
||||
|
||||
- `bytes passphrase`: The wallet's private passphrase.
|
||||
|
||||
- `uint32 account`: The account number to associate the imported key with.
|
||||
|
||||
- `string private_key_wif`: The private key, encoded using WIF.
|
||||
|
||||
- `bool rescan`: Whether or not to perform a blockchain rescan for the imported
|
||||
key.
|
||||
|
||||
**Response:** `ImportPrivateKeyResponse`
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `InvalidArgument`: The private key WIF string is not a valid WIF encoding.
|
||||
|
||||
- `Aborted`: The wallet database is closed.
|
||||
|
||||
- `InvalidArgument`: The private passphrase is incorrect.
|
||||
|
||||
- `NotFound`: The account does not exist.
|
||||
|
||||
**Stability:** Unstable
|
||||
|
||||
___
|
||||
|
||||
#### `FundTransaction`
|
||||
|
||||
The `FundTransaction` method queries the wallet for unspent transaction outputs
|
||||
controlled by some account. Results may be refined by setting a target output
|
||||
amount and limiting the required confirmations. The selection algorithm is
|
||||
unspecified.
|
||||
|
||||
Output results are always created even if a minimum target output amount could
|
||||
not be reached. This allows this method to behave similar to the `Balance`
|
||||
method while also including the outputs that make up that balance.
|
||||
|
||||
Change outputs can optionally be returned by this method as well. This can
|
||||
provide the caller with everything necessary to construct an unsigned
|
||||
transaction paying to already known addresses or scripts.
|
||||
|
||||
**Request:** `FundTransactionRequest`
|
||||
|
||||
- `uint32 account`: Account number containing the keys controlling the output
|
||||
set to query.
|
||||
|
||||
- `int64 target_amount`: If positive, the service may limit output results to
|
||||
those that sum to at least this amount (counted in Satoshis). If zero, all
|
||||
outputs not excluded by other arguments are returned. This may not be
|
||||
negative.
|
||||
|
||||
- `int32 required_confirmations`: The minimum number of block confirmations
|
||||
needed to consider including an output in the return set. This may not be
|
||||
negative.
|
||||
|
||||
- `bool include_immature_coinbases`: If true, immature coinbase outputs will
|
||||
also be included.
|
||||
|
||||
- `bool include_change_script`: If true, a change script is included in the
|
||||
response object.
|
||||
|
||||
**Response:** `FundTransactionResponse`
|
||||
|
||||
- `repeated PreviousOutput selected_outputs`: The output set returned as a list
|
||||
of `PreviousOutput` nested message objects.
|
||||
|
||||
**Nested message:** `PreviousOutput`
|
||||
|
||||
- `bytes transaction_hash`: The hash of the transaction this output originates
|
||||
from.
|
||||
|
||||
- `uint32 output_index`: The output index of the transaction this output
|
||||
originates from.
|
||||
|
||||
- `int64 amount`: The output value (counted in Satoshis) of the unspent
|
||||
transaction output.
|
||||
|
||||
- `bytes pk_script`: The output script of the unspent transaction output.
|
||||
|
||||
- `int64 receive_time`: The earliest Unix time the wallet became aware of the
|
||||
transaction containing this output.
|
||||
|
||||
- `bool from_coinbase`: Whether the output is a coinbase output.
|
||||
|
||||
- `int64 total_amount`: The sum of all returned output amounts. This may be
|
||||
less than a positive target amount if there were not enough eligible outputs
|
||||
available.
|
||||
|
||||
- `bytes change_pk_script`: A transaction output script used to pay the
|
||||
remaining amount to a newly-generated change address for the account. This is
|
||||
null if `include_change_script` was false or the target amount was not
|
||||
exceeded.
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `InvalidArgument`: The target amount is negative.
|
||||
|
||||
- `InvalidArgument`: The required confirmations is negative.
|
||||
|
||||
- `Aborted`: The wallet database is closed.
|
||||
|
||||
- `NotFound`: The account does not exist.
|
||||
|
||||
**Stability:** Unstable
|
||||
|
||||
___
|
||||
|
||||
#### `SignTransaction`
|
||||
|
||||
The `SignTransaction` method adds transaction input signatures to a serialized
|
||||
transaction using a wallet private keys.
|
||||
|
||||
**Request:** `SignTransactionRequest`
|
||||
|
||||
- `bytes passphrase`: The wallet's private passphrase.
|
||||
|
||||
- `bytes serialized_transaction`: The transaction to add input signatures to.
|
||||
|
||||
- `repeated uint32 input_indexes`: The input indexes that signature scripts must
|
||||
be created for. If there are no indexes, input scripts are created for every
|
||||
input that is missing an input script.
|
||||
|
||||
**Response:** `SignTransactionResponse`
|
||||
|
||||
- `bytes transaction`: The serialized transaction with added input scripts.
|
||||
|
||||
- `repeated uint32 unsigned_input_indexes`: The indexes of every input that an
|
||||
input script could not be created for.
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `InvalidArgument`: The serialized transaction can not be decoded.
|
||||
|
||||
- `Aborted`: The wallet database is closed.
|
||||
|
||||
- `InvalidArgument`: The private passphrase is incorrect.
|
||||
|
||||
**Stability:** Unstable: It is unclear if the request should include an account,
|
||||
and only secrets of that account are used when creating input scripts. It's
|
||||
also missing options similar to Core's signrawtransaction, such as the sighash
|
||||
flags and additional keys.
|
||||
|
||||
___
|
||||
|
||||
#### `PublishTransaction`
|
||||
|
||||
The `PublishTransaction` method publishes a signed, serialized transaction to
|
||||
the Bitcoin network. If the transaction spends any of the wallet's unspent
|
||||
outputs or creates a new output controlled by the wallet, it is saved by the
|
||||
wallet and republished later if it or a double spend are not mined.
|
||||
|
||||
**Request:** `PublishTransactionRequest`
|
||||
|
||||
- `bytes signed_transaction`: The signed transaction to publish.
|
||||
|
||||
**Response:** `PublishTransactionResponse`
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `InvalidArgument`: The serialized transaction can not be decoded or is missing
|
||||
input scripts.
|
||||
|
||||
- `Aborted`: The wallet database is closed.
|
||||
|
||||
**Stability:** Unstable
|
||||
|
||||
___
|
||||
|
||||
#### `TransactionNotifications`
|
||||
|
||||
The `TransactionNotifications` method returns a stream of notifications
|
||||
regarding changes to the blockchain and transactions relevant to the wallet.
|
||||
|
||||
**Request:** `TransactionNotificationsRequest`
|
||||
|
||||
**Response:** `stream TransactionNotificationsResponse`
|
||||
|
||||
- `repeated BlockDetails attached_blocks`: A list of blocks attached to the main
|
||||
chain, sorted by increasing height. All newly mined transactions are included
|
||||
in these messages, in the message corresponding to the block that contains
|
||||
them. If this field has zero length, the notification is due to an unmined
|
||||
transaction being added to the wallet.
|
||||
|
||||
The `BlockDetails` message is used by other methods and is documented
|
||||
[here](#blockdetails).
|
||||
|
||||
- `repeated bytes detached_blocks`: The hashes of every block that was
|
||||
reorganized out of the main chain. These are sorted by heights in decreasing
|
||||
order (newest blocks first).
|
||||
|
||||
- `repeated TransactionDetails unmined_transactions`: All newly added unmined
|
||||
transactions. When relevant transactions are reorganized out and not included
|
||||
in (or double-spent by) the new chain, they are included here.
|
||||
|
||||
The `TransactionDetails` message is used by other methods and is documented
|
||||
[here](#transactiondetails).
|
||||
|
||||
- `repeated bytes unmined_transaction_hashes`: The hashes of every
|
||||
currently-unmined transaction. This differs from the `unmined_transactions`
|
||||
field by including every unmined transaction, rather than those newly added to
|
||||
the unmined set.
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `Aborted`: The wallet database is closed.
|
||||
|
||||
**Stability:** Unstable: This method could use a better name.
|
||||
|
||||
___
|
||||
|
||||
#### `SpentnessNotifications`
|
||||
|
||||
The `SpentnessNotifications` method returns a stream of notifications regarding
|
||||
the spending of unspent outputs and/or the discovery of new unspent outputs for
|
||||
an account.
|
||||
|
||||
**Request:** `SpentnessNotificationsRequest`
|
||||
|
||||
- `uint32 account`: The account to create notifications for.
|
||||
|
||||
- `bool no_notify_unspent`: If true, do not send any notifications for
|
||||
newly-discovered unspent outputs controlled by the account.
|
||||
|
||||
- `bool no_notify_spent`: If true, do not send any notifications for newly-spent
|
||||
transactions controlled by the account.
|
||||
|
||||
**Response:** `stream SpentnessNotificationsResponse`
|
||||
|
||||
- `bytes transaction_hash`: The hash of the serialized transaction containing
|
||||
the output being reported.
|
||||
|
||||
- `uint32 output_index`: The output index of the output being reported.
|
||||
|
||||
- `Spender spender`: If null, the output is a newly-discovered unspent output.
|
||||
If not null, the message records the transaction input that spends the
|
||||
previously-unspent output.
|
||||
|
||||
**Nested message:** `Spender`
|
||||
|
||||
- `bytes transaction_hash`: The hash of the serialized transaction that spends
|
||||
the reported output.
|
||||
|
||||
- `uint32 input_index`: The index of the input that spends the reported
|
||||
output.
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `InvalidArgument`: The `no_notify_unspent` and `no_notify_spent` request
|
||||
fields are both true.
|
||||
|
||||
- `Aborted`: The wallet database is closed.
|
||||
|
||||
**Stability:** Unstable
|
||||
|
||||
___
|
||||
|
||||
#### `AccountNotifications`
|
||||
|
||||
The `AccountNotifications` method returns a stream of notifications for account
|
||||
property changes, such as name and key counts.
|
||||
|
||||
**Request:** `AccountNotificationsRequest`
|
||||
|
||||
**Response:** `stream AccountNotificationsResponse`
|
||||
|
||||
- `uint32 account_number`: The BIP0044 account being reported.
|
||||
|
||||
- `string account_name`: The current account name.
|
||||
|
||||
- `uint32 external_key_count`: The current number of BIP0032 external keys
|
||||
derived for the account.
|
||||
|
||||
- `uint32 internal_key_count`: The current number of BIP0032 internal keys
|
||||
derived for the account.
|
||||
|
||||
- `uint32 imported_key_count`: The current number of private keys imported into
|
||||
the account.
|
||||
|
||||
**Expected errors:**
|
||||
|
||||
- `Aborted`: The wallet database is closed.
|
||||
|
||||
**Stability:** Unstable: This should probably share a message with the
|
||||
`Accounts` method.
|
||||
|
||||
___
|
||||
|
||||
### Shared messages
|
||||
|
||||
The following messages are used by multiple methods. To avoid unnecessary
|
||||
duplication, they are documented once here.
|
||||
|
||||
#### `BlockDetails`
|
||||
|
||||
The `BlockDetails` message is included in responses to report a block and the
|
||||
wallet's relevant transactions contained therein.
|
||||
|
||||
- `bytes hash`: The hash of the block being reported.
|
||||
|
||||
- `int32 height`: The height of the block being reported.
|
||||
|
||||
- `int64 timestamp`: The Unix time included in the block header.
|
||||
|
||||
- `repeated TransactionDetails transactions`: All transactions relevant to the
|
||||
wallet that are mined in this block. Transactions are sorted by their block
|
||||
index in increasing order.
|
||||
|
||||
The `TransactionDetails` message is used by other methods and is documented
|
||||
[here](#transactiondetails).
|
||||
|
||||
**Stability**: Unstable: This should probably include the block version.
|
||||
|
||||
___
|
||||
|
||||
#### `TransactionDetails`
|
||||
|
||||
The `TransactionDetails` message is included in responses to report transactions
|
||||
relevant to the wallet. The message includes details such as which previous
|
||||
wallet inputs are spent by this transaction, whether each output is controlled
|
||||
by the wallet or not, the total fee (if calculable), and the earlist time the
|
||||
transaction was seen.
|
||||
|
||||
- `bytes hash`: The hash of the serialized transaction.
|
||||
|
||||
- `bytes transaction`: The serialized transaction.
|
||||
|
||||
- `repeated Input debits`: Properties for every previously-unspent wallet output
|
||||
spent by this transaction.
|
||||
|
||||
**Nested message:** `Input`
|
||||
|
||||
- `uint32 index`: The transaction input index of the input being reported.
|
||||
|
||||
- `uint32 previous_account`: The account that controlled the now-spent output.
|
||||
|
||||
- `int64 previous_amount`: The previous output value.
|
||||
|
||||
- `repeated Output credits`: Properties for every output controlled by the wallet.
|
||||
|
||||
**Nested message:** `Output`
|
||||
|
||||
- `uint32 index`: The transaction output index of the output being reported.
|
||||
|
||||
- `uint32 account`: The account number of the controlled output.
|
||||
|
||||
- `bool internal`: Whether the output pays to an address derived from the
|
||||
account's internal key series. This often means the output is a change
|
||||
output.
|
||||
|
||||
- `int64 fee`: The transaction fee, if calculable. The fee is only calculable
|
||||
when every previous output spent by this transaction is also recorded by
|
||||
wallet. Otherwise, this field is zero.
|
||||
|
||||
- `int64 timestamp`: The Unix time of the earliest time this transaction was
|
||||
seen.
|
||||
|
||||
**Stability**: Unstable: Since the caller is expected to decode the serialized
|
||||
transaction, and would have access to every output script, the output
|
||||
properties could be changed to only include outputs controlled by the wallet.
|
|
@ -1,439 +0,0 @@
|
|||
# Client usage
|
||||
|
||||
Clients use RPC to interact with the wallet. A client may be implemented in any
|
||||
language directly supported by [gRPC](http://www.grpc.io/), languages capable of
|
||||
performing [FFI](https://en.wikipedia.org/wiki/Foreign_function_interface) with
|
||||
these, and languages that share a common runtime (e.g. Scala, Kotlin, and Ceylon
|
||||
for the JVM, F# for the CLR, etc.). Exact instructions differ slightly
|
||||
depending on the language being used, but the general process is the same for
|
||||
each. In short summary, to call RPC server methods, a client must:
|
||||
|
||||
1. Generate client bindings specific for the [wallet RPC server API](./api.md)
|
||||
2. Import or include the gRPC dependency
|
||||
3. (Optional) Wrap the client bindings with application-specific types
|
||||
4. Open a gRPC channel using the wallet server's self-signed TLS certificate
|
||||
|
||||
The only exception to these steps is if the client is being written in Go. In
|
||||
that case, the first step may be omitted by importing the bindings from
|
||||
lbcwallet itself.
|
||||
|
||||
The rest of this document provides short examples of how to quickly get started
|
||||
by implementing a basic client that fetches the balance of the default account
|
||||
(account 0) from a testnet3 wallet listening on `localhost:19244` in several
|
||||
different languages:
|
||||
|
||||
- [Client usage](#client-usage)
|
||||
- [Go](#go)
|
||||
- [C++](#c)
|
||||
- [C#](#c-1)
|
||||
- [Node.js](#nodejs)
|
||||
- [Python](#python)
|
||||
|
||||
Unless otherwise stated under the language example, it is assumed that
|
||||
gRPC is already already installed. The gRPC installation procedure
|
||||
can vary greatly depending on the operating system being used and
|
||||
whether a gRPC source install is required. Follow the [gRPC install
|
||||
instructions](https://github.com/grpc/grpc/blob/master/INSTALL) if
|
||||
gRPC is not already installed. A full gRPC install also includes
|
||||
[Protocol Buffers](https://github.com/google/protobuf) (compiled with
|
||||
support for the proto3 language version), which contains the protoc
|
||||
tool and language plugins used to compile this project's `.proto`
|
||||
files to language-specific bindings.
|
||||
|
||||
## Go
|
||||
|
||||
The native gRPC library (gRPC Core) is not required for Go clients (a
|
||||
pure Go implementation is used instead) and no additional setup is
|
||||
required to generate Go bindings.
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
pb "github.com/lbryio/lbcwallet/rpc/walletrpc"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
)
|
||||
|
||||
var certificateFile = filepath.Join(btcutil.AppDataDir("lbcwallet", false), "rpc.cert")
|
||||
|
||||
func main() {
|
||||
creds, err := credentials.NewClientTLSFromFile(certificateFile, "localhost")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
conn, err := grpc.Dial("localhost:19244", grpc.WithTransportCredentials(creds))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
c := pb.NewWalletServiceClient(conn)
|
||||
|
||||
balanceRequest := &pb.BalanceRequest{
|
||||
AccountNumber: 0,
|
||||
RequiredConfirmations: 1,
|
||||
}
|
||||
balanceResponse, err := c.Balance(context.Background(), balanceRequest)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Spendable balance: ", btcutil.Amount(balanceResponse.Spendable))
|
||||
}
|
||||
```
|
||||
|
||||
<a name="cpp"/>
|
||||
## C++
|
||||
|
||||
**Note:** Protocol Buffers and gRPC require at least C++11. The example client
|
||||
is written using C++14.
|
||||
|
||||
**Note:** The following instructions assume the client is being written on a
|
||||
Unix-like platform (with instructions using the `sh` shell and Unix-isms in the
|
||||
example source code) with a source gRPC install in `/usr/local`.
|
||||
|
||||
First, generate the C++ language bindings by compiling the `.proto`:
|
||||
|
||||
```bash
|
||||
$ protoc -I/path/to/lbcwallet/rpc --cpp_out=. --grpc_out=. \
|
||||
--plugin=protoc-gen-grpc=$(which grpc_cpp_plugin) \
|
||||
/path/to/lbcwallet/rpc/api.proto
|
||||
```
|
||||
|
||||
Once the `.proto` file has been compiled, the example client can be completed.
|
||||
Note that the following code uses synchronous calls which will block the main
|
||||
thread on all gRPC IO.
|
||||
|
||||
```C++
|
||||
// example.cc
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <grpc++/grpc++.h>
|
||||
|
||||
#include "api.grpc.pb.h"
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
struct NoHomeDirectoryException : std::exception {
|
||||
char const* what() const noexcept override {
|
||||
return "Failed to lookup home directory";
|
||||
}
|
||||
};
|
||||
|
||||
auto read_file(std::string const& file_path) -> std::string {
|
||||
std::ifstream in{file_path};
|
||||
std::stringstream ss{};
|
||||
ss << in.rdbuf();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
auto main() -> int {
|
||||
// Before the gRPC native library (gRPC Core) is lazily loaded and
|
||||
// initialized, an environment variable must be set so BoringSSL is
|
||||
// configured to use ECDSA TLS certificates (required by lbcwallet).
|
||||
setenv("GRPC_SSL_CIPHER_SUITES", "HIGH+ECDSA", 1);
|
||||
|
||||
// Note: This path is operating system-dependent. This can be created
|
||||
// portably using boost::filesystem or the experimental filesystem class
|
||||
// expected to ship in C++17.
|
||||
auto wallet_tls_cert_file = []{
|
||||
auto pw = getpwuid(getuid());
|
||||
if (pw == nullptr || pw->pw_dir == nullptr) {
|
||||
throw NoHomeDirectoryException{};
|
||||
}
|
||||
return pw->pw_dir + "/.lbcwallet/rpc.cert"s;
|
||||
}();
|
||||
|
||||
grpc::SslCredentialsOptions cred_options{
|
||||
.pem_root_certs = read_file(wallet_tls_cert_file),
|
||||
};
|
||||
auto creds = grpc::SslCredentials(cred_options);
|
||||
auto channel = grpc::CreateChannel("localhost:19244", creds);
|
||||
auto stub = walletrpc::WalletService::NewStub(channel);
|
||||
|
||||
grpc::ClientContext context{};
|
||||
|
||||
walletrpc::BalanceRequest request{};
|
||||
request.set_account_number(0);
|
||||
request.set_required_confirmations(1);
|
||||
|
||||
walletrpc::BalanceResponse response{};
|
||||
auto status = stub->Balance(&context, request, &response);
|
||||
if (!status.ok()) {
|
||||
std::cout << status.error_message() << std::endl;
|
||||
} else {
|
||||
std::cout << "Spendable balance: " << response.spendable() << " Satoshis" << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The example can then be built with the following commands:
|
||||
|
||||
```bash
|
||||
$ c++ -std=c++14 -I/usr/local/include -pthread -c -o api.pb.o api.pb.cc
|
||||
$ c++ -std=c++14 -I/usr/local/include -pthread -c -o api.grpc.pb.o api.grpc.pb.cc
|
||||
$ c++ -std=c++14 -I/usr/local/include -pthread -c -o example.o example.cc
|
||||
$ c++ *.o -L/usr/local/lib -lgrpc++ -lgrpc -lgpr -lprotobuf -lpthread -ldl -o example
|
||||
```
|
||||
|
||||
<a name="csharp"/>
|
||||
## C#
|
||||
|
||||
The quickest way of generating client bindings in a Windows .NET environment is
|
||||
by using the protoc binary included in the gRPC NuGet package. From the NuGet
|
||||
package manager PowerShell console, this can be performed with:
|
||||
|
||||
```
|
||||
PM> Install-Package Grpc
|
||||
```
|
||||
|
||||
The protoc and C# plugin binaries can then be found in the packages directory.
|
||||
For example, `.\packages\Google.Protobuf.x.x.x\tools\protoc.exe` and
|
||||
`.\packages\Grpc.Tools.x.x.x\tools\grpc_csharp_plugin.exe`.
|
||||
|
||||
When writing a client on other platforms (e.g. Mono on OS X), or when doing a
|
||||
full gRPC source install on Windows, protoc and the C# plugin must be installed
|
||||
by other means. Consult the [official documentation](https://github.com/grpc/grpc/blob/master/src/csharp/README.md)
|
||||
for these steps.
|
||||
|
||||
Once protoc and the C# plugin have been obtained, client bindings can be
|
||||
generated. The following command generates the files `Api.cs` and `ApiGrpc.cs`
|
||||
in the `Example` project directory using the `Walletrpc` namespace:
|
||||
|
||||
```PowerShell
|
||||
PS> & protoc.exe -I \Path\To\lbcwallet\rpc --csharp_out=Example --grpc_out=Example `
|
||||
--plugin=protoc-gen-grpc=\Path\To\grpc_csharp_plugin.exe `
|
||||
\Path\To\lbcwallet\rpc\api.proto
|
||||
```
|
||||
|
||||
Once references have been added to the project for the `Google.Protobuf` and
|
||||
`Grpc.Core` assemblies, the example client can be implemented.
|
||||
|
||||
```C#
|
||||
using Grpc.Core;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Walletrpc;
|
||||
|
||||
namespace Example
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
ExampleAsync().Wait();
|
||||
}
|
||||
|
||||
static async Task ExampleAsync()
|
||||
{
|
||||
// Before the gRPC native library (gRPC Core) is lazily loaded and initialized,
|
||||
// an environment variable must be set so BoringSSL is configured to use ECDSA TLS
|
||||
// certificates (required by lbcwallet).
|
||||
Environment.SetEnvironmentVariable("GRPC_SSL_CIPHER_SUITES", "HIGH+ECDSA");
|
||||
|
||||
var walletAppData = Portability.LocalAppData(Environment.OSVersion.Platform, "lbcwallet");
|
||||
var walletTlsCertFile = Path.Combine(walletAppData, "rpc.cert");
|
||||
var cert = await FileUtils.ReadFileAsync(walletTlsCertFile);
|
||||
var channel = new Channel("localhost:19244", new SslCredentials(cert));
|
||||
try
|
||||
{
|
||||
var c = WalletService.NewClient(channel);
|
||||
var balanceRequest = new BalanceRequest
|
||||
{
|
||||
AccountNumber = 0,
|
||||
RequiredConfirmations = 1,
|
||||
};
|
||||
var balanceResponse = await c.BalanceAsync(balanceRequest);
|
||||
Console.WriteLine($"Spendable balance: {balanceResponse.Spendable} Satoshis");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await channel.ShutdownAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class FileUtils
|
||||
{
|
||||
public static async Task<string> ReadFileAsync(string filePath)
|
||||
{
|
||||
using (var r = new StreamReader(filePath, Encoding.UTF8))
|
||||
{
|
||||
return await r.ReadToEndAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class Portability
|
||||
{
|
||||
public static string LocalAppData(PlatformID platform, string processName)
|
||||
{
|
||||
if (processName == null)
|
||||
throw new ArgumentNullException(nameof(processName));
|
||||
if (processName.Length == 0)
|
||||
throw new ArgumentException(nameof(processName) + " may not have zero length");
|
||||
|
||||
switch (platform)
|
||||
{
|
||||
case PlatformID.Win32NT:
|
||||
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
ToUpper(processName));
|
||||
case PlatformID.MacOSX:
|
||||
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal),
|
||||
"Library", "Application Support", ToUpper(processName));
|
||||
case PlatformID.Unix:
|
||||
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal),
|
||||
ToDotLower(processName));
|
||||
default:
|
||||
throw new PlatformNotSupportedException($"PlatformID={platform}");
|
||||
}
|
||||
}
|
||||
|
||||
private static string ToUpper(string value)
|
||||
{
|
||||
var firstChar = value[0];
|
||||
if (char.IsUpper(firstChar))
|
||||
return value;
|
||||
else
|
||||
return char.ToUpper(firstChar) + value.Substring(1);
|
||||
}
|
||||
|
||||
private static string ToDotLower(string value)
|
||||
{
|
||||
var firstChar = value[0];
|
||||
return "." + char.ToLower(firstChar) + value.Substring(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Node.js
|
||||
|
||||
First, install gRPC (either by building the latest source release, or
|
||||
by installing a gRPC binary development package through your operating
|
||||
system's package manager). This is required to install the npm module
|
||||
as it wraps the native C library (gRPC Core) with C++ bindings.
|
||||
Installing the [grpc module](https://www.npmjs.com/package/grpc) to
|
||||
your project can then be done by executing:
|
||||
|
||||
```
|
||||
npm install grpc
|
||||
```
|
||||
|
||||
A Node.js client does not require generating JavaScript stub files for
|
||||
the wallet's API from the `.proto`. Instead, a call to `grpc.load`
|
||||
with the `.proto` file path dynamically loads the Protobuf descriptor
|
||||
and generates bindings for each service. Either copy the `.proto` to
|
||||
the client project directory, or reference the file from the
|
||||
`lbcwallet` project directory.
|
||||
|
||||
```JavaScript
|
||||
// Before the gRPC native library (gRPC Core) is lazily loaded and
|
||||
// initialized, an environment variable must be set so BoringSSL is
|
||||
// configured to use ECDSA TLS certificates (required by lbcwallet).
|
||||
process.env['GRPC_SSL_CIPHER_SUITES'] = 'HIGH+ECDSA';
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var os = require('os');
|
||||
var grpc = require('grpc');
|
||||
var protoDescriptor = grpc.load('./api.proto');
|
||||
var walletrpc = protoDescriptor.walletrpc;
|
||||
|
||||
var certPath = path.join(process.env.HOME, '.lbcwallet', 'rpc.cert');
|
||||
if (os.platform == 'win32') {
|
||||
certPath = path.join(process.env.LOCALAPPDATA, 'lbcwallet', 'rpc.cert');
|
||||
} else if (os.platform == 'darwin') {
|
||||
certPath = path.join(process.env.HOME, 'Library', 'Application Support',
|
||||
'lbcwallet', 'rpc.cert');
|
||||
}
|
||||
|
||||
var cert = fs.readFileSync(certPath);
|
||||
var creds = grpc.credentials.createSsl(cert);
|
||||
var client = new walletrpc.WalletService('localhost:19244', creds);
|
||||
|
||||
var request = {
|
||||
account_number: 0,
|
||||
required_confirmations: 1
|
||||
};
|
||||
client.balance(request, function(err, response) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
console.log('Spendable balance:', response.spendable, 'Satoshis');
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Python
|
||||
|
||||
**Note:** gRPC requires Python 2.7.
|
||||
|
||||
After installing gRPC Core and Python development headers, `pip`
|
||||
should be used to install the `grpc` module and its dependencies.
|
||||
Full instructions for this procedure can be found
|
||||
[here](https://github.com/grpc/grpc/blob/master/src/python/README.md).
|
||||
|
||||
Generate Python stubs from the `.proto`:
|
||||
|
||||
```bash
|
||||
$ protoc -I /path/to/lbryio/lbcwallet/rpc --python_out=. --grpc_out=. \
|
||||
--plugin=protoc-gen-grpc=$(which grpc_python_plugin) \
|
||||
/path/to/lbcwallet/rpc/api.proto
|
||||
```
|
||||
|
||||
Implement the client:
|
||||
|
||||
```Python
|
||||
import os
|
||||
import platform
|
||||
from grpc.beta import implementations
|
||||
|
||||
import api_pb2 as walletrpc
|
||||
|
||||
timeout = 1 # seconds
|
||||
|
||||
def main():
|
||||
# Before the gRPC native library (gRPC Core) is lazily loaded and
|
||||
# initialized, an environment variable must be set so BoringSSL is
|
||||
# configured to use ECDSA TLS certificates (required by lbcwallet).
|
||||
os.environ['GRPC_SSL_CIPHER_SUITES'] = 'HIGH+ECDSA'
|
||||
|
||||
cert_file_path = os.path.join(os.environ['HOME'], '.lbcwallet', 'rpc.cert')
|
||||
if platform.system() == 'Windows':
|
||||
cert_file_path = os.path.join(os.environ['LOCALAPPDATA'], "lbcwallet", "rpc.cert")
|
||||
elif platform.system() == 'Darwin':
|
||||
cert_file_path = os.path.join(os.environ['HOME'], 'Library', 'Application Support',
|
||||
'lbcwallet', 'rpc.cert')
|
||||
|
||||
with open(cert_file_path, 'r') as f:
|
||||
cert = f.read()
|
||||
creds = implementations.ssl_client_credentials(cert, None, None)
|
||||
channel = implementations.secure_channel('localhost', 19244, creds)
|
||||
stub = walletrpc.beta_create_WalletService_stub(channel)
|
||||
|
||||
request = walletrpc.BalanceRequest(account_number = 0, required_confirmations = 1)
|
||||
response = stub.Balance(request, timeout)
|
||||
print 'Spendable balance: %d Satoshis' % response.spendable
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
|
@ -1,94 +0,0 @@
|
|||
# Making API Changes
|
||||
|
||||
This document describes the process of how lbcwallet developers must make
|
||||
changes to the RPC API and server. Due to the use of gRPC and Protocol Buffers
|
||||
for the RPC implementation, changes to this API require extra dependencies and
|
||||
steps before changes to the server can be implemented.
|
||||
|
||||
## Requirements
|
||||
|
||||
- The Protocol Buffer compiler `protoc` installed with support for the `proto3`
|
||||
language
|
||||
|
||||
The `protoc` tool is part of the Protocol Buffers project. This can be
|
||||
installed [from source](https://github.com/google/protobuf/blob/master/INSTALL.txt),
|
||||
from an [official binary release](https://github.com/google/protobuf/releases),
|
||||
or through an operating system's package manager.
|
||||
|
||||
- The gRPC `protoc` plugin for Go
|
||||
|
||||
This plugin is written in Go and can be installed using `go get`:
|
||||
|
||||
```
|
||||
go get github.com/golang/protobuf/protoc-gen-go
|
||||
```
|
||||
|
||||
- Knowledge of Protocol Buffers version 3 (proto3)
|
||||
|
||||
Note that a full installation of gRPC Core is not required, and only the
|
||||
`protoc` compiler and Go plugins are necessary. This is due to the project
|
||||
using a pure Go gRPC implementation instead of wrapping the C library from gRPC
|
||||
Core.
|
||||
|
||||
## Step 1: Modify the `.proto`
|
||||
|
||||
Once the developer dependencies have been met, changes can be made to the API by
|
||||
modifying the Protocol Buffers descriptor file [`api.proto`](../api.proto).
|
||||
|
||||
The API is versioned according to the rules of [Semantic Versioning
|
||||
2.0](http://semver.org/). After any changes, bump the API version in the [API
|
||||
specification](./api.md) and add the changes to the spec.
|
||||
|
||||
Unless backwards compatibility is broken (and the version is bumped to represent
|
||||
this change), message fields must never be removed or changed, and new fields
|
||||
must always be appended.
|
||||
|
||||
It is forbidden to use the `required` attribute on a message field as this can
|
||||
cause errors during parsing when the new API is used by an older client.
|
||||
Instead, the (implicit) optional attribute is used, and the server
|
||||
implementation must return an appropriate error if the new request field is not
|
||||
set to a valid value.
|
||||
|
||||
## Step 2: Compile the `.proto`
|
||||
|
||||
Once changes to the descriptor file and API specification have been made, the
|
||||
`protoc` compiler must be used to compile the descriptor into a Go package.
|
||||
This code contains interfaces (stubs) for each service (to be implemented by the
|
||||
wallet) and message types used for each RPC. This same code can also be
|
||||
imported by a Go client that then calls same interface methods to perform RPC
|
||||
with the wallet.
|
||||
|
||||
By committing the autogenerated package to the project repo, the `proto3`
|
||||
compiler and plugin are not needed by users installing the project by source or
|
||||
by other developers not making changes to the RPC API.
|
||||
|
||||
A `sh` shell script is included to compile the Protocol Buffers descriptor. It
|
||||
must be run from the `rpc` directory.
|
||||
|
||||
```bash
|
||||
$ sh regen.sh
|
||||
```
|
||||
|
||||
If a `sh` shell is unavailable, the command can be run manually instead (again
|
||||
from the `rpc` directory).
|
||||
|
||||
```
|
||||
protoc -I. api.proto --go_out=plugins=grpc:walletrpc
|
||||
```
|
||||
|
||||
TODO(jrick): This step could be simplified and be more portable by putting the
|
||||
commands in a Go source file and executing them with `go generate`. It should,
|
||||
however, only be run when API changes are performed (not with `go generate
|
||||
./...` in the project root) since not all developers are expected to have
|
||||
`protoc` installed.
|
||||
|
||||
## Step 3: Implement the API change in the RPC server
|
||||
|
||||
After the Go code for the API has been regenated, the necessary changes can be
|
||||
implemented in the [`rpcserver`](../rpcserver/) package.
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Protocol Buffers Language Guide (proto3)](https://developers.google.com/protocol-buffers/docs/proto3)
|
||||
- [Protocol Buffers Basics: Go](https://developers.google.com/protocol-buffers/docs/gotutorial)
|
||||
- [gRPC Basics: Go](http://www.grpc.io/docs/tutorials/basic/go.html)
|
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -506,9 +518,6 @@ func getAddressInfo(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
|||
// just break out now if there is an error.
|
||||
script, err := ma.Script()
|
||||
if err != nil {
|
||||
if waddrmgr.IsError(err, waddrmgr.ErrWatchingOnly) {
|
||||
result.IsWatchOnly = true
|
||||
}
|
||||
break
|
||||
}
|
||||
hexScript := hex.EncodeToString(script)
|
||||
|
@ -575,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
|
||||
|
@ -692,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
|
||||
}
|
||||
|
@ -708,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
|
||||
|
@ -725,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
|
||||
|
@ -790,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
|
||||
|
@ -798,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,
|
||||
|
@ -806,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
|
||||
|
@ -821,26 +832,35 @@ 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) {
|
||||
func lookupKeyScope(kind *string) (*waddrmgr.KeyScope, error) {
|
||||
if kind == nil {
|
||||
return waddrmgr.KeyScopeBIP0044, nil
|
||||
return nil, nil
|
||||
}
|
||||
switch strings.ToLower(*kind) {
|
||||
case "legacy", "p2pkh": // could add "default" but it might be confused with the 1st parameter
|
||||
return waddrmgr.KeyScopeBIP0044, nil
|
||||
case "p2sh-p2wpkh", "p2sh-p2wkh", "p2sh-segwit":
|
||||
return waddrmgr.KeyScopeBIP0049Plus, nil
|
||||
case "p2wpkh", "p2wkh", "bech32":
|
||||
return waddrmgr.KeyScopeBIP0084, nil
|
||||
case "*":
|
||||
return nil, nil
|
||||
case "legacy":
|
||||
return &waddrmgr.KeyScopeBIP0044, nil
|
||||
case "p2sh-segwit":
|
||||
return &waddrmgr.KeyScopeBIP0049, nil
|
||||
case "bech32":
|
||||
return &waddrmgr.KeyScopeBIP0084, nil
|
||||
}
|
||||
return waddrmgr.KeyScopeBIP0044, fmt.Errorf("unrecognized address type: %s", *kind)
|
||||
return &waddrmgr.KeyScopeBIP0044, fmt.Errorf("unrecognized address type: %s. Must be legacy, p2sh-segwit, or bech32", *kind)
|
||||
}
|
||||
|
||||
// getNewAddress handles a getnewaddress request by returning a new
|
||||
|
@ -856,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
|
||||
}
|
||||
|
@ -881,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
|
||||
}
|
||||
|
@ -903,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
|
||||
|
@ -975,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 {
|
||||
|
@ -1058,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
|
||||
}
|
||||
|
@ -1206,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
|
||||
|
@ -1226,46 +1286,73 @@ func listLockUnspent(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
|||
|
||||
// listReceivedByAccount handles a listreceivedbyaccount request by returning
|
||||
// a slice of objects, each one containing:
|
||||
// "account": the receiving account;
|
||||
// "amount": total amount received by the account;
|
||||
// "confirmations": number of confirmations of the most recent transaction.
|
||||
//
|
||||
// "account": the receiving account;
|
||||
// "amount": total amount received by the account;
|
||||
// "confirmations": number of confirmations of the most recent transaction.
|
||||
//
|
||||
// It takes two parameters:
|
||||
// "minconf": minimum number of confirmations to consider a transaction -
|
||||
// default: one;
|
||||
// "includeempty": whether or not to include addresses that have no transactions -
|
||||
// default: false.
|
||||
//
|
||||
// "minconf": minimum number of confirmations to consider a transaction -
|
||||
// default: one;
|
||||
// "includeempty": whether or not to include addresses that have no transactions -
|
||||
// default: false.
|
||||
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
|
||||
}
|
||||
|
||||
// listReceivedByAddress handles a listreceivedbyaddress request by returning
|
||||
// a slice of objects, each one containing:
|
||||
// "account": the account of the receiving address;
|
||||
// "address": the receiving address;
|
||||
// "amount": total amount received by the address;
|
||||
// "confirmations": number of confirmations of the most recent transaction.
|
||||
//
|
||||
// "account": the account of the receiving address;
|
||||
// "address": the receiving address;
|
||||
// "amount": total amount received by the address;
|
||||
// "confirmations": number of confirmations of the most recent transaction.
|
||||
//
|
||||
// It takes two parameters:
|
||||
// "minconf": minimum number of confirmations to consider a transaction -
|
||||
// default: one;
|
||||
// "includeempty": whether or not to include addresses that have no transactions -
|
||||
// default: false.
|
||||
//
|
||||
// "minconf": minimum number of confirmations to consider a transaction -
|
||||
// default: one;
|
||||
// "includeempty": whether or not to include addresses that have no transactions -
|
||||
// default: false.
|
||||
func listReceivedByAddress(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||
cmd := icmd.(*btcjson.ListReceivedByAddressCmd)
|
||||
|
||||
|
@ -1348,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++
|
||||
}
|
||||
|
@ -1381,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
|
||||
}
|
||||
|
@ -1402,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
|
||||
|
@ -1429,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 {
|
||||
|
@ -1446,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
|
||||
|
@ -1454,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.
|
||||
|
@ -1526,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())
|
||||
|
@ -1538,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 {
|
||||
|
@ -1572,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
|
||||
|
@ -1584,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
|
||||
}
|
||||
|
@ -1599,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 {
|
||||
|
@ -1608,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)
|
||||
}
|
||||
|
||||
|
@ -1618,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
|
||||
|
@ -1629,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
|
||||
}
|
||||
|
@ -1640,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 {
|
||||
|
@ -1650,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
|
||||
|
@ -1685,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)
|
||||
}
|
||||
|
||||
|
@ -1861,6 +1984,12 @@ func signRawTransaction(icmd interface{}, w *wallet.Wallet, chainClient *chain.R
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result == nil {
|
||||
return nil, &btcjson.RPCError{
|
||||
Code: btcjson.ErrRPCNoTxInfo,
|
||||
Message: "Input %s not found" + outPoint.String(),
|
||||
}
|
||||
}
|
||||
script, err := hex.DecodeString(result.ScriptPubKey.Hex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1935,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
|
||||
}
|
||||
|
@ -2040,8 +2167,7 @@ func walletIsLocked(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
|||
}
|
||||
|
||||
// walletLock handles a walletlock request by locking the all account
|
||||
// wallets, returning an error if any wallet is not encrypted (for example,
|
||||
// a watching-only wallet).
|
||||
// wallets, returning an error if any wallet is not encrypted.
|
||||
func walletLock(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||
w.Lock()
|
||||
return nil, nil
|
||||
|
@ -2072,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{
|
||||
|
@ -2100,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
|
||||
}
|
||||
|
|
|
@ -4,52 +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",
|
||||
"exportwatchingwallet": "exportwatchingwallet (\"account\" download=false)\n\nCreates and returns a duplicate of the wallet database without any private keys to be used as a watching-only wallet.\n\nArguments:\n1. account (string, optional) Unused (must be unset or \"*\")\n2. download (boolean, optional, default=false) Unused\n\nResult:\n\"value\" (string) The watching-only database encoded as a base64 string\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",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,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\"\nexportwatchingwallet (\"account\" download=false)\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"
|
||||
|
|
|
@ -165,7 +165,7 @@ func NewServer(opts *Options, walletLoader *wallet.Loader, listeners []net.Liste
|
|||
// httpBasicAuth returns the UTF-8 bytes of the HTTP Basic authentication
|
||||
// string:
|
||||
//
|
||||
// "Basic " + base64(username + ":" + password)
|
||||
// "Basic " + base64(username + ":" + password)
|
||||
func httpBasicAuth(username, password string) []byte {
|
||||
const header = "Basic "
|
||||
base64 := base64.StdEncoding
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
protoc -I. api.proto --go_out=plugins=grpc:walletrpc
|
|
@ -1,81 +0,0 @@
|
|||
// Copyright (c) 2015-2016 The btcsuite developers
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package rpcserver
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/grpc/grpclog"
|
||||
|
||||
"github.com/btcsuite/btclog"
|
||||
)
|
||||
|
||||
// UseLogger sets the logger to use for the gRPC server.
|
||||
func UseLogger(l btclog.Logger) {
|
||||
grpclog.SetLogger(logger{l}) // nolint:staticcheck
|
||||
}
|
||||
|
||||
// logger uses a btclog.Logger to implement the grpclog.Logger interface.
|
||||
type logger struct {
|
||||
btclog.Logger
|
||||
}
|
||||
|
||||
// stripGrpcPrefix removes the package prefix for all logs made to the grpc
|
||||
// logger, since these are already included as the btclog subsystem name.
|
||||
func stripGrpcPrefix(logstr string) string {
|
||||
return strings.TrimPrefix(logstr, "grpc: ")
|
||||
}
|
||||
|
||||
// stripGrpcPrefixArgs removes the package prefix from the first argument, if it
|
||||
// exists and is a string, returning the same arg slice after reassigning the
|
||||
// first arg.
|
||||
func stripGrpcPrefixArgs(args ...interface{}) []interface{} {
|
||||
if len(args) == 0 {
|
||||
return args
|
||||
}
|
||||
firstArgStr, ok := args[0].(string)
|
||||
if ok {
|
||||
args[0] = stripGrpcPrefix(firstArgStr)
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func (l logger) Fatal(args ...interface{}) {
|
||||
l.Critical(stripGrpcPrefixArgs(args)...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (l logger) Fatalf(format string, args ...interface{}) {
|
||||
l.Criticalf(stripGrpcPrefix(format), args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (l logger) Fatalln(args ...interface{}) {
|
||||
l.Critical(stripGrpcPrefixArgs(args)...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (l logger) Print(args ...interface{}) {
|
||||
l.Info(stripGrpcPrefixArgs(args)...)
|
||||
}
|
||||
|
||||
func (l logger) Printf(format string, args ...interface{}) {
|
||||
l.Infof(stripGrpcPrefix(format), args...)
|
||||
}
|
||||
|
||||
func (l logger) Println(args ...interface{}) {
|
||||
l.Info(stripGrpcPrefixArgs(args)...)
|
||||
}
|
|
@ -1,810 +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 rpcserver implements the RPC API and is used by the main package to
|
||||
// start gRPC services.
|
||||
//
|
||||
// Full documentation of the API implemented by this package is maintained in a
|
||||
// language-agnostic document:
|
||||
//
|
||||
// https://github.com/lbryio/lbcwallet/blob/master/rpc/documentation/api.md
|
||||
//
|
||||
// Any API changes must be performed according to the steps listed here:
|
||||
//
|
||||
// https://github.com/lbryio/lbcwallet/blob/master/rpc/documentation/serverchanges.md
|
||||
package rpcserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"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/lbcutil/hdkeychain"
|
||||
"github.com/lbryio/lbcwallet/chain"
|
||||
"github.com/lbryio/lbcwallet/internal/cfgutil"
|
||||
"github.com/lbryio/lbcwallet/internal/zero"
|
||||
"github.com/lbryio/lbcwallet/netparams"
|
||||
pb "github.com/lbryio/lbcwallet/rpc/walletrpc"
|
||||
"github.com/lbryio/lbcwallet/waddrmgr"
|
||||
"github.com/lbryio/lbcwallet/wallet"
|
||||
"github.com/lbryio/lbcwallet/walletdb"
|
||||
)
|
||||
|
||||
// Public API version constants
|
||||
const (
|
||||
semverString = "2.0.1"
|
||||
semverMajor = 2
|
||||
semverMinor = 0
|
||||
semverPatch = 1
|
||||
)
|
||||
|
||||
// translateError creates a new gRPC error with an appropriate error code for
|
||||
// recognized errors.
|
||||
//
|
||||
// This function is by no means complete and should be expanded based on other
|
||||
// known errors. Any RPC handler not returning a gRPC error (with grpc.Errorf)
|
||||
// should return this result instead.
|
||||
func translateError(err error) error {
|
||||
code := errorCode(err)
|
||||
return status.Errorf(code, "%s", err.Error())
|
||||
}
|
||||
|
||||
func errorCode(err error) codes.Code {
|
||||
// waddrmgr.IsError is convenient, but not granular enough when the
|
||||
// underlying error has to be checked. Unwrap the underlying error
|
||||
// if it exists.
|
||||
if e, ok := err.(waddrmgr.ManagerError); ok {
|
||||
// For these waddrmgr error codes, the underlying error isn't
|
||||
// needed to determine the grpc error code.
|
||||
switch e.ErrorCode {
|
||||
case waddrmgr.ErrWrongPassphrase: // public and private
|
||||
return codes.InvalidArgument
|
||||
case waddrmgr.ErrAccountNotFound:
|
||||
return codes.NotFound
|
||||
case waddrmgr.ErrInvalidAccount: // reserved account
|
||||
return codes.InvalidArgument
|
||||
case waddrmgr.ErrDuplicateAccount:
|
||||
return codes.AlreadyExists
|
||||
}
|
||||
|
||||
err = e.Err
|
||||
}
|
||||
|
||||
switch err {
|
||||
case wallet.ErrLoaded:
|
||||
return codes.FailedPrecondition
|
||||
case walletdb.ErrDbNotOpen:
|
||||
return codes.Aborted
|
||||
case walletdb.ErrDbExists:
|
||||
return codes.AlreadyExists
|
||||
case walletdb.ErrDbDoesNotExist:
|
||||
return codes.NotFound
|
||||
case hdkeychain.ErrInvalidSeedLen:
|
||||
return codes.InvalidArgument
|
||||
default:
|
||||
return codes.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
// versionServer provides RPC clients with the ability to query the RPC server
|
||||
// version.
|
||||
type versionServer struct {
|
||||
}
|
||||
|
||||
// walletServer provides wallet services for RPC clients.
|
||||
type walletServer struct {
|
||||
wallet *wallet.Wallet
|
||||
}
|
||||
|
||||
// loaderServer provides RPC clients with the ability to load and close wallets,
|
||||
// as well as establishing a RPC connection to a consensus server.
|
||||
type loaderServer struct {
|
||||
loader *wallet.Loader
|
||||
activeNet *netparams.Params
|
||||
rpcClient *chain.RPCClient
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// StartVersionService creates an implementation of the VersionService and
|
||||
// registers it with the gRPC server.
|
||||
func StartVersionService(server *grpc.Server) {
|
||||
pb.RegisterVersionServiceServer(server, &versionServer{})
|
||||
}
|
||||
|
||||
func (*versionServer) Version(ctx context.Context, req *pb.VersionRequest) (*pb.VersionResponse, error) {
|
||||
return &pb.VersionResponse{
|
||||
VersionString: semverString,
|
||||
Major: semverMajor,
|
||||
Minor: semverMinor,
|
||||
Patch: semverPatch,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// StartWalletService creates an implementation of the WalletService and
|
||||
// registers it with the gRPC server.
|
||||
func StartWalletService(server *grpc.Server, wallet *wallet.Wallet) {
|
||||
service := &walletServer{wallet}
|
||||
pb.RegisterWalletServiceServer(server, service)
|
||||
}
|
||||
|
||||
func (s *walletServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PingResponse, error) {
|
||||
return &pb.PingResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *walletServer) Network(ctx context.Context, req *pb.NetworkRequest) (
|
||||
*pb.NetworkResponse, error) {
|
||||
|
||||
return &pb.NetworkResponse{ActiveNetwork: uint32(s.wallet.ChainParams().Net)}, nil
|
||||
}
|
||||
|
||||
func (s *walletServer) AccountNumber(ctx context.Context, req *pb.AccountNumberRequest) (
|
||||
*pb.AccountNumberResponse, error) {
|
||||
|
||||
accountNum, err := s.wallet.AccountNumber(waddrmgr.KeyScopeBIP0044, req.AccountName)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
return &pb.AccountNumberResponse{AccountNumber: accountNum}, nil
|
||||
}
|
||||
|
||||
func (s *walletServer) Accounts(ctx context.Context, req *pb.AccountsRequest) (
|
||||
*pb.AccountsResponse, error) {
|
||||
|
||||
resp, err := s.wallet.Accounts(waddrmgr.KeyScopeBIP0044)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
accounts := make([]*pb.AccountsResponse_Account, len(resp.Accounts))
|
||||
for i := range resp.Accounts {
|
||||
a := &resp.Accounts[i]
|
||||
accounts[i] = &pb.AccountsResponse_Account{
|
||||
AccountNumber: a.AccountNumber,
|
||||
AccountName: a.AccountName,
|
||||
TotalBalance: int64(a.TotalBalance),
|
||||
ExternalKeyCount: a.ExternalKeyCount,
|
||||
InternalKeyCount: a.InternalKeyCount,
|
||||
ImportedKeyCount: a.ImportedKeyCount,
|
||||
}
|
||||
}
|
||||
return &pb.AccountsResponse{
|
||||
Accounts: accounts,
|
||||
CurrentBlockHash: resp.CurrentBlockHash[:],
|
||||
CurrentBlockHeight: resp.CurrentBlockHeight,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *walletServer) RenameAccount(ctx context.Context, req *pb.RenameAccountRequest) (
|
||||
*pb.RenameAccountResponse, error) {
|
||||
|
||||
err := s.wallet.RenameAccount(waddrmgr.KeyScopeBIP0044, req.AccountNumber, req.NewName)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
return &pb.RenameAccountResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *walletServer) NextAccount(ctx context.Context, req *pb.NextAccountRequest) (
|
||||
*pb.NextAccountResponse, error) {
|
||||
|
||||
defer zero.Bytes(req.Passphrase)
|
||||
|
||||
if req.AccountName == "" {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "account name may not be empty")
|
||||
}
|
||||
|
||||
lock := make(chan time.Time, 1)
|
||||
defer func() {
|
||||
lock <- time.Time{} // send matters, not the value
|
||||
}()
|
||||
err := s.wallet.Unlock(req.Passphrase, lock)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
account, err := s.wallet.NextAccount(waddrmgr.KeyScopeBIP0044, req.AccountName)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
return &pb.NextAccountResponse{AccountNumber: account}, nil
|
||||
}
|
||||
|
||||
func (s *walletServer) NextAddress(ctx context.Context, req *pb.NextAddressRequest) (
|
||||
*pb.NextAddressResponse, error) {
|
||||
|
||||
var (
|
||||
addr btcutil.Address
|
||||
err error
|
||||
)
|
||||
switch req.Kind {
|
||||
case pb.NextAddressRequest_BIP0044_EXTERNAL:
|
||||
addr, err = s.wallet.NewAddress(req.Account, waddrmgr.KeyScopeBIP0044)
|
||||
case pb.NextAddressRequest_BIP0044_INTERNAL:
|
||||
addr, err = s.wallet.NewChangeAddress(req.Account, waddrmgr.KeyScopeBIP0044)
|
||||
default:
|
||||
return nil, status.Errorf(codes.InvalidArgument, "kind=%v", req.Kind)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
return &pb.NextAddressResponse{Address: addr.EncodeAddress()}, nil
|
||||
}
|
||||
|
||||
func (s *walletServer) ImportPrivateKey(ctx context.Context, req *pb.ImportPrivateKeyRequest) (
|
||||
*pb.ImportPrivateKeyResponse, error) {
|
||||
|
||||
defer zero.Bytes(req.Passphrase)
|
||||
|
||||
wif, err := btcutil.DecodeWIF(req.PrivateKeyWif)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument,
|
||||
"Invalid WIF-encoded private key: %v", err)
|
||||
}
|
||||
|
||||
lock := make(chan time.Time, 1)
|
||||
defer func() {
|
||||
lock <- time.Time{} // send matters, not the value
|
||||
}()
|
||||
err = s.wallet.Unlock(req.Passphrase, lock)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
// At the moment, only the special-cased import account can be used to
|
||||
// import keys.
|
||||
if req.Account != waddrmgr.ImportedAddrAccount {
|
||||
return nil, status.Errorf(codes.InvalidArgument,
|
||||
"Only the imported account accepts private key imports")
|
||||
}
|
||||
|
||||
_, err = s.wallet.ImportPrivateKey(waddrmgr.KeyScopeBIP0044, wif, nil, req.Rescan)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
return &pb.ImportPrivateKeyResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *walletServer) Balance(ctx context.Context, req *pb.BalanceRequest) (
|
||||
*pb.BalanceResponse, error) {
|
||||
|
||||
account := req.AccountNumber
|
||||
reqConfs := req.RequiredConfirmations
|
||||
bals, err := s.wallet.CalculateAccountBalances(account, reqConfs)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
// TODO: Spendable currently includes multisig outputs that may not
|
||||
// actually be spendable without additional keys.
|
||||
resp := &pb.BalanceResponse{
|
||||
Total: int64(bals.Total),
|
||||
Spendable: int64(bals.Spendable),
|
||||
ImmatureReward: int64(bals.ImmatureReward),
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *walletServer) FundTransaction(ctx context.Context, req *pb.FundTransactionRequest) (
|
||||
*pb.FundTransactionResponse, error) {
|
||||
|
||||
policy := wallet.OutputSelectionPolicy{
|
||||
Account: req.Account,
|
||||
RequiredConfirmations: req.RequiredConfirmations,
|
||||
IncludeStakes: req.IncludeStakes,
|
||||
}
|
||||
unspentOutputs, err := s.wallet.UnspentOutputs(policy)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
selectedOutputs := make([]*pb.FundTransactionResponse_PreviousOutput, 0, len(unspentOutputs))
|
||||
var totalAmount btcutil.Amount
|
||||
for _, output := range unspentOutputs {
|
||||
selectedOutputs = append(selectedOutputs, &pb.FundTransactionResponse_PreviousOutput{
|
||||
TransactionHash: output.OutPoint.Hash[:],
|
||||
OutputIndex: output.OutPoint.Index,
|
||||
Amount: output.Output.Value,
|
||||
PkScript: output.Output.PkScript,
|
||||
ReceiveTime: output.ReceiveTime.Unix(),
|
||||
FromCoinbase: output.OutputKind == wallet.OutputKindCoinbase,
|
||||
})
|
||||
totalAmount += btcutil.Amount(output.Output.Value)
|
||||
|
||||
if req.TargetAmount != 0 && totalAmount > btcutil.Amount(req.TargetAmount) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var changeScript []byte
|
||||
if req.IncludeChangeScript && totalAmount > btcutil.Amount(req.TargetAmount) {
|
||||
changeAddr, err := s.wallet.NewChangeAddress(req.Account, waddrmgr.KeyScopeBIP0044)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
changeScript, err = txscript.PayToAddrScript(changeAddr)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
}
|
||||
|
||||
return &pb.FundTransactionResponse{
|
||||
SelectedOutputs: selectedOutputs,
|
||||
TotalAmount: int64(totalAmount),
|
||||
ChangePkScript: changeScript,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func marshalGetTransactionsResult(wresp *wallet.GetTransactionsResult) (
|
||||
*pb.GetTransactionsResponse, error) {
|
||||
|
||||
resp := &pb.GetTransactionsResponse{
|
||||
MinedTransactions: marshalBlocks(wresp.MinedTransactions),
|
||||
UnminedTransactions: marshalTransactionDetails(wresp.UnminedTransactions),
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// BUGS:
|
||||
// - MinimumRecentTransactions is ignored.
|
||||
// - Wrong error codes when a block height or hash is not recognized
|
||||
func (s *walletServer) GetTransactions(ctx context.Context, req *pb.GetTransactionsRequest) (
|
||||
resp *pb.GetTransactionsResponse, err error) {
|
||||
|
||||
var startBlock, endBlock *wallet.BlockIdentifier
|
||||
if req.StartingBlockHash != nil && req.StartingBlockHeight != 0 { // nolint:gocritic
|
||||
return nil, errors.New(
|
||||
"starting block hash and height may not be specified simultaneously")
|
||||
} else if req.StartingBlockHash != nil {
|
||||
startBlockHash, err := chainhash.NewHash(req.StartingBlockHash)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "%s", err.Error())
|
||||
}
|
||||
startBlock = wallet.NewBlockIdentifierFromHash(startBlockHash)
|
||||
} else if req.StartingBlockHeight != 0 {
|
||||
startBlock = wallet.NewBlockIdentifierFromHeight(req.StartingBlockHeight)
|
||||
}
|
||||
|
||||
if req.EndingBlockHash != nil && req.EndingBlockHeight != 0 { // nolint:gocritic
|
||||
return nil, status.Errorf(codes.InvalidArgument,
|
||||
"ending block hash and height may not be specified simultaneously")
|
||||
} else if req.EndingBlockHash != nil {
|
||||
endBlockHash, err := chainhash.NewHash(req.EndingBlockHash)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "%s", err.Error())
|
||||
}
|
||||
endBlock = wallet.NewBlockIdentifierFromHash(endBlockHash)
|
||||
} else if req.EndingBlockHeight != 0 {
|
||||
endBlock = wallet.NewBlockIdentifierFromHeight(req.EndingBlockHeight)
|
||||
}
|
||||
|
||||
var minRecentTxs int
|
||||
if req.MinimumRecentTransactions != 0 {
|
||||
if endBlock != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument,
|
||||
"ending block and minimum number of recent transactions "+
|
||||
"may not be specified simultaneously")
|
||||
}
|
||||
minRecentTxs = int(req.MinimumRecentTransactions)
|
||||
if minRecentTxs < 0 {
|
||||
return nil, status.Errorf(codes.InvalidArgument,
|
||||
"minimum number of recent transactions may not be negative")
|
||||
}
|
||||
}
|
||||
|
||||
_ = minRecentTxs
|
||||
|
||||
gtr, err := s.wallet.GetTransactions(startBlock, endBlock, "", ctx.Done())
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
return marshalGetTransactionsResult(gtr)
|
||||
}
|
||||
|
||||
func (s *walletServer) ChangePassphrase(ctx context.Context, req *pb.ChangePassphraseRequest) (
|
||||
*pb.ChangePassphraseResponse, error) {
|
||||
|
||||
defer func() {
|
||||
zero.Bytes(req.OldPassphrase)
|
||||
zero.Bytes(req.NewPassphrase)
|
||||
}()
|
||||
|
||||
var err error
|
||||
switch req.Key {
|
||||
case pb.ChangePassphraseRequest_PRIVATE:
|
||||
err = s.wallet.ChangePrivatePassphrase(req.OldPassphrase, req.NewPassphrase)
|
||||
case pb.ChangePassphraseRequest_PUBLIC:
|
||||
err = s.wallet.ChangePublicPassphrase(req.OldPassphrase, req.NewPassphrase)
|
||||
default:
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Unknown key type (%d)", req.Key)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
return &pb.ChangePassphraseResponse{}, nil
|
||||
}
|
||||
|
||||
// BUGS:
|
||||
// - InputIndexes request field is ignored.
|
||||
func (s *walletServer) SignTransaction(ctx context.Context, req *pb.SignTransactionRequest) (
|
||||
*pb.SignTransactionResponse, error) {
|
||||
|
||||
defer zero.Bytes(req.Passphrase)
|
||||
|
||||
var tx wire.MsgTx
|
||||
err := tx.Deserialize(bytes.NewReader(req.SerializedTransaction))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument,
|
||||
"Bytes do not represent a valid raw transaction: %v", err)
|
||||
}
|
||||
|
||||
lock := make(chan time.Time, 1)
|
||||
defer func() {
|
||||
lock <- time.Time{} // send matters, not the value
|
||||
}()
|
||||
err = s.wallet.Unlock(req.Passphrase, lock)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
invalidSigs, err := s.wallet.SignTransaction(&tx, txscript.SigHashAll, nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
invalidInputIndexes := make([]uint32, len(invalidSigs))
|
||||
for i, e := range invalidSigs {
|
||||
invalidInputIndexes[i] = e.InputIndex
|
||||
}
|
||||
|
||||
var serializedTransaction bytes.Buffer
|
||||
serializedTransaction.Grow(tx.SerializeSize())
|
||||
err = tx.Serialize(&serializedTransaction)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
resp := &pb.SignTransactionResponse{
|
||||
Transaction: serializedTransaction.Bytes(),
|
||||
UnsignedInputIndexes: invalidInputIndexes,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// BUGS:
|
||||
// - The transaction is not inspected to be relevant before publishing using
|
||||
// sendrawtransaction, so connection errors to could result in the tx
|
||||
// never being added to the wallet database.
|
||||
// - Once the above bug is fixed, wallet will require a way to purge invalid
|
||||
// transactions from the database when they are rejected by the network, other
|
||||
// than double spending them.
|
||||
func (s *walletServer) PublishTransaction(ctx context.Context, req *pb.PublishTransactionRequest) (
|
||||
*pb.PublishTransactionResponse, error) {
|
||||
|
||||
var msgTx wire.MsgTx
|
||||
err := msgTx.Deserialize(bytes.NewReader(req.SignedTransaction))
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument,
|
||||
"Bytes do not represent a valid raw transaction: %v", err)
|
||||
}
|
||||
|
||||
err = s.wallet.PublishTransaction(&msgTx, "")
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
return &pb.PublishTransactionResponse{}, nil
|
||||
}
|
||||
|
||||
func marshalTransactionInputs(v []wallet.TransactionSummaryInput) []*pb.TransactionDetails_Input {
|
||||
inputs := make([]*pb.TransactionDetails_Input, len(v))
|
||||
for i := range v {
|
||||
input := &v[i]
|
||||
inputs[i] = &pb.TransactionDetails_Input{
|
||||
Index: input.Index,
|
||||
PreviousAccount: input.PreviousAccount,
|
||||
PreviousAmount: int64(input.PreviousAmount),
|
||||
}
|
||||
}
|
||||
return inputs
|
||||
}
|
||||
|
||||
func marshalTransactionOutputs(v []wallet.TransactionSummaryOutput) []*pb.TransactionDetails_Output {
|
||||
outputs := make([]*pb.TransactionDetails_Output, len(v))
|
||||
for i := range v {
|
||||
output := &v[i]
|
||||
outputs[i] = &pb.TransactionDetails_Output{
|
||||
Index: output.Index,
|
||||
Account: output.Account,
|
||||
Internal: output.Internal,
|
||||
}
|
||||
}
|
||||
return outputs
|
||||
}
|
||||
|
||||
func marshalTransactionDetails(v []wallet.TransactionSummary) []*pb.TransactionDetails {
|
||||
txs := make([]*pb.TransactionDetails, len(v))
|
||||
for i := range v {
|
||||
tx := &v[i]
|
||||
txs[i] = &pb.TransactionDetails{
|
||||
Hash: tx.Hash[:],
|
||||
Transaction: tx.Transaction,
|
||||
Debits: marshalTransactionInputs(tx.MyInputs),
|
||||
Credits: marshalTransactionOutputs(tx.MyOutputs),
|
||||
Fee: int64(tx.Fee),
|
||||
Timestamp: tx.Timestamp,
|
||||
}
|
||||
}
|
||||
return txs
|
||||
}
|
||||
|
||||
func marshalBlocks(v []wallet.Block) []*pb.BlockDetails {
|
||||
blocks := make([]*pb.BlockDetails, len(v))
|
||||
for i := range v {
|
||||
block := &v[i]
|
||||
blocks[i] = &pb.BlockDetails{
|
||||
Hash: block.Hash[:],
|
||||
Height: block.Height,
|
||||
Timestamp: block.Timestamp,
|
||||
Transactions: marshalTransactionDetails(block.Transactions),
|
||||
}
|
||||
}
|
||||
return blocks
|
||||
}
|
||||
|
||||
func marshalHashes(v []*chainhash.Hash) [][]byte {
|
||||
hashes := make([][]byte, len(v))
|
||||
for i, hash := range v {
|
||||
hashes[i] = hash[:]
|
||||
}
|
||||
return hashes
|
||||
}
|
||||
|
||||
func (s *walletServer) TransactionNotifications(req *pb.TransactionNotificationsRequest,
|
||||
svr pb.WalletService_TransactionNotificationsServer) error {
|
||||
|
||||
n := s.wallet.NtfnServer.TransactionNotifications()
|
||||
defer n.Done()
|
||||
|
||||
ctxDone := svr.Context().Done()
|
||||
for {
|
||||
select {
|
||||
case v := <-n.C:
|
||||
resp := pb.TransactionNotificationsResponse{
|
||||
AttachedBlocks: marshalBlocks(v.AttachedBlocks),
|
||||
DetachedBlocks: marshalHashes(v.DetachedBlocks),
|
||||
UnminedTransactions: marshalTransactionDetails(v.UnminedTransactions),
|
||||
UnminedTransactionHashes: marshalHashes(v.UnminedTransactionHashes),
|
||||
}
|
||||
err := svr.Send(&resp)
|
||||
if err != nil {
|
||||
return translateError(err)
|
||||
}
|
||||
|
||||
case <-ctxDone:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *walletServer) SpentnessNotifications(req *pb.SpentnessNotificationsRequest,
|
||||
svr pb.WalletService_SpentnessNotificationsServer) error {
|
||||
|
||||
if req.NoNotifyUnspent && req.NoNotifySpent {
|
||||
return status.Errorf(codes.InvalidArgument,
|
||||
"no_notify_unspent and no_notify_spent may not both be true")
|
||||
}
|
||||
|
||||
n := s.wallet.NtfnServer.AccountSpentnessNotifications(req.Account)
|
||||
defer n.Done()
|
||||
|
||||
ctxDone := svr.Context().Done()
|
||||
for {
|
||||
select {
|
||||
case v := <-n.C:
|
||||
spenderHash, spenderIndex, spent := v.Spender()
|
||||
if (spent && req.NoNotifySpent) || (!spent && req.NoNotifyUnspent) {
|
||||
continue
|
||||
}
|
||||
index := v.Index()
|
||||
resp := pb.SpentnessNotificationsResponse{
|
||||
TransactionHash: v.Hash()[:],
|
||||
OutputIndex: index,
|
||||
}
|
||||
if spent {
|
||||
resp.Spender = &pb.SpentnessNotificationsResponse_Spender{
|
||||
TransactionHash: spenderHash[:],
|
||||
InputIndex: spenderIndex,
|
||||
}
|
||||
}
|
||||
err := svr.Send(&resp)
|
||||
if err != nil {
|
||||
return translateError(err)
|
||||
}
|
||||
|
||||
case <-ctxDone:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *walletServer) AccountNotifications(req *pb.AccountNotificationsRequest,
|
||||
svr pb.WalletService_AccountNotificationsServer) error {
|
||||
|
||||
n := s.wallet.NtfnServer.AccountNotifications()
|
||||
defer n.Done()
|
||||
|
||||
ctxDone := svr.Context().Done()
|
||||
for {
|
||||
select {
|
||||
case v := <-n.C:
|
||||
resp := pb.AccountNotificationsResponse{
|
||||
AccountNumber: v.AccountNumber,
|
||||
AccountName: v.AccountName,
|
||||
ExternalKeyCount: v.ExternalKeyCount,
|
||||
InternalKeyCount: v.InternalKeyCount,
|
||||
ImportedKeyCount: v.ImportedKeyCount,
|
||||
}
|
||||
err := svr.Send(&resp)
|
||||
if err != nil {
|
||||
return translateError(err)
|
||||
}
|
||||
|
||||
case <-ctxDone:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StartWalletLoaderService creates an implementation of the WalletLoaderService
|
||||
// and registers it with the gRPC server.
|
||||
func StartWalletLoaderService(server *grpc.Server, loader *wallet.Loader,
|
||||
activeNet *netparams.Params) {
|
||||
|
||||
service := &loaderServer{loader: loader, activeNet: activeNet}
|
||||
pb.RegisterWalletLoaderServiceServer(server, service)
|
||||
}
|
||||
|
||||
func (s *loaderServer) CreateWallet(ctx context.Context, req *pb.CreateWalletRequest) (
|
||||
*pb.CreateWalletResponse, error) {
|
||||
|
||||
defer func() {
|
||||
zero.Bytes(req.PrivatePassphrase)
|
||||
zero.Bytes(req.Seed)
|
||||
}()
|
||||
|
||||
// Use an insecure public passphrase when the request's is empty.
|
||||
pubPassphrase := req.PublicPassphrase
|
||||
if len(pubPassphrase) == 0 {
|
||||
pubPassphrase = []byte(wallet.InsecurePubPassphrase)
|
||||
}
|
||||
|
||||
wallet, err := s.loader.CreateNewWallet(
|
||||
pubPassphrase, req.PrivatePassphrase, req.Seed, time.Now(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
if s.rpcClient != nil {
|
||||
wallet.SynchronizeRPC(s.rpcClient)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
return &pb.CreateWalletResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *loaderServer) OpenWallet(ctx context.Context, req *pb.OpenWalletRequest) (
|
||||
*pb.OpenWalletResponse, error) {
|
||||
|
||||
// Use an insecure public passphrase when the request's is empty.
|
||||
pubPassphrase := req.PublicPassphrase
|
||||
if len(pubPassphrase) == 0 {
|
||||
pubPassphrase = []byte(wallet.InsecurePubPassphrase)
|
||||
}
|
||||
|
||||
wallet, err := s.loader.OpenExistingWallet(pubPassphrase, false)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
if s.rpcClient != nil {
|
||||
wallet.SynchronizeRPC(s.rpcClient)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
return &pb.OpenWalletResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *loaderServer) WalletExists(ctx context.Context, req *pb.WalletExistsRequest) (
|
||||
*pb.WalletExistsResponse, error) {
|
||||
|
||||
exists, err := s.loader.WalletExists()
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
return &pb.WalletExistsResponse{Exists: exists}, nil
|
||||
}
|
||||
|
||||
func (s *loaderServer) CloseWallet(ctx context.Context, req *pb.CloseWalletRequest) (
|
||||
*pb.CloseWalletResponse, error) {
|
||||
|
||||
err := s.loader.UnloadWallet()
|
||||
if err == wallet.ErrNotLoaded {
|
||||
return nil, status.Errorf(codes.FailedPrecondition, "wallet is not loaded")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
return &pb.CloseWalletResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *loaderServer) StartConsensusRpc(ctx context.Context, // nolint:golint
|
||||
req *pb.StartConsensusRpcRequest) (*pb.StartConsensusRpcResponse,
|
||||
error) {
|
||||
|
||||
defer zero.Bytes(req.Password)
|
||||
|
||||
defer s.mu.Unlock()
|
||||
s.mu.Lock()
|
||||
|
||||
if s.rpcClient != nil {
|
||||
return nil, status.Errorf(codes.FailedPrecondition, "RPC client already created")
|
||||
}
|
||||
|
||||
networkAddress, err := cfgutil.NormalizeAddress(req.NetworkAddress,
|
||||
s.activeNet.RPCClientPort)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument,
|
||||
"Network address is ill-formed: %v", err)
|
||||
}
|
||||
|
||||
// Error if the wallet is already syncing with the network.
|
||||
wallet, walletLoaded := s.loader.LoadedWallet()
|
||||
if walletLoaded && wallet.SynchronizingToNetwork() {
|
||||
return nil, status.Errorf(codes.FailedPrecondition,
|
||||
"wallet is loaded and already synchronizing")
|
||||
}
|
||||
|
||||
rpcClient, err := chain.NewRPCClient(s.activeNet.Params, networkAddress, req.Username,
|
||||
string(req.Password), req.Certificate, len(req.Certificate) == 0, req.SkipVerify, 1)
|
||||
if err != nil {
|
||||
return nil, translateError(err)
|
||||
}
|
||||
|
||||
err = rpcClient.Start()
|
||||
if err != nil {
|
||||
if err == rpcclient.ErrInvalidAuth {
|
||||
return nil, status.Errorf(codes.InvalidArgument,
|
||||
"Invalid RPC credentials: %v", err)
|
||||
}
|
||||
return nil, status.Errorf(codes.NotFound,
|
||||
"Connection to RPC server failed: %v", err)
|
||||
}
|
||||
|
||||
s.rpcClient = rpcClient
|
||||
|
||||
if walletLoaded {
|
||||
wallet.SynchronizeRPC(rpcClient)
|
||||
}
|
||||
|
||||
return &pb.StartConsensusRpcResponse{}, nil
|
||||
}
|
File diff suppressed because it is too large
Load diff
54
rpcserver.go
54
rpcserver.go
|
@ -18,10 +18,7 @@ import (
|
|||
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/lbryio/lbcwallet/rpc/legacyrpc"
|
||||
"github.com/lbryio/lbcwallet/rpc/rpcserver"
|
||||
"github.com/lbryio/lbcwallet/wallet"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
// openRPCKeyPair creates or loads the RPC TLS keypair specified by the
|
||||
|
@ -102,9 +99,8 @@ func generateRPCKeyPair(writeKey bool) (tls.Certificate, error) {
|
|||
return keyPair, nil
|
||||
}
|
||||
|
||||
func startRPCServers(walletLoader *wallet.Loader) (*grpc.Server, *legacyrpc.Server, error) {
|
||||
func startRPCServers(walletLoader *wallet.Loader) (*legacyrpc.Server, error) {
|
||||
var (
|
||||
server *grpc.Server
|
||||
legacyServer *legacyrpc.Server
|
||||
legacyListen = net.Listen
|
||||
keyPair tls.Certificate
|
||||
|
@ -115,7 +111,7 @@ func startRPCServers(walletLoader *wallet.Loader) (*grpc.Server, *legacyrpc.Serv
|
|||
} else {
|
||||
keyPair, err = openRPCKeyPair()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Change the standard net.Listen function to the tls one.
|
||||
|
@ -128,40 +124,19 @@ func startRPCServers(walletLoader *wallet.Loader) (*grpc.Server, *legacyrpc.Serv
|
|||
return tls.Listen(net, laddr, tlsConfig)
|
||||
}
|
||||
|
||||
if len(cfg.ExperimentalRPCListeners) != 0 {
|
||||
listeners := makeListeners(cfg.ExperimentalRPCListeners, net.Listen)
|
||||
if len(listeners) == 0 {
|
||||
err := errors.New("failed to create listeners for RPC server")
|
||||
return nil, nil, err
|
||||
}
|
||||
creds := credentials.NewServerTLSFromCert(&keyPair)
|
||||
server = grpc.NewServer(grpc.Creds(creds))
|
||||
rpcserver.StartVersionService(server)
|
||||
rpcserver.StartWalletLoaderService(server, walletLoader, activeNet)
|
||||
for _, lis := range listeners {
|
||||
lis := lis
|
||||
go func() {
|
||||
log.Infof("Experimental RPC server listening on %s",
|
||||
lis.Addr())
|
||||
err := server.Serve(lis)
|
||||
log.Tracef("Finished serving expimental RPC: %v",
|
||||
err)
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
err := errors.New("failed to create listeners for legacy RPC server")
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
opts := legacyrpc.Options{
|
||||
Username: cfg.Username,
|
||||
Password: cfg.Password,
|
||||
Username: cfg.RPCUser,
|
||||
Password: cfg.RPCPass,
|
||||
MaxPOSTClients: cfg.LegacyRPCMaxClients,
|
||||
MaxWebsocketClients: cfg.LegacyRPCMaxWebsockets,
|
||||
}
|
||||
|
@ -169,11 +144,11 @@ func startRPCServers(walletLoader *wallet.Loader) (*grpc.Server, *legacyrpc.Serv
|
|||
}
|
||||
|
||||
// Error when neither the GRPC nor legacy RPC servers can be started.
|
||||
if server == nil && legacyServer == nil {
|
||||
return nil, nil, errors.New("no suitable RPC services can be started")
|
||||
if legacyServer == nil {
|
||||
return nil, errors.New("no suitable RPC services can be started")
|
||||
}
|
||||
|
||||
return server, legacyServer, nil
|
||||
return legacyServer, nil
|
||||
}
|
||||
|
||||
type listenFunc func(net string, laddr string) (net.Listener, error)
|
||||
|
@ -244,11 +219,6 @@ func makeListeners(normalizedListenAddrs []string, listen listenFunc) []net.List
|
|||
// with a wallet to enable remote wallet access. For the GRPC server, this
|
||||
// registers the WalletService service, and for the legacy JSON-RPC server it
|
||||
// enables methods that require a loaded wallet.
|
||||
func startWalletRPCServices(wallet *wallet.Wallet, server *grpc.Server, legacyServer *legacyrpc.Server) {
|
||||
if server != nil {
|
||||
rpcserver.StartWalletService(server, wallet)
|
||||
}
|
||||
if legacyServer != nil {
|
||||
legacyServer.RegisterWallet(wallet)
|
||||
}
|
||||
func startWalletRPCServices(wallet *wallet.Wallet, legacyServer *legacyrpc.Server) {
|
||||
legacyServer.RegisterWallet(wallet)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
8
tools/tools.go
Normal file
8
tools/tools.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
//go:build tools
|
||||
// +build tools
|
||||
|
||||
package tools
|
||||
|
||||
import (
|
||||
_ "golang.org/x/tools/cmd/goimports"
|
||||
)
|
|
@ -101,8 +101,7 @@ type ManagedPubKeyAddress interface {
|
|||
ExportPubKey() string
|
||||
|
||||
// PrivKey returns the private key for the address. It can fail if the
|
||||
// address manager is watching-only or locked, or the address does not
|
||||
// have any keys.
|
||||
// address manager is locked, or the address does not have any keys.
|
||||
PrivKey() (*btcec.PrivateKey, error)
|
||||
|
||||
// ExportPrivKey returns the private key associated with the address
|
||||
|
@ -155,12 +154,6 @@ func (a *managedAddress) unlock(key EncryptorDecryptor) ([]byte, error) {
|
|||
a.privKeyMutex.Lock()
|
||||
defer a.privKeyMutex.Unlock()
|
||||
|
||||
// If the address belongs to a watch-only account, the encrypted private
|
||||
// key won't be present, so we'll return an error.
|
||||
if len(a.privKeyEncrypted) == 0 {
|
||||
return nil, managerError(ErrWatchingOnly, errWatchingOnly, nil)
|
||||
}
|
||||
|
||||
if len(a.privKeyCT) == 0 {
|
||||
privKey, err := key.Decrypt(a.privKeyEncrypted)
|
||||
if err != nil {
|
||||
|
@ -284,14 +277,10 @@ func (a *managedAddress) ExportPubKey() string {
|
|||
}
|
||||
|
||||
// PrivKey returns the private key for the address. It can fail if the address
|
||||
// manager is watching-only or locked, or the address does not have any keys.
|
||||
// manager is locked, or the address does not have any keys.
|
||||
//
|
||||
// This is part of the ManagedPubKeyAddress interface implementation.
|
||||
func (a *managedAddress) PrivKey() (*btcec.PrivateKey, error) {
|
||||
// No private keys are available for a watching-only address manager.
|
||||
if a.manager.rootManager.WatchOnly() {
|
||||
return nil, managerError(ErrWatchingOnly, errWatchingOnly, nil)
|
||||
}
|
||||
|
||||
a.manager.mtx.Lock()
|
||||
defer a.manager.mtx.Unlock()
|
||||
|
@ -623,10 +612,6 @@ func (a *scriptAddress) Used(ns walletdb.ReadBucket) bool {
|
|||
//
|
||||
// This is part of the ManagedAddress interface implementation.
|
||||
func (a *scriptAddress) Script() ([]byte, error) {
|
||||
// No script is available for a watching-only address manager.
|
||||
if a.manager.rootManager.WatchOnly() {
|
||||
return nil, managerError(ErrWatchingOnly, errWatchingOnly, nil)
|
||||
}
|
||||
|
||||
a.manager.mtx.Lock()
|
||||
defer a.manager.mtx.Unlock()
|
||||
|
@ -721,10 +706,6 @@ func (a *witnessScriptAddress) Used(ns walletdb.ReadBucket) bool {
|
|||
//
|
||||
// This is part of the ManagedAddress interface implementation.
|
||||
func (a *witnessScriptAddress) Script() ([]byte, error) {
|
||||
// No script is available for a watching-only address manager.
|
||||
if a.isSecretScript && a.manager.rootManager.WatchOnly() {
|
||||
return nil, managerError(ErrWatchingOnly, errWatchingOnly, nil)
|
||||
}
|
||||
|
||||
a.manager.mtx.Lock()
|
||||
defer a.manager.mtx.Unlock()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
286
waddrmgr/db.go
286
waddrmgr/db.go
|
@ -6,7 +6,6 @@
|
|||
package waddrmgr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
@ -35,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
|
||||
|
@ -85,12 +84,6 @@ const (
|
|||
// database. This is an account that re-uses the key derivation schema
|
||||
// of BIP0044-like accounts.
|
||||
accountDefault accountType = 0 // not iota as they need to be stable
|
||||
|
||||
// accountWatchOnly is the account type used for storing watch-only
|
||||
// accounts within the database. This is an account that re-uses the key
|
||||
// derivation schema of BIP0044-like accounts and does not store private
|
||||
// keys.
|
||||
accountWatchOnly accountType = 1
|
||||
)
|
||||
|
||||
// dbAccountRow houses information stored about an account in the database.
|
||||
|
@ -110,18 +103,6 @@ type dbDefaultAccountRow struct {
|
|||
name string
|
||||
}
|
||||
|
||||
// dbWatchOnlyAccountRow houses additional information stored about a watch-only
|
||||
// account in the databse.
|
||||
type dbWatchOnlyAccountRow struct {
|
||||
dbAccountRow
|
||||
pubKeyEncrypted []byte
|
||||
masterKeyFingerprint uint32
|
||||
nextExternalIndex uint32
|
||||
nextInternalIndex uint32
|
||||
name string
|
||||
addrSchema *ScopeAddrSchema
|
||||
}
|
||||
|
||||
// dbAddressRow houses common information stored about an address in the
|
||||
// database.
|
||||
type dbAddressRow struct {
|
||||
|
@ -298,7 +279,6 @@ var (
|
|||
cryptoPrivKeyName = []byte("cpriv")
|
||||
cryptoPubKeyName = []byte("cpub")
|
||||
cryptoScriptKeyName = []byte("cscript")
|
||||
watchingOnlyName = []byte("watchonly")
|
||||
|
||||
// Sync related key names (sync bucket).
|
||||
syncedToName = []byte("syncedto")
|
||||
|
@ -439,9 +419,7 @@ func putManagerVersion(ns walletdb.ReadWriteBucket, version uint32) error {
|
|||
}
|
||||
|
||||
// fetchMasterKeyParams loads the master key parameters needed to derive them
|
||||
// (when given the correct user-supplied passphrase) from the database. Either
|
||||
// returned value can be nil, but in practice only the private key params will
|
||||
// be nil for a watching-only database.
|
||||
// (when given the correct user-supplied passphrase) from the database.
|
||||
func fetchMasterKeyParams(ns walletdb.ReadBucket) ([]byte, []byte, error) {
|
||||
bucket := ns.NestedReadBucket(mainBucketName)
|
||||
|
||||
|
@ -604,8 +582,7 @@ func fetchMasterHDKeys(ns walletdb.ReadBucket) ([]byte, []byte) {
|
|||
|
||||
// fetchCryptoKeys loads the encrypted crypto keys which are in turn used to
|
||||
// protect the extended keys, imported keys, and scripts. Any of the returned
|
||||
// values can be nil, but in practice only the crypto private and script keys
|
||||
// will be nil for a watching-only database.
|
||||
// values can be nil
|
||||
func fetchCryptoKeys(ns walletdb.ReadBucket) ([]byte, []byte, []byte, error) {
|
||||
bucket := ns.NestedReadBucket(mainBucketName)
|
||||
|
||||
|
@ -672,35 +649,6 @@ func putCryptoKeys(ns walletdb.ReadWriteBucket, pubKeyEncrypted, privKeyEncrypte
|
|||
return nil
|
||||
}
|
||||
|
||||
// fetchWatchingOnly loads the watching-only flag from the database.
|
||||
func fetchWatchingOnly(ns walletdb.ReadBucket) (bool, error) {
|
||||
bucket := ns.NestedReadBucket(mainBucketName)
|
||||
|
||||
buf := bucket.Get(watchingOnlyName)
|
||||
if len(buf) != 1 {
|
||||
str := "malformed watching-only flag stored in database"
|
||||
return false, managerError(ErrDatabase, str, nil)
|
||||
}
|
||||
|
||||
return buf[0] != 0, nil
|
||||
}
|
||||
|
||||
// putWatchingOnly stores the watching-only flag to the database.
|
||||
func putWatchingOnly(ns walletdb.ReadWriteBucket, watchingOnly bool) error {
|
||||
bucket := ns.NestedReadWriteBucket(mainBucketName)
|
||||
|
||||
var encoded byte
|
||||
if watchingOnly {
|
||||
encoded = 1
|
||||
}
|
||||
|
||||
if err := bucket.Put(watchingOnlyName, []byte{encoded}); err != nil {
|
||||
str := "failed to store watching only flag"
|
||||
return managerError(ErrDatabase, str, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// deserializeAccountRow deserializes the passed serialized account information.
|
||||
// This is used as a common base for the various account types to deserialize
|
||||
// the common parts.
|
||||
|
@ -817,159 +765,6 @@ func serializeDefaultAccountRow(encryptedPubKey, encryptedPrivKey []byte,
|
|||
return rawData
|
||||
}
|
||||
|
||||
// deserializeWatchOnlyAccountRow deserializes the raw data from the passed
|
||||
// account row as a watch-only account.
|
||||
func deserializeWatchOnlyAccountRow(accountID []byte,
|
||||
row *dbAccountRow) (*dbWatchOnlyAccountRow, error) {
|
||||
|
||||
// The serialized BIP0044 watch-only account raw data format is:
|
||||
// <encpubkeylen><encpubkey><masterkeyfingerprint><nextextidx>
|
||||
// <nextintidx><namelen><name>
|
||||
//
|
||||
// 4 bytes encrypted pubkey len + encrypted pubkey + 4 bytes master key
|
||||
// fingerprint + 4 bytes next external index + 4 bytes next internal
|
||||
// index + 4 bytes name len + name + 1 byte addr schema exists + 2 bytes
|
||||
// addr schema (if exists)
|
||||
|
||||
// Given the above, the length of the entry must be at a minimum
|
||||
// the constant value sizes.
|
||||
if len(row.rawData) < 21 {
|
||||
str := fmt.Sprintf("malformed serialized watch-only account "+
|
||||
"for key %x", accountID)
|
||||
return nil, managerError(ErrDatabase, str, nil)
|
||||
}
|
||||
|
||||
retRow := dbWatchOnlyAccountRow{
|
||||
dbAccountRow: *row,
|
||||
}
|
||||
r := bytes.NewReader(row.rawData)
|
||||
|
||||
var pubLen uint32
|
||||
err := binary.Read(r, binary.LittleEndian, &pubLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
retRow.pubKeyEncrypted = make([]byte, pubLen)
|
||||
err = binary.Read(r, binary.LittleEndian, &retRow.pubKeyEncrypted)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &retRow.masterKeyFingerprint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = binary.Read(r, binary.LittleEndian, &retRow.nextExternalIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Read(r, binary.LittleEndian, &retRow.nextInternalIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nameLen uint32
|
||||
err = binary.Read(r, binary.LittleEndian, &nameLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name := make([]byte, nameLen)
|
||||
err = binary.Read(r, binary.LittleEndian, &name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
retRow.name = string(name)
|
||||
|
||||
var addrSchemaExists bool
|
||||
err = binary.Read(r, binary.LittleEndian, &addrSchemaExists)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addrSchemaExists {
|
||||
var addrSchemaBytes [2]byte
|
||||
err = binary.Read(r, binary.LittleEndian, &addrSchemaBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
retRow.addrSchema = scopeSchemaFromBytes(addrSchemaBytes[:])
|
||||
}
|
||||
|
||||
return &retRow, nil
|
||||
}
|
||||
|
||||
// serializeWatchOnlyAccountRow returns the serialization of the raw data field
|
||||
// for a watch-only account.
|
||||
func serializeWatchOnlyAccountRow(encryptedPubKey []byte, masterKeyFingerprint,
|
||||
nextExternalIndex, nextInternalIndex uint32, name string,
|
||||
addrSchema *ScopeAddrSchema) ([]byte, error) {
|
||||
|
||||
// The serialized BIP0044 account raw data format is:
|
||||
// <encpubkeylen><encpubkey><masterkeyfingerprint><nextextidx>
|
||||
// <nextintidx><namelen><name>
|
||||
//
|
||||
// 4 bytes encrypted pubkey len + encrypted pubkey + 4 bytes master key
|
||||
// fingerprint + 4 bytes next external index + 4 bytes next internal
|
||||
// index + 4 bytes name len + name + 1 byte addr schema exists + 2 bytes
|
||||
// addr schema (if exists)
|
||||
pubLen := uint32(len(encryptedPubKey))
|
||||
nameLen := uint32(len(name))
|
||||
|
||||
addrSchemaExists := addrSchema != nil
|
||||
var addrSchemaBytes []byte
|
||||
if addrSchemaExists {
|
||||
addrSchemaBytes = scopeSchemaToBytes(addrSchema)
|
||||
}
|
||||
|
||||
bufLen := 21 + pubLen + nameLen + uint32(len(addrSchemaBytes))
|
||||
buf := bytes.NewBuffer(make([]byte, 0, bufLen))
|
||||
|
||||
err := binary.Write(buf, binary.LittleEndian, pubLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Write(buf, binary.LittleEndian, encryptedPubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = binary.Write(buf, binary.LittleEndian, masterKeyFingerprint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = binary.Write(buf, binary.LittleEndian, nextExternalIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Write(buf, binary.LittleEndian, nextInternalIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = binary.Write(buf, binary.LittleEndian, nameLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = binary.Write(buf, binary.LittleEndian, []byte(name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = binary.Write(buf, binary.LittleEndian, addrSchemaExists)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addrSchemaExists {
|
||||
err = binary.Write(buf, binary.LittleEndian, addrSchemaBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// forEachKeyScope calls the given function for each known manager scope
|
||||
// within the set of scopes known by the root manager.
|
||||
func forEachKeyScope(ns walletdb.ReadBucket, fn func(KeyScope) error) error {
|
||||
|
@ -1108,8 +903,6 @@ func fetchAccountInfo(ns walletdb.ReadBucket, scope *KeyScope,
|
|||
switch row.acctType {
|
||||
case accountDefault:
|
||||
return deserializeDefaultAccountRow(accountID, row)
|
||||
case accountWatchOnly:
|
||||
return deserializeWatchOnlyAccountRow(accountID, row)
|
||||
}
|
||||
|
||||
str := fmt.Sprintf("unsupported account type '%d'", row.acctType)
|
||||
|
@ -1270,30 +1063,6 @@ func putDefaultAccountInfo(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
|||
return putAccountInfo(ns, scope, account, &acctRow, name)
|
||||
}
|
||||
|
||||
// putWatchOnlyAccountInfo stores the provided watch-only account information to
|
||||
// the database.
|
||||
func putWatchOnlyAccountInfo(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||
account uint32, encryptedPubKey []byte, masterKeyFingerprint,
|
||||
nextExternalIndex, nextInternalIndex uint32, name string,
|
||||
addrSchema *ScopeAddrSchema) error {
|
||||
|
||||
rawData, err := serializeWatchOnlyAccountRow(
|
||||
encryptedPubKey, masterKeyFingerprint, nextExternalIndex,
|
||||
nextInternalIndex, name, addrSchema,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(roasbeef): pass scope bucket directly??
|
||||
|
||||
acctRow := dbAccountRow{
|
||||
acctType: accountWatchOnly,
|
||||
rawData: rawData,
|
||||
}
|
||||
return putAccountInfo(ns, scope, account, &acctRow, name)
|
||||
}
|
||||
|
||||
// putAccountInfo stores the provided account information to the database.
|
||||
func putAccountInfo(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
||||
account uint32, acctRow *dbAccountRow, name string) error {
|
||||
|
@ -1763,32 +1532,6 @@ func putChainedAddress(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
|||
arow.pubKeyEncrypted, arow.privKeyEncrypted,
|
||||
nextExternalIndex, nextInternalIndex, arow.name,
|
||||
)
|
||||
|
||||
case accountWatchOnly:
|
||||
arow, err := deserializeWatchOnlyAccountRow(accountID, row)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Increment the appropriate next index depending on whether the
|
||||
// branch is internal or external.
|
||||
nextExternalIndex := arow.nextExternalIndex
|
||||
nextInternalIndex := arow.nextInternalIndex
|
||||
if branch == InternalBranch {
|
||||
nextInternalIndex = index + 1
|
||||
} else {
|
||||
nextExternalIndex = index + 1
|
||||
}
|
||||
|
||||
// Reserialize the account with the updated index and store it.
|
||||
row.rawData, err = serializeWatchOnlyAccountRow(
|
||||
arow.pubKeyEncrypted, arow.masterKeyFingerprint,
|
||||
nextExternalIndex, nextInternalIndex, arow.name,
|
||||
arow.addrSchema,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = bucket.Put(accountID, serializeAccountRow(row))
|
||||
|
@ -1982,12 +1725,6 @@ func forEachActiveAddress(ns walletdb.ReadBucket, scope *KeyScope,
|
|||
}
|
||||
|
||||
// deletePrivateKeys removes all private key material from the database.
|
||||
//
|
||||
// NOTE: Care should be taken when calling this function. It is primarily
|
||||
// intended for use in converting to a watching-only copy. Removing the private
|
||||
// keys from the main database without also marking it watching-only will result
|
||||
// in an unusable database. It will also make any imported scripts and private
|
||||
// keys unrecoverable unless there is a backup copy available.
|
||||
func deletePrivateKeys(ns walletdb.ReadWriteBucket) error {
|
||||
bucket := ns.NestedReadWriteBucket(mainBucketName)
|
||||
|
||||
|
@ -2058,9 +1795,6 @@ func deletePrivateKeys(ns walletdb.ReadWriteBucket) error {
|
|||
str := "failed to delete account private key"
|
||||
return managerError(ErrDatabase, str, err)
|
||||
}
|
||||
|
||||
// Watch-only accounts don't contain any private keys.
|
||||
case accountWatchOnly:
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -2383,9 +2117,10 @@ func putBirthday(ns walletdb.ReadWriteBucket, t time.Time) error {
|
|||
// FetchBirthdayBlock retrieves the birthday block from the database.
|
||||
//
|
||||
// The block is serialized as follows:
|
||||
// [0:4] block height
|
||||
// [4:36] block hash
|
||||
// [36:44] block timestamp
|
||||
//
|
||||
// [0:4] block height
|
||||
// [4:36] block hash
|
||||
// [36:44] block timestamp
|
||||
func FetchBirthdayBlock(ns walletdb.ReadBucket) (BlockStamp, error) {
|
||||
var block BlockStamp
|
||||
|
||||
|
@ -2423,9 +2158,10 @@ func DeleteBirthdayBlock(ns walletdb.ReadWriteBucket) error {
|
|||
// PutBirthdayBlock stores the provided birthday block to the database.
|
||||
//
|
||||
// The block is serialized as follows:
|
||||
// [0:4] block height
|
||||
// [4:36] block hash
|
||||
// [36:44] block timestamp
|
||||
//
|
||||
// [0:4] block height
|
||||
// [4:36] block hash
|
||||
// [36:44] block timestamp
|
||||
//
|
||||
// NOTE: This does not alter the birthday block verification state.
|
||||
func PutBirthdayBlock(ns walletdb.ReadWriteBucket, block BlockStamp) error {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
Package waddrmgr provides a secure hierarchical deterministic wallet address
|
||||
manager.
|
||||
|
||||
Overview
|
||||
# Overview
|
||||
|
||||
One of the fundamental jobs of a wallet is to manage addresses, private keys,
|
||||
and script data associated with them. At a high level, this package provides
|
||||
|
@ -52,14 +52,14 @@ used to decrypt private keys and scripts on demand. Relocking the address
|
|||
manager actively zeros all private material from memory. In addition, temp
|
||||
private key material used internally is zeroed as soon as it's used.
|
||||
|
||||
Locking and Unlocking
|
||||
# Locking and Unlocking
|
||||
|
||||
As previously mentioned, this package provide facilities for locking and
|
||||
unlocking the address manager to protect access to private material and remove
|
||||
it from memory when locked. The Lock, Unlock, and IsLocked functions are used
|
||||
for this purpose.
|
||||
|
||||
Creating a New Address Manager
|
||||
# Creating a New Address Manager
|
||||
|
||||
A new address manager is created via the Create function. This function accepts
|
||||
a wallet database namespace, passphrases, network, and perhaps most importantly,
|
||||
|
@ -69,28 +69,28 @@ to be recovered with only the seed. The GenerateSeed function in the hdkeychain
|
|||
package can be used as a convenient way to create a random seed for use with
|
||||
this function. The address manager is locked immediately upon being created.
|
||||
|
||||
Opening an Existing Address Manager
|
||||
# Opening an Existing Address Manager
|
||||
|
||||
An existing address manager is opened via the Open function. This function
|
||||
accepts an existing wallet database namespace, the public passphrase, and
|
||||
network. The address manager is opened locked as expected since the open
|
||||
function does not take the private passphrase to unlock it.
|
||||
|
||||
Closing the Address Manager
|
||||
# Closing the Address Manager
|
||||
|
||||
The Close method should be called on the address manager when the caller is done
|
||||
with it. While it is not required, it is recommended because it sanely shuts
|
||||
down the database and ensures all private and public key material is purged from
|
||||
memory.
|
||||
|
||||
Managed Addresses
|
||||
# Managed Addresses
|
||||
|
||||
Each address returned by the address manager satisifies the ManagedAddress
|
||||
interface as well as either the ManagedPubKeyAddress or ManagedScriptAddress
|
||||
interfaces. These interfaces provide the means to obtain relevant information
|
||||
about the addresses such as their private keys and scripts.
|
||||
|
||||
Chained Addresses
|
||||
# Chained Addresses
|
||||
|
||||
Most callers will make use of the chained addresses for normal operations.
|
||||
Internal addresses are intended for internal wallet uses such as change outputs,
|
||||
|
@ -101,13 +101,13 @@ been provided. In addition, the LastInternalAddress and LastExternalAddress
|
|||
functions can be used to get the most recently provided internal and external
|
||||
address, respectively.
|
||||
|
||||
Requesting Existing Addresses
|
||||
# Requesting Existing Addresses
|
||||
|
||||
In addition to generating new addresses, access to old addresses is often
|
||||
required. Most notably, to sign transactions in order to redeem them. The
|
||||
Address function provides this capability and returns a ManagedAddress.
|
||||
|
||||
Importing Addresses
|
||||
# Importing Addresses
|
||||
|
||||
While the recommended approach is to use the chained addresses discussed above
|
||||
because they can be deterministically regenerated to avoid losing funds as long
|
||||
|
@ -116,27 +116,27 @@ and as a result, this package provides the ability to import existing private
|
|||
keys in Wallet Import Format (WIF) and hence the associated public key and
|
||||
address.
|
||||
|
||||
Importing Scripts
|
||||
# Importing Scripts
|
||||
|
||||
In order to support pay-to-script-hash transactions, the script must be securely
|
||||
stored as it is needed to redeem the transaction. This can be useful for a
|
||||
variety of scenarios, however the most common use is currently multi-signature
|
||||
transactions.
|
||||
|
||||
Syncing
|
||||
# Syncing
|
||||
|
||||
The address manager also supports storing and retrieving a block hash and height
|
||||
which the manager is known to have all addresses synced through. The manager
|
||||
itself does not have any notion of which addresses are synced or not. It only
|
||||
provides the storage as a convenience for the caller.
|
||||
|
||||
Network
|
||||
# Network
|
||||
|
||||
The address manager must be associated with a given network in order to provide
|
||||
appropriate addresses and reject imported addresses and scripts which don't
|
||||
apply to the associated network.
|
||||
|
||||
Errors
|
||||
# Errors
|
||||
|
||||
All errors returned from this package are of type ManagerError. This allows the
|
||||
caller to programmatically ascertain the specific reasons for failure by
|
||||
|
@ -144,12 +144,12 @@ examining the ErrorCode field of the type asserted ManagerError. For certain
|
|||
error codes, as documented by the specific error codes, the underlying error
|
||||
will be contained in the Err field.
|
||||
|
||||
Bitcoin Improvement Proposals
|
||||
# Bitcoin Improvement Proposals
|
||||
|
||||
This package includes concepts outlined by the following BIPs:
|
||||
|
||||
BIP0032 (https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
|
||||
BIP0043 (https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki)
|
||||
BIP0044 (https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)
|
||||
BIP0032 (https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
|
||||
BIP0043 (https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki)
|
||||
BIP0044 (https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)
|
||||
*/
|
||||
package waddrmgr
|
||||
|
|
|
@ -29,10 +29,6 @@ var (
|
|||
// errLocked is the common error description used for the ErrLocked
|
||||
// error code.
|
||||
errLocked = "address manager is locked"
|
||||
|
||||
// errWatchingOnly is the common error description used for the
|
||||
// ErrWatchingOnly error code.
|
||||
errWatchingOnly = "address manager is watching-only"
|
||||
)
|
||||
|
||||
// ErrorCode identifies a kind of error.
|
||||
|
@ -86,11 +82,6 @@ const (
|
|||
// manager to be unlocked, was requested on a locked account manager.
|
||||
ErrLocked
|
||||
|
||||
// ErrWatchingOnly indicates that an operation, which requires the
|
||||
// account manager to have access to private data, was requested on
|
||||
// a watching-only account manager.
|
||||
ErrWatchingOnly
|
||||
|
||||
// ErrInvalidAccount indicates that the requested account is not valid.
|
||||
ErrInvalidAccount
|
||||
|
||||
|
@ -157,7 +148,6 @@ var errorCodeStrings = map[ErrorCode]string{
|
|||
ErrCoinTypeTooHigh: "ErrCoinTypeTooHigh",
|
||||
ErrAccountNumTooHigh: "ErrAccountNumTooHigh",
|
||||
ErrLocked: "ErrLocked",
|
||||
ErrWatchingOnly: "ErrWatchingOnly",
|
||||
ErrInvalidAccount: "ErrInvalidAccount",
|
||||
ErrAddressNotFound: "ErrAddressNotFound",
|
||||
ErrAccountNotFound: "ErrAccountNotFound",
|
||||
|
|
|
@ -28,7 +28,6 @@ func TestErrorCodeStringer(t *testing.T) {
|
|||
{waddrmgr.ErrCoinTypeTooHigh, "ErrCoinTypeTooHigh"},
|
||||
{waddrmgr.ErrAccountNumTooHigh, "ErrAccountNumTooHigh"},
|
||||
{waddrmgr.ErrLocked, "ErrLocked"},
|
||||
{waddrmgr.ErrWatchingOnly, "ErrWatchingOnly"},
|
||||
{waddrmgr.ErrInvalidAccount, "ErrInvalidAccount"},
|
||||
{waddrmgr.ErrAddressNotFound, "ErrAddressNotFound"},
|
||||
{waddrmgr.ErrAccountNotFound, "ErrAccountNotFound"},
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -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)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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)
|
||||
|
|
@ -245,7 +245,7 @@ func (w *Wallet) disconnectBlock(dbtx walletdb.ReadWriteTx, b wtxmgr.BlockMeta)
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.Hash = *hash
|
||||
bs.Hash = *hash
|
||||
|
||||
client := w.ChainClient()
|
||||
header, err := client.GetBlockHeader(hash)
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
const (
|
||||
// defaultBlockInterval is the default time interval between any two
|
||||
// blocks in a mocked chain.
|
||||
defaultBlockInterval = 10 * time.Minute
|
||||
defaultBlockInterval = 150 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -197,40 +197,18 @@ func (w *Wallet) txToOutputs(outputs []*wire.TxOut, keyScope *waddrmgr.KeyScope,
|
|||
return walletdb.ErrDryRunRollBack
|
||||
}
|
||||
|
||||
// Before committing the transaction, we'll sign our inputs. If
|
||||
// the inputs are part of a watch-only account, there's no
|
||||
// private key information stored, so we'll skip signing such.
|
||||
var watchOnly bool
|
||||
if keyScope == nil {
|
||||
// If a key scope wasn't specified, then coin selection
|
||||
// was performed from the default wallet accounts
|
||||
// (NP2WKH, P2WKH), so any key scope provided doesn't
|
||||
// impact the result of this call.
|
||||
watchOnly, err = w.Manager.IsWatchOnlyAccount(
|
||||
addrmgrNs, waddrmgr.KeyScopeBIP0084, account,
|
||||
)
|
||||
} else {
|
||||
watchOnly, err = w.Manager.IsWatchOnlyAccount(
|
||||
addrmgrNs, *keyScope, account,
|
||||
)
|
||||
}
|
||||
err = tx.AddAllInputScripts(
|
||||
secretSource{w.Manager, addrmgrNs},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !watchOnly {
|
||||
err = tx.AddAllInputScripts(
|
||||
secretSource{w.Manager, addrmgrNs},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = validateMsgTx(
|
||||
tx.Tx, tx.PrevScripts, tx.PrevInputValues,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = validateMsgTx(
|
||||
tx.Tx, tx.PrevScripts, tx.PrevInputValues,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if tx.ChangeIndex >= 0 && account == waddrmgr.ImportedAddrAccount {
|
||||
|
|
|
@ -38,7 +38,7 @@ func TestTxToOutputsDryRun(t *testing.T) {
|
|||
defer cleanup()
|
||||
|
||||
// Create an address we can use to send some coins to.
|
||||
keyScope := waddrmgr.KeyScopeBIP0049Plus
|
||||
keyScope := waddrmgr.KeyScopeBIP0049
|
||||
addr, err := w.CurrentAddress(0, keyScope)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get current address: %v", addr)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -230,7 +230,7 @@ func TestTxToOutputsRandom(t *testing.T) {
|
|||
defer cleanup()
|
||||
|
||||
// Create an address we can use to send some coins to.
|
||||
keyScope := waddrmgr.KeyScopeBIP0049Plus
|
||||
keyScope := waddrmgr.KeyScopeBIP0049
|
||||
addr, err := w.CurrentAddress(0, keyScope)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get current address: %v", addr)
|
||||
|
|
|
@ -7,6 +7,5 @@ Package wallet provides ...
|
|||
TODO: Flesh out this section
|
||||
|
||||
Overview
|
||||
|
||||
*/
|
||||
package wallet
|
||||
|
|
|
@ -8,8 +8,6 @@ import (
|
|||
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/lbryio/lbcutil/hdkeychain"
|
||||
"github.com/lbryio/lbcwallet/waddrmgr"
|
||||
"github.com/lbryio/lbcwallet/walletdb"
|
||||
)
|
||||
|
||||
// defaultDBTimeout specifies the timeout value when opening the wallet
|
||||
|
@ -35,66 +33,20 @@ 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)
|
||||
}
|
||||
|
||||
return w, cleanup
|
||||
}
|
||||
|
||||
// testWalletWatchingOnly creates a test watch only wallet and unlocks it.
|
||||
func testWalletWatchingOnly(t *testing.T) (*Wallet, func()) {
|
||||
// Set up a wallet.
|
||||
dir, err := ioutil.TempDir("", "test_wallet_watch_only")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create db dir: %v", err)
|
||||
}
|
||||
|
||||
cleanup := func() {
|
||||
if err := os.RemoveAll(dir); err != nil {
|
||||
t.Fatalf("could not cleanup test: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
pubPass := []byte("hello")
|
||||
loader := NewLoader(
|
||||
&chaincfg.TestNet3Params, dir, true, defaultDBTimeout, 250,
|
||||
)
|
||||
w, err := loader.CreateNewWatchingOnlyWallet(pubPass, time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create wallet: %v", err)
|
||||
}
|
||||
chainClient := &mockChainClient{}
|
||||
w.chainClient = chainClient
|
||||
|
||||
err = walletdb.Update(w.Database(), func(tx walletdb.ReadWriteTx) error {
|
||||
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||
for scope, schema := range waddrmgr.ScopeAddrMap {
|
||||
_, err := w.Manager.NewScopedKeyManager(
|
||||
ns, scope, schema,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create default scopes: %v", err)
|
||||
}
|
||||
|
||||
return w, cleanup
|
||||
}
|
||||
|
|
|
@ -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 " +
|
||||
|
@ -49,7 +47,7 @@ func keyScopeFromPubKey(pubKey *hdkeychain.ExtendedKey,
|
|||
|
||||
switch *addrType {
|
||||
case waddrmgr.NestedWitnessPubKey:
|
||||
return waddrmgr.KeyScopeBIP0049Plus,
|
||||
return waddrmgr.KeyScopeBIP0049,
|
||||
&waddrmgr.KeyScopeBIP0049AddrSchema, nil
|
||||
|
||||
case waddrmgr.WitnessPubKey:
|
||||
|
@ -74,11 +72,11 @@ func keyScopeFromPubKey(pubKey *hdkeychain.ExtendedKey,
|
|||
|
||||
switch *addrType {
|
||||
case waddrmgr.NestedWitnessPubKey:
|
||||
return waddrmgr.KeyScopeBIP0049Plus,
|
||||
return waddrmgr.KeyScopeBIP0049,
|
||||
&waddrmgr.KeyScopeBIP0049AddrSchema, nil
|
||||
|
||||
case waddrmgr.WitnessPubKey:
|
||||
return waddrmgr.KeyScopeBIP0049Plus, nil, nil
|
||||
return waddrmgr.KeyScopeBIP0049, nil, nil
|
||||
|
||||
default:
|
||||
return waddrmgr.KeyScope{}, nil,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -268,12 +256,14 @@ func (w *Wallet) importAccountScope(ns walletdb.ReadWriteBucket, name string,
|
|||
}
|
||||
}
|
||||
|
||||
account, err := scopedMgr.NewAccountWatchingOnly(
|
||||
ns, name, accountPubKey, masterKeyFingerprint, addrSchema,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// FIXME
|
||||
// account, err := scopedMgr.NewAccountWatchingOnly(
|
||||
// ns, name, accountPubKey, masterKeyFingerprint, addrSchema,
|
||||
// )
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
var account uint32
|
||||
return scopedMgr.AccountProperties(ns, account)
|
||||
}
|
||||
|
||||
|
@ -324,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
|
||||
|
@ -370,7 +360,7 @@ func (w *Wallet) ImportPublicKey(pubKey *btcec.PublicKey,
|
|||
var keyScope waddrmgr.KeyScope
|
||||
switch addrType {
|
||||
case waddrmgr.NestedWitnessPubKey:
|
||||
keyScope = waddrmgr.KeyScopeBIP0049Plus
|
||||
keyScope = waddrmgr.KeyScopeBIP0049
|
||||
case waddrmgr.WitnessPubKey:
|
||||
keyScope = waddrmgr.KeyScopeBIP0084
|
||||
default:
|
||||
|
|
|
@ -79,7 +79,7 @@ var (
|
|||
"Y3BZdhdtUcw",
|
||||
accountIndex: 0,
|
||||
addrType: waddrmgr.NestedWitnessPubKey,
|
||||
expectedScope: waddrmgr.KeyScopeBIP0049Plus,
|
||||
expectedScope: waddrmgr.KeyScopeBIP0049,
|
||||
expectedAddr: "2N5YTxG9XtGXx1YyhZb7N2pwEjoZLLMHGKj",
|
||||
expectedChangeAddr: "2N7wpz5Gy2zEJTvq2MAuU6BCTEBLXNQ8dUw",
|
||||
}, {
|
||||
|
@ -99,7 +99,7 @@ var (
|
|||
"FD2KeY6G9",
|
||||
accountIndex: 9,
|
||||
addrType: waddrmgr.NestedWitnessPubKey,
|
||||
expectedScope: waddrmgr.KeyScopeBIP0049Plus,
|
||||
expectedScope: waddrmgr.KeyScopeBIP0049,
|
||||
expectedAddr: "2NBCJ9WzGXZqpLpXGq3Hacybj3c4eHRcqgh",
|
||||
expectedChangeAddr: "2N3bankFu6F3ZNU41iVJQqyS9MXqp9dvn1M",
|
||||
}, {
|
||||
|
@ -109,7 +109,7 @@ var (
|
|||
"FD2KeY6G9",
|
||||
accountIndex: 9,
|
||||
addrType: waddrmgr.WitnessPubKey,
|
||||
expectedScope: waddrmgr.KeyScopeBIP0049Plus,
|
||||
expectedScope: waddrmgr.KeyScopeBIP0049,
|
||||
expectedAddr: "2NBCJ9WzGXZqpLpXGq3Hacybj3c4eHRcqgh",
|
||||
expectedChangeAddr: "tb1qeqn05w2hfq6axpdprhs4y7x65gxkkvfvyxqk4u",
|
||||
}, {
|
||||
|
@ -137,23 +137,12 @@ func _TestImportAccount(t *testing.T) {
|
|||
w, cleanup := testWallet(t)
|
||||
defer cleanup()
|
||||
|
||||
testImportAccount(t, w, tc, false, tc.name)
|
||||
})
|
||||
|
||||
name := tc.name + " watch-only"
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
w, cleanup := testWalletWatchingOnly(t)
|
||||
defer cleanup()
|
||||
|
||||
testImportAccount(t, w, tc, true, name)
|
||||
testImportAccount(t, w, tc, tc.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testImportAccount(t *testing.T, w *Wallet, tc *testCase, watchOnly bool,
|
||||
name string) {
|
||||
func testImportAccount(t *testing.T, w *Wallet, tc *testCase, name string) {
|
||||
|
||||
// First derive the master public key of the account we want to import.
|
||||
root, err := hdkeychain.NewKeyFromString(tc.masterPriv)
|
||||
|
@ -208,9 +197,6 @@ func testImportAccount(t *testing.T, w *Wallet, tc *testCase, watchOnly bool,
|
|||
// If the wallet is watch only, there is no default account and our
|
||||
// imported account will be index 0.
|
||||
firstAccountIndex := uint32(1)
|
||||
if watchOnly {
|
||||
firstAccountIndex = 0
|
||||
}
|
||||
|
||||
// We should have 3 additional accounts now.
|
||||
acctResult, err := w.Accounts(tc.expectedScope)
|
||||
|
@ -220,7 +206,6 @@ func testImportAccount(t *testing.T, w *Wallet, tc *testCase, watchOnly bool,
|
|||
// Validate the state of the accounts.
|
||||
require.Equal(t, firstAccountIndex, acct1.AccountNumber)
|
||||
require.Equal(t, name+"1", acct1.AccountName)
|
||||
require.Equal(t, true, acct1.IsWatchOnly)
|
||||
require.Equal(t, root.ParentFingerprint(), acct1.MasterKeyFingerprint)
|
||||
require.NotNil(t, acct1.AccountPubKey)
|
||||
require.Equal(t, acct1Pub.String(), acct1.AccountPubKey.String())
|
||||
|
@ -230,7 +215,6 @@ func testImportAccount(t *testing.T, w *Wallet, tc *testCase, watchOnly bool,
|
|||
|
||||
require.Equal(t, firstAccountIndex+1, acct2.AccountNumber)
|
||||
require.Equal(t, name+"2", acct2.AccountName)
|
||||
require.Equal(t, true, acct2.IsWatchOnly)
|
||||
require.Equal(t, root.ParentFingerprint(), acct2.MasterKeyFingerprint)
|
||||
require.NotNil(t, acct2.AccountPubKey)
|
||||
require.Equal(t, acct2Pub.String(), acct2.AccountPubKey.String())
|
||||
|
@ -255,7 +239,6 @@ func testImportAccount(t *testing.T, w *Wallet, tc *testCase, watchOnly bool,
|
|||
|
||||
// Make sure we can't get private keys for the imported accounts.
|
||||
_, err = w.DumpWIFPrivateKey(intAddr)
|
||||
require.True(t, waddrmgr.IsError(err, waddrmgr.ErrWatchingOnly))
|
||||
|
||||
// Get the address info for the single key we imported.
|
||||
switch tc.addrType {
|
||||
|
|
|
@ -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,37 +166,21 @@ func (l *Loader) CreateNewWallet(pubPassphrase, privPassphrase, seed []byte,
|
|||
}
|
||||
}
|
||||
|
||||
return l.createNewWallet(
|
||||
pubPassphrase, privPassphrase, rootKey, bday, false,
|
||||
)
|
||||
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, false,
|
||||
)
|
||||
return l.createNewWallet(passphrase, rootKey, bday)
|
||||
}
|
||||
|
||||
// CreateNewWatchingOnlyWallet creates a new wallet using the provided
|
||||
// public passphrase. No seed or private passphrase may be provided
|
||||
// since the wallet is watching-only.
|
||||
func (l *Loader) CreateNewWatchingOnlyWallet(pubPassphrase []byte,
|
||||
bday time.Time) (*Wallet, error) {
|
||||
|
||||
return l.createNewWallet(
|
||||
pubPassphrase, nil, nil, bday, true,
|
||||
)
|
||||
}
|
||||
|
||||
func (l *Loader) createNewWallet(pubPassphrase, privPassphrase []byte,
|
||||
rootKey *hdkeychain.ExtendedKey, bday time.Time,
|
||||
isWatchingOnly bool) (*Wallet, error) {
|
||||
func (l *Loader) createNewWallet(passphrase []byte,
|
||||
rootKey *hdkeychain.ExtendedKey, bday time.Time) (*Wallet, error) {
|
||||
|
||||
defer l.mu.Unlock()
|
||||
l.mu.Lock()
|
||||
|
@ -232,26 +214,15 @@ func (l *Loader) createNewWallet(pubPassphrase, privPassphrase []byte,
|
|||
}
|
||||
|
||||
// Initialize the newly created database for the wallet before opening.
|
||||
if isWatchingOnly {
|
||||
err := CreateWatchingOnlyWithCallback(
|
||||
l.db, pubPassphrase, l.chainParams, bday,
|
||||
l.walletCreated,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
err := CreateWithCallback(
|
||||
l.db, pubPassphrase, privPassphrase, rootKey,
|
||||
l.chainParams, bday, l.walletCreated,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -263,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()
|
||||
|
||||
|
@ -297,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
|
||||
|
|
|
@ -330,37 +330,6 @@ func (w *Wallet) FinalizePsbt(keyScope *waddrmgr.KeyScope, account uint32,
|
|||
}
|
||||
}
|
||||
|
||||
// Finally, if the input doesn't belong to a watch-only account,
|
||||
// then we'll sign it as is, and populate the input with the
|
||||
// witness and sigScript (if needed).
|
||||
watchOnly := false
|
||||
err = walletdb.View(w.db, func(tx walletdb.ReadTx) error {
|
||||
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||
var err error
|
||||
if keyScope == nil {
|
||||
// If a key scope wasn't specified, then coin
|
||||
// selection was performed from the default
|
||||
// wallet accounts (NP2WKH, P2WKH), so any key
|
||||
// scope provided doesn't impact the result of
|
||||
// this call.
|
||||
watchOnly, err = w.Manager.IsWatchOnlyAccount(
|
||||
ns, waddrmgr.KeyScopeBIP0084, account,
|
||||
)
|
||||
} else {
|
||||
watchOnly, err = w.Manager.IsWatchOnlyAccount(
|
||||
ns, *keyScope, account,
|
||||
)
|
||||
}
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to determine if account is "+
|
||||
"watch-only: %v", err)
|
||||
}
|
||||
if watchOnly {
|
||||
continue
|
||||
}
|
||||
|
||||
witness, sigScript, err := w.ComputeInputScript(
|
||||
tx, signOutput, idx, sigHashes, in.SighashType, nil,
|
||||
)
|
||||
|
|
|
@ -43,7 +43,7 @@ func TestFundPsbt(t *testing.T) {
|
|||
}
|
||||
|
||||
// Also create a nested P2WKH address we can use to send some coins to.
|
||||
addr, err = w.CurrentAddress(0, waddrmgr.KeyScopeBIP0049Plus)
|
||||
addr, err = w.CurrentAddress(0, waddrmgr.KeyScopeBIP0049)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get current address: %v", addr)
|
||||
}
|
||||
|
@ -353,7 +353,7 @@ func TestFinalizePsbt(t *testing.T) {
|
|||
}
|
||||
|
||||
// Also create a nested P2WKH address we can send coins to.
|
||||
addr, err = w.CurrentAddress(0, waddrmgr.KeyScopeBIP0049Plus)
|
||||
addr, err = w.CurrentAddress(0, waddrmgr.KeyScopeBIP0049)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get current address: %v", addr)
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,11 +168,11 @@ func (rm *RecoveryManager) State() *RecoveryState {
|
|||
//
|
||||
// These are defined as:
|
||||
// - Inter-Block Gap: The maximum difference between the derived child indexes
|
||||
// of the last addresses used in any block and the next address consumed
|
||||
// by a later block.
|
||||
// of the last addresses used in any block and the next address consumed
|
||||
// by a later block.
|
||||
// - Intra-Block Gap: The maximum difference between the derived child indexes
|
||||
// of the first address used in any block and the last address used in the
|
||||
// same block.
|
||||
// of the first address used in any block and the last address used in the
|
||||
// same block.
|
||||
type RecoveryState struct {
|
||||
// recoveryWindow defines the key-derivation lookahead used when
|
||||
// attempting to recover the set of used addresses. This value will be
|
||||
|
@ -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,38 +238,23 @@ 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
|
||||
// derivation branch.
|
||||
//
|
||||
// A branch recovery state supports operations for:
|
||||
// - Expanding the look-ahead horizon based on which indexes have been found.
|
||||
// - Registering derived addresses with indexes within the horizon.
|
||||
// - Reporting an invalid child index that falls into the horizon.
|
||||
// - Reporting that an address has been found.
|
||||
// - Retrieving all currently derived addresses for the branch.
|
||||
// - Looking up a particular address by its child index.
|
||||
// - Expanding the look-ahead horizon based on which indexes have been found.
|
||||
// - Registering derived addresses with indexes within the horizon.
|
||||
// - Reporting an invalid child index that falls into the horizon.
|
||||
// - Reporting that an address has been found.
|
||||
// - Retrieving all currently derived addresses for the branch.
|
||||
// - Looking up a particular address by its child index.
|
||||
type BranchRecoveryState struct {
|
||||
// recoveryWindow defines the key-derivation lookahead used when
|
||||
// attempting to recover the set of addresses on this branch.
|
||||
|
|
|
@ -26,7 +26,7 @@ func TestComputeInputScript(t *testing.T) {
|
|||
expectedScriptLen: 0,
|
||||
}, {
|
||||
name: "BIP049 nested P2WKH",
|
||||
scope: waddrmgr.KeyScopeBIP0049Plus,
|
||||
scope: waddrmgr.KeyScopeBIP0049,
|
||||
expectedScriptLen: 23,
|
||||
}}
|
||||
|
||||
|
|
709
wallet/wallet.go
709
wallet/wallet.go
File diff suppressed because it is too large
Load diff
|
@ -17,7 +17,7 @@ var (
|
|||
)
|
||||
|
||||
// TestLocateBirthdayBlock ensures we can properly map a block in the chain to a
|
||||
//timestamp.
|
||||
// timestamp.
|
||||
func TestLocateBirthdayBlock(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright (c) 2018 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package wallet
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
_ "github.com/lbryio/lbcwallet/walletdb/bdb"
|
||||
)
|
||||
|
||||
// TestCreateWatchingOnly checks that we can construct a watching-only
|
||||
// wallet.
|
||||
func TestCreateWatchingOnly(t *testing.T) {
|
||||
// Set up a wallet.
|
||||
dir, err := ioutil.TempDir("", "watchingonly_test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create db dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
pubPass := []byte("hello")
|
||||
|
||||
loader := NewLoader(
|
||||
&chaincfg.TestNet3Params, dir, true, defaultDBTimeout, 250,
|
||||
)
|
||||
_, err = loader.CreateNewWatchingOnlyWallet(pubPass, time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create wallet: %v", err)
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
Package bdb implements an instance of walletdb that uses boltdb for the backing
|
||||
datastore.
|
||||
|
||||
Usage
|
||||
# Usage
|
||||
|
||||
This package is only a driver to the walletdb package and provides the database
|
||||
type of "bdb". The only parameters the Open and Create functions take are the
|
||||
|
|
|
@ -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)
|
|
@ -5,7 +5,7 @@
|
|||
/*
|
||||
Package walletdb provides a namespaced database interface for lbcwallet.
|
||||
|
||||
Overview
|
||||
# Overview
|
||||
|
||||
A wallet essentially consists of a multitude of stored data such as private
|
||||
and public keys, key derivation bits, pay-to-script-hash scripts, and various
|
||||
|
@ -26,16 +26,16 @@ the wallet.
|
|||
|
||||
A quick overview of the features walletdb provides are as follows:
|
||||
|
||||
- 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
|
||||
- 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
|
||||
|
||||
Database
|
||||
# Database
|
||||
|
||||
The main entry point is the DB interface. It exposes functionality for
|
||||
creating, retrieving, and removing namespaces. It is obtained via the Create
|
||||
|
@ -43,7 +43,7 @@ and Open functions which take a database type string that identifies the
|
|||
specific database driver (backend) to use as well as arguments specific to the
|
||||
specified driver.
|
||||
|
||||
Namespaces
|
||||
# Namespaces
|
||||
|
||||
The Namespace interface is an abstraction that provides facilities for obtaining
|
||||
transactions (the Tx interface) that are the basis of all database reads and
|
||||
|
@ -55,14 +55,14 @@ The Begin function provides an unmanaged transaction while the View and Update
|
|||
functions provide a managed transaction. These are described in more detail
|
||||
below.
|
||||
|
||||
Transactions
|
||||
# Transactions
|
||||
|
||||
The Tx interface provides facilities for rolling back or commiting changes that
|
||||
took place while the transaction was active. It also provides the root bucket
|
||||
under which all keys, values, and nested buckets are stored. A transaction
|
||||
can either be read-only or read-write and managed or unmanaged.
|
||||
|
||||
Managed versus Unmanaged Transactions
|
||||
# Managed versus Unmanaged Transactions
|
||||
|
||||
A managed transaction is one where the caller provides a function to execute
|
||||
within the context of the transaction and the commit or rollback is handled
|
||||
|
@ -75,7 +75,7 @@ call Commit or Rollback when they are finished with it. Leaving transactions
|
|||
open for long periods of time can have several adverse effects, so it is
|
||||
recommended that managed transactions are used instead.
|
||||
|
||||
Buckets
|
||||
# Buckets
|
||||
|
||||
The Bucket interface provides the ability to manipulate key/value pairs and
|
||||
nested buckets as well as iterate through them.
|
||||
|
@ -85,7 +85,7 @@ CreateBucket, CreateBucketIfNotExists, and DeleteBucket functions work with
|
|||
buckets. The ForEach function allows the caller to provide a function to be
|
||||
called with each key/value pair and nested bucket in the current bucket.
|
||||
|
||||
Root Bucket
|
||||
# Root Bucket
|
||||
|
||||
As discussed above, all of the functions which are used to manipulate key/value
|
||||
pairs and nested buckets exist on the Bucket interface. The root bucket is the
|
||||
|
@ -93,7 +93,7 @@ upper-most bucket in a namespace under which data is stored and is created at
|
|||
the same time as the namespace. Use the RootBucket function on the Tx interface
|
||||
to retrieve it.
|
||||
|
||||
Nested Buckets
|
||||
# Nested Buckets
|
||||
|
||||
The CreateBucket and CreateBucketIfNotExists functions on the Bucket interface
|
||||
provide the ability to create an arbitrary number of nested buckets. It is
|
||||
|
|
|
@ -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%
|
147
walletsetup.go
147
walletsetup.go
|
@ -13,10 +13,7 @@ import (
|
|||
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/lbryio/lbcd/wire"
|
||||
btcutil "github.com/lbryio/lbcutil"
|
||||
"github.com/lbryio/lbcwallet/internal/legacy/keystore"
|
||||
"github.com/lbryio/lbcwallet/internal/prompt"
|
||||
"github.com/lbryio/lbcwallet/waddrmgr"
|
||||
"github.com/lbryio/lbcwallet/wallet"
|
||||
"github.com/lbryio/lbcwallet/walletdb"
|
||||
_ "github.com/lbryio/lbcwallet/walletdb/bdb"
|
||||
|
@ -39,62 +36,6 @@ func networkDir(dataDir string, chainParams *chaincfg.Params) string {
|
|||
return filepath.Join(dataDir, netname)
|
||||
}
|
||||
|
||||
// convertLegacyKeystore converts all of the addresses in the passed legacy
|
||||
// key store to the new waddrmgr.Manager format. Both the legacy keystore and
|
||||
// the new manager must be unlocked.
|
||||
func convertLegacyKeystore(legacyKeyStore *keystore.Store, w *wallet.Wallet) {
|
||||
netParams := legacyKeyStore.Net()
|
||||
blockStamp := waddrmgr.BlockStamp{
|
||||
Height: 0,
|
||||
Hash: *netParams.GenesisHash,
|
||||
}
|
||||
for _, walletAddr := range legacyKeyStore.ActiveAddresses() {
|
||||
switch addr := walletAddr.(type) {
|
||||
case keystore.PubKeyAddress:
|
||||
privKey, err := addr.PrivKey()
|
||||
if err != nil {
|
||||
fmt.Printf("WARN: Failed to obtain private key "+
|
||||
"for address %v: %v\n", addr.Address(),
|
||||
err)
|
||||
continue
|
||||
}
|
||||
|
||||
wif, err := btcutil.NewWIF(
|
||||
privKey, netParams, addr.Compressed(),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Printf("WARN: Failed to create wallet "+
|
||||
"import format for address %v: %v\n",
|
||||
addr.Address(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = w.ImportPrivateKey(waddrmgr.KeyScopeBIP0044,
|
||||
wif, &blockStamp, false)
|
||||
if err != nil {
|
||||
fmt.Printf("WARN: Failed to import private "+
|
||||
"key for address %v: %v\n",
|
||||
addr.Address(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
case keystore.ScriptAddress:
|
||||
_, err := w.ImportP2SHRedeemScript(addr.Script())
|
||||
if err != nil {
|
||||
fmt.Printf("WARN: Failed to import "+
|
||||
"pay-to-script-hash script for "+
|
||||
"address %v: %v\n", addr.Address(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
default:
|
||||
fmt.Printf("WARN: Skipping unrecognized legacy "+
|
||||
"keystore type: %T\n", addr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// createWallet prompts the user for information needed to generate a new wallet
|
||||
// and generates the wallet accordingly. The new wallet will reside at the
|
||||
// provided path.
|
||||
|
@ -104,97 +45,28 @@ func createWallet(cfg *config) error {
|
|||
activeNet.Params, dbDir, true, cfg.DBTimeout, 250,
|
||||
)
|
||||
|
||||
// When there is a legacy keystore, open it now to ensure any errors
|
||||
// don't end up exiting the process after the user has spent time
|
||||
// entering a bunch of information.
|
||||
netDir := networkDir(cfg.AppDataDir.Value, activeNet.Params)
|
||||
keystorePath := filepath.Join(netDir, keystore.Filename)
|
||||
var legacyKeyStore *keystore.Store
|
||||
_, err := os.Stat(keystorePath)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
// A stat error not due to a non-existent file should be
|
||||
// returned to the caller.
|
||||
return err
|
||||
} else if err == nil {
|
||||
// Keystore file exists.
|
||||
legacyKeyStore, err = keystore.OpenDir(netDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Start by prompting for the passphrase.
|
||||
passphrase := []byte(cfg.Passphrase)
|
||||
|
||||
// Start by prompting for the private passphrase. When there is an
|
||||
// existing keystore, the user will be promped for that passphrase,
|
||||
// otherwise they will be prompted for a new one.
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
privPass, err := prompt.PrivatePass(reader, legacyKeyStore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// When there exists a legacy keystore, unlock it now and set up a
|
||||
// callback to import all keystore keys into the new walletdb
|
||||
// wallet
|
||||
if legacyKeyStore != nil {
|
||||
err = legacyKeyStore.Unlock(privPass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Import the addresses in the legacy keystore to the new wallet if
|
||||
// any exist, locking each wallet again when finished.
|
||||
loader.RunAfterLoad(func(w *wallet.Wallet) {
|
||||
defer func() { _ = legacyKeyStore.Lock() }()
|
||||
|
||||
fmt.Println("Importing addresses from existing wallet...")
|
||||
|
||||
lockChan := make(chan time.Time, 1)
|
||||
defer func() {
|
||||
lockChan <- time.Time{}
|
||||
}()
|
||||
err := w.Unlock(privPass, lockChan)
|
||||
if err != nil {
|
||||
fmt.Printf("ERR: Failed to unlock new wallet "+
|
||||
"during old wallet key import: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
convertLegacyKeystore(legacyKeyStore, w)
|
||||
|
||||
// Remove the legacy key store.
|
||||
err = os.Remove(keystorePath)
|
||||
if err != nil {
|
||||
fmt.Printf("WARN: Failed to remove legacy wallet "+
|
||||
"from'%s'\n", keystorePath)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 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.
|
||||
seed, err := prompt.Seed(reader)
|
||||
seed, bday, err := prompt.Seed(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Creating the wallet...")
|
||||
w, err := loader.CreateNewWallet(pubPass, privPass, seed, time.Now())
|
||||
w, err := loader.CreateNewWallet(passphrase, seed, bday)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Manager.Close()
|
||||
fmt.Println("The wallet has been created successfully.")
|
||||
|
||||
fmt.Println("The wallet has been created successfully with birthday:", bday.Format(time.UnixDate))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -204,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.
|
||||
|
@ -221,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
|
||||
}
|
||||
|
|
60
wtxmgr/db.go
60
wtxmgr/db.go
|
@ -678,21 +678,21 @@ func deleteRawCredit(ns walletdb.ReadWriteBucket, k []byte) error {
|
|||
//
|
||||
// Example usage:
|
||||
//
|
||||
// prefix := keyTxRecord(txHash, block)
|
||||
// it := makeCreditIterator(ns, prefix)
|
||||
// for it.next() {
|
||||
// // Use it.elem
|
||||
// // If necessary, read additional details from it.ck, it.cv
|
||||
// }
|
||||
// if it.err != nil {
|
||||
// // Handle error
|
||||
// }
|
||||
// prefix := keyTxRecord(txHash, block)
|
||||
// it := makeCreditIterator(ns, prefix)
|
||||
// for it.next() {
|
||||
// // Use it.elem
|
||||
// // If necessary, read additional details from it.ck, it.cv
|
||||
// }
|
||||
// if it.err != nil {
|
||||
// // Handle error
|
||||
// }
|
||||
//
|
||||
// The elem's Spent field is not set to true if the credit is spent by an
|
||||
// unmined transaction. To check for this case:
|
||||
//
|
||||
// k := canonicalOutPoint(&txHash, it.elem.Index)
|
||||
// it.elem.Spent = existsRawUnminedInput(ns, k) != nil
|
||||
// k := canonicalOutPoint(&txHash, it.elem.Index)
|
||||
// it.elem.Spent = existsRawUnminedInput(ns, k) != nil
|
||||
type creditIterator struct {
|
||||
c walletdb.ReadWriteCursor // Set to nil after final iteration
|
||||
prefix []byte
|
||||
|
@ -918,15 +918,15 @@ func deleteRawDebit(ns walletdb.ReadWriteBucket, k []byte) error {
|
|||
//
|
||||
// Example usage:
|
||||
//
|
||||
// prefix := keyTxRecord(txHash, block)
|
||||
// it := makeDebitIterator(ns, prefix)
|
||||
// for it.next() {
|
||||
// // Use it.elem
|
||||
// // If necessary, read additional details from it.ck, it.cv
|
||||
// }
|
||||
// if it.err != nil {
|
||||
// // Handle error
|
||||
// }
|
||||
// prefix := keyTxRecord(txHash, block)
|
||||
// it := makeDebitIterator(ns, prefix)
|
||||
// for it.next() {
|
||||
// // Use it.elem
|
||||
// // If necessary, read additional details from it.ck, it.cv
|
||||
// }
|
||||
// if it.err != nil {
|
||||
// // Handle error
|
||||
// }
|
||||
type debitIterator struct {
|
||||
c walletdb.ReadWriteCursor // Set to nil after final iteration
|
||||
prefix []byte
|
||||
|
@ -1092,22 +1092,22 @@ func deleteRawUnminedCredit(ns walletdb.ReadWriteBucket, k []byte) error {
|
|||
// unminedCreditIterator allows for cursor iteration over all credits, in order,
|
||||
// from a single unmined transaction.
|
||||
//
|
||||
// Example usage:
|
||||
// Example usage:
|
||||
//
|
||||
// it := makeUnminedCreditIterator(ns, txHash)
|
||||
// for it.next() {
|
||||
// // Use it.elem, it.ck and it.cv
|
||||
// // Optionally, use it.delete() to remove this k/v pair
|
||||
// }
|
||||
// if it.err != nil {
|
||||
// // Handle error
|
||||
// }
|
||||
// it := makeUnminedCreditIterator(ns, txHash)
|
||||
// for it.next() {
|
||||
// // Use it.elem, it.ck and it.cv
|
||||
// // Optionally, use it.delete() to remove this k/v pair
|
||||
// }
|
||||
// if it.err != nil {
|
||||
// // Handle error
|
||||
// }
|
||||
//
|
||||
// The spentness of the credit is not looked up for performance reasons (because
|
||||
// for unspent credits, it requires another lookup in another bucket). If this
|
||||
// is needed, it may be checked like this:
|
||||
//
|
||||
// spent := existsRawUnminedInput(ns, it.ck) != nil
|
||||
// spent := existsRawUnminedInput(ns, it.ck) != nil
|
||||
type unminedCreditIterator struct {
|
||||
c walletdb.ReadWriteCursor
|
||||
prefix []byte
|
||||
|
|
Loading…
Reference in a new issue