From 285606ef7a22ba3c470a55f92a3fd0a950e5a506 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 30 May 2018 17:15:41 -0700 Subject: [PATCH 1/5] BIP-0158: remove txid from extended filter --- bip-0158.mediawiki | 2 -- 1 file changed, 2 deletions(-) diff --git a/bip-0158.mediawiki b/bip-0158.mediawiki index 15caa682..1b7f8dff 100644 --- a/bip-0158.mediawiki +++ b/bip-0158.mediawiki @@ -269,8 +269,6 @@ sync a regular Bitcoin wallet. A basic filter MUST contain exactly the following items for each transaction in a block: * The outpoint of each input, except for the coinbase transaction * The scriptPubKey of each output -* The txid of the transaction itself - The extended filter contains extra data that is meant to enable applications with more advanced smart contracts. An extended filter MUST contain exactly the following items for each transaction in a block ''except the coinbase'': From 4a85759f0229450f4a63b9404dfe8e8c10bdc92e Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 30 May 2018 17:16:25 -0700 Subject: [PATCH 2/5] BIP-0158: remove the extended filter type --- bip-0158.mediawiki | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/bip-0158.mediawiki b/bip-0158.mediawiki index 1b7f8dff..034df633 100644 --- a/bip-0158.mediawiki +++ b/bip-0158.mediawiki @@ -258,9 +258,8 @@ against the decompressed GCS contents. See === Block Filters === -This BIP defines two initial filter types: +This BIP defines one initial filter type: * Basic (0x00) -* Extended (0x01) ==== Contents ==== @@ -269,15 +268,6 @@ sync a regular Bitcoin wallet. A basic filter MUST contain exactly the following items for each transaction in a block: * The outpoint of each input, except for the coinbase transaction * The scriptPubKey of each output -The extended filter contains extra data that is meant to enable applications -with more advanced smart contracts. An extended filter MUST contain exactly the -following items for each transaction in a block ''except the coinbase'': -* Each item within the witness stack of each input (if the input has a witness) -* Each data push in the scriptSig of each input - -Note that neither filter type interprets P2SH scripts or witness scripts to -extract data pushes from them. If necessary, future filter types may be designed -to do so. ==== Construction ==== From 1c2ed6dce331e81785f9f6e8926c6e023df8e8f3 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 30 May 2018 17:18:24 -0700 Subject: [PATCH 3/5] BIP-0158: allow filters to define values for P and M, reparameterize default filter --- bip-0158.mediawiki | 65 +++++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/bip-0158.mediawiki b/bip-0158.mediawiki index 034df633..1a0e95db 100644 --- a/bip-0158.mediawiki +++ b/bip-0158.mediawiki @@ -65,11 +65,14 @@ For each block, compact filters are derived containing sets of items associated with the block (eg. addresses sent to, outpoints spent, etc.). A set of such data objects is compressed into a probabilistic structure called a ''Golomb-coded set'' (GCS), which matches all items in the set with probability -1, and matches other items with probability 2^(-P) for some integer -parameter P. +1, and matches other items with probability 2^(-P) for some +integer parameter P. We also introduce parameter M +which allows filter to uniquely tune the range that items are hashed onto +before compressing. Each defined filter also selects distinct parameters for P +and M. At a high level, a GCS is constructed from a set of N items by: -# hashing all items to 64-bit integers in the range [0, N * 2^P) +# hashing all items to 64-bit integers in the range [0, N * M) # sorting the hashed values in ascending order # computing the differences between each value and the previous one # writing the differences sequentially, compressed with Golomb-Rice coding @@ -80,9 +83,13 @@ The following sections describe each step in greater detail. The first step in the filter construction is hashing the variable-sized raw items in the set to the range [0, F), where F = N * -2^P. Set membership queries against the hash outputs will have a false -positive rate of 2^(-P). To avoid integer overflow, the number of -items N MUST be <2^32 and P MUST be <=32. +M. Customarily, M is set to 2^P. However, if +one is able to select both Parameters independently, then more optimal values +can be +selectedhttps://gist.github.com/sipa/576d5f09c3b86c3b1b75598d799fc845. +Set membership queries against the hash outputs will have a false positive rate +of 2^(-P). To avoid integer overflow, the +number of items N MUST be <2^32 and M MUST be <2^32. The items are first passed through the pseudorandom function ''SipHash'', which takes a 128-bit key k and a variable-sized byte vector and produces @@ -104,9 +111,9 @@ result. hash_to_range(item: []byte, F: uint64, k: [16]byte) -> uint64: return (siphash(k, item) * F) >> 64 -hashed_set_construct(raw_items: [][]byte, P: uint, k: [16]byte) -> []uint64: +hashed_set_construct(raw_items: [][]byte, k: [16]byte, M: uint) -> []uint64: let N = len(raw_items) - let F = N << P + let F = N * M let set_items = [] @@ -197,8 +204,8 @@ with Golomb-Rice coding. Finally, the bit stream is padded with 0's to the nearest byte boundary and serialized to the output byte vector.
-construct_gcs(L: [][]byte, P: uint, k: [16]byte) -> []byte:
-    let set_items = hashed_set_construct(L, P, k)
+construct_gcs(L: [][]byte, P: uint, k: [16]byte, M: uint) -> []byte:
+    let set_items = hashed_set_construct(L, k, M)
 
     set_items.sort()
 
@@ -224,8 +231,8 @@ against the reconstructed values. Note that querying does not require the entire
 decompressed set be held in memory at once.
 
 
-gcs_match(key: [16]byte, compressed_set: []byte, target: []byte, P: uint, N: uint) -> bool:
-    let F = N << P
+gcs_match(key: [16]byte, compressed_set: []byte, target: []byte, P: uint, N: uint, M: uint) -> bool:
+    let F = N * M
     let target_hash = hash_to_range(target, F, k)
 
     stream = new_bit_stream(compressed_set)
@@ -260,6 +267,8 @@ against the decompressed GCS contents. See
 
 This BIP defines one initial filter type:
 * Basic (0x00)
+  * M = 784931
+  * P = 19
 
 ==== Contents ====
 
@@ -271,24 +280,27 @@ items for each transaction in a block:
 
 ==== Construction ====
 
-Both the basic and extended filter types are constructed as Golomb-coded sets
-with the following parameters.
+The basic type is constructed as Golomb-coded sets with the following
+parameters.
 
-The parameter P MUST be set to 20. This value was
-chosen as simulations show that it minimizes the bandwidth utilized, considering
-both the expected number of blocks downloaded due to false positives and the
-size of the filters themselves. The code along with a demo used for the
-parameter tuning can be found
-[https://github.com/Roasbeef/bips/blob/83b83c78e189be898573e0bfe936dd0c9b99ecb9/gcs_light_client/gentestvectors.go here].
+The parameter P MUST be set to 19, and the parameter
+M MUST be set to 784931. Analysis has shown that if
+one is able to select P and M independently, then
+setting M=1.497137 * 2^P is close to optimal
+https://gist.github.com/sipa/576d5f09c3b86c3b1b75598d799fc845.
+
+Empirical analysis also shows that was chosen as these parameters minimize the
+bandwidth utilized, considering both the expected number of blocks downloaded
+due to false positives and the size of the filters themselves. 
 
 The parameter k MUST be set to the first 16 bytes of the hash of
 the block for which the filter is constructed. This ensures the key is
 deterministic while still varying from block to block.
 
 Since the value N is required to decode a GCS, a serialized GCS
-includes it as a prefix, written as a CompactSize. Thus, the complete
-serialization of a filter is:
-* N, encoded as a CompactSize
+includes it as a prefix, written as a CompactSize. Thus, the
+complete serialization of a filter is:
+* N, encoded as a CompactSize
 * The bytes of the compressed filter itself
 
 ==== Signaling ====
@@ -311,7 +323,8 @@ though it requires implementation of the new filters.
 
 We would like to thank bfd (from the bitcoin-dev mailing list) for bringing the
 basis of this BIP to our attention, Greg Maxwell for pointing us in the
-direction of Golomb-Rice coding and fast range optimization, and Pedro
+direction of Golomb-Rice coding and fast range optimization, Pieter Wullie for
+his analysis of optimal GCS parameters, and Pedro
 Martelletto for writing the initial indexing code for btcd.
 
 We would also like to thank Dave Collins, JJ Jeffrey, and Eric Lombrozo for
@@ -363,8 +376,8 @@ easier to understand.
 === Golomb-Coded Set Multi-Match ===
 
 
-gcs_match_any(key: [16]byte, compressed_set: []byte, targets: [][]byte, P: uint, N: uint) -> bool:
-    let F = N << P
+gcs_match_any(key: [16]byte, compressed_set: []byte, targets: [][]byte, P: uint, N: uint, M: uint) -> bool:
+    let F = N * M
 
     // Map targets to the same range as the set hashes.
     let target_hashes = []

From 6a4e8198295b4ebbe47963362183aa5da53e7b27 Mon Sep 17 00:00:00 2001
From: Olaoluwa Osuntokun 
Date: Tue, 3 Jul 2018 19:06:41 -0500
Subject: [PATCH 4/5] BIP-0158: switch to prev output scripts, skip all
 OP_RETURN

---
 bip-0158.mediawiki | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/bip-0158.mediawiki b/bip-0158.mediawiki
index 1a0e95db..bf2e856e 100644
--- a/bip-0158.mediawiki
+++ b/bip-0158.mediawiki
@@ -273,10 +273,22 @@ This BIP defines one initial filter type:
 ==== Contents ====
 
 The basic filter is designed to contain everything that a light client needs to
-sync a regular Bitcoin wallet. A basic filter MUST contain exactly the following
-items for each transaction in a block:
-* The outpoint of each input, except for the coinbase transaction
-* The scriptPubKey of each output
+sync a regular Bitcoin wallet. A basic filter MUST contain exactly the
+following items for each transaction in a block:
+* The previous output script (the script being spent) for each input, except
+  for the coinbase transaction.
+* The scriptPubKey of each output, aside from all OP_RETURN output
+  scripts.
+
+Any "nil" items MUST NOT be included into the final set of filter elements. 
+
+We exclude all OP_RETURN outputs in order to allow filters to
+easily be committed to in the future via a soft-fork. A likely area for future
+commitments is an additional OP_RETURN output in the coinbase
+transaction similar to the current witness commitment
+https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki. By
+excluding all OP_RETURN outputs we avoid a circular dependency
+between the commitment, and the item being committed to.
 
 ==== Construction ====
 

From ac766445332303190fc678256045c80e3ca746ef Mon Sep 17 00:00:00 2001
From: Olaoluwa Osuntokun 
Date: Thu, 31 May 2018 19:12:09 -0700
Subject: [PATCH 5/5] BIP-0158: regenerate test vectors for fp=19, new reg
 filter, no ext filter

In this commit, we simplify the code that generates the test vectors to
only generate filters for a target fp of 19, and also only for the
regular filter, as it's the only filter type currently defined.

The test vectors have also been updated to include the previous output
scripts for all input within a block as these are now required to
construct the regular filter.

Finally, the generation code has been updated to properly fetch the
previous input scripts to the generation code can verify the filter it
generates manually against the end server.
---
 bip-0158/gentestvectors.go                    | 392 ++++++++----------
 bip-0158/{testnet-20.json => testnet-19.json} |  15 +-
 2 files changed, 171 insertions(+), 236 deletions(-)
 rename bip-0158/{testnet-20.json => testnet-19.json} (69%)

diff --git a/bip-0158/gentestvectors.go b/bip-0158/gentestvectors.go
index deaf2c74..472f8c1d 100644
--- a/bip-0158/gentestvectors.go
+++ b/bip-0158/gentestvectors.go
@@ -15,13 +15,15 @@ import (
 	"io"
 	"io/ioutil"
 	"os"
-	"path"
+	"path/filepath"
 
-	"github.com/roasbeef/btcd/chaincfg/chainhash"
-	"github.com/roasbeef/btcd/rpcclient"
-	"github.com/roasbeef/btcd/wire"
-	"github.com/roasbeef/btcutil/gcs"
-	"github.com/roasbeef/btcutil/gcs/builder"
+	"github.com/btcsuite/btcd/blockchain"
+	"github.com/btcsuite/btcd/chaincfg/chainhash"
+	"github.com/btcsuite/btcd/rpcclient"
+	"github.com/btcsuite/btcd/wire"
+	"github.com/btcsuite/btcutil"
+	"github.com/btcsuite/btcutil/gcs/builder"
+	"github.com/davecgh/go-spew/spew"
 )
 
 var (
@@ -29,13 +31,19 @@ var (
 	// vectors. Any new entries must be added in sorted order.
 	testBlockHeights = []testBlockCase{
 		{0, "Genesis block"},
-		{1, "Extended filter is empty"},
 		{2, ""},
 		{3, ""},
 		{926485, "Duplicate pushdata 913bcc2be49cb534c20474c4dee1e9c4c317e7eb"},
 		{987876, "Coinbase tx has unparseable output script"},
 		{1263442, "Includes witness data"},
 	}
+
+	defaultBtcdDir         = btcutil.AppDataDir("btcd", false)
+	defaultBtcdRPCCertFile = filepath.Join(defaultBtcdDir, "rpc.cert")
+)
+
+const (
+	fp = 19
 )
 
 type testBlockCase struct {
@@ -86,41 +94,78 @@ func (w *JSONTestWriter) Close() error {
 	return err
 }
 
+func fetchPrevOutputScripts(client *rpcclient.Client, block *wire.MsgBlock) ([][]byte, error) {
+	var prevScripts [][]byte
+
+	txCache := make(map[chainhash.Hash]*wire.MsgTx)
+	for _, tx := range block.Transactions {
+		if blockchain.IsCoinBaseTx(tx) {
+			continue
+		}
+
+		for _, txIn := range tx.TxIn {
+			prevOp := txIn.PreviousOutPoint
+
+			tx, ok := txCache[prevOp.Hash]
+			if !ok {
+				originTx, err := client.GetRawTransaction(
+					&prevOp.Hash,
+				)
+				if err != nil {
+					return nil, fmt.Errorf("unable to get "+
+						"txid=%v: %v", prevOp.Hash, err)
+				}
+
+				txCache[prevOp.Hash] = originTx.MsgTx()
+
+				tx = originTx.MsgTx()
+			}
+
+			index := prevOp.Index
+
+			prevScripts = append(
+				prevScripts, tx.TxOut[index].PkScript,
+			)
+		}
+	}
+
+	return prevScripts, nil
+}
+
 func main() {
-	err := os.Mkdir("gcstestvectors", os.ModeDir|0755)
-	if err != nil { // Don't overwrite existing output if any
-		fmt.Println("Couldn't create directory: ", err)
+	var (
+		writerFile      *JSONTestWriter
+		prevBasicHeader chainhash.Hash
+	)
+	fName := fmt.Sprintf("testnet-%02d.json", fp)
+	file, err := os.Create(fName)
+	if err != nil {
+		fmt.Println("Error creating output file: ", err.Error())
 		return
 	}
-	files := make([]*JSONTestWriter, 33)
-	prevBasicHeaders := make([]chainhash.Hash, 33)
-	prevExtHeaders := make([]chainhash.Hash, 33)
-	for i := 1; i <= 32; i++ { // Min 1 bit of collision space, max 32
-		fName := fmt.Sprintf("gcstestvectors/testnet-%02d.json", i)
-		file, err := os.Create(fName)
-		if err != nil {
-			fmt.Println("Error creating output file: ", err.Error())
-			return
-		}
-		defer file.Close()
+	defer file.Close()
 
-		writer := &JSONTestWriter{writer: file}
-		defer writer.Close()
-
-		err = writer.WriteComment("Block Height,Block Hash,Block,Previous Basic Header,Previous Ext Header,Basic Filter,Ext Filter,Basic Header,Ext Header,Notes")
-		if err != nil {
-			fmt.Println("Error writing to output file: ", err.Error())
-			return
-		}
-
-		files[i] = writer
+	writer := &JSONTestWriter{
+		writer: file,
 	}
-	cert, err := ioutil.ReadFile(
-		path.Join(os.Getenv("HOME"), "/.btcd/rpc.cert"))
+	defer writer.Close()
+
+	err = writer.WriteComment("Block Height,Block Hash,Block," +
+		"[Prev Output Scripts for Block],Previous Basic Header," +
+		"Basic Filter,Basic Header,Notes")
+	if err != nil {
+		fmt.Println("Error writing to output file: ", err.Error())
+		return
+	}
+
+	writerFile = writer
+
+	cert, err := ioutil.ReadFile(defaultBtcdRPCCertFile)
 	if err != nil {
 		fmt.Println("Couldn't read RPC cert: ", err.Error())
 		return
 	}
+
 	conf := rpcclient.ConnConfig{
 		Host:         "127.0.0.1:18334",
 		Endpoint:     "ws",
@@ -134,19 +179,20 @@ func main() {
 		return
 	}
 
-	var testBlockIndex int = 0
+	var testBlockIndex int
 	for height := 0; testBlockIndex < len(testBlockHeights); height++ {
-		fmt.Printf("Height: %d\n", height)
 		blockHash, err := client.GetBlockHash(int64(height))
 		if err != nil {
 			fmt.Println("Couldn't get block hash: ", err.Error())
 			return
 		}
+
 		block, err := client.GetBlock(blockHash)
 		if err != nil {
 			fmt.Println("Couldn't get block hash: ", err.Error())
 			return
 		}
+
 		var blockBuf bytes.Buffer
 		err = block.Serialize(&blockBuf)
 		if err != nil {
@@ -154,208 +200,98 @@ func main() {
 			return
 		}
 		blockBytes := blockBuf.Bytes()
-		for i := 1; i <= 32; i++ {
-			basicFilter, err := buildBasicFilter(block, uint8(i))
+
+		prevOutputScripts, err := fetchPrevOutputScripts(client, block)
+		if err != nil {
+			fmt.Println("Couldn't fetch prev output scipts: ", err)
+			return
+		}
+
+		basicFilter, err := builder.BuildBasicFilter(block, prevOutputScripts)
+		if err != nil {
+			fmt.Println("Error generating basic filter: ", err.Error())
+			return
+		}
+		basicHeader, err := builder.MakeHeaderForFilter(basicFilter, prevBasicHeader)
+		if err != nil {
+			fmt.Println("Error generating header for filter: ", err.Error())
+			return
+		}
+
+		// We'll now ensure that we've constructed the same filter as
+		// the chain server we're fetching blocks form.
+		filter, err := client.GetCFilter(
+			blockHash, wire.GCSFilterRegular,
+		)
+		if err != nil {
+			fmt.Println("Error getting basic filter: ",
+				err.Error())
+			return
+		}
+
+		nBytes, err := basicFilter.NBytes()
+		if err != nil {
+			fmt.Println("Couldn't get NBytes(): ", err)
+			return
+		}
+		if !bytes.Equal(filter.Data, nBytes) {
+			// Don't error on empty filters
+			fmt.Printf("basic filter doesn't match: generated "+
+				"%x, rpc returns %x, block %v", nBytes,
+				filter.Data, spew.Sdump(block))
+			return
+		}
+
+		header, err := client.GetCFilterHeader(
+			blockHash, wire.GCSFilterRegular,
+		)
+		if err != nil {
+			fmt.Println("Error getting basic header: ", err.Error())
+			return
+		}
+		if !bytes.Equal(header.PrevFilterHeader[:], basicHeader[:]) {
+			fmt.Println("Basic header doesn't match!")
+			return
+		}
+
+		if height%1000 == 0 {
+			fmt.Printf("Verified height %v against server\n", height)
+		}
+
+		if uint32(height) == testBlockHeights[testBlockIndex].height {
+			var bfBytes []byte
+			bfBytes, err = basicFilter.NBytes()
 			if err != nil {
-				fmt.Println("Error generating basic filter: ", err.Error())
+				fmt.Println("Couldn't get NBytes(): ", err)
 				return
 			}
-			basicHeader, err := builder.MakeHeaderForFilter(basicFilter,
-				prevBasicHeaders[i])
-			if err != nil {
-				fmt.Println("Error generating header for filter: ", err.Error())
-				return
-			}
-			if basicFilter == nil {
-				basicFilter = &gcs.Filter{}
-			}
-			extFilter, err := buildExtFilter(block, uint8(i))
-			if err != nil {
-				fmt.Println("Error generating ext filter: ", err.Error())
-				return
-			}
-			extHeader, err := builder.MakeHeaderForFilter(extFilter,
-				prevExtHeaders[i])
-			if err != nil {
-				fmt.Println("Error generating header for filter: ", err.Error())
-				return
-			}
-			if extFilter == nil {
-				extFilter = &gcs.Filter{}
-			}
-			if i == builder.DefaultP { // This is the default filter size so we can check against the server's info
-				filter, err := client.GetCFilter(blockHash, wire.GCSFilterRegular)
-				if err != nil {
-					fmt.Println("Error getting basic filter: ", err.Error())
-					return
-				}
-				nBytes, err := basicFilter.NBytes()
-				if err != nil {
-					fmt.Println("Couldn't get NBytes(): ", err)
-					return
-				}
-				if !bytes.Equal(filter.Data, nBytes) {
-					// Don't error on empty filters
-					fmt.Println("Basic filter doesn't match!\n", filter.Data, "\n", nBytes)
-					return
-				}
-				filter, err = client.GetCFilter(blockHash, wire.GCSFilterExtended)
-				if err != nil {
-					fmt.Println("Error getting extended filter: ", err.Error())
-					return
-				}
-				nBytes, err = extFilter.NBytes()
-				if err != nil {
-					fmt.Println("Couldn't get NBytes(): ", err)
-					return
-				}
-				if !bytes.Equal(filter.Data, nBytes) {
-					fmt.Println("Extended filter doesn't match!")
-					return
-				}
-				header, err := client.GetCFilterHeader(blockHash, wire.GCSFilterRegular)
-				if err != nil {
-					fmt.Println("Error getting basic header: ", err.Error())
-					return
-				}
-				if !bytes.Equal(header.PrevFilterHeader[:], basicHeader[:]) {
-					fmt.Println("Basic header doesn't match!")
-					return
-				}
-				header, err = client.GetCFilterHeader(blockHash, wire.GCSFilterExtended)
-				if err != nil {
-					fmt.Println("Error getting extended header: ", err.Error())
-					return
-				}
-				if !bytes.Equal(header.PrevFilterHeader[:], extHeader[:]) {
-					fmt.Println("Extended header doesn't match!")
-					return
-				}
-				fmt.Println("Verified against server")
-			}
 
-			if uint32(height) == testBlockHeights[testBlockIndex].height {
-				var bfBytes []byte
-				var efBytes []byte
-				bfBytes, err = basicFilter.NBytes()
-				if err != nil {
-					fmt.Println("Couldn't get NBytes(): ", err)
-					return
-				}
-				efBytes, err = extFilter.NBytes()
-				if err != nil {
-					fmt.Println("Couldn't get NBytes(): ", err)
-					return
-				}
-				row := []interface{}{
-					height,
-					blockHash.String(),
-					hex.EncodeToString(blockBytes),
-					prevBasicHeaders[i].String(),
-					prevExtHeaders[i].String(),
-					hex.EncodeToString(bfBytes),
-					hex.EncodeToString(efBytes),
-					basicHeader.String(),
-					extHeader.String(),
-					testBlockHeights[testBlockIndex].comment,
-				}
-				err = files[i].WriteTestCase(row)
-				if err != nil {
-					fmt.Println("Error writing test case to output: ", err.Error())
-					return
-				}
+			prevScriptStrings := make([]string, len(prevOutputScripts))
+			for i, prevScript := range prevOutputScripts {
+				prevScriptStrings[i] = hex.EncodeToString(prevScript)
+			}
+
+			row := []interface{}{
+				height,
+				blockHash.String(),
+				hex.EncodeToString(blockBytes),
+				prevScriptStrings,
+				prevBasicHeader.String(),
+				hex.EncodeToString(bfBytes),
+				basicHeader.String(),
+				testBlockHeights[testBlockIndex].comment,
+			}
+			err = writerFile.WriteTestCase(row)
+			if err != nil {
+				fmt.Println("Error writing test case to output: ", err.Error())
+				return
 			}
-			prevBasicHeaders[i] = basicHeader
-			prevExtHeaders[i] = extHeader
 		}
 
+		prevBasicHeader = basicHeader
+
 		if uint32(height) == testBlockHeights[testBlockIndex].height {
 			testBlockIndex++
 		}
 	}
 }
-
-// buildBasicFilter builds a basic GCS filter from a block. A basic GCS filter
-// will contain all the previous outpoints spent within a block, as well as the
-// data pushes within all the outputs created within a block. p is specified as
-// an argument in order to create test vectors with various values for p.
-func buildBasicFilter(block *wire.MsgBlock, p uint8) (*gcs.Filter, error) {
-	blockHash := block.BlockHash()
-	b := builder.WithKeyHashP(&blockHash, p)
-
-	// If the filter had an issue with the specified key, then we force it
-	// to bubble up here by calling the Key() function.
-	_, err := b.Key()
-	if err != nil {
-		return nil, err
-	}
-
-	// In order to build a basic filter, we'll range over the entire block,
-	// adding the outpoint data as well as the data pushes within the
-	// pkScript.
-	for i, tx := range block.Transactions {
-		// First we'll compute the bash of the transaction and add that
-		// directly to the filter.
-		txHash := tx.TxHash()
-		b.AddHash(&txHash)
-
-		// Skip the inputs for the coinbase transaction
-		if i != 0 {
-			// Each each txin, we'll add a serialized version of
-			// the txid:index to the filters data slices.
-			for _, txIn := range tx.TxIn {
-				b.AddOutPoint(txIn.PreviousOutPoint)
-			}
-		}
-
-		// For each output in a transaction, we'll add each of the
-		// individual data pushes within the script.
-		for _, txOut := range tx.TxOut {
-			b.AddEntry(txOut.PkScript)
-		}
-	}
-
-	return b.Build()
-}
-
-// buildExtFilter builds an extended GCS filter from a block. An extended
-// filter supplements a regular basic filter by include all the _witness_ data
-// found within a block. This includes all the data pushes within any signature
-// scripts as well as each element of an input's witness stack. Additionally,
-// the _hashes_ of each transaction are also inserted into the filter. p is
-// specified as an argument in order to create test vectors with various values
-// for p.
-func buildExtFilter(block *wire.MsgBlock, p uint8) (*gcs.Filter, error) {
-	blockHash := block.BlockHash()
-	b := builder.WithKeyHashP(&blockHash, p)
-
-	// If the filter had an issue with the specified key, then we force it
-	// to bubble up here by calling the Key() function.
-	_, err := b.Key()
-	if err != nil {
-		return nil, err
-	}
-
-	// In order to build an extended filter, we add the hash of each
-	// transaction as well as each piece of witness data included in both
-	// the sigScript and the witness stack of an input.
-	for i, tx := range block.Transactions {
-		// Skip the inputs for the coinbase transaction
-		if i != 0 {
-			// Next, for each input, we'll add the sigScript (if
-			// it's present), and also the witness stack (if it's
-			// present)
-			for _, txIn := range tx.TxIn {
-				if txIn.SignatureScript != nil {
-					b.AddScript(txIn.SignatureScript)
-				}
-
-				if len(txIn.Witness) != 0 {
-					b.AddWitness(txIn.Witness)
-				}
-			}
-		}
-	}
-
-	return b.Build()
-}
diff --git a/bip-0158/testnet-20.json b/bip-0158/testnet-19.json
similarity index 69%
rename from bip-0158/testnet-20.json
rename to bip-0158/testnet-19.json
index a768b49d..2f00728f 100644
--- a/bip-0158/testnet-20.json
+++ b/bip-0158/testnet-19.json
@@ -1,10 +1,9 @@
 [
-["Block Height,Block Hash,Block,Previous Basic Header,Previous Ext Header,Basic Filter,Ext Filter,Basic Header,Ext Header,Notes"],
-[0,"000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943","0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0285c7cdbe33a0","00","c0589c7f567cffaf7bc0c9f6ad61710b78d3c1afef5d65a2a08e8a753173aa54","753e0d1c28585269ab770b166ca2cd1b32f9bc918750547941ed4849d5a80ba8","Genesis block"],
-[1,"00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206","0100000043497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000bac8b0fa927c0ac8234287e33c5f74d38d354820e24756ad709d7038fc5f31f020e7494dffff001d03e4b6720101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0420e7494d017f062f503253482fffffffff0100f2052a010000002321021aeaf2f8638a129a3156fbe7e5ef635226b0bafd495ff03afe2c843d7e3a4b51ac00000000","c0589c7f567cffaf7bc0c9f6ad61710b78d3c1afef5d65a2a08e8a753173aa54","753e0d1c28585269ab770b166ca2cd1b32f9bc918750547941ed4849d5a80ba8","026929d09bee00","00","81e4f3e934488be62758f0b88037aa558262da3190ca018329997a319a0f8b5b","31b674ab635e074717329dabdb25d3cb0e14cb2526000cc2cedac7b5f2595110","Extended filter is empty"],
-[2,"000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820","0100000006128e87be8b1b4dea47a7247d5528d2702c96826c7a648497e773b800000000e241352e3bec0a95a6217e10c3abb54adfa05abb12c126695595580fb92e222032e7494dffff001d00d235340101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0432e7494d010e062f503253482fffffffff0100f2052a010000002321038a7f6ef1c8ca0c588aa53fa860128077c9e6c11e6830f4d7ee4e763a56b7718fac00000000","81e4f3e934488be62758f0b88037aa558262da3190ca018329997a319a0f8b5b","31b674ab635e074717329dabdb25d3cb0e14cb2526000cc2cedac7b5f2595110","0278fc41168ec0","00","ec48f9f8a625bd8adb2d2684867a05baafebf935553e0b78b386da98179dcf49","0dd53b407c3f242f1838e39e2fc0cfb89cdca27de07ec230568a08d1872f9e01",""],
-[3,"000000008b896e272758da5297bcd98fdc6d97c9b765ecec401e286dc1fdbe10","0100000020782a005255b657696ea057d5b98f34defcf75196f64f6eeac8026c0000000041ba5afc532aae03151b8aa87b65e1594f97504a768e010c98c0add79216247186e7494dffff001d058dc2b60101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0486e7494d0151062f503253482fffffffff0100f2052a01000000232103f6d9ff4c12959445ca5549c811683bf9c88e637b222dd2e0311154c4c85cf423ac00000000","ec48f9f8a625bd8adb2d2684867a05baafebf935553e0b78b386da98179dcf49","0dd53b407c3f242f1838e39e2fc0cfb89cdca27de07ec230568a08d1872f9e01","022ce4b3256540","00","060b7e1be150cc3cabd9e96b00af217132b387f31fd2bd9adfb0c7f5a09a3356","5b2a59bc476d52b45de1398ee34ed10d0cccd2a4b19ba502a456c7356b35be0d",""],
-[926485,"000000000000015d6077a411a8f5cc95caf775ccf11c54e27df75ce58d187313","0000002060bbab0edbf3ef8a49608ee326f8fd75c473b7e3982095e2d100000000000000c30134f8c9b6d2470488d7a67a888f6fa12f8692e0c3411fbfb92f0f68f67eedae03ca57ef13021acc22dc4105010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2f0315230e0004ae03ca57043e3d1e1d0c8796bf579aef0c0000000000122f4e696e6a61506f6f6c2f5345475749542fffffffff038427a112000000001976a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac0000000000000000266a24aa21a9ed5c748e121c0fe146d973a4ac26fa4a68b0549d46ee22d25f50a5e46fe1b377ee00000000000000002952534b424c4f434b3acd16772ad61a3c5f00287480b720f6035d5e54c9efc71be94bb5e3727f10909001200000000000000000000000000000000000000000000000000000000000000000000000000100000000010145310e878941a1b2bc2d33797ee4d89d95eaaf2e13488063a2aa9a74490f510a0100000023220020b6744de4f6ec63cc92f7c220cdefeeb1b1bed2b66c8e5706d80ec247d37e65a1ffffffff01002d3101000000001976a9143ebc40e411ed3c76f86711507ab952300890397288ac0400473044022001dd489a5d4e2fbd8a3ade27177f6b49296ba7695c40dbbe650ea83f106415fd02200b23a0602d8ff1bdf79dee118205fc7e9b40672bf31563e5741feb53fb86388501483045022100f88f040e90cc5dc6c6189d04718376ac19ed996bf9e4a3c29c3718d90ffd27180220761711f16c9e3a44f71aab55cbc0634907a1fa8bb635d971a9a01d368727bea10169522103b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb2103dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba621033d7c89bd9da29fa8d44db7906a9778b53121f72191184a9fee785c39180e4be153ae00000000010000000120925534261de4dcebb1ed5ab1b62bfe7a3ef968fb111dc2c910adfebc6e3bdf010000006b483045022100f50198f5ae66211a4f485190abe4dc7accdabe3bc214ebc9ea7069b97097d46e0220316a70a03014887086e335fc1b48358d46cd6bdc9af3b57c109c94af76fc915101210316cff587a01a2736d5e12e53551b18d73780b83c3bfb4fcf209c869b11b6415effffffff0220a10700000000001976a91450333046115eaa0ac9e0216565f945070e44573988ac2e7cd01a000000001976a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac00000000010000000203a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322010000006a47304402204efc3d70e4ca3049c2a425025edf22d5ca355f9ec899dbfbbeeb2268533a0f2b02204780d3739653035af4814ea52e1396d021953f948c29754edd0ee537364603dc012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff03a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322000000006a47304402202d96defdc5b4af71d6ba28c9a6042c2d5ee7bc6de565d4db84ef517445626e03022022da80320e9e489c8f41b74833dfb6a54a4eb5087cdb46eb663eef0b25caa526012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a914b7e6f7ff8658b2d1fb107e3d7be7af4742e6b1b3876f88fc00000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac0000000001000000043ffd60d3818431c495b89be84afac205d5d1ed663009291c560758bbd0a66df5010000006b483045022100f344607de9df42049688dcae8ff1db34c0c7cd25ec05516e30d2bc8f12ac9b2f022060b648f6a21745ea6d9782e17bcc4277b5808326488a1f40d41e125879723d3a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffffa5379401cce30f84731ef1ba65ce27edf2cc7ce57704507ebe8714aa16a96b92010000006a473044022020c37a63bf4d7f564c2192528709b6a38ab8271bd96898c6c2e335e5208661580220435c6f1ad4d9305d2c0a818b2feb5e45d443f2f162c0f61953a14d097fd07064012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff70e731e193235ff12c3184510895731a099112ffca4b00246c60003c40f843ce000000006a473044022053760f74c29a879e30a17b5f03a5bb057a5751a39f86fa6ecdedc36a1b7db04c022041d41c9b95f00d2d10a0373322a9025dba66c942196bc9d8adeb0e12d3024728012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff66b7a71b3e50379c8e85fc18fe3f1a408fc985f257036c34702ba205cef09f6f000000006a4730440220499bf9e2db3db6e930228d0661395f65431acae466634d098612fd80b08459ee022040e069fc9e3c60009f521cef54c38aadbd1251aee37940e6018aadb10f194d6a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a9148fc37ad460fdfbd2b44fe446f6e3071a4f64faa6878f447f0b000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac00000000","5c169b91332e661a4ebf684d6dffcf64aa139e1e736096ce462609b2e0783c55","5f3cd111e10ed28b7c2bc138fe4bc62e5df1cdc292c010b78c84e5111a5daaa6","16040c63f7ddea293f2d9c13690c0ba7b2910228c38b0fe542ce525021e49b598ada05f83bb9c37c711a02b1850265991c34c4fea6261d22a4b84596c0","0e6651beff00ee7a3be424a90e98450727b304558434c8d53781d469131ad21d399376c151ca28","c8f83ffbc9781c2bd4b7e3c055e888b00d3e2fea14e93b3c4f3adee86b063374","61f8ee615258981089be9f98337f53f44b14cc1532aa469a348fdee547117b80","Duplicate pushdata 913bcc2be49cb534c20474c4dee1e9c4c317e7eb"],
-[987876,"0000000000000c00901f2049055e2a437c819d79a3d54fd63e6af796cd7b8a79","000000202694f74969fdb542090e95a56bc8aa2d646e27033850e32f1c5f000000000000f7e53676b3f12d5beb524ed617f2d25f5a93b5f4f52c1ba2678260d72712f8dd0a6dfe5740257e1a4b1768960101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1603e4120ff9c30a1c216900002f424d4920546573742fffffff0001205fa012000000001e76a914c486de584a735ec2f22da7cd9681614681f92173d83d0aa68688ac00000000","37bf9e681888b3cd204ca4e0c995aad68cd0ecb86bdf19dd0fa2e72dbabcda28","c4c5051dd741c11840ef3f11fb4f372a16bb5aac1dc66576e89e8e6835d667e0","021016dc7a6a20","00","156e9bf3ec5be367f0a829858e9ee182cc3a6531bedced491a52fcfed841c6cb","cab50aab93410cb150fd761f2067a909d35c8a9c0114578efd9590e2d381ee02","Coinbase tx has unparseable output script"],
-[1263442,"000000006f27ddfe1dd680044a34548f41bed47eba9e6f0b310da21423bc5f33","000000201c8d1a529c39a396db2db234d5ec152fa651a2872966daccbde028b400000000083f14492679151dbfaa1a825ef4c18518e780c1f91044180280a7d33f4a98ff5f45765aaddc001d38333b9a02010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff230352471300fe5f45765afe94690a000963676d696e6572343208000000000000000000ffffffff024423a804000000001976a914f2c25ac3d59f3d674b1d1d0a25c27339aaac0ba688ac0000000000000000266a24aa21a9edcb26cb3052426b9ebb4d19c819ef87c19677bbf3a7c46ef0855bd1b2abe83491012000000000000000000000000000000000000000000000000000000000000000000000000002000000000101d20978463906ba4ff5e7192494b88dd5eb0de85d900ab253af909106faa22cc5010000000004000000014777ff000000000016001446c29eabe8208a33aa1023c741fa79aa92e881ff0347304402207d7ca96134f2bcfdd6b536536fdd39ad17793632016936f777ebb32c22943fda02206014d2fb8a6aa58279797f861042ba604ebd2f8f61e5bddbd9d3be5a245047b201004b632103eeaeba7ce5dc2470221e9517fb498e8d6bd4e73b85b8be655196972eb9ccd5566754b2752103a40b74d43df244799d041f32ce1ad515a6cd99501701540e38750d883ae21d3a68ac00000000","da6984906c48525f1d800b0e9b480b5fde1a3a32ad7e4cac6a0566d86b9b01a5","7820f3d2397d77068f0f0c824c4f403db89682e5ab6e82d027cb84d7beb8a08c","06970f05e70c2ec63508cdf07609cb8434","03049063c6b4e9a028","5a369775edcb1dcde9bbc88f0897bd3fd9ab0317d2906e8c10b62641c2104c54","73cee3d4b37b689f5da7251f175ff4630f2c142d7399b407d5cac65bc593b68d","Includes witness data"]
+["Block Height,Block Hash,Block,[Prev Output Scripts for Block],Previous Basic Header,Basic Filter,Basic Header,Notes"],
+[0,"000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943","0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000",[],"0000000000000000000000000000000000000000000000000000000000000000","019dfca8","21584579b7eb08997773e5aeff3a7f932700042d0ed2a6129012b7d7ae81b750","Genesis block"],
+[2,"000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820","0100000006128e87be8b1b4dea47a7247d5528d2702c96826c7a648497e773b800000000e241352e3bec0a95a6217e10c3abb54adfa05abb12c126695595580fb92e222032e7494dffff001d00d235340101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0432e7494d010e062f503253482fffffffff0100f2052a010000002321038a7f6ef1c8ca0c588aa53fa860128077c9e6c11e6830f4d7ee4e763a56b7718fac00000000",[],"d7bdac13a59d745b1add0d2ce852f1a0442e8945fc1bf3848d3cbffd88c24fe1","0174a170","186afd11ef2b5e7e3504f2e8cbf8df28a1fd251fe53d60dff8b1467d1b386cf0",""],
+[3,"000000008b896e272758da5297bcd98fdc6d97c9b765ecec401e286dc1fdbe10","0100000020782a005255b657696ea057d5b98f34defcf75196f64f6eeac8026c0000000041ba5afc532aae03151b8aa87b65e1594f97504a768e010c98c0add79216247186e7494dffff001d058dc2b60101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0486e7494d0151062f503253482fffffffff0100f2052a01000000232103f6d9ff4c12959445ca5549c811683bf9c88e637b222dd2e0311154c4c85cf423ac00000000",[],"186afd11ef2b5e7e3504f2e8cbf8df28a1fd251fe53d60dff8b1467d1b386cf0","016cf7a0","8d63aadf5ab7257cb6d2316a57b16f517bff1c6388f124ec4c04af1212729d2a",""],
+[926485,"000000000000015d6077a411a8f5cc95caf775ccf11c54e27df75ce58d187313","0000002060bbab0edbf3ef8a49608ee326f8fd75c473b7e3982095e2d100000000000000c30134f8c9b6d2470488d7a67a888f6fa12f8692e0c3411fbfb92f0f68f67eedae03ca57ef13021acc22dc4105010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2f0315230e0004ae03ca57043e3d1e1d0c8796bf579aef0c0000000000122f4e696e6a61506f6f6c2f5345475749542fffffffff038427a112000000001976a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac0000000000000000266a24aa21a9ed5c748e121c0fe146d973a4ac26fa4a68b0549d46ee22d25f50a5e46fe1b377ee00000000000000002952534b424c4f434b3acd16772ad61a3c5f00287480b720f6035d5e54c9efc71be94bb5e3727f10909001200000000000000000000000000000000000000000000000000000000000000000000000000100000000010145310e878941a1b2bc2d33797ee4d89d95eaaf2e13488063a2aa9a74490f510a0100000023220020b6744de4f6ec63cc92f7c220cdefeeb1b1bed2b66c8e5706d80ec247d37e65a1ffffffff01002d3101000000001976a9143ebc40e411ed3c76f86711507ab952300890397288ac0400473044022001dd489a5d4e2fbd8a3ade27177f6b49296ba7695c40dbbe650ea83f106415fd02200b23a0602d8ff1bdf79dee118205fc7e9b40672bf31563e5741feb53fb86388501483045022100f88f040e90cc5dc6c6189d04718376ac19ed996bf9e4a3c29c3718d90ffd27180220761711f16c9e3a44f71aab55cbc0634907a1fa8bb635d971a9a01d368727bea10169522103b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb2103dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba621033d7c89bd9da29fa8d44db7906a9778b53121f72191184a9fee785c39180e4be153ae00000000010000000120925534261de4dcebb1ed5ab1b62bfe7a3ef968fb111dc2c910adfebc6e3bdf010000006b483045022100f50198f5ae66211a4f485190abe4dc7accdabe3bc214ebc9ea7069b97097d46e0220316a70a03014887086e335fc1b48358d46cd6bdc9af3b57c109c94af76fc915101210316cff587a01a2736d5e12e53551b18d73780b83c3bfb4fcf209c869b11b6415effffffff0220a10700000000001976a91450333046115eaa0ac9e0216565f945070e44573988ac2e7cd01a000000001976a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac00000000010000000203a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322010000006a47304402204efc3d70e4ca3049c2a425025edf22d5ca355f9ec899dbfbbeeb2268533a0f2b02204780d3739653035af4814ea52e1396d021953f948c29754edd0ee537364603dc012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff03a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322000000006a47304402202d96defdc5b4af71d6ba28c9a6042c2d5ee7bc6de565d4db84ef517445626e03022022da80320e9e489c8f41b74833dfb6a54a4eb5087cdb46eb663eef0b25caa526012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a914b7e6f7ff8658b2d1fb107e3d7be7af4742e6b1b3876f88fc00000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac0000000001000000043ffd60d3818431c495b89be84afac205d5d1ed663009291c560758bbd0a66df5010000006b483045022100f344607de9df42049688dcae8ff1db34c0c7cd25ec05516e30d2bc8f12ac9b2f022060b648f6a21745ea6d9782e17bcc4277b5808326488a1f40d41e125879723d3a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffffa5379401cce30f84731ef1ba65ce27edf2cc7ce57704507ebe8714aa16a96b92010000006a473044022020c37a63bf4d7f564c2192528709b6a38ab8271bd96898c6c2e335e5208661580220435c6f1ad4d9305d2c0a818b2feb5e45d443f2f162c0f61953a14d097fd07064012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff70e731e193235ff12c3184510895731a099112ffca4b00246c60003c40f843ce000000006a473044022053760f74c29a879e30a17b5f03a5bb057a5751a39f86fa6ecdedc36a1b7db04c022041d41c9b95f00d2d10a0373322a9025dba66c942196bc9d8adeb0e12d3024728012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff66b7a71b3e50379c8e85fc18fe3f1a408fc985f257036c34702ba205cef09f6f000000006a4730440220499bf9e2db3db6e930228d0661395f65431acae466634d098612fd80b08459ee022040e069fc9e3c60009f521cef54c38aadbd1251aee37940e6018aadb10f194d6a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a9148fc37ad460fdfbd2b44fe446f6e3071a4f64faa6878f447f0b000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac00000000",["a914feb8a29635c56d9cd913122f90678756bf23887687","76a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac"],"da49977ba1ee0d620a2c4f8f646b03cd0d230f5c6c994722e3ba884889f0be1a","09027acea61b6cc3fb33f5d52f7d088a6b2f75d234e89ca800","4cd9dd007a325199102f1fc0b7d77ca25ee3c84d46018c4353ecfcb56c0d3e7a","Duplicate pushdata 913bcc2be49cb534c20474c4dee1e9c4c317e7eb"],
+[987876,"0000000000000c00901f2049055e2a437c819d79a3d54fd63e6af796cd7b8a79","000000202694f74969fdb542090e95a56bc8aa2d646e27033850e32f1c5f000000000000f7e53676b3f12d5beb524ed617f2d25f5a93b5f4f52c1ba2678260d72712f8dd0a6dfe5740257e1a4b1768960101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1603e4120ff9c30a1c216900002f424d4920546573742fffffff0001205fa012000000001e76a914c486de584a735ec2f22da7cd9681614681f92173d83d0aa68688ac00000000",[],"e9d729b72d533c29abe5276d5cf6c152f3723f10efe000b1e0c9ca5265a8beb6","010c0b40","e6137ae5a8424c40da1e5023c16975cc97b09300b4c050e6b1c713add3836c40","Coinbase tx has unparseable output script"],
+[1263442,"000000006f27ddfe1dd680044a34548f41bed47eba9e6f0b310da21423bc5f33","000000201c8d1a529c39a396db2db234d5ec152fa651a2872966daccbde028b400000000083f14492679151dbfaa1a825ef4c18518e780c1f91044180280a7d33f4a98ff5f45765aaddc001d38333b9a02010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff230352471300fe5f45765afe94690a000963676d696e6572343208000000000000000000ffffffff024423a804000000001976a914f2c25ac3d59f3d674b1d1d0a25c27339aaac0ba688ac0000000000000000266a24aa21a9edcb26cb3052426b9ebb4d19c819ef87c19677bbf3a7c46ef0855bd1b2abe83491012000000000000000000000000000000000000000000000000000000000000000000000000002000000000101d20978463906ba4ff5e7192494b88dd5eb0de85d900ab253af909106faa22cc5010000000004000000014777ff000000000016001446c29eabe8208a33aa1023c741fa79aa92e881ff0347304402207d7ca96134f2bcfdd6b536536fdd39ad17793632016936f777ebb32c22943fda02206014d2fb8a6aa58279797f861042ba604ebd2f8f61e5bddbd9d3be5a245047b201004b632103eeaeba7ce5dc2470221e9517fb498e8d6bd4e73b85b8be655196972eb9ccd5566754b2752103a40b74d43df244799d041f32ce1ad515a6cd99501701540e38750d883ae21d3a68ac00000000",["002027a5000c7917f785d8fc6e5a55adfca8717ecb973ebb7743849ff956d896a7ed"],"a4a4d6c6034da8aa06f01fe71f1fffbd79e032006b07f6c7a2c60a66aa310c01","0385acb4f0fe889ef0","3588f34fbbc11640f9ed40b2a66a4e096215d50389691309c1dac74d4268aa81","Includes witness data"]
 ]