diff options
Diffstat (limited to 'bip-0158/gentestvectors.go')
-rw-r--r-- | bip-0158/gentestvectors.go | 380 |
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++ + } + } } |