lnd.xprv/lnwallet/btcwallet/blockchain.go
2017-06-05 19:41:12 -07:00

155 lines
3.6 KiB
Go

package btcwallet
import (
"encoding/hex"
"errors"
"fmt"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/wire"
"github.com/lightninglabs/neutrino"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/roasbeef/btcwallet/chain"
"github.com/roasbeef/btcwallet/waddrmgr"
)
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")
)
// GetBestBlock returns the current height and hash of the best known block
// within the main chain.
//
// This method is a part of the lnwallet.BlockChainIO interface.
func (b *BtcWallet) GetBestBlock() (*chainhash.Hash, int32, error) {
switch backend := b.chain.(type) {
case *chain.NeutrinoClient:
header, height, err := backend.CS.BlockHeaders.ChainTip()
if err != nil {
return nil, -1, err
}
blockHash := header.BlockHash()
return &blockHash, int32(height), nil
case *chain.RPCClient:
return backend.GetBestBlock()
default:
return nil, -1, fmt.Errorf("unknown backend")
}
}
// GetUtxo returns the original output referenced by the passed outpoint.
//
// This method is a part of the lnwallet.BlockChainIO interface.
func (b *BtcWallet) GetUtxo(op *wire.OutPoint, heightHint uint32) (*wire.TxOut, error) {
switch backend := b.chain.(type) {
case *chain.NeutrinoClient:
spendReport, err := backend.CS.GetUtxo(
neutrino.WatchOutPoints(*op),
neutrino.StartBlock(&waddrmgr.BlockStamp{
Height: int32(heightHint),
}),
)
if err != nil {
return nil, err
}
if spendReport != nil && spendReport.SpendingTx != nil {
return nil, ErrOutputSpent
}
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
}
pkScript, err := hex.DecodeString(txout.ScriptPubKey.Hex)
if err != nil {
return nil, err
}
return &wire.TxOut{
// Sadly, gettxout returns the output value in BTC
// instead of satoshis.
Value: int64(txout.Value * 1e8),
PkScript: pkScript,
}, nil
default:
return nil, fmt.Errorf("unknown backend")
}
}
// GetBlock returns a raw block from the server given its hash.
//
// This method is a part of the lnwallet.BlockChainIO interface.
func (b *BtcWallet) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
switch backend := b.chain.(type) {
case *chain.NeutrinoClient:
block, err := backend.CS.GetBlockFromNetwork(*blockHash)
if err != nil {
return nil, err
}
return block.MsgBlock(), nil
case *chain.RPCClient:
block, err := backend.GetBlock(blockHash)
if err != nil {
return nil, err
}
return block, nil
default:
return nil, fmt.Errorf("unknown backend")
}
}
// GetBlockHash returns the hash of the block in the best blockchain at the
// given height.
//
// This method is a part of the lnwallet.BlockChainIO interface.
func (b *BtcWallet) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) {
switch backend := b.chain.(type) {
case *chain.NeutrinoClient:
height := uint32(blockHeight)
blockHeader, err := backend.CS.BlockHeaders.FetchHeaderByHeight(height)
if err != nil {
return nil, err
}
blockHash := blockHeader.BlockHash()
return &blockHash, nil
case *chain.RPCClient:
blockHash, err := backend.GetBlockHash(blockHeight)
if err != nil {
return nil, err
}
return blockHash, nil
default:
return nil, fmt.Errorf("unknown backend")
}
}
// A compile time check to ensure that BtcWallet implements the BlockChainIO
// interface.
var _ lnwallet.WalletController = (*BtcWallet)(nil)