Merge pull request #738 from guggero/build-improvement
Add Makefile, use golangci-lint
This commit is contained in:
commit
07d4cc155e
46 changed files with 420 additions and 492 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
btcwallet
|
btcwallet
|
||||||
vendor
|
vendor
|
||||||
.idea
|
.idea
|
||||||
|
coverage.txt
|
||||||
|
|
54
.golangci.yml
Normal file
54
.golangci.yml
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
run:
|
||||||
|
# timeout for analysis
|
||||||
|
deadline: 10m
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
govet:
|
||||||
|
# Don't report about shadowed variables
|
||||||
|
check-shadowing: false
|
||||||
|
gofmt:
|
||||||
|
# simplify code: gofmt with `-s` option, true by default
|
||||||
|
simplify: true
|
||||||
|
|
||||||
|
linters:
|
||||||
|
enable-all: true
|
||||||
|
disable:
|
||||||
|
# Global variables are used in many places throughout the code base.
|
||||||
|
- gochecknoglobals
|
||||||
|
|
||||||
|
# Some lines are over 80 characters on purpose and we don't want to make them
|
||||||
|
# even longer by marking them as 'nolint'.
|
||||||
|
- lll
|
||||||
|
|
||||||
|
# We don't care (enough) about misaligned structs to lint that.
|
||||||
|
- maligned
|
||||||
|
|
||||||
|
# We have long functions, especially in tests. Moving or renaming those would
|
||||||
|
# trigger funlen problems that we may not want to solve at that time.
|
||||||
|
- funlen
|
||||||
|
|
||||||
|
# Disable for now as we haven't yet tuned the sensitivity to our codebase
|
||||||
|
# yet. Enabling by default for example, would also force new contributors to
|
||||||
|
# potentially extensively refactor code, when they want to smaller change to
|
||||||
|
# land.
|
||||||
|
- gocyclo
|
||||||
|
|
||||||
|
# Instances of table driven tests that don't pre-allocate shouldn't trigger
|
||||||
|
# the linter.
|
||||||
|
- prealloc
|
||||||
|
|
||||||
|
# Init functions are used by loggers throughout the codebase.
|
||||||
|
- gochecknoinits
|
||||||
|
|
||||||
|
# Explicit types are okay.
|
||||||
|
- interfacer
|
||||||
|
|
||||||
|
issues:
|
||||||
|
exclude-rules:
|
||||||
|
# Exclude gosec from running for tests so that tests with weak randomness
|
||||||
|
# (math/rand) will pass the linter.
|
||||||
|
- path: _test\.go
|
||||||
|
linters:
|
||||||
|
- gosec
|
||||||
|
- errcheck
|
||||||
|
- dupl
|
|
@ -4,10 +4,11 @@ go:
|
||||||
sudo: false
|
sudo: false
|
||||||
install:
|
install:
|
||||||
- GO111MODULE=on go install -v ./...
|
- GO111MODULE=on go install -v ./...
|
||||||
- GO111MODULE=off go get -v golang.org/x/tools/cmd/cover
|
|
||||||
- GO111MODULE=off go get -v golang.org/x/lint/golint
|
|
||||||
- GO111MODULE=off go get -v github.com/davecgh/go-spew/spew
|
|
||||||
script:
|
script:
|
||||||
- export PATH=$PATH:$HOME/gopath/bin
|
- export PATH=$PATH:$HOME/gopath/bin
|
||||||
- export GO111MODULE=on
|
- export GO111MODULE=on
|
||||||
- ./goclean.sh
|
- make fmt
|
||||||
|
- make lint
|
||||||
|
- make unit-race
|
||||||
|
- make unit-cover
|
||||||
|
- make goveralls
|
||||||
|
|
132
Makefile
Normal file
132
Makefile
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
PKG := github.com/btcsuite/btcwallet
|
||||||
|
|
||||||
|
GOVERALLS_PKG := github.com/mattn/goveralls
|
||||||
|
LINT_PKG := github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||||
|
GOACC_PKG := github.com/ory/go-acc
|
||||||
|
GOIMPORTS_PKG := golang.org/x/tools/cmd/goimports
|
||||||
|
|
||||||
|
GO_BIN := ${GOPATH}/bin
|
||||||
|
GOVERALLS_BIN := $(GO_BIN)/goveralls
|
||||||
|
LINT_BIN := $(GO_BIN)/golangci-lint
|
||||||
|
GOACC_BIN := $(GO_BIN)/go-acc
|
||||||
|
|
||||||
|
LINT_COMMIT := v1.18.0
|
||||||
|
GOACC_COMMIT := ddc355013f90fea78d83d3a6c71f1d37ac07ecd5
|
||||||
|
|
||||||
|
DEPGET := cd /tmp && GO111MODULE=on go get -v
|
||||||
|
GOBUILD := GO111MODULE=on go build -v
|
||||||
|
GOINSTALL := GO111MODULE=on go install -v
|
||||||
|
GOTEST := GO111MODULE=on go test
|
||||||
|
|
||||||
|
GOLIST := go list -deps $(PKG)/... | grep '$(PKG)'
|
||||||
|
GOLIST_COVER := $$(go list -deps $(PKG)/... | grep '$(PKG)')
|
||||||
|
GOFILES_NOVENDOR = $(shell find . -type f -name '*.go' -not -path "./vendor/*")
|
||||||
|
|
||||||
|
RM := rm -f
|
||||||
|
CP := cp
|
||||||
|
MAKE := make
|
||||||
|
XARGS := xargs -L 1
|
||||||
|
|
||||||
|
# Linting uses a lot of memory, so keep it under control by limiting the number
|
||||||
|
# of workers if requested.
|
||||||
|
ifneq ($(workers),)
|
||||||
|
LINT_WORKERS = --concurrency=$(workers)
|
||||||
|
endif
|
||||||
|
|
||||||
|
LINT = $(LINT_BIN) run -v $(LINT_WORKERS)
|
||||||
|
|
||||||
|
GREEN := "\\033[0;32m"
|
||||||
|
NC := "\\033[0m"
|
||||||
|
define print
|
||||||
|
echo $(GREEN)$1$(NC)
|
||||||
|
endef
|
||||||
|
|
||||||
|
default: build
|
||||||
|
|
||||||
|
all: build check
|
||||||
|
|
||||||
|
# ============
|
||||||
|
# DEPENDENCIES
|
||||||
|
# ============
|
||||||
|
|
||||||
|
$(GOVERALLS_BIN):
|
||||||
|
@$(call print, "Fetching goveralls.")
|
||||||
|
go get -u $(GOVERALLS_PKG)
|
||||||
|
|
||||||
|
$(LINT_BIN):
|
||||||
|
@$(call print, "Fetching linter")
|
||||||
|
$(DEPGET) $(LINT_PKG)@$(LINT_COMMIT)
|
||||||
|
|
||||||
|
$(GOACC_BIN):
|
||||||
|
@$(call print, "Fetching go-acc")
|
||||||
|
$(DEPGET) $(GOACC_PKG)@$(GOACC_COMMIT)
|
||||||
|
|
||||||
|
goimports:
|
||||||
|
@$(call print, "Installing goimports.")
|
||||||
|
$(DEPGET) $(GOIMPORTS_PKG)
|
||||||
|
|
||||||
|
# ============
|
||||||
|
# INSTALLATION
|
||||||
|
# ============
|
||||||
|
|
||||||
|
build:
|
||||||
|
@$(call print, "Compiling btcwallet.")
|
||||||
|
$(GOBUILD) $(PKG)/...
|
||||||
|
|
||||||
|
install:
|
||||||
|
@$(call print, "Installing btcwallet.")
|
||||||
|
$(GOINSTALL) $(PKG)
|
||||||
|
$(GOINSTALL) $(PKG)/cmd/dropwtxmgr
|
||||||
|
$(GOINSTALL) $(PKG)/cmd/sweepaccount
|
||||||
|
|
||||||
|
# =======
|
||||||
|
# TESTING
|
||||||
|
# =======
|
||||||
|
|
||||||
|
check: unit
|
||||||
|
|
||||||
|
unit:
|
||||||
|
@$(call print, "Running unit tests.")
|
||||||
|
$(GOLIST) | $(XARGS) env $(GOTEST)
|
||||||
|
|
||||||
|
unit-cover: $(GOACC_BIN)
|
||||||
|
@$(call print, "Running unit coverage tests.")
|
||||||
|
$(GOACC_BIN) $(GOLIST_COVER)
|
||||||
|
|
||||||
|
unit-race:
|
||||||
|
@$(call print, "Running unit race tests.")
|
||||||
|
env CGO_ENABLED=1 GORACE="history_size=7 halt_on_errors=1" $(GOLIST) | $(XARGS) env $(GOTEST) -race
|
||||||
|
|
||||||
|
goveralls: $(GOVERALLS_BIN)
|
||||||
|
@$(call print, "Sending coverage report.")
|
||||||
|
$(GOVERALLS_BIN) -coverprofile=coverage.txt -service=travis-ci
|
||||||
|
|
||||||
|
# =========
|
||||||
|
# UTILITIES
|
||||||
|
# =========
|
||||||
|
|
||||||
|
fmt: goimports
|
||||||
|
@$(call print, "Fixing imports.")
|
||||||
|
goimports -w $(GOFILES_NOVENDOR)
|
||||||
|
@$(call print, "Formatting source.")
|
||||||
|
gofmt -l -w -s $(GOFILES_NOVENDOR)
|
||||||
|
|
||||||
|
lint: $(LINT_BIN)
|
||||||
|
@$(call print, "Linting source.")
|
||||||
|
$(LINT)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@$(call print, "Cleaning source.$(NC)")
|
||||||
|
$(RM) coverage.txt
|
||||||
|
|
||||||
|
.PHONY: all \
|
||||||
|
default \
|
||||||
|
build \
|
||||||
|
check \
|
||||||
|
unit \
|
||||||
|
unit-cover \
|
||||||
|
unit-race \
|
||||||
|
goveralls \
|
||||||
|
fmt \
|
||||||
|
lint \
|
||||||
|
clean
|
22
appveyor.yml
22
appveyor.yml
|
@ -1,22 +0,0 @@
|
||||||
version: "{build}"
|
|
||||||
|
|
||||||
clone_folder: c:\projects\src\github.com\btcsuite\btcwallet
|
|
||||||
|
|
||||||
environment:
|
|
||||||
PATH: c:\projects\bin;%PATH%
|
|
||||||
GOPATH: c:\projects
|
|
||||||
GORACE: halt_on_error=1
|
|
||||||
|
|
||||||
init:
|
|
||||||
- go version
|
|
||||||
- go get -u github.com/Masterminds/glide
|
|
||||||
- go get -u github.com/davecgh/go-spew/spew
|
|
||||||
|
|
||||||
install:
|
|
||||||
- glide install
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- ps: go get $(glide novendor)
|
|
||||||
|
|
||||||
test_script:
|
|
||||||
- ps: go test -race $(glide novendor)
|
|
|
@ -166,11 +166,11 @@ func rpcClientConnectLoop(legacyRPCServer *legacyrpc.Server, loader *wallet.Load
|
||||||
"bdb", filepath.Join(netDir, "neutrino.db"),
|
"bdb", filepath.Join(netDir, "neutrino.db"),
|
||||||
true, cfg.DBTimeout,
|
true, cfg.DBTimeout,
|
||||||
)
|
)
|
||||||
defer spvdb.Close()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to create Neutrino DB: %s", err)
|
log.Errorf("Unable to create Neutrino DB: %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
defer spvdb.Close()
|
||||||
chainService, err = neutrino.NewChainService(
|
chainService, err = neutrino.NewChainService(
|
||||||
neutrino.Config{
|
neutrino.Config{
|
||||||
DataDir: netDir,
|
DataDir: netDir,
|
||||||
|
|
|
@ -221,7 +221,7 @@ func (c *BitcoindClient) Notifications() <-chan interface{} {
|
||||||
//
|
//
|
||||||
// NOTE: This is part of the chain.Interface interface.
|
// NOTE: This is part of the chain.Interface interface.
|
||||||
func (c *BitcoindClient) NotifyReceived(addrs []btcutil.Address) error {
|
func (c *BitcoindClient) NotifyReceived(addrs []btcutil.Address) error {
|
||||||
c.NotifyBlocks()
|
_ = c.NotifyBlocks()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case c.rescanUpdate <- addrs:
|
case c.rescanUpdate <- addrs:
|
||||||
|
@ -235,7 +235,7 @@ func (c *BitcoindClient) NotifyReceived(addrs []btcutil.Address) error {
|
||||||
// NotifySpent allows the chain backend to notify the caller whenever a
|
// NotifySpent allows the chain backend to notify the caller whenever a
|
||||||
// transaction spends any of the given outpoints.
|
// transaction spends any of the given outpoints.
|
||||||
func (c *BitcoindClient) NotifySpent(outPoints []*wire.OutPoint) error {
|
func (c *BitcoindClient) NotifySpent(outPoints []*wire.OutPoint) error {
|
||||||
c.NotifyBlocks()
|
_ = c.NotifyBlocks()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case c.rescanUpdate <- outPoints:
|
case c.rescanUpdate <- outPoints:
|
||||||
|
@ -249,7 +249,7 @@ func (c *BitcoindClient) NotifySpent(outPoints []*wire.OutPoint) error {
|
||||||
// NotifyTx allows the chain backend to notify the caller whenever any of the
|
// NotifyTx allows the chain backend to notify the caller whenever any of the
|
||||||
// given transactions confirm within the chain.
|
// given transactions confirm within the chain.
|
||||||
func (c *BitcoindClient) NotifyTx(txids []chainhash.Hash) error {
|
func (c *BitcoindClient) NotifyTx(txids []chainhash.Hash) error {
|
||||||
c.NotifyBlocks()
|
_ = c.NotifyBlocks()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case c.rescanUpdate <- txids:
|
case c.rescanUpdate <- txids:
|
||||||
|
@ -371,6 +371,8 @@ func (c *BitcoindClient) RescanBlocks(
|
||||||
|
|
||||||
rescannedBlocks := make([]btcjson.RescannedBlock, 0, len(blockHashes))
|
rescannedBlocks := make([]btcjson.RescannedBlock, 0, len(blockHashes))
|
||||||
for _, hash := range blockHashes {
|
for _, hash := range blockHashes {
|
||||||
|
hash := hash
|
||||||
|
|
||||||
header, err := c.GetBlockHeaderVerbose(&hash)
|
header, err := c.GetBlockHeaderVerbose(&hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Unable to get header %s from bitcoind: %s",
|
log.Warnf("Unable to get header %s from bitcoind: %s",
|
||||||
|
@ -618,7 +620,7 @@ func (c *BitcoindClient) ntfnHandler() {
|
||||||
newBlockHeight := bestBlock.Height + 1
|
newBlockHeight := bestBlock.Height + 1
|
||||||
_ = c.filterBlock(newBlock, newBlockHeight, true)
|
_ = c.filterBlock(newBlock, newBlockHeight, true)
|
||||||
|
|
||||||
// With the block succesfully filtered, we'll
|
// With the block successfully filtered, we'll
|
||||||
// make it our new best block.
|
// make it our new best block.
|
||||||
bestBlock.Hash = newBlock.BlockHash()
|
bestBlock.Hash = newBlock.BlockHash()
|
||||||
bestBlock.Height = newBlockHeight
|
bestBlock.Height = newBlockHeight
|
||||||
|
@ -847,10 +849,11 @@ func (c *BitcoindClient) reorg(currentBlock waddrmgr.BlockStamp,
|
||||||
|
|
||||||
// Our current block should now reflect the previous one to
|
// Our current block should now reflect the previous one to
|
||||||
// continue the common ancestor search.
|
// continue the common ancestor search.
|
||||||
currentHeader, err = c.GetBlockHeader(¤tHeader.PrevBlock)
|
prevBlock := ¤tHeader.PrevBlock
|
||||||
|
currentHeader, err = c.GetBlockHeader(prevBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to get block header for %v: %v",
|
return fmt.Errorf("unable to get block header for %v: %v",
|
||||||
currentHeader.PrevBlock, err)
|
prevBlock, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
currentBlock.Height--
|
currentBlock.Height--
|
||||||
|
|
26
chain/log.go
26
chain/log.go
|
@ -28,29 +28,3 @@ func DisableLog() {
|
||||||
func UseLogger(logger btclog.Logger) {
|
func UseLogger(logger btclog.Logger) {
|
||||||
log = logger
|
log = logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogClosure is a closure that can be printed with %v to be used to
|
|
||||||
// generate expensive-to-create data for a detailed log level and avoid doing
|
|
||||||
// the work if the data isn't printed.
|
|
||||||
type logClosure func() string
|
|
||||||
|
|
||||||
// String invokes the log closure and returns the results string.
|
|
||||||
func (c logClosure) String() string {
|
|
||||||
return c()
|
|
||||||
}
|
|
||||||
|
|
||||||
// newLogClosure returns a new closure over the passed function which allows
|
|
||||||
// it to be used as a parameter in a logging function that is only invoked when
|
|
||||||
// the logging level is such that the message will actually be logged.
|
|
||||||
func newLogClosure(c func() string) logClosure {
|
|
||||||
return logClosure(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// pickNoun returns the singular or plural form of a noun depending
|
|
||||||
// on the count n.
|
|
||||||
func pickNoun(n int, singular, plural string) string {
|
|
||||||
if n == 1 {
|
|
||||||
return singular
|
|
||||||
}
|
|
||||||
return plural
|
|
||||||
}
|
|
||||||
|
|
|
@ -66,7 +66,10 @@ func (s *NeutrinoClient) BackEnd() string {
|
||||||
|
|
||||||
// Start replicates the RPC client's Start method.
|
// Start replicates the RPC client's Start method.
|
||||||
func (s *NeutrinoClient) Start() error {
|
func (s *NeutrinoClient) Start() error {
|
||||||
s.CS.Start()
|
if err := s.CS.Start(); err != nil {
|
||||||
|
return fmt.Errorf("error starting chain service: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
s.clientMtx.Lock()
|
s.clientMtx.Lock()
|
||||||
defer s.clientMtx.Unlock()
|
defer s.clientMtx.Unlock()
|
||||||
if !s.started {
|
if !s.started {
|
||||||
|
@ -178,8 +181,8 @@ func (s *NeutrinoClient) SendRawTransaction(tx *wire.MsgTx, allowHighFees bool)
|
||||||
// FilterBlocks scans the blocks contained in the FilterBlocksRequest for any
|
// FilterBlocks scans the blocks contained in the FilterBlocksRequest for any
|
||||||
// addresses of interest. For each requested block, the corresponding compact
|
// addresses of interest. For each requested block, the corresponding compact
|
||||||
// filter will first be checked for matches, skipping those that do not report
|
// filter will first be checked for matches, skipping those that do not report
|
||||||
// anything. If the filter returns a postive match, the full block will be
|
// anything. If the filter returns a positive match, the full block will be
|
||||||
// fetched and filtered. This method returns a FilterBlocksReponse for the first
|
// fetched and filtered. This method returns a FilterBlocksResponse for the first
|
||||||
// block containing a matching address. If no matches are found in the range of
|
// block containing a matching address. If no matches are found in the range of
|
||||||
// blocks requested, the returned response will be nil.
|
// blocks requested, the returned response will be nil.
|
||||||
func (s *NeutrinoClient) FilterBlocks(
|
func (s *NeutrinoClient) FilterBlocks(
|
||||||
|
@ -361,11 +364,11 @@ func (s *NeutrinoClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Addre
|
||||||
|
|
||||||
bestBlock, err := s.CS.BestBlock()
|
bestBlock, err := s.CS.BestBlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Can't get chain service's best block: %s", err)
|
return fmt.Errorf("can't get chain service's best block: %s", err)
|
||||||
}
|
}
|
||||||
header, err := s.CS.GetBlockHeader(&bestBlock.Hash)
|
header, err := s.CS.GetBlockHeader(&bestBlock.Hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Can't get block header for hash %v: %s",
|
return fmt.Errorf("can't get block header for hash %v: %s",
|
||||||
bestBlock.Hash, err)
|
bestBlock.Hash, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,7 +387,7 @@ func (s *NeutrinoClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Addre
|
||||||
select {
|
select {
|
||||||
case s.enqueueNotification <- &RescanFinished{
|
case s.enqueueNotification <- &RescanFinished{
|
||||||
Hash: startHash,
|
Hash: startHash,
|
||||||
Height: int32(bestBlock.Height),
|
Height: bestBlock.Height,
|
||||||
Time: header.Timestamp,
|
Time: header.Timestamp,
|
||||||
}:
|
}:
|
||||||
case <-s.quit:
|
case <-s.quit:
|
||||||
|
|
12
chain/rpc.go
12
chain/rpc.go
|
@ -154,8 +154,8 @@ func (c *RPCClient) IsCurrent() bool {
|
||||||
return bestHeader.Timestamp.After(time.Now().Add(-isCurrentDelta))
|
return bestHeader.Timestamp.After(time.Now().Add(-isCurrentDelta))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rescan wraps the normal Rescan command with an additional paramter that
|
// Rescan wraps the normal Rescan command with an additional parameter that
|
||||||
// allows us to map an oupoint to the address in the chain that it pays to.
|
// allows us to map an outpoint to the address in the chain that it pays to.
|
||||||
// This is useful when using BIP 158 filters as they include the prev pkScript
|
// This is useful when using BIP 158 filters as they include the prev pkScript
|
||||||
// rather than the full outpoint.
|
// rather than the full outpoint.
|
||||||
func (c *RPCClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Address,
|
func (c *RPCClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Address,
|
||||||
|
@ -163,10 +163,12 @@ func (c *RPCClient) Rescan(startHash *chainhash.Hash, addrs []btcutil.Address,
|
||||||
|
|
||||||
flatOutpoints := make([]*wire.OutPoint, 0, len(outPoints))
|
flatOutpoints := make([]*wire.OutPoint, 0, len(outPoints))
|
||||||
for ops := range outPoints {
|
for ops := range outPoints {
|
||||||
|
ops := ops
|
||||||
|
|
||||||
flatOutpoints = append(flatOutpoints, &ops)
|
flatOutpoints = append(flatOutpoints, &ops)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Client.Rescan(startHash, addrs, flatOutpoints)
|
return c.Client.Rescan(startHash, addrs, flatOutpoints) // nolint:staticcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitForShutdown blocks until both the client has finished disconnecting
|
// WaitForShutdown blocks until both the client has finished disconnecting
|
||||||
|
@ -198,8 +200,8 @@ func (c *RPCClient) BlockStamp() (*waddrmgr.BlockStamp, error) {
|
||||||
// FilterBlocks scans the blocks contained in the FilterBlocksRequest for any
|
// FilterBlocks scans the blocks contained in the FilterBlocksRequest for any
|
||||||
// addresses of interest. For each requested block, the corresponding compact
|
// addresses of interest. For each requested block, the corresponding compact
|
||||||
// filter will first be checked for matches, skipping those that do not report
|
// filter will first be checked for matches, skipping those that do not report
|
||||||
// anything. If the filter returns a postive match, the full block will be
|
// anything. If the filter returns a positive match, the full block will be
|
||||||
// fetched and filtered. This method returns a FilterBlocksReponse for the first
|
// fetched and filtered. This method returns a FilterBlocksResponse for the first
|
||||||
// block containing a matching address. If no matches are found in the range of
|
// block containing a matching address. If no matches are found in the range of
|
||||||
// blocks requested, the returned response will be nil.
|
// blocks requested, the returned response will be nil.
|
||||||
func (c *RPCClient) FilterBlocks(
|
func (c *RPCClient) FilterBlocks(
|
||||||
|
|
|
@ -146,6 +146,8 @@ func makeInputSource(outputs []btcjson.ListUnspentResult) txauthor.InputSource {
|
||||||
sourceErr error
|
sourceErr error
|
||||||
)
|
)
|
||||||
for _, output := range outputs {
|
for _, output := range outputs {
|
||||||
|
output := output
|
||||||
|
|
||||||
outputAmount, err := btcutil.NewAmount(output.Amount)
|
outputAmount, err := btcutil.NewAmount(output.Amount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sourceErr = fmt.Errorf(
|
sourceErr = fmt.Errorf(
|
||||||
|
@ -315,7 +317,8 @@ func sweep() error {
|
||||||
totalSwept, numPublished, transactionNoun)
|
totalSwept, numPublished, transactionNoun)
|
||||||
}
|
}
|
||||||
if numErrors > 0 {
|
if numErrors > 0 {
|
||||||
return fmt.Errorf("Failed to publish %d %s", numErrors, transactionNoun)
|
return fmt.Errorf("failed to publish %d %s", numErrors,
|
||||||
|
transactionNoun)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
28
config.go
28
config.go
|
@ -198,7 +198,7 @@ func parseAndSetDebugLevels(debugLevel string) error {
|
||||||
if !strings.Contains(debugLevel, ",") && !strings.Contains(debugLevel, "=") {
|
if !strings.Contains(debugLevel, ",") && !strings.Contains(debugLevel, "=") {
|
||||||
// Validate debug log level.
|
// Validate debug log level.
|
||||||
if !validLogLevel(debugLevel) {
|
if !validLogLevel(debugLevel) {
|
||||||
str := "The specified debug level [%v] is invalid"
|
str := "the specified debug level [%v] is invalid"
|
||||||
return fmt.Errorf(str, debugLevel)
|
return fmt.Errorf(str, debugLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ func parseAndSetDebugLevels(debugLevel string) error {
|
||||||
// issues and update the log levels accordingly.
|
// issues and update the log levels accordingly.
|
||||||
for _, logLevelPair := range strings.Split(debugLevel, ",") {
|
for _, logLevelPair := range strings.Split(debugLevel, ",") {
|
||||||
if !strings.Contains(logLevelPair, "=") {
|
if !strings.Contains(logLevelPair, "=") {
|
||||||
str := "The specified debug level contains an invalid " +
|
str := "the specified debug level contains an invalid " +
|
||||||
"subsystem/level pair [%v]"
|
"subsystem/level pair [%v]"
|
||||||
return fmt.Errorf(str, logLevelPair)
|
return fmt.Errorf(str, logLevelPair)
|
||||||
}
|
}
|
||||||
|
@ -223,14 +223,14 @@ func parseAndSetDebugLevels(debugLevel string) error {
|
||||||
|
|
||||||
// Validate subsystem.
|
// Validate subsystem.
|
||||||
if _, exists := subsystemLoggers[subsysID]; !exists {
|
if _, exists := subsystemLoggers[subsysID]; !exists {
|
||||||
str := "The specified subsystem [%v] is invalid -- " +
|
str := "the specified subsystem [%v] is invalid -- " +
|
||||||
"supported subsytems %v"
|
"supported subsytems %v"
|
||||||
return fmt.Errorf(str, subsysID, supportedSubsystems())
|
return fmt.Errorf(str, subsysID, supportedSubsystems())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate log level.
|
// Validate log level.
|
||||||
if !validLogLevel(logLevel) {
|
if !validLogLevel(logLevel) {
|
||||||
str := "The specified debug level [%v] is invalid"
|
str := "the specified debug level [%v] is invalid"
|
||||||
return fmt.Errorf(str, logLevel)
|
return fmt.Errorf(str, logLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,8 +418,8 @@ func loadConfig() (*config, []string, error) {
|
||||||
dbPath := filepath.Join(netDir, wallet.WalletDBName)
|
dbPath := filepath.Join(netDir, wallet.WalletDBName)
|
||||||
|
|
||||||
if cfg.CreateTemp && cfg.Create {
|
if cfg.CreateTemp && cfg.Create {
|
||||||
err := fmt.Errorf("The flags --create and --createtemp can not " +
|
err := fmt.Errorf("the flags --create and --createtemp can not " +
|
||||||
"be specified together. Use --help for more information.")
|
"be specified together. Use --help for more information")
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -430,7 +430,7 @@ func loadConfig() (*config, []string, error) {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.CreateTemp {
|
if cfg.CreateTemp { // nolint:gocritic
|
||||||
tempWalletExists := false
|
tempWalletExists := false
|
||||||
|
|
||||||
if dbFileExists {
|
if dbFileExists {
|
||||||
|
@ -457,8 +457,8 @@ func loadConfig() (*config, []string, error) {
|
||||||
// Error if the create flag is set and the wallet already
|
// Error if the create flag is set and the wallet already
|
||||||
// exists.
|
// exists.
|
||||||
if dbFileExists {
|
if dbFileExists {
|
||||||
err := fmt.Errorf("The wallet database file `%v` "+
|
err := fmt.Errorf("the wallet database file `%v` "+
|
||||||
"already exists.", dbPath)
|
"already exists", dbPath)
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -485,11 +485,11 @@ func loadConfig() (*config, []string, error) {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if !keystoreExists {
|
if !keystoreExists {
|
||||||
err = fmt.Errorf("The wallet does not exist. Run with the " +
|
err = fmt.Errorf("the wallet does not exist, run with " +
|
||||||
"--create option to initialize and create it.")
|
"the --create option to initialize and create it")
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("The wallet is in legacy format. Run with the " +
|
err = fmt.Errorf("the wallet is in legacy format, run " +
|
||||||
"--create option to import it.")
|
"with the --create option to import it")
|
||||||
}
|
}
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -605,7 +605,7 @@ func loadConfig() (*config, []string, error) {
|
||||||
for _, addr := range cfg.ExperimentalRPCListeners {
|
for _, addr := range cfg.ExperimentalRPCListeners {
|
||||||
_, seen := seenAddresses[addr]
|
_, seen := seenAddresses[addr]
|
||||||
if seen {
|
if seen {
|
||||||
err := fmt.Errorf("Address `%s` may not be "+
|
err := fmt.Errorf("address `%s` may not be "+
|
||||||
"used as a listener address for both "+
|
"used as a listener address for both "+
|
||||||
"RPC servers", addr)
|
"RPC servers", addr)
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
|
|
@ -22,12 +22,12 @@ func NewAmountFlag(defaultValue btcutil.Amount) *AmountFlag {
|
||||||
return &AmountFlag{defaultValue}
|
return &AmountFlag{defaultValue}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalFlag satisifes the flags.Marshaler interface.
|
// MarshalFlag satisfies the flags.Marshaler interface.
|
||||||
func (a *AmountFlag) MarshalFlag() (string, error) {
|
func (a *AmountFlag) MarshalFlag() (string, error) {
|
||||||
return a.Amount.String(), nil
|
return a.Amount.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalFlag satisifes the flags.Unmarshaler interface.
|
// UnmarshalFlag satisfies the flags.Unmarshaler interface.
|
||||||
func (a *AmountFlag) UnmarshalFlag(value string) error {
|
func (a *AmountFlag) UnmarshalFlag(value string) error {
|
||||||
value = strings.TrimSuffix(value, " BTC")
|
value = strings.TrimSuffix(value, " BTC")
|
||||||
valueF64, err := strconv.ParseFloat(value, 64)
|
valueF64, err := strconv.ParseFloat(value, 64)
|
||||||
|
|
|
@ -38,10 +38,6 @@ const (
|
||||||
|
|
||||||
// Length in bytes of KDF output.
|
// Length in bytes of KDF output.
|
||||||
kdfOutputBytes = 32
|
kdfOutputBytes = 32
|
||||||
|
|
||||||
// Maximum length in bytes of a comment that can have a size represented
|
|
||||||
// as a uint16.
|
|
||||||
maxCommentLen = (1 << 16) - 1
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -66,9 +62,9 @@ var fileID = [8]byte{0xba, 'W', 'A', 'L', 'L', 'E', 'T', 0x00}
|
||||||
type entryHeader byte
|
type entryHeader byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
addrCommentHeader entryHeader = 1 << iota
|
addrCommentHeader entryHeader = 1 << iota //nolint:varcheck,deadcode,unused
|
||||||
txCommentHeader
|
txCommentHeader // nolint:varcheck,deadcode,unused
|
||||||
deletedHeader
|
deletedHeader // nolint:varcheck,deadcode,unused
|
||||||
scriptHeader
|
scriptHeader
|
||||||
addrHeader entryHeader = 0
|
addrHeader entryHeader = 0
|
||||||
)
|
)
|
||||||
|
@ -233,9 +229,6 @@ func chainedPubKey(pubkey, chaincode []byte) ([]byte, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
newX, newY := btcec.S256().ScalarMult(oldPk.X, oldPk.Y, xorbytes)
|
newX, newY := btcec.S256().ScalarMult(oldPk.X, oldPk.Y, xorbytes)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
newPk := &btcec.PublicKey{
|
newPk := &btcec.PublicKey{
|
||||||
Curve: btcec.S256(),
|
Curve: btcec.S256(),
|
||||||
X: newX,
|
X: newX,
|
||||||
|
@ -501,9 +494,6 @@ func (net *netParams) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
|
||||||
// Stringified byte slices for use as map lookup keys.
|
// Stringified byte slices for use as map lookup keys.
|
||||||
type addressKey string
|
type addressKey string
|
||||||
type transactionHashKey string
|
|
||||||
|
|
||||||
type comment []byte
|
|
||||||
|
|
||||||
func getAddressKey(addr btcutil.Address) addressKey {
|
func getAddressKey(addr btcutil.Address) addressKey {
|
||||||
return addressKey(addr.ScriptAddress())
|
return addressKey(addr.ScriptAddress())
|
||||||
|
@ -775,7 +765,7 @@ func (s *Store) writeTo(w io.Writer) (n int64, err error) {
|
||||||
importedAddrs = append(importedAddrs, e)
|
importedAddrs = append(importedAddrs, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wts = append(chainedAddrs, importedAddrs...)
|
wts = append(chainedAddrs, importedAddrs...) // nolint:gocritic
|
||||||
appendedEntries := varEntries{store: s, entries: wts}
|
appendedEntries := varEntries{store: s, entries: wts}
|
||||||
|
|
||||||
// Iterate through each entry needing to be written. If data
|
// Iterate through each entry needing to be written. If data
|
||||||
|
@ -1376,7 +1366,7 @@ func (s *Store) SyncedTo() (hash *chainhash.Hash, height int32) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return // nolint:nakedret
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIterateRecentBlocks returns an iterator for recently-seen blocks.
|
// NewIterateRecentBlocks returns an iterator for recently-seen blocks.
|
||||||
|
@ -2108,7 +2098,7 @@ func (k *publicKey) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
n += read
|
n += read
|
||||||
|
|
||||||
*k = append([]byte{format}, s...)
|
*k = append([]byte{format}, s...)
|
||||||
return
|
return // nolint:nakedret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *publicKey) WriteTo(w io.Writer) (n int64, err error) {
|
func (k *publicKey) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
|
@ -2595,8 +2585,7 @@ func (a *btcAddress) ExportPrivKey() (*btcutil.WIF, error) {
|
||||||
// as our program's assumptions are so broken that this needs to be
|
// as our program's assumptions are so broken that this needs to be
|
||||||
// caught immediately, and a stack trace here is more useful than
|
// caught immediately, and a stack trace here is more useful than
|
||||||
// elsewhere.
|
// elsewhere.
|
||||||
wif, err := btcutil.NewWIF((*btcec.PrivateKey)(pk), a.store.netParams(),
|
wif, err := btcutil.NewWIF(pk, a.store.netParams(), a.Compressed())
|
||||||
a.Compressed())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -703,7 +703,7 @@ func TestImportPrivateKey(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// import priv key
|
// import priv key
|
||||||
wif, err := btcutil.NewWIF((*btcec.PrivateKey)(pk), tstNetParams, false)
|
wif, err := btcutil.NewWIF(pk, tstNetParams, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -761,8 +761,8 @@ func TestImportPrivateKey(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark imported address as partially synced with a block somewhere inbetween
|
// Mark imported address as partially synced with a block somewhere in
|
||||||
// the import height and the chain height.
|
// between the import height and the chain height.
|
||||||
partialHeight := (createHeight-importHeight)/2 + importHeight
|
partialHeight := (createHeight-importHeight)/2 + importHeight
|
||||||
if err := w2.SetSyncStatus(address, PartialSync(partialHeight)); err != nil {
|
if err := w2.SetSyncStatus(address, PartialSync(partialHeight)); err != nil {
|
||||||
t.Errorf("Cannot mark address partially synced: %v", err)
|
t.Errorf("Cannot mark address partially synced: %v", err)
|
||||||
|
@ -1037,7 +1037,7 @@ func TestImportScript(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if sinfo.RequiredSigs() != sinfo.RequiredSigs() {
|
if sinfo.RequiredSigs() != sinfo2.RequiredSigs() {
|
||||||
t.Errorf("original and serailised scriptinfo requiredsigs "+
|
t.Errorf("original and serailised scriptinfo requiredsigs "+
|
||||||
"don't match %d != %d", sinfo.RequiredSigs(),
|
"don't match %d != %d", sinfo.RequiredSigs(),
|
||||||
sinfo2.RequiredSigs())
|
sinfo2.RequiredSigs())
|
||||||
|
@ -1063,8 +1063,8 @@ func TestImportScript(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark imported address as partially synced with a block somewhere inbetween
|
// Mark imported address as partially synced with a block somewhere in
|
||||||
// the import height and the chain height.
|
// between the import height and the chain height.
|
||||||
partialHeight := (createHeight-importHeight)/2 + importHeight
|
partialHeight := (createHeight-importHeight)/2 + importHeight
|
||||||
if err := w2.SetSyncStatus(address, PartialSync(partialHeight)); err != nil {
|
if err := w2.SetSyncStatus(address, PartialSync(partialHeight)); err != nil {
|
||||||
t.Errorf("Cannot mark address partially synced: %v", err)
|
t.Errorf("Cannot mark address partially synced: %v", err)
|
||||||
|
|
|
@ -100,8 +100,10 @@ func promptList(reader *bufio.Reader, prefix string, validResponses []string, de
|
||||||
|
|
||||||
// promptListBool prompts the user for a boolean (yes/no) with the given prefix.
|
// promptListBool prompts the user for a boolean (yes/no) with the given prefix.
|
||||||
// The function will repeat the prompt to the user until they enter a valid
|
// The function will repeat the prompt to the user until they enter a valid
|
||||||
// reponse.
|
// response.
|
||||||
func promptListBool(reader *bufio.Reader, prefix string, defaultEntry string) (bool, error) {
|
func promptListBool(reader *bufio.Reader, prefix string,
|
||||||
|
defaultEntry string) (bool, error) { // nolint:unparam
|
||||||
|
|
||||||
// Setup the valid responses.
|
// Setup the valid responses.
|
||||||
valid := []string{"n", "no", "y", "yes"}
|
valid := []string{"n", "no", "y", "yes"}
|
||||||
response, err := promptList(reader, prefix, valid, defaultEntry)
|
response, err := promptList(reader, prefix, valid, defaultEntry)
|
||||||
|
@ -114,7 +116,7 @@ func promptListBool(reader *bufio.Reader, prefix string, defaultEntry string) (b
|
||||||
// promptPass prompts the user for a passphrase with the given prefix. The
|
// 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
|
// function will ask the user to confirm the passphrase and will repeat the
|
||||||
// prompts until they enter a matching response.
|
// prompts until they enter a matching response.
|
||||||
func promptPass(reader *bufio.Reader, prefix string, confirm bool) ([]byte, error) {
|
func promptPass(_ *bufio.Reader, prefix string, confirm bool) ([]byte, error) {
|
||||||
// Prompt the user until they enter a passphrase.
|
// Prompt the user until they enter a passphrase.
|
||||||
prompt := fmt.Sprintf("%s: ", prefix)
|
prompt := fmt.Sprintf("%s: ", prefix)
|
||||||
for {
|
for {
|
||||||
|
@ -177,7 +179,7 @@ func PrivatePass(reader *bufio.Reader, legacyKeyStore *keystore.Store) ([]byte,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep prompting the user until the passphrase is correct.
|
// Keep prompting the user until the passphrase is correct.
|
||||||
if err := legacyKeyStore.Unlock([]byte(privPass)); err != nil {
|
if err := legacyKeyStore.Unlock(privPass); err != nil {
|
||||||
if err == keystore.ErrWrongPassphrase {
|
if err == keystore.ErrWrongPassphrase {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
continue
|
continue
|
||||||
|
|
15
log.go
15
log.go
|
@ -26,8 +26,8 @@ import (
|
||||||
type logWriter struct{}
|
type logWriter struct{}
|
||||||
|
|
||||||
func (logWriter) Write(p []byte) (n int, err error) {
|
func (logWriter) Write(p []byte) (n int, err error) {
|
||||||
os.Stdout.Write(p)
|
_, _ = os.Stdout.Write(p)
|
||||||
logRotatorPipe.Write(p)
|
_, _ = logRotatorPipe.Write(p)
|
||||||
return len(p), nil
|
return len(p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ func initLogRotator(logFile string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pr, pw := io.Pipe()
|
pr, pw := io.Pipe()
|
||||||
go r.Run(pr)
|
go func() { _ = r.Run(pr) }()
|
||||||
|
|
||||||
logRotator = r
|
logRotator = r
|
||||||
logRotatorPipe = pw
|
logRotatorPipe = pw
|
||||||
|
@ -132,12 +132,3 @@ func setLogLevels(logLevel string) {
|
||||||
setLogLevel(subsystemID, logLevel)
|
setLogLevel(subsystemID, logLevel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pickNoun returns the singular or plural form of a noun depending
|
|
||||||
// on the count n.
|
|
||||||
func pickNoun(n int, singular, plural string) string {
|
|
||||||
if n == 1 {
|
|
||||||
return singular
|
|
||||||
}
|
|
||||||
return plural
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(jrick): There are several error paths which 'replace' various errors
|
// TODO(jrick): There are several error paths which 'replace' various errors
|
||||||
// with a more appropiate error from the btcjson package. Create a map of
|
// with a more appropriate error from the btcjson package. Create a map of
|
||||||
// these replacements so they can be handled once after an RPC handler has
|
// these replacements so they can be handled once after an RPC handler has
|
||||||
// returned and before the error is marshaled.
|
// returned and before the error is marshaled.
|
||||||
|
|
||||||
|
|
|
@ -30,11 +30,10 @@ import (
|
||||||
"github.com/btcsuite/btcwallet/wtxmgr"
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
// confirmed checks whether a transaction at height txHeight has met minconf
|
const (
|
||||||
// confirmations for a blockchain at height curHeight.
|
// defaultAccountName is the name of the wallet's default account.
|
||||||
func confirmed(minconf, txHeight, curHeight int32) bool {
|
defaultAccountName = "default"
|
||||||
return confirms(txHeight, curHeight) >= minconf
|
)
|
||||||
}
|
|
||||||
|
|
||||||
// confirms returns the number of confirmations for a transaction in a block at
|
// confirms returns the number of confirmations for a transaction in a block at
|
||||||
// height txHeight (or -1 for an unconfirmed tx) given the chain height
|
// height txHeight (or -1 for an unconfirmed tx) given the chain height
|
||||||
|
@ -51,7 +50,7 @@ func confirms(txHeight, curHeight int32) int32 {
|
||||||
// requestHandler is a handler function to handle an unmarshaled and parsed
|
// requestHandler is a handler function to handle an unmarshaled and parsed
|
||||||
// request into a marshalable response. If the error is a *btcjson.RPCError
|
// request into a marshalable response. If the error is a *btcjson.RPCError
|
||||||
// or any of the above special error classes, the server will respond with
|
// or any of the above special error classes, the server will respond with
|
||||||
// the JSON-RPC appropiate error code. All other errors use the wallet
|
// the JSON-RPC appropriate error code. All other errors use the wallet
|
||||||
// catch-all error code, btcjson.ErrRPCWallet.
|
// catch-all error code, btcjson.ErrRPCWallet.
|
||||||
type requestHandler func(interface{}, *wallet.Wallet) (interface{}, error)
|
type requestHandler func(interface{}, *wallet.Wallet) (interface{}, error)
|
||||||
|
|
||||||
|
@ -139,7 +138,7 @@ var rpcHandlers = map[string]struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// unimplemented handles an unimplemented RPC request with the
|
// unimplemented handles an unimplemented RPC request with the
|
||||||
// appropiate error.
|
// appropriate error.
|
||||||
func unimplemented(interface{}, *wallet.Wallet) (interface{}, error) {
|
func unimplemented(interface{}, *wallet.Wallet) (interface{}, error) {
|
||||||
return nil, &btcjson.RPCError{
|
return nil, &btcjson.RPCError{
|
||||||
Code: btcjson.ErrRPCUnimplemented,
|
Code: btcjson.ErrRPCUnimplemented,
|
||||||
|
@ -274,8 +273,7 @@ func jsonError(err error) *btcjson.RPCError {
|
||||||
case ParseError:
|
case ParseError:
|
||||||
code = btcjson.ErrRPCParse.Code
|
code = btcjson.ErrRPCParse.Code
|
||||||
case waddrmgr.ManagerError:
|
case waddrmgr.ManagerError:
|
||||||
switch e.ErrorCode {
|
if e.ErrorCode == waddrmgr.ErrWrongPassphrase {
|
||||||
case waddrmgr.ErrWrongPassphrase:
|
|
||||||
code = btcjson.ErrRPCWalletPassphraseIncorrect
|
code = btcjson.ErrRPCWalletPassphraseIncorrect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -375,7 +373,7 @@ func createMultiSig(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// dumpPrivKey handles a dumpprivkey request with the private key
|
// dumpPrivKey handles a dumpprivkey request with the private key
|
||||||
// for a single address, or an appropiate error if the wallet
|
// for a single address, or an appropriate error if the wallet
|
||||||
// is locked.
|
// is locked.
|
||||||
func dumpPrivKey(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
func dumpPrivKey(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
cmd := icmd.(*btcjson.DumpPrivKeyCmd)
|
cmd := icmd.(*btcjson.DumpPrivKeyCmd)
|
||||||
|
@ -394,18 +392,6 @@ func dumpPrivKey(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
return key, err
|
return key, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// dumpWallet handles a dumpwallet request by returning all private
|
|
||||||
// keys in a wallet, or an appropiate error if the wallet is locked.
|
|
||||||
// TODO: finish this to match bitcoind by writing the dump to a file.
|
|
||||||
func dumpWallet(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
|
||||||
keys, err := w.DumpPrivKeys()
|
|
||||||
if waddrmgr.IsError(err, waddrmgr.ErrLocked) {
|
|
||||||
return nil, &ErrWalletUnlockNeeded
|
|
||||||
}
|
|
||||||
|
|
||||||
return keys, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// getAddressesByAccount handles a getaddressesbyaccount request by returning
|
// getAddressesByAccount handles a getaddressesbyaccount request by returning
|
||||||
// all addresses for an account, or an error if the requested account does
|
// all addresses for an account, or an error if the requested account does
|
||||||
// not exist.
|
// not exist.
|
||||||
|
@ -584,7 +570,7 @@ func getAccountAddress(icmd interface{}, w *wallet.Wallet) (interface{}, error)
|
||||||
func getUnconfirmedBalance(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
func getUnconfirmedBalance(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
cmd := icmd.(*btcjson.GetUnconfirmedBalanceCmd)
|
cmd := icmd.(*btcjson.GetUnconfirmedBalanceCmd)
|
||||||
|
|
||||||
acctName := "default"
|
acctName := defaultAccountName
|
||||||
if cmd.Account != nil {
|
if cmd.Account != nil {
|
||||||
acctName = *cmd.Account
|
acctName = *cmd.Account
|
||||||
}
|
}
|
||||||
|
@ -669,7 +655,7 @@ func createNewAccount(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// renameAccount handles a renameaccount request by renaming an account.
|
// renameAccount handles a renameaccount request by renaming an account.
|
||||||
// If the account does not exist an appropiate error will be returned.
|
// If the account does not exist an appropriate error will be returned.
|
||||||
func renameAccount(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
func renameAccount(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
cmd := icmd.(*btcjson.RenameAccountCmd)
|
cmd := icmd.(*btcjson.RenameAccountCmd)
|
||||||
|
|
||||||
|
@ -688,14 +674,14 @@ func renameAccount(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getNewAddress handles a getnewaddress request by returning a new
|
// getNewAddress handles a getnewaddress request by returning a new
|
||||||
// address for an account. If the account does not exist an appropiate
|
// address for an account. If the account does not exist an appropriate
|
||||||
// error is returned.
|
// error is returned.
|
||||||
// TODO: Follow BIP 0044 and warn if number of unused addresses exceeds
|
// TODO: Follow BIP 0044 and warn if number of unused addresses exceeds
|
||||||
// the gap limit.
|
// the gap limit.
|
||||||
func getNewAddress(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
func getNewAddress(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
cmd := icmd.(*btcjson.GetNewAddressCmd)
|
cmd := icmd.(*btcjson.GetNewAddressCmd)
|
||||||
|
|
||||||
acctName := "default"
|
acctName := defaultAccountName
|
||||||
if cmd.Account != nil {
|
if cmd.Account != nil {
|
||||||
acctName = *cmd.Account
|
acctName = *cmd.Account
|
||||||
}
|
}
|
||||||
|
@ -720,7 +706,7 @@ func getNewAddress(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
func getRawChangeAddress(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
func getRawChangeAddress(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
cmd := icmd.(*btcjson.GetRawChangeAddressCmd)
|
cmd := icmd.(*btcjson.GetRawChangeAddressCmd)
|
||||||
|
|
||||||
acctName := "default"
|
acctName := defaultAccountName
|
||||||
if cmd.Account != nil {
|
if cmd.Account != nil {
|
||||||
acctName = *cmd.Account
|
acctName = *cmd.Account
|
||||||
}
|
}
|
||||||
|
@ -926,8 +912,8 @@ func getTransaction(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
// localeHelpDescs maps from locale strings (e.g. "en_US") to a function that
|
// localeHelpDescs maps from locale strings (e.g. "en_US") to a function that
|
||||||
// builds a map of help texts for each RPC server method. This prevents help
|
// builds a map of help texts for each RPC server method. This prevents help
|
||||||
// text maps for every locale map from being rooted and created during init.
|
// text maps for every locale map from being rooted and created during init.
|
||||||
// Instead, the appropiate function is looked up when help text is first needed
|
// Instead, the appropriate function is looked up when help text is first needed
|
||||||
// using the current locale and saved to the global below for futher reuse.
|
// using the current locale and saved to the global below for further reuse.
|
||||||
//
|
//
|
||||||
// requestUsages contains single line usages for every supported request,
|
// requestUsages contains single line usages for every supported request,
|
||||||
// separated by newlines. It is set during init. These usages are used for all
|
// separated by newlines. It is set during init. These usages are used for all
|
||||||
|
@ -958,11 +944,11 @@ func helpNoChainRPC(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
// methods, or full help for a specific method. The chainClient is optional,
|
// methods, or full help for a specific method. The chainClient is optional,
|
||||||
// and this is simply a helper function for the HelpNoChainRPC and
|
// and this is simply a helper function for the HelpNoChainRPC and
|
||||||
// HelpWithChainRPC handlers.
|
// HelpWithChainRPC handlers.
|
||||||
func help(icmd interface{}, w *wallet.Wallet, chainClient *chain.RPCClient) (interface{}, error) {
|
func help(icmd interface{}, _ *wallet.Wallet, chainClient *chain.RPCClient) (interface{}, error) {
|
||||||
cmd := icmd.(*btcjson.HelpCmd)
|
cmd := icmd.(*btcjson.HelpCmd)
|
||||||
|
|
||||||
// btcd returns different help messages depending on the kind of
|
// btcd returns different help messages depending on the kind of
|
||||||
// connection the client is using. Only methods availble to HTTP POST
|
// connection the client is using. Only methods available to HTTP POST
|
||||||
// clients are available to be used by wallet clients, even though
|
// clients are available to be used by wallet clients, even though
|
||||||
// wallet itself is a websocket client to btcd. Therefore, create a
|
// wallet itself is a websocket client to btcd. Therefore, create a
|
||||||
// POST client as needed.
|
// POST client as needed.
|
||||||
|
@ -1177,7 +1163,7 @@ func listReceivedByAddress(icmd interface{}, w *wallet.Wallet) (interface{}, err
|
||||||
|
|
||||||
// Massage address data into output format.
|
// Massage address data into output format.
|
||||||
numAddresses := len(allAddrData)
|
numAddresses := len(allAddrData)
|
||||||
ret := make([]btcjson.ListReceivedByAddressResult, numAddresses, numAddresses)
|
ret := make([]btcjson.ListReceivedByAddressResult, numAddresses)
|
||||||
idx := 0
|
idx := 0
|
||||||
for address, addrData := range allAddrData {
|
for address, addrData := range allAddrData {
|
||||||
ret[idx] = btcjson.ListReceivedByAddressResult{
|
ret[idx] = btcjson.ListReceivedByAddressResult{
|
||||||
|
@ -1185,6 +1171,7 @@ func listReceivedByAddress(icmd interface{}, w *wallet.Wallet) (interface{}, err
|
||||||
Amount: addrData.amount.ToBTC(),
|
Amount: addrData.amount.ToBTC(),
|
||||||
Confirmations: uint64(addrData.confirmations),
|
Confirmations: uint64(addrData.confirmations),
|
||||||
TxIDs: addrData.tx,
|
TxIDs: addrData.tx,
|
||||||
|
Account: addrData.account,
|
||||||
}
|
}
|
||||||
idx++
|
idx++
|
||||||
}
|
}
|
||||||
|
@ -1386,8 +1373,7 @@ func sendPairs(w *wallet.Wallet, amounts map[string]btcutil.Amount,
|
||||||
if waddrmgr.IsError(err, waddrmgr.ErrLocked) {
|
if waddrmgr.IsError(err, waddrmgr.ErrLocked) {
|
||||||
return "", &ErrWalletUnlockNeeded
|
return "", &ErrWalletUnlockNeeded
|
||||||
}
|
}
|
||||||
switch err.(type) {
|
if _, ok := err.(btcjson.RPCError); ok {
|
||||||
case btcjson.RPCError:
|
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1558,8 +1544,8 @@ func signMessage(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
wire.WriteVarString(&buf, 0, "Bitcoin Signed Message:\n")
|
_ = wire.WriteVarString(&buf, 0, "Bitcoin Signed Message:\n")
|
||||||
wire.WriteVarString(&buf, 0, cmd.Message)
|
_ = wire.WriteVarString(&buf, 0, cmd.Message)
|
||||||
messageHash := chainhash.DoubleHashB(buf.Bytes())
|
messageHash := chainhash.DoubleHashB(buf.Bytes())
|
||||||
sigbytes, err := btcec.SignCompact(btcec.S256(), privKey,
|
sigbytes, err := btcec.SignCompact(btcec.S256(), privKey,
|
||||||
messageHash, true)
|
messageHash, true)
|
||||||
|
@ -1600,7 +1586,7 @@ func signRawTransaction(icmd interface{}, w *wallet.Wallet, chainClient *chain.R
|
||||||
case "SINGLE|ANYONECANPAY":
|
case "SINGLE|ANYONECANPAY":
|
||||||
hashType = txscript.SigHashSingle | txscript.SigHashAnyOneCanPay
|
hashType = txscript.SigHashSingle | txscript.SigHashAnyOneCanPay
|
||||||
default:
|
default:
|
||||||
e := errors.New("Invalid sighash parameter")
|
e := errors.New("invalid sighash parameter")
|
||||||
return nil, InvalidParameterError{e}
|
return nil, InvalidParameterError{e}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1719,7 +1705,7 @@ func signRawTransaction(icmd interface{}, w *wallet.Wallet, chainClient *chain.R
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.Grow(tx.SerializeSize())
|
buf.Grow(tx.SerializeSize())
|
||||||
|
|
||||||
// All returned errors (not OOM, which panics) encounted during
|
// All returned errors (not OOM, which panics) encountered during
|
||||||
// bytes.Buffer writes are unexpected.
|
// bytes.Buffer writes are unexpected.
|
||||||
if err = tx.Serialize(&buf); err != nil {
|
if err = tx.Serialize(&buf); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -1845,8 +1831,8 @@ func verifyMessage(icmd interface{}, w *wallet.Wallet) (interface{}, error) {
|
||||||
// Validate the signature - this just shows that it was valid at all.
|
// Validate the signature - this just shows that it was valid at all.
|
||||||
// we will compare it with the key next.
|
// we will compare it with the key next.
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
wire.WriteVarString(&buf, 0, "Bitcoin Signed Message:\n")
|
_ = wire.WriteVarString(&buf, 0, "Bitcoin Signed Message:\n")
|
||||||
wire.WriteVarString(&buf, 0, cmd.Message)
|
_ = wire.WriteVarString(&buf, 0, cmd.Message)
|
||||||
expectedMessageHash := chainhash.DoubleHashB(buf.Bytes())
|
expectedMessageHash := chainhash.DoubleHashB(buf.Bytes())
|
||||||
pk, wasCompressed, err := btcec.RecoverCompact(btcec.S256(), sig,
|
pk, wasCompressed, err := btcec.RecoverCompact(btcec.S256(), sig,
|
||||||
expectedMessageHash)
|
expectedMessageHash)
|
||||||
|
|
|
@ -30,6 +30,7 @@ func TestThrottle(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
codes <- res.StatusCode
|
codes <- res.StatusCode
|
||||||
|
_ = res.Body.Close()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
|
@ -58,12 +57,11 @@ func (c *websocketClient) send(b []byte) error {
|
||||||
// Server holds the items the RPC server may need to access (auth,
|
// Server holds the items the RPC server may need to access (auth,
|
||||||
// config, shutdown, etc.)
|
// config, shutdown, etc.)
|
||||||
type Server struct {
|
type Server struct {
|
||||||
httpServer http.Server
|
httpServer http.Server
|
||||||
wallet *wallet.Wallet
|
wallet *wallet.Wallet
|
||||||
walletLoader *wallet.Loader
|
walletLoader *wallet.Loader
|
||||||
chainClient chain.Interface
|
chainClient chain.Interface
|
||||||
handlerLookup func(string) (requestHandler, bool)
|
handlerMu sync.Mutex
|
||||||
handlerMu sync.Mutex
|
|
||||||
|
|
||||||
listeners []net.Listener
|
listeners []net.Listener
|
||||||
authsha [sha256.Size]byte
|
authsha [sha256.Size]byte
|
||||||
|
@ -326,7 +324,7 @@ func throttled(threshold int64, h http.Handler) http.Handler {
|
||||||
|
|
||||||
if current-1 >= threshold {
|
if current-1 >= threshold {
|
||||||
log.Warnf("Reached threshold of %d concurrent active clients", threshold)
|
log.Warnf("Reached threshold of %d concurrent active clients", threshold)
|
||||||
http.Error(w, "429 Too Many Requests", 429)
|
http.Error(w, "429 Too Many Requests", http.StatusTooManyRequests)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,24 +332,6 @@ func throttled(threshold int64, h http.Handler) http.Handler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// sanitizeRequest returns a sanitized string for the request which may be
|
|
||||||
// safely logged. It is intended to strip private keys, passphrases, and any
|
|
||||||
// other secrets from request parameters before they may be saved to a log file.
|
|
||||||
func sanitizeRequest(r *btcjson.Request) string {
|
|
||||||
// These are considered unsafe to log, so sanitize parameters.
|
|
||||||
switch r.Method {
|
|
||||||
case "encryptwallet", "importprivkey", "importwallet",
|
|
||||||
"signrawtransaction", "walletpassphrase",
|
|
||||||
"walletpassphrasechange":
|
|
||||||
|
|
||||||
return fmt.Sprintf(`{"id":%v,"method":"%s","params":SANITIZED %d parameters}`,
|
|
||||||
r.ID, r.Method, len(r.Params))
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf(`{"id":%v,"method":"%s","params":%v}`, r.ID,
|
|
||||||
r.Method, r.Params)
|
|
||||||
}
|
|
||||||
|
|
||||||
// idPointer returns a pointer to the passed ID, or nil if the interface is nil.
|
// idPointer returns a pointer to the passed ID, or nil if the interface is nil.
|
||||||
// Interface pointers are usually a red flag of doing something incorrectly,
|
// Interface pointers are usually a red flag of doing something incorrectly,
|
||||||
// but this is only implemented here to work around an oddity with btcjson,
|
// but this is only implemented here to work around an oddity with btcjson,
|
||||||
|
@ -473,7 +453,7 @@ out:
|
||||||
break out
|
break out
|
||||||
}
|
}
|
||||||
s.requestProcessShutdown()
|
s.requestProcessShutdown()
|
||||||
break
|
break out
|
||||||
|
|
||||||
default:
|
default:
|
||||||
req := req // Copy for the closure
|
req := req // Copy for the closure
|
||||||
|
|
|
@ -25,7 +25,7 @@ import (
|
||||||
|
|
||||||
// UseLogger sets the logger to use for the gRPC server.
|
// UseLogger sets the logger to use for the gRPC server.
|
||||||
func UseLogger(l btclog.Logger) {
|
func UseLogger(l btclog.Logger) {
|
||||||
grpclog.SetLogger(logger{l})
|
grpclog.SetLogger(logger{l}) // nolint:staticcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
// logger uses a btclog.Logger to implement the grpclog.Logger interface.
|
// logger uses a btclog.Logger to implement the grpclog.Logger interface.
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/rpcclient"
|
"github.com/btcsuite/btcd/rpcclient"
|
||||||
|
@ -49,7 +50,7 @@ const (
|
||||||
semverPatch = 1
|
semverPatch = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
// translateError creates a new gRPC error with an appropiate error code for
|
// translateError creates a new gRPC error with an appropriate error code for
|
||||||
// recognized errors.
|
// recognized errors.
|
||||||
//
|
//
|
||||||
// This function is by no means complete and should be expanded based on other
|
// This function is by no means complete and should be expanded based on other
|
||||||
|
@ -57,7 +58,7 @@ const (
|
||||||
// should return this result instead.
|
// should return this result instead.
|
||||||
func translateError(err error) error {
|
func translateError(err error) error {
|
||||||
code := errorCode(err)
|
code := errorCode(err)
|
||||||
return grpc.Errorf(code, "%s", err.Error())
|
return status.Errorf(code, "%s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func errorCode(err error) codes.Code {
|
func errorCode(err error) codes.Code {
|
||||||
|
@ -202,7 +203,7 @@ func (s *walletServer) NextAccount(ctx context.Context, req *pb.NextAccountReque
|
||||||
defer zero.Bytes(req.Passphrase)
|
defer zero.Bytes(req.Passphrase)
|
||||||
|
|
||||||
if req.AccountName == "" {
|
if req.AccountName == "" {
|
||||||
return nil, grpc.Errorf(codes.InvalidArgument, "account name may not be empty")
|
return nil, status.Errorf(codes.InvalidArgument, "account name may not be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
lock := make(chan time.Time, 1)
|
lock := make(chan time.Time, 1)
|
||||||
|
@ -235,7 +236,7 @@ func (s *walletServer) NextAddress(ctx context.Context, req *pb.NextAddressReque
|
||||||
case pb.NextAddressRequest_BIP0044_INTERNAL:
|
case pb.NextAddressRequest_BIP0044_INTERNAL:
|
||||||
addr, err = s.wallet.NewChangeAddress(req.Account, waddrmgr.KeyScopeBIP0044)
|
addr, err = s.wallet.NewChangeAddress(req.Account, waddrmgr.KeyScopeBIP0044)
|
||||||
default:
|
default:
|
||||||
return nil, grpc.Errorf(codes.InvalidArgument, "kind=%v", req.Kind)
|
return nil, status.Errorf(codes.InvalidArgument, "kind=%v", req.Kind)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, translateError(err)
|
return nil, translateError(err)
|
||||||
|
@ -251,7 +252,7 @@ func (s *walletServer) ImportPrivateKey(ctx context.Context, req *pb.ImportPriva
|
||||||
|
|
||||||
wif, err := btcutil.DecodeWIF(req.PrivateKeyWif)
|
wif, err := btcutil.DecodeWIF(req.PrivateKeyWif)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, grpc.Errorf(codes.InvalidArgument,
|
return nil, status.Errorf(codes.InvalidArgument,
|
||||||
"Invalid WIF-encoded private key: %v", err)
|
"Invalid WIF-encoded private key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +268,7 @@ func (s *walletServer) ImportPrivateKey(ctx context.Context, req *pb.ImportPriva
|
||||||
// At the moment, only the special-cased import account can be used to
|
// At the moment, only the special-cased import account can be used to
|
||||||
// import keys.
|
// import keys.
|
||||||
if req.Account != waddrmgr.ImportedAddrAccount {
|
if req.Account != waddrmgr.ImportedAddrAccount {
|
||||||
return nil, grpc.Errorf(codes.InvalidArgument,
|
return nil, status.Errorf(codes.InvalidArgument,
|
||||||
"Only the imported account accepts private key imports")
|
"Only the imported account accepts private key imports")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,24 +300,6 @@ func (s *walletServer) Balance(ctx context.Context, req *pb.BalanceRequest) (
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// confirmed checks whether a transaction at height txHeight has met minconf
|
|
||||||
// confirmations for a blockchain at height curHeight.
|
|
||||||
func confirmed(minconf, txHeight, curHeight int32) bool {
|
|
||||||
return confirms(txHeight, curHeight) >= minconf
|
|
||||||
}
|
|
||||||
|
|
||||||
// confirms returns the number of confirmations for a transaction in a block at
|
|
||||||
// height txHeight (or -1 for an unconfirmed tx) given the chain height
|
|
||||||
// curHeight.
|
|
||||||
func confirms(txHeight, curHeight int32) int32 {
|
|
||||||
switch {
|
|
||||||
case txHeight == -1, txHeight > curHeight:
|
|
||||||
return 0
|
|
||||||
default:
|
|
||||||
return curHeight - txHeight + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *walletServer) FundTransaction(ctx context.Context, req *pb.FundTransactionRequest) (
|
func (s *walletServer) FundTransaction(ctx context.Context, req *pb.FundTransactionRequest) (
|
||||||
*pb.FundTransactionResponse, error) {
|
*pb.FundTransactionResponse, error) {
|
||||||
|
|
||||||
|
@ -383,26 +366,26 @@ func (s *walletServer) GetTransactions(ctx context.Context, req *pb.GetTransacti
|
||||||
resp *pb.GetTransactionsResponse, err error) {
|
resp *pb.GetTransactionsResponse, err error) {
|
||||||
|
|
||||||
var startBlock, endBlock *wallet.BlockIdentifier
|
var startBlock, endBlock *wallet.BlockIdentifier
|
||||||
if req.StartingBlockHash != nil && req.StartingBlockHeight != 0 {
|
if req.StartingBlockHash != nil && req.StartingBlockHeight != 0 { // nolint:gocritic
|
||||||
return nil, errors.New(
|
return nil, errors.New(
|
||||||
"starting block hash and height may not be specified simultaneously")
|
"starting block hash and height may not be specified simultaneously")
|
||||||
} else if req.StartingBlockHash != nil {
|
} else if req.StartingBlockHash != nil {
|
||||||
startBlockHash, err := chainhash.NewHash(req.StartingBlockHash)
|
startBlockHash, err := chainhash.NewHash(req.StartingBlockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, grpc.Errorf(codes.InvalidArgument, "%s", err.Error())
|
return nil, status.Errorf(codes.InvalidArgument, "%s", err.Error())
|
||||||
}
|
}
|
||||||
startBlock = wallet.NewBlockIdentifierFromHash(startBlockHash)
|
startBlock = wallet.NewBlockIdentifierFromHash(startBlockHash)
|
||||||
} else if req.StartingBlockHeight != 0 {
|
} else if req.StartingBlockHeight != 0 {
|
||||||
startBlock = wallet.NewBlockIdentifierFromHeight(req.StartingBlockHeight)
|
startBlock = wallet.NewBlockIdentifierFromHeight(req.StartingBlockHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.EndingBlockHash != nil && req.EndingBlockHeight != 0 {
|
if req.EndingBlockHash != nil && req.EndingBlockHeight != 0 { // nolint:gocritic
|
||||||
return nil, grpc.Errorf(codes.InvalidArgument,
|
return nil, status.Errorf(codes.InvalidArgument,
|
||||||
"ending block hash and height may not be specified simultaneously")
|
"ending block hash and height may not be specified simultaneously")
|
||||||
} else if req.EndingBlockHash != nil {
|
} else if req.EndingBlockHash != nil {
|
||||||
endBlockHash, err := chainhash.NewHash(req.EndingBlockHash)
|
endBlockHash, err := chainhash.NewHash(req.EndingBlockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, grpc.Errorf(codes.InvalidArgument, "%s", err.Error())
|
return nil, status.Errorf(codes.InvalidArgument, "%s", err.Error())
|
||||||
}
|
}
|
||||||
endBlock = wallet.NewBlockIdentifierFromHash(endBlockHash)
|
endBlock = wallet.NewBlockIdentifierFromHash(endBlockHash)
|
||||||
} else if req.EndingBlockHeight != 0 {
|
} else if req.EndingBlockHeight != 0 {
|
||||||
|
@ -412,13 +395,13 @@ func (s *walletServer) GetTransactions(ctx context.Context, req *pb.GetTransacti
|
||||||
var minRecentTxs int
|
var minRecentTxs int
|
||||||
if req.MinimumRecentTransactions != 0 {
|
if req.MinimumRecentTransactions != 0 {
|
||||||
if endBlock != nil {
|
if endBlock != nil {
|
||||||
return nil, grpc.Errorf(codes.InvalidArgument,
|
return nil, status.Errorf(codes.InvalidArgument,
|
||||||
"ending block and minimum number of recent transactions "+
|
"ending block and minimum number of recent transactions "+
|
||||||
"may not be specified simultaneously")
|
"may not be specified simultaneously")
|
||||||
}
|
}
|
||||||
minRecentTxs = int(req.MinimumRecentTransactions)
|
minRecentTxs = int(req.MinimumRecentTransactions)
|
||||||
if minRecentTxs < 0 {
|
if minRecentTxs < 0 {
|
||||||
return nil, grpc.Errorf(codes.InvalidArgument,
|
return nil, status.Errorf(codes.InvalidArgument,
|
||||||
"minimum number of recent transactions may not be negative")
|
"minimum number of recent transactions may not be negative")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -447,7 +430,7 @@ func (s *walletServer) ChangePassphrase(ctx context.Context, req *pb.ChangePassp
|
||||||
case pb.ChangePassphraseRequest_PUBLIC:
|
case pb.ChangePassphraseRequest_PUBLIC:
|
||||||
err = s.wallet.ChangePublicPassphrase(req.OldPassphrase, req.NewPassphrase)
|
err = s.wallet.ChangePublicPassphrase(req.OldPassphrase, req.NewPassphrase)
|
||||||
default:
|
default:
|
||||||
return nil, grpc.Errorf(codes.InvalidArgument, "Unknown key type (%d)", req.Key)
|
return nil, status.Errorf(codes.InvalidArgument, "Unknown key type (%d)", req.Key)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, translateError(err)
|
return nil, translateError(err)
|
||||||
|
@ -465,7 +448,7 @@ func (s *walletServer) SignTransaction(ctx context.Context, req *pb.SignTransact
|
||||||
var tx wire.MsgTx
|
var tx wire.MsgTx
|
||||||
err := tx.Deserialize(bytes.NewReader(req.SerializedTransaction))
|
err := tx.Deserialize(bytes.NewReader(req.SerializedTransaction))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, grpc.Errorf(codes.InvalidArgument,
|
return nil, status.Errorf(codes.InvalidArgument,
|
||||||
"Bytes do not represent a valid raw transaction: %v", err)
|
"Bytes do not represent a valid raw transaction: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,7 +498,7 @@ func (s *walletServer) PublishTransaction(ctx context.Context, req *pb.PublishTr
|
||||||
var msgTx wire.MsgTx
|
var msgTx wire.MsgTx
|
||||||
err := msgTx.Deserialize(bytes.NewReader(req.SignedTransaction))
|
err := msgTx.Deserialize(bytes.NewReader(req.SignedTransaction))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, grpc.Errorf(codes.InvalidArgument,
|
return nil, status.Errorf(codes.InvalidArgument,
|
||||||
"Bytes do not represent a valid raw transaction: %v", err)
|
"Bytes do not represent a valid raw transaction: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,18 +574,6 @@ func marshalHashes(v []*chainhash.Hash) [][]byte {
|
||||||
return hashes
|
return hashes
|
||||||
}
|
}
|
||||||
|
|
||||||
func marshalAccountBalances(v []wallet.AccountBalance) []*pb.AccountBalance {
|
|
||||||
balances := make([]*pb.AccountBalance, len(v))
|
|
||||||
for i := range v {
|
|
||||||
balance := &v[i]
|
|
||||||
balances[i] = &pb.AccountBalance{
|
|
||||||
Account: balance.Account,
|
|
||||||
TotalBalance: int64(balance.TotalBalance),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return balances
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *walletServer) TransactionNotifications(req *pb.TransactionNotificationsRequest,
|
func (s *walletServer) TransactionNotifications(req *pb.TransactionNotificationsRequest,
|
||||||
svr pb.WalletService_TransactionNotificationsServer) error {
|
svr pb.WalletService_TransactionNotificationsServer) error {
|
||||||
|
|
||||||
|
@ -634,7 +605,7 @@ func (s *walletServer) SpentnessNotifications(req *pb.SpentnessNotificationsRequ
|
||||||
svr pb.WalletService_SpentnessNotificationsServer) error {
|
svr pb.WalletService_SpentnessNotificationsServer) error {
|
||||||
|
|
||||||
if req.NoNotifyUnspent && req.NoNotifySpent {
|
if req.NoNotifyUnspent && req.NoNotifySpent {
|
||||||
return grpc.Errorf(codes.InvalidArgument,
|
return status.Errorf(codes.InvalidArgument,
|
||||||
"no_notify_unspent and no_notify_spent may not both be true")
|
"no_notify_unspent and no_notify_spent may not both be true")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -776,7 +747,7 @@ func (s *loaderServer) CloseWallet(ctx context.Context, req *pb.CloseWalletReque
|
||||||
|
|
||||||
err := s.loader.UnloadWallet()
|
err := s.loader.UnloadWallet()
|
||||||
if err == wallet.ErrNotLoaded {
|
if err == wallet.ErrNotLoaded {
|
||||||
return nil, grpc.Errorf(codes.FailedPrecondition, "wallet is not loaded")
|
return nil, status.Errorf(codes.FailedPrecondition, "wallet is not loaded")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, translateError(err)
|
return nil, translateError(err)
|
||||||
|
@ -785,8 +756,9 @@ func (s *loaderServer) CloseWallet(ctx context.Context, req *pb.CloseWalletReque
|
||||||
return &pb.CloseWalletResponse{}, nil
|
return &pb.CloseWalletResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *loaderServer) StartConsensusRpc(ctx context.Context, req *pb.StartConsensusRpcRequest) (
|
func (s *loaderServer) StartConsensusRpc(ctx context.Context, // nolint:golint
|
||||||
*pb.StartConsensusRpcResponse, error) {
|
req *pb.StartConsensusRpcRequest) (*pb.StartConsensusRpcResponse,
|
||||||
|
error) {
|
||||||
|
|
||||||
defer zero.Bytes(req.Password)
|
defer zero.Bytes(req.Password)
|
||||||
|
|
||||||
|
@ -794,20 +766,20 @@ func (s *loaderServer) StartConsensusRpc(ctx context.Context, req *pb.StartConse
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
|
|
||||||
if s.rpcClient != nil {
|
if s.rpcClient != nil {
|
||||||
return nil, grpc.Errorf(codes.FailedPrecondition, "RPC client already created")
|
return nil, status.Errorf(codes.FailedPrecondition, "RPC client already created")
|
||||||
}
|
}
|
||||||
|
|
||||||
networkAddress, err := cfgutil.NormalizeAddress(req.NetworkAddress,
|
networkAddress, err := cfgutil.NormalizeAddress(req.NetworkAddress,
|
||||||
s.activeNet.RPCClientPort)
|
s.activeNet.RPCClientPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, grpc.Errorf(codes.InvalidArgument,
|
return nil, status.Errorf(codes.InvalidArgument,
|
||||||
"Network address is ill-formed: %v", err)
|
"Network address is ill-formed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error if the wallet is already syncing with the network.
|
// Error if the wallet is already syncing with the network.
|
||||||
wallet, walletLoaded := s.loader.LoadedWallet()
|
wallet, walletLoaded := s.loader.LoadedWallet()
|
||||||
if walletLoaded && wallet.SynchronizingToNetwork() {
|
if walletLoaded && wallet.SynchronizingToNetwork() {
|
||||||
return nil, grpc.Errorf(codes.FailedPrecondition,
|
return nil, status.Errorf(codes.FailedPrecondition,
|
||||||
"wallet is loaded and already synchronizing")
|
"wallet is loaded and already synchronizing")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -820,10 +792,10 @@ func (s *loaderServer) StartConsensusRpc(ctx context.Context, req *pb.StartConse
|
||||||
err = rpcClient.Start()
|
err = rpcClient.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == rpcclient.ErrInvalidAuth {
|
if err == rpcclient.ErrInvalidAuth {
|
||||||
return nil, grpc.Errorf(codes.InvalidArgument,
|
return nil, status.Errorf(codes.InvalidArgument,
|
||||||
"Invalid RPC credentials: %v", err)
|
"Invalid RPC credentials: %v", err)
|
||||||
}
|
}
|
||||||
return nil, grpc.Errorf(codes.NotFound,
|
return nil, status.Errorf(codes.NotFound,
|
||||||
"Connection to RPC server failed: %v", err)
|
"Connection to RPC server failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,12 +60,15 @@ It has these top-level messages:
|
||||||
*/
|
*/
|
||||||
package walletrpc
|
package walletrpc
|
||||||
|
|
||||||
import proto "github.com/golang/protobuf/proto"
|
|
||||||
import fmt "fmt"
|
|
||||||
import math "math"
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
fmt "fmt"
|
||||||
|
|
||||||
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
|
math "math"
|
||||||
|
|
||||||
context "golang.org/x/net/context"
|
context "golang.org/x/net/context"
|
||||||
|
|
||||||
grpc "google.golang.org/grpc"
|
grpc "google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -222,7 +222,7 @@ func (sk *SecretKey) Decrypt(in []byte) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSecretKey returns a SecretKey structure based on the passed parameters.
|
// NewSecretKey returns a SecretKey structure based on the passed parameters.
|
||||||
func NewSecretKey(password *[]byte, N, r, p int) (*SecretKey, error) {
|
func NewSecretKey(password *[]byte, N, r, p int) (*SecretKey, error) { // nolint:gocritic
|
||||||
sk := SecretKey{
|
sk := SecretKey{
|
||||||
Key: (*CryptoKey)(&[KeySize]byte{}),
|
Key: (*CryptoKey)(&[KeySize]byte{}),
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,7 +130,6 @@ type managedAddress struct {
|
||||||
imported bool
|
imported bool
|
||||||
internal bool
|
internal bool
|
||||||
compressed bool
|
compressed bool
|
||||||
used bool
|
|
||||||
addrType AddressType
|
addrType AddressType
|
||||||
pubKey *btcec.PublicKey
|
pubKey *btcec.PublicKey
|
||||||
privKeyEncrypted []byte // nil if part of watch-only account
|
privKeyEncrypted []byte // nil if part of watch-only account
|
||||||
|
@ -367,7 +366,7 @@ func newManagedAddressWithoutPrivKey(m *ScopedKeyManager,
|
||||||
case NestedWitnessPubKey:
|
case NestedWitnessPubKey:
|
||||||
// For this address type we'l generate an address which is
|
// For this address type we'l generate an address which is
|
||||||
// backwards compatible to Bitcoin nodes running 0.6.0 onwards, but
|
// backwards compatible to Bitcoin nodes running 0.6.0 onwards, but
|
||||||
// allows us to take advantage of segwit's scripting improvments,
|
// allows us to take advantage of segwit's scripting improvements,
|
||||||
// and malleability fixes.
|
// and malleability fixes.
|
||||||
|
|
||||||
// First, we'll generate a normal p2wkh address from the pubkey hash.
|
// First, we'll generate a normal p2wkh address from the pubkey hash.
|
||||||
|
@ -511,7 +510,6 @@ type scriptAddress struct {
|
||||||
scriptEncrypted []byte
|
scriptEncrypted []byte
|
||||||
scriptCT []byte
|
scriptCT []byte
|
||||||
scriptMutex sync.Mutex
|
scriptMutex sync.Mutex
|
||||||
used bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enforce scriptAddress satisfies the ManagedScriptAddress interface.
|
// Enforce scriptAddress satisfies the ManagedScriptAddress interface.
|
||||||
|
|
|
@ -61,7 +61,7 @@ type syncStatus uint8
|
||||||
// of supporting sync status on a per-address basis.
|
// of supporting sync status on a per-address basis.
|
||||||
const (
|
const (
|
||||||
ssNone syncStatus = 0 // not iota as they need to be stable for db
|
ssNone syncStatus = 0 // not iota as they need to be stable for db
|
||||||
ssPartial syncStatus = 1
|
ssPartial syncStatus = 1 // nolint:varcheck,deadcode,unused
|
||||||
ssFull syncStatus = 2
|
ssFull syncStatus = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -294,14 +294,6 @@ func uint32ToBytes(number uint32) []byte {
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
// uint64ToBytes converts a 64 bit unsigned integer into a 8-byte slice in
|
|
||||||
// little-endian order: 1 -> [1 0 0 0 0 0 0 0].
|
|
||||||
func uint64ToBytes(number uint64) []byte {
|
|
||||||
buf := make([]byte, 8)
|
|
||||||
binary.LittleEndian.PutUint64(buf, number)
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// stringToBytes converts a string into a variable length byte slice in
|
// stringToBytes converts a string into a variable length byte slice in
|
||||||
// little-endian order: "abc" -> [3 0 0 0 61 62 63]
|
// little-endian order: "abc" -> [3 0 0 0 61 62 63]
|
||||||
func stringToBytes(s string) []byte {
|
func stringToBytes(s string) []byte {
|
||||||
|
@ -330,15 +322,6 @@ func scopeToBytes(scope *KeyScope) [scopeKeySize]byte {
|
||||||
return scopeBytes
|
return scopeBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
// scopeFromBytes decodes a serializes manager scope into its concrete manager
|
|
||||||
// scope struct.
|
|
||||||
func scopeFromBytes(scopeBytes []byte) KeyScope {
|
|
||||||
return KeyScope{
|
|
||||||
Purpose: binary.LittleEndian.Uint32(scopeBytes[:]),
|
|
||||||
Coin: binary.LittleEndian.Uint32(scopeBytes[4:]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// scopeSchemaToBytes encodes the passed scope schema as a set of bytes
|
// scopeSchemaToBytes encodes the passed scope schema as a set of bytes
|
||||||
// suitable for storage within the database.
|
// suitable for storage within the database.
|
||||||
func scopeSchemaToBytes(schema *ScopeAddrSchema) []byte {
|
func scopeSchemaToBytes(schema *ScopeAddrSchema) []byte {
|
||||||
|
@ -380,22 +363,6 @@ func fetchScopeAddrSchema(ns walletdb.ReadBucket,
|
||||||
return scopeSchemaFromBytes(schemaBytes), nil
|
return scopeSchemaFromBytes(schemaBytes), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// putScopeAddrSchema attempts to store the passed addr scehma for the given
|
|
||||||
// manager scope.
|
|
||||||
func putScopeAddrTypes(ns walletdb.ReadWriteBucket, scope *KeyScope,
|
|
||||||
schema *ScopeAddrSchema) error {
|
|
||||||
|
|
||||||
scopeSchemaBucket := ns.NestedReadWriteBucket(scopeSchemaBucketName)
|
|
||||||
if scopeSchemaBucket == nil {
|
|
||||||
str := fmt.Sprintf("unable to find scope schema bucket")
|
|
||||||
return managerError(ErrScopeNotFound, str, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
scopeKey := scopeToBytes(scope)
|
|
||||||
schemaBytes := scopeSchemaToBytes(schema)
|
|
||||||
return scopeSchemaBucket.Put(scopeKey[:], schemaBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchReadScopeBucket(ns walletdb.ReadBucket, scope *KeyScope) (walletdb.ReadBucket, error) {
|
func fetchReadScopeBucket(ns walletdb.ReadBucket, scope *KeyScope) (walletdb.ReadBucket, error) {
|
||||||
rootScopeBucket := ns.NestedReadBucket(scopeBucketName)
|
rootScopeBucket := ns.NestedReadBucket(scopeBucketName)
|
||||||
|
|
||||||
|
@ -590,7 +557,7 @@ func putMasterHDKeys(ns walletdb.ReadWriteBucket, masterHDPrivEnc, masterHDPubEn
|
||||||
// fetchMasterHDKeys attempts to fetch both the master HD private and public
|
// fetchMasterHDKeys attempts to fetch both the master HD private and public
|
||||||
// keys from the database. If this is a watch only wallet, then it's possible
|
// keys from the database. If this is a watch only wallet, then it's possible
|
||||||
// that the master private key isn't stored.
|
// that the master private key isn't stored.
|
||||||
func fetchMasterHDKeys(ns walletdb.ReadBucket) ([]byte, []byte, error) {
|
func fetchMasterHDKeys(ns walletdb.ReadBucket) ([]byte, []byte) {
|
||||||
bucket := ns.NestedReadBucket(mainBucketName)
|
bucket := ns.NestedReadBucket(mainBucketName)
|
||||||
|
|
||||||
var masterHDPrivEnc, masterHDPubEnc []byte
|
var masterHDPrivEnc, masterHDPubEnc []byte
|
||||||
|
@ -601,16 +568,16 @@ func fetchMasterHDKeys(ns walletdb.ReadBucket) ([]byte, []byte, error) {
|
||||||
key := bucket.Get(masterHDPrivName)
|
key := bucket.Get(masterHDPrivName)
|
||||||
if key != nil {
|
if key != nil {
|
||||||
masterHDPrivEnc = make([]byte, len(key))
|
masterHDPrivEnc = make([]byte, len(key))
|
||||||
copy(masterHDPrivEnc[:], key)
|
copy(masterHDPrivEnc, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
key = bucket.Get(masterHDPubName)
|
key = bucket.Get(masterHDPubName)
|
||||||
if key != nil {
|
if key != nil {
|
||||||
masterHDPubEnc = make([]byte, len(key))
|
masterHDPubEnc = make([]byte, len(key))
|
||||||
copy(masterHDPubEnc[:], key)
|
copy(masterHDPubEnc, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
return masterHDPrivEnc, masterHDPubEnc, nil
|
return masterHDPrivEnc, masterHDPubEnc
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchCryptoKeys loads the encrypted crypto keys which are in turn used to
|
// fetchCryptoKeys loads the encrypted crypto keys which are in turn used to
|
||||||
|
@ -993,7 +960,7 @@ func forEachKeyScope(ns walletdb.ReadBucket, fn func(KeyScope) error) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
scope := KeyScope{
|
scope := KeyScope{
|
||||||
Purpose: binary.LittleEndian.Uint32(k[:]),
|
Purpose: binary.LittleEndian.Uint32(k),
|
||||||
Coin: binary.LittleEndian.Uint32(k[4:]),
|
Coin: binary.LittleEndian.Uint32(k[4:]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1545,7 +1512,7 @@ func fetchAddressByHash(ns walletdb.ReadBucket, scope *KeyScope,
|
||||||
|
|
||||||
bucket := scopedBucket.NestedReadBucket(addrBucketName)
|
bucket := scopedBucket.NestedReadBucket(addrBucketName)
|
||||||
|
|
||||||
serializedRow := bucket.Get(addrHash[:])
|
serializedRow := bucket.Get(addrHash)
|
||||||
if serializedRow == nil {
|
if serializedRow == nil {
|
||||||
str := "address not found"
|
str := "address not found"
|
||||||
return nil, managerError(ErrAddressNotFound, str, nil)
|
return nil, managerError(ErrAddressNotFound, str, nil)
|
||||||
|
@ -2349,7 +2316,7 @@ func fetchBirthdayBlockVerification(ns walletdb.ReadBucket) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we'll determine if it's verified by the value stored.
|
// Otherwise, we'll determine if it's verified by the value stored.
|
||||||
verified := binary.BigEndian.Uint16(verifiedValue[:])
|
verified := binary.BigEndian.Uint16(verifiedValue)
|
||||||
return verified != 0
|
return verified != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2483,6 +2450,8 @@ func createManagerNS(ns walletdb.ReadWriteBucket,
|
||||||
// Next, we'll create the namespace for each of the relevant default
|
// Next, we'll create the namespace for each of the relevant default
|
||||||
// manager scopes.
|
// manager scopes.
|
||||||
for scope, scopeSchema := range defaultScopes {
|
for scope, scopeSchema := range defaultScopes {
|
||||||
|
scope, scopeSchema := scope, scopeSchema
|
||||||
|
|
||||||
// Before we create the entire namespace of this scope, we'll
|
// Before we create the entire namespace of this scope, we'll
|
||||||
// update the schema mapping to note what types of addresses it
|
// update the schema mapping to note what types of addresses it
|
||||||
// prefers.
|
// prefers.
|
||||||
|
|
|
@ -24,20 +24,3 @@ func DisableLog() {
|
||||||
func UseLogger(logger btclog.Logger) {
|
func UseLogger(logger btclog.Logger) {
|
||||||
log = logger
|
log = logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogClosure is a closure that can be printed with %v to be used to
|
|
||||||
// generate expensive-to-create data for a detailed log level and avoid doing
|
|
||||||
// the work if the data isn't printed.
|
|
||||||
type logClosure func() string
|
|
||||||
|
|
||||||
// String invokes the log closure and returns the results string.
|
|
||||||
func (c logClosure) String() string {
|
|
||||||
return c()
|
|
||||||
}
|
|
||||||
|
|
||||||
// newLogClosure returns a new closure over the passed function which allows
|
|
||||||
// it to be used as a parameter in a logging function that is only invoked when
|
|
||||||
// the logging level is such that the message will actually be logged.
|
|
||||||
func newLogClosure(c func() string) logClosure {
|
|
||||||
return logClosure(c)
|
|
||||||
}
|
|
||||||
|
|
|
@ -474,7 +474,6 @@ func (m *Manager) Close() {
|
||||||
m.masterKeyPub.Zero()
|
m.masterKeyPub.Zero()
|
||||||
|
|
||||||
m.closed = true
|
m.closed = true
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewScopedKeyManager creates a new scoped key manager from the root manager. A
|
// NewScopedKeyManager creates a new scoped key manager from the root manager. A
|
||||||
|
@ -509,10 +508,7 @@ func (m *Manager) NewScopedKeyManager(ns walletdb.ReadWriteBucket,
|
||||||
// Note that the path to the coin type is requires hardened
|
// Note that the path to the coin type is requires hardened
|
||||||
// derivation, therefore this can only be done if the wallet's
|
// derivation, therefore this can only be done if the wallet's
|
||||||
// root key hasn't been neutered.
|
// root key hasn't been neutered.
|
||||||
masterRootPrivEnc, _, err := fetchMasterHDKeys(ns)
|
masterRootPrivEnc, _ := fetchMasterHDKeys(ns)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the master root private key isn't found within the
|
// If the master root private key isn't found within the
|
||||||
// database, but we need to bail here as we can't create the
|
// database, but we need to bail here as we can't create the
|
||||||
|
@ -636,8 +632,7 @@ func (m *Manager) ScopesForExternalAddrType(addrType AddressType) []KeyScope {
|
||||||
m.mtx.RLock()
|
m.mtx.RLock()
|
||||||
defer m.mtx.RUnlock()
|
defer m.mtx.RUnlock()
|
||||||
|
|
||||||
scopes, _ := m.externalAddrSchemas[addrType]
|
return m.externalAddrSchemas[addrType]
|
||||||
return scopes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScopesForInternalAddrTypes returns the set of key scopes that are able to
|
// ScopesForInternalAddrTypes returns the set of key scopes that are able to
|
||||||
|
@ -646,8 +641,7 @@ func (m *Manager) ScopesForInternalAddrTypes(addrType AddressType) []KeyScope {
|
||||||
m.mtx.RLock()
|
m.mtx.RLock()
|
||||||
defer m.mtx.RUnlock()
|
defer m.mtx.RUnlock()
|
||||||
|
|
||||||
scopes, _ := m.internalAddrSchemas[addrType]
|
return m.internalAddrSchemas[addrType]
|
||||||
return scopes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NeuterRootKey is a special method that should be used once a caller is
|
// NeuterRootKey is a special method that should be used once a caller is
|
||||||
|
@ -658,10 +652,7 @@ func (m *Manager) NeuterRootKey(ns walletdb.ReadWriteBucket) error {
|
||||||
defer m.mtx.Unlock()
|
defer m.mtx.Unlock()
|
||||||
|
|
||||||
// First, we'll fetch the current master HD keys from the database.
|
// First, we'll fetch the current master HD keys from the database.
|
||||||
masterRootPrivEnc, _, err := fetchMasterHDKeys(ns)
|
masterRootPrivEnc, _ := fetchMasterHDKeys(ns)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the root master private key is already nil, then we'll return a
|
// If the root master private key is already nil, then we'll return a
|
||||||
// nil error here as the root key has already been permanently
|
// nil error here as the root key has already been permanently
|
||||||
|
@ -984,8 +975,8 @@ func (m *Manager) ChangePassphrase(ns walletdb.ReadWriteBucket, oldPassphrase,
|
||||||
|
|
||||||
// Now that the db has been successfully updated, clear the old
|
// Now that the db has been successfully updated, clear the old
|
||||||
// key and set the new one.
|
// key and set the new one.
|
||||||
copy(m.cryptoKeyPrivEncrypted[:], encPriv)
|
copy(m.cryptoKeyPrivEncrypted, encPriv)
|
||||||
copy(m.cryptoKeyScriptEncrypted[:], encScript)
|
copy(m.cryptoKeyScriptEncrypted, encScript)
|
||||||
m.masterKeyPriv.Zero() // Clear the old key.
|
m.masterKeyPriv.Zero() // Clear the old key.
|
||||||
m.masterKeyPriv = newMasterKey
|
m.masterKeyPriv = newMasterKey
|
||||||
m.privPassphraseSalt = passphraseSalt
|
m.privPassphraseSalt = passphraseSalt
|
||||||
|
@ -1421,7 +1412,7 @@ func deriveCoinTypeKey(masterNode *hdkeychain.ExtendedKey,
|
||||||
// The branch is 0 for external addresses and 1 for internal addresses.
|
// The branch is 0 for external addresses and 1 for internal addresses.
|
||||||
|
|
||||||
// Derive the purpose key as a child of the master node.
|
// Derive the purpose key as a child of the master node.
|
||||||
purpose, err := masterNode.DeriveNonStandard(
|
purpose, err := masterNode.DeriveNonStandard( // nolint:staticcheck
|
||||||
scope.Purpose + hdkeychain.HardenedKeyStart,
|
scope.Purpose + hdkeychain.HardenedKeyStart,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1429,7 +1420,7 @@ func deriveCoinTypeKey(masterNode *hdkeychain.ExtendedKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derive the coin type key as a child of the purpose key.
|
// Derive the coin type key as a child of the purpose key.
|
||||||
coinTypeKey, err := purpose.DeriveNonStandard(
|
coinTypeKey, err := purpose.DeriveNonStandard( // nolint:staticcheck
|
||||||
scope.Coin + hdkeychain.HardenedKeyStart,
|
scope.Coin + hdkeychain.HardenedKeyStart,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1454,7 +1445,7 @@ func deriveAccountKey(coinTypeKey *hdkeychain.ExtendedKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derive the account key as a child of the coin type key.
|
// Derive the account key as a child of the coin type key.
|
||||||
return coinTypeKey.DeriveNonStandard(
|
return coinTypeKey.DeriveNonStandard( // nolint:staticcheck
|
||||||
account + hdkeychain.HardenedKeyStart,
|
account + hdkeychain.HardenedKeyStart,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1471,12 +1462,12 @@ func deriveAccountKey(coinTypeKey *hdkeychain.ExtendedKey,
|
||||||
// The branch is 0 for external addresses and 1 for internal addresses.
|
// The branch is 0 for external addresses and 1 for internal addresses.
|
||||||
func checkBranchKeys(acctKey *hdkeychain.ExtendedKey) error {
|
func checkBranchKeys(acctKey *hdkeychain.ExtendedKey) error {
|
||||||
// Derive the external branch as the first child of the account key.
|
// Derive the external branch as the first child of the account key.
|
||||||
if _, err := acctKey.DeriveNonStandard(ExternalBranch); err != nil {
|
if _, err := acctKey.DeriveNonStandard(ExternalBranch); err != nil { // nolint:staticcheck
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derive the internal branch as the second child of the account key.
|
// Derive the internal branch as the second child of the account key.
|
||||||
_, err := acctKey.DeriveNonStandard(InternalBranch)
|
_, err := acctKey.DeriveNonStandard(InternalBranch) // nolint:staticcheck
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,18 +43,6 @@ func (c *failingCryptoKey) Decrypt(in []byte) ([]byte, error) {
|
||||||
return nil, errors.New("failed to decrypt")
|
return nil, errors.New("failed to decrypt")
|
||||||
}
|
}
|
||||||
|
|
||||||
// newHash converts the passed big-endian hex string into a chainhash.Hash.
|
|
||||||
// It only differs from the one available in wire in that it panics on an
|
|
||||||
// error since it will only (and must only) be called with hard-coded, and
|
|
||||||
// therefore known good, hashes.
|
|
||||||
func newHash(hexStr string) *chainhash.Hash {
|
|
||||||
hash, err := chainhash.NewHashFromStr(hexStr)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// failingSecretKeyGen is a SecretKeyGenerator that always returns
|
// failingSecretKeyGen is a SecretKeyGenerator that always returns
|
||||||
// snacl.ErrDecryptFailed.
|
// snacl.ErrDecryptFailed.
|
||||||
func failingSecretKeyGen(passphrase *[]byte,
|
func failingSecretKeyGen(passphrase *[]byte,
|
||||||
|
@ -97,7 +85,6 @@ type expectedAddr struct {
|
||||||
addressHash []byte
|
addressHash []byte
|
||||||
internal bool
|
internal bool
|
||||||
compressed bool
|
compressed bool
|
||||||
used bool
|
|
||||||
imported bool
|
imported bool
|
||||||
pubKey []byte
|
pubKey []byte
|
||||||
privKey []byte
|
privKey []byte
|
||||||
|
@ -628,11 +615,7 @@ func testInternalAddresses(tc *testContext) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
tc.unlocked = false
|
tc.unlocked = false
|
||||||
if !testResults() {
|
return testResults()
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// testLocking tests the basic locking semantics of the address manager work
|
// testLocking tests the basic locking semantics of the address manager work
|
||||||
|
@ -788,6 +771,8 @@ func testImportPrivateKey(tc *testContext) bool {
|
||||||
prefix := testNamePrefix(tc) + " testImportPrivateKey"
|
prefix := testNamePrefix(tc) + " testImportPrivateKey"
|
||||||
if tc.create {
|
if tc.create {
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
|
test := test
|
||||||
|
|
||||||
test.expected.privKeyWIF = test.in
|
test.expected.privKeyWIF = test.in
|
||||||
wif, err := btcutil.DecodeWIF(test.in)
|
wif, err := btcutil.DecodeWIF(test.in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -799,7 +784,9 @@ func testImportPrivateKey(tc *testContext) bool {
|
||||||
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
err = walletdb.Update(tc.db, func(tx walletdb.ReadWriteTx) error {
|
||||||
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
|
||||||
var err error
|
var err error
|
||||||
addr, err = tc.manager.ImportPrivateKey(ns, wif, &test.blockstamp)
|
addr, err = tc.manager.ImportPrivateKey(
|
||||||
|
ns, wif, &test.blockstamp,
|
||||||
|
)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -883,11 +870,7 @@ func testImportPrivateKey(tc *testContext) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
tc.unlocked = false
|
tc.unlocked = false
|
||||||
if !testResults() {
|
return testResults()
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// testImportScript tests that importing scripts works properly. It ensures
|
// testImportScript tests that importing scripts works properly. It ensures
|
||||||
|
@ -961,6 +944,7 @@ func testImportScript(tc *testContext) bool {
|
||||||
prefix := testNamePrefix(tc)
|
prefix := testNamePrefix(tc)
|
||||||
if tc.create {
|
if tc.create {
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
|
test := test
|
||||||
test.expected.script = test.in
|
test.expected.script = test.in
|
||||||
prefix := fmt.Sprintf("%s ImportScript #%d (%s)", prefix,
|
prefix := fmt.Sprintf("%s ImportScript #%d (%s)", prefix,
|
||||||
i, test.name)
|
i, test.name)
|
||||||
|
@ -1051,11 +1035,7 @@ func testImportScript(tc *testContext) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
tc.unlocked = false
|
tc.unlocked = false
|
||||||
if !testResults() {
|
return testResults()
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// testMarkUsed ensures used addresses are flagged as such.
|
// testMarkUsed ensures used addresses are flagged as such.
|
||||||
|
@ -1080,6 +1060,8 @@ func testMarkUsed(tc *testContext, doScript bool) bool {
|
||||||
prefix := fmt.Sprintf("(%s) MarkUsed", tc.caseName)
|
prefix := fmt.Sprintf("(%s) MarkUsed", tc.caseName)
|
||||||
chainParams := tc.manager.ChainParams()
|
chainParams := tc.manager.ChainParams()
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
|
i, test := i, test
|
||||||
|
|
||||||
if !doScript && test.typ == addrScriptHash {
|
if !doScript && test.typ == addrScriptHash {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -1380,10 +1362,7 @@ func testNewAccount(tc *testContext) bool {
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
wantErrCode = ErrInvalidAccount
|
wantErrCode = ErrInvalidAccount
|
||||||
if !checkManagerError(tc.t, testName, err, wantErrCode) {
|
return checkManagerError(tc.t, testName, err, wantErrCode)
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// testLookupAccount tests the basic account lookup func of the address manager
|
// testLookupAccount tests the basic account lookup func of the address manager
|
||||||
|
@ -1406,7 +1385,10 @@ func testLookupAccount(tc *testContext) bool {
|
||||||
|
|
||||||
func testLookupExpectedAccount(tc *testContext, expectedAccounts map[string]uint32,
|
func testLookupExpectedAccount(tc *testContext, expectedAccounts map[string]uint32,
|
||||||
expectedLastAccount uint32) bool {
|
expectedLastAccount uint32) bool {
|
||||||
|
|
||||||
for acctName, expectedAccount := range expectedAccounts {
|
for acctName, expectedAccount := range expectedAccounts {
|
||||||
|
acctName := acctName
|
||||||
|
|
||||||
var account uint32
|
var account uint32
|
||||||
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
|
err := walletdb.View(tc.db, func(tx walletdb.ReadTx) error {
|
||||||
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||||
|
@ -1445,6 +1427,10 @@ func testLookupExpectedAccount(tc *testContext, expectedAccounts map[string]uint
|
||||||
lastAccount, err = tc.manager.LastAccount(ns)
|
lastAccount, err = tc.manager.LastAccount(ns)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
tc.t.Errorf("LookupAccount: unexpected error: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lastAccount != expectedLastAccount {
|
if lastAccount != expectedLastAccount {
|
||||||
tc.t.Errorf("LookupAccount "+
|
tc.t.Errorf("LookupAccount "+
|
||||||
"account mismatch -- got %d, "+
|
"account mismatch -- got %d, "+
|
||||||
|
@ -1537,10 +1523,7 @@ func testRenameAccount(tc *testContext) bool {
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
wantErrCode = ErrAccountNotFound
|
wantErrCode = ErrAccountNotFound
|
||||||
if !checkManagerError(tc.t, testName, err, wantErrCode) {
|
return checkManagerError(tc.t, testName, err, wantErrCode)
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// testForEachAccount tests the retrieve all accounts func of the address
|
// testForEachAccount tests the retrieve all accounts func of the address
|
||||||
|
@ -2727,7 +2710,7 @@ func TestNewRawAccountHybrid(t *testing.T) {
|
||||||
testNewRawAccount(t, mgr, db, accountNum, scopedMgr)
|
testNewRawAccount(t, mgr, db, accountNum, scopedMgr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testNewRawAccount(t *testing.T, mgr *Manager, db walletdb.DB,
|
func testNewRawAccount(t *testing.T, _ *Manager, db walletdb.DB,
|
||||||
accountNum uint32, scopedMgr *ScopedKeyManager) {
|
accountNum uint32, scopedMgr *ScopedKeyManager) {
|
||||||
// With the account created, we should be able to derive new addresses
|
// With the account created, we should be able to derive new addresses
|
||||||
// from the account.
|
// from the account.
|
||||||
|
|
|
@ -93,7 +93,7 @@ func (m *MigrationManager) SetVersion(ns walletdb.ReadWriteBucket,
|
||||||
if ns == nil {
|
if ns == nil {
|
||||||
ns = m.ns
|
ns = m.ns
|
||||||
}
|
}
|
||||||
return putManagerVersion(m.ns, version)
|
return putManagerVersion(ns, version)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Versions returns all of the available database versions of the service.
|
// Versions returns all of the available database versions of the service.
|
||||||
|
|
|
@ -327,6 +327,7 @@ func TestMigrationStoreMaxReorgDepth(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
success := t.Run(testCase.name, func(t *testing.T) {
|
success := t.Run(testCase.name, func(t *testing.T) {
|
||||||
// We'll start the test by creating the number of blocks
|
// We'll start the test by creating the number of blocks
|
||||||
// we'll add to the chain. We start from height 1 as the
|
// we'll add to the chain. We start from height 1 as the
|
||||||
|
@ -387,13 +388,13 @@ func TestMigrationStoreMaxReorgDepth(t *testing.T) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
expectedBlock := blocks[len(blocks)-1]
|
expectedBlock := blocks[len(blocks)-1]
|
||||||
if block.Height != block.Height {
|
if expectedBlock.Height != block.Height {
|
||||||
return fmt.Errorf("expected synced to "+
|
return fmt.Errorf("expected synced to "+
|
||||||
"block height %v, got %v",
|
"block height %v, got %v",
|
||||||
expectedBlock.Height,
|
expectedBlock.Height,
|
||||||
block.Height)
|
block.Height)
|
||||||
}
|
}
|
||||||
if block.Hash != block.Hash {
|
if expectedBlock.Hash != block.Hash {
|
||||||
return fmt.Errorf("expected synced to "+
|
return fmt.Errorf("expected synced to "+
|
||||||
"block hash %v, got %v",
|
"block hash %v, got %v",
|
||||||
expectedBlock.Hash,
|
expectedBlock.Hash,
|
||||||
|
|
|
@ -255,7 +255,6 @@ func (s *ScopedKeyManager) Close() {
|
||||||
|
|
||||||
// Attempt to clear sensitive public key material from memory too.
|
// Attempt to clear sensitive public key material from memory too.
|
||||||
s.zeroSensitivePublicData()
|
s.zeroSensitivePublicData()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyToManaged returns a new managed address for the provided derived key and
|
// keyToManaged returns a new managed address for the provided derived key and
|
||||||
|
@ -318,15 +317,17 @@ func (s *ScopedKeyManager) deriveKey(acctInfo *accountInfo, branch,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derive and return the key.
|
// Derive and return the key.
|
||||||
branchKey, err := acctKey.DeriveNonStandard(branch)
|
branchKey, err := acctKey.DeriveNonStandard(branch) // nolint:staticcheck
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := fmt.Sprintf("failed to derive extended key branch %d",
|
str := fmt.Sprintf("failed to derive extended key branch %d",
|
||||||
branch)
|
branch)
|
||||||
return nil, managerError(ErrKeyChain, str, err)
|
return nil, managerError(ErrKeyChain, str, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
addressKey, err := branchKey.DeriveNonStandard(index)
|
addressKey, err := branchKey.DeriveNonStandard(index) // nolint:staticcheck
|
||||||
branchKey.Zero() // Zero branch key after it's used.
|
|
||||||
|
// Zero branch key after it's used.
|
||||||
|
branchKey.Zero()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := fmt.Sprintf("failed to derive child extended key -- "+
|
str := fmt.Sprintf("failed to derive child extended key -- "+
|
||||||
"branch %d, child %d",
|
"branch %d, child %d",
|
||||||
|
@ -430,7 +431,6 @@ func (s *ScopedKeyManager) loadAccountInfo(ns walletdb.ReadBucket,
|
||||||
return nil, managerError(ErrCrypto, str, err)
|
return nil, managerError(ErrCrypto, str, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
watchOnly = true
|
|
||||||
hasPrivateKey = false
|
hasPrivateKey = false
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -879,7 +879,7 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derive the appropriate branch key and ensure it is zeroed when done.
|
// Derive the appropriate branch key and ensure it is zeroed when done.
|
||||||
branchKey, err := acctKey.DeriveNonStandard(branchNum)
|
branchKey, err := acctKey.DeriveNonStandard(branchNum) // nolint:staticcheck
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := fmt.Sprintf("failed to derive extended key branch %d",
|
str := fmt.Sprintf("failed to derive extended key branch %d",
|
||||||
branchNum)
|
branchNum)
|
||||||
|
@ -896,7 +896,7 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket,
|
||||||
var nextKey *hdkeychain.ExtendedKey
|
var nextKey *hdkeychain.ExtendedKey
|
||||||
for {
|
for {
|
||||||
// Derive the next child in the external chain branch.
|
// Derive the next child in the external chain branch.
|
||||||
key, err := branchKey.DeriveNonStandard(nextIndex)
|
key, err := branchKey.DeriveNonStandard(nextIndex) // nolint:staticcheck
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// When this particular child is invalid, skip to the
|
// When this particular child is invalid, skip to the
|
||||||
// next index.
|
// next index.
|
||||||
|
@ -1081,7 +1081,7 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derive the appropriate branch key and ensure it is zeroed when done.
|
// Derive the appropriate branch key and ensure it is zeroed when done.
|
||||||
branchKey, err := acctKey.DeriveNonStandard(branchNum)
|
branchKey, err := acctKey.DeriveNonStandard(branchNum) // nolint:staticcheck
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := fmt.Sprintf("failed to derive extended key branch %d",
|
str := fmt.Sprintf("failed to derive extended key branch %d",
|
||||||
branchNum)
|
branchNum)
|
||||||
|
@ -1100,7 +1100,7 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket,
|
||||||
var nextKey *hdkeychain.ExtendedKey
|
var nextKey *hdkeychain.ExtendedKey
|
||||||
for {
|
for {
|
||||||
// Derive the next child in the external chain branch.
|
// Derive the next child in the external chain branch.
|
||||||
key, err := branchKey.DeriveNonStandard(nextIndex)
|
key, err := branchKey.DeriveNonStandard(nextIndex) // nolint:staticcheck
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// When this particular child is invalid, skip to the
|
// When this particular child is invalid, skip to the
|
||||||
// next index.
|
// next index.
|
||||||
|
|
|
@ -36,7 +36,7 @@ func (w *Wallet) handleChainNotifications() {
|
||||||
|
|
||||||
catchUpHashes := func(w *Wallet, client chain.Interface,
|
catchUpHashes := func(w *Wallet, client chain.Interface,
|
||||||
height int32) error {
|
height int32) error {
|
||||||
// TODO(aakselrod): There's a race conditon here, which
|
// TODO(aakselrod): There's a race condition here, which
|
||||||
// happens when a reorg occurs between the
|
// happens when a reorg occurs between the
|
||||||
// rescanProgress notification and the last GetBlockHash
|
// rescanProgress notification and the last GetBlockHash
|
||||||
// call. The solution when using btcd is to make btcd
|
// call. The solution when using btcd is to make btcd
|
||||||
|
@ -107,14 +107,14 @@ func (w *Wallet) handleChainNotifications() {
|
||||||
chainClient, birthdayStore,
|
chainClient, birthdayStore,
|
||||||
)
|
)
|
||||||
if err != nil && !waddrmgr.IsError(err, waddrmgr.ErrBirthdayBlockNotSet) {
|
if err != nil && !waddrmgr.IsError(err, waddrmgr.ErrBirthdayBlockNotSet) {
|
||||||
panic(fmt.Errorf("Unable to sanity "+
|
panic(fmt.Errorf("unable to sanity "+
|
||||||
"check wallet birthday block: %v",
|
"check wallet birthday block: %v",
|
||||||
err))
|
err))
|
||||||
}
|
}
|
||||||
|
|
||||||
err = w.syncWithChain(birthdayBlock)
|
err = w.syncWithChain(birthdayBlock)
|
||||||
if err != nil && !w.ShuttingDown() {
|
if err != nil && !w.ShuttingDown() {
|
||||||
panic(fmt.Errorf("Unable to synchronize "+
|
panic(fmt.Errorf("unable to synchronize "+
|
||||||
"wallet to chain: %v", err))
|
"wallet to chain: %v", err))
|
||||||
}
|
}
|
||||||
case chain.BlockConnected:
|
case chain.BlockConnected:
|
||||||
|
|
|
@ -118,7 +118,7 @@ func (w *Wallet) txToOutputs(outputs []*wire.TxOut, account uint32,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer dbtx.Rollback()
|
defer func() { _ = dbtx.Rollback() }()
|
||||||
|
|
||||||
addrmgrNs, changeSource := w.addrMgrWithChangeSource(dbtx, account)
|
addrmgrNs, changeSource := w.addrMgrWithChangeSource(dbtx, account)
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ func (w *Wallet) findEligibleOutputs(dbtx walletdb.ReadTx, account uint32, minco
|
||||||
// should be handled by the call to UnspentOutputs (or similar).
|
// should be handled by the call to UnspentOutputs (or similar).
|
||||||
// Because one of these filters requires matching the output script to
|
// Because one of these filters requires matching the output script to
|
||||||
// the desired account, this change depends on making wtxmgr a waddrmgr
|
// the desired account, this change depends on making wtxmgr a waddrmgr
|
||||||
// dependancy and requesting unspent outputs for a single account.
|
// dependency and requesting unspent outputs for a single account.
|
||||||
eligible := make([]wtxmgr.Credit, 0, len(unspent))
|
eligible := make([]wtxmgr.Credit, 0, len(unspent))
|
||||||
for i := range unspent {
|
for i := range unspent {
|
||||||
output := &unspent[i]
|
output := &unspent[i]
|
||||||
|
|
|
@ -350,7 +350,7 @@ func (w *Wallet) ImportPrivateKey(scope waddrmgr.KeyScope, wif *btcutil.WIF,
|
||||||
} else {
|
} else {
|
||||||
err := w.chainClient.NotifyReceived([]btcutil.Address{addr})
|
err := w.chainClient.NotifyReceived([]btcutil.Address{addr})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Failed to subscribe for address ntfns for "+
|
return "", fmt.Errorf("failed to subscribe for address ntfns for "+
|
||||||
"address %s: %s", addr.EncodeAddress(), err)
|
"address %s: %s", addr.EncodeAddress(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,6 @@ type testCase struct {
|
||||||
masterPriv string
|
masterPriv string
|
||||||
accountIndex uint32
|
accountIndex uint32
|
||||||
addrType waddrmgr.AddressType
|
addrType waddrmgr.AddressType
|
||||||
addrSchemaOverride *waddrmgr.ScopeAddrSchema
|
|
||||||
expectedScope waddrmgr.KeyScope
|
expectedScope waddrmgr.KeyScope
|
||||||
expectedAddr string
|
expectedAddr string
|
||||||
expectedChangeAddr string
|
expectedChangeAddr string
|
||||||
|
@ -100,7 +99,6 @@ var (
|
||||||
"FD2KeY6G9",
|
"FD2KeY6G9",
|
||||||
accountIndex: 9,
|
accountIndex: 9,
|
||||||
addrType: waddrmgr.NestedWitnessPubKey,
|
addrType: waddrmgr.NestedWitnessPubKey,
|
||||||
addrSchemaOverride: &waddrmgr.KeyScopeBIP0049AddrSchema,
|
|
||||||
expectedScope: waddrmgr.KeyScopeBIP0049Plus,
|
expectedScope: waddrmgr.KeyScopeBIP0049Plus,
|
||||||
expectedAddr: "2NBCJ9WzGXZqpLpXGq3Hacybj3c4eHRcqgh",
|
expectedAddr: "2NBCJ9WzGXZqpLpXGq3Hacybj3c4eHRcqgh",
|
||||||
expectedChangeAddr: "2N3bankFu6F3ZNU41iVJQqyS9MXqp9dvn1M",
|
expectedChangeAddr: "2N3bankFu6F3ZNU41iVJQqyS9MXqp9dvn1M",
|
||||||
|
|
|
@ -38,23 +38,6 @@ func UseLogger(logger btclog.Logger) {
|
||||||
wtxmgr.UseLogger(logger)
|
wtxmgr.UseLogger(logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogClosure is a closure that can be printed with %v to be used to
|
|
||||||
// generate expensive-to-create data for a detailed log level and avoid doing
|
|
||||||
// the work if the data isn't printed.
|
|
||||||
type logClosure func() string
|
|
||||||
|
|
||||||
// String invokes the log closure and returns the results string.
|
|
||||||
func (c logClosure) String() string {
|
|
||||||
return c()
|
|
||||||
}
|
|
||||||
|
|
||||||
// newLogClosure returns a new closure over the passed function which allows
|
|
||||||
// it to be used as a parameter in a logging function that is only invoked when
|
|
||||||
// the logging level is such that the message will actually be logged.
|
|
||||||
func newLogClosure(c func() string) logClosure {
|
|
||||||
return logClosure(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// pickNoun returns the singular or plural form of a noun depending
|
// pickNoun returns the singular or plural form of a noun depending
|
||||||
// on the count n.
|
// on the count n.
|
||||||
func pickNoun(n int, singular, plural string) string {
|
func pickNoun(n int, singular, plural string) string {
|
||||||
|
|
|
@ -27,7 +27,7 @@ func (w *Wallet) MakeMultiSigScript(addrs []btcutil.Address, nRequired int) ([]b
|
||||||
var addrmgrNs walletdb.ReadBucket
|
var addrmgrNs walletdb.ReadBucket
|
||||||
defer func() {
|
defer func() {
|
||||||
if dbtx != nil {
|
if dbtx != nil {
|
||||||
dbtx.Rollback()
|
_ = dbtx.Rollback()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
"github.com/btcsuite/btcwallet/walletdb"
|
"github.com/btcsuite/btcwallet/walletdb"
|
||||||
|
@ -184,7 +183,7 @@ func flattenBalanceMap(m map[uint32]btcutil.Amount) []AccountBalance {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func relevantAccounts(w *Wallet, m map[uint32]btcutil.Amount, txs []TransactionSummary) {
|
func relevantAccounts(_ *Wallet, m map[uint32]btcutil.Amount, txs []TransactionSummary) {
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
for _, d := range tx.MyInputs {
|
for _, d := range tx.MyInputs {
|
||||||
m[d.PreviousAccount] = 0
|
m[d.PreviousAccount] = 0
|
||||||
|
@ -255,7 +254,7 @@ func (s *NotificationServer) notifyMinedTransaction(dbtx walletdb.ReadTx, detail
|
||||||
}
|
}
|
||||||
txs := s.currentTxNtfn.AttachedBlocks[n-1].Transactions
|
txs := s.currentTxNtfn.AttachedBlocks[n-1].Transactions
|
||||||
s.currentTxNtfn.AttachedBlocks[n-1].Transactions =
|
s.currentTxNtfn.AttachedBlocks[n-1].Transactions =
|
||||||
append(txs, makeTxSummary(dbtx, s.wallet, details))
|
append(txs, makeTxSummary(dbtx, s.wallet, details)) // nolint:gocritic
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *NotificationServer) notifyAttachedBlock(dbtx walletdb.ReadTx, block *wtxmgr.BlockMeta) {
|
func (s *NotificationServer) notifyAttachedBlock(dbtx walletdb.ReadTx, block *wtxmgr.BlockMeta) {
|
||||||
|
@ -493,27 +492,6 @@ func (s *NotificationServer) notifyUnspentOutput(account uint32, hash *chainhash
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// notifySpentOutput notifies registered clients that a previously-unspent
|
|
||||||
// output is now spent, and includes the spender hash and input index in the
|
|
||||||
// notification.
|
|
||||||
func (s *NotificationServer) notifySpentOutput(account uint32, op *wire.OutPoint, spenderHash *chainhash.Hash, spenderIndex uint32) {
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
s.mu.Lock()
|
|
||||||
clients := s.spentness[account]
|
|
||||||
if len(clients) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n := &SpentnessNotifications{
|
|
||||||
hash: &op.Hash,
|
|
||||||
index: op.Index,
|
|
||||||
spenderHash: spenderHash,
|
|
||||||
spenderIndex: spenderIndex,
|
|
||||||
}
|
|
||||||
for _, c := range clients {
|
|
||||||
c <- n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SpentnessNotificationsClient receives SpentnessNotifications from the
|
// SpentnessNotificationsClient receives SpentnessNotifications from the
|
||||||
// NotificationServer over the channel C.
|
// NotificationServer over the channel C.
|
||||||
type SpentnessNotificationsClient struct {
|
type SpentnessNotificationsClient struct {
|
||||||
|
|
|
@ -65,7 +65,7 @@ type (
|
||||||
// delta.
|
// delta.
|
||||||
//
|
//
|
||||||
// NOTE: This should be used before applying any CheckDelta steps.
|
// NOTE: This should be used before applying any CheckDelta steps.
|
||||||
func (_ InitialDelta) Apply(i int, h *Harness) {
|
func (InitialDelta) Apply(i int, h *Harness) {
|
||||||
curHorizon, delta := h.brs.ExtendHorizon()
|
curHorizon, delta := h.brs.ExtendHorizon()
|
||||||
assertHorizon(h.t, i, curHorizon, h.expHorizon)
|
assertHorizon(h.t, i, curHorizon, h.expHorizon)
|
||||||
assertDelta(h.t, i, delta, h.recoveryWindow)
|
assertDelta(h.t, i, delta, h.recoveryWindow)
|
||||||
|
|
|
@ -34,6 +34,7 @@ func TestComputeInputScript(t *testing.T) {
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
runTestCase(t, w, tc.scope, tc.expectedScriptLen)
|
runTestCase(t, w, tc.scope, tc.expectedScriptLen)
|
||||||
})
|
})
|
||||||
|
|
|
@ -17,10 +17,10 @@ type unstableAPI struct {
|
||||||
|
|
||||||
// UnstableAPI exposes additional unstable public APIs for a Wallet. These APIs
|
// UnstableAPI exposes additional unstable public APIs for a Wallet. These APIs
|
||||||
// may be changed or removed at any time. Currently this type exists to ease
|
// may be changed or removed at any time. Currently this type exists to ease
|
||||||
// the transation (particularly for the legacy JSON-RPC server) from using
|
// the transition (particularly for the legacy JSON-RPC server) from using
|
||||||
// exported manager packages to a unified wallet package that exposes all
|
// exported manager packages to a unified wallet package that exposes all
|
||||||
// functionality by itself. New code should not be written using this API.
|
// functionality by itself. New code should not be written using this API.
|
||||||
func UnstableAPI(w *Wallet) unstableAPI { return unstableAPI{w} }
|
func UnstableAPI(w *Wallet) unstableAPI { return unstableAPI{w} } // nolint:golint
|
||||||
|
|
||||||
// TxDetails calls wtxmgr.Store.TxDetails under a single database view transaction.
|
// TxDetails calls wtxmgr.Store.TxDetails under a single database view transaction.
|
||||||
func (u unstableAPI) TxDetails(txHash *chainhash.Hash) (*wtxmgr.TxDetails, error) {
|
func (u unstableAPI) TxDetails(txHash *chainhash.Hash) (*wtxmgr.TxDetails, error) {
|
||||||
|
|
|
@ -45,8 +45,6 @@ const (
|
||||||
// data in the waddrmgr namespace. Transactions are not yet encrypted.
|
// data in the waddrmgr namespace. Transactions are not yet encrypted.
|
||||||
InsecurePubPassphrase = "public"
|
InsecurePubPassphrase = "public"
|
||||||
|
|
||||||
walletDbWatchingOnlyName = "wowallet.db"
|
|
||||||
|
|
||||||
// recoveryBatchSize is the default number of blocks that will be
|
// recoveryBatchSize is the default number of blocks that will be
|
||||||
// scanned successively by the recovery manager, in the event that the
|
// scanned successively by the recovery manager, in the event that the
|
||||||
// wallet is started in recovery mode.
|
// wallet is started in recovery mode.
|
||||||
|
@ -120,11 +118,6 @@ type Wallet struct {
|
||||||
changePassphrase chan changePassphraseRequest
|
changePassphrase chan changePassphraseRequest
|
||||||
changePassphrases chan changePassphrasesRequest
|
changePassphrases chan changePassphrasesRequest
|
||||||
|
|
||||||
// Information for reorganization handling.
|
|
||||||
reorganizingLock sync.Mutex
|
|
||||||
reorganizeToHash chainhash.Hash
|
|
||||||
reorganizing bool
|
|
||||||
|
|
||||||
NtfnServer *NotificationServer
|
NtfnServer *NotificationServer
|
||||||
|
|
||||||
chainParams *chaincfg.Params
|
chainParams *chaincfg.Params
|
||||||
|
@ -606,7 +599,7 @@ func locateBirthdayBlock(chainClient chainConn,
|
||||||
if mid == startHeight || mid == bestHeight || mid == left {
|
if mid == startHeight || mid == bestHeight || mid == left {
|
||||||
birthdayBlock = &waddrmgr.BlockStamp{
|
birthdayBlock = &waddrmgr.BlockStamp{
|
||||||
Hash: *hash,
|
Hash: *hash,
|
||||||
Height: int32(mid),
|
Height: mid,
|
||||||
Timestamp: header.Timestamp,
|
Timestamp: header.Timestamp,
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -628,7 +621,7 @@ func locateBirthdayBlock(chainClient chainConn,
|
||||||
|
|
||||||
birthdayBlock = &waddrmgr.BlockStamp{
|
birthdayBlock = &waddrmgr.BlockStamp{
|
||||||
Hash: *hash,
|
Hash: *hash,
|
||||||
Height: int32(mid),
|
Height: mid,
|
||||||
Timestamp: header.Timestamp,
|
Timestamp: header.Timestamp,
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -837,6 +830,7 @@ expandHorizons:
|
||||||
// Update the global set of watched outpoints with any that were found
|
// Update the global set of watched outpoints with any that were found
|
||||||
// in the block.
|
// in the block.
|
||||||
for outPoint, addr := range filterResp.FoundOutPoints {
|
for outPoint, addr := range filterResp.FoundOutPoints {
|
||||||
|
outPoint := outPoint
|
||||||
recoveryState.AddWatchedOutPoint(&outPoint, addr)
|
recoveryState.AddWatchedOutPoint(&outPoint, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1223,7 +1217,7 @@ type (
|
||||||
|
|
||||||
// heldUnlock is a tool to prevent the wallet from automatically
|
// heldUnlock is a tool to prevent the wallet from automatically
|
||||||
// locking after some timeout before an operation which needed
|
// locking after some timeout before an operation which needed
|
||||||
// the unlocked wallet has finished. Any aquired heldUnlock
|
// the unlocked wallet has finished. Any acquired heldUnlock
|
||||||
// *must* be released (preferably with a defer) or the wallet
|
// *must* be released (preferably with a defer) or the wallet
|
||||||
// will forever remain unlocked.
|
// will forever remain unlocked.
|
||||||
heldUnlock chan struct{}
|
heldUnlock chan struct{}
|
||||||
|
@ -1425,25 +1419,6 @@ func (w *Wallet) ChangePassphrases(publicOld, publicNew, privateOld,
|
||||||
return <-err
|
return <-err
|
||||||
}
|
}
|
||||||
|
|
||||||
// accountUsed returns whether there are any recorded transactions spending to
|
|
||||||
// a given account. It returns true if atleast one address in the account was
|
|
||||||
// used and false if no address in the account was used.
|
|
||||||
func (w *Wallet) accountUsed(addrmgrNs walletdb.ReadWriteBucket, account uint32) (bool, error) {
|
|
||||||
var used bool
|
|
||||||
err := w.Manager.ForEachAccountAddress(addrmgrNs, account,
|
|
||||||
func(maddr waddrmgr.ManagedAddress) error {
|
|
||||||
used = maddr.Used(addrmgrNs)
|
|
||||||
if used {
|
|
||||||
return waddrmgr.Break
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err == waddrmgr.Break {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
return used, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// AccountAddresses returns the addresses for every created address for an
|
// AccountAddresses returns the addresses for every created address for an
|
||||||
// account.
|
// account.
|
||||||
func (w *Wallet) AccountAddresses(account uint32) (addrs []btcutil.Address, err error) {
|
func (w *Wallet) AccountAddresses(account uint32) (addrs []btcutil.Address, err error) {
|
||||||
|
@ -1802,8 +1777,6 @@ func (w *Wallet) RenameAccount(scope waddrmgr.KeyScope, account uint32, newName
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxEmptyAccounts = 100
|
|
||||||
|
|
||||||
// NextAccount creates the next account and returns its account number. The
|
// NextAccount creates the next account and returns its account number. The
|
||||||
// name must be unique to the account. In order to support automatic seed
|
// name must be unique to the account. In order to support automatic seed
|
||||||
// restoring, new accounts may not be created when all of the previous 100
|
// restoring, new accounts may not be created when all of the previous 100
|
||||||
|
@ -2024,8 +1997,12 @@ func (w *Wallet) ListSinceBlock(start, end, syncHeight int32) ([]btcjson.ListTra
|
||||||
|
|
||||||
rangeFn := func(details []wtxmgr.TxDetails) (bool, error) {
|
rangeFn := func(details []wtxmgr.TxDetails) (bool, error) {
|
||||||
for _, detail := range details {
|
for _, detail := range details {
|
||||||
jsonResults := listTransactions(tx, &detail,
|
detail := detail
|
||||||
w.Manager, syncHeight, w.chainParams)
|
|
||||||
|
jsonResults := listTransactions(
|
||||||
|
tx, &detail, w.Manager, syncHeight,
|
||||||
|
w.chainParams,
|
||||||
|
)
|
||||||
txList = append(txList, jsonResults...)
|
txList = append(txList, jsonResults...)
|
||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
|
@ -2123,9 +2100,6 @@ func (w *Wallet) ListAddressTransactions(pkHashes map[string]struct{}) ([]btcjso
|
||||||
|
|
||||||
jsonResults := listTransactions(tx, detail,
|
jsonResults := listTransactions(tx, detail,
|
||||||
w.Manager, syncBlock.Height, w.chainParams)
|
w.Manager, syncBlock.Height, w.chainParams)
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
txList = append(txList, jsonResults...)
|
txList = append(txList, jsonResults...)
|
||||||
continue loopDetails
|
continue loopDetails
|
||||||
}
|
}
|
||||||
|
@ -2874,7 +2848,7 @@ func (w *Wallet) SortedActivePaymentAddresses() ([]string, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Sort(sort.StringSlice(addrStrs))
|
sort.Strings(addrStrs)
|
||||||
return addrStrs, nil
|
return addrStrs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,7 @@ func TestLocateBirthdayBlock(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
|
testCase := testCase
|
||||||
success := t.Run(testCase.name, func(t *testing.T) {
|
success := t.Run(testCase.name, func(t *testing.T) {
|
||||||
chainConn := createMockChainConn(
|
chainConn := createMockChainConn(
|
||||||
chainParams.GenesisBlock, numBlocks, blockInterval,
|
chainParams.GenesisBlock, numBlocks, blockInterval,
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec"
|
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
|
@ -30,7 +29,7 @@ func networkDir(dataDir string, chainParams *chaincfg.Params) string {
|
||||||
|
|
||||||
// For now, we must always name the testnet data directory as "testnet"
|
// For now, we must always name the testnet data directory as "testnet"
|
||||||
// and not "testnet3" or any other version, as the chaincfg testnet3
|
// and not "testnet3" or any other version, as the chaincfg testnet3
|
||||||
// paramaters will likely be switched to being named "testnet3" in the
|
// parameters will likely be switched to being named "testnet3" in the
|
||||||
// future. This is done to future proof that change, and an upgrade
|
// future. This is done to future proof that change, and an upgrade
|
||||||
// plan to move the testnet3 data directory can be worked out later.
|
// plan to move the testnet3 data directory can be worked out later.
|
||||||
if chainParams.Net == wire.TestNet3 {
|
if chainParams.Net == wire.TestNet3 {
|
||||||
|
@ -43,7 +42,7 @@ func networkDir(dataDir string, chainParams *chaincfg.Params) string {
|
||||||
// convertLegacyKeystore converts all of the addresses in the passed legacy
|
// convertLegacyKeystore converts all of the addresses in the passed legacy
|
||||||
// key store to the new waddrmgr.Manager format. Both the legacy keystore and
|
// key store to the new waddrmgr.Manager format. Both the legacy keystore and
|
||||||
// the new manager must be unlocked.
|
// the new manager must be unlocked.
|
||||||
func convertLegacyKeystore(legacyKeyStore *keystore.Store, w *wallet.Wallet) error {
|
func convertLegacyKeystore(legacyKeyStore *keystore.Store, w *wallet.Wallet) {
|
||||||
netParams := legacyKeyStore.Net()
|
netParams := legacyKeyStore.Net()
|
||||||
blockStamp := waddrmgr.BlockStamp{
|
blockStamp := waddrmgr.BlockStamp{
|
||||||
Height: 0,
|
Height: 0,
|
||||||
|
@ -60,8 +59,9 @@ func convertLegacyKeystore(legacyKeyStore *keystore.Store, w *wallet.Wallet) err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
wif, err := btcutil.NewWIF((*btcec.PrivateKey)(privKey),
|
wif, err := btcutil.NewWIF(
|
||||||
netParams, addr.Compressed())
|
privKey, netParams, addr.Compressed(),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("WARN: Failed to create wallet "+
|
fmt.Printf("WARN: Failed to create wallet "+
|
||||||
"import format for address %v: %v\n",
|
"import format for address %v: %v\n",
|
||||||
|
@ -93,8 +93,6 @@ func convertLegacyKeystore(legacyKeyStore *keystore.Store, w *wallet.Wallet) err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createWallet prompts the user for information needed to generate a new wallet
|
// createWallet prompts the user for information needed to generate a new wallet
|
||||||
|
@ -114,7 +112,7 @@ func createWallet(cfg *config) error {
|
||||||
var legacyKeyStore *keystore.Store
|
var legacyKeyStore *keystore.Store
|
||||||
_, err := os.Stat(keystorePath)
|
_, err := os.Stat(keystorePath)
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
// A stat error not due to a non-existant file should be
|
// A stat error not due to a non-existent file should be
|
||||||
// returned to the caller.
|
// returned to the caller.
|
||||||
return err
|
return err
|
||||||
} else if err == nil {
|
} else if err == nil {
|
||||||
|
@ -146,7 +144,7 @@ func createWallet(cfg *config) error {
|
||||||
// Import the addresses in the legacy keystore to the new wallet if
|
// Import the addresses in the legacy keystore to the new wallet if
|
||||||
// any exist, locking each wallet again when finished.
|
// any exist, locking each wallet again when finished.
|
||||||
loader.RunAfterLoad(func(w *wallet.Wallet) {
|
loader.RunAfterLoad(func(w *wallet.Wallet) {
|
||||||
defer legacyKeyStore.Lock()
|
defer func() { _ = legacyKeyStore.Lock() }()
|
||||||
|
|
||||||
fmt.Println("Importing addresses from existing wallet...")
|
fmt.Println("Importing addresses from existing wallet...")
|
||||||
|
|
||||||
|
@ -161,12 +159,7 @@ func createWallet(cfg *config) error {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = convertLegacyKeystore(legacyKeyStore, w)
|
convertLegacyKeystore(legacyKeyStore, w)
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("ERR: Failed to import keys from old "+
|
|
||||||
"wallet format: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the legacy key store.
|
// Remove the legacy key store.
|
||||||
err = os.Remove(keystorePath)
|
err = os.Remove(keystorePath)
|
||||||
|
|
Loading…
Reference in a new issue