From ec210ad79c0b0332b908cba579866b740761fe4c Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Mon, 14 Jan 2019 18:38:45 -0800 Subject: [PATCH] chain/bitcoind_client: extend filterTx with script spend support In this commit, we extend the BitcoindClient to properly detect the spend of an arbitrary script on-chain. This is possible by looking at a transaction's input and re-deriving the PkScript, from its signature script/witness, of the output it's attempting to spend. Upon detecting the spend, a chain.RelevantTx will be dispatched. --- chain/bitcoind_client.go | 58 +++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/chain/bitcoind_client.go b/chain/bitcoind_client.go index f285fd0..e193800 100644 --- a/chain/bitcoind_client.go +++ b/chain/bitcoind_client.go @@ -1205,16 +1205,47 @@ func (c *BitcoindClient) filterTx(tx *wire.MsgTx, // to determine if this transaction is somehow relevant to the caller. var isRelevant bool - // We'll start by cycling through its outputs to determine if it pays to - // any of the currently watched addresses. If an output matches, we'll - // add it to our watch list. - for i, out := range tx.TxOut { - _, addrs, _, err := txscript.ExtractPkScriptAddrs( - out.PkScript, c.chainParams, + // We'll start by checking all inputs and determining whether it spends + // an existing outpoint or a pkScript encoded as an address in our watch + // list. + for _, txIn := range tx.TxIn { + // If it matches an outpoint in our watch list, we can exit our + // loop early. + if _, ok := c.watchedOutPoints[txIn.PreviousOutPoint]; ok { + isRelevant = true + break + } + + // Otherwise, we'll check whether it matches a pkScript in our + // watch list encoded as an address. To do so, we'll re-derive + // the pkScript of the output the input is attempting to spend. + pkScript, err := txscript.ComputePkScript( + txIn.SignatureScript, txIn.Witness, ) if err != nil { - log.Debugf("Unable to parse output script in %s:%d: %v", - tx.TxHash(), i, err) + // Non-standard outputs can be safely skipped. + continue + } + addr, err := pkScript.Address(c.chainParams) + if err != nil { + // Non-standard outputs can be safely skipped. + continue + } + if _, ok := c.watchedAddresses[addr.String()]; ok { + isRelevant = true + break + } + } + + // We'll also cycle through its outputs to determine if it pays to + // any of the currently watched addresses. If an output matches, we'll + // add it to our watch list. + for i, txOut := range tx.TxOut { + _, addrs, _, err := txscript.ExtractPkScriptAddrs( + txOut.PkScript, c.chainParams, + ) + if err != nil { + // Non-standard outputs can be safely skipped. continue } @@ -1238,17 +1269,6 @@ func (c *BitcoindClient) filterTx(tx *wire.MsgTx, } } - // If the transaction didn't pay to any of our watched hashes, we'll - // check if it spends any of our watched outpoints. - if !isRelevant { - for _, in := range tx.TxIn { - if _, ok := c.watchedOutPoints[in.PreviousOutPoint]; ok { - isRelevant = true - break - } - } - } - // If the transaction is not relevant to us, we can simply exit. if !isRelevant { return false, rec, nil