2016-08-13 01:29:38 +03:00
|
|
|
package btcwallet
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/hex"
|
2016-12-27 08:30:24 +03:00
|
|
|
"errors"
|
2017-05-25 03:34:31 +03:00
|
|
|
"fmt"
|
2016-08-13 01:29:38 +03:00
|
|
|
|
2018-06-05 04:34:16 +03:00
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
|
|
"github.com/btcsuite/btcutil"
|
|
|
|
"github.com/btcsuite/btcwallet/chain"
|
2018-07-18 05:21:09 +03:00
|
|
|
"github.com/lightninglabs/neutrino"
|
2019-09-09 13:22:18 +03:00
|
|
|
"github.com/lightninglabs/neutrino/headerfs"
|
2021-04-06 11:42:09 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
2018-07-18 05:21:09 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lnwallet"
|
2016-08-13 01:29:38 +03:00
|
|
|
)
|
|
|
|
|
2016-12-27 08:30:24 +03:00
|
|
|
var (
|
|
|
|
// ErrOutputSpent is returned by the GetUtxo method if the target output
|
|
|
|
// for lookup has already been spent.
|
|
|
|
ErrOutputSpent = errors.New("target output has been spent")
|
2018-01-10 05:14:25 +03:00
|
|
|
|
|
|
|
// ErrOutputNotFound signals that the desired output could not be
|
|
|
|
// located.
|
|
|
|
ErrOutputNotFound = errors.New("target output was not found")
|
2016-12-27 08:30:24 +03:00
|
|
|
)
|
|
|
|
|
2016-12-22 23:34:51 +03:00
|
|
|
// GetBestBlock returns the current height and hash of the best known block
|
|
|
|
// within the main chain.
|
2016-08-13 01:29:38 +03:00
|
|
|
//
|
|
|
|
// This method is a part of the lnwallet.BlockChainIO interface.
|
2017-01-06 00:56:27 +03:00
|
|
|
func (b *BtcWallet) GetBestBlock() (*chainhash.Hash, int32, error) {
|
2017-11-10 03:30:20 +03:00
|
|
|
return b.chain.GetBestBlock()
|
2016-08-13 01:29:38 +03:00
|
|
|
}
|
|
|
|
|
2018-07-18 05:21:09 +03:00
|
|
|
// GetUtxo returns the original output referenced by the passed outpoint that
|
|
|
|
// creates the target pkScript.
|
2016-08-13 01:29:38 +03:00
|
|
|
//
|
|
|
|
// This method is a part of the lnwallet.BlockChainIO interface.
|
2018-07-18 05:21:09 +03:00
|
|
|
func (b *BtcWallet) GetUtxo(op *wire.OutPoint, pkScript []byte,
|
2018-08-24 16:31:57 +03:00
|
|
|
heightHint uint32, cancel <-chan struct{}) (*wire.TxOut, error) {
|
2018-07-18 05:21:09 +03:00
|
|
|
|
2017-05-25 03:34:31 +03:00
|
|
|
switch backend := b.chain.(type) {
|
2016-08-13 01:29:38 +03:00
|
|
|
|
2017-06-05 07:06:52 +03:00
|
|
|
case *chain.NeutrinoClient:
|
2017-05-25 03:34:31 +03:00
|
|
|
spendReport, err := backend.CS.GetUtxo(
|
2018-07-18 05:21:09 +03:00
|
|
|
neutrino.WatchInputs(neutrino.InputWithScript{
|
|
|
|
OutPoint: *op,
|
|
|
|
PkScript: pkScript,
|
|
|
|
}),
|
2019-09-09 13:22:18 +03:00
|
|
|
neutrino.StartBlock(&headerfs.BlockStamp{
|
2017-05-25 03:34:31 +03:00
|
|
|
Height: int32(heightHint),
|
|
|
|
}),
|
2018-08-24 16:35:34 +03:00
|
|
|
neutrino.QuitChan(cancel),
|
2017-05-25 03:34:31 +03:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-08-13 01:29:38 +03:00
|
|
|
|
2018-01-10 05:14:25 +03:00
|
|
|
// If the spend report is nil, then the output was not found in
|
|
|
|
// the rescan.
|
|
|
|
if spendReport == nil {
|
|
|
|
return nil, ErrOutputNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the spending transaction is populated in the spend report,
|
|
|
|
// this signals that the output has already been spent.
|
|
|
|
if spendReport.SpendingTx != nil {
|
2017-05-25 03:34:31 +03:00
|
|
|
return nil, ErrOutputSpent
|
|
|
|
}
|
2016-08-13 01:29:38 +03:00
|
|
|
|
2018-01-10 05:14:25 +03:00
|
|
|
// Otherwise, the output is assumed to be in the UTXO.
|
2017-05-25 03:34:31 +03:00
|
|
|
return spendReport.Output, nil
|
|
|
|
|
|
|
|
case *chain.RPCClient:
|
|
|
|
txout, err := backend.GetTxOut(&op.Hash, op.Index, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
} else if txout == nil {
|
|
|
|
return nil, ErrOutputSpent
|
|
|
|
}
|
2016-08-13 01:29:38 +03:00
|
|
|
|
2017-05-25 03:34:31 +03:00
|
|
|
pkScript, err := hex.DecodeString(txout.ScriptPubKey.Hex)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-03-26 05:15:05 +03:00
|
|
|
// We'll ensure we properly convert the amount given in BTC to
|
|
|
|
// satoshis.
|
|
|
|
amt, err := btcutil.NewAmount(txout.Value)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-05-25 03:34:31 +03:00
|
|
|
return &wire.TxOut{
|
2018-03-26 05:15:05 +03:00
|
|
|
Value: int64(amt),
|
2017-05-25 03:34:31 +03:00
|
|
|
PkScript: pkScript,
|
|
|
|
}, nil
|
|
|
|
|
2017-11-10 03:30:20 +03:00
|
|
|
case *chain.BitcoindClient:
|
|
|
|
txout, err := backend.GetTxOut(&op.Hash, op.Index, false)
|
2017-05-25 03:34:31 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2017-11-10 03:30:20 +03:00
|
|
|
} else if txout == nil {
|
|
|
|
return nil, ErrOutputSpent
|
2017-05-25 03:34:31 +03:00
|
|
|
}
|
|
|
|
|
2017-11-10 03:30:20 +03:00
|
|
|
pkScript, err := hex.DecodeString(txout.ScriptPubKey.Hex)
|
2017-05-25 03:34:31 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-03-26 05:15:05 +03:00
|
|
|
// Sadly, gettxout returns the output value in BTC instead of
|
|
|
|
// satoshis.
|
|
|
|
amt, err := btcutil.NewAmount(txout.Value)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-11-10 03:30:20 +03:00
|
|
|
return &wire.TxOut{
|
2018-03-26 05:15:05 +03:00
|
|
|
Value: int64(amt),
|
2017-11-10 03:30:20 +03:00
|
|
|
PkScript: pkScript,
|
|
|
|
}, nil
|
2016-12-07 18:49:58 +03:00
|
|
|
|
2017-05-25 03:34:31 +03:00
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("unknown backend")
|
|
|
|
}
|
2016-12-07 18:49:58 +03:00
|
|
|
}
|
|
|
|
|
2021-04-06 11:42:09 +03:00
|
|
|
// GetBlock returns a raw block from the server given its hash. For the Neutrino
|
|
|
|
// implementation of the lnwallet.BlockChainIO interface, the Neutrino GetBlock
|
|
|
|
// method is called directly. For other implementations, the block cache is used
|
|
|
|
// to wrap the call to GetBlock.
|
2017-11-10 03:30:20 +03:00
|
|
|
//
|
|
|
|
// This method is a part of the lnwallet.BlockChainIO interface.
|
|
|
|
func (b *BtcWallet) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
|
2021-04-06 11:42:09 +03:00
|
|
|
_, ok := b.chain.(*chain.NeutrinoClient)
|
|
|
|
if !ok {
|
|
|
|
return b.blockCache.GetBlock(blockHash, b.chain.GetBlock)
|
|
|
|
}
|
|
|
|
|
|
|
|
// For the neutrino implementation of lnwallet.BlockChainIO the neutrino
|
|
|
|
// GetBlock function can be called directly since it uses the same block
|
|
|
|
// cache. However, it does not lock the block cache mutex for the given
|
|
|
|
// block hash and so that is done here.
|
|
|
|
b.blockCache.HashMutex.Lock(lntypes.Hash(*blockHash))
|
|
|
|
defer b.blockCache.HashMutex.Unlock(lntypes.Hash(*blockHash))
|
|
|
|
|
|
|
|
return b.chain.GetBlock(blockHash)
|
2017-11-10 03:30:20 +03:00
|
|
|
}
|
|
|
|
|
2017-01-13 08:01:50 +03:00
|
|
|
// GetBlockHash returns the hash of the block in the best blockchain at the
|
2016-12-07 18:49:58 +03:00
|
|
|
// given height.
|
|
|
|
//
|
|
|
|
// This method is a part of the lnwallet.BlockChainIO interface.
|
2017-01-06 00:56:27 +03:00
|
|
|
func (b *BtcWallet) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) {
|
2017-11-10 03:30:20 +03:00
|
|
|
return b.chain.GetBlockHash(blockHeight)
|
2016-12-07 18:49:58 +03:00
|
|
|
}
|
2016-12-22 23:53:20 +03:00
|
|
|
|
|
|
|
// A compile time check to ensure that BtcWallet implements the BlockChainIO
|
|
|
|
// interface.
|
|
|
|
var _ lnwallet.WalletController = (*BtcWallet)(nil)
|