// matchTxAndUpdate returns true if the bloom filter matches data within the // passed transaction, otherwise false is returned. If the filter does match // the passed transaction, it will also update the filter depending on the bloom // update flags set via the loaded filter if needed. // // This function MUST be called with the filter lock held. func (bf *Filter) matchTxAndUpdate(tx *btcutil.Tx) bool { // Check if the filter matches the hash of the transaction. // This is useful for finding transactions when they appear in a block. matched := bf.matches(tx.Sha()[:]) // Check if the filter matches any data elements in the public key // scripts of any of the outputs. When it does, add the outpoint that // matched so transactions which spend from the matched transaction are // also included in the filter. This removes the burden of updating the // filter for this scenario from the client. It is also more efficient // on the network since it avoids the need for another filteradd message // from the client and avoids some potential races that could otherwise // occur. for i, txOut := range tx.MsgTx().TxOut { pushedData, err := txscript.PushedData(txOut.PkScript) if err != nil { continue } for _, data := range pushedData { if !bf.matches(data) { continue } matched = true bf.maybeAddOutpoint(txOut.PkScript, tx.Sha(), uint32(i)) break } } // Nothing more to do if a match has already been made. if matched { return true } // At this point, the transaction and none of the data elements in the // public key scripts of its outputs matched. // Check if the filter matches any outpoints this transaction spends or // any any data elements in the signature scripts of any of the inputs. for _, txin := range tx.MsgTx().TxIn { if bf.matchesOutPoint(&txin.PreviousOutPoint) { return true } pushedData, err := txscript.PushedData(txin.SignatureScript) if err != nil { continue } for _, data := range pushedData { if bf.matches(data) { return true } } } return false }
// indexScriptPubKey indexes all data pushes greater than 8 bytes within the // passed SPK. Our "address" index is actually a hash160 index, where in the // ideal case the data push is either the hash160 of a publicKey (P2PKH) or // a Script (P2SH). func indexScriptPubKey(addrIndex database.BlockAddrIndex, scriptPubKey []byte, locInBlock *wire.TxLoc) error { dataPushes, err := txscript.PushedData(scriptPubKey) if err != nil { adxrLog.Tracef("Couldn't get pushes: %v", err) return err } for _, data := range dataPushes { // Only index pushes greater than 8 bytes. if len(data) < 8 { continue } var indexKey [ripemd160.Size]byte // A perfect little hash160. if len(data) <= 20 { copy(indexKey[:], data) // Otherwise, could be a payToPubKey or an OP_RETURN, so we'll // make a hash160 out of it. } else { copy(indexKey[:], btcutil.Hash160(data)) } addrIndex[indexKey] = append(addrIndex[indexKey], locInBlock) } return nil }
// TestPushedData ensured the PushedData function extracts the expected data out // of various scripts. func TestPushedData(t *testing.T) { t.Parallel() var tests = []struct { script string out [][]byte valid bool }{ { "0 IF 0 ELSE 2 ENDIF", [][]byte{nil, nil}, true, }, { "16777216 10000000", [][]byte{ {0x00, 0x00, 0x00, 0x01}, // 16777216 {0x80, 0x96, 0x98, 0x00}, // 10000000 }, true, }, { "DUP HASH160 '17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem' EQUALVERIFY CHECKSIG", [][]byte{ // 17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem { 0x31, 0x37, 0x56, 0x5a, 0x4e, 0x58, 0x31, 0x53, 0x4e, 0x35, 0x4e, 0x74, 0x4b, 0x61, 0x38, 0x55, 0x51, 0x46, 0x78, 0x77, 0x51, 0x62, 0x46, 0x65, 0x46, 0x63, 0x33, 0x69, 0x71, 0x52, 0x59, 0x68, 0x65, 0x6d, }, }, true, }, { "PUSHDATA4 1000 EQUAL", nil, false, }, } for i, test := range tests { script := mustParseShortForm(test.script) data, err := txscript.PushedData(script) if test.valid && err != nil { t.Errorf("TestPushedData failed test #%d: %v\n", i, err) continue } else if !test.valid && err == nil { t.Errorf("TestPushedData failed test #%d: test should "+ "be invalid\n", i) continue } if !reflect.DeepEqual(data, test.out) { t.Errorf("TestPushedData failed test #%d: want: %x "+ "got: %x\n", i, test.out, data) } } }