diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 4b970a0bea..141f8a58b0 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -337,16 +337,6 @@ "Comment": "v1.0.0", "Rev": "d2cf3cdd35ce0d789056c4bc02a4d6349c947caf" }, - { - "ImportPath": "github.com/cpacia/BitcoinCash-Wallet", - "Comment": "v0.3.0-59-g4c3046e", - "Rev": "4c3046e2214e1761986809a54c3179f9a1d9cfd5" - }, - { - "ImportPath": "github.com/cpacia/BitcoinCash-Wallet/exchangerates", - "Comment": "v0.3.0-59-g4c3046e", - "Rev": "4c3046e2214e1761986809a54c3179f9a1d9cfd5" - }, { "ImportPath": "github.com/cpacia/bchutil", "Rev": "b126f6a35b6c2968c0877cb4d2ac5dcf67682d27" diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/.gitignore b/vendor/github.com/cpacia/BitcoinCash-Wallet/.gitignore deleted file mode 100644 index 66c9067146..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof -.idea/ -*.iml -.gx/ diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/.travis.yml b/vendor/github.com/cpacia/BitcoinCash-Wallet/.travis.yml deleted file mode 100644 index 6173aadd21..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: go -go: - - 1.7 -sudo: required -services: - - docker -env: - - "PATH=/home/travis/gopath/bin:$PATH" -before_install: - - go get github.com/tcnksm/ghr - - go get github.com/axw/gocov/gocov - - go get github.com/mattn/goveralls -script: - - diff -u <(echo -n) <(gofmt -d -s $(find . -type f -name '*.go' -not -path "./cmd/spvwallet/vendor/*" -not -path "./gui/resources.go" )) - - cd $TRAVIS_BUILD_DIR && chmod a+x test_compile.sh && ./test_compile.sh - - goveralls -coverprofile=coverage.out -service travis-ci \ No newline at end of file diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/LICENSE b/vendor/github.com/cpacia/BitcoinCash-Wallet/LICENSE deleted file mode 100644 index 1992900d2e..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (C) 2015-2016 The Lightning Network Developers -Copyright (c) 2016-2017 The OpenBazaar Developers - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/README.md b/vendor/github.com/cpacia/BitcoinCash-Wallet/README.md deleted file mode 100644 index 3429b6bfca..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Bitcoin Cash Wallet - - - -This is a fork of https://github.com/OpenBazaar/spvwallet modfied for Bitcoin Cash. It includes a fully functional GUI wallet and CLI. - -It uses stock btcd plus a few cash specific modifications found in the [bchutil](https://github.com/cpacia/bchutil) package. - -To compile and run: -```bash -go get github.com/cpacia/BitcoinCash-Wallet -make install -bitcoincash -``` -Or download a pre-compiled version from [releases](https://github.com/cpacia/BitcoinCash-Wallet/releases). diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/blockchain.go b/vendor/github.com/cpacia/BitcoinCash-Wallet/blockchain.go deleted file mode 100644 index adaa0aaad5..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/blockchain.go +++ /dev/null @@ -1,488 +0,0 @@ -// Copyright (C) 2015-2016 The Lightning Network Developers -// Copyright (c) 2016-2017 The OpenBazaar Developers - -package bitcoincash - -import ( - "errors" - "fmt" - "github.com/btcsuite/btcd/blockchain" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - "math/big" - "sort" - "sync" - "time" -) - -// Blockchain settings. These are kindof Bitcoin specific, but not contained in -// chaincfg.Params so they'll go here. If you're into the [ANN]altcoin scene, -// you may want to paramaterize these constants. -const ( - targetSpacing = 600 - medianTimeBlocks = 11 -) - -var OrphanHeaderError = errors.New("header does not extend any known headers") - -// Wrapper around Headers implementation that handles all blockchain operations -type Blockchain struct { - lock *sync.Mutex - params *chaincfg.Params - db Headers - crationDate time.Time - checkpoint Checkpoint -} - -func NewBlockchain(filePath string, walletCreationDate time.Time, params *chaincfg.Params) (*Blockchain, error) { - hdb, err := NewHeaderDB(filePath) - if err != nil { - return nil, err - } - b := &Blockchain{ - lock: new(sync.Mutex), - params: params, - db: hdb, - crationDate: walletCreationDate, - } - b.checkpoint = GetCheckpoint(walletCreationDate, params) - - h, err := b.db.Height() - if h == 0 || err != nil { - log.Info("Initializing headers db with checkpoints") - // Put the checkpoint to the db - sh := StoredHeader{ - header: b.checkpoint.Header, - height: b.checkpoint.Height, - totalWork: big.NewInt(0), - } - err := b.db.Put(sh, true) - if err != nil { - return nil, err - } - } - return b, nil -} - -func (b *Blockchain) CommitHeader(header wire.BlockHeader) (bool, *StoredHeader, uint32, error) { - b.lock.Lock() - defer b.lock.Unlock() - newTip := false - var commonAncestor *StoredHeader - // Fetch our current best header from the db - bestHeader, err := b.db.GetBestHeader() - if err != nil { - return false, nil, 0, err - } - tipHash := bestHeader.header.BlockHash() - var parentHeader StoredHeader - - // If the tip is also the parent of this header, then we can save a database read by skipping - // the lookup of the parent header. Otherwise (ophan?) we need to fetch the parent. - if header.PrevBlock.IsEqual(&tipHash) { - parentHeader = bestHeader - } else { - parentHeader, err = b.db.GetPreviousHeader(header) - if err != nil { - return false, nil, 0, fmt.Errorf("Header %s does not extend any known headers", header.BlockHash().String()) - } - } - valid := b.CheckHeader(header, parentHeader) - if !valid { - return false, nil, 0, nil - } - // If this block is already the tip, return - headerHash := header.BlockHash() - if tipHash.IsEqual(&headerHash) { - return newTip, nil, 0, nil - } - // Add the work of this header to the total work stored at the previous header - cumulativeWork := new(big.Int).Add(parentHeader.totalWork, blockchain.CalcWork(header.Bits)) - - // If the cumulative work is greater than the total work of our best header - // then we have a new best header. Update the chain tip and check for a reorg. - if cumulativeWork.Cmp(bestHeader.totalWork) == 1 { - newTip = true - prevHash := parentHeader.header.BlockHash() - // If this header is not extending the previous best header then we have a reorg. - if !tipHash.IsEqual(&prevHash) { - commonAncestor, err = b.GetCommonAncestor(StoredHeader{header: header, height: parentHeader.height + 1}, bestHeader) - if err != nil { - log.Errorf("Error calculating common ancestor: %s", err.Error()) - return newTip, commonAncestor, 0, err - } - log.Warningf("REORG!!! REORG!!! REORG!!! At block %d, Wiped out %d blocks", int(bestHeader.height), int(bestHeader.height-commonAncestor.height)) - } - } - newHeight := parentHeader.height + 1 - // Put the header to the database - err = b.db.Put(StoredHeader{ - header: header, - height: newHeight, - totalWork: cumulativeWork, - }, newTip) - if err != nil { - return newTip, commonAncestor, 0, err - } - return newTip, commonAncestor, newHeight, nil -} - -func (b *Blockchain) CheckHeader(header wire.BlockHeader, prevHeader StoredHeader) bool { - height := prevHeader.height - - // Due to the rolling difficulty period our checkpoint block consists of a block and a hash of a block 146 blocks later - // During this period we can skip the validity checks as long as block checkpoint + 146 matches the hardcoded hash. - if height+1 <= b.checkpoint.Height+147 { - h := header.BlockHash() - if b.checkpoint.Check2 != nil && height+1 == b.checkpoint.Height+147 && !b.checkpoint.Check2.IsEqual(&h) { - return false - } - return true - } - - // Get hash of n-1 header - prevHash := prevHeader.header.BlockHash() - - // Check if headers link together. That whole 'blockchain' thing. - if prevHash.IsEqual(&header.PrevBlock) == false { - log.Errorf("Headers %d and %d don't link.\n", height, height+1) - return false - } - - // Check the header meets the difficulty requirement - if b.params.Name != chaincfg.RegressionNetParams.Name { // Don't need to check difficulty on regtest - diffTarget, err := b.calcRequiredWork(header, int32(height+1), prevHeader) - if err != nil { - log.Errorf("Error calclating difficulty", err) - return false - } - if header.Bits != diffTarget && b.params.Name == chaincfg.MainNetParams.Name { - log.Warningf("Block %d %s incorrect difficulty. Read %d, expect %d\n", - height+1, header.BlockHash().String(), header.Bits, diffTarget) - return false - } else if diffTarget == b.params.PowLimitBits && header.Bits > diffTarget && b.params.Name == chaincfg.TestNet3Params.Name { - log.Warningf("Block %d %s incorrect difficulty. Read %d, expect %d\n", - height+1, header.BlockHash().String(), header.Bits, diffTarget) - return false - } - } - - // Check if there's a valid proof of work. That whole "Bitcoin" thing. - if !checkProofOfWork(header, b.params) { - log.Debugf("Block %d bad proof of work.\n", height+1) - return false - } - - return true // it must have worked if there's no errors and got to the end. -} - -// Get the PoW target this block should meet. We may need to handle a difficulty adjustment -// or testnet difficulty rules. -func (b *Blockchain) calcRequiredWork(header wire.BlockHeader, height int32, prevHeader StoredHeader) (uint32, error) { - // Special difficulty rule for testnet - if b.params.ReduceMinDifficulty && header.Timestamp.After(prevHeader.header.Timestamp.Add(targetSpacing*2)) { - return b.params.PowLimitBits, nil - } - - suitableHeader, err := b.GetSuitableBlock(prevHeader) - if err != nil { - log.Error(err) - return 0, err - } - epoch, err := b.GetEpoch(prevHeader.header) - if err != nil { - log.Error(err) - return 0, err - } - return calcDiffAdjust(epoch, suitableHeader, b.params), nil -} - -func (b *Blockchain) CalcMedianTimePast(header wire.BlockHeader) (time.Time, error) { - timestamps := make([]int64, medianTimeBlocks) - numNodes := 0 - iterNode := StoredHeader{header: header} - var err error - - for i := 0; i < medianTimeBlocks; i++ { - numNodes++ - timestamps[i] = iterNode.header.Timestamp.Unix() - iterNode, err = b.db.GetPreviousHeader(iterNode.header) - if err != nil { - return time.Time{}, err - } - } - timestamps = timestamps[:numNodes] - sort.Sort(timeSorter(timestamps)) - medianTimestamp := timestamps[numNodes/2] - return time.Unix(medianTimestamp, 0), nil -} - -// Rollsback and grabs block n-144, n-145, and n-146, sorts them by timestamps and returns the middle header. -func (b *Blockchain) GetEpoch(hdr wire.BlockHeader) (StoredHeader, error) { - sh := StoredHeader{header: hdr} - var err error - for i := 0; i < 144; i++ { - sh, err = b.db.GetPreviousHeader(sh.header) - if err != nil { - return sh, err - } - } - oneFourtyFour := sh - sh, err = b.db.GetPreviousHeader(oneFourtyFour.header) - if err != nil { - return sh, err - } - oneFourtyFive := sh - sh, err = b.db.GetPreviousHeader(oneFourtyFive.header) - if err != nil { - return sh, err - } - oneFourtySix := sh - headers := []StoredHeader{oneFourtyFour, oneFourtyFive, oneFourtySix} - sort.Sort(blockSorter(headers)) - return headers[1], nil -} - -// Rollsback grabs the last two headers before this one. Sorts the three and returns the mid. -func (b *Blockchain) GetSuitableBlock(hdr StoredHeader) (StoredHeader, error) { - n := hdr - sh, err := b.db.GetPreviousHeader(hdr.header) - if err != nil { - return sh, err - } - n1 := sh - sh, err = b.db.GetPreviousHeader(n1.header) - if err != nil { - return sh, err - } - n2 := sh - headers := []StoredHeader{n, n1, n2} - sort.Sort(blockSorter(headers)) - return headers[1], nil -} - -func (b *Blockchain) GetNPrevBlockHashes(n int) []*chainhash.Hash { - var ret []*chainhash.Hash - hdr, err := b.db.GetBestHeader() - if err != nil { - return ret - } - tipSha := hdr.header.BlockHash() - ret = append(ret, &tipSha) - for i := 0; i < n-1; i++ { - hdr, err = b.db.GetPreviousHeader(hdr.header) - if err != nil { - return ret - } - shaHash := hdr.header.BlockHash() - ret = append(ret, &shaHash) - } - return ret -} - -func (b *Blockchain) GetBlockLocator() blockchain.BlockLocator { - var ret []*chainhash.Hash - parent, err := b.db.GetBestHeader() - if err != nil { - return ret - } - - rollback := func(parent StoredHeader, n int) (StoredHeader, error) { - for i := 0; i < n; i++ { - parent, err = b.db.GetPreviousHeader(parent.header) - if err != nil { - return parent, err - } - } - return parent, nil - } - - step := 1 - start := 0 - for { - if start >= 9 { - step *= 2 - start = 0 - } - hash := parent.header.BlockHash() - ret = append(ret, &hash) - if len(ret) == 500 { - break - } - parent, err = rollback(parent, step) - if err != nil { - break - } - start += 1 - } - return blockchain.BlockLocator(ret) -} - -// Returns last header before reorg point -func (b *Blockchain) GetCommonAncestor(bestHeader, prevBestHeader StoredHeader) (*StoredHeader, error) { - var err error - rollback := func(parent StoredHeader, n int) (StoredHeader, error) { - for i := 0; i < n; i++ { - parent, err = b.db.GetPreviousHeader(parent.header) - if err != nil { - return parent, err - } - } - return parent, nil - } - - majority := bestHeader - minority := prevBestHeader - if bestHeader.height > prevBestHeader.height { - majority, err = rollback(majority, int(bestHeader.height-prevBestHeader.height)) - if err != nil { - return nil, err - } - } else if prevBestHeader.height > bestHeader.height { - minority, err = rollback(minority, int(prevBestHeader.height-bestHeader.height)) - if err != nil { - return nil, err - } - } - - for { - majorityHash := majority.header.BlockHash() - minorityHash := minority.header.BlockHash() - if majorityHash.IsEqual(&minorityHash) { - return &majority, nil - } - majority, err = b.db.GetPreviousHeader(majority.header) - if err != nil { - return nil, err - } - minority, err = b.db.GetPreviousHeader(minority.header) - if err != nil { - return nil, err - } - } -} - -// Rollback the header database to the last header before time t. -// We shouldn't go back further than the checkpoint -func (b *Blockchain) Rollback(t time.Time) error { - b.lock.Lock() - defer b.lock.Unlock() - checkpoint := GetCheckpoint(b.crationDate, b.params) - checkPointHash := checkpoint.Header.BlockHash() - sh, err := b.db.GetBestHeader() - if err != nil { - return err - } - // If t is greater than the timestamp at the tip then do nothing - if sh.header.Timestamp.Before(t) { - return nil - } - // If the tip is our checkpoint then do nothing - checkHash := sh.header.BlockHash() - if checkHash.IsEqual(&checkPointHash) { - return nil - } - rollbackHeight := uint32(0) - for i := 0; i < 1000000000; i++ { - sh, err = b.db.GetPreviousHeader(sh.header) - if err != nil { - return err - } - checkHash := sh.header.BlockHash() - // If we rolled back to the checkpoint then stop here and set the checkpoint as the tip - if checkHash.IsEqual(&checkPointHash) { - rollbackHeight = checkpoint.Height - break - } - // If we hit a header created before t then stop here and set this header as the tip - if sh.header.Timestamp.Before(t) { - rollbackHeight = sh.height - break - } - } - err = b.db.DeleteAfter(rollbackHeight) - if err != nil { - return err - } - return b.db.Put(sh, true) -} - -func (b *Blockchain) BestBlock() (StoredHeader, error) { - sh, err := b.db.GetBestHeader() - if err != nil { - return StoredHeader{}, err - } - return sh, nil -} - -func (b *Blockchain) GetHeader(hash *chainhash.Hash) (StoredHeader, error) { - sh, err := b.db.GetHeader(*hash) - if err != nil { - return sh, err - } - return sh, nil -} - -func (b *Blockchain) Close() { - b.lock.Lock() - b.db.Close() -} - -// Verifies the header hashes into something lower than specified by the 4-byte bits field. -func checkProofOfWork(header wire.BlockHeader, p *chaincfg.Params) bool { - target := blockchain.CompactToBig(header.Bits) - - // The target must more than 0. Why can you even encode negative... - if target.Sign() <= 0 { - log.Debugf("Block target %064x is neagtive(??)\n", target.Bytes()) - return false - } - // The target must be less than the maximum allowed (difficulty 1) - if target.Cmp(p.PowLimit) > 0 { - log.Debugf("Block target %064x is "+ - "higher than max of %064x", target, p.PowLimit.Bytes()) - return false - } - // The header hash must be less than the claimed target in the header. - blockHash := header.BlockHash() - hashNum := blockchain.HashToBig(&blockHash) - if hashNum.Cmp(target) > 0 { - log.Debugf("Block hash %064x is higher than "+ - "required target of %064x", hashNum, target) - return false - } - return true -} - -// This function takes in a start and end block header and uses the timestamps in each -// to calculate how much of a difficulty adjustment is needed. It returns a new compact -// difficulty target. -func calcDiffAdjust(start, end StoredHeader, p *chaincfg.Params) uint32 { - work := new(big.Int).Sub(end.totalWork, start.totalWork) - - // In order to avoid difficulty cliffs, we bound the amplitude of the - // adjustement we are going to do. - duration := end.header.Timestamp.Unix() - start.header.Timestamp.Unix() - if duration > 288*int64(targetSpacing) { - duration = 288 * int64(targetSpacing) - } else if duration < 72*int64(targetSpacing) { - duration = 72 * int64(targetSpacing) - } - - projectedWork := new(big.Int).Mul(work, big.NewInt(int64(targetSpacing))) - - pw := new(big.Int).Div(projectedWork, big.NewInt(duration)) - - e := new(big.Int).Exp(big.NewInt(2), big.NewInt(256), nil) - - nt := new(big.Int).Sub(e, pw) - - newTarget := new(big.Int).Div(nt, pw) - - // clip again if above minimum target (too easy) - if newTarget.Cmp(p.PowLimit) > 0 { - newTarget.Set(p.PowLimit) - } - return blockchain.BigToCompact(newTarget) -} diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/checkpoints.go b/vendor/github.com/cpacia/BitcoinCash-Wallet/checkpoints.go deleted file mode 100644 index 67322f1af2..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/checkpoints.go +++ /dev/null @@ -1,85 +0,0 @@ -package bitcoincash - -import ( - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - "time" -) - -type Checkpoint struct { - Height uint32 - Header wire.BlockHeader - Check2 *chainhash.Hash -} - -var mainnetCheckpoints []Checkpoint -var testnet3Checkpoints []Checkpoint -var regtestCheckpoint Checkpoint - -func init() { - // Mainnet - mainnetPrev, _ := chainhash.NewHashFromStr("0000000000000000011ebf65b60d0a3de80b8175be709d653b4c1a1beeb6ab9c") - mainnetMerk, _ := chainhash.NewHashFromStr("8ebf2179d8b1ba0aaf5f15357f963b56f53a8c6207e0156b4b6def119be61bee") - check2, _ := chainhash.NewHashFromStr("000000000000000001250f09f253d22d6ef14e924eccb7c1bbaa0695269cef59") - mainnetCheckpoints = append(mainnetCheckpoints, Checkpoint{ - Height: 504032, - Header: wire.BlockHeader{ - Version: 536870912, - PrevBlock: *mainnetPrev, - MerkleRoot: *mainnetMerk, - Timestamp: time.Unix(1510606995, 0), - Bits: 403026987, - Nonce: 273755974, - }, - Check2: check2, - }) - if mainnetCheckpoints[0].Header.BlockHash().String() != "00000000000000000343e9875012f2062554c8752929892c82a0c0743ac7dcfd" { - panic("Invalid checkpoint") - } - - // Testnet3 - testnet3Prev, _ := chainhash.NewHashFromStr("00000000824633a21bc41dccbd7a6d159a4deebaece6f6dcf2093301aea040a5") - testnet3Merk, _ := chainhash.NewHashFromStr("d69264d97d77da1b9bf0ae031512a89e0607e8200be29a74163a39b8558f5714") - testnetCheck2, _ := chainhash.NewHashFromStr("00000000000044040acf28b1bab8706f09f7862275b65a03580b6db136ad2dbd") - testnet3Checkpoints = append(testnet3Checkpoints, Checkpoint{ - Height: 1189213, - Header: wire.BlockHeader{ - Version: 536870912, - PrevBlock: *testnet3Prev, - MerkleRoot: *testnet3Merk, - Timestamp: time.Unix(1510739152, 0), - Bits: 486604799, - Nonce: 2325788686, - }, - Check2: testnetCheck2, - }) - if testnet3Checkpoints[0].Header.BlockHash().String() != "000000001f734385476b82be8eb10512c9fb5bd1534cf3ceb4af2d47a7b20ff7" { - panic("Invalid checkpoint") - } - - // Regtest - regtestCheckpoint = Checkpoint{0, chaincfg.RegressionNetParams.GenesisBlock.Header, nil} -} - -func GetCheckpoint(walletCreationDate time.Time, params *chaincfg.Params) Checkpoint { - switch params.Name { - case chaincfg.MainNetParams.Name: - for i := len(mainnetCheckpoints) - 1; i >= 0; i-- { - if walletCreationDate.After(mainnetCheckpoints[i].Header.Timestamp) { - return mainnetCheckpoints[i] - } - } - return mainnetCheckpoints[0] - case chaincfg.TestNet3Params.Name: - for i := len(testnet3Checkpoints) - 1; i >= 0; i-- { - if walletCreationDate.After(testnet3Checkpoints[i].Header.Timestamp) { - return testnet3Checkpoints[i] - } - } - return testnet3Checkpoints[0] - - default: - return regtestCheckpoint - } -} diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/config.go b/vendor/github.com/cpacia/BitcoinCash-Wallet/config.go deleted file mode 100644 index bb8eaccaee..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/config.go +++ /dev/null @@ -1,108 +0,0 @@ -package bitcoincash - -import ( - "github.com/OpenBazaar/wallet-interface" - "github.com/btcsuite/btcd/chaincfg" - "github.com/mitchellh/go-homedir" - "github.com/op/go-logging" - "golang.org/x/net/proxy" - "net" - "net/url" - "os" - "path/filepath" - "runtime" - "time" -) - -type Config struct { - // Network parameters. Set mainnet, testnet, or regtest using this. - Params *chaincfg.Params - - // Bip39 mnemonic string. If empty a new mnemonic will be created. - Mnemonic string - - // The date the wallet was created. - // If before the earliest checkpoint the chain will be synced using the earliest checkpoint. - CreationDate time.Time - - // The user-agent that shall be visible to peers - UserAgent string - - // Location of the data directory - RepoPath string - - // An implementation of the Datastore interface - DB wallet.Datastore - - // If you wish to connect to a single trusted peer set this. Otherwise leave nil. - TrustedPeer net.Addr - - // A Tor proxy can be set here causing the wallet will use Tor - Proxy proxy.Dialer - - // The default fee-per-byte for each level - LowFee uint64 - MediumFee uint64 - HighFee uint64 - - // The highest allowable fee-per-byte - MaxFee uint64 - - // External API to query to look up fees. If this field is nil then the default fees will be used. - // If the API is unreachable then the default fees will likewise be used. If the API returns a fee - // greater than MaxFee then the MaxFee will be used in place. The API response must be formatted as - // { "fastestFee": 40, "halfHourFee": 20, "hourFee": 10 } - FeeAPI url.URL - - // A logger. You can write the logs to file or stdout or however else you want. - Logger logging.Backend - - // A slice of additional items to add to the bloom filter - AdditionalFilters [][]byte - - // Disable exchange rate provider - DisableExchangeRates bool -} - -func NewDefaultConfig() *Config { - repoPath, _ := getRepoPath() - _, ferr := os.Stat(repoPath) - if os.IsNotExist(ferr) { - os.Mkdir(repoPath, os.ModePerm) - } - feeApi, _ := url.Parse("https://bitcoinfees.21.co/api/v1/fees/recommended") - return &Config{ - Params: &chaincfg.MainNetParams, - UserAgent: "spvwallet", - RepoPath: repoPath, - LowFee: 140, - MediumFee: 160, - HighFee: 180, - MaxFee: 2000, - FeeAPI: *feeApi, - Logger: logging.NewLogBackend(os.Stdout, "", 0), - } -} - -func getRepoPath() (string, error) { - // Set default base path and directory name - path := "~" - directoryName := "bitcoincash" - - // Override OS-specific names - switch runtime.GOOS { - case "linux": - directoryName = ".bitcoincash" - case "darwin": - path = "~/Library/Application Support" - } - - // Join the path and directory name, then expand the home path - fullPath, err := homedir.Expand(filepath.Join(path, directoryName)) - if err != nil { - return "", err - } - - // Return the shortest lexical representation of the path - return filepath.Clean(fullPath), nil -} diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/eight333.go b/vendor/github.com/cpacia/BitcoinCash-Wallet/eight333.go deleted file mode 100644 index 93e7109bde..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/eight333.go +++ /dev/null @@ -1,777 +0,0 @@ -package bitcoincash - -import ( - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - peerpkg "github.com/btcsuite/btcd/peer" - "github.com/btcsuite/btcd/wire" - "net" - "time" -) - -const ( - maxRequestedTxns = wire.MaxInvPerMsg - maxFalsePositives = 7 -) - -// newPeerMsg signifies a newly connected peer to the block handler. -type newPeerMsg struct { - peer *peerpkg.Peer -} - -// donePeerMsg signifies a newly disconnected peer to the block handler. -type donePeerMsg struct { - peer *peerpkg.Peer -} - -// headersMsg packages a bitcoin headers message and the peer it came from -// together so the handler has access to that information. -type headersMsg struct { - headers *wire.MsgHeaders - peer *peerpkg.Peer -} - -// merkleBlockMsg packages a merkle block message and the peer it came from -// together so the handler has access to that information. -type merkleBlockMsg struct { - merkleBlock *wire.MsgMerkleBlock - peer *peerpkg.Peer -} - -// invMsg packages a bitcoin inv message and the peer it came from together -// so the handler has access to that information. -type invMsg struct { - inv *wire.MsgInv - peer *peerpkg.Peer -} - -type heightAndTime struct { - height uint32 - timestamp time.Time -} - -// txMsg packages a bitcoin tx message and the peer it came from together -// so the handler has access to that information. -type txMsg struct { - tx *wire.MsgTx - peer *peerpkg.Peer - reply chan struct{} -} - -type updateFiltersMsg struct{} - -type WireServiceConfig struct { - params *chaincfg.Params - chain *Blockchain - txStore *TxStore - walletCreationDate time.Time - minPeersForSync int -} - -// peerSyncState stores additional information that the WireService tracks -// about a peer. -type peerSyncState struct { - syncCandidate bool - requestQueue []*wire.InvVect - requestedTxns map[chainhash.Hash]heightAndTime - requestedBlocks map[chainhash.Hash]struct{} - falsePositives uint32 - blockScore int32 -} - -type WireService struct { - params *chaincfg.Params - chain *Blockchain - txStore *TxStore - walletCreationDate time.Time - syncPeer *peerpkg.Peer - peerStates map[*peerpkg.Peer]*peerSyncState - requestedTxns map[chainhash.Hash]heightAndTime - requestedBlocks map[chainhash.Hash]struct{} - mempool map[chainhash.Hash]struct{} - msgChan chan interface{} - quit chan struct{} - minPeersForSync int - zeroHash chainhash.Hash -} - -func NewWireService(config *WireServiceConfig) *WireService { - return &WireService{ - params: config.params, - chain: config.chain, - walletCreationDate: config.walletCreationDate, - minPeersForSync: config.minPeersForSync, - txStore: config.txStore, - peerStates: make(map[*peerpkg.Peer]*peerSyncState), - requestedTxns: make(map[chainhash.Hash]heightAndTime), - requestedBlocks: make(map[chainhash.Hash]struct{}), - mempool: make(map[chainhash.Hash]struct{}), - msgChan: make(chan interface{}), - } -} - -func (ws *WireService) MsgChan() chan interface{} { - return ws.msgChan -} - -// The start function must be run in its own goroutine. The entire WireService is single -// threaded which means all messages are processed sequentially removing the need for complex -// locking. -func (ws *WireService) Start() { - ws.quit = make(chan struct{}) - best, err := ws.chain.BestBlock() - if err != nil { - log.Error(err) - } - log.Infof("Starting wire service at height %d", int(best.height)) -out: - for { - select { - case m := <-ws.msgChan: - switch msg := m.(type) { - case newPeerMsg: - ws.handleNewPeerMsg(msg.peer) - case donePeerMsg: - ws.handleDonePeerMsg(msg.peer) - case headersMsg: - ws.handleHeadersMsg(&msg) - case merkleBlockMsg: - ws.handleMerkleBlockMsg(&msg) - case invMsg: - ws.handleInvMsg(&msg) - case txMsg: - ws.handleTxMsg(&msg) - case updateFiltersMsg: - ws.handleUpdateFiltersMsg() - default: - log.Warningf("Unknown message type sent to WireService message chan: %T", msg) - } - case <-ws.quit: - break out - } - } -} - -func (ws *WireService) Stop() { - ws.syncPeer = nil - close(ws.quit) -} - -func (ws *WireService) Resync() { - ws.startSync(ws.syncPeer) -} - -func (ws *WireService) handleNewPeerMsg(peer *peerpkg.Peer) { - // Initialize the peer state - ws.peerStates[peer] = &peerSyncState{ - syncCandidate: ws.isSyncCandidate(peer), - requestedTxns: make(map[chainhash.Hash]heightAndTime), - requestedBlocks: make(map[chainhash.Hash]struct{}), - } - - ws.updateFilterAndSend(peer) - - // If we don't have a sync peer and we are not current we should start a sync - if ws.syncPeer == nil && !ws.Current() { - ws.startSync(nil) - } -} - -// isSyncCandidate returns whether or not the peer is a candidate to consider -// syncing from. -func (ws *WireService) isSyncCandidate(peer *peerpkg.Peer) bool { - // Typically a peer is not a candidate for sync if it's not a full node, - // however regression test is special in that the regression tool is - // not a full node and still needs to be considered a sync candidate. - if ws.params.Name == chaincfg.RegressionNetParams.Name { - // The peer is not a candidate if it's not coming from localhost - // or the hostname can't be determined for some reason. - host, _, err := net.SplitHostPort(peer.Addr()) - if err != nil { - return false - } - - if host != "127.0.0.1" && host != "localhost" { - return false - } - } else { - // The peer is not a candidate for sync if it's not a full node - nodeServices := peer.Services() - if nodeServices&wire.SFNodeNetwork != wire.SFNodeNetwork { - return false - } - } - - // Candidate if all checks passed. - return true -} - -func (ws *WireService) startSync(syncPeer *peerpkg.Peer) { - // Wait for a minimum number of peers to connect. This makes sure we have a good - // selection to choose from before starting the sync. - if len(ws.peerStates) < ws.minPeersForSync { - return - } - ws.Rebroadcast() - bestBlock, err := ws.chain.BestBlock() - if err != nil { - log.Error(err) - return - } - var bestPeer *peerpkg.Peer - if syncPeer == nil { - var bestPeerHeight int32 - for peer, state := range ws.peerStates { - if !state.syncCandidate { - continue - } - - // Remove sync candidate peers that are no longer candidates due - // to passing their latest known block. NOTE: The < is - // intentional as opposed to <=. While technically the peer - // doesn't have a later block when it's equal, it will likely - // have one soon so it is a reasonable choice. It also allows - // the case where both are at 0 such as during regression test. - if peer.LastBlock() < int32(bestBlock.height) { - state.syncCandidate = false - continue - } - - // Select peer which is reporting the greatest height - if peer.LastBlock() > bestPeerHeight { - bestPeer = peer - bestPeerHeight = peer.LastBlock() - } - } - } else { - bestPeer = syncPeer - } - - // Start syncing this bitch - if bestPeer != nil { - // TODO: use checkpoints here - ws.syncPeer = bestPeer - - // Clear the requestedBlocks if the sync peer changes, otherwise - // we may ignore blocks we need that the last sync peer failed - // to send. - ws.requestedBlocks = make(map[chainhash.Hash]struct{}) - - locator := ws.chain.GetBlockLocator() - - // If the best header we have was created before this wallet then we can sync just headers - // up to the wallet creation date since we know there wont be any transactions in those - // blocks we're interested in. However, if we're past the wallet creation date we need to - // start downloading merkle blocks so we learn of the wallet's transactions. We'll use a - // buffer of one week to make sure we don't miss anything. - log.Infof("Starting chain download from %s", bestPeer) - if bestBlock.header.Timestamp.Before(ws.walletCreationDate.Add(-time.Hour * 24 * 7)) { - bestPeer.PushGetHeadersMsg(locator, &ws.zeroHash) - } else { - bestPeer.PushGetBlocksMsg(locator, &ws.zeroHash) - } - } else { - log.Warning("No sync candidates available") - } -} - -func (ws *WireService) Current() bool { - best, err := ws.chain.BestBlock() - if err != nil { - return false - } - - // If our best header's timestamp was more than 24 hours ago, we're probably not current - if best.header.Timestamp.Before(time.Now().Add(-24 * time.Hour)) { - return false - } - - // Check our other peers to see if any are reporting a greater height than we have - for peer := range ws.peerStates { - if int32(best.height) < peer.LastBlock() { - return false - } - } - return true -} - -func (ws *WireService) handleDonePeerMsg(peer *peerpkg.Peer) { - state, exists := ws.peerStates[peer] - if !exists { - return - } - - // Remove the peer from the list of candidate peers. - delete(ws.peerStates, peer) - - // Remove requested transactions from the global map so that they will - // be fetched from elsewhere next time we get an inv. - for txHash := range state.requestedTxns { - delete(ws.requestedTxns, txHash) - } - - // Remove requested blocks from the global map so that they will be - // fetched from elsewhere next time we get an inv. - // TODO: we could possibly here check which peers have these blocks - // and request them now to speed things up a little. - for blockHash := range state.requestedBlocks { - delete(ws.requestedBlocks, blockHash) - } - - // Attempt to find a new peer to sync from if the quitting peer is the - // sync peer. - if ws.syncPeer == peer && !ws.Current() { - log.Info("Sync peer disconnected") - ws.syncPeer = nil - ws.startSync(nil) - } -} - -// handleHeadersMsg handles block header messages from all peers. Headers are -// requested when performing a headers-first sync. -func (ws *WireService) handleHeadersMsg(hmsg *headersMsg) { - peer := hmsg.peer - if peer != ws.syncPeer { - log.Warning("Received header message from a peer that isn't our sync peer") - peer.Disconnect() - return - } - _, exists := ws.peerStates[peer] - if !exists { - log.Warningf("Received headers message from unknown peer %s", peer) - peer.Disconnect() - return - } - - msg := hmsg.headers - numHeaders := len(msg.Headers) - - // Nothing to do for an empty headers message - if numHeaders == 0 { - return - } - - // Process each header we received. Make sure when check that each one is before our - // wallet creation date (minus the buffer). If we pass the creation date we will exit - // request merkle blocks from this point forward and exit the function. - badHeaders := 0 - for _, blockHeader := range msg.Headers { - if blockHeader.Timestamp.Before(ws.walletCreationDate.Add(-time.Hour * 24 * 7)) { - _, _, height, err := ws.chain.CommitHeader(*blockHeader) - if err != nil { - badHeaders++ - log.Errorf("Commit header error: %s", err.Error()) - } - log.Infof("Received header %s at height %d", blockHeader.BlockHash().String(), height) - } else { - log.Info("Switching to downloading merkle blocks") - locator := ws.chain.GetBlockLocator() - peer.PushGetBlocksMsg(locator, &ws.zeroHash) - return - } - } - // Usually the peer will send the header at the tip of the chain in each batch. This will trigger - // one commit error so we'll consider that acceptable, but anything more than that suggests misbehavior - // so we'll dump this peer. - if badHeaders > 1 { - log.Warningf("Disconnecting from peer %s because he sent us too many bad headers", peer) - peer.Disconnect() - return - } - - // Request the next batch of headers - locator := ws.chain.GetBlockLocator() - err := peer.PushGetHeadersMsg(locator, &ws.zeroHash) - if err != nil { - log.Warningf("Failed to send getheaders message to peer %s: %v", peer.Addr(), err) - return - } -} - -// handleMerkleBlockMsg handles merkle block messages from all peers. Merkle blocks are -// requested in response to inv packets both during initial sync and after. -func (ws *WireService) handleMerkleBlockMsg(bmsg *merkleBlockMsg) { - peer := bmsg.peer - - // We don't need to process blocks when we're syncing. They wont connect anyway - if peer != ws.syncPeer && !ws.Current() { - log.Warningf("Received block from %s when we aren't current", peer) - return - } - state, exists := ws.peerStates[peer] - if !exists { - log.Warningf("Received merkle block message from unknown peer %s", peer) - peer.Disconnect() - return - } - - // If we didn't ask for this block then the peer is misbehaving. - merkleBlock := bmsg.merkleBlock - header := merkleBlock.Header - blockHash := header.BlockHash() - if _, exists = state.requestedBlocks[blockHash]; !exists { - // The regression test intentionally sends some blocks twice - // to test duplicate block insertion fails. Don't disconnect - // the peer or ignore the block when we're in regression test - // mode in this case so the chain code is actually fed the - // duplicate blocks. - if ws.params.Name != chaincfg.RegressionNetParams.Name { - log.Warningf("Got unrequested block %v from %s -- "+ - "disconnecting", blockHash, peer.Addr()) - peer.Disconnect() - return - } - } - - // Remove block from request maps. Either chain will know about it and - // so we shouldn't have any more instances of trying to fetch it, or we - // will fail the insert and thus we'll retry next time we get an inv. - delete(state.requestedBlocks, blockHash) - delete(ws.requestedBlocks, blockHash) - - txids, err := checkMBlock(merkleBlock) - if err != nil { - log.Warningf("Peer %s sent an invalid MerkleBlock", peer) - peer.Disconnect() - return - } - - newBlock, reorg, newHeight, err := ws.chain.CommitHeader(header) - // If this is an orphan block which doesn't connect to the chain, it's possible - // that we might be synced on the longest chain, but not the most-work chain like - // we should be. To make sure this isn't the case, let's sync from the peer who - // sent us this orphan block. - if err == OrphanHeaderError && ws.Current() { - log.Debug("Received orphan header, checking peer for more blocks") - state.requestQueue = []*wire.InvVect{} - state.requestedBlocks = make(map[chainhash.Hash]struct{}) - ws.requestedBlocks = make(map[chainhash.Hash]struct{}) - ws.startSync(peer) - return - } else if err == OrphanHeaderError && !ws.Current() { - // The sync peer sent us an orphan header in the middle of a sync. This could - // just be the last block in the batch which represents the tip of the chain. - // In either case let's adjust the score for this peer downwards. If it goes - // negative it means he's slamming us with blocks that don't fit in our chain - // so disconnect. - state.blockScore-- - if state.blockScore < 0 { - log.Warningf("Disconnecting from peer %s because he sent us too many bad blocks", peer) - peer.Disconnect() - return - } - log.Warningf("Received unrequested block from peer %s", peer) - return - } else if err != nil { - log.Error(err) - return - } - state.blockScore++ - - if ws.Current() { - peer.UpdateLastBlockHeight(int32(newHeight)) - } - - // Request the transactions in this block - for _, txid := range txids { - ws.requestedTxns[*txid] = heightAndTime{newHeight, header.Timestamp} - limitMap(ws.requestedTxns, maxRequestedTxns) - state.requestedTxns[*txid] = heightAndTime{newHeight, header.Timestamp} - } - - // We can exit here if the block is already known - if !newBlock { - log.Debugf("Received duplicate block %s", blockHash.String()) - return - } - - log.Infof("Received merkle block %s at height %d", blockHash.String(), newHeight) - - // Check reorg - if reorg != nil && ws.Current() { - // Rollback the appropriate transactions in our database - err := ws.txStore.processReorg(reorg.height) - if err != nil { - log.Error(err) - } - // Set the reorg block as current best block in the header db - // This will cause a new chain sync from the reorg point - err = ws.chain.db.Put(*reorg, true) - if err != nil { - log.Error(err) - } - - // Clear request state for new sync - state.requestQueue = []*wire.InvVect{} - state.requestedBlocks = make(map[chainhash.Hash]struct{}) - ws.requestedBlocks = make(map[chainhash.Hash]struct{}) - } - - // Clear mempool - ws.mempool = make(map[chainhash.Hash]struct{}) - - // If we're not current and we've downloaded everything we've requested send another getblocks message. - // Otherwise we'll request the next block in the queue. - if !ws.Current() && len(state.requestQueue) == 0 { - locator := ws.chain.GetBlockLocator() - peer.PushGetBlocksMsg(locator, &ws.zeroHash) - log.Debug("Request queue at zero. Pushing new locator") - } else if !ws.Current() && len(state.requestQueue) > 0 { - iv := state.requestQueue[0] - iv.Type = wire.InvTypeFilteredBlock - state.requestQueue = state.requestQueue[1:] - state.requestedBlocks[iv.Hash] = struct{}{} - gdmsg2 := wire.NewMsgGetData() - gdmsg2.AddInvVect(iv) - peer.QueueMessage(gdmsg2, nil) - log.Debugf("Requesting block %s, len request queue: %d", iv.Hash.String(), len(state.requestQueue)) - } -} - -// handleInvMsg handles inv messages from all peers. -// We examine the inventory advertised by the remote peer and act accordingly. -func (ws *WireService) handleInvMsg(imsg *invMsg) { - peer := imsg.peer - state, exists := ws.peerStates[peer] - if !exists { - log.Warningf("Received inv message from unknown peer %s", peer) - return - } - - // Attempt to find the final block in the inventory list. There may - // not be one. - lastBlock := -1 - invVects := imsg.inv.InvList - for i := len(invVects) - 1; i >= 0; i-- { - if invVects[i].Type == wire.InvTypeBlock { - lastBlock = i - break - } - } - - // If this inv contains a block announcement, and this isn't coming from - // our current sync peer or we're current, then update the last - // announced block for this peer. We'll use this information later to - // update the heights of peers based on blocks we've accepted that they - // previously announced. - if lastBlock != -1 && (peer != ws.syncPeer || ws.Current()) { - peer.UpdateLastAnnouncedBlock(&invVects[lastBlock].Hash) - } - - // Ignore invs from peers that aren't the sync if we are not current. - // Helps prevent fetching a mass of orphans. - if peer != ws.syncPeer && !ws.Current() { - return - } - - // If our chain is current and a peer announces a block we already - // know of, then update their current block height. - if lastBlock != -1 && ws.Current() { - sh, err := ws.chain.GetHeader(&invVects[lastBlock].Hash) - if err == nil { - peer.UpdateLastBlockHeight(int32(sh.height)) - } - } - - // Request the advertised inventory if we don't already have it - gdmsg := wire.NewMsgGetData() - numRequested := 0 - shouldSendGetData := false - if len(state.requestQueue) == 0 { - shouldSendGetData = true - } - for _, iv := range invVects { - - // Add the inventory to the cache of known inventory - // for the peer. - peer.AddKnownInventory(iv) - - // Request the inventory if we don't already have it. - haveInv, err := ws.haveInventory(iv) - if err != nil { - log.Warningf("Unexpected failure when checking for "+ - "existing inventory during inv message "+ - "processing: %v", err) - continue - } - - switch iv.Type { - case wire.InvTypeFilteredBlock: - fallthrough - case wire.InvTypeBlock: - // Block inventory goes into a request queue to be downloaded - // one at a time. Sadly we can't batch these because the remote - // peer will not update the bloom filter until he's done processing - // the batch which means we will have a super high false positive rate. - if _, exists := ws.requestedBlocks[iv.Hash]; (!ws.Current() && !exists && !haveInv && shouldSendGetData) || ws.Current() { - iv.Type = wire.InvTypeFilteredBlock - state.requestQueue = append(state.requestQueue, iv) - } - case wire.InvTypeTx: - // Transaction inventory can be requested in batches - if _, exists := ws.requestedTxns[iv.Hash]; !exists && numRequested < wire.MaxInvPerMsg && !haveInv { - ws.requestedTxns[iv.Hash] = heightAndTime{0, time.Now()} // unconfirmed tx - limitMap(ws.requestedTxns, maxRequestedTxns) - state.requestedTxns[iv.Hash] = heightAndTime{0, time.Now()} - - gdmsg.AddInvVect(iv) - numRequested++ - } - default: - continue - } - } - - // Pop the first block off the queue and request it - if len(state.requestQueue) > 0 && (shouldSendGetData || ws.Current()) { - iv := state.requestQueue[0] - gdmsg.AddInvVect(iv) - if len(state.requestQueue) > 1 { - state.requestQueue = state.requestQueue[1:] - } else { - state.requestQueue = []*wire.InvVect{} - } - log.Debugf("Requesting block %s, len request queue: %d", iv.Hash.String(), len(state.requestQueue)) - state.requestedBlocks[iv.Hash] = struct{}{} - } - if len(gdmsg.InvList) > 0 { - peer.QueueMessage(gdmsg, nil) - } -} - -func (ws *WireService) handleTxMsg(tmsg *txMsg) { - tx := tmsg.tx - peer := tmsg.peer - txHash := tx.TxHash() - - state, exists := ws.peerStates[peer] - if !exists { - log.Warningf("Received tx message from unknown peer %s", peer) - return - } - ht, ok := state.requestedTxns[tx.TxHash()] - if !ok { - log.Warningf("Peer %s is sending us transactions we didn't request", peer) - peer.Disconnect() - return - } - ws.mempool[txHash] = struct{}{} - hits, err := ws.txStore.Ingest(tx, int32(ht.height), ht.timestamp) - if err != nil { - log.Errorf("Error ingesting tx: %s\n", err.Error()) - } - - // Remove transaction from request maps. Either the mempool/chain - // already knows about it and as such we shouldn't have any more - // instances of trying to fetch it, or we failed to insert and thus - // we'll retry next time we get an inv. - delete(state.requestedTxns, txHash) - delete(ws.requestedTxns, txHash) - - // If this transaction had no hits, update the peer's false positive counter - if hits == 0 { - log.Debugf("Tx %s from Peer%d had no hits, filter false positive.", txHash.String(), peer.ID()) - state.falsePositives++ - } else { - log.Noticef("Ingested new tx %s at height %d", txHash.String(), ht.height) - } - - // Check to see if false positives exceeds the maximum allowed. If so, reset and resend the filter. - if state.falsePositives > maxFalsePositives { - state.falsePositives = 0 - ws.updateFilterAndSend(peer) - } -} - -func (ws *WireService) Rebroadcast() { - // get all unconfirmed txs - invMsg, err := ws.txStore.GetPendingInv() - if err != nil { - log.Errorf("Rebroadcast error: %s", err.Error()) - } - // Nothing to broadcast, so don't - if len(invMsg.InvList) == 0 { - return - } - for peer := range ws.peerStates { - peer.QueueMessage(invMsg, nil) - } -} - -func (ws *WireService) updateFilterAndSend(peer *peerpkg.Peer) { - if ws.txStore != nil { - filter, err := ws.txStore.GimmeFilter() - if err == nil { - peer.QueueMessage(filter.MsgFilterLoad(), nil) - } else { - log.Errorf("Error loading bloom filter: %s", err.Error()) - } - } -} - -// handleUpdateFiltersMsg sends a filter update message to all peers -func (ws *WireService) handleUpdateFiltersMsg() { - for peer := range ws.peerStates { - ws.updateFilterAndSend(peer) - } -} - -// haveInventory returns whether or not the inventory represented by the passed -// inventory vector is known. This includes checking all of the various places -// inventory can be when it is in different states such as blocks that are part -// of the main chain, on a side chain, in the orphan pool, and transactions that -// are in the memory pool (either the main pool or orphan pool). -func (ws *WireService) haveInventory(invVect *wire.InvVect) (bool, error) { - switch invVect.Type { - case wire.InvTypeWitnessBlock: - fallthrough - case wire.InvTypeBlock: - // Ask chain if the block is known to it in any form (main - // chain, side chain, or orphan). - _, err := ws.chain.GetHeader(&invVect.Hash) - if err != nil { - return false, nil - } - return true, nil - case wire.InvTypeTx: - // Is transaction already in mempool - if _, ok := ws.mempool[invVect.Hash]; ok { - return true, nil - } - return false, nil - } - // The requested inventory is is an unsupported type, so just claim - // it is known to avoid requesting it. - return true, nil -} - -// limitMap is a helper function for maps that require a maximum limit by -// evicting a random transaction if adding a new value would cause it to -// overflow the maximum allowed. -func limitMap(i interface{}, limit int) { - m, ok := i.(map[chainhash.Hash]struct{}) - if ok { - if len(m)+1 > limit { - // Remove a random entry from the map. For most compilers, Go's - // range statement iterates starting at a random item although - // that is not 100% guaranteed by the spec. The iteration order - // is not important here because an adversary would have to be - // able to pull off preimage attacks on the hashing function in - // order to target eviction of specific entries anyways. - for txHash := range m { - delete(m, txHash) - return - } - } - return - } - n, ok := i.(map[chainhash.Hash]uint32) - if ok { - if len(n)+1 > limit { - for txHash := range n { - delete(n, txHash) - return - } - } - } -} diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/exchangerates/exchangerates.go b/vendor/github.com/cpacia/BitcoinCash-Wallet/exchangerates/exchangerates.go deleted file mode 100644 index cc458e6662..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/exchangerates/exchangerates.go +++ /dev/null @@ -1,241 +0,0 @@ -package exchangerates - -import ( - "encoding/json" - "errors" - "fmt" - "golang.org/x/net/proxy" - "net" - "net/http" - "reflect" - "strconv" - "sync" - "time" -) - -type ExchangeRateProvider struct { - fetchUrl string - cache map[string]float64 - client *http.Client - decoder ExchangeRateDecoder -} - -type ExchangeRateDecoder interface { - decode(dat interface{}, cache map[string]float64) (err error) -} - -type OpenBazaarDecoder struct{} -type KrakenDecoder struct{} -type BitfinexDecoder struct{} -type BittrexDecoder struct{} -type PoloniexDecoder struct{} - -type BitcoinCashPriceFetcher struct { - sync.Mutex - cache map[string]float64 - providers []*ExchangeRateProvider -} - -func NewBitcoinCashPriceFetcher(dialer proxy.Dialer) *BitcoinCashPriceFetcher { - b := BitcoinCashPriceFetcher{ - cache: make(map[string]float64), - } - dial := net.Dial - if dialer != nil { - dial = dialer.Dial - } - tbTransport := &http.Transport{Dial: dial} - client := &http.Client{Transport: tbTransport, Timeout: time.Minute} - - b.providers = []*ExchangeRateProvider{ - {"https://ticker.openbazaar.org/api", b.cache, client, OpenBazaarDecoder{}}, - {"https://poloniex.com/public?command=returnTicker", b.cache, client, PoloniexDecoder{}}, - {"https://api.kraken.com/0/public/Ticker?pair=BCHUSD", b.cache, client, KrakenDecoder{}}, - } - return &b -} - -func (b *BitcoinCashPriceFetcher) GetExchangeRate(currencyCode string) (float64, error) { - b.Lock() - defer b.Unlock() - price, ok := b.cache[currencyCode] - if !ok { - return 0, errors.New("Currency not tracked") - } - return price, nil -} - -func (b *BitcoinCashPriceFetcher) GetLatestRate(currencyCode string) (float64, error) { - b.fetchCurrentRates() - b.Lock() - defer b.Unlock() - price, ok := b.cache[currencyCode] - if !ok { - return 0, errors.New("Currency not tracked") - } - return price, nil -} - -func (b *BitcoinCashPriceFetcher) GetAllRates(cacheOK bool) (map[string]float64, error) { - if !cacheOK { - err := b.fetchCurrentRates() - if err != nil { - return nil, err - } - } - b.Lock() - defer b.Unlock() - return b.cache, nil -} - -func (b *BitcoinCashPriceFetcher) UnitsPerCoin() int { - return 100000000 -} - -func (b *BitcoinCashPriceFetcher) fetchCurrentRates() error { - b.Lock() - defer b.Unlock() - for _, provider := range b.providers { - err := provider.fetch() - if err == nil { - return nil - } - fmt.Println(err) - } - return errors.New("All exchange rate API queries failed") -} - -func (b *BitcoinCashPriceFetcher) Run() { - b.fetchCurrentRates() - ticker := time.NewTicker(time.Minute * 15) - for range ticker.C { - b.fetchCurrentRates() - } -} - -func (provider *ExchangeRateProvider) fetch() (err error) { - if len(provider.fetchUrl) == 0 { - err = errors.New("Provider has no fetchUrl") - return err - } - resp, err := provider.client.Get(provider.fetchUrl) - if err != nil { - return err - } - decoder := json.NewDecoder(resp.Body) - var dataMap interface{} - err = decoder.Decode(&dataMap) - if err != nil { - return err - } - return provider.decoder.decode(dataMap, provider.cache) -} - -func (b OpenBazaarDecoder) decode(dat interface{}, cache map[string]float64) (err error) { - data, ok := dat.(map[string]interface{}) - if !ok { - return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed") - } - bch, ok := data["BCH"] - if !ok { - return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'BCH' field") - } - val, ok := bch.(map[string]interface{}) - if !ok { - return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed") - } - bchRate, ok := val["last"].(float64) - if !ok { - return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'last' (float) field") - } - for k, v := range data { - if k != "timestamp" { - val, ok := v.(map[string]interface{}) - if !ok { - return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed") - } - price, ok := val["last"].(float64) - if !ok { - return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'last' (float) field") - } - cache[k] = price * (1 / bchRate) - } - } - return nil -} - -func (b KrakenDecoder) decode(dat interface{}, cache map[string]float64) (err error) { - obj, ok := dat.(map[string]interface{}) - if !ok { - return errors.New("KrackenDecoder type assertion failure") - } - result, ok := obj["result"] - if !ok { - return errors.New("KrakenDecoder: field `result` not found") - } - resultMap, ok := result.(map[string]interface{}) - if !ok { - return errors.New("KrackenDecoder type assertion failure") - } - pair, ok := resultMap["BCHUSD"] - if !ok { - return errors.New("KrakenDecoder: field `BCHUSD` not found") - } - pairMap, ok := pair.(map[string]interface{}) - if !ok { - return errors.New("KrackenDecoder type assertion failure") - } - c, ok := pairMap["c"] - if !ok { - return errors.New("KrakenDecoder: field `c` not found") - } - cList, ok := c.([]interface{}) - if !ok { - return errors.New("KrackenDecoder type assertion failure") - } - rateStr, ok := cList[0].(string) - if !ok { - return errors.New("KrackenDecoder type assertion failure") - } - price, err := strconv.ParseFloat(rateStr, 64) - if err != nil { - return err - } - rate := price - - if rate == 0 { - return errors.New("Bitcoin-BitcoinCash price data not available") - } - cache["USD"] = rate - return nil -} - -func (b PoloniexDecoder) decode(dat interface{}, cache map[string]float64) (err error) { - data, ok := dat.(map[string]interface{}) - if !ok { - return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed") - } - var rate float64 - v, ok := data["USDT_BCH"] - if !ok { - return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed") - } - val, ok := v.(map[string]interface{}) - if !ok { - return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed") - } - s, ok := val["last"].(string) - if !ok { - return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'last' (string) field") - } - price, err := strconv.ParseFloat(s, 64) - if err != nil { - return err - } - rate = price - if rate == 0 { - return errors.New("BitcoinCash price data not available") - } - cache["USD"] = rate - return nil -} diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/fees.go b/vendor/github.com/cpacia/BitcoinCash-Wallet/fees.go deleted file mode 100644 index c9e06fe8e3..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/fees.go +++ /dev/null @@ -1,103 +0,0 @@ -package bitcoincash - -import ( - "github.com/OpenBazaar/wallet-interface" - "net/http" - "time" -) - -type httpClient interface { - Get(string) (*http.Response, error) -} - -type feeCache struct { - fees *Fees - lastUpdated time.Time -} - -type Fees struct { - FastestFee uint64 - HalfHourFee uint64 - HourFee uint64 -} - -type FeeProvider struct { - maxFee uint64 - priorityFee uint64 - normalFee uint64 - economicFee uint64 - - exchangeRates wallet.ExchangeRates - - cache *feeCache -} - -// We will target a fee per byte such that it would equal -// 1 USD cent for economic, 5 USD cents for normal and -// 10 USD cents for priority for a median (226 byte) transaction. -type FeeTarget int - -const ( - EconomicTarget FeeTarget = 1 - NormalTarget FeeTarget = 5 - PriorityTarget FeeTarget = 10 -) - -func NewFeeProvider(maxFee, priorityFee, normalFee, economicFee uint64, exchangeRates wallet.ExchangeRates) *FeeProvider { - return &FeeProvider{ - maxFee: maxFee, - priorityFee: priorityFee, - normalFee: normalFee, - economicFee: economicFee, - exchangeRates: exchangeRates, - cache: new(feeCache), - } -} - -func (fp *FeeProvider) GetFeePerByte(feeLevel wallet.FeeLevel) uint64 { - defaultFee := func() uint64 { - switch feeLevel { - case wallet.PRIOIRTY: - return fp.priorityFee - case wallet.NORMAL: - return fp.normalFee - case wallet.ECONOMIC: - return fp.economicFee - case wallet.FEE_BUMP: - return fp.priorityFee * 2 - default: - return fp.normalFee - } - } - if fp.exchangeRates == nil { - return defaultFee() - } - - rate, err := fp.exchangeRates.GetLatestRate("USD") - if err != nil || rate == 0 { - log.Errorf("Error using exchange rate to calculate fee: %s\n", err.Error()) - return defaultFee() - } - - var target FeeTarget - switch feeLevel { - case wallet.PRIOIRTY: - target = PriorityTarget - case wallet.NORMAL: - target = NormalTarget - case wallet.ECONOMIC: - target = EconomicTarget - case wallet.FEE_BUMP: - target = PriorityTarget * 2 - default: - target = NormalTarget - } - - feePerByte := (((float64(target) / 100) / rate) * 100000000) / 226 - - if uint64(feePerByte) > fp.maxFee { - return fp.maxFee - } - - return uint64(feePerByte) -} diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/headers.go b/vendor/github.com/cpacia/BitcoinCash-Wallet/headers.go deleted file mode 100644 index 7b8a949943..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/headers.go +++ /dev/null @@ -1,456 +0,0 @@ -package bitcoincash - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "io" - "math/big" - "path" - "sort" - "sync" - - "github.com/boltdb/bolt" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - "github.com/cevaris/ordered_map" - "strings" -) - -const ( - MAX_HEADERS = 2000 - CACHE_SIZE = 200 -) - -// Database interface for storing block headers -type Headers interface { - // Put a block header to the database - // Total work and height are required to be calculated prior to insertion - // If this is the new best header, the chain tip should also be updated - Put(header StoredHeader, newBestHeader bool) error - - // Delete all headers after the MAX_HEADERS most recent - Prune() error - - // Delete all headers after the given height - DeleteAfter(height uint32) error - - // Returns all information about the previous header - GetPreviousHeader(header wire.BlockHeader) (StoredHeader, error) - - // Grab a header given hash - GetHeader(hash chainhash.Hash) (StoredHeader, error) - - // Retrieve the best header from the database - GetBestHeader() (StoredHeader, error) - - // Get the height of chain - Height() (uint32, error) - - // Cleanly close the db - Close() - - // Print all headers - Print(io.Writer) -} - -type StoredHeader struct { - header wire.BlockHeader - height uint32 - totalWork *big.Int -} - -// HeaderDB implements Headers using bolt DB -type HeaderDB struct { - lock *sync.Mutex - db *bolt.DB - filePath string - bestCache *StoredHeader - cache *HeaderCache -} - -var ( - BKTHeaders = []byte("Headers") - BKTChainTip = []byte("ChainTip") - KEYChainTip = []byte("ChainTip") -) - -func NewHeaderDB(filePath string) (*HeaderDB, error) { - if !strings.Contains(filePath, ".bin") { - filePath = path.Join(filePath, "headers.bin") - } - h := new(HeaderDB) - db, err := bolt.Open(filePath, 0644, &bolt.Options{InitialMmapSize: 5000000}) - if err != nil { - return nil, err - } - h.db = db - h.lock = new(sync.Mutex) - h.filePath = filePath - h.cache = &HeaderCache{ordered_map.NewOrderedMap(), sync.RWMutex{}, CACHE_SIZE} - - db.Update(func(btx *bolt.Tx) error { - _, err := btx.CreateBucketIfNotExists(BKTHeaders) - if err != nil { - return err - } - _, err = btx.CreateBucketIfNotExists(BKTChainTip) - if err != nil { - return err - } - return nil - }) - - h.initializeCache() - return h, nil -} - -func (h *HeaderDB) Put(sh StoredHeader, newBestHeader bool) error { - h.lock.Lock() - h.cache.Set(sh) - if newBestHeader { - h.bestCache = &sh - } - h.lock.Unlock() - go func() { - err := h.putToDB(sh, newBestHeader) - if err != nil { - log.Error(err) - } - }() - return nil -} - -func (h *HeaderDB) put(sh StoredHeader, newBestHeader bool) error { - h.lock.Lock() - h.cache.Set(sh) - if newBestHeader { - h.bestCache = &sh - } - h.lock.Unlock() - err := h.putToDB(sh, newBestHeader) - if err != nil { - log.Error(err) - } - return nil -} - -func (h *HeaderDB) putToDB(sh StoredHeader, newBestHeader bool) error { - h.lock.Lock() - defer h.lock.Unlock() - return h.db.Update(func(btx *bolt.Tx) error { - hdrs := btx.Bucket(BKTHeaders) - ser, err := serializeHeader(sh) - if err != nil { - return err - } - hash := sh.header.BlockHash() - err = hdrs.Put(hash.CloneBytes(), ser) - if err != nil { - return err - } - if newBestHeader { - tip := btx.Bucket(BKTChainTip) - err = tip.Put(KEYChainTip, ser) - if err != nil { - return err - } - } - return nil - }) -} - -func (h *HeaderDB) Prune() error { - h.lock.Lock() - defer h.lock.Unlock() - return h.db.Update(func(btx *bolt.Tx) error { - hdrs := btx.Bucket(BKTHeaders) - numHeaders := hdrs.Stats().KeyN - tip := btx.Bucket(BKTChainTip) - b := tip.Get(KEYChainTip) - if b == nil { - return errors.New("ChainTip not set") - } - sh, err := deserializeHeader(b) - if err != nil { - return err - } - height := sh.height - if numHeaders > MAX_HEADERS { - var toDelete [][]byte - pruneHeight := height - 2000 - err := hdrs.ForEach(func(k, v []byte) error { - sh, err := deserializeHeader(v) - if err != nil { - return err - } - if sh.height <= pruneHeight { - toDelete = append(toDelete, k) - } - return nil - }) - if err != nil { - return err - } - for _, k := range toDelete { - err := hdrs.Delete(k) - if err != nil { - return err - } - } - - } - return nil - }) -} - -func (h *HeaderDB) DeleteAfter(height uint32) error { - h.lock.Lock() - defer h.lock.Unlock() - return h.db.Update(func(btx *bolt.Tx) error { - hdrs := btx.Bucket(BKTHeaders) - var toDelete [][]byte - err := hdrs.ForEach(func(k, v []byte) error { - sh, err := deserializeHeader(v) - if err != nil { - return err - } - if sh.height > height { - toDelete = append(toDelete, k) - } - return nil - }) - if err != nil { - return err - } - for _, k := range toDelete { - err := hdrs.Delete(k) - if err != nil { - return err - } - } - return nil - }) -} - -func (h *HeaderDB) GetPreviousHeader(header wire.BlockHeader) (sh StoredHeader, err error) { - hash := header.PrevBlock - return h.GetHeader(hash) -} - -func (h *HeaderDB) GetHeader(hash chainhash.Hash) (sh StoredHeader, err error) { - h.lock.Lock() - defer h.lock.Unlock() - cachedHeader, cerr := h.cache.Get(hash) - if cerr == nil { - return cachedHeader, nil - } - err = h.db.View(func(btx *bolt.Tx) error { - hdrs := btx.Bucket(BKTHeaders) - b := hdrs.Get(hash.CloneBytes()) - if b == nil { - return errors.New("Header does not exist in database") - } - sh, err = deserializeHeader(b) - if err != nil { - return err - } - return nil - }) - if err != nil { - return sh, err - } - return sh, nil -} - -func (h *HeaderDB) GetBestHeader() (sh StoredHeader, err error) { - h.lock.Lock() - defer h.lock.Unlock() - if h.bestCache != nil { - best := h.bestCache - return *best, nil - } - err = h.db.View(func(btx *bolt.Tx) error { - tip := btx.Bucket(BKTChainTip) - b := tip.Get(KEYChainTip) - if b == nil { - return errors.New("ChainTip not set") - } - sh, err = deserializeHeader(b) - if err != nil { - return err - } - return nil - }) - if err != nil { - return sh, err - } - return sh, nil -} - -func (h *HeaderDB) Height() (uint32, error) { - h.lock.Lock() - defer h.lock.Unlock() - if h.bestCache != nil { - return h.bestCache.height, nil - } - var height uint32 - err := h.db.View(func(btx *bolt.Tx) error { - tip := btx.Bucket(BKTChainTip) - sh, err := deserializeHeader(tip.Get(KEYChainTip)) - if err != nil { - return err - } - height = sh.height - return nil - }) - if err != nil { - return height, err - } - return height, nil -} - -func (h *HeaderDB) Print(w io.Writer) { - h.lock.Lock() - defer h.lock.Unlock() - m := make(map[float64][]string) - h.db.View(func(tx *bolt.Tx) error { - // Assume bucket exists and has keys - bkt := tx.Bucket(BKTHeaders) - bkt.ForEach(func(k, v []byte) error { - sh, _ := deserializeHeader(v) - h := float64(sh.height) - _, ok := m[h] - if ok { - for { - h += .1 - _, ok := m[h] - if !ok { - break - } - } - } - m[h] = []string{sh.header.BlockHash().String(), sh.header.PrevBlock.String()} - return nil - }) - - return nil - }) - var keys []float64 - for k := range m { - keys = append(keys, float64(k)) - } - sort.Float64s(keys) - for _, k := range keys { - fmt.Fprintf(w, "Height: %.1f, Hash: %s, Parent: %s\n", k, m[k][0], m[k][1]) - } -} - -func (h *HeaderDB) initializeCache() { - best, err := h.GetBestHeader() - if err != nil { - return - } - h.bestCache = &best - headers := []StoredHeader{best} - for i := 0; i < 99; i++ { - sh, err := h.GetPreviousHeader(best.header) - if err != nil { - break - } - headers = append(headers, sh) - } - for i := len(headers) - 1; i >= 0; i-- { - h.cache.Set(headers[i]) - } -} - -func (h *HeaderDB) Close() { - h.lock.Lock() - h.db.Close() -} - -/*----- header serialization ------- */ -/* byteLength desc at offset - 80 header 0 - 4 height 80 - 32 total work 84 -*/ -func serializeHeader(sh StoredHeader) ([]byte, error) { - var buf bytes.Buffer - err := sh.header.Serialize(&buf) - if err != nil { - return nil, err - } - err = binary.Write(&buf, binary.BigEndian, sh.height) - if err != nil { - return nil, err - } - biBytes := sh.totalWork.Bytes() - pad := make([]byte, 32-len(biBytes)) - serializedBI := append(pad, biBytes...) - buf.Write(serializedBI) - return buf.Bytes(), nil -} - -func deserializeHeader(b []byte) (sh StoredHeader, err error) { - r := bytes.NewReader(b) - hdr := new(wire.BlockHeader) - err = hdr.Deserialize(r) - if err != nil { - return sh, err - } - var height uint32 - err = binary.Read(r, binary.BigEndian, &height) - if err != nil { - return sh, err - } - biBytes := make([]byte, 32) - _, err = r.Read(biBytes) - if err != nil { - return sh, err - } - bi := new(big.Int) - bi.SetBytes(biBytes) - sh = StoredHeader{ - header: *hdr, - height: height, - totalWork: bi, - } - return sh, nil -} - -type HeaderCache struct { - headers *ordered_map.OrderedMap - sync.RWMutex - cacheSize int -} - -func (h *HeaderCache) pop() { - iter := h.headers.IterFunc() - k, ok := iter() - if ok { - h.headers.Delete(k.Key) - } -} - -func (h *HeaderCache) Set(sh StoredHeader) { - h.Lock() - defer h.Unlock() - if h.headers.Len() > h.cacheSize { - h.pop() - } - hash := sh.header.BlockHash() - h.headers.Set(hash.String(), sh) -} - -func (h *HeaderCache) Get(hash chainhash.Hash) (StoredHeader, error) { - h.RLock() - defer h.RUnlock() - sh, ok := h.headers.Get(hash.String()) - if !ok { - return StoredHeader{}, errors.New("Not found") - } - return sh.(StoredHeader), nil -} diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/keys.go b/vendor/github.com/cpacia/BitcoinCash-Wallet/keys.go deleted file mode 100644 index 14f5af1c79..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/keys.go +++ /dev/null @@ -1,189 +0,0 @@ -package bitcoincash - -import ( - "github.com/OpenBazaar/wallet-interface" - "github.com/btcsuite/btcd/chaincfg" - hd "github.com/btcsuite/btcutil/hdkeychain" - "github.com/btcsuite/goleveldb/leveldb/errors" -) - -const LOOKAHEADWINDOW = 100 - -type KeyManager struct { - datastore wallet.Keys - params *chaincfg.Params - - internalKey *hd.ExtendedKey - externalKey *hd.ExtendedKey -} - -func NewKeyManager(db wallet.Keys, params *chaincfg.Params, masterPrivKey *hd.ExtendedKey) (*KeyManager, error) { - internal, external, err := Bip44Derivation(masterPrivKey) - if err != nil { - return nil, err - } - km := &KeyManager{ - datastore: db, - params: params, - internalKey: internal, - externalKey: external, - } - if err := km.lookahead(); err != nil { - return nil, err - } - return km, nil -} - -// m / purpose' / coin_type' / account' / change / address_index -func Bip44Derivation(masterPrivKey *hd.ExtendedKey) (internal, external *hd.ExtendedKey, err error) { - // Purpose = bip44 - fourtyFour, err := masterPrivKey.Child(hd.HardenedKeyStart + 44) - if err != nil { - return nil, nil, err - } - // Cointype = bitcoin - bitcoin, err := fourtyFour.Child(hd.HardenedKeyStart + 145) - if err != nil { - return nil, nil, err - } - // Account = 0 - account, err := bitcoin.Child(hd.HardenedKeyStart + 0) - if err != nil { - return nil, nil, err - } - // Change(0) = external - external, err = account.Child(0) - if err != nil { - return nil, nil, err - } - // Change(1) = internal - internal, err = account.Child(1) - if err != nil { - return nil, nil, err - } - return internal, external, nil -} - -func (km *KeyManager) GetCurrentKey(purpose wallet.KeyPurpose) (*hd.ExtendedKey, error) { - i, err := km.datastore.GetUnused(purpose) - if err != nil { - return nil, err - } - if len(i) == 0 { - return nil, errors.New("No unused keys in database") - } - return km.generateChildKey(purpose, uint32(i[0])) -} - -func (km *KeyManager) GetFreshKey(purpose wallet.KeyPurpose) (*hd.ExtendedKey, error) { - index, _, err := km.datastore.GetLastKeyIndex(purpose) - var childKey *hd.ExtendedKey - if err != nil { - index = 0 - } else { - index += 1 - } - for { - // There is a small possibility bip32 keys can be invalid. The procedure in such cases - // is to discard the key and derive the next one. This loop will continue until a valid key - // is derived. - childKey, err = km.generateChildKey(purpose, uint32(index)) - if err == nil { - break - } - index += 1 - } - addr, err := childKey.Address(km.params) - if err != nil { - return nil, err - } - p := wallet.KeyPath{wallet.KeyPurpose(purpose), index} - err = km.datastore.Put(addr.ScriptAddress(), p) - if err != nil { - return nil, err - } - return childKey, nil -} - -func (km *KeyManager) GetKeys() []*hd.ExtendedKey { - var keys []*hd.ExtendedKey - keyPaths, err := km.datastore.GetAll() - if err != nil { - return keys - } - for _, path := range keyPaths { - k, err := km.generateChildKey(path.Purpose, uint32(path.Index)) - if err != nil { - continue - } - keys = append(keys, k) - } - imported, err := km.datastore.GetImported() - if err != nil { - return keys - } - for _, key := range imported { - hdKey := hd.NewExtendedKey( - km.params.HDPrivateKeyID[:], - key.Serialize(), - make([]byte, 32), - []byte{0x00, 0x00, 0x00, 0x00}, - 0, - 0, - true) - keys = append(keys, hdKey) - } - return keys -} - -func (km *KeyManager) GetKeyForScript(scriptAddress []byte) (*hd.ExtendedKey, error) { - keyPath, err := km.datastore.GetPathForKey(scriptAddress) - if err != nil { - key, err := km.datastore.GetKey(scriptAddress) - if err != nil { - return nil, err - } - hdKey := hd.NewExtendedKey( - km.params.HDPrivateKeyID[:], - key.Serialize(), - make([]byte, 32), - []byte{0x00, 0x00, 0x00, 0x00}, - 0, - 0, - true) - return hdKey, nil - } - return km.generateChildKey(keyPath.Purpose, uint32(keyPath.Index)) -} - -// Mark the given key as used and extend the lookahead window -func (km *KeyManager) MarkKeyAsUsed(scriptAddress []byte) error { - if err := km.datastore.MarkKeyAsUsed(scriptAddress); err != nil { - return err - } - return km.lookahead() -} - -func (km *KeyManager) generateChildKey(purpose wallet.KeyPurpose, index uint32) (*hd.ExtendedKey, error) { - if purpose == wallet.EXTERNAL { - return km.externalKey.Child(index) - } else if purpose == wallet.INTERNAL { - return km.internalKey.Child(index) - } - return nil, errors.New("Unknown key purpose") -} - -func (km *KeyManager) lookahead() error { - lookaheadWindows := km.datastore.GetLookaheadWindows() - for purpose, size := range lookaheadWindows { - if size < LOOKAHEADWINDOW { - for i := 0; i < (LOOKAHEADWINDOW - size); i++ { - _, err := km.GetFreshKey(purpose) - if err != nil { - return err - } - } - } - } - return nil -} diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/makefile b/vendor/github.com/cpacia/BitcoinCash-Wallet/makefile deleted file mode 100644 index 30265908dc..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/makefile +++ /dev/null @@ -1,8 +0,0 @@ -install: - cd cmd/bitcoincash && go install - -protos: - cd api/pb && protoc --go_out=plugins=grpc:. api.proto - -resources: - cd gui && go-bindata -o resources.go -pkg gui resources/... diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/mblock.go b/vendor/github.com/cpacia/BitcoinCash-Wallet/mblock.go deleted file mode 100644 index ac60997196..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/mblock.go +++ /dev/null @@ -1,176 +0,0 @@ -package bitcoincash - -import ( - "fmt" - - "errors" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" -) - -func MakeMerkleParent(left *chainhash.Hash, right *chainhash.Hash) (*chainhash.Hash, error) { - // dupes can screw things up; CVE-2012-2459. check for them - if left != nil && right != nil && left.IsEqual(right) { - return nil, errors.New("DUP HASH CRASH") - } - // if left child is nil, output nil. Need this for hard mode. - if left == nil { - return nil, errors.New("Left child is nil") - } - // if right is nil, hash left with itself - if right == nil { - right = left - } - - // Concatenate the left and right nodes - var sha [64]byte - copy(sha[:32], left[:]) - copy(sha[32:], right[:]) - - newSha := chainhash.DoubleHashH(sha[:]) - return &newSha, nil -} - -type merkleNode struct { - p uint32 // position in the binary tree - h *chainhash.Hash // hash -} - -// given n merkle leaves, how deep is the tree? -// iterate shifting left until greater than n -func treeDepth(n uint32) (e uint8) { - for ; (1 << e) < n; e++ { - } - return -} - -// smallest power of 2 that can contain n -func nextPowerOfTwo(n uint32) uint32 { - return 1 << treeDepth(n) // 2^exponent -} - -// check if a node is populated based on node position and size of tree -func inDeadZone(pos, size uint32) bool { - msb := nextPowerOfTwo(size) - last := size - 1 // last valid position is 1 less than size - if pos > (msb<<1)-2 { // greater than root; not even in the tree - log.Debug(" ?? greater than root ") - return true - } - h := msb - for pos >= h { - h = h>>1 | msb - last = last>>1 | msb - } - return pos > last -} - -// take in a merkle block, parse through it, and return txids indicated -// If there's any problem return an error. Checks self-consistency only. -// doing it with a stack instead of recursion. Because... -// OK I don't know why I'm just not in to recursion OK? -func checkMBlock(m *wire.MsgMerkleBlock) ([]*chainhash.Hash, error) { - if m.Transactions == 0 { - return nil, fmt.Errorf("No transactions in merkleblock") - } - if len(m.Flags) == 0 { - return nil, fmt.Errorf("No flag bits") - } - var s []merkleNode // the stack - var r []*chainhash.Hash // slice to return; txids we care about - - // set initial position to root of merkle tree - msb := nextPowerOfTwo(m.Transactions) // most significant bit possible - pos := (msb << 1) - 2 // current position in tree - - var i uint8 // position in the current flag byte - var tip int - // main loop - for { - tip = len(s) - 1 // slice position of stack tip - // First check if stack operations can be performed - // is stack one filled item? that's complete. - if tip == 0 && s[0].h != nil { - if s[0].h.IsEqual(&m.Header.MerkleRoot) { - return r, nil - } - return nil, fmt.Errorf("computed root %s but expect %s\n", - s[0].h.String(), m.Header.MerkleRoot.String()) - } - // is current position in the tree's dead zone? partial parent - if inDeadZone(pos, m.Transactions) { - // create merkle parent from single side (left) - h, err := MakeMerkleParent(s[tip].h, nil) - if err != nil { - return r, err - } - s[tip-1].h = h - s = s[:tip] // remove 1 from stack - pos = s[tip-1].p | 1 // move position to parent's sibling - continue - } - // does stack have 3+ items? and are last 2 items filled? - if tip > 1 && s[tip-1].h != nil && s[tip].h != nil { - //fmt.Printf("nodes %d and %d combine into %d\n", - // s[tip-1].p, s[tip].p, s[tip-2].p) - // combine two filled nodes into parent node - h, err := MakeMerkleParent(s[tip-1].h, s[tip].h) - if err != nil { - return r, err - } - s[tip-2].h = h - // remove children - s = s[:tip-1] - // move position to parent's sibling - pos = s[tip-2].p | 1 - continue - } - - // no stack ops to perform, so make new node from message hashes - if len(m.Hashes) == 0 { - return nil, fmt.Errorf("Ran out of hashes at position %d.", pos) - } - if len(m.Flags) == 0 { - return nil, fmt.Errorf("Ran out of flag bits.") - } - var n merkleNode // make new node - n.p = pos // set current position for new node - - if pos&msb != 0 { // upper non-txid hash - if m.Flags[0]&(1<>1 | msb - } else { // left side, go to sibling - pos |= 1 - } - } else { // flag bit says skip; put empty on stack and descend - pos = (pos ^ msb) << 1 // descend to left - } - s = append(s, n) // push new node on stack - } else { // bottom row txid; flag bit indicates tx of interest - if pos >= m.Transactions { - // this can't happen because we check deadzone above... - return nil, fmt.Errorf("got into an invalid txid node") - } - n.h = m.Hashes[0] // copy hash from message - m.Hashes = m.Hashes[1:] // pop off message - if m.Flags[0]&(1< i { - i = key.path.Index - used = key.used - } - } - if i == -1 { - return i, used, errors.New("No saved keys") - } - return i, used, nil -} - -func (m *mockKeyStore) GetPathForKey(scriptAddress []byte) (wallet.KeyPath, error) { - key, ok := m.keys[hex.EncodeToString(scriptAddress)] - if !ok || key.path.Index == -1 { - return wallet.KeyPath{}, errors.New("key does not exist") - } - return key.path, nil -} - -func (m *mockKeyStore) GetKey(scriptAddress []byte) (*btcec.PrivateKey, error) { - for _, k := range m.keys { - if k.path.Index == -1 && bytes.Equal(scriptAddress, k.scriptAddress) { - return k.key, nil - } - } - return nil, errors.New("Not found") -} - -func (m *mockKeyStore) GetImported() ([]*btcec.PrivateKey, error) { - var keys []*btcec.PrivateKey - for _, k := range m.keys { - if k.path.Index == -1 { - keys = append(keys, k.key) - } - } - return keys, nil -} - -func (m *mockKeyStore) GetUnused(purpose wallet.KeyPurpose) ([]int, error) { - var i []int - for _, key := range m.keys { - if !key.used && key.path.Purpose == purpose { - i = append(i, key.path.Index) - } - } - sort.Ints(i) - return i, nil -} - -func (m *mockKeyStore) GetAll() ([]wallet.KeyPath, error) { - var kp []wallet.KeyPath - for _, key := range m.keys { - kp = append(kp, key.path) - } - return kp, nil -} - -func (m *mockKeyStore) GetLookaheadWindows() map[wallet.KeyPurpose]int { - internalLastUsed := -1 - externalLastUsed := -1 - for _, key := range m.keys { - if key.path.Purpose == wallet.INTERNAL && key.used && key.path.Index > internalLastUsed { - internalLastUsed = key.path.Index - } - if key.path.Purpose == wallet.EXTERNAL && key.used && key.path.Index > externalLastUsed { - externalLastUsed = key.path.Index - } - } - internalUnused := 0 - externalUnused := 0 - for _, key := range m.keys { - if key.path.Purpose == wallet.INTERNAL && !key.used && key.path.Index > internalLastUsed { - internalUnused++ - } - if key.path.Purpose == wallet.EXTERNAL && !key.used && key.path.Index > externalLastUsed { - externalUnused++ - } - } - mp := make(map[wallet.KeyPurpose]int) - mp[wallet.INTERNAL] = internalUnused - mp[wallet.EXTERNAL] = externalUnused - return mp -} - -type mockUtxoStore struct { - utxos map[string]*wallet.Utxo -} - -func (m *mockUtxoStore) Put(utxo wallet.Utxo) error { - key := utxo.Op.Hash.String() + ":" + strconv.Itoa(int(utxo.Op.Index)) - m.utxos[key] = &utxo - return nil -} - -func (m *mockUtxoStore) GetAll() ([]wallet.Utxo, error) { - var utxos []wallet.Utxo - for _, v := range m.utxos { - utxos = append(utxos, *v) - } - return utxos, nil -} - -func (m *mockUtxoStore) SetWatchOnly(utxo wallet.Utxo) error { - key := utxo.Op.Hash.String() + ":" + strconv.Itoa(int(utxo.Op.Index)) - u, ok := m.utxos[key] - if !ok { - return errors.New("Not found") - } - u.WatchOnly = true - return nil -} - -func (m *mockUtxoStore) Delete(utxo wallet.Utxo) error { - key := utxo.Op.Hash.String() + ":" + strconv.Itoa(int(utxo.Op.Index)) - _, ok := m.utxos[key] - if !ok { - return errors.New("Not found") - } - delete(m.utxos, key) - return nil -} - -type mockStxoStore struct { - stxos map[string]*wallet.Stxo -} - -func (m *mockStxoStore) Put(stxo wallet.Stxo) error { - m.stxos[stxo.SpendTxid.String()] = &stxo - return nil -} - -func (m *mockStxoStore) GetAll() ([]wallet.Stxo, error) { - var stxos []wallet.Stxo - for _, v := range m.stxos { - stxos = append(stxos, *v) - } - return stxos, nil -} - -func (m *mockStxoStore) Delete(stxo wallet.Stxo) error { - _, ok := m.stxos[stxo.SpendTxid.String()] - if !ok { - return errors.New("Not found") - } - delete(m.stxos, stxo.SpendTxid.String()) - return nil -} - -type txnStoreEntry struct { - txn []byte - txid string - value int - height int - timestamp time.Time - watchOnly bool -} - -type mockTxnStore struct { - txns map[string]*txnStoreEntry -} - -func (m *mockTxnStore) Put(txn []byte, txid string, value, height int, timestamp time.Time, watchOnly bool) error { - m.txns[txid] = &txnStoreEntry{ - txn: txn, - txid: txid, - value: value, - height: height, - timestamp: timestamp, - watchOnly: watchOnly, - } - return nil -} - -func (m *mockTxnStore) Get(txid chainhash.Hash) (wallet.Txn, error) { - t, ok := m.txns[txid.String()] - if !ok { - return wallet.Txn{}, errors.New("Not found") - } - return wallet.Txn{ - Txid: t.txid, - Value: int64(t.value), - Height: int32(t.height), - Timestamp: t.timestamp, - WatchOnly: t.watchOnly, - Bytes: t.txn, - }, nil -} - -func (m *mockTxnStore) GetAll(includeWatchOnly bool) ([]wallet.Txn, error) { - var txns []wallet.Txn - for _, t := range m.txns { - txn := wallet.Txn{ - Txid: t.txid, - Value: int64(t.value), - Height: int32(t.height), - Timestamp: t.timestamp, - WatchOnly: t.watchOnly, - Bytes: t.txn, - } - txns = append(txns, txn) - } - return txns, nil -} - -func (m *mockTxnStore) UpdateHeight(txid chainhash.Hash, height int, timestamp time.Time) error { - txn, ok := m.txns[txid.String()] - if !ok { - return errors.New("Not found") - } - txn.height = height - txn.timestamp = timestamp - return nil -} - -func (m *mockTxnStore) Delete(txid *chainhash.Hash) error { - _, ok := m.txns[txid.String()] - if !ok { - return errors.New("Not found") - } - delete(m.txns, txid.String()) - return nil -} - -type mockWatchedScriptsStore struct { - scripts map[string][]byte -} - -func (m *mockWatchedScriptsStore) Put(scriptPubKey []byte) error { - m.scripts[hex.EncodeToString(scriptPubKey)] = scriptPubKey - return nil -} - -func (m *mockWatchedScriptsStore) GetAll() ([][]byte, error) { - var ret [][]byte - for _, b := range m.scripts { - ret = append(ret, b) - } - return ret, nil -} - -func (m *mockWatchedScriptsStore) Delete(scriptPubKey []byte) error { - enc := hex.EncodeToString(scriptPubKey) - _, ok := m.scripts[enc] - if !ok { - return errors.New("Not found") - } - delete(m.scripts, enc) - return nil -} - -func TestUtxo_IsEqual(t *testing.T) { - h, err := chainhash.NewHashFromStr("16bed6368b8b1542cd6eb87f5bc20dc830b41a2258dde40438a75fa701d24e9a") - if err != nil { - t.Error(err) - } - u := &wallet.Utxo{ - Op: *wire.NewOutPoint(h, 0), - ScriptPubkey: make([]byte, 32), - AtHeight: 400000, - Value: 1000000, - } - if !u.IsEqual(u) { - t.Error("Failed to return utxos as equal") - } - testUtxo := *u - testUtxo.Op.Index = 3 - if u.IsEqual(&testUtxo) { - t.Error("Failed to return utxos as not equal") - } - testUtxo = *u - testUtxo.AtHeight = 1 - if u.IsEqual(&testUtxo) { - t.Error("Failed to return utxos as not equal") - } - testUtxo = *u - testUtxo.Value = 4 - if u.IsEqual(&testUtxo) { - t.Error("Failed to return utxos as not equal") - } - testUtxo = *u - ch2, err := chainhash.NewHashFromStr("1f64249abbf2fcc83fc060a64f69a91391e9f5d98c5d3135fe9716838283aa4c") - if err != nil { - t.Error(err) - } - testUtxo.Op.Hash = *ch2 - if u.IsEqual(&testUtxo) { - t.Error("Failed to return utxos as not equal") - } - testUtxo = *u - testUtxo.ScriptPubkey = make([]byte, 4) - if u.IsEqual(&testUtxo) { - t.Error("Failed to return utxos as not equal") - } - if u.IsEqual(nil) { - t.Error("Failed to return utxos as not equal") - } -} - -func TestStxo_IsEqual(t *testing.T) { - h, err := chainhash.NewHashFromStr("16bed6368b8b1542cd6eb87f5bc20dc830b41a2258dde40438a75fa701d24e9a") - if err != nil { - t.Error(err) - } - u := &wallet.Utxo{ - Op: *wire.NewOutPoint(h, 0), - ScriptPubkey: make([]byte, 32), - AtHeight: 400000, - Value: 1000000, - } - h2, err := chainhash.NewHashFromStr("1f64249abbf2fcc83fc060a64f69a91391e9f5d98c5d3135fe9716838283aa4c") - s := &wallet.Stxo{ - Utxo: *u, - SpendHeight: 400001, - SpendTxid: *h2, - } - if !s.IsEqual(s) { - t.Error("Failed to return stxos as equal") - } - - testStxo := *s - testStxo.SpendHeight = 5 - if s.IsEqual(&testStxo) { - t.Error("Failed to return stxos as not equal") - } - h3, err := chainhash.NewHashFromStr("3c5cea030a432ba9c8cf138a93f7b2e5b28263ea416894ee0bdf91bc31bb04f2") - testStxo = *s - testStxo.SpendTxid = *h3 - if s.IsEqual(&testStxo) { - t.Error("Failed to return stxos as not equal") - } - if s.IsEqual(nil) { - t.Error("Failed to return stxos as not equal") - } - testStxo = *s - testStxo.Utxo.AtHeight = 7 - if s.IsEqual(&testStxo) { - t.Error("Failed to return stxos as not equal") - } -} diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/peers.go b/vendor/github.com/cpacia/BitcoinCash-Wallet/peers.go deleted file mode 100644 index 5dc120000b..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/peers.go +++ /dev/null @@ -1,409 +0,0 @@ -package bitcoincash - -import ( - "errors" - "net" - "strconv" - "sync" - "time" - - "fmt" - "github.com/btcsuite/btcd/addrmgr" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/connmgr" - "github.com/btcsuite/btcd/peer" - "github.com/btcsuite/btcd/wire" - "github.com/cpacia/bchutil" - "golang.org/x/net/proxy" -) - -var ( - // Default number of outbound peers - defaultTargetOutbound = uint32(12) - - // Default duration of time for retrying a connection - defaultRetryDuration = time.Second * 5 - - // Default port per chain params - defaultPort uint16 -) - -const MaxGetAddressAttempts = 10 - -var SFNodeBitcoinCash wire.ServiceFlag = 1 << 5 - -type PeerManagerConfig struct { - - // The network parameters to use - Params *chaincfg.Params - - // The target number of outbound peers. Defaults to 10. - TargetOutbound uint32 - - // Duration of time to retry a connection. Defaults to 5 seconds. - RetryDuration time.Duration - - // UserAgentName specifies the user agent name to advertise. It is - // highly recommended to specify this value. - UserAgentName string - - // UserAgentVersion specifies the user agent version to advertise. It - // is highly recommended to specify this value and that it follows the - // form "major.minor.revision" e.g. "2.6.41". - UserAgentVersion string - - // The directory to store cached peers - AddressCacheDir string - - // If this field is not nil the PeerManager will only connect to this address - TrustedPeer net.Addr - - // Listeners to handle messages from peers. If nil, no messages will be handled. - Listeners *peer.MessageListeners - - // An optional proxy dialer. Will use net.Dial if nil. - Proxy proxy.Dialer - - // Function to return current block hash and height - GetNewestBlock func() (hash *chainhash.Hash, height int32, err error) - - // The main channel over which to send outgoing events - MsgChan chan interface{} -} - -type PeerManager struct { - addrManager *addrmgr.AddrManager - connManager *connmgr.ConnManager - sourceAddr *wire.NetAddress - peerConfig *peer.Config - peerMutex *sync.RWMutex - trustedPeer net.Addr - targetOutbound uint32 - proxy proxy.Dialer - recentlyTriedAddresses map[string]bool - connectedPeers map[uint64]*peer.Peer - msgChan chan interface{} -} - -func NewPeerManager(config *PeerManagerConfig) (*PeerManager, error) { - port, err := strconv.Atoi(config.Params.DefaultPort) - defaultPort = uint16(port) - if err != nil { - return nil, err - } - - pm := &PeerManager{ - addrManager: addrmgr.New(config.AddressCacheDir, nil), - peerMutex: new(sync.RWMutex), - sourceAddr: wire.NewNetAddressIPPort(net.ParseIP("0.0.0.0"), defaultPort, 0), - trustedPeer: config.TrustedPeer, - proxy: config.Proxy, - recentlyTriedAddresses: make(map[string]bool), - connectedPeers: make(map[uint64]*peer.Peer), - msgChan: config.MsgChan, - } - - targetOutbound := config.TargetOutbound - if config.TargetOutbound == 0 { - targetOutbound = defaultTargetOutbound - } - - if config.TrustedPeer != nil { - targetOutbound = 1 - } - pm.targetOutbound = targetOutbound - - retryDuration := config.RetryDuration - if config.RetryDuration <= 0 { - retryDuration = defaultRetryDuration - } - - dial := net.Dial - if config.Proxy != nil { - dial = config.Proxy.Dial - } - - connMgrConfig := &connmgr.Config{ - TargetOutbound: targetOutbound, - RetryDuration: retryDuration, - OnConnection: pm.onConnection, - OnDisconnection: pm.onDisconnection, - GetNewAddress: pm.getNewAddress, - Dial: func(addr net.Addr) (net.Conn, error) { - return dial("tcp", addr.String()) - }, - } - - connMgr, err := connmgr.New(connMgrConfig) - if err != nil { - return nil, err - } - pm.connManager = connMgr - - var listeners *peer.MessageListeners = config.Listeners - if listeners == nil { - listeners = &peer.MessageListeners{} - } - listeners.OnVerAck = pm.onVerack - listeners.OnAddr = pm.onAddr - listeners.OnHeaders = pm.onHeaders - listeners.OnMerkleBlock = pm.onMerkleBlock - listeners.OnInv = pm.onInv - listeners.OnTx = pm.onTx - listeners.OnReject = pm.onReject - - pm.peerConfig = &peer.Config{ - UserAgentName: config.UserAgentName, - UserAgentVersion: config.UserAgentVersion, - ChainParams: config.Params, - DisableRelayTx: true, - NewestBlock: config.GetNewestBlock, - Listeners: *listeners, - } - if config.Proxy != nil { - pm.peerConfig.Proxy = "0.0.0.0" - } - return pm, nil -} - -func (pm *PeerManager) ConnectedPeers() []*peer.Peer { - pm.peerMutex.RLock() - defer pm.peerMutex.RUnlock() - var ret []*peer.Peer - for _, p := range pm.connectedPeers { - ret = append(ret, p) - } - return ret -} - -func (pm *PeerManager) onConnection(req *connmgr.ConnReq, conn net.Conn) { - pm.peerMutex.Lock() - defer pm.peerMutex.Unlock() - - // Create a new peer for this connection - p, err := peer.NewOutboundPeer(pm.peerConfig, conn.RemoteAddr().String()) - if err != nil { - pm.connManager.Disconnect(req.ID()) - return - } - - // Associate the connection with the peer - p.AssociateConnection(conn) - - pm.connectedPeers[req.ID()] = p - - // Tell the addr manager we made a connection - pm.addrManager.Connected(p.NA()) - - // Handle disconnect - go func() { - p.WaitForDisconnect() - pm.connManager.Disconnect(req.ID()) - }() -} - -func (pm *PeerManager) onVerack(p *peer.Peer, msg *wire.MsgVerAck) { - // Check this peer offers bloom filtering services. If not dump them. - p.NA().Services = p.Services() - if !(p.NA().HasService(wire.SFNodeBloom) && p.NA().HasService(wire.SFNodeNetwork) && p.NA().HasService(bchutil.SFNodeBitcoinCash)) { - // onDisconnection will be called - // which will remove the peer from openPeers - log.Warningf("Peer %s does not support bloom filtering, diconnecting", p) - p.Disconnect() - return - } - log.Debugf("Connected to %s - %s\n", p.Addr(), p.UserAgent()) - // Tell the addr manager this is a good address - pm.addrManager.Good(p.NA()) - if pm.msgChan != nil { - pm.msgChan <- newPeerMsg{p} - } -} - -func (pm *PeerManager) onDisconnection(req *connmgr.ConnReq) { - // Remove from connected peers - pm.peerMutex.Lock() - defer pm.peerMutex.Unlock() - peer, ok := pm.connectedPeers[req.ID()] - if !ok { - return - } - log.Debugf("Peer %s disconnected", peer) - delete(pm.connectedPeers, req.ID()) - if pm.msgChan != nil { - pm.msgChan <- donePeerMsg{peer} - } -} - -// Called by connManager when it adds a new connection -func (pm *PeerManager) getNewAddress() (net.Addr, error) { - // If we have a trusted peer we'll just return it - if pm.trustedPeer == nil { - pm.peerMutex.Lock() - defer pm.peerMutex.Unlock() - // We're going to loop here and pull addresses from the addrManager until we get one that we - // are not currently connect to or haven't recently tried. - loop: - for tries := 0; tries < 100; tries++ { - ka := pm.addrManager.GetAddress() - if ka == nil { - continue - } - - // only allow recent nodes (10mins) after we failed 30 - // times - if tries < 30 && time.Since(ka.LastAttempt()) < 10*time.Minute { - continue - } - - // allow nondefault ports after 50 failed tries. - if tries < 50 && fmt.Sprintf("%d", ka.NetAddress().Port) != pm.peerConfig.ChainParams.DefaultPort { - continue - } - - knownAddress := ka.NetAddress() - - // Don't return addresses we're still connected to - for _, p := range pm.connectedPeers { - if p.NA().IP.String() == knownAddress.IP.String() { - continue loop - } - } - addr := &net.TCPAddr{ - Port: int(knownAddress.Port), - IP: knownAddress.IP, - } - pm.addrManager.Attempt(knownAddress) - return addr, nil - } - return nil, errors.New("failed to find appropriate address to return") - } else { - return pm.trustedPeer, nil - } -} - -// Query the DNS seeds and pass the addresses into the address manager. -func (pm *PeerManager) queryDNSSeeds() { - wg := new(sync.WaitGroup) - for _, seed := range bchutil.GetDNSSeed(pm.peerConfig.ChainParams) { - wg.Add(1) - go func(host string) { - returnedAddresses := 0 - var addrs []string - var err error - if pm.proxy != nil { - for i := 0; i < 5; i++ { - ips, err := TorLookupIP(host) - if err != nil { - wg.Done() - return - } - for _, ip := range ips { - addrs = append(addrs, ip.String()) - } - } - } else { - addrs, err = net.LookupHost(host) - if err != nil { - wg.Done() - return - } - } - for _, addr := range addrs { - netAddr := wire.NewNetAddressIPPort(net.ParseIP(addr), defaultPort, 0) - pm.addrManager.AddAddress(netAddr, pm.sourceAddr) - returnedAddresses++ - } - log.Debugf("%s returned %s addresses\n", host, strconv.Itoa(returnedAddresses)) - wg.Done() - }(seed.Host) - } - wg.Wait() -} - -// If we have connected peers let's use them to get more addresses. If not, use the DNS seeds -func (pm *PeerManager) getMoreAddresses() { - if pm.addrManager.NeedMoreAddresses() { - pm.peerMutex.RLock() - defer pm.peerMutex.RUnlock() - if len(pm.connectedPeers) > 0 { - log.Debug("Querying peers for more addresses") - for _, p := range pm.connectedPeers { - p.QueueMessage(wire.NewMsgGetAddr(), nil) - } - } else { - pm.queryDNSSeeds() - } - } -} - -func (pm *PeerManager) onAddr(p *peer.Peer, msg *wire.MsgAddr) { - pm.addrManager.AddAddresses(msg.AddrList, pm.sourceAddr) -} - -func (pm *PeerManager) onHeaders(p *peer.Peer, msg *wire.MsgHeaders) { - if pm.msgChan != nil { - pm.msgChan <- headersMsg{msg, p} - } -} - -func (pm *PeerManager) onMerkleBlock(p *peer.Peer, msg *wire.MsgMerkleBlock) { - if pm.msgChan != nil { - pm.msgChan <- merkleBlockMsg{msg, p} - } -} - -func (pm *PeerManager) onInv(p *peer.Peer, msg *wire.MsgInv) { - if pm.msgChan != nil { - pm.msgChan <- invMsg{msg, p} - } -} - -func (pm *PeerManager) onTx(p *peer.Peer, msg *wire.MsgTx) { - if pm.msgChan != nil { - pm.msgChan <- txMsg{msg, p, nil} - } -} - -func (pm *PeerManager) onReject(p *peer.Peer, msg *wire.MsgReject) { - log.Warningf("Received reject message from peer %d: Code: %s, Hash %s, Reason: %s", int(p.ID()), msg.Code.String(), msg.Hash.String(), msg.Reason) -} - -func (pm *PeerManager) Start() { - pm.addrManager.Start() - log.Infof("Loaded %d peers from cache\n", pm.addrManager.NumAddresses()) - if pm.trustedPeer == nil && pm.addrManager.NeedMoreAddresses() { - log.Info("Querying DNS seeds") - pm.queryDNSSeeds() - } - pm.connManager.Start() - go func() { - tick := time.NewTicker(time.Minute) - defer tick.Stop() - for { - select { - case <-tick.C: - pm.getMoreAddresses() - } - } - }() -} - -func (pm *PeerManager) Stop() { - pm.peerMutex.Lock() - defer pm.peerMutex.Unlock() - wg := new(sync.WaitGroup) - for _, peer := range pm.connectedPeers { - wg.Add(1) - go func() { - // onDisconnection will be called. - peer.Disconnect() - peer.WaitForDisconnect() - wg.Done() - }() - } - pm.addrManager.Stop() - pm.connectedPeers = make(map[uint64]*peer.Peer) - wg.Wait() -} diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/sortsignsend.go b/vendor/github.com/cpacia/BitcoinCash-Wallet/sortsignsend.go deleted file mode 100644 index 8fe3d85ea0..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/sortsignsend.go +++ /dev/null @@ -1,887 +0,0 @@ -// Copyright (C) 2015-2016 The Lightning Network Developers -// Copyright (c) 2016-2017 The OpenBazaar Developers - -package bitcoincash - -import ( - "bytes" - "encoding/hex" - "errors" - "fmt" - "time" - - "github.com/OpenBazaar/wallet-interface" - "github.com/btcsuite/btcd/blockchain" - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" - btc "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/coinset" - hd "github.com/btcsuite/btcutil/hdkeychain" - "github.com/btcsuite/btcutil/txsort" - "github.com/btcsuite/btcwallet/wallet/txauthor" - "github.com/btcsuite/btcwallet/wallet/txrules" - "github.com/cpacia/bchutil" -) - -func (s *SPVWallet) Broadcast(tx *wire.MsgTx) error { - - // Our own tx; don't keep track of false positives - _, err := s.txstore.Ingest(tx, 0, time.Now()) - if err != nil { - return err - } - - // make an inv message instead of a tx message to be polite - txid := tx.TxHash() - iv1 := wire.NewInvVect(wire.InvTypeTx, &txid) - invMsg := wire.NewMsgInv() - err = invMsg.AddInvVect(iv1) - if err != nil { - return err - } - - s.wireService.MsgChan() <- updateFiltersMsg{} - log.Debugf("Broadcasting tx %s to peers", tx.TxHash().String()) - for _, peer := range s.peerManager.ConnectedPeers() { - peer.QueueMessage(tx, nil) - } - return nil -} - -type Coin struct { - TxHash *chainhash.Hash - TxIndex uint32 - TxValue btc.Amount - TxNumConfs int64 - ScriptPubKey []byte -} - -func (c *Coin) Hash() *chainhash.Hash { return c.TxHash } -func (c *Coin) Index() uint32 { return c.TxIndex } -func (c *Coin) Value() btc.Amount { return c.TxValue } -func (c *Coin) PkScript() []byte { return c.ScriptPubKey } -func (c *Coin) NumConfs() int64 { return c.TxNumConfs } -func (c *Coin) ValueAge() int64 { return int64(c.TxValue) * c.TxNumConfs } - -func NewCoin(txid []byte, index uint32, value btc.Amount, numConfs int64, scriptPubKey []byte) coinset.Coin { - shaTxid, _ := chainhash.NewHash(txid) - c := &Coin{ - TxHash: shaTxid, - TxIndex: index, - TxValue: value, - TxNumConfs: numConfs, - ScriptPubKey: scriptPubKey, - } - return coinset.Coin(c) -} - -func (w *SPVWallet) gatherCoins() map[coinset.Coin]*hd.ExtendedKey { - height, _ := w.blockchain.db.Height() - utxos, _ := w.txstore.Utxos().GetAll() - m := make(map[coinset.Coin]*hd.ExtendedKey) - for _, u := range utxos { - if u.WatchOnly { - continue - } - var confirmations int32 - if u.AtHeight > 0 { - confirmations = int32(height) - u.AtHeight - } - c := NewCoin(u.Op.Hash.CloneBytes(), u.Op.Index, btc.Amount(u.Value), int64(confirmations), u.ScriptPubkey) - addr, err := w.ScriptToAddress(u.ScriptPubkey) - if err != nil { - continue - } - key, err := w.keyManager.GetKeyForScript(addr.ScriptAddress()) - if err != nil { - continue - } - m[c] = key - } - return m -} - -func (w *SPVWallet) Spend(amount int64, addr btc.Address, feeLevel wallet.FeeLevel, referenceID string, spendAll bool) (*chainhash.Hash, error) { - var ( - tx *wire.MsgTx - err error - ) - if spendAll { - tx, err = w.buildSpendAllTx(addr, feeLevel) - if err != nil { - return nil, err - } - } else { - tx, err = w.buildTx(amount, addr, feeLevel, nil) - if err != nil { - return nil, err - } - } - - // Broadcast - if err := w.Broadcast(tx); err != nil { - return nil, err - } - - ch := tx.TxHash() - return &ch, nil -} - -var BumpFeeAlreadyConfirmedError = errors.New("Transaction is confirmed, cannot bump fee") -var BumpFeeTransactionDeadError = errors.New("Cannot bump fee of dead transaction") -var BumpFeeNotFoundError = errors.New("Transaction either doesn't exist or has already been spent") - -func (w *SPVWallet) BumpFee(txid chainhash.Hash) (*chainhash.Hash, error) { - txn, err := w.txstore.Txns().Get(txid) - if err != nil { - return nil, err - } - if txn.Height > 0 { - return nil, BumpFeeAlreadyConfirmedError - } - if txn.Height < 0 { - return nil, BumpFeeTransactionDeadError - } - // Check stxos for RBF opportunity - /*stxos, _ := w.txstore.Stxos().GetAll() - for _, s := range stxos { - if s.SpendTxid.IsEqual(&txid) { - r := bytes.NewReader(txn.Bytes) - msgTx := wire.NewMsgTx(1) - msgTx.BtcDecode(r, 1) - for i, output := range msgTx.TxOut { - key, err := w.txstore.GetKeyForScript(output.PkScript) - if key != nil && err == nil { // This is our change output - // Calculate change - additional fee - feePerByte := w.GetFeePerByte(PRIOIRTY) - estimatedSize := EstimateSerializeSize(len(msgTx.TxIn), msgTx.TxOut, false) - fee := estimatedSize * int(feePerByte) - newValue := output.Value - int64(fee) - - // Check if still above dust value - if newValue <= 0 || txrules.IsDustAmount(btc.Amount(newValue), len(output.PkScript), txrules.DefaultRelayFeePerKb) { - msgTx.TxOut = append(msgTx.TxOut[:i], msgTx.TxOut[i+1:]...) - } else { - output.Value = newValue - } - - // Bump sequence number - optInRBF := false - for _, input := range msgTx.TxIn { - if input.Sequence < 4294967294 { - input.Sequence++ - optInRBF = true - } - } - if !optInRBF { - break - } - - //TODO: Re-sign transaction - - // Mark original tx as dead - if err = w.txstore.markAsDead(txid); err != nil { - return nil, err - } - - // Broadcast new tx - if err := w.Broadcast(msgTx); err != nil { - return nil, err - } - newTxid := msgTx.TxHash() - return &newTxid, nil - } - } - } - }*/ - // Check utxos for CPFP - utxos, _ := w.txstore.Utxos().GetAll() - for _, u := range utxos { - if u.Op.Hash.IsEqual(&txid) && u.AtHeight == 0 { - addr, err := w.ScriptToAddress(u.ScriptPubkey) - if err != nil { - return nil, err - } - key, err := w.keyManager.GetKeyForScript(addr.ScriptAddress()) - if err != nil { - return nil, err - } - h, err := hex.DecodeString(u.Op.Hash.String()) - if err != nil { - return nil, err - } - in := wallet.TransactionInput{ - LinkedAddress: addr, - OutpointIndex: u.Op.Index, - OutpointHash: h, - Value: u.Value, - } - transactionID, err := w.SweepAddress([]wallet.TransactionInput{in}, nil, key, nil, wallet.FEE_BUMP) - if err != nil { - return nil, err - } - return transactionID, nil - } - } - return nil, BumpFeeNotFoundError -} - -func (w *SPVWallet) EstimateFee(ins []wallet.TransactionInput, outs []wallet.TransactionOutput, feePerByte uint64) uint64 { - tx := wire.NewMsgTx(1) - for _, out := range outs { - scriptPubKey, _ := bchutil.PayToAddrScript(out.Address) - output := wire.NewTxOut(out.Value, scriptPubKey) - tx.TxOut = append(tx.TxOut, output) - } - estimatedSize := EstimateSerializeSize(len(ins), tx.TxOut, false, P2PKH) - fee := estimatedSize * int(feePerByte) - return uint64(fee) -} - -// Build a spend transaction for the amount and return the transaction fee -func (w *SPVWallet) EstimateSpendFee(amount int64, feeLevel wallet.FeeLevel) (uint64, error) { - // Since this is an estimate we can use a dummy output address. Let's use a long one so we don't under estimate. - addr, err := btc.DecodeAddress("114K8nZhYcG1rsxcc1YGujFwWj5NLByc5v", w.params) - if err != nil { - return 0, err - } - tx, err := w.buildTx(amount, addr, feeLevel, nil) - if err != nil { - return 0, err - } - var outval int64 - for _, output := range tx.TxOut { - outval += output.Value - } - var inval int64 - utxos, err := w.txstore.Utxos().GetAll() - if err != nil { - return 0, err - } - for _, input := range tx.TxIn { - for _, utxo := range utxos { - if utxo.Op.Hash.IsEqual(&input.PreviousOutPoint.Hash) && utxo.Op.Index == input.PreviousOutPoint.Index { - inval += utxo.Value - break - } - } - } - if inval < outval { - return 0, errors.New("Error building transaction: inputs less than outputs") - } - return uint64(inval - outval), err -} - -func (w *SPVWallet) GenerateMultisigScript(keys []hd.ExtendedKey, threshold int, timeout time.Duration, timeoutKey *hd.ExtendedKey) (addr btc.Address, redeemScript []byte, err error) { - if uint32(timeout.Hours()) > 0 && timeoutKey == nil { - return nil, nil, errors.New("Timeout key must be non nil when using an escrow timeout") - } - - if len(keys) < threshold { - return nil, nil, fmt.Errorf("unable to generate multisig script with "+ - "%d required signatures when there are only %d public "+ - "keys available", threshold, len(keys)) - } - - var ecKeys []*btcec.PublicKey - for _, key := range keys { - ecKey, err := key.ECPubKey() - if err != nil { - return nil, nil, err - } - ecKeys = append(ecKeys, ecKey) - } - - builder := txscript.NewScriptBuilder() - if uint32(timeout.Hours()) == 0 { - - builder.AddInt64(int64(threshold)) - for _, key := range ecKeys { - builder.AddData(key.SerializeCompressed()) - } - builder.AddInt64(int64(len(ecKeys))) - builder.AddOp(txscript.OP_CHECKMULTISIG) - - } else { - ecKey, err := timeoutKey.ECPubKey() - if err != nil { - return nil, nil, err - } - sequenceLock := blockchain.LockTimeToSequence(false, uint32(timeout.Hours()*6)) - builder.AddOp(txscript.OP_IF) - builder.AddInt64(int64(threshold)) - for _, key := range ecKeys { - builder.AddData(key.SerializeCompressed()) - } - builder.AddInt64(int64(len(ecKeys))) - builder.AddOp(txscript.OP_CHECKMULTISIG) - builder.AddOp(txscript.OP_ELSE). - AddInt64(int64(sequenceLock)). - AddOp(txscript.OP_CHECKSEQUENCEVERIFY). - AddOp(txscript.OP_DROP). - AddData(ecKey.SerializeCompressed()). - AddOp(txscript.OP_CHECKSIG). - AddOp(txscript.OP_ENDIF) - } - redeemScript, err = builder.Script() - if err != nil { - return nil, nil, err - } - addr, err = bchutil.NewCashAddressScriptHash(redeemScript, w.params) - if err != nil { - return nil, nil, err - } - return addr, redeemScript, nil -} - -func (w *SPVWallet) CreateMultisigSignature(ins []wallet.TransactionInput, outs []wallet.TransactionOutput, key *hd.ExtendedKey, redeemScript []byte, feePerByte uint64) ([]wallet.Signature, error) { - var sigs []wallet.Signature - tx := wire.NewMsgTx(1) - for _, in := range ins { - ch, err := chainhash.NewHashFromStr(hex.EncodeToString(in.OutpointHash)) - if err != nil { - return sigs, err - } - outpoint := wire.NewOutPoint(ch, in.OutpointIndex) - input := wire.NewTxIn(outpoint, []byte{}, [][]byte{}) - tx.TxIn = append(tx.TxIn, input) - } - for _, out := range outs { - scriptPubkey, err := bchutil.PayToAddrScript(out.Address) - if err != nil { - return sigs, err - } - output := wire.NewTxOut(out.Value, scriptPubkey) - tx.TxOut = append(tx.TxOut, output) - } - - // Subtract fee - txType := P2SH_2of3_Multisig - _, err := LockTimeFromRedeemScript(redeemScript) - if err == nil { - txType = P2SH_Multisig_Timelock_2Sigs - } - estimatedSize := EstimateSerializeSize(len(ins), tx.TxOut, false, txType) - fee := estimatedSize * int(feePerByte) - if len(tx.TxOut) > 0 { - feePerOutput := fee / len(tx.TxOut) - for _, output := range tx.TxOut { - output.Value -= int64(feePerOutput) - } - } - - // BIP 69 sorting - txsort.InPlaceSort(tx) - - signingKey, err := key.ECPrivKey() - if err != nil { - return sigs, err - } - - for i := range tx.TxIn { - sig, err := bchutil.RawTxInSignature(tx, i, redeemScript, txscript.SigHashAll, signingKey, ins[i].Value) - if err != nil { - continue - } - bs := wallet.Signature{InputIndex: uint32(i), Signature: sig} - sigs = append(sigs, bs) - } - return sigs, nil -} - -func (w *SPVWallet) Multisign(ins []wallet.TransactionInput, outs []wallet.TransactionOutput, sigs1 []wallet.Signature, sigs2 []wallet.Signature, redeemScript []byte, feePerByte uint64, broadcast bool) ([]byte, error) { - tx := wire.NewMsgTx(1) - for _, in := range ins { - ch, err := chainhash.NewHashFromStr(hex.EncodeToString(in.OutpointHash)) - if err != nil { - return nil, err - } - outpoint := wire.NewOutPoint(ch, in.OutpointIndex) - input := wire.NewTxIn(outpoint, []byte{}, [][]byte{}) - tx.TxIn = append(tx.TxIn, input) - } - for _, out := range outs { - scriptPubkey, err := bchutil.PayToAddrScript(out.Address) - if err != nil { - return nil, err - } - output := wire.NewTxOut(out.Value, scriptPubkey) - tx.TxOut = append(tx.TxOut, output) - } - - // Subtract fee - txType := P2SH_2of3_Multisig - _, err := LockTimeFromRedeemScript(redeemScript) - if err == nil { - txType = P2SH_Multisig_Timelock_2Sigs - } - estimatedSize := EstimateSerializeSize(len(ins), tx.TxOut, false, txType) - fee := estimatedSize * int(feePerByte) - if len(tx.TxOut) > 0 { - feePerOutput := fee / len(tx.TxOut) - for _, output := range tx.TxOut { - output.Value -= int64(feePerOutput) - } - } - - // BIP 69 sorting - txsort.InPlaceSort(tx) - - // Check if time locked - var timeLocked bool - if redeemScript[0] == txscript.OP_IF { - timeLocked = true - } - - for i, input := range tx.TxIn { - var sig1 []byte - var sig2 []byte - for _, sig := range sigs1 { - if int(sig.InputIndex) == i { - sig1 = sig.Signature - } - } - for _, sig := range sigs2 { - if int(sig.InputIndex) == i { - sig2 = sig.Signature - } - } - builder := txscript.NewScriptBuilder() - builder.AddOp(txscript.OP_0) - builder.AddData(sig1) - builder.AddData(sig2) - - if timeLocked { - builder.AddOp(txscript.OP_1) - } - - builder.AddData(redeemScript) - scriptSig, err := builder.Script() - if err != nil { - return nil, err - } - input.SignatureScript = scriptSig - } - // broadcast - if broadcast { - w.Broadcast(tx) - } - var buf bytes.Buffer - tx.BtcEncode(&buf, 1, wire.BaseEncoding) - return buf.Bytes(), nil -} - -func (w *SPVWallet) SweepAddress(ins []wallet.TransactionInput, address *btc.Address, key *hd.ExtendedKey, redeemScript *[]byte, feeLevel wallet.FeeLevel) (*chainhash.Hash, error) { - var internalAddr btc.Address - if address != nil { - internalAddr = *address - } else { - internalAddr = w.CurrentAddress(wallet.INTERNAL) - } - script, err := bchutil.PayToAddrScript(internalAddr) - if err != nil { - return nil, err - } - - var val int64 - var inputs []*wire.TxIn - additionalPrevScripts := make(map[wire.OutPoint][]byte) - for _, in := range ins { - val += in.Value - ch, err := chainhash.NewHashFromStr(hex.EncodeToString(in.OutpointHash)) - if err != nil { - return nil, err - } - script, err := bchutil.PayToAddrScript(in.LinkedAddress) - if err != nil { - return nil, err - } - outpoint := wire.NewOutPoint(ch, in.OutpointIndex) - input := wire.NewTxIn(outpoint, []byte{}, [][]byte{}) - inputs = append(inputs, input) - additionalPrevScripts[*outpoint] = script - } - out := wire.NewTxOut(val, script) - - txType := P2PKH - if redeemScript != nil { - txType = P2SH_1of2_Multisig - _, err := LockTimeFromRedeemScript(*redeemScript) - if err == nil { - txType = P2SH_Multisig_Timelock_1Sig - } - } - estimatedSize := EstimateSerializeSize(len(ins), []*wire.TxOut{out}, false, txType) - - // Calculate the fee - feePerByte := int(w.GetFeePerByte(feeLevel)) - fee := estimatedSize * feePerByte - - outVal := val - int64(fee) - if outVal < 0 { - outVal = 0 - } - out.Value = outVal - - tx := &wire.MsgTx{ - Version: wire.TxVersion, - TxIn: inputs, - TxOut: []*wire.TxOut{out}, - LockTime: 0, - } - - // BIP 69 sorting - txsort.InPlaceSort(tx) - - // Sign tx - privKey, err := key.ECPrivKey() - if err != nil { - return nil, err - } - pk := privKey.PubKey().SerializeCompressed() - addressPub, err := btc.NewAddressPubKey(pk, w.params) - - getKey := txscript.KeyClosure(func(addr btc.Address) (*btcec.PrivateKey, bool, error) { - if addressPub.EncodeAddress() == addr.EncodeAddress() { - wif, err := btc.NewWIF(privKey, w.params, true) - if err != nil { - return nil, false, err - } - return wif.PrivKey, wif.CompressPubKey, nil - } - return nil, false, errors.New("Not found") - }) - getScript := txscript.ScriptClosure(func(addr btc.Address) ([]byte, error) { - if redeemScript == nil { - return []byte{}, nil - } - return *redeemScript, nil - }) - - // Check if time locked - var timeLocked bool - if redeemScript != nil { - rs := *redeemScript - if rs[0] == txscript.OP_IF { - timeLocked = true - tx.Version = 2 - for _, txIn := range tx.TxIn { - locktime, err := LockTimeFromRedeemScript(*redeemScript) - if err != nil { - return nil, err - } - txIn.Sequence = locktime - } - } - } - - for i, txIn := range tx.TxIn { - if !timeLocked { - prevOutScript := additionalPrevScripts[txIn.PreviousOutPoint] - script, err := bchutil.SignTxOutput(w.params, - tx, i, prevOutScript, txscript.SigHashAll, getKey, - getScript, txIn.SignatureScript, ins[i].Value) - if err != nil { - return nil, errors.New("Failed to sign transaction") - } - txIn.SignatureScript = script - } else { - priv, err := key.ECPrivKey() - if err != nil { - return nil, err - } - script, err := bchutil.RawTxInSignature(tx, i, *redeemScript, txscript.SigHashAll, priv, ins[i].Value) - if err != nil { - return nil, err - } - builder := txscript.NewScriptBuilder(). - AddData(script). - AddOp(txscript.OP_0). - AddData(*redeemScript) - scriptSig, _ := builder.Script() - txIn.SignatureScript = scriptSig - } - } - - // broadcast - w.Broadcast(tx) - txid := tx.TxHash() - return &txid, nil -} - -func (w *SPVWallet) buildTx(amount int64, addr btc.Address, feeLevel wallet.FeeLevel, optionalOutput *wire.TxOut) (*wire.MsgTx, error) { - // Check for dust - script, _ := bchutil.PayToAddrScript(addr) - if txrules.IsDustAmount(btc.Amount(amount), len(script), txrules.DefaultRelayFeePerKb) { - return nil, errors.New("Amount is below dust threshold") - } - - var additionalPrevScripts map[wire.OutPoint][]byte - var additionalKeysByAddress map[string]*btc.WIF - var inVals map[wire.OutPoint]int64 - - // Create input source - coinMap := w.gatherCoins() - coins := make([]coinset.Coin, 0, len(coinMap)) - for k := range coinMap { - coins = append(coins, k) - } - inputSource := func(target btc.Amount) (total btc.Amount, inputs []*wire.TxIn, amounts []btc.Amount, scripts [][]byte, err error) { - coinSelector := coinset.MaxValueAgeCoinSelector{MaxInputs: 10000, MinChangeAmount: btc.Amount(0)} - coins, err := coinSelector.CoinSelect(target, coins) - if err != nil { - return total, inputs, []btc.Amount{}, scripts, errors.New("insuffient funds") - } - additionalPrevScripts = make(map[wire.OutPoint][]byte) - inVals = make(map[wire.OutPoint]int64) - additionalKeysByAddress = make(map[string]*btc.WIF) - for _, c := range coins.Coins() { - total += c.Value() - outpoint := wire.NewOutPoint(c.Hash(), c.Index()) - in := wire.NewTxIn(outpoint, []byte{}, [][]byte{}) - inputs = append(inputs, in) - additionalPrevScripts[*outpoint] = c.PkScript() - key := coinMap[c] - addr, err := key.Address(w.params) - if err != nil { - continue - } - privKey, err := key.ECPrivKey() - if err != nil { - continue - } - wif, _ := btc.NewWIF(privKey, w.params, true) - additionalKeysByAddress[addr.EncodeAddress()] = wif - val := c.Value() - sat := val.ToUnit(btc.AmountSatoshi) - inVals[*outpoint] = int64(sat) - } - return total, inputs, []btc.Amount{}, scripts, nil - } - - // Get the fee per kilobyte - feePerKB := int64(w.GetFeePerByte(feeLevel)) * 1000 - - // outputs - out := wire.NewTxOut(amount, script) - - // Create change source - changeSource := func() ([]byte, error) { - addr := w.CurrentAddress(wallet.INTERNAL) - script, err := bchutil.PayToAddrScript(addr) - if err != nil { - return []byte{}, err - } - return script, nil - } - - outputs := []*wire.TxOut{out} - if optionalOutput != nil { - outputs = append(outputs, optionalOutput) - } - authoredTx, err := NewUnsignedTransaction(outputs, btc.Amount(feePerKB), inputSource, changeSource) - if err != nil { - return nil, err - } - - // BIP 69 sorting - txsort.InPlaceSort(authoredTx.Tx) - - // Sign tx - getKey := txscript.KeyClosure(func(addr btc.Address) (*btcec.PrivateKey, bool, error) { - addrStr := addr.EncodeAddress() - wif := additionalKeysByAddress[addrStr] - return wif.PrivKey, wif.CompressPubKey, nil - }) - getScript := txscript.ScriptClosure(func( - addr btc.Address) ([]byte, error) { - return []byte{}, nil - }) - for i, txIn := range authoredTx.Tx.TxIn { - prevOutScript := additionalPrevScripts[txIn.PreviousOutPoint] - script, err := bchutil.SignTxOutput(w.params, - authoredTx.Tx, i, prevOutScript, txscript.SigHashAll, getKey, - getScript, txIn.SignatureScript, inVals[txIn.PreviousOutPoint]) - if err != nil { - return nil, errors.New("Failed to sign transaction") - } - txIn.SignatureScript = script - } - return authoredTx.Tx, nil -} - -func (w *SPVWallet) buildSpendAllTx(addr btc.Address, feeLevel wallet.FeeLevel) (*wire.MsgTx, error) { - tx := wire.NewMsgTx(1) - - coinMap := w.gatherCoins() - - inVals := make(map[wire.OutPoint]int64) - totalIn := int64(0) - additionalPrevScripts := make(map[wire.OutPoint][]byte) - additionalKeysByAddress := make(map[string]*btc.WIF) - - for coin, key := range coinMap { - outpoint := wire.NewOutPoint(coin.Hash(), coin.Index()) - in := wire.NewTxIn(outpoint, nil, nil) - additionalPrevScripts[*outpoint] = coin.PkScript() - tx.TxIn = append(tx.TxIn, in) - val := int64(coin.Value().ToUnit(btc.AmountSatoshi)) - totalIn += val - inVals[*outpoint] = val - - addr, err := key.Address(w.params) - if err != nil { - continue - } - privKey, err := key.ECPrivKey() - if err != nil { - continue - } - wif, _ := btc.NewWIF(privKey, w.params, true) - additionalKeysByAddress[addr.EncodeAddress()] = wif - } - - // outputs - script, err := bchutil.PayToAddrScript(addr) - if err != nil { - return nil, err - } - - // Get the fee - feePerByte := int64(w.GetFeePerByte(feeLevel)) - estimatedSize := EstimateSerializeSize(1, []*wire.TxOut{wire.NewTxOut(0, script)}, false, P2PKH) - fee := int64(estimatedSize) * feePerByte - - // Check for dust output - if txrules.IsDustAmount(btc.Amount(totalIn-fee), len(script), txrules.DefaultRelayFeePerKb) { - return nil, wallet.ErrorDustAmount - } - - // Build the output - out := wire.NewTxOut(totalIn-fee, script) - tx.TxOut = append(tx.TxOut, out) - - // BIP 69 sorting - txsort.InPlaceSort(tx) - - // Sign - getKey := txscript.KeyClosure(func(addr btc.Address) (*btcec.PrivateKey, bool, error) { - addrStr := addr.EncodeAddress() - wif, ok := additionalKeysByAddress[addrStr] - if !ok { - return nil, false, errors.New("key not found") - } - return wif.PrivKey, wif.CompressPubKey, nil - }) - getScript := txscript.ScriptClosure(func( - addr btc.Address) ([]byte, error) { - return []byte{}, nil - }) - for i, txIn := range tx.TxIn { - prevOutScript := additionalPrevScripts[txIn.PreviousOutPoint] - script, err := bchutil.SignTxOutput(w.params, - tx, i, prevOutScript, txscript.SigHashAll, getKey, - getScript, txIn.SignatureScript, inVals[txIn.PreviousOutPoint]) - if err != nil { - return nil, errors.New("failed to sign transaction") - } - txIn.SignatureScript = script - } - return tx, nil -} - -func NewUnsignedTransaction(outputs []*wire.TxOut, feePerKb btc.Amount, fetchInputs txauthor.InputSource, fetchChange txauthor.ChangeSource) (*txauthor.AuthoredTx, error) { - - var targetAmount btc.Amount - for _, txOut := range outputs { - targetAmount += btc.Amount(txOut.Value) - } - - estimatedSize := EstimateSerializeSize(1, outputs, true, P2PKH) - targetFee := txrules.FeeForSerializeSize(feePerKb, estimatedSize) - - for { - inputAmount, inputs, _, scripts, err := fetchInputs(targetAmount + targetFee) - if err != nil { - return nil, err - } - if inputAmount < targetAmount+targetFee { - return nil, errors.New("insufficient funds available to construct transaction") - } - - maxSignedSize := EstimateSerializeSize(len(inputs), outputs, true, P2PKH) - maxRequiredFee := txrules.FeeForSerializeSize(feePerKb, maxSignedSize) - remainingAmount := inputAmount - targetAmount - if remainingAmount < maxRequiredFee { - targetFee = maxRequiredFee - continue - } - - unsignedTransaction := &wire.MsgTx{ - Version: wire.TxVersion, - TxIn: inputs, - TxOut: outputs, - LockTime: 0, - } - changeIndex := -1 - changeAmount := inputAmount - targetAmount - maxRequiredFee - if changeAmount != 0 && !txrules.IsDustAmount(changeAmount, - P2PKHOutputSize, txrules.DefaultRelayFeePerKb) { - changeScript, err := fetchChange() - if err != nil { - return nil, err - } - if len(changeScript) > P2PKHPkScriptSize { - return nil, errors.New("fee estimation requires change " + - "scripts no larger than P2PKH output scripts") - } - change := wire.NewTxOut(int64(changeAmount), changeScript) - l := len(outputs) - unsignedTransaction.TxOut = append(outputs[:l:l], change) - changeIndex = l - } - - return &txauthor.AuthoredTx{ - Tx: unsignedTransaction, - PrevScripts: scripts, - TotalInput: inputAmount, - ChangeIndex: changeIndex, - }, nil - } -} - -func (w *SPVWallet) GetFeePerByte(feeLevel wallet.FeeLevel) uint64 { - return w.feeProvider.GetFeePerByte(feeLevel) -} - -func LockTimeFromRedeemScript(redeemScript []byte) (uint32, error) { - if len(redeemScript) < 113 { - return 0, errors.New("Redeem script invalid length") - } - if redeemScript[106] != 103 { - return 0, errors.New("Invalid redeem script") - } - if redeemScript[107] == 0 { - return 0, nil - } - if 81 <= redeemScript[107] && redeemScript[107] <= 96 { - return uint32((redeemScript[107] - 81) + 1), nil - } - var v []byte - op := redeemScript[107] - if 1 <= op && op <= 75 { - for i := 0; i < int(op); i++ { - v = append(v, []byte{redeemScript[108+i]}...) - } - } else { - return 0, errors.New("Too many bytes pushed for sequence") - } - var result int64 - for i, val := range v { - result |= int64(val) << uint8(8*i) - } - - return uint32(result), nil -} diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/test_compile.sh b/vendor/github.com/cpacia/BitcoinCash-Wallet/test_compile.sh deleted file mode 100755 index 878211bd62..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/test_compile.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -set -e -pwd -go get github.com/cpacia/BitcoinCash-Wallet -go get github.com/mattn/go-sqlite3 -go test -coverprofile=bitcoincash.cover.out ./ -echo "mode: set" > coverage.out && cat *.cover.out | grep -v mode: | sort -r | \ -awk '{if($1 != last) {print $0;last=$1}}' >> coverage.out -rm -rf *.cover.out diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/timesorter.go b/vendor/github.com/cpacia/BitcoinCash-Wallet/timesorter.go deleted file mode 100644 index f3139ec0fe..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/timesorter.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2013-2017 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package bitcoincash - -// timeSorter implements sort.Interface to allow a slice of timestamps to -// be sorted. -type timeSorter []int64 - -// Len returns the number of timestamps in the slice. It is part of the -// sort.Interface implementation. -func (s timeSorter) Len() int { - return len(s) -} - -// Swap swaps the timestamps at the passed indices. It is part of the -// sort.Interface implementation. -func (s timeSorter) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -// Less returns whether the timstamp with index i should sort before the -// timestamp with index j. It is part of the sort.Interface implementation. -func (s timeSorter) Less(i, j int) bool { - return s[i] < s[j] -} - -// timeSorter implements sort.Interface to allow a slice of block headers to -// be sorted by timestamp. -type blockSorter []StoredHeader - -// Len returns the number of timestamps in the slice. It is part of the -// sort.Interface implementation. -func (s blockSorter) Len() int { - return len(s) -} - -// Swap swaps the timestamps at the passed indices. It is part of the -// sort.Interface implementation. -func (s blockSorter) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -// Less returns whether the timstamp with index i should sort before the -// timestamp with index j. It is part of the sort.Interface implementation. -func (s blockSorter) Less(i, j int) bool { - if s[i].header.Timestamp.Before(s[j].header.Timestamp) || s[i].header.Timestamp.Equal(s[j].header.Timestamp) { - return true - } - return false -} diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/tor.go b/vendor/github.com/cpacia/BitcoinCash-Wallet/tor.go deleted file mode 100644 index 189bd3c86f..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/tor.go +++ /dev/null @@ -1,135 +0,0 @@ -package bitcoincash - -// Copyright (c) 2013-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -import ( - "encoding/binary" - "errors" - "net" -) - -const ( - torSucceeded = 0x00 - torGeneralError = 0x01 - torNotAllowed = 0x02 - torNetUnreachable = 0x03 - torHostUnreachable = 0x04 - torConnectionRefused = 0x05 - torTTLExpired = 0x06 - torCmdNotSupported = 0x07 - torAddrNotSupported = 0x08 -) - -var ( - // ErrTorInvalidAddressResponse indicates an invalid address was - // returned by the Tor DNS resolver. - ErrTorInvalidAddressResponse = errors.New("invalid address response") - - // ErrTorInvalidProxyResponse indicates the Tor proxy returned a - // response in an unexpected format. - ErrTorInvalidProxyResponse = errors.New("invalid proxy response") - - // ErrTorUnrecognizedAuthMethod indicates the authentication method - // provided is not recognized. - ErrTorUnrecognizedAuthMethod = errors.New("invalid proxy authentication method") - - torStatusErrors = map[byte]error{ - torSucceeded: errors.New("tor succeeded"), - torGeneralError: errors.New("tor general error"), - torNotAllowed: errors.New("tor not allowed"), - torNetUnreachable: errors.New("tor network is unreachable"), - torHostUnreachable: errors.New("tor host is unreachable"), - torConnectionRefused: errors.New("tor connection refused"), - torTTLExpired: errors.New("tor TTL expired"), - torCmdNotSupported: errors.New("tor command not supported"), - torAddrNotSupported: errors.New("tor address type not supported"), - } -) - -// TorLookupIP uses Tor to resolve DNS via the SOCKS extension they provide for -// resolution over the Tor network. Tor itself doesn't support ipv6 so this -// doesn't either. -func TorLookupIP(host string) ([]net.IP, error) { - conn, err := net.Dial("tcp", "127.0.0.1:9150") - if err != nil { - conn, err = net.Dial("tcp", "127.0.0.1:9050") - if err != nil { - return nil, err - } - } - defer conn.Close() - - buf := []byte{'\x05', '\x01', '\x00'} - _, err = conn.Write(buf) - if err != nil { - return nil, err - } - - buf = make([]byte, 2) - _, err = conn.Read(buf) - if err != nil { - return nil, err - } - if buf[0] != '\x05' { - return nil, ErrTorInvalidProxyResponse - } - if buf[1] != '\x00' { - return nil, ErrTorUnrecognizedAuthMethod - } - - buf = make([]byte, 7+len(host)) - buf[0] = 5 // protocol version - buf[1] = '\xF0' // Tor Resolve - buf[2] = 0 // reserved - buf[3] = 3 // Tor Resolve - buf[4] = byte(len(host)) - copy(buf[5:], host) - buf[5+len(host)] = 0 // Port 0 - - _, err = conn.Write(buf) - if err != nil { - return nil, err - } - - buf = make([]byte, 4) - _, err = conn.Read(buf) - if err != nil { - return nil, err - } - if buf[0] != 5 { - return nil, ErrTorInvalidProxyResponse - } - if buf[1] != 0 { - if int(buf[1]) > len(torStatusErrors) { - err = ErrTorInvalidProxyResponse - } else { - err = torStatusErrors[buf[1]] - if err == nil { - err = ErrTorInvalidProxyResponse - } - } - return nil, err - } - if buf[3] != 1 { - err := torStatusErrors[torGeneralError] - return nil, err - } - - buf = make([]byte, 4) - bytes, err := conn.Read(buf) - if err != nil { - return nil, err - } - if bytes != 4 { - return nil, ErrTorInvalidAddressResponse - } - - r := binary.BigEndian.Uint32(buf) - - addr := make([]net.IP, 1) - addr[0] = net.IPv4(byte(r>>24), byte(r>>16), byte(r>>8), byte(r)) - - return addr, nil -} diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/txsizes.go b/vendor/github.com/cpacia/BitcoinCash-Wallet/txsizes.go deleted file mode 100644 index 0db2af0eb2..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/txsizes.go +++ /dev/null @@ -1,249 +0,0 @@ -package bitcoincash - -// Copyright (c) 2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -/* Copied here from a btcd internal package*/ - -import ( - "github.com/btcsuite/btcd/wire" -) - -// Worst case script and input/output size estimates. -const ( - // RedeemP2PKHSigScriptSize is the worst case (largest) serialize size - // of a transaction input script that redeems a compressed P2PKH output. - // It is calculated as: - // - // - OP_DATA_73 - // - 72 bytes DER signature + 1 byte sighash - // - OP_DATA_33 - // - 33 bytes serialized compressed pubkey - RedeemP2PKHSigScriptSize = 1 + 73 + 1 + 33 - - // RedeemP2SHMultisigSigScriptSize is the worst case (largest) serialize size - // of a transaction input script that redeems a 2 of 3 P2SH multisig output with compressed keys. - // It is calculated as: - // - // - OP_0 - // - OP_DATA_72 - // - 72 bytes DER signature - // - OP_DATA_72 - // - 72 bytes DER signature - // - OP_PUSHDATA - // - OP_2 - // - OP_DATA_33 - // - 33 bytes serialized compressed pubkey - // - OP_DATA_33 - // - 33 bytes serialized compressed pubkey - // - OP_DATA_33 - // - 33 bytes serialized compressed pubkey - // - OP3 - // - OP_CHECKMULTISIG - RedeemP2SH2of3MultisigSigScriptSize = 1 + 1 + 72 + 1 + 72 + 1 + 1 + 1 + 33 + 1 + 33 + 1 + 33 + 1 + 1 - - // RedeemP2SH1of2MultisigSigScriptSize is the worst case (largest) serialize size - // of a transaction input script that redeems a 1 of 2 P2SH multisig output with compressed keys. - // It is calculated as: - // - // - OP_0 - // - OP_DATA_72 - // - 72 bytes DER signature - // - OP_PUSHDATA - // - OP_1 - // - OP_DATA_33 - // - 33 bytes serialized compressed pubkey - // - OP_DATA_33 - // - 33 bytes serialized compressed pubkey - // - OP2 - // - OP_CHECKMULTISIG - RedeemP2SH1of2MultisigSigScriptSize = 1 + 1 + 72 + 1 + 1 + 1 + 33 + 1 + 33 + 1 + 1 - - // RedeemP2SHMultisigTimelock1SigScriptSize is the worst case (largest) serialize size - // of a transaction input script that redeems a compressed P2SH timelocked multisig using the timeout. - // It is calculated as: - // - // - OP_DATA_72 - // - 72 bytes DER signature - // - OP_0 - // - OP_PUSHDATA - // - OP_IF - // - OP_2 - // - OP_DATA_33 - // - 33 bytes serialized compressed pubkey - // - OP_DATA_33 - // - 33 bytes serialized compressed pubkey - // - OP_DATA_33 - // - 33 bytes serialized compressed pubkey - // - OP3 - // - OP_CHECKMULTISIG - // - OP_ELSE - // - OP_PUSHDATA - // - 2 byte block height - // - OP_CHECKSEQUENCEVERIFY - // - OP_DROP - // - OP_DATA_33 - // - 33 bytes serialized compressed pubkey - // - OP_CHECKSIG - // - OP_ENDIF - RedeemP2SHMultisigTimelock1SigScriptSize = 1 + 72 + 1 + 1 + 1 + 1 + 1 + 33 + 1 + 33 + 1 + 33 + 1 + 1 + 1 + 1 + 2 + 1 + 1 + 1 + 33 + 1 + 1 - - // RedeemP2SHMultisigTimelock2SigScriptSize is the worst case (largest) serialize size - // of a transaction input script that redeems a compressed P2SH timelocked multisig without using the timeout. - // It is calculated as: - // - // - OP_0 - // - OP_DATA_72 - // - 72 bytes DER signature - // - OP_DATA_72 - // - 72 bytes DER signature - // - OP_1 - // - OP_PUSHDATA - // - OP_IF - // - OP_2 - // - OP_DATA_33 - // - 33 bytes serialized compressed pubkey - // - OP_DATA_33 - // - 33 bytes serialized compressed pubkey - // - OP_DATA_33 - // - 33 bytes serialized compressed pubkey - // - OP3 - // - OP_CHECKMULTISIG - // - OP_ELSE - // - OP_PUSHDATA - // - 2 byte block height - // - OP_CHECKSEQUENCEVERIFY - // - OP_DROP - // - OP_DATA_33 - // - 33 bytes serialized compressed pubkey - // - OP_CHECKSIG - // - OP_ENDIF - RedeemP2SHMultisigTimelock2SigScriptSize = 1 + 1 + 72 + +1 + 72 + 1 + 1 + 1 + 1 + 1 + 33 + 1 + 33 + 1 + 33 + 1 + 1 + 1 + 1 + 2 + 1 + 1 + 1 + 33 + 1 + 1 - - // P2PKHPkScriptSize is the size of a transaction output script that - // pays to a compressed pubkey hash. It is calculated as: - // - // - OP_DUP - // - OP_HASH160 - // - OP_DATA_20 - // - 20 bytes pubkey hash - // - OP_EQUALVERIFY - // - OP_CHECKSIG - P2PKHPkScriptSize = 1 + 1 + 1 + 20 + 1 + 1 - - // RedeemP2PKHInputSize is the worst case (largest) serialize size of a - // transaction input redeeming a compressed P2PKH output. It is - // calculated as: - // - // - 32 bytes previous tx - // - 4 bytes output index - // - 1 byte script len - // - signature script - // - 4 bytes sequence - RedeemP2PKHInputSize = 32 + 4 + 1 + RedeemP2PKHSigScriptSize + 4 - - // RedeemP2SH2of3MultisigInputSize is the worst case (largest) serialize size of a - // transaction input redeeming a compressed P2SH 2 of 3 multisig output. It is - // calculated as: - // - // - 32 bytes previous tx - // - 4 bytes output index - // - 1 byte script len - // - 4 bytes sequence - /// - witness discounted signature script - RedeemP2SH2of3MultisigInputSize = 32 + 4 + 1 + 4 + (RedeemP2SH2of3MultisigSigScriptSize / 4) - - // RedeemP2SH1of2MultisigInputSize is the worst case (largest) serialize size of a - // transaction input redeeming a compressed P2SH 2 of 3 multisig output. It is - // calculated as: - // - // - 32 bytes previous tx - // - 4 bytes output index - // - 1 byte script len - // - 4 bytes sequence - /// - witness discounted signature script - RedeemP2SH1of2MultisigInputSize = 32 + 4 + 1 + 4 + (RedeemP2SH1of2MultisigSigScriptSize / 4) - - // RedeemP2SHMultisigTimelock1InputSize is the worst case (largest) serialize size of a - // transaction input redeeming a compressed p2sh timelocked multig output with using the timeout. It is - // calculated as: - // - // - 32 bytes previous tx - // - 4 bytes output index - // - 1 byte script len - // - 4 bytes sequence - /// - witness discounted signature script - RedeemP2SHMultisigTimelock1InputSize = 32 + 4 + 1 + 4 + (RedeemP2SHMultisigTimelock1SigScriptSize / 4) - - // RedeemP2SHMultisigTimelock2InputSize is the worst case (largest) serialize size of a - // transaction input redeeming a compressed P2SH timelocked multisig output without using the timeout. It is - // calculated as: - // - // - 32 bytes previous tx - // - 4 bytes output index - // - 1 byte script len - // - 4 bytes sequence - /// - witness discounted signature script - RedeemP2SHMultisigTimelock2InputSize = 32 + 4 + 1 + 4 + (RedeemP2SHMultisigTimelock2SigScriptSize / 4) - - // P2PKHOutputSize is the serialize size of a transaction output with a - // P2PKH output script. It is calculated as: - // - // - 8 bytes output value - // - 1 byte compact int encoding value 25 - // - 25 bytes P2PKH output script - P2PKHOutputSize = 8 + 1 + P2PKHPkScriptSize -) - -type InputType int - -const ( - P2PKH InputType = iota - P2SH_1of2_Multisig - P2SH_2of3_Multisig - P2SH_Multisig_Timelock_1Sig - P2SH_Multisig_Timelock_2Sigs -) - -// EstimateSerializeSize returns a worst case serialize size estimate for a -// signed transaction that spends inputCount number of compressed P2PKH outputs -// and contains each transaction output from txOuts. The estimated size is -// incremented for an additional P2PKH change output if addChangeOutput is true. -func EstimateSerializeSize(inputCount int, txOuts []*wire.TxOut, addChangeOutput bool, inputType InputType) int { - changeSize := 0 - outputCount := len(txOuts) - if addChangeOutput { - changeSize = P2PKHOutputSize - outputCount++ - } - - var redeemScriptSize int - switch inputType { - case P2PKH: - redeemScriptSize = RedeemP2PKHInputSize - case P2SH_1of2_Multisig: - redeemScriptSize = RedeemP2SH1of2MultisigInputSize - case P2SH_2of3_Multisig: - redeemScriptSize = RedeemP2SH2of3MultisigInputSize - case P2SH_Multisig_Timelock_1Sig: - redeemScriptSize = RedeemP2SHMultisigTimelock1InputSize - case P2SH_Multisig_Timelock_2Sigs: - redeemScriptSize = RedeemP2SHMultisigTimelock2InputSize - } - - // 10 additional bytes are for version, locktime, and segwit flags - return 10 + wire.VarIntSerializeSize(uint64(inputCount)) + - wire.VarIntSerializeSize(uint64(outputCount)) + - inputCount*redeemScriptSize + - SumOutputSerializeSizes(txOuts) + - changeSize -} - -// SumOutputSerializeSizes sums up the serialized size of the supplied outputs. -func SumOutputSerializeSizes(outputs []*wire.TxOut) (serializeSize int) { - for _, txOut := range outputs { - serializeSize += txOut.SerializeSize() - } - return serializeSize -} diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/txstore.go b/vendor/github.com/cpacia/BitcoinCash-Wallet/txstore.go deleted file mode 100644 index 46fe6bc79d..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/txstore.go +++ /dev/null @@ -1,498 +0,0 @@ -// Copyright (C) 2015-2016 The Lightning Network Developers -// Copyright (c) 2016-2017 The OpenBazaar Developers - -package bitcoincash - -import ( - "bytes" - "errors" - "github.com/OpenBazaar/wallet-interface" - "github.com/btcsuite/btcd/blockchain" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/bloom" - "sync" - "time" -) - -type TxStore struct { - adrs []btcutil.Address - watchedScripts [][]byte - txids map[string]int32 - addrMutex *sync.Mutex - txidsMutex *sync.RWMutex - cbMutex *sync.Mutex - - keyManager *KeyManager - - params *chaincfg.Params - - listeners []func(wallet.TransactionCallback) - - additionalFilters [][]byte - - wallet.Datastore -} - -func NewTxStore(p *chaincfg.Params, db wallet.Datastore, keyManager *KeyManager, additionalFilters ...[]byte) (*TxStore, error) { - txs := &TxStore{ - params: p, - keyManager: keyManager, - addrMutex: new(sync.Mutex), - cbMutex: new(sync.Mutex), - txidsMutex: new(sync.RWMutex), - txids: make(map[string]int32), - Datastore: db, - additionalFilters: additionalFilters, - } - err := txs.PopulateAdrs() - if err != nil { - return nil, err - } - return txs, nil -} - -// ... or I'm gonna fade away -func (ts *TxStore) GimmeFilter() (*bloom.Filter, error) { - ts.PopulateAdrs() - - // get all utxos to add outpoints to filter - allUtxos, err := ts.Utxos().GetAll() - if err != nil { - return nil, err - } - - allStxos, err := ts.Stxos().GetAll() - if err != nil { - return nil, err - } - ts.addrMutex.Lock() - elem := uint32(len(ts.adrs)+len(allUtxos)+len(allStxos)) + uint32(len(ts.watchedScripts)) - f := bloom.NewFilter(elem, 0, 0.00003, wire.BloomUpdateAll) - - // note there could be false positives since we're just looking - // for the 20 byte PKH without the opcodes. - for _, a := range ts.adrs { // add 20-byte pubkeyhash - f.Add(a.ScriptAddress()) - } - ts.addrMutex.Unlock() - for _, u := range allUtxos { - f.AddOutPoint(&u.Op) - } - - for _, s := range allStxos { - f.AddOutPoint(&s.Utxo.Op) - } - for _, w := range ts.watchedScripts { - _, addrs, _, err := txscript.ExtractPkScriptAddrs(w, ts.params) - if err != nil { - continue - } - f.Add(addrs[0].ScriptAddress()) - } - for _, toAdd := range ts.additionalFilters { - f.Add(toAdd) - } - return f, nil -} - -// GetDoubleSpends takes a transaction and compares it with -// all transactions in the db. It returns a slice of all txids in the db -// which are double spent by the received tx. -func (ts *TxStore) CheckDoubleSpends(argTx *wire.MsgTx) ([]*chainhash.Hash, error) { - var dubs []*chainhash.Hash // slice of all double-spent txs - argTxid := argTx.TxHash() - txs, err := ts.Txns().GetAll(true) - if err != nil { - return dubs, err - } - for _, compTx := range txs { - if compTx.Height < 0 { - continue - } - r := bytes.NewReader(compTx.Bytes) - msgTx := wire.NewMsgTx(1) - msgTx.BtcDecode(r, 1, wire.WitnessEncoding) - compTxid := msgTx.TxHash() - for _, argIn := range argTx.TxIn { - // iterate through inputs of compTx - for _, compIn := range msgTx.TxIn { - if outPointsEqual(argIn.PreviousOutPoint, compIn.PreviousOutPoint) && !compTxid.IsEqual(&argTxid) { - // found double spend - dubs = append(dubs, &compTxid) - break // back to argIn loop - } - } - } - } - return dubs, nil -} - -// GetPendingInv returns an inv message containing all txs known to the -// db which are at height 0 (not known to be confirmed). -// This can be useful on startup or to rebroadcast unconfirmed txs. -func (ts *TxStore) GetPendingInv() (*wire.MsgInv, error) { - // use a map (really a set) do avoid dupes - txidMap := make(map[chainhash.Hash]struct{}) - - utxos, err := ts.Utxos().GetAll() // get utxos from db - if err != nil { - return nil, err - } - stxos, err := ts.Stxos().GetAll() // get stxos from db - if err != nil { - return nil, err - } - - // iterate through utxos, adding txids of anything with height 0 - for _, utxo := range utxos { - if utxo.AtHeight == 0 { - txidMap[utxo.Op.Hash] = struct{}{} // adds to map - } - } - // do the same with stxos based on height at which spent - for _, stxo := range stxos { - if stxo.SpendHeight == 0 { - txidMap[stxo.SpendTxid] = struct{}{} - } - } - - invMsg := wire.NewMsgInv() - for txid := range txidMap { - item := wire.NewInvVect(wire.InvTypeTx, &txid) - err = invMsg.AddInvVect(item) - if err != nil { - return nil, err - } - } - - // return inv message with all txids (maybe none) - return invMsg, nil -} - -// PopulateAdrs just puts a bunch of adrs in ram; it doesn't touch the DB -func (ts *TxStore) PopulateAdrs() error { - keys := ts.keyManager.GetKeys() - ts.addrMutex.Lock() - ts.adrs = []btcutil.Address{} - for _, k := range keys { - addr, err := k.Address(ts.params) - if err != nil { - continue - } - ts.adrs = append(ts.adrs, addr) - } - ts.addrMutex.Unlock() - ts.watchedScripts, _ = ts.WatchedScripts().GetAll() - txns, _ := ts.Txns().GetAll(true) - ts.txidsMutex.Lock() - for _, t := range txns { - ts.txids[t.Txid] = t.Height - } - ts.txidsMutex.Unlock() - return nil -} - -// Ingest puts a tx into the DB atomically. This can result in a -// gain, a loss, or no result. Gain or loss in satoshis is returned. -func (ts *TxStore) Ingest(tx *wire.MsgTx, height int32, timestamp time.Time) (uint32, error) { - var hits uint32 - var err error - // Tx has been OK'd by SPV; check tx sanity - utilTx := btcutil.NewTx(tx) // convert for validation - // Checks basic stuff like there are inputs and ouputs - err = blockchain.CheckTransactionSanity(utilTx) - if err != nil { - return hits, err - } - - // Check to see if we've already processed this tx. If so, return. - - ts.txidsMutex.RLock() - sh, ok := ts.txids[tx.TxHash().String()] - ts.txidsMutex.RUnlock() - if ok && (sh > 0 || (sh == 0 && height == 0)) { - return 1, nil - } - - // Check to see if this is a double spend - doubleSpends, err := ts.CheckDoubleSpends(tx) - if err != nil { - return hits, err - } - if len(doubleSpends) > 0 { - // First seen rule - if height == 0 { - return 0, nil - } else { - // Mark any unconfirmed doubles as dead - for _, double := range doubleSpends { - ts.markAsDead(*double) - } - } - } - - // Generate PKscripts for all addresses - ts.addrMutex.Lock() - PKscripts := make([][]byte, len(ts.adrs)) - for i := range ts.adrs { - // Iterate through all our addresses - // TODO: This will need to test both segwit and legacy once segwit activates - PKscripts[i], err = txscript.PayToAddrScript(ts.adrs[i]) - if err != nil { - ts.addrMutex.Unlock() - return hits, err - } - } - ts.addrMutex.Unlock() - - // Iterate through all outputs of this tx, see if we gain - cachedSha := tx.TxHash() - cb := wallet.TransactionCallback{Txid: cachedSha.String(), Height: height} - value := int64(0) - matchesWatchOnly := false - for i, txout := range tx.TxOut { - // Ignore the error here because the sender could have used and exotic script - // for his change and we don't want to fail in that case. - addr, _ := scriptToAddress(txout.PkScript, ts.params) - out := wallet.TransactionOutput{Address: addr, Value: txout.Value, Index: uint32(i)} - for _, script := range PKscripts { - if bytes.Equal(txout.PkScript, script) { // new utxo found - scriptAddress, _ := ts.extractScriptAddress(txout.PkScript) - ts.keyManager.MarkKeyAsUsed(scriptAddress) - newop := wire.OutPoint{ - Hash: cachedSha, - Index: uint32(i), - } - newu := wallet.Utxo{ - AtHeight: height, - Value: txout.Value, - ScriptPubkey: txout.PkScript, - Op: newop, - WatchOnly: false, - } - value += newu.Value - ts.Utxos().Put(newu) - hits++ - break - } - } - // Now check watched scripts - for _, script := range ts.watchedScripts { - if bytes.Equal(txout.PkScript, script) { - newop := wire.OutPoint{ - Hash: cachedSha, - Index: uint32(i), - } - newu := wallet.Utxo{ - AtHeight: height, - Value: txout.Value, - ScriptPubkey: txout.PkScript, - Op: newop, - WatchOnly: true, - } - ts.Utxos().Put(newu) - matchesWatchOnly = true - } - } - for _, f := range ts.additionalFilters { - if bytes.Contains(txout.PkScript, f) { - matchesWatchOnly = true - break - } - } - cb.Outputs = append(cb.Outputs, out) - } - utxos, err := ts.Utxos().GetAll() - if err != nil { - return 0, err - } - for _, txin := range tx.TxIn { - for i, u := range utxos { - if outPointsEqual(txin.PreviousOutPoint, u.Op) { - st := wallet.Stxo{ - Utxo: u, - SpendHeight: height, - SpendTxid: cachedSha, - } - ts.Stxos().Put(st) - ts.Utxos().Delete(u) - utxos = append(utxos[:i], utxos[i+1:]...) - if !u.WatchOnly { - value -= u.Value - hits++ - } else { - matchesWatchOnly = true - } - - // Ignore the error here because the sender could have used and exotic script - // for his change and we don't want to fail in that case. - addr, _ := scriptToAddress(u.ScriptPubkey, ts.params) - - in := wallet.TransactionInput{ - OutpointHash: u.Op.Hash.CloneBytes(), - OutpointIndex: u.Op.Index, - LinkedAddress: addr, - Value: u.Value, - } - cb.Inputs = append(cb.Inputs, in) - break - } - } - } - - // Update height of any stxos - if height > 0 { - stxos, err := ts.Stxos().GetAll() - if err != nil { - return 0, err - } - for _, stxo := range stxos { - if stxo.SpendTxid.IsEqual(&cachedSha) { - stxo.SpendHeight = height - ts.Stxos().Put(stxo) - if !stxo.Utxo.WatchOnly { - hits++ - } else { - matchesWatchOnly = true - } - break - } - } - } - - // If hits is nonzero it's a relevant tx and we should store it - if hits > 0 || matchesWatchOnly { - ts.cbMutex.Lock() - ts.txidsMutex.Lock() - txn, err := ts.Txns().Get(tx.TxHash()) - shouldCallback := false - if err != nil { - cb.Value = value - txn.Timestamp = timestamp - shouldCallback = true - var buf bytes.Buffer - tx.BtcEncode(&buf, 1, wire.BaseEncoding) - ts.Txns().Put(buf.Bytes(), tx.TxHash().String(), int(value), int(height), txn.Timestamp, hits == 0) - ts.txids[tx.TxHash().String()] = height - } - // Let's check the height before committing so we don't allow rogue peers to send us a lose - // tx that resets our height to zero. - if txn.Height <= 0 { - ts.Txns().UpdateHeight(tx.TxHash(), int(height), timestamp) - ts.txids[tx.TxHash().String()] = height - if height > 0 { - cb.Value = txn.Value - shouldCallback = true - } - } - cb.BlockTime = timestamp - ts.txidsMutex.Unlock() - if shouldCallback { - // Callback on listeners - for _, listener := range ts.listeners { - listener(cb) - } - } - ts.cbMutex.Unlock() - ts.PopulateAdrs() - hits++ - } - return hits, err -} - -func (ts *TxStore) markAsDead(txid chainhash.Hash) error { - stxos, err := ts.Stxos().GetAll() - if err != nil { - return err - } - markStxoAsDead := func(s wallet.Stxo) error { - err := ts.Stxos().Delete(s) - if err != nil { - return err - } - err = ts.Txns().UpdateHeight(s.SpendTxid, -1, time.Now()) - if err != nil { - return err - } - return nil - } - for _, s := range stxos { - // If an stxo is marked dead, move it back into the utxo table - if txid.IsEqual(&s.SpendTxid) { - if err := markStxoAsDead(s); err != nil { - return err - } - if err := ts.Utxos().Put(s.Utxo); err != nil { - return err - } - } - // If a dependency of the spend is dead then mark the spend as dead - if txid.IsEqual(&s.Utxo.Op.Hash) { - if err := markStxoAsDead(s); err != nil { - return err - } - if err := ts.markAsDead(s.SpendTxid); err != nil { - return err - } - } - } - utxos, err := ts.Utxos().GetAll() - if err != nil { - return err - } - // Dead utxos should just be deleted - for _, u := range utxos { - if txid.IsEqual(&u.Op.Hash) { - err := ts.Utxos().Delete(u) - if err != nil { - return err - } - } - } - ts.Txns().UpdateHeight(txid, -1, time.Now()) - return nil -} - -func (ts *TxStore) processReorg(lastGoodHeight uint32) error { - txns, err := ts.Txns().GetAll(true) - if err != nil { - return err - } - for i := len(txns) - 1; i >= 0; i-- { - if txns[i].Height > int32(lastGoodHeight) { - txid, err := chainhash.NewHashFromStr(txns[i].Txid) - if err != nil { - log.Error(err) - continue - } - err = ts.markAsDead(*txid) - if err != nil { - log.Error(err) - continue - } - } - } - return nil -} - -func (ts *TxStore) extractScriptAddress(script []byte) ([]byte, error) { - _, addrs, _, err := txscript.ExtractPkScriptAddrs(script, ts.params) - if err != nil { - return nil, err - } - if len(addrs) == 0 { - return nil, errors.New("unknown script") - } - return addrs[0].ScriptAddress(), nil -} - -func outPointsEqual(a, b wire.OutPoint) bool { - if !a.Hash.IsEqual(&b.Hash) { - return false - } - return a.Index == b.Index -} diff --git a/vendor/github.com/cpacia/BitcoinCash-Wallet/wallet.go b/vendor/github.com/cpacia/BitcoinCash-Wallet/wallet.go deleted file mode 100644 index c3cc353ef9..0000000000 --- a/vendor/github.com/cpacia/BitcoinCash-Wallet/wallet.go +++ /dev/null @@ -1,539 +0,0 @@ -package bitcoincash - -import ( - "bytes" - "errors" - "io" - "sync" - "time" - - "github.com/OpenBazaar/wallet-interface" - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/peer" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" - btc "github.com/btcsuite/btcutil" - hd "github.com/btcsuite/btcutil/hdkeychain" - "github.com/btcsuite/btcwallet/wallet/txrules" - "github.com/cpacia/BitcoinCash-Wallet/exchangerates" - "github.com/cpacia/bchutil" - "github.com/op/go-logging" - b39 "github.com/tyler-smith/go-bip39" -) - -func setupNetworkParams(params *chaincfg.Params) { - switch params.Name { - case chaincfg.MainNetParams.Name: - params.Net = bchutil.MainnetMagic - case chaincfg.TestNet3Params.Name: - params.Net = bchutil.TestnetMagic - case chaincfg.RegressionNetParams.Name: - params.Net = bchutil.Regtestmagic - } -} - -type SPVWallet struct { - params *chaincfg.Params - - masterPrivateKey *hd.ExtendedKey - masterPublicKey *hd.ExtendedKey - - mnemonic string - - feeProvider *FeeProvider - - repoPath string - - blockchain *Blockchain - txstore *TxStore - peerManager *PeerManager - keyManager *KeyManager - wireService *WireService - - fPositives chan *peer.Peer - stopChan chan int - fpAccumulator map[int32]int32 - mutex *sync.RWMutex - - creationDate time.Time - - running bool - - config *PeerManagerConfig - - exchangeRates wallet.ExchangeRates -} - -var log = logging.MustGetLogger("bitcoin") - -const WALLET_VERSION = "0.1.0" - -func NewSPVWallet(config *Config) (*SPVWallet, error) { - setupNetworkParams(config.Params) - - log.SetBackend(logging.AddModuleLevel(config.Logger)) - - if config.Mnemonic == "" { - ent, err := b39.NewEntropy(128) - if err != nil { - return nil, err - } - mnemonic, err := b39.NewMnemonic(ent) - if err != nil { - return nil, err - } - config.Mnemonic = mnemonic - config.CreationDate = time.Now() - } - seed := b39.NewSeed(config.Mnemonic, "") - - mPrivKey, err := hd.NewMaster(seed, config.Params) - if err != nil { - return nil, err - } - mPubKey, err := mPrivKey.Neuter() - if err != nil { - return nil, err - } - w := &SPVWallet{ - repoPath: config.RepoPath, - masterPrivateKey: mPrivKey, - masterPublicKey: mPubKey, - mnemonic: config.Mnemonic, - params: config.Params, - creationDate: config.CreationDate, - feeProvider: NewFeeProvider( - config.MaxFee, - config.HighFee, - config.MediumFee, - config.LowFee, - nil, - ), - fPositives: make(chan *peer.Peer), - stopChan: make(chan int), - fpAccumulator: make(map[int32]int32), - mutex: new(sync.RWMutex), - } - - er := exchangerates.NewBitcoinCashPriceFetcher(config.Proxy) - w.exchangeRates = er - if !config.DisableExchangeRates { - go er.Run() - w.feeProvider.exchangeRates = er - } - - w.keyManager, err = NewKeyManager(config.DB.Keys(), w.params, w.masterPrivateKey) - - w.txstore, err = NewTxStore(w.params, config.DB, w.keyManager, config.AdditionalFilters...) - if err != nil { - return nil, err - } - - w.blockchain, err = NewBlockchain(w.repoPath, w.creationDate, w.params) - if err != nil { - return nil, err - } - - minSync := 5 - if config.TrustedPeer != nil { - minSync = 1 - } - wireConfig := &WireServiceConfig{ - txStore: w.txstore, - chain: w.blockchain, - walletCreationDate: w.creationDate, - minPeersForSync: minSync, - params: w.params, - } - - ws := NewWireService(wireConfig) - w.wireService = ws - - getNewestBlock := func() (*chainhash.Hash, int32, error) { - sh, err := w.blockchain.BestBlock() - if err != nil { - return nil, 0, err - } - h := sh.header.BlockHash() - return &h, int32(sh.height), nil - } - - w.config = &PeerManagerConfig{ - UserAgentName: config.UserAgent, - UserAgentVersion: WALLET_VERSION, - Params: w.params, - AddressCacheDir: config.RepoPath, - Proxy: config.Proxy, - GetNewestBlock: getNewestBlock, - MsgChan: ws.MsgChan(), - } - - if config.TrustedPeer != nil { - w.config.TrustedPeer = config.TrustedPeer - } - - w.peerManager, err = NewPeerManager(w.config) - if err != nil { - return nil, err - } - - return w, nil -} - -func (w *SPVWallet) Start() { - w.running = true - go w.wireService.Start() - go w.peerManager.Start() -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// API -// -////////////// - -func (w *SPVWallet) CurrencyCode() string { - if w.params.Name == chaincfg.MainNetParams.Name { - return "bch" - } else { - return "tbch" - } -} - -func (w *SPVWallet) CreationDate() time.Time { - return w.creationDate -} - -func (w *SPVWallet) IsDust(amount int64) bool { - return txrules.IsDustAmount(btc.Amount(amount), 25, txrules.DefaultRelayFeePerKb) -} - -func (w *SPVWallet) MasterPrivateKey() *hd.ExtendedKey { - return w.masterPrivateKey -} - -func (w *SPVWallet) MasterPublicKey() *hd.ExtendedKey { - return w.masterPublicKey -} - -func (w *SPVWallet) ChildKey(keyBytes []byte, chaincode []byte, isPrivateKey bool) (*hd.ExtendedKey, error) { - parentFP := []byte{0x00, 0x00, 0x00, 0x00} - var id []byte - if isPrivateKey { - id = w.params.HDPrivateKeyID[:] - } else { - id = w.params.HDPublicKeyID[:] - } - hdKey := hd.NewExtendedKey( - id, - keyBytes, - chaincode, - parentFP, - 0, - 0, - isPrivateKey) - return hdKey.Child(0) -} - -func (w *SPVWallet) Mnemonic() string { - return w.mnemonic -} - -func (w *SPVWallet) ConnectedPeers() []*peer.Peer { - return w.peerManager.ConnectedPeers() -} - -func (w *SPVWallet) CurrentAddress(purpose wallet.KeyPurpose) btc.Address { - key, _ := w.keyManager.GetCurrentKey(purpose) - addr, _ := key.Address(w.params) - cashaddr, _ := bchutil.NewCashAddressPubKeyHash(addr.ScriptAddress(), w.params) - return btc.Address(cashaddr) -} - -func (w *SPVWallet) NewAddress(purpose wallet.KeyPurpose) btc.Address { - i, _ := w.txstore.Keys().GetUnused(purpose) - key, _ := w.keyManager.generateChildKey(purpose, uint32(i[1])) - addr, _ := key.Address(w.params) - w.txstore.Keys().MarkKeyAsUsed(addr.ScriptAddress()) - w.txstore.PopulateAdrs() - cashaddr, _ := bchutil.NewCashAddressPubKeyHash(addr.ScriptAddress(), w.params) - return btc.Address(cashaddr) -} - -func (w *SPVWallet) DecodeAddress(addr string) (btc.Address, error) { - // Legacy - decoded, err := btc.DecodeAddress(addr, w.params) - if err == nil { - return decoded, nil - } - // Cashaddr - decoded, err = bchutil.DecodeAddress(addr, w.params) - if err == nil { - return decoded, nil - } - // Bitpay - decoded, err = bchutil.DecodeBitpay(addr, w.params) - if err == nil { - return decoded, nil - } - return nil, errors.New("Unrecognized address format") -} - -func (w *SPVWallet) ScriptToAddress(script []byte) (btc.Address, error) { - return scriptToAddress(script, w.params) -} - -func scriptToAddress(script []byte, params *chaincfg.Params) (btc.Address, error) { - addr, err := bchutil.ExtractPkScriptAddrs(script, params) - if err != nil { - return nil, err - } - return btc.Address(addr), nil -} - -func (w *SPVWallet) AddressToScript(addr btc.Address) ([]byte, error) { - return bchutil.PayToAddrScript(addr) -} - -func (w *SPVWallet) HasKey(addr btc.Address) bool { - _, err := w.keyManager.GetKeyForScript(addr.ScriptAddress()) - if err != nil { - return false - } - return true -} - -func (w *SPVWallet) GetKey(addr btc.Address) (*btcec.PrivateKey, error) { - key, err := w.keyManager.GetKeyForScript(addr.ScriptAddress()) - if err != nil { - return nil, err - } - return key.ECPrivKey() -} - -func (w *SPVWallet) ListAddresses() []btc.Address { - keys := w.keyManager.GetKeys() - addrs := []btc.Address{} - for _, k := range keys { - addr, err := k.Address(w.params) - if err != nil { - continue - } - cashaddr, err := bchutil.NewCashAddressPubKeyHash(addr.ScriptAddress(), w.params) - if err != nil { - continue - } - addrs = append(addrs, cashaddr) - } - return addrs -} - -func (w *SPVWallet) ListKeys() []btcec.PrivateKey { - keys := w.keyManager.GetKeys() - list := []btcec.PrivateKey{} - for _, k := range keys { - priv, err := k.ECPrivKey() - if err != nil { - continue - } - list = append(list, *priv) - } - return list -} - -func (w *SPVWallet) ImportKey(privKey *btcec.PrivateKey, compress bool) error { - pub := privKey.PubKey() - var pubKeyBytes []byte - if compress { - pubKeyBytes = pub.SerializeCompressed() - } else { - pubKeyBytes = pub.SerializeUncompressed() - } - pkHash := btc.Hash160(pubKeyBytes) - addr, err := btc.NewAddressPubKeyHash(pkHash, w.params) - if err != nil { - return err - } - return w.keyManager.datastore.ImportKey(addr.ScriptAddress(), privKey) -} - -func (w *SPVWallet) Balance() (confirmed, unconfirmed int64) { - utxos, _ := w.txstore.Utxos().GetAll() - stxos, _ := w.txstore.Stxos().GetAll() - for _, utxo := range utxos { - if !utxo.WatchOnly { - if utxo.AtHeight > 0 { - confirmed += utxo.Value - } else { - if w.checkIfStxoIsConfirmed(utxo, stxos) { - confirmed += utxo.Value - } else { - unconfirmed += utxo.Value - } - } - } - } - return confirmed, unconfirmed -} - -func (w *SPVWallet) Transactions() ([]wallet.Txn, error) { - height, _ := w.ChainTip() - txns, err := w.txstore.Txns().GetAll(false) - if err != nil { - return txns, err - } - for i, tx := range txns { - var confirmations int32 - var status wallet.StatusCode - confs := int32(height) - tx.Height + 1 - if tx.Height <= 0 { - confs = tx.Height - } - switch { - case confs < 0: - status = wallet.StatusDead - case confs == 0 && time.Since(tx.Timestamp) <= time.Hour*6: - status = wallet.StatusUnconfirmed - case confs == 0 && time.Since(tx.Timestamp) > time.Hour*6: - status = wallet.StatusStuck - case confs > 0 && confs < 6: - status = wallet.StatusPending - confirmations = confs - case confs > 5: - status = wallet.StatusConfirmed - confirmations = confs - } - tx.Confirmations = int64(confirmations) - tx.Status = status - txns[i] = tx - } - return txns, nil -} - -func (w *SPVWallet) GetTransaction(txid chainhash.Hash) (wallet.Txn, error) { - txn, err := w.txstore.Txns().Get(txid) - if err == nil { - tx := wire.NewMsgTx(1) - rbuf := bytes.NewReader(txn.Bytes) - err := tx.BtcDecode(rbuf, wire.ProtocolVersion, wire.WitnessEncoding) - if err != nil { - return txn, err - } - outs := []wallet.TransactionOutput{} - for i, out := range tx.TxOut { - var addr btc.Address - _, addrs, _, err := txscript.ExtractPkScriptAddrs(out.PkScript, w.params) - if err != nil { - log.Warningf("error extracting address from txn pkscript: %v\n", err) - } - if len(addrs) == 0 { - addr = nil - } else { - addr = addrs[0] - } - tout := wallet.TransactionOutput{ - Address: addr, - Value: out.Value, - Index: uint32(i), - } - outs = append(outs, tout) - } - txn.Outputs = outs - } - return txn, err -} - -func (w *SPVWallet) GetConfirmations(txid chainhash.Hash) (uint32, uint32, error) { - txn, err := w.txstore.Txns().Get(txid) - if err != nil { - return 0, 0, err - } - if txn.Height == 0 { - return 0, 0, nil - } - chainTip, _ := w.ChainTip() - return chainTip - uint32(txn.Height) + 1, uint32(txn.Height), nil -} - -func (w *SPVWallet) checkIfStxoIsConfirmed(utxo wallet.Utxo, stxos []wallet.Stxo) bool { - for _, stxo := range stxos { - if !stxo.Utxo.WatchOnly { - if stxo.SpendTxid.IsEqual(&utxo.Op.Hash) { - if stxo.SpendHeight > 0 { - return true - } else { - return w.checkIfStxoIsConfirmed(stxo.Utxo, stxos) - } - } else if stxo.Utxo.IsEqual(&utxo) { - if stxo.Utxo.AtHeight > 0 { - return true - } else { - return false - } - } - } - } - return false -} - -func (w *SPVWallet) Params() *chaincfg.Params { - return w.params -} - -func (w *SPVWallet) AddTransactionListener(callback func(wallet.TransactionCallback)) { - w.txstore.listeners = append(w.txstore.listeners, callback) -} - -func (w *SPVWallet) ChainTip() (uint32, chainhash.Hash) { - var ch chainhash.Hash - sh, err := w.blockchain.db.GetBestHeader() - if err != nil { - return 0, ch - } - return sh.height, sh.header.BlockHash() -} - -func (w *SPVWallet) AddWatchedAddress(addr btc.Address) error { - script, err := w.AddressToScript(addr) - if err != nil { - return err - } - err = w.txstore.WatchedScripts().Put(script) - w.txstore.PopulateAdrs() - - w.wireService.MsgChan() <- updateFiltersMsg{} - return err -} - -func (w *SPVWallet) DumpHeaders(writer io.Writer) { - w.blockchain.db.Print(writer) -} - -func (w *SPVWallet) ExchangeRates() wallet.ExchangeRates { - return w.exchangeRates -} - -func (w *SPVWallet) Close() { - if w.running { - log.Info("Disconnecting from peers and shutting down") - w.peerManager.Stop() - w.blockchain.Close() - w.wireService.Stop() - w.running = false - } -} - -func (w *SPVWallet) ReSyncBlockchain(fromDate time.Time) { - w.blockchain.Rollback(fromDate) - w.txstore.PopulateAdrs() - w.wireService.Resync() -} - -// AssociateTransactionWithOrder used for ORDER_PAYMENT message -func (w *SPVWallet) AssociateTransactionWithOrder(cb wallet.TransactionCallback) { - for _, l := range w.txstore.listeners { - go l(cb) - } -} diff --git a/wallet/builder.go b/wallet/builder.go index 91568d6a85..5f9b957bae 100644 --- a/wallet/builder.go +++ b/wallet/builder.go @@ -24,7 +24,6 @@ import ( "github.com/OpenBazaar/spvwallet" "github.com/OpenBazaar/wallet-interface" "github.com/btcsuite/btcd/chaincfg" - bchspv "github.com/cpacia/BitcoinCash-Wallet" "github.com/op/go-logging" "golang.org/x/net/proxy" ) @@ -248,49 +247,6 @@ func createSPVWallet(coin wallet.CoinType, coinConfigOverrides *schema.CoinConfi actualCoin = wallet.Bitcoin } return actualCoin, newSPVWallet, nil - case wallet.BitcoinCash: - defaultConfig := defaultConfigSet.BCH - preparedConfig := &bchspv.Config{ - Mnemonic: cfg.Mnemonic, - Params: cfg.Params, - MaxFee: coinConfigOverrides.MaxFee, - LowFee: coinConfigOverrides.LowFeeDefault, - MediumFee: coinConfigOverrides.MediumFeeDefault, - HighFee: coinConfigOverrides.HighFeeDefault, - FeeAPI: *feeAPI, - RepoPath: walletRepoPath, - CreationDate: cfg.WalletCreationDate, - DB: CreateWalletDB(cfg.DB, coin), - UserAgent: "OpenBazaar", - TrustedPeer: trustedPeer, - Proxy: cfg.Proxy, - Logger: cfg.Logger, - DisableExchangeRates: cfg.DisableExchangeRates, - } - if preparedConfig.HighFee == 0 { - preparedConfig.HighFee = defaultConfig.HighFeeDefault - } - if preparedConfig.MediumFee == 0 { - preparedConfig.MediumFee = defaultConfig.MediumFeeDefault - } - if preparedConfig.LowFee == 0 { - preparedConfig.LowFee = defaultConfig.LowFeeDefault - } - if preparedConfig.MaxFee == 0 { - preparedConfig.MaxFee = defaultConfig.MaxFee - } - - newSPVWallet, err := bchspv.NewSPVWallet(preparedConfig) - if err != nil { - return InvalidCoinType, nil, err - } - - if notMainnet { - actualCoin = wallet.TestnetBitcoinCash - } else { - actualCoin = wallet.BitcoinCash - } - return actualCoin, newSPVWallet, nil } return InvalidCoinType, nil, fmt.Errorf("unable to create wallet for unknown coin %s", coin.String()) }