summaryrefslogtreecommitdiff
path: root/bip-0158/gentestvectors.go
diff options
context:
space:
mode:
Diffstat (limited to 'bip-0158/gentestvectors.go')
-rw-r--r--bip-0158/gentestvectors.go380
1 files changed, 158 insertions, 222 deletions
diff --git a/bip-0158/gentestvectors.go b/bip-0158/gentestvectors.go
index deaf2c7..472f8c1 100644
--- a/bip-0158/gentestvectors.go
+++ b/bip-0158/gentestvectors.go
@@ -15,13 +15,15 @@ import (
"io"
"io/ioutil"
"os"
- "path"
+ "path/filepath"
- "github.com/roasbeef/btcd/chaincfg/chainhash"
- "github.com/roasbeef/btcd/rpcclient"
- "github.com/roasbeef/btcd/wire"
- "github.com/roasbeef/btcutil/gcs"
- "github.com/roasbeef/btcutil/gcs/builder"
+ "github.com/btcsuite/btcd/blockchain"
+ "github.com/btcsuite/btcd/chaincfg/chainhash"
+ "github.com/btcsuite/btcd/rpcclient"
+ "github.com/btcsuite/btcd/wire"
+ "github.com/btcsuite/btcutil"
+ "github.com/btcsuite/btcutil/gcs/builder"
+ "github.com/davecgh/go-spew/spew"
)
var (
@@ -29,13 +31,19 @@ var (
// vectors. Any new entries must be added in sorted order.
testBlockHeights = []testBlockCase{
{0, "Genesis block"},
- {1, "Extended filter is empty"},
{2, ""},
{3, ""},
{926485, "Duplicate pushdata 913bcc2be49cb534c20474c4dee1e9c4c317e7eb"},
{987876, "Coinbase tx has unparseable output script"},
{1263442, "Includes witness data"},
}
+
+ defaultBtcdDir = btcutil.AppDataDir("btcd", false)
+ defaultBtcdRPCCertFile = filepath.Join(defaultBtcdDir, "rpc.cert")
+)
+
+const (
+ fp = 19
)
type testBlockCase struct {
@@ -86,41 +94,78 @@ func (w *JSONTestWriter) Close() error {
return err
}
-func 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)
- 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
+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
}
- defer file.Close()
- writer := &JSONTestWriter{writer: file}
- defer writer.Close()
+ for _, txIn := range tx.TxIn {
+ prevOp := txIn.PreviousOutPoint
- 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
+ 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,
+ )
}
+ }
- files[i] = writer
+ return prevScripts, nil
+}
+
+func main() {
+ 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
+ }
+ defer file.Close()
+
+ writer := &JSONTestWriter{
+ writer: file,
+ }
+ 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
}
- cert, err := ioutil.ReadFile(
- path.Join(os.Getenv("HOME"), "/.btcd/rpc.cert"))
+
+ 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))
- if err != nil {
- fmt.Println("Error generating basic filter: ", err.Error())
- 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
- }
- }
- prevBasicHeaders[i] = basicHeader
- prevExtHeaders[i] = extHeader
+ prevOutputScripts, err := fetchPrevOutputScripts(client, block)
+ if err != nil {
+ fmt.Println("Couldn't fetch prev output scipts: ", err)
+ return
}
- if uint32(height) == testBlockHeights[testBlockIndex].height {
- testBlockIndex++
+ 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
}
- }
-}
-// 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
- }
+ // 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
+ }
- // 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)
- }
+ 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
}
- // 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)
+ 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
}
- }
- return b.Build()
-}
+ if height%1000 == 0 {
+ fmt.Printf("Verified height %v against server\n", height)
+ }
-// 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
- }
+ if uint32(height) == testBlockHeights[testBlockIndex].height {
+ var bfBytes []byte
+ bfBytes, err = basicFilter.NBytes()
+ if err != nil {
+ fmt.Println("Couldn't get NBytes(): ", err)
+ return
+ }
- // 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)
- }
+ prevScriptStrings := make([]string, len(prevOutputScripts))
+ for i, prevScript := range prevOutputScripts {
+ prevScriptStrings[i] = hex.EncodeToString(prevScript)
+ }
- if len(txIn.Witness) != 0 {
- b.AddWitness(txIn.Witness)
- }
+ 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
}
}
- }
- return b.Build()
+ prevBasicHeader = basicHeader
+
+ if uint32(height) == testBlockHeights[testBlockIndex].height {
+ testBlockIndex++
+ }
+ }
}