mirror of
https://github.com/bitcoin/bips.git
synced 2025-03-18 05:42:12 +01:00
Merge pull request #687 from Roasbeef/bip158-updates
BIP-0158: remove extended filter, remove txid from regular filter, reparameterize gcs params
This commit is contained in:
commit
91bd69d29b
@ -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 <code>2^(-P)</code> for some integer
|
||||
parameter <code>P</code>.
|
||||
1, and matches other items with probability <code>2^(-P)</code> for some
|
||||
integer parameter <code>P</code>. We also introduce parameter <code>M</code>
|
||||
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 <code>N</code> items by:
|
||||
# hashing all items to 64-bit integers in the range <code>[0, N * 2^P)</code>
|
||||
# hashing all items to 64-bit integers in the range <code>[0, N * M)</code>
|
||||
# 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 <code>[0, F)</code>, where <code>F = N *
|
||||
2^P</code>. Set membership queries against the hash outputs will have a false
|
||||
positive rate of <code>2^(-P)</code>. To avoid integer overflow, the number of
|
||||
items <code>N</code> MUST be <2^32 and <code>P</code> MUST be <=32.
|
||||
M</code>. Customarily, <code>M</code> is set to <code>2^P</code>. However, if
|
||||
one is able to select both Parameters independently, then more optimal values
|
||||
can be
|
||||
selected<ref>https://gist.github.com/sipa/576d5f09c3b86c3b1b75598d799fc845</ref>.
|
||||
Set membership queries against the hash outputs will have a false positive rate
|
||||
of <code>2^(-P)</code>. To avoid integer overflow, the
|
||||
number of items <code>N</code> MUST be <2^32 and <code>M</code> MUST be <2^32.
|
||||
|
||||
The items are first passed through the pseudorandom function ''SipHash'', which
|
||||
takes a 128-bit key <code>k</code> 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.
|
||||
|
||||
<pre>
|
||||
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.
|
||||
|
||||
<pre>
|
||||
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)
|
||||
@ -258,49 +265,54 @@ against the decompressed GCS contents. See
|
||||
|
||||
=== Block Filters ===
|
||||
|
||||
This BIP defines two initial filter types:
|
||||
This BIP defines one initial filter type:
|
||||
* Basic (<code>0x00</code>)
|
||||
* Extended (<code>0x01</code>)
|
||||
* <code>M = 784931</code>
|
||||
* <code>P = 19</code>
|
||||
|
||||
==== 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
|
||||
* The <code>txid</code> of the transaction itself
|
||||
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 <code>OP_RETURN</code> output
|
||||
scripts.
|
||||
|
||||
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
|
||||
Any "nil" items MUST NOT be included into the final set of filter elements.
|
||||
|
||||
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.
|
||||
We exclude all <code>OP_RETURN</code> 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 <code>OP_RETURN</code> output in the coinbase
|
||||
transaction similar to the current witness commitment
|
||||
<ref>https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki</rev>. By
|
||||
excluding all <code>OP_RETURN</code> outputs we avoid a circular dependency
|
||||
between the commitment, and the item being committed to.
|
||||
|
||||
==== 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 <code>P</code> MUST be set to <code>20</code>. 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 <code>P</code> MUST be set to <code>19</code>, and the parameter
|
||||
<code>M</code> MUST be set to <code>784931</code>. Analysis has shown that if
|
||||
one is able to select <code>P</code> and <code>M</code> independently, then
|
||||
setting <code>M=1.497137 * 2^P</code> is close to optimal
|
||||
<ref>https://gist.github.com/sipa/576d5f09c3b86c3b1b75598d799fc845</ref>.
|
||||
|
||||
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 <code>k</code> 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 <code>N</code> 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:
|
||||
* <code>N</code>, encoded as a CompactSize
|
||||
includes it as a prefix, written as a <code>CompactSize</code>. Thus, the
|
||||
complete serialization of a filter is:
|
||||
* <code>N</code>, encoded as a <code>CompactSize</code>
|
||||
* The bytes of the compressed filter itself
|
||||
|
||||
==== Signaling ====
|
||||
@ -323,7 +335,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 <code>btcd</code>.
|
||||
|
||||
We would also like to thank Dave Collins, JJ Jeffrey, and Eric Lombrozo for
|
||||
@ -375,8 +388,8 @@ easier to understand.
|
||||
=== Golomb-Coded Set Multi-Match ===
|
||||
|
||||
<pre>
|
||||
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 = []
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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"]
|
||||
]
|
Loading…
x
Reference in New Issue
Block a user