chain/rpc: impl FilterBlocks using gc filter rescan
This commit is contained in:
parent
9b0831cd65
commit
9437c3784a
1 changed files with 88 additions and 0 deletions
88
chain/rpc.go
88
chain/rpc.go
|
@ -13,9 +13,12 @@ import (
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/rpcclient"
|
"github.com/btcsuite/btcd/rpcclient"
|
||||||
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
"github.com/btcsuite/btcwallet/wtxmgr"
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
|
"github.com/ltcsuite/ltcutil/gcs"
|
||||||
|
"github.com/ltcsuite/ltcutil/gcs/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RPCClient represents a persistent client connection to a bitcoin RPC server
|
// RPCClient represents a persistent client connection to a bitcoin RPC server
|
||||||
|
@ -163,6 +166,91 @@ func (c *RPCClient) BlockStamp() (*waddrmgr.BlockStamp, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FilterBlocks scans the blocks contained in the FilterBlocksRequest for any
|
||||||
|
// addresses of interest. For each requested block, the corresponding compact
|
||||||
|
// filter will first be checked for matches, skipping those that do not report
|
||||||
|
// anything. If the filter returns a postive match, the full block will be
|
||||||
|
// fetched and filtered. This method returns a FilterBlocksReponse for the first
|
||||||
|
// block containing a matching address. If no matches are found in the range of
|
||||||
|
// blocks requested, the returned response will be nil.
|
||||||
|
func (c *RPCClient) FilterBlocks(
|
||||||
|
req *FilterBlocksRequest) (*FilterBlocksResponse, error) {
|
||||||
|
|
||||||
|
blockFilterer := NewBlockFilterer(c.chainParams, req)
|
||||||
|
|
||||||
|
// Construct the watchlist using the addresses and outpoints contained
|
||||||
|
// in the filter blocks request.
|
||||||
|
watchList, err := buildFilterBlocksWatchList(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over the requested blocks, fetching the compact filter for
|
||||||
|
// each one, and matching it against the watchlist generated above. If
|
||||||
|
// the filter returns a positive match, the full block is then requested
|
||||||
|
// and scanned for addresses using the block filterer.
|
||||||
|
for i, blk := range req.Blocks {
|
||||||
|
rawFilter, err := c.GetCFilter(&blk.Hash, wire.GCSFilterRegular)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the filter is large enough to be deserialized.
|
||||||
|
if len(rawFilter.Data) < 4 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
filter, err := gcs.FromNBytes(builder.DefaultP, rawFilter.Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip any empty filters.
|
||||||
|
if filter.N() == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
key := builder.DeriveKey(&blk.Hash)
|
||||||
|
matched, err := filter.MatchAny(key, watchList)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !matched {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Fetching block height=%d hash=%v",
|
||||||
|
blk.Height, blk.Hash)
|
||||||
|
|
||||||
|
rawBlock, err := c.GetBlock(&blk.Hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !blockFilterer.FilterBlock(rawBlock) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If any external or internal addresses were detected in this
|
||||||
|
// block, we return them to the caller so that the rescan
|
||||||
|
// windows can widened with subsequent addresses. The
|
||||||
|
// `BatchIndex` is returned so that the caller can compute the
|
||||||
|
// *next* block from which to begin again.
|
||||||
|
resp := &FilterBlocksResponse{
|
||||||
|
BatchIndex: uint32(i),
|
||||||
|
BlockMeta: blk,
|
||||||
|
FoundExternalAddrs: blockFilterer.FoundExternal,
|
||||||
|
FoundInternalAddrs: blockFilterer.FoundInternal,
|
||||||
|
FoundOutPoints: blockFilterer.FoundOutPoints,
|
||||||
|
RelevantTxns: blockFilterer.RelevantTxns,
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// No addresses were found for this range.
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// parseBlock parses a btcws definition of the block a tx is mined it to the
|
// parseBlock parses a btcws definition of the block a tx is mined it to the
|
||||||
// Block structure of the wtxmgr package, and the block index. This is done
|
// Block structure of the wtxmgr package, and the block index. This is done
|
||||||
// here since rpcclient doesn't parse this nicely for us.
|
// here since rpcclient doesn't parse this nicely for us.
|
||||||
|
|
Loading…
Reference in a new issue