├── .gitignore ├── scripts ├── check-commit.sh ├── tidy_modules.sh └── check-each-commit.sh ├── internal ├── zero │ ├── doc.go │ ├── array.go │ ├── slice.go │ ├── benchmark_test.go │ └── zero_test.go ├── legacy │ └── rename │ │ ├── rename_unix.go │ │ └── rename_windows.go ├── cfgutil │ ├── file.go │ ├── amount.go │ ├── explicitflags.go │ └── normalization.go ├── prompt │ └── prompt_js.go └── rpchelp │ ├── genrpcserverhelp.go │ └── methods.go ├── rpc ├── .clang-format ├── legacyrpc │ ├── config.go │ ├── log.go │ ├── rpcserver_test.go │ ├── errors.go │ └── rpchelp_test.go ├── gen_protos_docker.sh ├── documentation │ ├── README.md │ └── serverchanges.md └── rpcserver │ └── log.go ├── wallet ├── rand.go ├── doc.go ├── txsizes │ └── go.mod ├── txrules │ ├── go.mod │ └── rules.go ├── disksync.go ├── txauthor │ ├── go.mod │ └── cprng.go ├── watchingonly_test.go ├── README.md ├── unstable.go ├── log.go ├── example_test.go ├── mock.go ├── signer_test.go ├── common.go ├── multisig.go ├── signer.go └── history.go ├── params.go ├── walletdb ├── cov_report.sh ├── go.mod ├── walletdbtest │ ├── doc.go │ └── tester.go ├── bdb │ ├── doc.go │ ├── interface_test.go │ ├── README.md │ └── driver.go ├── go.sum ├── migration │ └── log.go ├── test_coverage.txt ├── README.md ├── error.go └── db_test.go ├── signalsigterm.go ├── tools └── Dockerfile ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── bug_report.md │ └── feature_request.md ├── actions │ ├── cleanup-space │ │ └── action.yml │ └── rebase │ │ └── action.yml ├── pull_request_template.md └── workflows │ └── main.yml ├── waddrmgr ├── cov_report.sh ├── log.go ├── README.md ├── tapscript_test.go ├── db_test.go ├── tapscript.go ├── error_test.go └── sync.go ├── docs ├── developer │ ├── adr │ │ └── README.md │ └── README.md ├── README.md ├── releases │ └── TEMPLATE.md └── user │ └── force_rescans.md ├── LICENSE ├── wtxmgr ├── go.mod ├── README.md ├── log.go ├── doc.go ├── kahnsort.go ├── error.go ├── migrations.go └── kahnsort_test.go ├── .gemini └── config.yaml ├── chain ├── chainservice.go ├── rescan.go ├── btcd_test.go ├── log.go ├── errors_test.go ├── jitter_test.go ├── queue.go ├── jitter.go └── bitcoind_events.go ├── .protolint.yml ├── netparams └── params.go ├── go.mod ├── signal.go ├── make └── testing_flags.mk ├── snacl └── snacl_test.go ├── cmd └── dropwtxmgr │ └── main.go └── log.go /.gitignore: -------------------------------------------------------------------------------- 1 | btcwallet 2 | vendor 3 | .idea 4 | coverage.txt 5 | *.swp 6 | .vscode 7 | .DS_Store 8 | .aider* 9 | -------------------------------------------------------------------------------- /scripts/check-commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -x 4 | echo Testing $(git log -1 --oneline) 5 | make unit pkg=... case=_NONE_ 6 | -------------------------------------------------------------------------------- /internal/zero/doc.go: -------------------------------------------------------------------------------- 1 | // Package zero contains functions to clear data from byte slices and 2 | // multi-precision integers. 3 | package zero 4 | -------------------------------------------------------------------------------- /rpc/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Proto 3 | BasedOnStyle: Google 4 | IndentWidth: 4 5 | AllowShortFunctionsOnASingleLine: None 6 | SpaceBeforeParens: Always 7 | CompactNamespaces: false 8 | -------------------------------------------------------------------------------- /wallet/rand.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | // init initializes the random generator. 9 | func init() { 10 | rand.Seed(time.Now().Unix()) 11 | } 12 | -------------------------------------------------------------------------------- /wallet/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package wallet provides ... 7 | TODO: Flesh out this section 8 | 9 | Overview 10 | */ 11 | package wallet 12 | -------------------------------------------------------------------------------- /params.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import "github.com/btcsuite/btcwallet/netparams" 8 | 9 | var activeNet = &netparams.MainNetParams 10 | -------------------------------------------------------------------------------- /scripts/tidy_modules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SUBMODULES=$(find . -mindepth 2 -name "go.mod" | cut -d'/' -f2) 4 | 5 | 6 | # Run 'go mod tidy' for root. 7 | go mod tidy 8 | 9 | # Run 'go mod tidy' for each module. 10 | for submodule in $SUBMODULES 11 | do 12 | pushd $submodule 13 | 14 | go mod tidy 15 | 16 | popd 17 | done 18 | -------------------------------------------------------------------------------- /walletdb/cov_report.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This script uses go tool cover to generate a test coverage report. 4 | go test -coverprofile=cov.out && go tool cover -func=cov.out && rm -f cov.out 5 | echo "============================================================" 6 | (cd bdb && go test -coverprofile=cov.out && go tool cover -func=cov.out && \ 7 | rm -f cov.out) 8 | -------------------------------------------------------------------------------- /walletdb/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/btcsuite/btcwallet/walletdb 2 | 3 | require ( 4 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f 5 | github.com/davecgh/go-spew v1.1.1 6 | go.etcd.io/bbolt v1.3.11 7 | ) 8 | 9 | require ( 10 | github.com/stretchr/testify v1.9.0 // indirect 11 | golang.org/x/sys v0.19.0 // indirect 12 | ) 13 | 14 | go 1.22 15 | -------------------------------------------------------------------------------- /walletdb/walletdbtest/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package walletdbtest provides exported tests that can be imported and 6 | // consumed by walletdb driver tests to help ensure that drivers confirm to the 7 | // database driver interface correctly. 8 | package walletdbtest 9 | -------------------------------------------------------------------------------- /scripts/check-each-commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [[ "$1" = "" ]]; then 3 | echo "USAGE: $0 remote/head_branch" 4 | echo "eg $0 upstream/master" 5 | exit 1 6 | fi 7 | 8 | set -e 9 | set -x 10 | 11 | if [[ "$(git log --pretty="%H %D" | grep "^[0-9a-f]*.* $1")" = "" ]]; then 12 | echo "It seems like the current checked-out commit is not based on $1" 13 | exit 1 14 | fi 15 | git rebase --exec scripts/check-commit.sh $1 16 | -------------------------------------------------------------------------------- /rpc/legacyrpc/config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package legacyrpc 6 | 7 | // Options contains the required options for running the legacy RPC server. 8 | type Options struct { 9 | Username string 10 | Password string 11 | 12 | MaxPOSTClients int64 13 | MaxWebsocketClients int64 14 | } 15 | -------------------------------------------------------------------------------- /signalsigterm.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris 6 | // +build darwin dragonfly freebsd linux netbsd openbsd solaris 7 | 8 | package main 9 | 10 | import ( 11 | "os" 12 | "syscall" 13 | ) 14 | 15 | func init() { 16 | signals = []os.Signal{os.Interrupt, syscall.SIGTERM} 17 | } 18 | -------------------------------------------------------------------------------- /internal/legacy/rename/rename_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !windows && !plan9 6 | // +build !windows,!plan9 7 | 8 | package rename 9 | 10 | import ( 11 | "os" 12 | ) 13 | 14 | // Atomic provides an atomic file rename. newpath is replaced if it 15 | // already exists. 16 | func Atomic(oldpath, newpath string) error { 17 | return os.Rename(oldpath, newpath) 18 | } 19 | -------------------------------------------------------------------------------- /internal/cfgutil/file.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package cfgutil 6 | 7 | import "os" 8 | 9 | // FileExists reports whether the named file or directory exists. 10 | func FileExists(filePath string) (bool, error) { 11 | _, err := os.Stat(filePath) 12 | if err != nil { 13 | if os.IsNotExist(err) { 14 | return false, nil 15 | } 16 | return false, err 17 | } 18 | return true, nil 19 | } 20 | -------------------------------------------------------------------------------- /tools/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.24.6-alpine 2 | 3 | ENV GOFLAGS="-buildvcs=false" 4 | 5 | RUN apk update && apk add --no-cache git 6 | 7 | WORKDIR /build 8 | 9 | COPY tools/go.mod tools/go.sum ./ 10 | 11 | RUN go install -trimpath \ 12 | github.com/golangci/golangci-lint/v2/cmd/golangci-lint \ 13 | github.com/rinchsan/gosimports/cmd/gosimports \ 14 | && rm -rf /go/pkg/mod \ 15 | && rm -rf /root/.cache/go-build \ 16 | && rm -rf /go/src \ 17 | && rm -rf /tmp/* 18 | 19 | RUN chmod -R 777 /build \ 20 | && git config --global --add safe.directory /build 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Discussions 4 | url: https://github.com/btcsuite/btcwallet/discussions 5 | about: For general or troubleshooting questions or if you're not sure what issue type to pick. 6 | - name: Community Slack 7 | url: https://lightning.engineering/slack.html 8 | about: Please ask and answer questions here. 9 | - name: Security issue disclosure policy 10 | url: https://github.com/lightningnetwork/lnd#security 11 | about: Please refer to this document when reporting security related issues. 12 | -------------------------------------------------------------------------------- /waddrmgr/cov_report.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This script uses gocov to generate a test coverage report. 4 | # The gocov tool my be obtained with the following command: 5 | # go get github.com/axw/gocov/gocov 6 | # 7 | # It will be installed to $GOPATH/bin, so ensure that location is in your $PATH. 8 | 9 | # Check for gocov. 10 | type gocov >/dev/null 2>&1 11 | if [ $? -ne 0 ]; then 12 | echo >&2 "This script requires the gocov tool." 13 | echo >&2 "You may obtain it with the following command:" 14 | echo >&2 "go get github.com/axw/gocov/gocov" 15 | exit 1 16 | fi 17 | gocov test | gocov report 18 | -------------------------------------------------------------------------------- /internal/zero/array.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package zero 6 | 7 | // Bytea32 clears the 32-byte array by filling it with the zero value. 8 | // This is used to explicitly clear private key material from memory. 9 | func Bytea32(b *[32]byte) { 10 | *b = [32]byte{} 11 | } 12 | 13 | // Bytea64 clears the 64-byte array by filling it with the zero value. 14 | // This is used to explicitly clear sensitive material from memory. 15 | func Bytea64(b *[64]byte) { 16 | *b = [64]byte{} 17 | } 18 | -------------------------------------------------------------------------------- /.github/actions/cleanup-space/action.yml: -------------------------------------------------------------------------------- 1 | name: "Clean up runner disk space" 2 | description: "Removes large, non-essential toolsets to free up disk space on the runner." 3 | 4 | runs: 5 | using: "composite" 6 | steps: 7 | - name: Free up disk space 8 | shell: bash 9 | run: | 10 | echo "Removing large toolsets to free up disk space..." 11 | # Remove dotnet to save disk space. 12 | sudo rm -rf /usr/share/dotnet 13 | # Remove android to save disk space. 14 | sudo rm -rf /usr/local/lib/android 15 | # Remove ghc to save disk space. 16 | sudo rm -rf /opt/ghc 17 | -------------------------------------------------------------------------------- /rpc/legacyrpc/log.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package legacyrpc 6 | 7 | import ( 8 | "github.com/btcsuite/btclog" 9 | "github.com/btcsuite/btcwallet/build" 10 | ) 11 | 12 | var log = btclog.Disabled 13 | 14 | func init() { 15 | UseLogger(build.NewSubLogger("RPCS", nil)) 16 | } 17 | 18 | // UseLogger sets the package-wide logger. Any calls to this function must be 19 | // made before a server is created and used (it is not concurrent safe). 20 | func UseLogger(logger btclog.Logger) { 21 | log = logger 22 | } 23 | -------------------------------------------------------------------------------- /.github/actions/rebase/action.yml: -------------------------------------------------------------------------------- 1 | name: "Rebase on to the PR target base branch" 2 | description: "A reusable workflow that's used to rebase the PR code on to the target base branch." 3 | 4 | runs: 5 | using: "composite" 6 | 7 | steps: 8 | - name: fetch and rebase on ${{ github.base_ref }} 9 | shell: bash 10 | run: | 11 | git remote add upstream https://github.com/${{ github.repository }} 12 | git fetch upstream ${{ github.base_ref }}:refs/remotes/upstream/${{ github.base_ref }} 13 | export GIT_COMMITTER_EMAIL="btcwallet-ci@example.com" 14 | export GIT_COMMITTER_NAME="btcwallet CI" 15 | git rebase upstream/${{ github.base_ref }} 16 | -------------------------------------------------------------------------------- /docs/developer/adr/README.md: -------------------------------------------------------------------------------- 1 | # Architecture Decision Records (ADRs) 2 | 3 | This directory contains [Architecture Decision Records (ADRs)](https://github.com/joelparkerhenderson/architecture-decision-record) for the `btcwallet` project. ADRs are short, focused documents that capture significant architectural decisions, their context, the options considered, and their consequences. 4 | 5 | ADRs serve as a historical log of important design choices, providing context for future development and helping new contributors understand the rationale behind the system's architecture. 6 | 7 | ## Existing ADRs 8 | 9 | * [ADR 0001: Multi-Wallet Architecture](./0001-multi-wallet-architecture.md) 10 | -------------------------------------------------------------------------------- /wallet/txsizes/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/btcsuite/btcwallet/wallet/txsizes 2 | 3 | require github.com/btcsuite/btcd v0.23.4 4 | 5 | require ( 6 | github.com/btcsuite/btcd/btcec/v2 v2.1.3 // indirect 7 | github.com/btcsuite/btcd/btcutil v1.1.0 // indirect 8 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect 9 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect 10 | github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect 11 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect 12 | github.com/stretchr/testify v1.9.0 // indirect 13 | golang.org/x/crypto v0.22.0 // indirect 14 | golang.org/x/sys v0.19.0 // indirect 15 | ) 16 | 17 | go 1.22 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report. Please use the discussions section for general or troubleshooting questions. 4 | title: '[bug]: ' 5 | labels: ["bug", "needs triage"] 6 | assignees: '' 7 | --- 8 | 9 | ### Background 10 | 11 | Describe your issue here. 12 | 13 | ### Your environment 14 | 15 | * version of `btcwallet` 16 | * which operating system (`uname -a` on *Nix) 17 | * any other relevant environment details 18 | 19 | ### Steps to reproduce 20 | 21 | Tell us how to reproduce this issue. Please provide stacktraces and links to code in question. 22 | 23 | ### Expected behaviour 24 | 25 | Tell us what should happen 26 | 27 | ### Actual behaviour 28 | 29 | Tell us what happens instead 30 | -------------------------------------------------------------------------------- /rpc/gen_protos_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # golang docker image version used in this script. 4 | GO_IMAGE=docker.io/library/golang:1.23.12-alpine 5 | 6 | # protobuf generator version with legacy plugins=grpc support. Using v1.4.3 to 7 | # maintain backward compatibility - generates single file instead of separate 8 | # .pb.go and _grpc.pb.go files (modern approach). 9 | PROTOC_GEN_GO_VERSION=v1.4.3 10 | 11 | docker run --rm --volume "$(pwd):/workspace" --workdir /workspace \ 12 | ${GO_IMAGE} sh -c " 13 | apk add --no-cache protobuf-dev && \ 14 | go install github.com/golang/protobuf/protoc-gen-go@${PROTOC_GEN_GO_VERSION} && \ 15 | protoc -I. api.proto --go_out=plugins=grpc:walletrpc && \ 16 | chown -R $(id -u):$(id -g) walletrpc" 17 | -------------------------------------------------------------------------------- /walletdb/walletdbtest/tester.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package walletdbtest 6 | 7 | // Tester is an interface type that can be implemented by *testing.T. This 8 | // allows drivers to call into the non-test API using their own test contexts. 9 | type Tester interface { 10 | Error(...interface{}) 11 | Errorf(string, ...interface{}) 12 | Fail() 13 | FailNow() 14 | Failed() bool 15 | Fatal(...interface{}) 16 | Fatalf(string, ...interface{}) 17 | Log(...interface{}) 18 | Logf(string, ...interface{}) 19 | Parallel() 20 | Skip(...interface{}) 21 | SkipNow() 22 | Skipf(string, ...interface{}) 23 | Skipped() bool 24 | } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest a new feature for `btcwallet`. 4 | title: '[feature]: ' 5 | labels: enhancement 6 | assignees: '' 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | 11 | 12 | **Describe the solution you'd like** 13 | 14 | 15 | **Describe alternatives you've considered** 16 | 17 | 18 | **Additional context** 19 | 20 | -------------------------------------------------------------------------------- /rpc/documentation/README.md: -------------------------------------------------------------------------------- 1 | # RPC Documentation 2 | 3 | This project provides a [gRPC](http://www.grpc.io/) server for Remote Procedure 4 | Call (RPC) access from other processes. This is intended to be the primary 5 | means by which users, through other client programs, interact with the wallet. 6 | 7 | These documents cover the documentation for both consumers of the server and 8 | developers who must make changes or additions to the API and server 9 | implementation: 10 | 11 | - [API specification](./api.md) 12 | - [Client usage](./clientusage.md) 13 | - [Making API changes](./serverchanges.md) 14 | 15 | A legacy RPC server based on the JSON-RPC API of Bitcoin Core's wallet is also 16 | available, but documenting its usage is out of scope for these documents. 17 | -------------------------------------------------------------------------------- /wallet/txrules/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/btcsuite/btcwallet/wallet/txrules 2 | 3 | require ( 4 | github.com/btcsuite/btcd v0.23.4 5 | github.com/btcsuite/btcd/btcutil v1.1.0 6 | ) 7 | 8 | require ( 9 | github.com/aead/siphash v1.0.1 // indirect 10 | github.com/btcsuite/btcd/btcec/v2 v2.1.3 // indirect 11 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect 12 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect 13 | github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect 14 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect 15 | github.com/kkdai/bstream v1.0.0 // indirect 16 | github.com/stretchr/testify v1.9.0 // indirect 17 | golang.org/x/crypto v0.22.0 // indirect 18 | golang.org/x/sys v0.19.0 // indirect 19 | ) 20 | 21 | go 1.22 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2013-2017 The btcsuite developers 4 | Copyright (c) 2015-2016 The Decred developers 5 | 6 | Permission to use, copy, modify, and distribute this software for any 7 | purpose with or without fee is hereby granted, provided that the above 8 | copyright notice and this permission notice appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # btcwallet Documentation 2 | 3 | This directory contains all documentation for `btcwallet`, organized by audience and purpose. 4 | 5 | ## 📖 User Documentation 6 | 7 | Guides and tutorials for end-users and operators of `btcwallet`. This section covers topics like wallet setup, usage, and recovery. 8 | 9 | **[➡️ Go to User Guides](./user/)** 10 | 11 | --- 12 | 13 | ## 💻 Developer Documentation 14 | 15 | Information for contributors and developers working on `btcwallet`. This includes contribution guidelines, code formatting rules, architectural deep-dives, and implementation guides. 16 | 17 | **[➡️ Go to Developer Guides](./developer/)** 18 | 19 | --- 20 | 21 | ## 🚀 Release Notes 22 | 23 | Detailed release notes for each version of `btcwallet`, outlining new features, bug fixes, and breaking changes. 24 | 25 | **[➡️ Go to Release Notes](./releases/)** 26 | -------------------------------------------------------------------------------- /wallet/disksync.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package wallet 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | ) 11 | 12 | // checkCreateDir checks that the path exists and is a directory. 13 | // If path does not exist, it is created. 14 | func checkCreateDir(path string) error { 15 | if fi, err := os.Stat(path); err != nil { 16 | if os.IsNotExist(err) { 17 | // Attempt data directory creation 18 | if err = os.MkdirAll(path, 0700); err != nil { 19 | return fmt.Errorf("cannot create directory: %w", err) 20 | } 21 | } else { 22 | return fmt.Errorf("error checking directory: %w", err) 23 | } 24 | } else { 25 | if !fi.IsDir() { 26 | return fmt.Errorf("path '%s' is not a directory", path) 27 | } 28 | } 29 | 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /wallet/txauthor/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/btcsuite/btcwallet/wallet/txauthor 2 | 3 | require ( 4 | github.com/btcsuite/btcd v0.23.4 5 | github.com/btcsuite/btcd/btcutil v1.1.1 6 | github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 7 | github.com/btcsuite/btcwallet/wallet/txsizes v1.2.3 8 | ) 9 | 10 | require ( 11 | github.com/aead/siphash v1.0.1 // indirect 12 | github.com/btcsuite/btcd/btcec/v2 v2.1.3 // indirect 13 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect 14 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect 15 | github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect 16 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect 17 | github.com/kkdai/bstream v1.0.0 // indirect 18 | github.com/stretchr/testify v1.9.0 // indirect 19 | golang.org/x/crypto v0.22.0 // indirect 20 | golang.org/x/sys v0.19.0 // indirect 21 | ) 22 | 23 | go 1.22 24 | -------------------------------------------------------------------------------- /wallet/watchingonly_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package wallet 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | 11 | "github.com/btcsuite/btcd/chaincfg" 12 | _ "github.com/btcsuite/btcwallet/walletdb/bdb" 13 | ) 14 | 15 | // TestCreateWatchingOnly checks that we can construct a watching-only 16 | // wallet. 17 | func TestCreateWatchingOnly(t *testing.T) { 18 | // Set up a wallet. 19 | dir := t.TempDir() 20 | 21 | pubPass := []byte("hello") 22 | 23 | loader := NewLoader( 24 | &chaincfg.TestNet3Params, dir, true, defaultDBTimeout, 250, 25 | WithWalletSyncRetryInterval(10*time.Millisecond), 26 | ) 27 | _, err := loader.CreateNewWatchingOnlyWallet(pubPass, time.Now()) 28 | if err != nil { 29 | t.Fatalf("unable to create wallet: %v", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /waddrmgr/log.go: -------------------------------------------------------------------------------- 1 | package waddrmgr 2 | 3 | import ( 4 | "github.com/btcsuite/btclog" 5 | "github.com/btcsuite/btcwallet/build" 6 | ) 7 | 8 | // log is a logger that is initialized with no output filters. This 9 | // means the package will not perform any logging by default until the caller 10 | // requests it. 11 | var log btclog.Logger 12 | 13 | // The default amount of logging is none. 14 | func init() { 15 | UseLogger(build.NewSubLogger("AMGR", nil)) 16 | } 17 | 18 | // DisableLog disables all library log output. Logging output is disabled 19 | // by default until either UseLogger or SetLogWriter are called. 20 | func DisableLog() { 21 | UseLogger(btclog.Disabled) 22 | } 23 | 24 | // UseLogger uses a specified Logger to output package logging info. 25 | // This should be used in preference to SetLogWriter if the caller is also 26 | // using btclog. 27 | func UseLogger(logger btclog.Logger) { 28 | log = logger 29 | } 30 | -------------------------------------------------------------------------------- /wtxmgr/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/btcsuite/btcwallet/wtxmgr 2 | 3 | require ( 4 | github.com/btcsuite/btcd v0.23.4 5 | github.com/btcsuite/btcd/btcutil v1.1.0 6 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 7 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f 8 | github.com/btcsuite/btcwallet/walletdb v1.5.1 9 | github.com/lightningnetwork/lnd/clock v1.0.1 10 | github.com/stretchr/testify v1.9.0 11 | ) 12 | 13 | require ( 14 | github.com/btcsuite/btcd/btcec/v2 v2.1.3 // indirect 15 | github.com/davecgh/go-spew v1.1.1 // indirect 16 | github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect 17 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect 18 | github.com/pmezard/go-difflib v1.0.0 // indirect 19 | go.etcd.io/bbolt v1.3.11 // indirect 20 | golang.org/x/crypto v0.22.0 // indirect 21 | golang.org/x/sys v0.19.0 // indirect 22 | gopkg.in/yaml.v3 v3.0.1 // indirect 23 | ) 24 | 25 | go 1.22 26 | -------------------------------------------------------------------------------- /internal/prompt/prompt_js.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2021 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package prompt 6 | 7 | import ( 8 | "bufio" 9 | "fmt" 10 | 11 | "github.com/btcsuite/btcwallet/internal/legacy/keystore" 12 | ) 13 | 14 | func ProvideSeed() ([]byte, error) { 15 | return nil, fmt.Errorf("prompt not supported in WebAssembly") 16 | } 17 | 18 | func ProvidePrivPassphrase() ([]byte, error) { 19 | return nil, fmt.Errorf("prompt not supported in WebAssembly") 20 | } 21 | 22 | func PrivatePass(_ *bufio.Reader, _ *keystore.Store) ([]byte, error) { 23 | return nil, fmt.Errorf("prompt not supported in WebAssembly") 24 | } 25 | 26 | func PublicPass(_ *bufio.Reader, _, _, _ []byte) ([]byte, error) { 27 | return nil, fmt.Errorf("prompt not supported in WebAssembly") 28 | } 29 | 30 | func Seed(_ *bufio.Reader) ([]byte, error) { 31 | return nil, fmt.Errorf("prompt not supported in WebAssembly") 32 | } 33 | -------------------------------------------------------------------------------- /wallet/txauthor/cprng.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package txauthor 6 | 7 | import ( 8 | "crypto/rand" 9 | "encoding/binary" 10 | mrand "math/rand" 11 | "sync" 12 | ) 13 | 14 | // cprng is a cryptographically random-seeded math/rand prng. It is seeded 15 | // during package init. Any initialization errors result in panics. It is safe 16 | // for concurrent access. 17 | var cprng = cprngType{} 18 | 19 | type cprngType struct { 20 | r *mrand.Rand 21 | mu sync.Mutex 22 | } 23 | 24 | func init() { 25 | buf := make([]byte, 8) 26 | _, err := rand.Read(buf) 27 | if err != nil { 28 | panic("Failed to seed prng: " + err.Error()) 29 | } 30 | 31 | seed := int64(binary.LittleEndian.Uint64(buf)) 32 | cprng.r = mrand.New(mrand.NewSource(seed)) 33 | } 34 | 35 | func (c *cprngType) Int31n(n int32) int32 { 36 | defer c.mu.Unlock() // Int31n may panic 37 | c.mu.Lock() 38 | return c.r.Int31n(n) 39 | } 40 | -------------------------------------------------------------------------------- /walletdb/bdb/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package bdb implements an instance of walletdb that uses boltdb for the backing 7 | datastore. 8 | 9 | # Usage 10 | 11 | This package is only a driver to the walletdb package and provides the database 12 | type of "bdb". The only parameters the Open and Create functions take are the 13 | database path as a string, an option for the database to not sync its freelist 14 | to disk as a bool, a timeout value for opening the database as a 15 | time.Duration and an option to open the database in read-only mode as a bool: 16 | 17 | db, err := walletdb.Open( 18 | "bdb", "path/to/database.db", true, 60*time.Second, false, 19 | ) 20 | if err != nil { 21 | // Handle error 22 | } 23 | 24 | db, err := walletdb.Create( 25 | "bdb", "path/to/database.db", true, 60*time.Second, false, 26 | ) 27 | if err != nil { 28 | // Handle error 29 | } 30 | */ 31 | package bdb 32 | -------------------------------------------------------------------------------- /wallet/README.md: -------------------------------------------------------------------------------- 1 | wallet 2 | ====== 3 | 4 | [![Build Status](https://travis-ci.org/btcsuite/btcwallet.png?branch=master)] 5 | (https://travis-ci.org/btcsuite/btcwallet) 6 | 7 | ## Feature Overview 8 | 9 | TODO: Flesh out this section 10 | 11 | ## Documentation 12 | 13 | [![GoDoc](https://godoc.org/github.com/btcsuite/btcwallet/wallet?status.png)] 14 | (http://godoc.org/github.com/btcsuite/btcwallet/wallet) 15 | 16 | Full `go doc` style documentation for the project can be viewed online without 17 | installing this package by using the GoDoc site here: 18 | http://godoc.org/github.com/btcsuite/btcwallet/wallet 19 | 20 | You can also view the documentation locally once the package is installed with 21 | the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to 22 | http://localhost:6060/pkg/github.com/btcsuite/btcwallet/wallet 23 | 24 | ## Installation 25 | 26 | ```bash 27 | $ go get github.com/btcsuite/btcwallet/wallet 28 | ``` 29 | 30 | Package wallet is licensed under the [copyfree](http://copyfree.org) ISC 31 | License. 32 | -------------------------------------------------------------------------------- /walletdb/bdb/interface_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | // This file intended to be copied into each backend driver directory. Each 6 | // driver should have their own driver_test.go file which creates a database and 7 | // invokes the testInterface function in this file to ensure the driver properly 8 | // implements the interface. See the bdb backend driver for a working example. 9 | // 10 | // NOTE: When copying this file into the backend driver folder, the package name 11 | // will need to be changed accordingly. 12 | 13 | package bdb_test 14 | 15 | import ( 16 | "os" 17 | "path/filepath" 18 | "testing" 19 | 20 | "github.com/btcsuite/btcwallet/walletdb/walletdbtest" 21 | ) 22 | 23 | // TestInterface performs all interfaces tests for this database driver. 24 | func TestInterface(t *testing.T) { 25 | tempDir := t.TempDir() 26 | 27 | dbPath := filepath.Join(tempDir, "db") 28 | defer os.RemoveAll(dbPath) 29 | walletdbtest.TestInterface( 30 | t, dbType, dbPath, true, defaultDBTimeout, false, 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /rpc/legacyrpc/rpcserver_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package legacyrpc 6 | 7 | import ( 8 | "net/http" 9 | "net/http/httptest" 10 | "reflect" 11 | "testing" 12 | ) 13 | 14 | func TestThrottle(t *testing.T) { 15 | const threshold = 1 16 | busy := make(chan struct{}) 17 | 18 | srv := httptest.NewServer(throttledFn(threshold, 19 | func(w http.ResponseWriter, r *http.Request) { 20 | <-busy 21 | }), 22 | ) 23 | 24 | codes := make(chan int, 2) 25 | for i := 0; i < cap(codes); i++ { 26 | go func() { 27 | res, err := http.Get(srv.URL) 28 | if err != nil { 29 | t.Log(err) 30 | return 31 | } 32 | codes <- res.StatusCode 33 | _ = res.Body.Close() 34 | }() 35 | } 36 | 37 | got := make(map[int]int, cap(codes)) 38 | for i := 0; i < cap(codes); i++ { 39 | got[<-codes]++ 40 | 41 | if i == 0 { 42 | close(busy) 43 | } 44 | } 45 | 46 | want := map[int]int{200: 1, 429: 1} 47 | if !reflect.DeepEqual(want, got) { 48 | t.Fatalf("status codes: want: %v, got: %v", want, got) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /internal/zero/slice.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | // This file implements range-based zeroing, which as of Go 1.5 is 6 | // optimized using a Duff's device. 7 | 8 | package zero 9 | 10 | import "math/big" 11 | 12 | // Bytes sets all bytes in the passed slice to zero. This is used to 13 | // explicitly clear private key material from memory. 14 | // 15 | // In general, prefer to use the fixed-sized zeroing functions (Bytea*) 16 | // when zeroing bytes as they are much more efficient than the variable 17 | // sized zeroing func Bytes. 18 | func Bytes(b []byte) { 19 | for i := range b { 20 | b[i] = 0 21 | } 22 | } 23 | 24 | // BigInt sets all bytes in the passed big int to zero and then sets the 25 | // value to 0. This differs from simply setting the value in that it 26 | // specifically clears the underlying bytes whereas simply setting the value 27 | // does not. This is mostly useful to forcefully clear private keys. 28 | func BigInt(x *big.Int) { 29 | b := x.Bits() 30 | for i := range b { 31 | b[i] = 0 32 | } 33 | x.SetInt64(0) 34 | } 35 | -------------------------------------------------------------------------------- /.gemini/config.yaml: -------------------------------------------------------------------------------- 1 | # Config for the Gemini Pull Request Review Bot. 2 | # https://developers.google.com/gemini-code-assist/docs/customize-gemini-behavior-github 3 | 4 | # Enables fun features such as a poem in the initial pull request summary. 5 | # Type: boolean, default: false. 6 | have_fun: false 7 | 8 | code_review: 9 | # Disables Gemini from acting on PRs. 10 | # Type: boolean, default: false. 11 | disable: false 12 | 13 | # Minimum severity of comments to post (LOW, MEDIUM, HIGH, CRITICAL). 14 | # Type: string, default: MEDIUM. 15 | comment_severity_threshold: MEDIUM 16 | 17 | # Max number of review comments (-1 for unlimited). 18 | # Type: integer, default: -1. 19 | max_review_comments: -1 20 | 21 | pull_request_opened: 22 | # Post helpful instructions when PR is opened. 23 | # Type: boolean, default: false. 24 | help: false 25 | 26 | # Post PR summary when opened. 27 | # Type boolean, default: true. 28 | summary: true 29 | 30 | # Post code review on PR open. 31 | # Type boolean, default: true. 32 | code_review: true 33 | 34 | # List of glob patterns to ignore (files and directories). 35 | # Type: array of string, default: []. 36 | ignore_patterns: [] 37 | -------------------------------------------------------------------------------- /internal/cfgutil/amount.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package cfgutil 6 | 7 | import ( 8 | "strconv" 9 | "strings" 10 | 11 | "github.com/btcsuite/btcd/btcutil" 12 | ) 13 | 14 | // AmountFlag embeds a btcutil.Amount and implements the flags.Marshaler and 15 | // Unmarshaler interfaces so it can be used as a config struct field. 16 | type AmountFlag struct { 17 | btcutil.Amount 18 | } 19 | 20 | // NewAmountFlag creates an AmountFlag with a default btcutil.Amount. 21 | func NewAmountFlag(defaultValue btcutil.Amount) *AmountFlag { 22 | return &AmountFlag{defaultValue} 23 | } 24 | 25 | // MarshalFlag satisfies the flags.Marshaler interface. 26 | func (a *AmountFlag) MarshalFlag() (string, error) { 27 | return a.Amount.String(), nil 28 | } 29 | 30 | // UnmarshalFlag satisfies the flags.Unmarshaler interface. 31 | func (a *AmountFlag) UnmarshalFlag(value string) error { 32 | value = strings.TrimSuffix(value, " BTC") 33 | valueF64, err := strconv.ParseFloat(value, 64) 34 | if err != nil { 35 | return err 36 | } 37 | amount, err := btcutil.NewAmount(valueF64) 38 | if err != nil { 39 | return err 40 | } 41 | a.Amount = amount 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Change Description 2 | Description of change / link to associated issue. 3 | 4 | ## Steps to Test 5 | Steps for reviewers to follow to test the change. 6 | 7 | ## Pull Request Checklist 8 | ### Testing 9 | - [ ] Your PR passes all CI checks. 10 | - [ ] Tests covering the positive and negative (error paths) are included. 11 | - [ ] Bug fixes contain tests triggering the bug to prevent regressions. 12 | 13 | ### Code Style and Documentation 14 | - [ ] The change is not [insubstantial](https://github.com/btcsuite/btcwallet/blob/master/docs/developer/contribution_guidelines.md#substantial-contributions-only). Typo fixes are not accepted to fight bot spam. 15 | - [ ] The change obeys the [Code Documentation and Commenting](https://github.com/btcsuite/btcwallet/blob/master/docs/developer/contribution_guidelines.md#code-documentation-and-commenting) guidelines, and lines wrap at 80. 16 | - [ ] Commits follow the [Ideal Git Commit Structure](https://github.com/btcsuite/btcwallet/blob/master/docs/developer/contribution_guidelines.md#ideal-git-commit-structure). 17 | - [ ] Any new logging statements use an appropriate subsystem and logging level. 18 | 19 | 📝 Please see our [Contribution Guidelines](https://github.com/btcsuite/btcwallet/blob/master/docs/developer/contribution_guidelines.md) for further guidance. 20 | -------------------------------------------------------------------------------- /docs/developer/README.md: -------------------------------------------------------------------------------- 1 | # Developer Documentation 2 | 3 | This section contains documentation for developers and contributors to `btcwallet`. 4 | 5 | --- 6 | 7 | ## 📜 Contribution Guidelines 8 | 9 | Best practices for contributing code, including our development process, commit message standards, and code review process. 10 | 11 | **[➡️ Read the Contribution Guidelines](./contribution_guidelines.md)** 12 | 13 | --- 14 | 15 | ## 🎨 Code Formatting Rules 16 | 17 | Specific rules for code style and formatting to ensure consistency across the project. 18 | 19 | **[➡️ View the Formatting Rules](./code_formatting_rules.md)** 20 | 21 | --- 22 | 23 | ## ✅ Unit Testing Guidelines 24 | 25 | Best practices for writing clear, effective, and maintainable unit tests. 26 | 27 | **[➡️ Learn about Unit Testing](./unit_testing_guidelines.md)** 28 | 29 | --- 30 | 31 | ## 🏛️ Engineering Guide 32 | 33 | A deep dive into the core design philosophy, architectural patterns, and Go implementation details that guide the development of `btcwallet`. 34 | 35 | **[➡️ Read the Engineering Guide](./ENGINEERING_GUIDE.md)** 36 | 37 | --- 38 | 39 | ## 📝 Architecture Decision Records (ADRs) 40 | 41 | Formal documentation of significant architectural decisions, their context, and consequences. 42 | 43 | **[➡️ View Architecture Decision Records](./adr/README.md)** -------------------------------------------------------------------------------- /walletdb/go.sum: -------------------------------------------------------------------------------- 1 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= 2 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 8 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 9 | go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= 10 | go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= 11 | golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= 12 | golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 13 | golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= 14 | golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 15 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 16 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 17 | -------------------------------------------------------------------------------- /walletdb/migration/log.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import "github.com/btcsuite/btclog" 4 | 5 | // log is a logger that is initialized with no output filters. This 6 | // means the package will not perform any logging by default until the caller 7 | // requests it. 8 | var log btclog.Logger 9 | 10 | // The default amount of logging is none. 11 | func init() { 12 | DisableLog() 13 | } 14 | 15 | // DisableLog disables all library log output. Logging output is disabled 16 | // by default until either UseLogger or SetLogWriter are called. 17 | func DisableLog() { 18 | UseLogger(btclog.Disabled) 19 | } 20 | 21 | // UseLogger uses a specified Logger to output package logging info. 22 | // This should be used in preference to SetLogWriter if the caller is also 23 | // using btclog. 24 | func UseLogger(logger btclog.Logger) { 25 | log = logger 26 | } 27 | 28 | // LogClosure is a closure that can be printed with %v to be used to 29 | // generate expensive-to-create data for a detailed log level and avoid doing 30 | // the work if the data isn't printed. 31 | type logClosure func() string 32 | 33 | // String invokes the log closure and returns the results string. 34 | func (c logClosure) String() string { 35 | return c() 36 | } 37 | 38 | // newLogClosure returns a new closure over the passed function which allows 39 | // it to be used as a parameter in a logging function that is only invoked when 40 | // the logging level is such that the message will actually be logged. 41 | func newLogClosure(c func() string) logClosure { 42 | return logClosure(c) 43 | } 44 | -------------------------------------------------------------------------------- /chain/chainservice.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/btcsuite/btcd/btcutil" 7 | "github.com/btcsuite/btcd/btcutil/gcs" 8 | "github.com/btcsuite/btcd/chaincfg" 9 | "github.com/btcsuite/btcd/chaincfg/chainhash" 10 | "github.com/btcsuite/btcd/wire" 11 | "github.com/lightninglabs/neutrino" 12 | "github.com/lightninglabs/neutrino/banman" 13 | "github.com/lightninglabs/neutrino/headerfs" 14 | ) 15 | 16 | // NeutrinoChainService is an interface that encapsulates all the public 17 | // methods of a *neutrino.ChainService 18 | type NeutrinoChainService interface { 19 | Start(ctx context.Context) error 20 | GetBlock(chainhash.Hash, ...neutrino.QueryOption) (*btcutil.Block, error) 21 | GetBlockHeight(*chainhash.Hash) (int32, error) 22 | BestBlock() (*headerfs.BlockStamp, error) 23 | GetBlockHash(int64) (*chainhash.Hash, error) 24 | GetBlockHeader(*chainhash.Hash) (*wire.BlockHeader, error) 25 | IsCurrent() bool 26 | SendTransaction(*wire.MsgTx) error 27 | GetCFilter(chainhash.Hash, wire.FilterType, 28 | ...neutrino.QueryOption) (*gcs.Filter, error) 29 | GetUtxo(...neutrino.RescanOption) (*neutrino.SpendReport, error) 30 | BanPeer(string, banman.Reason) error 31 | IsBanned(addr string) bool 32 | AddPeer(*neutrino.ServerPeer) 33 | AddBytesSent(uint64) 34 | AddBytesReceived(uint64) 35 | NetTotals() (uint64, uint64) 36 | UpdatePeerHeights(*chainhash.Hash, int32, *neutrino.ServerPeer) 37 | ChainParams() chaincfg.Params 38 | Stop() error 39 | PeerByAddr(string) *neutrino.ServerPeer 40 | } 41 | 42 | var _ NeutrinoChainService = (*neutrino.ChainService)(nil) 43 | -------------------------------------------------------------------------------- /internal/cfgutil/explicitflags.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package cfgutil 6 | 7 | // ExplicitString is a string value implementing the flags.Marshaler and 8 | // flags.Unmarshaler interfaces so it may be used as a config struct field. It 9 | // records whether the value was explicitly set by the flags package. This is 10 | // useful when behavior must be modified depending on whether a flag was set by 11 | // the user or left as a default. Without recording this, it would be 12 | // impossible to determine whether flag with a default value was unmodified or 13 | // explicitly set to the default. 14 | type ExplicitString struct { 15 | Value string 16 | explicitlySet bool 17 | } 18 | 19 | // NewExplicitString creates a string flag with the provided default value. 20 | func NewExplicitString(defaultValue string) *ExplicitString { 21 | return &ExplicitString{Value: defaultValue, explicitlySet: false} 22 | } 23 | 24 | // ExplicitlySet returns whether the flag was explicitly set through the 25 | // flags.Unmarshaler interface. 26 | func (e *ExplicitString) ExplicitlySet() bool { return e.explicitlySet } 27 | 28 | // MarshalFlag implements the flags.Marshaler interface. 29 | func (e *ExplicitString) MarshalFlag() (string, error) { return e.Value, nil } 30 | 31 | // UnmarshalFlag implements the flags.Unmarshaler interface. 32 | func (e *ExplicitString) UnmarshalFlag(value string) error { 33 | e.Value = value 34 | e.explicitlySet = true 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /chain/rescan.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import "github.com/lightninglabs/neutrino" 4 | 5 | var _ rescanner = (*neutrino.Rescan)(nil) 6 | 7 | // rescanner is an interface that abstractly defines the public methods of 8 | // a *neutrino.Rescan. The interface is private because it is only ever 9 | // intended to be implemented by a *neutrino.Rescan. 10 | type rescanner interface { 11 | starter 12 | updater 13 | 14 | // WaitForShutdown blocks until the underlying rescan object is shutdown. 15 | // Close the quit channel before calling WaitForShutdown. 16 | WaitForShutdown() 17 | } 18 | 19 | // updater is the interface that wraps the Update method of a rescan object. 20 | type updater interface { 21 | // Update targets a long-running rescan/notification client with 22 | // updateable filters. Attempts to update the filters will fail 23 | // if either the rescan is no longer running or the shutdown signal is 24 | // received prior to sending the update. 25 | Update(...neutrino.UpdateOption) error 26 | } 27 | 28 | // starter is the interface that wraps the Start method of a rescan object. 29 | type starter interface { 30 | // Start initializes the rescan goroutine, which will begin to scan the chain 31 | // according to the specified rescan options. Start returns a channel that 32 | // communicates any startup errors. Attempts to start a running rescan 33 | // goroutine will error. 34 | Start() <-chan error 35 | } 36 | 37 | // newRescanFunc defines a constructor that accepts rescan options and returns 38 | // an object that satisfies rescanner interface. 39 | type newRescanFunc func(...neutrino.RescanOption) rescanner 40 | -------------------------------------------------------------------------------- /chain/btcd_test.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/btcsuite/btcd/chaincfg" 7 | "github.com/btcsuite/btcd/rpcclient" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | // TestValidateConfig checks the `validate` method on the RPCClientConfig 12 | // behaves as expected. 13 | func TestValidateConfig(t *testing.T) { 14 | t.Parallel() 15 | 16 | rt := require.New(t) 17 | 18 | // ReconnectAttempts must be positive. 19 | cfg := &RPCClientConfig{ 20 | ReconnectAttempts: -1, 21 | } 22 | rt.ErrorContains(cfg.validate(), "reconnectAttempts") 23 | 24 | // Must specify a chain params. 25 | cfg = &RPCClientConfig{ 26 | ReconnectAttempts: 1, 27 | } 28 | rt.ErrorContains(cfg.validate(), "chain params") 29 | 30 | // Must specify a connection config. 31 | cfg = &RPCClientConfig{ 32 | ReconnectAttempts: 1, 33 | Chain: &chaincfg.Params{}, 34 | } 35 | rt.ErrorContains(cfg.validate(), "conn config") 36 | 37 | // Must specify a certificate when using TLS. 38 | cfg = &RPCClientConfig{ 39 | ReconnectAttempts: 1, 40 | Chain: &chaincfg.Params{}, 41 | Conn: &rpcclient.ConnConfig{}, 42 | } 43 | rt.ErrorContains(cfg.validate(), "certs") 44 | 45 | // Validate config. 46 | cfg = &RPCClientConfig{ 47 | ReconnectAttempts: 1, 48 | Chain: &chaincfg.Params{}, 49 | Conn: &rpcclient.ConnConfig{ 50 | DisableTLS: true, 51 | }, 52 | } 53 | rt.NoError(cfg.validate()) 54 | 55 | // When a nil config is provided, it should return an error. 56 | _, err := NewRPCClientWithConfig(nil) 57 | rt.ErrorContains(err, "missing rpc config") 58 | } 59 | -------------------------------------------------------------------------------- /wtxmgr/README.md: -------------------------------------------------------------------------------- 1 | wtxmgr 2 | ====== 3 | 4 | [![Build Status](https://travis-ci.org/btcsuite/btcwallet.png?branch=master)] 5 | (https://travis-ci.org/btcsuite/btcwallet) 6 | 7 | Package wtxmgr provides storage and spend tracking of wallet transactions and 8 | their relevant input and outputs. 9 | 10 | ## Feature overview 11 | 12 | - Storage for relevant wallet transactions 13 | - Ability to mark outputs as controlled by wallet 14 | - Unspent transaction output index 15 | - Balance tracking 16 | - Automatic spend tracking for transaction inserts and removals 17 | - Double spend detection and correction after blockchain reorgs 18 | - Scalable design: 19 | - Utilizes similar prefixes to allow cursor iteration over relevant transaction 20 | inputs and outputs 21 | - Programmatically detectable errors, including encapsulation of errors from 22 | packages it relies on 23 | - Operates under its own walletdb namespace 24 | 25 | ## Documentation 26 | 27 | [![GoDoc](https://godoc.org/github.com/btcsuite/btcwallet/wtxmgr?status.png)] 28 | (http://godoc.org/github.com/btcsuite/btcwallet/wtxmgr) 29 | 30 | Full `go doc` style documentation for the project can be viewed online without 31 | installing this package by using the GoDoc site here: 32 | http://godoc.org/github.com/btcsuite/btcwallet/wtxmgr 33 | 34 | You can also view the documentation locally once the package is installed with 35 | the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to 36 | http://localhost:6060/pkg/github.com/btcsuite/btcwallet/wtxmgr 37 | 38 | ## Installation 39 | 40 | ```bash 41 | $ go get github.com/btcsuite/btcwallet/wtxmgr 42 | ``` 43 | 44 | Package wtxmgr is licensed under the [copyfree](http://copyfree.org) ISC 45 | License. 46 | -------------------------------------------------------------------------------- /chain/log.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package chain 6 | 7 | import ( 8 | "github.com/btcsuite/btclog" 9 | "github.com/btcsuite/btcwallet/build" 10 | "github.com/lightninglabs/neutrino/query" 11 | ) 12 | 13 | // log is a logger that is initialized with no output filters. This 14 | // means the package will not perform any logging by default until the caller 15 | // requests it. 16 | var log btclog.Logger 17 | 18 | // The default amount of logging is none. 19 | func init() { 20 | UseLogger(build.NewSubLogger("CHIO", nil)) 21 | } 22 | 23 | // DisableLog disables all library log output. Logging output is disabled 24 | // by default until either UseLogger or SetLogWriter are called. 25 | func DisableLog() { 26 | log = btclog.Disabled 27 | } 28 | 29 | // UseLogger uses a specified Logger to output package logging info. 30 | // This should be used in preference to SetLogWriter if the caller is also 31 | // using btclog. 32 | func UseLogger(logger btclog.Logger) { 33 | log = logger 34 | query.UseLogger(logger) 35 | } 36 | 37 | // LogClosure is used to provide a closure over expensive logging operations so 38 | // don't have to be performed when the logging level doesn't warrant it. 39 | type LogClosure func() string 40 | 41 | // String invokes the underlying function and returns the result. 42 | func (c LogClosure) String() string { 43 | return c() 44 | } 45 | 46 | // NewLogClosure returns a new closure over a function that returns a string 47 | // which itself provides a Stringer interface so that it can be used with the 48 | // logging system. 49 | func NewLogClosure(c func() string) LogClosure { 50 | return LogClosure(c) 51 | } 52 | -------------------------------------------------------------------------------- /internal/cfgutil/normalization.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package cfgutil 6 | 7 | import "net" 8 | 9 | // NormalizeAddress returns the normalized form of the address, adding a default 10 | // port if necessary. An error is returned if the address, even without a port, 11 | // is not valid. 12 | func NormalizeAddress(addr string, defaultPort string) (hostport string, err error) { 13 | // If the first SplitHostPort errors because of a missing port and not 14 | // for an invalid host, add the port. If the second SplitHostPort 15 | // fails, then a port is not missing and the original error should be 16 | // returned. 17 | host, port, origErr := net.SplitHostPort(addr) 18 | if origErr == nil { 19 | return net.JoinHostPort(host, port), nil 20 | } 21 | addr = net.JoinHostPort(addr, defaultPort) 22 | _, _, err = net.SplitHostPort(addr) 23 | if err != nil { 24 | return "", origErr 25 | } 26 | return addr, nil 27 | } 28 | 29 | // NormalizeAddresses returns a new slice with all the passed peer addresses 30 | // normalized with the given default port, and all duplicates removed. 31 | func NormalizeAddresses(addrs []string, defaultPort string) ([]string, error) { 32 | var ( 33 | normalized = make([]string, 0, len(addrs)) 34 | seenSet = make(map[string]struct{}) 35 | ) 36 | 37 | for _, addr := range addrs { 38 | normalizedAddr, err := NormalizeAddress(addr, defaultPort) 39 | if err != nil { 40 | return nil, err 41 | } 42 | _, seen := seenSet[normalizedAddr] 43 | if !seen { 44 | normalized = append(normalized, normalizedAddr) 45 | seenSet[normalizedAddr] = struct{}{} 46 | } 47 | } 48 | 49 | return normalized, nil 50 | } 51 | -------------------------------------------------------------------------------- /wtxmgr/log.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package wtxmgr 6 | 7 | import ( 8 | "github.com/btcsuite/btclog" 9 | "github.com/btcsuite/btcwallet/build" 10 | ) 11 | 12 | // log is a logger that is initialized with no output filters. This 13 | // means the package will not perform any logging by default until the caller 14 | // requests it. 15 | var log btclog.Logger 16 | 17 | // The default amount of logging is none. 18 | func init() { 19 | UseLogger(build.NewSubLogger("TMGR", nil)) 20 | } 21 | 22 | // DisableLog disables all library log output. Logging output is disabled 23 | // by default until either UseLogger or SetLogWriter are called. 24 | func DisableLog() { 25 | UseLogger(btclog.Disabled) 26 | } 27 | 28 | // UseLogger uses a specified Logger to output package logging info. 29 | // This should be used in preference to SetLogWriter if the caller is also 30 | // using btclog. 31 | func UseLogger(logger btclog.Logger) { 32 | log = logger 33 | } 34 | 35 | // LogClosure is a closure that can be printed with %v to be used to 36 | // generate expensive-to-create data for a detailed log level and avoid doing 37 | // the work if the data isn't printed. 38 | type logClosure func() string 39 | 40 | // String invokes the log closure and returns the results string. 41 | func (c logClosure) String() string { 42 | return c() 43 | } 44 | 45 | // newLogClosure returns a new closure over the passed function which allows 46 | // it to be used as a parameter in a logging function that is only invoked when 47 | // the logging level is such that the message will actually be logged. 48 | func newLogClosure(c func() string) logClosure { 49 | return logClosure(c) 50 | } 51 | -------------------------------------------------------------------------------- /walletdb/bdb/README.md: -------------------------------------------------------------------------------- 1 | bdb 2 | === 3 | 4 | [![Build Status](https://travis-ci.org/btcsuite/btcwallet.png?branch=master)] 5 | (https://travis-ci.org/btcsuite/btcwallet) 6 | 7 | Package bdb implements an driver for walletdb that uses boltdb for the backing 8 | datastore. Package bdb is licensed under the copyfree ISC license. 9 | 10 | ## Usage 11 | 12 | This package is only a driver to the walletdb package and provides the database 13 | type of "bdb". The only parameters the Open and Create functions take are the 14 | database path as a string, an option for the database to not sync its freelist 15 | to disk as a bool, a timeout value for opening the database as a time.Duration 16 | and an option to open the database in read-only mode as a bool: 17 | 18 | ```Go 19 | db, err := walletdb.Open( 20 | "bdb", "path/to/database.db", true, 60*time.Second,false, 21 | ) 22 | if err != nil { 23 | // Handle error 24 | } 25 | ``` 26 | 27 | ```Go 28 | db, err := walletdb.Create( 29 | "bdb", "path/to/database.db", true, 60*time.Second,false, 30 | ) 31 | if err != nil { 32 | // Handle error 33 | } 34 | ``` 35 | 36 | ## Documentation 37 | 38 | [![GoDoc](https://godoc.org/github.com/btcsuite/btcwallet/walletdb/bdb?status.png)] 39 | (http://godoc.org/github.com/btcsuite/btcwallet/walletdb/bdb) 40 | 41 | Full `go doc` style documentation for the project can be viewed online without 42 | installing this package by using the GoDoc site here: 43 | http://godoc.org/github.com/btcsuite/btcwallet/walletdb/bdb 44 | 45 | You can also view the documentation locally once the package is installed with 46 | the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to 47 | http://localhost:6060/pkg/github.com/btcsuite/btcwallet/walletdb/bdb 48 | 49 | ## License 50 | 51 | Package bdb is licensed under the [copyfree](http://copyfree.org) ISC 52 | License. 53 | -------------------------------------------------------------------------------- /wallet/unstable.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 The Decred developers 2 | // Copyright (c) 2017 The btcsuite developers 3 | // Use of this source code is governed by an ISC 4 | // license that can be found in the LICENSE file. 5 | 6 | package wallet 7 | 8 | import ( 9 | "github.com/btcsuite/btcd/chaincfg/chainhash" 10 | "github.com/btcsuite/btcwallet/walletdb" 11 | "github.com/btcsuite/btcwallet/wtxmgr" 12 | ) 13 | 14 | type unstableAPI struct { 15 | w *Wallet 16 | } 17 | 18 | // UnstableAPI exposes additional unstable public APIs for a Wallet. These APIs 19 | // may be changed or removed at any time. Currently this type exists to ease 20 | // the transition (particularly for the legacy JSON-RPC server) from using 21 | // exported manager packages to a unified wallet package that exposes all 22 | // functionality by itself. New code should not be written using this API. 23 | func UnstableAPI(w *Wallet) unstableAPI { return unstableAPI{w} } // nolint:golint 24 | 25 | // TxDetails calls wtxmgr.Store.TxDetails under a single database view transaction. 26 | func (u unstableAPI) TxDetails(txHash *chainhash.Hash) (*wtxmgr.TxDetails, error) { 27 | var details *wtxmgr.TxDetails 28 | err := walletdb.View(u.w.db, func(dbtx walletdb.ReadTx) error { 29 | txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey) 30 | var err error 31 | details, err = u.w.TxStore.TxDetails(txmgrNs, txHash) 32 | return err 33 | }) 34 | return details, err 35 | } 36 | 37 | // RangeTransactions calls wtxmgr.Store.RangeTransactions under a single 38 | // database view tranasction. 39 | func (u unstableAPI) RangeTransactions(begin, end int32, f func([]wtxmgr.TxDetails) (bool, error)) error { 40 | return walletdb.View(u.w.db, func(dbtx walletdb.ReadTx) error { 41 | txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey) 42 | return u.w.TxStore.RangeTransactions(txmgrNs, begin, end, f) 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /docs/releases/TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # btcwallet vX.Y.Z 2 | 3 | ## Release Highlights 4 | 5 | *A brief, 2-3 sentence summary of the most important changes in this release.* 6 | 7 | --- 8 | # Release Notes 9 | - [Bug Fixes](#bug-fixes) 10 | - [New Features](#new-features) 11 | - [Functional Enhancements](#functional-enhancements) 12 | - [RPC Additions](#rpc-additions) 13 | - [CLI Tools Additions](#cli-tools-additions) 14 | - [Improvements](#improvements) 15 | - [Functional Updates](#functional-updates) 16 | - [RPC Updates](#rpc-updates) 17 | - [CLI Tools Updates](#cli-tools-updates) 18 | - [Performance Improvements](#performance-improvements) 19 | - [Breaking Changes](#breaking-changes) 20 | - [Deprecations](#deprecations) 21 | - [Technical and Architectural Updates](#technical-and-architectural-updates) 22 | - [Database](#database) 23 | - [Testing](#testing) 24 | - [Code Health](#code-health) 25 | - [Tooling and Documentation](#tooling-and-documentation) 26 | - [Contributors](#contributors) 27 | 28 | --- 29 | 30 | # Bug Fixes 31 | 32 | * ... 33 | 34 | # New Features 35 | 36 | ## Functional Enhancements 37 | 38 | * ... 39 | 40 | ## RPC Additions 41 | 42 | * ... 43 | 44 | ## CLI Tools Additions 45 | 46 | * ... 47 | 48 | # Improvements 49 | 50 | ## Functional Updates 51 | 52 | * ... 53 | 54 | ## RPC Updates 55 | 56 | * ... 57 | 58 | ## CLI Tools Updates 59 | 60 | * ... 61 | 62 | ## Performance Improvements 63 | 64 | * ... 65 | 66 | ## Breaking Changes 67 | 68 | * ... 69 | 70 | ## Deprecations 71 | 72 | * ... 73 | 74 | # Technical and Architectural Updates 75 | 76 | ## Database 77 | 78 | * ... 79 | 80 | ## Testing 81 | 82 | * ... 83 | 84 | ## Code Health 85 | 86 | * ... 87 | 88 | ## Tooling and Documentation 89 | 90 | * ... 91 | 92 | # Contributors 93 | 94 | *A big thank you to everyone who contributed to this release!* 95 | 96 | * ... 97 | -------------------------------------------------------------------------------- /wtxmgr/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package wtxmgr provides an implementation of a transaction database handling 6 | // spend tracking for a bitcoin wallet. Its primary purpose is to save 7 | // transactions with outputs spendable with wallet keys and transactions that 8 | // are signed by wallet keys in memory, handle spend tracking for unspent 9 | // outputs and newly-inserted transactions, and report the spendable balance 10 | // from each unspent transaction output. It uses walletdb as the backend for 11 | // storing the serialized transaction objects in buckets. 12 | // 13 | // Transaction outputs which are spendable by wallet keys are called credits 14 | // (because they credit to a wallet's total spendable balance). Transaction 15 | // inputs which spend previously-inserted credits are called debits (because 16 | // they debit from the wallet's spendable balance). 17 | // 18 | // Spend tracking is mostly automatic. When a new transaction is inserted, if 19 | // it spends from any unspent credits, they are automatically marked spent by 20 | // the new transaction, and each input which spent a credit is marked as a 21 | // debit. However, transaction outputs of inserted transactions must manually 22 | // marked as credits, as this package has no knowledge of wallet keys or 23 | // addresses, and therefore cannot determine which outputs may be spent. 24 | // 25 | // Details regarding individual transactions and their credits and debits may be 26 | // queried either by just a transaction hash, or by hash and block. When 27 | // querying for just a transaction hash, the most recent transaction with a 28 | // matching hash will be queried. However, because transaction hashes may 29 | // collide with other transaction hashes, methods to query for specific 30 | // transactions in the chain (or unmined) are provided as well. 31 | package wtxmgr 32 | -------------------------------------------------------------------------------- /internal/zero/benchmark_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package zero_test 6 | 7 | import ( 8 | "testing" 9 | 10 | . "github.com/btcsuite/btcwallet/internal/zero" 11 | ) 12 | 13 | var ( 14 | bytes32 = make([]byte, 32) // typical key size 15 | bytes64 = make([]byte, 64) // passphrase hash size 16 | bytea32 = new([32]byte) 17 | bytea64 = new([64]byte) 18 | ) 19 | 20 | // xor is the "slow" byte zeroing implementation which this package 21 | // originally replaced. If this function benchmarks faster than the 22 | // functions exported by this package in a future Go version (perhaps 23 | // by calling runtime.memclr), replace the "optimized" versions with 24 | // this. 25 | func xor(b []byte) { 26 | for i := range b { 27 | b[i] ^= b[i] 28 | } 29 | } 30 | 31 | // zrange is an alternative zero implementation that, while currently 32 | // slower than the functions provided by this package, may be faster 33 | // in a future Go release. Switch to this or the xor implementation 34 | // if they ever become faster. 35 | func zrange(b []byte) { 36 | for i := range b { 37 | b[i] = 0 38 | } 39 | } 40 | 41 | func BenchmarkXor32(b *testing.B) { 42 | for i := 0; i < b.N; i++ { 43 | xor(bytes32) 44 | } 45 | } 46 | 47 | func BenchmarkXor64(b *testing.B) { 48 | for i := 0; i < b.N; i++ { 49 | xor(bytes64) 50 | } 51 | } 52 | 53 | func BenchmarkRange32(b *testing.B) { 54 | for i := 0; i < b.N; i++ { 55 | zrange(bytes32) 56 | } 57 | } 58 | 59 | func BenchmarkRange64(b *testing.B) { 60 | for i := 0; i < b.N; i++ { 61 | zrange(bytes64) 62 | } 63 | } 64 | 65 | func BenchmarkBytes32(b *testing.B) { 66 | for i := 0; i < b.N; i++ { 67 | Bytes(bytes32) 68 | } 69 | } 70 | 71 | func BenchmarkBytes64(b *testing.B) { 72 | for i := 0; i < b.N; i++ { 73 | Bytes(bytes64) 74 | } 75 | } 76 | 77 | func BenchmarkBytea32(b *testing.B) { 78 | for i := 0; i < b.N; i++ { 79 | Bytea32(bytea32) 80 | } 81 | } 82 | 83 | func BenchmarkBytea64(b *testing.B) { 84 | for i := 0; i < b.N; i++ { 85 | Bytea64(bytea64) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /wallet/log.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package wallet 6 | 7 | import ( 8 | "github.com/btcsuite/btclog" 9 | "github.com/btcsuite/btcwallet/build" 10 | "github.com/btcsuite/btcwallet/waddrmgr" 11 | "github.com/btcsuite/btcwallet/walletdb/migration" 12 | "github.com/btcsuite/btcwallet/wtxmgr" 13 | ) 14 | 15 | // log is a logger that is initialized with no output filters. This 16 | // means the package will not perform any logging by default until the caller 17 | // requests it. 18 | var log btclog.Logger 19 | 20 | // The default amount of logging is none. 21 | func init() { 22 | UseLogger(build.NewSubLogger("BTWL", nil)) 23 | } 24 | 25 | // DisableLog disables all library log output. Logging output is disabled 26 | // by default until either UseLogger or SetLogWriter are called. 27 | func DisableLog() { 28 | UseLogger(btclog.Disabled) 29 | } 30 | 31 | // UseLogger uses a specified Logger to output package logging info. 32 | // This should be used in preference to SetLogWriter if the caller is also 33 | // using btclog. 34 | func UseLogger(logger btclog.Logger) { 35 | log = logger 36 | 37 | migration.UseLogger(logger) 38 | waddrmgr.UseLogger(logger) 39 | wtxmgr.UseLogger(logger) 40 | } 41 | 42 | // pickNoun returns the singular or plural form of a noun depending 43 | // on the count n. 44 | func pickNoun(n int, singular, plural string) string { 45 | if n == 1 { 46 | return singular 47 | } 48 | return plural 49 | } 50 | 51 | // LogClosure is a closure that can be printed with %v to be used to 52 | // generate expensive-to-create data for a detailed log level and avoid doing 53 | // the work if the data isn't printed. 54 | type logClosure func() string 55 | 56 | // String invokes the log closure and returns the results string. 57 | func (c logClosure) String() string { 58 | return c() 59 | } 60 | 61 | // newLogClosure returns a new closure over the passed function which allows 62 | // it to be used as a parameter in a logging function that is only invoked when 63 | // the logging level is such that the message will actually be logged. 64 | func newLogClosure(c func() string) logClosure { 65 | return logClosure(c) 66 | } 67 | -------------------------------------------------------------------------------- /wallet/txrules/rules.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package txrules provides transaction rules that should be followed by 6 | // transaction authors for wide mempool acceptance and quick mining. 7 | package txrules 8 | 9 | import ( 10 | "errors" 11 | 12 | "github.com/btcsuite/btcd/btcutil" 13 | "github.com/btcsuite/btcd/mempool" 14 | "github.com/btcsuite/btcd/txscript" 15 | "github.com/btcsuite/btcd/wire" 16 | ) 17 | 18 | // DefaultRelayFeePerKb is the default minimum relay fee policy for a mempool. 19 | const DefaultRelayFeePerKb btcutil.Amount = 1e3 20 | 21 | // IsDustOutput determines whether a transaction output is considered dust. 22 | // Transactions with dust outputs are not standard and are rejected by mempools 23 | // with default policies. 24 | func IsDustOutput(output *wire.TxOut, relayFeePerKb btcutil.Amount) bool { 25 | // Unspendable outputs which solely carry data are not checked for dust. 26 | if txscript.GetScriptClass(output.PkScript) == txscript.NullDataTy { 27 | return false 28 | } 29 | 30 | return mempool.IsDust(output, relayFeePerKb) 31 | } 32 | 33 | // Transaction rule violations 34 | var ( 35 | ErrAmountNegative = errors.New("transaction output amount is negative") 36 | ErrAmountExceedsMax = errors.New("transaction output amount exceeds maximum value") 37 | ErrOutputIsDust = errors.New("transaction output is dust") 38 | ) 39 | 40 | // CheckOutput performs simple consensus and policy tests on a transaction 41 | // output. 42 | func CheckOutput(output *wire.TxOut, relayFeePerKb btcutil.Amount) error { 43 | if output.Value < 0 { 44 | return ErrAmountNegative 45 | } 46 | if output.Value > btcutil.MaxSatoshi { 47 | return ErrAmountExceedsMax 48 | } 49 | if IsDustOutput(output, relayFeePerKb) { 50 | return ErrOutputIsDust 51 | } 52 | return nil 53 | } 54 | 55 | // FeeForSerializeSize calculates the required fee for a transaction of some 56 | // arbitrary size given a mempool's relay fee policy. 57 | func FeeForSerializeSize(relayFeePerKb btcutil.Amount, txSerializeSize int) btcutil.Amount { 58 | fee := relayFeePerKb * btcutil.Amount(txSerializeSize) / 1000 59 | 60 | if fee == 0 && relayFeePerKb > 0 { 61 | fee = relayFeePerKb 62 | } 63 | 64 | if fee < 0 || fee > btcutil.MaxSatoshi { 65 | fee = btcutil.MaxSatoshi 66 | } 67 | 68 | return fee 69 | } 70 | -------------------------------------------------------------------------------- /wallet/example_test.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/btcsuite/btcd/btcutil/hdkeychain" 8 | "github.com/btcsuite/btcd/chaincfg" 9 | "github.com/btcsuite/btcwallet/waddrmgr" 10 | "github.com/btcsuite/btcwallet/walletdb" 11 | ) 12 | 13 | // defaultDBTimeout specifies the timeout value when opening the wallet 14 | // database. 15 | var defaultDBTimeout = 10 * time.Second 16 | 17 | // testWallet creates a test wallet and unlocks it. 18 | func testWallet(t *testing.T) (*Wallet, func()) { 19 | // Set up a wallet. 20 | dir := t.TempDir() 21 | 22 | seed, err := hdkeychain.GenerateSeed(hdkeychain.MinSeedBytes) 23 | if err != nil { 24 | t.Fatalf("unable to create seed: %v", err) 25 | } 26 | 27 | pubPass := []byte("hello") 28 | privPass := []byte("world") 29 | 30 | loader := NewLoader( 31 | &chaincfg.TestNet3Params, dir, true, defaultDBTimeout, 250, 32 | WithWalletSyncRetryInterval(10*time.Millisecond), 33 | ) 34 | w, err := loader.CreateNewWallet(pubPass, privPass, seed, time.Now()) 35 | if err != nil { 36 | t.Fatalf("unable to create wallet: %v", err) 37 | } 38 | chainClient := &mockChainClient{} 39 | w.chainClient = chainClient 40 | if err := w.Unlock(privPass, time.After(10*time.Minute)); err != nil { 41 | t.Fatalf("unable to unlock wallet: %v", err) 42 | } 43 | 44 | return w, func() {} 45 | } 46 | 47 | // testWalletWatchingOnly creates a test watch only wallet and unlocks it. 48 | func testWalletWatchingOnly(t *testing.T) (*Wallet, func()) { 49 | // Set up a wallet. 50 | dir := t.TempDir() 51 | 52 | pubPass := []byte("hello") 53 | loader := NewLoader( 54 | &chaincfg.TestNet3Params, dir, true, defaultDBTimeout, 250, 55 | WithWalletSyncRetryInterval(10*time.Millisecond), 56 | ) 57 | w, err := loader.CreateNewWatchingOnlyWallet(pubPass, time.Now()) 58 | if err != nil { 59 | t.Fatalf("unable to create wallet: %v", err) 60 | } 61 | chainClient := &mockChainClient{} 62 | w.chainClient = chainClient 63 | 64 | err = walletdb.Update(w.Database(), func(tx walletdb.ReadWriteTx) error { 65 | ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) 66 | for scope, schema := range waddrmgr.ScopeAddrMap { 67 | _, err := w.Manager.NewScopedKeyManager( 68 | ns, scope, schema, 69 | ) 70 | if err != nil { 71 | return err 72 | } 73 | } 74 | 75 | return nil 76 | }) 77 | if err != nil { 78 | t.Fatalf("unable to create default scopes: %v", err) 79 | } 80 | 81 | return w, func() {} 82 | } 83 | -------------------------------------------------------------------------------- /chain/errors_test.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | // TestMatchErrStr checks that `matchErrStr` can correctly replace the dashes 11 | // with spaces and turn title cases into lowercases for a given error and match 12 | // it against the specified string pattern. 13 | func TestMatchErrStr(t *testing.T) { 14 | t.Parallel() 15 | 16 | testCases := []struct { 17 | name string 18 | bitcoindErr error 19 | matchStr string 20 | matched bool 21 | }{ 22 | { 23 | name: "error without dashes", 24 | bitcoindErr: errors.New("missing input"), 25 | matchStr: "missing input", 26 | matched: true, 27 | }, 28 | { 29 | name: "match str without dashes", 30 | bitcoindErr: errors.New("missing-input"), 31 | matchStr: "missing input", 32 | matched: true, 33 | }, 34 | { 35 | name: "error with dashes", 36 | bitcoindErr: errors.New("missing-input"), 37 | matchStr: "missing input", 38 | matched: true, 39 | }, 40 | { 41 | name: "match str with dashes", 42 | bitcoindErr: errors.New("missing-input"), 43 | matchStr: "missing-input", 44 | matched: true, 45 | }, 46 | { 47 | name: "error with title case and dash", 48 | bitcoindErr: errors.New("Missing-Input"), 49 | matchStr: "missing input", 50 | matched: true, 51 | }, 52 | { 53 | name: "match str with title case and dash", 54 | bitcoindErr: errors.New("missing-input"), 55 | matchStr: "Missing-Input", 56 | matched: true, 57 | }, 58 | { 59 | name: "unmatched error", 60 | bitcoindErr: errors.New("missing input"), 61 | matchStr: "missingorspent", 62 | matched: false, 63 | }, 64 | } 65 | 66 | for _, tc := range testCases { 67 | t.Run(tc.name, func(t *testing.T) { 68 | matched := matchErrStr(tc.bitcoindErr, tc.matchStr) 69 | require.Equal(t, tc.matched, matched) 70 | }) 71 | } 72 | } 73 | 74 | // TestErrorSentinel checks that all defined RPCErr errors are added to the 75 | // method `Error`. 76 | func TestBitcoindErrorSentinel(t *testing.T) { 77 | t.Parallel() 78 | 79 | rt := require.New(t) 80 | 81 | for i := uint32(0); i < uint32(errSentinel); i++ { 82 | err := RPCErr(i) 83 | rt.NotEqualf(err.Error(), "unknown error", "error code %d is "+ 84 | "not defined, make sure to update it inside the Error "+ 85 | "method", i) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /.protolint.yml: -------------------------------------------------------------------------------- 1 | # The example configuration file for the protolint is located here: 2 | # https://github.com/yoheimuta/protolint/blob/master/_example/config/.protolint.yaml 3 | --- 4 | # Lint directives. 5 | lint: 6 | # Linter rules. 7 | # Run `protolint list` to see all available rules. 8 | rules: 9 | # Determines whether or not to include the default set of linters. 10 | no_default: true 11 | 12 | # Set the default to all linters. This option works the other way around as no_default does. 13 | # If you want to enable this option, delete the comment out below and no_default. 14 | # all_default: true. 15 | 16 | # The specific linters to add. 17 | add: 18 | - MESSAGE_NAMES_UPPER_CAMEL_CASE 19 | - MAX_LINE_LENGTH 20 | - INDENT 21 | - FILE_NAMES_LOWER_SNAKE_CASE 22 | - IMPORTS_SORTED 23 | - PACKAGE_NAME_LOWER_CASE 24 | - ORDER 25 | - SERVICES_HAVE_COMMENT 26 | - RPCS_HAVE_COMMENT 27 | - PROTO3_FIELDS_AVOID_REQUIRED 28 | - PROTO3_GROUPS_AVOID 29 | - SYNTAX_CONSISTENT 30 | - RPC_NAMES_CASE 31 | - QUOTE_CONSISTENT 32 | 33 | # Linter rules option. 34 | rules_option: 35 | # MAX_LINE_LENGTH rule option. 36 | max_line_length: 37 | # Enforces a maximum line length. 38 | max_chars: 80 39 | # Specifies the character count for tab characters. 40 | tab_chars: 2 41 | 42 | # INDENT rule option. 43 | indent: 44 | # Available styles are 4(4-spaces), 2(2-spaces) or tab. 45 | style: 4 46 | # Specifies if it should stop considering and inserting new lines at the appropriate positions. 47 | # when the inner elements are on the same line. Default is false. 48 | not_insert_newline: true 49 | 50 | # QUOTE_CONSISTENT rule option. 51 | quote_consistent: 52 | # Available quote are "double" or "single". 53 | quote: double 54 | 55 | # ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH rule option. 56 | enum_field_names_zero_value_end_with: 57 | suffix: INVALID 58 | 59 | # SERVICE_NAMES_END_WITH rule option. 60 | service_names_end_with: 61 | text: Service 62 | 63 | # REPEATED_FIELD_NAMES_PLURALIZED rule option. 64 | ## The spec for each rules follows the implementation of https://github.com/gertd/go-pluralize. 65 | ## Plus, you can refer to this rule's test code. 66 | repeated_field_names_pluralized: 67 | uncountable_rules: 68 | - paper 69 | irregular_rules: 70 | Irregular: Regular 71 | -------------------------------------------------------------------------------- /internal/rpchelp/genrpcserverhelp.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build generate 6 | // +build generate 7 | 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "log" 13 | "os" 14 | "strings" 15 | 16 | "github.com/btcsuite/btcd/btcjson" 17 | "github.com/btcsuite/btcwallet/internal/rpchelp" 18 | ) 19 | 20 | var outputFile = func() *os.File { 21 | fi, err := os.Create("rpcserverhelp.go") 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | return fi 26 | }() 27 | 28 | func writefln(format string, args ...interface{}) { 29 | _, err := fmt.Fprintf(outputFile, format, args...) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | _, err = outputFile.Write([]byte{'\n'}) 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | } 38 | 39 | func writeLocaleHelp(locale, goLocale string, descs map[string]string) { 40 | funcName := "helpDescs" + goLocale 41 | writefln("func %s() map[string]string {", funcName) 42 | writefln("return map[string]string{") 43 | for i := range rpchelp.Methods { 44 | m := &rpchelp.Methods[i] 45 | helpText, err := btcjson.GenerateHelp(m.Method, descs, m.ResultTypes...) 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | writefln("%q: %q,", m.Method, helpText) 50 | } 51 | writefln("}") 52 | writefln("}") 53 | } 54 | 55 | func writeLocales() { 56 | writefln("var localeHelpDescs = map[string]func() map[string]string{") 57 | for _, h := range rpchelp.HelpDescs { 58 | writefln("%q: helpDescs%s,", h.Locale, h.GoLocale) 59 | } 60 | writefln("}") 61 | } 62 | 63 | func writeUsage() { 64 | usageStrs := make([]string, len(rpchelp.Methods)) 65 | var err error 66 | for i := range rpchelp.Methods { 67 | usageStrs[i], err = btcjson.MethodUsageText(rpchelp.Methods[i].Method) 68 | if err != nil { 69 | log.Fatal(err) 70 | } 71 | } 72 | usages := strings.Join(usageStrs, "\n") 73 | writefln("var requestUsages = %q", usages) 74 | } 75 | 76 | func main() { 77 | defer outputFile.Close() 78 | 79 | packageName := "main" 80 | if len(os.Args) > 1 { 81 | packageName = os.Args[1] 82 | } 83 | 84 | writefln("// AUTOGENERATED by internal/rpchelp/genrpcserverhelp.go; do not edit.") 85 | writefln("") 86 | writefln("package %s", packageName) 87 | writefln("") 88 | for _, h := range rpchelp.HelpDescs { 89 | writeLocaleHelp(h.Locale, h.GoLocale, h.Descs) 90 | writefln("") 91 | } 92 | writeLocales() 93 | writefln("") 94 | writeUsage() 95 | } 96 | -------------------------------------------------------------------------------- /netparams/params.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package netparams 6 | 7 | import ( 8 | "github.com/btcsuite/btcd/chaincfg" 9 | "github.com/btcsuite/btcd/wire" 10 | ) 11 | 12 | // Params is used to group parameters for various networks such as the main 13 | // network and test networks. 14 | type Params struct { 15 | *chaincfg.Params 16 | RPCClientPort string 17 | RPCServerPort string 18 | } 19 | 20 | // MainNetParams contains parameters specific running btcwallet and 21 | // btcd on the main network (wire.MainNet). 22 | var MainNetParams = Params{ 23 | Params: &chaincfg.MainNetParams, 24 | RPCClientPort: "8334", 25 | RPCServerPort: "8332", 26 | } 27 | 28 | // TestNet3Params contains parameters specific running btcwallet and 29 | // btcd on the test network (version 3) (wire.TestNet3). 30 | var TestNet3Params = Params{ 31 | Params: &chaincfg.TestNet3Params, 32 | RPCClientPort: "18334", 33 | RPCServerPort: "18332", 34 | } 35 | 36 | // TestNet4Params contains parameters specific running btcwallet and btcd on the 37 | // test network (version 4) (wire.TestNet4). 38 | var TestNet4Params = Params{ 39 | Params: &chaincfg.TestNet4Params, 40 | RPCClientPort: "48334", 41 | RPCServerPort: "48332", 42 | } 43 | 44 | // SimNetParams contains parameters specific to the simulation test network 45 | // (wire.SimNet). 46 | var SimNetParams = Params{ 47 | Params: &chaincfg.SimNetParams, 48 | RPCClientPort: "18556", 49 | RPCServerPort: "18554", 50 | } 51 | 52 | // SigNetParams contains parameters specific to the signet test network 53 | // (wire.SigNet). 54 | var SigNetParams = Params{ 55 | Params: &chaincfg.SigNetParams, 56 | RPCClientPort: "38334", 57 | RPCServerPort: "38332", 58 | } 59 | 60 | // SigNetWire is a helper function that either returns the given chain 61 | // parameter's net value if the parameter represents a signet network or 0 if 62 | // it's not. This is necessary because there can be custom signet networks that 63 | // have a different net value. 64 | func SigNetWire(params *chaincfg.Params) wire.BitcoinNet { 65 | if params.Name == chaincfg.SigNetParams.Name { 66 | return params.Net 67 | } 68 | 69 | return 0 70 | } 71 | 72 | // RegressionNetParams contains parameters specific to the regression test 73 | // network (wire.RegressionNet). 74 | var RegressionNetParams = Params{ 75 | Params: &chaincfg.RegressionNetParams, 76 | RPCClientPort: "18334", 77 | RPCServerPort: "18332", 78 | } 79 | -------------------------------------------------------------------------------- /chain/jitter_test.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | // TestCalculateMinMax tests the calculation of the min and max jitter values. 11 | func TestCalculateMinMax(t *testing.T) { 12 | tests := []struct { 13 | name string 14 | duration int64 15 | scaler float64 16 | expected struct { 17 | min int64 18 | max int64 19 | } 20 | }{ 21 | { 22 | name: "Scaler is 0", 23 | duration: 1000, 24 | scaler: 0, 25 | expected: struct{ min, max int64 }{1000, 1000}, 26 | }, 27 | { 28 | name: "Scaler is 0.5", 29 | duration: 1000, 30 | scaler: 0.5, 31 | expected: struct{ min, max int64 }{500, 1500}, 32 | }, 33 | { 34 | name: "Scaler is 1", 35 | duration: 1000, 36 | scaler: 1, 37 | expected: struct{ min, max int64 }{0, 2000}, 38 | }, 39 | { 40 | name: "Scaler is greater than 1", 41 | duration: 1000, 42 | scaler: 1.5, 43 | expected: struct{ min, max int64 }{0, 2500}, 44 | }, 45 | { 46 | name: "Negative scaler", 47 | duration: 1000, 48 | scaler: -0.5, 49 | expected: struct{ min, max int64 }{0, 0}, 50 | }, 51 | } 52 | 53 | for _, tc := range tests { 54 | t.Run(tc.name, func(t *testing.T) { 55 | // Catch the panic if the scaler is negative. 56 | if tc.scaler < 0 { 57 | defer func() { 58 | require.NotNil(t, recover(), 59 | "expect panic") 60 | }() 61 | } 62 | 63 | min, max := calculateMinMax( 64 | time.Duration(tc.duration), tc.scaler, 65 | ) 66 | require.Equal(t, tc.expected.min, min) 67 | require.Equal(t, tc.expected.max, max) 68 | }) 69 | } 70 | } 71 | 72 | func TestJitterTicker(t *testing.T) { 73 | // Create a new JitterTicker with a duration of 100ms and a scaler of 74 | // 0.2. 75 | ticker := NewJitterTicker(100*time.Millisecond, 0.2) 76 | 77 | // Wait for the ticker to tick 5 times and collect the tick times. 78 | var tickTimes []time.Time 79 | for i := 0; i < 5; i++ { 80 | tickTime := <-ticker.C 81 | tickTimes = append(tickTimes, tickTime) 82 | } 83 | 84 | // Stop the ticker. 85 | ticker.Stop() 86 | 87 | // Check that the tick times are within the expected range. 88 | for i := 1; i < len(tickTimes); i++ { 89 | diff := tickTimes[i].Sub(tickTimes[i-1]) 90 | 91 | // Tick duration should be between 80ms and 120ms. 92 | require.True(t, diff >= 80*time.Millisecond, "diff: %v", diff) 93 | 94 | // We give 1ms more to account for the time it takes to run the 95 | // code. 96 | require.True(t, diff < 121*time.Millisecond, "diff: %v", diff) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/btcsuite/btcwallet 2 | 3 | require ( 4 | github.com/btcsuite/btcd v0.24.3-0.20250318170759-4f4ea81776d6 5 | github.com/btcsuite/btcd/btcec/v2 v2.3.5 6 | github.com/btcsuite/btcd/btcutil v1.1.6 7 | github.com/btcsuite/btcd/btcutil/psbt v1.1.10 8 | github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 9 | github.com/btcsuite/btclog v1.0.0 10 | github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5 11 | github.com/btcsuite/btcwallet/wallet/txrules v1.2.2 12 | github.com/btcsuite/btcwallet/wallet/txsizes v1.2.5 13 | github.com/btcsuite/btcwallet/walletdb v1.5.1 14 | github.com/btcsuite/btcwallet/wtxmgr v1.5.6 15 | github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 16 | github.com/davecgh/go-spew v1.1.1 17 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 18 | github.com/golang/protobuf v1.5.4 19 | github.com/jessevdk/go-flags v1.6.1 20 | github.com/jrick/logrotate v1.1.2 21 | github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf 22 | github.com/lightninglabs/neutrino v0.16.2 23 | github.com/lightninglabs/neutrino/cache v1.1.3 24 | github.com/lightningnetwork/lnd/fn/v2 v2.0.8 25 | github.com/lightningnetwork/lnd/ticker v1.1.1 26 | github.com/lightningnetwork/lnd/tlv v1.3.2 27 | github.com/stretchr/testify v1.10.0 28 | golang.org/x/crypto v0.41.0 29 | golang.org/x/net v0.43.0 30 | golang.org/x/sync v0.16.0 31 | golang.org/x/term v0.34.0 32 | google.golang.org/grpc v1.73.0 33 | google.golang.org/protobuf v1.36.6 34 | ) 35 | 36 | require ( 37 | github.com/aead/siphash v1.0.1 // indirect 38 | github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect 39 | github.com/decred/dcrd/crypto/blake256 v1.1.0 // indirect 40 | github.com/decred/dcrd/lru v1.1.2 // indirect 41 | github.com/go-logr/logr v1.4.3 // indirect 42 | github.com/kkdai/bstream v1.0.0 // indirect 43 | github.com/kr/pretty v0.3.1 // indirect 44 | github.com/lightningnetwork/lnd/clock v1.0.1 // indirect 45 | github.com/lightningnetwork/lnd/queue v1.0.1 // indirect 46 | github.com/pmezard/go-difflib v1.0.0 // indirect 47 | github.com/rogpeppe/go-internal v1.14.1 // indirect 48 | github.com/stretchr/objx v0.5.2 // indirect 49 | go.etcd.io/bbolt v1.3.11 // indirect 50 | go.opentelemetry.io/otel v1.36.0 // indirect 51 | golang.org/x/exp v0.0.0-20250811191247-51f88131bc50 // indirect 52 | golang.org/x/sys v0.35.0 // indirect 53 | golang.org/x/text v0.28.0 // indirect 54 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect 55 | gopkg.in/yaml.v3 v3.0.1 // indirect 56 | ) 57 | 58 | // If you change this please run `make lint` to see where else it needs to be 59 | // updated as well. 60 | go 1.24.6 61 | -------------------------------------------------------------------------------- /walletdb/test_coverage.txt: -------------------------------------------------------------------------------- 1 | PASS 2 | coverage: 100.0% of statements 3 | ok github.com/conformal/btcwallet/walletdb 0.130s 4 | github.com\conformal\btcwallet\walletdb\interface.go:190: RegisterDriver 100.0% 5 | github.com\conformal\btcwallet\walletdb\interface.go:201: SupportedDrivers 100.0% 6 | github.com\conformal\btcwallet\walletdb\interface.go:214: Create 100.0% 7 | github.com\conformal\btcwallet\walletdb\interface.go:228: Open 100.0% 8 | total: (statements) 100.0% 9 | ============================================================ 10 | PASS 11 | coverage: 91.7% of statements 12 | ok github.com/conformal/btcwallet/walletdb/bdb 0.149s 13 | github.com\conformal\btcwallet\walletdb\bdb\db.go:28: convertErr 76.9% 14 | github.com\conformal\btcwallet\walletdb\bdb\db.go:74: Bucket 100.0% 15 | github.com\conformal\btcwallet\walletdb\bdb\db.go:90: CreateBucket 100.0% 16 | github.com\conformal\btcwallet\walletdb\bdb\db.go:103: CreateBucketIfNotExists 100.0% 17 | github.com\conformal\btcwallet\walletdb\bdb\db.go:116: DeleteBucket 100.0% 18 | github.com\conformal\btcwallet\walletdb\bdb\db.go:129: ForEach 100.0% 19 | github.com\conformal\btcwallet\walletdb\bdb\db.go:136: Writable 100.0% 20 | github.com\conformal\btcwallet\walletdb\bdb\db.go:145: Put 100.0% 21 | github.com\conformal\btcwallet\walletdb\bdb\db.go:157: Get 100.0% 22 | github.com\conformal\btcwallet\walletdb\bdb\db.go:166: Delete 100.0% 23 | github.com\conformal\btcwallet\walletdb\bdb\db.go:185: RootBucket 100.0% 24 | github.com\conformal\btcwallet\walletdb\bdb\db.go:193: Commit 100.0% 25 | github.com\conformal\btcwallet\walletdb\bdb\db.go:201: Rollback 100.0% 26 | github.com\conformal\btcwallet\walletdb\bdb\db.go:227: Begin 85.7% 27 | github.com\conformal\btcwallet\walletdb\bdb\db.go:249: View 100.0% 28 | github.com\conformal\btcwallet\walletdb\bdb\db.go:270: Update 100.0% 29 | github.com\conformal\btcwallet\walletdb\bdb\db.go:294: Namespace 93.3% 30 | github.com\conformal\btcwallet\walletdb\bdb\db.go:329: DeleteNamespace 100.0% 31 | github.com\conformal\btcwallet\walletdb\bdb\db.go:339: WriteTo 0.0% 32 | github.com\conformal\btcwallet\walletdb\bdb\db.go:348: Close 100.0% 33 | github.com\conformal\btcwallet\walletdb\bdb\db.go:353: fileExists 100.0% 34 | github.com\conformal\btcwallet\walletdb\bdb\db.go:364: openDB 100.0% 35 | github.com\conformal\btcwallet\walletdb\bdb\driver.go:34: parseArgs 100.0% 36 | github.com\conformal\btcwallet\walletdb\bdb\driver.go:50: openDBDriver 100.0% 37 | github.com\conformal\btcwallet\walletdb\bdb\driver.go:60: createDBDriver 100.0% 38 | github.com\conformal\btcwallet\walletdb\bdb\driver.go:69: init 66.7% 39 | total: (statements) 91.7% 40 | -------------------------------------------------------------------------------- /internal/legacy/rename/rename_windows.go: -------------------------------------------------------------------------------- 1 | // The following is adapted from goleveldb 2 | // (https://github.com/syndtr/goleveldb) under the following license: 3 | // 4 | // Copyright 2012 Suryandaru Triandana 5 | // All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions are 9 | // met: 10 | // 11 | // * Redistributions of source code must retain the above copyright 12 | // notice, this list of conditions and the following disclaimer. 13 | // * Redistributions in binary form must reproduce the above copyright 14 | // notice, this list of conditions and the following disclaimer in the 15 | // documentation and/or other materials provided with the distribution. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | package rename 30 | 31 | import ( 32 | "syscall" 33 | "unsafe" 34 | ) 35 | 36 | var ( 37 | modkernel32 = syscall.NewLazyDLL("kernel32.dll") 38 | procMoveFileExW = modkernel32.NewProc("MoveFileExW") 39 | ) 40 | 41 | const ( 42 | _MOVEFILE_REPLACE_EXISTING = 1 43 | ) 44 | 45 | func moveFileEx(from *uint16, to *uint16, flags uint32) error { 46 | r1, _, e1 := syscall.Syscall(procMoveFileExW.Addr(), 3, 47 | uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), 48 | uintptr(flags)) 49 | if r1 == 0 { 50 | if e1 != 0 { 51 | return error(e1) 52 | } else { 53 | return syscall.EINVAL 54 | } 55 | } 56 | return nil 57 | } 58 | 59 | // Atomic provides an atomic file rename. newpath is replaced if it 60 | // already exists. 61 | func Atomic(oldpath, newpath string) error { 62 | from, err := syscall.UTF16PtrFromString(oldpath) 63 | if err != nil { 64 | return err 65 | } 66 | to, err := syscall.UTF16PtrFromString(newpath) 67 | if err != nil { 68 | return err 69 | } 70 | return moveFileEx(from, to, _MOVEFILE_REPLACE_EXISTING) 71 | } 72 | -------------------------------------------------------------------------------- /rpc/rpcserver/log.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 The btcsuite developers 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | package rpcserver 16 | 17 | import ( 18 | "os" 19 | "strings" 20 | 21 | "github.com/btcsuite/btclog" 22 | "google.golang.org/grpc/grpclog" 23 | ) 24 | 25 | // UseLogger sets the logger to use for the gRPC server. 26 | func UseLogger(l btclog.Logger) { 27 | grpclog.SetLogger(logger{l}) // nolint:staticcheck 28 | } 29 | 30 | // logger uses a btclog.Logger to implement the grpclog.Logger interface. 31 | type logger struct { 32 | btclog.Logger 33 | } 34 | 35 | // stripGrpcPrefix removes the package prefix for all logs made to the grpc 36 | // logger, since these are already included as the btclog subsystem name. 37 | func stripGrpcPrefix(logstr string) string { 38 | return strings.TrimPrefix(logstr, "grpc: ") 39 | } 40 | 41 | // stripGrpcPrefixArgs removes the package prefix from the first argument, if it 42 | // exists and is a string, returning the same arg slice after reassigning the 43 | // first arg. 44 | func stripGrpcPrefixArgs(args ...interface{}) []interface{} { 45 | if len(args) == 0 { 46 | return args 47 | } 48 | firstArgStr, ok := args[0].(string) 49 | if ok { 50 | args[0] = stripGrpcPrefix(firstArgStr) 51 | } 52 | return args 53 | } 54 | 55 | func (l logger) Fatal(args ...interface{}) { 56 | l.Critical(stripGrpcPrefixArgs(args)...) 57 | os.Exit(1) 58 | } 59 | 60 | func (l logger) Fatalf(format string, args ...interface{}) { 61 | l.Criticalf(stripGrpcPrefix(format), args...) 62 | os.Exit(1) 63 | } 64 | 65 | func (l logger) Fatalln(args ...interface{}) { 66 | l.Critical(stripGrpcPrefixArgs(args)...) 67 | os.Exit(1) 68 | } 69 | 70 | func (l logger) Print(args ...interface{}) { 71 | l.Info(stripGrpcPrefixArgs(args)...) 72 | } 73 | 74 | func (l logger) Printf(format string, args ...interface{}) { 75 | l.Infof(stripGrpcPrefix(format), args...) 76 | } 77 | 78 | func (l logger) Println(args ...interface{}) { 79 | l.Info(stripGrpcPrefixArgs(args)...) 80 | } 81 | -------------------------------------------------------------------------------- /waddrmgr/README.md: -------------------------------------------------------------------------------- 1 | waddrmgr 2 | ======== 3 | 4 | [![Build Status](https://travis-ci.org/btcsuite/btcwallet.png?branch=master)] 5 | (https://travis-ci.org/btcsuite/btcwallet) 6 | 7 | Package waddrmgr provides a secure hierarchical deterministic wallet address 8 | manager. 9 | 10 | A suite of tests is provided to ensure proper functionality. See 11 | `test_coverage.txt` for the gocov coverage report. Alternatively, if you are 12 | running a POSIX OS, you can run the `cov_report.sh` script for a real-time 13 | report. Package waddrmgr is licensed under the liberal ISC license. 14 | 15 | ## Feature Overview 16 | 17 | - BIP0032 hierarchical deterministic keys 18 | - BIP0043/BIP0044 multi-account hierarchy 19 | - Strong focus on security: 20 | - Fully encrypted database including public information such as addresses as 21 | well as private information such as private keys and scripts needed to 22 | redeem pay-to-script-hash transactions 23 | - Hardened against memory scraping through the use of actively clearing 24 | private material from memory when locked 25 | - Different crypto keys used for public, private, and script data 26 | - Ability for different passphrases for public and private data 27 | - Scrypt-based key derivation 28 | - NaCl-based secretbox cryptography (XSalsa20 and Poly1305) 29 | - Scalable design: 30 | - Multi-tier key design to allow instant password changes regardless of the 31 | number of addresses stored 32 | - Import WIF keys 33 | - Import pay-to-script-hash scripts for things such as multi-signature 34 | transactions 35 | - Ability to start in watching-only mode which does not contain any private 36 | key material 37 | - Ability to convert to watching-only mode 38 | - Programmatically detectable errors, including encapsulation of errors from 39 | packages it relies on 40 | - Address synchronization capabilities 41 | - Comprehensive test coverage 42 | 43 | ## Documentation 44 | 45 | [![GoDoc](https://godoc.org/github.com/btcsuite/btcwallet/waddrmgr?status.png)] 46 | (http://godoc.org/github.com/btcsuite/btcwallet/waddrmgr) 47 | 48 | Full `go doc` style documentation for the project can be viewed online without 49 | installing this package by using the GoDoc site here: 50 | http://godoc.org/github.com/btcsuite/btcwallet/waddrmgr 51 | 52 | You can also view the documentation locally once the package is installed with 53 | the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to 54 | http://localhost:6060/pkg/github.com/btcsuite/btcwallet/waddrmgr 55 | 56 | ## Installation 57 | 58 | ```bash 59 | $ go get github.com/btcsuite/btcwallet/waddrmgr 60 | ``` 61 | 62 | Package waddrmgr is licensed under the [copyfree](http://copyfree.org) ISC 63 | License. 64 | -------------------------------------------------------------------------------- /signal.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "os" 9 | "os/signal" 10 | ) 11 | 12 | // interruptChannel is used to receive SIGINT (Ctrl+C) signals. 13 | var interruptChannel chan os.Signal 14 | 15 | // addHandlerChannel is used to add an interrupt handler to the list of handlers 16 | // to be invoked on SIGINT (Ctrl+C) signals. 17 | var addHandlerChannel = make(chan func()) 18 | 19 | // interruptHandlersDone is closed after all interrupt handlers run the first 20 | // time an interrupt is signaled. 21 | var interruptHandlersDone = make(chan struct{}) 22 | 23 | var simulateInterruptChannel = make(chan struct{}, 1) 24 | 25 | // signals defines the signals that are handled to do a clean shutdown. 26 | // Conditional compilation is used to also include SIGTERM on Unix. 27 | var signals = []os.Signal{os.Interrupt} 28 | 29 | // simulateInterrupt requests invoking the clean termination process by an 30 | // internal component instead of a SIGINT. 31 | func simulateInterrupt() { 32 | select { 33 | case simulateInterruptChannel <- struct{}{}: 34 | default: 35 | } 36 | } 37 | 38 | // mainInterruptHandler listens for SIGINT (Ctrl+C) signals on the 39 | // interruptChannel and invokes the registered interruptCallbacks accordingly. 40 | // It also listens for callback registration. It must be run as a goroutine. 41 | func mainInterruptHandler() { 42 | // interruptCallbacks is a list of callbacks to invoke when a 43 | // SIGINT (Ctrl+C) is received. 44 | var interruptCallbacks []func() 45 | invokeCallbacks := func() { 46 | // run handlers in LIFO order. 47 | for i := range interruptCallbacks { 48 | idx := len(interruptCallbacks) - 1 - i 49 | interruptCallbacks[idx]() 50 | } 51 | close(interruptHandlersDone) 52 | } 53 | 54 | for { 55 | select { 56 | case sig := <-interruptChannel: 57 | log.Infof("Received signal (%s). Shutting down...", sig) 58 | invokeCallbacks() 59 | return 60 | case <-simulateInterruptChannel: 61 | log.Info("Received shutdown request. Shutting down...") 62 | invokeCallbacks() 63 | return 64 | 65 | case handler := <-addHandlerChannel: 66 | interruptCallbacks = append(interruptCallbacks, handler) 67 | } 68 | } 69 | } 70 | 71 | // addInterruptHandler adds a handler to call when a SIGINT (Ctrl+C) is 72 | // received. 73 | func addInterruptHandler(handler func()) { 74 | // Create the channel and start the main interrupt handler which invokes 75 | // all other callbacks and exits if not already done. 76 | if interruptChannel == nil { 77 | interruptChannel = make(chan os.Signal, 1) 78 | signal.Notify(interruptChannel, signals...) 79 | go mainInterruptHandler() 80 | } 81 | 82 | addHandlerChannel <- handler 83 | } 84 | -------------------------------------------------------------------------------- /make/testing_flags.mk: -------------------------------------------------------------------------------- 1 | DEV_TAGS = dev 2 | LOG_TAGS = 3 | 4 | GOCC ?= go 5 | GOLIST := $(GOCC) list -tags="$(DEV_TAGS)" -deps $(PKG)/... | grep '$(PKG)' 6 | GOTEST := GO111MODULE=on $(GOCC) test 7 | 8 | TEST_FLAGS = 9 | COVER_PKG = $$($(GOCC) list -deps -tags="$(DEV_TAGS)" ./... | grep '$(PKG)') 10 | COVER_FLAGS = -coverprofile=coverage.txt -covermode=atomic -coverpkg=$(PKG)/... 11 | 12 | # If specific package is being unit tested, construct the full name of the 13 | # subpackage. 14 | ifneq ($(pkg),) 15 | UNITPKG := $(PKG)/$(pkg) 16 | UNIT_TARGETED = yes 17 | COVER_PKG = $(PKG)/$(pkg) 18 | endif 19 | 20 | # If a specific unit test case is being target, construct test.run filter. 21 | ifneq ($(case),) 22 | TEST_FLAGS += -test.run=$(case) 23 | UNIT_TARGETED = yes 24 | endif 25 | 26 | # If a timeout was requested, construct initialize the proper flag for the go 27 | # test command. If not, we set 20m (btcwallet default). 28 | ifneq ($(timeout),) 29 | TEST_FLAGS += -test.timeout=$(timeout) 30 | else 31 | TEST_FLAGS += -test.timeout=20m 32 | endif 33 | 34 | ifneq ($(verbose),) 35 | TEST_FLAGS += -test.v 36 | endif 37 | 38 | ifneq ($(nocache),) 39 | TEST_FLAGS += -test.count=1 40 | endif 41 | 42 | # Define the log tags that will be applied only when running unit tests. If none 43 | # are provided, we default to "debug stdlog" which will be standard debug log 44 | # output. 45 | ifneq ($(log),) 46 | LOG_TAGS := $(log) 47 | else 48 | LOG_TAGS := debug stdlog 49 | endif 50 | 51 | # UNIT_TARGETED is undefined iff a specific package and/or unit test case is 52 | # not being targeted. 53 | UNIT_TARGETED ?= no 54 | 55 | # If a specific package/test case was requested, run the unit test for the 56 | # targeted case. Otherwise, default to running all tests. 57 | ifeq ($(UNIT_TARGETED), yes) 58 | UNIT := $(GOTEST) -tags="$(DEV_TAGS) $(LOG_TAGS)" $(TEST_FLAGS) $(UNITPKG) 59 | UNIT_DEBUG := $(GOTEST) -v -tags="$(DEV_TAGS) $(LOG_TAGS)" $(TEST_FLAGS) $(UNITPKG) 60 | UNIT_RACE := $(GOTEST) -tags="$(DEV_TAGS) $(LOG_TAGS)" $(TEST_FLAGS) -race $(UNITPKG) 61 | 62 | # NONE is a special value which selects no other tests but only executes the 63 | # benchmark tests here. 64 | UNIT_BENCH := $(GOTEST) -tags="$(DEV_TAGS) $(LOG_TAGS)" -test.bench=. -test.run=NONE $(UNITPKG) 65 | endif 66 | 67 | ifeq ($(UNIT_TARGETED), no) 68 | UNIT := $(GOLIST) | $(XARGS) env $(GOTEST) -tags="$(DEV_TAGS) $(LOG_TAGS)" $(TEST_FLAGS) 69 | UNIT_DEBUG := $(GOLIST) | $(XARGS) env $(GOTEST) -v -tags="$(DEV_TAGS) $(LOG_TAGS)" $(TEST_FLAGS) 70 | 71 | # NONE is a special value which selects no other tests but only executes the 72 | # benchmark tests here. 73 | UNIT_BENCH := $(GOLIST) | $(XARGS) env $(GOTEST) -tags="$(DEV_TAGS) $(LOG_TAGS)" -test.bench=. -test.run=NONE 74 | UNIT_RACE := $(UNIT) -race 75 | endif 76 | 77 | UNIT_COVER := $(GOTEST) $(COVER_FLAGS) -tags="$(DEV_TAGS) $(LOG_TAGS)" $(TEST_FLAGS) $(COVER_PKG) 78 | -------------------------------------------------------------------------------- /walletdb/bdb/driver.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package bdb 6 | 7 | import ( 8 | "fmt" 9 | "time" 10 | 11 | "github.com/btcsuite/btcwallet/walletdb" 12 | ) 13 | 14 | const ( 15 | dbType = "bdb" 16 | ) 17 | 18 | // parseArgs parses the arguments from the walletdb Open/Create methods. 19 | func parseArgs(funcName string, 20 | args ...interface{}) (string, bool, time.Duration, bool, error) { 21 | 22 | if len(args) != 4 { 23 | return "", false, 0, false, fmt.Errorf("invalid arguments to %s.%s "+ 24 | "-- expected database path, no-freelist-sync, "+ 25 | "timeout option and read-only flag", 26 | dbType, funcName) 27 | } 28 | 29 | dbPath, ok := args[0].(string) 30 | if !ok { 31 | return "", false, 0, false, fmt.Errorf("first argument to %s.%s is "+ 32 | "invalid -- expected database path string", dbType, 33 | funcName) 34 | } 35 | 36 | noFreelistSync, ok := args[1].(bool) 37 | if !ok { 38 | return "", false, 0, false, fmt.Errorf("second argument to %s.%s is "+ 39 | "invalid -- expected no-freelist-sync bool", dbType, 40 | funcName) 41 | } 42 | 43 | timeout, ok := args[2].(time.Duration) 44 | if !ok { 45 | return "", false, 0, false, fmt.Errorf("third argument to %s.%s is "+ 46 | "invalid -- expected timeout time.Duration", dbType, 47 | funcName) 48 | } 49 | 50 | readOnly, ok := args[3].(bool) 51 | if !ok { 52 | return "", false, 0, false, fmt.Errorf("fourth argument to %s.%s is "+ 53 | "invalid -- expected read-only bool", dbType, 54 | funcName) 55 | } 56 | return dbPath, noFreelistSync, timeout, readOnly, nil 57 | } 58 | 59 | // openDBDriver is the callback provided during driver registration that opens 60 | // an existing database for use. 61 | func openDBDriver(args ...interface{}) (walletdb.DB, error) { 62 | dbPath, noFreelistSync, timeout, readOnly, err := parseArgs("Open", args...) 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | return openDB(dbPath, noFreelistSync, false, timeout, readOnly) 68 | } 69 | 70 | // createDBDriver is the callback provided during driver registration that 71 | // creates, initializes, and opens a database for use. 72 | func createDBDriver(args ...interface{}) (walletdb.DB, error) { 73 | dbPath, noFreelistSync, timeout, readOnly, err := parseArgs("Create", args...) 74 | if err != nil { 75 | return nil, err 76 | } 77 | 78 | return openDB(dbPath, noFreelistSync, true, timeout, readOnly) 79 | } 80 | 81 | func init() { 82 | // Register the driver. 83 | driver := walletdb.Driver{ 84 | DbType: dbType, 85 | Create: createDBDriver, 86 | Open: openDBDriver, 87 | } 88 | if err := walletdb.RegisterDriver(driver); err != nil { 89 | panic(fmt.Sprintf("Failed to regiser database driver '%s': %v", 90 | dbType, err)) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /rpc/legacyrpc/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package legacyrpc 6 | 7 | import ( 8 | "errors" 9 | 10 | "github.com/btcsuite/btcd/btcjson" 11 | ) 12 | 13 | // TODO(jrick): There are several error paths which 'replace' various errors 14 | // with a more appropriate error from the btcjson package. Create a map of 15 | // these replacements so they can be handled once after an RPC handler has 16 | // returned and before the error is marshaled. 17 | 18 | // Error types to simplify the reporting of specific categories of 19 | // errors, and their *btcjson.RPCError creation. 20 | type ( 21 | // DeserializationError describes a failed deserializaion due to bad 22 | // user input. It corresponds to btcjson.ErrRPCDeserialization. 23 | DeserializationError struct { 24 | error 25 | } 26 | 27 | // InvalidParameterError describes an invalid parameter passed by 28 | // the user. It corresponds to btcjson.ErrRPCInvalidParameter. 29 | InvalidParameterError struct { 30 | error 31 | } 32 | 33 | // ParseError describes a failed parse due to bad user input. It 34 | // corresponds to btcjson.ErrRPCParse. 35 | ParseError struct { 36 | error 37 | } 38 | ) 39 | 40 | // Errors variables that are defined once here to avoid duplication below. 41 | var ( 42 | ErrNeedPositiveAmount = InvalidParameterError{ 43 | errors.New("amount must be positive"), 44 | } 45 | 46 | ErrNeedPositiveMinconf = InvalidParameterError{ 47 | errors.New("minconf must be positive"), 48 | } 49 | 50 | ErrAddressNotInWallet = btcjson.RPCError{ 51 | Code: btcjson.ErrRPCWallet, 52 | Message: "address not found in wallet", 53 | } 54 | 55 | ErrAddressTypeUnknown = btcjson.RPCError{ 56 | Code: btcjson.ErrRPCWalletInvalidAddressType, 57 | Message: "unknown address type", 58 | } 59 | 60 | ErrAccountNameNotFound = btcjson.RPCError{ 61 | Code: btcjson.ErrRPCWalletInvalidAccountName, 62 | Message: "account name not found", 63 | } 64 | 65 | ErrUnloadedWallet = btcjson.RPCError{ 66 | Code: btcjson.ErrRPCWallet, 67 | Message: "Request requires a wallet but wallet has not loaded yet", 68 | } 69 | 70 | ErrWalletUnlockNeeded = btcjson.RPCError{ 71 | Code: btcjson.ErrRPCWalletUnlockNeeded, 72 | Message: "Enter the wallet passphrase with walletpassphrase first", 73 | } 74 | 75 | ErrNotImportedAccount = btcjson.RPCError{ 76 | Code: btcjson.ErrRPCWallet, 77 | Message: "imported addresses must belong to the imported account", 78 | } 79 | 80 | ErrNoTransactionInfo = btcjson.RPCError{ 81 | Code: btcjson.ErrRPCNoTxInfo, 82 | Message: "No information for transaction", 83 | } 84 | 85 | ErrReservedAccountName = btcjson.RPCError{ 86 | Code: btcjson.ErrRPCInvalidParameter, 87 | Message: "Account name is reserved by RPC server", 88 | } 89 | ) 90 | -------------------------------------------------------------------------------- /snacl/snacl_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package snacl 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | ) 11 | 12 | var ( 13 | password = []byte("sikrit") 14 | message = []byte("this is a secret message of sorts") 15 | key *SecretKey 16 | params []byte 17 | blob []byte 18 | ) 19 | 20 | func TestNewSecretKey(t *testing.T) { 21 | var err error 22 | key, err = NewSecretKey(&password, DefaultN, DefaultR, DefaultP) 23 | if err != nil { 24 | t.Error(err) 25 | return 26 | } 27 | } 28 | 29 | func TestMarshalSecretKey(t *testing.T) { 30 | params = key.Marshal() 31 | } 32 | 33 | func TestUnmarshalSecretKey(t *testing.T) { 34 | var sk SecretKey 35 | if err := sk.Unmarshal(params); err != nil { 36 | t.Errorf("unexpected unmarshal error: %v", err) 37 | return 38 | } 39 | 40 | if err := sk.DeriveKey(&password); err != nil { 41 | t.Errorf("unexpected DeriveKey error: %v", err) 42 | return 43 | } 44 | 45 | if !bytes.Equal(sk.Key[:], key.Key[:]) { 46 | t.Errorf("keys not equal") 47 | } 48 | } 49 | 50 | func TestUnmarshalSecretKeyInvalid(t *testing.T) { 51 | var sk SecretKey 52 | if err := sk.Unmarshal(params); err != nil { 53 | t.Errorf("unexpected unmarshal error: %v", err) 54 | return 55 | } 56 | 57 | p := []byte("wrong password") 58 | if err := sk.DeriveKey(&p); err != ErrInvalidPassword { 59 | t.Errorf("wrong password didn't fail") 60 | return 61 | } 62 | } 63 | 64 | func TestEncrypt(t *testing.T) { 65 | var err error 66 | 67 | blob, err = key.Encrypt(message) 68 | if err != nil { 69 | t.Error(err) 70 | return 71 | } 72 | } 73 | 74 | func TestDecrypt(t *testing.T) { 75 | decryptedMessage, err := key.Decrypt(blob) 76 | if err != nil { 77 | t.Error(err) 78 | return 79 | } 80 | 81 | if !bytes.Equal(decryptedMessage, message) { 82 | t.Errorf("decryption failed") 83 | return 84 | } 85 | } 86 | 87 | func TestDecryptCorrupt(t *testing.T) { 88 | blob[len(blob)-15] = blob[len(blob)-15] + 1 89 | _, err := key.Decrypt(blob) 90 | if err == nil { 91 | t.Errorf("corrupt message decrypted") 92 | return 93 | } 94 | } 95 | 96 | func TestZero(t *testing.T) { 97 | var zeroKey [32]byte 98 | 99 | key.Zero() 100 | if !bytes.Equal(key.Key[:], zeroKey[:]) { 101 | t.Errorf("zero key failed") 102 | } 103 | } 104 | 105 | func TestDeriveKey(t *testing.T) { 106 | if err := key.DeriveKey(&password); err != nil { 107 | t.Errorf("unexpected DeriveKey key failure: %v", err) 108 | } 109 | } 110 | 111 | func TestDeriveKeyInvalid(t *testing.T) { 112 | bogusPass := []byte("bogus") 113 | if err := key.DeriveKey(&bogusPass); err != ErrInvalidPassword { 114 | t.Errorf("unexpected DeriveKey key failure: %v", err) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /chain/queue.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "container/list" 5 | ) 6 | 7 | // ConcurrentQueue is a concurrent-safe FIFO queue with unbounded capacity. 8 | // Clients interact with the queue by pushing items into the in channel and 9 | // popping items from the out channel. There is a goroutine that manages moving 10 | // items from the in channel to the out channel in the correct order that must 11 | // be started by calling Start(). 12 | type ConcurrentQueue struct { 13 | chanIn chan interface{} 14 | chanOut chan interface{} 15 | quit chan struct{} 16 | overflow *list.List 17 | } 18 | 19 | // NewConcurrentQueue constructs a ConcurrentQueue. The bufferSize parameter is 20 | // the capacity of the output channel. When the size of the queue is below this 21 | // threshold, pushes do not incur the overhead of the less efficient overflow 22 | // structure. 23 | func NewConcurrentQueue(bufferSize int) *ConcurrentQueue { 24 | return &ConcurrentQueue{ 25 | chanIn: make(chan interface{}), 26 | chanOut: make(chan interface{}, bufferSize), 27 | quit: make(chan struct{}), 28 | overflow: list.New(), 29 | } 30 | } 31 | 32 | // ChanIn returns a channel that can be used to push new items into the queue. 33 | func (cq *ConcurrentQueue) ChanIn() chan<- interface{} { 34 | return cq.chanIn 35 | } 36 | 37 | // ChanOut returns a channel that can be used to pop items from the queue. 38 | func (cq *ConcurrentQueue) ChanOut() <-chan interface{} { 39 | return cq.chanOut 40 | } 41 | 42 | // Start begins a goroutine that manages moving items from the in channel to 43 | // the out channel. The queue tries to move items directly to the out channel 44 | // minimize overhead, but if the out channel is full it pushes items to an 45 | // overflow queue. This must be called before using the queue. 46 | func (cq *ConcurrentQueue) Start() { 47 | go func() { 48 | for { 49 | nextElement := cq.overflow.Front() 50 | if nextElement == nil { 51 | // The overflow queue is empty, so incoming 52 | // items can be pushed directly to the output 53 | // channel. However, if output channel is full, 54 | // we'll push to the overflow list instead. 55 | select { 56 | case item := <-cq.chanIn: 57 | select { 58 | case cq.chanOut <- item: 59 | case <-cq.quit: 60 | return 61 | default: 62 | cq.overflow.PushBack(item) 63 | } 64 | case <-cq.quit: 65 | return 66 | } 67 | } else { 68 | // The overflow queue is not empty, so any new 69 | // items get pushed to the back to preserve 70 | // order. 71 | select { 72 | case item := <-cq.chanIn: 73 | cq.overflow.PushBack(item) 74 | case cq.chanOut <- nextElement.Value: 75 | cq.overflow.Remove(nextElement) 76 | case <-cq.quit: 77 | return 78 | } 79 | } 80 | } 81 | }() 82 | } 83 | 84 | // Stop ends the goroutine that moves items from the in channel to the out 85 | // channel. 86 | func (cq *ConcurrentQueue) Stop() { 87 | close(cq.quit) 88 | } 89 | -------------------------------------------------------------------------------- /cmd/dropwtxmgr/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "bufio" 9 | "fmt" 10 | "os" 11 | "path/filepath" 12 | "time" 13 | 14 | "github.com/btcsuite/btcd/btcutil" 15 | "github.com/btcsuite/btcwallet/wallet" 16 | "github.com/btcsuite/btcwallet/walletdb" 17 | _ "github.com/btcsuite/btcwallet/walletdb/bdb" 18 | "github.com/jessevdk/go-flags" 19 | ) 20 | 21 | const defaultNet = "mainnet" 22 | 23 | var ( 24 | datadir = btcutil.AppDataDir("btcwallet", false) 25 | ) 26 | 27 | // Flags. 28 | var opts = struct { 29 | Force bool `short:"f" description:"Force removal without prompt"` 30 | DbPath string `long:"db" description:"Path to wallet database"` 31 | DropLabels bool `long:"droplabels" description:"Drop transaction labels"` 32 | Timeout time.Duration `long:"timeout" description:"Timeout value when opening the wallet database"` 33 | }{ 34 | Force: false, 35 | DbPath: filepath.Join(datadir, defaultNet, wallet.WalletDBName), 36 | Timeout: wallet.DefaultDBTimeout, 37 | } 38 | 39 | func init() { 40 | _, err := flags.Parse(&opts) 41 | if err != nil { 42 | os.Exit(1) 43 | } 44 | } 45 | 46 | func yes(s string) bool { 47 | switch s { 48 | case "y", "Y", "yes", "Yes": 49 | return true 50 | default: 51 | return false 52 | } 53 | } 54 | 55 | func no(s string) bool { 56 | switch s { 57 | case "n", "N", "no", "No": 58 | return true 59 | default: 60 | return false 61 | } 62 | } 63 | 64 | func main() { 65 | os.Exit(mainInt()) 66 | } 67 | 68 | func mainInt() int { 69 | fmt.Println("Database path:", opts.DbPath) 70 | _, err := os.Stat(opts.DbPath) 71 | if os.IsNotExist(err) { 72 | fmt.Println("Database file does not exist") 73 | return 1 74 | } 75 | 76 | for !opts.Force { 77 | fmt.Print("Drop all btcwallet transaction history? [y/N] ") 78 | 79 | scanner := bufio.NewScanner(bufio.NewReader(os.Stdin)) 80 | if !scanner.Scan() { 81 | // Exit on EOF. 82 | return 0 83 | } 84 | err := scanner.Err() 85 | if err != nil { 86 | fmt.Println() 87 | fmt.Println(err) 88 | return 1 89 | } 90 | resp := scanner.Text() 91 | if yes(resp) { 92 | break 93 | } 94 | if no(resp) || resp == "" { 95 | return 0 96 | } 97 | 98 | fmt.Println("Enter yes or no.") 99 | } 100 | 101 | db, err := walletdb.Open("bdb", opts.DbPath, true, opts.Timeout, false) 102 | if err != nil { 103 | fmt.Println("Failed to open database:", err) 104 | return 1 105 | } 106 | defer db.Close() 107 | 108 | fmt.Println("Dropping btcwallet transaction history") 109 | 110 | err = wallet.DropTransactionHistory(db, !opts.DropLabels) 111 | if err != nil { 112 | fmt.Println("Failed to drop and re-create namespace:", err) 113 | return 1 114 | } 115 | 116 | return 0 117 | } 118 | -------------------------------------------------------------------------------- /wallet/mock.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/btcsuite/btcd/btcjson" 8 | "github.com/btcsuite/btcd/btcutil" 9 | "github.com/btcsuite/btcd/chaincfg/chainhash" 10 | "github.com/btcsuite/btcd/wire" 11 | "github.com/btcsuite/btcwallet/chain" 12 | "github.com/btcsuite/btcwallet/waddrmgr" 13 | ) 14 | 15 | type mockChainClient struct { 16 | getBestBlockHeight int32 17 | getBlockHashFunc func() (*chainhash.Hash, error) 18 | getBlockHeader *wire.BlockHeader 19 | } 20 | 21 | var _ chain.Interface = (*mockChainClient)(nil) 22 | 23 | func (m *mockChainClient) Start(_ context.Context) error { 24 | return nil 25 | } 26 | 27 | func (m *mockChainClient) Stop() { 28 | } 29 | 30 | func (m *mockChainClient) WaitForShutdown() {} 31 | 32 | func (m *mockChainClient) GetBestBlock() (*chainhash.Hash, int32, error) { 33 | return nil, m.getBestBlockHeight, nil 34 | } 35 | 36 | func (m *mockChainClient) GetBlock(*chainhash.Hash) (*wire.MsgBlock, error) { 37 | return nil, nil 38 | } 39 | 40 | func (m *mockChainClient) GetBlockHash(int64) (*chainhash.Hash, error) { 41 | if m.getBlockHashFunc != nil { 42 | return m.getBlockHashFunc() 43 | } 44 | return nil, nil 45 | } 46 | 47 | func (m *mockChainClient) GetBlockHeader(*chainhash.Hash) (*wire.BlockHeader, 48 | error) { 49 | return m.getBlockHeader, nil 50 | } 51 | 52 | func (m *mockChainClient) IsCurrent() bool { 53 | return false 54 | } 55 | 56 | func (m *mockChainClient) FilterBlocks(*chain.FilterBlocksRequest) ( 57 | *chain.FilterBlocksResponse, error) { 58 | return nil, nil 59 | } 60 | 61 | func (m *mockChainClient) BlockStamp() (*waddrmgr.BlockStamp, error) { 62 | return &waddrmgr.BlockStamp{ 63 | Height: 500000, 64 | Hash: chainhash.Hash{}, 65 | Timestamp: time.Unix(1234, 0), 66 | }, nil 67 | } 68 | 69 | func (m *mockChainClient) SendRawTransaction(*wire.MsgTx, bool) ( 70 | *chainhash.Hash, error) { 71 | return nil, nil 72 | } 73 | 74 | func (m *mockChainClient) Rescan(*chainhash.Hash, []btcutil.Address, 75 | map[wire.OutPoint]btcutil.Address) error { 76 | return nil 77 | } 78 | 79 | func (m *mockChainClient) NotifyReceived([]btcutil.Address) error { 80 | return nil 81 | } 82 | 83 | func (m *mockChainClient) NotifyBlocks() error { 84 | return nil 85 | } 86 | 87 | func (m *mockChainClient) Notifications() <-chan interface{} { 88 | return nil 89 | } 90 | 91 | func (m *mockChainClient) BackEnd() string { 92 | return "mock" 93 | } 94 | 95 | // TestMempoolAcceptCmd returns result of mempool acceptance tests indicating 96 | // if raw transaction(s) would be accepted by mempool. 97 | // 98 | // NOTE: This is part of the chain.Interface interface. 99 | func (m *mockChainClient) TestMempoolAccept(txns []*wire.MsgTx, 100 | maxFeeRate float64) ([]*btcjson.TestMempoolAcceptResult, error) { 101 | 102 | return nil, nil 103 | } 104 | 105 | func (m *mockChainClient) MapRPCErr(err error) error { 106 | return nil 107 | } 108 | -------------------------------------------------------------------------------- /waddrmgr/tapscript_test.go: -------------------------------------------------------------------------------- 1 | package waddrmgr 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/btcsuite/btcd/btcec/v2" 7 | "github.com/btcsuite/btcd/btcec/v2/schnorr" 8 | "github.com/btcsuite/btcd/txscript" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | var ( 13 | testInternalKey, _ = btcec.ParsePubKey(hexToBytes( 14 | "020ef94ee79c07cbd1988fffd6e6aea1e25c3b033a2fd64fe14a9b955e53" + 15 | "55f0c6", 16 | )) 17 | 18 | testScript1 = hexToBytes( 19 | "76a914f6c97547d73156abb300ae059905c4acaadd09dd88", 20 | ) 21 | testScript2 = hexToBytes( 22 | "200ef94ee79c07cbd1988fffd6e6aea1e25c3b033a2fd64fe14a9b955e53" + 23 | "55f0c6ac", 24 | ) 25 | testScript1Proof = hexToBytes( 26 | "6c2e4bb01e316abaaee288d69c06cc608cedefd6e1a06813786c4ec51b6e" + 27 | "1d38", 28 | ) 29 | 30 | testTaprootKey = hexToBytes( 31 | "e15405aab8fd601206a3848b0ec495df75d8a602465d8dbba42a7493bd88" + 32 | "9b78", 33 | ) 34 | testTaprootKey2 = hexToBytes( 35 | "b1ef5fafd9a55b8c4bb3c2eee3fcf033194891ebf89b1d9b666c6306acc3" + 36 | "a3df", 37 | ) 38 | ) 39 | 40 | // TestTaprootKey tests that the taproot tweaked key can be calculated correctly 41 | // for both a tree with all leaves known as well as a partially revealed tree 42 | // with an inclusion/merkle proof. 43 | func TestTaprootKey(t *testing.T) { 44 | t.Parallel() 45 | 46 | testCases := []struct { 47 | name string 48 | given *Tapscript 49 | expected []byte 50 | }{{ 51 | name: "full tree", 52 | given: &Tapscript{ 53 | Type: TapscriptTypeFullTree, 54 | Leaves: []txscript.TapLeaf{ 55 | txscript.NewBaseTapLeaf(testScript1), 56 | txscript.NewBaseTapLeaf(testScript2), 57 | }, 58 | ControlBlock: &txscript.ControlBlock{ 59 | InternalKey: testInternalKey, 60 | LeafVersion: txscript.BaseLeafVersion, 61 | }, 62 | }, 63 | expected: testTaprootKey, 64 | }, { 65 | name: "partial tree with proof", 66 | given: &Tapscript{ 67 | Type: TapscriptTypePartialReveal, 68 | RevealedScript: testScript2, 69 | ControlBlock: &txscript.ControlBlock{ 70 | InternalKey: testInternalKey, 71 | LeafVersion: txscript.BaseLeafVersion, 72 | InclusionProof: testScript1Proof, 73 | }, 74 | }, 75 | expected: testTaprootKey, 76 | }, { 77 | name: "root hash only", 78 | given: &Tapscript{ 79 | Type: TaprootKeySpendRootHash, 80 | ControlBlock: &txscript.ControlBlock{ 81 | InternalKey: testInternalKey, 82 | }, 83 | RootHash: []byte("I could be a root hash"), 84 | }, 85 | expected: testTaprootKey2, 86 | }, { 87 | name: "full key only", 88 | given: &Tapscript{ 89 | Type: TaprootFullKeyOnly, 90 | FullOutputKey: testInternalKey, 91 | }, 92 | expected: schnorr.SerializePubKey(testInternalKey), 93 | }} 94 | 95 | for _, tc := range testCases { 96 | tc := tc 97 | 98 | t.Run(tc.name, func(tt *testing.T) { 99 | taprootKey, err := tc.given.TaprootKey() 100 | require.NoError(tt, err) 101 | 102 | require.Equal( 103 | tt, tc.expected, schnorr.SerializePubKey( 104 | taprootKey, 105 | ), 106 | ) 107 | }) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /chain/jitter.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "errors" 5 | "math" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | // JitterTicker is a ticker that adds jitter to the tick duration. 11 | type JitterTicker struct { 12 | // C is a read-only channel that receives ticks. 13 | C <-chan time.Time 14 | 15 | // c is the internal channel that receives ticks. 16 | c chan time.Time 17 | 18 | // duration is the base duration of the ticker. 19 | duration time.Duration 20 | 21 | // scaler defines the jitter scaler. The jitter is calculated as, 22 | // - min: duration * (1 - scaler) or 0 if scaler > 1, 23 | // - max: duration * (1 + scaler). 24 | // 25 | // NOTE: when scaler is 0, this ticker behaves as a normal ticker. 26 | scaler float64 27 | 28 | // min and max store the duration values. 29 | min int64 30 | max int64 31 | 32 | // quit is closed when the ticker is stopped. 33 | quit chan struct{} 34 | } 35 | 36 | // NewJitterTicker returns a new JitterTicker. 37 | func NewJitterTicker(d time.Duration, jitter float64) *JitterTicker { 38 | // Calculate the min and max duration values. 39 | min, max := calculateMinMax(d, jitter) 40 | 41 | // Create a new ticker. 42 | t := &JitterTicker{ 43 | c: make(chan time.Time, 1), 44 | scaler: jitter, 45 | duration: d, 46 | min: min, 47 | max: max, 48 | quit: make(chan struct{}), 49 | } 50 | 51 | // Mount the tick channel to a read-only channel. 52 | t.C = (<-chan time.Time)(t.c) 53 | 54 | // Start the ticker. 55 | go t.start() 56 | 57 | return t 58 | } 59 | 60 | // calculateMinMax calculates the min and max duration values. If the 61 | // calculated min is negative, it will be set to 0. 62 | func calculateMinMax(d time.Duration, scaler float64) (int64, int64) { 63 | // If the scaler is negative, we will panic. 64 | if scaler < 0 { 65 | panic(errors.New("scaler must be positive")) 66 | } 67 | 68 | // Calculate the min and max jitter values. 69 | min := math.Floor(float64(d) * (1 - scaler)) 70 | max := math.Ceil(float64(d) * (1 + scaler)) 71 | 72 | // If the scaler is greater than 1, we would use a zero min instead of 73 | // a negative one. 74 | if 1-scaler < 0 { 75 | min = 0 76 | } 77 | 78 | return int64(min), int64(max) 79 | } 80 | 81 | // Stop stops the ticker. 82 | func (jt *JitterTicker) Stop() { 83 | close(jt.quit) 84 | } 85 | 86 | // start starts the ticker. 87 | func (jt *JitterTicker) start() { 88 | // Create a new timer with a random duration. 89 | timer := time.NewTimer(jt.rand()) 90 | 91 | for { 92 | select { 93 | case t := <-timer.C: 94 | // Reset the timer when it fires. 95 | timer.Reset(jt.rand()) 96 | 97 | // Send the tick to the channel. 98 | // 99 | // NOTE: must be non-blocking. 100 | select { 101 | case jt.c <- t: 102 | default: 103 | } 104 | 105 | case <-jt.quit: 106 | // Stop the timer and clean the channel when it stops. 107 | if !timer.Stop() { 108 | <-timer.C 109 | } 110 | } 111 | } 112 | } 113 | 114 | // rand returns a random duration between the min and max values. 115 | func (jt *JitterTicker) rand() time.Duration { 116 | if jt.max == jt.min { 117 | return jt.duration 118 | } 119 | 120 | d := rand.Int63n(jt.max-jt.min) + jt.min //nolint:gosec 121 | return time.Duration(d) 122 | } 123 | -------------------------------------------------------------------------------- /walletdb/README.md: -------------------------------------------------------------------------------- 1 | walletdb 2 | ======== 3 | 4 | [![Build Status](https://travis-ci.org/btcsuite/btcwallet.png?branch=master)] 5 | (https://travis-ci.org/btcsuite/btcwallet) 6 | 7 | Package walletdb provides a namespaced database interface for btcwallet. 8 | 9 | A wallet essentially consists of a multitude of stored data such as private 10 | and public keys, key derivation bits, pay-to-script-hash scripts, and various 11 | metadata. One of the issues with many wallets is they are tightly integrated. 12 | Designing a wallet with loosely coupled components that provide specific 13 | functionality is ideal, however it presents a challenge in regards to data 14 | storage since each component needs to store its own data without knowing the 15 | internals of other components or breaking atomicity. 16 | 17 | This package solves this issue by providing a namespaced database interface that 18 | is intended to be used by the main wallet daemon. This allows the potential for 19 | any backend database type with a suitable driver. Each component, which will 20 | typically be a package, can then implement various functionality such as address 21 | management, voting pools, and colored coin metadata in their own namespace 22 | without having to worry about conflicts with other packages even though they are 23 | sharing the same database that is managed by the wallet. 24 | 25 | A suite of tests is provided to ensure proper functionality. See 26 | `test_coverage.txt` for the gocov coverage report. Alternatively, if you are 27 | running a POSIX OS, you can run the `cov_report.sh` script for a real-time 28 | report. Package walletdb is licensed under the copyfree ISC license. 29 | 30 | This interfaces provided by this package were heavily inspired by the excellent 31 | boltdb project at https://github.com/boltdb/bolt by Ben B. Johnson. 32 | 33 | ## Feature Overview 34 | 35 | - Key/value store 36 | - Namespace support 37 | - Allows multiple packages to have their own area in the database without 38 | worrying about conflicts 39 | - Read-only and read-write transactions with both manual and managed modes 40 | - Nested buckets 41 | - Supports registration of backend databases 42 | - Comprehensive test coverage 43 | 44 | ## Documentation 45 | 46 | [![GoDoc](https://godoc.org/github.com/btcsuite/btcwallet/walletdb?status.png)] 47 | (http://godoc.org/github.com/btcsuite/btcwallet/walletdb) 48 | 49 | Full `go doc` style documentation for the project can be viewed online without 50 | installing this package by using the GoDoc site here: 51 | http://godoc.org/github.com/btcsuite/btcwallet/walletdb 52 | 53 | You can also view the documentation locally once the package is installed with 54 | the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to 55 | http://localhost:6060/pkg/github.com/btcsuite/btcwallet/walletdb 56 | 57 | ## Installation 58 | 59 | ```bash 60 | $ go get github.com/btcsuite/btcwallet/walletdb 61 | ``` 62 | 63 | ## Examples 64 | 65 | * [Basic Usage Example] 66 | (http://godoc.org/github.com/btcsuite/btcwallet/walletdb#example-package--BasicUsage) 67 | Demonstrates creating a new database, getting a namespace from it, and using a 68 | managed read-write transaction against the namespace to store and retrieve 69 | data. 70 | 71 | 72 | ## License 73 | 74 | Package walletdb is licensed under the [copyfree](http://copyfree.org) ISC 75 | License. 76 | -------------------------------------------------------------------------------- /wallet/signer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package wallet 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/btcsuite/btcd/btcutil" 11 | "github.com/btcsuite/btcd/txscript" 12 | "github.com/btcsuite/btcd/wire" 13 | "github.com/btcsuite/btcwallet/waddrmgr" 14 | ) 15 | 16 | // TestComputeInputScript checks that the wallet can create the full 17 | // witness script for a witness output. 18 | func TestComputeInputScript(t *testing.T) { 19 | t.Parallel() 20 | 21 | testCases := []struct { 22 | name string 23 | scope waddrmgr.KeyScope 24 | expectedScriptLen int 25 | }{{ 26 | name: "BIP084 P2WKH", 27 | scope: waddrmgr.KeyScopeBIP0084, 28 | expectedScriptLen: 0, 29 | }, { 30 | name: "BIP049 nested P2WKH", 31 | scope: waddrmgr.KeyScopeBIP0049Plus, 32 | expectedScriptLen: 23, 33 | }} 34 | 35 | w, cleanup := testWallet(t) 36 | defer cleanup() 37 | 38 | for _, tc := range testCases { 39 | tc := tc 40 | t.Run(tc.name, func(t *testing.T) { 41 | runTestCase(t, w, tc.scope, tc.expectedScriptLen) 42 | }) 43 | } 44 | } 45 | 46 | func runTestCase(t *testing.T, w *Wallet, scope waddrmgr.KeyScope, 47 | scriptLen int) { 48 | 49 | // Create an address we can use to send some coins to. 50 | addr, err := w.CurrentAddress(0, scope) 51 | if err != nil { 52 | t.Fatalf("unable to get current address: %v", addr) 53 | } 54 | p2shAddr, err := txscript.PayToAddrScript(addr) 55 | if err != nil { 56 | t.Fatalf("unable to convert wallet address to p2sh: %v", err) 57 | } 58 | 59 | // Add an output paying to the wallet's address to the database. 60 | utxOut := wire.NewTxOut(100000, p2shAddr) 61 | incomingTx := &wire.MsgTx{ 62 | TxIn: []*wire.TxIn{{}}, 63 | TxOut: []*wire.TxOut{utxOut}, 64 | } 65 | addUtxo(t, w, incomingTx) 66 | 67 | // Create a transaction that spends the UTXO created above and spends to 68 | // the same address again. 69 | prevOut := wire.OutPoint{ 70 | Hash: incomingTx.TxHash(), 71 | Index: 0, 72 | } 73 | outgoingTx := &wire.MsgTx{ 74 | TxIn: []*wire.TxIn{{ 75 | PreviousOutPoint: prevOut, 76 | }}, 77 | TxOut: []*wire.TxOut{utxOut}, 78 | } 79 | fetcher := txscript.NewCannedPrevOutputFetcher( 80 | utxOut.PkScript, utxOut.Value, 81 | ) 82 | sigHashes := txscript.NewTxSigHashes(outgoingTx, fetcher) 83 | 84 | // Compute the input script to spend the UTXO now. 85 | witness, script, err := w.ComputeInputScript( 86 | outgoingTx, utxOut, 0, sigHashes, txscript.SigHashAll, nil, 87 | ) 88 | if err != nil { 89 | t.Fatalf("error computing input script: %v", err) 90 | } 91 | if len(script) != scriptLen { 92 | t.Fatalf("unexpected script length, got %d wanted %d", 93 | len(script), scriptLen) 94 | } 95 | if len(witness) != 2 { 96 | t.Fatalf("unexpected witness stack length, got %d, wanted %d", 97 | len(witness), 2) 98 | } 99 | 100 | // Finally verify that the created witness is valid. 101 | outgoingTx.TxIn[0].Witness = witness 102 | outgoingTx.TxIn[0].SignatureScript = script 103 | err = validateMsgTx( 104 | outgoingTx, [][]byte{utxOut.PkScript}, []btcutil.Amount{100000}, 105 | ) 106 | if err != nil { 107 | t.Fatalf("error validating tx: %v", err) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /internal/rpchelp/methods.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !generate 6 | // +build !generate 7 | 8 | package rpchelp 9 | 10 | import "github.com/btcsuite/btcd/btcjson" 11 | 12 | // Common return types. 13 | var ( 14 | returnsBool = []interface{}{(*bool)(nil)} 15 | returnsNumber = []interface{}{(*float64)(nil)} 16 | returnsString = []interface{}{(*string)(nil)} 17 | returnsStringArray = []interface{}{(*[]string)(nil)} 18 | returnsLTRArray = []interface{}{(*[]btcjson.ListTransactionsResult)(nil)} 19 | ) 20 | 21 | // Methods contains all methods and result types that help is generated for, 22 | // for every locale. 23 | var Methods = []struct { 24 | Method string 25 | ResultTypes []interface{} 26 | }{ 27 | {"addmultisigaddress", returnsString}, 28 | {"createmultisig", []interface{}{(*btcjson.CreateMultiSigResult)(nil)}}, 29 | {"dumpprivkey", returnsString}, 30 | {"getaccount", returnsString}, 31 | {"getaccountaddress", returnsString}, 32 | {"getaddressesbyaccount", returnsStringArray}, 33 | {"getbalance", append(returnsNumber, returnsNumber[0])}, 34 | {"getbestblockhash", returnsString}, 35 | {"getblockcount", returnsNumber}, 36 | {"getinfo", []interface{}{(*btcjson.InfoWalletResult)(nil)}}, 37 | {"getnewaddress", returnsString}, 38 | {"getrawchangeaddress", returnsString}, 39 | {"getreceivedbyaccount", returnsNumber}, 40 | {"getreceivedbyaddress", returnsNumber}, 41 | {"gettransaction", []interface{}{(*btcjson.GetTransactionResult)(nil)}}, 42 | {"help", append(returnsString, returnsString[0])}, 43 | {"importprivkey", nil}, 44 | {"keypoolrefill", nil}, 45 | {"listaccounts", []interface{}{(*map[string]float64)(nil)}}, 46 | {"listlockunspent", []interface{}{(*[]btcjson.TransactionInput)(nil)}}, 47 | {"listreceivedbyaccount", []interface{}{(*[]btcjson.ListReceivedByAccountResult)(nil)}}, 48 | {"listreceivedbyaddress", []interface{}{(*[]btcjson.ListReceivedByAddressResult)(nil)}}, 49 | {"listsinceblock", []interface{}{(*btcjson.ListSinceBlockResult)(nil)}}, 50 | {"listtransactions", returnsLTRArray}, 51 | {"listunspent", []interface{}{(*btcjson.ListUnspentResult)(nil)}}, 52 | {"lockunspent", returnsBool}, 53 | {"sendfrom", returnsString}, 54 | {"sendmany", returnsString}, 55 | {"sendtoaddress", returnsString}, 56 | {"settxfee", returnsBool}, 57 | {"signmessage", returnsString}, 58 | {"signrawtransaction", []interface{}{(*btcjson.SignRawTransactionResult)(nil)}}, 59 | {"validateaddress", []interface{}{(*btcjson.ValidateAddressWalletResult)(nil)}}, 60 | {"verifymessage", returnsBool}, 61 | {"walletlock", nil}, 62 | {"walletpassphrase", nil}, 63 | {"walletpassphrasechange", nil}, 64 | {"createnewaccount", nil}, 65 | {"exportwatchingwallet", returnsString}, 66 | {"getbestblock", []interface{}{(*btcjson.GetBestBlockResult)(nil)}}, 67 | {"getunconfirmedbalance", returnsNumber}, 68 | {"listaddresstransactions", returnsLTRArray}, 69 | {"listalltransactions", returnsLTRArray}, 70 | {"renameaccount", nil}, 71 | {"walletislocked", returnsBool}, 72 | } 73 | 74 | // HelpDescs contains the locale-specific help strings along with the locale. 75 | var HelpDescs = []struct { 76 | Locale string // Actual locale, e.g. en_US 77 | GoLocale string // Locale used in Go names, e.g. EnUS 78 | Descs map[string]string 79 | }{ 80 | {"en_US", "EnUS", helpDescsEnUS}, // helpdescs_en_US.go 81 | } 82 | -------------------------------------------------------------------------------- /wallet/common.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 The Decred developers 2 | // Copyright (c) 2017 The btcsuite developers 3 | // Use of this source code is governed by an ISC 4 | // license that can be found in the LICENSE file. 5 | 6 | package wallet 7 | 8 | import ( 9 | "time" 10 | 11 | "github.com/btcsuite/btcd/btcutil" 12 | "github.com/btcsuite/btcd/chaincfg/chainhash" 13 | "github.com/btcsuite/btcd/wire" 14 | ) 15 | 16 | // Note: The following common types should never reference the Wallet type. 17 | // Long term goal is to move these to their own package so that the database 18 | // access APIs can create them directly for the wallet to return. 19 | 20 | // BlockIdentity identifies a block, or the lack of one (used to describe an 21 | // unmined transaction). 22 | type BlockIdentity struct { 23 | Hash chainhash.Hash 24 | Height int32 25 | } 26 | 27 | // None returns whether there is no block described by the instance. When 28 | // associated with a transaction, this indicates the transaction is unmined. 29 | func (b *BlockIdentity) None() bool { 30 | // BUG: Because dcrwallet uses both 0 and -1 in various places to refer 31 | // to an unmined transaction this must check against both and may not 32 | // ever be usable to represent the genesis block. 33 | return *b == BlockIdentity{Height: -1} || *b == BlockIdentity{} 34 | } 35 | 36 | // OutputKind describes a kind of transaction output. This is used to 37 | // differentiate between coinbase, stakebase, and normal outputs. 38 | type OutputKind byte 39 | 40 | // Defined OutputKind constants 41 | const ( 42 | OutputKindNormal OutputKind = iota 43 | OutputKindCoinbase 44 | ) 45 | 46 | // TransactionOutput describes an output that was or is at least partially 47 | // controlled by the wallet. Depending on context, this could refer to an 48 | // unspent output, or a spent one. 49 | type TransactionOutput struct { 50 | OutPoint wire.OutPoint 51 | Output wire.TxOut 52 | OutputKind OutputKind 53 | // These should be added later when the DB can return them more 54 | // efficiently: 55 | //TxLockTime uint32 56 | //TxExpiry uint32 57 | ContainingBlock BlockIdentity 58 | ReceiveTime time.Time 59 | } 60 | 61 | // OutputRedeemer identifies the transaction input which redeems an output. 62 | type OutputRedeemer struct { 63 | TxHash chainhash.Hash 64 | InputIndex uint32 65 | } 66 | 67 | // P2SHMultiSigOutput describes a transaction output with a pay-to-script-hash 68 | // output script and an imported redemption script. Along with common details 69 | // of the output, this structure also includes the P2SH address the script was 70 | // created from and the number of signatures required to redeem it. 71 | // 72 | // TODO: Could be useful to return how many of the required signatures can be 73 | // created by this wallet. 74 | type P2SHMultiSigOutput struct { 75 | // TODO: Add a TransactionOutput member to this struct and remove these 76 | // fields which are duplicated by it. This improves consistency. Only 77 | // not done now because wtxmgr APIs don't support an efficient way of 78 | // fetching other Transactionoutput data together with the rest of the 79 | // multisig info. 80 | OutPoint wire.OutPoint 81 | OutputAmount btcutil.Amount 82 | ContainingBlock BlockIdentity 83 | 84 | P2SHAddress *btcutil.AddressScriptHash 85 | RedeemScript []byte 86 | M, N uint8 // M of N signatures required to redeem 87 | Redeemer *OutputRedeemer // nil unless spent 88 | } 89 | -------------------------------------------------------------------------------- /chain/bitcoind_events.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/btcsuite/btcd/chaincfg/chainhash" 8 | "github.com/btcsuite/btcd/rpcclient" 9 | "github.com/btcsuite/btcd/wire" 10 | ) 11 | 12 | // BitcoindEvents is the interface that must be satisfied by any type that 13 | // serves bitcoind block and transactions events. 14 | type BitcoindEvents interface { 15 | // TxNotifications will return a channel which will deliver new 16 | // transactions. 17 | TxNotifications() <-chan *wire.MsgTx 18 | 19 | // BlockNotifications will return a channel which will deliver new 20 | // blocks. 21 | BlockNotifications() <-chan *wire.MsgBlock 22 | 23 | // LookupInputSpend will return the transaction found in mempool that 24 | // spends the given input. 25 | LookupInputSpend(op wire.OutPoint) (chainhash.Hash, bool) 26 | 27 | // Start will kick off any goroutines required for operation. 28 | Start() error 29 | 30 | // Stop will clean up any resources and goroutines. 31 | Stop() error 32 | } 33 | 34 | // Ensure rpcclient.Client implements the rpcClient interface at compile time. 35 | var _ batchClient = (*rpcclient.Client)(nil) 36 | 37 | // NewBitcoindEventSubscriber initialises a new BitcoinEvents object impl 38 | // depending on the config passed. 39 | func NewBitcoindEventSubscriber(cfg *BitcoindConfig, client *rpcclient.Client, 40 | bClient batchClient) (BitcoindEvents, error) { 41 | 42 | if cfg.PollingConfig != nil && cfg.ZMQConfig != nil { 43 | return nil, fmt.Errorf("either PollingConfig or ZMQConfig " + 44 | "should be specified, not both") 45 | } 46 | 47 | // Check if the bitcoind node is on a version that has the 48 | // gettxspendingprevout RPC. If it does, then we don't need to maintain 49 | // a mempool for ZMQ clients and can maintain a smaller mempool for RPC 50 | // clients. 51 | hasRPC, err := hasSpendingPrevoutRPC(client) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | if cfg.PollingConfig != nil { 57 | if client == nil { 58 | return nil, fmt.Errorf("rpc client must be given " + 59 | "if rpc polling is to be used for event " + 60 | "subscriptions") 61 | } 62 | 63 | pollingEvents := newBitcoindRPCPollingEvents( 64 | cfg.PollingConfig, client, bClient, hasRPC, 65 | ) 66 | 67 | return pollingEvents, nil 68 | } 69 | 70 | if cfg.ZMQConfig == nil { 71 | return nil, fmt.Errorf("ZMQConfig must be specified if " + 72 | "rpcpolling is disabled") 73 | } 74 | 75 | return newBitcoindZMQEvents(cfg.ZMQConfig, client, bClient, hasRPC) 76 | } 77 | 78 | // hasSpendingPrevoutRPC returns whether or not the bitcoind has the newer 79 | // gettxspendingprevout RPC. 80 | func hasSpendingPrevoutRPC(client *rpcclient.Client) (bool, error) { 81 | // Fetch the bitcoind version. 82 | resp, err := client.RawRequest("getnetworkinfo", nil) 83 | if err != nil { 84 | return false, err 85 | } 86 | 87 | info := struct { 88 | Version int64 `json:"version"` 89 | }{} 90 | 91 | if err := json.Unmarshal(resp, &info); err != nil { 92 | return false, err 93 | } 94 | 95 | // Bitcoind returns a single value representing the semantic version: 96 | // 10000 * CLIENT_VERSION_MAJOR + 100 * CLIENT_VERSION_MINOR 97 | // + 1 * CLIENT_VERSION_BUILD 98 | // 99 | // The gettxspendingprevout call was added in version 24.0.0, so we 100 | // return for versions >= 240000. 101 | return info.Version >= 240000, nil 102 | } 103 | -------------------------------------------------------------------------------- /walletdb/error.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package walletdb 6 | 7 | import ( 8 | "errors" 9 | ) 10 | 11 | // Errors that can occur during driver registration. 12 | var ( 13 | // ErrDbTypeRegistered is returned when two different database drivers 14 | // attempt to register with the name database type. 15 | ErrDbTypeRegistered = errors.New("database type already registered") 16 | ) 17 | 18 | // Errors that the various database functions may return. 19 | var ( 20 | // ErrDbUnknownType is returned when there is no driver registered for 21 | // the specified database type. 22 | ErrDbUnknownType = errors.New("unknown database type") 23 | 24 | // ErrDbDoesNotExist is returned when open is called for a database that 25 | // does not exist. 26 | ErrDbDoesNotExist = errors.New("database does not exist") 27 | 28 | // ErrDbExists is returned when create is called for a database that 29 | // already exists. 30 | ErrDbExists = errors.New("database already exists") 31 | 32 | // ErrDbNotOpen is returned when a database instance is accessed before 33 | // it is opened or after it is closed. 34 | ErrDbNotOpen = errors.New("database not open") 35 | 36 | // ErrDbAlreadyOpen is returned when open is called on a database that 37 | // is already open. 38 | ErrDbAlreadyOpen = errors.New("database already open") 39 | 40 | // ErrInvalid is returned if the specified database is not valid. 41 | ErrInvalid = errors.New("invalid database") 42 | 43 | // ErrDryRunRollBack is returned if a database transaction should be 44 | // rolled back because its changes were a dry-run only. 45 | ErrDryRunRollBack = errors.New("dry run only; should roll back") 46 | ) 47 | 48 | // Errors that can occur when beginning or committing a transaction. 49 | var ( 50 | // ErrTxClosed is returned when attempting to commit or rollback a 51 | // transaction that has already had one of those operations performed. 52 | ErrTxClosed = errors.New("tx closed") 53 | 54 | // ErrTxNotWritable is returned when an operation that requires write 55 | // access to the database is attempted against a read-only transaction. 56 | ErrTxNotWritable = errors.New("tx not writable") 57 | ) 58 | 59 | // Errors that can occur when putting or deleting a value or bucket. 60 | var ( 61 | // ErrBucketNotFound is returned when trying to access a bucket that has 62 | // not been created yet. 63 | ErrBucketNotFound = errors.New("bucket not found") 64 | 65 | // ErrBucketExists is returned when creating a bucket that already exists. 66 | ErrBucketExists = errors.New("bucket already exists") 67 | 68 | // ErrBucketNameRequired is returned when creating a bucket with a blank name. 69 | ErrBucketNameRequired = errors.New("bucket name required") 70 | 71 | // ErrKeyRequired is returned when inserting a zero-length key. 72 | ErrKeyRequired = errors.New("key required") 73 | 74 | // ErrKeyTooLarge is returned when inserting a key that is larger than MaxKeySize. 75 | ErrKeyTooLarge = errors.New("key too large") 76 | 77 | // ErrValueTooLarge is returned when inserting a value that is larger than MaxValueSize. 78 | ErrValueTooLarge = errors.New("value too large") 79 | 80 | // ErrIncompatibleValue is returned when trying create or delete a 81 | // bucket on an existing non-bucket key or when trying to create or 82 | // delete a non-bucket key on an existing bucket key. 83 | ErrIncompatibleValue = errors.New("incompatible value") 84 | ) 85 | -------------------------------------------------------------------------------- /wtxmgr/kahnsort.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package wtxmgr 6 | 7 | import ( 8 | "github.com/btcsuite/btcd/chaincfg/chainhash" 9 | "github.com/btcsuite/btcd/wire" 10 | ) 11 | 12 | type graphNode struct { 13 | value *wire.MsgTx 14 | outEdges []*chainhash.Hash 15 | inDegree int 16 | } 17 | 18 | type hashGraph map[chainhash.Hash]graphNode 19 | 20 | func makeGraph(set map[chainhash.Hash]*wire.MsgTx) hashGraph { 21 | graph := make(hashGraph) 22 | 23 | for _, tx := range set { 24 | // Add a node for every transaction. The output edges and input 25 | // degree are set by iterating over each transaction's inputs 26 | // below. 27 | txHash := tx.TxHash() 28 | if _, ok := graph[txHash]; !ok { 29 | graph[txHash] = graphNode{value: tx} 30 | } 31 | 32 | inputLoop: 33 | for _, input := range tx.TxIn { 34 | // Transaction inputs that reference transactions not 35 | // included in the set do not create any (local) graph 36 | // edges. 37 | if _, ok := set[input.PreviousOutPoint.Hash]; !ok { 38 | continue 39 | } 40 | 41 | inputNode := graph[input.PreviousOutPoint.Hash] 42 | 43 | // Skip duplicate edges. 44 | for _, outEdge := range inputNode.outEdges { 45 | if *outEdge == input.PreviousOutPoint.Hash { 46 | continue inputLoop 47 | } 48 | } 49 | 50 | // Mark a directed edge from the previous transaction 51 | // hash to this transaction and increase the input 52 | // degree for this transaction's node. 53 | inputTx := inputNode.value 54 | if inputTx == nil { 55 | inputTx = set[input.PreviousOutPoint.Hash] 56 | } 57 | graph[input.PreviousOutPoint.Hash] = graphNode{ 58 | value: inputTx, 59 | outEdges: append(inputNode.outEdges, &txHash), 60 | inDegree: inputNode.inDegree, 61 | } 62 | node := graph[txHash] 63 | graph[txHash] = graphNode{ 64 | value: tx, 65 | outEdges: node.outEdges, 66 | inDegree: node.inDegree + 1, 67 | } 68 | } 69 | } 70 | 71 | return graph 72 | } 73 | 74 | // graphRoots returns the roots of the graph. That is, it returns the node's 75 | // values for all nodes which contain an input degree of 0. 76 | func graphRoots(graph hashGraph) []*wire.MsgTx { 77 | roots := make([]*wire.MsgTx, 0, len(graph)) 78 | for _, node := range graph { 79 | if node.inDegree == 0 { 80 | roots = append(roots, node.value) 81 | } 82 | } 83 | return roots 84 | } 85 | 86 | // DependencySort topologically sorts a set of transactions by their dependency 87 | // order. It is implemented using Kahn's algorithm. 88 | func DependencySort(txs map[chainhash.Hash]*wire.MsgTx) []*wire.MsgTx { 89 | graph := makeGraph(txs) 90 | s := graphRoots(graph) 91 | 92 | // If there are no edges (no transactions from the map reference each 93 | // other), then Kahn's algorithm is unnecessary. 94 | if len(s) == len(txs) { 95 | return s 96 | } 97 | 98 | sorted := make([]*wire.MsgTx, 0, len(txs)) 99 | for len(s) != 0 { 100 | tx := s[0] 101 | s = s[1:] 102 | sorted = append(sorted, tx) 103 | 104 | n := graph[tx.TxHash()] 105 | for _, mHash := range n.outEdges { 106 | m := graph[*mHash] 107 | if m.inDegree != 0 { 108 | m.inDegree-- 109 | graph[*mHash] = m 110 | if m.inDegree == 0 { 111 | s = append(s, m.value) 112 | } 113 | } 114 | } 115 | } 116 | return sorted 117 | } 118 | -------------------------------------------------------------------------------- /internal/zero/zero_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package zero_test 6 | 7 | import ( 8 | "fmt" 9 | "math/big" 10 | "strings" 11 | "testing" 12 | 13 | . "github.com/btcsuite/btcwallet/internal/zero" 14 | ) 15 | 16 | func makeOneBytes(n int) []byte { 17 | b := make([]byte, n) 18 | for i := range b { 19 | b[i] = 1 20 | } 21 | return b 22 | } 23 | 24 | func checkZeroBytes(b []byte) error { 25 | for i, v := range b { 26 | if v != 0 { 27 | return fmt.Errorf("b[%d] = %d", i, v) 28 | } 29 | } 30 | return nil 31 | } 32 | 33 | func TestBytes(t *testing.T) { 34 | tests := []int{ 35 | 0, 36 | 31, 37 | 32, 38 | 33, 39 | 127, 40 | 128, 41 | 129, 42 | 255, 43 | 256, 44 | 256, 45 | 257, 46 | 383, 47 | 384, 48 | 385, 49 | 511, 50 | 512, 51 | 513, 52 | } 53 | 54 | for i, n := range tests { 55 | b := makeOneBytes(n) 56 | Bytes(b) 57 | err := checkZeroBytes(b) 58 | if err != nil { 59 | t.Errorf("Test %d (n=%d) failed: %v", i, n, err) 60 | continue 61 | } 62 | } 63 | } 64 | 65 | func checkZeroWords(b []big.Word) error { 66 | for i, v := range b { 67 | if v != 0 { 68 | return fmt.Errorf("b[%d] = %d", i, v) 69 | } 70 | } 71 | return nil 72 | } 73 | 74 | var bigZero = new(big.Int) 75 | 76 | func TestBigInt(t *testing.T) { 77 | tests := []string{ 78 | // 16 0xFFFFFFFF 32-bit uintptrs 79 | strings.Repeat("FFFFFFFF", 16), 80 | 81 | // 17 32-bit uintptrs, minimum value which enters loop on 32-bit 82 | "01" + strings.Repeat("00000000", 16), 83 | 84 | // 32 0xFFFFFFFF 32-bit uintptrs, maximum value which enters loop exactly once on 32-bit 85 | strings.Repeat("FFFFFFFF", 32), 86 | 87 | // 33 32-bit uintptrs, minimum value which enters loop twice on 32-bit 88 | "01" + strings.Repeat("00000000", 32), 89 | 90 | // 16 0xFFFFFFFFFFFFFFFF 64-bit uintptrs 91 | strings.Repeat("FFFFFFFFFFFFFFFF", 16), 92 | 93 | // 17 64-bit uintptrs, minimum value which enters loop on 64-bit 94 | "01" + strings.Repeat("0000000000000000", 16), 95 | 96 | // 32 0xFFFFFFFFFFFFFFFF 64-bit uintptrs, maximum value which enters loop exactly once on 64-bit 97 | strings.Repeat("FFFFFFFFFFFFFFFF", 32), 98 | 99 | // 33 64-bit uintptrs, minimum value which enters loop twice on 64-bit 100 | "01" + strings.Repeat("0000000000000000", 32), 101 | } 102 | 103 | for i, s := range tests { 104 | v, ok := new(big.Int).SetString(s, 16) 105 | if !ok { 106 | t.Errorf("Test %d includes invalid hex number %s", i, s) 107 | continue 108 | } 109 | 110 | BigInt(v) 111 | err := checkZeroWords(v.Bits()) 112 | if err != nil { 113 | t.Errorf("Test %d (s=%s) failed: %v", i, s, err) 114 | continue 115 | } 116 | if v.Cmp(bigZero) != 0 { 117 | t.Errorf("Test %d (s=%s) zeroed big.Int represents non-zero number %v", i, s, v) 118 | continue 119 | } 120 | } 121 | } 122 | 123 | func TestBytea32(t *testing.T) { 124 | const sz = 32 125 | var b [sz]byte 126 | copy(b[:], makeOneBytes(sz)) 127 | 128 | Bytea32(&b) 129 | 130 | err := checkZeroBytes(b[:]) 131 | if err != nil { 132 | t.Error(err) 133 | } 134 | } 135 | 136 | func TestBytea64(t *testing.T) { 137 | const sz = 64 138 | var b [sz]byte 139 | copy(b[:], makeOneBytes(sz)) 140 | 141 | Bytea64(&b) 142 | 143 | err := checkZeroBytes(b[:]) 144 | if err != nil { 145 | t.Error(err) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /rpc/legacyrpc/rpchelp_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 The btcsuite developers 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | package legacyrpc 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | "github.com/btcsuite/btcd/btcjson" 22 | "github.com/btcsuite/btcwallet/internal/rpchelp" 23 | ) 24 | 25 | func serverMethods() map[string]struct{} { 26 | m := make(map[string]struct{}) 27 | for method, handlerData := range rpcHandlers { 28 | if !handlerData.noHelp { 29 | m[method] = struct{}{} 30 | } 31 | } 32 | return m 33 | } 34 | 35 | // TestRPCMethodHelpGeneration ensures that help text can be generated for every 36 | // method of the RPC server for every supported locale. 37 | func TestRPCMethodHelpGeneration(t *testing.T) { 38 | needsGenerate := false 39 | 40 | defer func() { 41 | if needsGenerate && !t.Failed() { 42 | t.Error("Generated help texts are out of date: run 'go generate'") 43 | return 44 | } 45 | if t.Failed() { 46 | t.Log("Regenerate help texts with 'go generate' after fixing") 47 | } 48 | }() 49 | 50 | for i := range rpchelp.HelpDescs { 51 | svrMethods := serverMethods() 52 | locale := rpchelp.HelpDescs[i].Locale 53 | generatedDescs := localeHelpDescs[locale]() 54 | for _, m := range rpchelp.Methods { 55 | delete(svrMethods, m.Method) 56 | 57 | helpText, err := btcjson.GenerateHelp(m.Method, rpchelp.HelpDescs[i].Descs, m.ResultTypes...) 58 | if err != nil { 59 | t.Errorf("Cannot generate '%s' help for method '%s': missing description for '%s'", 60 | locale, m.Method, err) 61 | continue 62 | } 63 | if !needsGenerate && helpText != generatedDescs[m.Method] { 64 | needsGenerate = true 65 | } 66 | } 67 | 68 | for m := range svrMethods { 69 | t.Errorf("Missing '%s' help for method '%s'", locale, m) 70 | } 71 | } 72 | } 73 | 74 | // TestRPCMethodUsageGeneration ensures that single line usage text can be 75 | // generated for every supported request of the RPC server. 76 | func TestRPCMethodUsageGeneration(t *testing.T) { 77 | needsGenerate := false 78 | 79 | defer func() { 80 | if needsGenerate && !t.Failed() { 81 | t.Error("Generated help usages are out of date: run 'go generate'") 82 | return 83 | } 84 | if t.Failed() { 85 | t.Log("Regenerate help usage with 'go generate' after fixing") 86 | } 87 | }() 88 | 89 | svrMethods := serverMethods() 90 | usageStrs := make([]string, 0, len(rpchelp.Methods)) 91 | for _, m := range rpchelp.Methods { 92 | delete(svrMethods, m.Method) 93 | 94 | usage, err := btcjson.MethodUsageText(m.Method) 95 | if err != nil { 96 | t.Errorf("Cannot generate single line usage for method '%s': %v", 97 | m.Method, err) 98 | } 99 | 100 | if !t.Failed() { 101 | usageStrs = append(usageStrs, usage) 102 | } 103 | } 104 | 105 | if !t.Failed() { 106 | usages := strings.Join(usageStrs, "\n") 107 | needsGenerate = usages != requestUsages 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /docs/user/force_rescans.md: -------------------------------------------------------------------------------- 1 | # Rebuilding transaction history 2 | 3 | It is unlikely, but possible and unfortunate, that transaction history in the 4 | wallet database may not represent reality. This may be due to a programming 5 | mistake or the transaction database becoming corrupted. Thankfully, all 6 | transactions are publicly recorded on the blockchain, and transactions 7 | necessary for a fully functional wallet can be recovered. This process is 8 | called rescanning, and the following guide will demonstrate how to force such a 9 | rescan. 10 | 11 | Rescans are automatically performed each time the wallet syncs to the network. 12 | These are used to "catch up" the wallet to the newest best block in the block 13 | chain. For example, the following log messages at startup indicate that an 14 | out-of-sync wallet started a rescan for all addresses and unspent outputs since 15 | some block. 16 | 17 | ``` 18 | 13:45:03 2015-04-13 [INF] WLLT: Started rescan from block 00000000001703b1a9dfd4865d587cd3f3cbb2f8e6ce9b44668e78ad8d4a7377 (height 205921) for 1 address 19 | ... 20 | 13:45:49 2015-04-13 [INF] WLLT: Finished rescan for 1 address (synced to block 0000000005cecab1013ecb1275a3e0c9623c4a497a57b6b6bf0fc1525aca1fbf, height 335146) 21 | ``` 22 | 23 | During the rescan, relevant transactions from previously unseen blocks are added 24 | to the wallet database and spend tracking is updated accordingly. After the 25 | rescan at startup finishes, a wallet is marked in sync with the chain. 26 | 27 | When wallet is started without any transaction history, a rescan is performed 28 | for all blocks since the creation date of the wallet's first address. There are 29 | two situations when this holds true: 30 | 31 | 1. The wallet is newly created or was recreated from the seed 32 | 2. The transaction history is explicitly deleted 33 | 34 | The second case is how a forced rescan is performed. 35 | 36 | btcwallet will not drop transaction history by itself, as this is something that 37 | should not be necessary under normal wallet operation. However, a tool, 38 | `dropwtxmgr`, is provided in the `cmd/dropwtxmgr` directory which may be used to 39 | drop the wallet transaction manager (wtxmgr) history from a wallet database. 40 | The tool may already be installed in your PATH, but if not, installing it is easy: 41 | 42 | ``` 43 | $ cd $GOPATH/src/github.com/btcsuite/btcwallet/cmd/dropwtxmgr 44 | $ go get 45 | ``` 46 | 47 | Dropping transaction history given the default database location can be 48 | performed by stopping wallet (to release the database) and running the tool, 49 | answering yes to the prompt: 50 | 51 | ``` 52 | $ dropwtxmgr 53 | Database path: /home/username/.btcwallet/mainnet/wallet.db 54 | Drop all btcwallet transaction history? [y/N] y 55 | Dropping wtxmgr namespace 56 | ``` 57 | 58 | If the wallet database is in another location or transaction history for a 59 | different network (e.g. testnet or simnet) must be dropped, the full database 60 | path may be specified: 61 | 62 | ``` 63 | $ dropwtxmgr --db ~/.btcwallet/testnet/wallet.db 64 | Database path: /home/username/.btcwallet/testnet/wallet.db 65 | Drop all btcwallet transaction history? [y/N] y 66 | Dropping wtxmgr namespace 67 | ``` 68 | 69 | After dropping transaction history, btcwallet may be restarted and a full rescan 70 | will be triggered to sync the wallet: 71 | 72 | ``` 73 | $ btcwallet 74 | 14:05:31 2015-04-13 [INF] BTCW: No recorded transaction history -- needs full rescan 75 | ... 76 | 14:05:31 2015-04-13 [INF] WLLT: Started rescan from block 000000000000e37b0f99af2e434834123b5459e31e17937169ce81ed0cc4d61c (height 193191) for 1 address 77 | ... 78 | 14:07:06 2015-04-13 [INF] WLLT: Finished rescan for 1 address (synced to block 00000000049041b5bd7f8ac86c8f1d32065053aefbe8c31e25ed03ef015a725a, height 335482) 79 | 80 | ``` 81 | -------------------------------------------------------------------------------- /wtxmgr/error.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2017 The btcsuite developers 2 | // Copyright (c) 2015-2016 The Decred developers 3 | // Use of this source code is governed by an ISC 4 | // license that can be found in the LICENSE file. 5 | 6 | package wtxmgr 7 | 8 | import "fmt" 9 | 10 | // ErrorCode identifies a category of error. 11 | type ErrorCode uint8 12 | 13 | // These constants are used to identify a specific Error. 14 | const ( 15 | // ErrDatabase indicates an error with the underlying database. When 16 | // this error code is set, the Err field of the Error will be 17 | // set to the underlying error returned from the database. 18 | ErrDatabase ErrorCode = iota 19 | 20 | // ErrData describes an error where data stored in the transaction 21 | // database is incorrect. This may be due to missing values, values of 22 | // wrong sizes, or data from different buckets that is inconsistent with 23 | // itself. Recovering from an ErrData requires rebuilding all 24 | // transaction history or manual database surgery. If the failure was 25 | // not due to data corruption, this error category indicates a 26 | // programming error in this package. 27 | ErrData 28 | 29 | // ErrInput describes an error where the variables passed into this 30 | // function by the caller are obviously incorrect. Examples include 31 | // passing transactions which do not serialize, or attempting to insert 32 | // a credit at an index for which no transaction output exists. 33 | ErrInput 34 | 35 | // ErrAlreadyExists describes an error where creating the store cannot 36 | // continue because a store already exists in the namespace. 37 | ErrAlreadyExists 38 | 39 | // ErrNoExists describes an error where the store cannot be opened due to 40 | // it not already existing in the namespace. This error should be 41 | // handled by creating a new store. 42 | ErrNoExists 43 | 44 | // ErrNeedsUpgrade describes an error during store opening where the 45 | // database contains an older version of the store. 46 | ErrNeedsUpgrade 47 | 48 | // ErrUnknownVersion describes an error where the store already exists 49 | // but the database version is newer than latest version known to this 50 | // software. This likely indicates an outdated binary. 51 | ErrUnknownVersion 52 | ) 53 | 54 | var errStrs = [...]string{ 55 | ErrDatabase: "ErrDatabase", 56 | ErrData: "ErrData", 57 | ErrInput: "ErrInput", 58 | ErrAlreadyExists: "ErrAlreadyExists", 59 | ErrNoExists: "ErrNoExists", 60 | ErrNeedsUpgrade: "ErrNeedsUpgrade", 61 | ErrUnknownVersion: "ErrUnknownVersion", 62 | } 63 | 64 | // String returns the ErrorCode as a human-readable name. 65 | func (e ErrorCode) String() string { 66 | if e < ErrorCode(len(errStrs)) { 67 | return errStrs[e] 68 | } 69 | return fmt.Sprintf("ErrorCode(%d)", e) 70 | } 71 | 72 | // Error provides a single type for errors that can happen during Store 73 | // operation. 74 | type Error struct { 75 | Code ErrorCode // Describes the kind of error 76 | Desc string // Human-readable description of the issue 77 | Err error // Underlying error, optional 78 | } 79 | 80 | // Error satisfies the error interface and prints human-readable errors. 81 | func (e Error) Error() string { 82 | if e.Err != nil { 83 | return e.Desc + ": " + e.Err.Error() 84 | } 85 | return e.Desc 86 | } 87 | 88 | // Unwrap returns the underlying error, if any. 89 | func (e Error) Unwrap() error { 90 | return e.Err 91 | } 92 | 93 | func storeError(c ErrorCode, desc string, err error) Error { 94 | return Error{Code: c, Desc: desc, Err: err} 95 | } 96 | 97 | // IsNoExists returns whether an error is a Error with the ErrNoExists error 98 | // code. 99 | func IsNoExists(err error) bool { 100 | serr, ok := err.(Error) 101 | return ok && serr.Code == ErrNoExists 102 | } 103 | -------------------------------------------------------------------------------- /wtxmgr/migrations.go: -------------------------------------------------------------------------------- 1 | package wtxmgr 2 | 3 | import ( 4 | "github.com/btcsuite/btcwallet/walletdb" 5 | "github.com/btcsuite/btcwallet/walletdb/migration" 6 | ) 7 | 8 | // versions is a list of the different database versions. The last entry should 9 | // reflect the latest database state. If the database happens to be at a version 10 | // number lower than the latest, migrations will be performed in order to catch 11 | // it up. 12 | var versions = []migration.Version{ 13 | { 14 | Number: 1, 15 | Migration: nil, 16 | }, 17 | { 18 | Number: 2, 19 | Migration: dropTransactionHistory, 20 | }, 21 | } 22 | 23 | // getLatestVersion returns the version number of the latest database version. 24 | func getLatestVersion() uint32 { 25 | return versions[len(versions)-1].Number 26 | } 27 | 28 | // MigrationManager is an implementation of the migration.Manager interface that 29 | // will be used to handle migrations for the address manager. It exposes the 30 | // necessary parameters required to successfully perform migrations. 31 | type MigrationManager struct { 32 | ns walletdb.ReadWriteBucket 33 | } 34 | 35 | // A compile-time assertion to ensure that MigrationManager implements the 36 | // migration.Manager interface. 37 | var _ migration.Manager = (*MigrationManager)(nil) 38 | 39 | // NewMigrationManager creates a new migration manager for the transaction 40 | // manager. The given bucket should reflect the top-level bucket in which all 41 | // of the transaction manager's data is contained within. 42 | func NewMigrationManager(ns walletdb.ReadWriteBucket) *MigrationManager { 43 | return &MigrationManager{ns: ns} 44 | } 45 | 46 | // Name returns the name of the service we'll be attempting to upgrade. 47 | // 48 | // NOTE: This method is part of the migration.Manager interface. 49 | func (m *MigrationManager) Name() string { 50 | return "wallet transaction manager" 51 | } 52 | 53 | // Namespace returns the top-level bucket of the service. 54 | // 55 | // NOTE: This method is part of the migration.Manager interface. 56 | func (m *MigrationManager) Namespace() walletdb.ReadWriteBucket { 57 | return m.ns 58 | } 59 | 60 | // CurrentVersion returns the current version of the service's database. 61 | // 62 | // NOTE: This method is part of the migration.Manager interface. 63 | func (m *MigrationManager) CurrentVersion(ns walletdb.ReadBucket) (uint32, error) { 64 | if ns == nil { 65 | ns = m.ns 66 | } 67 | return fetchVersion(m.ns) 68 | } 69 | 70 | // SetVersion sets the version of the service's database. 71 | // 72 | // NOTE: This method is part of the migration.Manager interface. 73 | func (m *MigrationManager) SetVersion(ns walletdb.ReadWriteBucket, 74 | version uint32) error { 75 | 76 | if ns == nil { 77 | ns = m.ns 78 | } 79 | return putVersion(m.ns, version) 80 | } 81 | 82 | // Versions returns all of the available database versions of the service. 83 | // 84 | // NOTE: This method is part of the migration.Manager interface. 85 | func (m *MigrationManager) Versions() []migration.Version { 86 | return versions 87 | } 88 | 89 | // dropTransactionHistory is a migration that attempts to recreate the 90 | // transaction store with a clean state. 91 | func dropTransactionHistory(ns walletdb.ReadWriteBucket) error { 92 | log.Info("Dropping wallet transaction history") 93 | 94 | // To drop the store's transaction history, we'll need to remove all of 95 | // the relevant descendant buckets and key/value pairs. 96 | if err := deleteBuckets(ns); err != nil { 97 | return err 98 | } 99 | if err := ns.Delete(rootMinedBalance); err != nil { 100 | return err 101 | } 102 | 103 | // With everything removed, we'll now recreate our buckets. 104 | if err := createBuckets(ns); err != nil { 105 | return err 106 | } 107 | 108 | // Finally, we'll insert a 0 value for our mined balance. 109 | return putMinedBalance(ns, 0) 110 | } 111 | -------------------------------------------------------------------------------- /wallet/multisig.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 The btcsuite developers 2 | // Copyright (c) 2016 The Decred developers 3 | // Use of this source code is governed by an ISC 4 | // license that can be found in the LICENSE file. 5 | 6 | package wallet 7 | 8 | import ( 9 | "errors" 10 | 11 | "github.com/btcsuite/btcd/btcutil" 12 | "github.com/btcsuite/btcd/txscript" 13 | "github.com/btcsuite/btcwallet/waddrmgr" 14 | "github.com/btcsuite/btcwallet/walletdb" 15 | ) 16 | 17 | // MakeMultiSigScript creates a multi-signature script that can be redeemed with 18 | // nRequired signatures of the passed keys and addresses. If the address is a 19 | // P2PKH address, the associated pubkey is looked up by the wallet if possible, 20 | // otherwise an error is returned for a missing pubkey. 21 | // 22 | // This function only works with pubkeys and P2PKH addresses derived from them. 23 | func (w *Wallet) MakeMultiSigScript(addrs []btcutil.Address, nRequired int) ([]byte, error) { 24 | pubKeys := make([]*btcutil.AddressPubKey, len(addrs)) 25 | 26 | var dbtx walletdb.ReadTx 27 | var addrmgrNs walletdb.ReadBucket 28 | defer func() { 29 | if dbtx != nil { 30 | _ = dbtx.Rollback() 31 | } 32 | }() 33 | 34 | // The address list will made up either of addreseses (pubkey hash), for 35 | // which we need to look up the keys in wallet, straight pubkeys, or a 36 | // mixture of the two. 37 | for i, addr := range addrs { 38 | switch addr := addr.(type) { 39 | default: 40 | return nil, errors.New("cannot make multisig script for " + 41 | "a non-secp256k1 public key or P2PKH address") 42 | 43 | case *btcutil.AddressPubKey: 44 | pubKeys[i] = addr 45 | 46 | case *btcutil.AddressPubKeyHash: 47 | if dbtx == nil { 48 | var err error 49 | dbtx, err = w.db.BeginReadTx() 50 | if err != nil { 51 | return nil, err 52 | } 53 | addrmgrNs = dbtx.ReadBucket(waddrmgrNamespaceKey) 54 | } 55 | addrInfo, err := w.Manager.Address(addrmgrNs, addr) 56 | if err != nil { 57 | return nil, err 58 | } 59 | serializedPubKey := addrInfo.(waddrmgr.ManagedPubKeyAddress). 60 | PubKey().SerializeCompressed() 61 | 62 | pubKeyAddr, err := btcutil.NewAddressPubKey( 63 | serializedPubKey, w.chainParams) 64 | if err != nil { 65 | return nil, err 66 | } 67 | pubKeys[i] = pubKeyAddr 68 | } 69 | } 70 | 71 | return txscript.MultiSigScript(pubKeys, nRequired) 72 | } 73 | 74 | // ImportP2SHRedeemScript adds a P2SH redeem script to the wallet. 75 | func (w *Wallet) ImportP2SHRedeemScript(script []byte) (*btcutil.AddressScriptHash, error) { 76 | var p2shAddr *btcutil.AddressScriptHash 77 | err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error { 78 | addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey) 79 | 80 | // TODO(oga) blockstamp current block? 81 | bs := &waddrmgr.BlockStamp{ 82 | Hash: *w.ChainParams().GenesisHash, 83 | Height: 0, 84 | } 85 | 86 | // As this is a regular P2SH script, we'll import this into the 87 | // BIP0044 scope. 88 | bip44Mgr, err := w.Manager.FetchScopedKeyManager( 89 | waddrmgr.KeyScopeBIP0084, 90 | ) 91 | if err != nil { 92 | return err 93 | } 94 | 95 | addrInfo, err := bip44Mgr.ImportScript(addrmgrNs, script, bs) 96 | if err != nil { 97 | // Don't care if it's already there, but still have to 98 | // set the p2shAddr since the address manager didn't 99 | // return anything useful. 100 | if waddrmgr.IsError(err, waddrmgr.ErrDuplicateAddress) { 101 | // This function will never error as it always 102 | // hashes the script to the correct length. 103 | p2shAddr, _ = btcutil.NewAddressScriptHash(script, 104 | w.chainParams) 105 | return nil 106 | } 107 | return err 108 | } 109 | 110 | p2shAddr = addrInfo.Address().(*btcutil.AddressScriptHash) 111 | return nil 112 | }) 113 | return p2shAddr, err 114 | } 115 | -------------------------------------------------------------------------------- /waddrmgr/db_test.go: -------------------------------------------------------------------------------- 1 | package waddrmgr 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/btcsuite/btcd/chaincfg/chainhash" 9 | "github.com/btcsuite/btcwallet/walletdb" 10 | ) 11 | 12 | // TestStoreMaxReorgDepth ensures that we can only store up to MaxReorgDepth 13 | // blocks at any given time. 14 | func TestStoreMaxReorgDepth(t *testing.T) { 15 | t.Parallel() 16 | 17 | teardown, db, _ := setupManager(t) 18 | defer teardown() 19 | 20 | // We'll start the test by simulating a synced chain where we start from 21 | // 1000 and end at 109999. 22 | const ( 23 | startHeight = 1000 24 | numBlocks = MaxReorgDepth - 1 25 | ) 26 | 27 | blocks := make([]*BlockStamp, 0, numBlocks) 28 | for i := int32(startHeight); i <= startHeight+numBlocks; i++ { 29 | var hash chainhash.Hash 30 | binary.BigEndian.PutUint32(hash[:], uint32(i)) 31 | blocks = append(blocks, &BlockStamp{ 32 | Hash: hash, 33 | Height: i, 34 | }) 35 | } 36 | 37 | // We'll write all of the blocks to the database. 38 | err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { 39 | ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) 40 | for _, block := range blocks { 41 | if err := PutSyncedTo(ns, block); err != nil { 42 | return err 43 | } 44 | } 45 | return nil 46 | }) 47 | if err != nil { 48 | t.Fatal(err) 49 | } 50 | 51 | // We should be able to retrieve them all as we have MaxReorgDepth 52 | // blocks. 53 | err = walletdb.View(db, func(tx walletdb.ReadTx) error { 54 | ns := tx.ReadBucket(waddrmgrNamespaceKey) 55 | syncedTo, err := fetchSyncedTo(ns) 56 | if err != nil { 57 | return err 58 | } 59 | lastBlock := blocks[len(blocks)-1] 60 | if syncedTo.Height != lastBlock.Height { 61 | return fmt.Errorf("expected synced to block height "+ 62 | "%v, got %v", lastBlock.Height, syncedTo.Height) 63 | } 64 | if syncedTo.Hash != lastBlock.Hash { 65 | return fmt.Errorf("expected synced to block hash %v, "+ 66 | "got %v", lastBlock.Hash, syncedTo.Hash) 67 | } 68 | 69 | firstBlock := blocks[0] 70 | hash, err := fetchBlockHash(ns, firstBlock.Height) 71 | if err != nil { 72 | return err 73 | } 74 | if *hash != firstBlock.Hash { 75 | return fmt.Errorf("expected hash %v for height %v, "+ 76 | "got %v", firstBlock.Hash, firstBlock.Height, 77 | hash) 78 | } 79 | 80 | return nil 81 | }) 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | 86 | // Then, we'll create a new block which we'll use to extend the chain. 87 | lastBlock := blocks[len(blocks)-1] 88 | newBlockHeight := lastBlock.Height + 1 89 | var newBlockHash chainhash.Hash 90 | binary.BigEndian.PutUint32(newBlockHash[:], uint32(newBlockHeight)) 91 | newBlock := &BlockStamp{Height: newBlockHeight, Hash: newBlockHash} 92 | 93 | err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { 94 | ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) 95 | return PutSyncedTo(ns, newBlock) 96 | }) 97 | if err != nil { 98 | t.Fatal(err) 99 | } 100 | 101 | // Extending the chain would cause us to exceed our MaxReorgDepth blocks 102 | // stored, so we should see the first block we ever added to now be 103 | // removed. 104 | err = walletdb.View(db, func(tx walletdb.ReadTx) error { 105 | ns := tx.ReadBucket(waddrmgrNamespaceKey) 106 | syncedTo, err := fetchSyncedTo(ns) 107 | if err != nil { 108 | return err 109 | } 110 | if syncedTo.Height != newBlock.Height { 111 | return fmt.Errorf("expected synced to block height "+ 112 | "%v, got %v", newBlock.Height, syncedTo.Height) 113 | } 114 | if syncedTo.Hash != newBlock.Hash { 115 | return fmt.Errorf("expected synced to block hash %v, "+ 116 | "got %v", newBlock.Hash, syncedTo.Hash) 117 | } 118 | 119 | firstBlock := blocks[0] 120 | _, err = fetchBlockHash(ns, firstBlock.Height) 121 | if !IsError(err, ErrBlockNotFound) { 122 | return fmt.Errorf("expected ErrBlockNotFound, got %w", 123 | err) 124 | } 125 | 126 | return nil 127 | }) 128 | if err != nil { 129 | t.Fatal(err) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /wtxmgr/kahnsort_test.go: -------------------------------------------------------------------------------- 1 | package wtxmgr_test 2 | 3 | import ( 4 | "math/rand" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/btcsuite/btcd/chaincfg/chainhash" 9 | "github.com/btcsuite/btcd/wire" 10 | "github.com/btcsuite/btcwallet/wtxmgr" 11 | ) 12 | 13 | // createTx is a helper method to create random transactions that spend 14 | // particular inputs. 15 | func createTx(t *testing.T, numOutputs int, inputs ...wire.OutPoint) *wire.MsgTx { 16 | t.Helper() 17 | 18 | tx := wire.NewMsgTx(1) 19 | if len(inputs) == 0 { 20 | tx.AddTxIn(&wire.TxIn{}) 21 | } else { 22 | for _, input := range inputs { 23 | tx.AddTxIn(&wire.TxIn{PreviousOutPoint: input}) 24 | } 25 | } 26 | for i := 0; i < numOutputs; i++ { 27 | var pkScript [32]byte 28 | if _, err := rand.Read(pkScript[:]); err != nil { 29 | t.Fatal(err) 30 | } 31 | 32 | tx.AddTxOut(&wire.TxOut{ 33 | Value: rand.Int63(), 34 | PkScript: pkScript[:], 35 | }) 36 | } 37 | 38 | return tx 39 | } 40 | 41 | // getOutPoint returns the outpoint for the output with the given index in the 42 | // transaction. 43 | func getOutPoint(tx *wire.MsgTx, index uint32) wire.OutPoint { 44 | return wire.OutPoint{Hash: tx.TxHash(), Index: index} 45 | } 46 | 47 | // TestDependencySort ensures that transactions are topologically sorted by 48 | // their dependency order under multiple scenarios. A transaction (a) can depend 49 | // on another (b) as long as (a) spends an output created in (b). 50 | func TestDependencySort(t *testing.T) { 51 | t.Parallel() 52 | 53 | tests := []struct { 54 | name string 55 | 56 | // setup is in charge of setting the dependency graph and 57 | // returning the transactions in their expected sorted order. 58 | setup func(t *testing.T) []*wire.MsgTx 59 | }{ 60 | { 61 | name: "single dependency chain", 62 | setup: func(t *testing.T) []*wire.MsgTx { 63 | // a -> b -> c 64 | a := createTx(t, 1) 65 | b := createTx(t, 1, getOutPoint(a, 0)) 66 | c := createTx(t, 1, getOutPoint(b, 0)) 67 | return []*wire.MsgTx{a, b, c} 68 | }, 69 | }, 70 | { 71 | name: "double dependency chain", 72 | setup: func(t *testing.T) []*wire.MsgTx { 73 | // a -> b 74 | // a -> c 75 | // c -> d 76 | // d -> b 77 | a := createTx(t, 2) 78 | c := createTx(t, 1, getOutPoint(a, 1)) 79 | d := createTx(t, 1, getOutPoint(c, 0)) 80 | b := createTx(t, 1, getOutPoint(a, 0), getOutPoint(d, 0)) 81 | return []*wire.MsgTx{a, c, d, b} 82 | }, 83 | }, 84 | { 85 | name: "multi dependency chain", 86 | setup: func(t *testing.T) []*wire.MsgTx { 87 | // a -> e 88 | // a -> c 89 | // e -> c 90 | // c -> g 91 | // a -> b 92 | // g -> b 93 | // e -> f 94 | // c -> f 95 | // g -> f 96 | // b -> f 97 | // b -> d 98 | // f -> d 99 | a := createTx(t, 3) 100 | 101 | a0 := getOutPoint(a, 0) 102 | e := createTx(t, 2, a0) 103 | 104 | a1 := getOutPoint(a, 1) 105 | e0 := getOutPoint(e, 0) 106 | c := createTx(t, 2, a1, e0) 107 | 108 | c0 := getOutPoint(c, 0) 109 | g := createTx(t, 2, c0) 110 | 111 | a2 := getOutPoint(a, 2) 112 | g0 := getOutPoint(g, 0) 113 | b := createTx(t, 1, a2, g0) 114 | 115 | e1 := getOutPoint(e, 1) 116 | c1 := getOutPoint(c, 1) 117 | g1 := getOutPoint(g, 1) 118 | b0 := getOutPoint(b, 0) 119 | f := createTx(t, 1, e1, c1, g1, b0) 120 | 121 | f0 := getOutPoint(f, 0) 122 | d := createTx(t, 1, b0, f0) 123 | 124 | return []*wire.MsgTx{a, e, c, g, b, f, d} 125 | }, 126 | }, 127 | } 128 | 129 | for _, test := range tests { 130 | test := test 131 | t.Run(test.name, func(t *testing.T) { 132 | t.Parallel() 133 | 134 | exp := test.setup(t) 135 | 136 | txSet := make(map[chainhash.Hash]*wire.MsgTx, len(exp)) 137 | for _, tx := range exp { 138 | txSet[tx.TxHash()] = tx 139 | } 140 | 141 | sortedTxs := wtxmgr.DependencySort(txSet) 142 | 143 | if !reflect.DeepEqual(sortedTxs, exp) { 144 | t.Fatalf("expected %v, got %v", exp, sortedTxs) 145 | } 146 | }) 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /rpc/documentation/serverchanges.md: -------------------------------------------------------------------------------- 1 | # Making API Changes 2 | 3 | This document describes the process of how btcwallet developers must make 4 | changes to the RPC API and server. Due to the use of gRPC and Protocol Buffers 5 | for the RPC implementation, changes to this API require extra dependencies and 6 | steps before changes to the server can be implemented. 7 | 8 | ## Requirements 9 | 10 | - The Protocol Buffer compiler `protoc` installed with support for the `proto3` 11 | language 12 | 13 | The `protoc` tool is part of the Protocol Buffers project. This can be 14 | installed [from source](https://github.com/google/protobuf/blob/master/INSTALL.txt), 15 | from an [official binary release](https://github.com/google/protobuf/releases), 16 | or through an operating system's package manager. 17 | 18 | - The gRPC `protoc` plugin for Go 19 | 20 | This plugin is written in Go and can be installed using `go get`: 21 | 22 | ``` 23 | go get github.com/golang/protobuf/protoc-gen-go 24 | ``` 25 | 26 | - Knowledge of Protocol Buffers version 3 (proto3) 27 | 28 | Note that a full installation of gRPC Core is not required, and only the 29 | `protoc` compiler and Go plugins are necessary. This is due to the project 30 | using a pure Go gRPC implementation instead of wrapping the C library from gRPC 31 | Core. 32 | 33 | ## Step 1: Modify the `.proto` 34 | 35 | Once the developer dependencies have been met, changes can be made to the API by 36 | modifying the Protocol Buffers descriptor file [`api.proto`](../api.proto). 37 | 38 | The API is versioned according to the rules of [Semantic Versioning 39 | 2.0](http://semver.org/). After any changes, bump the API version in the [API 40 | specification](./api.md) and add the changes to the spec. 41 | 42 | Unless backwards compatibility is broken (and the version is bumped to represent 43 | this change), message fields must never be removed or changed, and new fields 44 | must always be appended. 45 | 46 | It is forbidden to use the `required` attribute on a message field as this can 47 | cause errors during parsing when the new API is used by an older client. 48 | Instead, the (implicit) optional attribute is used, and the server 49 | implementation must return an appropriate error if the new request field is not 50 | set to a valid value. 51 | 52 | ## Step 2: Compile the `.proto` 53 | 54 | Once changes to the descriptor file and API specification have been made, the 55 | `protoc` compiler must be used to compile the descriptor into a Go package. 56 | This code contains interfaces (stubs) for each service (to be implemented by the 57 | wallet) and message types used for each RPC. This same code can also be 58 | imported by a Go client that then calls same interface methods to perform RPC 59 | with the wallet. 60 | 61 | By committing the autogenerated package to the project repo, the `proto3` 62 | compiler and plugin are not needed by users installing the project by source or 63 | by other developers not making changes to the RPC API. 64 | 65 | A `sh` shell script is included to compile the Protocol Buffers descriptor. It 66 | must be run from the `rpc` directory. 67 | 68 | ```bash 69 | $ sh gen_protos_docker.sh 70 | ``` 71 | 72 | If a `sh` shell is unavailable, the command can be run manually instead (again 73 | from the `rpc` directory). 74 | 75 | ``` 76 | protoc -I. api.proto --go_out=plugins=grpc:walletrpc 77 | ``` 78 | 79 | TODO(jrick): This step could be simplified and be more portable by putting the 80 | commands in a Go source file and executing them with `go generate`. It should, 81 | however, only be run when API changes are performed (not with `go generate 82 | ./...` in the project root) since not all developers are expected to have 83 | `protoc` installed. 84 | 85 | ## Step 3: Implement the API change in the RPC server 86 | 87 | After the Go code for the API has been regenerated, the necessary changes can be 88 | implemented in the [`rpcserver`](../rpcserver/) package. 89 | 90 | ## Additional Resources 91 | 92 | - [Protocol Buffers Language Guide (proto3)](https://developers.google.com/protocol-buffers/docs/proto3) 93 | - [Protocol Buffers Basics: Go](https://developers.google.com/protocol-buffers/docs/gotutorial) 94 | - [gRPC Basics: Go](http://www.grpc.io/docs/tutorials/basic/go.html) 95 | -------------------------------------------------------------------------------- /waddrmgr/tapscript.go: -------------------------------------------------------------------------------- 1 | package waddrmgr 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/btcsuite/btcd/btcec/v2" 7 | "github.com/btcsuite/btcd/txscript" 8 | ) 9 | 10 | // TapscriptType is a special type denoting the different variants of 11 | // tapscripts. 12 | type TapscriptType uint8 13 | 14 | const ( 15 | // TapscriptTypeFullTree is the type of tapscript that knows its full 16 | // tree with all individual leaves present. 17 | TapscriptTypeFullTree TapscriptType = 0 18 | 19 | // TapscriptTypePartialReveal is the type of tapscript that only knows 20 | // a single revealed leaf and the merkle/inclusion proof for the rest of 21 | // the tree. 22 | TapscriptTypePartialReveal TapscriptType = 1 23 | 24 | // TaprootKeySpendRootHash is the type of tapscript that only knows the 25 | // root hash of the Taproot commitment and therefore only allows for key 26 | // spends within the wallet, since a full control block cannot be 27 | // constructed. 28 | TaprootKeySpendRootHash TapscriptType = 2 29 | 30 | // TaprootFullKeyOnly is the type of tapscript that only knows the final 31 | // Taproot key and no additional information about its internal key or 32 | // the type of tap tweak that was used. This can be useful for tracking 33 | // arbitrary Taproot outputs without the goal of ever being able to 34 | // spend from them through the internal wallet. 35 | TaprootFullKeyOnly TapscriptType = 3 36 | ) 37 | 38 | // Tapscript is a struct that holds either a full taproot tapscript with all 39 | // individual leaves or a single leaf and the corresponding proof to arrive at 40 | // the root hash. 41 | type Tapscript struct { 42 | // Type is the type of the tapscript. 43 | Type TapscriptType 44 | 45 | // ControlBlock houses the main information about the internal key and 46 | // the resulting key's parity. And, in case of the 47 | // TapscriptTypePartialReveal type, the control block also contains the 48 | // inclusion proof and the leaf version for the revealed script. 49 | ControlBlock *txscript.ControlBlock 50 | 51 | // Leaves is the full set of tap leaves in their proper order. This is 52 | // only set if the Type is TapscriptTypeFullTree. 53 | Leaves []txscript.TapLeaf 54 | 55 | // RevealedScript is the script of the single revealed script. Is only 56 | // set if the Type is TapscriptTypePartialReveal. 57 | RevealedScript []byte 58 | 59 | // RootHash is the root hash of a tapscript tree that is committed to in 60 | // the Taproot output. This is only set if the Type is 61 | // TaprootKeySpendRootHash. 62 | RootHash []byte 63 | 64 | // FullOutputKey is the fully tweaked Taproot output key as it appears 65 | // on the chain. This is only set if the Type is TaprootFullKeyOnly. 66 | FullOutputKey *btcec.PublicKey 67 | } 68 | 69 | // TaprootKey calculates the tweaked taproot key from the given internal key and 70 | // the tree information in this tapscript struct. If any information required to 71 | // calculate the root hash is missing, this method returns an error. 72 | func (t *Tapscript) TaprootKey() (*btcec.PublicKey, error) { 73 | if t.Type != TaprootFullKeyOnly && 74 | (t.ControlBlock == nil || t.ControlBlock.InternalKey == nil) { 75 | 76 | return nil, fmt.Errorf("internal key is missing") 77 | } 78 | 79 | switch t.Type { 80 | case TapscriptTypeFullTree: 81 | if len(t.Leaves) == 0 { 82 | return nil, fmt.Errorf("missing leaves") 83 | } 84 | 85 | tree := txscript.AssembleTaprootScriptTree(t.Leaves...) 86 | rootHash := tree.RootNode.TapHash() 87 | return txscript.ComputeTaprootOutputKey( 88 | t.ControlBlock.InternalKey, rootHash[:], 89 | ), nil 90 | 91 | case TapscriptTypePartialReveal: 92 | if len(t.RevealedScript) == 0 { 93 | return nil, fmt.Errorf("revealed script missing") 94 | } 95 | 96 | rootHash := t.ControlBlock.RootHash(t.RevealedScript) 97 | return txscript.ComputeTaprootOutputKey( 98 | t.ControlBlock.InternalKey, rootHash, 99 | ), nil 100 | 101 | case TaprootKeySpendRootHash: 102 | if len(t.RootHash) == 0 { 103 | return nil, fmt.Errorf("root hash is missing") 104 | } 105 | 106 | return txscript.ComputeTaprootOutputKey( 107 | t.ControlBlock.InternalKey, t.RootHash, 108 | ), nil 109 | 110 | case TaprootFullKeyOnly: 111 | return t.FullOutputKey, nil 112 | 113 | default: 114 | return nil, fmt.Errorf("unknown tapscript type %d", t.Type) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /waddrmgr/error_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package waddrmgr_test 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "testing" 11 | 12 | "github.com/btcsuite/btcwallet/waddrmgr" 13 | ) 14 | 15 | // TestErrorCodeStringer tests the stringized output for the ErrorCode type. 16 | func TestErrorCodeStringer(t *testing.T) { 17 | tests := []struct { 18 | in waddrmgr.ErrorCode 19 | want string 20 | }{ 21 | {waddrmgr.ErrDatabase, "ErrDatabase"}, 22 | {waddrmgr.ErrUpgrade, "ErrUpgrade"}, 23 | {waddrmgr.ErrKeyChain, "ErrKeyChain"}, 24 | {waddrmgr.ErrCrypto, "ErrCrypto"}, 25 | {waddrmgr.ErrInvalidKeyType, "ErrInvalidKeyType"}, 26 | {waddrmgr.ErrNoExist, "ErrNoExist"}, 27 | {waddrmgr.ErrAlreadyExists, "ErrAlreadyExists"}, 28 | {waddrmgr.ErrCoinTypeTooHigh, "ErrCoinTypeTooHigh"}, 29 | {waddrmgr.ErrAccountNumTooHigh, "ErrAccountNumTooHigh"}, 30 | {waddrmgr.ErrLocked, "ErrLocked"}, 31 | {waddrmgr.ErrWatchingOnly, "ErrWatchingOnly"}, 32 | {waddrmgr.ErrInvalidAccount, "ErrInvalidAccount"}, 33 | {waddrmgr.ErrAddressNotFound, "ErrAddressNotFound"}, 34 | {waddrmgr.ErrAccountNotFound, "ErrAccountNotFound"}, 35 | {waddrmgr.ErrDuplicateAddress, "ErrDuplicateAddress"}, 36 | {waddrmgr.ErrDuplicateAccount, "ErrDuplicateAccount"}, 37 | {waddrmgr.ErrTooManyAddresses, "ErrTooManyAddresses"}, 38 | {waddrmgr.ErrWrongPassphrase, "ErrWrongPassphrase"}, 39 | {waddrmgr.ErrWrongNet, "ErrWrongNet"}, 40 | {waddrmgr.ErrCallBackBreak, "ErrCallBackBreak"}, 41 | {waddrmgr.ErrEmptyPassphrase, "ErrEmptyPassphrase"}, 42 | {0xffff, "Unknown ErrorCode (65535)"}, 43 | } 44 | t.Logf("Running %d tests", len(tests)) 45 | for i, test := range tests { 46 | result := test.in.String() 47 | if result != test.want { 48 | t.Errorf("String #%d\ngot: %s\nwant: %s", i, result, 49 | test.want) 50 | continue 51 | } 52 | } 53 | } 54 | 55 | // TestManagerError tests the error output for the ManagerError type. 56 | func TestManagerError(t *testing.T) { 57 | tests := []struct { 58 | in waddrmgr.ManagerError 59 | want string 60 | }{ 61 | // Manager level error. 62 | { 63 | waddrmgr.ManagerError{Description: "human-readable error"}, 64 | "human-readable error", 65 | }, 66 | 67 | // Encapsulated database error. 68 | { 69 | waddrmgr.ManagerError{ 70 | Description: "failed to store master private " + 71 | "key parameters", 72 | ErrorCode: waddrmgr.ErrDatabase, 73 | Err: fmt.Errorf("underlying db error"), 74 | }, 75 | "failed to store master private key parameters: " + 76 | "underlying db error", 77 | }, 78 | 79 | // Encapsulated key chain error. 80 | { 81 | waddrmgr.ManagerError{ 82 | Description: "failed to derive extended key " + 83 | "branch 0", 84 | ErrorCode: waddrmgr.ErrKeyChain, 85 | Err: fmt.Errorf("underlying error"), 86 | }, 87 | "failed to derive extended key branch 0: underlying " + 88 | "error", 89 | }, 90 | 91 | // Encapsulated crypto error. 92 | { 93 | waddrmgr.ManagerError{ 94 | Description: "failed to decrypt account 0 " + 95 | "private key", 96 | ErrorCode: waddrmgr.ErrCrypto, 97 | Err: fmt.Errorf("underlying error"), 98 | }, 99 | "failed to decrypt account 0 private key: underlying " + 100 | "error", 101 | }, 102 | } 103 | 104 | t.Logf("Running %d tests", len(tests)) 105 | for i, test := range tests { 106 | result := test.in.Error() 107 | if result != test.want { 108 | t.Errorf("Error #%d\ngot: %s\nwant: %s", i, result, 109 | test.want) 110 | continue 111 | } 112 | } 113 | } 114 | 115 | // TestIsError tests the IsError func. 116 | func TestIsError(t *testing.T) { 117 | tests := []struct { 118 | err error 119 | code waddrmgr.ErrorCode 120 | exp bool 121 | }{ 122 | { 123 | err: waddrmgr.ManagerError{ 124 | ErrorCode: waddrmgr.ErrDatabase, 125 | }, 126 | code: waddrmgr.ErrDatabase, 127 | exp: true, 128 | }, 129 | { 130 | // package should never return *ManagerError 131 | err: &waddrmgr.ManagerError{ 132 | ErrorCode: waddrmgr.ErrDatabase, 133 | }, 134 | code: waddrmgr.ErrDatabase, 135 | exp: false, 136 | }, 137 | { 138 | err: waddrmgr.ManagerError{ 139 | ErrorCode: waddrmgr.ErrCrypto, 140 | }, 141 | code: waddrmgr.ErrDatabase, 142 | exp: false, 143 | }, 144 | { 145 | err: errors.New("not a ManagerError"), 146 | code: waddrmgr.ErrDatabase, 147 | exp: false, 148 | }, 149 | } 150 | 151 | for i, test := range tests { 152 | got := waddrmgr.IsError(test.err, test.code) 153 | if got != test.exp { 154 | t.Errorf("Test %d: got %v expected %v", i, got, test.exp) 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /walletdb/db_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package walletdb_test 6 | 7 | import ( 8 | "fmt" 9 | "path/filepath" 10 | "testing" 11 | "time" 12 | 13 | "github.com/btcsuite/btcwallet/walletdb" 14 | _ "github.com/btcsuite/btcwallet/walletdb/bdb" 15 | ) 16 | 17 | var ( 18 | // ignoreDbTypes are types which should be ignored when running tests 19 | // that iterate all supported DB types. This allows some tests to add 20 | // bogus drivers for testing purposes while still allowing other tests 21 | // to easily iterate all supported drivers. 22 | ignoreDbTypes = map[string]bool{"createopenfail": true} 23 | 24 | // defaultDBTimeout specifies the timeout value when opening the wallet 25 | // database. 26 | defaultDBTimeout = 10 * time.Second 27 | ) 28 | 29 | // TestAddDuplicateDriver ensures that adding a duplicate driver does not 30 | // overwrite an existing one. 31 | func TestAddDuplicateDriver(t *testing.T) { 32 | supportedDrivers := walletdb.SupportedDrivers() 33 | if len(supportedDrivers) == 0 { 34 | t.Errorf("no backends to test") 35 | return 36 | } 37 | 38 | dbType := supportedDrivers[0] 39 | 40 | // bogusCreateDB is a function which acts as a bogus create and open 41 | // driver function and intentionally returns a failure that can be 42 | // detected if the interface allows a duplicate driver to overwrite an 43 | // existing one. 44 | bogusCreateDB := func(args ...interface{}) (walletdb.DB, error) { 45 | return nil, fmt.Errorf("duplicate driver allowed for database "+ 46 | "type [%v]", dbType) 47 | } 48 | 49 | // Create a driver that tries to replace an existing one. Set its 50 | // create and open functions to a function that causes a test failure if 51 | // they are invoked. 52 | driver := walletdb.Driver{ 53 | DbType: dbType, 54 | Create: bogusCreateDB, 55 | Open: bogusCreateDB, 56 | } 57 | err := walletdb.RegisterDriver(driver) 58 | if err != walletdb.ErrDbTypeRegistered { 59 | t.Errorf("unexpected duplicate driver registration error - "+ 60 | "got %v, want %v", err, walletdb.ErrDbTypeRegistered) 61 | } 62 | 63 | tempDir := t.TempDir() 64 | 65 | dbPath := filepath.Join(tempDir, "db") 66 | db, err := walletdb.Create( 67 | dbType, dbPath, true, defaultDBTimeout, false, 68 | ) 69 | if err != nil { 70 | t.Errorf("failed to create database: %v", err) 71 | return 72 | } 73 | db.Close() 74 | 75 | } 76 | 77 | // TestCreateOpenFail ensures that errors which occur while opening or closing 78 | // a database are handled properly. 79 | func TestCreateOpenFail(t *testing.T) { 80 | // bogusCreateDB is a function which acts as a bogus create and open 81 | // driver function that intentionally returns a failure which can be 82 | // detected. 83 | dbType := "createopenfail" 84 | openError := fmt.Errorf("failed to create or open database for "+ 85 | "database type [%v]", dbType) 86 | bogusCreateDB := func(args ...interface{}) (walletdb.DB, error) { 87 | return nil, openError 88 | } 89 | 90 | // Create and add driver that intentionally fails when created or opened 91 | // to ensure errors on database open and create are handled properly. 92 | driver := walletdb.Driver{ 93 | DbType: dbType, 94 | Create: bogusCreateDB, 95 | Open: bogusCreateDB, 96 | } 97 | walletdb.RegisterDriver(driver) 98 | 99 | // Ensure creating a database with the new type fails with the expected 100 | // error. 101 | _, err := walletdb.Create(dbType) 102 | if err != openError { 103 | t.Errorf("expected error not received - got: %v, want %v", err, 104 | openError) 105 | return 106 | } 107 | 108 | // Ensure opening a database with the new type fails with the expected 109 | // error. 110 | _, err = walletdb.Open(dbType) 111 | if err != openError { 112 | t.Errorf("expected error not received - got: %v, want %v", err, 113 | openError) 114 | return 115 | } 116 | } 117 | 118 | // TestCreateOpenUnsupported ensures that attempting to create or open an 119 | // unsupported database type is handled properly. 120 | func TestCreateOpenUnsupported(t *testing.T) { 121 | // Ensure creating a database with an unsupported type fails with the 122 | // expected error. 123 | dbType := "unsupported" 124 | _, err := walletdb.Create(dbType) 125 | if err != walletdb.ErrDbUnknownType { 126 | t.Errorf("expected error not received - got: %v, want %v", err, 127 | walletdb.ErrDbUnknownType) 128 | return 129 | } 130 | 131 | // Ensure opening a database with the an unsupported type fails with the 132 | // expected error. 133 | _, err = walletdb.Open(dbType) 134 | if err != walletdb.ErrDbUnknownType { 135 | t.Errorf("expected error not received - got: %v, want %v", err, 136 | walletdb.ErrDbUnknownType) 137 | return 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | pull_request: 8 | branches: 9 | - "*" 10 | concurrency: 11 | # Cancel any previous workflows if they are from a PR or push. 12 | group: ${{ github.event.pull_request.number || github.ref }} 13 | cancel-in-progress: true 14 | 15 | defaults: 16 | run: 17 | shell: bash 18 | 19 | env: 20 | # go needs absolute directories, using the $HOME variable doesn't work here. 21 | GOCACHE: /home/runner/work/go/pkg/build 22 | GOPATH: /home/runner/work/go 23 | GOBIN: /home/runner/work/go/bin 24 | GO111MODULE: on 25 | 26 | # If you change this please run `make lint` to see where else it needs to be 27 | # updated as well. 28 | GO_VERSION: 1.24.6 29 | 30 | BITCOIND_VERSION: '22.0' 31 | BITCOIND_IMAGE: 'lightninglabs/bitcoin-core' 32 | 33 | jobs: 34 | ######################## 35 | # Format, compileation and lint check 36 | ######################## 37 | lint-check: 38 | name: Format, compilation and lint check 39 | runs-on: ubuntu-latest 40 | steps: 41 | - name: git checkout 42 | uses: actions/checkout@v5 43 | with: 44 | fetch-depth: 0 45 | 46 | - name: Clean up runner space 47 | uses: ./.github/actions/cleanup-space 48 | 49 | - name: setup go ${{ env.GO_VERSION }} 50 | uses: actions/setup-go@v5 51 | with: 52 | go-version: '${{ env.GO_VERSION }}' 53 | 54 | - name: Check default values in sample-btcwallet.conf file 55 | run: make sample-conf-check 56 | 57 | - name: Check code format 58 | run: make fmt-check 59 | 60 | - name: Check go modules tidiness 61 | run: make tidy-module-check 62 | 63 | - name: Check RPC format 64 | run: make rpc-check 65 | 66 | - name: compile code 67 | run: go install -v ./... 68 | 69 | - name: Lint proto files 70 | run: make protolint 71 | 72 | - name: run lint 73 | run: make lint-check 74 | 75 | check-commits: 76 | if: github.event_name == 'pull_request' 77 | name: Check commits 78 | runs-on: ubuntu-latest 79 | steps: 80 | - name: git checkout 81 | uses: actions/checkout@v5 82 | with: 83 | fetch-depth: 0 84 | 85 | - name: Clean up runner space 86 | uses: ./.github/actions/cleanup-space 87 | 88 | - name: setup go ${{ env.GO_VERSION }} 89 | uses: actions/setup-go@v5 90 | with: 91 | go-version: '${{ env.GO_VERSION }}' 92 | 93 | - name: fetch and rebase on ${{ github.base_ref }} 94 | uses: ./.github/actions/rebase 95 | 96 | - name: check commits 97 | run: scripts/check-each-commit.sh upstream/${{ github.base_ref }} 98 | 99 | ######################## 100 | # run unit tests 101 | ######################## 102 | unit-test: 103 | name: run unit tests 104 | runs-on: ubuntu-latest 105 | strategy: 106 | # Allow other tests in the matrix to continue if one fails. 107 | fail-fast: false 108 | matrix: 109 | unit_type: 110 | - unit-race 111 | - unit-cover 112 | steps: 113 | - name: extract bitcoind from docker image 114 | run: |- 115 | docker pull ${{ env.BITCOIND_IMAGE }}:${{ env.BITCOIND_VERSION }} 116 | CONTAINER_ID=$(docker create ${{ env.BITCOIND_IMAGE }}:${{ env.BITCOIND_VERSION }}) 117 | sudo docker cp $CONTAINER_ID:/opt/bitcoin-${{ env.BITCOIND_VERSION }}/bin/bitcoind /usr/local/bin/bitcoind 118 | docker rm $CONTAINER_ID 119 | 120 | - name: git checkout 121 | uses: actions/checkout@v5 122 | 123 | - name: Clean up runner space 124 | uses: ./.github/actions/cleanup-space 125 | 126 | - name: go cache 127 | uses: actions/cache@v4 128 | with: 129 | path: /home/runner/work/go 130 | key: btcwallet-${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ github.job }}-${{ hashFiles('**/go.sum') }} 131 | restore-keys: | 132 | btcwallet-${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ github.job }}-${{ hashFiles('**/go.sum') }} 133 | btcwallet-${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ github.job }}- 134 | btcwallet-${{ runner.os }}-go-${{ env.GO_VERSION }}- 135 | btcwallet-${{ runner.os }}-go- 136 | 137 | - name: setup go ${{ env.GO_VERSION }} 138 | uses: actions/setup-go@v5 139 | with: 140 | go-version: '${{ env.GO_VERSION }}' 141 | 142 | - name: run ${{ matrix.unit_type }} 143 | run: make ${{ matrix.unit_type }} 144 | 145 | - name: Send coverage 146 | uses: shogo82148/actions-goveralls@v1 147 | if: matrix.unit_type == 'unit-cover' 148 | continue-on-error: true 149 | with: 150 | path-to-profile: coverage.txt 151 | parallel: true 152 | -------------------------------------------------------------------------------- /wallet/signer.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package wallet 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/btcsuite/btcd/btcec/v2" 11 | "github.com/btcsuite/btcd/btcutil" 12 | "github.com/btcsuite/btcd/txscript" 13 | "github.com/btcsuite/btcd/wire" 14 | "github.com/btcsuite/btcwallet/waddrmgr" 15 | ) 16 | 17 | // ScriptForOutput returns the address, witness program and redeem script for a 18 | // given UTXO. An error is returned if the UTXO does not belong to our wallet or 19 | // it is not a managed pubKey address. 20 | func (w *Wallet) ScriptForOutput(output *wire.TxOut) ( 21 | waddrmgr.ManagedPubKeyAddress, []byte, []byte, error) { 22 | 23 | // First make sure we can sign for the input by making sure the script 24 | // in the UTXO belongs to our wallet and we have the private key for it. 25 | walletAddr, err := w.fetchOutputAddr(output.PkScript) 26 | if err != nil { 27 | return nil, nil, nil, err 28 | } 29 | 30 | pubKeyAddr, ok := walletAddr.(waddrmgr.ManagedPubKeyAddress) 31 | if !ok { 32 | return nil, nil, nil, fmt.Errorf("address %s is not a "+ 33 | "p2wkh or np2wkh address", walletAddr.Address()) 34 | } 35 | 36 | var ( 37 | witnessProgram []byte 38 | sigScript []byte 39 | ) 40 | 41 | switch { 42 | // If we're spending p2wkh output nested within a p2sh output, then 43 | // we'll need to attach a sigScript in addition to witness data. 44 | case walletAddr.AddrType() == waddrmgr.NestedWitnessPubKey: 45 | pubKey := pubKeyAddr.PubKey() 46 | pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed()) 47 | 48 | // Next, we'll generate a valid sigScript that will allow us to 49 | // spend the p2sh output. The sigScript will contain only a 50 | // single push of the p2wkh witness program corresponding to 51 | // the matching public key of this address. 52 | p2wkhAddr, err := btcutil.NewAddressWitnessPubKeyHash( 53 | pubKeyHash, w.chainParams, 54 | ) 55 | if err != nil { 56 | return nil, nil, nil, err 57 | } 58 | witnessProgram, err = txscript.PayToAddrScript(p2wkhAddr) 59 | if err != nil { 60 | return nil, nil, nil, err 61 | } 62 | 63 | bldr := txscript.NewScriptBuilder() 64 | bldr.AddData(witnessProgram) 65 | sigScript, err = bldr.Script() 66 | if err != nil { 67 | return nil, nil, nil, err 68 | } 69 | 70 | // Otherwise, this is a regular p2wkh or p2tr output, so we include the 71 | // witness program itself as the subscript to generate the proper 72 | // sighash digest. As part of the new sighash digest algorithm, the 73 | // p2wkh witness program will be expanded into a regular p2kh 74 | // script. 75 | default: 76 | witnessProgram = output.PkScript 77 | } 78 | 79 | return pubKeyAddr, witnessProgram, sigScript, nil 80 | } 81 | 82 | // PrivKeyTweaker is a function type that can be used to pass in a callback for 83 | // tweaking a private key before it's used to sign an input. 84 | type PrivKeyTweaker func(*btcec.PrivateKey) (*btcec.PrivateKey, error) 85 | 86 | // ComputeInputScript generates a complete InputScript for the passed 87 | // transaction with the signature as defined within the passed SignDescriptor. 88 | // This method is capable of generating the proper input script for both 89 | // regular p2wkh output and p2wkh outputs nested within a regular p2sh output. 90 | func (w *Wallet) ComputeInputScript(tx *wire.MsgTx, output *wire.TxOut, 91 | inputIndex int, sigHashes *txscript.TxSigHashes, 92 | hashType txscript.SigHashType, tweaker PrivKeyTweaker) (wire.TxWitness, 93 | []byte, error) { 94 | 95 | walletAddr, witnessProgram, sigScript, err := w.ScriptForOutput(output) 96 | if err != nil { 97 | return nil, nil, err 98 | } 99 | 100 | privKey, err := walletAddr.PrivKey() 101 | if err != nil { 102 | return nil, nil, err 103 | } 104 | 105 | // If we need to maybe tweak our private key, do it now. 106 | if tweaker != nil { 107 | privKey, err = tweaker(privKey) 108 | if err != nil { 109 | return nil, nil, err 110 | } 111 | } 112 | 113 | // We need to produce a Schnorr signature for p2tr key spend addresses. 114 | if txscript.IsPayToTaproot(output.PkScript) { 115 | // We can now generate a valid witness which will allow us to 116 | // spend this output. 117 | witnessScript, err := txscript.TaprootWitnessSignature( 118 | tx, sigHashes, inputIndex, output.Value, 119 | output.PkScript, hashType, privKey, 120 | ) 121 | if err != nil { 122 | return nil, nil, err 123 | } 124 | 125 | return witnessScript, nil, nil 126 | } 127 | 128 | // Generate a valid witness stack for the input. 129 | witnessScript, err := txscript.WitnessSignature( 130 | tx, sigHashes, inputIndex, output.Value, witnessProgram, 131 | hashType, privKey, true, 132 | ) 133 | if err != nil { 134 | return nil, nil, err 135 | } 136 | 137 | return witnessScript, sigScript, nil 138 | } 139 | -------------------------------------------------------------------------------- /wallet/history.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2020 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package wallet 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/btcsuite/btcd/chaincfg/chainhash" 11 | "github.com/btcsuite/btcwallet/waddrmgr" 12 | "github.com/btcsuite/btcwallet/walletdb" 13 | "github.com/btcsuite/btcwallet/wtxmgr" 14 | ) 15 | 16 | var ( 17 | // bucketTxLabels is the name of the label sub bucket of the wtxmgr 18 | // top level bucket that stores the mapping between a txid and a 19 | // user-defined transaction label. 20 | bucketTxLabels = []byte("l") 21 | ) 22 | 23 | // DropTransactionHistory completely removes and re-creates the transaction 24 | // manager namespace from the given wallet database. This can be used to force 25 | // a full chain rescan of all wallet transaction and UTXO data. User-defined 26 | // transaction labels can optionally be kept by setting keepLabels to true. 27 | func DropTransactionHistory(db walletdb.DB, keepLabels bool) error { 28 | log.Infof("Dropping btcwallet transaction history") 29 | 30 | err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { 31 | // If we want to keep our tx labels, we read them out so we 32 | // can re-add them after we have deleted our wtxmgr. 33 | var ( 34 | labels map[chainhash.Hash]string 35 | err error 36 | ) 37 | if keepLabels { 38 | labels, err = fetchAllLabels(tx) 39 | if err != nil { 40 | return err 41 | } 42 | } 43 | 44 | err = tx.DeleteTopLevelBucket(wtxmgrNamespaceKey) 45 | if err != nil && err != walletdb.ErrBucketNotFound { 46 | return err 47 | } 48 | ns, err := tx.CreateTopLevelBucket(wtxmgrNamespaceKey) 49 | if err != nil { 50 | return err 51 | } 52 | err = wtxmgr.Create(ns) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | // If we want to re-add our labels, we do so now. 58 | if keepLabels { 59 | if err := putTxLabels(ns, labels); err != nil { 60 | return err 61 | } 62 | } 63 | 64 | ns = tx.ReadWriteBucket(waddrmgrNamespaceKey) 65 | birthdayBlock, err := waddrmgr.FetchBirthdayBlock(ns) 66 | if err != nil { 67 | log.Warnf("Wallet does not have a birthday block " + 68 | "set, falling back to rescan from genesis") 69 | 70 | startBlock, err := waddrmgr.FetchStartBlock(ns) 71 | if err != nil { 72 | return err 73 | } 74 | return waddrmgr.PutSyncedTo(ns, startBlock) 75 | } 76 | 77 | // We'll need to remove our birthday block first because it 78 | // serves as a barrier when updating our state to detect reorgs 79 | // due to the wallet not storing all block hashes of the chain. 80 | if err := waddrmgr.DeleteBirthdayBlock(ns); err != nil { 81 | return err 82 | } 83 | 84 | if err := waddrmgr.PutSyncedTo(ns, &birthdayBlock); err != nil { 85 | return err 86 | } 87 | return waddrmgr.PutBirthdayBlock(ns, birthdayBlock) 88 | }) 89 | if err != nil { 90 | return fmt.Errorf("failed to drop and re-create namespace: %w", 91 | err) 92 | } 93 | 94 | return nil 95 | } 96 | 97 | // fetchAllLabels returns a map of hex-encoded txid to label. 98 | func fetchAllLabels(tx walletdb.ReadWriteTx) (map[chainhash.Hash]string, 99 | error) { 100 | 101 | // Get our top level bucket, if it does not exist we just exit. 102 | txBucket := tx.ReadBucket(wtxmgrNamespaceKey) 103 | if txBucket == nil { 104 | return nil, nil 105 | } 106 | 107 | // If we do not have a labels bucket, there are no labels so we exit. 108 | labelsBucket := txBucket.NestedReadBucket(bucketTxLabels) 109 | if labelsBucket == nil { 110 | return nil, nil 111 | } 112 | 113 | labels := make(map[chainhash.Hash]string) 114 | if err := labelsBucket.ForEach(func(k, v []byte) error { 115 | txid, err := chainhash.NewHash(k) 116 | if err != nil { 117 | return err 118 | } 119 | 120 | label, err := wtxmgr.DeserializeLabel(v) 121 | if err != nil { 122 | return err 123 | } 124 | 125 | // Add an entry to our map of labels. 126 | labels[*txid] = label 127 | 128 | return nil 129 | }); err != nil { 130 | return nil, err 131 | } 132 | 133 | return labels, nil 134 | } 135 | 136 | // putTxLabels re-adds a nested labels bucket and entries to the bucket provided 137 | // if there are any labels present. 138 | func putTxLabels(ns walletdb.ReadWriteBucket, 139 | labels map[chainhash.Hash]string) error { 140 | 141 | // If there are no labels, exit early. 142 | if len(labels) == 0 { 143 | return nil 144 | } 145 | 146 | // First, we create a labels bucket which we will add all labels to. 147 | labelBucket, err := ns.CreateBucketIfNotExists(bucketTxLabels) 148 | if err != nil { 149 | return err 150 | } 151 | 152 | // Next, we re-add every label to the bucket. 153 | for txid, label := range labels { 154 | err := wtxmgr.PutTxLabel(labelBucket, txid, label) 155 | if err != nil { 156 | return err 157 | } 158 | } 159 | 160 | return nil 161 | } 162 | -------------------------------------------------------------------------------- /log.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2017 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "io" 10 | "os" 11 | "path/filepath" 12 | 13 | "github.com/btcsuite/btcd/rpcclient" 14 | "github.com/btcsuite/btclog" 15 | "github.com/btcsuite/btcwallet/chain" 16 | "github.com/btcsuite/btcwallet/rpc/legacyrpc" 17 | "github.com/btcsuite/btcwallet/rpc/rpcserver" 18 | "github.com/btcsuite/btcwallet/waddrmgr" 19 | "github.com/btcsuite/btcwallet/wallet" 20 | "github.com/btcsuite/btcwallet/wtxmgr" 21 | "github.com/jrick/logrotate/rotator" 22 | "github.com/lightninglabs/neutrino" 23 | ) 24 | 25 | // logWriter implements an io.Writer that outputs to both standard output and 26 | // the write-end pipe of an initialized log rotator. 27 | type logWriter struct{} 28 | 29 | func (logWriter) Write(p []byte) (n int, err error) { 30 | _, _ = os.Stdout.Write(p) 31 | _, _ = logRotatorPipe.Write(p) 32 | return len(p), nil 33 | } 34 | 35 | // Loggers per subsystem. A single backend logger is created and all subsytem 36 | // loggers created from it will write to the backend. When adding new 37 | // subsystems, add the subsystem logger variable here and to the 38 | // subsystemLoggers map. 39 | // 40 | // Loggers can not be used before the log rotator has been initialized with a 41 | // log file. This must be performed early during application startup by calling 42 | // initLogRotator. 43 | var ( 44 | // backendLog is the logging backend used to create all subsystem loggers. 45 | // The backend must not be used before the log rotator has been initialized, 46 | // or data races and/or nil pointer dereferences will occur. 47 | backendLog = btclog.NewBackend(logWriter{}) 48 | 49 | // logRotator is one of the logging outputs. It should be closed on 50 | // application shutdown. 51 | logRotator *rotator.Rotator 52 | 53 | // logRotatorPipe is the write-end pipe for writing to the log rotator. It 54 | // is written to by the Write method of the logWriter type. 55 | logRotatorPipe *io.PipeWriter 56 | 57 | log = backendLog.Logger("BTCW") 58 | walletLog = backendLog.Logger("BTWL") 59 | waddrmgrLog = backendLog.Logger("AMGR") 60 | txmgrLog = backendLog.Logger("TMGR") 61 | chainLog = backendLog.Logger("CHIO") 62 | grpcLog = backendLog.Logger("GRPC") 63 | legacyRPCLog = backendLog.Logger("RPCS") 64 | btcnLog = backendLog.Logger("BTCN") 65 | ) 66 | 67 | // Initialize package-global logger variables. 68 | func init() { 69 | wallet.UseLogger(walletLog) 70 | waddrmgr.UseLogger(waddrmgrLog) 71 | wtxmgr.UseLogger(txmgrLog) 72 | chain.UseLogger(chainLog) 73 | rpcclient.UseLogger(chainLog) 74 | rpcserver.UseLogger(grpcLog) 75 | legacyrpc.UseLogger(legacyRPCLog) 76 | neutrino.UseLogger(btcnLog) 77 | } 78 | 79 | // subsystemLoggers maps each subsystem identifier to its associated logger. 80 | var subsystemLoggers = map[string]btclog.Logger{ 81 | "BTCW": log, 82 | "BTWL": walletLog, 83 | "AMGR": waddrmgrLog, 84 | "TMGR": txmgrLog, 85 | "CHIO": chainLog, 86 | "GRPC": grpcLog, 87 | "RPCS": legacyRPCLog, 88 | "BTCN": btcnLog, 89 | } 90 | 91 | // initLogRotator initializes the logging rotater to write logs to logFile and 92 | // create roll files in the same directory. It must be called before the 93 | // package-global log rotater variables are used. 94 | func initLogRotator(logFile string) { 95 | logDir, _ := filepath.Split(logFile) 96 | err := os.MkdirAll(logDir, 0700) 97 | if err != nil { 98 | fmt.Fprintf(os.Stderr, "failed to create log directory: %v\n", err) 99 | os.Exit(1) 100 | } 101 | r, err := rotator.New(logFile, 10*1024, false, 3) 102 | if err != nil { 103 | fmt.Fprintf(os.Stderr, "failed to create file rotator: %v\n", err) 104 | os.Exit(1) 105 | } 106 | 107 | pr, pw := io.Pipe() 108 | go func() { _ = r.Run(pr) }() 109 | 110 | logRotator = r 111 | logRotatorPipe = pw 112 | } 113 | 114 | // setLogLevel sets the logging level for provided subsystem. Invalid 115 | // subsystems are ignored. Uninitialized subsystems are dynamically created as 116 | // needed. 117 | func setLogLevel(subsystemID string, logLevel string) { 118 | // Ignore invalid subsystems. 119 | logger, ok := subsystemLoggers[subsystemID] 120 | if !ok { 121 | return 122 | } 123 | 124 | // Defaults to info if the log level is invalid. 125 | level, _ := btclog.LevelFromString(logLevel) 126 | logger.SetLevel(level) 127 | } 128 | 129 | // setLogLevels sets the log level for all subsystem loggers to the passed 130 | // level. It also dynamically creates the subsystem loggers as needed, so it 131 | // can be used to initialize the logging system. 132 | func setLogLevels(logLevel string) { 133 | // Configure all sub-systems with the new logging level. Dynamically 134 | // create loggers as needed. 135 | for subsystemID := range subsystemLoggers { 136 | setLogLevel(subsystemID, logLevel) 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /waddrmgr/sync.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package waddrmgr 6 | 7 | import ( 8 | "time" 9 | 10 | "github.com/btcsuite/btcd/chaincfg/chainhash" 11 | "github.com/btcsuite/btcwallet/walletdb" 12 | ) 13 | 14 | // BlockStamp defines a block (by height and a unique hash) and is used to mark 15 | // a point in the blockchain that an address manager element is 16 | // synced to. 17 | type BlockStamp struct { 18 | Height int32 19 | Hash chainhash.Hash 20 | Timestamp time.Time 21 | } 22 | 23 | // syncState houses the sync state of the manager. It consists of the recently 24 | // seen blocks as height, as well as the start and current sync block stamps. 25 | type syncState struct { 26 | // startBlock is the first block that can be safely used to start a 27 | // rescan. It is either the block the manager was created with, or the 28 | // earliest block provided with imported addresses or scripts. 29 | startBlock BlockStamp 30 | 31 | // syncedTo is the current block the addresses in the manager are known 32 | // to be synced against. 33 | syncedTo BlockStamp 34 | } 35 | 36 | // newSyncState returns a new sync state with the provided parameters. 37 | func newSyncState(startBlock, syncedTo *BlockStamp) *syncState { 38 | 39 | return &syncState{ 40 | startBlock: *startBlock, 41 | syncedTo: *syncedTo, 42 | } 43 | } 44 | 45 | // SetSyncedTo marks the address manager to be in sync with the recently-seen 46 | // block described by the blockstamp. When the provided blockstamp is nil, the 47 | // oldest blockstamp of the block the manager was created at and of all 48 | // imported addresses will be used. This effectively allows the manager to be 49 | // marked as unsynced back to the oldest known point any of the addresses have 50 | // appeared in the block chain. 51 | func (m *Manager) SetSyncedTo(ns walletdb.ReadWriteBucket, bs *BlockStamp) error { 52 | m.mtx.Lock() 53 | defer m.mtx.Unlock() 54 | 55 | // Use the stored start blockstamp and reset recent hashes and height 56 | // when the provided blockstamp is nil. 57 | if bs == nil { 58 | bs = &m.syncState.startBlock 59 | } 60 | 61 | // Update the database. 62 | err := PutSyncedTo(ns, bs) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | // Update memory now that the database is updated. 68 | m.syncState.syncedTo = *bs 69 | return nil 70 | } 71 | 72 | // SyncedTo returns details about the block height and hash that the address 73 | // manager is synced through at the very least. The intention is that callers 74 | // can use this information for intelligently initiating rescans to sync back to 75 | // the best chain from the last known good block. 76 | func (m *Manager) SyncedTo() BlockStamp { 77 | m.mtx.RLock() 78 | defer m.mtx.RUnlock() 79 | 80 | return m.syncState.syncedTo 81 | } 82 | 83 | // BlockHash returns the block hash at a particular block height. This 84 | // information is useful for comparing against the chain back-end to see if a 85 | // reorg is taking place and how far back it goes. 86 | func (m *Manager) BlockHash(ns walletdb.ReadBucket, height int32) ( 87 | *chainhash.Hash, error) { 88 | 89 | return fetchBlockHash(ns, height) 90 | } 91 | 92 | // Birthday returns the birthday, or earliest time a key could have been used, 93 | // for the manager. 94 | func (m *Manager) Birthday() time.Time { 95 | m.mtx.RLock() 96 | defer m.mtx.RUnlock() 97 | 98 | return m.birthday 99 | } 100 | 101 | // SetBirthday sets the birthday, or earliest time a key could have been used, 102 | // for the manager. 103 | func (m *Manager) SetBirthday(ns walletdb.ReadWriteBucket, 104 | birthday time.Time) error { 105 | m.mtx.Lock() 106 | defer m.mtx.Unlock() 107 | 108 | m.birthday = birthday 109 | return putBirthday(ns, birthday) 110 | } 111 | 112 | // BirthdayBlock returns the birthday block, or earliest block a key could have 113 | // been used, for the manager. A boolean is also returned to indicate whether 114 | // the birthday block has been verified as correct. 115 | func (m *Manager) BirthdayBlock(ns walletdb.ReadBucket) (BlockStamp, bool, error) { 116 | birthdayBlock, err := FetchBirthdayBlock(ns) 117 | if err != nil { 118 | return BlockStamp{}, false, err 119 | } 120 | 121 | return birthdayBlock, fetchBirthdayBlockVerification(ns), nil 122 | } 123 | 124 | // SetBirthdayBlock sets the birthday block, or earliest time a key could have 125 | // been used, for the manager. The verified boolean can be used to specify 126 | // whether this birthday block should be sanity checked to determine if there 127 | // exists a better candidate to prevent less block fetching. 128 | func (m *Manager) SetBirthdayBlock(ns walletdb.ReadWriteBucket, 129 | block BlockStamp, verified bool) error { 130 | 131 | if err := PutBirthdayBlock(ns, block); err != nil { 132 | return err 133 | } 134 | return putBirthdayBlockVerification(ns, verified) 135 | } 136 | --------------------------------------------------------------------------------