diff options
-rw-r--r-- | bip-0158.mediawiki | 2 | ||||
-rw-r--r-- | bip-0158/gentestvectors.go | 368 | ||||
-rw-r--r-- | bip-0158/testnet-20.csv | 7 |
3 files changed, 376 insertions, 1 deletions
diff --git a/bip-0158.mediawiki b/bip-0158.mediawiki index e7075be..15caa68 100644 --- a/bip-0158.mediawiki +++ b/bip-0158.mediawiki @@ -420,7 +420,7 @@ gcs_match_any(key: [16]byte, compressed_set: []byte, targets: [][]byte, P: uint, == Appendix C: Test Vectors == -TODO: To be generated. +Test vectors for a P value of 20 on five testnet blocks, including the filters and filter headers, can be found [[bip-0158/testnet-20.csv|here]]. The code to generate these vectors for P values of 1 through 32 can be found [[bip-0158/gentestvectors.go|here]]. == References == diff --git a/bip-0158/gentestvectors.go b/bip-0158/gentestvectors.go new file mode 100644 index 0000000..f6560cb --- /dev/null +++ b/bip-0158/gentestvectors.go @@ -0,0 +1,368 @@ +// This program connects to your local btcd and generates test vectors for +// 5 blocks and collision space sizes of 1-32 bits. Change the RPC cert path +// and credentials to run on your system. The program assumes you're running +// a btcd with cfilter support, which mainline btcd doesn't have; in order to +// circumvent this assumption, comment out the if block that checks for +// filter size of DefaultP. + +package main + +import ( + "bytes" + "encoding/hex" + "fmt" + "io/ioutil" + "os" + "path" + + "github.com/roasbeef/btcd/chaincfg" + "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" +) + +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([]*os.File, 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 + var blockBuf bytes.Buffer + fName := fmt.Sprintf("gcstestvectors/testnet-%02d.csv", i) + file, err := os.Create(fName) + if err != nil { + fmt.Println("Error creating CSV file: ", err.Error()) + return + } + _, err = file.WriteString("Block Height,Block Hash,Block,Previous Basic Header,Previous Ext Header,Basic Filter,Ext Filter,Basic Header,Ext Header\n") + if err != nil { + fmt.Println("Error writing to CSV file: ", err.Error()) + return + } + files[i] = file + basicFilter, err := buildBasicFilter( + chaincfg.TestNet3Params.GenesisBlock, uint8(i)) + if err != nil { + fmt.Println("Error generating basic filter: ", err.Error()) + return + } + prevBasicHeaders[i], err = builder.MakeHeaderForFilter(basicFilter, + chaincfg.TestNet3Params.GenesisBlock.Header.PrevBlock) + if err != nil { + fmt.Println("Error generating header for filter: ", err.Error()) + return + } + if basicFilter == nil { + basicFilter = &gcs.Filter{} + } + extFilter, err := buildExtFilter( + chaincfg.TestNet3Params.GenesisBlock, uint8(i)) + if err != nil { + fmt.Println("Error generating ext filter: ", err.Error()) + return + } + prevExtHeaders[i], err = builder.MakeHeaderForFilter(extFilter, + chaincfg.TestNet3Params.GenesisBlock.Header.PrevBlock) + if err != nil { + fmt.Println("Error generating header for filter: ", err.Error()) + return + } + if extFilter == nil { + extFilter = &gcs.Filter{} + } + err = chaincfg.TestNet3Params.GenesisBlock.Serialize(&blockBuf) + if err != nil { + fmt.Println("Error serializing block to buffer: ", err.Error()) + return + } + 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 + } + err = writeCSVRow( + file, + 0, // Height + *chaincfg.TestNet3Params.GenesisHash, + blockBuf.Bytes(), + chaincfg.TestNet3Params.GenesisBlock.Header.PrevBlock, + chaincfg.TestNet3Params.GenesisBlock.Header.PrevBlock, + bfBytes, + efBytes, + prevBasicHeaders[i], + prevExtHeaders[i], + ) + if err != nil { + fmt.Println("Error writing to CSV file: ", err.Error()) + return + } + } + cert, err := ioutil.ReadFile( + path.Join(os.Getenv("HOME"), "/.btcd/rpc.cert")) + if err != nil { + fmt.Println("Couldn't read RPC cert: ", err.Error()) + return + } + conf := rpcclient.ConnConfig{ + Host: "127.0.0.1:18334", + Endpoint: "ws", + User: "kek", + Pass: "kek", + Certificates: cert, + } + client, err := rpcclient.New(&conf, nil) + if err != nil { + fmt.Println("Couldn't create a new client: ", err.Error()) + return + } + for height := 1; height < 988000; 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 { + fmt.Println("Error serializing block to buffer: ", err.Error()) + 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") + } + switch height { + case 1, 2, 3, 926485, 987876: // Blocks for test cases + var bfBytes []byte + var efBytes []byte + if basicFilter.N() > 0 { + bfBytes, err = basicFilter.NBytes() + if err != nil { + fmt.Println("Couldn't get NBytes(): ", err) + return + } + } + if extFilter.N() > 0 { // Exclude special case for block 987876 + efBytes, err = extFilter.NBytes() + if err != nil { + fmt.Println("Couldn't get NBytes(): ", err) + return + } + } + writeCSVRow( + files[i], + height, + *blockHash, + blockBytes, + prevBasicHeaders[i], + prevExtHeaders[i], + bfBytes, + efBytes, + basicHeader, + extHeader) + } + prevBasicHeaders[i] = basicHeader + prevExtHeaders[i] = extHeader + } + } +} + +// writeCSVRow writes a test vector to a CSV file. +func writeCSVRow(file *os.File, height int, blockHash chainhash.Hash, + blockBytes []byte, prevBasicHeader, prevExtHeader chainhash.Hash, + basicFilter, extFilter []byte, basicHeader, extHeader chainhash.Hash) error { + row := fmt.Sprintf("%d,%s,%s,%s,%s,%s,%s,%s,%s\n", + height, + blockHash.String(), + hex.EncodeToString(blockBytes), + prevBasicHeader.String(), + prevExtHeader.String(), + hex.EncodeToString(basicFilter), + hex.EncodeToString(extFilter), + basicHeader.String(), + extHeader.String(), + ) + _, err := file.WriteString(row) + if err != nil { + return err + } + return nil +} + +// buildBasicFilter builds a basic GCS filter from a block. A basic GCS filter +// will contain all the previous outpoints spent within a block, as well as the +// data pushes within all the outputs created within a block. p is specified as +// an argument in order to create test vectors with various values for p. +func buildBasicFilter(block *wire.MsgBlock, p uint8) (*gcs.Filter, error) { + blockHash := block.BlockHash() + b := builder.WithKeyHashP(&blockHash, p) + + // If the filter had an issue with the specified key, then we force it + // to bubble up here by calling the Key() function. + _, err := b.Key() + if err != nil { + return nil, err + } + + // In order to build a basic filter, we'll range over the entire block, + // adding the outpoint data as well as the data pushes within the + // pkScript. + for i, tx := range block.Transactions { + // First we'll compute the bash of the transaction and add that + // directly to the filter. + txHash := tx.TxHash() + b.AddHash(&txHash) + + // Skip the inputs for the coinbase transaction + if i != 0 { + // Each each txin, we'll add a serialized version of + // the txid:index to the filters data slices. + for _, txIn := range tx.TxIn { + b.AddOutPoint(txIn.PreviousOutPoint) + } + } + + // For each output in a transaction, we'll add each of the + // individual data pushes within the script. + for _, txOut := range tx.TxOut { + b.AddEntry(txOut.PkScript) + } + } + + return b.Build() +} + +// buildExtFilter builds an extended GCS filter from a block. An extended +// filter supplements a regular basic filter by include all the _witness_ data +// found within a block. This includes all the data pushes within any signature +// scripts as well as each element of an input's witness stack. Additionally, +// the _hashes_ of each transaction are also inserted into the filter. p is +// specified as an argument in order to create test vectors with various values +// for p. +func buildExtFilter(block *wire.MsgBlock, p uint8) (*gcs.Filter, error) { + blockHash := block.BlockHash() + b := builder.WithKeyHashP(&blockHash, p) + + // If the filter had an issue with the specified key, then we force it + // to bubble up here by calling the Key() function. + _, err := b.Key() + if err != nil { + return nil, err + } + + // In order to build an extended filter, we add the hash of each + // transaction as well as each piece of witness data included in both + // the sigScript and the witness stack of an input. + for i, tx := range block.Transactions { + // Skip the inputs for the coinbase transaction + if i != 0 { + // Next, for each input, we'll add the sigScript (if + // it's present), and also the witness stack (if it's + // present) + for _, txIn := range tx.TxIn { + if txIn.SignatureScript != nil { + b.AddScript(txIn.SignatureScript) + } + + if len(txIn.Witness) != 0 { + b.AddWitness(txIn.Witness) + } + } + } + } + + return b.Build() +} diff --git a/bip-0158/testnet-20.csv b/bip-0158/testnet-20.csv new file mode 100644 index 0000000..606e40d --- /dev/null +++ b/bip-0158/testnet-20.csv @@ -0,0 +1,7 @@ +Block Height,Block Hash,Block,Previous Basic Header,Previous Ext Header,Basic Filter,Ext Filter,Basic Header,Ext Header +0,000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943,0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000,0000000000000000000000000000000000000000000000000000000000000000,0000000000000000000000000000000000000000000000000000000000000000,0285c7cdbe33a0,00,c0589c7f567cffaf7bc0c9f6ad61710b78d3c1afef5d65a2a08e8a753173aa54,753e0d1c28585269ab770b166ca2cd1b32f9bc918750547941ed4849d5a80ba8 +1,00000000b873e79784647a6c82962c70d228557d24a747ea4d1b8bbe878e1206,0100000043497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000bac8b0fa927c0ac8234287e33c5f74d38d354820e24756ad709d7038fc5f31f020e7494dffff001d03e4b6720101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0420e7494d017f062f503253482fffffffff0100f2052a010000002321021aeaf2f8638a129a3156fbe7e5ef635226b0bafd495ff03afe2c843d7e3a4b51ac00000000,c0589c7f567cffaf7bc0c9f6ad61710b78d3c1afef5d65a2a08e8a753173aa54,753e0d1c28585269ab770b166ca2cd1b32f9bc918750547941ed4849d5a80ba8,026929d09bee00,,81e4f3e934488be62758f0b88037aa558262da3190ca018329997a319a0f8b5b,31b674ab635e074717329dabdb25d3cb0e14cb2526000cc2cedac7b5f2595110 +2,000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820,0100000006128e87be8b1b4dea47a7247d5528d2702c96826c7a648497e773b800000000e241352e3bec0a95a6217e10c3abb54adfa05abb12c126695595580fb92e222032e7494dffff001d00d235340101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0432e7494d010e062f503253482fffffffff0100f2052a010000002321038a7f6ef1c8ca0c588aa53fa860128077c9e6c11e6830f4d7ee4e763a56b7718fac00000000,81e4f3e934488be62758f0b88037aa558262da3190ca018329997a319a0f8b5b,31b674ab635e074717329dabdb25d3cb0e14cb2526000cc2cedac7b5f2595110,0278fc41168ec0,,ec48f9f8a625bd8adb2d2684867a05baafebf935553e0b78b386da98179dcf49,0dd53b407c3f242f1838e39e2fc0cfb89cdca27de07ec230568a08d1872f9e01 +3,000000008b896e272758da5297bcd98fdc6d97c9b765ecec401e286dc1fdbe10,0100000020782a005255b657696ea057d5b98f34defcf75196f64f6eeac8026c0000000041ba5afc532aae03151b8aa87b65e1594f97504a768e010c98c0add79216247186e7494dffff001d058dc2b60101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0486e7494d0151062f503253482fffffffff0100f2052a01000000232103f6d9ff4c12959445ca5549c811683bf9c88e637b222dd2e0311154c4c85cf423ac00000000,ec48f9f8a625bd8adb2d2684867a05baafebf935553e0b78b386da98179dcf49,0dd53b407c3f242f1838e39e2fc0cfb89cdca27de07ec230568a08d1872f9e01,022ce4b3256540,,060b7e1be150cc3cabd9e96b00af217132b387f31fd2bd9adfb0c7f5a09a3356,5b2a59bc476d52b45de1398ee34ed10d0cccd2a4b19ba502a456c7356b35be0d +926485,000000000000015d6077a411a8f5cc95caf775ccf11c54e27df75ce58d187313,0000002060bbab0edbf3ef8a49608ee326f8fd75c473b7e3982095e2d100000000000000c30134f8c9b6d2470488d7a67a888f6fa12f8692e0c3411fbfb92f0f68f67eedae03ca57ef13021acc22dc4105010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2f0315230e0004ae03ca57043e3d1e1d0c8796bf579aef0c0000000000122f4e696e6a61506f6f6c2f5345475749542fffffffff038427a112000000001976a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac0000000000000000266a24aa21a9ed5c748e121c0fe146d973a4ac26fa4a68b0549d46ee22d25f50a5e46fe1b377ee00000000000000002952534b424c4f434b3acd16772ad61a3c5f00287480b720f6035d5e54c9efc71be94bb5e3727f10909001200000000000000000000000000000000000000000000000000000000000000000000000000100000000010145310e878941a1b2bc2d33797ee4d89d95eaaf2e13488063a2aa9a74490f510a0100000023220020b6744de4f6ec63cc92f7c220cdefeeb1b1bed2b66c8e5706d80ec247d37e65a1ffffffff01002d3101000000001976a9143ebc40e411ed3c76f86711507ab952300890397288ac0400473044022001dd489a5d4e2fbd8a3ade27177f6b49296ba7695c40dbbe650ea83f106415fd02200b23a0602d8ff1bdf79dee118205fc7e9b40672bf31563e5741feb53fb86388501483045022100f88f040e90cc5dc6c6189d04718376ac19ed996bf9e4a3c29c3718d90ffd27180220761711f16c9e3a44f71aab55cbc0634907a1fa8bb635d971a9a01d368727bea10169522103b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb2103dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba621033d7c89bd9da29fa8d44db7906a9778b53121f72191184a9fee785c39180e4be153ae00000000010000000120925534261de4dcebb1ed5ab1b62bfe7a3ef968fb111dc2c910adfebc6e3bdf010000006b483045022100f50198f5ae66211a4f485190abe4dc7accdabe3bc214ebc9ea7069b97097d46e0220316a70a03014887086e335fc1b48358d46cd6bdc9af3b57c109c94af76fc915101210316cff587a01a2736d5e12e53551b18d73780b83c3bfb4fcf209c869b11b6415effffffff0220a10700000000001976a91450333046115eaa0ac9e0216565f945070e44573988ac2e7cd01a000000001976a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac00000000010000000203a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322010000006a47304402204efc3d70e4ca3049c2a425025edf22d5ca355f9ec899dbfbbeeb2268533a0f2b02204780d3739653035af4814ea52e1396d021953f948c29754edd0ee537364603dc012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff03a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322000000006a47304402202d96defdc5b4af71d6ba28c9a6042c2d5ee7bc6de565d4db84ef517445626e03022022da80320e9e489c8f41b74833dfb6a54a4eb5087cdb46eb663eef0b25caa526012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a914b7e6f7ff8658b2d1fb107e3d7be7af4742e6b1b3876f88fc00000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac0000000001000000043ffd60d3818431c495b89be84afac205d5d1ed663009291c560758bbd0a66df5010000006b483045022100f344607de9df42049688dcae8ff1db34c0c7cd25ec05516e30d2bc8f12ac9b2f022060b648f6a21745ea6d9782e17bcc4277b5808326488a1f40d41e125879723d3a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffffa5379401cce30f84731ef1ba65ce27edf2cc7ce57704507ebe8714aa16a96b92010000006a473044022020c37a63bf4d7f564c2192528709b6a38ab8271bd96898c6c2e335e5208661580220435c6f1ad4d9305d2c0a818b2feb5e45d443f2f162c0f61953a14d097fd07064012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff70e731e193235ff12c3184510895731a099112ffca4b00246c60003c40f843ce000000006a473044022053760f74c29a879e30a17b5f03a5bb057a5751a39f86fa6ecdedc36a1b7db04c022041d41c9b95f00d2d10a0373322a9025dba66c942196bc9d8adeb0e12d3024728012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff66b7a71b3e50379c8e85fc18fe3f1a408fc985f257036c34702ba205cef09f6f000000006a4730440220499bf9e2db3db6e930228d0661395f65431acae466634d098612fd80b08459ee022040e069fc9e3c60009f521cef54c38aadbd1251aee37940e6018aadb10f194d6a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a9148fc37ad460fdfbd2b44fe446f6e3071a4f64faa6878f447f0b000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac00000000,5c169b91332e661a4ebf684d6dffcf64aa139e1e736096ce462609b2e0783c55,5f3cd111e10ed28b7c2bc138fe4bc62e5df1cdc292c010b78c84e5111a5daaa6,16040c63f7ddea293f2d9c13690c0ba7b2910228c38b0fe542ce525021e49b598ada05f83bb9c37c711a02b1850265991c34c4fea6261d22a4b84596c0,0e6651beff00ee7a3be424a90e98450727b304558434c8d53781d469131ad21d399376c151ca28,c8f83ffbc9781c2bd4b7e3c055e888b00d3e2fea14e93b3c4f3adee86b063374,61f8ee615258981089be9f98337f53f44b14cc1532aa469a348fdee547117b80 +987876,0000000000000c00901f2049055e2a437c819d79a3d54fd63e6af796cd7b8a79,000000202694f74969fdb542090e95a56bc8aa2d646e27033850e32f1c5f000000000000f7e53676b3f12d5beb524ed617f2d25f5a93b5f4f52c1ba2678260d72712f8dd0a6dfe5740257e1a4b1768960101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1603e4120ff9c30a1c216900002f424d4920546573742fffffff0001205fa012000000001e76a914c486de584a735ec2f22da7cd9681614681f92173d83d0aa68688ac00000000,37bf9e681888b3cd204ca4e0c995aad68cd0ecb86bdf19dd0fa2e72dbabcda28,c4c5051dd741c11840ef3f11fb4f372a16bb5aac1dc66576e89e8e6835d667e0,021016dc7a6a20,,156e9bf3ec5be367f0a829858e9ee182cc3a6531bedced491a52fcfed841c6cb,cab50aab93410cb150fd761f2067a909d35c8a9c0114578efd9590e2d381ee02 |