├── .gitignore ├── rpc ├── regen.sh ├── legacyrpc │ ├── config.go │ ├── log.go │ ├── rpcserver_test.go │ ├── errors.go │ └── rpchelp_test.go ├── documentation │ ├── README.md │ └── serverchanges.md └── rpcserver │ └── log.go ├── 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 ├── docs ├── README.md └── force_rescans.md ├── wallet ├── rand.go ├── txrules │ ├── go.mod │ └── rules.go ├── doc.go ├── txauthor │ ├── go.mod │ └── cprng.go ├── txsizes │ └── go.mod ├── disksync.go ├── watchingonly_test.go ├── README.md ├── log.go ├── unstable.go ├── mock.go ├── example_test.go ├── utxos_test.go ├── signer_test.go ├── common.go ├── multisig.go ├── signer.go └── history.go ├── walletdb ├── go.mod ├── cov_report.sh ├── walletdbtest │ ├── doc.go │ └── tester.go ├── go.sum ├── bdb │ ├── doc.go │ ├── interface_test.go │ ├── README.md │ └── driver.go ├── migration │ ├── log.go │ └── manager.go ├── test_coverage.txt ├── README.md ├── error.go ├── db_test.go └── doc.go ├── params.go ├── wtxmgr ├── go.mod ├── log.go ├── README.md ├── doc.go ├── kahnsort.go ├── error.go ├── migrations.go └── kahnsort_test.go ├── signalsigterm.go ├── waddrmgr ├── cov_report.sh ├── log.go ├── README.md ├── tapscript_test.go ├── db_test.go ├── tapscript.go ├── error_test.go ├── sync.go └── tlv_test.go ├── LICENSE ├── .golangci.yml ├── chain ├── log.go ├── queue.go └── interface.go ├── deps.txt ├── go.mod ├── goclean.sh ├── netparams └── params.go ├── signal.go ├── version.go ├── snacl └── snacl_test.go ├── cmd └── dropwtxmgr │ └── main.go ├── .github └── workflows │ └── main.yml ├── Makefile ├── log.go └── sample-btcwallet.conf /.gitignore: -------------------------------------------------------------------------------- 1 | btcwallet 2 | vendor 3 | .idea 4 | coverage.txt 5 | -------------------------------------------------------------------------------- /rpc/regen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | protoc -I. api.proto --go_out=plugins=grpc:walletrpc 4 | -------------------------------------------------------------------------------- /internal/zero/doc.go: -------------------------------------------------------------------------------- 1 | // Package zero contains functions to clear data from byte slices and 2 | // multi-precision integers. 3 | package zero 4 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ### Guides 2 | 3 | [Rebuilding all transaction history with forced rescans](https://github.com/btcsuite/btcwallet/tree/master/docs/force_rescans.md) 4 | -------------------------------------------------------------------------------- /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/txrules/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/btcsuite/btcwallet/wallet/txrules 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/btcsuite/btcd v0.22.0-beta.0.20220204213055-eaf0459ff879 7 | github.com/btcsuite/btcd/btcutil v1.1.0 8 | ) 9 | -------------------------------------------------------------------------------- /walletdb/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/btcsuite/btcwallet/walletdb 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f 7 | github.com/davecgh/go-spew v1.1.1 8 | go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50 9 | ) 10 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | */ 12 | package wallet 13 | -------------------------------------------------------------------------------- /wallet/txauthor/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/btcsuite/btcwallet/wallet/txauthor 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/btcsuite/btcd v0.22.0-beta.0.20220316175102-8d5c75c28923 7 | github.com/btcsuite/btcd/btcutil v1.1.1 8 | github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 9 | github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0 10 | ) 11 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /wtxmgr/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/btcsuite/btcwallet/wtxmgr 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/btcsuite/btcd v0.22.0-beta.0.20220204213055-eaf0459ff879 7 | github.com/btcsuite/btcd/btcutil v1.1.0 8 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f 9 | github.com/btcsuite/btcwallet/walletdb v1.3.5 10 | github.com/lightningnetwork/lnd/clock v1.0.1 11 | github.com/stretchr/testify v1.5.1 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 "github.com/btcsuite/btclog" 8 | 9 | var log = btclog.Disabled 10 | 11 | // UseLogger sets the package-wide logger. Any calls to this function must be 12 | // made before a server is created and used (it is not concurrent safe). 13 | func UseLogger(logger btclog.Logger) { 14 | log = logger 15 | } 16 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/txsizes/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/btcsuite/btcwallet/wallet/txsizes 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3 7 | github.com/btcsuite/goleveldb v1.0.0 // indirect 8 | github.com/davecgh/go-spew v1.1.1 // indirect 9 | github.com/kr/pretty v0.1.0 // indirect 10 | golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 // indirect 11 | golang.org/x/net v0.0.0-20190206173232-65e2d4e15006 // indirect 12 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect 13 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd // indirect 14 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 // indirect 15 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect 16 | gopkg.in/yaml.v2 v2.2.2 // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /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 | go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50 h1:ASw9n1EHMftwnP3Az4XW6e308+gNsrHzmdhd0Olz9Hs= 6 | go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= 7 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0= 8 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 9 | -------------------------------------------------------------------------------- /waddrmgr/log.go: -------------------------------------------------------------------------------- 1 | package waddrmgr 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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: %s", err) 20 | } 21 | } else { 22 | return fmt.Errorf("error checking directory: %s", 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 | -------------------------------------------------------------------------------- /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, and a timeout value for opening the database as a 15 | time.Duration: 16 | 17 | db, err := walletdb.Open("bdb", "path/to/database.db", true, 60*time.Second) 18 | if err != nil { 19 | // Handle error 20 | } 21 | 22 | db, err := walletdb.Create("bdb", "path/to/database.db", true, 60*time.Second) 23 | if err != nil { 24 | // Handle error 25 | } 26 | */ 27 | package bdb 28 | -------------------------------------------------------------------------------- /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/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 | "io/ioutil" 9 | "os" 10 | "testing" 11 | "time" 12 | 13 | "github.com/btcsuite/btcd/chaincfg" 14 | _ "github.com/btcsuite/btcwallet/walletdb/bdb" 15 | ) 16 | 17 | // TestCreateWatchingOnly checks that we can construct a watching-only 18 | // wallet. 19 | func TestCreateWatchingOnly(t *testing.T) { 20 | // Set up a wallet. 21 | dir, err := ioutil.TempDir("", "watchingonly_test") 22 | if err != nil { 23 | t.Fatalf("Failed to create db dir: %v", err) 24 | } 25 | defer os.RemoveAll(dir) 26 | 27 | pubPass := []byte("hello") 28 | 29 | loader := NewLoader( 30 | &chaincfg.TestNet3Params, dir, true, defaultDBTimeout, 250, 31 | ) 32 | _, err = loader.CreateNewWatchingOnlyWallet(pubPass, time.Now()) 33 | if err != nil { 34 | t.Fatalf("unable to create wallet: %v", err) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | # timeout for analysis 3 | deadline: 10m 4 | 5 | linters-settings: 6 | govet: 7 | # Don't report about shadowed variables 8 | check-shadowing: false 9 | gofmt: 10 | # simplify code: gofmt with `-s` option, true by default 11 | simplify: true 12 | 13 | linters: 14 | disable-all: true 15 | enable: 16 | - asciicheck 17 | - deadcode 18 | - dupl 19 | - errcheck 20 | - goimports 21 | - gosec 22 | - gosimple 23 | - govet 24 | - ineffassign 25 | - nolintlint 26 | - prealloc 27 | - staticcheck 28 | - structcheck 29 | - typecheck 30 | - unconvert 31 | - unused 32 | - varcheck 33 | 34 | # Others to consider that do not pass presently: 35 | # - nilerr 36 | # - makezero 37 | 38 | issues: 39 | exclude-rules: 40 | # Exclude gosec from running for tests so that tests with weak randomness 41 | # (math/rand) will pass the linter. 42 | - path: _test\.go 43 | linters: 44 | - gosec 45 | - errcheck 46 | - dupl 47 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/lightninglabs/neutrino/query" 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 | DisableLog() 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 | log = 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 | query.UseLogger(logger) 34 | } 35 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /deps.txt: -------------------------------------------------------------------------------- 1 | Dependency Commits 2 | ================== 3 | 4 | btcwallet 0.7.0 Alpha 5 | ---------------- 6 | bolt 1139dd23c5f9d1a28096b511959044b29364a3fe 7 | btcd cea5d3c1cc16eb07a1686c557db23004655ae11b 8 | btclog 5005b7240f310ae8f01c7664a3954d280241eb2b 9 | btcrpcclient b81555beeac8eda71e8150cc9d63631aaa756965 10 | btcutil ff82dacded1c76d101bce55c394c03c0bbff69e8 11 | fastsha256 302ad4db268b46f9ebda3078f6f7397f96047735 12 | go-flags 6c288d648c1cc1befcb90cb5511dcacf64ae8e61 13 | go-socks cfe8b59e565c1a5bd4e2005d77cd9aa8b2e14524 14 | golangcrypto 53f62d9b43e87a6c56975cf862af7edf33a8d0df 15 | seelog 313961b101eb55f65ae0f03ddd4e322731763b6c 16 | websocket 31079b6807923eb23992c421b114992b95131b55 17 | 18 | btcwallet 0.1.0 Alpha 19 | ---------------- 20 | btcd 3108b944017b14a3c5863ed1401f1a2471907d84 21 | btcec a97fd5fe2c670030f8d77dc13b9fa8401ef9f349 22 | btcjson d20f958c92e1444d83215c3cf98d6eef41898dcb 23 | btcscript f4a6449ad3b90d0c830bf2895b83ced8d5fb91e9 24 | btcutil aa811871654079f5036d3692dcf6c66928d19447 25 | btcwire dd41f7e91a682b7c1ceed633e12ece6ba7b6bc72 26 | btcws 497f1770445677372557d70621782d921a5318e3 27 | go-flags fa177a84d3b73bf7e4b79125b2a963bc134eff77 28 | seelog 6b91ad56123bb473755caa213db2bde5422177bf 29 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | "io/ioutil" 17 | "os" 18 | "path/filepath" 19 | "testing" 20 | 21 | "github.com/btcsuite/btcwallet/walletdb/walletdbtest" 22 | ) 23 | 24 | // TestInterface performs all interfaces tests for this database driver. 25 | func TestInterface(t *testing.T) { 26 | tempDir, err := ioutil.TempDir("", "interfacetest") 27 | if err != nil { 28 | t.Errorf("unable to create temp dir: %v", err) 29 | return 30 | } 31 | defer os.Remove(tempDir) 32 | 33 | dbPath := filepath.Join(tempDir, "db") 34 | defer os.RemoveAll(dbPath) 35 | walletdbtest.TestInterface(t, dbType, dbPath, true, defaultDBTimeout) 36 | } 37 | -------------------------------------------------------------------------------- /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/waddrmgr" 10 | "github.com/btcsuite/btcwallet/walletdb/migration" 11 | "github.com/btcsuite/btcwallet/wtxmgr" 12 | ) 13 | 14 | // log is a logger that is initialized with no output filters. This 15 | // means the package will not perform any logging by default until the caller 16 | // requests it. 17 | var log btclog.Logger 18 | 19 | // The default amount of logging is none. 20 | func init() { 21 | DisableLog() 22 | } 23 | 24 | // DisableLog disables all library log output. Logging output is disabled 25 | // by default until either UseLogger or SetLogWriter are called. 26 | func DisableLog() { 27 | UseLogger(btclog.Disabled) 28 | } 29 | 30 | // UseLogger uses a specified Logger to output package logging info. 31 | // This should be used in preference to SetLogWriter if the caller is also 32 | // using btclog. 33 | func UseLogger(logger btclog.Logger) { 34 | log = logger 35 | 36 | migration.UseLogger(logger) 37 | waddrmgr.UseLogger(logger) 38 | wtxmgr.UseLogger(logger) 39 | } 40 | 41 | // pickNoun returns the singular or plural form of a noun depending 42 | // on the count n. 43 | func pickNoun(n int, singular, plural string) string { 44 | if n == 1 { 45 | return singular 46 | } 47 | return plural 48 | } 49 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/btcsuite/btcwallet 2 | 3 | require ( 4 | github.com/btcsuite/btcd v0.23.1 5 | github.com/btcsuite/btcd/btcec/v2 v2.1.3 6 | github.com/btcsuite/btcd/btcutil v1.1.1 7 | github.com/btcsuite/btcd/btcutil/psbt v1.1.4 8 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 9 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f 10 | github.com/btcsuite/btcwallet/wallet/txauthor v1.2.3 11 | github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 12 | github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0 13 | github.com/btcsuite/btcwallet/walletdb v1.4.0 14 | github.com/btcsuite/btcwallet/wtxmgr v1.5.0 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.0.1 18 | github.com/golang/protobuf v1.4.2 19 | github.com/jessevdk/go-flags v1.4.0 20 | github.com/jrick/logrotate v1.0.0 21 | github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec // indirect 22 | github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf 23 | github.com/lightninglabs/neutrino v0.14.2 24 | github.com/lightningnetwork/lnd/ticker v1.0.0 25 | github.com/lightningnetwork/lnd/tlv v1.0.2 26 | github.com/stretchr/testify v1.7.0 27 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 28 | golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc 29 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect 30 | google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922 // indirect 31 | google.golang.org/grpc v1.18.0 32 | ) 33 | 34 | go 1.16 35 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 "github.com/btcsuite/btclog" 8 | 9 | // log is a logger that is initialized with no output filters. This 10 | // means the package will not perform any logging by default until the caller 11 | // requests it. 12 | var log btclog.Logger 13 | 14 | // The default amount of logging is none. 15 | func init() { 16 | DisableLog() 17 | } 18 | 19 | // DisableLog disables all library log output. Logging output is disabled 20 | // by default until either UseLogger or SetLogWriter are called. 21 | func DisableLog() { 22 | UseLogger(btclog.Disabled) 23 | } 24 | 25 | // UseLogger uses a specified Logger to output package logging info. 26 | // This should be used in preference to SetLogWriter if the caller is also 27 | // using btclog. 28 | func UseLogger(logger btclog.Logger) { 29 | log = logger 30 | } 31 | 32 | // LogClosure is a closure that can be printed with %v to be used to 33 | // generate expensive-to-create data for a detailed log level and avoid doing 34 | // the work if the data isn't printed. 35 | type logClosure func() string 36 | 37 | // String invokes the log closure and returns the results string. 38 | func (c logClosure) String() string { 39 | return c() 40 | } 41 | 42 | // newLogClosure returns a new closure over the passed function which allows 43 | // it to be used as a parameter in a logging function that is only invoked when 44 | // the logging level is such that the message will actually be logged. 45 | func newLogClosure(c func() string) logClosure { 46 | return logClosure(c) 47 | } 48 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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, and a timeout value for opening the database as a 16 | time.Duration: 17 | 18 | ```Go 19 | db, err := walletdb.Open("bdb", "path/to/database.db", true, 60*time.Second) 20 | if err != nil { 21 | // Handle error 22 | } 23 | ``` 24 | 25 | ```Go 26 | db, err := walletdb.Create("bdb", "path/to/database.db", true, 60*time.Second) 27 | if err != nil { 28 | // Handle error 29 | } 30 | ``` 31 | 32 | ## Documentation 33 | 34 | [![GoDoc](https://godoc.org/github.com/btcsuite/btcwallet/walletdb/bdb?status.png)] 35 | (http://godoc.org/github.com/btcsuite/btcwallet/walletdb/bdb) 36 | 37 | Full `go doc` style documentation for the project can be viewed online without 38 | installing this package by using the GoDoc site here: 39 | http://godoc.org/github.com/btcsuite/btcwallet/walletdb/bdb 40 | 41 | You can also view the documentation locally once the package is installed with 42 | the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to 43 | http://localhost:6060/pkg/github.com/btcsuite/btcwallet/walletdb/bdb 44 | 45 | ## License 46 | 47 | Package bdb is licensed under the [copyfree](http://copyfree.org) ISC 48 | License. 49 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /goclean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # The script does automatic checking on a Go package and its sub-packages, including: 3 | # 1. go fmt (http://golang.org/cmd/gofmt/) 4 | # 2. golint (https://github.com/golang/lint) 5 | # 3. go vet (http://golang.org/cmd/vet) 6 | # 4. race detector (http://blog.golang.org/race-detector) 7 | 8 | set -ex 9 | 10 | test_targets=$(go list -deps ./... | grep 'btcwallet') 11 | 12 | # Automatic checks 13 | test -z "$(go fmt $test_targets | tee /dev/stderr)" 14 | 15 | # test -z "$(goimports -l -w . | tee /dev/stderr)" 16 | test -z "$(for package in $test_targets; do golint $package; done | grep -v 'ALL_CAPS\|OP_\|NewFieldVal\|RpcCommand\|RpcRawCommand\|RpcSend\|Dns\|api.pb.go\|StartConsensusRpc\|factory_test.go\|legacy\|UnstableAPI' | tee /dev/stderr)" 17 | 18 | test -z "$(go vet $test_targets 2>&1 | grep -v '^exit status \|Example\|newestSha\| not a string in call to Errorf$' | tee /dev/stderr)" 19 | 20 | env GORACE="history_size=7 halt_on_errors=1" go test -v -race $test_targets 21 | 22 | # Run test coverage on each subdirectories and merge the coverage profile. 23 | 24 | #set +x 25 | #echo "mode: count" > profile.cov 26 | 27 | # Standard go tooling behavior is to ignore dirs with leading underscores. 28 | #for dir in $(find . -maxdepth 10 -not -path '.' -not -path './.git*' \ 29 | # -not -path '*/_*' -not -path './cmd*' -not -path './release*' -type d) 30 | #do 31 | #if ls $dir/*.go &> /dev/null; then 32 | # go test -covermode=count -coverprofile=$dir/profile.tmp $dir 33 | # if [ -f $dir/profile.tmp ]; then 34 | # cat $dir/profile.tmp | tail -n +2 >> profile.cov 35 | # rm $dir/profile.tmp 36 | # fi 37 | #fi 38 | #done 39 | 40 | # To submit the test coverage result to coveralls.io, 41 | # use goveralls (https://github.com/mattn/goveralls) 42 | # goveralls -coverprofile=profile.cov -service=travis-ci 43 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | // SimNetParams contains parameters specific to the simulation test network 37 | // (wire.SimNet). 38 | var SimNetParams = Params{ 39 | Params: &chaincfg.SimNetParams, 40 | RPCClientPort: "18556", 41 | RPCServerPort: "18554", 42 | } 43 | 44 | // SigNetParams contains parameters specific to the signet test network 45 | // (wire.SigNet). 46 | var SigNetParams = Params{ 47 | Params: &chaincfg.SigNetParams, 48 | RPCClientPort: "38334", 49 | RPCServerPort: "38332", 50 | } 51 | 52 | // SigNetWire is a helper function that either returns the given chain 53 | // parameter's net value if the parameter represents a signet network or 0 if 54 | // it's not. This is necessary because there can be custom signet networks that 55 | // have a different net value. 56 | func SigNetWire(params *chaincfg.Params) wire.BitcoinNet { 57 | if params.Name == chaincfg.SigNetParams.Name { 58 | return params.Net 59 | } 60 | 61 | return 0 62 | } 63 | -------------------------------------------------------------------------------- /wallet/mock.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/btcsuite/btcd/btcutil" 7 | "github.com/btcsuite/btcd/chaincfg/chainhash" 8 | "github.com/btcsuite/btcd/wire" 9 | "github.com/btcsuite/btcwallet/chain" 10 | "github.com/btcsuite/btcwallet/waddrmgr" 11 | ) 12 | 13 | type mockChainClient struct { 14 | } 15 | 16 | var _ chain.Interface = (*mockChainClient)(nil) 17 | 18 | func (m *mockChainClient) Start() error { 19 | return nil 20 | } 21 | 22 | func (m *mockChainClient) Stop() { 23 | } 24 | 25 | func (m *mockChainClient) WaitForShutdown() {} 26 | 27 | func (m *mockChainClient) GetBestBlock() (*chainhash.Hash, int32, error) { 28 | return nil, 0, nil 29 | } 30 | 31 | func (m *mockChainClient) GetBlock(*chainhash.Hash) (*wire.MsgBlock, error) { 32 | return nil, nil 33 | } 34 | 35 | func (m *mockChainClient) GetBlockHash(int64) (*chainhash.Hash, error) { 36 | return nil, nil 37 | } 38 | 39 | func (m *mockChainClient) GetBlockHeader(*chainhash.Hash) (*wire.BlockHeader, 40 | error) { 41 | return nil, nil 42 | } 43 | 44 | func (m *mockChainClient) IsCurrent() bool { 45 | return false 46 | } 47 | 48 | func (m *mockChainClient) FilterBlocks(*chain.FilterBlocksRequest) ( 49 | *chain.FilterBlocksResponse, error) { 50 | return nil, nil 51 | } 52 | 53 | func (m *mockChainClient) BlockStamp() (*waddrmgr.BlockStamp, error) { 54 | return &waddrmgr.BlockStamp{ 55 | Height: 500000, 56 | Hash: chainhash.Hash{}, 57 | Timestamp: time.Unix(1234, 0), 58 | }, nil 59 | } 60 | 61 | func (m *mockChainClient) SendRawTransaction(*wire.MsgTx, bool) ( 62 | *chainhash.Hash, error) { 63 | return nil, nil 64 | } 65 | 66 | func (m *mockChainClient) Rescan(*chainhash.Hash, []btcutil.Address, 67 | map[wire.OutPoint]btcutil.Address) error { 68 | return nil 69 | } 70 | 71 | func (m *mockChainClient) NotifyReceived([]btcutil.Address) error { 72 | return nil 73 | } 74 | 75 | func (m *mockChainClient) NotifyBlocks() error { 76 | return nil 77 | } 78 | 79 | func (m *mockChainClient) Notifications() <-chan interface{} { 80 | return nil 81 | } 82 | 83 | func (m *mockChainClient) BackEnd() string { 84 | return "mock" 85 | } 86 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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, error) { 21 | 22 | if len(args) != 3 { 23 | return "", false, 0, fmt.Errorf("invalid arguments to %s.%s "+ 24 | "-- expected database path, no-freelist-sync and "+ 25 | "timeout option", 26 | dbType, funcName) 27 | } 28 | 29 | dbPath, ok := args[0].(string) 30 | if !ok { 31 | return "", false, 0, 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, 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, fmt.Errorf("third argument to %s.%s is "+ 46 | "invalid -- expected timeout time.Duration", dbType, 47 | funcName) 48 | } 49 | 50 | return dbPath, noFreelistSync, timeout, nil 51 | } 52 | 53 | // openDBDriver is the callback provided during driver registration that opens 54 | // an existing database for use. 55 | func openDBDriver(args ...interface{}) (walletdb.DB, error) { 56 | dbPath, noFreelistSync, timeout, err := parseArgs("Open", args...) 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | return openDB(dbPath, noFreelistSync, false, timeout) 62 | } 63 | 64 | // createDBDriver is the callback provided during driver registration that 65 | // creates, initializes, and opens a database for use. 66 | func createDBDriver(args ...interface{}) (walletdb.DB, error) { 67 | dbPath, noFreelistSync, timeout, err := parseArgs("Create", args...) 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | return openDB(dbPath, noFreelistSync, true, timeout) 73 | } 74 | 75 | func init() { 76 | // Register the driver. 77 | driver := walletdb.Driver{ 78 | DbType: dbType, 79 | Create: createDBDriver, 80 | Open: openDBDriver, 81 | } 82 | if err := walletdb.RegisterDriver(driver); err != nil { 83 | panic(fmt.Sprintf("Failed to regiser database driver '%s': %v", 84 | dbType, err)) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /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 | "google.golang.org/grpc/grpclog" 22 | 23 | "github.com/btcsuite/btclog" 24 | ) 25 | 26 | // UseLogger sets the logger to use for the gRPC server. 27 | func UseLogger(l btclog.Logger) { 28 | grpclog.SetLogger(logger{l}) // nolint:staticcheck 29 | } 30 | 31 | // logger uses a btclog.Logger to implement the grpclog.Logger interface. 32 | type logger struct { 33 | btclog.Logger 34 | } 35 | 36 | // stripGrpcPrefix removes the package prefix for all logs made to the grpc 37 | // logger, since these are already included as the btclog subsystem name. 38 | func stripGrpcPrefix(logstr string) string { 39 | return strings.TrimPrefix(logstr, "grpc: ") 40 | } 41 | 42 | // stripGrpcPrefixArgs removes the package prefix from the first argument, if it 43 | // exists and is a string, returning the same arg slice after reassigning the 44 | // first arg. 45 | func stripGrpcPrefixArgs(args ...interface{}) []interface{} { 46 | if len(args) == 0 { 47 | return args 48 | } 49 | firstArgStr, ok := args[0].(string) 50 | if ok { 51 | args[0] = stripGrpcPrefix(firstArgStr) 52 | } 53 | return args 54 | } 55 | 56 | func (l logger) Fatal(args ...interface{}) { 57 | l.Critical(stripGrpcPrefixArgs(args)...) 58 | os.Exit(1) 59 | } 60 | 61 | func (l logger) Fatalf(format string, args ...interface{}) { 62 | l.Criticalf(stripGrpcPrefix(format), args...) 63 | os.Exit(1) 64 | } 65 | 66 | func (l logger) Fatalln(args ...interface{}) { 67 | l.Critical(stripGrpcPrefixArgs(args)...) 68 | os.Exit(1) 69 | } 70 | 71 | func (l logger) Print(args ...interface{}) { 72 | l.Info(stripGrpcPrefixArgs(args)...) 73 | } 74 | 75 | func (l logger) Printf(format string, args ...interface{}) { 76 | l.Infof(stripGrpcPrefix(format), args...) 77 | } 78 | 79 | func (l logger) Println(args ...interface{}) { 80 | l.Info(stripGrpcPrefixArgs(args)...) 81 | } 82 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /version.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 | "bytes" 9 | "fmt" 10 | "strings" 11 | ) 12 | 13 | // semanticAlphabet 14 | const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-" 15 | 16 | // These constants define the application version and follow the semantic 17 | // versioning 2.0.0 spec (http://semver.org/). 18 | const ( 19 | appMajor uint = 0 20 | appMinor uint = 15 21 | appPatch uint = 1 22 | 23 | // appPreRelease MUST only contain characters from semanticAlphabet 24 | // per the semantic versioning spec. 25 | appPreRelease = "alpha" 26 | ) 27 | 28 | // appBuild is defined as a variable so it can be overridden during the build 29 | // process with '-ldflags "-X main.appBuild foo' if needed. It MUST only 30 | // contain characters from semanticAlphabet per the semantic versioning spec. 31 | var appBuild string 32 | 33 | // version returns the application version as a properly formed string per the 34 | // semantic versioning 2.0.0 spec (http://semver.org/). 35 | func version() string { 36 | // Start with the major, minor, and path versions. 37 | version := fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch) 38 | 39 | // Append pre-release version if there is one. The hyphen called for 40 | // by the semantic versioning spec is automatically appended and should 41 | // not be contained in the pre-release string. The pre-release version 42 | // is not appended if it contains invalid characters. 43 | preRelease := normalizeVerString(appPreRelease) 44 | if preRelease != "" { 45 | version = fmt.Sprintf("%s-%s", version, preRelease) 46 | } 47 | 48 | // Append build metadata if there is any. The plus called for 49 | // by the semantic versioning spec is automatically appended and should 50 | // not be contained in the build metadata string. The build metadata 51 | // string is not appended if it contains invalid characters. 52 | build := normalizeVerString(appBuild) 53 | if build != "" { 54 | version = fmt.Sprintf("%s+%s", version, build) 55 | } 56 | 57 | return version 58 | } 59 | 60 | // normalizeVerString returns the passed string stripped of all characters which 61 | // are not valid according to the semantic versioning guidelines for pre-release 62 | // version and build metadata strings. In particular they MUST only contain 63 | // characters in semanticAlphabet. 64 | func normalizeVerString(str string) string { 65 | result := bytes.Buffer{} 66 | for _, r := range str { 67 | if strings.ContainsRune(semanticAlphabet, r) { 68 | _, err := result.WriteRune(r) 69 | // Writing to a bytes.Buffer panics on OOM, and all 70 | // errors are unexpected. 71 | if err != nil { 72 | panic(err) 73 | } 74 | } 75 | } 76 | return result.String() 77 | } 78 | -------------------------------------------------------------------------------- /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) 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/example_test.go: -------------------------------------------------------------------------------- 1 | package wallet 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "testing" 7 | "time" 8 | 9 | "github.com/btcsuite/btcd/btcutil/hdkeychain" 10 | "github.com/btcsuite/btcd/chaincfg" 11 | "github.com/btcsuite/btcwallet/waddrmgr" 12 | "github.com/btcsuite/btcwallet/walletdb" 13 | ) 14 | 15 | // defaultDBTimeout specifies the timeout value when opening the wallet 16 | // database. 17 | var defaultDBTimeout = 10 * time.Second 18 | 19 | // testWallet creates a test wallet and unlocks it. 20 | func testWallet(t *testing.T) (*Wallet, func()) { 21 | // Set up a wallet. 22 | dir, err := ioutil.TempDir("", "test_wallet") 23 | if err != nil { 24 | t.Fatalf("Failed to create db dir: %v", err) 25 | } 26 | 27 | cleanup := func() { 28 | if err := os.RemoveAll(dir); err != nil { 29 | t.Fatalf("could not cleanup test: %v", err) 30 | } 31 | } 32 | 33 | seed, err := hdkeychain.GenerateSeed(hdkeychain.MinSeedBytes) 34 | if err != nil { 35 | t.Fatalf("unable to create seed: %v", err) 36 | } 37 | 38 | pubPass := []byte("hello") 39 | privPass := []byte("world") 40 | 41 | loader := NewLoader( 42 | &chaincfg.TestNet3Params, dir, true, defaultDBTimeout, 250, 43 | ) 44 | w, err := loader.CreateNewWallet(pubPass, privPass, seed, time.Now()) 45 | if err != nil { 46 | t.Fatalf("unable to create wallet: %v", err) 47 | } 48 | chainClient := &mockChainClient{} 49 | w.chainClient = chainClient 50 | if err := w.Unlock(privPass, time.After(10*time.Minute)); err != nil { 51 | t.Fatalf("unable to unlock wallet: %v", err) 52 | } 53 | 54 | return w, cleanup 55 | } 56 | 57 | // testWalletWatchingOnly creates a test watch only wallet and unlocks it. 58 | func testWalletWatchingOnly(t *testing.T) (*Wallet, func()) { 59 | // Set up a wallet. 60 | dir, err := ioutil.TempDir("", "test_wallet_watch_only") 61 | if err != nil { 62 | t.Fatalf("Failed to create db dir: %v", err) 63 | } 64 | 65 | cleanup := func() { 66 | if err := os.RemoveAll(dir); err != nil { 67 | t.Fatalf("could not cleanup test: %v", err) 68 | } 69 | } 70 | 71 | pubPass := []byte("hello") 72 | loader := NewLoader( 73 | &chaincfg.TestNet3Params, dir, true, defaultDBTimeout, 250, 74 | ) 75 | w, err := loader.CreateNewWatchingOnlyWallet(pubPass, time.Now()) 76 | if err != nil { 77 | t.Fatalf("unable to create wallet: %v", err) 78 | } 79 | chainClient := &mockChainClient{} 80 | w.chainClient = chainClient 81 | 82 | err = walletdb.Update(w.Database(), func(tx walletdb.ReadWriteTx) error { 83 | ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) 84 | for scope, schema := range waddrmgr.ScopeAddrMap { 85 | _, err := w.Manager.NewScopedKeyManager( 86 | ns, scope, schema, 87 | ) 88 | if err != nil { 89 | return err 90 | } 91 | } 92 | 93 | return nil 94 | }) 95 | if err != nil { 96 | t.Fatalf("unable to create default scopes: %v", err) 97 | } 98 | 99 | return w, cleanup 100 | } 101 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | pull_request: 8 | branches: 9 | - "*" 10 | 11 | defaults: 12 | run: 13 | shell: bash 14 | 15 | env: 16 | # go needs absolute directories, using the $HOME variable doesn't work here. 17 | GOCACHE: /home/runner/work/go/pkg/build 18 | GOPATH: /home/runner/work/go 19 | GO111MODULE: on 20 | 21 | GO_VERSION: '^1.17.0' 22 | BITCOIND_VERSION: '22.0' 23 | BITCOIND_IMAGE: 'lightninglabs/bitcoin-core' 24 | 25 | jobs: 26 | ######################## 27 | # Format, compileation and lint check 28 | ######################## 29 | lint-check: 30 | name: Format, compilation and lint check 31 | runs-on: ubuntu-latest 32 | steps: 33 | - name: git checkout 34 | uses: actions/checkout@v2 35 | 36 | - name: setup go ${{ env.GO_VERSION }} 37 | uses: actions/setup-go@v2 38 | with: 39 | go-version: '${{ env.GO_VERSION }}' 40 | 41 | - name: run format 42 | run: make fmt 43 | 44 | - name: compile code 45 | run: go install -v ./... 46 | 47 | - name: run lint 48 | run: make lint 49 | 50 | ######################## 51 | # run unit tests 52 | ######################## 53 | unit-test: 54 | name: run unit tests 55 | runs-on: ubuntu-latest 56 | strategy: 57 | # Allow other tests in the matrix to continue if one fails. 58 | fail-fast: false 59 | matrix: 60 | unit_type: 61 | - unit-race 62 | - unit-cover 63 | steps: 64 | - name: extract bitcoind from docker image 65 | run: |- 66 | docker pull ${{ env.BITCOIND_IMAGE }}:${{ env.BITCOIND_VERSION }} 67 | CONTAINER_ID=$(docker create ${{ env.BITCOIND_IMAGE }}:${{ env.BITCOIND_VERSION }}) 68 | sudo docker cp $CONTAINER_ID:/opt/bitcoin-${{ env.BITCOIND_VERSION }}/bin/bitcoind /usr/local/bin/bitcoind 69 | docker rm $CONTAINER_ID 70 | 71 | - name: git checkout 72 | uses: actions/checkout@v2 73 | 74 | - name: go cache 75 | uses: actions/cache@v1 76 | with: 77 | path: /home/runner/work/go 78 | key: btcwallet-${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ github.job }}-${{ hashFiles('**/go.sum') }} 79 | restore-keys: | 80 | btcwallet-${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ github.job }}-${{ hashFiles('**/go.sum') }} 81 | btcwallet-${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ github.job }}- 82 | btcwallet-${{ runner.os }}-go-${{ env.GO_VERSION }}- 83 | btcwallet-${{ runner.os }}-go- 84 | 85 | - name: setup go ${{ env.GO_VERSION }} 86 | uses: actions/setup-go@v2 87 | with: 88 | go-version: '${{ env.GO_VERSION }}' 89 | 90 | - name: run ${{ matrix.unit_type }} 91 | run: make ${{ matrix.unit_type }} 92 | 93 | - name: Send coverage 94 | uses: shogo82148/actions-goveralls@v1 95 | if: matrix.unit_type == 'unit-cover' 96 | with: 97 | path-to-profile: coverage.txt 98 | parallel: true 99 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PKG := github.com/btcsuite/btcwallet 2 | 3 | LINT_PKG := github.com/golangci/golangci-lint/cmd/golangci-lint 4 | GOACC_PKG := github.com/ory/go-acc 5 | GOIMPORTS_PKG := golang.org/x/tools/cmd/goimports 6 | 7 | GO_BIN := ${GOPATH}/bin 8 | LINT_BIN := $(GO_BIN)/golangci-lint 9 | GOACC_BIN := $(GO_BIN)/go-acc 10 | 11 | LINT_COMMIT := v1.46.0 12 | GOACC_COMMIT := 80342ae2e0fcf265e99e76bcc4efd022c7c3811b 13 | GOIMPORTS_COMMIT := v0.1.10 14 | 15 | GOBUILD := GO111MODULE=on go build -v 16 | GOINSTALL := GO111MODULE=on go install -v 17 | GOTEST := GO111MODULE=on go test 18 | 19 | GOLIST := go list -deps $(PKG)/... | grep '$(PKG)' 20 | GOLIST_COVER := $$(go list -deps $(PKG)/... | grep '$(PKG)') 21 | GOFILES_NOVENDOR = $(shell find . -type f -name '*.go' -not -path "./vendor/*") 22 | 23 | RM := rm -f 24 | CP := cp 25 | MAKE := make 26 | XARGS := xargs -L 1 27 | 28 | # Linting uses a lot of memory, so keep it under control by limiting the number 29 | # of workers if requested. 30 | ifneq ($(workers),) 31 | LINT_WORKERS = --concurrency=$(workers) 32 | endif 33 | 34 | LINT = $(LINT_BIN) run -v $(LINT_WORKERS) 35 | 36 | GREEN := "\\033[0;32m" 37 | NC := "\\033[0m" 38 | define print 39 | echo $(GREEN)$1$(NC) 40 | endef 41 | 42 | default: build 43 | 44 | all: build check 45 | 46 | # ============ 47 | # DEPENDENCIES 48 | # ============ 49 | 50 | $(LINT_BIN): 51 | @$(call print, "Fetching linter") 52 | $(GOINSTALL) $(LINT_PKG)@$(LINT_COMMIT) 53 | 54 | $(GOACC_BIN): 55 | @$(call print, "Fetching go-acc") 56 | $(GOINSTALL) $(GOACC_PKG)@$(GOACC_COMMIT) 57 | 58 | goimports: 59 | @$(call print, "Installing goimports.") 60 | $(GOINSTALL) $(GOIMPORTS_PKG)@${GOIMPORTS_COMMIT} 61 | 62 | # ============ 63 | # INSTALLATION 64 | # ============ 65 | 66 | build: 67 | @$(call print, "Compiling btcwallet.") 68 | $(GOBUILD) $(PKG)/... 69 | 70 | install: 71 | @$(call print, "Installing btcwallet.") 72 | $(GOINSTALL) $(PKG) 73 | $(GOINSTALL) $(PKG)/cmd/dropwtxmgr 74 | $(GOINSTALL) $(PKG)/cmd/sweepaccount 75 | 76 | # ======= 77 | # TESTING 78 | # ======= 79 | 80 | check: unit 81 | 82 | unit: 83 | @$(call print, "Running unit tests.") 84 | $(GOLIST) | $(XARGS) env $(GOTEST) -test.timeout=20m 85 | 86 | unit-cover: $(GOACC_BIN) 87 | @$(call print, "Running unit coverage tests.") 88 | $(GOACC_BIN) $(GOLIST_COVER) 89 | 90 | unit-race: 91 | @$(call print, "Running unit race tests.") 92 | env CGO_ENABLED=1 GORACE="history_size=7 halt_on_errors=1" $(GOLIST) | $(XARGS) env $(GOTEST) -race -test.timeout=20m 93 | 94 | # ========= 95 | # UTILITIES 96 | # ========= 97 | 98 | fmt: goimports 99 | @$(call print, "Fixing imports.") 100 | goimports -w $(GOFILES_NOVENDOR) 101 | @$(call print, "Formatting source.") 102 | gofmt -l -w -s $(GOFILES_NOVENDOR) 103 | 104 | lint: $(LINT_BIN) 105 | @$(call print, "Linting source.") 106 | $(LINT) 107 | 108 | clean: 109 | @$(call print, "Cleaning source.$(NC)") 110 | $(RM) coverage.txt 111 | 112 | .PHONY: all \ 113 | default \ 114 | build \ 115 | check \ 116 | unit \ 117 | unit-cover \ 118 | unit-race \ 119 | fmt \ 120 | lint \ 121 | clean 122 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /wallet/utxos_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 | "bytes" 9 | "testing" 10 | 11 | "github.com/btcsuite/btcd/btcutil/hdkeychain" 12 | "github.com/btcsuite/btcd/txscript" 13 | "github.com/btcsuite/btcd/wire" 14 | "github.com/btcsuite/btcwallet/waddrmgr" 15 | ) 16 | 17 | // TestFetchInputInfo checks that the wallet can gather information about an 18 | // output based on the address. 19 | func TestFetchInputInfo(t *testing.T) { 20 | w, cleanup := testWallet(t) 21 | defer cleanup() 22 | 23 | // Create an address we can use to send some coins to. 24 | addr, err := w.CurrentAddress(0, waddrmgr.KeyScopeBIP0084) 25 | if err != nil { 26 | t.Fatalf("unable to get current address: %v", addr) 27 | } 28 | p2shAddr, err := txscript.PayToAddrScript(addr) 29 | if err != nil { 30 | t.Fatalf("unable to convert wallet address to p2sh: %v", err) 31 | } 32 | 33 | // Add an output paying to the wallet's address to the database. 34 | utxOut := wire.NewTxOut(100000, p2shAddr) 35 | incomingTx := &wire.MsgTx{ 36 | TxIn: []*wire.TxIn{{}}, 37 | TxOut: []*wire.TxOut{utxOut}, 38 | } 39 | addUtxo(t, w, incomingTx) 40 | 41 | // Look up the UTXO for the outpoint now and compare it to our 42 | // expectations. 43 | prevOut := &wire.OutPoint{ 44 | Hash: incomingTx.TxHash(), 45 | Index: 0, 46 | } 47 | tx, out, derivationPath, confirmations, err := w.FetchInputInfo(prevOut) 48 | if err != nil { 49 | t.Fatalf("error fetching input info: %v", err) 50 | } 51 | if !bytes.Equal(out.PkScript, utxOut.PkScript) || out.Value != utxOut.Value { 52 | t.Fatalf("unexpected TX out, got %v wanted %v", out, utxOut) 53 | } 54 | if !bytes.Equal(tx.TxOut[prevOut.Index].PkScript, utxOut.PkScript) { 55 | t.Fatalf("unexpected TX out, got %v wanted %v", 56 | tx.TxOut[prevOut.Index].PkScript, utxOut) 57 | } 58 | if len(derivationPath.Bip32Path) != 5 { 59 | t.Fatalf("expected derivation path of length %v, got %v", 3, 60 | len(derivationPath.Bip32Path)) 61 | } 62 | if derivationPath.Bip32Path[0] != 63 | waddrmgr.KeyScopeBIP0084.Purpose+hdkeychain.HardenedKeyStart { 64 | t.Fatalf("expected purpose %v, got %v", 65 | waddrmgr.KeyScopeBIP0084.Purpose, 66 | derivationPath.Bip32Path[0]) 67 | } 68 | if derivationPath.Bip32Path[1] != 69 | waddrmgr.KeyScopeBIP0084.Coin+hdkeychain.HardenedKeyStart { 70 | t.Fatalf("expected coin type %v, got %v", 71 | waddrmgr.KeyScopeBIP0084.Coin, 72 | derivationPath.Bip32Path[1]) 73 | } 74 | if derivationPath.Bip32Path[2] != hdkeychain.HardenedKeyStart { 75 | t.Fatalf("expected account %v, got %v", 76 | hdkeychain.HardenedKeyStart, derivationPath.Bip32Path[2]) 77 | } 78 | if derivationPath.Bip32Path[3] != 0 { 79 | t.Fatalf("expected branch %v, got %v", 0, 80 | derivationPath.Bip32Path[3]) 81 | } 82 | if derivationPath.Bip32Path[4] != 0 { 83 | t.Fatalf("expected index %v, got %v", 0, 84 | derivationPath.Bip32Path[4]) 85 | } 86 | if confirmations != int64(0-testBlockHeight) { 87 | t.Fatalf("unexpected number of confirmations, got %d wanted %d", 88 | confirmations, 0-testBlockHeight) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /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 | testCases := []struct { 20 | name string 21 | scope waddrmgr.KeyScope 22 | expectedScriptLen int 23 | }{{ 24 | name: "BIP084 P2WKH", 25 | scope: waddrmgr.KeyScopeBIP0084, 26 | expectedScriptLen: 0, 27 | }, { 28 | name: "BIP049 nested P2WKH", 29 | scope: waddrmgr.KeyScopeBIP0049Plus, 30 | expectedScriptLen: 23, 31 | }} 32 | 33 | w, cleanup := testWallet(t) 34 | defer cleanup() 35 | 36 | for _, tc := range testCases { 37 | tc := tc 38 | t.Run(tc.name, func(t *testing.T) { 39 | runTestCase(t, w, tc.scope, tc.expectedScriptLen) 40 | }) 41 | } 42 | } 43 | 44 | func runTestCase(t *testing.T, w *Wallet, scope waddrmgr.KeyScope, 45 | scriptLen int) { 46 | 47 | // Create an address we can use to send some coins to. 48 | addr, err := w.CurrentAddress(0, scope) 49 | if err != nil { 50 | t.Fatalf("unable to get current address: %v", addr) 51 | } 52 | p2shAddr, err := txscript.PayToAddrScript(addr) 53 | if err != nil { 54 | t.Fatalf("unable to convert wallet address to p2sh: %v", err) 55 | } 56 | 57 | // Add an output paying to the wallet's address to the database. 58 | utxOut := wire.NewTxOut(100000, p2shAddr) 59 | incomingTx := &wire.MsgTx{ 60 | TxIn: []*wire.TxIn{{}}, 61 | TxOut: []*wire.TxOut{utxOut}, 62 | } 63 | addUtxo(t, w, incomingTx) 64 | 65 | // Create a transaction that spends the UTXO created above and spends to 66 | // the same address again. 67 | prevOut := wire.OutPoint{ 68 | Hash: incomingTx.TxHash(), 69 | Index: 0, 70 | } 71 | outgoingTx := &wire.MsgTx{ 72 | TxIn: []*wire.TxIn{{ 73 | PreviousOutPoint: prevOut, 74 | }}, 75 | TxOut: []*wire.TxOut{utxOut}, 76 | } 77 | fetcher := txscript.NewCannedPrevOutputFetcher( 78 | utxOut.PkScript, utxOut.Value, 79 | ) 80 | sigHashes := txscript.NewTxSigHashes(outgoingTx, fetcher) 81 | 82 | // Compute the input script to spend the UTXO now. 83 | witness, script, err := w.ComputeInputScript( 84 | outgoingTx, utxOut, 0, sigHashes, txscript.SigHashAll, nil, 85 | ) 86 | if err != nil { 87 | t.Fatalf("error computing input script: %v", err) 88 | } 89 | if len(script) != scriptLen { 90 | t.Fatalf("unexpected script length, got %d wanted %d", 91 | len(script), scriptLen) 92 | } 93 | if len(witness) != 2 { 94 | t.Fatalf("unexpected witness stack length, got %d, wanted %d", 95 | len(witness), 2) 96 | } 97 | 98 | // Finally verify that the created witness is valid. 99 | outgoingTx.TxIn[0].Witness = witness 100 | outgoingTx.TxIn[0].SignatureScript = script 101 | err = validateMsgTx( 102 | outgoingTx, [][]byte{utxOut.PkScript}, []btcutil.Amount{100000}, 103 | ) 104 | if err != nil { 105 | t.Fatalf("error validating tx: %v", err) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | func storeError(c ErrorCode, desc string, err error) Error { 89 | return Error{Code: c, Desc: desc, Err: err} 90 | } 91 | 92 | // IsNoExists returns whether an error is a Error with the ErrNoExists error 93 | // code. 94 | func IsNoExists(err error) bool { 95 | serr, ok := err.(Error) 96 | return ok && serr.Code == ErrNoExists 97 | } 98 | -------------------------------------------------------------------------------- /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/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/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 %v", 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 regen.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 regenated, 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 | -------------------------------------------------------------------------------- /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/wallet" 19 | "github.com/btcsuite/btcwallet/wtxmgr" 20 | "github.com/jrick/logrotate/rotator" 21 | "github.com/lightninglabs/neutrino" 22 | ) 23 | 24 | // logWriter implements an io.Writer that outputs to both standard output and 25 | // the write-end pipe of an initialized log rotator. 26 | type logWriter struct{} 27 | 28 | func (logWriter) Write(p []byte) (n int, err error) { 29 | _, _ = os.Stdout.Write(p) 30 | _, _ = logRotatorPipe.Write(p) 31 | return len(p), nil 32 | } 33 | 34 | // Loggers per subsystem. A single backend logger is created and all subsytem 35 | // loggers created from it will write to the backend. When adding new 36 | // subsystems, add the subsystem logger variable here and to the 37 | // subsystemLoggers map. 38 | // 39 | // Loggers can not be used before the log rotator has been initialized with a 40 | // log file. This must be performed early during application startup by calling 41 | // initLogRotator. 42 | var ( 43 | // backendLog is the logging backend used to create all subsystem loggers. 44 | // The backend must not be used before the log rotator has been initialized, 45 | // or data races and/or nil pointer dereferences will occur. 46 | backendLog = btclog.NewBackend(logWriter{}) 47 | 48 | // logRotator is one of the logging outputs. It should be closed on 49 | // application shutdown. 50 | logRotator *rotator.Rotator 51 | 52 | // logRotatorPipe is the write-end pipe for writing to the log rotator. It 53 | // is written to by the Write method of the logWriter type. 54 | logRotatorPipe *io.PipeWriter 55 | 56 | log = backendLog.Logger("BTCW") 57 | walletLog = backendLog.Logger("WLLT") 58 | txmgrLog = backendLog.Logger("TMGR") 59 | chainLog = backendLog.Logger("CHNS") 60 | grpcLog = backendLog.Logger("GRPC") 61 | legacyRPCLog = backendLog.Logger("RPCS") 62 | btcnLog = backendLog.Logger("BTCN") 63 | ) 64 | 65 | // Initialize package-global logger variables. 66 | func init() { 67 | wallet.UseLogger(walletLog) 68 | wtxmgr.UseLogger(txmgrLog) 69 | chain.UseLogger(chainLog) 70 | rpcclient.UseLogger(chainLog) 71 | rpcserver.UseLogger(grpcLog) 72 | legacyrpc.UseLogger(legacyRPCLog) 73 | neutrino.UseLogger(btcnLog) 74 | } 75 | 76 | // subsystemLoggers maps each subsystem identifier to its associated logger. 77 | var subsystemLoggers = map[string]btclog.Logger{ 78 | "BTCW": log, 79 | "WLLT": walletLog, 80 | "TMGR": txmgrLog, 81 | "CHNS": chainLog, 82 | "GRPC": grpcLog, 83 | "RPCS": legacyRPCLog, 84 | "BTCN": btcnLog, 85 | } 86 | 87 | // initLogRotator initializes the logging rotater to write logs to logFile and 88 | // create roll files in the same directory. It must be called before the 89 | // package-global log rotater variables are used. 90 | func initLogRotator(logFile string) { 91 | logDir, _ := filepath.Split(logFile) 92 | err := os.MkdirAll(logDir, 0700) 93 | if err != nil { 94 | fmt.Fprintf(os.Stderr, "failed to create log directory: %v\n", err) 95 | os.Exit(1) 96 | } 97 | r, err := rotator.New(logFile, 10*1024, false, 3) 98 | if err != nil { 99 | fmt.Fprintf(os.Stderr, "failed to create file rotator: %v\n", err) 100 | os.Exit(1) 101 | } 102 | 103 | pr, pw := io.Pipe() 104 | go func() { _ = r.Run(pr) }() 105 | 106 | logRotator = r 107 | logRotatorPipe = pw 108 | } 109 | 110 | // setLogLevel sets the logging level for provided subsystem. Invalid 111 | // subsystems are ignored. Uninitialized subsystems are dynamically created as 112 | // needed. 113 | func setLogLevel(subsystemID string, logLevel string) { 114 | // Ignore invalid subsystems. 115 | logger, ok := subsystemLoggers[subsystemID] 116 | if !ok { 117 | return 118 | } 119 | 120 | // Defaults to info if the log level is invalid. 121 | level, _ := btclog.LevelFromString(logLevel) 122 | logger.SetLevel(level) 123 | } 124 | 125 | // setLogLevels sets the log level for all subsystem loggers to the passed 126 | // level. It also dynamically creates the subsystem loggers as needed, so it 127 | // can be used to initialize the logging system. 128 | func setLogLevels(logLevel string) { 129 | // Configure all sub-systems with the new logging level. Dynamically 130 | // create loggers as needed. 131 | for subsystemID := range subsystemLoggers { 132 | setLogLevel(subsystemID, logLevel) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /chain/interface.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/btcsuite/btcd/btcutil" 7 | "github.com/btcsuite/btcd/chaincfg/chainhash" 8 | "github.com/btcsuite/btcd/wire" 9 | "github.com/btcsuite/btcwallet/waddrmgr" 10 | "github.com/btcsuite/btcwallet/wtxmgr" 11 | ) 12 | 13 | // isCurrentDelta is the delta duration we'll use from the present time to 14 | // determine if a backend is considered "current", i.e. synced to the tip of 15 | // the chain. 16 | const isCurrentDelta = 2 * time.Hour 17 | 18 | // BackEnds returns a list of the available back ends. 19 | // TODO: Refactor each into a driver and use dynamic registration. 20 | func BackEnds() []string { 21 | return []string{ 22 | "bitcoind", 23 | "btcd", 24 | "neutrino", 25 | "bitcoind-rpc-polling", 26 | } 27 | } 28 | 29 | // Interface allows more than one backing blockchain source, such as a 30 | // btcd RPC chain server, or an SPV library, as long as we write a driver for 31 | // it. 32 | type Interface interface { 33 | Start() error 34 | Stop() 35 | WaitForShutdown() 36 | GetBestBlock() (*chainhash.Hash, int32, error) 37 | GetBlock(*chainhash.Hash) (*wire.MsgBlock, error) 38 | GetBlockHash(int64) (*chainhash.Hash, error) 39 | GetBlockHeader(*chainhash.Hash) (*wire.BlockHeader, error) 40 | IsCurrent() bool 41 | FilterBlocks(*FilterBlocksRequest) (*FilterBlocksResponse, error) 42 | BlockStamp() (*waddrmgr.BlockStamp, error) 43 | SendRawTransaction(*wire.MsgTx, bool) (*chainhash.Hash, error) 44 | Rescan(*chainhash.Hash, []btcutil.Address, map[wire.OutPoint]btcutil.Address) error 45 | NotifyReceived([]btcutil.Address) error 46 | NotifyBlocks() error 47 | Notifications() <-chan interface{} 48 | BackEnd() string 49 | } 50 | 51 | // Notification types. These are defined here and processed from from reading 52 | // a notificationChan to avoid handling these notifications directly in 53 | // rpcclient callbacks, which isn't very Go-like and doesn't allow 54 | // blocking client calls. 55 | type ( 56 | // ClientConnected is a notification for when a client connection is 57 | // opened or reestablished to the chain server. 58 | ClientConnected struct{} 59 | 60 | // BlockConnected is a notification for a newly-attached block to the 61 | // best chain. 62 | BlockConnected wtxmgr.BlockMeta 63 | 64 | // FilteredBlockConnected is an alternate notification that contains 65 | // both block and relevant transaction information in one struct, which 66 | // allows atomic updates. 67 | FilteredBlockConnected struct { 68 | Block *wtxmgr.BlockMeta 69 | RelevantTxs []*wtxmgr.TxRecord 70 | } 71 | 72 | // FilterBlocksRequest specifies a range of blocks and the set of 73 | // internal and external addresses of interest, indexed by corresponding 74 | // scoped-index of the child address. A global set of watched outpoints 75 | // is also included to monitor for spends. 76 | FilterBlocksRequest struct { 77 | Blocks []wtxmgr.BlockMeta 78 | ExternalAddrs map[waddrmgr.ScopedIndex]btcutil.Address 79 | InternalAddrs map[waddrmgr.ScopedIndex]btcutil.Address 80 | WatchedOutPoints map[wire.OutPoint]btcutil.Address 81 | } 82 | 83 | // FilterBlocksResponse reports the set of all internal and external 84 | // addresses found in response to a FilterBlockRequest, any outpoints 85 | // found that correspond to those addresses, as well as the relevant 86 | // transactions that can modify the wallet's balance. The index of the 87 | // block within the FilterBlocksRequest is returned, such that the 88 | // caller can reinitiate a request for the subsequent block after 89 | // updating the addresses of interest. 90 | FilterBlocksResponse struct { 91 | BatchIndex uint32 92 | BlockMeta wtxmgr.BlockMeta 93 | FoundExternalAddrs map[waddrmgr.KeyScope]map[uint32]struct{} 94 | FoundInternalAddrs map[waddrmgr.KeyScope]map[uint32]struct{} 95 | FoundOutPoints map[wire.OutPoint]btcutil.Address 96 | RelevantTxns []*wire.MsgTx 97 | } 98 | 99 | // BlockDisconnected is a notifcation that the block described by the 100 | // BlockStamp was reorganized out of the best chain. 101 | BlockDisconnected wtxmgr.BlockMeta 102 | 103 | // RelevantTx is a notification for a transaction which spends wallet 104 | // inputs or pays to a watched address. 105 | RelevantTx struct { 106 | TxRecord *wtxmgr.TxRecord 107 | Block *wtxmgr.BlockMeta // nil if unmined 108 | } 109 | 110 | // RescanProgress is a notification describing the current status 111 | // of an in-progress rescan. 112 | RescanProgress struct { 113 | Hash *chainhash.Hash 114 | Height int32 115 | Time time.Time 116 | } 117 | 118 | // RescanFinished is a notification that a previous rescan request 119 | // has finished. 120 | RescanFinished struct { 121 | Hash *chainhash.Hash 122 | Height int32 123 | Time time.Time 124 | } 125 | ) 126 | -------------------------------------------------------------------------------- /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: %v", 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | "io/ioutil" 10 | "os" 11 | "path/filepath" 12 | "testing" 13 | "time" 14 | 15 | "github.com/btcsuite/btcwallet/walletdb" 16 | _ "github.com/btcsuite/btcwallet/walletdb/bdb" 17 | ) 18 | 19 | var ( 20 | // ignoreDbTypes are types which should be ignored when running tests 21 | // that iterate all supported DB types. This allows some tests to add 22 | // bogus drivers for testing purposes while still allowing other tests 23 | // to easily iterate all supported drivers. 24 | ignoreDbTypes = map[string]bool{"createopenfail": true} 25 | 26 | // defaultDBTimeout specifies the timeout value when opening the wallet 27 | // database. 28 | defaultDBTimeout = 10 * time.Second 29 | ) 30 | 31 | // TestAddDuplicateDriver ensures that adding a duplicate driver does not 32 | // overwrite an existing one. 33 | func TestAddDuplicateDriver(t *testing.T) { 34 | supportedDrivers := walletdb.SupportedDrivers() 35 | if len(supportedDrivers) == 0 { 36 | t.Errorf("no backends to test") 37 | return 38 | } 39 | dbType := supportedDrivers[0] 40 | 41 | // bogusCreateDB is a function which acts as a bogus create and open 42 | // driver function and intentionally returns a failure that can be 43 | // detected if the interface allows a duplicate driver to overwrite an 44 | // existing one. 45 | bogusCreateDB := func(args ...interface{}) (walletdb.DB, error) { 46 | return nil, fmt.Errorf("duplicate driver allowed for database "+ 47 | "type [%v]", dbType) 48 | } 49 | 50 | // Create a driver that tries to replace an existing one. Set its 51 | // create and open functions to a function that causes a test failure if 52 | // they are invoked. 53 | driver := walletdb.Driver{ 54 | DbType: dbType, 55 | Create: bogusCreateDB, 56 | Open: bogusCreateDB, 57 | } 58 | err := walletdb.RegisterDriver(driver) 59 | if err != walletdb.ErrDbTypeRegistered { 60 | t.Errorf("unexpected duplicate driver registration error - "+ 61 | "got %v, want %v", err, walletdb.ErrDbTypeRegistered) 62 | } 63 | 64 | tempDir, err := ioutil.TempDir("", "dupdrivertest") 65 | if err != nil { 66 | t.Errorf("unable to create temp dir: %v", err) 67 | return 68 | } 69 | defer os.Remove(tempDir) 70 | 71 | dbPath := filepath.Join(tempDir, "db") 72 | db, err := walletdb.Create(dbType, dbPath, true, defaultDBTimeout) 73 | if err != nil { 74 | t.Errorf("failed to create database: %v", err) 75 | return 76 | } 77 | db.Close() 78 | 79 | } 80 | 81 | // TestCreateOpenFail ensures that errors which occur while opening or closing 82 | // a database are handled properly. 83 | func TestCreateOpenFail(t *testing.T) { 84 | // bogusCreateDB is a function which acts as a bogus create and open 85 | // driver function that intentionally returns a failure which can be 86 | // detected. 87 | dbType := "createopenfail" 88 | openError := fmt.Errorf("failed to create or open database for "+ 89 | "database type [%v]", dbType) 90 | bogusCreateDB := func(args ...interface{}) (walletdb.DB, error) { 91 | return nil, openError 92 | } 93 | 94 | // Create and add driver that intentionally fails when created or opened 95 | // to ensure errors on database open and create are handled properly. 96 | driver := walletdb.Driver{ 97 | DbType: dbType, 98 | Create: bogusCreateDB, 99 | Open: bogusCreateDB, 100 | } 101 | walletdb.RegisterDriver(driver) 102 | 103 | // Ensure creating a database with the new type fails with the expected 104 | // error. 105 | _, err := walletdb.Create(dbType) 106 | if err != openError { 107 | t.Errorf("expected error not received - got: %v, want %v", err, 108 | openError) 109 | return 110 | } 111 | 112 | // Ensure opening a database with the new type fails with the expected 113 | // error. 114 | _, err = walletdb.Open(dbType) 115 | if err != openError { 116 | t.Errorf("expected error not received - got: %v, want %v", err, 117 | openError) 118 | return 119 | } 120 | } 121 | 122 | // TestCreateOpenUnsupported ensures that attempting to create or open an 123 | // unsupported database type is handled properly. 124 | func TestCreateOpenUnsupported(t *testing.T) { 125 | // Ensure creating a database with an unsupported type fails with the 126 | // expected error. 127 | dbType := "unsupported" 128 | _, err := walletdb.Create(dbType) 129 | if err != walletdb.ErrDbUnknownType { 130 | t.Errorf("expected error not received - got: %v, want %v", err, 131 | walletdb.ErrDbUnknownType) 132 | return 133 | } 134 | 135 | // Ensure opening a database with the an unsupported type fails with the 136 | // expected error. 137 | _, err = walletdb.Open(dbType) 138 | if err != walletdb.ErrDbUnknownType { 139 | t.Errorf("expected error not received - got: %v, want %v", err, 140 | walletdb.ErrDbUnknownType) 141 | return 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /walletdb/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 walletdb provides a namespaced database interface for btcwallet. 7 | 8 | Overview 9 | 10 | A wallet essentially consists of a multitude of stored data such as private 11 | and public keys, key derivation bits, pay-to-script-hash scripts, and various 12 | metadata. One of the issues with many wallets is they are tightly integrated. 13 | Designing a wallet with loosely coupled components that provide specific 14 | functionality is ideal, however it presents a challenge in regards to data 15 | storage since each component needs to store its own data without knowing the 16 | internals of other components or breaking atomicity. 17 | 18 | This package solves this issue by providing a pluggable driver, namespaced 19 | database interface that is intended to be used by the main wallet daemon. This 20 | allows the potential for any backend database type with a suitable driver. Each 21 | component, which will typically be a package, can then implement various 22 | functionality such as address management, voting pools, and colored coin 23 | metadata in their own namespace without having to worry about conflicts with 24 | other packages even though they are sharing the same database that is managed by 25 | the wallet. 26 | 27 | A quick overview of the features walletdb provides are as follows: 28 | 29 | - Key/value store 30 | - Namespace support 31 | - Allows multiple packages to have their own area in the database without 32 | worrying about conflicts 33 | - Read-only and read-write transactions with both manual and managed modes 34 | - Nested buckets 35 | - Supports registration of backend databases 36 | - Comprehensive test coverage 37 | 38 | Database 39 | 40 | The main entry point is the DB interface. It exposes functionality for 41 | creating, retrieving, and removing namespaces. It is obtained via the Create 42 | and Open functions which take a database type string that identifies the 43 | specific database driver (backend) to use as well as arguments specific to the 44 | specified driver. 45 | 46 | Namespaces 47 | 48 | The Namespace interface is an abstraction that provides facilities for obtaining 49 | transactions (the Tx interface) that are the basis of all database reads and 50 | writes. Unlike some database interfaces that support reading and writing 51 | without transactions, this interface requires transactions even when only 52 | reading or writing a single key. 53 | 54 | The Begin function provides an unmanaged transaction while the View and Update 55 | functions provide a managed transaction. These are described in more detail 56 | below. 57 | 58 | Transactions 59 | 60 | The Tx interface provides facilities for rolling back or commiting changes that 61 | took place while the transaction was active. It also provides the root bucket 62 | under which all keys, values, and nested buckets are stored. A transaction 63 | can either be read-only or read-write and managed or unmanaged. 64 | 65 | Managed versus Unmanaged Transactions 66 | 67 | A managed transaction is one where the caller provides a function to execute 68 | within the context of the transaction and the commit or rollback is handled 69 | automatically depending on whether or not the provided function returns an 70 | error. Attempting to manually call Rollback or Commit on the managed 71 | transaction will result in a panic. 72 | 73 | An unmanaged transaction, on the other hand, requires the caller to manually 74 | call Commit or Rollback when they are finished with it. Leaving transactions 75 | open for long periods of time can have several adverse effects, so it is 76 | recommended that managed transactions are used instead. 77 | 78 | Buckets 79 | 80 | The Bucket interface provides the ability to manipulate key/value pairs and 81 | nested buckets as well as iterate through them. 82 | 83 | The Get, Put, and Delete functions work with key/value pairs, while the Bucket, 84 | CreateBucket, CreateBucketIfNotExists, and DeleteBucket functions work with 85 | buckets. The ForEach function allows the caller to provide a function to be 86 | called with each key/value pair and nested bucket in the current bucket. 87 | 88 | Root Bucket 89 | 90 | As discussed above, all of the functions which are used to manipulate key/value 91 | pairs and nested buckets exist on the Bucket interface. The root bucket is the 92 | upper-most bucket in a namespace under which data is stored and is created at 93 | the same time as the namespace. Use the RootBucket function on the Tx interface 94 | to retrieve it. 95 | 96 | Nested Buckets 97 | 98 | The CreateBucket and CreateBucketIfNotExists functions on the Bucket interface 99 | provide the ability to create an arbitrary number of nested buckets. It is 100 | a good idea to avoid a lot of buckets with little data in them as it could lead 101 | to poor page utilization depending on the specific driver in use. 102 | */ 103 | package walletdb 104 | -------------------------------------------------------------------------------- /sample-btcwallet.conf: -------------------------------------------------------------------------------- 1 | [Application Options] 2 | 3 | ; ------------------------------------------------------------------------------ 4 | ; Bitcoin wallet settings 5 | ; ------------------------------------------------------------------------------ 6 | 7 | ; Use testnet (cannot be used with simnet=1). 8 | ; testnet=0 9 | 10 | ; Use simnet (cannot be used with testnet=1). 11 | ; simnet=0 12 | 13 | ; The directory to open and save wallet, transaction, and unspent transaction 14 | ; output files. Two directories, `mainnet` and `testnet` are used in this 15 | ; directory for mainnet and testnet wallets, respectively. 16 | ; appdata=~/.btcwallet 17 | 18 | 19 | ; ------------------------------------------------------------------------------ 20 | ; RPC client settings 21 | ; ------------------------------------------------------------------------------ 22 | 23 | ; Connect via a SOCKS5 proxy. NOTE: Specifying a proxy will disable listening 24 | ; for incoming connections unless listen addresses are provided via the 25 | ; 'rpclisten' option. 26 | ; proxy=127.0.0.1:9050 27 | ; proxyuser= 28 | ; proxypass= 29 | 30 | ; The server and port used for btcd websocket connections. 31 | ; rpcconnect=localhost:18334 32 | 33 | ; File containing root certificates to authenticate a TLS connections with btcd 34 | ; cafile=~/.btcwallet/btcd.cert 35 | 36 | 37 | 38 | ; ------------------------------------------------------------------------------ 39 | ; RPC server settings 40 | ; ------------------------------------------------------------------------------ 41 | 42 | ; TLS certificate and key file locations 43 | ; rpccert=~/.btcwallet/rpc.cert 44 | ; rpckey=~/.btcwallet/rpc.key 45 | 46 | ; Enable one time TLS keys. This option results in the process generating 47 | ; a new certificate pair each startup, writing only the certificate file 48 | ; to disk. This is a more secure option for clients that only interact with 49 | ; a local wallet process where persistent certs are not needed. 50 | ; 51 | ; This option will error at startup if the key specified by the rpckey option 52 | ; already exists. 53 | ; onetimetlskey=0 54 | 55 | ; Specify the interfaces for the RPC server listen on. One rpclisten address 56 | ; per line. Multiple rpclisten options may be set in the same configuration, 57 | ; and each will be used to listen for connections. NOTE: The default port is 58 | ; modified by some options such as 'testnet', so it is recommended to not 59 | ; specify a port and allow a proper default to be chosen unless you have a 60 | ; specific reason to do otherwise. 61 | ; rpclisten= ; all interfaces on default port 62 | ; rpclisten=0.0.0.0 ; all ipv4 interfaces on default port 63 | ; rpclisten=:: ; all ipv6 interfaces on default port 64 | ; rpclisten=:8332 ; all interfaces on port 8332 65 | ; rpclisten=0.0.0.0:8332 ; all ipv4 interfaces on port 8332 66 | ; rpclisten=[::]:8332 ; all ipv6 interfaces on port 8332 67 | ; rpclisten=127.0.0.1:8332 ; only ipv4 localhost on port 8332 (this is a default) 68 | ; rpclisten=[::1]:8332 ; only ipv6 localhost on port 8332 (this is a default) 69 | ; rpclisten=127.0.0.1:8337 ; only ipv4 localhost on non-standard port 8337 70 | ; rpclisten=:8337 ; all interfaces on non-standard port 8337 71 | ; rpclisten=0.0.0.0:8337 ; all ipv4 interfaces on non-standard port 8337 72 | ; rpclisten=[::]:8337 ; all ipv6 interfaces on non-standard port 8337 73 | 74 | ; Legacy (Bitcoin Core-compatible) RPC listener addresses. Addresses without a 75 | ; port specified use the same default port as the new server. Listeners cannot 76 | ; be shared between both RPC servers. 77 | ; 78 | ; Adding any legacy RPC listen addresses disable all default rpclisten options. 79 | ; If both servers must run, all listen addresses must be manually specified for 80 | ; each. 81 | ; legacyrpclisten= 82 | 83 | 84 | 85 | ; ------------------------------------------------------------------------------ 86 | ; RPC settings (both client and server) 87 | ; ------------------------------------------------------------------------------ 88 | 89 | ; Username and password to authenticate to btcd a RPC server and authenticate 90 | ; new client connections 91 | ; username= 92 | ; password= 93 | 94 | ; Alternative username and password for btcd. If set, these will be used 95 | ; instead of the username and password set above for authentication to a 96 | ; btcd RPC server. 97 | ; btcdusername= 98 | ; btcdpassword= 99 | 100 | 101 | ; ------------------------------------------------------------------------------ 102 | ; Debug 103 | ; ------------------------------------------------------------------------------ 104 | 105 | ; Debug logging level. 106 | ; Valid options are {trace, debug, info, warn, error, critical} 107 | ; debuglevel=info 108 | 109 | ; The port used to listen for HTTP profile requests. The profile server will 110 | ; be disabled if this option is not specified. The profile information can be 111 | ; accessed at http://localhost:/debug/pprof once running. 112 | ; profile=6062 113 | -------------------------------------------------------------------------------- /waddrmgr/tlv_test.go: -------------------------------------------------------------------------------- 1 | package waddrmgr 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/btcsuite/btcd/btcec/v2/schnorr" 7 | "github.com/btcsuite/btcd/txscript" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | var ( 12 | testPubKey, _ = schnorr.ParsePubKey(hexToBytes( 13 | "29faddf1254d490d6add49e2b08cf52b561038c72baec0edb3cfacff71" + 14 | "ff1021", 15 | )) 16 | testScript = []byte{99, 88, 77, 66, 55, 44} 17 | testProof = [32]byte{99, 88, 77, 66} 18 | ) 19 | 20 | // TestTlvEncodeDecode tests encoding and decoding of taproot script TLV data. 21 | func TestTlvEncodeDecode(t *testing.T) { 22 | t.Parallel() 23 | 24 | testCases := []struct { 25 | name string 26 | given *Tapscript 27 | expected *Tapscript 28 | expectedErrEncode string 29 | expectedErrDecode string 30 | }{{ 31 | name: "nil", 32 | expectedErrEncode: "cannot encode nil script", 33 | }, { 34 | name: "empty", 35 | given: &Tapscript{}, 36 | expected: &Tapscript{}, 37 | }, { 38 | name: "no leaves", 39 | given: &Tapscript{ 40 | Type: TapscriptTypeFullTree, 41 | ControlBlock: &txscript.ControlBlock{ 42 | InternalKey: testPubKey, 43 | }, 44 | }, 45 | expected: &Tapscript{ 46 | Type: TapscriptTypeFullTree, 47 | ControlBlock: &txscript.ControlBlock{ 48 | InternalKey: testPubKey, 49 | InclusionProof: []byte{}, 50 | }, 51 | }, 52 | }, { 53 | name: "no pubkey", 54 | given: &Tapscript{ 55 | Type: TapscriptTypeFullTree, 56 | ControlBlock: &txscript.ControlBlock{}, 57 | }, 58 | expectedErrEncode: "control block is missing internal key", 59 | }, { 60 | name: "empty leaf", 61 | given: &Tapscript{ 62 | Type: TapscriptTypeFullTree, 63 | ControlBlock: &txscript.ControlBlock{ 64 | InternalKey: testPubKey, 65 | }, 66 | Leaves: []txscript.TapLeaf{{}}, 67 | }, 68 | expected: &Tapscript{ 69 | Type: TapscriptTypeFullTree, 70 | ControlBlock: &txscript.ControlBlock{ 71 | InternalKey: testPubKey, 72 | InclusionProof: []byte{}, 73 | }, 74 | Leaves: []txscript.TapLeaf{{}}, 75 | }, 76 | }, { 77 | name: "full key and leaves", 78 | given: &Tapscript{ 79 | Type: TapscriptTypeFullTree, 80 | ControlBlock: &txscript.ControlBlock{ 81 | InternalKey: testPubKey, 82 | }, 83 | Leaves: []txscript.TapLeaf{ 84 | txscript.NewBaseTapLeaf(testScript), 85 | }, 86 | }, 87 | expected: &Tapscript{ 88 | Type: TapscriptTypeFullTree, 89 | ControlBlock: &txscript.ControlBlock{ 90 | InternalKey: testPubKey, 91 | InclusionProof: []byte{}, 92 | }, 93 | Leaves: []txscript.TapLeaf{ 94 | txscript.NewBaseTapLeaf(testScript), 95 | }, 96 | }, 97 | }, { 98 | name: "invalid proof", 99 | given: &Tapscript{ 100 | Type: TapscriptTypePartialReveal, 101 | ControlBlock: &txscript.ControlBlock{ 102 | InternalKey: testPubKey, 103 | InclusionProof: testScript, 104 | }, 105 | RevealedScript: testScript, 106 | }, 107 | expectedErrDecode: "error decoding control block: control " + 108 | "block proof is not a multiple of 32: 6", 109 | }, { 110 | name: "inclusion proof no leaves", 111 | given: &Tapscript{ 112 | Type: TapscriptTypePartialReveal, 113 | ControlBlock: &txscript.ControlBlock{ 114 | InternalKey: testPubKey, 115 | InclusionProof: testProof[:], 116 | }, 117 | RevealedScript: testScript, 118 | }, 119 | expected: &Tapscript{ 120 | Type: TapscriptTypePartialReveal, 121 | ControlBlock: &txscript.ControlBlock{ 122 | InternalKey: testPubKey, 123 | InclusionProof: testProof[:], 124 | }, 125 | RevealedScript: testScript, 126 | }, 127 | }, { 128 | name: "root hash only", 129 | given: &Tapscript{ 130 | Type: TaprootKeySpendRootHash, 131 | ControlBlock: &txscript.ControlBlock{ 132 | InternalKey: testPubKey, 133 | }, 134 | RootHash: testScript, 135 | }, 136 | expected: &Tapscript{ 137 | Type: TaprootKeySpendRootHash, 138 | ControlBlock: &txscript.ControlBlock{ 139 | InternalKey: testPubKey, 140 | InclusionProof: []byte{}, 141 | }, 142 | RootHash: testScript, 143 | }, 144 | }, { 145 | name: "full key only", 146 | given: &Tapscript{ 147 | Type: TapscriptTypeFullTree, 148 | FullOutputKey: testPubKey, 149 | }, 150 | expected: &Tapscript{ 151 | Type: TapscriptTypeFullTree, 152 | FullOutputKey: testPubKey, 153 | }, 154 | }} 155 | 156 | for _, tc := range testCases { 157 | tc := tc 158 | 159 | t.Run(tc.name, func(tt *testing.T) { 160 | data, err := tlvEncodeTaprootScript(tc.given) 161 | 162 | if tc.expectedErrEncode != "" { 163 | require.Error(tt, err) 164 | require.Contains( 165 | tt, err.Error(), tc.expectedErrEncode, 166 | ) 167 | 168 | return 169 | } 170 | 171 | require.NoError(tt, err) 172 | require.NotEmpty(tt, data) 173 | 174 | decoded, err := tlvDecodeTaprootTaprootScript(data) 175 | if tc.expectedErrDecode != "" { 176 | require.Error(tt, err) 177 | require.Contains( 178 | tt, err.Error(), tc.expectedErrDecode, 179 | ) 180 | 181 | return 182 | } 183 | 184 | require.NoError(tt, err) 185 | 186 | require.Equal(tt, tc.expected, decoded) 187 | }) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /walletdb/migration/manager.go: -------------------------------------------------------------------------------- 1 | package migration 2 | 3 | import ( 4 | "errors" 5 | "sort" 6 | 7 | "github.com/btcsuite/btcwallet/walletdb" 8 | ) 9 | 10 | var ( 11 | // ErrReversion is an error returned when an attempt to revert to a 12 | // previous version is detected. This is done to provide safety to users 13 | // as some upgrades may not be backwards-compatible. 14 | ErrReversion = errors.New("reverting to a previous version is not " + 15 | "supported") 16 | ) 17 | 18 | // Version denotes the version number of the database. A migration can be used 19 | // to bring a previous version of the database to a later one. 20 | type Version struct { 21 | // Number represents the number of this version. 22 | Number uint32 23 | 24 | // Migration represents a migration function that modifies the database 25 | // state. Care must be taken so that consequent migrations build off of 26 | // the previous one in order to ensure the consistency of the database. 27 | Migration func(walletdb.ReadWriteBucket) error 28 | } 29 | 30 | // Manager is an interface that exposes the necessary methods needed in order to 31 | // migrate/upgrade a service. Each service (i.e., an implementation of this 32 | // interface) can then use the Upgrade function to perform any required database 33 | // migrations. 34 | type Manager interface { 35 | // Name returns the name of the service we'll be attempting to upgrade. 36 | Name() string 37 | 38 | // Namespace returns the top-level bucket of the service. 39 | Namespace() walletdb.ReadWriteBucket 40 | 41 | // CurrentVersion returns the current version of the service's database. 42 | CurrentVersion(walletdb.ReadBucket) (uint32, error) 43 | 44 | // SetVersion sets the version of the service's database. 45 | SetVersion(walletdb.ReadWriteBucket, uint32) error 46 | 47 | // Versions returns all of the available database versions of the 48 | // service. 49 | Versions() []Version 50 | } 51 | 52 | // GetLatestVersion returns the latest version available from the given slice. 53 | func GetLatestVersion(versions []Version) uint32 { 54 | if len(versions) == 0 { 55 | return 0 56 | } 57 | 58 | // Before determining the latest version number, we'll sort the slice to 59 | // ensure it reflects the last element. 60 | sort.Slice(versions, func(i, j int) bool { 61 | return versions[i].Number < versions[j].Number 62 | }) 63 | 64 | return versions[len(versions)-1].Number 65 | } 66 | 67 | // VersionsToApply determines which versions should be applied as migrations 68 | // based on the current version. 69 | func VersionsToApply(currentVersion uint32, versions []Version) []Version { 70 | // Assuming the migration versions are in increasing order, we'll apply 71 | // any migrations that have a version number lower than our current one. 72 | var upgradeVersions []Version 73 | for _, version := range versions { 74 | if version.Number > currentVersion { 75 | upgradeVersions = append(upgradeVersions, version) 76 | } 77 | } 78 | 79 | // Before returning, we'll sort the slice by its version number to 80 | // ensure the migrations are applied in their intended order. 81 | sort.Slice(upgradeVersions, func(i, j int) bool { 82 | return upgradeVersions[i].Number < upgradeVersions[j].Number 83 | }) 84 | 85 | return upgradeVersions 86 | } 87 | 88 | // Upgrade attempts to upgrade a group of services exposed through the Manager 89 | // interface. Each service will go through its available versions and determine 90 | // whether it needs to apply any. 91 | // 92 | // NOTE: In order to guarantee fault-tolerance, each service upgrade should 93 | // happen within the same database transaction. 94 | func Upgrade(mgrs ...Manager) error { 95 | for _, mgr := range mgrs { 96 | if err := upgrade(mgr); err != nil { 97 | return err 98 | } 99 | } 100 | 101 | return nil 102 | } 103 | 104 | // upgrade attempts to upgrade a service expose through its implementation of 105 | // the Manager interface. This function will determine whether any new versions 106 | // need to be applied based on the service's current version and latest 107 | // available one. 108 | func upgrade(mgr Manager) error { 109 | // We'll start by fetching the service's current and latest version. 110 | ns := mgr.Namespace() 111 | currentVersion, err := mgr.CurrentVersion(ns) 112 | if err != nil { 113 | return err 114 | } 115 | versions := mgr.Versions() 116 | latestVersion := GetLatestVersion(versions) 117 | 118 | switch { 119 | // If the current version is greater than the latest, then the service 120 | // is attempting to revert to a previous version that's possibly 121 | // backwards-incompatible. To prevent this, we'll return an error 122 | // indicating so. 123 | case currentVersion > latestVersion: 124 | return ErrReversion 125 | 126 | // If the current version is behind the latest version, we'll need to 127 | // apply all of the newer versions in order to catch up to the latest. 128 | case currentVersion < latestVersion: 129 | versions := VersionsToApply(currentVersion, versions) 130 | mgrName := mgr.Name() 131 | ns := mgr.Namespace() 132 | 133 | for _, version := range versions { 134 | log.Infof("Applying %v migration #%d", mgrName, 135 | version.Number) 136 | 137 | // We'll only run a migration if there is one available 138 | // for this version. 139 | if version.Migration != nil { 140 | err := version.Migration(ns) 141 | if err != nil { 142 | log.Errorf("Unable to apply %v "+ 143 | "migration #%d: %v", mgrName, 144 | version.Number, err) 145 | return err 146 | } 147 | } 148 | } 149 | 150 | // With all of the versions applied, we can now reflect the 151 | // latest version upon the service. 152 | if err := mgr.SetVersion(ns, latestVersion); err != nil { 153 | return err 154 | } 155 | 156 | // If the current version matches the latest one, there's no upgrade 157 | // needed and we can safely exit. 158 | case currentVersion == latestVersion: 159 | } 160 | 161 | return nil 162 | } 163 | --------------------------------------------------------------------------------