From 3296909c82ce20c587c8919646d96b01cc10203e Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 24 Apr 2024 11:57:00 -0500 Subject: [PATCH 01/22] Fix Sapling spend parsing --- CHANGELOG.md | 1 + librustzcash-wrapper/src/lib.rs | 16 ++++++++-------- test/Spec.hs | 26 +++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7d844c..e077efd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Orchard note nullifier calculation +- Sapling spend field parsing ## [0.5.5.2] diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index e42f374..ab7c934 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -401,9 +401,9 @@ impl ToHaskell for HSBundle { impl HSBundle { pub fn from_bundle(sb: &SaplingBundle) -> HSBundle { - let s = Cursor::new(Vec::new()); - sb.authorization().binding_sig.write(s.clone()).unwrap(); - return HSBundle {empty: false, spends: Hspend::pack(sb.shielded_spends()) , outputs: HshieldedOutput::pack(sb.shielded_outputs()) , value: i64::from(sb.value_balance()) , sig: s.into_inner() } + let mut s: Vec = Vec::new(); + sb.authorization().binding_sig.write(&mut s).unwrap(); + return HSBundle {empty: false, spends: Hspend::pack(sb.shielded_spends()) , outputs: HshieldedOutput::pack(sb.shielded_outputs()) , value: i64::from(sb.value_balance()) , sig: s } } } @@ -428,11 +428,11 @@ impl Hspend { pub fn pack(sp: &[SpendDescription]) -> Vec { let mut r = Vec::new(); for s in sp { - let rk = Cursor::new(Vec::new()); - let authsig = Cursor::new(Vec::new()); - s.rk().write(rk.clone()).unwrap(); - s.spend_auth_sig().write(authsig.clone()).unwrap(); - r.push(Hspend {cv: Hhex{bytes:s.cv().to_bytes().to_vec()}, anchor: Hhex{bytes:s.anchor().to_bytes().to_vec()}, nullifier: Hhex{bytes:s.nullifier().to_vec()}, rk: Hhex{bytes: rk.into_inner()}, proof: Hhex{bytes:s.zkproof().to_vec()}, authsig: Hhex{bytes:authsig.into_inner()}}); + let mut rk = Vec::new(); + let mut authsig = Vec::new(); + s.rk().write(&mut rk).unwrap(); + s.spend_auth_sig().write(&mut authsig).unwrap(); + r.push(Hspend {cv: Hhex{bytes:s.cv().to_bytes().to_vec()}, anchor: Hhex{bytes:s.anchor().to_bytes().to_vec()}, nullifier: Hhex{bytes:s.nullifier().to_vec()}, rk: Hhex{bytes: rk}, proof: Hhex{bytes:s.zkproof().to_vec()}, authsig: Hhex{bytes:authsig}}); } return r } diff --git a/test/Spec.hs b/test/Spec.hs index 49e985c..be1b49a 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -798,7 +798,11 @@ main = do let h = hexString "0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02" + let h1 = + hexString + "0400008085202f89014b6ded3d2ee43e54135c04f9a305b473d7d741bbe6f383aa636e03e948705e0d01000000fdfd0000483045022100b34ca0fb34959230b95a48e9de95521dc4c013bdc0e6bd2a77a67b373fadea42022030a1b13f16993ab212d9f674b7b8dd3c6723137c183dbd41a9bbfcf66d7811c40147304402200f0bd12663a52ccb5e6211ec633932bbc7f0151ab51194038534abf88f689025022051fe62591dfa11a96095b4aeee2114c77a0d773242939f7ac2fa8e5262ff0865014c6952210231ee0c161e846bc133ad41d295f560d7e24829415afc78ead24d4b10198bb7342103b0b11f2dd10611d06cd6232233678dce736ce2b2141c62bdf0b4fe6f79d736da21029cbeda22b05352f66a338561604cbe11e8332f836385f16abf5088045c39d4dd53aefdffffff02b63500000000000017a91499a8b5b1f3d3a1a12e92dd4f3d94bee8fc34c527873362fa020000000017a914d75fe4791fcd50f2874f1b7457fca5833293b3a38700000000000000000000000000000000000000" let t = readZebraTransaction h + let t1 = readZebraTransaction h1 it "Transparent output is identified" $ do case t of Nothing -> assertFailure "Couldn't decode" @@ -820,7 +824,27 @@ main = do (\(TxOut v s) -> s) (head (tb_vout myTb')) pkHash `shouldBe` maybe "" (hexBytes . tr_bytes) (t_rec addr) - myTb `shouldNotBe` Nothing + it "Transparent input is identified" $ do + case t1 of + Nothing -> assertFailure "Couldn't decode" + Just t1' -> do + let tb1 = zt_tBundle t1' + print tb1 + let txInHex = + HexString + "Km\237=.\228>T\DC3\\\EOT\249\163\ENQ\180s\215\215A\187\230\243\131\170cn\ETX\233Hp^\r" + toText txInHex `shouldBe` "Blank" + it "Sapling component is read" $ do + case t of + Nothing -> assertFailure "Couldn't decode" + Just t' -> do + let sb = zt_sBundle t' + fromRawSBundle sb `shouldNotBe` Nothing + describe "Raw transaction with Sap spend" $ do + let h = + hexString + "050000800a27a726b4d0d6c200000000c3c02a0000000111ac7f9600d034fec892f50600006b09ad88c990a8f3c5bffb6856beebd0ce356c5d1413c63a9a88db71c3f41dc12cd60197ee742fc75b217215e7144db48bd3fa55de211e0c8d7da70aba0efe17124b5250fc9fa095c8e50b53168142eb7ec402289a84ab762ddafb0f972fc07c61eb45249922752dfac17fd704a3b6156a6955286c04587f37fc781a9a0b28e92664dde2300c6321dd21d367c16b4c2b510e5d0a4358624e1ceb816da155e588726f783ed6bf396eaecada3d3bde7a0722419014c509a7fe264003af5388c5c8cdf2076137b26cf66383b41dea2af415f3d16767124616609b96789e2811f7bc8b36c0b746d225c0aa2c98b370fd39b2015eea43d523e5fbccc7ca127e59bc92ac8ad32d05243d278a26f07f33f45df23c3e8dd40d72af35a70068cc3f8445478c9cd3a56d3515e207a1b95c39c9d8b45e66827cf63e6018bf1012a47aa23ec0174735949f62036df3f677f92d3aa10365e2d358d92f77e9a6294c245eb8536493e8480f5215cdf7c863623727efa188fd8679f95318d0072915dc2be8e77b5778c48d3a2ea614990984c29dfaf7c582d269f0e9ffe6e3c475c99dbf5f8756c334b3c7b5954d68549c65fc2648bb389ba63397525829c48f7f32e43e866dfbc8ad86f54a867e592524f9c43b007328b78220cab4537c2d65f64c819ecc19ae09e17b3c84208cc169c2eaf8831d452dc83527a966b288aacce6f261aa8ed23e2c0613c48161c1eb0199915ee9c1f9cf0e7e5445539f5016b3ff701408b45557154a53e48998b49a2d149a67202caa6b585348b42fbeda770e084128760c742537743ba8cec4f997fe575db9cb370d89278c607a598ff06f1ece047855ea0a3d808d84272e22bcdeb2eb5bbe5754947be5d6fe42038d26f13313e039adf7e1091a901a6daa24b247b13c810eb5b50cd6040463c6bfa209a792d77eafb0f063167b11777d97ad05ca7eec1f1a45698e33f8e8b11a0ab0ad16ec10e4abba6de69255e0e00c7e712503a107012a46fcb9c5186a1fc5be96dcc279fa3dd18e69ee4a8ba75edaff105716b4751717aa818d514620ddd13a6716729417571a342dcc8a6f341d05db7614e729491095e4bcc6fbdec2908b6f74d7db81106ab2e1c0ee6217ba3cbb174232a42f2e6aba7e92eb2934dcd91c5a36eae1aac5160bf6076a39baeea84b8bad043c565eb8c4206a74b68c9342f9d7b1b31eec86bb9c5c73549313dd2cbe3bce55ecfdd952f3eb4dd5fa7c2000727b62a0faeb0fac1cf05246221c37560359c44610ee8f93cdd1179fc35fbd808152f507e790eb2140faccb2f6c9897a3a219d8752388e846c7277d82dc80850aeef2de46eb2d81563d53714d39e3553f5133abeae6c487d10664e1b10a90a5319adfa7471aa429095bd272ff4d3df227bdc6075e0cafba54201bddc44edcdfd2317f682f40a25e582273c4845b550b17724eca81e89268eafef9e98533b85bb2c54cdcb6beb4d6f2fc0459a2b36cbb43fcbab720678553fc930063c64a09445ff637c12d0ade4acdef1e19e8481a0b7c2dd968a2ceb7f50efbb76fe21f49d8829d8a26b10453626593a694586a828705c5ab02cccd60e351171aea3e1aaeda07cd960e0f5205019766ff3afe124a47f4ef16f0e671e1968e662c7974ee54872538c7019cc5396f165013f4c482d400ed4ff5fb34189c75e92d336cb25563867215dc3aa195f736e06657c9e27f1468acd580ee51ca44220be83c215c1223758a7676d376ea0d0aa74b73032b85a45f69e6f8d4ec3efb6861d5adc35f57e6112dfa7334ba6fbd64b14498a89d8596423125b6486d9208937e9aa010831edcc168be8b7abe7ac21cae4794fe5f019abe1715d5d8601b646767d396e1b7fb8ff50aa43d88a2426545270093ce80be1aaa849dd2edc0310e3cb0bc13a23108776a44e3250fe5aa89b6d62159206377e03e302e3dabff9ecd8675870c7d55e33cb915d7e73b765d1c61f9ba0d504429fe99516df0534cf6a5ad53424b9be39e4835dfb301ae5cbd9edd628f0cdb6c54bd4986a9fba65f60e833bf17b461746ffcc312d9584a55c55c42c8e760d017be56f54a4e1423648c3877f8a44e3a56c21bf2bf85550f3d8a8f040c8c6687aadb2c4d1e1613626a11d30fb25340f5f14b6a7752c5e6c7b866b10f1155360f35ddc8f07ed591fa3366948fbcde1a16c35a589457ded90b69675bad01a1fa9386e68dd2001498d41033a6effe882d9e864ea7991e0c71b3e65c158cba64014f800000000003b38927526d5fc5a1b2f16addcd4aa162272f280e977087de02ec2969f10d25ea3de9a5d3d3975b4b683897bf828be631813bd5b949ddf9c3c5b103b69aa483f5fc46485bc7a5dd51a47543f57d346e680e12d9429ef7c21cacf407656779235d205dffae23666e01d92fb18c063d1bd7cd00935f029149c7e20b3d72b1b951117f0629b1ef1e49047532a59fd8bc1817343d45e832078806eb10304b8e034fdce70464d33cf6c66e857fbda170573a7965f8eed58e6cb7edec4e9f8a666fbca0aeeca2d4d161caae23005f6e0adf6859c8f1bece409aa5d44d285a8e8e9fe86365af4e92384dce318724443e8a95cc790df47cdd3e8aa9ccf19acc5eb19b4cb517c3f63e59512c037a66312786cb6cd2bcd4480e0a21ac63a83b7b9c84d3207a6410aa51a60d15b5526e3ffb4cbb02ee9f43f7ce316ef01dd75544224481d4b8ad2a8944928e2b4432812d1b20aeae3b51808028321588952ea70afeef1709eff5a75463a2201299484ecc421c8452e1700ca9f4932b0aba6b5d46f36e80f2f0297eaf7f44a79cbd21046ca53073ee60d5f2ffe2352b2538326e3e7ed851e8a62612a9b974ceb5757835b974892b049a7a0c531390a4455657b5b5afc5f0f09f8bf838313f403c96c4c2c72c4ced6e642e86b0eff220c1cd3388ffc902af68f87a1b7e8920d7f014e6cd2694b926fcbef23dc645cd75b051add0fc2f99c0346d45c2521b27a00a13dc9754306222f54a2b4f3058e80e0821d6c78e5e80a509525631f34c343567513636855d1c318c49debc651212938c487e069c390868ce700c4d19e1e52eb7b536e1568d03421c3769b920224113bea4d345510bd0393efa1ec22b87e973c663be75c84e7278108ada31af0311796de927257f59823f1ee9955f0f39b618054476658c165ef443d169afe46007ac80a0efa3d678c2707a7a001812f02dac999796872d3328eeaab4b61cadb8be9c4ee0ff9104d417ba71076054735c12fabd5d78f6de83a57f46b8d071e3c273c60a226fa6d555fa3580002cf3b6f3e50a8f5616eef66e7bb28523858315b3cde381003ab885ece89973aa4e228cd5da0d24ebd876097e7664a2b903430963517bb895435703029ea30081a874d15f4997dec3b89cd728075a0b21d838a57052f02fad03c1a3f74427d149c6225b41339a00dd764b452fca190a0245e7118224965942e3a6d798365c34631585288b7ac6c67346b1d9ab1ee7d24e8cb7e35f2d2b5401a89af6a1648d19f8fcd14973c4c2db543d772b81bc363e707bc534f00b872df0e3d4c73e9bc513b2e6c49e07d4475f251c21d60cf8dc2c9aed2922e4efbe8cad80db14531a63e629f4cfb01df3f61fc470123a7caa141f104fb37d2712628648eec56759bd59cff1da5e7cac043539a2040b6d364f1449ec104bec6f487dd1f784dfe377d070fe704bcfaf4ca99cabf23f4ac1f58afdcd7c8a58ede5023034cb324722e349135c0125f49a7b4fcb22d81d641c06dd0cad8d0bcadd5c4ab7c2015ad7f98b1e4db7b89dd2aa3d057b13456f4ba3b0d52d3d0c4efb35deae8d1bfa39f593be4cd8311f6e89ac462a116ab4776576c1f87b7aff41ddc728fbc601aac334148307d9a3be7c11c4b122045ef2ff913eea3af62bff2b71433f3b65f8ea939fdc19877e98a70c8cce6b94eb2bf4d696392183f8224132b004a29db1fc0702b3c84e0d345d82cd622ff9fcf567b7dd88163ca21a32122f7c2c98c8d9d0861c02548a831e5e99a064b2be13774f76669b80f90cf8c1c0749dd646dabd68d18bb6e6f84dd8ee32acc2edce1c7d02b3b145f416b54b7bbeece8087eb03f75dd317caacfe8cb2c4573f4ff9beaa2c5ddc75b427db079c7951b88acdb8a7f4dbde154cd399cf579aa683e30bb430533b3241c8703bc6576666ffd299fb4d623f277b1f8be372f705ffe594644eed3ef2bb7070e843851b11252ae8aa8414ba17c94e63685815376246361ddc46301c995a2a6857004376bdee9c92f908595977109a0074ea3a8ea89131454429deb02d6431e3514e50632000117106dba12cae3925f260d527a7bb2cd6851cc91e5d4162a2b66b88540c1110cee4672b23b32a9269eeb86063df9d076ece42a9c7938c6fd5d037a4e4ac46942c365c75b75aa08c403690de155f96ddd23a3183653a9db614f2bf069620be62842947f6aa8abc340140aea22fa3b1fa7fb552a4f0a43fc89b3790cd3721967624451f94983d90d65063d4d9c60bad78f7bb11bdb8e6af9f806bcb34d7fdb8a2da4f6c3bc27ce4a48ad529f6a5b6e055f35c344a1dd2d6a085d1fa5bbfe9fcf6bda46b320e02c864db8b574f165f616d48e2f12eb25099b5c90186af26d9e50f5058863e7f01c4200214e2969fb6c0fab8dc4ddb8475e28a21eb6b902d542dba5e4ad0b3b89b76ea777e6d9152feab5b0dd55f4f2d3db5f4e4013e8eb6f637f661f4f5b6ea8a73437437d14190bab1fc44df4fa78558c2b53e23283f4553101daf28f7c4ec5d9d93c47ace2ccf6d0514e0d33ea597b07f88ba4c272def6ba368f0239ba59003728fc20e9cc3e3c367a04f8faefb5a9725f0c8fa298aae306089908168cd53ea4fe769175863360f8838dbb3a185612d35b97aabd00c4fcc40245349196190a01f3b74fe6ac7f1dc854be928108bb23c67742fe4845b79b6286d99771d85bd9145b20d94b38ad9b273ccbad0ddeb5b7ba1ac322a20619b4d1ca9476ce741a0cca4c90b3b783f3b3d5064a7c15f72b97836db164d77661d415b12df263b1ad2e2efe92c5ccc50ba10c2d5d2debf89132649aa3163a23648f9d53bf14f183b920d0379ea04897a6b9245aa22ed2e4d913bace308307ea05d31753a43f62968f2653a6aad5fb483e4e78bbc42b4b853d92c81bb9c4c03934dafc8daf9ad16b3252d29028a9e5ea4278e928570c1bd112d6900df3da90a8eea441fcd1a73a1d982b010e6e7f73023dc36fbff9600f788da14b1410b31fff4ab2cd895f318aa9cc7baf9aaff1066ae4be169f663b6bdca53fa462174b56c6883e1b4b84df12b1f246de72e1af85f913000407ac30410149d1e4255ce94bc26b6ae62223f238be472f0dc9c812d137d88a7fa3fc0808693f9add9db0827d40d3dfca3dac9000a12b56d8803aff5517682e9cda163128fe166033397b79afee3f74528ccc31e3c5289812ee730cf5011ae15b0c86740644806048ac2282b7ddaed2e469c289cc70689a385e2c0459affae17a3e6d3815433363be215da2c110369a6ae12325d3c40340df4d77d09106f75d51d0c481bcf8228a4152c4dc45026f4e8de1270e04b60ac57ba0bf116fa003cc65d8ecc210e757365287203582608ffffffffffbc221f01e2f8306f906e82bb3b0952ca54890e06591275b726215e27c828761bfd601ce344fd29dfbfb9b37d62c86723a0d6a75fa6d2d8bd0c6ce822109d0ce816c8add2ea6343855d5e9ad63f3a730020eb46909af6d606ee3b5811cb807b9bfe2b2033d9ac8732b2b0eb9b28bec9eb6133f6c20b4c05e1dfa77155d289bbaa3ea6042e65326d32224e00b96f4a7af04461e1dd662284d566d6a9de2e435caca9c51fee6b665d6a1f641509b176cb29ff3fc67401e766f5ca1fab7584b36396eaba2f05e5ad0cbbba29c5372d5443f9afb4e2411f1284da03f9cbd2827ca725afe395024bc9436910053f8f8c4cef37acfc78cc05d7de0c77fc1492ef46469d93c49aadd42036c5fd6ef8d0c70d7c57e0e0e9f75ac94bbfaac9373ddc202f3393bc0f37b0a7769fca64300cd3594a29536692e575c9ef4e5dd544e108cad4ff895b24b431a86f1da264ede7b4b452e600035a38c902557b1ebb2abead1783765e778e7bec298ac9e021c82fbca82acf80094093afaa499f55a164237303d2ddfd0f8726320a0260dc2d59961c33c2d89ba40e65d46075b2d3206d76b27d00575a963489d2f168a53a0b5f04227d4cf935a2e82917c018270b7714b5598c838ed82e9ddfbdaa4448858db050cd14c500ac0e5fd566610e1d4ae2534565715cc96e499e5c190be37eea9d8fd23814658038fcc761b887568097da83232ee39b47e73c9ac854b8e70f38f6999e6f889a16ade97b9b206eae237f597f8ba0d2f2bf8f3794ea782f2264e9688a717f20431734d1bb989bfb4e40d3f4e8ccf1b8e938802a27402bd3a02d3d073e555acc5c4d941d5fbb3fef890b2e59a6a745b2e966c1903a988d586b53f63bc2a9253c7afb73f204fd3aa4a82332b0b010da159f29176ba146769d340399225d61bfad40ee21f765c641552d41b46bf5113de89318a6b0ab66ce02aea59ee302d8db4b2b1b4c71693b5159fd53627844f3b24288f585f536a52a3dcc4c71227b3a1489a5d31fc37e5a708bd724a889efec04c0c57730352493c5aead0eeb412b03a7514c24f4f3e659d529796218bb825c901da92869bc2b93afd359e24c8d55006d883e2380be6aa77d55218e831a7d5c4d566e293e4110b02ccc0508902a3d876dfd31f83b7b51b7d0d82d5838f6ed96ebb7dacf438526387a0b5053b522db03e5241b8069e9d43042922d4cb2a8c1112b05ccaae37e23511cdc90c6895713f7574f615df37c425d7cfa368ec72703a464b1d347c6db13d6496ffbb868594a330c213fd222a0ac618f1b312f4d6f5dd078ceafa20691aec35fd3b8d6479a63b82bff06a6eb619ae87ade1a5542b9582b1effde9e5d1db3c619458fab459ea2f91232089b716afa2de4ac9830830ce49284de17556de89400e2555960fee44db6379085636a5e4b938b1cd067f8d5bece171d463b432225a1290c306727187e1d2d482662c783736700be93e3f9304e0113d244e8fe4e1b38be3ec342a5a5c062cf1e20f3dc60f519947156221ee244b7e14c39f495720e4b727daf2e39c2e352b0a247660550ca8f82796db4e424be367d3d1ab70eb9b37649d9634c456d889354be44a45c61c37b5def297fc36a76d9dd676c2f643d997ab97b7735f914fbdd38f86508406a8de7dac15a1cb5ac5d9f90eb1f8b2301906c52d5c21674fe09e7f3b9c5990250efb5a890c420aeb1c5fe137755416d69aecc7549168876eb00f71865d04c946f11976cae4085eebd4e73be71ed8505470b0bfb446d735fe1afa33380ceeece76a8347e8fe2f58e34a579fd57468968d712218cbc99a68c3377a6ac542888542fc36fef2cf55c0a99266766abe6ecdbd00675c0935afe8cf8d938c1df6f6f4c849d72f354da1f09b6e7fc9b497544cf6930050d6ab8980e09feb965eebd50e91efebdb234e533372a67445de2088803328f429a8d6923899b849fb7f0af2132808544cb6bf99d0b18abe3df119bb5fdf5818e836064e5a0b329339c35ec7481628a71d35a7f5021001f72bc2b98a15aaabb4c5c462b44db100efb3a3589ec4b9f5e7900dc293443d9e655c8a50e51a00980be62f3821174c918500f9f06a25e109d3f7d63d588271a2a502e89a2d084350af0089aa4ca95be15133fac659f79df3bdde5384c2c25cdc4f94cffccfc125aba0fcdbfaa5fa9a18821f17a807aae39e4307f3b6d207a45f81877a988615473bbab0a580ae41e911fc54edd3b0cfa2391ba4c88945d2fdfd63c9a9905c2f847beb833d429af910fecd4a301e09244743d21439a66ec026ccf1ef1fd5f89d8600eaa5e07328c315205c005ed4caa4514df692a59db7b218e7543499323d4f90004398e425f77dccb3e35374bdbfc1daf35a4eed54758a1cdbe35a52d597f9cf0b3414d00d2dd5cf8dcfe51b2af90ddb2c0c59b391bead7b8fa2073061b4dd04406f2e7cda52ff000bc15dc02bf2bf22838e0ef641845bc4ce6c143d56e9a3b833e5cf4186978bf7c0e13b40e5e7f1d0ef28a3b388fa16619c13bb47d5eab57fb1c4ecc0b2ebfbf26200075b0cf5137e8f13703b7fdebd72218f32b8a0050c6633b5f1a3b9c62c83e81fc1c55741e759c1274d77a029eb7af4c3fd689331b69f80343332f541d1026a66c49fc5bd7012847d4d48be2b41064293785105872300f2ef8cd8f8b9892e96309456ecebfc893404da002867264d0a3caab84867488bb269e1ecc346d1ef7c1c76b6d16882dc5ea9dbee597dfa2d3be8e79091236dd750ad10a50e2f9f6adc1722f6131cc4986b41cd4acbe479f6ece8eb0892345662a2a0064ed74cd74957e796c4fac9c7145b0103e60750bc351f9d6d72faf2858ef23b107c07caff3940696ccf74e41001f17aa29edb068eeb6ec0b0bff10fab2571356ff5a3cff63767cad097f9dd4a5919700bd7e24182acdabf903171788800f17d5005dc07d6d5bed1dec81e13bb33196852dbcf978b45932bc28e60a83900502b11f09ff61feb208682500654cc9d2b42b60ecf99724bec398dd6829bd44a30151ab5d05c39284be657e7dde539668b5dd0b8ddcb331c4fcacb7b9c9520cc01c421c9955d7b168a9416e36c6e436e6d328d9e0fadd441fbd0d1d5bdbebbe9c19a3607746d06282c0d440ac8a243c47c27f04ac2d24620ee83fa6b1319fb4a137d1c82c436676f8d75f1b9d99b01c6f9cb4a91027631cc5717d07961179bac417ef8ae8bec969828872794cdda7b6f2e7472c50497a35782ca5841d5419ef3805c9fb30b8205f8fad2226e67048a4923de049ff3ae1f804b3ed33637c81e7530e970e2f51f9bc435e2d4ce3ebc8aea207115a4ec369a4caa7a3fc814224e8fa0e9a85f1c928ba24507d2ba16e176ad8cac7c125b5edef7d8efe1423c857949a1768748377a988d68098502720e75e138abda3f73eb9b2afdd50ef2f8d20ed5f36343fddeb59a8a591582d2d4c66ec0a89dccab3bc6cdc5aaf93a7518dd1c3a92ca486b6833bed70294d6d04b02803e4841a7faf9ab06a9c05ef672ba2b61c5e004bdfd6e490da458ccfa58be7dfef9dcc0dc79179f14659d8c4456045e48ce0039982580b9d7240209d4e5767b0bb391ef1f8929bf06d5e3c8226ecbdf8844902eab10f22b9f23381ba1b5f5c241a614b0a3cde02559669e0520ba5116f58e12fca16603e129e2e5315d581acc0d2c6ff9caaa7742b6c3e28d37f0766ed08a33f39cff6919af762d4ad2168614a190700aa87a95759951364d8445e680cc9ad0b75827810546a9510926d97835d62a65a3e5a375150b6e9a96fdca74a59269b2dd788320b96d92186f3226031684c45ab69c26cb754296a2a40c0026eb95c4f0b64db9872710d731d42c7510922dc4459600886d44586099fb957f0b6158ad703d11569699664e839d8acff3a5261f8b72cc43763f02568dea203b65be9cb06161a09d17203f168cd8469d805475dccc7c76ae2ef216e068f39ec7344f05c3d2ad794cacca891b3d0bfed335c1519203d289f74b896cb6c33c15ca0791ffeaf11c9d91b95438be68769f91d08997b77905d5a89460a3619dd44d5d92b5f88542521763c815ce15bf4f34c018297d777a602e27276021619a82a21be68cb5f4b321bca1baa7befd5cdffc80121a1fa379004f441148057d6f1f66863921c3bdd3117bb5d9ad52c45ac9a45391ec83cee3c6dcdfc40ddb3ed25a87f793430dc440a6d33218a04426b5bb7031d33b2439f01ab2fc2f64a43d2a482b1428ff5417d2d33c818166c20effbf46811d50d9d05f7ef1a37d249f64689cb37d8cbca2dd7086e141caea4c00b35286c9601f5f1cb6186452405c4a975e1c13cc10cf33e5908287f5f3fe36af89f374d172403dadf01bd06055582753b0a0a387361b91f233e6beb112b9b1eaf0a835b4d14c82477ef46868d09d03601a88f4da2dc7cef2a04a62e61311cc0284e8e48c571f622bf5ee95992ef425227d6b9272b6727b10005c53778ae7a6b155f3a7f00086e1e952527cee0d40ddd5e0caefdb19a082ea43d85dec0168d5641b570fbcbbe9058e49ed4dc546fec49d1c2041b6343dee68a09beb95df54eacdfe2792307c88b20408692eef15bb3e370ac877e927cf58ace1931e2dde4ddbd228de70fc718a5bb30d68e0196bd2d00c1a3ef138adf1640403c513c07a0e8aefdca2bfd3a9404f4c9af683bf52980c43d48912ac5347c438100eb50613a082270e1bdb9a1a2241310fcf422611ff6b67cca4c9988489d344215b9b8f8b6247903edbd5689733427c555656a916d106fe6ebba66c25c1c659d27f86b712bfac6705301f03584da09b9da428092504dae0bda0b95cb7efbed8224349462ddd2a6966cffc455ddf78d3964d1b320dddd31e3e7edd4aa40afbe0d1d01ef0b1b2eab7a266201b3ed078199c33167da82795a3dca83aa06e921a4802fc34ff1e66bf31b2e047734ae2eaf765184498e127a296c5226af7a7a03ab5815efd8ee2660a4ae67a26b88d90665f5143b503998374a0175f2e2f6ac5bd25f21fc62ff01bf96ea49a15b1585441c91bde7e634ced455dec7d3a6ba115ff3b12bcfe04cbecf6af5afb4fc5b98392d2fc2deb203ad0c5f3b7ffc2fe116d7a4c40487f5d56421b031f17ef5592e9d2f052fbd8e62dc004b9caf7ced9204fc49cb2ba40ab94bffebc09851f785230ff2137e992a04aa7f63305c9d2e3b6f07f94a09b17fb79eb6cbd5f76f84ec738e80b75a229df1215931a14bc22f14a7d85a2f2f9ba9e7cb01be437e6117178c7640b766eed27820449bb3a477d3bc9f75acd433701d2a01d2dd0d7d29deaafce62cc16b83e0fa72229c045af4f16d9cf33e1e3e8a137e7da245a9906e0968fde8037a6c6a6de0ca425650c10ce8311fc1290f02ad70256bec8a70501866c727c1abd2ec9e38bee97010e2f6727888ec24d15235f58458f6436545aee826d06569824efc250d8064406cbeb735e6e2a5c0c04b2fb267952091813646018655d1bc38828d4ff512fa4c127245ee0436ad4f39272cf0024eacfeefc8adabb6947d7ca619a5fdf16e45cce1bab1b93881eba5827332e4f6449871e1281e94b61e8e4a0827314b502af91e4dc372034dc4a76beabd158014fe0909c4218308ec23258a09de7c2761bb90c07736faa900e44c1d46252fdeaa69dff7de1b235950537043d28b8f55faac068f7cc307716524d691d11d2f9243ed14b10686e7ad6507d91ba599f76e4b82b8b7b3aeb91355cae7595b913b280b6e2063544a350d855e06732d72bbeae44c4aeb3c2617b137ae71ec54f0073b0aea31ddc7f9cc230542410be56f25ff12bfad25a02753a051bdbb793fc1388ef1a294b525afb5d1f00909d9b267ae4ccc7e62cb6418edb56a9752619ada13cf24c7c383c68f33daf425bca04b9b991cafaa21932e248ede5f0ba666ddc92dfc894627ef08847726002572e51a952e48c875a9f3daf5529b80734622d2ff33c1361dbcf2db76b5a76408eaf5060428763de6150cb883997488cd2936b1f92ff9bbe5eea42c55f090e11affba3154c01261267b65ab13ceebd58ed5676ccd1340e922109ec3a5b7dc115511486c308db7de52b192f642f2e9f5344b66e57d0f380b2bd318c435a9c095db5133ed53c9dde09879743b172c3f3464a0526cc3083d04c8da3b467eb9197d696e3888e3cd59bdd01261b529eb7da5ac57143c130195f5b5d7c0cf75c2bdbfc9e98b56b449f8e9b4eec249a140a3bf22498bcba318c35a7394acd36f79d67942147df440b6c249ad6f4a650ad12479fc7dbd474b34415fe6ed5b1ae62116f2d0738e84cb74814365c2eaae75d75c1e97ed02e65012e3f78505e42017786cbbaa290db6104da5df3ce5eb84dbe2f4cf2a0a36e5391814e4d05b2fb4a876636367e9c44fc975e77e4f56e62edc1cecfe6bc75550c00f2a0d3629f3b2314cb8dbda80b03437fd88eff616a3a4a03000ba3936b2d33229b09823de81235d67577d92e61a9bc8f31f5656db69ff54c97c1e410d2281c40e8ea799a3a8fe19383398856c12fa95693d545567d3df118494c401c9d77236045bd62a651af5c4842f0b119996312a2b555cb4799bec2890ba4a4624ce3eaf38e988510a7f5bf9573611d0b9b88b8606cfd1b7e57355a28110f9f0a8b46a2f235f9470ba45f0063556084640f79d837c8071b34408ff7bb78d08ffcac535fa3de0685b5daa94a89ced1a1c6d32ab63bbbb1b50c1d6033e9e4c377d5eccd0533b53587f4728291d7bba94c9dc15c64df469f29bdfd5cc621fee43bb33b5969111de0aa7cd65dcf0c927edf37ca242bc64f810c9c7c6ea9652f876d05219ea660688d07d7e0e85625e47afd3652b4bf13d5e28dc94076b12f7640edfe303bd5a1dd07ddfcd29675632f62843b8268a3291f38c53fb115f5325f8e71b5f728319296d7181d8dd5df7524b1d0908edc699c48fa5868f5e7f6015f649b6d82998d43e061cf94fbfc3562c72dccfd085fd0842be56d141c081d4431d7efe66c3c9581e68f74c739bec99b003890c5f9e37497353d6937da0a355128c4ba0d657177518157e811454c0000a340b6ea94d644fc967a3b1934806e4ffb2b6c44fd68f0d0771854cd92db6a4cacb4a51cba72e98077b8de6867885151c9d5a6875764e230f2ca135d546f10057a2f243c0227c92ce23cb1c219cc8225ced38a987ec5c80148bb8fcd36c5fe12d1b097cca1c669739ed04641d14a5614852b71e600f39f100b48bc384befa6e9184eda4515f30e48f2d8e7da182994a2d8039a5f167743e3a83ae225772c31dc15ed64829b285dce99d29d8ffc28b77bae6a5db570b676327eaca9a081330b3d950b25cf9b0199dbd9d734407a7b6788eb1c7b190b326161f86133f7061573dab25df10bdb9ad0327bcd8e6376d60b011265bb48603bf2730e8737fd3675bb1fbacf0971ac3818eaef2e48d264d80b74033992b6dccaa3309b771cacbabc31f1b5df9c5ca982c8cdc813956b9c7afa7651e781f31161bb61e68141b0bc9bb46de37e468170cdeaea20c730b81eaef4a7d51506a6e8d40e6355aeb1e3b51d43cd39ff4eeba80cdb70a6a0d85dbadcd64a80898df76cedb8506902f2e63206225147df250d4226fd9cf1c6f5cea87167a81630d7234e4048216a7758da572a26702d5083b1e1906281bf92eedfc2f988ec2828dd7079430bd2f0111edcac208beea9c4bb01ad70123a7d335d191fabfd6c637e9899fbf50262addadf5a3cf4a138954738b456bb9c8605625b2aae94128991e91c0e62828ad204e522ba6a55ab1c3064d56514da5c46943a1d8421fac70d584daea333df201203be96e5b6effe1725e541deedbedfb1c99d73d857321b5c79bc7e5078d34763f5ecdf30bf8ce3509263ff8516cc11b0d4e7892442f6756e23f05a98759de5209589c86c716d28ec30376b1aa2603a35615d9f712aa112c19b4364cdd55947c12dd8704ed1e6d804a6f3947fedb1c22e6b5d9ec4182ce82248484f1c077d8fb18ea2633caeff87daa396089a27078c0670fcdaba1d61091e2565950799be18b329f57e4cf4c80010c48ce4d970bffb525ba672ef04cf1ccd1feaf4d91a7f4b60011945364a4888d78e0538abfc2c85c4079e0895718a5802098fbca9570219d11d695164cc61e58502be4f8f3a3a2f4b5765d870ff98bb237b86180fef9029d0093b7fe806893d1a25440698b239a1ff3db8f7c8a687739f3d180d81fbe75bb323585016d69348c8d20c9704547fedfbcb90f82bfd3337e280f81fa7cefc7fc2b71645de84bb5e75a52227b773ed01aa743182c1716e3b8fb62984300696c2c29a14cdeb8033ff2952105b54d50e4a2bfb590f59a794dc6e3cd293106f806e838451e7a45d953a214dc1d630563b895880aabfce34ce9261c87a6be925eb37316382a0f8c5440254be1aad69e7685730d42bbcd0576627c38848bdc92b16ef61366fb6632202f483f25032043f06f85009b4e13015865a387bfdd7c6aa4436b32ab04041848b968bb97e0d19a0c0e0c0899c1f463547e9250ba8d13109dc52e216b614f1193a6f44d58959e8b4acd4511d9d1c4adedb9aa9c3cdaaa5e65c6641b8194bca4a574ea98a907b79f0e10b1d729c9a1e42a87b8df7abf1d73cb835a3df2469b47696d9512576d9aef15ae943e48cb86c535bee7370589f9b288f1062f9c16fd7d5e9033b8a1efe20773a313c68ee4219f9d449fbd9e67f55d276f6a21c305edddfeefae11444635ef64f2a9e69e6de505fa9dc82822033265a1d86a1a301695d0ae9310d9e29b253f31af878631b6e750e150004b1aea96be32d6a4380e2952c0b67ccdbc2d363cac6cb66bde674cc0e413a195459503b78c9ab8a606da64a170c63479a96dd3051b1e97153be047747cb20fccaeac1512d28b8126136b95a4379f59382bef28ff51f6b04ff085ee6762b7772b81dcdb4efcb95b52175d52325c8e954c6864096e93b87ea0cbf575ac028eccc698a5fc6f4a07ceea0ade09f88abd662057f7fb3a520d2c473cccb2a7047fc28f797936c16d993f2011e9524fca9d46832acdcd6ef1a8027bbe285d916bd31617d59ed53c7e1289661106b25a937c68e755d23cfa9491056c2c3e12c503c4d2c72ed942e55f2359243c85569cc240b7d9a81eb5e7836431cb4b5c5e26fd627e24a9e7d1487c4e7f64232ca1f23048158092016ef5e7647e0ae4d313a6422be0e998446fbf79cb7d70b33962e0f6c2ceea814a8ce8d8ea72186fec4f5dcc63ef82a577f5a8e84579623f6993b5b6c76e4a5390b5557d7b8a085229827c08f2ee968dfa49997e59ed068d4118b0339605305b8a2b1ba3a54cd9eb915acf75eeb0748da73e2b20c81de83de1b74181574e8648315a12a39276361845bbeaad5604229d7f4e3a08af4a9eaf93fbb8db90b0015992587e6520cc603516374320ab70b61c9ac5c1e8e81c97b1fe15916f7791b7f657c27f5fa5c0db41245a9615a4b50d3802fbdad494b6eb22abc3bcf030622c7e4067dc6941b961ec685fb554acde232580c42608d4eb9c0dd0659567348696cb99e33efc5063455cc0820f886e4b3b2cce680c0f77dd77098eb87d7f35474f1e5f2dba3329dccba450c6f97f5d2298056afd4218aca1ee835621896838a791e2e04d17f0b4f7f3a3f30a0fb717e054bf11f775e20d7a5aac4841c31cb8d3570855504be775c4582963bb2538189e204a8bfdfa59955a2bac2705dd78a6f8c9f3259326febcf70a337166ee934856fc8234a2d61b044578b940179b590b40363159538658a9b39ce9d39d51aface156c9149f47912f50dfb5b8e241c2e60f78bfa45e2996a981bf51afe1989bb2ad6a8cd6ecab3424893eb760aa4ac1ad724b15d7859915bcd9d625624570e454b5e1eb69b441c729d42a2755bfc3fc8d7e7a5c38fc07b576ae9904eae0104652be03e99b8e98183b175596708f40aced3e814c940b590a26d34206808438a41d169a6a33b6b47f3d3c4a2c8ae643d8cf184833cd6c9a0de44070557be274e0cbe89991ee768df8a98bcf180159187fafc44d9a91e3fabbd81e2d66ea303b963059c215562aba48b8e76c215b06757bbe70d2452fc57d003f4a5081fc4c4849bccf0ac8cf9504f34fb2c383eebad5a6d105c16d83a902b1adbd347dff749bec214d209cfddc171b6ee5ea3b201c07c622d2b7e4ecaf989ff8ecf7b4682b83b0c84d0766547e77ea1d63cb8e45e86b6df0e912b4ac70c3e15784484d714f8873e91ef724ec0b5396e2700d35607cd73847cb966bd95a18ce5b3d32456a76cb1a96172034f8391f2b63c305132e745cbe5426c5c31a30e77108459507674ac0aa22c2e6df15f11285529ddf11e1865d4b3616fe0f04aa186eb8d9cfcce8440fac9fee9cf0fc1872f4a0ad28236f3e66d8123d80d08590d5ad6d31dd69ea10b762b3698ece119d190d53f004b65c947d46b3967db026d76b9460bd78f67077eb78e8828f2d6a2fe2452ccbe006f201977461bf38e3b136e04293b65b07bb1f08070bbd8cf284b76ee4b21faa01c880ee6376b9746f03c9ad034fefce1fd5df27ad9073299966771be915e68636" + let t = readZebraTransaction h it "Sapling component is read" $ do case t of Nothing -> assertFailure "Couldn't decode" -- 2.43.0 From b74cd5f35467f84088b9b982736306d6ccfc5791 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 24 Apr 2024 16:04:56 -0500 Subject: [PATCH 02/22] Update note position calculation --- librustzcash-wrapper/src/lib.rs | 12 ++---------- test/Spec.hs | 24 +++++++++++++----------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index ab7c934..67a7447 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1247,16 +1247,8 @@ pub extern "C" fn rust_wrapper_read_sapling_position( let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW); let wit_reader = Cursor::new(wit_in); let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); - let path = iw.path(); - match path { - Some(p) => { - let pos = p.position(); - return u64::from(pos); - }, - None => { - return 0; - } - } + let pos = iw.witnessed_position(); + return u64::from(pos); } #[no_mangle] diff --git a/test/Spec.hs b/test/Spec.hs index 5c6f3a5..19ca7a4 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -1113,23 +1113,25 @@ main = do MainNet External p - dn `shouldNotBe` Nothing - + dn `shouldBe` Nothing describe "Generate an ExchangeAddress (MainNet) from transparent address" $ do let ta = decodeTransparentAddress "t1dMjvesbzdG41xgKaGU3HgwYJwSgbCK54e" it "Try to generate valid ExchangeAddress from Transparent Address" $ do - case ta of - Nothing -> assertFailure "Failed to decode transparent address" - Just t -> do - case (tr_type (ta_receiver t) ) of - P2SH -> assertFailure "P2SH not supported for ExchengeAddress generation" + case ta of + Nothing -> assertFailure "Failed to decode transparent address" + Just t -> do + case (tr_type (ta_receiver t)) of + P2SH -> + assertFailure + "P2SH not supported for ExchengeAddress generation" P2PKH -> do - let exch = encodeExchangeAddress (ta_network t) (ta_receiver t) + let exch = + encodeExchangeAddress (ta_network t) (ta_receiver t) case exch of - Nothing -> assertFailure "Failed to encode Exchange address" - Just addr -> do + Nothing -> assertFailure "Failed to encode Exchange address" + Just addr -> do let eadr = decodeExchangeAddress addr - eadr `shouldNotBe` Nothing + eadr `shouldNotBe` Nothing -- | Properties prop_PhraseLength :: Property -- 2.43.0 From af9806d72df6c551b5e61eb19d7a605011d32b1d Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 25 Apr 2024 19:56:29 -0500 Subject: [PATCH 03/22] Add witness update functionality --- CHANGELOG.md | 15 ++++++-- librustzcash-wrapper/src/lib.rs | 68 +++++++++++++++++++++++++++++++++ src/C/Zcash.chs | 16 ++++++++ src/ZcashHaskell/Orchard.hs | 7 ++++ src/ZcashHaskell/Sapling.hs | 42 +++++++++++--------- zcash-haskell.cabal | 2 +- 6 files changed, 127 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70317db..7c13f62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,14 +5,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.5.4] + +### Added + +- Functions to update Sapling witnesses. +- Functions to update Orchard witnesses. + ## [0.5.5.3] ### Added -- Added unction to generate an ExchangeAddress in Human Readable Format Using a TransparentAddress in HRF - `encodeExchangeAddress` a function to create a ExchangeAddress in HRF - `decodeExchangeAddress` a function to obtaina a TransparentAddress object from an ExchangeAddress in HRF -- Added new type ExchangeAddress +- Added function to generate an `ExchangeAddress` in Human Readable Format Using a `TransparentAddress` in HRF +- `encodeExchangeAddress` a function to create a `ExchangeAddress` in HRF +- `decodeExchangeAddress` a function to obtain a `TransparentAddress` object from an `ExchangeAddress` in HRF +- Added new type `ExchangeAddress` ### Fixed diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 67a7447..985c3c4 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1251,6 +1251,40 @@ pub extern "C" fn rust_wrapper_read_sapling_position( return u64::from(pos); } +#[no_mangle] +pub extern "C" fn rust_wrapper_update_sapling_witness( + wit: *const u8, + wit_len: usize, + cmus: *const u8, + cmus_len: usize, + out: *mut u8, + out_len: &mut usize + ) { + let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW); + let wit_reader = Cursor::new(wit_in); + let mut iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); + let cmu: Vec> = marshall_from_haskell_var(cmus, cmus_len, RW); + for c in cmu { + let sap_note_comm = SaplingNoteCommitment::from_bytes(&to_array(c)); + if sap_note_comm.is_some().into() { + let n = Node::from_cmu(&sap_note_comm.unwrap()); + iw.append(n); + } + } + let mut out_bytes: Vec = Vec::new(); + let result = write_incremental_witness(&iw, &mut out_bytes); + match result { + Ok(()) => { + let h = Hhex { bytes: out_bytes}; + marshall_to_haskell_var(&h, out, out_len, RW); + }, + Err(_e) => { + let h0 = Hhex { bytes: vec![0]}; + marshall_to_haskell_var(&h0, out, out_len, RW); + } + } +} + #[no_mangle] pub extern "C" fn rust_wrapper_read_orchard_commitment_tree( tree: *const u8, @@ -1340,6 +1374,40 @@ pub extern "C" fn rust_wrapper_read_orchard_position( } } +#[no_mangle] +pub extern "C" fn rust_wrapper_update_orchard_witness( + wit: *const u8, + wit_len: usize, + cm: *const u8, + cm_len: usize, + out: *mut u8, + out_len: &mut usize + ) { + let wit_in: Vec = marshall_from_haskell_var(wit, wit_len, RW); + let wit_reader = Cursor::new(wit_in); + let mut iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); + let cmu: Vec> = marshall_from_haskell_var(cm, cm_len, RW); + for c in cmu { + let orchard_note_comm = ExtractedNoteCommitment::from_bytes(&to_array(c)); + if orchard_note_comm.is_some().into() { + let n = MerkleHashOrchard::from_cmx(&orchard_note_comm.unwrap()); + iw.append(n); + } + let mut out_bytes: Vec = Vec::new(); + let result = write_incremental_witness(&iw, &mut out_bytes); + match result { + Ok(()) => { + let h = Hhex { bytes: out_bytes}; + marshall_to_haskell_var(&h, out, out_len, RW); + }, + Err(_e) => { + let h0 = Hhex { bytes: vec![0]}; + marshall_to_haskell_var(&h0, out, out_len, RW); + } + } + } +} + #[no_mangle] pub extern "C" fn rust_wrapper_decode_sapling_address( sapling: *const u8, diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index a4bfb9d..0300865 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -259,3 +259,19 @@ import ZcashHaskell.Types } -> `Word64' #} + +{# fun unsafe rust_wrapper_update_sapling_witness as rustWrapperUpdateSaplingWitness + { toBorshVar* `BS.ByteString'& + , toBorshVar* `[BS.ByteString]'& + , getVarBuffer `Buffer HexString'& + } + -> `()' +#} + +{# fun unsafe rust_wrapper_update_orchard_witness as rustWrapperUpdateOrchardWitness + { toBorshVar* `BS.ByteString'& + , toBorshVar* `[BS.ByteString]'& + , getVarBuffer `Buffer HexString'& + } + -> `()' +#} diff --git a/src/ZcashHaskell/Orchard.hs b/src/ZcashHaskell/Orchard.hs index 7e6fc6c..d7c3e38 100644 --- a/src/ZcashHaskell/Orchard.hs +++ b/src/ZcashHaskell/Orchard.hs @@ -28,6 +28,7 @@ import C.Zcash , rustWrapperReadOrchardWitness , rustWrapperUADecode , rustWrapperUfvkDecode + , rustWrapperUpdateOrchardWitness ) import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as C @@ -219,3 +220,9 @@ getOrchardWitness tree = getOrchardNotePosition :: OrchardWitness -> Integer getOrchardNotePosition = fromIntegral . rustWrapperReadOrchardPosition . hexBytes . orchWit + +updateOrchardWitness :: OrchardWitness -> [HexString] -> OrchardWitness +updateOrchardWitness wit cmus = + OrchardWitness $ + withPureBorshVarBuffer $ + rustWrapperUpdateOrchardWitness (toBytes $ orchWit wit) (map toBytes cmus) diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index 6e3c7b0..1b4e3bc 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -18,11 +18,11 @@ module ZcashHaskell.Sapling where import C.Zcash - ( rustWrapperIsShielded + ( rustWrapperDecodeSaplingAddress + , rustWrapperIsShielded , rustWrapperReadSaplingCommitmentTree , rustWrapperReadSaplingPosition , rustWrapperReadSaplingWitness - , rustWrapperDecodeSaplingAddress , rustWrapperSaplingCheck , rustWrapperSaplingChgPaymentAddress , rustWrapperSaplingDecodeEsk @@ -31,18 +31,19 @@ import C.Zcash , rustWrapperSaplingSpendingkey , rustWrapperSaplingVkDecode , rustWrapperTxParse + , rustWrapperUpdateSaplingWitness ) import Data.Aeson import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as C -import qualified Data.Text as T import Data.HexString (HexString(..), fromText, hexString, toBytes, toText) +import qualified Data.Text as T import Data.Word import Foreign.Rust.Marshall.Variable ( withPureBorshVarBuffer , withPureBorshVarBuffer ) -import ZcashHaskell.Types +import ZcashHaskell.Types import ZcashHaskell.Utils (decodeBech32, encodeBech32, encodeBech32m) -- | Check if given bytesting is a valid encoded shielded address @@ -192,32 +193,37 @@ getSaplingNotePosition :: SaplingWitness -> Integer getSaplingNotePosition = fromIntegral . rustWrapperReadSaplingPosition . hexBytes . sapWit +updateSaplingWitness :: SaplingWitness -> [HexString] -> SaplingWitness +updateSaplingWitness wit cmus = + SaplingWitness $ + withPureBorshVarBuffer $ + rustWrapperUpdateSaplingWitness (toBytes $ sapWit wit) (map toBytes cmus) + -- | Encode a SaplingReceiver into HRF text encodeSaplingAddress :: ZcashNet -> SaplingReceiver -> Maybe T.Text -encodeSaplingAddress net sr = do +encodeSaplingAddress net sr = do case net of - MainNet -> - Just $ encodeBech32 (C.pack sapPaymentAddressHrp) (getBytes sr) + MainNet -> Just $ encodeBech32 (C.pack sapPaymentAddressHrp) (getBytes sr) TestNet -> Just $ encodeBech32 (C.pack sapTestPaymentAddressHrp) (getBytes sr) -- | Helper to get de Nework Id from FFI response -getNetId:: [Word8] -> ZcashNet -getNetId [x] = do - case x of +getNetId :: [Word8] -> ZcashNet +getNetId [x] = do + case x of 1 -> MainNet - 2 -> TestNet + 2 -> TestNet -- | decode a Sapling address decodeSaplingAddress :: BS.ByteString -> Maybe SaplingAddress -decodeSaplingAddress sapling_address = do +decodeSaplingAddress sapling_address = do if BS.length sa > 1 then do let sa0 = BS.unpack sa - Just $ SaplingAddress (getNetId (take 1 sa0)) - $ SaplingReceiver (BS.pack (drop 1 sa0)) + Just $ + SaplingAddress (getNetId (take 1 sa0)) $ + SaplingReceiver (BS.pack (drop 1 sa0)) else Nothing - where - sa = - withPureBorshVarBuffer $ - rustWrapperDecodeSaplingAddress sapling_address + where + sa = + withPureBorshVarBuffer $ rustWrapperDecodeSaplingAddress sapling_address diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index be2387f..e8bff8c 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.5.5.3 +version: 0.5.5.4 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain -- 2.43.0 From 8901d97c64559047700ede55d44849360efb4bb7 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Mon, 29 Apr 2024 10:27:45 -0500 Subject: [PATCH 04/22] Upgrade of Zcash Rust crates --- CHANGELOG.md | 16 ++++ librustzcash-wrapper/Cargo.lock | 157 ++++++++++++++++++++++++++++---- librustzcash-wrapper/Cargo.toml | 7 +- librustzcash-wrapper/src/lib.rs | 142 +++++++++++++++-------------- src/C/Zcash.chs | 6 +- src/ZcashHaskell/Sapling.hs | 6 +- src/ZcashHaskell/Types.hs | 9 +- test/Spec.hs | 56 +++++++++++- zcash-haskell.cabal | 2 +- 9 files changed, 297 insertions(+), 104 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c13f62..f1a6082 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.6.0.0] + +### Added + +- Rust crates: + - `sapling-crypto` 0.1.3 + +### Changed + +- Modified handling of `ShieldedOutput`s based on new Rust crates +- Upgraded Rust crates: + - `orchard` to 0.7.1 + - `zcash_primitives` to 0.14.0 + - `zcash_client_backend` to 0.11.1 + + ## [0.5.5.4] ### Added diff --git a/librustzcash-wrapper/Cargo.lock b/librustzcash-wrapper/Cargo.lock index 6a172d0..afcddf1 100644 --- a/librustzcash-wrapper/Cargo.lock +++ b/librustzcash-wrapper/Cargo.lock @@ -88,6 +88,27 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" +[[package]] +name = "bellman" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afceed28bac7f9f5a508bca8aeeff51cdfa4770c0b967ac55c621e2ddfd6171" +dependencies = [ + "bitvec", + "blake2s_simd", + "byteorder", + "crossbeam-channel", + "ff", + "group", + "lazy_static", + "log", + "num_cpus", + "pairing", + "rand_core", + "rayon", + "subtle", +] + [[package]] name = "bincode" version = "1.3.3" @@ -468,6 +489,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "document-features" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95" +dependencies = [ + "litrs", +] + [[package]] name = "either" version = "1.8.1" @@ -711,9 +741,9 @@ dependencies = [ [[package]] name = "incrementalmerkletree" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361c467824d4d9d4f284be4b2608800839419dccc4d4608f28345237fe354623" +checksum = "eb1872810fb725b06b8c153dde9e86f3ec26747b9b60096da7a869883b549cbe" dependencies = [ "either", ] @@ -807,6 +837,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + [[package]] name = "log" version = "0.4.19" @@ -935,9 +971,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "orchard" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d31e68534df32024dcc89a8390ec6d7bef65edd87d91b45cfb481a2eb2d77c5" +checksum = "1fb255c3ffdccd3c84fe9ebed72aef64fdc72e6a3e4180dd411002d47abaad42" dependencies = [ "aes", "bitvec", @@ -959,6 +995,8 @@ dependencies = [ "subtle", "tracing", "zcash_note_encryption", + "zcash_spec", + "zip32", ] [[package]] @@ -1218,6 +1256,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "redjubjub" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a60db2c3bc9c6fd1e8631fee75abc008841d27144be744951d6b9b75f9b569c" +dependencies = [ + "rand_core", + "reddsa", + "serde", + "thiserror", + "zeroize", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -1288,6 +1339,7 @@ dependencies = [ "nonempty", "orchard", "proc-macro2", + "sapling-crypto", "zcash_address 0.2.0", "zcash_client_backend", "zcash_note_encryption", @@ -1295,6 +1347,38 @@ dependencies = [ "zip32", ] +[[package]] +name = "sapling-crypto" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02f4270033afcb0c74c5c7d59c73cfd1040367f67f224fe7ed9a919ae618f1b7" +dependencies = [ + "aes", + "bellman", + "bitvec", + "blake2b_simd", + "blake2s_simd", + "bls12_381", + "byteorder", + "document-features", + "ff", + "fpe", + "group", + "hex", + "incrementalmerkletree", + "jubjub", + "lazy_static", + "memuse", + "rand", + "rand_core", + "redjubjub", + "subtle", + "tracing", + "zcash_note_encryption", + "zcash_spec", + "zip32", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -1356,9 +1440,9 @@ dependencies = [ [[package]] name = "shardtree" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19f96dde3a8693874f7e7c53d95616569b4009379a903789efbd448f4ea9cc7" +checksum = "dbf20c7a2747d9083092e3a3eeb9a7ed75577ae364896bebbc5e0bdcd4e97735" dependencies = [ "bitflags 2.4.2", "either", @@ -1701,24 +1785,27 @@ dependencies = [ [[package]] name = "zcash_client_backend" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6a382af39be9ee5a3788157145c404b7cd19acc440903f6c34b09fb44f0e991" +checksum = "001ec65dc2828ee648dc6d29f0944d7a877fe68ad06e001a203c11770ab1b3d4" dependencies = [ "base64", "bech32 0.9.1", "bls12_381", "bs58 0.5.0", "crossbeam-channel", + "document-features", "group", "hex", "incrementalmerkletree", "memuse", "nom", - "orchard", + "nonempty", "percent-encoding", "prost", + "rand_core", "rayon", + "sapling-crypto", "secrecy", "shardtree", "subtle", @@ -1728,8 +1815,10 @@ dependencies = [ "which", "zcash_address 0.3.1", "zcash_encoding", + "zcash_keys", "zcash_note_encryption", "zcash_primitives", + "zip32", ] [[package]] @@ -1742,6 +1831,29 @@ dependencies = [ "nonempty", ] +[[package]] +name = "zcash_keys" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f22d3407fdd6992b49f037f23862ab376be6013be6f2d0bc85948a635edc1f5" +dependencies = [ + "bech32 0.9.1", + "bls12_381", + "bs58 0.5.0", + "document-features", + "group", + "memuse", + "nonempty", + "rand_core", + "sapling-crypto", + "subtle", + "tracing", + "zcash_address 0.3.1", + "zcash_encoding", + "zcash_primitives", + "zip32", +] + [[package]] name = "zcash_note_encryption" version = "0.4.0" @@ -1757,17 +1869,15 @@ dependencies = [ [[package]] name = "zcash_primitives" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d17e4c94ca8d69d2fcf2be97522da5732a580eb2125cda3b150761952f8df8e6" +checksum = "9070e084570bb78aed4f8d71fd6254492e62c87a5d01e084183980e98117092d" dependencies = [ "aes", "bip0039", - "bitvec", "blake2b_simd", - "blake2s_simd", - "bls12_381", "byteorder", + "document-features", "equihash", "ff", "fpe", @@ -1775,17 +1885,30 @@ dependencies = [ "hex", "incrementalmerkletree", "jubjub", - "lazy_static", "memuse", "nonempty", "orchard", "rand", "rand_core", + "redjubjub", + "sapling-crypto", "sha2 0.10.6", "subtle", + "tracing", "zcash_address 0.3.1", "zcash_encoding", "zcash_note_encryption", + "zcash_spec", + "zip32", +] + +[[package]] +name = "zcash_spec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3bf58b673cb3dacd8ae09ba345998923a197ab0da70d6239d8e8838949e9b" +dependencies = [ + "blake2b_simd", ] [[package]] @@ -1810,9 +1933,9 @@ dependencies = [ [[package]] name = "zip32" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d724a63be4dfb50b7f3617e542984e22e4b4a5b8ca5de91f55613152885e6b22" +checksum = "4226d0aee9c9407c27064dfeec9d7b281c917de3374e1e5a2e2cfad9e09de19e" dependencies = [ "blake2b_simd", "memuse", diff --git a/librustzcash-wrapper/Cargo.toml b/librustzcash-wrapper/Cargo.toml index 73ee84c..5ae9b85 100644 --- a/librustzcash-wrapper/Cargo.toml +++ b/librustzcash-wrapper/Cargo.toml @@ -11,10 +11,11 @@ f4jumble = "0.1" zcash_address = "0.2.0" borsh = "0.10" bech32 = "0.11" -orchard = "0.6.0" +orchard = "0.7.1" zcash_note_encryption = "0.4.0" -zcash_primitives = "0.13.0" -zcash_client_backend = "0.10.0" +zcash_primitives = "0.14.0" +zcash_client_backend = "0.11.1" +sapling-crypto = "0.1.3" zip32 = "0.1.0" proc-macro2 = "1.0.66" nonempty = "0.7.0" diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 985c3c4..4c43d75 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -6,8 +6,7 @@ use std::{ marker::PhantomData, io::{ Write, - Cursor, - Error + Cursor }, }; @@ -29,7 +28,40 @@ use incrementalmerkletree::{ witness::IncrementalWitness }; -use zip32; +use zip32::{ + Scope as SaplingScope, + ChildIndex +}; + + +use sapling_crypto::{ + NOTE_COMMITMENT_TREE_DEPTH as SAPLING_DEPTH, + Node, + MerklePath, + PaymentAddress, + value::ValueCommitment as SaplingValueCommitment, + note::ExtractedNoteCommitment as SaplingNoteCommitment, + keys::{ + PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey, + ExpandedSpendingKey, + FullViewingKey as SaplingFullViewingKey + }, + note_encryption::{ + SaplingDomain, + Zip212Enforcement + }, + bundle::{ + GrothProofBytes, + OutputDescription, + SpendDescription, + Authorized as SaplingAuthorized, + Bundle as SaplingBundle + }, + zip32::{ + sapling_find_address, + DiversifierKey + } +}; use zcash_primitives::{ merkle_tree::{ @@ -38,12 +70,6 @@ use zcash_primitives::{ read_incremental_witness, write_incremental_witness }, - zip32::{ - Scope as SaplingScope, - ChildIndex, - sapling_find_address, - sapling::DiversifierKey - }, zip339::{Count, Mnemonic}, transaction::components::{ amount::Amount, @@ -53,28 +79,8 @@ use zcash_primitives::{ TxOut, OutPoint, Authorized - }, - sapling::{ - GrothProofBytes, - OutputDescription, - SpendDescription, - Authorized as SaplingAuthorized, - Bundle as SaplingBundle } }, - sapling::{ - Node, - MerklePath, - NOTE_COMMITMENT_TREE_DEPTH as SAPLING_DEPTH, - PaymentAddress, - note::ExtractedNoteCommitment as SaplingNoteCommitment, - keys::{ - PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey, - ExpandedSpendingKey, - FullViewingKey as SaplingFullViewingKey - }, - note_encryption::SaplingDomain - }, transaction::Transaction, consensus::{ BranchId::Nu5, @@ -99,7 +105,6 @@ use zcash_client_backend::keys::sapling::{ }; use zcash_primitives::zip32::DiversifierIndex; -use zcash_primitives::block::BlockHeader; use orchard::{ Bundle as OrchardBundle, @@ -161,7 +166,7 @@ impl ToHaskell for HrawTx { #[derive(BorshSerialize, BorshDeserialize)] pub struct HshieldedOutput { - cv: Hhex, + pub cv: Hhex, cmu: Hhex, eph_key: Hhex, enc_txt: Hhex, @@ -194,6 +199,13 @@ impl HshieldedOutput { } return r } + pub fn to_output_description(&mut self) -> Result> { + let cv = SaplingValueCommitment::from_bytes_not_small_order(&to_array(self.cv.bytes.clone())).unwrap(); + let cmu = SaplingNoteCommitment::from_bytes(&to_array(self.cmu.bytes.clone())).unwrap(); + let eph_key = zcash_note_encryption::EphemeralKeyBytes::from(to_array(self.eph_key.bytes.clone())); + let x = OutputDescription::from_parts(cv, cmu, eph_key, to_array(self.enc_txt.bytes.clone()), to_array(self.out_txt.bytes.clone()), to_array(self.proof.bytes.clone())); + return Ok(x) + } } #[derive(BorshSerialize, BorshDeserialize)] @@ -400,10 +412,9 @@ impl ToHaskell for HSBundle { } impl HSBundle { - pub fn from_bundle(sb: &SaplingBundle) -> HSBundle { - let mut s: Vec = Vec::new(); - sb.authorization().binding_sig.write(&mut s).unwrap(); - return HSBundle {empty: false, spends: Hspend::pack(sb.shielded_spends()) , outputs: HshieldedOutput::pack(sb.shielded_outputs()) , value: i64::from(sb.value_balance()) , sig: s } + pub fn from_bundle(sb: &SaplingBundle) -> HSBundle { + let sig = <[u8; 64]>::from(sb.authorization().binding_sig); + return HSBundle {empty: false, spends: Hspend::pack(sb.shielded_spends()) , outputs: HshieldedOutput::pack(sb.shielded_outputs()) , value: i64::from(sb.value_balance()) , sig: sig.to_vec()} } } @@ -428,11 +439,9 @@ impl Hspend { pub fn pack(sp: &[SpendDescription]) -> Vec { let mut r = Vec::new(); for s in sp { - let mut rk = Vec::new(); - let mut authsig = Vec::new(); - s.rk().write(&mut rk).unwrap(); - s.spend_auth_sig().write(&mut authsig).unwrap(); - r.push(Hspend {cv: Hhex{bytes:s.cv().to_bytes().to_vec()}, anchor: Hhex{bytes:s.anchor().to_bytes().to_vec()}, nullifier: Hhex{bytes:s.nullifier().to_vec()}, rk: Hhex{bytes: rk}, proof: Hhex{bytes:s.zkproof().to_vec()}, authsig: Hhex{bytes:authsig}}); + let rk = s.rk().clone(); + let sig = s.spend_auth_sig().clone(); + r.push(Hspend {cv: Hhex{bytes:s.cv().to_bytes().to_vec()}, anchor: Hhex{bytes:s.anchor().to_bytes().to_vec()}, nullifier: Hhex{bytes:s.nullifier().to_vec()}, rk: Hhex{bytes: <[u8; 32]>::from(rk).to_vec()}, proof: Hhex{bytes:s.zkproof().to_vec()}, authsig: Hhex{bytes: <[u8; 64]>::from(sig).to_vec()}}); } return r } @@ -654,10 +663,10 @@ pub extern "C" fn rust_wrapper_svk_check_address( let sa = PaymentAddress::from_bytes(&to_array(address_input)).unwrap(); match svk { Ok(k) => { - let (div_index, def_address) = k.default_address(); + let (_div_index, def_address) = k.default_address(); sa == def_address } - Err(e) => { + Err(_e) => { false } } @@ -740,11 +749,10 @@ pub extern "C" fn rust_wrapper_sapling_esk_decrypt( out_len: &mut usize ){ let sk: Vec = marshall_from_haskell_var(key, key_len, RW); - let note_input: Vec = marshall_from_haskell_var(note,note_len,RW); - let mut note_reader = Cursor::new(note_input); + let mut note_input: HshieldedOutput = marshall_from_haskell_var(note,note_len,RW); let esk = ExtendedSpendingKey::from_bytes(&sk); - let main_domain = SaplingDomain::for_height(MainNetwork, BlockHeight::from_u32(2000000)); - let test_domain = SaplingDomain::for_height(TestNetwork, BlockHeight::from_u32(2000000)); + let main_domain = SaplingDomain::new(Zip212Enforcement::On); + //let test_domain = SaplingDomain::for_height(TestNetwork, BlockHeight::from_u32(2000000)); let scope = if external { SaplingScope::External } else { @@ -752,15 +760,14 @@ pub extern "C" fn rust_wrapper_sapling_esk_decrypt( }; match esk { Ok(k) => { - let action = OutputDescription::read(&mut note_reader); - match action { + let action = note_input.to_output_description(); + match action { Ok(action2) => { let dfvk = k.to_diversifiable_full_viewing_key(); let ivk = dfvk.to_ivk(scope); let nk = dfvk.to_nk(scope); let pivk = SaplingPreparedIncomingViewingKey::new(&ivk); - let result = if net { zcash_note_encryption::try_note_decryption(&main_domain, &pivk, &action2)} - else {zcash_note_encryption::try_note_decryption(&test_domain, &pivk, &action2)}; + let result = zcash_note_encryption::try_note_decryption(&main_domain, &pivk, &action2); match result { Some((n, r, m)) => { let nullifier = n.nf(&nk, pos); @@ -796,13 +803,12 @@ pub extern "C" fn rust_wrapper_sapling_note_decrypt_v2( out_len: &mut usize ){ let evk: Vec = marshall_from_haskell_var(key, key_len, RW); - let note_input: Vec = marshall_from_haskell_var(note,note_len,RW); - let mut note_reader = Cursor::new(note_input); + let mut note_input: HshieldedOutput = marshall_from_haskell_var(note,note_len,RW); let svk = ExtendedFullViewingKey::read(&*evk); match svk { Ok(k) => { - let domain = SaplingDomain::for_height(MainNetwork, BlockHeight::from_u32(2000000)); - let action2 = OutputDescription::read(&mut note_reader); + let domain = SaplingDomain::new(Zip212Enforcement::On); + let action2 = note_input.to_output_description(); match action2 { Ok(action3) => { let fvk = k.to_diversifiable_full_viewing_key().to_ivk(SaplingScope::External); @@ -976,22 +982,14 @@ pub extern "C" fn rust_wrapper_tx_parse( out_len: &mut usize ){ let tx_input: Vec = marshall_from_haskell_var(tx, tx_len, RW); - let tx_bytes: Vec = tx_input.clone(); let mut tx_reader = Cursor::new(tx_input); - let s_o = false; - let o_a = false; let parsed_tx = Transaction::read(&mut tx_reader, Nu5); match parsed_tx { Ok(t) => { let s_bundle = t.sapling_bundle(); match s_bundle { Some(b) => { - let mut s_output = Vec::new(); - for s_each_out in b.shielded_outputs().iter() { - let mut out_bytes = Vec::new(); - let _ = s_each_out.write_v4(&mut out_bytes); - s_output.push(out_bytes); - } + let s_output = HshieldedOutput::pack(b.shielded_outputs()); marshall_to_haskell_var(&s_output, out, out_len, RW); }, None => { @@ -1067,7 +1065,7 @@ pub extern "C" fn rust_wrapper_sapling_paymentaddress( let sp_key = ExtendedSpendingKey::from_bytes(&extspk); match sp_key { Ok(sp_key_x) => { - let (def_div, def_address) = sp_key_x.default_address(); + let (_def_div, def_address) = sp_key_x.default_address(); marshall_to_haskell_var(&def_address.to_bytes().to_vec(), out, out_len, RW); }, Err(_e) => { @@ -1102,14 +1100,14 @@ pub extern "C" fn rust_wrapper_sapling_chgpaymentaddress( let extspku8 : &[u8] = &vexspkp; let extspk = match ExtendedSpendingKey::from_bytes(&extspku8) { Ok( k ) => k, - Err( e ) => { + Err( _e ) => { // error recovering ExtendedSpendingKey marshall_to_haskell_var(&vec![0], out, out_len, RW); return } }; let dfvk = extspk.to_diversifiable_full_viewing_key(); - let ( divIx, cPmtAddress ) = dfvk.change_address(); + let ( _divIx, cPmtAddress ) = dfvk.change_address(); marshall_to_haskell_var(&cPmtAddress.to_bytes().to_vec(), out, out_len, RW); } @@ -1123,7 +1121,7 @@ pub extern "C" fn rust_wrapper_derive_orchard_spending_key( out_len: &mut usize ){ let s: Vec = marshall_from_haskell_var(seed, seed_len, RW); - let sk = SpendingKey::from_zip32_seed(&s, coin_type, u32::from(zip32::AccountId::try_from(acc_id).unwrap())); + let sk = SpendingKey::from_zip32_seed(&s, coin_type, zip32::AccountId::try_from(acc_id).unwrap()); match sk { Ok(key) => { marshall_to_haskell_var(&key.to_bytes().to_vec(), out, out_len, RW); @@ -1441,7 +1439,7 @@ pub extern "C" fn rust_wrapper_decode_sapling_address( let out_bytes: Vec = out_bytes_temp.to_vec(); marshall_to_haskell_var(&out_bytes, out, out_len, RW); } - Err(e) => { + Err(_e) => { let h = vec![0]; marshall_to_haskell_var(&h, out, out_len, RW); } @@ -1453,3 +1451,11 @@ pub extern "C" fn rust_wrapper_decode_sapling_address( } } } + +#[no_mangle] +pub extern "C" fn rust_wrapper_create_transaction( + sapling: *const u8, + sapling_len: usize, + out: *mut u8, + out_len: &mut usize){ +} diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 0300865..6e0dff8 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -96,7 +96,7 @@ import ZcashHaskell.Types {# fun unsafe rust_wrapper_sapling_note_decrypt_v2 as rustWrapperSaplingNoteDecode { toBorshVar* `BS.ByteString'& - , toBorshVar* `BS.ByteString'& + , toBorshVar* `ShieldedOutput'& , getVarBuffer `Buffer DecodedNote'& } -> `()' @@ -104,7 +104,7 @@ import ZcashHaskell.Types {# fun unsafe rust_wrapper_sapling_esk_decrypt as rustWrapperSaplingDecodeEsk { toBorshVar* `BS.ByteString'& - , toBorshVar* `BS.ByteString'& + , toBorshVar* `ShieldedOutput'& , `Bool' , `Bool' , `Word64' @@ -138,7 +138,7 @@ import ZcashHaskell.Types #} {# fun unsafe rust_wrapper_tx_parse as rustWrapperTxParse { toBorshVar* `BS.ByteString'& - , getVarBuffer `Buffer [BS.ByteString]'& + , getVarBuffer `Buffer [ShieldedOutput]'& } -> `()' #} diff --git a/src/ZcashHaskell/Sapling.hs b/src/ZcashHaskell/Sapling.hs index 1b4e3bc..5ba501e 100644 --- a/src/ZcashHaskell/Sapling.hs +++ b/src/ZcashHaskell/Sapling.hs @@ -50,7 +50,7 @@ import ZcashHaskell.Utils (decodeBech32, encodeBech32, encodeBech32m) isValidShieldedAddress :: BS.ByteString -> Bool isValidShieldedAddress = rustWrapperIsShielded -getShieldedOutputs :: HexString -> [BS.ByteString] +getShieldedOutputs :: HexString -> [ShieldedOutput] getShieldedOutputs t = withPureBorshVarBuffer $ rustWrapperTxParse $ toBytes t serializeShieldedOutput :: ShieldedOutput -> BS.ByteString @@ -76,7 +76,7 @@ matchSaplingAddress :: BS.ByteString -> BS.ByteString -> Bool matchSaplingAddress = rustWrapperSaplingCheck -- | Attempt to decode the given raw tx with the given Sapling viewing key -decodeSaplingOutput :: BS.ByteString -> BS.ByteString -> Maybe DecodedNote +decodeSaplingOutput :: BS.ByteString -> ShieldedOutput -> Maybe DecodedNote decodeSaplingOutput key out = case a_value decodedAction of 0 -> Nothing @@ -119,7 +119,7 @@ decodeSaplingOutputEsk key out znet scope pos = withPureBorshVarBuffer $ rustWrapperSaplingDecodeEsk (getBytes key) - (serializeShieldedOutput out) + out (scope == External) (znet == MainNet) (fromIntegral pos) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 25bb43f..91ccae3 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -259,7 +259,7 @@ data RawTxResponse = RawTxResponse { rt_id :: !HexString , rt_hex :: !HexString , rt_shieldedSpends :: ![ShieldedSpend] - , rt_shieldedOutputs :: ![BS.ByteString] + , rt_shieldedOutputs :: ![ShieldedOutput] , rt_orchardActions :: ![OrchardAction] , rt_blockheight :: !Integer , rt_confirmations :: !Integer @@ -483,10 +483,9 @@ newtype SaplingReceiver = instance ToBytes SaplingReceiver where getBytes (SaplingReceiver s) = s -data SaplingAddress = SaplingAddress - { - net_type :: !ZcashNet - , sa_receiver :: !SaplingReceiver +data SaplingAddress = SaplingAddress + { net_type :: !ZcashNet + , sa_receiver :: !SaplingReceiver } deriving (Eq, Prelude.Show, Read) -- | Type to represent a Sapling Shielded Spend as provided by the @getrawtransaction@ RPC method diff --git a/test/Spec.hs b/test/Spec.hs index 19ca7a4..7b0aac0 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -25,6 +25,7 @@ import Control.Monad.IO.Class (liftIO) import Data.Aeson import Data.Bool (Bool(True)) import qualified Data.ByteString as BS +import qualified Data.ByteString.Char8 as C import Data.Either (isRight) import Data.Foldable (sequenceA_) import Data.HexString @@ -60,6 +61,7 @@ import ZcashHaskell.Sapling , isValidShieldedAddress , matchSaplingAddress , updateSaplingCommitmentTree + , updateSaplingWitness ) import ZcashHaskell.Transparent import ZcashHaskell.Types @@ -68,6 +70,7 @@ import ZcashHaskell.Types , CoinType(..) , DecodedNote(..) , OrchardAction(..) + , OrchardBundle(..) , OrchardCommitmentTree(..) , OrchardSpendingKey(..) , OrchardWitness(..) @@ -84,6 +87,7 @@ import ZcashHaskell.Types , SaplingCommitmentTree(..) , SaplingReceiver(..) , SaplingSpendingKey(..) + , SaplingWitness(..) , Scope(..) , Seed(..) , ShieldedOutput(..) @@ -438,7 +442,8 @@ main = do let a = decodeSaplingOutput (bytes rawKey) (head x) it "amount should match" $ do maybe 0 a_value a `shouldBe` 10000 it "memo should match" $ do - maybe "" a_memo a `shouldBe` "Tx with Sapling and Orchard" + C.filter (/= '\NUL') (maybe "" a_memo a) `shouldBe` + "Tx with Sapling and Orchard" describe "fails with incorrect key" $ do let a = decodeSaplingOutput (bytes badKey) (head x) it "amount should not match" $ do maybe 0 a_value a `shouldNotBe` 10000 @@ -829,11 +834,11 @@ main = do Nothing -> assertFailure "Couldn't decode" Just t1' -> do let tb1 = zt_tBundle t1' - print tb1 let txInHex = HexString "Km\237=.\228>T\DC3\\\EOT\249\163\ENQ\180s\215\215A\187\230\243\131\170cn\ETX\233Hp^\r" - toText txInHex `shouldBe` "Blank" + toText txInHex `shouldBe` + "4b6ded3d2ee43e54135c04f9a305b473d7d741bbe6f383aa636e03e948705e0d" it "Sapling component is read" $ do case t of Nothing -> assertFailure "Couldn't decode" @@ -868,6 +873,14 @@ main = do Just t' -> do let ob = zt_oBundle t' fromRawOBundle ob `shouldNotBe` Nothing + it "Orchard CMX is present" $ do + case t of + Nothing -> assertFailure "Couldn't decode" + Just t' -> do + let ob = zt_oBundle t' + case fromRawOBundle ob of + Nothing -> assertFailure "Couldn't open the orchard bundle" + Just s -> toBytes (cmx (head (obActions s))) `shouldNotBe` "" describe "Raw transaction with Transparent inputs" $ do let h = hexString @@ -1113,7 +1126,7 @@ main = do MainNet External p - dn `shouldBe` Nothing + dn `shouldNotBe` Nothing describe "Generate an ExchangeAddress (MainNet) from transparent address" $ do let ta = decodeTransparentAddress "t1dMjvesbzdG41xgKaGU3HgwYJwSgbCK54e" it "Try to generate valid ExchangeAddress from Transparent Address" $ do @@ -1132,6 +1145,41 @@ main = do Just addr -> do let eadr = decodeExchangeAddress addr eadr `shouldNotBe` Nothing + describe "Witness updates" $ do + it "Sapling" $ do + let wit = + SaplingWitness $ + hexString + "01bd8a3f3cfc964332a2ada8c09a0da9dfc24174befb938abb086b9be5ca049e49013607f5e51826c8e5f660571ddfae14cd6fb1dc026bcd6855459b4e9339b20521100000019f0d7efb00169bb2202152d3266059d208ab17d14642c3339f9075e997160657000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39038cd7f6e2238d16ef49420963348dd4e4c7d23d5e5dac69507fba8937f63eb626f6856115bea2fa8db3a65a0ab294db41c51435d3b7ea27c7b2835aca28e82a2c1d9634efe07449a47c251518ac6f92c49f3a1ef119948f6a824d1e7ff7d0443e0101e57ec972a9b9383dc9cb228980d2d7752bb2abebc4a604ca48c5457039d2e05b000301392bed8592185dde5ab7fc81aed75e98fcf041f1a3fda55ad0b0b139ba9380130001808304b4d7c4fc407f5ce28247a7119013aeaaf1481902419c42bc8b21575c15" + let cmus = + [ hexString + "958ccdc752f2f593f6c1c8e2d7201348cd896e54c6d3c92200bdbe8b859eac44" + , hexString + "e49992fdd071d90bf56242d1aa625bbe267a34e0debd4307818a686d05b45447" + , hexString + "0c4b26766d89bf6cdb4fd3b0317b4e9a2fb3850f6a24869f32fe7cb0fd512e18" + ] + updateSaplingWitness wit cmus `shouldBe` + SaplingWitness + (hexString + "01bd8a3f3cfc964332a2ada8c09a0da9dfc24174befb938abb086b9be5ca049e49013607f5e51826c8e5f660571ddfae14cd6fb1dc026bcd6855459b4e9339b20521100000019f0d7efb00169bb2202152d3266059d208ab17d14642c3339f9075e997160657000000012f4f72c03f8c937a94919a01a07f21165cc8394295291cb888ca91ed003810390107114fe4bb4cd08b47f6ae47477c182d5da9fe5c189061808c1091e9bf3b4524000001447d6b9100cddd5f80c8cf4ddee2b87eba053bd987465aec2293bd0514e68b0d015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39038cd7f6e2238d16ef49420963348dd4e4c7d23d5e5dac69507fba8937f63eb626f6856115bea2fa8db3a65a0ab294db41c51435d3b7ea27c7b2835aca28e82a2c1d9634efe07449a47c251518ac6f92c49f3a1ef119948f6a824d1e7ff7d0443e0101e49992fdd071d90bf56242d1aa625bbe267a34e0debd4307818a686d05b45447010c4b26766d89bf6cdb4fd3b0317b4e9a2fb3850f6a24869f32fe7cb0fd512e1803000121c06ee1f1584f79d50785797a694c742be2ded600367ab7d54f3ed49e3adf7201808304b4d7c4fc407f5ce28247a7119013aeaaf1481902419c42bc8b21575c15") + it "Orchard" $ do + let wit = + OrchardWitness $ + hexString + "016225b41339a00dd764b452fca190a0245e7118224965942e3a6d798365c34631001f0000011d6f5da3f619bfaab957fc643c17eb144db0101c90f422da2fcbe0e80d74412e000000000001746e6bc066a10e7f80a9ff8993dcb25c819edd64f2ca10ac248ef7848d41450500011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000040e02c864db8b574f165f616d48e2f12eb25099b5c90186af26d9e50f5058863e0504bfbc12edc35e05042c16bbfb8fed591f01f18fe128eeb57f2c456c9eb222d6d261c549e95d9007bce4c6ae0b86bc865711cdd9f0fa92e2d5b5e149b51f3be127df3b1d2372adf6c811b2e456c1d64d0e9eb167a995f9c6b66a03c9cbda250101c094201bae3b4ef582a3e8654f65a72fbd41e20e1ec9a43d3f4101afc868731e000200019df5b9366d0f21caa678d1567390b5bfd3cfa0438271bcfe301b5558a2863301" + let cmxs = + [ hexString + "712ba86615ff4447e8d7c7b59f3873f03c03a173438b8e4c8d416756ed4fae10" + , hexString + "c094201bae3b4ef582a3e8654f65a72fbd41e20e1ec9a43d3f4101afc868731e" + , hexString + "ac20b8170b008888c19fc6e16f5e30a5ef1653e5219d0cd0c9353c3aa8f79823" + ] + updateOrchardWitness wit cmxs `shouldBe` + OrchardWitness + (hexString + "016225b41339a00dd764b452fca190a0245e7118224965942e3a6d798365c34631001f0000011d6f5da3f619bfaab957fc643c17eb144db0101c90f422da2fcbe0e80d74412e000000000001746e6bc066a10e7f80a9ff8993dcb25c819edd64f2ca10ac248ef7848d41450500011e6191f91b3fceb62dc881a156e1b9d2e88e09dca25093cf9c4936c8869fb41a013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000040e02c864db8b574f165f616d48e2f12eb25099b5c90186af26d9e50f5058863e0504bfbc12edc35e05042c16bbfb8fed591f01f18fe128eeb57f2c456c9eb222d6d261c549e95d9007bce4c6ae0b86bc865711cdd9f0fa92e2d5b5e149b51f3be127df3b1d2372adf6c811b2e456c1d64d0e9eb167a995f9c6b66a03c9cbda250101c094201bae3b4ef582a3e8654f65a72fbd41e20e1ec9a43d3f4101afc868731e0002010cfb50d8c877eb39e9c07082a032dd99d34be7c19fa7f30e9fecf5f14736240f019df5b9366d0f21caa678d1567390b5bfd3cfa0438271bcfe301b5558a2863301") -- | Properties prop_PhraseLength :: Property diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index e8bff8c..e739a59 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.5.5.4 +version: 0.6.0.0 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain -- 2.43.0 From ad9e2f074d76d5aa84a8c7a63a715b2537630020 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 30 Apr 2024 16:54:15 -0500 Subject: [PATCH 05/22] Implement transaction creation --- CHANGELOG.md | 21 ++ librustzcash-wrapper/Cargo.lock | 168 +++++++++++++ librustzcash-wrapper/Cargo.toml | 5 +- librustzcash-wrapper/src/lib.rs | 414 +++++++++++++++++++++++++++++--- src/C/Zcash.chs | 16 ++ src/ZcashHaskell/Transparent.hs | 65 ++--- src/ZcashHaskell/Types.hs | 49 ++++ zcash-haskell.cabal | 2 +- 8 files changed, 682 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1a6082..6f9e53b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.6.1.0] + +### Added + +- Function to create a raw transaction +- New types for transaction creation: + - `Rseed` + - `TransparentTxSpend` + - `SaplingTxSpend` + - `OrchardTxSpend` + - `OutgoingNote` +- Rust crates: + - `secp256k1` + - `jubjub` + - `rand_core` + + +### Changed + +- `DecodedNote` type now includes a field for `rho` and one for `rseed` + ## [0.6.0.0] ### Added diff --git a/librustzcash-wrapper/Cargo.lock b/librustzcash-wrapper/Cargo.lock index afcddf1..53583a4 100644 --- a/librustzcash-wrapper/Cargo.lock +++ b/librustzcash-wrapper/Cargo.lock @@ -318,6 +318,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "bumpalo" +version = "3.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" + [[package]] name = "byteorder" version = "1.4.3" @@ -703,6 +709,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "hdwallet" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a03ba7d4c9ea41552cd4351965ff96883e629693ae85005c501bb4b9e1c48a7" +dependencies = [ + "lazy_static", + "rand_core", + "ring", + "secp256k1", + "thiserror", +] + [[package]] name = "heck" version = "0.4.1" @@ -796,6 +815,15 @@ dependencies = [ "either", ] +[[package]] +name = "js-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "jubjub" version = "0.10.0" @@ -1313,6 +1341,30 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "rustix" version = "0.37.20" @@ -1336,10 +1388,13 @@ dependencies = [ "f4jumble", "haskell-ffi", "incrementalmerkletree", + "jubjub", "nonempty", "orchard", "proc-macro2", + "rand_core", "sapling-crypto", + "secp256k1", "zcash_address 0.2.0", "zcash_client_backend", "zcash_note_encryption", @@ -1385,6 +1440,24 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "secp256k1" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4124a35fe33ae14259c490fd70fa199a32b9ce9502f2ee6bc4f81ec06fa65894" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" +dependencies = [ + "cc", +] + [[package]] name = "secrecy" version = "0.8.0" @@ -1661,6 +1734,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "version_check" version = "0.9.4" @@ -1673,6 +1752,70 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.32", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "which" version = "4.4.0" @@ -1684,6 +1827,28 @@ dependencies = [ "once_cell", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.48.0" @@ -1882,6 +2047,7 @@ dependencies = [ "ff", "fpe", "group", + "hdwallet", "hex", "incrementalmerkletree", "jubjub", @@ -1891,7 +2057,9 @@ dependencies = [ "rand", "rand_core", "redjubjub", + "ripemd", "sapling-crypto", + "secp256k1", "sha2 0.10.6", "subtle", "tracing", diff --git a/librustzcash-wrapper/Cargo.toml b/librustzcash-wrapper/Cargo.toml index 5ae9b85..b8b2b37 100644 --- a/librustzcash-wrapper/Cargo.toml +++ b/librustzcash-wrapper/Cargo.toml @@ -13,13 +13,16 @@ borsh = "0.10" bech32 = "0.11" orchard = "0.7.1" zcash_note_encryption = "0.4.0" -zcash_primitives = "0.14.0" +zcash_primitives = { version = "0.14.0", features = ["transparent-inputs"]} zcash_client_backend = "0.11.1" sapling-crypto = "0.1.3" zip32 = "0.1.0" proc-macro2 = "1.0.66" nonempty = "0.7.0" incrementalmerkletree = "0.5.0" +secp256k1 = "0.26.0" +jubjub = "0.10.0" +rand_core = { version = "0.6.4", features = ["getrandom"]} [features] diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 4c43d75..6d98974 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -12,6 +12,8 @@ use std::{ use nonempty::NonEmpty; +use rand_core::OsRng; + use f4jumble; use borsh::{BorshDeserialize, BorshSerialize}; @@ -23,6 +25,10 @@ use haskell_ffi::{ FromHaskell, HaskellSize, ToHaskell }; +use secp256k1::SecretKey; + +use jubjub::Fr; + use incrementalmerkletree::{ frontier::CommitmentTree, witness::IncrementalWitness @@ -39,8 +45,13 @@ use sapling_crypto::{ Node, MerklePath, PaymentAddress, + Anchor as SaplingAnchor, value::ValueCommitment as SaplingValueCommitment, - note::ExtractedNoteCommitment as SaplingNoteCommitment, + note::{ + ExtractedNoteCommitment as SaplingNoteCommitment, + Note as SaplingNote, + Rseed + }, keys::{ PreparedIncomingViewingKey as SaplingPreparedIncomingViewingKey, ExpandedSpendingKey, @@ -57,6 +68,11 @@ use sapling_crypto::{ Authorized as SaplingAuthorized, Bundle as SaplingBundle }, + value::NoteValue as SaplingNoteValue, + circuit::{ + SpendParameters, + OutputParameters + }, zip32::{ sapling_find_address, DiversifierKey @@ -70,18 +86,34 @@ use zcash_primitives::{ read_incremental_witness, write_incremental_witness }, + legacy::{ + Script, + TransparentAddress + }, zip339::{Count, Mnemonic}, - transaction::components::{ - amount::Amount, - transparent::{ - Bundle as TransparentBundle, - TxIn, - TxOut, - OutPoint, - Authorized + transaction::{ + Transaction, + fees::zip317::FeeRule, + builder::{ + Builder, + Error, + BuildConfig + }, + components::{ + amount::{ + Amount, + NonNegativeAmount + }, + transparent::{ + Bundle as TransparentBundle, + TxIn, + TxOut, + OutPoint, + Authorized + } } }, - transaction::Transaction, + memo::MemoBytes, consensus::{ BranchId::Nu5, MainNetwork, @@ -107,6 +139,7 @@ use zcash_client_backend::keys::sapling::{ use zcash_primitives::zip32::DiversifierIndex; use orchard::{ + Address as OrchardAddress, Bundle as OrchardBundle, bundle::{ Authorized as OrchardAuthorized, @@ -114,11 +147,18 @@ use orchard::{ }, Action, keys::{SpendingKey, FullViewingKey, PreparedIncomingViewingKey, Scope}, - note::{Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment}, + note::{RandomSeed, Note, Nullifier, TransmittedNoteCiphertext, ExtractedNoteCommitment}, note_encryption::OrchardDomain, primitives::redpallas::{VerificationKey, SpendAuth, Signature}, - tree::MerkleHashOrchard, - value::ValueCommitment + tree::{ + MerklePath as OrchardMerklePath, + MerkleHashOrchard, + Anchor as OrchardAnchor + }, + value::{ + ValueCommitment, + NoteValue + } }; use bech32::{ @@ -255,7 +295,9 @@ pub struct Hnote { note: u64, recipient: Vec, memo: Vec, - nullifier: Vec + nullifier: Vec, + rho: Vec, + rseed: Hrseed } impl ToHaskell for Hnote { @@ -265,6 +307,27 @@ impl ToHaskell for Hnote { } } +#[derive(BorshSerialize, BorshDeserialize)] +pub struct Hrseed { + kind: u8, + bytes: Vec +} + + +impl FromHaskell for Hrseed { + fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { + let x = Hrseed::deserialize(buf)?; + Ok(x) + } +} + +impl ToHaskell for Hrseed { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } +} + #[derive(BorshSerialize, BorshDeserialize)] pub struct Hua { net: u8, @@ -363,6 +426,13 @@ pub struct HTxOut { script: Vec } +impl FromHaskell for HTxOut { + fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { + let x = HTxOut::deserialize(buf)?; + Ok(x) + } +} + impl ToHaskell for HTxOut { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; @@ -374,6 +444,9 @@ impl HTxOut { pub fn pack(t: &TxOut) -> HTxOut { return HTxOut { amt: i64::from_le_bytes(t.value.to_i64_le_bytes()) , script: t.script_pubkey.0.clone() } } + pub fn unpack(&self) -> TxOut { + TxOut { value: NonNegativeAmount::from_nonnegative_i64(self.amt).unwrap(), script_pubkey: Script(self.script.clone())} + } } #[derive(BorshSerialize, BorshDeserialize)] @@ -382,6 +455,13 @@ pub struct Houtpoint { index: u32 } +impl FromHaskell for Houtpoint { + fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { + let x = Houtpoint::deserialize(buf)?; + Ok(x) + } +} + impl ToHaskell for Houtpoint { fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { self.serialize(writer)?; @@ -393,6 +473,31 @@ impl Houtpoint { pub fn pack(o: &OutPoint) -> Houtpoint { return Houtpoint {hash: o.hash().to_vec() , index: o.n() } } + + pub fn unpack(&self) -> OutPoint { + OutPoint::new(to_array(self.hash.clone()), self.index) + } +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct HtransparentInput { + sk: Vec, + utxo: Houtpoint, + coin: HTxOut +} + +impl FromHaskell for HtransparentInput { + fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { + let x = HtransparentInput::deserialize(buf)?; + Ok(x) + } +} + +impl ToHaskell for HtransparentInput { + fn to_haskell(&self, writer: &mut W, _tag: PhantomData) -> Result<()> { + self.serialize(writer)?; + Ok(()) + } } #[derive(BorshSerialize, BorshDeserialize)] @@ -447,6 +552,52 @@ impl Hspend { } } +#[derive(BorshSerialize, BorshDeserialize)] +pub struct HsaplingInput { + sk: Vec, + note: Hnote, + iw: Vec +} + +impl FromHaskell for HsaplingInput { + fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { + let x = HsaplingInput::deserialize(buf)?; + Ok(x) + } +} + + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct HorchardInput { + sk: Vec, + note: Hnote, + iw: Vec +} + +impl FromHaskell for HorchardInput { + fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { + let x = HorchardInput::deserialize(buf)?; + Ok(x) + } +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct Houtput { + kind: u8, + ovk: Vec, + to: Vec, + amt: u64, + memo: Vec, + chg: bool +} + +impl FromHaskell for Houtput { + fn from_haskell(buf: &mut &[u8], _tag: PhantomData) -> Result { + let x = Houtput::deserialize(buf)?; + Ok(x) + } +} + #[derive(BorshSerialize, BorshDeserialize)] pub struct HOBundle { empty: bool, @@ -771,23 +922,31 @@ pub extern "C" fn rust_wrapper_sapling_esk_decrypt( match result { Some((n, r, m)) => { let nullifier = n.nf(&nk, pos); - let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec(), nullifier: nullifier.to_vec() }; + let rseed = match n.rseed() { + Rseed::BeforeZip212(x) => { + Hrseed {kind: 1, bytes: x.to_bytes().to_vec()} + }, + Rseed::AfterZip212(y) => { + Hrseed {kind: 2, bytes: y.to_vec()} + } + }; + let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec(), nullifier: nullifier.to_vec(), rho: vec![0], rseed }; marshall_to_haskell_var(&hn, out, out_len, RW); }, None => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed{ kind: 0, bytes: vec![0]}}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } }, Err(_e1) => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0] }; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed{ kind: 0, bytes: vec![0]} }; marshall_to_haskell_var(&hn0, out, out_len, RW); } } }, Err(_e) => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0] }; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed{ kind: 0, bytes: vec![0]} }; marshall_to_haskell_var(&hn0, out, out_len, RW); } } @@ -816,23 +975,31 @@ pub extern "C" fn rust_wrapper_sapling_note_decrypt_v2( let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action3); match result { Some((n, r, m)) => { - let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec(), nullifier: vec![0]}; + let rseed = match n.rseed() { + Rseed::BeforeZip212(x) => { + Hrseed { kind: 1, bytes: x.to_bytes().to_vec()} + }, + Rseed::AfterZip212(y) => { + Hrseed { kind: 2, bytes: y.to_vec()} + } + }; + let hn = Hnote {note: n.value().inner(), recipient: r.to_bytes().to_vec(), memo: m.as_slice().to_vec(), nullifier: vec![0], rho: vec![0], rseed}; marshall_to_haskell_var(&hn, out, out_len, RW); } None => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } }, Err(_e1) => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] , nullifier: vec![0]}; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0] , nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } } Err(_e) => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } @@ -866,17 +1033,19 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt( let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action); match result { Some((n, r, m)) => { - let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec(), nullifier: vec![0]}; + let rho = n.rho().to_bytes().to_vec(); + let rseed = Hrseed {kind: 3, bytes: n.rseed().as_bytes().to_vec()}; + let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec(), nullifier: vec![0], rho, rseed}; marshall_to_haskell_var(&hn, out, out_len, RW); } None => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } }, None => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } @@ -914,11 +1083,13 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt_sk( let result = zcash_note_encryption::try_note_decryption(&domain, &pivk, &action); match result { Some((n, r, m)) => { - let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec(), nullifier: n.nullifier(&fvk).to_bytes().to_vec()}; + let rho = n.rho().to_bytes().to_vec(); + let rseed = Hrseed {kind: 3, bytes: n.rseed().as_bytes().to_vec()}; + let hn = Hnote {note: n.value().inner(), recipient: r.to_raw_address_bytes().to_vec(), memo: m.to_vec(), nullifier: n.nullifier(&fvk).to_bytes().to_vec(), rho, rseed}; marshall_to_haskell_var(&hn, out, out_len, RW); } None => { - let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0]}; + let hn0 = Hnote { note: 0, recipient: vec![0], memo: vec![0], nullifier: vec![0], rho: vec![0], rseed: Hrseed {kind: 0, bytes: vec![0]}}; marshall_to_haskell_var(&hn0, out, out_len, RW); } } @@ -1454,8 +1625,193 @@ pub extern "C" fn rust_wrapper_decode_sapling_address( #[no_mangle] pub extern "C" fn rust_wrapper_create_transaction( - sapling: *const u8, - sapling_len: usize, + sap_wit: *const u8, + sap_wit_len: usize, + orch_wit: *const u8, + orch_wit_len: usize, + t_input: *const u8, + t_input_len: usize, + s_input: *const u8, + s_input_len: usize, + o_input: *const u8, + o_input_len: usize, + out_list: *const u8, + out_list_len: usize, + sapspend: *const u8, + sapspend_len: usize, + sapoutput: *const u8, + sapoutput_len: usize, + net: bool, + bl_height: u32, out: *mut u8, out_len: &mut usize){ + let sap_wit_in: Vec = marshall_from_haskell_var(sap_wit, sap_wit_len, RW); + let sap_wit_reader = Cursor::new(sap_wit_in); + let sap_iw: Option> = read_incremental_witness(sap_wit_reader).ok(); + let sap_anchor = match sap_iw { + Some(s_iw) => { + Some(SaplingAnchor::from(s_iw.root())) + }, + None => { + None + } + }; + let orch_wit_in: Vec = marshall_from_haskell_var(orch_wit, orch_wit_len, RW); + let orch_wit_reader = Cursor::new(orch_wit_in); + let orch_iw: Option> = read_incremental_witness(orch_wit_reader).ok(); + let orch_anchor = match orch_iw { + Some(o_iw) => { + Some(OrchardAnchor::from(o_iw.root())) + }, + None => { + None + } + }; + let build_config = BuildConfig::Standard {sapling_anchor: sap_anchor, orchard_anchor: orch_anchor}; + let mut main_builder = Builder::new(MainNetwork, BlockHeight::from(bl_height), build_config); + let mut test_builder = Builder::new(TestNetwork, BlockHeight::from(bl_height), build_config); + let trans_input: Vec = marshall_from_haskell_var(t_input, t_input_len, RW); + for t_in in trans_input { + if t_in.sk.len() > 1 { + let k = SecretKey::from_slice(&t_in.sk).unwrap(); + if net { + match main_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) { + Ok(()) => { + continue; + }, + Err(_e) => { println!("Error reading transparent input"); } + } + } else { + match test_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) { + Ok(()) => { + continue; + }, + Err(_e) => { println!("Error reading transparent input"); } + } + } + } + } + let sap_input: Vec = marshall_from_haskell_var(s_input, s_input_len, RW); + for s_in in sap_input { + if s_in.sk.len() > 1 { + let sp_key = ExtendedSpendingKey::from_bytes(&s_in.sk); + match sp_key { + Ok(sk) => { + let pay_addr = PaymentAddress::from_bytes(&to_array(s_in.note.recipient)).unwrap(); + let rseed = if s_in.note.rseed.kind == 1 { + Rseed::BeforeZip212(Fr::from_bytes(&to_array(s_in.note.rseed.bytes)).unwrap()) + } else { + Rseed::AfterZip212(to_array(s_in.note.rseed.bytes)) + }; + let note = SaplingNote::from_parts(pay_addr, SaplingNoteValue::from_raw(s_in.note.note), rseed); + let wit_reader = Cursor::new(s_in.iw); + let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); + let merkle_path = iw.path().unwrap(); + if net { + let _mb = main_builder.add_sapling_spend::(&sk, note, merkle_path).unwrap(); + } else { + let _tb = test_builder.add_sapling_spend::(&sk, note, merkle_path).unwrap(); + } + }, + Err(_e) => { + continue; + } + } + } + } + let orch_input: Vec = marshall_from_haskell_var(o_input, o_input_len, RW); + for o_in in orch_input { + if o_in.sk.len() > 1 { + let sp_key = SpendingKey::from_bytes(o_in.sk[0..32].try_into().unwrap()).unwrap(); + let pay_addr = OrchardAddress::from_raw_address_bytes(&to_array(o_in.note.recipient)).unwrap(); + let rho = Nullifier::from_bytes(&to_array(o_in.note.rho)).unwrap(); + let rseed = RandomSeed::from_bytes(to_array(o_in.note.rseed.bytes), &rho).unwrap(); + let val = NoteValue::from_raw(o_in.note.note); + let note = Note::from_parts(pay_addr, val, rho, rseed).unwrap(); + let wit_reader = Cursor::new(o_in.iw); + let iw: IncrementalWitness = read_incremental_witness(wit_reader).unwrap(); + let merkle_path = OrchardMerklePath::from(iw.path().unwrap()); + if net { + let _mb = main_builder.add_orchard_spend::(&sp_key, note, merkle_path).unwrap(); + } else { + let _tb = test_builder.add_orchard_spend::(&sp_key, note, merkle_path).unwrap(); + } + } + } + let outputs: Vec = marshall_from_haskell_var(out_list, out_list_len, RW); + for output in outputs { + match output.kind { + 1 => { + let recipient = TransparentAddress::PublicKeyHash(to_array(output.to)); + let val = NonNegativeAmount::from_u64(output.amt).unwrap(); + if net { + let _mb = main_builder.add_transparent_output(&recipient, val); + } else { + let _tb = test_builder.add_transparent_output(&recipient, val); + } + }, + 2 => { + let recipient = TransparentAddress::ScriptHash(to_array(output.to)); + let val = NonNegativeAmount::from_u64(output.amt).unwrap(); + if net { + let _mb = main_builder.add_transparent_output(&recipient, val); + } else { + let _tb = test_builder.add_transparent_output(&recipient, val); + } + }, + 3 => { + let ovk = Some(ExpandedSpendingKey::from_spending_key(&output.ovk).ovk); + let recipient = PaymentAddress::from_bytes(&to_array(output.to)).unwrap(); + let val = NonNegativeAmount::from_u64(output.amt).unwrap(); + let memo = MemoBytes::from_bytes(&output.memo).unwrap(); + if net { + let _mb = main_builder.add_sapling_output::(ovk, recipient, val, memo); + } else { + let _tb = test_builder.add_sapling_output::(ovk, recipient, val, memo); + } + }, + 4 => { + let sk = SpendingKey::from_bytes(output.ovk[0..32].try_into().unwrap()).unwrap(); + let ovk = if output.chg { + Some(FullViewingKey::from(&sk).to_ovk(Scope::Internal)) + }else { + Some(FullViewingKey::from(&sk).to_ovk(Scope::External)) + }; + let recipient = OrchardAddress::from_raw_address_bytes(&to_array(output.to)).unwrap(); + let val = output.amt; + let memo = MemoBytes::from_bytes(&output.memo).unwrap(); + if net { + let _mb = main_builder.add_orchard_output::(ovk, recipient, val, memo); + } else { + let _tb = test_builder.add_orchard_output::(ovk, recipient, val, memo); + } + }, + _ => { + continue; + } + } + } + let spend_params_in: Vec = marshall_from_haskell_var(sapspend, sapspend_len, RW); + let spend_params_reader = Cursor::new(spend_params_in); + let spend_prover = SpendParameters::read(spend_params_reader, true).unwrap(); + let output_params_in: Vec = marshall_from_haskell_var(sapoutput, sapoutput_len, RW); + let output_params_reader = Cursor::new(output_params_in); + let output_prover = OutputParameters::read(output_params_reader, true).unwrap(); + let result = if net { + main_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) + } else { + test_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) + }; + match result { + Ok(r) => { + let mut out_bytes: Vec = Vec::new(); + let _t = r.transaction().write_v5(&mut out_bytes); + let h = Hhex {bytes: out_bytes}; + marshall_to_haskell_var(&h, out, out_len, RW); + }, + Err(_e) => { + let x = Hhex {bytes: vec![0]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } + } } diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 6e0dff8..8f27fa2 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -275,3 +275,19 @@ import ZcashHaskell.Types } -> `()' #} + +{# fun unsafe rust_wrapper_create_transaction as rustWrapperCreateTx + { toBorshVar* `BS.ByteString'& + , toBorshVar* `BS.ByteString'& + , toBorshVar* `[TransparentTxSpend]'& + , toBorshVar* `[SaplingTxSpend]'& + , toBorshVar* `[OrchardTxSpend]'& + , toBorshVar* `[OutgoingNote]'& + , toBorshVar* `BS.ByteString'& + , toBorshVar* `BS.ByteString'& + , `Bool' + , `Word64' + , getVarBuffer `Buffer HexString'& + } + -> `()' +#} diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index 4bdc46d..afe09e3 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -21,9 +21,9 @@ import Crypto.Secp256k1 import qualified Data.ByteArray as BA import qualified Data.ByteString as BS import Data.ByteString.Base58 (bitcoinAlphabet, decodeBase58, encodeBase58) -import Data.Char (chr) import qualified Data.ByteString.Char8 as BC -import Data.HexString +import Data.Char (chr) +import Data.HexString import qualified Data.Text as T import qualified Data.Text.Encoding as E import Data.Word @@ -31,6 +31,7 @@ import Haskoin.Address (Address(..)) import qualified Haskoin.Crypto.Hash as H import Haskoin.Crypto.Keys.Extended import ZcashHaskell.Types + -- ( AccountId -- , CoinType(..) -- , Scope(..) @@ -44,7 +45,7 @@ import ZcashHaskell.Types -- , getTransparentPrefix -- , getValue -- ) -import ZcashHaskell.Utils( encodeBech32m, decodeBech32 ) +import ZcashHaskell.Utils (decodeBech32, encodeBech32m) -- | Required for `TransparentReceiver` encoding and decoding sha256 :: BS.ByteString -> BS.ByteString @@ -127,60 +128,70 @@ decodeTransparentAddress taddress = do 189 -> Just $ TransparentAddress MainNet $ - TransparentReceiver P2SH (fromRawBytes transparentReceiver) + TransparentReceiver + P2SH + (fromRawBytes transparentReceiver) 186 -> Just $ TransparentAddress TestNet $ - TransparentReceiver P2SH (fromRawBytes transparentReceiver) + TransparentReceiver + P2SH + (fromRawBytes transparentReceiver) 184 -> Just $ TransparentAddress MainNet $ - TransparentReceiver P2PKH (fromRawBytes transparentReceiver) + TransparentReceiver + P2PKH + (fromRawBytes transparentReceiver) _ -> Nothing 29 -> if sb == 37 then Just $ TransparentAddress TestNet $ - TransparentReceiver P2PKH (fromRawBytes transparentReceiver) + TransparentReceiver + P2PKH + (fromRawBytes transparentReceiver) else Nothing _ -> Nothing -- | Encode an Exchange Addresss into HRF from TransparentReceiver -encodeExchangeAddress:: ZcashNet -> TransparentReceiver -> Maybe T.Text -encodeExchangeAddress net tr = do +encodeExchangeAddress :: ZcashNet -> TransparentReceiver -> Maybe T.Text +encodeExchangeAddress net tr = do case (tr_type tr) of - P2PKH -> do + P2PKH -> do case net of MainNet -> do let vhash = encodeBech32m (BC.pack "tex") (toBytes (tr_bytes tr)) Just vhash - TestNet -> do + TestNet -> do let vhash = encodeBech32m (BC.pack "textest") (toBytes (tr_bytes tr)) Just vhash - _ -> Nothing + _ -> Nothing -- | Decode an Exchange Address into a ExchangeAddress -decodeExchangeAddress:: T.Text-> Maybe ExchangeAddress -decodeExchangeAddress ex = do - if (T.length ex ) > 1 - then do - let rawd = decodeBech32 (E.encodeUtf8 ex) - let tMain = BS.unpack (BC.pack "tex") - let tTest = BS.unpack (BC.pack "textest") - let tFail = BS.unpack (BC.pack "fail") - let hr = BS.unpack (hrp rawd) +decodeExchangeAddress :: T.Text -> Maybe ExchangeAddress +decodeExchangeAddress ex = do + if (T.length ex) > 1 + then do + let rawd = decodeBech32 (E.encodeUtf8 ex) + let tMain = BS.unpack (BC.pack "tex") + let tTest = BS.unpack (BC.pack "textest") + let tFail = BS.unpack (BC.pack "fail") + let hr = BS.unpack (hrp rawd) if hr /= tFail then do let transparentReceiver = bytes rawd - if hr == tMain + if hr == tMain then Just $ ExchangeAddress MainNet $ TransparentReceiver P2PKH (fromRawBytes transparentReceiver) - else do - if hr == tTest + else do + if hr == tTest then Just $ ExchangeAddress TestNet $ - TransparentReceiver P2PKH (fromRawBytes transparentReceiver) - else Nothing + TransparentReceiver + P2PKH + (fromRawBytes transparentReceiver) + else Nothing else Nothing - else Nothing \ No newline at end of file + else Nothing diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 91ccae3..91f01b5 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -638,11 +638,60 @@ data DecodedNote = DecodedNote , a_recipient :: !BS.ByteString -- ^ The recipient Orchard receiver. , a_memo :: !BS.ByteString -- ^ The decoded shielded memo field. , a_nullifier :: !HexString -- ^ The calculated nullifier + , a_rho :: !BS.ByteString + , a_rseed :: !Rseed } deriving stock (Eq, Prelude.Show, GHC.Generic) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct DecodedNote +data Rseed = Rseed + { rs_kind :: !Word8 + , rs_bytes :: !BS.ByteString + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct Rseed + +data TransparentTxSpend = TransparentTxSpend + { ts_sk :: !BS.ByteString + , ts_utxo :: !RawOutPoint + , ts_coin :: !RawTxOut + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct TransparentTxSpend + +data SaplingTxSpend = SaplingTxSpend + { ss_sk :: !BS.ByteString + , ss_note :: !DecodedNote + , ss_iw :: !BS.ByteString + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct SaplingTxSpend + +data OrchardTxSpend = OrchardTxSpend + { ss_sk :: !BS.ByteString + , ss_note :: !DecodedNote + , ss_iw :: !BS.ByteString + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardTxSpend + +data OutgoingNote = OutGoingNote + { on_kind :: !Word8 + , on_key :: !BS.ByteString + , on_recipient :: !BS.ByteString + , on_amt :: !Word64 + , on_memo :: !BS.ByteString + , on_chg :: !Bool + } deriving stock (Eq, Prelude.Show, GHC.Generic) + deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) + deriving anyclass (Data.Structured.Show) + deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OutgoingNote + -- * Classes -- | Class to represent types with a bytestring representation class ToBytes a where diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index e739a59..4730957 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.6.0.0 +version: 0.6.1.0 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain -- 2.43.0 From 26a79443a78dce9a23cdd176311974c4757f5080 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 30 Apr 2024 19:14:30 -0500 Subject: [PATCH 06/22] Add Read instance to Rseed --- CHANGELOG.md | 6 ++++++ src/ZcashHaskell/Types.hs | 2 +- zcash-haskell.cabal | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f9e53b..bee207c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.6.1.1] + +### Changed + +- Add `Read` instance for `Rseed` + ## [0.6.1.0] ### Added diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 91f01b5..259c080 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -648,7 +648,7 @@ data DecodedNote = DecodedNote data Rseed = Rseed { rs_kind :: !Word8 , rs_bytes :: !BS.ByteString - } deriving stock (Eq, Prelude.Show, GHC.Generic) + } deriving stock (Eq, Prelude.Show, GHC.Generic, Read) deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct Rseed diff --git a/zcash-haskell.cabal b/zcash-haskell.cabal index 4730957..361d33e 100644 --- a/zcash-haskell.cabal +++ b/zcash-haskell.cabal @@ -5,7 +5,7 @@ cabal-version: 3.0 -- see: https://github.com/sol/hpack name: zcash-haskell -version: 0.6.1.0 +version: 0.6.1.1 synopsis: Utilities to interact with the Zcash blockchain description: Please see the README on the repo at category: Blockchain -- 2.43.0 From 787cf40629575abede6da5998fd69d8a9ba45f02 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 1 May 2024 09:12:58 -0500 Subject: [PATCH 07/22] Add functionality for transaction creation --- CHANGELOG.md | 6 ++++ librustzcash-wrapper/src/lib.rs | 50 ++++++++++++++++++++++++++++++-- src/ZcashHaskell/Types.hs | 23 +++++++++++++++ src/ZcashHaskell/Utils.hs | 51 +++++++++++++++++++++++++++++++-- 4 files changed, 125 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bee207c..7e315c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.6.1.1] +### Added + +- Type for transaction creation errors +- Types for Sapling circuit parameters +- Function to create transaction + ### Changed - Add `Read` instance for `Rseed` diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 6d98974..c4f3316 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1809,9 +1809,53 @@ pub extern "C" fn rust_wrapper_create_transaction( let h = Hhex {bytes: out_bytes}; marshall_to_haskell_var(&h, out, out_len, RW); }, - Err(_e) => { - let x = Hhex {bytes: vec![0]}; - marshall_to_haskell_var(&x, out, out_len, RW); + Err(e) => { + match e { + Error::InsufficientFunds(_y) => { + let x = Hhex {bytes: vec![0]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::ChangeRequired(_y1) => { + let x = Hhex {bytes: vec![1]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::Fee(_y2) => { + let x = Hhex {bytes: vec![2]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::Balance(x) => { + let x = Hhex {bytes: vec![3]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::TransparentBuild(x) => { + let x = Hhex {bytes: vec![4]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::SaplingBuild(x) => { + let x = Hhex {bytes: vec![5]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::OrchardBuild(x) => { + let x = Hhex {bytes: vec![6]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::OrchardSpend(x) => { + let x = Hhex {bytes: vec![7]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::OrchardRecipient(x) => { + let x = Hhex {bytes: vec![8]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::SaplingBuilderNotAvailable => { + let x = Hhex {bytes: vec![9]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::OrchardBuilderNotAvailable => { + let x = Hhex {bytes: vec![10]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } + } } } } diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index 259c080..ed8cbb9 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -692,6 +692,29 @@ data OutgoingNote = OutGoingNote deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OutgoingNote +newtype SaplingSpendParams = SaplingSpendParams + { sapSParams :: BS.ByteString + } deriving newtype (Eq, Prelude.Show, Read) + +newtype SaplingOutputParams = SaplingOutputParams + { sapOParams :: BS.ByteString + } deriving newtype (Eq, Prelude.Show, Read) + +data TxError + = InsufficientFunds + | ChangeRequired + | Fee + | Balance + | TransparentBuild + | SaplingBuild + | OrchardBuild + | OrchardSpend + | OrchardRecipient + | SaplingBuilderNotAvailable + | OrchardBuilderNotAvailable + | ZHError + deriving (Eq, Prelude.Show, Read) + -- * Classes -- | Class to represent types with a bytestring representation class ToBytes a where diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 3187c62..9c87f39 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -19,8 +19,9 @@ module ZcashHaskell.Utils where import C.Zcash ( rustWrapperBech32Decode - , rustWrapperBech32mEncode , rustWrapperBech32Encode + , rustWrapperBech32mEncode + , rustWrapperCreateTx , rustWrapperF4Jumble , rustWrapperF4UnJumble , rustWrapperTxRead @@ -31,7 +32,7 @@ import Data.Aeson import Data.Binary.Get import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as LBS -import Data.HexString (HexString(..)) +import Data.HexString (HexString(..), toBytes) import qualified Data.Text as T import qualified Data.Text.Encoding as E import Foreign.Rust.Marshall.Variable @@ -121,3 +122,49 @@ readZebraTransaction hex = where rawTx = (withPureBorshVarBuffer . rustWrapperTxRead) $ hexBytes hex +createTransaction :: + Maybe SaplingWitness -- ^ to obtain the Sapling anchor + -> Maybe OrchardWitness -- ^ to obtain the Orchard anchor + -> [TransparentTxSpend] -- ^ the list of transparent notes to spend + -> [SaplingTxSpend] -- ^ the list of Sapling notes to spend + -> [OrchardTxSpend] -- ^ the list of Orchard notes to spend + -> [OutgoingNote] -- ^ the list of outgoing notes, including change notes + -> SaplingSpendParams -- ^ the Sapling circuit spending parameters + -> SaplingOutputParams -- ^ the Sapling circuit output parameters + -> ZcashNet -- ^ the network to be used + -> Int -- ^ target block height + -> Either TxError HexString +createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing sParams oParams znet bh = + if BS.length (hexBytes txResult) > 1 + then Right txResult + else case head (BS.unpack $ hexBytes txResult) of + 0 -> Left InsufficientFunds + 1 -> Left ChangeRequired + 2 -> Left Fee + 3 -> Left Balance + 4 -> Left TransparentBuild + 5 -> Left SaplingBuild + 6 -> Left OrchardBuild + 7 -> Left OrchardSpend + 8 -> Left OrchardRecipient + 9 -> Left SaplingBuilderNotAvailable + 10 -> Left OrchardBuilderNotAvailable + _ -> Left ZHError + where + txResult = + withPureBorshVarBuffer $ + rustWrapperCreateTx + (case sapAnchor of + Nothing -> "0" + Just sA -> toBytes $ sapWit sA) + (case orchAnchor of + Nothing -> "0" + Just oA -> toBytes $ orchWit oA) + tSpend + sSpend + oSpend + outgoing + (sapSParams sParams) + (sapOParams oParams) + (znet == MainNet) + (fromIntegral bh) -- 2.43.0 From bc074d0386a1560cd923f6399a362739a2c9ca49 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 1 May 2024 14:24:18 -0500 Subject: [PATCH 08/22] Add transparent key derivation --- CHANGELOG.md | 1 + src/ZcashHaskell/Transparent.hs | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e315c8..cf7b6d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Type for transaction creation errors - Types for Sapling circuit parameters - Function to create transaction +- Function to derive distinct transparent spending keys ### Changed diff --git a/src/ZcashHaskell/Transparent.hs b/src/ZcashHaskell/Transparent.hs index afe09e3..878a7d8 100644 --- a/src/ZcashHaskell/Transparent.hs +++ b/src/ZcashHaskell/Transparent.hs @@ -101,7 +101,22 @@ genTransparentReceiver i scope xprvk = do ScriptAddress j -> return $ TransparentReceiver P2SH $ fromBinary j _anyOtherKind -> throwIO $ userError "Unsupported transparent address type" --- } decode a Transparent Address in HRF and return a TransparentAddress object +-- | Generate a transparent receiver +genTransparentSecretKey :: + Int -- ^ The index of the address to be created + -> Scope -- ^ `External` for wallet addresses or `Internal` for change addresses + -> XPrvKey -- ^ The transparent private key + -> IO TransparentSpendingKey +genTransparentSecretKey i scope xprvk = do + ioCtx <- createContext + let s = + case scope of + External -> 0 + Internal -> 1 + let path = Deriv :/ s :/ fromIntegral i :: DerivPath + return $ derivePath ioCtx path xprvk + +-- | decode a Transparent Address in HRF and return a TransparentAddress object decodeTransparentAddress :: BS.ByteString -> Maybe TransparentAddress decodeTransparentAddress taddress = do if BS.length taddress < 34 -- 2.43.0 From bea4d2f07143f3a01562c595bb0eff3ad6fd8511 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 1 May 2024 16:14:41 -0500 Subject: [PATCH 09/22] Fix typo in constructor --- src/ZcashHaskell/Types.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZcashHaskell/Types.hs b/src/ZcashHaskell/Types.hs index ed8cbb9..af60f0f 100644 --- a/src/ZcashHaskell/Types.hs +++ b/src/ZcashHaskell/Types.hs @@ -680,7 +680,7 @@ data OrchardTxSpend = OrchardTxSpend deriving anyclass (Data.Structured.Show) deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct OrchardTxSpend -data OutgoingNote = OutGoingNote +data OutgoingNote = OutgoingNote { on_kind :: !Word8 , on_key :: !BS.ByteString , on_recipient :: !BS.ByteString -- 2.43.0 From 5fd33e7e43f8ef78c82d82655936ca24528903c1 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 07:22:12 -0500 Subject: [PATCH 10/22] Add debuggin for create transaction --- librustzcash-wrapper/src/lib.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index c4f3316..f9a144d 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1656,6 +1656,7 @@ pub extern "C" fn rust_wrapper_create_transaction( None } }; + println!(sap_anchor); let orch_wit_in: Vec = marshall_from_haskell_var(orch_wit, orch_wit_len, RW); let orch_wit_reader = Cursor::new(orch_wit_in); let orch_iw: Option> = read_incremental_witness(orch_wit_reader).ok(); @@ -1667,16 +1668,19 @@ pub extern "C" fn rust_wrapper_create_transaction( None } }; + println!(orch_anchor); let build_config = BuildConfig::Standard {sapling_anchor: sap_anchor, orchard_anchor: orch_anchor}; let mut main_builder = Builder::new(MainNetwork, BlockHeight::from(bl_height), build_config); let mut test_builder = Builder::new(TestNetwork, BlockHeight::from(bl_height), build_config); let trans_input: Vec = marshall_from_haskell_var(t_input, t_input_len, RW); + println!(trans_input); for t_in in trans_input { if t_in.sk.len() > 1 { let k = SecretKey::from_slice(&t_in.sk).unwrap(); if net { match main_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) { Ok(()) => { + println!("added t-input in main"); continue; }, Err(_e) => { println!("Error reading transparent input"); } @@ -1684,6 +1688,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } else { match test_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) { Ok(()) => { + println!("added t-input in test"); continue; }, Err(_e) => { println!("Error reading transparent input"); } @@ -1692,6 +1697,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let sap_input: Vec = marshall_from_haskell_var(s_input, s_input_len, RW); + println!(sap_input); for s_in in sap_input { if s_in.sk.len() > 1 { let sp_key = ExtendedSpendingKey::from_bytes(&s_in.sk); @@ -1720,6 +1726,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let orch_input: Vec = marshall_from_haskell_var(o_input, o_input_len, RW); + println!(orch_input); for o_in in orch_input { if o_in.sk.len() > 1 { let sp_key = SpendingKey::from_bytes(o_in.sk[0..32].try_into().unwrap()).unwrap(); @@ -1739,6 +1746,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let outputs: Vec = marshall_from_haskell_var(out_list, out_list_len, RW); + println!(outputs); for output in outputs { match output.kind { 1 => { @@ -1782,8 +1790,10 @@ pub extern "C" fn rust_wrapper_create_transaction( let memo = MemoBytes::from_bytes(&output.memo).unwrap(); if net { let _mb = main_builder.add_orchard_output::(ovk, recipient, val, memo); + println!("added o-input to main"); } else { let _tb = test_builder.add_orchard_output::(ovk, recipient, val, memo); + println!("added o-input to test"); } }, _ => { @@ -1797,9 +1807,12 @@ pub extern "C" fn rust_wrapper_create_transaction( let output_params_in: Vec = marshall_from_haskell_var(sapoutput, sapoutput_len, RW); let output_params_reader = Cursor::new(output_params_in); let output_prover = OutputParameters::read(output_params_reader, true).unwrap(); + println!("loaded params"); let result = if net { + println!("started main build"); main_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) } else { + println!("started test build"); test_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) }; match result { -- 2.43.0 From c144a6d62423edae7cc807c4d54cc0555cdbc2eb Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 07:31:01 -0500 Subject: [PATCH 11/22] Add more debugging to Rust --- librustzcash-wrapper/src/lib.rs | 60 ++++++++++++++++----------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index f9a144d..821019f 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -170,7 +170,7 @@ use bech32::{ pub enum RW {} pub const RW: PhantomData = PhantomData; -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct RawData { hrp: Vec, bytes: Vec @@ -190,7 +190,7 @@ impl ToHaskell for RawData { //} //} -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HrawTx { bytes: Vec, s: bool, @@ -204,7 +204,7 @@ impl ToHaskell for HrawTx { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HshieldedOutput { pub cv: Hhex, cmu: Hhex, @@ -248,7 +248,7 @@ impl HshieldedOutput { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hhex { bytes: Vec } @@ -261,7 +261,7 @@ impl ToHaskell for Hhex { } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Haction { nf: Hhex, rk: Hhex, @@ -290,7 +290,7 @@ impl Haction { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hnote { note: u64, recipient: Vec, @@ -307,7 +307,7 @@ impl ToHaskell for Hnote { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hrseed { kind: u8, bytes: Vec @@ -328,7 +328,7 @@ impl ToHaskell for Hrseed { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hua { net: u8, o_rec: Vec, @@ -361,7 +361,7 @@ impl Hua { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Htx { txid: Vec, locktime: u32, @@ -379,7 +379,7 @@ impl ToHaskell for Htx { } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HTBundle { empty: bool, vin: Vec, @@ -400,7 +400,7 @@ impl HTBundle { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HTxIn { outpoint: Houtpoint, script: Vec, @@ -420,7 +420,7 @@ impl HTxIn { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HTxOut { amt: i64, script: Vec @@ -449,7 +449,7 @@ impl HTxOut { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Houtpoint { hash: Vec, index: u32 @@ -479,7 +479,7 @@ impl Houtpoint { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HtransparentInput { sk: Vec, utxo: Houtpoint, @@ -500,7 +500,7 @@ impl ToHaskell for HtransparentInput { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HSBundle { empty: bool, spends: Vec, @@ -523,7 +523,7 @@ impl HSBundle { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hspend { cv: Hhex, anchor: Hhex, @@ -552,7 +552,7 @@ impl Hspend { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HsaplingInput { sk: Vec, note: Hnote, @@ -567,7 +567,7 @@ impl FromHaskell for HsaplingInput { } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HorchardInput { sk: Vec, note: Hnote, @@ -581,7 +581,7 @@ impl FromHaskell for HorchardInput { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Houtput { kind: u8, ovk: Vec, @@ -598,7 +598,7 @@ impl FromHaskell for Houtput { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct HOBundle { empty: bool, actions: Vec, @@ -623,7 +623,7 @@ impl HOBundle { } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hflags { spends: bool, outputs: bool @@ -642,7 +642,7 @@ impl Hflags { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hufvk { net: u8, orchard: Vec, @@ -671,7 +671,7 @@ impl Hufvk { } } -#[derive(BorshSerialize, BorshDeserialize)] +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Hsvk { vk: Vec, ovk: Vec @@ -795,7 +795,7 @@ pub extern "C" fn rust_wrapper_svk_decode( true } Err(e) => { - print!("{}", e); + print!("{:?}", e); false } } @@ -1656,7 +1656,7 @@ pub extern "C" fn rust_wrapper_create_transaction( None } }; - println!(sap_anchor); + println!("{:?}", sap_anchor); let orch_wit_in: Vec = marshall_from_haskell_var(orch_wit, orch_wit_len, RW); let orch_wit_reader = Cursor::new(orch_wit_in); let orch_iw: Option> = read_incremental_witness(orch_wit_reader).ok(); @@ -1668,12 +1668,12 @@ pub extern "C" fn rust_wrapper_create_transaction( None } }; - println!(orch_anchor); + println!("{:?}", orch_anchor); let build_config = BuildConfig::Standard {sapling_anchor: sap_anchor, orchard_anchor: orch_anchor}; let mut main_builder = Builder::new(MainNetwork, BlockHeight::from(bl_height), build_config); let mut test_builder = Builder::new(TestNetwork, BlockHeight::from(bl_height), build_config); let trans_input: Vec = marshall_from_haskell_var(t_input, t_input_len, RW); - println!(trans_input); + println!("{:?}", trans_input); for t_in in trans_input { if t_in.sk.len() > 1 { let k = SecretKey::from_slice(&t_in.sk).unwrap(); @@ -1697,7 +1697,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let sap_input: Vec = marshall_from_haskell_var(s_input, s_input_len, RW); - println!(sap_input); + println!("{:?}", sap_input); for s_in in sap_input { if s_in.sk.len() > 1 { let sp_key = ExtendedSpendingKey::from_bytes(&s_in.sk); @@ -1726,7 +1726,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let orch_input: Vec = marshall_from_haskell_var(o_input, o_input_len, RW); - println!(orch_input); + println!("{:?}", orch_input); for o_in in orch_input { if o_in.sk.len() > 1 { let sp_key = SpendingKey::from_bytes(o_in.sk[0..32].try_into().unwrap()).unwrap(); @@ -1746,7 +1746,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let outputs: Vec = marshall_from_haskell_var(out_list, out_list_len, RW); - println!(outputs); + println!("{:?}", outputs); for output in outputs { match output.kind { 1 => { -- 2.43.0 From 652e95c3618c1336edc1427f238e33a720fb0510 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 12:10:53 -0500 Subject: [PATCH 12/22] Improve error messaging --- librustzcash-wrapper/src/lib.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 821019f..2291f48 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1824,11 +1824,13 @@ pub extern "C" fn rust_wrapper_create_transaction( }, Err(e) => { match e { - Error::InsufficientFunds(_y) => { + Error::InsufficientFunds(y) => { + println!("{:?}", y); let x = Hhex {bytes: vec![0]}; marshall_to_haskell_var(&x, out, out_len, RW); }, - Error::ChangeRequired(_y1) => { + Error::ChangeRequired(y1) => { + println!("{:?}", y1); let x = Hhex {bytes: vec![1]}; marshall_to_haskell_var(&x, out, out_len, RW); }, @@ -1836,27 +1838,27 @@ pub extern "C" fn rust_wrapper_create_transaction( let x = Hhex {bytes: vec![2]}; marshall_to_haskell_var(&x, out, out_len, RW); }, - Error::Balance(x) => { + Error::Balance(y3) => { let x = Hhex {bytes: vec![3]}; marshall_to_haskell_var(&x, out, out_len, RW); }, - Error::TransparentBuild(x) => { + Error::TransparentBuild(y4) => { let x = Hhex {bytes: vec![4]}; marshall_to_haskell_var(&x, out, out_len, RW); }, - Error::SaplingBuild(x) => { + Error::SaplingBuild(y5) => { let x = Hhex {bytes: vec![5]}; marshall_to_haskell_var(&x, out, out_len, RW); }, - Error::OrchardBuild(x) => { + Error::OrchardBuild(y7) => { let x = Hhex {bytes: vec![6]}; marshall_to_haskell_var(&x, out, out_len, RW); }, - Error::OrchardSpend(x) => { + Error::OrchardSpend(y8) => { let x = Hhex {bytes: vec![7]}; marshall_to_haskell_var(&x, out, out_len, RW); }, - Error::OrchardRecipient(x) => { + Error::OrchardRecipient(y9) => { let x = Hhex {bytes: vec![8]}; marshall_to_haskell_var(&x, out, out_len, RW); }, -- 2.43.0 From 2d14ef6b22664bc85d5193cdc3d5837e37bd6b2b Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 12:35:36 -0500 Subject: [PATCH 13/22] Update to use commitment trees for anchors --- librustzcash-wrapper/src/lib.rs | 12 ++++++------ src/ZcashHaskell/Utils.hs | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 2291f48..6e2a5f5 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1647,24 +1647,24 @@ pub extern "C" fn rust_wrapper_create_transaction( out_len: &mut usize){ let sap_wit_in: Vec = marshall_from_haskell_var(sap_wit, sap_wit_len, RW); let sap_wit_reader = Cursor::new(sap_wit_in); - let sap_iw: Option> = read_incremental_witness(sap_wit_reader).ok(); + let sap_iw = read_commitment_tree::>, SAPLING_DEPTH>(sap_wit_reader); let sap_anchor = match sap_iw { - Some(s_iw) => { + Ok(s_iw) => { Some(SaplingAnchor::from(s_iw.root())) }, - None => { + Err(_e) => { None } }; println!("{:?}", sap_anchor); let orch_wit_in: Vec = marshall_from_haskell_var(orch_wit, orch_wit_len, RW); let orch_wit_reader = Cursor::new(orch_wit_in); - let orch_iw: Option> = read_incremental_witness(orch_wit_reader).ok(); + let orch_iw = read_commitment_tree::>, 32>(orch_wit_reader); let orch_anchor = match orch_iw { - Some(o_iw) => { + Ok(o_iw) => { Some(OrchardAnchor::from(o_iw.root())) }, - None => { + Err(_e) => { None } }; diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index 9c87f39..db001c8 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -123,8 +123,8 @@ readZebraTransaction hex = rawTx = (withPureBorshVarBuffer . rustWrapperTxRead) $ hexBytes hex createTransaction :: - Maybe SaplingWitness -- ^ to obtain the Sapling anchor - -> Maybe OrchardWitness -- ^ to obtain the Orchard anchor + Maybe SaplingCommitmentTree -- ^ to obtain the Sapling anchor + -> Maybe OrchardCommitmentTree -- ^ to obtain the Orchard anchor -> [TransparentTxSpend] -- ^ the list of transparent notes to spend -> [SaplingTxSpend] -- ^ the list of Sapling notes to spend -> [OrchardTxSpend] -- ^ the list of Orchard notes to spend @@ -156,10 +156,10 @@ createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing sParams oPa rustWrapperCreateTx (case sapAnchor of Nothing -> "0" - Just sA -> toBytes $ sapWit sA) + Just sA -> toBytes $ sapTree sA) (case orchAnchor of Nothing -> "0" - Just oA -> toBytes $ orchWit oA) + Just oA -> toBytes $ orchTree oA) tSpend sSpend oSpend -- 2.43.0 From c7f2bca2c47fbc4e0c6ff8eedac66a574e861de9 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 12:48:30 -0500 Subject: [PATCH 14/22] Remove ovk from transaction creation --- librustzcash-wrapper/src/lib.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 6e2a5f5..04c1139 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1779,12 +1779,13 @@ pub extern "C" fn rust_wrapper_create_transaction( } }, 4 => { - let sk = SpendingKey::from_bytes(output.ovk[0..32].try_into().unwrap()).unwrap(); - let ovk = if output.chg { - Some(FullViewingKey::from(&sk).to_ovk(Scope::Internal)) - }else { - Some(FullViewingKey::from(&sk).to_ovk(Scope::External)) - }; + //let sk = SpendingKey::from_bytes(output.ovk[0..32].try_into().unwrap()).unwrap(); + //let ovk = if output.chg { + //Some(FullViewingKey::from(&sk).to_ovk(Scope::Internal)) + //} else { + //Some(FullViewingKey::from(&sk).to_ovk(Scope::External)) + //}; + let ovk = None; let recipient = OrchardAddress::from_raw_address_bytes(&to_array(output.to)).unwrap(); let val = output.amt; let memo = MemoBytes::from_bytes(&output.memo).unwrap(); -- 2.43.0 From d5e17afc99222a83eadb41db4e8de493f658a97a Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 13:34:26 -0500 Subject: [PATCH 15/22] Debugging parameters --- librustzcash-wrapper/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 04c1139..fbaa5c6 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1804,10 +1804,10 @@ pub extern "C" fn rust_wrapper_create_transaction( } let spend_params_in: Vec = marshall_from_haskell_var(sapspend, sapspend_len, RW); let spend_params_reader = Cursor::new(spend_params_in); - let spend_prover = SpendParameters::read(spend_params_reader, true).unwrap(); + let spend_prover = SpendParameters::read(spend_params_reader, false).unwrap(); let output_params_in: Vec = marshall_from_haskell_var(sapoutput, sapoutput_len, RW); let output_params_reader = Cursor::new(output_params_in); - let output_prover = OutputParameters::read(output_params_reader, true).unwrap(); + let output_prover = OutputParameters::read(output_params_reader, false).unwrap(); println!("loaded params"); let result = if net { println!("started main build"); -- 2.43.0 From 31c5cda65aecb251fd37b996fe603309dd467031 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 13:38:12 -0500 Subject: [PATCH 16/22] Improve error message --- librustzcash-wrapper/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index fbaa5c6..0fe909d 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1831,7 +1831,7 @@ pub extern "C" fn rust_wrapper_create_transaction( marshall_to_haskell_var(&x, out, out_len, RW); }, Error::ChangeRequired(y1) => { - println!("{:?}", y1); + println!("Change required {:#?}", y1); let x = Hhex {bytes: vec![1]}; marshall_to_haskell_var(&x, out, out_len, RW); }, -- 2.43.0 From 6a632d45c1a6dea59d6c4b1e09057682cdc25d11 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 14:07:54 -0500 Subject: [PATCH 17/22] Change error messaging --- librustzcash-wrapper/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 0fe909d..64e2317 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1831,7 +1831,7 @@ pub extern "C" fn rust_wrapper_create_transaction( marshall_to_haskell_var(&x, out, out_len, RW); }, Error::ChangeRequired(y1) => { - println!("Change required {:#?}", y1); + println!("Change required {:?}", y1); let x = Hhex {bytes: vec![1]}; marshall_to_haskell_var(&x, out, out_len, RW); }, -- 2.43.0 From c583451c835e097f53485735c38077b9e155f69b Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 14:30:59 -0500 Subject: [PATCH 18/22] Update debugging --- librustzcash-wrapper/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 64e2317..4af2b56 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1826,12 +1826,10 @@ pub extern "C" fn rust_wrapper_create_transaction( Err(e) => { match e { Error::InsufficientFunds(y) => { - println!("{:?}", y); let x = Hhex {bytes: vec![0]}; marshall_to_haskell_var(&x, out, out_len, RW); }, Error::ChangeRequired(y1) => { - println!("Change required {:?}", y1); let x = Hhex {bytes: vec![1]}; marshall_to_haskell_var(&x, out, out_len, RW); }, -- 2.43.0 From 1e31bb73c228554b2f28e4758526c6f568858093 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Thu, 2 May 2024 14:55:24 -0500 Subject: [PATCH 19/22] Update Rust debugging --- librustzcash-wrapper/src/lib.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 4af2b56..a027dc5 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1668,12 +1668,10 @@ pub extern "C" fn rust_wrapper_create_transaction( None } }; - println!("{:?}", orch_anchor); let build_config = BuildConfig::Standard {sapling_anchor: sap_anchor, orchard_anchor: orch_anchor}; let mut main_builder = Builder::new(MainNetwork, BlockHeight::from(bl_height), build_config); let mut test_builder = Builder::new(TestNetwork, BlockHeight::from(bl_height), build_config); let trans_input: Vec = marshall_from_haskell_var(t_input, t_input_len, RW); - println!("{:?}", trans_input); for t_in in trans_input { if t_in.sk.len() > 1 { let k = SecretKey::from_slice(&t_in.sk).unwrap(); @@ -1697,7 +1695,6 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let sap_input: Vec = marshall_from_haskell_var(s_input, s_input_len, RW); - println!("{:?}", sap_input); for s_in in sap_input { if s_in.sk.len() > 1 { let sp_key = ExtendedSpendingKey::from_bytes(&s_in.sk); @@ -1726,7 +1723,6 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let orch_input: Vec = marshall_from_haskell_var(o_input, o_input_len, RW); - println!("{:?}", orch_input); for o_in in orch_input { if o_in.sk.len() > 1 { let sp_key = SpendingKey::from_bytes(o_in.sk[0..32].try_into().unwrap()).unwrap(); @@ -1746,7 +1742,6 @@ pub extern "C" fn rust_wrapper_create_transaction( } } let outputs: Vec = marshall_from_haskell_var(out_list, out_list_len, RW); - println!("{:?}", outputs); for output in outputs { match output.kind { 1 => { @@ -1791,10 +1786,8 @@ pub extern "C" fn rust_wrapper_create_transaction( let memo = MemoBytes::from_bytes(&output.memo).unwrap(); if net { let _mb = main_builder.add_orchard_output::(ovk, recipient, val, memo); - println!("added o-input to main"); } else { let _tb = test_builder.add_orchard_output::(ovk, recipient, val, memo); - println!("added o-input to test"); } }, _ => { @@ -1808,12 +1801,9 @@ pub extern "C" fn rust_wrapper_create_transaction( let output_params_in: Vec = marshall_from_haskell_var(sapoutput, sapoutput_len, RW); let output_params_reader = Cursor::new(output_params_in); let output_prover = OutputParameters::read(output_params_reader, false).unwrap(); - println!("loaded params"); let result = if net { - println!("started main build"); main_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) } else { - println!("started test build"); test_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) }; match result { -- 2.43.0 From 2108f46afa1b3af2d400846f17f88c46bbcf2514 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 7 May 2024 12:13:54 -0500 Subject: [PATCH 20/22] Remove debugging --- librustzcash-wrapper/src/lib.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index a027dc5..9956dde 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1656,7 +1656,7 @@ pub extern "C" fn rust_wrapper_create_transaction( None } }; - println!("{:?}", sap_anchor); + //println!("{:?}", sap_anchor); let orch_wit_in: Vec = marshall_from_haskell_var(orch_wit, orch_wit_len, RW); let orch_wit_reader = Cursor::new(orch_wit_in); let orch_iw = read_commitment_tree::>, 32>(orch_wit_reader); @@ -1678,7 +1678,7 @@ pub extern "C" fn rust_wrapper_create_transaction( if net { match main_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) { Ok(()) => { - println!("added t-input in main"); + //println!("added t-input in main"); continue; }, Err(_e) => { println!("Error reading transparent input"); } @@ -1686,7 +1686,7 @@ pub extern "C" fn rust_wrapper_create_transaction( } else { match test_builder.add_transparent_input(k, t_in.utxo.unpack(), t_in.coin.unpack()) { Ok(()) => { - println!("added t-input in test"); + //println!("added t-input in test"); continue; }, Err(_e) => { println!("Error reading transparent input"); } @@ -1774,13 +1774,12 @@ pub extern "C" fn rust_wrapper_create_transaction( } }, 4 => { - //let sk = SpendingKey::from_bytes(output.ovk[0..32].try_into().unwrap()).unwrap(); - //let ovk = if output.chg { - //Some(FullViewingKey::from(&sk).to_ovk(Scope::Internal)) - //} else { - //Some(FullViewingKey::from(&sk).to_ovk(Scope::External)) - //}; - let ovk = None; + let sk = SpendingKey::from_bytes(output.ovk[0..32].try_into().unwrap()).unwrap(); + let ovk = if output.chg { + Some(FullViewingKey::from(&sk).to_ovk(Scope::Internal)) + } else { + Some(FullViewingKey::from(&sk).to_ovk(Scope::External)) + }; let recipient = OrchardAddress::from_raw_address_bytes(&to_array(output.to)).unwrap(); let val = output.amt; let memo = MemoBytes::from_bytes(&output.memo).unwrap(); -- 2.43.0 From 37b485288d0cf8999e35c24382dd9c69a4a4a00a Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Tue, 7 May 2024 15:19:54 -0500 Subject: [PATCH 21/22] Fix bug in Orchard witness calculation --- librustzcash-wrapper/src/lib.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index 9956dde..bc234f5 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1562,17 +1562,17 @@ pub extern "C" fn rust_wrapper_update_orchard_witness( let n = MerkleHashOrchard::from_cmx(&orchard_note_comm.unwrap()); iw.append(n); } - let mut out_bytes: Vec = Vec::new(); - let result = write_incremental_witness(&iw, &mut out_bytes); - match result { - Ok(()) => { - let h = Hhex { bytes: out_bytes}; - marshall_to_haskell_var(&h, out, out_len, RW); - }, - Err(_e) => { - let h0 = Hhex { bytes: vec![0]}; - marshall_to_haskell_var(&h0, out, out_len, RW); - } + } + let mut out_bytes: Vec = Vec::new(); + let result = write_incremental_witness(&iw, &mut out_bytes); + match result { + Ok(()) => { + let h = Hhex { bytes: out_bytes}; + marshall_to_haskell_var(&h, out, out_len, RW); + }, + Err(_e) => { + let h0 = Hhex { bytes: vec![0]}; + marshall_to_haskell_var(&h0, out, out_len, RW); } } } -- 2.43.0 From 148abd95d0fcce4b4a5a0569a9bedd55a17b8140 Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 8 May 2024 13:49:39 -0500 Subject: [PATCH 22/22] Implement fee calculator --- librustzcash-wrapper/src/lib.rs | 145 ++++++++++++++++++-------------- src/C/Zcash.chs | 1 + src/ZcashHaskell/Utils.hs | 4 +- 3 files changed, 86 insertions(+), 64 deletions(-) diff --git a/librustzcash-wrapper/src/lib.rs b/librustzcash-wrapper/src/lib.rs index bc234f5..7397401 100644 --- a/librustzcash-wrapper/src/lib.rs +++ b/librustzcash-wrapper/src/lib.rs @@ -1643,6 +1643,7 @@ pub extern "C" fn rust_wrapper_create_transaction( sapoutput_len: usize, net: bool, bl_height: u32, + build: bool, out: *mut u8, out_len: &mut usize){ let sap_wit_in: Vec = marshall_from_haskell_var(sap_wit, sap_wit_len, RW); @@ -1794,71 +1795,89 @@ pub extern "C" fn rust_wrapper_create_transaction( } } } - let spend_params_in: Vec = marshall_from_haskell_var(sapspend, sapspend_len, RW); - let spend_params_reader = Cursor::new(spend_params_in); - let spend_prover = SpendParameters::read(spend_params_reader, false).unwrap(); - let output_params_in: Vec = marshall_from_haskell_var(sapoutput, sapoutput_len, RW); - let output_params_reader = Cursor::new(output_params_in); - let output_prover = OutputParameters::read(output_params_reader, false).unwrap(); - let result = if net { - main_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) - } else { - test_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) - }; - match result { - Ok(r) => { - let mut out_bytes: Vec = Vec::new(); - let _t = r.transaction().write_v5(&mut out_bytes); - let h = Hhex {bytes: out_bytes}; - marshall_to_haskell_var(&h, out, out_len, RW); - }, - Err(e) => { - match e { - Error::InsufficientFunds(y) => { - let x = Hhex {bytes: vec![0]}; - marshall_to_haskell_var(&x, out, out_len, RW); - }, - Error::ChangeRequired(y1) => { - let x = Hhex {bytes: vec![1]}; - marshall_to_haskell_var(&x, out, out_len, RW); - }, - Error::Fee(_y2) => { - let x = Hhex {bytes: vec![2]}; - marshall_to_haskell_var(&x, out, out_len, RW); - }, - Error::Balance(y3) => { - let x = Hhex {bytes: vec![3]}; - marshall_to_haskell_var(&x, out, out_len, RW); - }, - Error::TransparentBuild(y4) => { - let x = Hhex {bytes: vec![4]}; - marshall_to_haskell_var(&x, out, out_len, RW); - }, - Error::SaplingBuild(y5) => { - let x = Hhex {bytes: vec![5]}; - marshall_to_haskell_var(&x, out, out_len, RW); - }, - Error::OrchardBuild(y7) => { - let x = Hhex {bytes: vec![6]}; - marshall_to_haskell_var(&x, out, out_len, RW); - }, - Error::OrchardSpend(y8) => { - let x = Hhex {bytes: vec![7]}; - marshall_to_haskell_var(&x, out, out_len, RW); - }, - Error::OrchardRecipient(y9) => { - let x = Hhex {bytes: vec![8]}; - marshall_to_haskell_var(&x, out, out_len, RW); - }, - Error::SaplingBuilderNotAvailable => { - let x = Hhex {bytes: vec![9]}; - marshall_to_haskell_var(&x, out, out_len, RW); - }, - Error::OrchardBuilderNotAvailable => { - let x = Hhex {bytes: vec![10]}; - marshall_to_haskell_var(&x, out, out_len, RW); + if build { + let spend_params_in: Vec = marshall_from_haskell_var(sapspend, sapspend_len, RW); + let spend_params_reader = Cursor::new(spend_params_in); + let spend_prover = SpendParameters::read(spend_params_reader, false).unwrap(); + let output_params_in: Vec = marshall_from_haskell_var(sapoutput, sapoutput_len, RW); + let output_params_reader = Cursor::new(output_params_in); + let output_prover = OutputParameters::read(output_params_reader, false).unwrap(); + let result = if net { + main_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) + } else { + test_builder.build(OsRng, &spend_prover, &output_prover, &FeeRule::standard()) + }; + match result { + Ok(r) => { + let mut out_bytes: Vec = Vec::new(); + let _t = r.transaction().write_v5(&mut out_bytes); + let h = Hhex {bytes: out_bytes}; + marshall_to_haskell_var(&h, out, out_len, RW); + }, + Err(e) => { + match e { + Error::InsufficientFunds(y) => { + let x = Hhex {bytes: vec![0]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::ChangeRequired(y1) => { + let x = Hhex {bytes: vec![1]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::Fee(_y2) => { + let x = Hhex {bytes: vec![2]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::Balance(y3) => { + let x = Hhex {bytes: vec![3]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::TransparentBuild(y4) => { + let x = Hhex {bytes: vec![4]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::SaplingBuild(y5) => { + let x = Hhex {bytes: vec![5]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::OrchardBuild(y7) => { + let x = Hhex {bytes: vec![6]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::OrchardSpend(y8) => { + let x = Hhex {bytes: vec![7]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::OrchardRecipient(y9) => { + let x = Hhex {bytes: vec![8]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::SaplingBuilderNotAvailable => { + let x = Hhex {bytes: vec![9]}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Error::OrchardBuilderNotAvailable => { + let x = Hhex {bytes: vec![10]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } } } } + } else { + let result = if net { + main_builder.get_fee(&FeeRule::standard()) + } else { + test_builder.get_fee(&FeeRule::standard()) + }; + match result { + Ok(r) => { + let x = Hhex {bytes: r.to_i64_le_bytes().to_vec()}; + marshall_to_haskell_var(&x, out, out_len, RW); + }, + Err(e) => { + let x = Hhex {bytes: vec![2]}; + marshall_to_haskell_var(&x, out, out_len, RW); + } + } } } diff --git a/src/C/Zcash.chs b/src/C/Zcash.chs index 8f27fa2..0f35be4 100644 --- a/src/C/Zcash.chs +++ b/src/C/Zcash.chs @@ -287,6 +287,7 @@ import ZcashHaskell.Types , toBorshVar* `BS.ByteString'& , `Bool' , `Word64' + , `Bool' , getVarBuffer `Buffer HexString'& } -> `()' diff --git a/src/ZcashHaskell/Utils.hs b/src/ZcashHaskell/Utils.hs index db001c8..59b38ed 100644 --- a/src/ZcashHaskell/Utils.hs +++ b/src/ZcashHaskell/Utils.hs @@ -133,8 +133,9 @@ createTransaction :: -> SaplingOutputParams -- ^ the Sapling circuit output parameters -> ZcashNet -- ^ the network to be used -> Int -- ^ target block height + -> Bool -- ^ True to build, False to estimate fee -> Either TxError HexString -createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing sParams oParams znet bh = +createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing sParams oParams znet bh build = if BS.length (hexBytes txResult) > 1 then Right txResult else case head (BS.unpack $ hexBytes txResult) of @@ -168,3 +169,4 @@ createTransaction sapAnchor orchAnchor tSpend sSpend oSpend outgoing sParams oPa (sapOParams oParams) (znet == MainNet) (fromIntegral bh) + build -- 2.43.0