Merge pull request #738 from guggero/build-improvement

Add Makefile, use golangci-lint
This commit is contained in:
Olaoluwa Osuntokun 2021-03-29 15:55:46 -07:00 committed by GitHub
commit 07d4cc155e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 420 additions and 492 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
btcwallet btcwallet
vendor vendor
.idea .idea
coverage.txt

54
.golangci.yml Normal file
View 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

View file

@ -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
View 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

View file

@ -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)

View file

@ -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,

View file

@ -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(&currentHeader.PrevBlock) prevBlock := &currentHeader.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--

View file

@ -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
}

View file

@ -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:

View file

@ -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(

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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)
} }

View file

@ -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)

View file

@ -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
View file

@ -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
}

View file

@ -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.

View file

@ -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)

View file

@ -30,6 +30,7 @@ func TestThrottle(t *testing.T) {
return return
} }
codes <- res.StatusCode codes <- res.StatusCode
_ = res.Body.Close()
}() }()
} }

View file

@ -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

View file

@ -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.

View file

@ -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)
} }

View file

@ -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"
) )

View file

@ -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{}),
} }

View file

@ -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.

View file

@ -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.

View file

@ -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)
}

View file

@ -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
} }

View file

@ -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.

View file

@ -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.

View file

@ -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,

View file

@ -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.

View file

@ -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:

View file

@ -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]

View file

@ -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)
} }
} }

View file

@ -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",

View file

@ -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 {

View file

@ -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()
} }
}() }()

View file

@ -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 {

View file

@ -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)

View file

@ -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)
}) })

View file

@ -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) {

View file

@ -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
} }

View file

@ -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,

View file

@ -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)