lnwallet/btcwallet: update BlockChainIO implementation to be backend aware

This commit is contained in:
Olaoluwa Osuntokun 2017-05-24 17:34:31 -07:00
parent aca729abfe
commit 0e8af209bd
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
2 changed files with 149 additions and 53 deletions

@ -3,10 +3,17 @@ package btcwallet
import ( import (
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/wire"
"github.com/lightninglabs/neutrino"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcd/wire"
"github.com/roasbeef/btcwallet/chain"
"github.com/roasbeef/btcwallet/waddrmgr"
) )
var ( var (
@ -20,56 +27,98 @@ var (
// //
// This method is a part of the lnwallet.BlockChainIO interface. // This method is a part of the lnwallet.BlockChainIO interface.
func (b *BtcWallet) GetBestBlock() (*chainhash.Hash, int32, error) { func (b *BtcWallet) GetBestBlock() (*chainhash.Hash, int32, error) {
return b.rpc.GetBestBlock() switch backend := b.chain.(type) {
case *chain.SPVChain:
header, height, err := backend.CS.LatestBlock()
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. // GetUtxo returns the original output referenced by the passed outpoint.
// //
// This method is a part of the lnwallet.BlockChainIO interface. // This method is a part of the lnwallet.BlockChainIO interface.
func (b *BtcWallet) GetUtxo(op *wire.OutPoint, _ uint32) (*wire.TxOut, error) { func (b *BtcWallet) GetUtxo(op *wire.OutPoint, heightHint uint32) (*wire.TxOut, error) {
txout, err := b.rpc.GetTxOut(&op.Hash, op.Index, false) switch backend := b.chain.(type) {
if err != nil {
return nil, err case *chain.SPVChain:
} else if txout == nil { spendReport, err := backend.CS.GetUtxo(
return nil, ErrOutputSpent 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")
} }
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
}
// GetTransaction returns the full transaction identified by the passed
// transaction ID.
//
// This method is a part of the lnwallet.BlockChainIO interface.
func (b *BtcWallet) GetTransaction(txid *chainhash.Hash) (*wire.MsgTx, error) {
tx, err := b.rpc.GetRawTransaction(txid)
if err != nil {
return nil, err
}
return tx.MsgTx(), nil
} }
// GetBlock returns a raw block from the server given its hash. // GetBlock returns a raw block from the server given its hash.
// //
// This method is a part of the lnwallet.BlockChainIO interface. // This method is a part of the lnwallet.BlockChainIO interface.
func (b *BtcWallet) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) { func (b *BtcWallet) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
block, err := b.rpc.GetBlock(blockHash) switch backend := b.chain.(type) {
if err != nil {
return nil, err
}
return block, nil case *chain.SPVChain:
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 // GetBlockHash returns the hash of the block in the best blockchain at the
@ -77,12 +126,28 @@ func (b *BtcWallet) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error)
// //
// This method is a part of the lnwallet.BlockChainIO interface. // This method is a part of the lnwallet.BlockChainIO interface.
func (b *BtcWallet) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) { func (b *BtcWallet) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) {
blockHash, err := b.rpc.GetBlockHash(blockHeight) switch backend := b.chain.(type) {
if err != nil {
return nil, err
}
return blockHash, nil case *chain.SPVChain:
blockHeader, err := backend.CS.GetBlockByHeight(uint32(blockHeight))
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 // A compile time check to ensure that BtcWallet implements the BlockChainIO

@ -614,11 +614,30 @@ func (b *BtcWallet) IsSynced() (bool, error) {
// Grab the best chain state the wallet is currently aware of. // Grab the best chain state the wallet is currently aware of.
syncState := b.wallet.Manager.SyncedTo() syncState := b.wallet.Manager.SyncedTo()
// Next, query the btcd node to grab the info about the tip of the main var (
// chain. bestHash *chainhash.Hash
bestHash, bestHeight, err := b.rpc.GetBestBlock() bestHeight int32
if err != nil { err error
return false, err )
// Next, query the chain backend to grab the info about the tip of the
// main chain.
switch backend := b.cfg.ChainSource.(type) {
case *chain.SPVChain:
header, height, err := backend.CS.LatestBlock()
if err != nil {
return false, err
}
bh := header.BlockHash()
bestHash = &bh
bestHeight = int32(height)
case *chain.RPCClient:
bestHash, bestHeight, err = backend.GetBestBlock()
if err != nil {
return false, err
}
} }
// If the wallet hasn't yet fully synced to the node's best chain tip, // If the wallet hasn't yet fully synced to the node's best chain tip,
@ -628,12 +647,24 @@ func (b *BtcWallet) IsSynced() (bool, error) {
} }
// If the wallet is on par with the current best chain tip, then we // If the wallet is on par with the current best chain tip, then we
// still may not yet be synced as the btcd node may still be catching // still may not yet be synced as the chain backend may still be
// up to the main chain. So we'll grab the block header in order to // catching up to the main chain. So we'll grab the block header in
// make a guess based on the current time stamp. // order to make a guess based on the current time stamp.
blockHeader, err := b.rpc.GetBlockHeader(bestHash) var blockHeader *wire.BlockHeader
if err != nil { switch backend := b.cfg.ChainSource.(type) {
return false, err
case *chain.SPVChain:
bh, _, err := backend.CS.GetBlockByHash(*bestHash)
if err != nil {
return false, err
}
blockHeader = &bh
case *chain.RPCClient:
blockHeader, err = backend.GetBlockHeader(bestHash)
if err != nil {
return false, err
}
} }
// If the timestamp no the best header is more than 2 hours in the // If the timestamp no the best header is more than 2 hours in the