mirror of
https://github.com/bitcoin/bips.git
synced 2025-05-12 12:03:29 +00: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
|
with the block (eg. addresses sent to, outpoints spent, etc.). A set of such
|
||||||
data objects is compressed into a probabilistic structure called a
|
data objects is compressed into a probabilistic structure called a
|
||||||
''Golomb-coded set'' (GCS), which matches all items in the set with probability
|
''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
|
1, and matches other items with probability <code>2^(-P)</code> for some
|
||||||
parameter <code>P</code>.
|
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:
|
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
|
# sorting the hashed values in ascending order
|
||||||
# computing the differences between each value and the previous one
|
# computing the differences between each value and the previous one
|
||||||
# writing the differences sequentially, compressed with Golomb-Rice coding
|
# 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
|
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 *
|
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
|
M</code>. Customarily, <code>M</code> is set to <code>2^P</code>. However, if
|
||||||
positive rate of <code>2^(-P)</code>. To avoid integer overflow, the number of
|
one is able to select both Parameters independently, then more optimal values
|
||||||
items <code>N</code> MUST be <2^32 and <code>P</code> MUST be <=32.
|
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
|
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
|
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:
|
hash_to_range(item: []byte, F: uint64, k: [16]byte) -> uint64:
|
||||||
return (siphash(k, item) * F) >> 64
|
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 N = len(raw_items)
|
||||||
let F = N << P
|
let F = N * M
|
||||||
|
|
||||||
let set_items = []
|
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.
|
nearest byte boundary and serialized to the output byte vector.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
construct_gcs(L: [][]byte, P: uint, k: [16]byte) -> []byte:
|
construct_gcs(L: [][]byte, P: uint, k: [16]byte, M: uint) -> []byte:
|
||||||
let set_items = hashed_set_construct(L, P, k)
|
let set_items = hashed_set_construct(L, k, M)
|
||||||
|
|
||||||
set_items.sort()
|
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.
|
decompressed set be held in memory at once.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
gcs_match(key: [16]byte, compressed_set: []byte, target: []byte, P: uint, N: uint) -> bool:
|
gcs_match(key: [16]byte, compressed_set: []byte, target: []byte, P: uint, N: uint, M: uint) -> bool:
|
||||||
let F = N << P
|
let F = N * M
|
||||||
let target_hash = hash_to_range(target, F, k)
|
let target_hash = hash_to_range(target, F, k)
|
||||||
|
|
||||||
stream = new_bit_stream(compressed_set)
|
stream = new_bit_stream(compressed_set)
|
||||||
@ -258,49 +265,54 @@ against the decompressed GCS contents. See
|
|||||||
|
|
||||||
=== Block Filters ===
|
=== Block Filters ===
|
||||||
|
|
||||||
This BIP defines two initial filter types:
|
This BIP defines one initial filter type:
|
||||||
* Basic (<code>0x00</code>)
|
* Basic (<code>0x00</code>)
|
||||||
* Extended (<code>0x01</code>)
|
* <code>M = 784931</code>
|
||||||
|
* <code>P = 19</code>
|
||||||
|
|
||||||
==== Contents ====
|
==== Contents ====
|
||||||
|
|
||||||
The basic filter is designed to contain everything that a light client needs to
|
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
|
sync a regular Bitcoin wallet. A basic filter MUST contain exactly the
|
||||||
items for each transaction in a block:
|
following items for each transaction in a block:
|
||||||
* The outpoint of each input, except for the coinbase transaction
|
* The previous output script (the script being spent) for each input, except
|
||||||
* The scriptPubKey of each output
|
for the coinbase transaction.
|
||||||
* The <code>txid</code> of the transaction itself
|
* 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
|
Any "nil" items MUST NOT be included into the final set of filter elements.
|
||||||
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
|
We exclude all <code>OP_RETURN</code> outputs in order to allow filters to
|
||||||
extract data pushes from them. If necessary, future filter types may be designed
|
easily be committed to in the future via a soft-fork. A likely area for future
|
||||||
to do so.
|
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 ====
|
==== Construction ====
|
||||||
|
|
||||||
Both the basic and extended filter types are constructed as Golomb-coded sets
|
The basic type is constructed as Golomb-coded sets with the following
|
||||||
with the following parameters.
|
parameters.
|
||||||
|
|
||||||
The parameter <code>P</code> MUST be set to <code>20</code>. This value was
|
The parameter <code>P</code> MUST be set to <code>19</code>, and the parameter
|
||||||
chosen as simulations show that it minimizes the bandwidth utilized, considering
|
<code>M</code> MUST be set to <code>784931</code>. Analysis has shown that if
|
||||||
both the expected number of blocks downloaded due to false positives and the
|
one is able to select <code>P</code> and <code>M</code> independently, then
|
||||||
size of the filters themselves. The code along with a demo used for the
|
setting <code>M=1.497137 * 2^P</code> is close to optimal
|
||||||
parameter tuning can be found
|
<ref>https://gist.github.com/sipa/576d5f09c3b86c3b1b75598d799fc845</ref>.
|
||||||
[https://github.com/Roasbeef/bips/blob/83b83c78e189be898573e0bfe936dd0c9b99ecb9/gcs_light_client/gentestvectors.go here].
|
|
||||||
|
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 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
|
the block for which the filter is constructed. This ensures the key is
|
||||||
deterministic while still varying from block to block.
|
deterministic while still varying from block to block.
|
||||||
|
|
||||||
Since the value <code>N</code> is required to decode a GCS, a serialized GCS
|
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
|
includes it as a prefix, written as a <code>CompactSize</code>. Thus, the
|
||||||
serialization of a filter is:
|
complete serialization of a filter is:
|
||||||
* <code>N</code>, encoded as a CompactSize
|
* <code>N</code>, encoded as a <code>CompactSize</code>
|
||||||
* The bytes of the compressed filter itself
|
* The bytes of the compressed filter itself
|
||||||
|
|
||||||
==== Signaling ====
|
==== 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
|
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
|
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>.
|
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
|
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 ===
|
=== Golomb-Coded Set Multi-Match ===
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
gcs_match_any(key: [16]byte, compressed_set: []byte, targets: [][]byte, P: uint, N: uint) -> bool:
|
gcs_match_any(key: [16]byte, compressed_set: []byte, targets: [][]byte, P: uint, N: uint, M: uint) -> bool:
|
||||||
let F = N << P
|
let F = N * M
|
||||||
|
|
||||||
// Map targets to the same range as the set hashes.
|
// Map targets to the same range as the set hashes.
|
||||||
let target_hashes = []
|
let target_hashes = []
|
||||||
|
@ -15,13 +15,15 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/blockchain"
|
||||||
"github.com/roasbeef/btcd/rpcclient"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/roasbeef/btcd/wire"
|
"github.com/btcsuite/btcd/rpcclient"
|
||||||
"github.com/roasbeef/btcutil/gcs"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/roasbeef/btcutil/gcs/builder"
|
"github.com/btcsuite/btcutil"
|
||||||
|
"github.com/btcsuite/btcutil/gcs/builder"
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -29,13 +31,19 @@ var (
|
|||||||
// vectors. Any new entries must be added in sorted order.
|
// vectors. Any new entries must be added in sorted order.
|
||||||
testBlockHeights = []testBlockCase{
|
testBlockHeights = []testBlockCase{
|
||||||
{0, "Genesis block"},
|
{0, "Genesis block"},
|
||||||
{1, "Extended filter is empty"},
|
|
||||||
{2, ""},
|
{2, ""},
|
||||||
{3, ""},
|
{3, ""},
|
||||||
{926485, "Duplicate pushdata 913bcc2be49cb534c20474c4dee1e9c4c317e7eb"},
|
{926485, "Duplicate pushdata 913bcc2be49cb534c20474c4dee1e9c4c317e7eb"},
|
||||||
{987876, "Coinbase tx has unparseable output script"},
|
{987876, "Coinbase tx has unparseable output script"},
|
||||||
{1263442, "Includes witness data"},
|
{1263442, "Includes witness data"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultBtcdDir = btcutil.AppDataDir("btcd", false)
|
||||||
|
defaultBtcdRPCCertFile = filepath.Join(defaultBtcdDir, "rpc.cert")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
fp = 19
|
||||||
)
|
)
|
||||||
|
|
||||||
type testBlockCase struct {
|
type testBlockCase struct {
|
||||||
@ -86,41 +94,78 @@ func (w *JSONTestWriter) Close() error {
|
|||||||
return err
|
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() {
|
func main() {
|
||||||
err := os.Mkdir("gcstestvectors", os.ModeDir|0755)
|
var (
|
||||||
if err != nil { // Don't overwrite existing output if any
|
writerFile *JSONTestWriter
|
||||||
fmt.Println("Couldn't create directory: ", err)
|
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
|
return
|
||||||
}
|
}
|
||||||
files := make([]*JSONTestWriter, 33)
|
defer file.Close()
|
||||||
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()
|
|
||||||
|
|
||||||
writer := &JSONTestWriter{writer: file}
|
writer := &JSONTestWriter{
|
||||||
defer writer.Close()
|
writer: file,
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
cert, err := ioutil.ReadFile(
|
defer writer.Close()
|
||||||
path.Join(os.Getenv("HOME"), "/.btcd/rpc.cert"))
|
|
||||||
|
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 {
|
if err != nil {
|
||||||
fmt.Println("Couldn't read RPC cert: ", err.Error())
|
fmt.Println("Couldn't read RPC cert: ", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
conf := rpcclient.ConnConfig{
|
conf := rpcclient.ConnConfig{
|
||||||
Host: "127.0.0.1:18334",
|
Host: "127.0.0.1:18334",
|
||||||
Endpoint: "ws",
|
Endpoint: "ws",
|
||||||
@ -134,19 +179,20 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var testBlockIndex int = 0
|
var testBlockIndex int
|
||||||
for height := 0; testBlockIndex < len(testBlockHeights); height++ {
|
for height := 0; testBlockIndex < len(testBlockHeights); height++ {
|
||||||
fmt.Printf("Height: %d\n", height)
|
|
||||||
blockHash, err := client.GetBlockHash(int64(height))
|
blockHash, err := client.GetBlockHash(int64(height))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Couldn't get block hash: ", err.Error())
|
fmt.Println("Couldn't get block hash: ", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
block, err := client.GetBlock(blockHash)
|
block, err := client.GetBlock(blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Couldn't get block hash: ", err.Error())
|
fmt.Println("Couldn't get block hash: ", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var blockBuf bytes.Buffer
|
var blockBuf bytes.Buffer
|
||||||
err = block.Serialize(&blockBuf)
|
err = block.Serialize(&blockBuf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -154,208 +200,98 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
blockBytes := blockBuf.Bytes()
|
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 {
|
if err != nil {
|
||||||
fmt.Println("Error generating basic filter: ", err.Error())
|
fmt.Println("Couldn't get NBytes(): ", err)
|
||||||
return
|
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 {
|
prevScriptStrings := make([]string, len(prevOutputScripts))
|
||||||
var bfBytes []byte
|
for i, prevScript := range prevOutputScripts {
|
||||||
var efBytes []byte
|
prevScriptStrings[i] = hex.EncodeToString(prevScript)
|
||||||
bfBytes, err = basicFilter.NBytes()
|
}
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Couldn't get NBytes(): ", err)
|
row := []interface{}{
|
||||||
return
|
height,
|
||||||
}
|
blockHash.String(),
|
||||||
efBytes, err = extFilter.NBytes()
|
hex.EncodeToString(blockBytes),
|
||||||
if err != nil {
|
prevScriptStrings,
|
||||||
fmt.Println("Couldn't get NBytes(): ", err)
|
prevBasicHeader.String(),
|
||||||
return
|
hex.EncodeToString(bfBytes),
|
||||||
}
|
basicHeader.String(),
|
||||||
row := []interface{}{
|
testBlockHeights[testBlockIndex].comment,
|
||||||
height,
|
}
|
||||||
blockHash.String(),
|
err = writerFile.WriteTestCase(row)
|
||||||
hex.EncodeToString(blockBytes),
|
if err != nil {
|
||||||
prevBasicHeaders[i].String(),
|
fmt.Println("Error writing test case to output: ", err.Error())
|
||||||
prevExtHeaders[i].String(),
|
return
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
prevBasicHeaders[i] = basicHeader
|
|
||||||
prevExtHeaders[i] = extHeader
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prevBasicHeader = basicHeader
|
||||||
|
|
||||||
if uint32(height) == testBlockHeights[testBlockIndex].height {
|
if uint32(height) == testBlockHeights[testBlockIndex].height {
|
||||||
testBlockIndex++
|
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"],
|
["Block Height,Block Hash,Block,[Prev Output Scripts for Block],Previous Basic Header,Basic Filter,Basic Header,Notes"],
|
||||||
[0,"000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943","0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000","0000000000000000000000000000000000000000000000000000000000000000","0000000000000000000000000000000000000000000000000000000000000000","0285c7cdbe33a0","00","c0589c7f567cffaf7bc0c9f6ad61710b78d3c1afef5d65a2a08e8a753173aa54","753e0d1c28585269ab770b166ca2cd1b32f9bc918750547941ed4849d5a80ba8","Genesis block"],
|
[0,"000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943","0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000",[],"0000000000000000000000000000000000000000000000000000000000000000","019dfca8","21584579b7eb08997773e5aeff3a7f932700042d0ed2a6129012b7d7ae81b750","Genesis block"],
|
||||||
[1,"00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206","0100000043497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000bac8b0fa927c0ac8234287e33c5f74d38d354820e24756ad709d7038fc5f31f020e7494dffff001d03e4b6720101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0420e7494d017f062f503253482fffffffff0100f2052a010000002321021aeaf2f8638a129a3156fbe7e5ef635226b0bafd495ff03afe2c843d7e3a4b51ac00000000","c0589c7f567cffaf7bc0c9f6ad61710b78d3c1afef5d65a2a08e8a753173aa54","753e0d1c28585269ab770b166ca2cd1b32f9bc918750547941ed4849d5a80ba8","026929d09bee00","00","81e4f3e934488be62758f0b88037aa558262da3190ca018329997a319a0f8b5b","31b674ab635e074717329dabdb25d3cb0e14cb2526000cc2cedac7b5f2595110","Extended filter is empty"],
|
[2,"000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820","0100000006128e87be8b1b4dea47a7247d5528d2702c96826c7a648497e773b800000000e241352e3bec0a95a6217e10c3abb54adfa05abb12c126695595580fb92e222032e7494dffff001d00d235340101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0432e7494d010e062f503253482fffffffff0100f2052a010000002321038a7f6ef1c8ca0c588aa53fa860128077c9e6c11e6830f4d7ee4e763a56b7718fac00000000",[],"d7bdac13a59d745b1add0d2ce852f1a0442e8945fc1bf3848d3cbffd88c24fe1","0174a170","186afd11ef2b5e7e3504f2e8cbf8df28a1fd251fe53d60dff8b1467d1b386cf0",""],
|
||||||
[2,"000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820","0100000006128e87be8b1b4dea47a7247d5528d2702c96826c7a648497e773b800000000e241352e3bec0a95a6217e10c3abb54adfa05abb12c126695595580fb92e222032e7494dffff001d00d235340101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0432e7494d010e062f503253482fffffffff0100f2052a010000002321038a7f6ef1c8ca0c588aa53fa860128077c9e6c11e6830f4d7ee4e763a56b7718fac00000000","81e4f3e934488be62758f0b88037aa558262da3190ca018329997a319a0f8b5b","31b674ab635e074717329dabdb25d3cb0e14cb2526000cc2cedac7b5f2595110","0278fc41168ec0","00","ec48f9f8a625bd8adb2d2684867a05baafebf935553e0b78b386da98179dcf49","0dd53b407c3f242f1838e39e2fc0cfb89cdca27de07ec230568a08d1872f9e01",""],
|
[3,"000000008b896e272758da5297bcd98fdc6d97c9b765ecec401e286dc1fdbe10","0100000020782a005255b657696ea057d5b98f34defcf75196f64f6eeac8026c0000000041ba5afc532aae03151b8aa87b65e1594f97504a768e010c98c0add79216247186e7494dffff001d058dc2b60101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0486e7494d0151062f503253482fffffffff0100f2052a01000000232103f6d9ff4c12959445ca5549c811683bf9c88e637b222dd2e0311154c4c85cf423ac00000000",[],"186afd11ef2b5e7e3504f2e8cbf8df28a1fd251fe53d60dff8b1467d1b386cf0","016cf7a0","8d63aadf5ab7257cb6d2316a57b16f517bff1c6388f124ec4c04af1212729d2a",""],
|
||||||
[3,"000000008b896e272758da5297bcd98fdc6d97c9b765ecec401e286dc1fdbe10","0100000020782a005255b657696ea057d5b98f34defcf75196f64f6eeac8026c0000000041ba5afc532aae03151b8aa87b65e1594f97504a768e010c98c0add79216247186e7494dffff001d058dc2b60101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0486e7494d0151062f503253482fffffffff0100f2052a01000000232103f6d9ff4c12959445ca5549c811683bf9c88e637b222dd2e0311154c4c85cf423ac00000000","ec48f9f8a625bd8adb2d2684867a05baafebf935553e0b78b386da98179dcf49","0dd53b407c3f242f1838e39e2fc0cfb89cdca27de07ec230568a08d1872f9e01","022ce4b3256540","00","060b7e1be150cc3cabd9e96b00af217132b387f31fd2bd9adfb0c7f5a09a3356","5b2a59bc476d52b45de1398ee34ed10d0cccd2a4b19ba502a456c7356b35be0d",""],
|
[926485,"000000000000015d6077a411a8f5cc95caf775ccf11c54e27df75ce58d187313","0000002060bbab0edbf3ef8a49608ee326f8fd75c473b7e3982095e2d100000000000000c30134f8c9b6d2470488d7a67a888f6fa12f8692e0c3411fbfb92f0f68f67eedae03ca57ef13021acc22dc4105010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2f0315230e0004ae03ca57043e3d1e1d0c8796bf579aef0c0000000000122f4e696e6a61506f6f6c2f5345475749542fffffffff038427a112000000001976a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac0000000000000000266a24aa21a9ed5c748e121c0fe146d973a4ac26fa4a68b0549d46ee22d25f50a5e46fe1b377ee00000000000000002952534b424c4f434b3acd16772ad61a3c5f00287480b720f6035d5e54c9efc71be94bb5e3727f10909001200000000000000000000000000000000000000000000000000000000000000000000000000100000000010145310e878941a1b2bc2d33797ee4d89d95eaaf2e13488063a2aa9a74490f510a0100000023220020b6744de4f6ec63cc92f7c220cdefeeb1b1bed2b66c8e5706d80ec247d37e65a1ffffffff01002d3101000000001976a9143ebc40e411ed3c76f86711507ab952300890397288ac0400473044022001dd489a5d4e2fbd8a3ade27177f6b49296ba7695c40dbbe650ea83f106415fd02200b23a0602d8ff1bdf79dee118205fc7e9b40672bf31563e5741feb53fb86388501483045022100f88f040e90cc5dc6c6189d04718376ac19ed996bf9e4a3c29c3718d90ffd27180220761711f16c9e3a44f71aab55cbc0634907a1fa8bb635d971a9a01d368727bea10169522103b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb2103dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba621033d7c89bd9da29fa8d44db7906a9778b53121f72191184a9fee785c39180e4be153ae00000000010000000120925534261de4dcebb1ed5ab1b62bfe7a3ef968fb111dc2c910adfebc6e3bdf010000006b483045022100f50198f5ae66211a4f485190abe4dc7accdabe3bc214ebc9ea7069b97097d46e0220316a70a03014887086e335fc1b48358d46cd6bdc9af3b57c109c94af76fc915101210316cff587a01a2736d5e12e53551b18d73780b83c3bfb4fcf209c869b11b6415effffffff0220a10700000000001976a91450333046115eaa0ac9e0216565f945070e44573988ac2e7cd01a000000001976a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac00000000010000000203a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322010000006a47304402204efc3d70e4ca3049c2a425025edf22d5ca355f9ec899dbfbbeeb2268533a0f2b02204780d3739653035af4814ea52e1396d021953f948c29754edd0ee537364603dc012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff03a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322000000006a47304402202d96defdc5b4af71d6ba28c9a6042c2d5ee7bc6de565d4db84ef517445626e03022022da80320e9e489c8f41b74833dfb6a54a4eb5087cdb46eb663eef0b25caa526012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a914b7e6f7ff8658b2d1fb107e3d7be7af4742e6b1b3876f88fc00000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac0000000001000000043ffd60d3818431c495b89be84afac205d5d1ed663009291c560758bbd0a66df5010000006b483045022100f344607de9df42049688dcae8ff1db34c0c7cd25ec05516e30d2bc8f12ac9b2f022060b648f6a21745ea6d9782e17bcc4277b5808326488a1f40d41e125879723d3a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffffa5379401cce30f84731ef1ba65ce27edf2cc7ce57704507ebe8714aa16a96b92010000006a473044022020c37a63bf4d7f564c2192528709b6a38ab8271bd96898c6c2e335e5208661580220435c6f1ad4d9305d2c0a818b2feb5e45d443f2f162c0f61953a14d097fd07064012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff70e731e193235ff12c3184510895731a099112ffca4b00246c60003c40f843ce000000006a473044022053760f74c29a879e30a17b5f03a5bb057a5751a39f86fa6ecdedc36a1b7db04c022041d41c9b95f00d2d10a0373322a9025dba66c942196bc9d8adeb0e12d3024728012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff66b7a71b3e50379c8e85fc18fe3f1a408fc985f257036c34702ba205cef09f6f000000006a4730440220499bf9e2db3db6e930228d0661395f65431acae466634d098612fd80b08459ee022040e069fc9e3c60009f521cef54c38aadbd1251aee37940e6018aadb10f194d6a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a9148fc37ad460fdfbd2b44fe446f6e3071a4f64faa6878f447f0b000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac00000000",["a914feb8a29635c56d9cd913122f90678756bf23887687","76a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac"],"da49977ba1ee0d620a2c4f8f646b03cd0d230f5c6c994722e3ba884889f0be1a","09027acea61b6cc3fb33f5d52f7d088a6b2f75d234e89ca800","4cd9dd007a325199102f1fc0b7d77ca25ee3c84d46018c4353ecfcb56c0d3e7a","Duplicate pushdata 913bcc2be49cb534c20474c4dee1e9c4c317e7eb"],
|
||||||
[926485,"000000000000015d6077a411a8f5cc95caf775ccf11c54e27df75ce58d187313","0000002060bbab0edbf3ef8a49608ee326f8fd75c473b7e3982095e2d100000000000000c30134f8c9b6d2470488d7a67a888f6fa12f8692e0c3411fbfb92f0f68f67eedae03ca57ef13021acc22dc4105010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2f0315230e0004ae03ca57043e3d1e1d0c8796bf579aef0c0000000000122f4e696e6a61506f6f6c2f5345475749542fffffffff038427a112000000001976a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac0000000000000000266a24aa21a9ed5c748e121c0fe146d973a4ac26fa4a68b0549d46ee22d25f50a5e46fe1b377ee00000000000000002952534b424c4f434b3acd16772ad61a3c5f00287480b720f6035d5e54c9efc71be94bb5e3727f10909001200000000000000000000000000000000000000000000000000000000000000000000000000100000000010145310e878941a1b2bc2d33797ee4d89d95eaaf2e13488063a2aa9a74490f510a0100000023220020b6744de4f6ec63cc92f7c220cdefeeb1b1bed2b66c8e5706d80ec247d37e65a1ffffffff01002d3101000000001976a9143ebc40e411ed3c76f86711507ab952300890397288ac0400473044022001dd489a5d4e2fbd8a3ade27177f6b49296ba7695c40dbbe650ea83f106415fd02200b23a0602d8ff1bdf79dee118205fc7e9b40672bf31563e5741feb53fb86388501483045022100f88f040e90cc5dc6c6189d04718376ac19ed996bf9e4a3c29c3718d90ffd27180220761711f16c9e3a44f71aab55cbc0634907a1fa8bb635d971a9a01d368727bea10169522103b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb2103dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba621033d7c89bd9da29fa8d44db7906a9778b53121f72191184a9fee785c39180e4be153ae00000000010000000120925534261de4dcebb1ed5ab1b62bfe7a3ef968fb111dc2c910adfebc6e3bdf010000006b483045022100f50198f5ae66211a4f485190abe4dc7accdabe3bc214ebc9ea7069b97097d46e0220316a70a03014887086e335fc1b48358d46cd6bdc9af3b57c109c94af76fc915101210316cff587a01a2736d5e12e53551b18d73780b83c3bfb4fcf209c869b11b6415effffffff0220a10700000000001976a91450333046115eaa0ac9e0216565f945070e44573988ac2e7cd01a000000001976a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac00000000010000000203a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322010000006a47304402204efc3d70e4ca3049c2a425025edf22d5ca355f9ec899dbfbbeeb2268533a0f2b02204780d3739653035af4814ea52e1396d021953f948c29754edd0ee537364603dc012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff03a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322000000006a47304402202d96defdc5b4af71d6ba28c9a6042c2d5ee7bc6de565d4db84ef517445626e03022022da80320e9e489c8f41b74833dfb6a54a4eb5087cdb46eb663eef0b25caa526012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a914b7e6f7ff8658b2d1fb107e3d7be7af4742e6b1b3876f88fc00000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac0000000001000000043ffd60d3818431c495b89be84afac205d5d1ed663009291c560758bbd0a66df5010000006b483045022100f344607de9df42049688dcae8ff1db34c0c7cd25ec05516e30d2bc8f12ac9b2f022060b648f6a21745ea6d9782e17bcc4277b5808326488a1f40d41e125879723d3a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffffa5379401cce30f84731ef1ba65ce27edf2cc7ce57704507ebe8714aa16a96b92010000006a473044022020c37a63bf4d7f564c2192528709b6a38ab8271bd96898c6c2e335e5208661580220435c6f1ad4d9305d2c0a818b2feb5e45d443f2f162c0f61953a14d097fd07064012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff70e731e193235ff12c3184510895731a099112ffca4b00246c60003c40f843ce000000006a473044022053760f74c29a879e30a17b5f03a5bb057a5751a39f86fa6ecdedc36a1b7db04c022041d41c9b95f00d2d10a0373322a9025dba66c942196bc9d8adeb0e12d3024728012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff66b7a71b3e50379c8e85fc18fe3f1a408fc985f257036c34702ba205cef09f6f000000006a4730440220499bf9e2db3db6e930228d0661395f65431acae466634d098612fd80b08459ee022040e069fc9e3c60009f521cef54c38aadbd1251aee37940e6018aadb10f194d6a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a9148fc37ad460fdfbd2b44fe446f6e3071a4f64faa6878f447f0b000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac00000000","5c169b91332e661a4ebf684d6dffcf64aa139e1e736096ce462609b2e0783c55","5f3cd111e10ed28b7c2bc138fe4bc62e5df1cdc292c010b78c84e5111a5daaa6","16040c63f7ddea293f2d9c13690c0ba7b2910228c38b0fe542ce525021e49b598ada05f83bb9c37c711a02b1850265991c34c4fea6261d22a4b84596c0","0e6651beff00ee7a3be424a90e98450727b304558434c8d53781d469131ad21d399376c151ca28","c8f83ffbc9781c2bd4b7e3c055e888b00d3e2fea14e93b3c4f3adee86b063374","61f8ee615258981089be9f98337f53f44b14cc1532aa469a348fdee547117b80","Duplicate pushdata 913bcc2be49cb534c20474c4dee1e9c4c317e7eb"],
|
[987876,"0000000000000c00901f2049055e2a437c819d79a3d54fd63e6af796cd7b8a79","000000202694f74969fdb542090e95a56bc8aa2d646e27033850e32f1c5f000000000000f7e53676b3f12d5beb524ed617f2d25f5a93b5f4f52c1ba2678260d72712f8dd0a6dfe5740257e1a4b1768960101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1603e4120ff9c30a1c216900002f424d4920546573742fffffff0001205fa012000000001e76a914c486de584a735ec2f22da7cd9681614681f92173d83d0aa68688ac00000000",[],"e9d729b72d533c29abe5276d5cf6c152f3723f10efe000b1e0c9ca5265a8beb6","010c0b40","e6137ae5a8424c40da1e5023c16975cc97b09300b4c050e6b1c713add3836c40","Coinbase tx has unparseable output script"],
|
||||||
[987876,"0000000000000c00901f2049055e2a437c819d79a3d54fd63e6af796cd7b8a79","000000202694f74969fdb542090e95a56bc8aa2d646e27033850e32f1c5f000000000000f7e53676b3f12d5beb524ed617f2d25f5a93b5f4f52c1ba2678260d72712f8dd0a6dfe5740257e1a4b1768960101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1603e4120ff9c30a1c216900002f424d4920546573742fffffff0001205fa012000000001e76a914c486de584a735ec2f22da7cd9681614681f92173d83d0aa68688ac00000000","37bf9e681888b3cd204ca4e0c995aad68cd0ecb86bdf19dd0fa2e72dbabcda28","c4c5051dd741c11840ef3f11fb4f372a16bb5aac1dc66576e89e8e6835d667e0","021016dc7a6a20","00","156e9bf3ec5be367f0a829858e9ee182cc3a6531bedced491a52fcfed841c6cb","cab50aab93410cb150fd761f2067a909d35c8a9c0114578efd9590e2d381ee02","Coinbase tx has unparseable output script"],
|
[1263442,"000000006f27ddfe1dd680044a34548f41bed47eba9e6f0b310da21423bc5f33","000000201c8d1a529c39a396db2db234d5ec152fa651a2872966daccbde028b400000000083f14492679151dbfaa1a825ef4c18518e780c1f91044180280a7d33f4a98ff5f45765aaddc001d38333b9a02010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff230352471300fe5f45765afe94690a000963676d696e6572343208000000000000000000ffffffff024423a804000000001976a914f2c25ac3d59f3d674b1d1d0a25c27339aaac0ba688ac0000000000000000266a24aa21a9edcb26cb3052426b9ebb4d19c819ef87c19677bbf3a7c46ef0855bd1b2abe83491012000000000000000000000000000000000000000000000000000000000000000000000000002000000000101d20978463906ba4ff5e7192494b88dd5eb0de85d900ab253af909106faa22cc5010000000004000000014777ff000000000016001446c29eabe8208a33aa1023c741fa79aa92e881ff0347304402207d7ca96134f2bcfdd6b536536fdd39ad17793632016936f777ebb32c22943fda02206014d2fb8a6aa58279797f861042ba604ebd2f8f61e5bddbd9d3be5a245047b201004b632103eeaeba7ce5dc2470221e9517fb498e8d6bd4e73b85b8be655196972eb9ccd5566754b2752103a40b74d43df244799d041f32ce1ad515a6cd99501701540e38750d883ae21d3a68ac00000000",["002027a5000c7917f785d8fc6e5a55adfca8717ecb973ebb7743849ff956d896a7ed"],"a4a4d6c6034da8aa06f01fe71f1fffbd79e032006b07f6c7a2c60a66aa310c01","0385acb4f0fe889ef0","3588f34fbbc11640f9ed40b2a66a4e096215d50389691309c1dac74d4268aa81","Includes witness data"]
|
||||||
[1263442,"000000006f27ddfe1dd680044a34548f41bed47eba9e6f0b310da21423bc5f33","000000201c8d1a529c39a396db2db234d5ec152fa651a2872966daccbde028b400000000083f14492679151dbfaa1a825ef4c18518e780c1f91044180280a7d33f4a98ff5f45765aaddc001d38333b9a02010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff230352471300fe5f45765afe94690a000963676d696e6572343208000000000000000000ffffffff024423a804000000001976a914f2c25ac3d59f3d674b1d1d0a25c27339aaac0ba688ac0000000000000000266a24aa21a9edcb26cb3052426b9ebb4d19c819ef87c19677bbf3a7c46ef0855bd1b2abe83491012000000000000000000000000000000000000000000000000000000000000000000000000002000000000101d20978463906ba4ff5e7192494b88dd5eb0de85d900ab253af909106faa22cc5010000000004000000014777ff000000000016001446c29eabe8208a33aa1023c741fa79aa92e881ff0347304402207d7ca96134f2bcfdd6b536536fdd39ad17793632016936f777ebb32c22943fda02206014d2fb8a6aa58279797f861042ba604ebd2f8f61e5bddbd9d3be5a245047b201004b632103eeaeba7ce5dc2470221e9517fb498e8d6bd4e73b85b8be655196972eb9ccd5566754b2752103a40b74d43df244799d041f32ce1ad515a6cd99501701540e38750d883ae21d3a68ac00000000","da6984906c48525f1d800b0e9b480b5fde1a3a32ad7e4cac6a0566d86b9b01a5","7820f3d2397d77068f0f0c824c4f403db89682e5ab6e82d027cb84d7beb8a08c","06970f05e70c2ec63508cdf07609cb8434","03049063c6b4e9a028","5a369775edcb1dcde9bbc88f0897bd3fd9ab0317d2906e8c10b62641c2104c54","73cee3d4b37b689f5da7251f175ff4630f2c142d7399b407d5cac65bc593b68d","Includes witness data"]
|
|
||||||
]
|
]
|
Loading…
x
Reference in New Issue
Block a user