-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
blockchain.go
162 lines (137 loc) · 4.51 KB
/
blockchain.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package btcwallet
import (
"encoding/hex"
"errors"
"fmt"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/chain"
"github.com/lightninglabs/neutrino"
"github.com/lightninglabs/neutrino/headerfs"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet"
)
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")
// ErrOutputNotFound signals that the desired output could not be
// located.
ErrOutputNotFound = errors.New("target output was not found")
)
// 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) {
return b.chain.GetBestBlock()
}
// GetUtxo returns the original output referenced by the passed outpoint that
// creates the target pkScript.
//
// This method is a part of the lnwallet.BlockChainIO interface.
func (b *BtcWallet) GetUtxo(op *wire.OutPoint, pkScript []byte,
heightHint uint32, cancel <-chan struct{}) (*wire.TxOut, error) {
switch backend := b.chain.(type) {
case *chain.NeutrinoClient:
spendReport, err := backend.CS.GetUtxo(
neutrino.WatchInputs(neutrino.InputWithScript{
OutPoint: *op,
PkScript: pkScript,
}),
neutrino.StartBlock(&headerfs.BlockStamp{
Height: int32(heightHint),
}),
neutrino.QuitChan(cancel),
)
if err != nil {
return nil, err
}
// 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 {
return nil, ErrOutputSpent
}
// Otherwise, the output is assumed to be in the UTXO.
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
}
// 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
}
return &wire.TxOut{
Value: int64(amt),
PkScript: pkScript,
}, nil
case *chain.BitcoindClient:
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
}
// Sadly, gettxout returns the output value in BTC instead of
// satoshis.
amt, err := btcutil.NewAmount(txout.Value)
if err != nil {
return nil, err
}
return &wire.TxOut{
Value: int64(amt),
PkScript: pkScript,
}, nil
default:
return nil, fmt.Errorf("unknown backend")
}
}
// 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.
//
// This method is a part of the lnwallet.BlockChainIO interface.
func (b *BtcWallet) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
_, 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)
}
// 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) {
return b.chain.GetBlockHash(blockHeight)
}
// A compile time check to ensure that BtcWallet implements the BlockChainIO
// interface.
var _ lnwallet.WalletController = (*BtcWallet)(nil)