Skip to content

Commit 91c7c01

Browse files
committed
Merge branch 'lnd-16-0'
2 parents e43dfc4 + cd37d1a commit 91c7c01

17 files changed

+1175
-1595
lines changed

.editorconfig

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# EditorConfig is awesome: https://EditorConfig.org
2+
3+
# Top-most EditorConfig file.
4+
root = true
5+
6+
# Unix-style newlines with a newline ending every file.
7+
[*.md]
8+
end_of_line = lf
9+
insert_final_newline = true
10+
max_line_length = 80
11+
12+
# 8 space indentation for Golang code.
13+
[*.go]
14+
indent_style = tab
15+
indent_size = 8
16+
max_line_length = 80

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ env:
1616
# go needs absolute directories, using the $HOME variable doesn't work here.
1717
GOCACHE: /home/runner/work/go/pkg/build
1818
GOPATH: /home/runner/work/go
19-
GO_VERSION: 1.18.x
19+
GO_VERSION: 1.19.x
2020

2121
jobs:
2222
build:

chainkit_client.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package lndclient
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"sync"
7+
"time"
8+
9+
"github.com/btcsuite/btcd/chaincfg/chainhash"
10+
"github.com/btcsuite/btcd/wire"
11+
"github.com/lightningnetwork/lnd/lnrpc/chainrpc"
12+
"google.golang.org/grpc"
13+
)
14+
15+
// ChainKitClient exposes chain functionality.
16+
type ChainKitClient interface {
17+
// GetBlock returns a block given the corresponding block hash.
18+
GetBlock(ctx context.Context, hash chainhash.Hash) (*wire.MsgBlock,
19+
error)
20+
21+
// GetBestBlock returns the latest block hash and current height of the
22+
// valid most-work chain.
23+
GetBestBlock(ctx context.Context) (chainhash.Hash, int32, error)
24+
25+
// GetBlockHash returns the hash of the block in the best blockchain
26+
// at the given height.
27+
GetBlockHash(ctx context.Context, blockHeight int64) (chainhash.Hash,
28+
error)
29+
}
30+
31+
type chainKitClient struct {
32+
client chainrpc.ChainKitClient
33+
chainMac serializedMacaroon
34+
timeout time.Duration
35+
36+
wg sync.WaitGroup
37+
}
38+
39+
func newChainKitClient(conn grpc.ClientConnInterface,
40+
chainMac serializedMacaroon, timeout time.Duration) *chainKitClient {
41+
42+
return &chainKitClient{
43+
client: chainrpc.NewChainKitClient(conn),
44+
chainMac: chainMac,
45+
timeout: timeout,
46+
}
47+
}
48+
49+
func (s *chainKitClient) WaitForFinished() {
50+
s.wg.Wait()
51+
}
52+
53+
// GetBlock returns a block given the corresponding block hash.
54+
func (s *chainKitClient) GetBlock(ctxParent context.Context,
55+
hash chainhash.Hash) (*wire.MsgBlock, error) {
56+
57+
ctx, cancel := context.WithTimeout(ctxParent, s.timeout)
58+
defer cancel()
59+
60+
macaroonAuth := s.chainMac.WithMacaroonAuth(ctx)
61+
req := &chainrpc.GetBlockRequest{
62+
BlockHash: hash[:],
63+
}
64+
resp, err := s.client.GetBlock(macaroonAuth, req)
65+
if err != nil {
66+
return nil, err
67+
}
68+
69+
// Convert raw block bytes into wire.MsgBlock.
70+
msgBlock := &wire.MsgBlock{}
71+
blockReader := bytes.NewReader(resp.RawBlock)
72+
err = msgBlock.Deserialize(blockReader)
73+
if err != nil {
74+
return nil, err
75+
}
76+
77+
return msgBlock, nil
78+
}
79+
80+
// GetBestBlock returns the block hash and current height from the valid
81+
// most-work chain.
82+
func (s *chainKitClient) GetBestBlock(ctxParent context.Context) (chainhash.Hash,
83+
int32, error) {
84+
85+
ctx, cancel := context.WithTimeout(ctxParent, s.timeout)
86+
defer cancel()
87+
88+
macaroonAuth := s.chainMac.WithMacaroonAuth(ctx)
89+
resp, err := s.client.GetBestBlock(
90+
macaroonAuth, &chainrpc.GetBestBlockRequest{},
91+
)
92+
if err != nil {
93+
return chainhash.Hash{}, 0, err
94+
}
95+
96+
// Cast gRPC block hash bytes as chain hash type.
97+
var blockHash chainhash.Hash
98+
copy(blockHash[:], resp.BlockHash)
99+
100+
return blockHash, resp.BlockHeight, nil
101+
}
102+
103+
// GetBlockHash returns the hash of the block in the best blockchain at the
104+
// given height.
105+
func (s *chainKitClient) GetBlockHash(ctxParent context.Context,
106+
blockHeight int64) (chainhash.Hash, error) {
107+
108+
ctx, cancel := context.WithTimeout(ctxParent, s.timeout)
109+
defer cancel()
110+
111+
macaroonAuth := s.chainMac.WithMacaroonAuth(ctx)
112+
req := &chainrpc.GetBlockHashRequest{BlockHeight: blockHeight}
113+
resp, err := s.client.GetBlockHash(macaroonAuth, req)
114+
if err != nil {
115+
return chainhash.Hash{}, err
116+
}
117+
118+
// Cast gRPC block hash bytes as chain hash type.
119+
var blockHash chainhash.Hash
120+
copy(blockHash[:], resp.BlockHash)
121+
122+
return blockHash, nil
123+
}

chainnotifier_client.go

Lines changed: 97 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,60 @@ import (
1313
"google.golang.org/grpc"
1414
)
1515

16+
// notifierOptions is a set of functional options that allow callers to further
17+
// modify the type of chain even notifications they receive.
18+
type notifierOptions struct {
19+
// includeBlock if true, then the dispatched confirmation notification
20+
// will include the block that mined the transaction.
21+
includeBlock bool
22+
23+
// reOrgChan if set, will be sent on if the transaction is re-organized
24+
// out of the chain. This channel being set will also imply that we
25+
// don't cancel the notification listener after having received one
26+
// confirmation event. That means the caller manually needs to cancel
27+
// the passed in context to cancel being notified once the required
28+
// number of confirmations have been reached.
29+
reOrgChan chan struct{}
30+
}
31+
32+
// defaultNotifierOptions returns the set of default options for the notifier.
33+
func defaultNotifierOptions() *notifierOptions {
34+
return &notifierOptions{}
35+
}
36+
37+
// NotifierOption is a functional option that allows a caller to modify the
38+
// events received from the notifier.
39+
type NotifierOption func(*notifierOptions)
40+
41+
// WithIncludeBlock is an optional argument that allows the caller to specify
42+
// that the block that mined a transaction should be included in the response.
43+
func WithIncludeBlock() NotifierOption {
44+
return func(o *notifierOptions) {
45+
o.includeBlock = true
46+
}
47+
}
48+
49+
// WithReOrgChan configures a channel that will be sent on if the transaction is
50+
// re-organized out of the chain. This channel being set will also imply that we
51+
// don't cancel the notification listener after having received one confirmation
52+
// event. That means the caller manually needs to cancel the passed in context
53+
// to cancel being notified once the required number of confirmations have been
54+
// reached.
55+
func WithReOrgChan(reOrgChan chan struct{}) NotifierOption {
56+
return func(o *notifierOptions) {
57+
o.reOrgChan = reOrgChan
58+
}
59+
}
60+
1661
// ChainNotifierClient exposes base lightning functionality.
1762
type ChainNotifierClient interface {
1863
RegisterBlockEpochNtfn(ctx context.Context) (
1964
chan int32, chan error, error)
2065

2166
RegisterConfirmationsNtfn(ctx context.Context, txid *chainhash.Hash,
22-
pkScript []byte, numConfs, heightHint int32) (
23-
chan *chainntnfs.TxConfirmation, chan error, error)
67+
pkScript []byte, numConfs, heightHint int32,
68+
opts ...NotifierOption) (chan *chainntnfs.TxConfirmation,
69+
chan error, error)
2470

2571
RegisterSpendNtfn(ctx context.Context,
2672
outpoint *wire.OutPoint, pkScript []byte, heightHint int32) (
@@ -126,20 +172,26 @@ func (s *chainNotifierClient) RegisterSpendNtfn(ctx context.Context,
126172
}
127173

128174
func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context,
129-
txid *chainhash.Hash, pkScript []byte, numConfs, heightHint int32) (
130-
chan *chainntnfs.TxConfirmation, chan error, error) {
175+
txid *chainhash.Hash, pkScript []byte, numConfs, heightHint int32,
176+
optFuncs ...NotifierOption) (chan *chainntnfs.TxConfirmation,
177+
chan error, error) {
178+
179+
opts := defaultNotifierOptions()
180+
for _, optFunc := range optFuncs {
181+
optFunc(opts)
182+
}
131183

132184
var txidSlice []byte
133185
if txid != nil {
134186
txidSlice = txid[:]
135187
}
136188
confStream, err := s.client.RegisterConfirmationsNtfn(
137-
s.chainMac.WithMacaroonAuth(ctx),
138-
&chainrpc.ConfRequest{
139-
Script: pkScript,
140-
NumConfs: uint32(numConfs),
141-
HeightHint: uint32(heightHint),
142-
Txid: txidSlice,
189+
s.chainMac.WithMacaroonAuth(ctx), &chainrpc.ConfRequest{
190+
Script: pkScript,
191+
NumConfs: uint32(numConfs),
192+
HeightHint: uint32(heightHint),
193+
Txid: txidSlice,
194+
IncludeBlock: opts.includeBlock,
143195
},
144196
)
145197
if err != nil {
@@ -162,30 +214,60 @@ func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context,
162214
}
163215

164216
switch c := confEvent.Event.(type) {
165-
// Script confirmed
217+
// Script confirmed.
166218
case *chainrpc.ConfEvent_Conf:
167219
tx, err := decodeTx(c.Conf.RawTx)
168220
if err != nil {
169221
errChan <- err
170222
return
171223
}
224+
225+
var block *wire.MsgBlock
226+
if opts.includeBlock {
227+
block, err = decodeBlock(
228+
c.Conf.RawBlock,
229+
)
230+
if err != nil {
231+
errChan <- err
232+
return
233+
}
234+
}
235+
172236
blockHash, err := chainhash.NewHash(
173237
c.Conf.BlockHash,
174238
)
175239
if err != nil {
176240
errChan <- err
177241
return
178242
}
243+
179244
confChan <- &chainntnfs.TxConfirmation{
180245
BlockHeight: c.Conf.BlockHeight,
181246
BlockHash: blockHash,
182247
Tx: tx,
183248
TxIndex: c.Conf.TxIndex,
249+
Block: block,
184250
}
185-
return
186251

187-
// Ignore reorg events, not supported.
252+
// If we're running in re-org aware mode, then
253+
// we don't return here, since we might want to
254+
// be informed about the new block we got
255+
// confirmed in after a re-org.
256+
if opts.reOrgChan == nil {
257+
return
258+
}
259+
260+
// On a re-org, we just need to signal, we don't have
261+
// any additional information. But we only signal if the
262+
// caller requested to be notified about re-orgs.
188263
case *chainrpc.ConfEvent_Reorg:
264+
if opts.reOrgChan != nil {
265+
select {
266+
case opts.reOrgChan <- struct{}{}:
267+
case <-ctx.Done():
268+
return
269+
}
270+
}
189271
continue
190272

191273
// Nil event, should never happen.
@@ -195,9 +277,8 @@ func (s *chainNotifierClient) RegisterConfirmationsNtfn(ctx context.Context,
195277

196278
// Unexpected type.
197279
default:
198-
errChan <- fmt.Errorf(
199-
"conf event has unexpected type",
200-
)
280+
errChan <- fmt.Errorf("conf event has " +
281+
"unexpected type")
201282
return
202283
}
203284
}

0 commit comments

Comments
 (0)