diff --git a/Cargo.lock b/Cargo.lock index 75951450..621c656a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,11 @@ dependencies = [ "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "assert_matches" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "atty" version = "0.2.3" @@ -116,11 +121,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "bitcrypto" version = "0.1.0" dependencies = [ - "blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc.git?branch=persona)", + "bellman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)", "bn 0.4.4 (git+https://github.com/paritytech/bn)", + "pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", "primitives 0.1.0", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sapling-crypto 0.0.1 (git+https://github.com/zcash-hackworks/sapling-crypto.git?rev=21084bde2019c04bd34208e63c3560fe2c02fb0e)", "serde 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -142,16 +150,6 @@ name = "bitflags" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "blake2-rfc" -version = "0.2.18" -source = "git+https://github.com/gtank/blake2-rfc.git?branch=persona#c7c458429c429b81fea845421f5ab859710fa8af" -dependencies = [ - "arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "blake2-rfc" version = "0.2.18" @@ -1582,7 +1580,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "verification" version = "0.1.0" dependencies = [ - "bellman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitcrypto 0.1.0", "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "chain 0.1.0", @@ -1590,13 +1588,11 @@ dependencies = [ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "network 0.1.0", - "pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "primitives 0.1.0", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sapling-crypto 0.0.1 (git+https://github.com/zcash-hackworks/sapling-crypto.git?rev=21084bde2019c04bd34208e63c3560fe2c02fb0e)", "script 0.1.0", "serialization 0.1.0", "storage 0.1.0", @@ -1685,6 +1681,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" "checksum app_dirs 1.2.1 (git+https://github.com/paritytech/app-dirs-rs)" = "" "checksum arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2f0ef4a9820019a0c91d918918c93dc71d469f581a49b47ddc1d285d4270bbe2" +"checksum assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5" "checksum atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21e50800ec991574876040fff8ee46b136a53e985286fbe6a3bdfe6421b78860" "checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" @@ -1694,7 +1691,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" -"checksum blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc.git?branch=persona)" = "" "checksum blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)" = "" "checksum bn 0.4.4 (git+https://github.com/paritytech/bn)" = "" "checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9" diff --git a/chain/src/block.rs b/chain/src/block.rs index 3b635327..cf1a5e6f 100644 --- a/chain/src/block.rs +++ b/chain/src/block.rs @@ -68,6 +68,13 @@ mod tests { "0400000072d43a7895d2774d081ccee922a81d77a8879f46a8aa86d65aebd38e9800000012f742113a92b8f55a2474017e30d09ba7f0b3d9e144db761a77e08d0ec476640000000000000000000000000000000000000000000000000000000000000000868c1358a376011e9e02000000000000030000000000000000000000000000000000000000000000fd4005002b983cb99149af0d05b36f45cc2f4d47af59f79d0253cc3ce8395065da1862fc31a5268d5663f0eccd02dee3b544c93fc45900e65d13466f12c039bbff5a076ed18698035914244c108f8355494df71bbb3c7d084293d3838913485b45a1553f42fc7208ae7d7350299c82639bec1a8fd4e8286a73e3750732e0fb05ad08fd80ceca44dd4d53c581356d4e572cfabff96a4c19248c3a87eab33becee256903ded2916cb3accd75042caf1981510fc1c83476b17e66d9be8f04b9dd8d0d75a9d95bcf3367e023b45f8eb06ce9c3f1d7a16013f4b70bfdcaf3530e3177a8c1f5f901fab59a5a771c31e57c8ece507d729a33383358fc7126093bb7d011d7f392235431292fadf2ffd8a929aa95f957503279afa40ef229290996c6ba136a752b5eef96178754461b9d22a51703bf99dbf710f9ec61e1ccb61828cb52ab04564cb434fbd09e79308d4b4ab27bd5b4838b02a0d77023c54e0cf43e44dfadd75392817c75177219c70c7635cbdcf111117780dec878b785a35e1329118ade96dd4dea211784c16d72dbe14f62935fc66c2ebafffc59cf2488e28ada5e586a9f5752d57e5d3e07ab7b332f90a585ffe72164abece07db6cdf1fc961335f3360d1e3c81d490126bcde56a5e16eb56e6f0126bf44358574843b586984a29f6f2163bb7f217be271dbae0142c7f697768f8d2cb54195e5ef675dab203998961afec029dccaa21d16069267630ef7c00881fa7c2bb761b9c2cdfa8a29d40c1db875b8d7d55f008ef8081a8cfbde7632330ebe0281a0092b5507d6c275269400453a46cb2ae27ccf1e18f3622daf9af3f048803cd27cdd72d287af371e921cbf708eab933a0151725a5a3f1e331b378c348594f0d319faf51dd200ed463245f175d5be68aa1a9804812d94a7334f9bc31bb09ce11d1341712e408069dc6c39789c3de18eb00f8af8e010aa8af5407d3a16ca2c59d59abf522391453992e2e18ad57f55f716d80d9c0e9710e4bd79014be01c0792199274b058386a8ebe9067de29cac0221859174079b8b2311c75320a8c96023b9c01e992601b75cfe8090a65356bcf5ec7ac4c5a69f50794f310b2408cdf10f9d9ec85183f4de6c1ba1da0bd99c9a3180eaabb4b50d11b93775d03fce9289818d4d7bf236c25675a49ba3dae9ca9b0561eebd4785cd1c829f01c2a79bdb8a96c0d2a7719dc9a6c8ec9ec6d70cbf56ab1623a5dab59cf74ac9874f7275b705747846881bcf86c8c824ca3944fa31c8e7b88cad2c5309ae0d4b8796a5f8570916fb3c858cdb60f697ae855e532210759d6aa7050fd4df4c56852dccc1fad5d91cd1ce2aa1719319a4951f31280aa33ddfe4fac2fd9b554e125f02ada0cb6906f85666cb07cd0036661859d428166d17c7c7e1c011c5c0366a61ded942898b37583001f0b2cbe66285a730a4d2e4ed7eaa53a85d3e2f4a0493f95927194d1bc80a38710aeb620e7bebfce81b4c14c744d8f7c131e645861c6abe1f72ba50798b655cd94fa7129a9e0fc0cb58f439d0b65ec9eedee0f00366cd8145503c9f1936b084f435f494488a45d19530f574b6de8fe6d32e2b43948e6ae8561a185772aa18d586163554019383abb2ea02482e82ffd59b4ee01aa7c414a09e9071e2a501ad56c9a410f352d1713103fd94762e023aad97a75086a6c49f2d923e8f15640e70117a01969320fb1992d4bbb9c1bef33c7a22d013ee35a896e46d038179a21ad31bc8a5097150d4da2bfc8f1c16e771d1b5bd7419d4c839a76faf5edeba151603821c52284f7722b3c8125e5c3d3901768fb65bf6d51f3fad0277a6eee79b60d8c7f38f995f2b5a1ed3d677fef0e417deacf8d2707f1c265fedf9750938625fc25628cef592aad4dd3ce54ae1f147de71370201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff06028c0102f401ffffffff02c01f2e01000000002321033bf6b16c6987b017991932dc66dc96ccdbde81e4c0b2ea086d246349bedde903acf0874b000000000017a9147d46a730d31f97b1930d3368a967c309bd4d136a870000000002000000010a141a3f21ed57fa8449ceac0b11909f1b5560f06b772753ca008d49675d45310000000048473044022041aaea8391c0182bf71bd974662e99534d99849b167062f7e8372c4f1a16c2d50220291b2ca6ae7616cd1f1bfddcda5ef2f53d78c2e153d3a8db571885f9adb5f05401ffffffff0000000000011070d900000000000000000000000000d7c612c817793191a1e68652121876d6b3bde40f4fa52bc314145ce6e5cdd2597ae7c48e86173b231e84fbdcb4d8f569f28f71ebf0f9b5867f9d4c12e031a2acc0108235936d2fa2d2c968654fbea2a89fde8522ec7c227d2ff3c10bff9c1197d8a290cca91f23792df8e56aed6c142eaa322e66360b5c49132b940689fb2bc5e77f7877bba6d2c4425d9861515cbe8a5c87dfd7cf159e9d4ac9ff63c096fbcd91d2a459877b1ed40748e2f020cdc678cf576a62c63138d820aba3df4074014bb1624b703774e138c706ba394698fd33c58424bb1a8d22be0d7bc8fe58d369e89836fe673c246d8d0cb1d7e1cc94acfa5b8d76010db8d53a36a3f0e33f0ccbc0f861b5e3d0a92e1c05c6bca775ba7389f6444f0e6cbd34141953220718594664022cbbb59465c880f50d42d0d49d6422197b5f823c2b3ffdb341869b98ed2eb2fd031b271702bda61ff885788363a7cf980a134c09a24c9911dc94cbe970bd613b700b0891fe8b8b05d9d2e7e51df9d6959bdf0a3f2310164afb197a229486a0e8e3808d76c75662b568839ebac7fbf740db9d576523282e6cdd1adf8b0f9c183ae95b0301fa1146d35af869cc47c51cfd827b7efceeca3c55884f54a68e38ee7682b5d102131b9b1198ed371e7e3da9f5a8b9ad394ab5a29f67a1d9b6ca1b8449862c69a5022e5d671e6989d33c182e0a6bbbe4a9da491dbd93ca3c01490c8f74a780479c7c031fb473670cacde779713dcd8cbdad802b8d418e007335919837becf46a3b1d0e02120af9d926bed2b28ed8a2b8307b3da2a171b3ee1bc1e6196773b570407df6b43b51b52c43f834ee0854577cd3a57f8fc23b02a3845cc1f0f42410f363d862e436bf06dbc5f94eddd3b83cdf47cf0acbd7750dff5cba86ea6f1f46a5013e0dc76715d7230e44a038a527cb9033f3eeaeac661264dc6a384788a7cd8aed59589bca6205fe1bd683fa392e7a3c6cc364bba36ad75ee9babf90f7b94071953df95effc0b1c3f542913ed1eb68e15534f9ceb7777c946edf55f129df128c3f767d8d60c4aa0c5e61d00f8e495e78334e2a9feddd9302e9880cb6174d201c89a1d6bc6e83a80cbf80ab3959dcc6cdd12e3d2f6f14d226e6948954f05544941d16ed1d498532722fa39bb985c3224915dd42d70be61217fdcb4aa023251af38b5576ff9eb865a471f2cb2dbc674e401d18014e6119464768778ddcd00907f20279bdecda3880fbbb4d00bb6c5aa3e06113a2f12fcc298f34ccb6bc2c2887b0b064f3bc2e2b507d31e022e65800dd7d30f25266914646bfc07c1eafbbf1e1163c439774b47e8e844799bc8fd06db050f97f5c74ca833e81bcdcf9d864be5746f965ef41838a3535666df867ef79e07068dc7ef809fb0e08e1629bab3215fe36d0f0e0f8c6bb319f93a0f408ff4abbd88c21afaec2e7720674eaceb27efb9144f619bad6f033cbefcebfbe66cabe8286f2ff97b91f4aeef5cbd99a9b862cb904dc085d96238caaad259280ff35caa211e00324f51ff03b6a1cd159cd501faef780ef7f25a98cdcd05ef67596d58d4aea1f9f3e95aae44fd4d4ea679c5e393d4670fb35bf12d036ea731bdfad297303239251a91f9a900e06987eb8e9f5bb1fb847f5ae47e6724ddeb5a3ac01b706a02e494c5547ce338302b4906cf2c91d59a87324322763a12e13a512ace3afb897510ad9ec95aa14ca568a9962da64e5bc7fd15b3e103ab461ee7db3fc9da0a523fc403c11254cd567ca48c8dac5e5b54953e5c754e31def90fff6c56d589a5c4b9a710ccb43cd24988b2fb9336b5508aa553cfdbd1f32dfb4ff16eae066b5fb244bc9058a91898c4ae893eaf0006dae1185c7f553e6e09d12a0a2a9c181c5e4d87c8895b74b0e23a8dc87faf5d6acd5e98cb1df5585f026ae94b77db0e95c5fe22692bd2e70e8e87d07d92b98cdfcc5367e52014163a6e4511d482816259215ee7df246e493523ee51617c318e1a9825f82e73e640fbc2d25c12ce5a07875d489db6a111afdc87061047077030d32de45cd4e575c02a60c4048560bd02cf9203426f589f429b413390ace832b3ddd3dd371750d94f9c34f60a0f1b621b445525d2190a185feaab9e56a079c46236161559713d585a07e94f2316a92fffa7838f1aea39d7846638d16f9b4d1a7dc053e0ddc6620f30e3e798eba900fd25c10c5d6672c9ed7d4d2fa80c0f0137ff24933c37fcd91b19bc7cdd828f7f3f1df0e45cafca795d847e83bca8baa321006581b024306e24c4c2294c0f41b932c1e9f7602f377e8484c7eeb184fab1f747b1dff5b6e2e89f1e5c4232b5a0a41ed6a3775f8942217078b7e035747891cabd2099bfcbf6a8d4680f51265d9e7d05794514f02470e0eb003ad1222cd4fe8bcd077310c5aff274b19608c31f77453d01c9aa9c21a8d9b71de44386aee2145648f7ead471cabed297b8610bba370baa42603f21f5f4640e5bc1a0402d40394e176a0db8cedb33a9d84c48b58d3851617046511946a3700aabe8f69cdb0469ee67776480be090cad2c7adc0bf59551ef6f1ac3119e5c29ab3b82dd945dab00dc4a91d3826c4e488047a4f3ab2d57c0abe1ee7aba304784e7ad211c32c4058fca7b1db2e282132e5ccafe79fc51ab37334f03715f4ad8735b6e03f01", "000000e869e3a0fa79858a51b4b1d09a6480dcdb37bae63653fcb11a718abf3f", "6476c40e8de0771a76db44e1d9b3f0a79bd0307e0174245af5b8923a1142f712", + ), + ( + // https://zcash.blockexplorer.com/block/00000000007ef95f986ed8309d0ed6a1b6174c90b9c7f4d0dfc40f7147315e79 + // https://zcash.blockexplorer.com/api/rawblock/00000000007ef95f986ed8309d0ed6a1b6174c90b9c7f4d0dfc40f7147315e79 + "04000000bd3c4ad2cecf0fc1b26e72d31e9ccb4a61083c23ff78757579d35600000000006be99b7c4e1c7d02f8200fa106571b0e9e1fd0ff81a5e053d07c1f5f68bf56237d71967f4b31fbaaf221a958bbe3e6bc02cc3c3fbe2fed80657bd9fb14f3be12b72f1b5ce42f021c000000008c132a8040000924000000000000a500000000000000000000000000fd400500392d6c2bd1ee9fa99041f5f659645122bb2aa73a2a452959a796e296ced7550ec4dfee2a13995b2a180f28cd4b0f86c947138db3da1d4f530146e4beaa6a40fadd2b2f1e002f770f8422fe4c6a7a346959825c02f0510b1dd6209bbcc874c1b74d4ce6d7bb7fc6c4135d4406c6cdf8ea882fd493517baa4790e65cf4551f4547824b11e569d4c79376ca5d0c97128a399b9d4c8ff58df3d9e0c9f01449a9305047f6decc5cc2fb01838b3e74d71e9fc8e25033c34e3fea9116bb649f01dd076e925946cb17db403350714341b959ded59c1691a7df4419f209c902925934d05eadfbe4fd332e1a218988888b3353dfd498c320eda3fed9e0d83ad908b0379a1c5a35571f9b244e1c7be22e0c95d8f3ba2155357086e39f7d25ba649e8dcee3b19e495110ba27c50767a517c3a5b815a51ac5f744cdc020b6b7bd3183c65561dc765f4b57155c31ca89523f0196b3e702513de4fc8363dfc66981a5dc421e20a392f5487a0305cbcec10996dee18e572dc6412cf1ddf15d3f9306a00a54c416ae40e40735f01d77b65e99d398764d241e6bbfa073c1c1a4df648f72deb60bcddd3f9e740f9144935a4c00c9a08497588eee01166501f801b73d02baa90b11bda8ed6d970e453ea166eed51973ac377e939e4fdd5818f4b4087f135ee31abc1b57b89c3f0105c3cfa923bba62e361719415bb384c3fe214c0a2022d86fcd33bf3f41641484bfd71e018735ef8f535293683e99b61fb005f9dab277796a79695524491e0d5991a198fe4b2fe7e7ef3a5a0b6ea5277cb60e2dde8b65e8a40953f890e797aebd753f297bfa1a5912dc60e79091791734c05169cf649361dd94bf1f8355792540d8eab27d9aa70664d144644688e3df108720db673b33f38a4da641c66f0f673282690b7e2b84352f6c1ba491747cc4a4057838d1e2fa468138ba240139e038ebcc4669fedaa55938671551fd3b19479241d0737b38e3a955c243a625b1c8350265045df84405a760d0990af733301b04a7d96b7f55d7ffdea3e326caa4d057e4ee69841668e19d7813fe45309e90ea04d6a43caf926da543f8824230cf52f6d58f18d580063d013ea3ea5cfb6bca36014ed5a02a5adbdaf0e61c3295c7800ba2cf93c46355eaba2b21281c7656b92cd6f239b9ee4cdd72bda74038f8a2f30f81f9a7c7053a8b9c5e0efe690b5df69362fa61da37ab37c5f715c512ae44cc769af434124484af4b7165f3daeae5084e413aa94cc047511d310b5df82edde9e92fca5321d6d4e2d892aa429ab4f7cddef4e1ca906ffd4bfe0723d9334d430a09499752be299d8312787afc345c2032b6b9bbe24d2735a5f4d58a317f69ff31dc5121149b029973c6bc486e9e530c9c3c94e5810c9b2de642c38e4e78b040efeb0ab871557bbdca3e9a3d7fc5021416ed8ba514076260d3d1217efd7170a18eda291a59a7ec3d5c46295d4165d52866d19a1ed3bd39940f6c7a6e3f594b5cd124b22df95febe518c32b3f711f805f2ca7d3316325eaf2b7d628c7cd57549df3a8050d1f22df901ac1c004411fb4d60656224ad3cd8939956a62f9a5c953d14834b22c4ae68d35d81466b307b81206f6970189097428b1307602e251f55cfee43ac5ad87e812f756e848e41327490e2b2453bb7ce2054e47bd25d90c89f59672f6267895114eab31f0b50c4a7226bf0f7166c7d4b5e633ba01d29b507f84360bc51c7008a70c07cb45e26cc7a72da1b38ed69829393952942f1040199cdb6790294526ea6621b5637e05920c84346074c5b609424c24df9a10eda7ff59a7351af3ce2b0f0c01c64df46a7fe1a18ec81576aa740a6b10c5019bfebb4958d110fdc00df73554ddcdfe0e868c6e37a3b0b3e7a222ed6ce6b35e887b15a94a080400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff2a0360da060004b72f1b5ce475a064e5e716a5dc6c5714316d151882eac199a88b50caae6aaa5299bf29a0ffffffff025d5f9b3b000000001976a914130f06e8110c04122ebb08bb49e3453d206cf8e488ac80b2e60e0000000017a914e38130c1e0add9fb67365f61c6978f2c1891a5d387000000000000000000000000000000000000000400008085202f89014adba0e9f805f333511f1e97ec50e95f5b70c78fce34c593f784c96761663d80020000006a47304402202a0ef385827211421f7ad629ba7ddbfbca97682f098d8fde72f0d5764f273d97022017298fc7db27efd1499e55a28c33368f45b29919b9e2fa9ac50e03f5f64151d80121027b1c69c615ade0ac84a934333b36d1c344d77bcc51937868824335c01d25ec29feffffff02b46402b2550000001976a914ed9019d8252f78c7651da69b0adba3014518640b88ac1cd51206000000001976a914df1cfef1073f717a3c7ef433ab03b172565ae91188ac4eda060074da060000000000000000000000000400008085202f89016b46468486b84c3ce8a37a7d1bed68186b692a4a4bdad2314d0b3b8c3d63762dc70000006a4730440220449f8ffaa3346152bea1825a0ff560725540ac616faeda7f334985eb1919122a02207de01f4bf86adc49cb3df34279d883f4192e1f6a296cf240b0a5222a2758a0b60121022539acfb7d76a3d0ecce4278259f3678b4693a87cc6d58e5cd6e66098463c4830000000002600bb101000000001976a9146375971b222b3f18a48f77f92c97bc9338acfc5788ac9b0e0000000000001976a91401a4db5a5b93f1733fdd83746d70b010872a211588ac000000000000000000000000000000000000000400008085202f890128a359daa6090aaa87982c3592b768a9afafb60cd9b9e068aaf0fe2d405300d8000000006b48304502210092b794d3c292abd216265373faaa05c0d017a83acdf9435e6d60d6bcf7ed8bce02206fa6ec0f85cffd6d7c9157c3f4a7e3b6ac5f84664e90f847a4a5d17209119743012102de6af5046400c030ac05e18d187c29a6c40e741706403af97f3a40b719391476feffffff02c6972e44010000001976a914337681dded0736e6c5d26a1385c5ffa6d59beb3488ace06c1f07000000001976a9140c8c7f49860e97b14b62763039e014e825c078c188ac54da060073da060000000000000000000000000400008085202f8902a999deae50bd4b318b1811d443b7c906ef49d919da451bc211c830f7ec432b8e000000006b483045022100d42dfd57dd5ea076bb198cef55e51c2486ebeb037212aa60006b93fe08e8787102206ec26c097084f09e308ad5b9e3a5454a4b9c98ad30a142526879b49044d6b78601210255eb1549442b3e21cbb98213b62ab5340da8b7d173402d7c8cab16c6659fa7f1feffffffb90eeef1500ee2e1a4a46c4bc1a999a4f434db2d6dd7fbe8878b560dcaef03ae000000006a47304402200c11af18aab29245a5ca33e1e02e06d40e3c36bc303d13bcdd106d4489d4ff9f0220718f8741adfc078ba48ccf790de2f64954e218c00ec7c21503d0df41127369870121039d9a916b2b3ae6d0abd556aec9a3a81e54049b9b123aa977e84cd00501039479feffffff020b5b5600000000001976a91444915cdec4626362e51f03045198d9c163152f0388ac4e751200000000001976a914a950d1a0d291b81e3f55a205efe415219080a4a388ac55da060074da060000000000000000000000000400008085202f890001f8680e2e030000001976a914849d7fb393d294aa4cbc3b73591ad3da95e22b1988ac0000000067da060008900e2e0300000001251bb2e003abe1421823eb492fe85ce17a8cf4f6076f34fa5d94772b143057708facfb01cde6f5723e76f8d4c4fab73517f32c2a8f1fb64ceb9f2978af40024c62c69a3890b6156fa8a6378086981bf8ceec2ece0e6cf41b9b34b25352bcd1caa9a5107e0cdce1c56433da4600169bf86f8d088c6243fc172e6626f43373660298157fdc1f0c96dcbd312ebe97b0845c7d4b51d9190b42f4347f7802556064902eacd8264d94b3dcadb14f4c345a6b38b1d80220c6440b708f8f4078f0de3c09f2b2f1a76df4633a07e2d96db6e67ab879729818d9b9d3e84f4505ee0b73daaf02cd848ac7acdce3708544aaf51f07544495ec162f9ab74f3efca1635ed23197568b44f1a266210195369a8959cfba2da3532ec89c50598ab1ff2a839cc2ad2e339e446b224e0530f15514be0f93ea1b7c70fa67787c6a9aac3da402808f8df76daf656d1a351c61c509ea0015c98de8474d7d610e5f8a8e138b135d695e80ef6322dbf389a7af048e6c1c7d821109ca57a86a90c5145feb2eb56d8e15b4ca030000fb748212e9081c616430f441a2a757d8a93fc8b0cd93d6121db78dae700dd148c70c8220bd34dd036d750f818b8bec08dda1408ee7ee35eda8b4f7f0b902e1020400008085202f8900000000000072da060010270000000000000148b1c0668fce604361fbb1b89bbd76f8fee09b51a9dc0fdfcf6c6720cd596083d970234fcc0e9a70fdfed82d32fbb9ca92c9c5c3bad5daad9ac62b5bf4255817ee5bc95a9af453bb9cc7e2c544aa29efa20011a65b624998369c849aa8f0bc83d60e7902a3cfe6eeaeb8d583a491de5982c5ded29e64cd8f8fac594a5bb4f2838e6c30876e36a18d8d935238815c8d9205a4f1f523ff76b51f614bff1064d1c5fa0a27ec0c43c8a6c2714e7234d32e9a8934a3e9c0f74f1fdac2ddf6be3b13bc933b0478cae556a2d387cc23b05e8b0bd53d9e838ad2d2cb31daccefe256087511b044dfae665f0af0fa968edeea4cbb437a8099724159471adf7946eec434cccc1129f4d1e31d7f3f8be524226c65f28897d3604c14efb64bea6a889b2705617432927229dfa382e78c0ace31cc158fbf3ec1597242955e45af1ee5cfaffd789cc80dc53d6b18d42033ec2c327170e2811fe8ec00feadeb1033eb48ab24a6dce2480ad428be57c4619466fc3181ece69b914fed30566ff853250ef19ef7370601f4c24b0125e4059eec61f63ccbe277363172f2bdee384412ea073c5aca06b94e402ba3a43e15bd9c65bbfb194c561c24a031dec43be95c59eb6b568c176b1038d5b7b057dc032488335284adebfb6607e6a995b7fa418f13c8a61b343e5df44faa1050d9d76550748d9efebe01da97ade5937afd5f007ed26e0af03f283611655e91bc6a4857f66a57a1584ff687c4baf725f4a1b32fae53a3e6e8b98bca319bb1badb704c9c1a04f401f33d813d605eef6943c2c52dbc85ab7081d1f8f69d3202aae281bf42336a949a12a7dbbd22abdd6e92996282ebd69033c22cb0539d97f83636d6a8232209a7411e8b03bef180d83e608563ea2d0becff56dc996c2049df054961bfb21b7cbef5049a7dacc18f2c977aa1b2d48291abc19c3c8ea25d2e61901048354b17ce952f6f2248cf3a0eb54c19b507b41d7281c3d227e2b142ff695d8b925a4bb942ed9492a73a17468a8332a367fd16295420bdca6c04d380271f40440709998fce3a3af3e1e505f5402e5dd464dd179cb0eede3d494a95b84d2fb2eb5abb425cf2c712af999c65259c4782a5ec97388324c67738908a5ba43b6db62a10f50cddf9b5039123437c74165921ac8cf4f13292a216baef9d00bd544106b52755986c98a462ade1149f69367e926d88eb92798c0e56cd19a1bcf264fd93293033b758da65c7901eb5b4a17ee265a3312dbc477868da0057e1b3cbf47726dead6ecfcc8e1044c6f311ff0fc83192dc2f75a89626ba33364dac747b63ff3c8337e00332c8783ba9c8dc13cdf0750d7adc3926fbe1279017d50adba35c38c5b810f73abe5d759cd7fb650f6b0a1f78dc1f62fd017090ff4de4cf54c883752ddda68083d4617ed2c38bab8da313965dd3f7b755aec23a2d9e2965d08d2134827a72ffb3bd65b1fd5410da105bfba7a74ddff0928a654aca1ee211ac9dce8019ddcbb52263ce44b2544a314355c1e8c8543f3ed3e883e7a7a8f9e3c7c11f41ab9069854fb21e9b3660a860df19d289d54b29d82522b32d187cde6261eb0a429c3994dff6f37b9ab9102281223e3cd584790a909e05ba0ea1a2d9aef8e571986e98e09312dccaf8e739d718a1edd217dc4c8a5c8a650015405b592a7c674a451d7d1686c7ea6d93e74a8fe4ade12b679ac780457f08a79bfbf96dcf7eefe9a39b99f1ae39d2c5f86aadf156b7d5ce4b2733f307cfe1e1ff6de0ff2006d9cba535b0c40dfb7a98399cdff8e681fc38c7b9aa94ee5eb89432e28d94ee27f238776ba964a87caf58eddbb64771e64de094305a8eb848d2d9ad6373903687d22170f48f1ae8d714514034ee2733857af4747312bb006e6ce3918ede8c730bacc7821b81c1b93bb50b219e79e8e0d74531ed18c1145632d9847d38783b49141ac5353aaa7d125fb2934e681467e16b28090978e74e0b0400008085202f89032d9418dc7d2882fe6be0ed319a1c549f673a622f071bde5eb52f55bdc54f549b2e0100006b483045022100d459951782ab9179738629855d970fe71a0164b3e873f541f4d8437892ebfbc4022007dd68f95d6095ce1c7437af3a0478e70449027410911b5d1cd0e11bd27d7ded012102c72bc522a7d79e6697ec01ff7c35daf3e5ee7c73ceb9b58d5af7d9814f61031dffffffff7e850495c92512a2cfe21f7cf26e1eff4a03ddb26e2d01f252995ba07382911e010000006b483045022100e02e454dad48d9f1700bdead87c33db9a2495275279a252c8348940eab53e82702205d92c7741067d68bad90dd12562a4a192ae33dd6cfd6fe930be7edc867e9c1d0012102c72bc522a7d79e6697ec01ff7c35daf3e5ee7c73ceb9b58d5af7d9814f61031dffffffffebc0256e10a516907f7442877219d20573728055033d6d64661f494e825a2775010000006b483045022100ffe35271c58cfc9584c883e8aeb5b71a51082ba6c775f9c13424b1ee11e5f01e02200e305d36736d1f18743b8d21885b26e0cbc4b08d856820a1baf0251df98010ed012102c72bc522a7d79e6697ec01ff7c35daf3e5ee7c73ceb9b58d5af7d9814f61031dffffffff02e0561d01000000001976a914535b4f24926d5e1df9ae606fcf27ad73e41888c688acde150000000000001976a914587dea6af9830d207c81290d578777b67a34c71f88ac00000000ff64cd1d0000000000000000000000", + "00000000007ef95f986ed8309d0ed6a1b6174c90b9c7f4d0dfc40f7147315e79", + "2356bf685f1f7cd053e0a581ffd01f9e0e1b5706a10f20f8027d1c4e7c9be96b", ) ]; diff --git a/chain/src/transaction.rs b/chain/src/transaction.rs index 549e208a..8c1f6e95 100644 --- a/chain/src/transaction.rs +++ b/chain/src/transaction.rs @@ -399,7 +399,7 @@ mod tests { // Test vector 1 from: // https://github.com/zcash/zips/blob/9515d73aac0aea3494f77bcd634e1e4fbd744b97/zip-0243.rst #[test] - fn test_sapling_transaction() { + fn test_sapling_transaction_1() { let hex = "0400008085202f890002e7719811893e0000095200ac6551ac636565b2835a0805750200025151481cdd86b3cc4318442117623ceb0500031b3d1a027c2c40590958b7eb13d742a997738c46a458965baf276ba92f272c721fe01f7e9c8e36d6a5e29d4e30a73594bf5098421c69378af1e40f64e125946f62c2fa7b2fecbcb64b6968912a6381ce3dc166d56a1d62f5a8d7551db5fd9313e8c7203d996af7d477083756d59af80d06a745f44ab023752cb5b406ed8985e18130ab33362697b0e4e4c763ccb8f676495c222f7fba1e31defa3d5a57efc2e1e9b01a035587d5fb1a38e01d94903d3c3e0ad3360c1d3710acd20b183e31d49f25c9a138f49b1a537edcf04be34a9851a7af9db6990ed83dd64af3597c04323ea51b0052ad8084a8b9da948d320dadd64f5431e61ddf658d24ae67c22c8d1309131fc00fe7f235734276d38d47f1e191e00c7a1d48af046827591e9733a97fa6b679f3dc601d008285edcbdae69ce8fc1be4aac00ff2711ebd931de518856878f73476f21a482ec9378365c8f7393c94e2885315eb4671098b79535e790fe53e29fef2b3766697ac32b4f473f468a008e72389fc03880d780cb07fcfaabe3f1a15825b7acb4d6b57a61bc68f242b52e4fbf85cf1a09cc45b6d6bb3a391578f499486a7afd04a0d9c74c2995d96b4de37b36046a1ef6d190b916b1111c92887311a20da8aba18d1dbebbc862ded42435e92476930d069896cff30eb414f727b89e001afa2fb8dc3436d75a4a6f26572504b192232ecb9f0c02411e52596bc5e90457e745939ffedbd12863ce71a02af117d417adb3d15cc54dcb1fce467500c6b8fb86b12b56da9c382857deecc40a98d5f2935395ee4762dd21afdbb5d47fa9a6dd984d567db2857b927b7fae2db587105415d4642789d38f50b8dbcc129cab3d17d19f3355bcf73cecb8cb8a5da01307152f13936a270572670dc82d39026c6cb4cd4b0f7f5aa2a4f5a5341ec5dd715406f2fdd2afa733f5f641c8c21862a1bafce2609d9eecfa158cfb5cd79f88008e315dc7d8388e76c1782fd2795d18a763624c25fa959cc97489ce75745824b77868c53239cfbdf73caec65604037314faaceb56218c6bd30f8374ac13386793f21a9fb80ad03bc0cda4a44946c00e1b102c78f11876b7065212183199fb5979ca77d2c24c738fe5145f02602053bb4c2f6556df6ed4b4ddd3d9a69f53357d7767f4f5ccbdbc596631277f8fecd08cb056b95e3025b9792fff7f244fc716269b926d62e9596fa825c6bf21aff9e68625a192440ea06828123d97884806f15fa08da52754a1095e3ff1abd5ce4fddfccfc3a6128aef784a64610a89d1a7099216d0814d3a2d452431c32d411ac1cce82ad0229407bbc48985675e3f874a4533f1d63a84dfa3e0f460fe2f57e34fbc75423c3737f5b2a0615f5722db041a3ef66fa483afd3c2e19e59444a64add6df1d963f5dd5b5010d3d025f0287c4cf19c75f33d51ddddba5d657b43ee8da645443814cc7329f3e9b4e54c236c29af3923101756d9fa4bd0f7d2ddaacb6b0f86a2658e0a07a05ac5b950051cd24c47a88d13d659ba2a46ca1830816d09cd7646f76f716abec5de07fe9b523410806ea6f288f8736c23357c85f45791e1708029d9824d90704607f387a03e49bf9836574431345a7877efaa8a08e73081ef8d62cb780a010fa3207ee2f0408097d563da1b2146819edf88d33e7753664fb71d122a6e36998fbd467f75b780149ae8808f4e68f50c0536acddf6f1aeab016b6bc1ec144b4e59aeb77eef49d00e5fbb67101cdd41e6bc9cf641a52fca98be915f8440a410d74cb30e15914f01bc6bc2307b488d2556d7b7380ea4ffd712f6b02fe806b94569cd4059f396bf29b99d0a40e5e1711ca944f72d436a102fca4b97693da0b086fe9d2e7162470d02e0f05d4bec9512bfb3f38327296efaa74328b118c27402c70c3a90b49ad4bbc68e37c0aa7d9b3fe17799d73b841e751713a02943905aae0803fd69442eb7681ec2a05600054e92eed555028f21b6a155268a2dd6640a69301a52a38d4d9f9f957ae35af7167118141ce4c9be0a6a492fe79f1581a155fa3a2b9dafd82e650b386ad3a08cb6b83131ac300b0846354a7eef9c410e4b62c47c5426907dfc6685c5c99b7141ac626ab4761fd3f41e728e1a28f89db89ffdeca364dd2f0f0739f0534556483199c71f189341ac9b78a269164206a0ea1ce73bfb2a942e7370b247c046f8e75ef8e3f8bd821cf577491864e20e6d08fd2e32b555c92c661f19588b72a89599710a88061253ca285b6304b37da2b5294f5cb354a894322848ccbdc7c2545b7da568afac87ffa005c312241c2d57f4b45d6419f0d2e2c5af33ae243785b325cdab95404fc7aed70525cddb41872cfcc214b13232edc78609753dbff930eb0dc156612b9cb434bc4b693392deb87c530435312edcedc6a961133338d786c4a3e103f60110a16b1337129704bf4754ff6ba9fbe65951e610620f71cda8fc877625f2c5bb04cbe1228b1e886f4050afd8fe94e97d2e9e85c6bb748c0042d3249abb1342bb0eebf62058bf3de080d94611a3750915b5dc6c0b3899d41222bace760ee9c8818ded599e34c56d7372af1eb86852f2a732104bdb750739de6c2c6e0f9eb7cb17f1942bfc9f4fd6ebb6b4cdd4da2bca26fac4578e9f543405acc7d86ff59158bd0cba3aef6f4a8472d144d99f8b8d1dedaa9077d4f01d4bb27bbe31d88fbefac3dcd4797563a26b1d61fcd9a464ab21ed550fe6fa09695ba0b2f10eea6468cc6e20a66f826e3d14c5006f0563887f5e1289be1b2004caca8d3f34d6e84bf59c1e04619a7c23a996941d889e4622a9b9b1d59d5e319094318cd405ba27b7e2c084762d31453ec4549a4d97729d033460fcf89d6494f2ffd789e98082ea5ce9534b3acd60fe49e37e4f666931677319ed89f85588741b3128901a93bd78e4be0225a9e2692c77c969ed0176bdf9555948cbd5a332d045de6ba6bf4490adfe7444cd467a09075417fc0200000000000000000000000000000000062e49f008c51ad4227439c1b4476ccd8e97862dab7be1e8d399c05ef27c6e22ee273e15786e394c8f1be31682a30147963ac8da8d41d804258426a3f70289b8ad19d8de13be4eebe3bd4c8a6f55d6e0c373d456851879f5fbc282db9e134806bff71e11bc33ab75dd6ca067fb73a043b646a7cf39cab4928386786d2f24141ee120fdc34d6764eafc66880ee0204f53cc1167ed20b43a52dea3ca7cff8ef35cd8e6d7c111a68ef44bcd0c1513ad47ca61c659cc5d325b440f6b9f59aff66879bb6688fd2859362b182f207b3175961f6411a493bffd048e7d0d87d82fe6f990a2b0a25f5aa0111a6e68f37bf6f3ac2d26b84686e569d58d99c1383597fad81193c4c1b16e6a90e2d507cdfe6fbdaa86163e9cf5de3100fbca7e8da047b090db9f37952fbfee76af61668190bd52ed490e677b515d014384af07219c7c0ee7fc7bfc79f325644e4df4c0d7db08e9f0bd024943c705abff8994bfa605cfbc7ed746a7d3f7c37d9e8bdc433b7d79e08a12f738a8f0dbddfef2f2657ef3e47d1b0fd11e6a13311fb799c79c641d9da43b33e7ad012e28255398789262275f1175be8462c01491c4d842406d0ec4282c9526174a09878fe8fdde33a29604e5e5e7b2a025d6650b97dbb52befb59b1d30a57433b0a351474444099daa371046613260cf3354cfcdada663ece824ffd7e44393886a86165ddddf2b4c41773554c86995269408b11e6737a4c447586f69173446d8e48bf84cbc000a807899973eb93c5e819aad669413f8387933ad1584aa35e43f4ecd1e2d0407c0b1b89920ffdfdb9bea51ac95b557af71b89f903f5d9848f14fcbeb1837570f544d6359eb23faf38a0822da36ce426c4a2fbeffeb0a8a2e297a9d19ba15024590e3329d9fa9261f9938a4032dd34606c9cf9f3dd33e576f05cd1dd6811c6298757d77d9e810abdb226afcaa4346a6560f8932b3181fd355d5d391976183f8d99388839632d6354f666d09d3e5629ea19737388613d38a34fd0f6e50ee5a0cc9677177f50028c141378187bd2819403fc534f80076e9380cb4964d3b6b45819d3b8e9caf54f051852d671bf8c1ffde2d1510756418cb4810936aa57e6965d6fb656a760b7f19adf96c173488552193b147ee58858033dac7cd0eb204c06490bbdedf5f7571acb2ebe76acef3f2a01ee987486dfe6c3f0a5e234c127258f97a28fb5d164a8176be946b8097d0e317287f33bf9c16f9a545409ce29b1f4273725fc0df02a04ebae178b3414fb0a82d50deb09fcf4e6ee9d180ff4f56ff3bc1d3601fc2dc90d814c3256f4967d3a8d64c83fea339c51f5a8e5801fbb97835581b602465dee04b5922c2761b54245bec0c9eef2db97d22b2b3556cc969fbb13d06509765a52b3fac54b93f421bf08e18d52ddd52cc1c8ca8adfaccab7e5cc2f4573fbbf8239bb0b8aedbf8dad16282da5c9125dba1c059d0df8abf621078f02d6c4bc86d40845ac1d59710c45f07d585eb48b32fc0167ba256e73ca3b9311c62d109497957d8dbe10aa3e866b40c0baa2bc492c19ad1e6372d9622bf163fbffeaeee796a3cd9b6fbbfa4d792f34d7fd6e763cd5859dd26833d21d9bc5452bd19515dff9f4995b35bc0c1f876e6ad11f2452dc9ae85aec01fc56f8cbfda75a7727b75ebbd6bbffb43b63a3b1b671e40feb0db002974a3c3b1a788567231bf6399ff89236981149d423802d2341a3bedb9ddcbac1fe7b6435e1479c72e7089d029e7fbbaf3cf37e9b9a6b776791e4c5e6fda57e8d5f14c8c35a2d270846b9dbe005cda16af4408f3ab06a916eeeb9c9594b70424a4c1d171295b6763b22f47f80b53ccbb904bd68fd65fbd3fbdea1035e98c21a7dbc91a9b5bc7690f05ec317c97f8764eb48e911d428ec8d861b708e8298acb62155145155ae95f0a1d1501034753146e22d05f586d7f6b4fe12dad9a17f5db70b1db96b8d9a83edadc966c8a5466b61fc998c31f1070d9a5c9a6d268d304fe6b8fd3b4010348611abdcbd49fe4f85b623c7828c71382e1034ea67bc8ae97404b0c50b2a04f559e49950afcb0ef462a2ae024b0f0224dfd73684b88c7fbe92d02b68f759c4752663cd7b97a14943649305521326bde085630864629291bae25ff8822a14c4b666a9259ad0dc42a8290ac7bc7f53a16f379f758e5de750f04fd7cad47701c8597f97888bea6fa0bf2999956fbfd0ee68ec36e4688809ae231eb8bc4369f5fe1573f57e099d9c09901bf39caac48dc11956a8ae905ead86954547c448ae43d315e669c4242da565938f417bf43ce7b2b30b1cd4018388e1a910f0fc41fb0877a5925e466819d375b0a912d4fe843b76ef6f223f0f7c894f38f7ab780dfd75f669c8c06cffa0000000000000000000000000000000043eb47565a50e3b1fa45ad61ce9a1c4727b7aaa53562f523e73952bbf33d8a4104078ade3eaaa49699a69fdf1c5ac7732146ee5e1d6b6ca9b9180f964cc9d0878ae1373524d7d510e58227df6de9d30d271867640177b0f1856e28d5c8afb095ef6184fed651589022eeaea4c0ce1fa6f085092b04979489172b3ef8194a798df5724d6b05f1ae000013a08d612bca8a8c31443c10346dbf61de8475c0bbec5104b47556af3d514458e2321d146071789d2335934a680614e83562f82dfd405b54a45eb32c165448d4d5d61ca2859585369f53f1a137e9e82b67b8fdaf01bda54a317311896ae10280a032440c420a421e944d1e952b70d5826cd3b08b7db9630fe4fd5f22125de840fcc40b98038af11d55be25432597b4b65b9ec1c7a8bbfd052cbf7e1c1785314934b262d5853754f1f17771cfb7503072655753fa3f54ecc587e9f83b581916092df26e63e18994cb0db91a0bbdc7b6119b32222adf5e61d8d8ae89dae4954b54813bb33f08d562ba513fee1b09c0fcd516055419474dd7fda038a89c84ea7b9468287f0eb0c10c4b132520194d3d8d5351fc10d09c15c8cc101aa1663bbf17b84111f38bb439f07353bdea3596d15e713e1e2e7d3f1c383135b47fa7f81f46df7a902a404699ec912f5656c35b85763e4de583aecaa1dfd5d2677d9c8ffee877f63f40a5ca0d67f6e554124739f805af876aeede53aa8b0f8e5604a73c30cbd09dad963d6f8a5dcc40def40797342113ba206fae8ebe4f3bc3caf69259e462eff9ba8b3f4bfaa1300c26925a8729cd32915bfc966086f0d5560bbe32a598c22adfb48cef72ba5d4287c0cefbacfd8ce195b4963c34a94bba7a175dae4bbe3ef4863d53708915090f47a068e227433f9e49d3aa09e356d8d66d0c0121e91a3c4aa3f27fa1b63396e2b41db908fdab8b18cc7304e94e970568f9421c0dbbbaf84598d972b0534f48a5e52670436aaa776ed2482ad703430201e53443c36dcfd34a0cb6637876105e79bf3bd58ec148cb64970e3223a91f71dfcfd5a04b667fbaf3d4b3b908b9828820dfecdd753750b5f9d2216e56c615272f854464c0ca4b1e85aedd038292c4e1a57744ebba010b9ebfbb011bd6f0b78805025d27f3c17746bae116c15d9f471f0f6288a150647b2afe9df7cccf01f5cde5f04680bbfed87f6cf429fb27ad6babe791766611cf5bc20e48bef119259b9b8a0e39c3df28cb9582ea338601cdc481b32fb82adeebb3dade25d1a3df20c37e712506b5d996c49a9f0f30ddcb91fe9004e1e83294a6c9203d94e8dc2cbb449de4155032604e47997016b304fd437d8235045e255a19b743a0a9f2e336b44cae307bb3987bd3e4e777fbb34c0ab8cc3d67466c0a88dd4ccad18a07a8d1068df5b629e5718d0f6df5c957cf71bb00a5178f175caca944e635c5159f738e2402a2d21aa081e10e456afb00b9f62416c8b9c0f7228f510729e0be3f305313d77f7379dc2af24869c6c74ee4471498861d192f0ff0f508285dab6b6a36ccf7d12256cc76b95503720ac672d08268d2cf7773b6ba2a5f664847bf707f2fc10c98f2f006ec22ccb5a8c8b7c40c7c2d49a6639b9f2ce33c25c04bc461e744dfa536b00d94baddf4f4d14044c695a33881477df124f0fcf206a9fb2e65e304cdbf0c4d2390170c130ab849c2f22b5cdd3921640c8cf1976ae1010b0dfd9cb2543e45f99749cc4d61f2e8aabfe98bd905fa39951b33ea769c45ab9531c57209862ad12fd76ba4807e65417b6cd12fa8ec916f013ebb8706a96effeda06c4be24b04846392e9d1e6930eae01fa21fbd700583fb598b92c8f4eb8a61aa6235db60f2841cf3a1c6ab54c67066844711d091eb931a1bd6281aedf2a0e8fab18817202a9be06402ed9cc720c16bfe881e4df4255e87afb7fc62f38116bbe03cd8a3cb11a27d568414782f47b1a44c97c680467694bc9709d32916c97e8006cbb07ba0e4180a3738038c374c4cce8f32959afb25f303f5815c4533124acf9d18940e77522ac5dc4b9570aae8f47b7f57fd8767bea1a24ae7bed65b4afdc8f1278c30e2db98fd172730ac6bbed4f1127cd32b04a95b205526cfcb4c4e1cc955175b3e8de1f5d81b18669692350aaa1a1d797617582e54d7a5b57a683b32fb1098062dad7b0c2eb518f6862e83db25e3dbaf7aed504de932acb99d735992ce62bae9ef893ff6acc0ffcf8e3483e146b9d49dd8c7835f43a37dca0787e3ec9f6605223d5ba7ae0ab9025b73bc03f7fac36c009a56d4d95d1e81d3b3ebca7e54cc1a12d127b57c8138976e791013b015f06a624f521b6ee04ec980893c7e5e01a336203594094f82833d7445fe2d09130f63511da54832de9136b39f4599f5aa5dfbb45da60cdceab7eefde89be63f3f7c0d2324847cce1405def7c469b0e272494e5df54f568656cb9c8818d92b72b8bc34db7bb3112487e746eefe4e808bbb287d99bf07d00dabededc5e5f074ffeae0cba7da3a516c173be1c513323e119f635e8209a074b216b7023fadc2d25949c90037e71e3e550726d210a2c688342e52440635e9cc14afe10102621a9c9accb782e9e4a5fa87f0a956f5b"; // deserialize && check tx @@ -419,6 +419,20 @@ mod tests { assert_eq!(t, hex); } + // tx: https://zcash.blockexplorer.com/tx/bd4fe81c15cfbd125f5ca6fe51fb5ac4ef340e64a36f576a6a09f7528eb2e176 + // rawtx is broken for this tx => parsed from rawblock + // https://zcash.blockexplorer.com/api/rawblock/00000000007ef95f986ed8309d0ed6a1b6174c90b9c7f4d0dfc40f7147315e79 + #[test] + fn test_sapling_transaction_2() { + let hex = "0400008085202f8900000000000072da060010270000000000000148b1c0668fce604361fbb1b89bbd76f8fee09b51a9dc0fdfcf6c6720cd596083d970234fcc0e9a70fdfed82d32fbb9ca92c9c5c3bad5daad9ac62b5bf4255817ee5bc95a9af453bb9cc7e2c544aa29efa20011a65b624998369c849aa8f0bc83d60e7902a3cfe6eeaeb8d583a491de5982c5ded29e64cd8f8fac594a5bb4f2838e6c30876e36a18d8d935238815c8d9205a4f1f523ff76b51f614bff1064d1c5fa0a27ec0c43c8a6c2714e7234d32e9a8934a3e9c0f74f1fdac2ddf6be3b13bc933b0478cae556a2d387cc23b05e8b0bd53d9e838ad2d2cb31daccefe256087511b044dfae665f0af0fa968edeea4cbb437a8099724159471adf7946eec434cccc1129f4d1e31d7f3f8be524226c65f28897d3604c14efb64bea6a889b2705617432927229dfa382e78c0ace31cc158fbf3ec1597242955e45af1ee5cfaffd789cc80dc53d6b18d42033ec2c327170e2811fe8ec00feadeb1033eb48ab24a6dce2480ad428be57c4619466fc3181ece69b914fed30566ff853250ef19ef7370601f4c24b0125e4059eec61f63ccbe277363172f2bdee384412ea073c5aca06b94e402ba3a43e15bd9c65bbfb194c561c24a031dec43be95c59eb6b568c176b1038d5b7b057dc032488335284adebfb6607e6a995b7fa418f13c8a61b343e5df44faa1050d9d76550748d9efebe01da97ade5937afd5f007ed26e0af03f283611655e91bc6a4857f66a57a1584ff687c4baf725f4a1b32fae53a3e6e8b98bca319bb1badb704c9c1a04f401f33d813d605eef6943c2c52dbc85ab7081d1f8f69d3202aae281bf42336a949a12a7dbbd22abdd6e92996282ebd69033c22cb0539d97f83636d6a8232209a7411e8b03bef180d83e608563ea2d0becff56dc996c2049df054961bfb21b7cbef5049a7dacc18f2c977aa1b2d48291abc19c3c8ea25d2e61901048354b17ce952f6f2248cf3a0eb54c19b507b41d7281c3d227e2b142ff695d8b925a4bb942ed9492a73a17468a8332a367fd16295420bdca6c04d380271f40440709998fce3a3af3e1e505f5402e5dd464dd179cb0eede3d494a95b84d2fb2eb5abb425cf2c712af999c65259c4782a5ec97388324c67738908a5ba43b6db62a10f50cddf9b5039123437c74165921ac8cf4f13292a216baef9d00bd544106b52755986c98a462ade1149f69367e926d88eb92798c0e56cd19a1bcf264fd93293033b758da65c7901eb5b4a17ee265a3312dbc477868da0057e1b3cbf47726dead6ecfcc8e1044c6f311ff0fc83192dc2f75a89626ba33364dac747b63ff3c8337e00332c8783ba9c8dc13cdf0750d7adc3926fbe1279017d50adba35c38c5b810f73abe5d759cd7fb650f6b0a1f78dc1f62fd017090ff4de4cf54c883752ddda68083d4617ed2c38bab8da313965dd3f7b755aec23a2d9e2965d08d2134827a72ffb3bd65b1fd5410da105bfba7a74ddff0928a654aca1ee211ac9dce8019ddcbb52263ce44b2544a314355c1e8c8543f3ed3e883e7a7a8f9e3c7c11f41ab9069854fb21e9b3660a860df19d289d54b29d82522b32d187cde6261eb0a429c3994dff6f37b9ab9102281223e3cd584790a909e05ba0ea1a2d9aef8e571986e98e09312dccaf8e739d718a1edd217dc4c8a5c8a650015405b592a7c674a451d7d1686c7ea6d93e74a8fe4ade12b679ac780457f08a79bfbf96dcf7eefe9a39b99f1ae39d2c5f86aadf156b7d5ce4b2733f307cfe1e1ff6de0ff2006d9cba535b0c40dfb7a98399cdff8e681fc38c7b9aa94ee5eb89432e28d94ee27f238776ba964a87caf58eddbb64771e64de094305a8eb848d2d9ad6373903687d22170f48f1ae8d714514034ee2733857af4747312bb006e6ce3918ede8c730bacc7821b81c1b93bb50b219e79e8e0d74531ed18c1145632d9847d38783b49141ac5353aaa7d125fb2934e681467e16b28090978e74e0b"; + + // deserialize && check tx + let t: Transaction = hex.into(); + assert!(t.sapling.is_some()); + assert_eq!(t.sapling.as_ref().unwrap().spends.len(), 1); + assert_eq!(t.sapling.as_ref().unwrap().outputs.len(), 1); + } + #[test] fn test_transaction_hash() { let t: Transaction = "0100000001a6b97044d03da79c005b20ea9c0e1a6d9dc12d9f7b91a5911c9030a439eed8f5000000004948304502206e21798a42fae0e854281abd38bacd1aeed3ee3738d9e1446618c4571d1090db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d5d6cc8d25c6b241501ffffffff0100f2052a010000001976a914404371705fa9bd789a2fcd52d2c580b65d35549d88ac00000000".into(); diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 35165af9..a74cda80 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -4,14 +4,15 @@ version = "0.1.0" authors = ["debris "] [dependencies] -blake2-rfc = { git = "https://github.com/gtank/blake2-rfc.git", branch = "persona" } +bellman = "0.1" +blake2-rfc = { git = "https://github.com/gtank/blake2-rfc.git", rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" } +pairing = "0.14.2" rust-crypto = "0.2.36" +sapling-crypto = { git = "https://github.com/zcash-hackworks/sapling-crypto.git", rev = "21084bde2019c04bd34208e63c3560fe2c02fb0e" } +serde_json = "1.0" siphasher = "0.1.1" primitives = { path = "../primitives" } bn = { git = "https://github.com/paritytech/bn" } serde = "1.0" serde_derive = "1.0" rustc-hex = "2" - -[dev-dependencies] -serde_json = "1.0" \ No newline at end of file diff --git a/crypto/src/json/groth16.rs b/crypto/src/json/groth16.rs new file mode 100644 index 00000000..cdad1a36 --- /dev/null +++ b/crypto/src/json/groth16.rs @@ -0,0 +1,155 @@ +use std::fmt; +use hex::FromHex; +use bellman::groth16::{prepare_verifying_key, VerifyingKey as BellmanVerifyingKey}; +use pairing::{EncodedPoint, bls12_381::{Bls12, G1Uncompressed, G2Uncompressed}}; +use serde::de::{self, Visitor, Deserialize, Deserializer}; + +use json::pghr13::clean_0x; +use Groth16VerifyingKey; + +/// Load Sapling spend verification key. +pub fn load_sapling_spend_verifying_key() -> Result { + let spend_vk_json = include_bytes!("../../../res/sapling-spend-verifying-key.json"); + let spend_vk = serde_json::from_slice::(&spend_vk_json[..]).unwrap(); + Ok(Groth16VerifyingKey(prepare_verifying_key(&spend_vk.into()))) +} + +/// Load Sapling output verification key. +pub fn load_sapling_output_verifying_key() -> Result { + let output_vk_json = include_bytes!("../../../res/sapling-output-verifying-key.json"); + let output_vk = serde_json::from_slice::(&output_vk_json[..]).unwrap(); + Ok(Groth16VerifyingKey(prepare_verifying_key(&output_vk.into()))) +} + +type G1 = Point; +type G2 = Point; + +#[derive(Clone, Deserialize)] +struct VerifyingKey { + #[serde(rename = "alphaG1")] + pub alpha_g1: G1, + #[serde(rename = "betaG1")] + pub beta_g1: G1, + #[serde(rename = "betaG2")] + pub beta_g2: G2, + #[serde(rename = "gammaG2")] + pub gamma_g2: G2, + #[serde(rename = "deltaG1")] + pub delta_g1: G1, + #[serde(rename = "deltaG2")] + pub delta_g2: G2, + #[serde(rename = "ic")] + pub ic: Vec, +} + +impl From for BellmanVerifyingKey { + fn from(vk: VerifyingKey) -> BellmanVerifyingKey { + BellmanVerifyingKey { + alpha_g1: vk.alpha_g1.0, + beta_g1: vk.beta_g1.0, + beta_g2: vk.beta_g2.0, + gamma_g2: vk.gamma_g2.0, + delta_g1: vk.delta_g1.0, + delta_g2: vk.delta_g2.0, + ic: vk.ic.into_iter().map(|p| p.0).collect(), + } + } +} + +#[derive(Debug, Clone)] +struct Point(EP::Affine); + +impl<'de, EP: EncodedPoint> Deserialize<'de> for Point { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct EncodedPointVisitor(::std::marker::PhantomData); + + impl<'de, EP: EncodedPoint> Visitor<'de> for EncodedPointVisitor { + type Value = Point; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a hex string") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + let mut point = EP::empty(); + let point_raw = clean_0x(value).from_hex::>() + .map_err(|e| de::Error::custom(format!("Expected hex string: {}", e)))?; + if point.as_ref().len() != point_raw.len() { + return Err(de::Error::custom(format!("Expected hex string of length {}", point.as_ref().len()))); + } + + point.as_mut().copy_from_slice(&point_raw); + point.into_affine() + .map_err(|e| de::Error::custom(format!("Invalid curve point: {}", e))) + .map(Point) + } + } + + deserializer.deserialize_str(EncodedPointVisitor::(Default::default())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn g1() { + let valid = r#""0x0db882cf5db3e8567f16b4db1772d4d1f5a3fe8d62f0df2eb8a5cfa50806702afde8fc25335eb5ec859c2818b2610b2e19ab445dac720bb1f2b0cd3336f7a1acc62bf1b3a321826264dc7e469281e23b218394d598689da04e136878ff9a7897""#; + serde_json::from_str::(valid).unwrap(); + } + + #[test] + fn g1_messed() { + // too few chars + let invalid = r#""0xb882cf5db3e8567f16b4db1772d4d1f5a3fe8d62f0df2eb8a5cfa50806702afde8fc25335eb5ec859c2818b2610b2e19ab445dac720bb1f2b0cd3336f7a1acc62bf1b3a321826264dc7e469281e23b218394d598689da04e136878ff9a7897""#; + serde_json::from_str::(invalid).unwrap_err(); + + // too much chars + let invalid = r#""0xFF0db882cf5db3e8567f16b4db1772d4d1f5a3fe8d62f0df2eb8a5cfa50806702afde8fc25335eb5ec859c2818b2610b2e19ab445dac720bb1f2b0cd3336f7a1acc62bf1b3a321826264dc7e469281e23b218394d598689da04e136878ff9a7897""#; + serde_json::from_str::(invalid).unwrap_err(); + + // invalid curve point + let invalid = r#""0x19ab445dac720bb1f2b0cd3336f7a1acc62bf1b3a321826264dc7e469281e23b218394d598689da04e136878ff9a78970db882cf5db3e8567f16b4db1772d4d1f5a3fe8d62f0df2eb8a5cfa50806702afde8fc25335eb5ec859c2818b2610b2e""#; + serde_json::from_str::(invalid).unwrap_err(); + } + + #[test] + fn g2() { + let valid = r#""0x050302fecf4d86671c66ed1ee097efccd2a2add6fd42c9d0a809bb6a3e0f8348bfac6cfa4427c83d5ed1ff844a5b1b1209c069a8a1ccd8c7c22b2a84fede0e53b536cabd7d4c7f0ddc53bec42eeda2b09190d43bcbaece88f7a2a1fc686076d20f2acbc06f28f913a2a77a731d96133aeb5282461cd452a3f3f1d3b63907840dc79b1066e898a335c3a676de9c97507c0c4824b4c9ac0dfc2b1b017e1ebe1b96920a80a7f7e61e39d2f275c51ea8c0b4a6aa86643ee4696af6611d027c58401c""#; + serde_json::from_str::(valid).unwrap(); + } + + #[test] + fn g2_messed() { + // too few chars + let invalid = r#""0x0302fecf4d86671c66ed1ee097efccd2a2add6fd42c9d0a809bb6a3e0f8348bfac6cfa4427c83d5ed1ff844a5b1b1209c069a8a1ccd8c7c22b2a84fede0e53b536cabd7d4c7f0ddc53bec42eeda2b09190d43bcbaece88f7a2a1fc686076d20f2acbc06f28f913a2a77a731d96133aeb5282461cd452a3f3f1d3b63907840dc79b1066e898a335c3a676de9c97507c0c4824b4c9ac0dfc2b1b017e1ebe1b96920a80a7f7e61e39d2f275c51ea8c0b4a6aa86643ee4696af6611d027c58401c""#; + serde_json::from_str::(invalid).unwrap_err(); + + // too much chars + let invalid = r#""0xFF050302fecf4d86671c66ed1ee097efccd2a2add6fd42c9d0a809bb6a3e0f8348bfac6cfa4427c83d5ed1ff844a5b1b1209c069a8a1ccd8c7c22b2a84fede0e53b536cabd7d4c7f0ddc53bec42eeda2b09190d43bcbaece88f7a2a1fc686076d20f2acbc06f28f913a2a77a731d96133aeb5282461cd452a3f3f1d3b63907840dc79b1066e898a335c3a676de9c97507c0c4824b4c9ac0dfc2b1b017e1ebe1b96920a80a7f7e61e39d2f275c51ea8c0b4a6aa86643ee4696af6611d027c58401c""#; + serde_json::from_str::(invalid).unwrap_err(); + + // invalid curve point + let invalid = r#""0x09c069a8a1ccd8c7c22b2a84fede0e53b536cabd7d4c7f0ddc53bec42eeda2b09190d43bcbaece88f7a2a1fc686076d2050302fecf4d86671c66ed1ee097efccd2a2add6fd42c9d0a809bb6a3e0f8348bfac6cfa4427c83d5ed1ff844a5b1b120c4824b4c9ac0dfc2b1b017e1ebe1b96920a80a7f7e61e39d2f275c51ea8c0b4a6aa86643ee4696af6611d027c58401c0f2acbc06f28f913a2a77a731d96133aeb5282461cd452a3f3f1d3b63907840dc79b1066e898a335c3a676de9c97507c""#; + serde_json::from_str::(invalid).unwrap_err(); + } + + #[test] + fn output_key() { + let output_vk_json = include_bytes!("../../../res/sapling-output-verifying-key.json"); + serde_json::from_slice::(&output_vk_json[..]).unwrap(); + } + + #[test] + fn spend_key() { + let spend_vk_json = include_bytes!("../../../res/sapling-spend-verifying-key.json"); + serde_json::from_slice::(&spend_vk_json[..]).unwrap(); + } +} diff --git a/crypto/src/json/mod.rs b/crypto/src/json/mod.rs new file mode 100644 index 00000000..c5c6aff8 --- /dev/null +++ b/crypto/src/json/mod.rs @@ -0,0 +1,2 @@ +pub mod groth16; +pub mod pghr13; diff --git a/crypto/src/json.rs b/crypto/src/json/pghr13.rs similarity index 99% rename from crypto/src/json.rs rename to crypto/src/json/pghr13.rs index 0feaab4b..60428512 100644 --- a/crypto/src/json.rs +++ b/crypto/src/json/pghr13.rs @@ -10,7 +10,7 @@ pub struct G1(bn::G1); struct G1Visitor; -fn clean_0x(s: &str) -> &str { +pub(crate) fn clean_0x(s: &str) -> &str { if s.starts_with("0x") { &s[2..] } else { @@ -120,8 +120,6 @@ pub struct VerifyingKey { #[cfg(test)] mod tests { - - extern crate serde_json; use super::*; #[test] diff --git a/crypto/src/lib.rs b/crypto/src/lib.rs index a26389bd..de57a46b 100644 --- a/crypto/src/lib.rs +++ b/crypto/src/lib.rs @@ -1,15 +1,20 @@ extern crate blake2_rfc; extern crate crypto as rcrypto; extern crate primitives; +extern crate serde_json; extern crate siphasher; extern crate bn; extern crate serde; extern crate rustc_hex as hex; +pub extern crate bellman; +pub extern crate pairing; +pub extern crate sapling_crypto; + #[macro_use] extern crate serde_derive; -mod pghr13; mod json; +mod pghr13; pub use rcrypto::digest::Digest; pub use blake2_rfc::blake2b::Blake2b; @@ -21,11 +26,17 @@ use rcrypto::ripemd160::Ripemd160; use siphasher::sip::SipHasher24; use primitives::hash::{H32, H160, H256}; +pub use json::groth16::{ + load_sapling_spend_verifying_key, load_sapling_output_verifying_key, +}; + pub use pghr13::{ VerifyingKey as Pghr13VerifyingKey, Proof as Pghr13Proof, verify as pghr13_verify, G1, G2, Fr, Group, }; +pub struct Groth16VerifyingKey(pub bellman::groth16::PreparedVerifyingKey); + pub struct DHash160 { sha256: Sha256, ripemd: Ripemd160, @@ -195,6 +206,12 @@ pub fn checksum(data: &[u8]) -> H32 { result } +impl ::std::fmt::Debug for Groth16VerifyingKey { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.write_str("Groth16VerifyingKey") + } +} + #[cfg(test)] mod tests { use primitives::bytes::Bytes; diff --git a/db/src/block_chain_db.rs b/db/src/block_chain_db.rs index 0b5d3fa1..3098125a 100644 --- a/db/src/block_chain_db.rs +++ b/db/src/block_chain_db.rs @@ -7,7 +7,7 @@ use bytes::Bytes; use primitives::compact::Compact; use chain::{ IndexedBlock, IndexedBlockHeader, IndexedTransaction, BlockHeader, Block, Transaction, - OutPoint, TransactionOutput, SAPLING_TX_VERSION_GROUP_ID, + OutPoint, TransactionOutput, }; use ser::{ deserialize, serialize, List @@ -287,15 +287,13 @@ impl BlockChainDatabase where T: KeyValueDatabase { } for tx in block.transactions.iter().skip(1) { - let is_sapling_group = tx.raw.version_group_id == SAPLING_TX_VERSION_GROUP_ID; - modified_meta.insert(tx.hash.clone(), TransactionMeta::new(new_best_block.number, tx.raw.outputs.len())); if let Some(ref js) = tx.raw.join_split { for js_descriptor in js.descriptions.iter() { for nullifier in &js_descriptor.nullifiers[..] { let nullifier_key = Nullifier::new( - if is_sapling_group { NullifierTag::Sapling } else { NullifierTag::Sprout }, + NullifierTag::Sprout, H256::from(&nullifier[..]) ); if self.contains_nullifier(nullifier_key) { @@ -307,6 +305,20 @@ impl BlockChainDatabase where T: KeyValueDatabase { } } + if let Some(ref sapling) = tx.raw.sapling { + for spend in &sapling.spends { + let nullifier_key = Nullifier::new( + NullifierTag::Sapling, + H256::from(&spend.nullifier[..]) + ); + if self.contains_nullifier(nullifier_key) { + trace!(target: "db", "Duplicate nullifer during canonization: {:?}", nullifier_key); + return Err(Error::CannotCanonize); + } + update.insert(KeyValue::Nullifier(nullifier_key)); + } + } + for input in &tx.raw.inputs { use std::collections::hash_map::Entry; @@ -363,12 +375,11 @@ impl BlockChainDatabase where T: KeyValueDatabase { let mut modified_meta: HashMap = HashMap::new(); for tx in block.transactions.iter().skip(1) { - let is_sapling_group = tx.raw.version_group_id == SAPLING_TX_VERSION_GROUP_ID; if let Some(ref js) = tx.raw.join_split { for js_descriptor in js.descriptions.iter() { for nullifier in &js_descriptor.nullifiers[..] { let nullifier_key = Nullifier::new( - if is_sapling_group { NullifierTag::Sapling } else { NullifierTag::Sprout }, + NullifierTag::Sprout, H256::from(&nullifier[..]) ); if !self.contains_nullifier(nullifier_key) { @@ -380,6 +391,20 @@ impl BlockChainDatabase where T: KeyValueDatabase { } } + if let Some(ref sapling) = tx.raw.sapling { + for spend in &sapling.spends { + let nullifier_key = Nullifier::new( + NullifierTag::Sapling, + H256::from(&spend.nullifier[..]) + ); + if !self.contains_nullifier(nullifier_key) { + warn!(target: "db", "cannot decanonize, no nullifier: {:?}", nullifier_key); + return Err(Error::CannotDecanonize); + } + update.delete(Key::Nullifier(nullifier_key)); + } + } + for input in &tx.raw.inputs { use std::collections::hash_map::Entry; diff --git a/message/src/common/inventory.rs b/message/src/common/inventory.rs index 399ccf68..cc765095 100644 --- a/message/src/common/inventory.rs +++ b/message/src/common/inventory.rs @@ -115,10 +115,10 @@ mod tests { #[test] fn test_inventory_type_conversion() { - assert_eq!(0u32, InventoryType::Error.into()); - assert_eq!(1u32, InventoryType::MessageTx.into()); - assert_eq!(2u32, InventoryType::MessageBlock.into()); - assert_eq!(3u32, InventoryType::MessageFilteredBlock.into()); + assert_eq!(0u32, u32::from(InventoryType::Error)); + assert_eq!(1u32, u32::from(InventoryType::MessageTx)); + assert_eq!(2u32, u32::from(InventoryType::MessageBlock)); + assert_eq!(3u32, u32::from(InventoryType::MessageFilteredBlock)); assert_eq!(InventoryType::from_u32(0).unwrap(), InventoryType::Error); assert_eq!(InventoryType::from_u32(1).unwrap(), InventoryType::MessageTx); diff --git a/network/src/consensus.rs b/network/src/consensus.rs index 36772d7d..b70500ce 100644 --- a/network/src/consensus.rs +++ b/network/src/consensus.rs @@ -1,5 +1,12 @@ use {Network, Magic, Deployment, crypto}; +lazy_static! { + static ref SAPLING_SPEND_VK: crypto::Groth16VerifyingKey = crypto::load_sapling_spend_verifying_key() + .expect("hardcoded value should load without errors"); + static ref SAPLING_OUTPUT_VK: crypto::Groth16VerifyingKey = crypto::load_sapling_output_verifying_key() + .expect("hardcoded value should load without errors"); +} + #[derive(Debug, Clone)] /// Parameters that influence chain consensus. pub struct ConsensusParams { @@ -45,6 +52,11 @@ pub struct ConsensusParams { /// Active key for pghr13 joinsplit verification pub joinsplit_verification_key: crypto::Pghr13VerifyingKey, + + /// Sapling spend verification key. + pub sapling_spend_verifying_key: &'static crypto::Groth16VerifyingKey, + /// Sapling output verification key. + pub sapling_output_verifying_key: &'static crypto::Groth16VerifyingKey, } fn mainnet_pghr_verification_key() -> crypto::Pghr13VerifyingKey { @@ -135,6 +147,9 @@ impl ConsensusParams { equihash_params: Some((200, 9)), joinsplit_verification_key: mainnet_pghr_verification_key(), + + sapling_spend_verifying_key: &SAPLING_SPEND_VK, + sapling_output_verifying_key: &SAPLING_OUTPUT_VK, }, Network::Testnet => ConsensusParams { network: network, @@ -157,6 +172,9 @@ impl ConsensusParams { equihash_params: Some((200, 9)), joinsplit_verification_key: testnet_pghr_verification_key(), + + sapling_spend_verifying_key: &SAPLING_SPEND_VK, + sapling_output_verifying_key: &SAPLING_OUTPUT_VK, }, Network::Regtest => ConsensusParams { network: network, @@ -179,6 +197,9 @@ impl ConsensusParams { equihash_params: Some((200, 9)), joinsplit_verification_key: regtest_pghr_verification_key(), + + sapling_spend_verifying_key: &SAPLING_SPEND_VK, + sapling_output_verifying_key: &SAPLING_OUTPUT_VK, }, Network::Unitest => ConsensusParams { network: network, @@ -201,6 +222,9 @@ impl ConsensusParams { equihash_params: None, joinsplit_verification_key: unitest_pghr_verification_key(), + + sapling_spend_verifying_key: &SAPLING_SPEND_VK, + sapling_output_verifying_key: &SAPLING_OUTPUT_VK, }, } } diff --git a/res/sapling-output-verifying-key.json b/res/sapling-output-verifying-key.json new file mode 100644 index 00000000..dc454468 --- /dev/null +++ b/res/sapling-output-verifying-key.json @@ -0,0 +1,16 @@ +{ + "alphaG1": "0x0db882cf5db3e8567f16b4db1772d4d1f5a3fe8d62f0df2eb8a5cfa50806702afde8fc25335eb5ec859c2818b2610b2e19ab445dac720bb1f2b0cd3336f7a1acc62bf1b3a321826264dc7e469281e23b218394d598689da04e136878ff9a7897", + "betaG1": "0x014a78a8d17180a37c4ca8fb231f264ab89bd14863777fc1ffe901fd92444365d18f78237612ac38e39f419c32f0824515219ec45c26c1fad530514ed891a0d0043acedf348922102e95b3e6d07e0afa94c58aa41480631fc1ca36e55aae51fd", + "betaG2": "0x0a416b8187450b28f025c421e3ff14d38f9abd9af2f1046b914b53ab37e9aebba683cb25284e5c22fa341129985250a103547de5d005df48265f7cb258162253d56fbc682d106a1ecb07666ebf7524a364e512c37aa62f82d6e7dd4ed8838478104376a98072766c29959358e9cde6a4985618f65ea257e8f288974f4aedde52e5dac2fb7ae5d30eab7cd828a2c8b15f15b16f139f2c33ef33d63befe404e696c97077d17ea42f4ff9d82ec456aaf43914a3d07968111a3a348f157e64c0278a", + "gammaG2": "0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801", + "deltaG1": "0x0f61208393fe783b523d23c911eda2b295231684a8c3b3898d8fce83fa949fd6a25aee7fe4d0d37f7a892bf7c2639d5b1571bb257e41dab703d78f54a71dadec6f37e40d21bb29f5eca47c91078a016cf95fb7e6aa31c1321450202037c69ee7", + "deltaG2": "0x050302fecf4d86671c66ed1ee097efccd2a2add6fd42c9d0a809bb6a3e0f8348bfac6cfa4427c83d5ed1ff844a5b1b1209c069a8a1ccd8c7c22b2a84fede0e53b536cabd7d4c7f0ddc53bec42eeda2b09190d43bcbaece88f7a2a1fc686076d20f2acbc06f28f913a2a77a731d96133aeb5282461cd452a3f3f1d3b63907840dc79b1066e898a335c3a676de9c97507c0c4824b4c9ac0dfc2b1b017e1ebe1b96920a80a7f7e61e39d2f275c51ea8c0b4a6aa86643ee4696af6611d027c58401c", + "ic": [ + "0x04fd67184c37c5240183cff73f52544ecd6b629e775ad6648696f99a431a63ca8b386bdcb94cb1e4c53c4ec0276d3dc50c2bd5d31bf904b30b9d0508d59c915bfaea64830425366257fe599f2eaf2f6650bea1abe4bab1955da88a6860c31b4d", + "0x073143b431c7e8e9323f3693c470fd9a3adf199a31b6e3006c64189bde7a07a316f9f7252d042147ac4c68be3a62a367012534e285c0ac0e01c7c8f9d013f13aa1da0c06777564d76b27ab788fd46ca5a9a9854424c325eab85298044b258da3", + "0x13cca7cdbcfb681f36c0a3d9ab957960b57dd73002f042f7222eec23d5d60a30bf93bd8d0564113c0be8f1baf577ca570088e381355d3f7e8dab653e7a2e758724b39a9d63e4905fbc2927d5246d2e6072ad142f81498e6d5aecdf36200de579", + "0x0ffb9b423f4631fbbeb423d81fe9b244953a2b701eb2f5d96bf97981eb50227705128a401c10789582bbc8648150270f0d77cb44de2f73bdea9e4405174ef3034ce88bc4b02d4e0886b3fa5fb5e6b0b1f404bfa18ad308aadddd2634c9765d5b", + "0x0f91ffe602148dd9a6404f1701af31a48481ef23bf05beed476428178c2f972082d533348904348831ea61ce33bf6ccc0b04fae453962cb4c2634bc75eaecaaf9a034cb30d82968bb08a46403b308ac0be82f5964c0a9631032c363147097ad2", + "0x0936d64376b8cff46bf7a9c3db65b47ad83c1cdf2cea84f2671278d597f8867a3a4a955aaab958169bf96839b923b27a08e7d52742571a73eb842f9c31b8972505079760a63c909f0033ba1331c8131c3eafb8945f2112c405869b54721b014e" + ] +} diff --git a/res/sapling-spend-verifying-key.json b/res/sapling-spend-verifying-key.json new file mode 100644 index 00000000..a5fd74d1 --- /dev/null +++ b/res/sapling-spend-verifying-key.json @@ -0,0 +1,18 @@ +{ + "alphaG1": "0x0db882cf5db3e8567f16b4db1772d4d1f5a3fe8d62f0df2eb8a5cfa50806702afde8fc25335eb5ec859c2818b2610b2e19ab445dac720bb1f2b0cd3336f7a1acc62bf1b3a321826264dc7e469281e23b218394d598689da04e136878ff9a7897", + "betaG1": "0x014a78a8d17180a37c4ca8fb231f264ab89bd14863777fc1ffe901fd92444365d18f78237612ac38e39f419c32f0824515219ec45c26c1fad530514ed891a0d0043acedf348922102e95b3e6d07e0afa94c58aa41480631fc1ca36e55aae51fd", + "betaG2": "0x0a416b8187450b28f025c421e3ff14d38f9abd9af2f1046b914b53ab37e9aebba683cb25284e5c22fa341129985250a103547de5d005df48265f7cb258162253d56fbc682d106a1ecb07666ebf7524a364e512c37aa62f82d6e7dd4ed8838478104376a98072766c29959358e9cde6a4985618f65ea257e8f288974f4aedde52e5dac2fb7ae5d30eab7cd828a2c8b15f15b16f139f2c33ef33d63befe404e696c97077d17ea42f4ff9d82ec456aaf43914a3d07968111a3a348f157e64c0278a", + "gammaG2": "0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801", + "deltaG1": "0x0b9d259333f82bb97fd52701bc1e2bf04755c376a19aaadef0ebe8466a4f30862accb63e627e73fab29b929d6106795f0fd0296b4a6945694850071bf5ca887cd39fd803f34c00cf2965cf5218cf58d66fb7eecaf1d17055eb465bf3584fa638", + "deltaG2": "0x077284b5eb4a16c08a9b4810bcb80336367adbc7425bb37cb5dfb41977a78e152125fad6cb858a38edbe25ad2a34f7f019841965c9831072efe2b9739766edd008283f835d09206e51bfc1f97d9b2eb1c8b726195b2ffb5e5506fccb8f93e6060bdfb00b4aa6237784e645e0cda032243c65b73634f508403ae8297e4b894d313bce5b96db0a3311c28182d3a798a02f185e77ca7a24fe2a1a9447c93de31bd3e481fa9f623ae8d27baf78e6547e83b3d99a6eda5b455b09469c0d6ecd442ba2", + "ic": [ + "0x12daedb4014a51305d017ac2b895d7dae97d813b243a7d33ed0135ad58a7fbc80b8ebc25ee1ff1af7c9c21b4e4fdf5b213567233a168b9ec9b81ee411aa9937f9f89e847c26810bd5a177dfb9ad3a6a3ac1b46ddffcc32ce3b8277200d9bfb08", + "0x175e64129e1d2394bca57349b2845248b7e92f41acdbb23ca1532898ca29fe08ce2a0ff8795c35fe711d6959ab1ba1051617085c03e25b57a689f5ecfa2ecda5ab0f5eff00079ea957283a482cf7de40839ed0683603af30768a3206ea816fd0", + "0x02198d34ecd17c02740fed470c026a5e4e5fe2ad22fb5ba7b68eac3fc82ef37dd921f7ae8e1773f4c164b447f550760101a1e6108959ce1c98a64b96bd76c474360c630cc5977267855288c0f9fca4a205604a77aa655507169929d7fb52eb95", + "0x071151c8ae74d6bcb6e24b466c59d242009deaf65cd82fb9b662c867a7c88377c115e90f8bb9bda528b9f1541ac15d850befd35dd79811c12a7065a075bb2a412e28592cdd307e75c4231c8ce3e3f8b6ac1a5a87666c55f4f8bc2d770ea5259b", + "0x0f008453cef037d48e211c6eea1610304c9e1aedc28b8208b9f16ba471156a9ab870bbba871f9b26c3a31a67268df26b11c9e64194deb7b9b095903271d42753f1d66fe66d11a82f8e12096333b06d6f35e675ff650189cbb9a010776fd73a95", + "0x0f51fec047929e2802c2840269daa7a7f082db690e6a49c16a749df8ab82a02eb7356f70dc8c7e68682695dfc815491e0172a3966c1e2effc8bc7d0fb462f033cdb93065c9e09d2865c99b494b5fabbddc86194a4b0109a4da58152990e68b93", + "0x183d15a395bd62e4844440f3e9612db6315fd16f0b395155205464ecd9546477d797d353bc9c045acd18c5d9498071c416637dd87e22f4e26bfbf7b94d6cc073ab5cda9423f9f8a30cc024529ff8fea2c7338d34fec14a5f390b4fee213d4e41", + "0x02d64a3c00693baa032b101608c51fc37aa1e99bf255bd4f293592ffd6bf2ffadfc842fc1dcf3b7142eeb3f8edce90e61885b1f5779fc3c818df56d05a48f96f9710652a8b43833a35fd947b9cf3081bc245bf6cf4c380c9b702e7b0b8338d86" + ] +} diff --git a/verification/Cargo.toml b/verification/Cargo.toml index 00c6ec63..5adb1b7c 100644 --- a/verification/Cargo.toml +++ b/verification/Cargo.toml @@ -8,11 +8,8 @@ time = "0.1" log = "0.4" rayon = "1.0" parking_lot = "0.4" -bellman = "0.1" byteorder = "1.2" lazy_static = "1.2.0" -pairing = "0.14.2" -sapling-crypto = { git = "https://github.com/zcash-hackworks/sapling-crypto.git", rev = "21084bde2019c04bd34208e63c3560fe2c02fb0e" } primitives = { path = "../primitives" } chain = { path = "../chain" } serialization = { path = "../serialization" } @@ -26,3 +23,4 @@ rustc-hex = "2" rand = "0.4" test-data = { path = "../test-data" } db = { path = "../db" } +assert_matches = "1.3.0" diff --git a/verification/src/accept_transaction.rs b/verification/src/accept_transaction.rs index a219a4c8..0024005f 100644 --- a/verification/src/accept_transaction.rs +++ b/verification/src/accept_transaction.rs @@ -1,4 +1,5 @@ use ser::Serializable; +use crypto::Groth16VerifyingKey; use storage::{TransactionMetaProvider, TransactionOutputProvider, Nullifier, NullifierTag, NullifierTracker}; use network::{ConsensusParams}; use script::{Script, verify_script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner, SighashBase}; @@ -10,7 +11,6 @@ use canon::CanonTransaction; use constants::COINBASE_MATURITY; use error::TransactionError; use primitives::hash::H256; -use chain::SAPLING_TX_VERSION_GROUP_ID; use VerificationLevel; pub struct TransactionAcceptor<'a> { @@ -21,8 +21,8 @@ pub struct TransactionAcceptor<'a> { pub overspent: TransactionOverspent<'a>, pub double_spent: TransactionDoubleSpend<'a>, pub eval: TransactionEval<'a>, - pub join_split: Option>, - pub sapling_valid: TransactionSaplingValid<'a>, + pub join_split: JoinSplitVerification<'a>, + pub sapling: SaplingVerification<'a>, } impl<'a> TransactionAcceptor<'a> { @@ -47,13 +47,16 @@ impl<'a> TransactionAcceptor<'a> { bip30: TransactionBip30::new_for_sync(transaction, meta_store), missing_inputs: TransactionMissingInputs::new(transaction, output_store, transaction_index), maturity: TransactionMaturity::new(transaction, meta_store, height), - sapling_valid: TransactionSaplingValid::new(transaction), overspent: TransactionOverspent::new(transaction, output_store), double_spent: TransactionDoubleSpend::new(transaction, output_store), eval: TransactionEval::new(transaction, output_store, consensus, verification_level, height, time, deployments), - join_split: transaction.join_split().map(|js| { - JoinSplitVerification::new(transaction.raw.version_group_id, js, nullifier_tracker) - }), + join_split: JoinSplitVerification::new(transaction, nullifier_tracker), + sapling: SaplingVerification::new( + nullifier_tracker, + consensus.sapling_spend_verifying_key, + consensus.sapling_output_verifying_key, + transaction, + ), } } @@ -68,7 +71,8 @@ impl<'a> TransactionAcceptor<'a> { // to make sure we're using the sighash-cache, let's make all sighash-related // calls from single checker && pass sighash to other checkers let sighash = self.eval.check()?; - self.sapling_valid.check(sighash)?; + self.join_split.check()?; + self.sapling.check(sighash)?; Ok(()) } @@ -472,11 +476,11 @@ impl<'a> TransactionSize<'a> { /// Check the joinsplit proof of the transaction pub struct JoinSplitProof<'a> { - _join_split: &'a chain::JoinSplit, + _transaction: CanonTransaction<'a>, } impl<'a> JoinSplitProof<'a> { - fn new(join_split: &'a chain::JoinSplit) -> Self { JoinSplitProof { _join_split: join_split }} + fn new(transaction: CanonTransaction<'a>) -> Self { JoinSplitProof { _transaction: transaction }} fn check(&self) -> Result<(), TransactionError> { // TODO: Zero-knowledge proof @@ -484,49 +488,28 @@ impl<'a> JoinSplitProof<'a> { } } -/// Check if nullifiers are unique -pub struct Nullifiers<'a> { - tag: NullifierTag, +/// Check if join split nullifiers are unique +pub struct JoinSplitNullifiers<'a> { tracker: &'a NullifierTracker, - join_split: &'a chain::JoinSplit, -} - -impl<'a> Nullifiers<'a> { - fn new(tag: NullifierTag, tracker: &'a NullifierTracker, join_split: &'a chain::JoinSplit) -> Self { - Nullifiers { tag: tag, tracker: tracker, join_split: join_split } - } - - fn check(&self) -> Result<(), TransactionError> { - for description in self.join_split.descriptions.iter() { - for nullifier in &description.nullifiers[..] { - let check = Nullifier::new(self.tag, H256::from(&nullifier[..])); - - if self.tracker.contains_nullifier(check) { - return Err(TransactionError::JoinSplitDeclared(*check.hash())) - } - } - } - - Ok(()) - } -} - -/// Checks that sapling signatures/proofs are valid. -pub struct TransactionSaplingValid<'a> { transaction: CanonTransaction<'a>, } -impl<'a> TransactionSaplingValid<'a> { - fn new(transaction: CanonTransaction<'a>) -> Self { - TransactionSaplingValid { - transaction: transaction, - } +impl<'a> JoinSplitNullifiers<'a> { + fn new(tracker: &'a NullifierTracker, transaction: CanonTransaction<'a>) -> Self { + JoinSplitNullifiers { tracker: tracker, transaction: transaction } } - fn check(&self, sighash: H256) -> Result<(), TransactionError> { - if let Some(sapling) = self.transaction.raw.sapling.as_ref() { - accept_sapling(&sighash, sapling) - .map_err(|_| TransactionError::InvalidSapling)?; + fn check(&self) -> Result<(), TransactionError> { + if let Some(ref join_split) = self.transaction.raw.join_split { + for description in join_split.descriptions.iter() { + for nullifier in &description.nullifiers[..] { + let check = Nullifier::new(NullifierTag::Sprout, H256::from(&nullifier[..])); + + if self.tracker.contains_nullifier(check) { + return Err(TransactionError::JoinSplitDeclared(*check.hash())) + } + } + } } Ok(()) @@ -536,19 +519,16 @@ impl<'a> TransactionSaplingValid<'a> { /// Join split verification pub struct JoinSplitVerification<'a> { proof: JoinSplitProof<'a>, - nullifiers: Nullifiers<'a>, + nullifiers: JoinSplitNullifiers<'a>, } impl<'a> JoinSplitVerification<'a> { - pub fn new(tx_version_group: u32, join_split: &'a chain::JoinSplit, tracker: &'a NullifierTracker) + pub fn new(transaction: CanonTransaction<'a>, tracker: &'a NullifierTracker) -> Self { - let tag = if tx_version_group == SAPLING_TX_VERSION_GROUP_ID - { NullifierTag::Sapling } else { NullifierTag::Sprout }; - JoinSplitVerification { - proof: JoinSplitProof::new(join_split), - nullifiers: Nullifiers::new(tag, tracker, join_split), + proof: JoinSplitProof::new(transaction), + nullifiers: JoinSplitNullifiers::new(tracker, transaction), } } @@ -558,11 +538,96 @@ impl<'a> JoinSplitVerification<'a> { } } +/// Check if Sapling nullifiers are unique +pub struct SaplingNullifiers<'a> { + tracker: &'a NullifierTracker, + transaction: CanonTransaction<'a>, +} + +impl<'a> SaplingNullifiers<'a> { + fn new(tracker: &'a NullifierTracker, transaction: CanonTransaction<'a>) -> Self { + SaplingNullifiers { tracker: tracker, transaction: transaction } + } + + fn check(&self) -> Result<(), TransactionError> { + if let Some(ref sapling) = self.transaction.raw.sapling { + for spend in &sapling.spends { + let check = Nullifier::new(NullifierTag::Sapling, H256::from(&spend.nullifier[..])); + + if self.tracker.contains_nullifier(check) { + return Err(TransactionError::SaplingDeclared(*check.hash())) + } + } + } + + Ok(()) + } +} + + +/// Checks that sapling signatures/proofs are valid. +pub struct SaplingProof<'a> { + spend_vk: &'a Groth16VerifyingKey, + output_vk: &'a Groth16VerifyingKey, + transaction: CanonTransaction<'a>, +} + +impl<'a> SaplingProof<'a> { + fn new( + spend_vk: &'a Groth16VerifyingKey, + output_vk: &'a Groth16VerifyingKey, + transaction: CanonTransaction<'a>, + ) -> Self { + SaplingProof { + spend_vk, + output_vk, + transaction: transaction, + } + } + + fn check(&self, sighash: H256) -> Result<(), TransactionError> { + if let Some(sapling) = self.transaction.raw.sapling.as_ref() { + accept_sapling(self.spend_vk, self.output_vk, &sighash, sapling) + .map_err(|_| TransactionError::InvalidSapling)?; + } + + Ok(()) + } +} + +/// Sapling verification +pub struct SaplingVerification<'a> { + proof: SaplingProof<'a>, + nullifiers: SaplingNullifiers<'a>, +} + +impl<'a> SaplingVerification<'a> { + pub fn new( + tracker: &'a NullifierTracker, + spend_vk: &'a Groth16VerifyingKey, + output_vk: &'a Groth16VerifyingKey, + transaction: CanonTransaction<'a> + ) -> Self + { + SaplingVerification { + proof: SaplingProof::new(spend_vk, output_vk, transaction), + nullifiers: SaplingNullifiers::new(tracker, transaction), + } + } + + pub fn check(&self, sighash: H256) -> Result<(), TransactionError> { + self.proof.check(sighash)?; + self.nullifiers.check() + } +} + #[cfg(test)] mod tests { - use chain::Transaction; + use chain::{Transaction, Sapling}; + use db::BlockChainDatabase; use script::{Script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner, verify_script}; + use super::*; #[test] fn join_split() { @@ -593,4 +658,34 @@ mod tests { .verify_p2sh(true); assert_eq!(verify_script(&input_script, &output_script, &flags, &mut checker), Ok(())); } + + #[test] + fn sapling_nullifiers_works() { + let storage = BlockChainDatabase::init_test_chain(vec![test_data::genesis().into()]); + + let tx: Transaction = test_data::TransactionBuilder::with_sapling(Sapling { + spends: vec![Default::default()], + ..Default::default() + }).into(); + let block = test_data::block_builder() + .header().parent(test_data::genesis().hash()).build() + .transaction().coinbase().build() + .with_transaction(tx.clone()) + .build(); + let tx = tx.into(); + let block_hash = block.hash(); + + // when nullifier is not in the db + assert_eq!(SaplingNullifiers::new(&storage, CanonTransaction::new(&tx)).check(), Ok(())); + + // insert nullifier into db + storage.insert(block.into()).unwrap(); + storage.canonize(&block_hash).unwrap(); + + // when nullifier is in the db + assert_eq!( + SaplingNullifiers::new(&storage, CanonTransaction::new(&tx)).check(), + Err(TransactionError::SaplingDeclared(Default::default())) + ); + } } diff --git a/verification/src/error.rs b/verification/src/error.rs index 82f28307..29d87a61 100644 --- a/verification/src/error.rs +++ b/verification/src/error.rs @@ -136,5 +136,7 @@ pub enum TransactionError { JoinSplitVersionInvalid, /// Transaction sapling verification has failed. InvalidSapling, + /// Sapling nullifier already revealed earlier in the chain. + SaplingDeclared(H256), } diff --git a/verification/src/lib.rs b/verification/src/lib.rs index ffa51c66..b65dd0d6 100644 --- a/verification/src/lib.rs +++ b/verification/src/lib.rs @@ -56,15 +56,12 @@ extern crate time; extern crate log; extern crate parking_lot; extern crate rayon; -extern crate bellman; extern crate byteorder; #[macro_use] extern crate lazy_static; #[cfg(test)] extern crate rand; extern crate rustc_hex as hex; -extern crate pairing; -extern crate sapling_crypto; extern crate storage; extern crate chain; @@ -76,6 +73,10 @@ extern crate bitcrypto as crypto; #[cfg(test)] extern crate db; +#[cfg(test)] +#[macro_use] +extern crate assert_matches; + pub mod constants; mod canon; mod deployments; diff --git a/verification/src/sapling.rs b/verification/src/sapling.rs index b5275c32..ba619e89 100644 --- a/verification/src/sapling.rs +++ b/verification/src/sapling.rs @@ -1,20 +1,21 @@ use std::io::Error as IoError; use chain::{Sapling, SaplingSpendDescription, SaplingOutputDescription}; -use pairing::{bls12_381::{Bls12, Fr, FrRepr}, PrimeField, PrimeFieldRepr, PrimeFieldDecodingError}; -use bellman::{SynthesisError, groth16::{verify_proof, PreparedVerifyingKey, Proof,}}; - -use sapling_crypto::{circuit::multipack, redjubjub::{self, Signature}}; -use sapling_crypto::jubjub::{edwards,fs::FsRepr, FixedGenerators, JubjubBls12, JubjubParams, Unknown}; +use crypto::{ + Groth16VerifyingKey, + pairing::{bls12_381::{Bls12, Fr, FrRepr}, PrimeField, PrimeFieldRepr, PrimeFieldDecodingError}, + bellman::{SynthesisError, groth16::{verify_proof, Proof}}, + sapling_crypto::{circuit::multipack, redjubjub::{self, Signature}}, + sapling_crypto::jubjub::{edwards,fs::FsRepr, FixedGenerators, JubjubBls12, JubjubParams, Unknown} +}; type Point = edwards::Point; lazy_static! { static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() }; - static ref SAPLING_SPEND_VK: Option> = None; - static ref SAPLING_OUTPUT_VK: Option> = None; } /// Errors that could occur during sapling verification. +#[derive(Debug)] pub enum Error { /// Spend description verification error. Spend(usize, SpendError), @@ -22,25 +23,27 @@ pub enum Error { Output(usize, OutputError), /// Invalid balance value. InvalidBalanceValue, - /// Error deserializing/verifying binding_sig. - BindingSig(SignatureError), + /// Error verifying binding_sig. + BadBindingSignature, } /// Errors that can occur during spend description verification. +#[derive(Debug)] pub enum SpendError { /// Error deserializing value commitment. ValueCommitment(PointError), /// Error deserializing anchor. Anchor(PrimeFieldDecodingError), /// Error deserializing randomized key. - RandomizedKey(PublicKeyError), - /// Error deserializing/verifying spend_auth_sig. - SpendAuthSig(SignatureError), + RandomizedKey(PointError), + /// Error verifying spend_auth_sig. + BadSpendAuthSig, /// Error deserializing/verifying zk-proof. Proof(ProofError), } /// Errors that can occur during output description verification. +#[derive(Debug)] pub enum OutputError { /// Error deserializing value commitment. ValueCommitment(PointError), @@ -53,6 +56,7 @@ pub enum OutputError { } /// Errors that can occur during point deserialization. +#[derive(Debug)] pub enum PointError { /// The point is invalid. Invalid(IoError), @@ -60,23 +64,8 @@ pub enum PointError { SmallOrder, } -/// Errors that can occur during public key deserialization. -pub enum PublicKeyError { - /// The public key is invalid. - Invalid(IoError), - /// The point corresponding to the public key MUST NOT be small order. - SmallOrder, -} - -/// Error that can occur during signature deserialization/verification. -pub enum SignatureError { - /// The signature is invalid. - Invalid(IoError), - /// The signature verifciation has failed. - Failed, -} - /// Proof verification error. +#[derive(Debug)] pub enum ProofError { /// The proof is invalid. Invalid(IoError), @@ -87,19 +76,24 @@ pub enum ProofError { } /// Verify sapling proofs/signatures validity. -pub fn accept_sapling(sighash: &[u8; 32], sapling: &Sapling) -> Result<(), Error> { +pub fn accept_sapling( + spend_vk: &Groth16VerifyingKey, + output_vk: &Groth16VerifyingKey, + sighash: &[u8; 32], + sapling: &Sapling, +) -> Result<(), Error> { // binding verification key is not encoded explicitly in transaction and must be recalculated let mut total = edwards::Point::zero(); // verify each spend description for (idx, spend) in sapling.spends.iter().enumerate() { - accept_spend(sighash, &mut total, spend) + accept_spend(spend_vk, sighash, &mut total, spend) .map_err(|err| Error::Spend(idx, err))?; } // verify each output description for (idx, output) in sapling.outputs.iter().enumerate() { - accept_output(&mut total, output) + accept_output(output_vk, &mut total, output) .map_err(|err| Error::Output(idx, err))?; } @@ -108,7 +102,12 @@ pub fn accept_sapling(sighash: &[u8; 32], sapling: &Sapling) -> Result<(), Error } /// Verify sapling spend description. -fn accept_spend(sighash: &[u8; 32], total: &mut Point, spend: &SaplingSpendDescription) -> Result<(), SpendError> { +fn accept_spend( + spend_vk: &Groth16VerifyingKey, + sighash: &[u8; 32], + total: &mut Point, + spend: &SaplingSpendDescription, +) -> Result<(), SpendError> { // deserialize and check value commitment let value_commitment = require_non_small_order_point(&spend.value_commitment) .map_err(SpendError::ValueCommitment)?; @@ -127,18 +126,18 @@ fn accept_spend(sighash: &[u8; 32], total: &mut Point, spend: &SaplingSpendDescr // deserialize and check randomized key let randomized_key = redjubjub::PublicKey::::read(&spend.randomized_key[..], &JUBJUB) - .map_err(|err| SpendError::RandomizedKey(PublicKeyError::Invalid(err)))?; + .map_err(|err| SpendError::RandomizedKey(PointError::Invalid(err)))?; if is_small_order(&randomized_key.0) { - return Err(SpendError::RandomizedKey(PublicKeyError::SmallOrder)); + return Err(SpendError::RandomizedKey(PointError::SmallOrder)); } // deserialize the signature let spend_auth_sig = Signature::read(&spend.spend_auth_sig[..]) - .map_err(|err| SpendError::SpendAuthSig(SignatureError::Invalid(err)))?; + .expect("only could fail if length of passed buffer != 64; qed"); // verify the spend_auth_sig if !randomized_key.verify(&data_to_be_signed, &spend_auth_sig, FixedGenerators::SpendingKeyGenerator, &JUBJUB) { - return Err(SpendError::SpendAuthSig(SignatureError::Failed)); + return Err(SpendError::BadSpendAuthSig); } // Add the nullifier through multiscalar packing @@ -164,8 +163,7 @@ fn accept_spend(sighash: &[u8; 32], total: &mut Point, spend: &SaplingSpendDescr .map_err(|err| SpendError::Proof(ProofError::Invalid(err)))?; // check the proof - let verification_key = SAPLING_SPEND_VK.as_ref().expect("TODO"); - let is_verification_ok = verify_proof(verification_key, &zkproof, &public_input[..]) + let is_verification_ok = verify_proof(&spend_vk.0, &zkproof, &public_input[..]) .map_err(|err| SpendError::Proof(ProofError::Synthesis(err)))?; if !is_verification_ok { return Err(SpendError::Proof(ProofError::Failed)); @@ -174,7 +172,11 @@ fn accept_spend(sighash: &[u8; 32], total: &mut Point, spend: &SaplingSpendDescr Ok(()) } -fn accept_output(total: &mut Point, output: &SaplingOutputDescription) -> Result<(), OutputError> { +fn accept_output( + output_vk: &Groth16VerifyingKey, + total: &mut Point, + output: &SaplingOutputDescription, +) -> Result<(), OutputError> { // deserialize and check value commitment let value_commitment = require_non_small_order_point(&output.value_commitment) .map_err(OutputError::ValueCommitment)?; @@ -206,8 +208,7 @@ fn accept_output(total: &mut Point, output: &SaplingOutputDescription) -> Result .map_err(|err| OutputError::Proof(ProofError::Invalid(err)))?; // check the proof - let verification_key = SAPLING_OUTPUT_VK.as_ref().expect("TODO"); - let is_verification_ok = verify_proof(verification_key, &zkproof, &public_input[..]) + let is_verification_ok = verify_proof(&output_vk.0, &zkproof, &public_input[..]) .map_err(|err| OutputError::Proof(ProofError::Synthesis(err)))?; if !is_verification_ok { return Err(OutputError::Proof(ProofError::Failed)); @@ -234,13 +235,13 @@ fn accept_sapling_final(sighash: &[u8; 32], total: Point, sapling: &Sapling) -> // deserialize the binding signature let binding_sig = Signature::read(&sapling.binding_sig[..]) - .map_err(|err| Error::BindingSig(SignatureError::Invalid(err)))?; + .expect("only could fail if length of passed buffer != 64; qed"); // check the binding signature let is_verification_ok = binding_verification_key .verify(&data_to_be_signed, &binding_sig, FixedGenerators::ValueCommitmentRandomness, &JUBJUB); if !is_verification_ok { - return Err(Error::BindingSig(SignatureError::Failed)); + return Err(Error::BadBindingSignature); } Ok(()) @@ -296,5 +297,239 @@ fn is_small_order(point: &Point) -> bool { #[cfg(test)] mod tests { - // TODO: detailed tests when sighash + verification keys are available + extern crate test_data; + + use chain::Transaction; + use script::{TransactionInputSigner, SighashBase}; + use super::*; + + // tx: https://zcash.blockexplorer.com/tx/bd4fe81c15cfbd125f5ca6fe51fb5ac4ef340e64a36f576a6a09f7528eb2e176 + fn test_tx() -> Transaction { + "0400008085202f8900000000000072da060010270000000000000148b1c0668fce604361fbb1b89bbd76f8fee09b51a9dc0fdfcf6c6720cd596083d970234fcc0e9a70fdfed82d32fbb9ca92c9c5c3bad5daad9ac62b5bf4255817ee5bc95a9af453bb9cc7e2c544aa29efa20011a65b624998369c849aa8f0bc83d60e7902a3cfe6eeaeb8d583a491de5982c5ded29e64cd8f8fac594a5bb4f2838e6c30876e36a18d8d935238815c8d9205a4f1f523ff76b51f614bff1064d1c5fa0a27ec0c43c8a6c2714e7234d32e9a8934a3e9c0f74f1fdac2ddf6be3b13bc933b0478cae556a2d387cc23b05e8b0bd53d9e838ad2d2cb31daccefe256087511b044dfae665f0af0fa968edeea4cbb437a8099724159471adf7946eec434cccc1129f4d1e31d7f3f8be524226c65f28897d3604c14efb64bea6a889b2705617432927229dfa382e78c0ace31cc158fbf3ec1597242955e45af1ee5cfaffd789cc80dc53d6b18d42033ec2c327170e2811fe8ec00feadeb1033eb48ab24a6dce2480ad428be57c4619466fc3181ece69b914fed30566ff853250ef19ef7370601f4c24b0125e4059eec61f63ccbe277363172f2bdee384412ea073c5aca06b94e402ba3a43e15bd9c65bbfb194c561c24a031dec43be95c59eb6b568c176b1038d5b7b057dc032488335284adebfb6607e6a995b7fa418f13c8a61b343e5df44faa1050d9d76550748d9efebe01da97ade5937afd5f007ed26e0af03f283611655e91bc6a4857f66a57a1584ff687c4baf725f4a1b32fae53a3e6e8b98bca319bb1badb704c9c1a04f401f33d813d605eef6943c2c52dbc85ab7081d1f8f69d3202aae281bf42336a949a12a7dbbd22abdd6e92996282ebd69033c22cb0539d97f83636d6a8232209a7411e8b03bef180d83e608563ea2d0becff56dc996c2049df054961bfb21b7cbef5049a7dacc18f2c977aa1b2d48291abc19c3c8ea25d2e61901048354b17ce952f6f2248cf3a0eb54c19b507b41d7281c3d227e2b142ff695d8b925a4bb942ed9492a73a17468a8332a367fd16295420bdca6c04d380271f40440709998fce3a3af3e1e505f5402e5dd464dd179cb0eede3d494a95b84d2fb2eb5abb425cf2c712af999c65259c4782a5ec97388324c67738908a5ba43b6db62a10f50cddf9b5039123437c74165921ac8cf4f13292a216baef9d00bd544106b52755986c98a462ade1149f69367e926d88eb92798c0e56cd19a1bcf264fd93293033b758da65c7901eb5b4a17ee265a3312dbc477868da0057e1b3cbf47726dead6ecfcc8e1044c6f311ff0fc83192dc2f75a89626ba33364dac747b63ff3c8337e00332c8783ba9c8dc13cdf0750d7adc3926fbe1279017d50adba35c38c5b810f73abe5d759cd7fb650f6b0a1f78dc1f62fd017090ff4de4cf54c883752ddda68083d4617ed2c38bab8da313965dd3f7b755aec23a2d9e2965d08d2134827a72ffb3bd65b1fd5410da105bfba7a74ddff0928a654aca1ee211ac9dce8019ddcbb52263ce44b2544a314355c1e8c8543f3ed3e883e7a7a8f9e3c7c11f41ab9069854fb21e9b3660a860df19d289d54b29d82522b32d187cde6261eb0a429c3994dff6f37b9ab9102281223e3cd584790a909e05ba0ea1a2d9aef8e571986e98e09312dccaf8e739d718a1edd217dc4c8a5c8a650015405b592a7c674a451d7d1686c7ea6d93e74a8fe4ade12b679ac780457f08a79bfbf96dcf7eefe9a39b99f1ae39d2c5f86aadf156b7d5ce4b2733f307cfe1e1ff6de0ff2006d9cba535b0c40dfb7a98399cdff8e681fc38c7b9aa94ee5eb89432e28d94ee27f238776ba964a87caf58eddbb64771e64de094305a8eb848d2d9ad6373903687d22170f48f1ae8d714514034ee2733857af4747312bb006e6ce3918ede8c730bacc7821b81c1b93bb50b219e79e8e0d74531ed18c1145632d9847d38783b49141ac5353aaa7d125fb2934e681467e16b28090978e74e0b".into() + } + + fn compute_sighash(tx: Transaction) -> [u8; 32] { + let signer: TransactionInputSigner = tx.into(); + signer.signature_hash(&mut None, None, 0, &From::from(vec![]), SighashBase::All.into(), 0x76b809bb).into() + } + + fn run_accept_sapling(tx: Transaction) -> Result<(), Error> { + let spend_vk = crypto::load_sapling_spend_verifying_key().unwrap(); + let output_vk = crypto::load_sapling_output_verifying_key().unwrap(); + + let sighash = compute_sighash(tx.clone()); + let sapling = tx.sapling.unwrap(); + + accept_sapling(&spend_vk, &output_vk, &sighash, &sapling) + } + + fn swap_xy(point: [u8; 32]) -> [u8; 32] { + let mut new_point = [0; 32]; + new_point[..16].copy_from_slice(&point[16..]); + new_point[16..].copy_from_slice(&point[..16]); + new_point + } + + fn small_order_point() -> [u8; 32] { + [0; 32] + } + + fn not_in_field_number() -> [u8; 32] { + [0xFF; 32] + } + + fn bad_signature() -> [u8; 64] { + [0; 64] + } + + fn bad_proof() -> [u8; 192] { + [0; 192] + } + + fn bad_verifying_key() -> Groth16VerifyingKey { + use crypto::pairing::{CurveAffine, bls12_381::{G1Affine, G2Affine}}; + use crypto::bellman::groth16::{VerifyingKey, prepare_verifying_key}; + + Groth16VerifyingKey(prepare_verifying_key(&VerifyingKey { + alpha_g1: G1Affine::zero(), + beta_g1: G1Affine::zero(), + beta_g2: G2Affine::zero(), + gamma_g2: G2Affine::zero(), + delta_g1: G1Affine::zero(), + delta_g2: G2Affine::zero(), + ic: vec![], + })) + } + + #[test] + fn accept_sapling_works() { + run_accept_sapling(test_tx()).unwrap(); + } + + #[test] + fn accept_spend_fails() { + let spend_vk = crypto::load_sapling_spend_verifying_key().unwrap(); + let sighash = compute_sighash(test_tx()); + let sapling = test_tx().sapling.unwrap(); + let mut total = edwards::Point::zero(); + + // when value commitment isn't an on-curve point + let mut spend = sapling.spends[0].clone(); + spend.value_commitment = swap_xy(spend.value_commitment); + assert_matches!( + accept_spend(&spend_vk, &sighash, &mut total, &spend), + Err(SpendError::ValueCommitment(PointError::Invalid(_))) + ); + + // when value commitment is a small order point + let mut spend = sapling.spends[0].clone(); + spend.value_commitment = small_order_point(); + assert_matches!( + accept_spend(&spend_vk, &sighash, &mut total, &spend), + Err(SpendError::ValueCommitment(PointError::SmallOrder)) + ); + + // when anchor is not in field + let mut spend = sapling.spends[0].clone(); + spend.anchor = not_in_field_number(); + assert_matches!( + accept_spend(&spend_vk, &sighash, &mut total, &spend), + Err(SpendError::Anchor(_)) + ); + + // when randomized key isn't represented by an on-curve point + let mut spend = sapling.spends[0].clone(); + spend.randomized_key = swap_xy(spend.randomized_key); + assert_matches!( + accept_spend(&spend_vk, &sighash, &mut total, &spend), + Err(SpendError::RandomizedKey(PointError::Invalid(_))) + ); + + // when randomized key is represented by a small order point + let mut spend = sapling.spends[0].clone(); + spend.randomized_key = small_order_point(); + assert_matches!( + accept_spend(&spend_vk, &sighash, &mut total, &spend), + Err(SpendError::RandomizedKey(PointError::SmallOrder)) + ); + + // when spend auth signature verification fails + let mut spend = sapling.spends[0].clone(); + spend.spend_auth_sig = bad_signature(); + assert_matches!( + accept_spend(&spend_vk, &sighash, &mut total, &spend), + Err(SpendError::BadSpendAuthSig) + ); + + // when proof is failed to deserialize + let mut spend = sapling.spends[0].clone(); + spend.zkproof = bad_proof(); + assert_matches!( + accept_spend(&spend_vk, &sighash, &mut total, &spend), + Err(SpendError::Proof(ProofError::Invalid(_))) + ); + + // when proof isn't compatible with verifying key + assert_matches!( + accept_spend(&bad_verifying_key(), &sighash, &mut total, &sapling.spends[0]), + Err(SpendError::Proof(ProofError::Synthesis(_))) + ); + + // when proof verification has failed + let mut spend = sapling.spends[0].clone(); + spend.nullifier = [0; 32]; + assert_matches!( + accept_spend(&spend_vk, &sighash, &mut total, &spend), + Err(SpendError::Proof(ProofError::Failed)) + ); + } + + #[test] + fn accept_output_fails() { + let output_vk = crypto::load_sapling_output_verifying_key().unwrap(); + let sapling = test_tx().sapling.unwrap(); + let mut total = edwards::Point::zero(); + + // when value commitment isn't an on-curve point + let mut output = sapling.outputs[0].clone(); + output.value_commitment = swap_xy(sapling.spends[0].value_commitment); + assert_matches!( + accept_output(&output_vk, &mut total, &output), + Err(OutputError::ValueCommitment(PointError::Invalid(_))) + ); + + // when value commitment is a small order point + let mut output = sapling.outputs[0].clone(); + output.value_commitment = small_order_point(); + assert_matches!( + accept_output(&output_vk, &mut total, &output), + Err(OutputError::ValueCommitment(PointError::SmallOrder)) + ); + + // when note commitment is not in field + let mut output = sapling.outputs[0].clone(); + output.note_commitment = not_in_field_number(); + assert_matches!( + accept_output(&output_vk, &mut total, &output), + Err(OutputError::NoteCommitment(_)) + ); + + // when empeheral key isn't represented by an on-curve point + let mut output = sapling.outputs[0].clone(); + output.ephemeral_key = swap_xy(output.ephemeral_key); + assert_matches!( + accept_output(&output_vk, &mut total, &output), + Err(OutputError::EphemeralKey(PointError::Invalid(_))) + ); + + // when empeheral key is represented by a small order point + let mut output = sapling.outputs[0].clone(); + output.ephemeral_key = small_order_point(); + assert_matches!( + accept_output(&output_vk, &mut total, &output), + Err(OutputError::EphemeralKey(PointError::SmallOrder)) + ); + + // when proof is failed to deserialize + let mut output = sapling.outputs[0].clone(); + output.zkproof = bad_proof(); + assert_matches!( + accept_output(&output_vk, &mut total, &output), + Err(OutputError::Proof(ProofError::Invalid(_))) + ); + + // when proof isn't compatible with verifying key + assert_matches!( + accept_output(&bad_verifying_key(), &mut total, &sapling.outputs[0]), + Err(OutputError::Proof(ProofError::Synthesis(_))) + ); + + // when proof verification has failed + let mut output = sapling.outputs[0].clone(); + output.note_commitment = output.value_commitment.clone(); + assert_matches!( + accept_output(&output_vk, &mut total, &output), + Err(OutputError::Proof(ProofError::Failed)) + ); + } + + #[test] + fn accept_sapling_final_fails() { + let sighash = compute_sighash(test_tx().clone()); + let sapling = test_tx().sapling.unwrap(); + + // when total value is -i64::MAX + let mut bad_sapling = sapling.clone(); + bad_sapling.balancing_value = ::std::i64::MIN; + assert_matches!( + accept_sapling_final(&sighash, Point::zero(), &bad_sapling), + Err(Error::InvalidBalanceValue) + ); + + // when proof verification has failed + assert_matches!( + accept_sapling_final(&sighash, Point::zero(), &sapling), + Err(Error::BadBindingSignature) + ); + } }