diff --git a/.changelog/unreleased/features/1110-wallet-deterministic.md b/.changelog/unreleased/features/1110-wallet-deterministic.md new file mode 100644 index 0000000000..d9551b5ea6 --- /dev/null +++ b/.changelog/unreleased/features/1110-wallet-deterministic.md @@ -0,0 +1,2 @@ +- Implements HD wallet derivation / recovery from a given mnemonic code + ([\#1110](https://github.com/anoma/namada/pull/1110)) \ No newline at end of file diff --git a/.changelog/unreleased/improvements/1093-signable-txs.md b/.changelog/unreleased/improvements/1093-signable-txs.md new file mode 100644 index 0000000000..e1e244bd0f --- /dev/null +++ b/.changelog/unreleased/improvements/1093-signable-txs.md @@ -0,0 +1,2 @@ +- Make Namada transactions signable on hardware constrained wallets by making + them smaller. ([#1093](https://github.com/anoma/namada/pull/1093)) \ No newline at end of file diff --git a/.changelog/unreleased/improvements/1238-optinal-multicore-feature.md b/.changelog/unreleased/improvements/1238-optinal-multicore-feature.md new file mode 100644 index 0000000000..ee1a0c1ba1 --- /dev/null +++ b/.changelog/unreleased/improvements/1238-optinal-multicore-feature.md @@ -0,0 +1,4 @@ +- Added `multicore` feature flag to the namada and namada_core + crate that can be switched off for JS WASM build. + Additionally, changed the `trait ShieldedUtils` to be async. + ([\#1238](https://github.com/anoma/namada/pull/1238)) \ No newline at end of file diff --git a/.changelog/unreleased/improvements/1425-wallet-zeroize.md b/.changelog/unreleased/improvements/1425-wallet-zeroize.md new file mode 100644 index 0000000000..ab32b10f65 --- /dev/null +++ b/.changelog/unreleased/improvements/1425-wallet-zeroize.md @@ -0,0 +1,2 @@ +- Zeroizes memory containing passphrases in wallet. + ([\#1425](https://github.com/anoma/namada/issues/1425)) \ No newline at end of file diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index eec0bb533f..b07e2212e9 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -4,13 +4,11 @@ on: push: branches: - main - - maint-* - "!eth-bridge-integration" # Run in PRs with conflicts (https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request) pull_request_target: branches: - main - - maint-* - "!eth-bridge-integration" types: [opened, synchronize, reopened] workflow_dispatch: diff --git a/Cargo.lock b/Cargo.lock index 43e8327787..f88a6ad938 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,10 +19,11 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aead" -version = "0.4.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ + "crypto-common", "generic-array 0.14.7", ] @@ -33,7 +34,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ "cfg-if 1.0.0", - "cipher", + "cipher 0.3.0", "cpufeatures", "opaque-debug 0.3.0", ] @@ -49,17 +50,6 @@ dependencies = [ "version_check 0.9.4", ] -[[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if 1.0.0", - "once_cell", - "version_check 0.9.4", -] - [[package]] name = "aho-corasick" version = "1.0.1" @@ -393,7 +383,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -410,7 +400,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -527,6 +517,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base58" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" + +[[package]] +name = "base58" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" + [[package]] name = "base64" version = "0.9.3" @@ -586,25 +588,36 @@ dependencies = [ "blake2s_simd 0.5.11", "byteorder", "crossbeam-channel 0.5.8", - "ff", - "group", + "ff 0.11.1", + "group 0.11.0", "lazy_static", "log 0.4.17", "num_cpus", - "pairing", + "pairing 0.21.0", "rand_core 0.6.4", "rayon", - "subtle", + "subtle 2.4.1", ] [[package]] -name = "bigint" -version = "4.4.3" +name = "bellman" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0e8c8a600052b52482eff2cf4d810e462fdff1f656ac1ecb6232132a1ed7def" +checksum = "a4dd656ef4fdf7debb6d87d4dd92642fcbcdb78cbf6600c13e25c87e4d1a3807" dependencies = [ + "bitvec 1.0.1", + "blake2s_simd 1.0.1", "byteorder", - "crunchy 0.1.6", + "crossbeam-channel 0.5.8", + "ff 0.12.1", + "group 0.12.1", + "lazy_static", + "log 0.4.17", + "num_cpus", + "pairing 0.22.0", + "rand_core 0.6.4", + "rayon", + "subtle 2.4.1", ] [[package]] @@ -637,13 +650,13 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "prettyplease 0.2.4", + "prettyplease 0.2.5", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -683,7 +696,7 @@ checksum = "0694ea59225b0c5f3cb405ff3f670e4828358ed26aec49dc352f730f0cb1a8a3" dependencies = [ "bech32 0.9.1", "bitcoin_hashes", - "secp256k1 0.24.3", + "secp256k1", "serde 1.0.163", ] @@ -745,17 +758,6 @@ dependencies = [ "cty", ] -[[package]] -name = "blake2b_simd" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "constant_time_eq 0.1.5", -] - [[package]] name = "blake2b_simd" version = "1.0.1" @@ -840,7 +842,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" dependencies = [ "block-padding 0.2.1", - "cipher", + "cipher 0.3.0", ] [[package]] @@ -879,52 +881,42 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a829c821999c06be34de314eaeb7dd1b42be38661178bc26ad47a4eacebdb0f9" dependencies = [ - "ff", - "group", - "pairing", + "ff 0.11.1", + "group 0.11.0", + "pairing 0.21.0", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] -name = "borsh" -version = "0.9.4" -source = "git+https://github.com/heliaxdev/borsh-rs.git?rev=cd5223e5103c4f139e0c54cf8259b7ec5ec4073a#cd5223e5103c4f139e0c54cf8259b7ec5ec4073a" -dependencies = [ - "borsh-derive 0.9.4", - "hashbrown 0.11.2", -] - -[[package]] -name = "borsh" -version = "0.10.3" +name = "bls12_381" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +checksum = "a3c196a77437e7cc2fb515ce413a6401291578b5afc8ecb29a3c7ab957f05941" dependencies = [ - "borsh-derive 0.10.3", - "hashbrown 0.13.2", + "ff 0.12.1", + "group 0.12.1", + "pairing 0.22.0", + "rand_core 0.6.4", + "subtle 2.4.1", ] [[package]] -name = "borsh-derive" +name = "borsh" version = "0.9.4" source = "git+https://github.com/heliaxdev/borsh-rs.git?rev=cd5223e5103c4f139e0c54cf8259b7ec5ec4073a#cd5223e5103c4f139e0c54cf8259b7ec5ec4073a" dependencies = [ - "borsh-derive-internal 0.9.4", - "borsh-schema-derive-internal 0.9.4", - "proc-macro-crate 0.1.5", - "proc-macro2", - "syn 1.0.109", + "borsh-derive", + "hashbrown 0.11.2", ] [[package]] name = "borsh-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" +version = "0.9.4" +source = "git+https://github.com/heliaxdev/borsh-rs.git?rev=cd5223e5103c4f139e0c54cf8259b7ec5ec4073a#cd5223e5103c4f139e0c54cf8259b7ec5ec4073a" dependencies = [ - "borsh-derive-internal 0.10.3", - "borsh-schema-derive-internal 0.10.3", + "borsh-derive-internal", + "borsh-schema-derive-internal", "proc-macro-crate 0.1.5", "proc-macro2", "syn 1.0.109", @@ -940,17 +932,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "borsh-derive-internal" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "borsh-schema-derive-internal" version = "0.9.4" @@ -961,17 +942,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "borsh-schema-derive-internal" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "bs58" version = "0.4.0" @@ -1152,20 +1122,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" dependencies = [ "cfg-if 1.0.0", - "cipher", + "cipher 0.3.0", "cpufeatures", - "zeroize", ] [[package]] -name = "chacha20poly1305" +name = "chacha20" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if 1.0.0", + "cipher 0.4.4", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ "aead", - "chacha20", - "cipher", + "chacha20 0.9.1", + "cipher 0.4.4", "poly1305", "zeroize", ] @@ -1197,6 +1177,17 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + [[package]] name = "circular-queue" version = "0.2.6" @@ -1515,12 +1506,6 @@ dependencies = [ "cfg-if 1.0.0", ] -[[package]] -name = "crunchy" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" - [[package]] name = "crunchy" version = "0.2.2" @@ -1535,7 +1520,7 @@ checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -1551,37 +1536,32 @@ dependencies = [ [[package]] name = "crypto-mac" -version = "0.8.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" dependencies = [ - "generic-array 0.14.7", - "subtle", + "generic-array 0.12.4", + "subtle 1.0.0", ] [[package]] name = "crypto-mac" -version = "0.11.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ "generic-array 0.14.7", - "subtle", + "subtle 2.4.1", ] [[package]] -name = "crypto_api" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f855e87e75a4799e18b8529178adcde6fd4f97c1449ff4821e747ff728bb102" - -[[package]] -name = "crypto_api_chachapoly" -version = "0.4.3" +name = "crypto-mac" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930b6a026ce9d358a17f9c9046c55d90b14bb847f36b6ebb6b19365d4feffb8" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "crypto_api", + "generic-array 0.14.7", + "subtle 2.4.1", ] [[package]] @@ -1624,7 +1604,7 @@ dependencies = [ "byteorder", "digest 0.9.0", "rand_core 0.5.1", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -1661,7 +1641,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -1672,7 +1652,7 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" dependencies = [ "darling_core", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -1756,7 +1736,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.4", "crypto-common", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -1808,7 +1788,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -1935,12 +1915,12 @@ dependencies = [ "base16ct", "crypto-bigint", "der", - "ff", + "ff 0.11.1", "generic-array 0.14.7", - "group", + "group 0.11.0", "rand_core 0.6.4", "sec1", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -1991,16 +1971,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.15", -] - -[[package]] -name = "equihash" -version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" -dependencies = [ - "blake2b_simd 1.0.1", - "byteorder", + "syn 2.0.16", ] [[package]] @@ -2118,8 +2089,8 @@ dependencies = [ "ark-std", "bincode", "blake2", - "blake2b_simd 1.0.1", - "borsh 0.9.4", + "blake2b_simd", + "borsh", "digest 0.10.6", "ed25519-dalek", "either", @@ -2136,7 +2107,7 @@ dependencies = [ "serde_bytes", "serde_json", "subproductdomain", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -2161,7 +2132,18 @@ checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924" dependencies = [ "bitvec 0.22.3", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "bitvec 1.0.1", + "rand_core 0.6.4", + "subtle 2.4.1", ] [[package]] @@ -2272,7 +2254,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd910db5f9ca4dc3116f8c46367825807aa2b942f72565f16b4be0b208a00a9e" dependencies = [ "block-modes", - "cipher", + "cipher 0.3.0", "libm", "num-bigint", "num-integer", @@ -2395,7 +2377,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -2454,8 +2436,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if 1.0.0", + "js-sys", "libc", "wasi 0.9.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -2465,8 +2449,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if 1.0.0", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -2526,9 +2512,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" dependencies = [ "byteorder", - "ff", + "ff 0.11.1", + "rand_core 0.6.4", + "subtle 2.4.1", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "memuse", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -2543,8 +2541,8 @@ dependencies = [ "ark-poly", "ark-serialize", "ark-std", - "blake2b_simd 1.0.1", - "chacha20", + "blake2b_simd", + "chacha20 0.8.2", "hex", "itertools", "miracl_core", @@ -2600,27 +2598,13 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" -[[package]] -name = "halo2" -version = "0.1.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f186b85ed81082fb1cf59d52b0111f02915e89a4ac61d292b38d075e570f3a9" -dependencies = [ - "blake2b_simd 0.5.11", - "ff", - "group", - "pasta_curves", - "rand 0.8.5", - "rayon", -] - [[package]] name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ - "ahash 0.7.6", + "ahash", ] [[package]] @@ -2629,23 +2613,14 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.6", -] - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.3", + "ahash", ] [[package]] name = "hdpath" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ae1615f843ce3981b47468f3f7c435ac17deb33c2261e64d7f1e87f5c11acc" +checksum = "dfa5bc9db2c17d2660f53ce217b778d06d68de13d1cd01c0f4e5de4b7918935f" dependencies = [ "byteorder", ] @@ -2721,6 +2696,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" +dependencies = [ + "crypto-mac 0.7.0", + "digest 0.8.1", +] + [[package]] name = "hmac" version = "0.8.1" @@ -2750,6 +2735,17 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "hmac-drbg" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" +dependencies = [ + "digest 0.8.1", + "generic-array 0.12.4", + "hmac 0.7.1", +] + [[package]] name = "hmac-drbg" version = "0.3.0" @@ -2761,6 +2757,12 @@ dependencies = [ "hmac 0.8.1", ] +[[package]] +name = "hmac-sha512" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e806677ce663d0a199541030c816847b36e8dc095f70dae4a4f4ad63da5383" + [[package]] name = "http" version = "0.2.9" @@ -2939,38 +2941,6 @@ dependencies = [ "cc", ] -[[package]] -name = "ibc" -version = "0.36.0" -source = "git+https://github.com/heliaxdev/cosmos-ibc-rs?rev=db14744bfba6239cc5f58345ff90f8b7d42637d6#db14744bfba6239cc5f58345ff90f8b7d42637d6" -dependencies = [ - "bytes 1.4.0", - "cfg-if 1.0.0", - "derive_more", - "displaydoc", - "dyn-clone", - "erased-serde", - "ibc-proto 0.26.0 (git+https://github.com/heliaxdev/ibc-proto-rs?rev=dd8ba23110a144ffe2074a0b889676468266435a)", - "ics23", - "num-traits 0.2.15", - "parking_lot 0.12.1", - "primitive-types", - "prost", - "safe-regex", - "serde 1.0.163", - "serde_derive", - "serde_json", - "sha2 0.10.6", - "subtle-encoding", - "tendermint 0.23.5", - "tendermint-light-client-verifier 0.23.5", - "tendermint-proto 0.23.5", - "tendermint-testgen 0.23.5", - "time 0.3.17", - "tracing 0.1.37", - "uint", -] - [[package]] name = "ibc" version = "0.36.0" @@ -2982,7 +2952,7 @@ dependencies = [ "displaydoc", "dyn-clone", "erased-serde", - "ibc-proto 0.26.0 (git+https://github.com/heliaxdev/ibc-proto-rs.git?rev=6f4038fcf4981f1ed70771d1cd89931267f917af)", + "ibc-proto", "ics23", "num-traits 0.2.15", "parking_lot 0.12.1", @@ -2994,10 +2964,10 @@ dependencies = [ "serde_json", "sha2 0.10.6", "subtle-encoding", - "tendermint 0.23.6", - "tendermint-light-client-verifier 0.23.6", - "tendermint-proto 0.23.6", - "tendermint-testgen 0.23.6", + "tendermint", + "tendermint-light-client-verifier", + "tendermint-proto", + "tendermint-testgen", "time 0.3.17", "tracing 0.1.37", "uint", @@ -3014,27 +2984,10 @@ dependencies = [ "prost", "serde 1.0.163", "subtle-encoding", - "tendermint-proto 0.23.6", + "tendermint-proto", "tonic", ] -[[package]] -name = "ibc-proto" -version = "0.26.0" -source = "git+https://github.com/heliaxdev/ibc-proto-rs?rev=dd8ba23110a144ffe2074a0b889676468266435a#dd8ba23110a144ffe2074a0b889676468266435a" -dependencies = [ - "base64 0.13.1", - "borsh 0.10.3", - "bytes 1.4.0", - "flex-error", - "parity-scale-codec", - "prost", - "scale-info", - "serde 1.0.163", - "subtle-encoding", - "tendermint-proto 0.23.5", -] - [[package]] name = "ibc-relayer" version = "0.22.0" @@ -3060,7 +3013,7 @@ dependencies = [ "http", "humantime", "humantime-serde", - "ibc-proto 0.26.0 (git+https://github.com/heliaxdev/ibc-proto-rs.git?rev=6f4038fcf4981f1ed70771d1cd89931267f917af)", + "ibc-proto", "ibc-relayer-types", "itertools", "moka", @@ -3070,7 +3023,7 @@ dependencies = [ "regex", "retry", "ripemd", - "secp256k1 0.24.3", + "secp256k1", "semver 1.0.17", "serde 1.0.163", "serde_derive", @@ -3079,19 +3032,19 @@ dependencies = [ "signature", "strum", "subtle-encoding", - "tendermint 0.23.6", + "tendermint", "tendermint-light-client", - "tendermint-light-client-verifier 0.23.6", - "tendermint-proto 0.23.6", - "tendermint-rpc 0.23.6", + "tendermint-light-client-verifier", + "tendermint-proto", + "tendermint-rpc", "thiserror", - "tiny-bip39", + "tiny-bip39 1.0.0", "tiny-keccak", "tokio", "toml", "tonic", "tracing 0.1.37", - "uuid 1.3.2", + "uuid 1.3.3", ] [[package]] @@ -3104,7 +3057,7 @@ dependencies = [ "dyn-clone", "erased-serde", "flex-error", - "ibc-proto 0.26.0 (git+https://github.com/heliaxdev/ibc-proto-rs.git?rev=6f4038fcf4981f1ed70771d1cd89931267f917af)", + "ibc-proto", "ics23", "itertools", "num-rational", @@ -3115,11 +3068,11 @@ dependencies = [ "serde_derive", "serde_json", "subtle-encoding", - "tendermint 0.23.6", - "tendermint-light-client-verifier 0.23.6", - "tendermint-proto 0.23.6", - "tendermint-rpc 0.23.6", - "tendermint-testgen 0.23.6", + "tendermint", + "tendermint-light-client-verifier", + "tendermint-proto", + "tendermint-rpc", + "tendermint-testgen", "time 0.3.17", "uint", ] @@ -3197,9 +3150,9 @@ dependencies = [ [[package]] name = "incrementalmerkletree" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186fd3ab92aeac865d4b80b410de9a7b341d31ba8281373caed0b6d17b2b5e96" +checksum = "d5ad43a3f5795945459d577f6589cf62a476e92c79b75e70cd954364e14ce17b" dependencies = [ "serde 1.0.163", ] @@ -3215,7 +3168,7 @@ name = "index-set" version = "0.7.1" source = "git+https://github.com/heliaxdev/index-set?tag=v0.7.1#dc24cdbbe3664514d59f1a4c4031863fc565f1c2" dependencies = [ - "borsh 0.9.4", + "borsh", "serde 1.0.163", ] @@ -3230,6 +3183,15 @@ dependencies = [ "serde 1.0.163", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "input_buffer" version = "0.4.0" @@ -3303,25 +3265,25 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.62" +version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" dependencies = [ "wasm-bindgen", ] [[package]] name = "jubjub" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7baec19d4e83f9145d4891178101a604565edff9645770fc979804138b04c" +checksum = "a575df5f985fe1cd5b2b05664ff6accfc46559032b954529fd225a2168d27b0f" dependencies = [ - "bitvec 0.22.3", - "bls12_381", - "ff", - "group", + "bitvec 1.0.1", + "bls12_381 0.7.1", + "ff 0.12.1", + "group 0.12.1", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -3434,9 +3396,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "librocksdb-sys" @@ -3454,6 +3416,22 @@ dependencies = [ "zstd-sys", ] +[[package]] +name = "libsecp256k1" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" +dependencies = [ + "arrayref", + "crunchy", + "digest 0.8.1", + "hmac-drbg 0.2.0", + "rand 0.7.3", + "sha2 0.8.2", + "subtle 2.4.1", + "typenum", +] + [[package]] name = "libsecp256k1" version = "0.7.0" @@ -3462,7 +3440,7 @@ dependencies = [ "arrayref", "base64 0.13.1", "digest 0.9.0", - "hmac-drbg", + "hmac-drbg 0.3.0", "libsecp256k1-core", "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", @@ -3477,9 +3455,9 @@ name = "libsecp256k1-core" version = "0.3.0" source = "git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9#bbb3bd44a49db361f21d9db80f9a087c194c0ae9" dependencies = [ - "crunchy 0.2.2", + "crunchy", "digest 0.9.0", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -3621,60 +3599,69 @@ dependencies = [ "serde_yaml", ] +[[package]] +name = "masp_note_encryption" +version = "0.2.0" +source = "git+https://github.com/anoma/masp?rev=cfea8c95d3f73077ca3e25380fd27e5b46e828fd#cfea8c95d3f73077ca3e25380fd27e5b46e828fd" +dependencies = [ + "borsh", + "chacha20 0.9.1", + "chacha20poly1305", + "cipher 0.4.4", + "rand_core 0.6.4", + "subtle 2.4.1", +] + [[package]] name = "masp_primitives" -version = "0.5.0" -source = "git+https://github.com/anoma/masp?rev=bee40fc465f6afbd10558d12fe96eb1742eee45c#bee40fc465f6afbd10558d12fe96eb1742eee45c" +version = "0.9.0" +source = "git+https://github.com/anoma/masp?rev=cfea8c95d3f73077ca3e25380fd27e5b46e828fd#cfea8c95d3f73077ca3e25380fd27e5b46e828fd" dependencies = [ "aes", "bip0039", - "bitvec 0.22.3", - "blake2b_simd 1.0.1", + "bitvec 1.0.1", + "blake2b_simd", "blake2s_simd 1.0.1", - "bls12_381", - "borsh 0.9.4", + "bls12_381 0.7.1", + "borsh", "byteorder", - "chacha20poly1305", - "crypto_api_chachapoly", - "ff", + "ff 0.12.1", "fpe", - "group", + "group 0.12.1", "hex", "incrementalmerkletree", "jubjub", "lazy_static", + "masp_note_encryption", + "memuse", + "nonempty", "rand 0.8.5", "rand_core 0.6.4", - "ripemd160", - "secp256k1 0.20.3", - "serde 1.0.163", "sha2 0.9.9", - "subtle", + "subtle 2.4.1", "zcash_encoding", - "zcash_primitives", ] [[package]] name = "masp_proofs" -version = "0.5.0" -source = "git+https://github.com/anoma/masp?rev=bee40fc465f6afbd10558d12fe96eb1742eee45c#bee40fc465f6afbd10558d12fe96eb1742eee45c" +version = "0.9.0" +source = "git+https://github.com/anoma/masp?rev=cfea8c95d3f73077ca3e25380fd27e5b46e828fd#cfea8c95d3f73077ca3e25380fd27e5b46e828fd" dependencies = [ - "bellman", - "blake2b_simd 1.0.1", - "bls12_381", - "byteorder", + "bellman 0.13.1", + "blake2b_simd", + "bls12_381 0.7.1", "directories", - "ff", - "group", + "getrandom 0.2.9", + "group 0.12.1", "itertools", "jubjub", "lazy_static", "masp_primitives", "minreq", "rand_core 0.6.4", + "redjubjub", + "tracing 0.1.37", "wagyu-zcash-parameters", - "zcash_primitives", - "zcash_proofs", ] [[package]] @@ -3752,9 +3739,12 @@ name = "memuse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2145869435ace5ea6ea3d35f59be559317ec9a0d04e1812d5f185a87b6d36f1a" -dependencies = [ - "nonempty", -] + +[[package]] +name = "memzero" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c0d11ac30a033511ae414355d80f70d9f29a44a49140face477117a1ee90db" [[package]] name = "merlin" @@ -3899,7 +3889,7 @@ dependencies = [ "tagptr", "thiserror", "triomphe", - "uuid 1.3.2", + "uuid 1.3.3", ] [[package]] @@ -3921,21 +3911,22 @@ dependencies = [ "assert_matches", "async-std", "async-trait", - "bellman", + "base58 0.2.0", + "bellman 0.11.2", "bimap", - "bls12_381", - "borsh 0.9.4", + "bls12_381 0.6.1", + "borsh", "byte-unit", "circular-queue", "clru", "data-encoding", + "derivation-path", "derivative", - "ibc 0.36.0 (git+https://github.com/heliaxdev/cosmos-ibc-rs?rev=db14744bfba6239cc5f58345ff90f8b7d42637d6)", - "ibc 0.36.0 (git+https://github.com/heliaxdev/cosmos-ibc-rs.git?rev=e71bc2cc79f8c2b32e970d95312f251398c93d9e)", - "ibc-proto 0.26.0 (git+https://github.com/heliaxdev/ibc-proto-rs.git?rev=6f4038fcf4981f1ed70771d1cd89931267f917af)", - "ibc-proto 0.26.0 (git+https://github.com/heliaxdev/ibc-proto-rs?rev=dd8ba23110a144ffe2074a0b889676468266435a)", + "hex", + "ibc", + "ibc-proto", "itertools", - "libsecp256k1", + "libsecp256k1 0.7.0", "loupe", "masp_primitives", "masp_proofs", @@ -3952,17 +3943,21 @@ dependencies = [ "rand 0.8.5", "rand_core 0.6.4", "rayon", + "ripemd", "rust_decimal", "rust_decimal_macros", "serde 1.0.163", "serde_json", "sha2 0.9.9", + "slip10_ed25519", "tempfile", - "tendermint 0.23.6", - "tendermint-proto 0.23.6", - "tendermint-rpc 0.23.6", + "tendermint", + "tendermint-proto", + "tendermint-rpc", "test-log", "thiserror", + "tiny-bip39 0.8.2", + "tiny-hderive", "tokio", "toml", "tracing 0.1.37", @@ -3991,7 +3986,7 @@ dependencies = [ "bimap", "bit-set", "blake2b-rs", - "borsh 0.9.4", + "borsh", "byte-unit", "byteorder", "clap", @@ -4029,6 +4024,7 @@ dependencies = [ "rayon", "regex", "reqwest", + "ripemd", "rlimit", "rocksdb", "rpassword", @@ -4043,10 +4039,10 @@ dependencies = [ "sysinfo", "tar", "tempfile", - "tendermint 0.23.6", - "tendermint-config 0.23.6", - "tendermint-proto 0.23.6", - "tendermint-rpc 0.23.6", + "tendermint", + "tendermint-config", + "tendermint-proto", + "tendermint-rpc", "test-log", "thiserror", "tokio", @@ -4054,13 +4050,13 @@ dependencies = [ "toml", "tonic", "tower", - "tower-abci 0.1.0 (git+https://github.com/heliaxdev/tower-abci.git?rev=367d8d958b83c501ed2c09e9c4595f8bf75a0b01)", - "tower-abci 0.1.0 (git+https://github.com/heliaxdev/tower-abci?rev=a31ce06533f5fbd943508676059d44de27395792)", + "tower-abci", "tracing 0.1.37", "tracing-log", "tracing-subscriber 0.3.17", "websocket", "winapi 0.3.9", + "zeroize", ] [[package]] @@ -4072,8 +4068,8 @@ dependencies = [ "ark-serialize", "assert_matches", "bech32 0.8.1", - "bellman", - "borsh 0.9.4", + "bellman 0.11.2", + "borsh", "chrono", "data-encoding", "derivative", @@ -4081,14 +4077,12 @@ dependencies = [ "ferveo", "ferveo-common", "group-threshold-cryptography", - "ibc 0.36.0 (git+https://github.com/heliaxdev/cosmos-ibc-rs?rev=db14744bfba6239cc5f58345ff90f8b7d42637d6)", - "ibc 0.36.0 (git+https://github.com/heliaxdev/cosmos-ibc-rs.git?rev=e71bc2cc79f8c2b32e970d95312f251398c93d9e)", - "ibc-proto 0.26.0 (git+https://github.com/heliaxdev/ibc-proto-rs.git?rev=6f4038fcf4981f1ed70771d1cd89931267f917af)", - "ibc-proto 0.26.0 (git+https://github.com/heliaxdev/ibc-proto-rs?rev=dd8ba23110a144ffe2074a0b889676468266435a)", + "ibc", + "ibc-proto", "ics23", "index-set", "itertools", - "libsecp256k1", + "libsecp256k1 0.7.0", "masp_primitives", "namada_macros", "pretty_assertions", @@ -4104,8 +4098,8 @@ dependencies = [ "serde_json", "sha2 0.9.9", "sparse-merkle-tree", - "tendermint 0.23.6", - "tendermint-proto 0.23.6", + "tendermint", + "tendermint-proto", "test-log", "thiserror", "tonic-build", @@ -4118,7 +4112,7 @@ dependencies = [ name = "namada_encoding_spec" version = "0.16.0" dependencies = [ - "borsh 0.9.4", + "borsh", "itertools", "lazy_static", "madato", @@ -4138,7 +4132,7 @@ dependencies = [ name = "namada_proof_of_stake" version = "0.16.0" dependencies = [ - "borsh 0.9.4", + "borsh", "data-encoding", "derivative", "hex", @@ -4158,7 +4152,7 @@ dependencies = [ name = "namada_test_utils" version = "0.16.0" dependencies = [ - "borsh 0.9.4", + "borsh", "namada_core", "strum", ] @@ -4168,7 +4162,7 @@ name = "namada_tests" version = "0.16.0" dependencies = [ "assert_cmd", - "borsh 0.9.4", + "borsh", "chrono", "color-eyre", "concat-idents", @@ -4198,10 +4192,10 @@ dependencies = [ "serde_json", "sha2 0.9.9", "tempfile", - "tendermint 0.23.6", - "tendermint-config 0.23.6", - "tendermint-proto 0.23.6", - "tendermint-rpc 0.23.6", + "tendermint", + "tendermint-config", + "tendermint-proto", + "tendermint-rpc", "test-log", "tokio", "toml", @@ -4213,7 +4207,7 @@ dependencies = [ name = "namada_tx_prelude" version = "0.16.0" dependencies = [ - "borsh 0.9.4", + "borsh", "masp_primitives", "namada_core", "namada_macros", @@ -4228,10 +4222,9 @@ dependencies = [ name = "namada_vm_env" version = "0.16.0" dependencies = [ - "borsh 0.9.4", + "borsh", "hex", "masp_primitives", - "masp_proofs", "namada_core", ] @@ -4239,7 +4232,7 @@ dependencies = [ name = "namada_vp_prelude" version = "0.16.0" dependencies = [ - "borsh 0.9.4", + "borsh", "namada_core", "namada_macros", "namada_proof_of_stake", @@ -4520,7 +4513,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -4541,33 +4534,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "orchard" -version = "0.1.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31f03b6d0aee6d993cac35388b818e04f076ded0ab284979e4d7cd5a8b3c2be" -dependencies = [ - "aes", - "arrayvec 0.7.2", - "bigint", - "bitvec 0.22.3", - "blake2b_simd 1.0.1", - "ff", - "fpe", - "group", - "halo2", - "incrementalmerkletree", - "lazy_static", - "memuse", - "nonempty", - "pasta_curves", - "rand 0.8.5", - "reddsa", - "serde 1.0.163", - "subtle", - "zcash_note_encryption 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "orion" version = "0.16.1" @@ -4576,7 +4542,7 @@ checksum = "c6624905ddd92e460ff0685567539ed1ac985b2dee4c92c7edcd64fce905b00c" dependencies = [ "ct-codecs", "getrandom 0.2.9", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -4613,7 +4579,16 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2e415e349a3006dd7d9482cdab1c980a845bed1377777d768cb693a44540b42" dependencies = [ - "group", + "group 0.11.0", +] + +[[package]] +name = "pairing" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135590d8bdba2b31346f9cd1fb2a912329f5135e832a4f422942eb6ead8b6b3b" +dependencies = [ + "group 0.12.1", ] [[package]] @@ -4707,26 +4682,11 @@ dependencies = [ name = "password-hash" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "pasta_curves" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d647d91972bad78120fd61e06b225fcda117805c9bbf17676b51bd03a251278b" -dependencies = [ - "blake2b_simd 0.5.11", - "ff", - "group", - "lazy_static", - "rand 0.8.5", - "static_assertions", - "subtle", +checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle 2.4.1", ] [[package]] @@ -4735,6 +4695,15 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +[[package]] +name = "pbkdf2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +dependencies = [ + "crypto-mac 0.8.0", +] + [[package]] name = "pbkdf2" version = "0.9.0" @@ -4836,7 +4805,7 @@ checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -4873,9 +4842,9 @@ dependencies = [ [[package]] name = "poly1305" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures", "opaque-debug 0.3.0", @@ -4939,12 +4908,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058" +checksum = "617feabb81566b593beb4886fb8c1f38064169dae4dccad0e3220160c3b37203" dependencies = [ "proc-macro2", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -5004,9 +4973,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" dependencies = [ "unicode-ident", ] @@ -5406,17 +5375,15 @@ dependencies = [ ] [[package]] -name = "reddsa" -version = "0.1.0" +name = "redjubjub" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90e2c94bca3445cae0d55dff7370e29c24885d2403a1665ce19c106c79455e6" +checksum = "6039ff156887caf92df308cbaccdc058c9d3155a913da046add6e48c4cdbd91d" dependencies = [ - "blake2b_simd 0.5.11", + "blake2b_simd", "byteorder", "digest 0.9.0", - "group", "jubjub", - "pasta_curves", "rand_core 0.6.4", "serde 1.0.163", "thiserror", @@ -5524,9 +5491,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.17" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ "base64 0.21.0", "bytes 1.4.0", @@ -5625,7 +5592,7 @@ dependencies = [ "rkyv_derive", "seahash", "tinyvec", - "uuid 1.3.2", + "uuid 1.3.3", ] [[package]] @@ -5681,7 +5648,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee9164faf726e4f3ece4978b25ca877ddc6802fa77f38cdccb32c7f805ecd70c" dependencies = [ "arrayvec 0.7.2", - "borsh 0.9.4", + "borsh", "num-traits 0.2.15", "serde 1.0.163", ] @@ -5899,30 +5866,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scale-info" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfdef77228a4c05dc94211441595746732131ad7f6530c6c18f045da7b7ab937" -dependencies = [ - "cfg-if 1.0.0", - "derive_more", - "parity-scale-codec", - "scale-info-derive", -] - -[[package]] -name = "scale-info-derive" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53012eae69e5aa5c14671942a5dd47de59d4cdcff8532a6dd0e081faf1119482" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "schannel" version = "0.1.21" @@ -5981,19 +5924,10 @@ checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" dependencies = [ "der", "generic-array 0.14.7", - "subtle", + "subtle 2.4.1", "zeroize", ] -[[package]] -name = "secp256k1" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d03ceae636d0fed5bae6a7f4f664354c5f4fcedf6eef053fef17e49f837d0a" -dependencies = [ - "secp256k1-sys 0.4.2", -] - [[package]] name = "secp256k1" version = "0.24.3" @@ -6002,19 +5936,10 @@ checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" dependencies = [ "bitcoin_hashes", "rand 0.8.5", - "secp256k1-sys 0.6.1", + "secp256k1-sys", "serde 1.0.163", ] -[[package]] -name = "secp256k1-sys" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957da2573cde917463ece3570eab4a0b3f19de6f1646cde62e6fd3868f566036" -dependencies = [ - "cc", -] - [[package]] name = "secp256k1-sys" version = "0.6.1" @@ -6143,7 +6068,7 @@ checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -6165,7 +6090,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -6228,6 +6153,18 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + [[package]] name = "sha2" version = "0.9.9" @@ -6342,6 +6279,15 @@ dependencies = [ "autocfg 1.1.0", ] +[[package]] +name = "slip10_ed25519" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be0ff28bf14f9610a342169084e87a4f435ad798ec528dc7579a3678fa9dc9a" +dependencies = [ + "hmac-sha512", +] + [[package]] name = "smallvec" version = "0.6.14" @@ -6373,7 +6319,7 @@ version = "0.3.1-pre" source = "git+https://github.com/heliaxdev/sparse-merkle-tree?rev=e086b235ed6e68929bf73f617dd61cd17b000a56#e086b235ed6e68929bf73f617dd61cd17b000a56" dependencies = [ "blake2b-rs", - "borsh 0.9.4", + "borsh", "cfg-if 1.0.0", "ics23", "sha2 0.9.9", @@ -6438,6 +6384,12 @@ dependencies = [ "ark-std", ] +[[package]] +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" + [[package]] name = "subtle" version = "2.4.1" @@ -6472,9 +6424,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" dependencies = [ "proc-macro2", "quote", @@ -6543,34 +6495,6 @@ dependencies = [ "windows-sys 0.45.0", ] -[[package]] -name = "tendermint" -version = "0.23.5" -source = "git+https://github.com/heliaxdev/tendermint-rs?rev=a3a0ad5f07d380976bbd5321239aec9cc3a8f916#a3a0ad5f07d380976bbd5321239aec9cc3a8f916" -dependencies = [ - "async-trait", - "bytes 1.4.0", - "ed25519", - "ed25519-dalek", - "flex-error", - "futures 0.3.28", - "num-traits 0.2.15", - "once_cell", - "prost", - "prost-types", - "serde 1.0.163", - "serde_bytes", - "serde_json", - "serde_repr", - "sha2 0.9.9", - "signature", - "subtle", - "subtle-encoding", - "tendermint-proto 0.23.5", - "time 0.3.17", - "zeroize", -] - [[package]] name = "tendermint" version = "0.23.6" @@ -6594,26 +6518,13 @@ dependencies = [ "serde_repr", "sha2 0.9.9", "signature", - "subtle", + "subtle 2.4.1", "subtle-encoding", - "tendermint-proto 0.23.6", + "tendermint-proto", "time 0.3.17", "zeroize", ] -[[package]] -name = "tendermint-config" -version = "0.23.5" -source = "git+https://github.com/heliaxdev/tendermint-rs?rev=a3a0ad5f07d380976bbd5321239aec9cc3a8f916#a3a0ad5f07d380976bbd5321239aec9cc3a8f916" -dependencies = [ - "flex-error", - "serde 1.0.163", - "serde_json", - "tendermint 0.23.5", - "toml", - "url 2.3.1", -] - [[package]] name = "tendermint-config" version = "0.23.6" @@ -6622,7 +6533,7 @@ dependencies = [ "flex-error", "serde 1.0.163", "serde_json", - "tendermint 0.23.6", + "tendermint", "toml", "url 2.3.1", ] @@ -6641,26 +6552,13 @@ dependencies = [ "serde_cbor", "serde_derive", "static_assertions", - "tendermint 0.23.6", - "tendermint-light-client-verifier 0.23.6", - "tendermint-rpc 0.23.6", + "tendermint", + "tendermint-light-client-verifier", + "tendermint-rpc", "time 0.3.17", "tokio", ] -[[package]] -name = "tendermint-light-client-verifier" -version = "0.23.5" -source = "git+https://github.com/heliaxdev/tendermint-rs?rev=a3a0ad5f07d380976bbd5321239aec9cc3a8f916#a3a0ad5f07d380976bbd5321239aec9cc3a8f916" -dependencies = [ - "derive_more", - "flex-error", - "serde 1.0.163", - "tendermint 0.23.5", - "tendermint-rpc 0.23.5", - "time 0.3.17", -] - [[package]] name = "tendermint-light-client-verifier" version = "0.23.6" @@ -6669,24 +6567,7 @@ dependencies = [ "derive_more", "flex-error", "serde 1.0.163", - "tendermint 0.23.6", - "time 0.3.17", -] - -[[package]] -name = "tendermint-proto" -version = "0.23.5" -source = "git+https://github.com/heliaxdev/tendermint-rs?rev=a3a0ad5f07d380976bbd5321239aec9cc3a8f916#a3a0ad5f07d380976bbd5321239aec9cc3a8f916" -dependencies = [ - "bytes 1.4.0", - "flex-error", - "num-derive", - "num-traits 0.2.15", - "prost", - "prost-types", - "serde 1.0.163", - "serde_bytes", - "subtle-encoding", + "tendermint", "time 0.3.17", ] @@ -6707,30 +6588,6 @@ dependencies = [ "time 0.3.17", ] -[[package]] -name = "tendermint-rpc" -version = "0.23.5" -source = "git+https://github.com/heliaxdev/tendermint-rs?rev=a3a0ad5f07d380976bbd5321239aec9cc3a8f916#a3a0ad5f07d380976bbd5321239aec9cc3a8f916" -dependencies = [ - "bytes 1.4.0", - "flex-error", - "getrandom 0.2.9", - "peg", - "pin-project", - "serde 1.0.163", - "serde_bytes", - "serde_json", - "subtle-encoding", - "tendermint 0.23.5", - "tendermint-config 0.23.5", - "tendermint-proto 0.23.5", - "thiserror", - "time 0.3.17", - "url 2.3.1", - "uuid 0.8.2", - "walkdir", -] - [[package]] name = "tendermint-rpc" version = "0.23.6" @@ -6752,9 +6609,9 @@ dependencies = [ "serde_bytes", "serde_json", "subtle-encoding", - "tendermint 0.23.6", - "tendermint-config 0.23.6", - "tendermint-proto 0.23.6", + "tendermint", + "tendermint-config", + "tendermint-proto", "thiserror", "time 0.3.17", "tokio", @@ -6764,21 +6621,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "tendermint-testgen" -version = "0.23.5" -source = "git+https://github.com/heliaxdev/tendermint-rs?rev=a3a0ad5f07d380976bbd5321239aec9cc3a8f916#a3a0ad5f07d380976bbd5321239aec9cc3a8f916" -dependencies = [ - "ed25519-dalek", - "gumdrop", - "serde 1.0.163", - "serde_json", - "simple-error", - "tempfile", - "tendermint 0.23.5", - "time 0.3.17", -] - [[package]] name = "tendermint-testgen" version = "0.23.6" @@ -6790,7 +6632,7 @@ dependencies = [ "serde_json", "simple-error", "tempfile", - "tendermint 0.23.6", + "tendermint", "time 0.3.17", ] @@ -6846,7 +6688,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -6905,6 +6747,24 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-bip39" +version = "0.8.2" +source = "git+https://github.com/anoma/tiny-bip39.git?rev=bf0f6d8713589b83af7a917366ec31f5275c0e57#bf0f6d8713589b83af7a917366ec31f5275c0e57" +dependencies = [ + "anyhow", + "hmac 0.8.1", + "once_cell", + "pbkdf2 0.4.0", + "rand 0.7.3", + "rustc-hash", + "sha2 0.9.9", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + [[package]] name = "tiny-bip39" version = "1.0.0" @@ -6924,13 +6784,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "tiny-hderive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01b874a4992538d4b2f4fbbac11b9419d685f4b39bdc3fed95b04e07bfd76040" +dependencies = [ + "base58 0.1.0", + "hmac 0.7.1", + "libsecp256k1 0.3.5", + "memzero", + "sha2 0.8.2", +] + [[package]] name = "tiny-keccak" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" dependencies = [ - "crunchy 0.2.2", + "crunchy", ] [[package]] @@ -7029,7 +6902,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -7181,15 +7054,15 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.19.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "92d964908cec0d030b812013af25a0e57fddfadb1e066ecc6681d86253129d4f" dependencies = [ "indexmap", "toml_datetime", @@ -7274,25 +7147,7 @@ dependencies = [ "futures 0.3.28", "pin-project", "prost", - "tendermint-proto 0.23.6", - "tokio", - "tokio-stream", - "tokio-util 0.6.10", - "tower", - "tracing 0.1.30", - "tracing-tower", -] - -[[package]] -name = "tower-abci" -version = "0.1.0" -source = "git+https://github.com/heliaxdev/tower-abci?rev=a31ce06533f5fbd943508676059d44de27395792#a31ce06533f5fbd943508676059d44de27395792" -dependencies = [ - "bytes 1.4.0", - "futures 0.3.28", - "pin-project", - "prost", - "tendermint-proto 0.23.5", + "tendermint-proto", "tokio", "tokio-stream", "tokio-util 0.6.10", @@ -7364,7 +7219,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -7543,7 +7398,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ "byteorder", - "crunchy 0.2.2", + "crunchy", "hex", "static_assertions", ] @@ -7607,12 +7462,12 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "universal-hash" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" dependencies = [ - "generic-array 0.14.7", - "subtle", + "crypto-common", + "subtle 2.4.1", ] [[package]] @@ -7663,9 +7518,9 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" [[package]] name = "uuid" -version = "1.3.2" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dad5567ad0cf5b760e5665964bec1b47dfd077ba8a2544b513f3556d3d239a2" +checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" dependencies = [ "getrandom 0.2.9", ] @@ -7815,9 +7670,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.85" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -7825,24 +7680,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.85" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" dependencies = [ "bumpalo", "log 0.4.17", "once_cell", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.35" +version = "0.4.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "083abe15c5d88556b77bdf7aef403625be9e327ad37c62c4e4129af740168163" +checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -7852,9 +7707,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.85" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7862,28 +7717,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.85" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.85" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" [[package]] name = "wasm-encoder" -version = "0.26.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05d0b6fcd0aeb98adf16e7975331b3c17222aa815148f5b976370ce589d80ef" +checksum = "e77053dc709db790691d3732cfc458adc5acc881dec524965c608effdcd9c581" dependencies = [ "leb128", ] @@ -8126,9 +7981,9 @@ checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" [[package]] name = "wast" -version = "57.0.0" +version = "58.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eb0f5ed17ac4421193c7477da05892c2edafd67f9639e3c11a82086416662dc" +checksum = "372eecae2d10a5091c2005b32377d7ecd6feecdf2c05838056d02d8b4f07c429" dependencies = [ "leb128", "memchr", @@ -8138,18 +7993,18 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9ab0d87337c3be2bb6fc5cd331c4ba9fd6bcb4ee85048a0dd59ed9ecf92e53" +checksum = "6d47446190e112ab1579ab40b3ad7e319d859d74e5134683f04e9f0747bf4173" dependencies = [ "wast", ] [[package]] name = "web-sys" -version = "0.3.62" +version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b5f940c7edfdc6d12126d98c9ef4d1b3d470011c47c76a6581df47ad9ba721" +checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" dependencies = [ "js-sys", "wasm-bindgen", @@ -8563,83 +8418,10 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" -dependencies = [ - "byteorder", - "nonempty", -] - -[[package]] -name = "zcash_note_encryption" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33f84ae538f05a8ac74c82527f06b77045ed9553a0871d9db036166a4c344e3a" -dependencies = [ - "chacha20", - "chacha20poly1305", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "zcash_note_encryption" -version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" +source = "git+https://github.com/zcash/librustzcash?rev=43c18d0#43c18d000fcbe45363b2d53585d5102841eff99e" dependencies = [ - "chacha20", - "chacha20poly1305", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "zcash_primitives" -version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" -dependencies = [ - "aes", - "bip0039", - "bitvec 0.22.3", - "blake2b_simd 1.0.1", - "blake2s_simd 1.0.1", - "bls12_381", "byteorder", - "chacha20poly1305", - "equihash", - "ff", - "fpe", - "group", - "hex", - "incrementalmerkletree", - "jubjub", - "lazy_static", - "memuse", "nonempty", - "orchard", - "rand 0.8.5", - "rand_core 0.6.4", - "sha2 0.9.9", - "subtle", - "zcash_encoding", - "zcash_note_encryption 0.1.0 (git+https://github.com/zcash/librustzcash/?rev=2425a08)", -] - -[[package]] -name = "zcash_proofs" -version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" -dependencies = [ - "bellman", - "blake2b_simd 1.0.1", - "bls12_381", - "byteorder", - "directories", - "ff", - "group", - "jubjub", - "lazy_static", - "rand_core 0.6.4", - "zcash_primitives", ] [[package]] @@ -8659,7 +8441,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] diff --git a/Makefile b/Makefile index a05dffd398..2f8b1b7465 100644 --- a/Makefile +++ b/Makefile @@ -41,15 +41,6 @@ check: make -C $(wasms_for_tests) check && \ $(foreach wasm,$(wasm_templates),$(check-wasm) && ) true -check-abcipp: - $(cargo) +$(nightly) check \ - --workspace \ - --exclude namada_tests \ - --all-targets \ - --no-default-features \ - --features "abcipp ibc-mocks-abcipp testing" \ - -Z unstable-options - check-mainnet: $(cargo) check --workspace --features "mainnet" @@ -61,25 +52,6 @@ clippy: make -C $(wasms_for_tests) clippy && \ $(foreach wasm,$(wasm_templates),$(clippy-wasm) && ) true -clippy-abcipp: - NAMADA_DEV=false $(cargo) +$(nightly) clippy --all-targets \ - --manifest-path ./apps/Cargo.toml \ - --no-default-features \ - --features "std testing abcipp" && \ - $(cargo) +$(nightly) clippy --all-targets \ - --manifest-path ./proof_of_stake/Cargo.toml \ - --features "testing" && \ - $(cargo) +$(nightly) clippy --all-targets \ - --manifest-path ./shared/Cargo.toml \ - --no-default-features \ - --features "testing wasm-runtime abcipp ibc-mocks-abcipp ferveo-tpke" && \ - $(cargo) +$(nightly) clippy \ - --all-targets \ - --manifest-path ./vm_env/Cargo.toml \ - --no-default-features && \ - make -C $(wasms) clippy && \ - $(foreach wasm,$(wasm_templates),$(clippy-wasm) && ) true - clippy-mainnet: $(cargo) +$(nightly) clippy --all-targets --features "mainnet" -- -D warnings @@ -137,36 +109,6 @@ else -Z unstable-options --report-time endif -test-unit-abcipp: - $(cargo) test \ - --manifest-path ./apps/Cargo.toml \ - --no-default-features \ - --features "testing std abcipp" \ - -Z unstable-options \ - $(TEST_FILTER) -- \ - -Z unstable-options --report-time && \ - $(cargo) test \ - --manifest-path \ - ./proof_of_stake/Cargo.toml \ - --features "testing" \ - -Z unstable-options \ - $(TEST_FILTER) -- \ - -Z unstable-options --report-time && \ - $(cargo) test \ - --manifest-path ./shared/Cargo.toml \ - --no-default-features \ - --features "testing wasm-runtime abcipp ibc-mocks-abcipp" \ - -Z unstable-options \ - $(TEST_FILTER) -- \ - -Z unstable-options --report-time && \ - $(cargo) test \ - --manifest-path ./vm_env/Cargo.toml \ - --no-default-features \ - --features "abcipp" \ - -Z unstable-options \ - $(TEST_FILTER) -- \ - -Z unstable-options --report-time - test-unit: $(cargo) +$(nightly) test \ $(TEST_FILTER) \ @@ -278,4 +220,4 @@ test-miri: MIRIFLAGS="-Zmiri-disable-isolation" $(cargo) +$(nightly) miri test -.PHONY : build check build-release clippy install run-ledger run-gossip reset-ledger test test-debug fmt watch clean build-doc doc build-wasm-scripts-docker debug-wasm-scripts-docker build-wasm-scripts debug-wasm-scripts clean-wasm-scripts dev-deps test-miri test-unit test-unit-abcipp clippy-abcipp +.PHONY : build check build-release clippy install run-ledger run-gossip reset-ledger test test-debug fmt watch clean build-doc doc build-wasm-scripts-docker debug-wasm-scripts-docker build-wasm-scripts debug-wasm-scripts clean-wasm-scripts dev-deps test-miri test-unit diff --git a/apps/Cargo.toml b/apps/Cargo.toml index d79451a358..710d02daf3 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -45,20 +45,10 @@ mainnet = [ "namada/mainnet", ] dev = ["namada/dev"] -std = ["ed25519-consensus/std", "rand/std", "rand_core/std"] +std = ["ed25519-consensus/std", "rand/std", "rand_core/std", "namada/std"] # for integration tests and test utilies testing = ["dev"] -abcipp = [ - "namada/abcipp", - "namada/tendermint-rpc-abcipp", - "tendermint-abcipp", - "tendermint-config-abcipp", - "tendermint-proto-abcipp", - "tendermint-rpc-abcipp", - "tower-abci-abcipp", -] - abciplus = [ "namada/abciplus", "namada/tendermint-rpc", @@ -70,7 +60,7 @@ abciplus = [ ] [dependencies] -namada = {path = "../shared", default-features = false, features = ["wasm-runtime", "ferveo-tpke", "masp-tx-gen"]} +namada = {path = "../shared", default-features = false, features = ["wasm-runtime", "ferveo-tpke", "masp-tx-gen", "multicore"]} ark-serialize = "0.3.0" ark-std = "0.3.0" # branch = "bat/arse-merkle-tree" @@ -90,6 +80,7 @@ color-eyre = "0.5.10" config = "0.11.0" data-encoding = "2.3.2" derivative = "2.2.0" +directories = "4.0.1" ed25519-consensus = "1.2.0" ferveo = {git = "https://github.com/anoma/ferveo", rev = "e5abd0acc938da90140351a65a26472eb495ce4d"} ferveo-common = {git = "https://github.com/anoma/ferveo", rev = "e5abd0acc938da90140351a65a26472eb495ce4d"} @@ -113,6 +104,7 @@ rand_core = {version = "0.6", default-features = false} rayon = "=1.5.3" regex = "1.4.5" reqwest = "0.11.4" +ripemd = "0.1" rlimit = "0.5.4" rocksdb = {version = "0.21.0", features = ['zstd', 'jemalloc'], default-features = false} rpassword = "5.0.1" @@ -125,10 +117,6 @@ signal-hook = "0.3.9" sysinfo = {version = "=0.21.1", default-features = false} tar = "0.4.37" # temporarily using fork work-around -tendermint-abcipp = {package = "tendermint", git = "https://github.com/heliaxdev/tendermint-rs", rev = "02b256829e80f8cfecf3fa0d625c2a76c79cd043", optional = true} -tendermint-config-abcipp = {package = "tendermint-config", git = "https://github.com/heliaxdev/tendermint-rs", rev = "02b256829e80f8cfecf3fa0d625c2a76c79cd043", optional = true} -tendermint-proto-abcipp = {package = "tendermint-proto", git = "https://github.com/heliaxdev/tendermint-rs", rev = "02b256829e80f8cfecf3fa0d625c2a76c79cd043", optional = true} -tendermint-rpc-abcipp = {package = "tendermint-rpc", git = "https://github.com/heliaxdev/tendermint-rs", rev = "02b256829e80f8cfecf3fa0d625c2a76c79cd043", features = ["http-client", "websocket-client"], optional = true} tendermint = {version = "0.23.6", optional = true} tendermint-config = {version = "0.23.6", optional = true} tendermint-proto = {version = "0.23.6", optional = true} @@ -140,20 +128,19 @@ tonic = "0.8.3" tower = "0.4" # Also, using the same version of tendermint-rs as we do here. # with a patch for https://github.com/penumbra-zone/tower-abci/issues/7. -tower-abci-abcipp = {package = "tower-abci", git = "https://github.com/heliaxdev/tower-abci", rev = "a31ce06533f5fbd943508676059d44de27395792", optional = true} tower-abci = {version = "0.1.0", optional = true} tracing = "0.1.30" tracing-log = "0.1.2" tracing-subscriber = {version = "0.3.7", features = ["env-filter", "json"]} websocket = "0.26.2" winapi = "0.3.9" -#libmasp = { git = "https://github.com/anoma/masp", branch = "murisi/masp-incentive" } -masp_primitives = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c", features = ["transparent-inputs"] } -masp_proofs = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c", features = ["bundled-prover", "download-params"] } +# branch = "murisi/namada-integration" +masp_primitives = { git = "https://github.com/anoma/masp", rev = "cfea8c95d3f73077ca3e25380fd27e5b46e828fd", features = ["transparent-inputs"] } +masp_proofs = { git = "https://github.com/anoma/masp", rev = "cfea8c95d3f73077ca3e25380fd27e5b46e828fd", features = ["bundled-prover", "download-params"] } bimap = {version = "0.6.2", features = ["serde"]} rust_decimal = "=1.26.1" rust_decimal_macros = "=1.26.1" -directories = "4.0.1" +zeroize = "1.5.5" [dev-dependencies] namada = {path = "../shared", default-features = false, features = ["testing", "wasm-runtime"]} diff --git a/apps/src/bin/namada-wallet/cli.rs b/apps/src/bin/namada-wallet/cli.rs index 2ec7a3a31c..fb7599ace3 100644 --- a/apps/src/bin/namada-wallet/cli.rs +++ b/apps/src/bin/namada-wallet/cli.rs @@ -8,14 +8,14 @@ use color_eyre::eyre::Result; use itertools::sorted; use masp_primitives::zip32::ExtendedFullViewingKey; use namada::ledger::masp::find_valid_diversifier; -use namada::ledger::wallet::FindKeyError; +use namada::ledger::wallet::{DecryptionError, FindKeyError}; use namada::types::key::*; use namada::types::masp::{MaspValue, PaymentAddress}; use namada_apps::cli; use namada_apps::cli::args::CliToSdk; use namada_apps::cli::{args, cmds, Context}; use namada_apps::wallet::{ - read_and_confirm_pwd, CliWalletUtils, DecryptionError, + read_and_confirm_encryption_password, CliWalletUtils, }; use rand_core::OsRng; @@ -23,6 +23,9 @@ pub fn main() -> Result<()> { let (cmd, mut ctx) = cli::namada_wallet_cli()?; match cmd { cmds::NamadaWallet::Key(sub) => match sub { + cmds::WalletKey::Restore(cmds::KeyRestore(args)) => { + key_and_address_restore(ctx, args) + } cmds::WalletKey::Gen(cmds::KeyGen(args)) => { key_and_address_gen(ctx, args) } @@ -36,6 +39,9 @@ pub fn main() -> Result<()> { cmds::WalletAddress::Gen(cmds::AddressGen(args)) => { key_and_address_gen(ctx, args) } + cmds::WalletAddress::Restore(cmds::AddressRestore(args)) => { + key_and_address_restore(ctx, args) + } cmds::WalletAddress::Find(cmds::AddressOrAliasFind(args)) => { address_or_alias_find(ctx, args) } @@ -205,7 +211,7 @@ fn spending_key_gen( ) { let mut wallet = ctx.wallet; let alias = alias.to_lowercase(); - let password = read_and_confirm_pwd(unsafe_dont_encrypt); + let password = read_and_confirm_encryption_password(unsafe_dont_encrypt); let (alias, _key) = wallet.gen_spending_key(alias, password, alias_force); namada_apps::wallet::save(&wallet) .unwrap_or_else(|err| eprintln!("{}", err)); @@ -273,7 +279,8 @@ fn address_key_add( (alias, "viewing key") } MaspValue::ExtendedSpendingKey(spending_key) => { - let password = read_and_confirm_pwd(unsafe_dont_encrypt); + let password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); let alias = ctx .wallet .encrypt_insert_spending_key( @@ -307,6 +314,41 @@ fn address_key_add( ); } +/// Restore a keypair and an implicit address from the mnemonic code in the +/// wallet. +fn key_and_address_restore( + ctx: Context, + args::KeyAndAddressRestore { + scheme, + alias, + alias_force, + unsafe_dont_encrypt, + derivation_path, + }: args::KeyAndAddressRestore, +) { + let mut wallet = ctx.wallet; + let encryption_password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); + let (alias, _key) = wallet + .derive_key_from_user_mnemonic_code( + scheme, + alias, + alias_force, + derivation_path, + encryption_password, + ) + .unwrap_or_else(|err| { + eprintln!("{}", err); + cli::safe_exit(1) + }); + namada_apps::wallet::save(&wallet) + .unwrap_or_else(|err| eprintln!("{}", err)); + println!( + "Successfully added a key and an address with alias: \"{}\"", + alias + ); +} + /// Generate a new keypair and derive implicit address from it and store them in /// the wallet. fn key_and_address_gen( @@ -316,11 +358,27 @@ fn key_and_address_gen( alias, alias_force, unsafe_dont_encrypt, + derivation_path, }: args::KeyAndAddressGen, ) { let mut wallet = ctx.wallet; - let password = read_and_confirm_pwd(unsafe_dont_encrypt); - let (alias, _key) = wallet.gen_key(scheme, alias, password, alias_force); + let encryption_password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); + let mut rng = OsRng; + let derivation_path_and_mnemonic_rng = + derivation_path.map(|p| (p, &mut rng)); + let (alias, _key) = wallet + .gen_key( + scheme, + alias, + alias_force, + encryption_password, + derivation_path_and_mnemonic_rng, + ) + .unwrap_or_else(|err| { + eprintln!("{}", err); + cli::safe_exit(1); + }); namada_apps::wallet::save(&wallet) .unwrap_or_else(|err| eprintln!("{}", err)); println!( diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index daecbf5eee..392600d303 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -359,6 +359,7 @@ pub mod cmds { #[derive(Clone, Debug)] #[allow(clippy::large_enum_variant)] pub enum WalletKey { + Restore(KeyRestore), Gen(KeyGen), Find(KeyFind), List(KeyList), @@ -371,10 +372,11 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { matches.subcommand_matches(Self::CMD).and_then(|matches| { let generate = SubCmd::parse(matches).map(Self::Gen); + let restore = SubCmd::parse(matches).map(Self::Restore); let lookup = SubCmd::parse(matches).map(Self::Find); let list = SubCmd::parse(matches).map(Self::List); let export = SubCmd::parse(matches).map(Self::Export); - generate.or(lookup).or(list).or(export) + generate.or(restore).or(lookup).or(list).or(export) }) } @@ -385,6 +387,7 @@ pub mod cmds { look-up keys.", ) .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand(KeyRestore::def()) .subcommand(KeyGen::def()) .subcommand(KeyFind::def()) .subcommand(KeyList::def()) @@ -392,6 +395,31 @@ pub mod cmds { } } + /// Restore a keypair and implicit address from the mnemonic code + #[derive(Clone, Debug)] + pub struct KeyRestore(pub args::KeyAndAddressRestore); + + impl SubCmd for KeyRestore { + const CMD: &'static str = "restore"; + + fn parse(matches: &ArgMatches) -> Option { + matches + .subcommand_matches(Self::CMD) + .map(|matches| Self(args::KeyAndAddressRestore::parse(matches))) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Restores a keypair from the given mnemonic code and HD \ + derivation path and derives the implicit address from \ + its public key. Stores the keypair and the address with \ + the given alias.", + ) + .add_args::() + } + } + /// Generate a new keypair and an implicit address derived from it #[derive(Clone, Debug)] pub struct KeyGen(pub args::KeyAndAddressGen); @@ -408,7 +436,7 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about( - "Generates a keypair with a given alias and derive the \ + "Generates a keypair with a given alias and derives the \ implicit address from its public key. The address will \ be stored with the same alias.", ) @@ -640,6 +668,7 @@ pub mod cmds { #[derive(Clone, Debug)] pub enum WalletAddress { Gen(AddressGen), + Restore(AddressRestore), Find(AddressOrAliasFind), List(AddressList), Add(AddressAdd), @@ -651,10 +680,11 @@ pub mod cmds { fn parse(matches: &ArgMatches) -> Option { matches.subcommand_matches(Self::CMD).and_then(|matches| { let gen = SubCmd::parse(matches).map(Self::Gen); + let restore = SubCmd::parse(matches).map(Self::Restore); let find = SubCmd::parse(matches).map(Self::Find); let list = SubCmd::parse(matches).map(Self::List); let add = SubCmd::parse(matches).map(Self::Add); - gen.or(find).or(list).or(add) + gen.or(restore).or(find).or(list).or(add) }) } @@ -666,6 +696,7 @@ pub mod cmds { ) .setting(AppSettings::SubcommandRequiredElseHelp) .subcommand(AddressGen::def()) + .subcommand(AddressRestore::def()) .subcommand(AddressOrAliasFind::def()) .subcommand(AddressList::def()) .subcommand(AddressAdd::def()) @@ -688,7 +719,7 @@ pub mod cmds { fn def() -> App { App::new(Self::CMD) .about( - "Generates a keypair with a given alias and derive the \ + "Generates a keypair with a given alias and derives the \ implicit address from its public key. The address will \ be stored with the same alias.", ) @@ -696,6 +727,30 @@ pub mod cmds { } } + /// Restore a keypair and an implicit address from the mnemonic code + #[derive(Clone, Debug)] + pub struct AddressRestore(pub args::KeyAndAddressRestore); + + impl SubCmd for AddressRestore { + const CMD: &'static str = "restore"; + + fn parse(matches: &ArgMatches) -> Option { + matches.subcommand_matches(Self::CMD).map(|matches| { + AddressRestore(args::KeyAndAddressRestore::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Restores a keypair from the given mnemonic code and \ + derives the implicit address from its public key. Stores \ + the keypair and the address with the given alias.", + ) + .add_args::() + } + } + /// Find an address by its alias #[derive(Clone, Debug)] pub struct AddressOrAliasFind(pub args::AddressOrAliasFind); @@ -1734,6 +1789,9 @@ pub mod args { pub const GENESIS_VALIDATOR: ArgOpt = arg("genesis-validator").opt(); pub const HALT_ACTION: ArgFlag = flag("halt"); + pub const HD_WALLET_DERIVATION_PATH: Arg = arg("hd-path"); + pub const HD_WALLET_DERIVATION_PATH_OPT: ArgOpt = + HD_WALLET_DERIVATION_PATH.opt(); pub const HISTORIC: ArgFlag = flag("historic"); pub const LEDGER_ADDRESS_ABOUT: &str = "Address of a ledger node as \"{scheme}://{host}:{port}\". If the \ @@ -2054,7 +2112,7 @@ pub mod args { sub_prefix: self.sub_prefix, amount: self.amount, native_token: ctx.native_token.clone(), - tx_code_path: ctx.read_wasm(self.tx_code_path), + tx_code_path: self.tx_code_path.to_path_buf(), } } } @@ -2109,7 +2167,7 @@ pub mod args { channel_id: self.channel_id, timeout_height: self.timeout_height, timeout_sec_offset: self.timeout_sec_offset, - tx_code_path: ctx.read_wasm(self.tx_code_path), + tx_code_path: self.tx_code_path.to_path_buf(), } } } @@ -2170,15 +2228,8 @@ pub mod args { TxInitAccount:: { tx: self.tx.to_sdk(ctx), source: ctx.get(&self.source), - vp_code: ctx.read_wasm(self.vp_code), - vp_code_path: self - .vp_code_path - .as_path() - .to_str() - .unwrap() - .to_string() - .into_bytes(), - tx_code_path: ctx.read_wasm(self.tx_code_path), + vp_code_path: self.vp_code_path.to_path_buf(), + tx_code_path: self.tx_code_path.to_path_buf(), public_key: ctx.get_cached(&self.public_key), } } @@ -2191,13 +2242,11 @@ pub mod args { let vp_code_path = CODE_PATH_OPT .parse(matches) .unwrap_or_else(|| PathBuf::from(VP_USER_WASM)); - let vp_code = vp_code_path.clone(); let tx_code_path = PathBuf::from(TX_INIT_ACCOUNT_WASM); let public_key = PUBLIC_KEY.parse(matches); Self { tx, source, - vp_code, vp_code_path, public_key, tx_code_path, @@ -2234,13 +2283,9 @@ pub mod args { max_commission_rate_change: self.max_commission_rate_change, validator_vp_code_path: self .validator_vp_code_path - .as_path() - .to_str() - .unwrap() - .to_string() - .into_bytes(), + .to_path_buf(), unsafe_dont_encrypt: self.unsafe_dont_encrypt, - tx_code_path: ctx.read_wasm(self.tx_code_path), + tx_code_path: self.tx_code_path.to_path_buf(), } } } @@ -2324,20 +2369,8 @@ pub mod args { fn to_sdk(self, ctx: &mut Context) -> TxUpdateVp { TxUpdateVp:: { tx: self.tx.to_sdk(ctx), - vp_code_path: self - .vp_code_path - .as_path() - .to_str() - .unwrap() - .to_string() - .into_bytes(), - tx_code_path: self - .tx_code_path - .as_path() - .to_str() - .unwrap() - .to_string() - .into_bytes(), + vp_code_path: self.vp_code_path, + tx_code_path: self.tx_code_path, addr: ctx.get(&self.addr), } } @@ -2379,7 +2412,7 @@ pub mod args { amount: self.amount, source: self.source.map(|x| ctx.get(&x)), native_token: ctx.native_token.clone(), - tx_code_path: ctx.read_wasm(self.tx_code_path), + tx_code_path: self.tx_code_path.to_path_buf(), } } } @@ -2419,7 +2452,7 @@ pub mod args { validator: ctx.get(&self.validator), amount: self.amount, source: self.source.map(|x| ctx.get(&x)), - tx_code_path: ctx.read_wasm(self.tx_code_path), + tx_code_path: self.tx_code_path.to_path_buf(), } } } @@ -2466,7 +2499,7 @@ pub mod args { /// Native token address pub native_token: C::NativeAddress, /// Path to the TX WASM code file - pub tx_code_path: C::Data, + pub tx_code_path: PathBuf, } impl CliToSdk> for InitProposal { @@ -2476,7 +2509,7 @@ pub mod args { proposal_data: self.proposal_data, offline: self.offline, native_token: ctx.native_token.clone(), - tx_code_path: ctx.read_wasm(self.tx_code_path), + tx_code_path: self.tx_code_path, } } } @@ -2527,7 +2560,7 @@ pub mod args { /// The proposal file path pub proposal_data: Option, /// Path to the TX WASM code file - pub tx_code_path: C::Data, + pub tx_code_path: PathBuf, } impl CliToSdk> for VoteProposal { @@ -2538,7 +2571,7 @@ pub mod args { vote: self.vote, offline: self.offline, proposal_data: self.proposal_data, - tx_code_path: ctx.read_wasm(self.tx_code_path), + tx_code_path: self.tx_code_path.to_path_buf(), proposal_pgf: self.proposal_pgf, proposal_eth: self.proposal_eth, } @@ -2763,7 +2796,7 @@ pub mod args { tx: self.tx.to_sdk(ctx), validator: ctx.get(&self.validator), source: self.source.map(|x| ctx.get(&x)), - tx_code_path: ctx.read_wasm(self.tx_code_path), + tx_code_path: self.tx_code_path.to_path_buf(), } } } @@ -2995,7 +3028,7 @@ pub mod args { tx: self.tx.to_sdk(ctx), validator: ctx.get(&self.validator), rate: self.rate, - tx_code_path: ctx.read_wasm(self.tx_code_path), + tx_code_path: self.tx_code_path.to_path_buf(), } } } @@ -3165,7 +3198,7 @@ pub mod args { gas_limit: self.gas_limit, signing_key: self.signing_key.map(|x| ctx.get_cached(&x)), signer: self.signer.map(|x| ctx.get(&x)), - tx_code_path: ctx.read_wasm(self.tx_code_path), + tx_reveal_code_path: self.tx_reveal_code_path, password: self.password, expiration: self.expiration, chain_id: self.chain_id, @@ -3201,6 +3234,9 @@ pub mod args { initialized, the alias will be the prefix of each new \ address joined with a number.", )) + .arg(WALLET_ALIAS_FORCE.def().about( + "Override the alias without confirmation if it already exists.", + )) .arg(GAS_AMOUNT.def().about( "The amount being paid for the inclusion of this transaction", )) @@ -3251,7 +3287,7 @@ pub mod args { let expiration = EXPIRATION_OPT.parse(matches); let signing_key = SIGNING_KEY_OPT.parse(matches); let signer = SIGNER.parse(matches); - let tx_code_path = PathBuf::from(TX_REVEAL_PK); + let tx_reveal_code_path = PathBuf::from(TX_REVEAL_PK); let chain_id = CHAIN_ID_OPT.parse(matches); let password = None; Self { @@ -3268,7 +3304,7 @@ pub mod args { expiration, signing_key, signer, - tx_code_path, + tx_reveal_code_path, password, chain_id, } @@ -3318,6 +3354,9 @@ pub mod args { .def() .about("An alias to be associated with the new entry."), ) + .arg(ALIAS_FORCE.def().about( + "Override the alias without confirmation if it already exists.", + )) .arg( MASP_VALUE .def() @@ -3386,6 +3425,9 @@ pub mod args { "An alias to be associated with the payment address.", ), ) + .arg(ALIAS_FORCE.def().about( + "Override the alias without confirmation if it already exists.", + )) .arg(VIEWING_KEY.def().about("The viewing key.")) .arg(PIN.def().about( "Require that the single transaction to this address be \ @@ -3394,17 +3436,65 @@ pub mod args { } } + impl Args for KeyAndAddressRestore { + fn parse(matches: &ArgMatches) -> Self { + let scheme = SCHEME.parse(matches); + let alias = ALIAS_OPT.parse(matches); + let alias_force = ALIAS_FORCE.parse(matches); + let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); + let derivation_path = HD_WALLET_DERIVATION_PATH_OPT.parse(matches); + Self { + scheme, + alias, + alias_force, + unsafe_dont_encrypt, + derivation_path, + } + } + + fn def(app: App) -> App { + app.arg(SCHEME.def().about( + "The type of key that should be added. Argument must be \ + either ed25519 or secp256k1. If none provided, the default \ + key scheme is ed25519.", + )) + .arg(ALIAS_OPT.def().about( + "The key and address alias. If none provided, the alias will \ + be the public key hash.", + )) + .arg( + ALIAS_FORCE + .def() + .about("Force overwrite the alias if it already exists."), + ) + .arg(UNSAFE_DONT_ENCRYPT.def().about( + "UNSAFE: Do not encrypt the keypair. Do not use this for keys \ + used in a live network.", + )) + .arg(HD_WALLET_DERIVATION_PATH_OPT.def().about( + "HD key derivation path. Use keyword `default` to refer to a \ + scheme default path:\n- m/44'/60'/0'/0/0 for secp256k1 \ + scheme\n- m/44'/877'/0'/0'/0' for ed25519 scheme.\nFor \ + ed25519, all path indices will be promoted to hardened \ + indexes. If none is specified, the scheme default path is \ + used.", + )) + } + } + impl Args for KeyAndAddressGen { fn parse(matches: &ArgMatches) -> Self { let scheme = SCHEME.parse(matches); let alias = ALIAS_OPT.parse(matches); let alias_force = ALIAS_FORCE.parse(matches); let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches); + let derivation_path = HD_WALLET_DERIVATION_PATH_OPT.parse(matches); Self { scheme, alias, alias_force, unsafe_dont_encrypt, + derivation_path, } } @@ -3418,10 +3508,22 @@ pub mod args { "The key and address alias. If none provided, the alias will \ be the public key hash.", )) + .arg(ALIAS_FORCE.def().about( + "Override the alias without confirmation if it already exists.", + )) .arg(UNSAFE_DONT_ENCRYPT.def().about( "UNSAFE: Do not encrypt the keypair. Do not use this for keys \ used in a live network.", )) + .arg(HD_WALLET_DERIVATION_PATH_OPT.def().about( + "Generate a new key and wallet using BIP39 mnemonic code and \ + HD derivation path. Use keyword `default` to refer to a \ + scheme default path:\n- m/44'/60'/0'/0/0 for secp256k1 \ + scheme\n- m/44'/877'/0'/0'/0' for ed25519 scheme.\nFor \ + ed25519, all path indices will be promoted to hardened \ + indexes. If none specified, mnemonic code and derivation \ + path are not used.", + )) } } @@ -3586,6 +3688,9 @@ pub mod args { .def() .about("An alias to be associated with the address."), ) + .arg(ALIAS_FORCE.def().about( + "Override the alias without confirmation if it already exists.", + )) .arg( RAW_ADDRESS .def() diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index 42c1bb6c94..630d329515 100644 --- a/apps/src/lib/cli/context.rs +++ b/apps/src/lib/cli/context.rs @@ -1,6 +1,6 @@ //! CLI input types can be used for command arguments -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::env; use std::marker::PhantomData; use std::path::{Path, PathBuf}; @@ -194,10 +194,31 @@ impl Context { wasm_loader::read_wasm_or_exit(self.wasm_dir(), file_name) } - /// Get address with vp type + /// Try to find an alias for a given address from the wallet. If not found, + /// formats the address into a string. + pub fn lookup_alias(&self, addr: &Address) -> String { + match self.wallet.find_alias(addr) { + Some(alias) => alias.to_string(), + None => addr.to_string(), + } + } + + /// Get addresses with tokens VP type. pub fn tokens(&self) -> HashSet
{ self.wallet.get_addresses_with_vp_type(AddressVpType::Token) } + + /// Get addresses with tokens VP type associated with their aliases. + pub fn tokens_with_aliases(&self) -> HashMap { + self.wallet + .get_addresses_with_vp_type(AddressVpType::Token) + .into_iter() + .map(|addr| { + let alias = self.lookup_alias(&addr); + (addr, alias) + }) + .collect() + } } /// Load global config from expected path in the `base_dir` or try to generate a diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 109b0ffa2d..d0839fe267 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -16,8 +16,7 @@ use data_encoding::HEXLOWER; use itertools::Either; use masp_primitives::asset_type::AssetType; use masp_primitives::merkle_tree::MerklePath; -use masp_primitives::primitives::ViewingKey; -use masp_primitives::sapling::Node; +use masp_primitives::sapling::{Node, ViewingKey}; use masp_primitives::transaction::components::Amount; use masp_primitives::zip32::ExtendedFullViewingKey; use namada::core::types::transaction::governance::ProposalType; diff --git a/apps/src/lib/client/signing.rs b/apps/src/lib/client/signing.rs index eec89b4f80..c44da88f9b 100644 --- a/apps/src/lib/client/signing.rs +++ b/apps/src/lib/client/signing.rs @@ -5,10 +5,10 @@ use namada::ledger::rpc::TxBroadcastData; use namada::ledger::signing::TxSigningKey; use namada::ledger::tx; use namada::ledger::wallet::{Wallet, WalletUtils}; +use namada::proof_of_stake::Epoch; use namada::proto::Tx; use namada::types::address::Address; use namada::types::key::*; -use namada::types::storage::Epoch; use crate::cli::args; @@ -77,8 +77,12 @@ pub async fn sign_tx< /// Create a wrapper tx from a normal tx. Get the hash of the /// wrapper and its payload which is needed for monitoring its /// progress on chain. -pub async fn sign_wrapper( +pub async fn sign_wrapper< + C: namada::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, + wallet: &mut Wallet, args: &args::Tx, epoch: Epoch, tx: Tx, @@ -87,6 +91,7 @@ pub async fn sign_wrapper( ) -> TxBroadcastData { namada::ledger::signing::sign_wrapper( client, + wallet, args, epoch, tx, diff --git a/apps/src/lib/client/tx.rs b/apps/src/lib/client/tx.rs index ce334adb41..0ce4351967 100644 --- a/apps/src/lib/client/tx.rs +++ b/apps/src/lib/client/tx.rs @@ -7,6 +7,7 @@ use std::path::PathBuf; use async_std::io; use async_std::io::prelude::WriteExt; +use async_trait::async_trait; use borsh::{BorshDeserialize, BorshSerialize}; use data_encoding::HEXLOWER_PERMISSIVE; use masp_proofs::prover::LocalTxProver; @@ -15,19 +16,21 @@ use namada::ledger::rpc::{TxBroadcastData, TxResponse}; use namada::ledger::signing::TxSigningKey; use namada::ledger::wallet::{Wallet, WalletUtils}; use namada::ledger::{masp, tx}; -use namada::proto::Tx; +use namada::proto::{Code, Data, Section, Tx}; use namada::types::address::Address; use namada::types::governance::{ OfflineProposal, OfflineVote, Proposal, ProposalVote, VoteType, }; +use namada::types::hash::Hash; use namada::types::key::*; use namada::types::storage::{Epoch, Key}; use namada::types::token; use namada::types::transaction::governance::{ InitProposalData, ProposalType, VoteProposalData, }; -use namada::types::transaction::InitValidator; +use namada::types::transaction::{InitValidator, TxType}; use rust_decimal::Decimal; +use sha2::{Digest as Sha2Digest, Sha256}; use tendermint_rpc::HttpClient; use super::rpc; @@ -35,9 +38,12 @@ use crate::cli::context::WalletAddress; use crate::cli::{args, safe_exit, Context}; use crate::client::rpc::query_wasm_code_hash; use crate::client::signing::find_keypair; +use crate::client::tx::tx::ProcessTxResponse; use crate::facade::tendermint_rpc::endpoint::broadcast::tx_sync::Response; use crate::node::ledger::tendermint_node; -use crate::wallet::{gen_validator_keys, read_and_confirm_pwd, CliWalletUtils}; +use crate::wallet::{ + gen_validator_keys, read_and_confirm_encryption_password, CliWalletUtils, +}; pub async fn submit_custom( client: &C, @@ -111,14 +117,17 @@ pub async fn submit_init_validator< let consensus_key_alias = format!("{}-consensus-key", alias); let account_key = account_key.unwrap_or_else(|| { println!("Generating validator account key..."); - let password = read_and_confirm_pwd(unsafe_dont_encrypt); + let password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); ctx.wallet .gen_key( scheme, Some(validator_key_alias.clone()), - password, tx_args.wallet_alias_force, + password, + None, ) + .expect("Key generation should not fail.") .1 .ref_to() }); @@ -133,15 +142,18 @@ pub async fn submit_init_validator< }) .unwrap_or_else(|| { println!("Generating consensus key..."); - let password = read_and_confirm_pwd(unsafe_dont_encrypt); + let password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); ctx.wallet .gen_key( // Note that TM only allows ed25519 for consensus key SchemeType::Ed25519, Some(consensus_key_alias.clone()), - password, tx_args.wallet_alias_force, + password, + None, ) + .expect("Key generation should not fail.") .1 }); @@ -160,11 +172,12 @@ pub async fn submit_init_validator< .expect("DKG sessions keys should have been created") .public(); - let vp_code_path = String::from_utf8(validator_vp_code_path).unwrap(); - let validator_vp_code_hash = - query_wasm_code_hash::(client, vp_code_path) - .await - .unwrap(); + let validator_vp_code_hash = query_wasm_code_hash::( + client, + validator_vp_code_path.to_str().unwrap(), + ) + .await + .unwrap(); // Validate the commission rate data if commission_rate > Decimal::ONE || commission_rate < Decimal::ZERO { @@ -192,6 +205,12 @@ pub async fn submit_init_validator< .await .unwrap(); + let mut tx = Tx::new(TxType::Raw); + let extra = tx.add_section(Section::ExtraData(Code::from_hash( + validator_vp_code_hash, + ))); + let extra_hash = + Hash(extra.hash(&mut Sha256::new()).finalize_reset().into()); let data = InitValidator { account_key, consensus_key: consensus_key.ref_to(), @@ -199,15 +218,14 @@ pub async fn submit_init_validator< dkg_key, commission_rate, max_commission_rate_change, - validator_vp_code_hash, + validator_vp_code_hash: extra_hash, }; let data = data.try_to_vec().expect("Encoding tx data shouldn't fail"); - let tx = Tx::new( - tx_code_hash.to_vec(), - Some(data), - tx_args.chain_id.clone().unwrap(), - tx_args.expiration, - ); + tx.header.chain_id = tx_args.chain_id.clone().unwrap(); + tx.header.expiration = tx_args.expiration; + tx.set_data(Data::new(data)); + tx.set_code(Code::from_hash(tx_code_hash)); + let (mut ctx, result) = process_tx( client, ctx, @@ -315,7 +333,7 @@ impl CLIShieldedUtils { && output_path.exists()) { println!("MASP parameters not present, downloading..."); - masp_proofs::download_parameters() + masp_proofs::download_masp_parameters(None) .expect("MASP parameters not present or downloadable"); println!("MASP parameter download complete, resuming execution..."); } @@ -336,6 +354,7 @@ impl Default for CLIShieldedUtils { } } +#[async_trait(?Send)] impl masp::ShieldedUtils for CLIShieldedUtils { type C = tendermint_rpc::HttpClient; @@ -354,7 +373,7 @@ impl masp::ShieldedUtils for CLIShieldedUtils { /// Try to load the last saved shielded context from the given context /// directory. If this fails, then leave the current context unchanged. - fn load(self) -> std::io::Result> { + async fn load(self) -> std::io::Result> { // Try to load shielded context from file let mut ctx_file = File::open(self.context_dir.join(FILE_NAME))?; let mut bytes = Vec::new(); @@ -367,7 +386,10 @@ impl masp::ShieldedUtils for CLIShieldedUtils { } /// Save this shielded context into its associated context directory - fn save(&self, ctx: &masp::ShieldedContext) -> std::io::Result<()> { + async fn save( + &self, + ctx: &masp::ShieldedContext, + ) -> std::io::Result<()> { // TODO: use mktemp crate? let tmp_path = self.context_dir.join(TMP_FILE_NAME); { @@ -434,9 +456,9 @@ pub async fn submit_init_proposal( serde_json::from_reader(file).expect("JSON was not well-formatted"); let signer = WalletAddress::new(proposal.clone().author.to_string()); - let governance_parameters = rpc::get_governance_parameters(client).await; let current_epoch = rpc::query_and_print_epoch(client).await; + let governance_parameters = rpc::get_governance_parameters(client).await; if proposal.voting_start_epoch <= current_epoch || proposal.voting_start_epoch.0 % governance_parameters.min_proposal_period @@ -527,13 +549,10 @@ pub async fn submit_init_proposal( safe_exit(1) }; - let balance = rpc::get_token_balance( - client, - &args.native_token, - &proposal.author, - ) - .await - .unwrap_or_default(); + let balance = + rpc::get_token_balance(client, &ctx.native_token, &proposal.author) + .await + .unwrap_or_default(); if balance < token::Amount::from(governance_parameters.min_proposal_fund) { @@ -551,18 +570,17 @@ pub async fn submit_init_proposal( safe_exit(1); } + let mut tx = Tx::new(TxType::Raw); let data = init_proposal_data .try_to_vec() .expect("Encoding proposal data shouldn't fail"); let tx_code_hash = query_wasm_code_hash(client, args::TX_INIT_PROPOSAL) .await .unwrap(); - let tx = Tx::new( - tx_code_hash.to_vec(), - Some(data), - ctx.config.ledger.chain_id.clone(), - args.tx.expiration, - ); + tx.header.chain_id = ctx.config.ledger.chain_id.clone(); + tx.header.expiration = args.tx.expiration; + tx.set_data(Data::new(data)); + tx.set_code(Code::from_hash(tx_code_hash)); process_tx::( client, @@ -806,8 +824,18 @@ pub async fn submit_vote_proposal( let data = tx_data .try_to_vec() .expect("Encoding proposal data shouldn't fail"); - let tx_code = args.tx_code_path; - let tx = Tx::new(tx_code, Some(data), chain_id, expiration); + + let tx_code_hash = query_wasm_code_hash( + client, + args.tx_code_path.to_str().unwrap(), + ) + .await + .unwrap(); + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = chain_id; + tx.header.expiration = expiration; + tx.set_data(Data::new(data)); + tx.set_code(Code::from_hash(tx_code_hash)); process_tx::( client, @@ -873,7 +901,7 @@ pub async fn submit_reveal_pk_aux( ctx: &mut Context, public_key: &common::PublicKey, args: &args::Tx, -) -> Result<(), tx::Error> { +) -> Result { let args = args::Tx { chain_id: args .clone() @@ -1001,7 +1029,10 @@ async fn process_tx( #[cfg(not(feature = "mainnet"))] requires_pow: bool, ) -> Result<(Context, Vec
), tx::Error> { let args = args::Tx { - chain_id: args.clone().chain_id.or_else(|| Some(tx.chain_id.clone())), + chain_id: args + .clone() + .chain_id + .or_else(|| Some(tx.header.chain_id.clone())), ..args.clone() }; let res: Vec
= tx::process_tx::( @@ -1013,7 +1044,8 @@ async fn process_tx( #[cfg(not(feature = "mainnet"))] requires_pow, ) - .await?; + .await? + .initialized_accounts(); Ok((ctx, res)) } diff --git a/apps/src/lib/client/utils.rs b/apps/src/lib/client/utils.rs index 107c5465dd..cb3f42b377 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -31,7 +31,9 @@ use crate::config::{self, Config, TendermintMode}; use crate::facade::tendermint::node::Id as TendermintNodeId; use crate::facade::tendermint_config::net::Address as TendermintAddress; use crate::node::ledger::tendermint_node; -use crate::wallet::{pre_genesis, read_and_confirm_pwd, CliWalletUtils}; +use crate::wallet::{ + pre_genesis, read_and_confirm_encryption_password, CliWalletUtils, +}; use crate::wasm_loader; pub const NET_ACCOUNTS_DIR: &str = "setup"; @@ -491,7 +493,7 @@ pub fn init_network( config.address = Some(address.to_string()); // Generate the consensus, account and reward keys, unless they're - // pre-defined. + // pre-defined. Do not use mnemonic code / HD derivation path. let mut wallet = crate::wallet::load_or_new(&chain_dir); let consensus_pk = try_parse_public_key( @@ -501,13 +503,11 @@ pub fn init_network( .unwrap_or_else(|| { let alias = format!("{}-consensus-key", name); println!("Generating validator {} consensus key...", name); - let password = read_and_confirm_pwd(unsafe_dont_encrypt); - let (_alias, keypair) = wallet.gen_key( - SchemeType::Ed25519, - Some(alias), - password, - true, - ); + let password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); + let (_alias, keypair) = wallet + .gen_key(SchemeType::Ed25519, Some(alias), true, password, None) + .expect("Key generation should not fail."); // Write consensus key for Tendermint tendermint_node::write_validator_key(&tm_home_dir, &keypair); @@ -522,13 +522,11 @@ pub fn init_network( .unwrap_or_else(|| { let alias = format!("{}-account-key", name); println!("Generating validator {} account key...", name); - let password = read_and_confirm_pwd(unsafe_dont_encrypt); - let (_alias, keypair) = wallet.gen_key( - SchemeType::Ed25519, - Some(alias), - password, - true, - ); + let password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); + let (_alias, keypair) = wallet + .gen_key(SchemeType::Ed25519, Some(alias), true, password, None) + .expect("Key generation should not fail."); keypair.ref_to() }); @@ -539,13 +537,11 @@ pub fn init_network( .unwrap_or_else(|| { let alias = format!("{}-protocol-key", name); println!("Generating validator {} protocol signing key...", name); - let password = read_and_confirm_pwd(unsafe_dont_encrypt); - let (_alias, keypair) = wallet.gen_key( - SchemeType::Ed25519, - Some(alias), - password, - true, - ); + let password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); + let (_alias, keypair) = wallet + .gen_key(SchemeType::Ed25519, Some(alias), true, password, None) + .expect("Key generation should not fail."); keypair.ref_to() }); @@ -593,7 +589,8 @@ pub fn init_network( crate::wallet::save(&wallet).unwrap(); }); - // Create a wallet for all accounts other than validators + // Create a wallet for all accounts other than validators. + // Do not use mnemonic code / HD derivation path. let mut wallet = crate::wallet::load_or_new(&accounts_dir.join(NET_OTHER_ACCOUNTS_DIR)); if let Some(established) = &mut config.established { @@ -625,13 +622,17 @@ pub fn init_network( "Generating implicit account {} key and address ...", name ); - let password = read_and_confirm_pwd(unsafe_dont_encrypt); - let (_alias, keypair) = wallet.gen_key( - SchemeType::Ed25519, - Some(name.clone()), - password, - true, - ); + let password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); + let (_alias, keypair) = wallet + .gen_key( + SchemeType::Ed25519, + Some(name.clone()), + true, + password, + None, + ) + .expect("Key generation should not fail."); let public_key = genesis_config::HexString(keypair.ref_to().to_string()); config.public_key = Some(public_key); @@ -877,13 +878,17 @@ fn init_established_account( } if config.public_key.is_none() { println!("Generating established account {} key...", name.as_ref()); - let password = read_and_confirm_pwd(unsafe_dont_encrypt); - let (_alias, keypair) = wallet.gen_key( - SchemeType::Ed25519, - Some(format!("{}-key", name.as_ref())), - password, - true, - ); + let password = + read_and_confirm_encryption_password(unsafe_dont_encrypt); + let (_alias, keypair) = wallet + .gen_key( + SchemeType::Ed25519, + Some(format!("{}-key", name.as_ref())), + true, + password, + None, // do not use mnemonic code / HD derivation path + ) + .expect("Key generation should not fail."); let public_key = genesis_config::HexString(keypair.ref_to().to_string()); config.public_key = Some(public_key); diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 84a8bd6efd..3b22975d1e 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -127,26 +127,19 @@ where if ErrorCodes::from_u32(processed_tx.result.code).unwrap() == ErrorCodes::InvalidSig { - let mut tx_event = match process_tx(tx.clone()) { - Ok(tx @ TxType::Wrapper(_)) - | Ok(tx @ TxType::Protocol(_)) => { + let mut tx_event = match tx.header().tx_type { + TxType::Wrapper(_) | TxType::Protocol(_) => { Event::new_tx_event(&tx, height.0) } - _ => match TxType::try_from(tx) { - Ok(tx @ TxType::Wrapper(_)) - | Ok(tx @ TxType::Protocol(_)) => { - Event::new_tx_event(&tx, height.0) - } - _ => { - tracing::error!( - "Internal logic error: FinalizeBlock received \ - a tx with an invalid signature error code \ - that could not be deserialized to a \ - WrapperTx / ProtocolTx type" - ); - continue; - } - }, + _ => { + tracing::error!( + "Internal logic error: FinalizeBlock received a \ + tx with an invalid signature error code that \ + could not be deserialized to a WrapperTx / \ + ProtocolTx type" + ); + continue; + } }; tx_event["code"] = processed_tx.result.code.to_string(); tx_event["info"] = @@ -156,8 +149,8 @@ where continue; } - let tx_type = if let Ok(tx_type) = process_tx(tx) { - tx_type + let tx = if let Ok(()) = tx.validate_header() { + tx } else { tracing::error!( "Internal logic error: FinalizeBlock received tx that \ @@ -165,12 +158,13 @@ where ); continue; }; + let tx_type = tx.header(); // If [`process_proposal`] rejected a Tx, emit an event here and // move on to next tx if ErrorCodes::from_u32(processed_tx.result.code).unwrap() != ErrorCodes::Ok { - let mut tx_event = Event::new_tx_event(&tx_type, height.0); + let mut tx_event = Event::new_tx_event(&tx, height.0); tx_event["code"] = processed_tx.result.code.to_string(); tx_event["info"] = format!("Tx rejected: {}", &processed_tx.result.info); @@ -179,7 +173,7 @@ where // if the rejected tx was decrypted, remove it // from the queue of txs to be processed and remove the hash // from storage - if let TxType::Decrypted(_) = &tx_type { + if let TxType::Decrypted(_) = &tx_type.tx_type { let tx_hash = self .wl_storage .storage @@ -187,7 +181,9 @@ where .pop() .expect("Missing wrapper tx in queue") .tx - .tx_hash; + .clone() + .update_header(TxType::Raw) + .header_hash(); let tx_hash_key = replay_protection::get_tx_hash_key(&tx_hash); self.wl_storage @@ -198,24 +194,26 @@ where continue; } - let (mut tx_event, tx_unsigned_hash) = match &tx_type { + let (mut tx_event, tx_unsigned_hash) = match &tx_type.tx_type { TxType::Wrapper(wrapper) => { stats.increment_wrapper_txs(); - let mut tx_event = Event::new_tx_event(&tx_type, height.0); + let mut tx_event = Event::new_tx_event(&tx, height.0); // Writes both txs hash to storage - let tx = Tx::try_from(processed_tx.tx.as_ref()).unwrap(); + let processed_tx = + Tx::try_from(processed_tx.tx.as_ref()).unwrap(); let wrapper_tx_hash_key = replay_protection::get_tx_hash_key(&hash::Hash( - tx.unsigned_hash(), + processed_tx.header_hash().0, )); self.wl_storage .storage .write(&wrapper_tx_hash_key, vec![]) .expect("Error while writing tx hash to storage"); - let inner_tx_hash_key = - replay_protection::get_tx_hash_key(&wrapper.tx_hash); + let inner_tx_hash_key = replay_protection::get_tx_hash_key( + &tx.clone().update_header(TxType::Raw).header_hash(), + ); self.wl_storage .storage .write(&inner_tx_hash_key, vec![]) @@ -276,8 +274,8 @@ where } } - self.wl_storage.storage.tx_queue.push(WrapperTxInQueue { - tx: wrapper.clone(), + self.wl_storage.storage.tx_queue.push(TxInQueue { + tx: processed_tx.clone(), #[cfg(not(feature = "mainnet"))] has_valid_pow, }); @@ -292,20 +290,23 @@ where .pop() .expect("Missing wrapper tx in queue") .tx - .tx_hash; - let mut event = Event::new_tx_event(&tx_type, height.0); + .clone() + .update_header(TxType::Raw) + .header_hash(); + let mut event = Event::new_tx_event(&tx, height.0); match inner { - DecryptedTx::Decrypted { - tx, - has_valid_pow: _, - } => { - stats.increment_tx_type( - namada::core::types::hash::Hash(tx.code_hash()) - .to_string(), - ); + DecryptedTx::Decrypted { has_valid_pow: _ } => { + if let Some(code_sec) = tx + .get_section(tx.code_sechash()) + .and_then(Section::code_sec) + { + stats.increment_tx_type( + code_sec.code.hash().to_string(), + ); + } } - DecryptedTx::Undecryptable(_) => { + DecryptedTx::Undecryptable => { event["log"] = "Transaction could not be decrypted.".into(); event["code"] = ErrorCodes::Undecryptable.into(); @@ -313,7 +314,7 @@ where } (event, Some(wrapper_hash)) } - TxType::Raw(_) => { + TxType::Raw => { tracing::error!( "Internal logic error: FinalizeBlock received a \ TxType::Raw transaction" @@ -330,7 +331,7 @@ where }; match protocol::apply_tx( - tx_type, + tx.clone(), tx_length, TxIndex( tx_index @@ -418,8 +419,8 @@ where .storage .delete(&tx_hash_key) .expect( - "Error while deleting tx hash key from storage", - ); + "Error while deleting tx hash key from storage", + ); } } @@ -898,6 +899,7 @@ mod test_finalize_block { rewards_accumulator_handle, validator_consensus_key_handle, validator_rewards_products_handle, }; + use namada::proto::{Code, Data, Section, Signature}; use namada::types::governance::ProposalVote; use namada::types::key::tm_consensus_key_raw_hash; use namada::types::storage::Epoch; @@ -905,7 +907,7 @@ mod test_finalize_block { use namada::types::transaction::governance::{ InitProposalData, ProposalType, VoteProposalData, }; - use namada::types::transaction::{EncryptionKey, Fee, WrapperTx, MIN_FEE}; + use namada::types::transaction::{Fee, WrapperTx, MIN_FEE}; use namada_test_utils::TestWasms; use rust_decimal_macros::dec; use test_log::test; @@ -940,31 +942,31 @@ mod test_finalize_block { // create some wrapper txs for i in 1u64..5 { - let raw_tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some(format!("transaction data: {}", i).as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( - Fee { - amount: MIN_FEE.into(), - token: shell.wl_storage.storage.native_token.clone(), - }, + let mut wrapper = + Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: MIN_FEE.into(), + token: shell.wl_storage.storage.native_token.clone(), + }, + &keypair, + Epoch(0), + 0.into(), + #[cfg(not(feature = "mainnet"))] + None, + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_data(Data::new("wasm_code".as_bytes().to_owned())); + wrapper.set_code(Code::new( + format!("transaction data: {}", i).as_bytes().to_owned(), + )); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), &keypair, - Epoch(0), - 0.into(), - raw_tx.clone(), - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - ); - let tx = wrapper - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Test failed"); + ))); + wrapper.encrypt(&Default::default()); if i > 1 { processed_txs.push(ProcessedTx { - tx: tx.to_bytes(), + tx: wrapper.to_bytes(), result: TxResult { code: u32::try_from(i.rem_euclid(2)) .expect("Test failed"), @@ -1000,10 +1002,9 @@ mod test_finalize_block { for wrapper in shell.iter_tx_queue() { // we cannot easily implement the PartialEq trait for WrapperTx // so we check the hashes of the inner txs for equality - assert_eq!( - wrapper.tx.tx_hash, - valid_tx.next().expect("Test failed").tx_hash - ); + let valid_tx = valid_tx.next().expect("Test failed"); + assert_eq!(wrapper.tx.header.code_hash, *valid_tx.code_sechash()); + assert_eq!(wrapper.tx.header.data_hash, *valid_tx.data_sechash()); counter += 1; } assert_eq!(counter, 3); @@ -1017,13 +1018,7 @@ mod test_finalize_block { fn test_process_proposal_rejected_decrypted_tx() { let (mut shell, _) = setup(1); let keypair = gen_keypair(); - let raw_tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some(String::from("transaction data").as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( + let mut outer_tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1031,25 +1026,28 @@ mod test_finalize_block { &keypair, Epoch(0), 0.into(), - raw_tx.clone(), - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - + )))); + outer_tx.header.chain_id = shell.chain_id.clone(); + outer_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + outer_tx.set_data(Data::new( + String::from("transaction data").as_bytes().to_owned(), + )); + outer_tx.encrypt(&Default::default()); + shell.enqueue_tx(outer_tx.clone()); + + outer_tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + has_valid_pow: false, + })); let processed_tx = ProcessedTx { - tx: Tx::from(TxType::Decrypted(DecryptedTx::Decrypted { - tx: raw_tx, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: false, - })) - .to_bytes(), + tx: outer_tx.to_bytes(), result: TxResult { code: ErrorCodes::InvalidTx.into(), info: "".into(), }, }; - shell.enqueue_tx(wrapper); // check that the decrypted tx was not applied for event in shell @@ -1074,13 +1072,7 @@ mod test_finalize_block { let (mut shell, _) = setup(1); let keypair = crate::wallet::defaults::daewon_keypair(); - let pubkey = EncryptionKey::default(); // not valid tx bytes - let tx = "garbage data".as_bytes().to_owned(); - let inner_tx = - namada::types::transaction::encrypted::EncryptedTx::encrypt( - &tx, pubkey, - ); let wrapper = WrapperTx { fee: Fee { amount: 0.into(), @@ -1089,23 +1081,20 @@ mod test_finalize_block { pk: keypair.ref_to(), epoch: Epoch(0), gas_limit: 0.into(), - inner_tx, - tx_hash: hash_tx(&tx), #[cfg(not(feature = "mainnet"))] pow_solution: None, }; let processed_tx = ProcessedTx { - tx: Tx::from(TxType::Decrypted(DecryptedTx::Undecryptable( - wrapper.clone(), - ))) - .to_bytes(), + tx: Tx::new(TxType::Decrypted(DecryptedTx::Undecryptable)) + .to_bytes(), result: TxResult { code: ErrorCodes::Ok.into(), info: "".into(), }, }; - shell.enqueue_tx(wrapper); + let tx = Tx::new(TxType::Wrapper(Box::new(wrapper))); + shell.enqueue_tx(tx); // check that correct error message is returned for event in shell @@ -1149,37 +1138,35 @@ mod test_finalize_block { // create two decrypted txs let tx_code = TestWasms::TxNoOp.read_bytes(); for i in 0..2 { - let raw_tx = Tx::new( - tx_code.clone(), - Some( - format!("Decrypted transaction data: {}", i) - .as_bytes() - .to_owned(), - ), - shell.chain_id.clone(), - None, - ); - let wrapper_tx = WrapperTx::new( - Fee { - amount: MIN_FEE.into(), - token: shell.wl_storage.storage.native_token.clone(), - }, - &keypair, - Epoch(0), - 0.into(), - raw_tx.clone(), - Default::default(), + let mut outer_tx = + Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: MIN_FEE.into(), + token: shell.wl_storage.storage.native_token.clone(), + }, + &keypair, + Epoch(0), + 0.into(), + #[cfg(not(feature = "mainnet"))] + None, + )))); + outer_tx.header.chain_id = shell.chain_id.clone(); + outer_tx.set_code(Code::new(tx_code.clone())); + outer_tx.set_data(Data::new( + format!("Decrypted transaction data: {}", i) + .as_bytes() + .to_owned(), + )); + outer_tx.encrypt(&Default::default()); + shell.enqueue_tx(outer_tx.clone()); + outer_tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { #[cfg(not(feature = "mainnet"))] - None, - ); - shell.enqueue_tx(wrapper_tx); + has_valid_pow: false, + })); + outer_tx.decrypt(::G2Affine::prime_subgroup_generator()) + .expect("Test failed"); processed_txs.push(ProcessedTx { - tx: Tx::from(TxType::Decrypted(DecryptedTx::Decrypted { - tx: raw_tx, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: false, - })) - .to_bytes(), + tx: outer_tx.to_bytes(), result: TxResult { code: ErrorCodes::Ok.into(), info: "".into(), @@ -1188,35 +1175,33 @@ mod test_finalize_block { } // create two wrapper txs for i in 0..2 { - let raw_tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some( - format!("Encrypted transaction data: {}", i) - .as_bytes() - .to_owned(), - ), - shell.chain_id.clone(), - None, - ); - let wrapper_tx = WrapperTx::new( - Fee { - amount: MIN_FEE.into(), - token: shell.wl_storage.storage.native_token.clone(), - }, + let mut wrapper_tx = + Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: MIN_FEE.into(), + token: shell.wl_storage.storage.native_token.clone(), + }, + &keypair, + Epoch(0), + 0.into(), + #[cfg(not(feature = "mainnet"))] + None, + )))); + wrapper_tx.header.chain_id = shell.chain_id.clone(); + wrapper_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper_tx.set_data(Data::new( + format!("Encrypted transaction data: {}", i) + .as_bytes() + .to_owned(), + )); + wrapper_tx.add_section(Section::Signature(Signature::new( + &wrapper_tx.header_hash(), &keypair, - Epoch(0), - 0.into(), - raw_tx.clone(), - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - ); - let wrapper = wrapper_tx - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Test failed"); - valid_txs.push(wrapper_tx); + ))); + wrapper_tx.encrypt(&Default::default()); + valid_txs.push(wrapper_tx.clone()); processed_txs.push(ProcessedTx { - tx: wrapper.to_bytes(), + tx: wrapper_tx.to_bytes(), result: TxResult { code: ErrorCodes::Ok.into(), info: "".into(), @@ -1262,10 +1247,9 @@ mod test_finalize_block { let mut counter = 0; for wrapper in shell.iter_tx_queue() { - assert_eq!( - wrapper.tx.tx_hash, - txs.next().expect("Test failed").tx_hash - ); + let next = txs.next().expect("Test failed"); + assert_eq!(wrapper.tx.header.code_hash, *next.code_sechash()); + assert_eq!(wrapper.tx.header.data_hash, *next.data_sechash()); counter += 1; } assert_eq!(counter, 2); @@ -1701,29 +1685,35 @@ mod test_finalize_block { wasm_path.push("wasm_for_tests/tx_no_op.wasm"); let tx_code = std::fs::read(wasm_path) .expect("Expected a file at given code path"); - let raw_tx = Tx::new( - tx_code, - Some("Encrypted transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper_tx = WrapperTx::new( - Fee { - amount: 0.into(), - token: shell.wl_storage.storage.native_token.clone(), - }, - &keypair, - Epoch(0), - 0.into(), - raw_tx.clone(), - Default::default(), + let mut wrapper_tx = + Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: 0.into(), + token: shell.wl_storage.storage.native_token.clone(), + }, + &keypair, + Epoch(0), + 0.into(), + #[cfg(not(feature = "mainnet"))] + None, + )))); + wrapper_tx.header.chain_id = shell.chain_id.clone(); + wrapper_tx.set_code(Code::new(tx_code)); + wrapper_tx.set_data(Data::new( + "Encrypted transaction data".as_bytes().to_owned(), + )); + let mut decrypted_tx = wrapper_tx.clone(); + wrapper_tx.encrypt(&Default::default()); + + decrypted_tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { #[cfg(not(feature = "mainnet"))] - None, - ); + has_valid_pow: false, + })); // Write inner hash in storage - let inner_hash_key = - replay_protection::get_tx_hash_key(&wrapper_tx.tx_hash); + let inner_hash_key = replay_protection::get_tx_hash_key( + &wrapper_tx.clone().update_header(TxType::Raw).header_hash(), + ); shell .wl_storage .storage @@ -1731,12 +1721,7 @@ mod test_finalize_block { .expect("Test failed"); let processed_tx = ProcessedTx { - tx: Tx::from(TxType::Decrypted(DecryptedTx::Decrypted { - tx: raw_tx, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: false, - })) - .to_bytes(), + tx: decrypted_tx.to_bytes(), result: TxResult { code: ErrorCodes::Ok.into(), info: "".into(), diff --git a/apps/src/lib/node/ledger/shell/governance.rs b/apps/src/lib/node/ledger/shell/governance.rs index dfdae4d04e..9ff5ebc279 100644 --- a/apps/src/lib/node/ledger/shell/governance.rs +++ b/apps/src/lib/node/ledger/shell/governance.rs @@ -12,6 +12,7 @@ use namada::ledger::storage::types::encode; use namada::ledger::storage::{DBIter, StorageHasher, DB}; use namada::ledger::storage_api::{token, StorageWrite}; use namada::proof_of_stake::read_total_stake; +use namada::proto::{Code, Data}; use namada::types::address::Address; use namada::types::governance::{Council, Tally, TallyResult, VotePower}; use namada::types::storage::Epoch; @@ -147,17 +148,13 @@ where let proposal_code = shell.read_storage_key_bytes(&proposal_code_key); match proposal_code { Some(proposal_code) => { - let tx = Tx::new( - proposal_code, - Some(encode(&id)), - shell.chain_id.clone(), - None, - ); - let tx_type = TxType::Decrypted(DecryptedTx::Decrypted { - tx, + let mut tx = Tx::new(TxType::Decrypted(DecryptedTx::Decrypted { #[cfg(not(feature = "mainnet"))] has_valid_pow: false, - }); + })); + tx.header.chain_id = shell.chain_id.clone(); + tx.set_data(Data::new(encode(&id))); + tx.set_code(Code::new(proposal_code)); let pending_execution_key = gov_storage::get_proposal_execution_key(id); shell @@ -165,7 +162,7 @@ where .write(&pending_execution_key, ()) .expect("Should be able to write to storage."); let tx_result = protocol::apply_tx( - tx_type, + tx, 0, /* this is used to compute the fee * based on the code size. We dont * need it here. */ diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index acf2e103a0..144be51299 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -22,7 +22,6 @@ use std::path::{Path, PathBuf}; use std::rc::Rc; use borsh::{BorshDeserialize, BorshSerialize}; -use namada::core::types::hash::Hash; use namada::ledger::events::log::EventLog; use namada::ledger::events::Event; use namada::ledger::gas::BlockGasMeter; @@ -36,10 +35,10 @@ use namada::ledger::storage::{ use namada::ledger::storage_api::{self, StorageRead, StorageWrite}; use namada::ledger::{ibc, pos, protocol, replay_protection}; use namada::proof_of_stake::{self, read_pos_params, slash}; -use namada::proto::{self, Tx}; +use namada::proto::{self, Section, Tx}; use namada::types::address::{masp, masp_tx_key, Address}; use namada::types::chain::ChainId; -use namada::types::internal::WrapperTxInQueue; +use namada::types::internal::TxInQueue; use namada::types::key::*; use namada::types::storage::{BlockHeight, Key, TxIndex}; use namada::types::time::{DateTimeUtc, TimeZone, Utc}; @@ -47,8 +46,8 @@ use namada::types::token::{self}; #[cfg(not(feature = "mainnet"))] use namada::types::transaction::MIN_FEE; use namada::types::transaction::{ - hash_tx, process_tx, verify_decrypted_correctly, AffineCurve, DecryptedTx, - EllipticCurve, PairingEngine, TxType, WrapperTx, + hash_tx, verify_decrypted_correctly, AffineCurve, DecryptedTx, + EllipticCurve, PairingEngine, TxType, }; use namada::types::{address, hash}; use namada::vm::wasm::{TxCache, VpCache}; @@ -414,7 +413,7 @@ where /// Iterate over the wrapper txs in order #[allow(dead_code)] - fn iter_tx_queue(&mut self) -> impl Iterator { + fn iter_tx_queue(&mut self) -> impl Iterator { self.wl_storage.storage.tx_queue.iter() } @@ -656,19 +655,20 @@ where /// block construction and validation pub fn replay_protection_checks( &self, - wrapper: &WrapperTx, + wrapper: &Tx, tx_bytes: &[u8], temp_wl_storage: &mut TempWlStorage, ) -> Result<()> { - let inner_hash_key = - replay_protection::get_tx_hash_key(&wrapper.tx_hash); + let inner_tx_hash = + wrapper.clone().update_header(TxType::Raw).header_hash(); + let inner_hash_key = replay_protection::get_tx_hash_key(&inner_tx_hash); if temp_wl_storage .has_key(&inner_hash_key) .expect("Error while checking inner tx hash key in storage") { return Err(Error::ReplayAttempt(format!( "Inner transaction hash {} already in storage", - &wrapper.tx_hash + &inner_tx_hash, ))); } @@ -679,7 +679,7 @@ where let tx = Tx::try_from(tx_bytes).expect("Deserialization shouldn't fail"); - let wrapper_hash = Hash(tx.unsigned_hash()); + let wrapper_hash = tx.header_hash(); let wrapper_hash_key = replay_protection::get_tx_hash_key(&wrapper_hash); if temp_wl_storage @@ -728,17 +728,17 @@ where }; // Tx chain id - if tx.chain_id != self.chain_id { + if tx.header.chain_id != self.chain_id { response.code = ErrorCodes::InvalidChainId.into(); response.log = format!( "Tx carries a wrong chain id: expected {}, found {}", - self.chain_id, tx.chain_id + self.chain_id, tx.header.chain_id ); return response; } // Tx expiration - if let Some(exp) = tx.expiration { + if let Some(exp) = tx.header.expiration { let last_block_timestamp = self.get_block_timestamp(None); if last_block_timestamp > exp { @@ -752,8 +752,8 @@ where } // Tx signature check - let tx_type = match process_tx(tx) { - Ok(ty) => ty, + let tx_type = match tx.validate_header() { + Ok(()) => tx.header(), Err(msg) => { response.code = ErrorCodes::InvalidSig.into(); response.log = msg.to_string(); @@ -762,10 +762,13 @@ where }; // Tx type check - if let TxType::Wrapper(wrapper) = tx_type { + if let TxType::Wrapper(wrapper) = tx_type.tx_type { // Replay protection check + let mut inner_tx = tx; + inner_tx.update_header(TxType::Raw); + let inner_tx_hash = &inner_tx.header_hash(); let inner_hash_key = - replay_protection::get_tx_hash_key(&wrapper.tx_hash); + replay_protection::get_tx_hash_key(inner_tx_hash); if self .wl_storage .storage @@ -777,14 +780,14 @@ where response.log = format!( "Inner transaction hash {} already in storage, replay \ attempt", - wrapper.tx_hash + inner_tx_hash ); return response; } let tx = Tx::try_from(tx_bytes).expect("Deserialization shouldn't fail"); - let wrapper_hash = hash::Hash(tx.unsigned_hash()); + let wrapper_hash = hash::Hash(tx.header_hash().0); let wrapper_hash_key = replay_protection::get_tx_hash_key(&wrapper_hash); if self @@ -848,13 +851,6 @@ where let mut tx_wasm_cache = self.tx_wasm_cache.read_only(); match Tx::try_from(tx_bytes) { Ok(tx) => { - let tx = TxType::Decrypted(DecryptedTx::Decrypted { - tx, - #[cfg(not(feature = "mainnet"))] - // To be able to dry-run testnet faucet withdrawal, pretend - // that we got a valid PoW - has_valid_pow: true, - }); match protocol::apply_tx( tx, tx_bytes.len(), @@ -985,6 +981,7 @@ mod test_utils { use namada::ledger::storage::mockdb::MockDB; use namada::ledger::storage::{update_allowed_conversions, Sha256Hasher}; + use namada::proto::{Code, Data}; use namada::types::chain::ChainId; use namada::types::hash::Hash; use namada::types::key::*; @@ -1141,16 +1138,12 @@ mod test_utils { /// Add a wrapper tx to the queue of txs to be decrypted /// in the current block proposal #[cfg(test)] - pub fn enqueue_tx(&mut self, wrapper: WrapperTx) { - self.shell - .wl_storage - .storage - .tx_queue - .push(WrapperTxInQueue { - tx: wrapper, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: false, - }); + pub fn enqueue_tx(&mut self, tx: Tx) { + self.shell.wl_storage.storage.tx_queue.push(TxInQueue { + tx, + #[cfg(not(feature = "mainnet"))] + has_valid_pow: false, + }); } } @@ -1224,13 +1217,7 @@ mod test_utils { .expect("begin_block failed"); let keypair = gen_keypair(); // enqueue a wrapper tx - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: native_token, @@ -1238,12 +1225,15 @@ mod test_utils { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - shell.wl_storage.storage.tx_queue.push(WrapperTxInQueue { + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.encrypt(&Default::default()); + + shell.wl_storage.storage.tx_queue.push(TxInQueue { tx: wrapper, #[cfg(not(feature = "mainnet"))] has_valid_pow: false, @@ -1281,7 +1271,7 @@ mod test_utils { #[cfg(test)] mod test_mempool_validate { use namada::proof_of_stake::Epoch; - use namada::proto::SignedTxData; + use namada::proto::{Code, Data, Section, Signature, Tx}; use namada::types::transaction::{Fee, WrapperTx}; use super::test_utils::TestShell; @@ -1294,41 +1284,23 @@ mod test_mempool_validate { let keypair = super::test_utils::gen_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - - let mut wrapper = WrapperTx::new( - Fee { - amount: 100.into(), - token: shell.wl_storage.storage.native_token.clone(), - }, - &keypair, - Epoch(0), - 0.into(), - tx, - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - ) - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Wrapper signing failed"); - - let unsigned_wrapper = if let Some(Ok(SignedTxData { - data: Some(data), - sig: _, - })) = wrapper - .data - .take() - .map(|data| SignedTxData::try_from_slice(&data[..])) - { - Tx::new(vec![], Some(data), shell.chain_id.clone(), None) - } else { - panic!("Test failed") - }; + let mut unsigned_wrapper = + Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: 100.into(), + token: shell.wl_storage.storage.native_token.clone(), + }, + &keypair, + Epoch(0), + 0.into(), + #[cfg(not(feature = "mainnet"))] + None, + )))); + unsigned_wrapper.header.chain_id = shell.chain_id.clone(); + unsigned_wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + unsigned_wrapper + .set_data(Data::new("transaction data".as_bytes().to_owned())); + unsigned_wrapper.encrypt(&Default::default()); let mut result = shell.mempool_validate( unsigned_wrapper.to_bytes().as_ref(), @@ -1349,67 +1321,33 @@ mod test_mempool_validate { let keypair = super::test_utils::gen_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - - let mut wrapper = WrapperTx::new( - Fee { - amount: 100.into(), - token: shell.wl_storage.storage.native_token.clone(), - }, + let mut invalid_wrapper = + Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: 100.into(), + token: shell.wl_storage.storage.native_token.clone(), + }, + &keypair, + Epoch(0), + 0.into(), + #[cfg(not(feature = "mainnet"))] + None, + )))); + invalid_wrapper.header.chain_id = shell.chain_id.clone(); + invalid_wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + invalid_wrapper + .set_data(Data::new("transaction data".as_bytes().to_owned())); + invalid_wrapper.add_section(Section::Signature(Signature::new( + &invalid_wrapper.header_hash(), &keypair, - Epoch(0), - 0.into(), - tx, - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - ) - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Wrapper signing failed"); - - let invalid_wrapper = if let Some(Ok(SignedTxData { - data: Some(data), - sig, - })) = wrapper - .data - .take() - .map(|data| SignedTxData::try_from_slice(&data[..])) - { - let mut new_wrapper = if let TxType::Wrapper(wrapper) = - ::deserialize(&mut data.as_ref()) - .expect("Test failed") - { - wrapper - } else { - panic!("Test failed") - }; + ))); + invalid_wrapper.encrypt(&Default::default()); - // we mount a malleability attack to try and remove the fee - new_wrapper.fee.amount = 0.into(); - let new_data = TxType::Wrapper(new_wrapper) - .try_to_vec() - .expect("Test failed"); - Tx::new( - vec![], - Some( - SignedTxData { - sig, - data: Some(new_data), - } - .try_to_vec() - .expect("Test failed"), - ), - shell.chain_id.clone(), - None, - ) - } else { - panic!("Test failed"); - }; + // we mount a malleability attack to try and remove the fee + let mut new_wrapper = + invalid_wrapper.header().wrapper().expect("Test failed"); + new_wrapper.fee.amount = 0.into(); + invalid_wrapper.update_header(TxType::Wrapper(Box::new(new_wrapper))); let mut result = shell.mempool_validate( invalid_wrapper.to_bytes().as_ref(), @@ -1429,12 +1367,9 @@ mod test_mempool_validate { let (shell, _) = TestShell::new(); // Test Raw TxType - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - None, - shell.chain_id.clone(), - None, - ); + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = shell.chain_id.clone(); + tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); let result = shell.mempool_validate( tx.to_bytes().as_ref(), @@ -1452,14 +1387,7 @@ mod test_mempool_validate { let keypair = super::test_utils::gen_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 100.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1467,27 +1395,26 @@ mod test_mempool_validate { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ) - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Wrapper signing failed"); - - let tx_type = match process_tx(wrapper.clone()).expect("Test failed") { - TxType::Wrapper(t) => t, - _ => panic!("Test failed"), - }; + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &keypair, + ))); + wrapper.encrypt(&Default::default()); // Write wrapper hash to storage - let wrapper_hash = hash::Hash(wrapper.unsigned_hash()); + let wrapper_hash = wrapper.header_hash(); let wrapper_hash_key = replay_protection::get_tx_hash_key(&wrapper_hash); shell .wl_storage .storage - .write(&wrapper_hash_key, &wrapper_hash) + .write(&wrapper_hash_key, wrapper_hash) .expect("Test failed"); // Try wrapper tx replay attack @@ -1519,13 +1446,14 @@ mod test_mempool_validate { ) ); + let inner_tx_hash = + wrapper.clone().update_header(TxType::Raw).header_hash(); // Write inner hash in storage - let inner_hash_key = - replay_protection::get_tx_hash_key(&tx_type.tx_hash); + let inner_hash_key = replay_protection::get_tx_hash_key(&inner_tx_hash); shell .wl_storage .storage - .write(&inner_hash_key, &tx_type.tx_hash) + .write(&inner_hash_key, inner_tx_hash) .expect("Test failed"); // Try inner tx replay attack @@ -1538,7 +1466,7 @@ mod test_mempool_validate { result.log, format!( "Inner transaction hash {} already in storage, replay attempt", - tx_type.tx_hash + inner_tx_hash ) ); @@ -1551,7 +1479,7 @@ mod test_mempool_validate { result.log, format!( "Inner transaction hash {} already in storage, replay attempt", - tx_type.tx_hash + inner_tx_hash ) ) } @@ -1564,13 +1492,14 @@ mod test_mempool_validate { let keypair = super::test_utils::gen_keypair(); let wrong_chain_id = ChainId("Wrong chain id".to_string()); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - wrong_chain_id.clone(), - None, - ) - .sign(&keypair); + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = wrong_chain_id.clone(); + tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + tx.add_section(Section::Signature(Signature::new( + &tx.header_hash(), + &keypair, + ))); let result = shell.mempool_validate( tx.to_bytes().as_ref(), @@ -1593,13 +1522,15 @@ mod test_mempool_validate { let keypair = super::test_utils::gen_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - Some(DateTimeUtc::now()), - ) - .sign(&keypair); + let mut tx = Tx::new(TxType::Raw); + tx.header.expiration = Some(DateTimeUtc::now()); + tx.header.chain_id = shell.chain_id.clone(); + tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + tx.add_section(Section::Signature(Signature::new( + &tx.header_hash(), + &keypair, + ))); let result = shell.mempool_validate( tx.to_bytes().as_ref(), diff --git a/apps/src/lib/node/ledger/shell/prepare_proposal.rs b/apps/src/lib/node/ledger/shell/prepare_proposal.rs index 41d05fea5d..7d126a5218 100644 --- a/apps/src/lib/node/ledger/shell/prepare_proposal.rs +++ b/apps/src/lib/node/ledger/shell/prepare_proposal.rs @@ -4,11 +4,12 @@ use namada::core::hints; use namada::ledger::storage::{DBIter, StorageHasher, TempWlStorage, DB}; use namada::proof_of_stake::pos_queries::PosQueries; use namada::proto::Tx; -use namada::types::internal::WrapperTxInQueue; +use namada::types::internal::TxInQueue; use namada::types::time::DateTimeUtc; -use namada::types::transaction::tx_types::TxType; use namada::types::transaction::wrapper::wrapper_tx::PairingEngine; -use namada::types::transaction::{AffineCurve, DecryptedTx, EllipticCurve}; +use namada::types::transaction::{ + AffineCurve, DecryptedTx, EllipticCurve, TxType, +}; use super::super::*; #[allow(unused_imports)] @@ -21,8 +22,10 @@ use super::block_space_alloc::{AllocFailure, BlockSpaceAllocator}; #[cfg(feature = "abcipp")] use crate::facade::tendermint_proto::abci::ExtendedCommitInfo; use crate::facade::tendermint_proto::abci::RequestPrepareProposal; +#[cfg(feature = "abcipp")] +use crate::facade::tendermint_proto::abci::{tx_record::TxAction, TxRecord}; use crate::facade::tendermint_proto::google::protobuf::Timestamp; -use crate::node::ledger::shell::{process_tx, ShellMode}; +use crate::node::ledger::shell::ShellMode; use crate::node::ledger::shims::abcipp_shim_types::shim::{response, TxBytes}; impl Shell @@ -45,7 +48,6 @@ where let txs = if let ShellMode::Validator { .. } = self.mode { // start counting allotted space for txs let alloc = self.get_encrypted_txs_allocator(); - // add encrypted txs let (encrypted_txs, alloc) = self.build_encrypted_txs( alloc, @@ -140,13 +142,11 @@ where // If tx doesn't have an expiration it is valid. If time cannot be // retrieved from block default to last block datetime which has // already been checked by mempool_validate, so it's valid - if let (Some(block_time), Some(exp)) = (block_time.as_ref(), &tx.expiration) { + if let (Some(block_time), Some(exp)) = (block_time.as_ref(), &tx.header.expiration) { if block_time > exp { return None } } - if let Ok(TxType::Wrapper(ref wrapper)) = process_tx(tx) { - if self.replay_protection_checks(wrapper, tx_bytes.as_slice(), &mut temp_wl_storage).is_ok() { - return Some(tx_bytes.clone()) - } + if tx.validate_header().is_ok() && tx.header().wrapper().is_some() && self.replay_protection_checks(&tx, tx_bytes.as_slice(), &mut temp_wl_storage).is_ok() { + return Some(tx_bytes.clone()); } } None @@ -202,7 +202,6 @@ where // TODO: This should not be hardcoded let privkey = ::G2Affine::prime_subgroup_generator(); - let pos_queries = self.wl_storage.pos_queries(); let txs = self .wl_storage @@ -210,20 +209,30 @@ where .tx_queue .iter() .map( - |WrapperTxInQueue { + |TxInQueue { tx, #[cfg(not(feature = "mainnet"))] has_valid_pow, - }| { - Tx::from(match tx.decrypt(privkey) { - Ok(tx) => DecryptedTx::Decrypted { - tx, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: *has_valid_pow, + }| { + let mut tx = tx.clone(); + match tx.decrypt(privkey).ok() + { + Some(()) => { + tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + has_valid_pow: *has_valid_pow, + })); + tx + }, + // An absent or undecryptable inner_tx are both + // treated as undecryptable + None => { + tx.update_header(TxType::Decrypted( + DecryptedTx::Undecryptable + )); + tx }, - _ => DecryptedTx::Undecryptable(tx.clone()), - }) - .to_bytes() + }.to_bytes() }, ) // TODO: make sure all decrypted txs are accepted @@ -280,7 +289,7 @@ mod test_prepare_proposal { use borsh::BorshSerialize; use namada::ledger::replay_protection; use namada::proof_of_stake::Epoch; - use namada::types::hash::Hash; + use namada::proto::{Code, Data, Header, Section, Signature}; use namada::types::transaction::{Fee, WrapperTx}; use super::*; @@ -292,12 +301,10 @@ mod test_prepare_proposal { #[test] fn test_prepare_proposal_rejects_non_wrapper_tx() { let (shell, _) = test_utils::setup(1); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction_data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); + let mut tx = Tx::new(TxType::Decrypted(DecryptedTx::Decrypted { + has_valid_pow: true, + })); + tx.header.chain_id = shell.chain_id.clone(); let req = RequestPrepareProposal { txs: vec![tx.to_bytes()], ..Default::default() @@ -312,36 +319,23 @@ mod test_prepare_proposal { fn test_error_in_processing_tx() { let (shell, _) = test_utils::setup(1); let keypair = gen_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction_data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); // an unsigned wrapper will cause an error in processing - let wrapper = Tx::new( - "".as_bytes().to_owned(), - Some( - WrapperTx::new( - Fee { - amount: 0.into(), - token: shell.wl_storage.storage.native_token.clone(), - }, - &keypair, - Epoch(0), - 0.into(), - tx, - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - ) - .try_to_vec() - .expect("Test failed"), - ), - shell.chain_id.clone(), + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: 0.into(), + token: shell.wl_storage.storage.native_token.clone(), + }, + &keypair, + Epoch(0), + 0.into(), + #[cfg(not(feature = "mainnet"))] None, - ) - .to_bytes(); + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction_data".as_bytes().to_owned())); + wrapper.encrypt(&Default::default()); + let wrapper = wrapper.to_bytes(); #[allow(clippy::redundant_clone)] let req = RequestPrepareProposal { txs: vec![wrapper.clone()], @@ -367,18 +361,7 @@ mod test_prepare_proposal { // create a request with two new wrappers from mempool and // two wrappers from the previous block to be decrypted for i in 0..2 { - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some(format!("transaction data: {}", i).as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - expected_decrypted.push(Tx::from(DecryptedTx::Decrypted { - tx: tx.clone(), - #[cfg(not(feature = "mainnet"))] - has_valid_pow: false, - })); - let wrapper_tx = WrapperTx::new( + let mut tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -386,39 +369,58 @@ mod test_prepare_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - let wrapper = wrapper_tx - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Test failed"); - shell.enqueue_tx(wrapper_tx); - expected_wrapper.push(wrapper.clone()); - req.txs.push(wrapper.to_bytes()); + )))); + tx.header.chain_id = shell.chain_id.clone(); + tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + tx.set_data(Data::new( + format!("transaction data: {}", i).as_bytes().to_owned(), + )); + tx.add_section(Section::Signature(Signature::new( + &tx.header_hash(), + &keypair, + ))); + tx.encrypt(&Default::default()); + + shell.enqueue_tx(tx.clone()); + expected_wrapper.push(tx.clone()); + req.txs.push(tx.to_bytes()); + tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + has_valid_pow: false, + })); + expected_decrypted.push(tx.clone()); } - let expected_txs: Vec = expected_wrapper + // we extract the inner data from the txs for testing + // equality since otherwise changes in timestamps would + // fail the test + let expected_txs: Vec
= expected_wrapper .into_iter() .chain(expected_decrypted.into_iter()) - // we extract the inner data from the txs for testing - // equality since otherwise changes in timestamps would - // fail the test - .map(|tx| tx.data.expect("Test failed")) + .map(|tx| tx.header) .collect(); - let received: Vec = shell + let received: Vec
= shell .prepare_proposal(req) .txs .into_iter() .map(|tx_bytes| { Tx::try_from(tx_bytes.as_slice()) .expect("Test failed") - .data - .expect("Test failed") + .header }) .collect(); // check that the order of the txs is correct - assert_eq!(received, expected_txs); + assert_eq!( + received + .iter() + .map(|x| x.try_to_vec().unwrap()) + .collect::>(), + expected_txs + .iter() + .map(|x| x.try_to_vec().unwrap()) + .collect::>(), + ); } /// Test that if the unsigned wrapper tx hash is known (replay attack), the @@ -428,14 +430,7 @@ mod test_prepare_proposal { let (mut shell, _) = test_utils::setup(1); let keypair = crate::wallet::defaults::daewon_keypair(); - - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -443,17 +438,20 @@ mod test_prepare_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - let signed = wrapper - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Test failed"); + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &keypair, + ))); + wrapper.encrypt(&Default::default()); // Write wrapper hash to storage - let wrapper_unsigned_hash = Hash(signed.unsigned_hash()); + let wrapper_unsigned_hash = wrapper.header_hash(); let hash_key = replay_protection::get_tx_hash_key(&wrapper_unsigned_hash); shell @@ -463,7 +461,7 @@ mod test_prepare_proposal { .expect("Test failed"); let req = RequestPrepareProposal { - txs: vec![signed.to_bytes()], + txs: vec![wrapper.to_bytes()], ..Default::default() }; @@ -471,7 +469,7 @@ mod test_prepare_proposal { shell.prepare_proposal(req).txs.into_iter().map(|tx_bytes| { Tx::try_from(tx_bytes.as_slice()) .expect("Test failed") - .data + .data() .expect("Test failed") }); assert_eq!(received.len(), 0); @@ -484,14 +482,7 @@ mod test_prepare_proposal { let (shell, _) = test_utils::setup(1); let keypair = crate::wallet::defaults::daewon_keypair(); - - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -499,23 +490,27 @@ mod test_prepare_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - let signed = wrapper - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Test failed"); + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &keypair, + ))); + wrapper.encrypt(&Default::default()); + let req = RequestPrepareProposal { - txs: vec![signed.to_bytes(); 2], + txs: vec![wrapper.to_bytes(); 2], ..Default::default() }; let received = shell.prepare_proposal(req).txs.into_iter().map(|tx_bytes| { Tx::try_from(tx_bytes.as_slice()) .expect("Test failed") - .data + .data() .expect("Test failed") }); assert_eq!(received.len(), 1); @@ -528,14 +523,7 @@ mod test_prepare_proposal { let (mut shell, _) = test_utils::setup(1); let keypair = crate::wallet::defaults::daewon_keypair(); - - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -543,15 +531,19 @@ mod test_prepare_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - let inner_unsigned_hash = wrapper.tx_hash.clone(); - let signed = wrapper - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Test failed"); + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &keypair, + ))); + wrapper.encrypt(&Default::default()); + let inner_unsigned_hash = + wrapper.clone().update_header(TxType::Raw).header_hash(); // Write inner hash to storage let hash_key = replay_protection::get_tx_hash_key(&inner_unsigned_hash); @@ -562,7 +554,7 @@ mod test_prepare_proposal { .expect("Test failed"); let req = RequestPrepareProposal { - txs: vec![signed.to_bytes()], + txs: vec![wrapper.to_bytes()], ..Default::default() }; @@ -570,7 +562,7 @@ mod test_prepare_proposal { shell.prepare_proposal(req).txs.into_iter().map(|tx_bytes| { Tx::try_from(tx_bytes.as_slice()) .expect("Test failed") - .data + .data() .expect("Test failed") }); assert_eq!(received.len(), 0); @@ -584,14 +576,7 @@ mod test_prepare_proposal { let keypair = crate::wallet::defaults::daewon_keypair(); let keypair_2 = crate::wallet::defaults::daewon_keypair(); - - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -599,41 +584,51 @@ mod test_prepare_proposal { &keypair, Epoch(0), 0.into(), - tx.clone(), - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - let signed = wrapper - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Test failed"); + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + let tx_code = Code::new("wasm_code".as_bytes().to_owned()); + wrapper.set_code(tx_code.clone()); + let tx_data = Data::new("transaction data".as_bytes().to_owned()); + wrapper.set_data(tx_data.clone()); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &keypair, + ))); + wrapper.encrypt(&Default::default()); - let new_wrapper = WrapperTx::new( - Fee { - amount: 0.into(), - token: shell.wl_storage.storage.native_token.clone(), - }, - &keypair_2, - Epoch(0), - 0.into(), - tx, - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - ); - let new_signed = new_wrapper - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Test failed"); + let mut new_wrapper = + Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: 0.into(), + token: shell.wl_storage.storage.native_token.clone(), + }, + &keypair_2, + Epoch(0), + 0.into(), + #[cfg(not(feature = "mainnet"))] + None, + )))); + new_wrapper.header.chain_id = shell.chain_id.clone(); + new_wrapper.header.timestamp = wrapper.header.timestamp; + new_wrapper.set_code(tx_code); + new_wrapper.set_data(tx_data); + new_wrapper.add_section(Section::Signature(Signature::new( + &new_wrapper.header_hash(), + &keypair, + ))); + new_wrapper.encrypt(&Default::default()); let req = RequestPrepareProposal { - txs: vec![signed.to_bytes(), new_signed.to_bytes()], + txs: vec![wrapper.to_bytes(), new_wrapper.to_bytes()], ..Default::default() }; let received = shell.prepare_proposal(req).txs.into_iter().map(|tx_bytes| { Tx::try_from(tx_bytes.as_slice()) .expect("Test failed") - .data + .data() .expect("Test failed") }); assert_eq!(received.len(), 1); @@ -645,28 +640,28 @@ mod test_prepare_proposal { let (shell, _) = test_utils::setup(1); let keypair = gen_keypair(); let tx_time = DateTimeUtc::now(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper_tx = WrapperTx::new( - Fee { - amount: 0.into(), - token: shell.wl_storage.storage.native_token.clone(), - }, + let mut wrapper_tx = + Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: 0.into(), + token: shell.wl_storage.storage.native_token.clone(), + }, + &keypair, + Epoch(0), + 0.into(), + #[cfg(not(feature = "mainnet"))] + None, + )))); + wrapper_tx.header.chain_id = shell.chain_id.clone(); + wrapper_tx.header.expiration = Some(tx_time); + wrapper_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper_tx + .set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper_tx.add_section(Section::Signature(Signature::new( + &wrapper_tx.header_hash(), &keypair, - Epoch(0), - 0.into(), - tx, - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - ); - let wrapper = wrapper_tx - .sign(&keypair, shell.chain_id.clone(), Some(tx_time)) - .expect("Test failed"); + ))); + wrapper_tx.encrypt(&Default::default()); let time = DateTimeUtc::now(); let block_time = @@ -675,7 +670,7 @@ mod test_prepare_proposal { nanos: time.0.timestamp_subsec_nanos() as i32, }; let req = RequestPrepareProposal { - txs: vec![wrapper.to_bytes()], + txs: vec![wrapper_tx.to_bytes()], max_tx_bytes: 0, time: Some(block_time), ..Default::default() diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index 6ad5a71048..2837ac6600 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -6,7 +6,7 @@ use namada::core::hints; use namada::core::ledger::storage::WlStorage; use namada::ledger::storage::TempWlStorage; use namada::proof_of_stake::pos_queries::PosQueries; -use namada::types::internal::WrapperTxInQueue; +use namada::types::internal::TxInQueue; use super::*; use crate::facade::tendermint_proto::abci::response_process_proposal::ProposalStatus; @@ -193,7 +193,7 @@ where pub(crate) fn process_single_tx<'a>( &self, tx_bytes: &[u8], - tx_queue_iter: &mut impl Iterator, + tx_queue_iter: &mut impl Iterator, metadata: &mut ValidationMeta, temp_wl_storage: &mut TempWlStorage, block_time: DateTimeUtc, @@ -229,17 +229,17 @@ where }) }, |tx| { - let tx_chain_id = tx.chain_id.clone(); - let tx_expiration = tx.expiration; - let tx_type = process_tx(tx).map_err(|err| { + let tx_chain_id = tx.header.chain_id.clone(); + let tx_expiration = tx.header.expiration; + if let Err(err) = tx.validate_header() { // This occurs if the wrapper / protocol tx signature is // invalid - TxResult { + return Err(TxResult { code: ErrorCodes::InvalidSig.into(), info: err.to_string(), - } - })?; - Ok((tx_chain_id, tx_expiration, tx_type)) + }); + } + Ok((tx_chain_id, tx_expiration, tx)) }, ); let (tx_chain_id, tx_expiration, tx) = match maybe_tx { @@ -250,9 +250,15 @@ where // TODO: This should not be hardcoded let privkey = ::G2Affine::prime_subgroup_generator(); - match tx { + if let Err(err) = tx.validate_header() { + return TxResult { + code: ErrorCodes::InvalidSig.into(), + info: err.to_string(), + }; + } + match tx.header().tx_type { // If it is a raw transaction, we do no further validation - TxType::Raw(_) => TxResult { + TxType::Raw => TxResult { code: ErrorCodes::InvalidTx.into(), info: "Transaction rejected: Non-encrypted transactions are \ not supported" @@ -290,11 +296,19 @@ where .into(), } } - TxType::Decrypted(tx) => { + TxType::Decrypted(tx_header) => { metadata.has_decrypted_txs = true; match tx_queue_iter.next() { Some(wrapper) => { - if wrapper.tx.tx_hash != tx.hash_commitment() { + let mut inner_tx = tx; + inner_tx.update_header(TxType::Raw); + if wrapper + .tx + .clone() + .update_header(TxType::Raw) + .header_hash() + != inner_tx.header_hash() + { TxResult { code: ErrorCodes::InvalidOrder.into(), info: "Process proposal rejected a decrypted \ @@ -302,41 +316,38 @@ where determined in the previous block" .into(), } - } else if verify_decrypted_correctly(&tx, privkey) { - if let DecryptedTx::Decrypted { - tx, - has_valid_pow: _, - } = tx - { - // Tx chain id - if tx.chain_id != self.chain_id { + } else if verify_decrypted_correctly( + &tx_header, + wrapper.tx.clone(), + privkey, + ) { + // Tx chain id + if wrapper.tx.header.chain_id != self.chain_id { + return TxResult { + code: ErrorCodes::InvalidDecryptedChainId + .into(), + info: format!( + "Decrypted tx carries a wrong chain \ + id: expected {}, found {}", + self.chain_id, + wrapper.tx.header.chain_id + ), + }; + } + + // Tx expiration + if let Some(exp) = wrapper.tx.header.expiration { + if block_time > exp { return TxResult { - code: - ErrorCodes::InvalidDecryptedChainId - .into(), + code: ErrorCodes::ExpiredDecryptedTx + .into(), info: format!( - "Decrypted tx carries a wrong \ - chain id: expected {}, found {}", - self.chain_id, tx.chain_id + "Decrypted tx expired at {:#?}, \ + block time: {:#?}", + exp, block_time ), }; } - - // Tx expiration - if let Some(exp) = tx.expiration { - if block_time > exp { - return TxResult { - code: - ErrorCodes::ExpiredDecryptedTx - .into(), - info: format!( - "Decrypted tx expired at \ - {:#?}, block time: {:#?}", - exp, block_time - ), - }; - } - } } TxResult { code: ErrorCodes::Ok.into(), @@ -423,7 +434,7 @@ where } // validate the ciphertext via Ferveo - if !wrapper.validate_ciphertext() { + if !tx.validate_ciphertext() { TxResult { code: ErrorCodes::InvalidTx.into(), info: format!( @@ -434,7 +445,7 @@ where } else { // Replay protection checks if let Err(e) = self.replay_protection_checks( - &wrapper, + &tx, tx_bytes, temp_wl_storage, ) { @@ -504,16 +515,14 @@ where /// are covered by the e2e tests. #[cfg(test)] mod test_process_proposal { - use borsh::BorshDeserialize; use namada::ledger::parameters::storage::get_wrapper_tx_fees_key; - use namada::proto::SignedTxData; + use namada::proto::{Code, Data, Section, Signature}; use namada::types::hash::Hash; use namada::types::key::*; use namada::types::storage::Epoch; use namada::types::token::Amount; - use namada::types::transaction::encrypted::EncryptedTx; - use namada::types::transaction::protocol::ProtocolTxType; - use namada::types::transaction::{EncryptionKey, Fee, WrapperTx, MIN_FEE}; + use namada::types::transaction::protocol::{ProtocolTx, ProtocolTxType}; + use namada::types::transaction::{Fee, WrapperTx, MIN_FEE}; use super::*; use crate::node::ledger::shell::test_utils::{ @@ -526,13 +535,7 @@ mod test_process_proposal { fn test_unsigned_wrapper_rejected() { let (mut shell, _) = test_utils::setup(1); let keypair = gen_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( + let mut outer_tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -540,18 +543,14 @@ mod test_process_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - let tx = Tx::new( - vec![], - Some(TxType::Wrapper(wrapper).try_to_vec().expect("Test failed")), - shell.chain_id.clone(), - None, - ) - .to_bytes(); + )))); + outer_tx.header.chain_id = shell.chain_id.clone(); + outer_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + outer_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + outer_tx.encrypt(&Default::default()); + let tx = outer_tx.to_bytes(); #[allow(clippy::redundant_clone)] let request = ProcessProposal { txs: vec![tx.clone()], @@ -566,7 +565,10 @@ mod test_process_proposal { ); assert_eq!( response[0].result.info, - String::from("Wrapper transactions must be signed") + String::from( + "WrapperTx signature verification failed: Transaction \ + doesn't have any data with a signature." + ) ); } } @@ -578,14 +580,7 @@ mod test_process_proposal { fn test_wrapper_bad_signature_rejected() { let (mut shell, _) = test_utils::setup(1); let keypair = gen_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let timestamp = tx.timestamp; - let mut wrapper = WrapperTx::new( + let mut outer_tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 100.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -593,51 +588,23 @@ mod test_process_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ) - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Test failed"); - let new_tx = if let Some(Ok(SignedTxData { - data: Some(data), - sig, - })) = wrapper - .data - .take() - .map(|data| SignedTxData::try_from_slice(&data[..])) - { - let mut new_wrapper = if let TxType::Wrapper(wrapper) = - ::deserialize(&mut data.as_ref()) - .expect("Test failed") - { - wrapper - } else { - panic!("Test failed") - }; - + )))); + outer_tx.header.chain_id = shell.chain_id.clone(); + outer_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + outer_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + outer_tx.add_section(Section::Signature(Signature::new( + &outer_tx.header_hash(), + &keypair, + ))); + outer_tx.encrypt(&Default::default()); + let mut new_tx = outer_tx.clone(); + if let TxType::Wrapper(wrapper) = &mut new_tx.header.tx_type { // we mount a malleability attack to try and remove the fee - new_wrapper.fee.amount = 0.into(); - let new_data = TxType::Wrapper(new_wrapper) - .try_to_vec() - .expect("Test failed"); - Tx { - code_or_hash: vec![], - data: Some( - SignedTxData { - sig, - data: Some(new_data), - } - .try_to_vec() - .expect("Test failed"), - ), - timestamp, - chain_id: shell.chain_id.clone(), - expiration: None, - } + wrapper.fee.amount = 0.into(); } else { - panic!("Test failed"); + panic!("Test failed") }; let request = ProcessProposal { txs: vec![new_tx.to_bytes()], @@ -646,8 +613,9 @@ mod test_process_proposal { match shell.process_proposal(request) { Ok(_) => panic!("Test failed"), Err(TestError::RejectProposal(response)) => { - let expected_error = - "Signature verification failed: Invalid signature"; + let expected_error = "WrapperTx signature verification \ + failed: Transaction doesn't have any \ + data with a signature."; assert_eq!( response[0].result.code, u32::from(ErrorCodes::InvalidSig) @@ -676,13 +644,7 @@ mod test_process_proposal { ) .unwrap(); let keypair = gen_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( + let mut outer_tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 1.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -690,15 +652,20 @@ mod test_process_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ) - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Test failed"); + )))); + outer_tx.header.chain_id = shell.chain_id.clone(); + outer_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + outer_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + outer_tx.add_section(Section::Signature(Signature::new( + &outer_tx.header_hash(), + &keypair, + ))); + outer_tx.encrypt(&Default::default()); + let request = ProcessProposal { - txs: vec![wrapper.to_bytes()], + txs: vec![outer_tx.to_bytes()], }; match shell.process_proposal(request) { @@ -744,13 +711,7 @@ mod test_process_proposal { ) .unwrap(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( + let mut outer_tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: Amount::whole(1_000_100), token: shell.wl_storage.storage.native_token.clone(), @@ -758,16 +719,20 @@ mod test_process_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ) - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Test failed"); + )))); + outer_tx.header.chain_id = shell.chain_id.clone(); + outer_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + outer_tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + outer_tx.add_section(Section::Signature(Signature::new( + &outer_tx.header_hash(), + &keypair, + ))); + outer_tx.encrypt(&Default::default()); let request = ProcessProposal { - txs: vec![wrapper.to_bytes()], + txs: vec![outer_tx.to_bytes()], }; match shell.process_proposal(request) { @@ -796,34 +761,31 @@ mod test_process_proposal { let keypair = gen_keypair(); let mut txs = vec![]; for i in 0..3 { - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some(format!("transaction data: {}", i).as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( - Fee { - amount: i.into(), - token: shell.wl_storage.storage.native_token.clone(), - }, - &keypair, - Epoch(0), - 0.into(), - tx.clone(), - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - ); - shell.enqueue_tx(wrapper); - let mut decrypted_tx = - Tx::from(TxType::Decrypted(DecryptedTx::Decrypted { - tx, + let mut outer_tx = + Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: i.into(), + token: shell.wl_storage.storage.native_token.clone(), + }, + &keypair, + Epoch(0), + 0.into(), #[cfg(not(feature = "mainnet"))] - has_valid_pow: false, - })); - decrypted_tx.chain_id = shell.chain_id.clone(); - txs.push(decrypted_tx); + None, + )))); + outer_tx.header.chain_id = shell.chain_id.clone(); + outer_tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + outer_tx.set_data(Data::new( + format!("transaction data: {}", i).as_bytes().to_owned(), + )); + outer_tx.encrypt(&Default::default()); + shell.enqueue_tx(outer_tx.clone()); + + outer_tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + has_valid_pow: false, + })); + txs.push(outer_tx); } let response = { let request = ProcessProposal { @@ -859,13 +821,7 @@ mod test_process_proposal { let (mut shell, _) = test_utils::setup(1); let keypair = gen_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( + let mut tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -873,17 +829,16 @@ mod test_process_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - shell.enqueue_tx(wrapper.clone()); - - let mut tx = - Tx::from(TxType::Decrypted(DecryptedTx::Undecryptable(wrapper))); - tx.chain_id = shell.chain_id.clone(); - + )))); + tx.header.chain_id = shell.chain_id.clone(); + tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + tx.encrypt(&Default::default()); + shell.enqueue_tx(tx.clone()); + + tx.header.tx_type = TxType::Decrypted(DecryptedTx::Undecryptable); let request = ProcessProposal { txs: vec![tx.to_bytes()], }; @@ -914,13 +869,7 @@ mod test_process_proposal { let (mut shell, _) = test_utils::setup(1); let keypair = crate::wallet::defaults::daewon_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let mut wrapper = WrapperTx::new( + let mut tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -928,20 +877,19 @@ mod test_process_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - wrapper.tx_hash = Hash([0; 32]); + )))); + tx.header.chain_id = shell.chain_id.clone(); + tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + tx.set_code_sechash(Hash([0u8; 32])); + tx.set_data_sechash(Hash([0u8; 32])); + tx.encrypt(&Default::default()); - shell.enqueue_tx(wrapper.clone()); - let mut tx = Tx::from(TxType::Decrypted(DecryptedTx::Undecryptable( - #[allow(clippy::redundant_clone)] - wrapper.clone(), - ))); - tx.chain_id = shell.chain_id.clone(); + shell.enqueue_tx(tx.clone()); + tx.header.tx_type = TxType::Decrypted(DecryptedTx::Undecryptable); let request = ProcessProposal { txs: vec![tx.to_bytes()], }; @@ -964,10 +912,7 @@ mod test_process_proposal { fn test_undecryptable() { let (mut shell, _) = test_utils::setup(1); let keypair = crate::wallet::defaults::daewon_keypair(); - let pubkey = EncryptionKey::default(); // not valid tx bytes - let tx = "garbage data".as_bytes().to_owned(); - let inner_tx = EncryptedTx::encrypt(&tx, pubkey); let wrapper = WrapperTx { fee: Fee { amount: 0.into(), @@ -976,21 +921,18 @@ mod test_process_proposal { pk: keypair.ref_to(), epoch: Epoch(0), gas_limit: 0.into(), - inner_tx, - tx_hash: hash_tx(&tx), #[cfg(not(feature = "mainnet"))] pow_solution: None, }; - shell.enqueue_tx(wrapper.clone()); - let mut signed = - Tx::from(TxType::Decrypted(DecryptedTx::Undecryptable( - #[allow(clippy::redundant_clone)] - wrapper.clone(), - ))); - signed.chain_id = shell.chain_id.clone(); + let tx = Tx::new(TxType::Wrapper(Box::new(wrapper))); + let mut decrypted = tx.clone(); + decrypted.update_header(TxType::Decrypted(DecryptedTx::Undecryptable)); + + shell.enqueue_tx(tx); + let request = ProcessProposal { - txs: vec![signed.to_bytes()], + txs: vec![decrypted.to_bytes()], }; let response = if let [resp] = shell .process_proposal(request) @@ -1009,20 +951,13 @@ mod test_process_proposal { #[test] fn test_too_many_decrypted_txs() { let (mut shell, _) = test_utils::setup(1); - - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - - let mut tx = Tx::from(TxType::Decrypted(DecryptedTx::Decrypted { - tx, + let mut tx = Tx::new(TxType::Decrypted(DecryptedTx::Decrypted { #[cfg(not(feature = "mainnet"))] has_valid_pow: false, })); - tx.chain_id = shell.chain_id.clone(); + tx.header.chain_id = shell.chain_id.clone(); + tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + tx.set_data(Data::new("transaction data".as_bytes().to_owned())); let request = ProcessProposal { txs: vec![tx.to_bytes()], @@ -1050,14 +985,11 @@ mod test_process_proposal { fn test_raw_tx_rejected() { let (mut shell, _) = test_utils::setup(1); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let mut tx = Tx::from(TxType::Raw(tx)); - tx.chain_id = shell.chain_id.clone(); + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = shell.chain_id.clone(); + tx.set_code(Code::new("wasm_code".as_bytes().to_owned())); + tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + let request = ProcessProposal { txs: vec![tx.to_bytes()], }; @@ -1088,13 +1020,7 @@ mod test_process_proposal { let keypair = crate::wallet::defaults::daewon_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1102,17 +1028,20 @@ mod test_process_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - let signed = wrapper - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Test failed"); + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &keypair, + ))); + wrapper.encrypt(&Default::default()); // Write wrapper hash to storage - let wrapper_unsigned_hash = Hash(signed.unsigned_hash()); + let wrapper_unsigned_hash = wrapper.header_hash(); let hash_key = replay_protection::get_tx_hash_key(&wrapper_unsigned_hash); shell @@ -1123,8 +1052,9 @@ mod test_process_proposal { // Run validation let request = ProcessProposal { - txs: vec![signed.to_bytes()], + txs: vec![wrapper.to_bytes()], }; + match shell.process_proposal(request) { Ok(_) => panic!("Test failed"), Err(TestError::RejectProposal(response)) => { @@ -1162,13 +1092,7 @@ mod test_process_proposal { .write(&balance_key, Amount::whole(1000).try_to_vec().unwrap()) .unwrap(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1176,18 +1100,21 @@ mod test_process_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - let signed = wrapper - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Test failed"); + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &keypair, + ))); + wrapper.encrypt(&Default::default()); // Run validation let request = ProcessProposal { - txs: vec![signed.to_bytes(); 2], + txs: vec![wrapper.to_bytes(); 2], }; match shell.process_proposal(request) { Ok(_) => panic!("Test failed"), @@ -1205,7 +1132,10 @@ mod test_process_proposal { format!( "Transaction replay attempt: Inner transaction hash \ {} already in storage", - wrapper.tx_hash + wrapper + .clone() + .update_header(TxType::Raw) + .header_hash(), ) ); } @@ -1220,13 +1150,7 @@ mod test_process_proposal { let keypair = crate::wallet::defaults::daewon_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1234,15 +1158,19 @@ mod test_process_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - let inner_unsigned_hash = wrapper.tx_hash.clone(); - let signed = wrapper - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Test failed"); + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &keypair, + ))); + wrapper.encrypt(&Default::default()); + let inner_unsigned_hash = + wrapper.clone().update_header(TxType::Raw).header_hash(); // Write inner hash to storage let hash_key = replay_protection::get_tx_hash_key(&inner_unsigned_hash); @@ -1254,7 +1182,7 @@ mod test_process_proposal { // Run validation let request = ProcessProposal { - txs: vec![signed.to_bytes()], + txs: vec![wrapper.to_bytes()], }; match shell.process_proposal(request) { Ok(_) => panic!("Test failed"), @@ -1306,13 +1234,7 @@ mod test_process_proposal { .write(&balance_key, Amount::whole(1000).try_to_vec().unwrap()) .unwrap(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1320,17 +1242,22 @@ mod test_process_proposal { &keypair, Epoch(0), 0.into(), - tx.clone(), - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - let inner_unsigned_hash = wrapper.tx_hash.clone(); - let signed = wrapper - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Test failed"); + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + let mut new_wrapper = wrapper.clone(); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &keypair, + ))); + wrapper.encrypt(&Default::default()); + let inner_unsigned_hash = + wrapper.clone().update_header(TxType::Raw).header_hash(); - let new_wrapper = WrapperTx::new( + new_wrapper.update_header(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1338,18 +1265,18 @@ mod test_process_proposal { &keypair_2, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - let new_signed = new_wrapper - .sign(&keypair, shell.chain_id.clone(), None) - .expect("Test failed"); + )))); + new_wrapper.add_section(Section::Signature(Signature::new( + &new_wrapper.header_hash(), + &keypair, + ))); + new_wrapper.encrypt(&Default::default()); // Run validation let request = ProcessProposal { - txs: vec![signed.to_bytes(), new_signed.to_bytes()], + txs: vec![wrapper.to_bytes(), new_wrapper.to_bytes()], }; match shell.process_proposal(request) { Ok(_) => panic!("Test failed"), @@ -1378,13 +1305,7 @@ mod test_process_proposal { let (mut shell, _) = test_utils::setup(1); let keypair = crate::wallet::defaults::daewon_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1392,25 +1313,32 @@ mod test_process_proposal { &keypair, Epoch(0), 0.into(), - tx.clone(), - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); + )))); let wrong_chain_id = ChainId("Wrong chain id".to_string()); - let signed = wrapper - .sign(&keypair, wrong_chain_id.clone(), None) - .expect("Test failed"); + wrapper.header.chain_id = wrong_chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + let mut protocol_tx = wrapper.clone(); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &keypair, + ))); + wrapper.encrypt(&Default::default()); - let protocol_tx = ProtocolTxType::EthereumStateUpdate(tx).sign( - &keypair.ref_to(), + protocol_tx.update_header(TxType::Protocol(Box::new(ProtocolTx { + pk: keypair.ref_to(), + tx: ProtocolTxType::EthereumStateUpdate, + }))); + protocol_tx.add_section(Section::Signature(Signature::new( + &protocol_tx.header_hash(), &keypair, - wrong_chain_id.clone(), - ); + ))); // Run validation let request = ProcessProposal { - txs: vec![signed.to_bytes(), protocol_tx.to_bytes()], + txs: vec![wrapper.to_bytes(), protocol_tx.to_bytes()], }; match shell.process_proposal(request) { Ok(_) => panic!("Test failed"), @@ -1441,19 +1369,7 @@ mod test_process_proposal { let keypair = crate::wallet::defaults::daewon_keypair(); let wrong_chain_id = ChainId("Wrong chain id".to_string()); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("new transaction data".as_bytes().to_owned()), - wrong_chain_id.clone(), - None, - ); - let decrypted: Tx = DecryptedTx::Decrypted { - tx: tx.clone(), - has_valid_pow: false, - } - .into(); - let signed_decrypted = decrypted.sign(&keypair); - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1461,12 +1377,24 @@ mod test_process_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - let wrapper_in_queue = WrapperTxInQueue { + )))); + wrapper.header.chain_id = wrong_chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper + .set_data(Data::new("new transaction data".as_bytes().to_owned())); + let mut decrypted = wrapper.clone(); + wrapper.encrypt(&Default::default()); + + decrypted.update_header(TxType::Decrypted(DecryptedTx::Decrypted { + has_valid_pow: false, + })); + decrypted.add_section(Section::Signature(Signature::new( + &decrypted.header_hash(), + &keypair, + ))); + let wrapper_in_queue = TxInQueue { tx: wrapper, has_valid_pow: false, }; @@ -1474,7 +1402,7 @@ mod test_process_proposal { // Run validation let request = ProcessProposal { - txs: vec![signed_decrypted.to_bytes()], + txs: vec![decrypted.to_bytes()], }; match shell.process_proposal(request) { @@ -1502,13 +1430,7 @@ mod test_process_proposal { let (mut shell, _) = test_utils::setup(1); let keypair = crate::wallet::defaults::daewon_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - None, - ); - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1516,18 +1438,22 @@ mod test_process_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - let signed = wrapper - .sign(&keypair, shell.chain_id.clone(), Some(DateTimeUtc::now())) - .expect("Test failed"); + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.header.expiration = Some(DateTimeUtc::now()); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &keypair, + ))); + wrapper.encrypt(&Default::default()); // Run validation let request = ProcessProposal { - txs: vec![signed.to_bytes()], + txs: vec![wrapper.to_bytes()], }; match shell.process_proposal(request) { Ok(_) => panic!("Test failed"), @@ -1547,19 +1473,7 @@ mod test_process_proposal { let (mut shell, _) = test_utils::setup(1); let keypair = crate::wallet::defaults::daewon_keypair(); - let tx = Tx::new( - "wasm_code".as_bytes().to_owned(), - Some("new transaction data".as_bytes().to_owned()), - shell.chain_id.clone(), - Some(DateTimeUtc::now()), - ); - let decrypted: Tx = DecryptedTx::Decrypted { - tx: tx.clone(), - has_valid_pow: false, - } - .into(); - let signed_decrypted = decrypted.sign(&keypair); - let wrapper = WrapperTx::new( + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -1567,12 +1481,25 @@ mod test_process_proposal { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ); - let wrapper_in_queue = WrapperTxInQueue { + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.header.expiration = Some(DateTimeUtc::now()); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper + .set_data(Data::new("new transaction data".as_bytes().to_owned())); + let mut decrypted = wrapper.clone(); + wrapper.encrypt(&Default::default()); + + decrypted.update_header(TxType::Decrypted(DecryptedTx::Decrypted { + has_valid_pow: false, + })); + decrypted.add_section(Section::Signature(Signature::new( + &decrypted.header_hash(), + &keypair, + ))); + let wrapper_in_queue = TxInQueue { tx: wrapper, has_valid_pow: false, }; @@ -1580,7 +1507,7 @@ mod test_process_proposal { // Run validation let request = ProcessProposal { - txs: vec![signed_decrypted.to_bytes()], + txs: vec![decrypted.to_bytes()], }; match shell.process_proposal(request) { Ok(response) => { diff --git a/apps/src/lib/node/ledger/shims/abcipp_shim.rs b/apps/src/lib/node/ledger/shims/abcipp_shim.rs index 05ffbf8de0..7bb02a6aef 100644 --- a/apps/src/lib/node/ledger/shims/abcipp_shim.rs +++ b/apps/src/lib/node/ledger/shims/abcipp_shim.rs @@ -186,7 +186,7 @@ impl AbcippShim { let mut end_block_request: FinalizeBlock = begin_block_request.into(); let hash = self.get_hash(); - end_block_request.hash = BlockHash::from(hash.clone()); + end_block_request.hash = BlockHash::from(hash); end_block_request.txs = txs; self.service .call(Request::FinalizeBlock(end_block_request)) diff --git a/apps/src/lib/node/ledger/storage/mod.rs b/apps/src/lib/node/ledger/storage/mod.rs index 73c21ba6ca..e9222ab213 100644 --- a/apps/src/lib/node/ledger/storage/mod.rs +++ b/apps/src/lib/node/ledger/storage/mod.rs @@ -246,7 +246,7 @@ mod tests { // insert let vp1 = Hash::sha256("vp1".as_bytes()); - storage.write(&key, vp1.clone()).expect("write failed"); + storage.write(&key, vp1).expect("write failed"); // check let (vp_code_hash, gas) = diff --git a/apps/src/lib/wallet/keys.rs b/apps/src/lib/wallet/keys.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/apps/src/lib/wallet/keys.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/apps/src/lib/wallet/mod.rs b/apps/src/lib/wallet/mod.rs index 04aae73dc6..5e97d1bd68 100644 --- a/apps/src/lib/wallet/mod.rs +++ b/apps/src/lib/wallet/mod.rs @@ -1,5 +1,4 @@ pub mod defaults; -mod keys; pub mod pre_genesis; mod store; @@ -7,15 +6,16 @@ use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::{env, fs}; +use namada::bip39::{Language, Mnemonic}; pub use namada::ledger::wallet::alias::Alias; use namada::ledger::wallet::{ - ConfirmationResponse, FindKeyError, Wallet, WalletUtils, -}; -pub use namada::ledger::wallet::{ - DecryptionError, StoredKeypair, ValidatorData, ValidatorKeys, + ConfirmationResponse, FindKeyError, GenRestoreKeyError, Wallet, WalletUtils, }; +pub use namada::ledger::wallet::{ValidatorData, ValidatorKeys}; use namada::types::key::*; +use rand_core::OsRng; pub use store::wallet_file; +use zeroize::Zeroizing; use crate::cli; use crate::config::genesis::genesis_config::GenesisConfig; @@ -24,28 +24,57 @@ use crate::config::genesis::genesis_config::GenesisConfig; pub struct CliWalletUtils; impl WalletUtils for CliWalletUtils { + type Rng = OsRng; type Storage = PathBuf; - /// Read the password for encryption/decryption from the file/env/stdin. - /// Panics if all options are empty/invalid. - fn read_password(prompt_msg: &str) -> String { + fn read_decryption_password() -> Zeroizing { + match env::var("NAMADA_WALLET_PASSWORD_FILE") { + Ok(path) => Zeroizing::new( + fs::read_to_string(path) + .expect("Something went wrong reading the file"), + ), + Err(_) => match env::var("NAMADA_WALLET_PASSWORD") { + Ok(password) => Zeroizing::new(password), + Err(_) => { + let prompt = "Enter your decryption password: "; + rpassword::read_password_from_tty(Some(prompt)) + .map(Zeroizing::new) + .expect("Failed reading password from tty.") + } + }, + } + } + + fn read_encryption_password() -> Zeroizing { let pwd = match env::var("NAMADA_WALLET_PASSWORD_FILE") { - Ok(path) => fs::read_to_string(path) - .expect("Something went wrong reading the file"), + Ok(path) => Zeroizing::new( + fs::read_to_string(path) + .expect("Something went wrong reading the file"), + ), Err(_) => match env::var("NAMADA_WALLET_PASSWORD") { - Ok(password) => password, - Err(_) => rpassword::read_password_from_tty(Some(prompt_msg)) - .unwrap_or_default(), + Ok(password) => Zeroizing::new(password), + Err(_) => { + let prompt = "Enter your encryption password: "; + read_and_confirm_passphrase_tty(prompt).unwrap_or_else( + |e| { + eprintln!("{e}"); + eprintln!( + "Action cancelled, no changes persisted." + ); + cli::safe_exit(1) + }, + ) + } }, }; - if pwd.is_empty() { + if pwd.as_str().is_empty() { eprintln!("Password cannot be empty"); + eprintln!("Action cancelled, no changes persisted."); cli::safe_exit(1) } pwd } - /// Read an alias from the file/env/stdin. fn read_alias(prompt_msg: &str) -> String { print!("Choose an alias for {}: ", prompt_msg); io::stdout().flush().unwrap(); @@ -54,6 +83,26 @@ impl WalletUtils for CliWalletUtils { alias.trim().to_owned() } + fn read_mnemonic_code() -> Result { + let phrase = get_secure_user_input("Input mnemonic code: ") + .map_err(|_| GenRestoreKeyError::MnemonicInputError)?; + Mnemonic::from_phrase(phrase.as_ref(), Language::English) + .map_err(|_| GenRestoreKeyError::MnemonicInputError) + } + + fn read_mnemonic_passphrase(confirm: bool) -> Zeroizing { + let prompt = "Enter BIP39 passphrase (empty for none): "; + let result = if confirm { + read_and_confirm_passphrase_tty(prompt) + } else { + rpassword::read_password_from_tty(Some(prompt)).map(Zeroizing::new) + }; + result.unwrap_or_else(|e| { + eprintln!("{}", e); + cli::safe_exit(1); + }) + } + // The given alias has been selected but conflicts with another alias in // the store. Offer the user to either replace existing mapping, alter the // chosen alias to a name of their chosing, or cancel the aliasing. @@ -101,6 +150,38 @@ impl WalletUtils for CliWalletUtils { } } +fn get_secure_user_input(request: S) -> std::io::Result> +where + S: std::fmt::Display, +{ + print!("{} ", request); + std::io::stdout().flush()?; + + let mut response = Zeroizing::default(); + std::io::stdin().read_line(&mut response)?; + Ok(response) +} + +pub fn read_and_confirm_passphrase_tty( + prompt: &str, +) -> Result, std::io::Error> { + let passphrase = + rpassword::read_password_from_tty(Some(prompt)).map(Zeroizing::new)?; + if !passphrase.is_empty() { + let confirmed = rpassword::read_password_from_tty(Some( + "Enter same passphrase again: ", + )) + .map(Zeroizing::new)?; + if confirmed != passphrase { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Passphrases did not match", + )); + } + } + Ok(passphrase) +} + /// Generate keypair /// for signing protocol txs and for the DKG (which will also be stored) /// A protocol keypair may be optionally provided, indicating that @@ -183,28 +264,38 @@ pub fn load_or_new_from_genesis( Wallet::::new(store_dir.to_path_buf(), store) } -/// Read the password for encryption from the file/env/stdin with -/// confirmation. -pub fn read_and_confirm_pwd(unsafe_dont_encrypt: bool) -> Option { - let password = if unsafe_dont_encrypt { +/// Read the password for encryption from the file/env/stdin, with +/// confirmation if read from stdin. +pub fn read_and_confirm_encryption_password( + unsafe_dont_encrypt: bool, +) -> Option> { + if unsafe_dont_encrypt { println!("Warning: The keypair will NOT be encrypted."); None } else { - Some(CliWalletUtils::read_password( - "Enter your encryption password: ", - )) - }; - // Bis repetita for confirmation. - let to_confirm = if unsafe_dont_encrypt { - None - } else { - Some(CliWalletUtils::read_password( - "To confirm, please enter the same encryption password once more: ", - )) - }; - if to_confirm != password { - eprintln!("Your two inputs do not match!"); - cli::safe_exit(1) + Some(CliWalletUtils::read_encryption_password()) + } +} + +#[cfg(test)] +mod tests { + use namada::bip39::MnemonicType; + use namada::ledger::wallet::WalletUtils; + use rand_core; + + use super::CliWalletUtils; + + #[test] + fn test_generate_mnemonic() { + const MNEMONIC_TYPE: MnemonicType = MnemonicType::Words12; + + let mut rng = rand_core::OsRng; + let mnemonic1 = + CliWalletUtils::generate_mnemonic_code(MNEMONIC_TYPE, &mut rng) + .unwrap(); + let mnemonic2 = + CliWalletUtils::generate_mnemonic_code(MNEMONIC_TYPE, &mut rng) + .unwrap(); + assert_ne!(mnemonic1.into_phrase(), mnemonic2.into_phrase()); } - password } diff --git a/apps/src/lib/wallet/pre_genesis.rs b/apps/src/lib/wallet/pre_genesis.rs index 12209d5674..55638ab31e 100644 --- a/apps/src/lib/wallet/pre_genesis.rs +++ b/apps/src/lib/wallet/pre_genesis.rs @@ -8,9 +8,10 @@ use namada::ledger::wallet::pre_genesis::{ }; use namada::ledger::wallet::{gen_key_to_store, WalletUtils}; use namada::types::key::SchemeType; +use zeroize::Zeroizing; use crate::wallet::store::gen_validator_keys; -use crate::wallet::{read_and_confirm_pwd, CliWalletUtils}; +use crate::wallet::{read_and_confirm_encryption_password, CliWalletUtils}; /// Validator pre-genesis wallet file name const VALIDATOR_FILE_NAME: &str = "wallet.toml"; @@ -27,7 +28,7 @@ pub fn gen_and_store( unsafe_dont_encrypt: bool, store_dir: &Path, ) -> std::io::Result { - let password = read_and_confirm_pwd(unsafe_dont_encrypt); + let password = read_and_confirm_encryption_password(unsafe_dont_encrypt); let validator = gen(scheme, password); let data = validator.store.encode(); let wallet_path = validator_file_name(store_dir); @@ -66,9 +67,7 @@ pub fn load(store_dir: &Path) -> Result { || store.consensus_key.is_encrypted() || store.account_key.is_encrypted() { - Some(CliWalletUtils::read_password( - "Enter decryption password: ", - )) + Some(CliWalletUtils::read_decryption_password()) } else { None }; @@ -99,17 +98,20 @@ pub fn load(store_dir: &Path) -> Result { /// Generate a new [`ValidatorWallet`] with required pre-genesis keys. Will /// prompt for password when `!unsafe_dont_encrypt`. -fn gen(scheme: SchemeType, password: Option) -> ValidatorWallet { - let (account_key, account_sk) = gen_key_to_store(scheme, &password); +fn gen( + scheme: SchemeType, + password: Option>, +) -> ValidatorWallet { + let (account_key, account_sk) = gen_key_to_store(scheme, password.clone()); let (consensus_key, consensus_sk) = gen_key_to_store( // Note that TM only allows ed25519 for consensus key SchemeType::Ed25519, - &password, + password.clone(), ); let (tendermint_node_key, tendermint_node_sk) = gen_key_to_store( // Note that TM only allows ed25519 for node IDs SchemeType::Ed25519, - &password, + password, ); let validator_keys = gen_validator_keys(None, scheme); let store = ValidatorStore { diff --git a/apps/src/lib/wallet/store.rs b/apps/src/lib/wallet/store.rs index fcdcfb24d9..4b2fcfa9ed 100644 --- a/apps/src/lib/wallet/store.rs +++ b/apps/src/lib/wallet/store.rs @@ -12,7 +12,7 @@ use file_lock::{FileLock, FileOptions}; use namada::ledger::wallet::store::AddressVpType; #[cfg(feature = "dev")] use namada::ledger::wallet::StoredKeypair; -use namada::ledger::wallet::{gen_sk, Store, ValidatorKeys}; +use namada::ledger::wallet::{gen_sk_rng, Store, ValidatorKeys}; #[cfg(not(feature = "dev"))] use namada::types::address::Address; use namada::types::key::*; @@ -169,7 +169,8 @@ pub fn gen_validator_keys( protocol_keypair: Option, scheme: SchemeType, ) -> ValidatorKeys { - let protocol_keypair = protocol_keypair.unwrap_or_else(|| gen_sk(scheme)); + let protocol_keypair = + protocol_keypair.unwrap_or_else(|| gen_sk_rng(scheme)); let dkg_keypair = ferveo_common::Keypair::::new( &mut StdRng::from_entropy(), ); diff --git a/core/Cargo.toml b/core/Cargo.toml index a2d8358a23..8f155db2d5 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -7,7 +7,7 @@ resolver = "2" version = "0.16.0" [features] -default = [] +default = ["multicore"] mainnet = [] ferveo-tpke = [ "ferveo", @@ -25,12 +25,6 @@ secp256k1-sign-verify = [ "libsecp256k1/hmac", ] -abcipp = [ - "ibc-proto-abcipp", - "ibc-abcipp", - "tendermint-abcipp", - "tendermint-proto-abcipp", -] abciplus = [ "ibc", "ibc-proto", @@ -42,9 +36,9 @@ ibc-mocks = [ "ibc/mocks", "ibc/std", ] -ibc-mocks-abcipp = [ - "ibc-abcipp/mocks", - "ibc-abcipp/std", + +multicore = [ + "bellman/multicore" ] # for integration tests and test utilies @@ -63,7 +57,7 @@ ark-serialize = {version = "0.3"} # branch = "bat/arse-merkle-tree" arse-merkle-tree = {package = "sparse-merkle-tree", git = "https://github.com/heliaxdev/sparse-merkle-tree", rev = "e086b235ed6e68929bf73f617dd61cd17b000a56", default-features = false, features = ["std", "borsh"]} bech32 = "0.8.0" -bellman = "0.11.2" +bellman = { version = "0.11.2", default-features = false, features = ["groth16"] } borsh = "0.9.0" chrono = {version = "0.4.22", default-features = false, features = ["clock", "std"]} data-encoding = "2.3.2" @@ -75,13 +69,12 @@ tpke = {package = "group-threshold-cryptography", optional = true, git = "https: # TODO using the same version of tendermint-rs as we do here. ibc = {version = "0.36.0", default-features = false, features = ["serde"], optional = true} ibc-proto = {version = "0.26.0", default-features = false, optional = true} -ibc-abcipp = {package = "ibc", git = "https://github.com/heliaxdev/cosmos-ibc-rs", rev = "db14744bfba6239cc5f58345ff90f8b7d42637d6", default-features = false, features = ["serde"], optional = true} -ibc-proto-abcipp = {package = "ibc-proto", git = "https://github.com/heliaxdev/ibc-proto-rs", rev = "dd8ba23110a144ffe2074a0b889676468266435a", default-features = false, optional = true} ics23 = "0.9.0" index-set = {git = "https://github.com/heliaxdev/index-set", tag = "v0.7.1", features = ["serialize-borsh", "serialize-serde"]} itertools = "0.10.0" libsecp256k1 = {git = "https://github.com/heliaxdev/libsecp256k1", rev = "bbb3bd44a49db361f21d9db80f9a087c194c0ae9", default-features = false, features = ["std", "static-context"]} -masp_primitives = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c" } +# branch = "murisi/namada-integration" +masp_primitives = { git = "https://github.com/anoma/masp", rev = "cfea8c95d3f73077ca3e25380fd27e5b46e828fd" } proptest = {git = "https://github.com/heliaxdev/proptest", rev = "8f1b4abe7ebd35c0781bf9a00a4ee59833ffa2a1", optional = true} prost = "0.11.6" prost-types = "0.11.6" @@ -95,8 +88,6 @@ serde_json = "1.0.62" sha2 = "0.9.3" tendermint = {version = "0.23.6", optional = true} tendermint-proto = {version = "0.23.6", optional = true} -tendermint-abcipp = {package = "tendermint", git = "https://github.com/heliaxdev/tendermint-rs", rev = "02b256829e80f8cfecf3fa0d625c2a76c79cd043", optional = true} -tendermint-proto-abcipp = {package = "tendermint-proto", git = "https://github.com/heliaxdev/tendermint-rs", rev = "02b256829e80f8cfecf3fa0d625c2a76c79cd043", optional = true} thiserror = "1.0.38" tracing = "0.1.30" zeroize = {version = "1.5.5", features = ["zeroize_derive"]} diff --git a/core/src/ledger/storage/merkle_tree.rs b/core/src/ledger/storage/merkle_tree.rs index 31fb40b1eb..046ebca4e6 100644 --- a/core/src/ledger/storage/merkle_tree.rs +++ b/core/src/ledger/storage/merkle_tree.rs @@ -723,7 +723,7 @@ mod test { let stores_write = tree.stores(); let mut stores_read = MerkleTreeStoresRead::default(); for st in StoreType::iter() { - stores_read.set_root(st, stores_write.root(st).clone()); + stores_read.set_root(st, *stores_write.root(st)); stores_read.set_store(stores_write.store(st).to_owned()); } let restored_tree = diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index cb3f9299df..3eafa8950b 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -919,7 +919,6 @@ where } } } - Ok(()) } } diff --git a/core/src/ledger/storage/write_log.rs b/core/src/ledger/storage/write_log.rs index bf4881aab0..938e4e8948 100644 --- a/core/src/ledger/storage/write_log.rs +++ b/core/src/ledger/storage/write_log.rs @@ -439,11 +439,7 @@ impl WriteLog { } StorageModification::InitAccount { vp_code_hash } => { storage - .batch_write_subspace_val( - batch, - key, - vp_code_hash.clone(), - ) + .batch_write_subspace_val(batch, key, *vp_code_hash) .map_err(Error::StorageError)?; } // temporary value isn't persisted @@ -609,7 +605,7 @@ mod tests { // init let init_vp = "initialized".as_bytes().to_vec(); let vp_hash = Hash::sha256(init_vp); - let (addr, gas) = write_log.init_account(&address_gen, vp_hash.clone()); + let (addr, gas) = write_log.init_account(&address_gen, vp_hash); let vp_key = storage::Key::validity_predicate(&addr); assert_eq!(gas, (vp_key.len() + vp_hash.len()) as u64); @@ -693,7 +689,7 @@ mod tests { // initialize an account let vp1 = Hash::sha256("vp1".as_bytes()); - let (addr1, _) = write_log.init_account(&address_gen, vp1.clone()); + let (addr1, _) = write_log.init_account(&address_gen, vp1); write_log.commit_tx(); // write values diff --git a/core/src/ledger/vp_env.rs b/core/src/ledger/vp_env.rs index f1d1210e28..b9ce0caced 100644 --- a/core/src/ledger/vp_env.rs +++ b/core/src/ledger/vp_env.rs @@ -4,9 +4,9 @@ use borsh::BorshDeserialize; use super::storage_api::{self, StorageRead}; +use crate::proto::Tx; use crate::types::address::Address; use crate::types::hash::Hash; -use crate::types::key::common; use crate::types::storage::{ BlockHash, BlockHeight, Epoch, Header, Key, TxIndex, }; @@ -91,20 +91,11 @@ where fn eval( &self, vp_code: Hash, - input_data: Vec, - ) -> Result; - - /// Verify a transaction signature. The signature is expected to have been - /// produced on the encoded transaction [`crate::proto::Tx`] - /// using [`crate::proto::Tx::sign`]. - fn verify_tx_signature( - &self, - pk: &common::PublicKey, - sig: &common::Signature, + input_data: Tx, ) -> Result; /// Get a tx hash - fn get_tx_code_hash(&self) -> Result; + fn get_tx_code_hash(&self) -> Result, storage_api::Error>; /// Verify a MASP transaction fn verify_masp(&self, tx: Vec) -> Result; diff --git a/core/src/proto/mod.rs b/core/src/proto/mod.rs index daeb0e9a49..37886a3b7c 100644 --- a/core/src/proto/mod.rs +++ b/core/src/proto/mod.rs @@ -3,27 +3,23 @@ pub mod generated; mod types; -pub use types::{Dkg, Error, Signed, SignedTxData, Tx}; +pub use types::{ + Code, Commitment, Data, Dkg, Error, Header, MaspBuilder, Section, + Signature, Tx, TxError, +}; #[cfg(test)] mod tests { - use std::time::SystemTime; - use data_encoding::HEXLOWER; use generated::types::Tx; use prost::Message; use super::*; - use crate::types::chain::ChainId; #[test] fn encoding_round_trip() { let tx = Tx { - code_or_hash: "wasm code".as_bytes().to_owned(), - data: Some("arbitrary data".as_bytes().to_owned()), - timestamp: Some(SystemTime::now().into()), - chain_id: ChainId::default().0, - expiration: Some(SystemTime::now().into()), + data: "arbitrary data".as_bytes().to_owned(), }; let mut tx_bytes = vec![]; tx.encode(&mut tx_bytes).unwrap(); diff --git a/core/src/proto/types.rs b/core/src/proto/types.rs index f82fb7bd8b..e350ed4ee7 100644 --- a/core/src/proto/types.rs +++ b/core/src/proto/types.rs @@ -1,31 +1,47 @@ -use std::convert::{TryFrom, TryInto}; -use std::hash::{Hash, Hasher}; +use std::collections::HashSet; +use std::convert::TryFrom; +#[cfg(feature = "ferveo-tpke")] +use ark_ec::AffineCurve; +#[cfg(feature = "ferveo-tpke")] +use ark_ec::PairingEngine; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use masp_primitives::transaction::builder::Builder; +use masp_primitives::transaction::components::sapling::builder::SaplingMetadata; +use masp_primitives::transaction::Transaction; +use masp_primitives::zip32::ExtendedFullViewingKey; use prost::Message; +use serde::de::Error as SerdeError; use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; use thiserror::Error; use super::generated::types; #[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] use crate::tendermint_proto::abci::ResponseDeliverTx; +use crate::types::address::Address; use crate::types::chain::ChainId; use crate::types::key::*; +use crate::types::storage::Epoch; use crate::types::time::DateTimeUtc; #[cfg(feature = "ferveo-tpke")] use crate::types::token::Transfer; -use crate::types::transaction::hash_tx; #[cfg(feature = "ferveo-tpke")] -use crate::types::transaction::process_tx; +use crate::types::transaction::protocol::ProtocolTx; +#[cfg(feature = "ferveo-tpke")] +use crate::types::transaction::EllipticCurve; #[cfg(feature = "ferveo-tpke")] -use crate::types::transaction::DecryptedTx; +use crate::types::transaction::EncryptionKey; #[cfg(feature = "ferveo-tpke")] -use crate::types::transaction::TxType; +use crate::types::transaction::WrapperTxErr; +use crate::types::transaction::{hash_tx, DecryptedTx, TxType, WrapperTx}; #[derive(Error, Debug)] pub enum Error { #[error("Error decoding a transaction from bytes: {0}")] TxDecodingError(prost::DecodeError), + #[error("Error deserializing transaction field bytes: {0}")] + TxDeserializingError(std::io::Error), #[error("Error decoding an DkgGossipMessage from bytes: {0}")] DkgDecodingError(prost::DecodeError), #[error("Dkg is empty")] @@ -38,416 +54,1077 @@ pub enum Error { pub type Result = std::result::Result; -/// This can be used to sign an arbitrary tx. The signature is produced and -/// verified on the tx data concatenated with the tx code, however the tx code -/// itself is not part of this structure. -/// -/// Because the signature is not checked by the ledger, we don't inline it into -/// the `Tx` type directly. Instead, the signature is attached to the `tx.data`, -/// which can then be checked by a validity predicate wasm. -#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema)] -pub struct SignedTxData { - /// The original tx data bytes, if any - pub data: Option>, - /// The signature is produced on the tx data concatenated with the tx code - /// and the timestamp. - pub sig: common::Signature, -} - -/// A generic signed data wrapper for Borsh encode-able data. +/// A section representing transaction data #[derive( - Clone, Debug, BorshSerialize, BorshDeserialize, Serialize, Deserialize, + Clone, + Debug, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, )] -pub struct Signed { - /// Arbitrary data to be signed - pub data: T, - /// The signature of the data - pub sig: common::Signature, +pub struct Data { + pub salt: [u8; 8], + pub data: Vec, } -impl PartialEq for Signed -where - T: BorshSerialize + BorshDeserialize + PartialEq, -{ - fn eq(&self, other: &Self) -> bool { - self.data == other.data && self.sig == other.sig +impl Data { + /// Make a new data section with the given bytes + pub fn new(data: Vec) -> Self { + Self { + salt: DateTimeUtc::now().0.timestamp_millis().to_le_bytes(), + data, + } + } + + /// Hash this data section + pub fn hash<'a>(&self, hasher: &'a mut Sha256) -> &'a mut Sha256 { + hasher.update( + self.try_to_vec().expect("unable to serialize data section"), + ); + hasher } } -impl Eq for Signed where - T: BorshSerialize + BorshDeserialize + Eq + PartialEq -{ +/// Error representing the case where the supplied code has incorrect hash +pub struct CommitmentError; + +/// Represents either some code bytes or their SHA-256 hash +#[derive( + Clone, + Debug, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, +)] +pub enum Commitment { + /// Result of applying hash function to bytes + Hash(crate::types::hash::Hash), + /// Result of applying identity function to bytes + Id(Vec), } -impl Hash for Signed -where - T: BorshSerialize + BorshDeserialize + Hash, -{ - fn hash(&self, state: &mut H) { - self.data.hash(state); - self.sig.hash(state); +impl Commitment { + /// Substitute bytes with their SHA-256 hash + pub fn contract(&mut self) { + if let Self::Id(code) = self { + *self = Self::Hash(hash_tx(code)); + } + } + + /// Substitute a code hash with the supplied bytes if the hashes are + /// consistent, otherwise return an error + pub fn expand( + &mut self, + code: Vec, + ) -> std::result::Result<(), CommitmentError> { + match self { + Self::Id(c) if *c == code => Ok(()), + Self::Hash(hash) if *hash == hash_tx(&code) => { + *self = Self::Id(code); + Ok(()) + } + _ => Err(CommitmentError), + } + } + + /// Return the contained hash commitment + pub fn hash(&self) -> crate::types::hash::Hash { + match self { + Self::Id(code) => hash_tx(code), + Self::Hash(hash) => *hash, + } + } + + /// Return the result of applying identity function if there is any + pub fn id(&self) -> Option> { + if let Self::Id(code) = self { + Some(code.clone()) + } else { + None + } + } +} + +/// A section representing transaction code +#[derive( + Clone, + Debug, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, +)] +pub struct Code { + /// Additional random data + pub salt: [u8; 8], + /// Actual transaction code + pub code: Commitment, +} + +impl Code { + /// Make a new code section with the given bytes + pub fn new(code: Vec) -> Self { + Self { + salt: DateTimeUtc::now().0.timestamp_millis().to_le_bytes(), + code: Commitment::Id(code), + } + } + + /// Make a new code section with the given hash + pub fn from_hash(hash: crate::types::hash::Hash) -> Self { + Self { + salt: DateTimeUtc::now().0.timestamp_millis().to_le_bytes(), + code: Commitment::Hash(hash), + } + } + + /// Hash this code section + pub fn hash<'a>(&self, hasher: &'a mut Sha256) -> &'a mut Sha256 { + hasher.update(self.salt); + hasher.update(self.code.hash()); + hasher + } +} + +/// A section representing the signature over another section +#[derive( + Clone, + Debug, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, +)] +pub struct Signature { + /// Additional random data + salt: [u8; 8], + /// The hash of the section being signed + target: crate::types::hash::Hash, + /// The signature over the above has + pub signature: common::Signature, + /// The public key to verrify the above siggnature + pub_key: common::PublicKey, +} + +impl Signature { + /// Sign the given section hash with the given key and return a section + pub fn new( + target: &crate::types::hash::Hash, + sec_key: &common::SecretKey, + ) -> Self { + Self { + salt: DateTimeUtc::now().0.timestamp_millis().to_le_bytes(), + target: *target, + signature: common::SigScheme::sign(sec_key, target), + pub_key: sec_key.ref_to(), + } + } + + /// Hash this signature section + pub fn hash<'a>(&self, hasher: &'a mut Sha256) -> &'a mut Sha256 { + hasher.update( + self.try_to_vec() + .expect("unable to serialize signature section"), + ); + hasher + } +} + +/// Represents a section obtained by encrypting another section +#[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "ferveo-tpke", serde(from = "SerializedCiphertext"))] +#[cfg_attr(feature = "ferveo-tpke", serde(into = "SerializedCiphertext"))] +#[cfg_attr( + not(feature = "ferveo-tpke"), + derive(BorshSerialize, BorshDeserialize, BorshSchema) +)] +pub struct Ciphertext { + /// The ciphertext corresponding to the original section serialization + #[cfg(feature = "ferveo-tpke")] + pub ciphertext: tpke::Ciphertext, + /// Ciphertext representation when ferveo not available + #[cfg(not(feature = "ferveo-tpke"))] + pub opaque: Vec, +} + +impl Ciphertext { + /// Make a ciphertext section based on the given sections. Note that this + /// encryption is not idempotent + #[cfg(feature = "ferveo-tpke")] + pub fn new(sections: Vec
, pubkey: &EncryptionKey) -> Self { + let mut rng = rand::thread_rng(); + let bytes = + sections.try_to_vec().expect("unable to serialize sections"); + Self { + ciphertext: tpke::encrypt(&bytes, pubkey.0, &mut rng), + } + } + + /// Decrypt this ciphertext back to the original plaintext sections. + #[cfg(feature = "ferveo-tpke")] + pub fn decrypt( + &self, + privkey: ::G2Affine, + ) -> std::io::Result> { + let bytes = tpke::decrypt(&self.ciphertext, privkey); + Vec::
::try_from_slice(&bytes) + } + + /// Get the hash of this ciphertext section. This operation is done in such + /// a way it matches the hash of the type pun + pub fn hash<'a>(&self, hasher: &'a mut Sha256) -> &'a mut Sha256 { + hasher.update( + self.try_to_vec().expect("unable to serialize decrypted tx"), + ); + hasher + } +} + +#[cfg(feature = "ferveo-tpke")] +impl borsh::ser::BorshSerialize for Ciphertext { + fn serialize( + &self, + writer: &mut W, + ) -> std::io::Result<()> { + use ark_serialize::CanonicalSerialize; + let tpke::Ciphertext { + nonce, + ciphertext, + auth_tag, + } = &self.ciphertext; + // Serialize the nonce into bytes + let mut nonce_buffer = Vec::::new(); + nonce.serialize(&mut nonce_buffer).map_err(|err| { + std::io::Error::new(std::io::ErrorKind::InvalidData, err) + })?; + // serialize the auth_tag to bytes + let mut tag_buffer = Vec::::new(); + auth_tag.serialize(&mut tag_buffer).map_err(|err| { + std::io::Error::new(std::io::ErrorKind::InvalidData, err) + })?; + let mut payload = Vec::new(); + // serialize the three byte arrays + BorshSerialize::serialize( + &(nonce_buffer, ciphertext, tag_buffer), + &mut payload, + )?; + // now serialize the ciphertext payload with length + BorshSerialize::serialize(&payload, writer) + } +} + +#[cfg(feature = "ferveo-tpke")] +impl borsh::BorshDeserialize for Ciphertext { + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + type VecTuple = (u32, Vec, Vec, Vec); + let (_length, nonce, ciphertext, auth_tag): VecTuple = + BorshDeserialize::deserialize(buf)?; + Ok(Self { + ciphertext: tpke::Ciphertext { + nonce: ark_serialize::CanonicalDeserialize::deserialize( + &*nonce, + ) + .map_err(|err| { + std::io::Error::new(std::io::ErrorKind::InvalidData, err) + })?, + ciphertext, + auth_tag: ark_serialize::CanonicalDeserialize::deserialize( + &*auth_tag, + ) + .map_err(|err| { + std::io::Error::new(std::io::ErrorKind::InvalidData, err) + })?, + }, + }) + } +} + +#[cfg(feature = "ferveo-tpke")] +impl borsh::BorshSchema for Ciphertext { + fn add_definitions_recursively( + definitions: &mut std::collections::HashMap< + borsh::schema::Declaration, + borsh::schema::Definition, + >, + ) { + // Encoded as `(Vec, Vec, Vec)` + let elements = "u8".into(); + let definition = borsh::schema::Definition::Sequence { elements }; + definitions.insert("Vec".into(), definition); + let elements = + vec!["Vec".into(), "Vec".into(), "Vec".into()]; + let definition = borsh::schema::Definition::Tuple { elements }; + definitions.insert(Self::declaration(), definition); + } + + fn declaration() -> borsh::schema::Declaration { + "Ciphertext".into() + } +} + +/// A helper struct for serializing EncryptedTx structs +/// as an opaque blob +#[cfg(feature = "ferveo-tpke")] +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(transparent)] +struct SerializedCiphertext { + payload: Vec, +} + +#[cfg(feature = "ferveo-tpke")] +impl From for SerializedCiphertext { + fn from(tx: Ciphertext) -> Self { + SerializedCiphertext { + payload: tx + .try_to_vec() + .expect("Unable to serialize encrypted transaction"), + } + } +} + +#[cfg(feature = "ferveo-tpke")] +impl From for Ciphertext { + fn from(ser: SerializedCiphertext) -> Self { + BorshDeserialize::deserialize(&mut ser.payload.as_ref()) + .expect("Unable to deserialize encrypted transactions") + } +} + +#[derive(serde::Serialize, serde::Deserialize)] +struct TransactionSerde(Vec); + +impl From> for TransactionSerde { + fn from(tx: Vec) -> Self { + Self(tx) + } +} + +impl From for Vec { + fn from(tx: TransactionSerde) -> Vec { + tx.0 } } -impl PartialOrd for Signed +fn borsh_serde( + obj: &impl BorshSerialize, + ser: S, +) -> std::result::Result where - T: BorshSerialize + BorshDeserialize + PartialOrd, + S: serde::Serializer, + T: From>, + T: serde::Serialize, { - fn partial_cmp(&self, other: &Self) -> Option { - self.data.partial_cmp(&other.data) - } + Into::::into(obj.try_to_vec().unwrap()).serialize(ser) } -impl Signed +fn serde_borsh<'de, T, S, U>(ser: S) -> std::result::Result where - T: BorshSerialize + BorshDeserialize, + S: serde::Deserializer<'de>, + T: Into>, + T: serde::Deserialize<'de>, + U: BorshDeserialize, { - /// Initialize a new signed data. - pub fn new(keypair: &common::SecretKey, data: T) -> Self { - let to_sign = data - .try_to_vec() - .expect("Encoding data for signing shouldn't fail"); - let sig = common::SigScheme::sign(keypair, to_sign); - Self { data, sig } - } - - /// Verify that the data has been signed by the secret key - /// counterpart of the given public key. - pub fn verify( - &self, - pk: &common::PublicKey, - ) -> std::result::Result<(), VerifySigError> { - let bytes = self - .data - .try_to_vec() - .expect("Encoding data for verifying signature shouldn't fail"); - common::SigScheme::verify_signature_raw(pk, &bytes, &self.sig) + BorshDeserialize::try_from_slice(&Into::>::into(T::deserialize( + ser, + )?)) + .map_err(S::Error::custom) +} + +/// A structure to facilitate Serde (de)serializations of Builders +#[derive(serde::Serialize, serde::Deserialize)] +struct BuilderSerde(Vec); + +impl From> for BuilderSerde { + fn from(tx: Vec) -> Self { + Self(tx) } } -/// A Tx with its code replaced by a hash salted with the Borsh -/// serialized timestamp of the transaction. This structure will almost -/// certainly be smaller than a Tx, yet in the usual cases it contains -/// enough information to confirm that the Tx is as intended and make a -/// non-malleable signature. +impl From for Vec { + fn from(tx: BuilderSerde) -> Vec { + tx.0 + } +} + +/// A structure to facilitate Serde (de)serializations of SaplingMetadata +#[derive(serde::Serialize, serde::Deserialize)] +pub struct SaplingMetadataSerde(Vec); + +impl From> for SaplingMetadataSerde { + fn from(tx: Vec) -> Self { + Self(tx) + } +} + +impl From for Vec { + fn from(tx: SaplingMetadataSerde) -> Vec { + tx.0 + } +} + +/// A section providing the auxiliary inputs used to construct a MASP +/// transaction #[derive( - Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, BorshSchema, Hash, + Clone, Debug, BorshSerialize, BorshDeserialize, Serialize, Deserialize, )] -pub struct SigningTx { - pub code_hash: [u8; 32], - pub data: Option>, - pub timestamp: DateTimeUtc, +pub struct MaspBuilder { + /// The MASP transaction that this section witnesses + pub target: crate::types::hash::Hash, + /// The decoded set of asset types used by the transaction. Useful for + /// offline wallets trying to display AssetTypes. + pub asset_types: HashSet<(Address, Epoch)>, + /// Track how Info objects map to descriptors and outputs + #[serde( + serialize_with = "borsh_serde::", + deserialize_with = "serde_borsh::" + )] + pub metadata: SaplingMetadata, + /// The data that was used to construct the target transaction + #[serde( + serialize_with = "borsh_serde::", + deserialize_with = "serde_borsh::" + )] + pub builder: Builder<(), (), ExtendedFullViewingKey, ()>, +} + +impl MaspBuilder { + /// Get the hash of this ciphertext section. This operation is done in such + /// a way it matches the hash of the type pun + pub fn hash<'a>(&self, hasher: &'a mut Sha256) -> &'a mut Sha256 { + hasher.update( + self.try_to_vec().expect("unable to serialize MASP builder"), + ); + hasher + } +} + +impl borsh::BorshSchema for MaspBuilder { + fn add_definitions_recursively( + _definitions: &mut std::collections::HashMap< + borsh::schema::Declaration, + borsh::schema::Definition, + >, + ) { + } + + fn declaration() -> borsh::schema::Declaration { + "Builder".into() + } +} + +/// A section of a transaction. Carries an independent piece of information +/// necessary for the processing of a transaction. +#[derive( + Clone, + Debug, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, +)] +pub enum Section { + /// Transaction data that needs to be sent to hardware wallets + Data(Data), + /// Transaction data that does not need to be sent to hardware wallets + ExtraData(Code), + /// Transaction code. Sending to hardware wallets optional + Code(Code), + /// A transaction signature. Often produced by hardware wallets + Signature(Signature), + /// Ciphertext obtained by encrypting arbitrary transaction sections + Ciphertext(Ciphertext), + /// Embedded MASP transaction section + #[serde( + serialize_with = "borsh_serde::", + deserialize_with = "serde_borsh::" + )] + MaspTx(Transaction), + /// A section providing the auxiliary inputs used to construct a MASP + /// transaction. Only send to wallet, never send to protocol. + MaspBuilder(MaspBuilder), +} + +impl Section { + /// Hash this section. Section hashes are useful for signatures and also for + /// allowing transaction sections to cross reference. + pub fn hash<'a>(&self, hasher: &'a mut Sha256) -> &'a mut Sha256 { + // Get the index corresponding to this variant + let discriminant = + self.try_to_vec().expect("sections should serialize")[0]; + // Use Borsh's discriminant in the Section's hash + hasher.update([discriminant]); + match self { + Self::Data(data) => data.hash(hasher), + Self::ExtraData(extra) => extra.hash(hasher), + Self::Code(code) => code.hash(hasher), + Self::Signature(sig) => sig.hash(hasher), + Self::Ciphertext(ct) => ct.hash(hasher), + Self::MaspBuilder(mb) => mb.hash(hasher), + Self::MaspTx(tx) => { + hasher.update(tx.txid().as_ref()); + hasher + } + } + } + + /// Sign over the hash of this section and return a signature section that + /// can be added to the container transaction + pub fn sign(&self, sec_key: &common::SecretKey) -> Signature { + let mut hasher = Sha256::new(); + self.hash(&mut hasher); + Signature::new( + &crate::types::hash::Hash(hasher.finalize().into()), + sec_key, + ) + } + + /// Extract the data from this section if possible + pub fn data(&self) -> Option { + if let Self::Data(data) = self { + Some(data.clone()) + } else { + None + } + } + + /// Extract the extra data from this section if possible + pub fn extra_data_sec(&self) -> Option { + if let Self::ExtraData(data) = self { + Some(data.clone()) + } else { + None + } + } + + /// Extract the extra data from this section if possible + pub fn extra_data(&self) -> Option> { + if let Self::ExtraData(data) = self { + data.code.id() + } else { + None + } + } + + /// Extract the code from this section is possible + pub fn code_sec(&self) -> Option { + if let Self::Code(data) = self { + Some(data.clone()) + } else { + None + } + } + + /// Extract the code from this section is possible + pub fn code(&self) -> Option> { + if let Self::Code(data) = self { + data.code.id() + } else { + None + } + } + + /// Extract the signature from this section if possible + pub fn signature(&self) -> Option { + if let Self::Signature(data) = self { + Some(data.clone()) + } else { + None + } + } + + /// Extract the ciphertext from this section if possible + pub fn ciphertext(&self) -> Option { + if let Self::Ciphertext(data) = self { + Some(data.clone()) + } else { + None + } + } + + /// Extract the MASP transaction from this section if possible + pub fn masp_tx(&self) -> Option { + if let Self::MaspTx(data) = self { + Some(data.clone()) + } else { + None + } + } + + /// Extract the MASP builder from this section if possible + pub fn masp_builder(&self) -> Option { + if let Self::MaspBuilder(data) = self { + Some(data.clone()) + } else { + None + } + } +} + +/// A Namada transaction header indicating where transaction subcomponents can +/// be found +#[derive( + Clone, + Debug, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, +)] +pub struct Header { + /// The chain which this transaction is being submitted to pub chain_id: ChainId, + /// The time at which this transaction expires pub expiration: Option, + /// A transaction timestamp + pub timestamp: DateTimeUtc, + /// The SHA-256 hash of the transaction's code section + pub code_hash: crate::types::hash::Hash, + /// The SHA-256 hash of the transaction's data section + pub data_hash: crate::types::hash::Hash, + /// The type of this transaction + pub tx_type: TxType, } -impl SigningTx { - pub fn hash(&self) -> [u8; 32] { - let timestamp = Some(self.timestamp.into()); - let expiration = self.expiration.map(|e| e.into()); - let mut bytes = vec![]; - types::Tx { - code_or_hash: self.code_hash.to_vec(), - data: self.data.clone(), - timestamp, - chain_id: self.chain_id.as_str().to_owned(), - expiration, - } - .encode(&mut bytes) - .expect("encoding a transaction failed"); - hash_tx(&bytes).0 - } - - /// Sign a transaction using [`SignedTxData`]. - pub fn sign(self, keypair: &common::SecretKey) -> Self { - let to_sign = self.hash(); - let sig = common::SigScheme::sign(keypair, to_sign); - let signed = SignedTxData { - data: self.data, - sig, - } - .try_to_vec() - .expect("Encoding transaction data shouldn't fail"); - SigningTx { - code_hash: self.code_hash, - data: Some(signed), - timestamp: self.timestamp, - chain_id: self.chain_id, - expiration: self.expiration, - } - } - - /// Verify that the transaction has been signed by the secret key - /// counterpart of the given public key. - pub fn verify_sig( - &self, - pk: &common::PublicKey, - sig: &common::Signature, - ) -> std::result::Result<(), VerifySigError> { - // Try to get the transaction data from decoded `SignedTxData` - let tx_data = self.data.clone().ok_or(VerifySigError::MissingData)?; - let signed_tx_data = SignedTxData::try_from_slice(&tx_data[..]) - .expect("Decoding transaction data shouldn't fail"); - let data = signed_tx_data.data; - let tx = SigningTx { - code_hash: self.code_hash, - data, - timestamp: self.timestamp, - chain_id: self.chain_id.clone(), - expiration: self.expiration, - }; - let signed_data = tx.hash(); - common::SigScheme::verify_signature_raw(pk, &signed_data, sig) - } - - /// Expand this reduced Tx using the supplied code only if the the code - /// hashes to the stored code hash - pub fn expand(self, code: Vec) -> Option { - if hash_tx(&code).0 == self.code_hash { - Some(Tx { - code_or_hash: code, - data: self.data, - timestamp: self.timestamp, - chain_id: self.chain_id, - expiration: self.expiration, - }) +impl Header { + /// Make a new header of the given transaction type + pub fn new(tx_type: TxType) -> Self { + Self { + tx_type, + chain_id: ChainId::default(), + expiration: None, + timestamp: DateTimeUtc::now(), + code_hash: crate::types::hash::Hash::default(), + data_hash: crate::types::hash::Hash::default(), + } + } + + /// Get the hash of this transaction header. + pub fn hash<'a>(&self, hasher: &'a mut Sha256) -> &'a mut Sha256 { + hasher.update( + self.try_to_vec() + .expect("unable to serialize transaction header"), + ); + hasher + } + + /// Get the wrapper header if it is present + pub fn wrapper(&self) -> Option { + if let TxType::Wrapper(wrapper) = &self.tx_type { + Some(*wrapper.clone()) + } else { + None + } + } + + /// Get the decrypted header if it is present + pub fn decrypted(&self) -> Option { + if let TxType::Decrypted(decrypted) = &self.tx_type { + Some(decrypted.clone()) } else { None } } -} -impl From for SigningTx { - fn from(tx: Tx) -> SigningTx { - SigningTx { - code_hash: hash_tx(&tx.code_or_hash).0, - data: tx.data, - timestamp: tx.timestamp, - chain_id: tx.chain_id, - expiration: tx.expiration, + #[cfg(feature = "ferveo-tpke")] + /// Get the protocol header if it is present + pub fn protocol(&self) -> Option { + if let TxType::Protocol(protocol) = &self.tx_type { + Some(*protocol.clone()) + } else { + None } } } -/// A SigningTx but with the full code embedded. This structure will almost -/// certainly be bigger than SigningTxs and contains enough information to -/// execute the transaction. +/// Errors relating to decrypting a wrapper tx and its +/// encrypted payload from a Tx type +#[allow(missing_docs)] +#[derive(thiserror::Error, Debug, PartialEq)] +pub enum TxError { + #[error("{0}")] + Unsigned(String), + #[error("{0}")] + SigError(String), + #[error("Failed to deserialize Tx: {0}")] + Deserialization(String), +} + +/// A Namada transaction is represented as a header followed by a series of +/// seections providing additional details. #[derive( - Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, BorshSchema, Hash, + Clone, + Debug, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, )] pub struct Tx { - pub code_or_hash: Vec, - pub data: Option>, - pub timestamp: DateTimeUtc, - pub chain_id: ChainId, - pub expiration: Option, + /// Type indicating how to process transaction + pub header: Header, + /// Additional details necessary to process transaction + pub sections: Vec
, } +/// Deserialize Tx from protobufs impl TryFrom<&[u8]> for Tx { type Error = Error; fn try_from(tx_bytes: &[u8]) -> Result { let tx = types::Tx::decode(tx_bytes).map_err(Error::TxDecodingError)?; - let timestamp = match tx.timestamp { - Some(t) => t.try_into().map_err(Error::InvalidTimestamp)?, - None => return Err(Error::NoTimestampError), - }; - let chain_id = ChainId(tx.chain_id); - let expiration = match tx.expiration { - Some(e) => Some(e.try_into().map_err(Error::InvalidTimestamp)?), - None => None, - }; - - Ok(Tx { - code_or_hash: tx.code_or_hash, - data: tx.data, - timestamp, - chain_id, - expiration, - }) + BorshDeserialize::try_from_slice(&tx.data) + .map_err(Error::TxDeserializingError) } } -impl From for types::Tx { - fn from(tx: Tx) -> Self { - let timestamp = Some(tx.timestamp.into()); - let expiration = tx.expiration.map(|e| e.into()); - - types::Tx { - code_or_hash: tx.code_or_hash, - data: tx.data, - timestamp, - chain_id: tx.chain_id.as_str().to_owned(), - expiration, +impl Tx { + /// Create a transaction of the given type + pub fn new(header: TxType) -> Self { + Tx { + header: Header::new(header), + sections: vec![], } } -} -#[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] -impl From for ResponseDeliverTx { - #[cfg(not(feature = "ferveo-tpke"))] - fn from(_tx: Tx) -> ResponseDeliverTx { - Default::default() + /// Get the transaction header + pub fn header(&self) -> Header { + self.header.clone() } - /// Annotate the Tx with meta-data based on its contents - #[cfg(feature = "ferveo-tpke")] - fn from(tx: Tx) -> ResponseDeliverTx { - use crate::tendermint_proto::abci::{Event, EventAttribute}; + /// Get the transaction header hash + pub fn header_hash(&self) -> crate::types::hash::Hash { + crate::types::hash::Hash( + self.header.hash(&mut Sha256::new()).finalize_reset().into(), + ) + } - #[cfg(feature = "ABCI")] - fn encode_str(x: &str) -> Vec { - x.as_bytes().to_vec() - } - #[cfg(not(feature = "ABCI"))] - fn encode_str(x: &str) -> String { - x.to_string() - } - #[cfg(feature = "ABCI")] - fn encode_string(x: String) -> Vec { - x.into_bytes() - } - #[cfg(not(feature = "ABCI"))] - fn encode_string(x: String) -> String { - x - } - match process_tx(tx) { - Ok(TxType::Decrypted(DecryptedTx::Decrypted { - tx, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: _, - })) => { - let empty_vec = vec![]; - let tx_data = tx.data.as_ref().unwrap_or(&empty_vec); - let signed = - if let Ok(signed) = SignedTxData::try_from_slice(tx_data) { - signed - } else { - return Default::default(); - }; - if let Ok(transfer) = Transfer::try_from_slice( - signed.data.as_ref().unwrap_or(&empty_vec), - ) { - let events = vec![Event { - r#type: "transfer".to_string(), - attributes: vec![ - EventAttribute { - key: encode_str("source"), - value: encode_string(transfer.source.encode()), - index: true, - }, - EventAttribute { - key: encode_str("target"), - value: encode_string(transfer.target.encode()), - index: true, - }, - EventAttribute { - key: encode_str("token"), - value: encode_string(transfer.token.encode()), - index: true, - }, - EventAttribute { - key: encode_str("amount"), - value: encode_string( - transfer.amount.to_string(), - ), - index: true, - }, - ], - }]; - ResponseDeliverTx { - events, - info: "Transfer tx".to_string(), - ..Default::default() - } - } else { - Default::default() - } + /// Update the header whilst maintaining existing cross-references + pub fn update_header(&mut self, tx_type: TxType) -> &mut Self { + self.header.tx_type = tx_type; + self + } + + /// Get the transaction section with the given hash + pub fn get_section( + &self, + hash: &crate::types::hash::Hash, + ) -> Option<&Section> { + for section in &self.sections { + let sechash = crate::types::hash::Hash( + section.hash(&mut Sha256::new()).finalize_reset().into(), + ); + if sechash == *hash { + return Some(section); } - _ => Default::default(), } + None } -} -impl Tx { - /// Create a new transaction. `code_or_hash` should be set as the wasm code - /// bytes or hash. - pub fn new( - code_or_hash: Vec, - data: Option>, - chain_id: ChainId, - expiration: Option, - ) -> Self { - Tx { - code_or_hash, - data, - timestamp: DateTimeUtc::now(), - chain_id, - expiration, + /// Add a new section to the transaction + pub fn add_section(&mut self, section: Section) -> &mut Section { + self.sections.push(section); + self.sections.last_mut().unwrap() + } + + /// Get the hash of this transaction's code from the heeader + pub fn code_sechash(&self) -> &crate::types::hash::Hash { + &self.header.code_hash + } + + /// Set the transaction code hash stored in the header + pub fn set_code_sechash(&mut self, hash: crate::types::hash::Hash) { + self.header.code_hash = hash + } + + /// Get the code designated by the transaction code hash in the header + pub fn code(&self) -> Option> { + match self.get_section(self.code_sechash()) { + Some(Section::Code(section)) => section.code.id(), + _ => None, } } + /// Add the given code to the transaction and set code hash in the header + pub fn set_code(&mut self, code: Code) -> &mut Section { + let sec = Section::Code(code); + let mut hasher = Sha256::new(); + sec.hash(&mut hasher); + let hash = crate::types::hash::Hash(hasher.finalize().into()); + self.set_code_sechash(hash); + self.sections.push(sec); + self.sections.last_mut().unwrap() + } + + /// Get the transaction data hash stored in the header + pub fn data_sechash(&self) -> &crate::types::hash::Hash { + &self.header.data_hash + } + + /// Set the transaction data hash stored in the header + pub fn set_data_sechash(&mut self, hash: crate::types::hash::Hash) { + self.header.data_hash = hash + } + + /// Add the given code to the transaction and set the hash in the header + pub fn set_data(&mut self, data: Data) -> &mut Section { + let sec = Section::Data(data); + let mut hasher = Sha256::new(); + sec.hash(&mut hasher); + let hash = crate::types::hash::Hash(hasher.finalize().into()); + self.set_data_sechash(hash); + self.sections.push(sec); + self.sections.last_mut().unwrap() + } + + /// Get the data designated by the transaction data hash in the header + pub fn data(&self) -> Option> { + match self.get_section(self.data_sechash()) { + Some(Section::Data(data)) => Some(data.data.clone()), + _ => None, + } + } + + /// Convert this transaction into protobufs pub fn to_bytes(&self) -> Vec { let mut bytes = vec![]; - let tx: types::Tx = self.clone().into(); + let tx: types::Tx = types::Tx { + data: self.try_to_vec().expect("encoding a transaction failed"), + }; tx.encode(&mut bytes) .expect("encoding a transaction failed"); bytes } - pub fn hash(&self) -> [u8; 32] { - SigningTx::from(self.clone()).hash() - } - - pub fn unsigned_hash(&self) -> [u8; 32] { - match self.data { - Some(ref data) => { - match SignedTxData::try_from_slice(data) { - Ok(signed_data) => { - // Reconstruct unsigned tx - let unsigned_tx = Tx { - code_or_hash: self.code_or_hash.clone(), - data: signed_data.data, - timestamp: self.timestamp, - chain_id: self.chain_id.clone(), - expiration: self.expiration, - }; - unsigned_tx.hash() - } - Err(_) => { - // Unsigned tx - self.hash() - } + /// Verify that the section with the given hash has been signed by the given + /// public key + pub fn verify_signature( + &self, + pk: &common::PublicKey, + hash: &crate::types::hash::Hash, + ) -> std::result::Result<(), VerifySigError> { + for section in &self.sections { + if let Section::Signature(sig_sec) = section { + if sig_sec.pub_key == *pk && sig_sec.target == *hash { + return common::SigScheme::verify_signature_raw( + pk, + &hash.0, + &sig_sec.signature, + ); } } - None => { - // Unsigned tx - self.hash() + } + Err(VerifySigError::MissingData) + } + + /// Validate any and all ciphertexts stored in this transaction + #[cfg(feature = "ferveo-tpke")] + pub fn validate_ciphertext(&self) -> bool { + let mut valid = true; + for section in &self.sections { + if let Section::Ciphertext(ct) = section { + valid = valid && ct.ciphertext.check( + &::G1Prepared::from( + -::G1Affine::prime_subgroup_generator(), + ) + ); + } + } + valid + } + + /// Decrypt any and all ciphertexts stored in this transaction use the + /// given decryption key + #[cfg(feature = "ferveo-tpke")] + pub fn decrypt( + &mut self, + privkey: ::G2Affine, + ) -> std::result::Result<(), WrapperTxErr> { + // Iterate backwrds to sidestep the effects of deletion on indexing + for i in (0..self.sections.len()).rev() { + if let Section::Ciphertext(ct) = &self.sections[i] { + // Add all the deecrypted sections + self.sections.extend( + ct.decrypt(privkey).map_err(|_| WrapperTxErr::InvalidTx)?, + ); + // Remove the original ciphertext + self.sections.remove(i); } } + self.data().ok_or(WrapperTxErr::DecryptedHash)?; + self.get_section(self.code_sechash()) + .ok_or(WrapperTxErr::DecryptedHash)?; + Ok(()) } - pub fn code_hash(&self) -> [u8; 32] { - SigningTx::from(self.clone()).code_hash + /// Encrypt all sections in this transaction other than the header and + /// signatures over it + #[cfg(feature = "ferveo-tpke")] + pub fn encrypt(&mut self, pubkey: &EncryptionKey) { + let header_hash = self.header_hash(); + let mut plaintexts = vec![]; + // Iterate backwrds to sidestep the effects of deletion on indexing + for i in (0..self.sections.len()).rev() { + match &self.sections[i] { + Section::Signature(sig) if sig.target == header_hash => {} + // Add eligible section to the list of sections to encrypt + _ => plaintexts.push(self.sections.remove(i)), + } + } + // Encrypt all eligible sections in one go + self.sections + .push(Section::Ciphertext(Ciphertext::new(plaintexts, pubkey))); } - /// Sign a transaction using [`SignedTxData`]. - pub fn sign(self, keypair: &common::SecretKey) -> Self { - let code = self.code_or_hash.clone(); - SigningTx::from(self) - .sign(keypair) - .expand(code) - .expect("code hashes to unexpected value") + /// Determines the type of the input Tx + /// + /// If it is a raw Tx, signed or not, the Tx is + /// returned unchanged inside an enum variant stating its type. + /// + /// If it is a decrypted tx, signing it adds no security so we + /// extract the signed data without checking the signature (if it + /// is signed) or return as is. Either way, it is returned in + /// an enum variant stating its type. + /// + /// If it is a WrapperTx, we extract the signed data of + /// the Tx and verify it is of the appropriate form. This means + /// 1. The wrapper tx is indeed signed + /// 2. The signature is valid + pub fn validate_header(&self) -> std::result::Result<(), TxError> { + match &self.header.tx_type { + // verify signature and extract signed data + TxType::Wrapper(wrapper) => { + self.verify_signature(&wrapper.pk, &self.header_hash()) + .map_err(|err| { + TxError::SigError(format!( + "WrapperTx signature verification failed: {}", + err + )) + })?; + Ok(()) + } + // verify signature and extract signed data + #[cfg(feature = "ferveo-tpke")] + TxType::Protocol(protocol) => { + self.verify_signature(&protocol.pk, &self.header_hash()) + .map_err(|err| { + TxError::SigError(format!( + "ProtocolTx signature verification failed: {}", + err + )) + })?; + Ok(()) + } + // we extract the signed data, but don't check the signature + TxType::Decrypted(_) => Ok(()), + // return as is + TxType::Raw => Ok(()), + } } - /// Verify that the transaction has been signed by the secret key - /// counterpart of the given public key. - pub fn verify_sig( - &self, - pk: &common::PublicKey, - sig: &common::Signature, - ) -> std::result::Result<(), VerifySigError> { - SigningTx::from(self.clone()).verify_sig(pk, sig) + /// Filter out all the sections that must not be submitted to the protocol + /// and return them. + pub fn protocol_filter(&mut self) -> Vec
{ + let mut filtered = Vec::new(); + for i in (0..self.sections.len()).rev() { + if let Section::MaspBuilder(_) = self.sections[i] { + // MASP Builders containin extended full viewing keys amongst + // other private information and must be removed prior to + // submission to protocol + filtered.push(self.sections.remove(i)); + } + } + filtered + } + + /// Filter out all the sections that need not be sent to the hardware wallet + /// and return them + pub fn wallet_filter(&mut self) -> Vec
{ + let mut filtered = Vec::new(); + for i in (0..self.sections.len()).rev() { + match &mut self.sections[i] { + // This section is known to be large and can be contracted + Section::Code(section) => { + filtered.push(Section::Code(section.clone())); + section.code.contract(); + } + // This section is known to be large and can be contracted + Section::ExtraData(section) => { + filtered.push(Section::ExtraData(section.clone())); + section.code.contract(); + } + // Everything else is fine to add + _ => {} + } + } + filtered + } +} + +#[cfg(any(feature = "tendermint", feature = "tendermint-abcipp"))] +impl From for ResponseDeliverTx { + #[cfg(not(feature = "ferveo-tpke"))] + fn from(_tx: Tx) -> ResponseDeliverTx { + Default::default() + } + + /// Annotate the Tx with meta-data based on its contents + #[cfg(feature = "ferveo-tpke")] + fn from(tx: Tx) -> ResponseDeliverTx { + use crate::tendermint_proto::abci::{Event, EventAttribute}; + + // If data cannot be extracteed, then attach no events + let tx_data = if let Some(data) = tx.data() { + data + } else { + return Default::default(); + }; + // If the data is not a Transfer, then attach no events + let transfer = if let Ok(transfer) = Transfer::try_from_slice(&tx_data) + { + transfer + } else { + return Default::default(); + }; + // Otherwise attach all Transfer events + let events = vec![Event { + r#type: "transfer".to_string(), + attributes: vec![ + EventAttribute { + key: "source".to_string(), + value: transfer.source.encode(), + index: true, + }, + EventAttribute { + key: "target".to_string(), + value: transfer.target.encode(), + index: true, + }, + EventAttribute { + key: "token".to_string(), + value: transfer.token.encode(), + index: true, + }, + EventAttribute { + key: "amount".to_string(), + value: transfer.amount.to_string(), + index: true, + }, + ], + }]; + ResponseDeliverTx { + events, + info: "Transfer tx".to_string(), + ..Default::default() + } } } @@ -529,34 +1206,6 @@ impl Dkg { mod tests { use super::*; - #[test] - fn test_tx() { - let code = "wasm code".as_bytes().to_owned(); - let data = "arbitrary data".as_bytes().to_owned(); - let chain_id = ChainId::default(); - let tx = - Tx::new(code.clone(), Some(data.clone()), chain_id.clone(), None); - - let bytes = tx.to_bytes(); - let tx_from_bytes = - Tx::try_from(bytes.as_ref()).expect("decoding failed"); - assert_eq!(tx_from_bytes, tx); - - let types_tx = types::Tx { - code_or_hash: code, - data: Some(data), - timestamp: None, - chain_id: chain_id.0, - expiration: None, - }; - let mut bytes = vec![]; - types_tx.encode(&mut bytes).expect("encoding failed"); - match Tx::try_from(bytes.as_ref()) { - Err(Error::NoTimestampError) => {} - _ => panic!("unexpected result"), - } - } - #[test] fn test_dkg_gossip_message() { let data = "arbitrary string".to_owned(); @@ -578,4 +1227,91 @@ mod tests { let dkg_from_types = Dkg::from(types_dkg); assert_eq!(dkg_from_types, dkg); } + + /// Test that encryption and decryption are inverses. + #[cfg(feature = "ferveo-tpke")] + #[test] + fn test_encrypt_decrypt() { + // The trivial public - private keypair + let pubkey = EncryptionKey(::G1Affine::prime_subgroup_generator()); + let privkey = ::G2Affine::prime_subgroup_generator(); + // generate encrypted payload + let plaintext = vec![Section::Data(Data::new( + "Super secret stuff".as_bytes().to_vec(), + ))]; + let encrypted = Ciphertext::new(plaintext.clone(), &pubkey); + // check that encryption doesn't do trivial things + assert_ne!( + encrypted.ciphertext.ciphertext, + plaintext.try_to_vec().expect("Test failed") + ); + // decrypt the payload and check we got original data back + let decrypted = encrypted.decrypt(privkey); + assert_eq!( + decrypted + .expect("Test failed") + .try_to_vec() + .expect("Test failed"), + plaintext.try_to_vec().expect("Test failed"), + ); + } + + /// Test that serializing and deserializing again via Borsh produces + /// original payload + #[cfg(feature = "ferveo-tpke")] + #[test] + fn test_encrypted_tx_round_trip_borsh() { + // The trivial public - private keypair + let pubkey = EncryptionKey(::G1Affine::prime_subgroup_generator()); + let privkey = ::G2Affine::prime_subgroup_generator(); + // generate encrypted payload + let plaintext = vec![Section::Data(Data::new( + "Super secret stuff".as_bytes().to_vec(), + ))]; + let encrypted = Ciphertext::new(plaintext.clone(), &pubkey); + // serialize via Borsh + let borsh = encrypted.try_to_vec().expect("Test failed"); + // deserialize again + let new_encrypted: Ciphertext = + BorshDeserialize::deserialize(&mut borsh.as_ref()) + .expect("Test failed"); + // check that decryption works as expected + let decrypted = new_encrypted.decrypt(privkey); + assert_eq!( + decrypted + .expect("Test failed") + .try_to_vec() + .expect("Test failed"), + plaintext.try_to_vec().expect("Test failed"), + ); + } + + /// Test that serializing and deserializing again via Serde produces + /// original payload + #[cfg(feature = "ferveo-tpke")] + #[test] + fn test_encrypted_tx_round_trip_serde() { + // The trivial public - private keypair + let pubkey = EncryptionKey(::G1Affine::prime_subgroup_generator()); + let privkey = ::G2Affine::prime_subgroup_generator(); + // generate encrypted payload + let plaintext = vec![Section::Data(Data::new( + "Super secret stuff".as_bytes().to_vec(), + ))]; + let encrypted = Ciphertext::new(plaintext.clone(), &pubkey); + // serialize via Serde + let js = serde_json::to_string(&encrypted).expect("Test failed"); + // deserialize it again + let new_encrypted: Ciphertext = + serde_json::from_str(&js).expect("Test failed"); + let decrypted = new_encrypted.decrypt(privkey); + // check that decryption works as expected + assert_eq!( + decrypted + .expect("Test failed") + .try_to_vec() + .expect("Test failed"), + plaintext.try_to_vec().expect("Test failed"), + ); + } } diff --git a/core/src/types/hash.rs b/core/src/types/hash.rs index 080826a415..db45d2ab58 100644 --- a/core/src/types/hash.rs +++ b/core/src/types/hash.rs @@ -31,6 +31,7 @@ pub type HashResult = std::result::Result; #[derive( Clone, + Copy, Debug, Default, PartialOrd, diff --git a/core/src/types/internal.rs b/core/src/types/internal.rs index d13d392381..a15efc8105 100644 --- a/core/src/types/internal.rs +++ b/core/src/types/internal.rs @@ -48,12 +48,14 @@ impl From for HostEnvResult { mod tx_queue { use borsh::{BorshDeserialize, BorshSerialize}; + use crate::proto::Tx; + /// A wrapper for `crate::types::transaction::WrapperTx` to conditionally /// add `has_valid_pow` flag for only used in testnets. #[derive(Debug, Clone, BorshDeserialize, BorshSerialize)] - pub struct WrapperTxInQueue { + pub struct TxInQueue { /// Wrapper tx - pub tx: crate::types::transaction::WrapperTx, + pub tx: Tx, #[cfg(not(feature = "mainnet"))] /// A PoW solution can be used to allow zero-fee testnet /// transactions. @@ -64,23 +66,21 @@ mod tx_queue { #[derive(Default, Debug, Clone, BorshDeserialize, BorshSerialize)] /// Wrapper txs to be decrypted in the next block proposal - pub struct TxQueue(std::collections::VecDeque); + pub struct TxQueue(std::collections::VecDeque); impl TxQueue { /// Add a new wrapper at the back of the queue - pub fn push(&mut self, wrapper: WrapperTxInQueue) { + pub fn push(&mut self, wrapper: TxInQueue) { self.0.push_back(wrapper); } /// Remove the wrapper at the head of the queue - pub fn pop(&mut self) -> Option { + pub fn pop(&mut self) -> Option { self.0.pop_front() } /// Get an iterator over the queue - pub fn iter( - &self, - ) -> impl std::iter::Iterator { + pub fn iter(&self) -> impl std::iter::Iterator { self.0.iter() } @@ -92,11 +92,11 @@ mod tx_queue { /// Get reference to the element at the given index. /// Returns [`None`] if index exceeds the queue lenght. - pub fn get(&self, index: usize) -> Option<&WrapperTxInQueue> { + pub fn get(&self, index: usize) -> Option<&TxInQueue> { self.0.get(index) } } } #[cfg(feature = "ferveo-tpke")] -pub use tx_queue::{TxQueue, WrapperTxInQueue}; +pub use tx_queue::{TxInQueue, TxQueue}; diff --git a/core/src/types/key/common.rs b/core/src/types/key/common.rs index b0da165bd9..8bc51afe4e 100644 --- a/core/src/types/key/common.rs +++ b/core/src/types/key/common.rs @@ -287,6 +287,13 @@ impl super::SigScheme for SigScheme { ); } + fn from_bytes(_seed: [u8; 32]) -> Self::SecretKey { + unimplemented!( + "Cannot generate common signing scheme. Must convert from \ + alternative scheme." + ); + } + fn sign(keypair: &SecretKey, data: impl AsRef<[u8]>) -> Self::Signature { match keypair { SecretKey::Ed25519(kp) => { diff --git a/core/src/types/key/ed25519.rs b/core/src/types/key/ed25519.rs index 052461de9a..9dcde9f3c0 100644 --- a/core/src/types/key/ed25519.rs +++ b/core/src/types/key/ed25519.rs @@ -336,6 +336,10 @@ impl super::SigScheme for SigScheme { SecretKey(Box::new(ed25519_consensus::SigningKey::new(csprng))) } + fn from_bytes(bytes: [u8; 32]) -> SecretKey { + SecretKey(Box::new(ed25519_consensus::SigningKey::from(bytes))) + } + fn sign(keypair: &SecretKey, data: impl AsRef<[u8]>) -> Self::Signature { Signature(keypair.0.sign(data.as_ref())) } diff --git a/core/src/types/key/mod.rs b/core/src/types/key/mod.rs index 157b0f4f5b..8d698d50ae 100644 --- a/core/src/types/key/mod.rs +++ b/core/src/types/key/mod.rs @@ -267,6 +267,8 @@ pub trait SigScheme: Eq + Ord + Debug + Serialize + Default { fn generate(csprng: &mut R) -> Self::SecretKey where R: CryptoRng + RngCore; + /// Instantiate a secret key from the bytes. + fn from_bytes(bytes: [u8; 32]) -> Self::SecretKey; /// Sign the data with a key. fn sign( keypair: &Self::SecretKey, diff --git a/core/src/types/key/secp256k1.rs b/core/src/types/key/secp256k1.rs index d901e46d25..1808ff7534 100644 --- a/core/src/types/key/secp256k1.rs +++ b/core/src/types/key/secp256k1.rs @@ -468,6 +468,13 @@ impl super::SigScheme for SigScheme { SecretKey(Box::new(libsecp256k1::SecretKey::random(csprng))) } + fn from_bytes(sk: [u8; 32]) -> SecretKey { + SecretKey(Box::new( + libsecp256k1::SecretKey::parse_slice(&sk) + .expect("Secret key parsing should not fail."), + )) + } + /// Sign the data with a key fn sign(keypair: &SecretKey, data: impl AsRef<[u8]>) -> Self::Signature { #[cfg(not(any(test, feature = "secp256k1-sign-verify")))] diff --git a/core/src/types/masp.rs b/core/src/types/masp.rs index c0ccb67d1e..5c2b3fb2d4 100644 --- a/core/src/types/masp.rs +++ b/core/src/types/masp.rs @@ -132,7 +132,7 @@ impl<'de> serde::Deserialize<'de> for ExtendedViewingKey { BorshSerialize, BorshDeserialize, )] -pub struct PaymentAddress(masp_primitives::primitives::PaymentAddress, bool); +pub struct PaymentAddress(masp_primitives::sapling::PaymentAddress, bool); impl PaymentAddress { /// Turn this PaymentAddress into a pinned/unpinned one @@ -157,14 +157,14 @@ impl PaymentAddress { } } -impl From for masp_primitives::primitives::PaymentAddress { +impl From for masp_primitives::sapling::PaymentAddress { fn from(addr: PaymentAddress) -> Self { addr.0 } } -impl From for PaymentAddress { - fn from(addr: masp_primitives::primitives::PaymentAddress) -> Self { +impl From for PaymentAddress { + fn from(addr: masp_primitives::sapling::PaymentAddress) -> Self { Self(addr, false) } } @@ -222,7 +222,7 @@ impl FromStr for PaymentAddress { }; let bytes: Vec = FromBase32::from_base32(&base32) .map_err(DecodeError::DecodeBase32)?; - masp_primitives::primitives::PaymentAddress::from_bytes( + masp_primitives::sapling::PaymentAddress::from_bytes( &bytes.try_into().map_err(addr_len_err)?, ) .ok_or_else(addr_data_err) @@ -366,6 +366,14 @@ impl TransferSource { _ => None, } } + + /// Get the contained Address, if any + pub fn address(&self) -> Option
{ + match self { + Self::Address(x) => Some(x.clone()), + _ => None, + } + } } impl Display for TransferSource { diff --git a/core/src/types/time.rs b/core/src/types/time.rs index af596db545..e06dbc21c7 100644 --- a/core/src/types/time.rs +++ b/core/src/types/time.rs @@ -96,7 +96,19 @@ impl From for DurationNanos { pub struct Rfc3339String(pub String); /// A duration in seconds precision. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + serde::Serialize, + serde::Deserialize, +)] +#[serde(try_from = "Rfc3339String", into = "Rfc3339String")] pub struct DateTimeUtc(pub DateTime); impl DateTimeUtc { @@ -154,7 +166,7 @@ impl BorshSerialize for DateTimeUtc { writer: &mut W, ) -> std::io::Result<()> { let raw = self.0.to_rfc3339(); - raw.serialize(writer) + BorshSerialize::serialize(&raw, writer) } } diff --git a/core/src/types/token.rs b/core/src/types/token.rs index 9c1433b464..e6d6aa924c 100644 --- a/core/src/types/token.rs +++ b/core/src/types/token.rs @@ -5,13 +5,13 @@ use std::ops::{Add, AddAssign, Mul, Sub, SubAssign}; use std::str::FromStr; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use masp_primitives::transaction::Transaction; use rust_decimal::prelude::{Decimal, ToPrimitive}; use serde::{Deserialize, Serialize}; use thiserror::Error; use crate::ibc::applications::transfer::Amount as IbcAmount; use crate::types::address::{masp, Address, DecodeError as AddressError}; +use crate::types::hash::Hash; use crate::types::storage::{DbKeySeg, Key, KeySeg}; /// Amount in micro units. For different granularity another representation @@ -491,7 +491,7 @@ pub struct Transfer { /// The unused storage location at which to place TxId pub key: Option, /// Shielded transaction part - pub shielded: Option, + pub shielded: Option, } #[allow(missing_docs)] diff --git a/core/src/types/transaction/decrypted.rs b/core/src/types/transaction/decrypted.rs index 3407179168..59b9965160 100644 --- a/core/src/types/transaction/decrypted.rs +++ b/core/src/types/transaction/decrypted.rs @@ -3,18 +3,21 @@ pub use ark_bls12_381::Bls12_381 as EllipticCurve; /// Integration of Ferveo cryptographic primitives /// to enable decrypting txs. /// *Not wasm compatible* -#[cfg(feature = "ferveo-tpke")] pub mod decrypted_tx { - + #[cfg(feature = "ferveo-tpke")] use ark_ec::PairingEngine; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; + use sha2::{Digest, Sha256}; - use super::EllipticCurve; - use crate::proto::Tx; - use crate::types::chain::ChainId; - use crate::types::transaction::{Hash, TxType, WrapperTx}; - - #[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema)] + #[derive( + Clone, + Debug, + BorshSerialize, + BorshDeserialize, + BorshSchema, + serde::Serialize, + serde::Deserialize, + )] #[allow(clippy::large_enum_variant)] /// Holds the result of attempting to decrypt /// a transaction and the data necessary for @@ -22,11 +25,6 @@ pub mod decrypted_tx { pub enum DecryptedTx { /// The decrypted payload Decrypted { - /// Inner tx. - // For some reason, we get `warning: fields `tx` and - // `has_valid_pow` are never read` even though they are being used! - #[allow(dead_code)] - tx: Tx, #[cfg(not(feature = "mainnet"))] /// A PoW solution can be used to allow zero-fee testnet /// transactions. @@ -38,73 +36,33 @@ pub mod decrypted_tx { has_valid_pow: bool, }, /// The wrapper whose payload could not be decrypted - Undecryptable(WrapperTx), + Undecryptable, } impl DecryptedTx { - /// Convert the inner tx value to bytes - pub fn to_bytes(&self) -> Vec { - match self { - DecryptedTx::Decrypted { - tx, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: _, - } => tx.to_bytes(), - DecryptedTx::Undecryptable(wrapper) => { - wrapper.try_to_vec().unwrap() - } - } - } - - /// Return the hash used as a commitment to the tx's contents in the - /// wrapper tx that includes this tx as an encrypted payload. The - /// commitment is computed on the unsigned tx if tx is signed - pub fn hash_commitment(&self) -> Hash { - match self { - DecryptedTx::Decrypted { - tx, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: _, - } => Hash(tx.unsigned_hash()), - DecryptedTx::Undecryptable(wrapper) => wrapper.tx_hash.clone(), - } + /// Produce a SHA-256 hash of this header + pub fn hash<'a>(&self, hasher: &'a mut Sha256) -> &'a mut Sha256 { + hasher.update( + self.try_to_vec().expect("unable to serialize decrypted tx"), + ); + hasher } } /// Verify that if the encrypted payload was marked /// "undecryptable", we should not be able to decrypt /// it + #[cfg(feature = "ferveo-tpke")] pub fn verify_decrypted_correctly( decrypted: &DecryptedTx, - privkey: ::G2Affine, + mut otx: crate::proto::Tx, + privkey: ::G2Affine, ) -> bool { match decrypted { DecryptedTx::Decrypted { .. } => true, - DecryptedTx::Undecryptable(tx) => tx.decrypt(privkey).is_err(), - } - } - - impl From for Tx { - fn from(decrypted: DecryptedTx) -> Self { - Tx::new( - vec![], - Some( - TxType::Decrypted(decrypted) - .try_to_vec() - .expect("Encrypting transaction should not fail"), - ), - // If undecrytable we cannot extract the ChainId and - // expiration. If instead the tx gets decrypted - // successfully, the correct chain id and - // expiration are serialized inside the data field - // of the Tx, while the ones available - // in the chain_id and expiration field are just placeholders - ChainId(String::new()), - None, - ) + DecryptedTx::Undecryptable => otx.decrypt(privkey).is_err(), } } } -#[cfg(feature = "ferveo-tpke")] pub use decrypted_tx::*; diff --git a/core/src/types/transaction/encrypted.rs b/core/src/types/transaction/encrypted.rs index b73868bbba..277ec6d3fd 100644 --- a/core/src/types/transaction/encrypted.rs +++ b/core/src/types/transaction/encrypted.rs @@ -8,8 +8,6 @@ pub mod encrypted_tx { use ark_ec::PairingEngine; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use borsh::{BorshDeserialize, BorshSerialize}; - use serde::{Deserialize, Serialize}; - use tpke::{encrypt, Ciphertext}; use crate::types::transaction::{AffineCurve, EllipticCurve}; /// The first group in our elliptic curve bilinear pairing @@ -42,180 +40,6 @@ pub mod encrypted_tx { )) } } - - /// We use a specific choice of two groups and bilinear pairing - /// We use a wrapper type to add traits - #[derive(Clone, Debug, Serialize, Deserialize)] - #[serde(from = "SerializedCiphertext")] - #[serde(into = "SerializedCiphertext")] - pub struct EncryptedTx(pub Ciphertext); - - impl EncryptedTx { - /// Encrypt a message to give a new ciphertext - pub fn encrypt(msg: &[u8], pubkey: EncryptionKey) -> Self { - let mut rng = rand::thread_rng(); - Self(encrypt(msg, pubkey.0, &mut rng)) - } - - /// Decrypt a message and return it as raw bytes - pub fn decrypt( - &self, - privkey: ::G2Affine, - ) -> Vec { - tpke::decrypt(&self.0, privkey) - } - } - - impl borsh::ser::BorshSerialize for EncryptedTx { - fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - let Ciphertext { - nonce, - ciphertext, - auth_tag, - } = &self.0; - // Serialize the nonce into bytes - let mut nonce_buffer = Vec::::new(); - nonce - .serialize(&mut nonce_buffer) - .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; - // serialize the auth_tag to bytes - let mut tag_buffer = Vec::::new(); - auth_tag - .serialize(&mut tag_buffer) - .map_err(|err| Error::new(ErrorKind::InvalidData, err))?; - // serialize the three byte arrays - BorshSerialize::serialize( - &(nonce_buffer, ciphertext, tag_buffer), - writer, - ) - } - } - - impl borsh::BorshDeserialize for EncryptedTx { - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - type VecTuple = (Vec, Vec, Vec); - let (nonce, ciphertext, auth_tag): VecTuple = - BorshDeserialize::deserialize(buf)?; - Ok(EncryptedTx(Ciphertext { - nonce: CanonicalDeserialize::deserialize(&*nonce) - .map_err(|err| Error::new(ErrorKind::InvalidData, err))?, - ciphertext, - auth_tag: CanonicalDeserialize::deserialize(&*auth_tag) - .map_err(|err| Error::new(ErrorKind::InvalidData, err))?, - })) - } - } - - impl borsh::BorshSchema for EncryptedTx { - fn add_definitions_recursively( - definitions: &mut std::collections::HashMap< - borsh::schema::Declaration, - borsh::schema::Definition, - >, - ) { - // Encoded as `(Vec, Vec, Vec)` - let elements = "u8".into(); - let definition = borsh::schema::Definition::Sequence { elements }; - definitions.insert("Vec".into(), definition); - let elements = - vec!["Vec".into(), "Vec".into(), "Vec".into()]; - let definition = borsh::schema::Definition::Tuple { elements }; - definitions.insert(Self::declaration(), definition); - } - - fn declaration() -> borsh::schema::Declaration { - "EncryptedTx".into() - } - } - - /// A helper struct for serializing EncryptedTx structs - /// as an opaque blob - #[derive(Clone, Debug, Serialize, Deserialize)] - #[serde(transparent)] - struct SerializedCiphertext { - payload: Vec, - } - - impl From for SerializedCiphertext { - fn from(tx: EncryptedTx) -> Self { - SerializedCiphertext { - payload: tx - .try_to_vec() - .expect("Unable to serialize encrypted transaction"), - } - } - } - - impl From for EncryptedTx { - fn from(ser: SerializedCiphertext) -> Self { - BorshDeserialize::deserialize(&mut ser.payload.as_ref()) - .expect("Unable to deserialize encrypted transactions") - } - } - - #[cfg(test)] - mod test_encrypted_tx { - use ark_ec::AffineCurve; - - use super::*; - - /// Test that encryption and decryption are inverses. - #[test] - fn test_encrypt_decrypt() { - // The trivial public - private keypair - let pubkey = EncryptionKey(::G1Affine::prime_subgroup_generator()); - let privkey = ::G2Affine::prime_subgroup_generator(); - // generate encrypted payload - let encrypted = - EncryptedTx::encrypt("Super secret stuff".as_bytes(), pubkey); - // check that encryption doesn't do trivial things - assert_ne!(encrypted.0.ciphertext, "Super secret stuff".as_bytes()); - // decrypt the payload and check we got original data back - let decrypted = encrypted.decrypt(privkey); - assert_eq!(decrypted, "Super secret stuff".as_bytes()); - } - - /// Test that serializing and deserializing again via Borsh produces - /// original payload - #[test] - fn test_encrypted_tx_round_trip_borsh() { - // The trivial public - private keypair - let pubkey = EncryptionKey(::G1Affine::prime_subgroup_generator()); - let privkey = ::G2Affine::prime_subgroup_generator(); - // generate encrypted payload - let encrypted = - EncryptedTx::encrypt("Super secret stuff".as_bytes(), pubkey); - // serialize via Borsh - let borsh = encrypted.try_to_vec().expect("Test failed"); - // deserialize again - let new_encrypted: EncryptedTx = - BorshDeserialize::deserialize(&mut borsh.as_ref()) - .expect("Test failed"); - // check that decryption works as expected - let decrypted = new_encrypted.decrypt(privkey); - assert_eq!(decrypted, "Super secret stuff".as_bytes()); - } - - /// Test that serializing and deserializing again via Serde produces - /// original payload - #[test] - fn test_encrypted_tx_round_trip_serde() { - // The trivial public - private keypair - let pubkey = EncryptionKey(::G1Affine::prime_subgroup_generator()); - let privkey = ::G2Affine::prime_subgroup_generator(); - // generate encrypted payload - let encrypted = - EncryptedTx::encrypt("Super secret stuff".as_bytes(), pubkey); - // serialize via Serde - let js = serde_json::to_string(&encrypted).expect("Test failed"); - // deserialize it again - let new_encrypted: EncryptedTx = - serde_json::from_str(&js).expect("Test failed"); - let decrypted = new_encrypted.decrypt(privkey); - // check that decryption works as expected - assert_eq!(decrypted, "Super secret stuff".as_bytes()); - } - } } #[cfg(feature = "ferveo-tpke")] diff --git a/core/src/types/transaction/mod.rs b/core/src/types/transaction/mod.rs index e643a52662..5b9069b731 100644 --- a/core/src/types/transaction/mod.rs +++ b/core/src/types/transaction/mod.rs @@ -32,6 +32,8 @@ use crate::types::hash::Hash; use crate::types::ibc::IbcEvent; use crate::types::key::*; use crate::types::storage; +#[cfg(feature = "ferveo-tpke")] +use crate::types::transaction::protocol::ProtocolTx; /// Get the hash of a transaction pub fn hash_tx(tx_bytes: &[u8]) -> Hash { @@ -198,398 +200,297 @@ pub struct InitValidator { pub validator_vp_code_hash: Hash, } -/// Module that includes helper functions for classifying -/// different types of transactions that the ledger -/// must support as well as conversion functions -/// between them. -#[cfg(feature = "ferveo-tpke")] -pub mod tx_types { - use std::convert::TryFrom; +/// Struct that classifies that kind of Tx +/// based on the contents of its data. +#[derive( + Clone, + Debug, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, +)] +pub enum TxType { + /// An ordinary tx + Raw, + /// A Tx that contains an encrypted raw tx + Wrapper(Box), + /// An attempted decryption of a wrapper tx + Decrypted(DecryptedTx), + /// Txs issued by validators as part of internal protocols + #[cfg(feature = "ferveo-tpke")] + Protocol(Box), +} - use thiserror; +impl TxType { + /// Produce a SHA-256 hash of this header + pub fn hash<'a>(&self, hasher: &'a mut Sha256) -> &'a mut Sha256 { + hasher.update(self.try_to_vec().expect("unable to serialize header")); + hasher + } +} +#[cfg(test)] +mod test_process_tx { use super::*; - use crate::proto::{SignedTxData, Tx}; - use crate::types::chain::ChainId; - use crate::types::transaction::protocol::ProtocolTx; + use crate::proto::{Code, Data, Section, Signature, Tx, TxError}; + use crate::types::address::nam; + use crate::types::storage::Epoch; - /// Errors relating to decrypting a wrapper tx and its - /// encrypted payload from a Tx type - #[allow(missing_docs)] - #[derive(thiserror::Error, Debug, PartialEq)] - pub enum TxError { - #[error("{0}")] - Unsigned(String), - #[error("{0}")] - SigError(String), - #[error("Failed to deserialize Tx: {0}")] - Deserialization(String), - } + fn gen_keypair() -> common::SecretKey { + use rand::prelude::ThreadRng; + use rand::thread_rng; - /// Struct that classifies that kind of Tx - /// based on the contents of its data. - #[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema)] - pub enum TxType { - /// An ordinary tx - Raw(Tx), - /// A Tx that contains an encrypted raw tx - Wrapper(WrapperTx), - /// An attempted decryption of a wrapper tx - Decrypted(DecryptedTx), - /// Txs issued by validators as part of internal protocols - Protocol(ProtocolTx), + let mut rng: ThreadRng = thread_rng(); + ed25519::SigScheme::generate(&mut rng).try_to_sk().unwrap() } - impl From for Tx { - fn from(ty: TxType) -> Self { - Tx::new( - vec![], - Some(ty.try_to_vec().unwrap()), - ChainId(String::new()), /* No need to provide a valid - * ChainId or expiration when - * casting back from - * TxType */ - None, - ) + /// Test that process_tx correctly identifies a raw tx with no + /// data and returns an identical copy + #[test] + fn test_process_tx_raw_tx_no_data() { + let mut outer_tx = Tx::new(TxType::Raw); + let code_sec = outer_tx + .set_code(Code::new("wasm code".as_bytes().to_owned())) + .clone(); + outer_tx.validate_header().expect("Test failed"); + match outer_tx.header().tx_type { + TxType::Raw => assert_eq!( + Hash(code_sec.hash(&mut Sha256::new()).finalize_reset().into()), + outer_tx.header.code_hash, + ), + _ => panic!("Test failed: Expected Raw Tx"), } } - /// We deserialize the Tx data; it should be a TxType which - /// tells us how to handle it. Otherwise, we return an error. - /// The exception is when the Tx data field is empty. We - /// allow this and type it as a Raw TxType. - impl TryFrom for TxType { - type Error = std::io::Error; - - fn try_from(tx: Tx) -> std::io::Result { - if let Some(ref data) = tx.data { - BorshDeserialize::deserialize(&mut data.as_ref()) - } else { - // We allow Txs with empty data fields, which we - // will assume to be of Raw TxType - Ok(TxType::Raw(tx)) + /// Test that process_tx correctly identifies tx containing + /// a raw tx with some data and returns an identical copy + /// of the inner data + #[test] + fn test_process_tx_raw_tx_some_data() { + let mut tx = Tx::new(TxType::Raw); + let code_sec = tx + .set_code(Code::new("wasm code".as_bytes().to_owned())) + .clone(); + let data_sec = tx + .set_data(Data::new("transaction data".as_bytes().to_owned())) + .clone(); + + tx.validate_header().expect("Test failed"); + match tx.header().tx_type { + TxType::Raw => { + assert_eq!( + Hash( + code_sec + .hash(&mut Sha256::new()) + .finalize_reset() + .into() + ), + tx.header().code_hash, + ); + assert_eq!( + Hash( + data_sec + .hash(&mut Sha256::new()) + .finalize_reset() + .into() + ), + tx.header().data_hash, + ); } + _ => panic!("Test failed: Expected Raw Tx"), } } - /// Determines the type of the input Tx - /// - /// If it is a raw Tx, signed or not, the Tx is - /// returned unchanged inside an enum variant stating its type. - /// - /// If it is a decrypted tx, signing it adds no security so we - /// extract the signed data without checking the signature (if it - /// is signed) or return as is. Either way, it is returned in - /// an enum variant stating its type. - /// - /// If it is a WrapperTx, we extract the signed data of - /// the Tx and verify it is of the appropriate form. This means - /// 1. The signed Tx data deserializes to a WrapperTx type - /// 2. The wrapper tx is indeed signed - /// 3. The signature is valid - /// - /// We modify the data of the WrapperTx to contain only the signed - /// data if valid and return it wrapped in a enum variant - /// indicating it is a wrapper. Otherwise, an error is - /// returned indicating the signature was not valid - pub fn process_tx(tx: Tx) -> Result { - if let Some(Ok(SignedTxData { - data: Some(data), - ref sig, - })) = tx - .data - .as_ref() - .map(|data| SignedTxData::try_from_slice(&data[..])) - { - let signed_hash = Tx { - code_or_hash: tx.code_or_hash, - data: Some(data.clone()), - timestamp: tx.timestamp, - chain_id: tx.chain_id.clone(), - expiration: tx.expiration, - } - .hash(); - match TxType::try_from(Tx { - code_or_hash: vec![], - data: Some(data), - timestamp: tx.timestamp, - chain_id: tx.chain_id, - expiration: tx.expiration, - }) - .map_err(|err| TxError::Deserialization(err.to_string()))? - { - // verify signature and extract signed data - TxType::Wrapper(wrapper) => { - wrapper.validate_sig(signed_hash, sig)?; - Ok(TxType::Wrapper(wrapper)) - } - // verify signature and extract signed data - TxType::Protocol(protocol) => { - protocol.validate_sig(signed_hash, sig)?; - Ok(TxType::Protocol(protocol)) - } - // we extract the signed data, but don't check the signature - decrypted @ TxType::Decrypted(_) => Ok(decrypted), - // return as is - raw @ TxType::Raw(_) => Ok(raw), - } - } else { - match TxType::try_from(tx) - .map_err(|err| TxError::Deserialization(err.to_string()))? - { - // we only accept signed wrappers - TxType::Wrapper(_) => Err(TxError::Unsigned( - "Wrapper transactions must be signed".into(), - )), - TxType::Protocol(_) => Err(TxError::Unsigned( - "Protocol transactions must be signed".into(), - )), - // return as is - val => Ok(val), + /// Test that process_tx correctly identifies a raw tx with some + /// signed data and returns an identical copy of the inner data + #[test] + fn test_process_tx_raw_tx_some_signed_data() { + let mut tx = Tx::new(TxType::Raw); + let code_sec = tx + .set_code(Code::new("wasm code".as_bytes().to_owned())) + .clone(); + let data_sec = tx + .set_data(Data::new("transaction data".as_bytes().to_owned())) + .clone(); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &gen_keypair(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &gen_keypair(), + ))); + + tx.validate_header().expect("Test failed"); + match tx.header().tx_type { + TxType::Raw => { + assert_eq!( + Hash( + code_sec + .hash(&mut Sha256::new()) + .finalize_reset() + .into() + ), + tx.header().code_hash, + ); + assert_eq!( + Hash( + data_sec + .hash(&mut Sha256::new()) + .finalize_reset() + .into() + ), + tx.header().data_hash, + ); } + _ => panic!("Test failed: Expected Raw Tx"), } } - #[cfg(test)] - mod test_process_tx { - use super::*; - use crate::types::address::nam; - use crate::types::storage::Epoch; - use crate::types::time::DateTimeUtc; - - fn gen_keypair() -> common::SecretKey { - use rand::prelude::ThreadRng; - use rand::thread_rng; - - let mut rng: ThreadRng = thread_rng(); - ed25519::SigScheme::generate(&mut rng).try_to_sk().unwrap() - } - - /// Test that process_tx correctly identifies a raw tx with no - /// data and returns an identical copy - #[test] - fn test_process_tx_raw_tx_no_data() { - let tx = Tx::new( - "wasm code".as_bytes().to_owned(), - None, - ChainId::default(), - None, - ); - - match process_tx(tx.clone()).expect("Test failed") { - TxType::Raw(raw) => assert_eq!(tx, raw), - _ => panic!("Test failed: Expected Raw Tx"), - } - } - - /// Test that process_tx correctly identifies tx containing - /// a raw tx with some data and returns an identical copy - /// of the inner data - #[test] - fn test_process_tx_raw_tx_some_data() { - let inner = Tx::new( - "code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - ChainId::default(), - None, - ); - let tx = Tx::new( - "wasm code".as_bytes().to_owned(), - Some( - TxType::Raw(inner.clone()) - .try_to_vec() - .expect("Test failed"), - ), - inner.chain_id.clone(), - None, - ); - - match process_tx(tx).expect("Test failed") { - TxType::Raw(raw) => assert_eq!(inner, raw), - _ => panic!("Test failed: Expected Raw Tx"), - } - } - - /// Test that process_tx correctly identifies a raw tx with some - /// signed data and returns an identical copy of the inner data - #[test] - fn test_process_tx_raw_tx_some_signed_data() { - let inner = Tx::new( - "code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - ChainId::default(), - None, - ); - let tx = Tx::new( - "wasm code".as_bytes().to_owned(), - Some( - TxType::Raw(inner.clone()) - .try_to_vec() - .expect("Test failed"), - ), - inner.chain_id.clone(), - None, - ) - .sign(&gen_keypair()); - - match process_tx(tx).expect("Test failed") { - TxType::Raw(raw) => assert_eq!(inner, raw), - _ => panic!("Test failed: Expected Raw Tx"), + /// Test that process_tx correctly identifies a wrapper tx with some + /// data and extracts the signed data. + #[test] + fn test_process_tx_wrapper_tx() { + let keypair = gen_keypair(); + // the signed tx + let mut tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: 10.into(), + token: nam(), + }, + &keypair, + Epoch(0), + 0.into(), + #[cfg(not(feature = "mainnet"))] + None, + )))); + tx.set_code(Code::new("wasm code".as_bytes().to_owned())); + tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + tx.add_section(Section::Signature(Signature::new( + &tx.header_hash(), + &keypair, + ))); + tx.encrypt(&Default::default()); + + tx.validate_header().expect("Test failed"); + match tx.header().tx_type { + TxType::Wrapper(_) => { + tx.decrypt(::G2Affine::prime_subgroup_generator()) + .expect("Test failed"); + // assert_eq!(tx, decrypted); } + _ => panic!("Test failed: Expected Wrapper Tx"), } + } - /// Test that process_tx correctly identifies a wrapper tx with some - /// data and extracts the signed data. - #[test] - fn test_process_tx_wrapper_tx() { - let keypair = gen_keypair(); - let tx = Tx::new( - "wasm code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - ChainId::default(), - None, - ); - // the signed tx - let wrapper = WrapperTx::new( - Fee { - amount: 10.into(), - token: nam(), - }, - &keypair, - Epoch(0), - 0.into(), - tx.clone(), - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - ) - .sign(&keypair, tx.chain_id.clone(), Some(DateTimeUtc::now())) - .expect("Test failed"); - - match process_tx(wrapper).expect("Test failed") { - TxType::Wrapper(wrapper) => { - let decrypted = - wrapper.decrypt(::G2Affine::prime_subgroup_generator()) - .expect("Test failed"); - assert_eq!(tx, decrypted); - } - _ => panic!("Test failed: Expected Wrapper Tx"), - } - } + /// Test that process_tx correctly returns an error on a wrapper tx + /// with some unsigned data + #[test] + fn test_process_tx_wrapper_tx_unsigned() { + let keypair = gen_keypair(); + // the signed tx + let mut tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: 10.into(), + token: nam(), + }, + &keypair, + Epoch(0), + 0.into(), + #[cfg(not(feature = "mainnet"))] + None, + )))); + tx.set_code(Code::new("wasm code".as_bytes().to_owned())); + tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + tx.encrypt(&Default::default()); + let result = tx.validate_header().expect_err("Test failed"); + assert_matches!(result, TxError::SigError(_)); + } +} - /// Test that process_tx correctly returns an error on a wrapper tx - /// with some unsigned data - #[test] - fn test_process_tx_wrapper_tx_unsigned() { - let keypair = gen_keypair(); - let tx = Tx::new( - "wasm code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - ChainId::default(), - None, - ); - // the signed tx - let wrapper = WrapperTx::new( - Fee { - amount: 10.into(), - token: nam(), - }, - &keypair, - Epoch(0), - 0.into(), - tx, - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, +/// Test that process_tx correctly identifies a DecryptedTx +/// with some unsigned data and returns an identical copy +#[test] +fn test_process_tx_decrypted_unsigned() { + use crate::proto::{Code, Data, Tx}; + let mut tx = Tx::new(TxType::Decrypted(DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + has_valid_pow: false, + })); + let code_sec = tx + .set_code(Code::new("transaction data".as_bytes().to_owned())) + .clone(); + let data_sec = tx + .set_data(Data::new("transaction data".as_bytes().to_owned())) + .clone(); + tx.validate_header().expect("Test failed"); + match tx.header().tx_type { + TxType::Decrypted(DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + has_valid_pow: _, + }) => { + assert_eq!( + tx.header().code_hash, + Hash(code_sec.hash(&mut Sha256::new()).finalize_reset().into()), ); - - let tx = Tx::new( - vec![], - Some( - TxType::Wrapper(wrapper).try_to_vec().expect("Test failed"), - ), - ChainId::default(), - None, + assert_eq!( + tx.header().data_hash, + Hash(data_sec.hash(&mut Sha256::new()).finalize_reset().into()), ); - let result = process_tx(tx).expect_err("Test failed"); - assert_matches!(result, TxError::Unsigned(_)); } + _ => panic!("Test failed"), } +} - /// Test that process_tx correctly identifies a DecryptedTx - /// with some unsigned data and returns an identical copy - #[test] - fn test_process_tx_decrypted_unsigned() { - let payload = Tx::new( - "transaction data".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - ChainId::default(), - None, - ); - let decrypted = DecryptedTx::Decrypted { - tx: payload.clone(), - #[cfg(not(feature = "mainnet"))] - has_valid_pow: false, - }; - let tx = Tx::from(TxType::Decrypted(decrypted)); - match process_tx(tx).expect("Test failed") { - TxType::Decrypted(DecryptedTx::Decrypted { - tx: processed, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: _, - }) => { - assert_eq!(payload, processed); - } - _ => panic!("Test failed"), - } +/// Test that process_tx correctly identifies a DecryptedTx +/// with some signed data and extracts it without checking +/// signature +#[test] +fn test_process_tx_decrypted_signed() { + use crate::proto::{Code, Data, Section, Signature, Tx}; + fn gen_keypair() -> common::SecretKey { + use rand::prelude::ThreadRng; + use rand::thread_rng; + + let mut rng: ThreadRng = thread_rng(); + ed25519::SigScheme::generate(&mut rng).try_to_sk().unwrap() } - /// Test that process_tx correctly identifies a DecryptedTx - /// with some signed data and extracts it without checking - /// signature - #[test] - fn test_process_tx_decrypted_signed() { - let payload = Tx::new( - "transaction data".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - ChainId::default(), - None, - ); - let decrypted = DecryptedTx::Decrypted { - tx: payload.clone(), + use crate::types::key::Signature as S; + let mut decrypted = Tx::new(TxType::Decrypted(DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + has_valid_pow: false, + })); + // Invalid signed data + let ed_sig = + ed25519::Signature::try_from_slice([0u8; 64].as_ref()).unwrap(); + let mut sig_sec = Signature::new(&decrypted.header_hash(), &gen_keypair()); + sig_sec.signature = common::Signature::try_from_sig(&ed_sig).unwrap(); + decrypted.add_section(Section::Signature(sig_sec)); + // create the tx with signed decrypted data + let code_sec = decrypted + .set_code(Code::new("transaction data".as_bytes().to_owned())) + .clone(); + let data_sec = decrypted + .set_data(Data::new("transaction data".as_bytes().to_owned())) + .clone(); + decrypted.validate_header().expect("Test failed"); + match decrypted.header().tx_type { + TxType::Decrypted(DecryptedTx::Decrypted { #[cfg(not(feature = "mainnet"))] - has_valid_pow: false, - }; - // Invalid signed data - let ed_sig = - ed25519::Signature::try_from_slice([0u8; 64].as_ref()).unwrap(); - let signed = SignedTxData { - data: Some( - TxType::Decrypted(decrypted) - .try_to_vec() - .expect("Test failed"), - ), - sig: common::Signature::try_from_sig(&ed_sig).unwrap(), - }; - // create the tx with signed decrypted data - let tx = Tx::new( - vec![], - Some(signed.try_to_vec().expect("Test failed")), - ChainId::default(), - None, - ); - match process_tx(tx).expect("Test failed") { - TxType::Decrypted(DecryptedTx::Decrypted { - tx: processed, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: _, - }) => { - assert_eq!(payload, processed); - } - _ => panic!("Test failed"), + has_valid_pow: _, + }) => { + assert_eq!( + decrypted.header.code_hash, + Hash(code_sec.hash(&mut Sha256::new()).finalize_reset().into()), + ); + assert_eq!( + decrypted.header.data_hash, + Hash(data_sec.hash(&mut Sha256::new()).finalize_reset().into()), + ); } + _ => panic!("Test failed"), } } - -#[cfg(feature = "ferveo-tpke")] -pub use tx_types::*; diff --git a/core/src/types/transaction/protocol.rs b/core/src/types/transaction/protocol.rs index a0aee560ed..910f401a66 100644 --- a/core/src/types/transaction/protocol.rs +++ b/core/src/types/transaction/protocol.rs @@ -32,14 +32,22 @@ mod protocol_txs { use serde_json; use super::*; - use crate::proto::Tx; + use crate::proto::{Code, Data, Section, Signature, Tx, TxError}; use crate::types::chain::ChainId; use crate::types::key::*; - use crate::types::transaction::{EllipticCurve, TxError, TxType}; + use crate::types::transaction::{Digest, EllipticCurve, Sha256, TxType}; const TX_NEW_DKG_KP_WASM: &str = "tx_update_dkg_session_keypair.wasm"; - #[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema)] + #[derive( + Clone, + Debug, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, + )] /// Txs sent by validators as part of internal protocols pub struct ProtocolTx { /// we require ProtocolTxs be signed @@ -63,49 +71,42 @@ mod protocol_txs { )) }) } + + /// Produce a SHA-256 hash of this section + pub fn hash<'a>(&self, hasher: &'a mut Sha256) -> &'a mut Sha256 { + hasher.update( + self.try_to_vec().expect("unable to serialize protocol"), + ); + hasher + } } /// DKG message wrapper type that adds Borsh encoding. - #[derive(Clone, Debug)] + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct DkgMessage(pub Message); - #[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema)] + #[derive( + Clone, + Debug, + BorshSerialize, + BorshDeserialize, + BorshSchema, + Serialize, + Deserialize, + )] #[allow(clippy::large_enum_variant)] /// Types of protocol messages to be sent pub enum ProtocolTxType { /// Messages to be given to the DKG state machine DKG(DkgMessage), /// Tx requesting a new DKG session keypair - NewDkgKeypair(Tx), + NewDkgKeypair, /// Aggregation of Ethereum state changes /// voted on by validators in last block - EthereumStateUpdate(Tx), + EthereumStateUpdate, } impl ProtocolTxType { - /// Sign a ProtocolTxType and wrap it up in a normal Tx - pub fn sign( - self, - pk: &common::PublicKey, - signing_key: &common::SecretKey, - chain_id: ChainId, - ) -> Tx { - Tx::new( - vec![], - Some( - TxType::Protocol(ProtocolTx { - pk: pk.clone(), - tx: self, - }) - .try_to_vec() - .expect("Could not serialize ProtocolTx"), - ), - chain_id, - None, - ) - .sign(signing_key) - } - /// Create a new tx requesting a new DKG session keypair pub fn request_new_dkg_keypair<'a, F>( data: UpdateDkgSessionKey, @@ -113,7 +114,7 @@ mod protocol_txs { wasm_dir: &'a Path, wasm_loader: F, chain_id: ChainId, - ) -> Self + ) -> Tx where F: FnOnce(&'a str, &'static str) -> Vec, { @@ -123,18 +124,22 @@ mod protocol_txs { .expect("Converting path to string should not fail"), TX_NEW_DKG_KP_WASM, ); - Self::NewDkgKeypair( - Tx::new( - code, - Some( - data.try_to_vec() - .expect("Serializing request should not fail"), - ), - chain_id, - None, - ) - .sign(signing_key), - ) + let mut outer_tx = + Tx::new(TxType::Protocol(Box::new(ProtocolTx { + pk: signing_key.ref_to(), + tx: Self::NewDkgKeypair, + }))); + outer_tx.header.chain_id = chain_id; + outer_tx.set_code(Code::new(code)); + outer_tx.set_data(Data::new( + data.try_to_vec() + .expect("Serializing request should not fail"), + )); + outer_tx.add_section(Section::Signature(Signature::new( + &outer_tx.header_hash(), + signing_key, + ))); + outer_tx } } diff --git a/core/src/types/transaction/wrapper.rs b/core/src/types/transaction/wrapper.rs index 5de138bacd..a92d5720ee 100644 --- a/core/src/types/transaction/wrapper.rs +++ b/core/src/types/transaction/wrapper.rs @@ -1,25 +1,19 @@ /// Integration of Ferveo cryptographic primitives /// to enable encrypted txs inside of normal txs. /// *Not wasm compatible* -#[cfg(feature = "ferveo-tpke")] pub mod wrapper_tx { - use std::convert::TryFrom; - pub use ark_bls12_381::Bls12_381 as EllipticCurve; + #[cfg(feature = "ferveo-tpke")] pub use ark_ec::{AffineCurve, PairingEngine}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use serde::{Deserialize, Serialize}; + use sha2::{Digest, Sha256}; use thiserror::Error; - use crate::proto::Tx; use crate::types::address::Address; - use crate::types::chain::ChainId; use crate::types::key::*; use crate::types::storage::Epoch; - use crate::types::time::DateTimeUtc; use crate::types::token::Amount; - use crate::types::transaction::encrypted::EncryptedTx; - use crate::types::transaction::{EncryptionKey, Hash, TxError, TxType}; /// Minimum fee amount in micro NAMs pub const MIN_FEE: u64 = 100; @@ -56,6 +50,7 @@ pub mod wrapper_tx { BorshSchema, Serialize, Deserialize, + Eq, )] pub struct Fee { /// amount of the fee @@ -80,6 +75,7 @@ pub mod wrapper_tx { BorshSerialize, BorshDeserialize, BorshSchema, + Eq, )] #[serde(from = "u64")] #[serde(into = "u64")] @@ -173,11 +169,6 @@ pub mod wrapper_tx { pub epoch: Epoch, /// Max amount of gas that can be used when executing the inner tx pub gas_limit: GasLimit, - /// the encrypted payload - pub inner_tx: EncryptedTx, - /// sha-2 hash of the inner transaction acting as a commitment - /// the contents of the encrypted payload - pub tx_hash: Hash, #[cfg(not(feature = "mainnet"))] /// A PoW solution can be used to allow zero-fee testnet transactions pub pow_solution: Option, @@ -193,20 +184,15 @@ pub mod wrapper_tx { keypair: &common::SecretKey, epoch: Epoch, gas_limit: GasLimit, - tx: Tx, - encryption_key: EncryptionKey, #[cfg(not(feature = "mainnet"))] pow_solution: Option< crate::ledger::testnet_pow::Solution, >, ) -> WrapperTx { - let inner_tx = EncryptedTx::encrypt(&tx.to_bytes(), encryption_key); Self { fee, pk: keypair.ref_to(), epoch, gas_limit, - inner_tx, - tx_hash: Hash(tx.unsigned_hash()), #[cfg(not(feature = "mainnet"))] pow_solution, } @@ -218,71 +204,12 @@ pub mod wrapper_tx { Address::from(&self.pk) } - /// A validity check on the ciphertext. - pub fn validate_ciphertext(&self) -> bool { - self.inner_tx.0.check(&::G1Prepared::from( - -::G1Affine::prime_subgroup_generator(), - )) - } - - /// Decrypt the wrapped transaction. - /// - /// Will fail if the inner transaction doesn't match the - /// hash commitment or we are unable to recover a - /// valid Tx from the decoded byte stream. - pub fn decrypt( - &self, - privkey: ::G2Affine, - ) -> Result { - // decrypt the inner tx - let decrypted = self.inner_tx.decrypt(privkey); - let decrypted_tx = Tx::try_from(decrypted.as_ref()) - .map_err(|_| WrapperTxErr::InvalidTx)?; - - // check that the hash equals commitment - if decrypted_tx.unsigned_hash() != self.tx_hash.0 { - return Err(WrapperTxErr::DecryptedHash); - } - - Ok(decrypted_tx) - } - - /// Sign the wrapper transaction and convert to a normal Tx type - pub fn sign( - &self, - keypair: &common::SecretKey, - chain_id: ChainId, - expiration: Option, - ) -> Result { - if self.pk != keypair.ref_to() { - return Err(WrapperTxErr::InvalidKeyPair); - } - Ok(Tx::new( - vec![], - Some( - TxType::Wrapper(self.clone()) - .try_to_vec() - .expect("Could not serialize WrapperTx"), - ), - chain_id, - expiration, - ) - .sign(keypair)) - } - - /// Validate the signature of a wrapper tx - pub fn validate_sig( - &self, - signed_data: [u8; 32], - sig: &common::Signature, - ) -> Result<(), TxError> { - common::SigScheme::verify_signature(&self.pk, &signed_data, sig) - .map_err(|err| { - TxError::SigError(format!( - "WrapperTx signature verification failed: {}", - err - )) - }) + /// Produce a SHA-256 hash of this section + pub fn hash<'a>(&self, hasher: &'a mut Sha256) -> &'a mut Sha256 { + hasher.update( + self.try_to_vec().expect("unable to serialize wrapper"), + ); + hasher } } @@ -351,8 +278,9 @@ pub mod wrapper_tx { #[cfg(test)] mod test_wrapper_tx { use super::*; - use crate::proto::SignedTxData; + use crate::proto::{Code, Data, Section, Signature, Tx, TxError}; use crate::types::address::nam; + use crate::types::transaction::{Hash, TxType}; fn gen_keypair() -> common::SecretKey { use rand::prelude::ThreadRng; @@ -367,58 +295,62 @@ pub mod wrapper_tx { #[test] fn test_encryption_round_trip() { let keypair = gen_keypair(); - let tx = Tx::new( - "wasm code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - ChainId::default(), - Some(DateTimeUtc::now()), - ); - - let wrapper = WrapperTx::new( - Fee { - amount: 10.into(), - token: nam(), - }, + let mut wrapper = + Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: 10.into(), + token: nam(), + }, + &keypair, + Epoch(0), + 0.into(), + #[cfg(not(feature = "mainnet"))] + None, + )))); + wrapper.set_code(Code::new("wasm code".as_bytes().to_owned())); + wrapper + .set_data(Data::new("transaction data".as_bytes().to_owned())); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), &keypair, - Epoch(0), - 0.into(), - tx.clone(), - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - ); - assert!(wrapper.validate_ciphertext()); + ))); + let mut encrypted_tx = wrapper.clone(); + encrypted_tx.encrypt(&Default::default()); + assert!(encrypted_tx.validate_ciphertext()); let privkey = ::G2Affine::prime_subgroup_generator(); - let decrypted = wrapper.decrypt(privkey).expect("Test failed"); - assert_eq!(tx, decrypted); + encrypted_tx.decrypt(privkey).expect("Test failed"); + assert_eq!(wrapper.data(), encrypted_tx.data()); + assert_eq!(wrapper.code(), encrypted_tx.code()); } /// We test that when we try to decrypt a tx and it /// does not match the commitment, an error is returned #[test] fn test_decryption_invalid_hash() { - let tx = Tx::new( - "wasm code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - ChainId::default(), - Some(DateTimeUtc::now()), - ); - - let mut wrapper = WrapperTx::new( - Fee { - amount: 10.into(), - token: nam(), - }, - &gen_keypair(), - Epoch(0), - 0.into(), - tx, - Default::default(), - #[cfg(not(feature = "mainnet"))] - None, - ); + let keypair = gen_keypair(); + let mut wrapper = + Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: 10.into(), + token: nam(), + }, + &keypair, + Epoch(0), + 0.into(), + #[cfg(not(feature = "mainnet"))] + None, + )))); + wrapper.set_code(Code::new("wasm code".as_bytes().to_owned())); + wrapper + .set_data(Data::new("transaction data".as_bytes().to_owned())); // give a incorrect commitment to the decrypted contents of the tx - wrapper.tx_hash = Hash([0u8; 32]); + wrapper.set_code_sechash(Hash([0u8; 32])); + wrapper.set_data_sechash(Hash([0u8; 32])); + wrapper.add_section(Section::Signature(Signature::new( + &wrapper.header_hash(), + &keypair, + ))); + wrapper.encrypt(&Default::default()); assert!(wrapper.validate_ciphertext()); let privkey = ::G2Affine::prime_subgroup_generator(); let err = wrapper.decrypt(privkey).expect_err("Test failed"); @@ -430,17 +362,9 @@ pub mod wrapper_tx { /// via the signature. #[test] fn test_malleability_attack_detection() { - let pubkey = ::G1Affine::prime_subgroup_generator(); let keypair = gen_keypair(); - // The intended tx - let tx = Tx::new( - "wasm code".as_bytes().to_owned(), - Some("transaction data".as_bytes().to_owned()), - ChainId::default(), - Some(DateTimeUtc::now()), - ); // the signed tx - let mut tx = WrapperTx::new( + let mut tx = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 10.into(), token: nam(), @@ -448,70 +372,42 @@ pub mod wrapper_tx { &keypair, Epoch(0), 0.into(), - tx, - Default::default(), #[cfg(not(feature = "mainnet"))] None, - ) - .sign(&keypair, ChainId::default(), None) - .expect("Test failed"); - - // we now try to alter the inner tx maliciously - let mut wrapper = if let TxType::Wrapper(wrapper) = - crate::types::transaction::process_tx(tx.clone()) - .expect("Test failed") - { - wrapper - } else { - panic!("Test failed") - }; + )))); - let mut signed_tx_data = - SignedTxData::try_from_slice(&tx.data.unwrap()[..]) - .expect("Test failed"); + tx.set_code(Code::new("wasm code".as_bytes().to_owned())); + tx.set_data(Data::new("transaction data".as_bytes().to_owned())); + tx.add_section(Section::Signature(Signature::new( + &tx.header_hash(), + &keypair, + ))); + // we now try to alter the inner tx maliciously // malicious transaction - let malicious = Tx::new( - "Give me all the money".as_bytes().to_owned(), - None, - ChainId::default(), - None, - ); - // We replace the inner tx with a malicious one - wrapper.inner_tx = EncryptedTx::encrypt( - &malicious.to_bytes(), - EncryptionKey(pubkey), - ); - // We change the commitment appropriately - wrapper.tx_hash = Hash(malicious.unsigned_hash()); + let malicious = "Give me all the money".as_bytes().to_owned(); + tx.set_data(Data::new(malicious.clone())); + tx.encrypt(&Default::default()); // we check ciphertext validity still passes - assert!(wrapper.validate_ciphertext()); + assert!(tx.validate_ciphertext()); // we check that decryption still succeeds - let decrypted = wrapper.decrypt( - ::G2Affine::prime_subgroup_generator() + tx.decrypt( + ::G2Affine::prime_subgroup_generator(), ) .expect("Test failed"); - assert_eq!(decrypted, malicious); - - // we substitute in the modified wrapper - signed_tx_data.data = Some( - TxType::Wrapper(wrapper).try_to_vec().expect("Test failed"), - ); - tx.data = Some(signed_tx_data.try_to_vec().expect("Test failed")); + assert_eq!(tx.data(), Some(malicious)); // check that the signature is not valid - tx.verify_sig(&keypair.ref_to(), &signed_tx_data.sig) + tx.verify_signature(&keypair.ref_to(), &tx.header_hash()) .expect_err("Test failed"); // check that the try from method also fails - let err = crate::types::transaction::process_tx(tx) - .expect_err("Test failed"); + let err = tx.validate_header().expect_err("Test failed"); assert_matches!(err, TxError::SigError(_)); } } } -#[cfg(feature = "ferveo-tpke")] pub use wrapper_tx::*; diff --git a/core/src/types/validity_predicate.rs b/core/src/types/validity_predicate.rs index a9bfb81168..a40fc1f99d 100644 --- a/core/src/types/validity_predicate.rs +++ b/core/src/types/validity_predicate.rs @@ -3,22 +3,17 @@ use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Serialize}; +use crate::proto::Tx; use crate::types::hash::Hash; /// A validity predicate with an input that is intended to be invoked via `eval` /// host function. #[derive( - Debug, - Clone, - PartialEq, - BorshSerialize, - BorshDeserialize, - Serialize, - Deserialize, + Debug, Clone, BorshSerialize, BorshDeserialize, Serialize, Deserialize, )] pub struct EvalVp { /// The VP code hash to `eval` pub vp_code_hash: Hash, /// The input for the `eval`ed VP - pub input: Vec, + pub input: Tx, } diff --git a/encoding_spec/src/main.rs b/encoding_spec/src/main.rs index 5d2fb6db2f..b9e2b034ef 100644 --- a/encoding_spec/src/main.rs +++ b/encoding_spec/src/main.rs @@ -23,7 +23,6 @@ use itertools::Itertools; use lazy_static::lazy_static; use madato::types::TableRow; use namada::ledger::parameters::Parameters; -use namada::proto::SignedTxData; use namada::types::address::Address; use namada::types::key::ed25519::{PublicKey, Signature}; use namada::types::storage::{self, Epoch}; @@ -71,7 +70,6 @@ fn main() -> Result<(), Box> { let public_key_schema = PublicKey::schema_container(); // TODO update after let signature_schema = Signature::schema_container(); - let signed_tx_data_schema = SignedTxData::schema_container(); let init_account_schema = transaction::InitAccount::schema_container(); let init_validator_schema = transaction::InitValidator::schema_container(); let token_transfer_schema = token::Transfer::schema_container(); @@ -97,7 +95,6 @@ fn main() -> Result<(), Box> { definitions.extend(parameters_schema.definitions); definitions.extend(public_key_schema.definitions); definitions.extend(signature_schema.definitions); - definitions.extend(signed_tx_data_schema.definitions); definitions.extend(init_account_schema.definitions); definitions.extend(init_validator_schema.definitions); definitions.extend(token_transfer_schema.definitions); @@ -155,15 +152,6 @@ fn main() -> Result<(), Box> { "https://dev.namada.net/master/rustdoc/namada/types/key/ed25519/struct.Signature.html"); tables.push(signature_table); - let signed_tx_data_definition = definitions - .remove(&signed_tx_data_schema.declaration) - .unwrap(); - let signed_tx_data_table = - definition_to_table(signed_tx_data_schema.declaration, signed_tx_data_definition).with_rust_doc_link( - // TODO update after - "https://dev.namada.net/master/rustdoc/namada/types/key/ed25519/struct.SignedTxData.html"); - tables.push(signed_tx_data_table); - let init_account_definition = definitions .remove(&init_account_schema.declaration) .unwrap(); diff --git a/genesis/e2e-tests-single-node.toml b/genesis/e2e-tests-single-node.toml index 6a1cc634f3..add7242e56 100644 --- a/genesis/e2e-tests-single-node.toml +++ b/genesis/e2e-tests-single-node.toml @@ -200,7 +200,7 @@ light_client_attack_min_slash_rate = 0.001 # minimum amount of nam token to lock min_proposal_fund = 500 # proposal code size in bytes -max_proposal_code_size = 300000 +max_proposal_code_size = 500000 # min proposal period length in epochs min_proposal_period = 3 # max proposal period length in epochs diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 50f14258d7..037cdee6e1 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -42,7 +42,7 @@ pub fn transaction(_attr: TokenStream, input: TokenStream) -> TokenStream { tx_data_len as _, ) }; - let tx_data = slice.to_vec(); + let tx_data = Tx::try_from_slice(slice).unwrap(); // The context on WASM side is only provided by the VM once its // being executed (in here it's implicit). But because we want to @@ -112,7 +112,7 @@ pub fn validity_predicate( tx_data_len as _, ) }; - let tx_data = slice.to_vec(); + let tx_data = Tx::try_from_slice(slice).unwrap(); let slice = unsafe { core::slice::from_raw_parts( diff --git a/proto/types.proto b/proto/types.proto index 710cfcd2ba..44ad70b77e 100644 --- a/proto/types.proto +++ b/proto/types.proto @@ -5,12 +5,7 @@ import "google/protobuf/timestamp.proto"; package types; message Tx { - bytes code_or_hash = 1; - // TODO this optional is useless because it's default on proto3 - optional bytes data = 2; - google.protobuf.Timestamp timestamp = 3; - string chain_id = 4; - optional google.protobuf.Timestamp expiration = 5; + bytes data = 1; } message Dkg { string data = 1; } diff --git a/scripts/generator.sh b/scripts/generator.sh new file mode 100755 index 0000000000..9c8a2de352 --- /dev/null +++ b/scripts/generator.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash + +# A script to generate some transaction test vectors. It must be executed at the +# root of the Namada repository. All transaction types except vote-proposal are +# tested. This is because vote-proposal needs to query RPC for delegation. This +# script assumes that the WASM scripts have already been built using +# `make build-wasm-scripts`. Run `./scripts/online_generator server` to start a +# server and then run `./scripts/online_generator client` to generate the test +# vectors. + +NAMADA_DIR="$(pwd)" +export NAMADA_LEDGER_LOG_PATH="$(pwd)/vectors.json" +export NAMADA_TX_LOG_PATH="$(pwd)/debugs.txt" + +echo '{ + "content": { + "title": "TheTitle", + "authors": "test@test.com", + "discussions-to": "www.github.com/anoma/aip/1", + "created": "2022-03-10T08:54:37Z", + "license": "MIT", + "abstract": "Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices. Quisque viverra varius cursus. Praesent sed mauris gravida, pharetra turpis non, gravida eros. Nullam sed ex justo. Ut at placerat ipsum, sit amet rhoncus libero. Sed blandit non purus non suscipit. Phasellus sed quam nec augue bibendum bibendum ut vitae urna. Sed odio diam, ornare nec sapien eget, congue viverra enim.", + "motivation": "Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices.", + "details": "Ut convallis eleifend orci vel venenatis. Duis vulputate metus in lacus sollicitudin vestibulum. Suspendisse vel velit ac est consectetur feugiat nec ac urna. Ut faucibus ex nec dictum fermentum. Morbi aliquet purus at sollicitudin ultrices. Quisque viverra varius cursus. Praesent sed mauris gravida, pharetra turpis non, gravida eros.", + "requires": "2" + }, + "author": "atest1v4ehgw36xvcyyvejgvenxs34g3zygv3jxqunjd6rxyeyys3sxy6rwvfkx4qnj33hg9qnvse4lsfctw", + "voting_start_epoch": 12, + "voting_end_epoch": 24, + "grace_epoch": 30, + "type": { "Default": "'"$NAMADA_DIR"'/wasm_for_tests/tx_no_op.wasm" } +} +' > valid_proposal.json + +if [ "$#" -ne 1 ]; then + echo "Illegal number of parameters" +elif [ "$1" = "server" ]; then + cp genesis/e2e-tests-single-node.toml genesis/test-vectors-single-node.toml + + sed -i 's/^epochs_per_year = 31_536_000$/epochs_per_year = 262_800/' genesis/test-vectors-single-node.toml + + NAMADA_GENESIS_FILE=$(cargo run --bin namadac -- --mode validator utils init-network --genesis-path genesis/test-vectors-single-node.toml --wasm-checksums-path wasm/checksums.json --chain-prefix e2e-test --unsafe-dont-encrypt --localhost --allow-duplicate-ip | grep 'Genesis file generated at ' | sed 's/^Genesis file generated at //') + + rm genesis/test-vectors-single-node.toml + + NAMADA_BASE_DIR=${NAMADA_GENESIS_FILE%.toml} + + cp wasm/*.wasm $NAMADA_BASE_DIR/wasm/ + + cp wasm/*.wasm $NAMADA_BASE_DIR/setup/validator-0/.namada/$(basename $NAMADA_BASE_DIR)/wasm/ + + cp $NAMADA_BASE_DIR/setup/other/wallet.toml $NAMADA_BASE_DIR/wallet.toml + + cargo run --bin namadan -- --mode validator --base-dir $NAMADA_BASE_DIR/setup/validator-0/.namada/ ledger +elif [ "$1" = "client" ]; then + echo > $NAMADA_TX_LOG_PATH + + echo $'[' > $NAMADA_LEDGER_LOG_PATH + + cargo run --bin namadac --features std -- transfer --source bertha --target christel --token btc --amount 23 --force --signing-key bertha-key --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- bond --validator bertha --amount 25 --signing-key bertha-key --force --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- reveal-pk --public-key albert-key --force --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- update --code-path vp_user.wasm --address bertha --signing-key bertha-key --force --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- init-validator --alias bertha-validator --source bertha --commission-rate 0.05 --max-commission-rate-change 0.01 --signing-key bertha-key --unsafe-dont-encrypt --force --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- unbond --validator christel --amount 5 --signing-key christel-key --force --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- withdraw --validator albert --signing-key albert-key --force --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- init-account --alias albert-account --source albert --public-key albert-key --signing-key albert-key --force --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- tx --code-path $NAMADA_DIR/wasm_for_tests/tx_no_op.wasm --data-path README.md --signing-key albert-key --force --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- ibc-transfer --source bertha --receiver christel --token btc --amount 24 --channel-id channel-141 --signing-key bertha-key --force --ledger-address 127.0.0.1:27657 + + #cargo run --bin namadac -- init-proposal --data-path valid_proposal.json --epoch 2 --force --signing-key bertha-key --ledger-address 127.0.0.1:27657 + + cargo run --bin namadaw -- masp add --alias a_spending_key --value xsktest1qqqqqqqqqqqqqq9v0sls5r5de7njx8ehu49pqgmqr9ygelg87l5x8y4s9r0pjlvu69au6gn3su5ewneas486hdccyayx32hxvt64p3d0hfuprpgcgv2q9gdx3jvxrn02f0nnp3jtdd6f5vwscfuyum083cvfv4jun75ak5sdgrm2pthzj3sflxc0jx0edrakx3vdcngrfjmru8ywkguru8mxss2uuqxdlglaz6undx5h8w7g70t2es850g48xzdkqay5qs0yw06rtxcvedhsv --unsafe-dont-encrypt + + cargo run --bin namadaw -- masp add --alias b_spending_key --value xsktest1qqqqqqqqqqqqqqpagte43rsza46v55dlz8cffahv0fnr6eqacvnrkyuf9lmndgal7c2k4r7f7zu2yr5rjwr374unjjeuzrh6mquzy6grfdcnnu5clzaq2llqhr70a8yyx0p62aajqvrqjxrht3myuyypsvm725uyt5vm0fqzrzuuedtf6fala4r4nnazm9y9hq5yu6pq24arjskmpv4mdgfn3spffxxv8ugvym36kmnj45jcvvmm227vqjm5fq8882yhjsq97p7xrwqqd82s0 --unsafe-dont-encrypt + + cargo run --bin namadaw -- masp add --alias ab_payment_address --value patest1dxj5kfjvm27rk5wg8ym0mjrhthz6whagdfj9krqfvyszffh4n0mx9f7cauvz6tr43vp22qgsefr + + cargo run --bin namadaw -- masp add --alias aa_payment_address --value patest1a8sfz9c6axdhn925e5qrgzz86msq6yj4uhmxayynucea7gssepk89dgqkx00srfkn4m6kt9jpau + + cargo run --bin namadaw -- masp add --alias bb_payment_address --value patest1vqe0vyxh6wmhahwa52gthgd6edgqxfmgyv8e94jtwn55mdvpvylcyqnp59595272qrz3zxn0ysg + + cargo run --bin namadac --features std -- --mode full transfer --source albert --target aa_payment_address --token btc --amount 20 --force --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- --mode full transfer --source a_spending_key --target ab_payment_address --token btc --amount 7 --force --ledger-address 127.0.0.1:27657 + + until cargo run --bin namadac -- --mode full epoch --ledger-address 127.0.0.1:27657 | grep -m1 "Last committed epoch: 2" ; do sleep 10 ; done; + + cargo run --bin namadac --features std -- --mode full transfer --source a_spending_key --target bb_payment_address --token btc --amount 7 --force --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- --mode full transfer --source a_spending_key --target bb_payment_address --token btc --amount 6 --force --ledger-address 127.0.0.1:27657 + + cargo run --bin namadac --features std -- --mode full transfer --source b_spending_key --target bb_payment_address --token btc --amount 6 --force --ledger-address 127.0.0.1:27657 + + perl -0777 -i.original -pe 's/,\s*$//igs' $NAMADA_LEDGER_LOG_PATH + + echo $'\n]' >> $NAMADA_LEDGER_LOG_PATH +fi + +rm valid_proposal.json diff --git a/shared/Cargo.toml b/shared/Cargo.toml index e8953d6bd8..437a2c0c92 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -13,6 +13,7 @@ default = ["abciplus", "namada-sdk"] mainnet = [ "namada_core/mainnet", ] +std = [] # NOTE "dev" features that shouldn't be used in live networks are enabled by default for now dev = [] ferveo-tpke = [ @@ -40,20 +41,7 @@ tendermint-rpc = [ "async-client", "dep:tendermint-rpc", ] -tendermint-rpc-abcipp = [ - "async-client", - "dep:tendermint-rpc-abcipp", -] -abcipp = [ - "namada_core/abcipp", - "ibc-proto-abcipp", - "ibc-abcipp", - "tendermint-abcipp", - "tendermint-proto-abcipp", - # it's OK to include the tendermint-rpc feature here, as we aren't currently building wasms with `abcipp` - "tendermint-rpc-abcipp", -] abciplus = [ "namada_core/abciplus", "namada_proof_of_stake/abciplus", @@ -66,9 +54,6 @@ abciplus = [ ibc-mocks = [ "namada_core/ibc-mocks", ] -ibc-mocks-abcipp = [ - "namada_core/ibc-mocks-abcipp", -] masp-tx-gen = [ "rand", @@ -91,22 +76,23 @@ namada-sdk = [ "masp_primitives/transparent-inputs", ] +multicore = ["bellman/multicore", "namada_core/multicore", "masp_proofs/multicore"] + [dependencies] async-std = "1.11.0" namada_core = {path = "../core", default-features = false, features = ["secp256k1-sign-verify"]} namada_proof_of_stake = {path = "../proof_of_stake", default-features = false} async-trait = {version = "0.1.51", optional = true} -bellman = "0.11.2" +bellman = { version = "0.11.2", default-features = false, features = ["groth16"] } bls12_381 = "0.6.1" borsh = "0.9.0" circular-queue = "0.2.6" # Using unreleased commit on top of version 0.5.0 that adds Sync to the CLruCache clru = {git = "https://github.com/marmeladema/clru-rs.git", rev = "71ca566"} data-encoding = "2.3.2" +derivation-path = "0.2.0" derivative = "2.2.0" # TODO using the same version of tendermint-rs as we do here. -ibc-abcipp = {package = "ibc", git = "https://github.com/heliaxdev/cosmos-ibc-rs", rev = "db14744bfba6239cc5f58345ff90f8b7d42637d6", features = ["serde"], optional = true} -ibc-proto-abcipp = {package = "ibc-proto", git = "https://github.com/heliaxdev/ibc-proto-rs", rev = "dd8ba23110a144ffe2074a0b889676468266435a", default-features = false, optional = true} ibc = {version = "0.36.0", default-features = false, features = ["serde"], optional = true} ibc-proto = {version = "0.26.0", default-features = false, optional = true} itertools = "0.10.0" @@ -118,16 +104,17 @@ proptest = {git = "https://github.com/heliaxdev/proptest", rev = "8f1b4abe7ebd35 prost = "0.11.6" pwasm-utils = {git = "https://github.com/heliaxdev/wasm-utils", tag = "v0.20.0", features = ["sign_ext"], optional = true} rayon = {version = "=1.5.3", optional = true} +ripemd = "0.1" rust_decimal = "=1.26.1" rust_decimal_macros = "=1.26.1" serde = {version = "1.0.125", features = ["derive"]} serde_json = "1.0.62" sha2 = "0.9.3" +slip10_ed25519 = "0.1.3" +tiny-bip39 = {git = "https://github.com/anoma/tiny-bip39.git", rev = "bf0f6d8713589b83af7a917366ec31f5275c0e57"} +tiny-hderive = "0.3.0" # We switch off "blake2b" because it cannot be compiled to wasm tempfile = {version = "3.2.0", optional = true} -tendermint-abcipp = {package = "tendermint", git = "https://github.com/heliaxdev/tendermint-rs", rev = "02b256829e80f8cfecf3fa0d625c2a76c79cd043", optional = true} -tendermint-rpc-abcipp = {package = "tendermint-rpc", git = "https://github.com/heliaxdev/tendermint-rs", rev = "02b256829e80f8cfecf3fa0d625c2a76c79cd043", features = ["http-client"], optional = true} -tendermint-proto-abcipp = {package = "tendermint-proto", git = "https://github.com/heliaxdev/tendermint-rs", rev = "02b256829e80f8cfecf3fa0d625c2a76c79cd043", optional = true} tendermint = {version = "0.23.6", optional = true} tendermint-rpc = {version = "0.23.6", default-features = false, features = ["trait-client"], optional = true} tendermint-proto = {version = "0.23.6", optional = true} @@ -140,9 +127,9 @@ wasmer-engine-dylib = {version = "=2.2.0", optional = true} wasmer-engine-universal = {version = "=2.2.0", optional = true} wasmer-vm = {version = "2.2.0", optional = true} wasmparser = "0.83.0" -#libmasp = { git = "https://github.com/anoma/masp", branch = "murisi/masp-incentive" } -masp_primitives = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c" } -masp_proofs = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c" } +# branch = "murisi/namada-integration" +masp_primitives = { git = "https://github.com/anoma/masp", rev = "cfea8c95d3f73077ca3e25380fd27e5b46e828fd" } +masp_proofs = { git = "https://github.com/anoma/masp", rev = "cfea8c95d3f73077ca3e25380fd27e5b46e828fd", default-features = false, features = ["local-prover"] } rand = {version = "0.8", default-features = false, optional = true} rand_core = {version = "0.6", default-features = false, optional = true} zeroize = "1.5.5" @@ -154,7 +141,9 @@ tokio = {version = "1.8.2", default-features = false} [dev-dependencies] assert_matches = "1.5.0" async-trait = {version = "0.1.51"} +base58 = "0.2.0" byte-unit = "4.0.13" +hex = "0.4.3" libsecp256k1 = {git = "https://github.com/heliaxdev/libsecp256k1", rev = "bbb3bd44a49db361f21d9db80f9a087c194c0ae9"} namada_test_utils = {path = "../test_utils"} pretty_assertions = "0.7.2" diff --git a/shared/src/ledger/args.rs b/shared/src/ledger/args.rs index 738c371ef3..ffa329b4ee 100644 --- a/shared/src/ledger/args.rs +++ b/shared/src/ledger/args.rs @@ -1,7 +1,10 @@ //! Structures encapsulating SDK arguments +use std::path::PathBuf; + use namada_core::types::chain::ChainId; use namada_core::types::time::DateTimeUtc; use rust_decimal::Decimal; +use zeroize::Zeroizing; use crate::ibc::core::ics24_host::identifier::{ChannelId, PortId}; use crate::types::address::Address; @@ -97,7 +100,7 @@ pub struct TxTransfer { /// Native token address pub native_token: C::NativeAddress, /// Path to the TX WASM code file - pub tx_code_path: C::Data, + pub tx_code_path: PathBuf, } /// IBC transfer transaction arguments @@ -124,7 +127,7 @@ pub struct TxIbcTransfer { /// Timeout timestamp offset pub timeout_sec_offset: Option, /// Path to the TX WASM code file - pub tx_code_path: C::Data, + pub tx_code_path: PathBuf, } /// Transaction to initialize a new account @@ -134,12 +137,10 @@ pub struct TxInitAccount { pub tx: Tx, /// Address of the source account pub source: C::Address, - /// Wasm VP for the new account - pub vp_code: C::Data, /// Path to the VP WASM code file for the new account - pub vp_code_path: C::Data, + pub vp_code_path: PathBuf, /// Path to the TX WASM code file - pub tx_code_path: C::Data, + pub tx_code_path: PathBuf, /// Public key for the new account pub public_key: C::PublicKey, } @@ -164,9 +165,9 @@ pub struct TxInitValidator { /// Maximum commission rate change pub max_commission_rate_change: Decimal, /// Path to the VP WASM code file - pub validator_vp_code_path: C::Data, + pub validator_vp_code_path: PathBuf, /// Path to the TX WASM code file - pub tx_code_path: C::Data, + pub tx_code_path: PathBuf, /// Don't encrypt the keypair pub unsafe_dont_encrypt: bool, } @@ -177,9 +178,9 @@ pub struct TxUpdateVp { /// Common tx arguments pub tx: Tx, /// Path to the VP WASM code file - pub vp_code_path: C::Data, + pub vp_code_path: PathBuf, /// Path to the TX WASM code file - pub tx_code_path: C::Data, + pub tx_code_path: PathBuf, /// Address of the account whose VP is to be updated pub addr: C::Address, } @@ -199,7 +200,7 @@ pub struct Bond { /// Native token address pub native_token: C::NativeAddress, /// Path to the TX WASM code file - pub tx_code_path: C::Data, + pub tx_code_path: PathBuf, } /// Unbond arguments @@ -215,7 +216,7 @@ pub struct Unbond { /// self-bonds, the validator is also the source pub source: Option, /// Path to the TX WASM code file - pub tx_code_path: C::Data, + pub tx_code_path: PathBuf, } /// Reveal public key @@ -254,7 +255,7 @@ pub struct Withdraw { /// from self-bonds, the validator is also the source pub source: Option, /// Path to the TX WASM code file - pub tx_code_path: C::Data, + pub tx_code_path: PathBuf, } /// Query asset conversions @@ -326,7 +327,7 @@ pub struct TxCommissionRateChange { /// Value to which the tx changes the commission rate pub rate: Decimal, /// Path to the TX WASM code file - pub tx_code_path: C::Data, + pub tx_code_path: PathBuf, } /// Query PoS commission rate @@ -400,10 +401,10 @@ pub struct Tx { pub signing_key: Option, /// Sign the tx with the keypair of the public key of the given address pub signer: Option, - /// Path to the TX WASM code file - pub tx_code_path: C::Data, + /// Path to the TX WASM code file to reveal PK + pub tx_reveal_code_path: PathBuf, /// Password to decrypt key - pub password: Option, + pub password: Option>, } /// MASP add key or address arguments @@ -454,6 +455,23 @@ pub struct KeyAndAddressGen { pub alias_force: bool, /// Don't encrypt the keypair pub unsafe_dont_encrypt: bool, + /// BIP44 derivation path + pub derivation_path: Option, +} + +/// Wallet restore key and implicit address arguments +#[derive(Clone, Debug)] +pub struct KeyAndAddressRestore { + /// Scheme type + pub scheme: SchemeType, + /// Key alias + pub alias: Option, + /// Whether to force overwrite the alias, if provided + pub alias_force: bool, + /// Don't encrypt the keypair + pub unsafe_dont_encrypt: bool, + /// BIP44 derivation path + pub derivation_path: Option, } /// Wallet key lookup arguments diff --git a/shared/src/ledger/eth_bridge/vp.rs b/shared/src/ledger/eth_bridge/vp.rs index 479541e181..a2c5588aa8 100644 --- a/shared/src/ledger/eth_bridge/vp.rs +++ b/shared/src/ledger/eth_bridge/vp.rs @@ -5,6 +5,7 @@ use std::collections::BTreeSet; use crate::ledger::native_vp::{Ctx, NativeVp}; use crate::ledger::storage as ledger_storage; use crate::ledger::storage::StorageHasher; +use crate::proto::Tx; use crate::types::address::{Address, InternalAddress}; use crate::types::storage::Key; use crate::vm::WasmCacheAccess; @@ -42,12 +43,12 @@ where fn validate_tx( &self, - _tx_data: &[u8], + _tx_data: &Tx, _keys_changed: &BTreeSet, _verifiers: &BTreeSet
, ) -> Result { tracing::debug!( - tx_data_len = _tx_data.len(), + tx_data_len = _tx_data.data().as_ref().map(Vec::len).unwrap_or(0), keys_changed_len = _keys_changed.len(), verifiers_len = _verifiers.len(), "Validity predicate triggered", diff --git a/shared/src/ledger/events.rs b/shared/src/ledger/events.rs index bc58d09aa5..44425b111e 100644 --- a/shared/src/ledger/events.rs +++ b/shared/src/ledger/events.rs @@ -13,7 +13,7 @@ use crate::ledger::native_vp::governance::utils::ProposalEvent; use crate::tendermint_proto::abci::EventAttribute; use crate::types::ibc::IbcEvent; #[cfg(feature = "ferveo-tpke")] -use crate::types::transaction::{hash_tx, TxType}; +use crate::types::transaction::TxType; /// Indicates if an event is emitted do to /// an individual Tx or the nature of a finalized block @@ -67,42 +67,37 @@ impl Event { /// Creates a new event with the hash and height of the transaction /// already filled in #[cfg(feature = "ferveo-tpke")] - pub fn new_tx_event(tx: &TxType, height: u64) -> Self { - let mut event = match tx { - TxType::Wrapper(wrapper) => { + pub fn new_tx_event(tx: &crate::proto::Tx, height: u64) -> Self { + let mut event = match tx.header().tx_type { + TxType::Wrapper(_) => { let mut event = Event { event_type: EventType::Accepted, level: EventLevel::Tx, attributes: HashMap::new(), }; - event["hash"] = hash_tx( - &wrapper - .try_to_vec() - .expect("Serializing wrapper should not fail"), - ) - .to_string(); + event["hash"] = tx.header_hash().to_string(); event } - TxType::Decrypted(decrypted) => { + TxType::Decrypted(_) => { let mut event = Event { event_type: EventType::Applied, level: EventLevel::Tx, attributes: HashMap::new(), }; - event["hash"] = decrypted.hash_commitment().to_string(); + event["hash"] = tx + .clone() + .update_header(TxType::Raw) + .header_hash() + .to_string(); event } - tx @ TxType::Protocol(_) => { + TxType::Protocol(_) => { let mut event = Event { event_type: EventType::Applied, level: EventLevel::Tx, attributes: HashMap::new(), }; - event["hash"] = hash_tx( - &tx.try_to_vec() - .expect("Serializing protocol tx should not fail"), - ) - .to_string(); + event["hash"] = tx.header_hash().to_string(); event } _ => unreachable!(), diff --git a/shared/src/ledger/ibc/vp/mod.rs b/shared/src/ledger/ibc/vp/mod.rs index dc0a021b6f..2fe1791782 100644 --- a/shared/src/ledger/ibc/vp/mod.rs +++ b/shared/src/ledger/ibc/vp/mod.rs @@ -9,7 +9,6 @@ use std::collections::{BTreeSet, HashSet}; use std::rc::Rc; use std::time::Duration; -use borsh::BorshDeserialize; use context::{PseudoExecutionContext, VpValidationContext}; use namada_core::ledger::ibc::storage::{is_ibc_denom_key, is_ibc_key}; use namada_core::ledger::ibc::{ @@ -17,7 +16,7 @@ use namada_core::ledger::ibc::{ }; use namada_core::ledger::storage::write_log::StorageModification; use namada_core::ledger::storage::{self as ledger_storage, StorageHasher}; -use namada_core::proto::SignedTxData; +use namada_core::proto::Tx; use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::storage::Key; use namada_proof_of_stake::read_pos_params; @@ -73,23 +72,22 @@ where fn validate_tx( &self, - tx_data: &[u8], + tx_data: &Tx, keys_changed: &BTreeSet, _verifiers: &BTreeSet
, ) -> VpResult { - let signed = - SignedTxData::try_from_slice(tx_data).map_err(Error::Decoding)?; - let tx_data = &signed.data.ok_or(Error::NoTxData)?; + let signed = tx_data; + let tx_data = signed.data().ok_or(Error::NoTxData)?; // Pseudo execution and compare them - self.validate_state(tx_data, keys_changed)?; + self.validate_state(&tx_data, keys_changed)?; // Validate the state according to the given IBC message - self.validate_with_msg(tx_data)?; + self.validate_with_msg(&tx_data)?; // Validate the denom store if a denom key has been changed if keys_changed.iter().any(is_ibc_denom_key) { - self.validate_denom(tx_data).map_err(Error::Denom)?; + self.validate_denom(&tx_data).map_err(Error::Denom)?; } Ok(true) @@ -348,13 +346,14 @@ mod tests { use crate::ledger::parameters::EpochDuration; use crate::ledger::{ibc, pos}; use crate::proof_of_stake::parameters::PosParams; - use crate::proto::Tx; + use crate::proto::{Code, Data, Section, Signature, Tx}; use crate::tendermint::time::Time as TmTime; use crate::tendermint_proto::Protobuf as TmProtobuf; use crate::types::key::testing::keypair_1; use crate::types::storage::{BlockHash, BlockHeight, TxIndex}; use crate::types::time::DurationSecs; use crate::types::token::{balance_key, Amount}; + use crate::types::transaction::TxType; use crate::vm::wasm; const ADDRESS: Address = Address::Internal(InternalAddress::Ibc); @@ -710,23 +709,28 @@ mod tests { let tx_code = vec![]; let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); let verifiers = BTreeSet::new(); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.header.chain_id = wl_storage.storage.chain_id.clone(); + outer_tx.set_code(Code::new(tx_code)); + outer_tx.set_data(Data::new(tx_data)); + outer_tx.add_section(Section::Signature(Signature::new( + outer_tx.code_sechash(), + &keypair_1(), + ))); + outer_tx.add_section(Section::Signature(Signature::new( + outer_tx.data_sechash(), + &keypair_1(), + ))); let ctx = Ctx::new( &ADDRESS, &wl_storage.storage, &wl_storage.write_log, - &tx, + &outer_tx, &tx_index, gas_meter, &keys_changed, @@ -737,12 +741,8 @@ mod tests { let ibc = Ibc { ctx }; // this should return true because state has been stored assert!( - ibc.validate_tx( - tx.data.as_ref().unwrap(), - &keys_changed, - &verifiers - ) - .expect("validation failed") + ibc.validate_tx(&outer_tx, &keys_changed, &verifiers) + .expect("validation failed") ); } @@ -791,13 +791,20 @@ mod tests { let tx_code = vec![]; let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); + + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = wl_storage.storage.chain_id.clone(); + tx.set_code(Code::new(tx_code)); + tx.set_data(Data::new(tx_data)); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair_1(), + ))); + let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -817,9 +824,8 @@ mod tests { let ibc = Ibc { ctx }; // this should fail because no state is stored - let result = ibc - .validate_tx(tx.data.as_ref().unwrap(), &keys_changed, &verifiers) - .unwrap_err(); + let result = + ibc.validate_tx(&tx, &keys_changed, &verifiers).unwrap_err(); assert_matches!(result, Error::StateChange(_)); } @@ -927,13 +933,20 @@ mod tests { let tx_code = vec![]; let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); + + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = wl_storage.storage.chain_id.clone(); + tx.set_code(Code::new(tx_code)); + tx.set_data(Data::new(tx_data)); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair_1(), + ))); + let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -953,12 +966,8 @@ mod tests { let ibc = Ibc { ctx }; // this should return true because state has been stored assert!( - ibc.validate_tx( - tx.data.as_ref().unwrap(), - &keys_changed, - &verifiers - ) - .expect("validation failed") + ibc.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") ); } @@ -1037,13 +1046,18 @@ mod tests { let tx_code = vec![]; let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.header.chain_id = wl_storage.storage.chain_id.clone(); + outer_tx.set_code(Code::new(tx_code)); + outer_tx.set_data(Data::new(tx_data)); + outer_tx.add_section(Section::Signature(Signature::new( + outer_tx.code_sechash(), + &keypair_1(), + ))); + outer_tx.add_section(Section::Signature(Signature::new( + outer_tx.data_sechash(), + &keypair_1(), + ))); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -1053,7 +1067,7 @@ mod tests { &ADDRESS, &wl_storage.storage, &wl_storage.write_log, - &tx, + &outer_tx, &tx_index, gas_meter, &keys_changed, @@ -1063,12 +1077,8 @@ mod tests { let ibc = Ibc { ctx }; // this should return true because state has been stored assert!( - ibc.validate_tx( - tx.data.as_ref().unwrap(), - &keys_changed, - &verifiers - ) - .expect("validation failed") + ibc.validate_tx(&outer_tx, &keys_changed, &verifiers) + .expect("validation failed") ); } @@ -1135,13 +1145,20 @@ mod tests { let tx_code = vec![]; let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); + + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = wl_storage.storage.chain_id.clone(); + tx.set_code(Code::new(tx_code)); + tx.set_data(Data::new(tx_data)); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair_1(), + ))); + let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -1160,9 +1177,8 @@ mod tests { ); let ibc = Ibc { ctx }; // this should fail because no event - let result = ibc - .validate_tx(tx.data.as_ref().unwrap(), &keys_changed, &verifiers) - .unwrap_err(); + let result = + ibc.validate_tx(&tx, &keys_changed, &verifiers).unwrap_err(); assert_matches!(result, Error::IbcEvent(_)); } @@ -1262,13 +1278,18 @@ mod tests { let tx_code = vec![]; let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = wl_storage.storage.chain_id.clone(); + tx.set_code(Code::new(tx_code)); + tx.set_data(Data::new(tx_data)); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair_1(), + ))); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -1288,12 +1309,8 @@ mod tests { let ibc = Ibc { ctx }; // this should return true because state has been stored assert!( - ibc.validate_tx( - tx.data.as_ref().unwrap(), - &keys_changed, - &verifiers - ) - .expect("validation failed") + ibc.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") ); } @@ -1373,13 +1390,18 @@ mod tests { let tx_index = TxIndex::default(); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.header.chain_id = wl_storage.storage.chain_id.clone(); + outer_tx.set_code(Code::new(tx_code)); + outer_tx.set_data(Data::new(tx_data)); + outer_tx.add_section(Section::Signature(Signature::new( + outer_tx.code_sechash(), + &keypair_1(), + ))); + outer_tx.add_section(Section::Signature(Signature::new( + outer_tx.data_sechash(), + &keypair_1(), + ))); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -1389,7 +1411,7 @@ mod tests { &ADDRESS, &wl_storage.storage, &wl_storage.write_log, - &tx, + &outer_tx, &tx_index, gas_meter, &keys_changed, @@ -1398,12 +1420,8 @@ mod tests { ); let ibc = Ibc { ctx }; assert!( - ibc.validate_tx( - tx.data.as_ref().unwrap(), - &keys_changed, - &verifiers - ) - .expect("validation failed") + ibc.validate_tx(&outer_tx, &keys_changed, &verifiers) + .expect("validation failed") ); } @@ -1461,13 +1479,18 @@ mod tests { let tx_index = TxIndex::default(); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.header.chain_id = wl_storage.storage.chain_id.clone(); + outer_tx.set_code(Code::new(tx_code)); + outer_tx.set_data(Data::new(tx_data)); + outer_tx.add_section(Section::Signature(Signature::new( + outer_tx.code_sechash(), + &keypair_1(), + ))); + outer_tx.add_section(Section::Signature(Signature::new( + outer_tx.data_sechash(), + &keypair_1(), + ))); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -1477,7 +1500,7 @@ mod tests { &ADDRESS, &wl_storage.storage, &wl_storage.write_log, - &tx, + &outer_tx, &tx_index, gas_meter, &keys_changed, @@ -1486,12 +1509,8 @@ mod tests { ); let ibc = Ibc { ctx }; assert!( - ibc.validate_tx( - tx.data.as_ref().unwrap(), - &keys_changed, - &verifiers - ) - .expect("validation failed") + ibc.validate_tx(&outer_tx, &keys_changed, &verifiers) + .expect("validation failed") ); } @@ -1585,13 +1604,18 @@ mod tests { let tx_code = vec![]; let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.header.chain_id = wl_storage.storage.chain_id.clone(); + outer_tx.set_code(Code::new(tx_code)); + outer_tx.set_data(Data::new(tx_data)); + outer_tx.add_section(Section::Signature(Signature::new( + outer_tx.code_sechash(), + &keypair_1(), + ))); + outer_tx.add_section(Section::Signature(Signature::new( + outer_tx.data_sechash(), + &keypair_1(), + ))); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -1601,7 +1625,7 @@ mod tests { &ADDRESS, &wl_storage.storage, &wl_storage.write_log, - &tx, + &outer_tx, &tx_index, gas_meter, &keys_changed, @@ -1610,12 +1634,8 @@ mod tests { ); let ibc = Ibc { ctx }; assert!( - ibc.validate_tx( - tx.data.as_ref().unwrap(), - &keys_changed, - &verifiers - ) - .expect("validation failed") + ibc.validate_tx(&outer_tx, &keys_changed, &verifiers) + .expect("validation failed") ); } @@ -1710,13 +1730,18 @@ mod tests { let tx_code = vec![]; let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.header.chain_id = wl_storage.storage.chain_id.clone(); + outer_tx.set_code(Code::new(tx_code)); + outer_tx.set_data(Data::new(tx_data)); + outer_tx.add_section(Section::Signature(Signature::new( + outer_tx.code_sechash(), + &keypair_1(), + ))); + outer_tx.add_section(Section::Signature(Signature::new( + outer_tx.data_sechash(), + &keypair_1(), + ))); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -1726,7 +1751,7 @@ mod tests { &ADDRESS, &wl_storage.storage, &wl_storage.write_log, - &tx, + &outer_tx, &tx_index, gas_meter, &keys_changed, @@ -1735,12 +1760,8 @@ mod tests { ); let ibc = Ibc { ctx }; assert!( - ibc.validate_tx( - tx.data.as_ref().unwrap(), - &keys_changed, - &verifiers - ) - .expect("validation failed") + ibc.validate_tx(&outer_tx, &keys_changed, &verifiers) + .expect("validation failed") ); } @@ -1819,13 +1840,18 @@ mod tests { let tx_code = vec![]; let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.header.chain_id = wl_storage.storage.chain_id.clone(); + outer_tx.set_code(Code::new(tx_code)); + outer_tx.set_data(Data::new(tx_data)); + outer_tx.add_section(Section::Signature(Signature::new( + outer_tx.code_sechash(), + &keypair_1(), + ))); + outer_tx.add_section(Section::Signature(Signature::new( + outer_tx.data_sechash(), + &keypair_1(), + ))); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -1835,7 +1861,7 @@ mod tests { &ADDRESS, &wl_storage.storage, &wl_storage.write_log, - &tx, + &outer_tx, &tx_index, gas_meter, &keys_changed, @@ -1844,12 +1870,8 @@ mod tests { ); let ibc = Ibc { ctx }; assert!( - ibc.validate_tx( - tx.data.as_ref().unwrap(), - &keys_changed, - &verifiers - ) - .expect("validation failed") + ibc.validate_tx(&outer_tx, &keys_changed, &verifiers) + .expect("validation failed") ); } @@ -1926,13 +1948,18 @@ mod tests { let tx_code = vec![]; let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = wl_storage.storage.chain_id.clone(); + tx.set_code(Code::new(tx_code)); + tx.set_data(Data::new(tx_data)); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair_1(), + ))); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -1951,12 +1978,8 @@ mod tests { ); let ibc = Ibc { ctx }; assert!( - ibc.validate_tx( - tx.data.as_ref().unwrap(), - &keys_changed, - &verifiers - ) - .expect("validation failed") + ibc.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") ); } @@ -2066,13 +2089,18 @@ mod tests { let tx_code = vec![]; let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = wl_storage.storage.chain_id.clone(); + tx.set_code(Code::new(tx_code)); + tx.set_data(Data::new(tx_data)); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair_1(), + ))); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -2091,12 +2119,8 @@ mod tests { ); let ibc = Ibc { ctx }; assert!( - ibc.validate_tx( - tx.data.as_ref().unwrap(), - &keys_changed, - &verifiers - ) - .expect("validation failed") + ibc.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") ); } @@ -2244,13 +2268,18 @@ mod tests { let tx_code = vec![]; let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = wl_storage.storage.chain_id.clone(); + tx.set_code(Code::new(tx_code)); + tx.set_data(Data::new(tx_data)); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair_1(), + ))); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -2269,12 +2298,8 @@ mod tests { ); let ibc = Ibc { ctx }; assert!( - ibc.validate_tx( - tx.data.as_ref().unwrap(), - &keys_changed, - &verifiers - ) - .expect("validation failed") + ibc.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") ); } @@ -2391,13 +2416,18 @@ mod tests { let tx_code = vec![]; let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = wl_storage.storage.chain_id.clone(); + tx.set_code(Code::new(tx_code)); + tx.set_data(Data::new(tx_data)); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair_1(), + ))); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -2416,12 +2446,8 @@ mod tests { ); let ibc = Ibc { ctx }; assert!( - ibc.validate_tx( - tx.data.as_ref().unwrap(), - &keys_changed, - &verifiers - ) - .expect("validation failed") + ibc.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") ); } @@ -2543,13 +2569,18 @@ mod tests { let tx_code = vec![]; let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = wl_storage.storage.chain_id.clone(); + tx.set_code(Code::new(tx_code)); + tx.set_data(Data::new(tx_data)); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair_1(), + ))); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -2568,12 +2599,8 @@ mod tests { ); let ibc = Ibc { ctx }; assert!( - ibc.validate_tx( - tx.data.as_ref().unwrap(), - &keys_changed, - &verifiers - ) - .expect("validation failed") + ibc.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") ); } @@ -2695,13 +2722,18 @@ mod tests { let tx_code = vec![]; let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx::new( - tx_code, - Some(tx_data), - wl_storage.storage.chain_id.clone(), - None, - ) - .sign(&keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = wl_storage.storage.chain_id.clone(); + tx.set_code(Code::new(tx_code)); + tx.set_data(Data::new(tx_data)); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair_1(), + ))); let gas_meter = VpGasMeter::new(0); let (vp_wasm_cache, _vp_cache_dir) = wasm::compilation_cache::common::testing::cache(); @@ -2720,12 +2752,8 @@ mod tests { ); let ibc = Ibc { ctx }; assert!( - ibc.validate_tx( - tx.data.as_ref().unwrap(), - &keys_changed, - &verifiers - ) - .expect("validation failed") + ibc.validate_tx(&tx, &keys_changed, &verifiers) + .expect("validation failed") ); } } diff --git a/shared/src/ledger/ibc/vp/token.rs b/shared/src/ledger/ibc/vp/token.rs index b735ababe6..da0043046f 100644 --- a/shared/src/ledger/ibc/vp/token.rs +++ b/shared/src/ledger/ibc/vp/token.rs @@ -23,7 +23,7 @@ use crate::ibc_proto::google::protobuf::Any; use crate::ledger::ibc::storage as ibc_storage; use crate::ledger::native_vp::{self, Ctx, NativeVp, VpEnv}; use crate::ledger::storage::{self as ledger_storage, StorageHasher}; -use crate::proto::SignedTxData; +use crate::proto::Tx; use crate::types::address::{Address, InternalAddress}; use crate::types::storage::Key; use crate::types::token::{self, Amount, AmountParseError}; @@ -84,13 +84,12 @@ where fn validate_tx( &self, - tx_data: &[u8], + tx_data: &Tx, keys_changed: &BTreeSet, _verifiers: &BTreeSet
, ) -> Result { - let signed = - SignedTxData::try_from_slice(tx_data).map_err(Error::Decoding)?; - let tx_data = &signed.data.ok_or(Error::NoTxData)?; + let signed = tx_data; + let tx_data = signed.data().ok_or(Error::NoTxData)?; // Check the non-onwer balance updates let ibc_keys_changed: HashSet = keys_changed diff --git a/shared/src/ledger/masp.rs b/shared/src/ledger/masp.rs index 2c43028333..f986e06495 100644 --- a/shared/src/ledger/masp.rs +++ b/shared/src/ledger/masp.rs @@ -10,50 +10,57 @@ use std::ops::Deref; use std::path::PathBuf; use async_trait::async_trait; -use bellman::groth16::{prepare_verifying_key, PreparedVerifyingKey}; -use bls12_381::Bls12; // use async_std::io::prelude::WriteExt; // use async_std::io::{self}; use borsh::{BorshDeserialize, BorshSerialize}; use itertools::Either; use masp_primitives::asset_type::AssetType; -use masp_primitives::consensus::BranchId::Sapling; -use masp_primitives::consensus::{BranchId, TestNetwork}; +use masp_primitives::consensus::TestNetwork; use masp_primitives::convert::AllowedConversion; use masp_primitives::ff::PrimeField; -use masp_primitives::group::cofactor::CofactorGroup; -use masp_primitives::keys::FullViewingKey; -#[cfg(feature = "masp-tx-gen")] -use masp_primitives::legacy::TransparentAddress; +use masp_primitives::group::GroupEncoding; +use masp_primitives::memo::MemoBytes; use masp_primitives::merkle_tree::{ CommitmentTree, IncrementalWitness, MerklePath, }; -use masp_primitives::note_encryption::*; -use masp_primitives::primitives::{Diversifier, Note, ViewingKey}; -use masp_primitives::redjubjub::PublicKey; -use masp_primitives::sapling::Node; +use masp_primitives::sapling::keys::FullViewingKey; +use masp_primitives::sapling::note_encryption::*; +use masp_primitives::sapling::redjubjub::PublicKey; +use masp_primitives::sapling::{ + Diversifier, Node, Note, Nullifier, ViewingKey, +}; #[cfg(feature = "masp-tx-gen")] -use masp_primitives::transaction::builder::{self, secp256k1, *}; +use masp_primitives::transaction::builder::{self, *}; +use masp_primitives::transaction::components::sapling::builder::SaplingMetadata; +use masp_primitives::transaction::components::transparent::builder::TransparentBuilder; use masp_primitives::transaction::components::{ - Amount, ConvertDescription, OutputDescription, SpendDescription, + Amount, ConvertDescription, OutputDescription, SpendDescription, TxOut, }; -#[cfg(feature = "masp-tx-gen")] -use masp_primitives::transaction::components::{OutPoint, TxOut}; +use masp_primitives::transaction::fees::fixed::FeeRule; +use masp_primitives::transaction::sighash::{signature_hash, SignableInput}; +use masp_primitives::transaction::txid::TxIdDigester; use masp_primitives::transaction::{ - signature_hash_data, Transaction, SIGHASH_ALL, + Authorization, Authorized, Transaction, TransactionData, + TransparentAddress, Unauthorized, }; use masp_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}; +use masp_proofs::bellman::groth16::{ + prepare_verifying_key, PreparedVerifyingKey, +}; +use masp_proofs::bls12_381::Bls12; use masp_proofs::prover::LocalTxProver; use masp_proofs::sapling::SaplingVerificationContext; use namada_core::types::transaction::AffineCurve; #[cfg(feature = "masp-tx-gen")] use rand_core::{CryptoRng, OsRng, RngCore}; +use ripemd::Digest as RipemdDigest; #[cfg(feature = "masp-tx-gen")] use sha2::Digest; use crate::ledger::queries::Client; +use crate::ledger::rpc::query_storage_value; use crate::ledger::{args, rpc}; -use crate::proto::{SignedTxData, Tx}; +use crate::proto::Tx; use crate::tendermint_rpc::query::Query; use crate::tendermint_rpc::Order; use crate::types::address::{masp, Address}; @@ -63,14 +70,18 @@ use crate::types::token; use crate::types::token::{ Transfer, HEAD_TX_KEY, PIN_KEY_PREFIX, TX_KEY_PREFIX, }; -use crate::types::transaction::{ - process_tx, DecryptedTx, EllipticCurve, PairingEngine, TxType, WrapperTx, -}; +use crate::types::transaction::{EllipticCurve, PairingEngine, WrapperTx}; /// Env var to point to a dir with MASP parameters. When not specified, /// the default OS specific path is used. pub const ENV_VAR_MASP_PARAMS_DIR: &str = "NAMADA_MASP_PARAMS_DIR"; +/// The network to use for MASP +#[cfg(feature = "mainnet")] +const NETWORK: MainNetwork = MainNetwork; +#[cfg(not(feature = "mainnet"))] +const NETWORK: TestNetwork = TestNetwork; + // TODO these could be exported from masp_proof crate /// Spend circuit name pub const SPEND_NAME: &str = "masp-spend.params"; @@ -81,8 +92,8 @@ pub const CONVERT_NAME: &str = "masp-convert.params"; /// Load Sapling spend params. pub fn load_spend_params() -> ( - bellman::groth16::Parameters, - bellman::groth16::PreparedVerifyingKey, + masp_proofs::bellman::groth16::Parameters, + masp_proofs::bellman::groth16::PreparedVerifyingKey, ) { let params_dir = get_params_dir(); let spend_path = params_dir.join(SPEND_NAME); @@ -94,15 +105,17 @@ pub fn load_spend_params() -> ( panic!("MASP parameters not present or downloadable"); } let param_f = File::open(spend_path).unwrap(); - let params = bellman::groth16::Parameters::read(¶m_f, false).unwrap(); + let params = + masp_proofs::bellman::groth16::Parameters::read(¶m_f, false) + .unwrap(); let vk = prepare_verifying_key(¶ms.vk); (params, vk) } /// Load Sapling convert params. pub fn load_convert_params() -> ( - bellman::groth16::Parameters, - bellman::groth16::PreparedVerifyingKey, + masp_proofs::bellman::groth16::Parameters, + masp_proofs::bellman::groth16::PreparedVerifyingKey, ) { let params_dir = get_params_dir(); let spend_path = params_dir.join(CONVERT_NAME); @@ -114,15 +127,17 @@ pub fn load_convert_params() -> ( panic!("MASP parameters not present or downloadable"); } let param_f = File::open(spend_path).unwrap(); - let params = bellman::groth16::Parameters::read(¶m_f, false).unwrap(); + let params = + masp_proofs::bellman::groth16::Parameters::read(¶m_f, false) + .unwrap(); let vk = prepare_verifying_key(¶ms.vk); (params, vk) } /// Load Sapling output params. pub fn load_output_params() -> ( - bellman::groth16::Parameters, - bellman::groth16::PreparedVerifyingKey, + masp_proofs::bellman::groth16::Parameters, + masp_proofs::bellman::groth16::PreparedVerifyingKey, ) { let params_dir = get_params_dir(); let output_path = params_dir.join(OUTPUT_NAME); @@ -134,27 +149,33 @@ pub fn load_output_params() -> ( panic!("MASP parameters not present or downloadable"); } let param_f = File::open(output_path).unwrap(); - let params = bellman::groth16::Parameters::read(¶m_f, false).unwrap(); + let params = + masp_proofs::bellman::groth16::Parameters::read(¶m_f, false) + .unwrap(); let vk = prepare_verifying_key(¶ms.vk); (params, vk) } /// check_spend wrapper pub fn check_spend( - spend: &SpendDescription, + spend: &SpendDescription<::SaplingAuth>, sighash: &[u8; 32], ctx: &mut SaplingVerificationContext, parameters: &PreparedVerifyingKey, ) -> bool { let zkproof = - bellman::groth16::Proof::read(spend.zkproof.as_slice()).unwrap(); + masp_proofs::bellman::groth16::Proof::read(spend.zkproof.as_slice()); + let zkproof = match zkproof { + Ok(zkproof) => zkproof, + _ => return false, + }; ctx.check_spend( spend.cv, spend.anchor, - &spend.nullifier, + &spend.nullifier.0, PublicKey(spend.rk.0), sighash, - spend.spend_auth_sig.unwrap(), + spend.spend_auth_sig, zkproof, parameters, ) @@ -162,59 +183,121 @@ pub fn check_spend( /// check_output wrapper pub fn check_output( - output: &OutputDescription, + output: &OutputDescription<<::SaplingAuth as masp_primitives::transaction::components::sapling::Authorization>::Proof>, ctx: &mut SaplingVerificationContext, parameters: &PreparedVerifyingKey, ) -> bool { let zkproof = - bellman::groth16::Proof::read(output.zkproof.as_slice()).unwrap(); - ctx.check_output( - output.cv, - output.cmu, - output.ephemeral_key, - zkproof, - parameters, - ) + masp_proofs::bellman::groth16::Proof::read(output.zkproof.as_slice()); + let zkproof = match zkproof { + Ok(zkproof) => zkproof, + _ => return false, + }; + let epk = + masp_proofs::jubjub::ExtendedPoint::from_bytes(&output.ephemeral_key.0); + let epk = match epk.into() { + Some(p) => p, + None => return false, + }; + ctx.check_output(output.cv, output.cmu, epk, zkproof, parameters) } /// check convert wrapper pub fn check_convert( - convert: &ConvertDescription, + convert: &ConvertDescription<<::SaplingAuth as masp_primitives::transaction::components::sapling::Authorization>::Proof>, ctx: &mut SaplingVerificationContext, parameters: &PreparedVerifyingKey, ) -> bool { let zkproof = - bellman::groth16::Proof::read(convert.zkproof.as_slice()).unwrap(); + masp_proofs::bellman::groth16::Proof::read(convert.zkproof.as_slice()); + let zkproof = match zkproof { + Ok(zkproof) => zkproof, + _ => return false, + }; ctx.check_convert(convert.cv, convert.anchor, zkproof, parameters) } +/// Represents an authorization where the Sapling bundle is authorized and the +/// transparent bundle is unauthorized. +pub struct PartialAuthorized; + +impl Authorization for PartialAuthorized { + type SaplingAuth = ::SaplingAuth; + type TransparentAuth = ::TransparentAuth; +} + +/// Partially deauthorize the transparent bundle +fn partial_deauthorize( + tx_data: &TransactionData, +) -> Option> { + let transp = tx_data.transparent_bundle().and_then(|x| { + let mut tb = TransparentBuilder::empty(); + for vin in &x.vin { + tb.add_input(TxOut { + asset_type: vin.asset_type, + value: vin.value, + address: vin.address, + }) + .ok()?; + } + for vout in &x.vout { + tb.add_output(&vout.address, vout.asset_type, vout.value) + .ok()?; + } + tb.build() + }); + if tx_data.transparent_bundle().is_some() != transp.is_some() { + return None; + } + Some(TransactionData::from_parts( + tx_data.version(), + tx_data.consensus_branch_id(), + tx_data.lock_time(), + tx_data.expiry_height(), + transp, + tx_data.sapling_bundle().cloned(), + )) +} + /// Verify a shielded transaction. pub fn verify_shielded_tx(transaction: &Transaction) -> bool { tracing::info!("entered verify_shielded_tx()"); - let mut ctx = SaplingVerificationContext::new(); + let sapling_bundle = if let Some(bundle) = transaction.sapling_bundle() { + bundle + } else { + return false; + }; let tx_data = transaction.deref(); - let (_, spend_pvk) = load_spend_params(); - let (_, convert_pvk) = load_convert_params(); - let (_, output_pvk) = load_output_params(); + // Partially deauthorize the transparent bundle + let unauth_tx_data = match partial_deauthorize(tx_data) { + Some(tx_data) => tx_data, + None => return false, + }; - let sighash: [u8; 32] = - signature_hash_data(tx_data, Sapling, SIGHASH_ALL, None) - .try_into() - .unwrap(); + let txid_parts = unauth_tx_data.digest(TxIdDigester); + // the commitment being signed is shared across all Sapling inputs; once + // V4 transactions are deprecated this should just be the txid, but + // for now we need to continue to compute it here. + let sighash = + signature_hash(&unauth_tx_data, &SignableInput::Shielded, &txid_parts); tracing::info!("sighash computed"); - let spends_valid = tx_data - .shielded_spends - .iter() - .all(|spend| check_spend(spend, &sighash, &mut ctx, &spend_pvk)); - let converts_valid = tx_data + let (_, spend_pvk) = load_spend_params(); + let (_, convert_pvk) = load_convert_params(); + let (_, output_pvk) = load_output_params(); + + let mut ctx = SaplingVerificationContext::new(true); + let spends_valid = sapling_bundle.shielded_spends.iter().all(|spend| { + check_spend(spend, sighash.as_ref(), &mut ctx, &spend_pvk) + }); + let converts_valid = sapling_bundle .shielded_converts .iter() .all(|convert| check_convert(convert, &mut ctx, &convert_pvk)); - let outputs_valid = tx_data + let outputs_valid = sapling_bundle .shielded_outputs .iter() .all(|output| check_output(output, &mut ctx, &output_pvk)); @@ -225,15 +308,17 @@ pub fn verify_shielded_tx(transaction: &Transaction) -> bool { tracing::info!("passed spend/output verification"); - let assets_and_values: Vec<(AssetType, i64)> = - tx_data.value_balance.clone().into_components().collect(); + let assets_and_values: Amount = sapling_bundle.value_balance.clone(); - tracing::info!("accumulated {} assets/values", assets_and_values.len()); + tracing::info!( + "accumulated {} assets/values", + assets_and_values.components().len() + ); let result = ctx.final_check( - assets_and_values.as_slice(), - &sighash, - tx_data.binding_sig.unwrap(), + assets_and_values, + sighash.as_ref(), + sapling_bundle.authorization.binding_sig, ); tracing::info!("final check result {result}"); result @@ -250,9 +335,45 @@ pub fn get_params_dir() -> PathBuf { } } +/// Freeze a Builder into the format necessary for inclusion in a Tx. This is +/// the format used by hardware wallets to validate a MASP Transaction. +struct WalletMap; + +impl + masp_primitives::transaction::components::sapling::builder::MapBuilder< + P1, + ExtendedSpendingKey, + (), + ExtendedFullViewingKey, + > for WalletMap +{ + fn map_params(&self, _s: P1) {} + + fn map_key(&self, s: ExtendedSpendingKey) -> ExtendedFullViewingKey { + (&s).into() + } +} + +impl + MapBuilder< + P1, + R1, + ExtendedSpendingKey, + N1, + (), + (), + ExtendedFullViewingKey, + (), + > for WalletMap +{ + fn map_rng(&self, _s: R1) {} + + fn map_notifier(&self, _s: N1) {} +} + /// Abstracts platform specific details away from the logic of shielded pool /// operations. -#[async_trait] +#[async_trait(?Send)] pub trait ShieldedUtils: Sized + BorshDeserialize + BorshSerialize + Default + Clone { @@ -263,10 +384,10 @@ pub trait ShieldedUtils: fn local_tx_prover(&self) -> LocalTxProver; /// Load up the currently saved ShieldedContext - fn load(self) -> std::io::Result>; + async fn load(self) -> std::io::Result>; /// Sace the given ShieldedContext for future loads - fn save(&self, ctx: &ShieldedContext) -> std::io::Result<()>; + async fn save(&self, ctx: &ShieldedContext) -> std::io::Result<()>; } /// Make a ViewingKey that can view notes encrypted by given ExtendedSpendingKey @@ -347,11 +468,11 @@ pub struct ShieldedContext { /// Maps viewing keys to applicable note positions pub pos_map: HashMap>, /// Maps a nullifier to the note position to which it applies - pub nf_map: HashMap<[u8; 32], usize>, + pub nf_map: HashMap, /// Maps note positions to their corresponding notes pub note_map: HashMap, /// Maps note positions to their corresponding memos - pub memo_map: HashMap, + pub memo_map: HashMap, /// Maps note positions to the diversifier of their payment address pub div_map: HashMap, /// Maps note positions to their witness (used to make merkle paths) @@ -394,15 +515,15 @@ impl Default for ShieldedContext { impl ShieldedContext { /// Try to load the last saved shielded context from the given context /// directory. If this fails, then leave the current context unchanged. - pub fn load(&mut self) -> std::io::Result<()> { - let new_ctx = self.utils.clone().load()?; + pub async fn load(&mut self) -> std::io::Result<()> { + let new_ctx = self.utils.clone().load().await?; *self = new_ctx; Ok(()) } /// Save this shielded context into its associated context directory - pub fn save(&self) -> std::io::Result<()> { - self.utils.save(self) + pub async fn save(&self) -> std::io::Result<()> { + self.utils.save(self).await } /// Merge data from the given shielded context into the current shielded @@ -475,8 +596,9 @@ impl ShieldedContext { } // Update this unknown shielded context until it is level with self while tx_ctx.last_txidx != self.last_txidx { - if let Some(((height, idx), (epoch, tx))) = tx_iter.next() { - tx_ctx.scan_tx(*height, *idx, *epoch, tx); + if let Some(((height, idx), (epoch, tx, stx))) = tx_iter.next() + { + tx_ctx.scan_tx(*height, *idx, *epoch, tx, stx); } else { break; } @@ -491,8 +613,8 @@ impl ShieldedContext { } // Now that we possess the unspent notes corresponding to both old and // new keys up until tx_pos, proceed to scan the new transactions. - for ((height, idx), (epoch, tx)) in &mut tx_iter { - self.scan_tx(*height, *idx, *epoch, tx); + for ((height, idx), (epoch, tx, stx)) in &mut tx_iter { + self.scan_tx(*height, *idx, *epoch, tx, stx); } } @@ -504,7 +626,7 @@ impl ShieldedContext { pub async fn fetch_shielded_transfers( client: &U::C, last_txidx: u64, - ) -> BTreeMap<(BlockHeight, TxIndex), (Epoch, Transfer)> { + ) -> BTreeMap<(BlockHeight, TxIndex), (Epoch, Transfer, Transaction)> { // The address of the MASP account let masp_addr = masp(); // Construct the key where last transaction pointer is stored @@ -512,10 +634,9 @@ impl ShieldedContext { .push(&HEAD_TX_KEY.to_owned()) .expect("Cannot obtain a storage key"); // Query for the index of the last accepted transaction - let head_txidx = - rpc::query_storage_value::(client, &head_tx_key) - .await - .unwrap_or(0); + let head_txidx = query_storage_value::(client, &head_tx_key) + .await + .unwrap_or(0); let mut shielded_txs = BTreeMap::new(); // Fetch all the transactions we do not have yet for i in last_txidx..head_txidx { @@ -524,15 +645,18 @@ impl ShieldedContext { .push(&(TX_KEY_PREFIX.to_owned() + &i.to_string())) .expect("Cannot obtain a storage key"); // Obtain the current transaction - let (tx_epoch, tx_height, tx_index, current_tx) = - rpc::query_storage_value::< + let (tx_epoch, tx_height, tx_index, current_tx, current_stx) = + query_storage_value::< U::C, - (Epoch, BlockHeight, TxIndex, Transfer), + (Epoch, BlockHeight, TxIndex, Transfer, Transaction), >(client, ¤t_tx_key) .await .unwrap(); // Collect the current transaction - shielded_txs.insert((tx_height, tx_index), (tx_epoch, current_tx)); + shielded_txs.insert( + (tx_height, tx_index), + (tx_epoch, current_tx, current_stx), + ); } shielded_txs } @@ -551,17 +675,15 @@ impl ShieldedContext { index: TxIndex, epoch: Epoch, tx: &Transfer, + shielded: &Transaction, ) { - // Ignore purely transparent transactions - let shielded = if let Some(shielded) = &tx.shielded { - shielded - } else { - return; - }; // For tracking the account changes caused by this Transaction let mut transaction_delta = TransactionDelta::new(); // Listen for notes sent to our viewing keys - for so in &shielded.shielded_outputs { + for so in shielded + .sapling_bundle() + .map_or(&vec![], |x| &x.shielded_outputs) + { // Create merkle tree leaf node from note commitment let node = Node::new(so.cmu.to_repr()); // Update each merkle tree in the witness map with the latest @@ -580,12 +702,11 @@ impl ShieldedContext { // Let's try to see if any of our viewing keys can decrypt latest // note for (vk, notes) in self.pos_map.iter_mut() { - let decres = try_sapling_note_decryption::( - 0, - &vk.ivk().0, - &so.ephemeral_key.into_subgroup().unwrap(), - &so.cmu, - &so.enc_ciphertext, + let decres = try_sapling_note_decryption::<_, OutputDescription<<::SaplingAuth as masp_primitives::transaction::components::sapling::Authorization>::Proof>>( + &NETWORK, + 1.into(), + &PreparedIncomingViewingKey::new(&vk.ivk()), + so, ); // So this current viewing key does decrypt this current note... if let Some((note, pa, memo)) = decres { @@ -593,13 +714,13 @@ impl ShieldedContext { // key notes.insert(note_pos); // Compute the nullifier now to quickly recognize when spent - let nf = note.nf(vk, note_pos.try_into().unwrap()); + let nf = note.nf(&vk.nk, note_pos.try_into().unwrap()); self.note_map.insert(note_pos, note); self.memo_map.insert(note_pos, memo); // The payment address' diversifier is required to spend // note self.div_map.insert(note_pos, *pa.diversifier()); - self.nf_map.insert(nf.0, note_pos); + self.nf_map.insert(nf, note_pos); // Note the account changes let balance = transaction_delta .entry(*vk) @@ -615,7 +736,10 @@ impl ShieldedContext { } } // Cancel out those of our notes that have been spent - for ss in &shielded.shielded_spends { + for ss in shielded + .sapling_bundle() + .map_or(&vec![], |x| &x.shielded_spends) + { // If the shielded spend's nullifier is in our map, then target note // is rendered unusable if let Some(note_pos) = self.nf_map.get(&ss.nullifier) { @@ -952,7 +1076,7 @@ impl ShieldedContext { // Check that the supplied viewing key corresponds to given payment // address let counter_owner = viewing_key.to_payment_address( - *masp_primitives::primitives::PaymentAddress::diversifier( + *masp_primitives::sapling::PaymentAddress::diversifier( &owner.into(), ), ); @@ -975,25 +1099,25 @@ impl ShieldedContext { .push(&(TX_KEY_PREFIX.to_owned() + &txidx.to_string())) .expect("Cannot obtain a storage key"); // Obtain the pointed to transaction - let (tx_epoch, _tx_height, _tx_index, tx) = rpc::query_storage_value::< - U::C, - (Epoch, BlockHeight, TxIndex, Transfer), - >(client, &tx_key) - .await - .expect("Ill-formed epoch, transaction pair"); + let (tx_epoch, _tx_height, _tx_index, _tx, shielded) = + rpc::query_storage_value::< + U::C, + (Epoch, BlockHeight, TxIndex, Transfer, Transaction), + >(client, &tx_key) + .await + .expect("Ill-formed epoch, transaction pair"); // Accumulate the combined output note value into this Amount let mut val_acc = Amount::zero(); - let tx = tx - .shielded - .expect("Pinned Transfers should have shielded part"); - for so in &tx.shielded_outputs { + for so in shielded + .sapling_bundle() + .map_or(&vec![], |x| &x.shielded_outputs) + { // Let's try to see if our viewing key can decrypt current note - let decres = try_sapling_note_decryption::( - 0, - &viewing_key.ivk().0, - &so.ephemeral_key.into_subgroup().unwrap(), - &so.cmu, - &so.enc_ciphertext, + let decres = try_sapling_note_decryption::<_, OutputDescription<<::SaplingAuth as masp_primitives::transaction::components::sapling::Authorization>::Proof>>( + &NETWORK, + 1.into(), + &PreparedIncomingViewingKey::new(&viewing_key.ivk()), + so, ); match decres { // So the given viewing key does decrypt this current note... @@ -1090,47 +1214,56 @@ impl ShieldedContext { client: &U::C, args: args::TxTransfer, shielded_gas: bool, - ) -> Result, builder::Error> - { + ) -> Result< + Option<( + Builder<(), (), ExtendedFullViewingKey, ()>, + Transaction, + SaplingMetadata, + Epoch, + )>, + builder::Error, + > { // No shielded components are needed when neither source nor destination // are shielded let spending_key = args.source.spending_key(); let payment_address = args.target.payment_address(); + // No shielded components are needed when neither source nor + // destination are shielded if spending_key.is_none() && payment_address.is_none() { return Ok(None); } // We want to fund our transaction solely from supplied spending key let spending_key = spending_key.map(|x| x.into()); let spending_keys: Vec<_> = spending_key.into_iter().collect(); - // Load the current shielded context given the spending key we possess + // Load the current shielded context given the spending key we + // possess let _ = self.load(); self.fetch(client, &spending_keys, &[]).await; - // Save the update state so that future fetches can be short-circuited + // Save the update state so that future fetches can be + // short-circuited let _ = self.save(); // Determine epoch in which to submit potential shielded transaction let epoch = rpc::query_epoch(client).await; // Context required for storing which notes are in the source's // possesion - let consensus_branch_id = BranchId::Sapling; let amt: u64 = args.amount.into(); - let memo: Option = None; + let memo = MemoBytes::empty(); // Now we build up the transaction within this object - let mut builder = Builder::::new(0u32); + let mut builder = Builder::<_, OsRng>::new(NETWORK, 1.into()); // Convert transaction amount into MASP types let (asset_type, amount) = convert_amount(epoch, &args.token, args.amount); + // The fee to be paid for the transaction + let tx_fee; - // Transactions with transparent input and shielded output - // may be affected if constructed close to epoch boundary - let mut epoch_sensitive: bool = false; // If there are shielded inputs if let Some(sk) = spending_key { // Transaction fees need to match the amount in the wrapper Transfer // when MASP source is used let (_, fee) = convert_amount(epoch, &args.tx.fee_token, args.tx.fee_amount); - builder.set_fee(fee.clone())?; + tx_fee = fee.clone(); // If the gas is coming from the shielded pool, then our shielded // inputs must also cover the gas fee let required_amt = if shielded_gas { amount + fee } else { amount }; @@ -1145,65 +1278,61 @@ impl ShieldedContext { .await; // Commit the notes found to our transaction for (diversifier, note, merkle_path) in unspent_notes { - builder.add_sapling_spend( - sk, - diversifier, - note, - merkle_path, - )?; + builder + .add_sapling_spend(sk, diversifier, note, merkle_path) + .map_err(builder::Error::SaplingBuild)?; } // Commit the conversion notes used during summation for (conv, wit, value) in used_convs.values() { if *value > 0 { - builder.add_convert( - conv.clone(), - *value as u64, - wit.clone(), - )?; + builder + .add_sapling_convert( + conv.clone(), + *value as u64, + wit.clone(), + ) + .map_err(builder::Error::SaplingBuild)?; } } } else { // No transfer fees come from the shielded transaction for non-MASP // sources - builder.set_fee(Amount::zero())?; + tx_fee = Amount::zero(); // We add a dummy UTXO to our transaction, but only the source of // the parent Transfer object is used to validate fund // availability - let secp_sk = secp256k1::SecretKey::from_slice(&[0xcd; 32]) - .expect("secret key"); - let secp_ctx = - secp256k1::Secp256k1::::gen_new(); - let secp_pk = - secp256k1::PublicKey::from_secret_key(&secp_ctx, &secp_sk) - .serialize(); - let hash = ripemd160::Ripemd160::digest( - sha2::Sha256::digest(&secp_pk).as_slice(), - ); - let script = TransparentAddress::PublicKey(hash.into()).script(); - epoch_sensitive = true; - builder.add_transparent_input( - secp_sk, - OutPoint::new([0u8; 32], 0), - TxOut { + let source_enc = args + .source + .address() + .expect("source address should be transparent") + .try_to_vec() + .expect("source address encoding"); + let hash = ripemd::Ripemd160::digest(sha2::Sha256::digest( + source_enc.as_ref(), + )); + let script = TransparentAddress(hash.into()); + builder + .add_transparent_input(TxOut { asset_type, - value: amt, - script_pubkey: script, - }, - )?; + value: amt.try_into().expect("supplied amount too large"), + address: script, + }) + .map_err(builder::Error::TransparentBuild)?; } // Now handle the outputs of this transaction // If there is a shielded output if let Some(pa) = payment_address { let ovk_opt = spending_key.map(|x| x.expsk.ovk); - builder.add_sapling_output( - ovk_opt, - pa.into(), - asset_type, - amt, - memo.clone(), - )?; + builder + .add_sapling_output( + ovk_opt, + pa.into(), + asset_type, + amt, + memo.clone(), + ) + .map_err(builder::Error::SaplingBuild)?; } else { - epoch_sensitive = false; // Embed the transparent target address into the shielded // transaction so that it can be signed let target_enc = args @@ -1212,75 +1341,69 @@ impl ShieldedContext { .expect("target address should be transparent") .try_to_vec() .expect("target address encoding"); - let hash = ripemd160::Ripemd160::digest( - sha2::Sha256::digest(target_enc.as_ref()).as_slice(), - ); - builder.add_transparent_output( - &TransparentAddress::PublicKey(hash.into()), - asset_type, - amt, - )?; + let hash = ripemd::Ripemd160::digest(sha2::Sha256::digest( + target_enc.as_ref(), + )); + builder + .add_transparent_output( + &TransparentAddress(hash.into()), + asset_type, + amt.try_into().expect("supplied amount too large"), + ) + .map_err(builder::Error::TransparentBuild)?; } - let prover = self.utils.local_tx_prover(); - // Build and return the constructed transaction - let mut tx = builder.build(consensus_branch_id, &prover); - - if epoch_sensitive { - let new_epoch = rpc::query_epoch(client).await; - - // If epoch has changed, recalculate shielded outputs to match new - // epoch - if new_epoch != epoch { - // Hack: build new shielded transfer with updated outputs - let mut replay_builder = - Builder::::new(0u32); - replay_builder.set_fee(Amount::zero())?; - let ovk_opt = spending_key.map(|x| x.expsk.ovk); - let (new_asset_type, _) = - convert_amount(new_epoch, &args.token, args.amount); - replay_builder.add_sapling_output( - ovk_opt, - payment_address.unwrap().into(), - new_asset_type, - amt, - memo, - )?; - - let secp_sk = secp256k1::SecretKey::from_slice(&[0xcd; 32]) - .expect("secret key"); - let secp_ctx = - secp256k1::Secp256k1::::gen_new(); - let secp_pk = - secp256k1::PublicKey::from_secret_key(&secp_ctx, &secp_sk) - .serialize(); - let hash = ripemd160::Ripemd160::digest( - sha2::Sha256::digest(&secp_pk).as_slice(), - ); - let script = - TransparentAddress::PublicKey(hash.into()).script(); - replay_builder.add_transparent_input( - secp_sk, - OutPoint::new([0u8; 32], 0), - TxOut { - asset_type: new_asset_type, - value: amt, - script_pubkey: script, - }, - )?; - - let (replay_tx, _) = - replay_builder.build(consensus_branch_id, &prover)?; - tx = tx.map(|(t, tm)| { - let mut temp = t.deref().clone(); - temp.shielded_outputs = replay_tx.shielded_outputs.clone(); - temp.value_balance = temp.value_balance.reject(asset_type) - - Amount::from_pair(new_asset_type, amt).unwrap(); - (temp.freeze().unwrap(), tm) - }); + + // Now add outputs representing the change from this payment + if let Some(sk) = spending_key { + // Represents the amount of inputs we are short by + let mut additional = Amount::zero(); + // The change left over from this transaction + let value_balance = builder + .value_balance() + .expect("unable to compute value balance") + - tx_fee.clone(); + for (asset_type, amt) in value_balance.components() { + if *amt >= 0 { + // Send the change in this asset type back to the sender + builder + .add_sapling_output( + Some(sk.expsk.ovk), + sk.default_address().1, + *asset_type, + *amt as u64, + memo.clone(), + ) + .map_err(builder::Error::SaplingBuild)?; + } else { + // Record how much of the current asset type we are short by + additional += + Amount::from_nonnegative(*asset_type, -*amt).unwrap(); + } + } + // If we are short by a non-zero amount, then we have insufficient + // funds + if additional != Amount::zero() { + return Err(builder::Error::InsufficientFunds(additional)); } } - tx.map(Some) + let prover = if let Ok(params_dir) = env::var(ENV_VAR_MASP_PARAMS_DIR) { + let params_dir = PathBuf::from(params_dir); + let spend_path = params_dir.join(SPEND_NAME); + let convert_path = params_dir.join(CONVERT_NAME); + let output_path = params_dir.join(OUTPUT_NAME); + LocalTxProver::new(&spend_path, &output_path, &convert_path) + } else { + LocalTxProver::with_default_location() + .expect("unable to load MASP Parameters") + }; + // Build and return the constructed transaction + builder + .clone() + .build(&prover, &FeeRule::non_standard(tx_fee)) + .map(|(tx, metadata)| { + Some((builder.map_builder(WalletMap), tx, metadata, epoch)) + }) } /// Obtain the known effects of all accepted shielded and transparent @@ -1405,45 +1528,21 @@ impl ShieldedContext { /// Extract the payload from the given Tx object fn extract_payload( - tx: Tx, + mut tx: Tx, wrapper: &mut Option, transfer: &mut Option, ) { - match process_tx(tx) { - Ok(TxType::Wrapper(wrapper_tx)) => { - let privkey = ::G2Affine::prime_subgroup_generator(); - extract_payload( - Tx::from(match wrapper_tx.decrypt(privkey) { - Ok(tx) => DecryptedTx::Decrypted { - tx, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: false, - }, - _ => DecryptedTx::Undecryptable(wrapper_tx.clone()), - }), - wrapper, - transfer, - ); - *wrapper = Some(wrapper_tx); - } - Ok(TxType::Decrypted(DecryptedTx::Decrypted { - tx, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: _, - })) => { - let empty_vec = vec![]; - let tx_data = tx.data.as_ref().unwrap_or(&empty_vec); - let _ = SignedTxData::try_from_slice(tx_data).map(|signed| { - Transfer::try_from_slice(&signed.data.unwrap()[..]) - .map(|tfer| *transfer = Some(tfer)) - }); - } - _ => {} - } + let privkey = + ::G2Affine::prime_subgroup_generator(); + tx.decrypt(privkey).expect("unable to decrypt transaction"); + *wrapper = tx.header.wrapper(); + let _ = tx.data().map(|signed| { + Transfer::try_from_slice(&signed[..]).map(|tfer| *transfer = Some(tfer)) + }); } /// Make asset type corresponding to given address and epoch -fn make_asset_type(epoch: Epoch, token: &Address) -> AssetType { +pub fn make_asset_type(epoch: Epoch, token: &Address) -> AssetType { // Typestamp the chosen token with the current epoch let token_bytes = (token, epoch.0) .try_to_vec() diff --git a/shared/src/ledger/native_vp/governance/mod.rs b/shared/src/ledger/native_vp/governance/mod.rs index 2e0de7a18f..2c15a8d3a2 100644 --- a/shared/src/ledger/native_vp/governance/mod.rs +++ b/shared/src/ledger/native_vp/governance/mod.rs @@ -15,6 +15,7 @@ use utils::is_valid_validator_voting_period; use crate::ledger::native_vp::{Ctx, NativeVp}; use crate::ledger::storage_api::StorageRead; use crate::ledger::{native_vp, pos}; +use crate::proto::Tx; use crate::types::address::{Address, InternalAddress}; use crate::types::storage::{Epoch, Key}; use crate::types::token; @@ -53,7 +54,7 @@ where fn validate_tx( &self, - tx_data: &[u8], + tx_data: &Tx, keys_changed: &BTreeSet, verifiers: &BTreeSet
, ) -> Result { @@ -100,7 +101,13 @@ where (KeyType::PROPOSAL_COMMIT, _) => { self.is_valid_proposal_commit() } - (KeyType::PARAMETER, _) => self.is_valid_parameter(tx_data), + (KeyType::PARAMETER, _) => self.is_valid_parameter( + if let Some(data) = &tx_data.data() { + data + } else { + return false; + }, + ), (KeyType::BALANCE, _) => self.is_valid_balance(&native_token), (KeyType::UNKNOWN_GOVERNANCE, _) => Ok(false), (KeyType::UNKNOWN, _) => Ok(true), diff --git a/shared/src/ledger/native_vp/mod.rs b/shared/src/ledger/native_vp/mod.rs index f1fbf9944a..82e94ceae6 100644 --- a/shared/src/ledger/native_vp/mod.rs +++ b/shared/src/ledger/native_vp/mod.rs @@ -42,7 +42,7 @@ pub trait NativeVp { /// Run the validity predicate fn validate_tx( &self, - tx_data: &[u8], + tx_data: &Tx, keys_changed: &BTreeSet, verifiers: &BTreeSet
, ) -> std::result::Result; @@ -473,7 +473,7 @@ where fn eval( &self, vp_code_hash: Hash, - input_data: Vec, + input_data: Tx, ) -> Result { #[cfg(feature = "wasm-runtime")] { @@ -532,19 +532,11 @@ where } } - fn verify_tx_signature( - &self, - pk: &crate::types::key::common::PublicKey, - sig: &crate::types::key::common::Signature, - ) -> Result { - Ok(self.tx.verify_sig(pk, sig).is_ok()) - } - fn verify_masp(&self, _tx: Vec) -> Result { unimplemented!("no masp native vp") } - fn get_tx_code_hash(&self) -> Result { + fn get_tx_code_hash(&self) -> Result, storage_api::Error> { vp_host_fns::get_tx_code_hash(&mut self.gas_meter.borrow_mut(), self.tx) .into_storage_result() } diff --git a/shared/src/ledger/native_vp/parameters.rs b/shared/src/ledger/native_vp/parameters.rs index 6c41ba8dc3..d367c16698 100644 --- a/shared/src/ledger/native_vp/parameters.rs +++ b/shared/src/ledger/native_vp/parameters.rs @@ -3,6 +3,7 @@ use std::collections::BTreeSet; use namada_core::ledger::storage; +use namada_core::proto::Tx; use namada_core::types::address::{Address, InternalAddress}; use namada_core::types::storage::Key; use thiserror::Error; @@ -44,16 +45,21 @@ where fn validate_tx( &self, - tx_data: &[u8], + tx_data: &Tx, keys_changed: &BTreeSet, _verifiers: &BTreeSet
, ) -> Result { let result = keys_changed.iter().all(|key| { let key_type: KeyType = key.into(); + let data = if let Some(data) = tx_data.data() { + data + } else { + return false; + }; match key_type { KeyType::PARAMETER => governance::utils::is_proposal_accepted( &self.ctx.pre(), - tx_data, + &data, ) .unwrap_or(false), KeyType::UNKNOWN_PARAMETER => false, diff --git a/shared/src/ledger/native_vp/replay_protection.rs b/shared/src/ledger/native_vp/replay_protection.rs index 3e3c4b7ca0..9b300e376b 100644 --- a/shared/src/ledger/native_vp/replay_protection.rs +++ b/shared/src/ledger/native_vp/replay_protection.rs @@ -8,6 +8,7 @@ use namada_core::types::storage::Key; use thiserror::Error; use crate::ledger::native_vp::{self, Ctx, NativeVp}; +use crate::proto::Tx; use crate::vm::WasmCacheAccess; #[allow(missing_docs)] @@ -43,7 +44,7 @@ where fn validate_tx( &self, - _tx_data: &[u8], + _tx_data: &Tx, _keys_changed: &BTreeSet, _verifiers: &BTreeSet
, ) -> Result { diff --git a/shared/src/ledger/native_vp/slash_fund.rs b/shared/src/ledger/native_vp/slash_fund.rs index 4caf0f3401..bed71d3bd9 100644 --- a/shared/src/ledger/native_vp/slash_fund.rs +++ b/shared/src/ledger/native_vp/slash_fund.rs @@ -10,6 +10,7 @@ use thiserror::Error; use crate::ledger::native_vp::{self, governance, Ctx, NativeVp}; use crate::ledger::storage::{self as ledger_storage, StorageHasher}; use crate::ledger::storage_api::StorageRead; +use crate::proto::Tx; use crate::types::address::{Address, InternalAddress}; use crate::types::storage::Key; use crate::types::token; @@ -48,7 +49,7 @@ where fn validate_tx( &self, - tx_data: &[u8], + tx_data: &Tx, keys_changed: &BTreeSet, _verifiers: &BTreeSet
, ) -> Result { @@ -60,9 +61,14 @@ where if addr.ne(&slash_fund::ADDRESS) { return true; } + let data = if let Some(data) = tx_data.data() { + data + } else { + return false; + }; governance::utils::is_proposal_accepted( &self.ctx.pre(), - tx_data, + &data, ) .unwrap_or(false) } diff --git a/shared/src/ledger/pos/vp.rs b/shared/src/ledger/pos/vp.rs index 1a3bf48fc5..099b6e1798 100644 --- a/shared/src/ledger/pos/vp.rs +++ b/shared/src/ledger/pos/vp.rs @@ -20,6 +20,7 @@ use crate::ledger::native_vp::{self, governance, Ctx, NativeVp}; // }; use crate::ledger::storage::{self as ledger_storage, StorageHasher}; use crate::ledger::storage_api::StorageRead; +use crate::proto::Tx; use crate::types::address::{Address, InternalAddress}; use crate::types::storage::{Key, KeySeg}; use crate::vm::WasmCacheAccess; @@ -93,7 +94,7 @@ where fn validate_tx( &self, - tx_data: &[u8], + tx_data: &Tx, keys_changed: &BTreeSet, _verifiers: &BTreeSet
, ) -> Result { @@ -110,9 +111,14 @@ where for key in keys_changed { // println!("KEY: {}\n", key); if is_params_key(key) { + let data = if let Some(data) = tx_data.data() { + data + } else { + return Ok(false); + }; if !governance::utils::is_proposal_accepted( &self.ctx.pre(), - tx_data, + &data, ) .map_err(Error::NativeVpError)? { diff --git a/shared/src/ledger/protocol/mod.rs b/shared/src/ledger/protocol/mod.rs index 4090c05a4d..94e63e542d 100644 --- a/shared/src/ledger/protocol/mod.rs +++ b/shared/src/ledger/protocol/mod.rs @@ -28,6 +28,8 @@ use crate::vm::{self, wasm, WasmCacheAccess}; #[allow(missing_docs)] #[derive(Error, Debug)] pub enum Error { + #[error("Missing wasm code error")] + MissingCode, #[error("Storage error: {0}")] StorageError(crate::ledger::storage::Error), #[error("Error decoding a transaction from bytes: {0}")] @@ -78,7 +80,7 @@ pub type Result = std::result::Result; /// but no further validations. #[allow(clippy::too_many_arguments)] pub fn apply_tx( - tx: TxType, + tx: Tx, tx_length: usize, tx_index: TxIndex, block_gas_meter: &mut BlockGasMeter, @@ -96,10 +98,9 @@ where block_gas_meter .add_base_transaction_fee(tx_length) .map_err(Error::GasError)?; - match tx { - TxType::Raw(_) => Err(Error::TxTypeError), + match tx.header().tx_type { + TxType::Raw => Err(Error::TxTypeError), TxType::Decrypted(DecryptedTx::Decrypted { - tx, #[cfg(not(feature = "mainnet"))] has_valid_pow, }) => { @@ -167,15 +168,12 @@ where H: 'static + StorageHasher + Sync, CA: 'static + WasmCacheAccess + Sync, { - let empty = vec![]; - let tx_data = tx.data.as_ref().unwrap_or(&empty); wasm::run::tx( storage, write_log, gas_meter, tx_index, - &tx.code_or_hash, - tx_data, + tx, vp_wasm_cache, tx_wasm_cache, ) @@ -295,10 +293,6 @@ where &verifiers, vp_wasm_cache.clone(), ); - let tx_data = match tx.data.as_ref() { - Some(data) => &data[..], - None => &[], - }; let accepted: Result = match internal_addr { InternalAddress::PoS => { @@ -313,7 +307,7 @@ where let result = match panic::catch_unwind(move || { pos_ref .validate_tx( - tx_data, + tx, keys_changed_ref, verifiers_addr_ref, ) @@ -335,7 +329,7 @@ where InternalAddress::Ibc => { let ibc = Ibc { ctx }; let result = ibc - .validate_tx(tx_data, &keys_changed, &verifiers) + .validate_tx(tx, &keys_changed, &verifiers) .map_err(Error::IbcNativeVpError); // Take the gas meter back out of the context gas_meter = ibc.ctx.gas_meter.into_inner(); @@ -344,7 +338,7 @@ where InternalAddress::Parameters => { let parameters = ParametersVp { ctx }; let result = parameters - .validate_tx(tx_data, &keys_changed, &verifiers) + .validate_tx(tx, &keys_changed, &verifiers) .map_err(Error::ParametersNativeVpError); // Take the gas meter back out of the context gas_meter = parameters.ctx.gas_meter.into_inner(); @@ -360,7 +354,7 @@ where InternalAddress::Governance => { let governance = GovernanceVp { ctx }; let result = governance - .validate_tx(tx_data, &keys_changed, &verifiers) + .validate_tx(tx, &keys_changed, &verifiers) .map_err(Error::GovernanceNativeVpError); gas_meter = governance.ctx.gas_meter.into_inner(); result @@ -368,7 +362,7 @@ where InternalAddress::SlashFund => { let slash_fund = SlashFundVp { ctx }; let result = slash_fund - .validate_tx(tx_data, &keys_changed, &verifiers) + .validate_tx(tx, &keys_changed, &verifiers) .map_err(Error::SlashFundNativeVpError); gas_meter = slash_fund.ctx.gas_meter.into_inner(); result @@ -380,7 +374,7 @@ where // validate the transfer let ibc_token = IbcToken { ctx }; let result = ibc_token - .validate_tx(tx_data, &keys_changed, &verifiers) + .validate_tx(tx, &keys_changed, &verifiers) .map_err(Error::IbcTokenNativeVpError); gas_meter = ibc_token.ctx.gas_meter.into_inner(); result @@ -388,7 +382,7 @@ where InternalAddress::EthBridge => { let bridge = EthBridge { ctx }; let result = bridge - .validate_tx(tx_data, &keys_changed, &verifiers) + .validate_tx(tx, &keys_changed, &verifiers) .map_err(Error::EthBridgeNativeVpError); gas_meter = bridge.ctx.gas_meter.into_inner(); result @@ -397,7 +391,7 @@ where let replay_protection_vp = ReplayProtectionVp { ctx }; let result = replay_protection_vp - .validate_tx(tx_data, &keys_changed, &verifiers) + .validate_tx(tx, &keys_changed, &verifiers) .map_err(Error::ReplayProtectionNativeVpError); gas_meter = replay_protection_vp.ctx.gas_meter.into_inner(); diff --git a/shared/src/ledger/queries/shell.rs b/shared/src/ledger/queries/shell.rs index 37e6fe38bb..daec69cfb6 100644 --- a/shared/src/ledger/queries/shell.rs +++ b/shared/src/ledger/queries/shell.rs @@ -74,16 +74,10 @@ where use crate::ledger::storage::write_log::WriteLog; use crate::proto::Tx; use crate::types::storage::TxIndex; - use crate::types::transaction::{DecryptedTx, TxType}; let mut gas_meter = BlockGasMeter::default(); let mut write_log = WriteLog::default(); let tx = Tx::try_from(&request.data[..]).into_storage_result()?; - let tx = TxType::Decrypted(DecryptedTx::Decrypted { - tx, - #[cfg(not(feature = "mainnet"))] - has_valid_pow: true, - }); let data = protocol::apply_tx( tx, request.data.len(), @@ -350,9 +344,11 @@ mod test { use crate::ledger::queries::testing::TestClient; use crate::ledger::queries::RPC; use crate::ledger::storage_api::{self, StorageWrite}; - use crate::proto::Tx; + use crate::proto::{Code, Data, Tx}; use crate::types::hash::Hash; use crate::types::storage::Key; + use crate::types::transaction::decrypted::DecryptedTx; + use crate::types::transaction::TxType; use crate::types::{address, token}; #[test] @@ -393,13 +389,16 @@ mod test { assert_eq!(current_epoch, read_epoch); // Request dry run tx - let tx = Tx::new( - tx_hash.to_vec(), - None, - client.wl_storage.storage.chain_id.clone(), - None, - ); - let tx_bytes = tx.to_bytes(); + let mut outer_tx = Tx::new(TxType::Decrypted(DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + // To be able to dry-run testnet faucet withdrawal, pretend + // that we got a valid PoW + has_valid_pow: true, + })); + outer_tx.header.chain_id = client.wl_storage.storage.chain_id.clone(); + outer_tx.set_code(Code::from_hash(tx_hash)); + outer_tx.set_data(Data::new(vec![])); + let tx_bytes = outer_tx.to_bytes(); let result = RPC .shell() .dry_run_tx(&client, Some(tx_bytes), None, false) diff --git a/shared/src/ledger/signing.rs b/shared/src/ledger/signing.rs index e01e765613..864ffcef2e 100644 --- a/shared/src/ledger/signing.rs +++ b/shared/src/ledger/signing.rs @@ -1,18 +1,62 @@ //! Functions to sign transactions -use borsh::BorshSerialize; -use namada_core::ledger::parameters::storage as parameter_storage; -use namada_core::types::address::{Address, ImplicitAddress}; +use std::collections::{BTreeMap, HashMap}; +#[cfg(feature = "std")] +use std::env; +#[cfg(feature = "std")] +use std::fs::File; +use std::io::ErrorKind; +#[cfg(feature = "std")] +use std::io::Write; + +use borsh::{BorshDeserialize, BorshSerialize}; +use data_encoding::HEXLOWER; +use itertools::Itertools; +use masp_primitives::asset_type::AssetType; +use masp_primitives::transaction::components::sapling::fees::{ + InputView, OutputView, +}; +use namada_core::types::address::{masp, Address, ImplicitAddress}; use namada_core::types::token::{self, Amount}; -use namada_core::types::transaction::MIN_FEE; +use namada_core::types::transaction::{pos, MIN_FEE}; +use prost::Message; +use serde::{Deserialize, Serialize}; +use zeroize::Zeroizing; -use crate::ledger::rpc::TxBroadcastData; -use crate::ledger::tx::Error; +use crate::ibc::applications::transfer::msgs::transfer::{ + MsgTransfer, TYPE_URL as MSG_TRANSFER_TYPE_URL, +}; +use crate::ibc_proto::google::protobuf::Any; +use crate::ledger::masp::make_asset_type; +use crate::ledger::parameters::storage as parameter_storage; +use crate::ledger::rpc::{query_wasm_code_hash, TxBroadcastData}; +use crate::ledger::tx::{ + Error, TX_BOND_WASM, TX_CHANGE_COMMISSION_WASM, TX_IBC_WASM, + TX_INIT_ACCOUNT_WASM, TX_INIT_PROPOSAL, TX_INIT_VALIDATOR_WASM, + TX_REVEAL_PK, TX_TRANSFER_WASM, TX_UNBOND_WASM, TX_UPDATE_VP_WASM, + TX_VOTE_PROPOSAL, TX_WITHDRAW_WASM, VP_USER_WASM, +}; +pub use crate::ledger::wallet::store::AddressVpType; use crate::ledger::wallet::{Wallet, WalletUtils}; use crate::ledger::{args, rpc}; -use crate::proto::Tx; +use crate::proto::{Section, Signature, Tx}; use crate::types::key::*; +use crate::types::masp::{ExtendedViewingKey, PaymentAddress}; use crate::types::storage::Epoch; -use crate::types::transaction::{hash_tx, Fee, WrapperTx}; +use crate::types::token::Transfer; +use crate::types::transaction::decrypted::DecryptedTx; +use crate::types::transaction::governance::{ + InitProposalData, VoteProposalData, +}; +use crate::types::transaction::{ + Fee, InitAccount, InitValidator, TxType, UpdateVp, WrapperTx, +}; + +#[cfg(feature = "std")] +/// Env. var specifying where to store signing test vectors +const ENV_VAR_LEDGER_LOG_PATH: &str = "NAMADA_LEDGER_LOG_PATH"; +#[cfg(feature = "std")] +/// Env. var specifying where to store transaction debug outputs +const ENV_VAR_TX_LOG_PATH: &str = "NAMADA_TX_LOG_PATH"; /// Find the public key for the given address and try to load the keypair /// for it from the wallet. If the keypair is encrypted but a password is not @@ -25,7 +69,7 @@ pub async fn find_keypair< client: &C, wallet: &mut Wallet, addr: &Address, - password: Option, + password: Option>, ) -> Result { match addr { Address::Established(_) => { @@ -122,13 +166,7 @@ pub async fn tx_signer< } Ok(signing_key) } - TxSigningKey::SecretKey(signing_key) => { - // Check if the signing key needs to reveal its PK first - let pk: common::PublicKey = signing_key.ref_to(); - super::tx::reveal_pk_if_needed::(client, wallet, &pk, args) - .await?; - Ok(signing_key) - } + TxSigningKey::SecretKey(signing_key) => Ok(signing_key), TxSigningKey::None => other_err( "All transactions must be signed; please either specify the key \ or the address from which to look up the signing key." @@ -151,21 +189,37 @@ pub async fn sign_tx< >( client: &C, wallet: &mut Wallet, - tx: Tx, + mut tx: Tx, args: &args::Tx, default: TxSigningKey, #[cfg(not(feature = "mainnet"))] requires_pow: bool, ) -> Result { let keypair = tx_signer::(client, wallet, args, default).await?; - let tx = tx.sign(&keypair); + // Sign over the transacttion data + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair, + ))); + // Sign over the transaction code + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &keypair, + ))); let epoch = rpc::query_epoch(client).await; let broadcast_data = if args.dry_run { + tx.update_header(TxType::Decrypted(DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + // To be able to dry-run testnet faucet withdrawal, pretend + // that we got a valid PoW + has_valid_pow: true, + })); TxBroadcastData::DryRun(tx) } else { sign_wrapper( client, + wallet, args, epoch, tx, @@ -182,11 +236,15 @@ pub async fn sign_tx< /// Create a wrapper tx from a normal tx. Get the hash of the /// wrapper and its payload which is needed for monitoring its /// progress on chain. -pub async fn sign_wrapper( +pub async fn sign_wrapper< + C: crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( client: &C, + #[allow(unused_variables)] wallet: &mut Wallet, args: &args::Tx, epoch: Epoch, - tx: Tx, + mut tx: Tx, keypair: &common::SecretKey, #[cfg(not(feature = "mainnet"))] requires_pow: bool, ) -> TxBroadcastData { @@ -201,22 +259,33 @@ pub async fn sign_wrapper( .await .unwrap_or_default() }; - let fee_token = args.fee_token.clone(); + let fee_token = &args.fee_token; let source = Address::from(&keypair.ref_to()); - let balance_key = token::balance_key(&fee_token, &source); + let balance_key = token::balance_key(fee_token, &source); let balance = rpc::query_storage_value::(client, &balance_key) .await .unwrap_or_default(); - - // todo: provide sdk clients an error if the fee balance is insufficient + let is_bal_sufficient = fee_amount <= balance; + if !is_bal_sufficient { + eprintln!( + "The wrapper transaction source doesn't have enough balance to \ + pay fee {fee_amount}, got {balance}." + ); + if !args.force && cfg!(feature = "mainnet") { + panic!( + "The wrapper transaction source doesn't have enough balance \ + to pay fee {fee_amount}, got {balance}." + ); + } + } #[cfg(not(feature = "mainnet"))] // A PoW solution can be used to allow zero-fee testnet transactions - let pow_solution: Option = { + let pow_solution: Option = { // If the address derived from the keypair doesn't have enough balance // to pay for the fee, allow to find a PoW solution instead. - if requires_pow || balance < fee_amount { + if requires_pow || !is_bal_sufficient { println!( "The transaction requires the completion of a PoW challenge." ); @@ -232,32 +301,77 @@ pub async fn sign_wrapper( } }; - let tx = { - WrapperTx::new( - Fee { - amount: fee_amount, - token: fee_token, - }, - keypair, - epoch, - args.gas_limit.clone(), - tx, - // TODO: Actually use the fetched encryption key - Default::default(), - #[cfg(not(feature = "mainnet"))] - pow_solution, - ) - }; + // This object governs how the payload will be processed + tx.update_header(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount: fee_amount, + token: fee_token.clone(), + }, + keypair, + epoch, + args.gas_limit.clone(), + #[cfg(not(feature = "mainnet"))] + pow_solution, + )))); + tx.header.chain_id = args.chain_id.clone().unwrap(); + tx.header.expiration = args.expiration; + // Then sign over the bound wrapper + tx.add_section(Section::Signature(Signature::new( + &tx.header_hash(), + keypair, + ))); + + #[cfg(feature = "std")] + // Attempt to decode the construction + if let Ok(path) = env::var(ENV_VAR_LEDGER_LOG_PATH) { + let mut tx = tx.clone(); + // Contract the large data blobs in the transaction + tx.wallet_filter(); + // Convert the transaction to Ledger format + let decoding = to_ledger_vector(client, wallet, &tx) + .await + .expect("unable to decode transaction"); + let output = serde_json::to_string(&decoding) + .expect("failed to serialize decoding"); + // Record the transaction at the identified path + let mut f = File::options() + .append(true) + .create(true) + .open(path) + .expect("failed to open test vector file"); + writeln!(f, "{},", output) + .expect("unable to write test vector to file"); + } + #[cfg(feature = "std")] + // Attempt to decode the construction + if let Ok(path) = env::var(ENV_VAR_TX_LOG_PATH) { + let mut tx = tx.clone(); + // Contract the large data blobs in the transaction + tx.wallet_filter(); + // Record the transaction at the identified path + let mut f = File::options() + .append(true) + .create(true) + .open(path) + .expect("failed to open test vector file"); + writeln!(f, "{:x?},", tx).expect("unable to write test vector to file"); + } + // Remove all the sensitive sections + tx.protocol_filter(); + // Encrypt all sections not relating to the header + tx.encrypt(&Default::default()); // We use this to determine when the wrapper tx makes it on-chain - let wrapper_hash = hash_tx(&tx.try_to_vec().unwrap()).to_string(); + let wrapper_hash = tx.header_hash().to_string(); // We use this to determine when the decrypted inner tx makes it // on-chain - let decrypted_hash = tx.tx_hash.to_string(); + let decrypted_hash = tx + .clone() + .update_header(TxType::Raw) + .header_hash() + .to_string(); TxBroadcastData::Wrapper { - tx: tx - .sign(keypair, args.chain_id.clone().unwrap(), args.expiration) - .expect("Wrapper tx signing keypair should be correct"), + tx, wrapper_hash, decrypted_hash, } @@ -266,3 +380,679 @@ pub async fn sign_wrapper( fn other_err(string: String) -> Result { Err(Error::Other(string)) } + +/// Represents the transaction data that is displayed on a Ledger device +#[derive(Default, Serialize, Deserialize)] +pub struct LedgerVector { + blob: String, + index: u64, + name: String, + output: Vec, + output_expert: Vec, + valid: bool, +} + +/// Adds a Ledger output line describing a given transaction amount and address +fn make_ledger_amount_addr( + tokens: &HashMap, + output: &mut Vec, + amount: Amount, + token: &Address, + prefix: &str, +) { + if let Some(token) = tokens.get(token) { + output.push(format!("{}Amount: {} {}", prefix, token, amount)); + } else { + output.extend(vec![ + format!("{}Token: {}", prefix, token), + format!("{}Amount: {}", prefix, amount), + ]); + } +} + +/// Adds a Ledger output line describing a given transaction amount and asset +/// type +fn make_ledger_amount_asset( + tokens: &HashMap, + output: &mut Vec, + amount: u64, + token: &AssetType, + assets: &HashMap, + prefix: &str, +) { + if let Some((token, _epoch)) = assets.get(token) { + // If the AssetType can be decoded, then at least display Addressees + if let Some(token) = tokens.get(token) { + output.push(format!( + "{}Amount: {} {}", + prefix, + token, + Amount::from(amount) + )); + } else { + output.extend(vec![ + format!("{}Token: {}", prefix, token), + format!("{}Amount: {}", prefix, Amount::from(amount)), + ]); + } + } else { + // Otherwise display the raw AssetTypes + output.extend(vec![ + format!("{}Token: {}", prefix, token), + format!("{}Amount: {}", prefix, Amount::from(amount)), + ]); + } +} + +/// Split the lines in the vector that are longer than the Ledger device's +/// character width +fn format_outputs(output: &mut Vec) { + const LEDGER_WIDTH: usize = 60; + + let mut i = 0; + let mut pos = 0; + // Break down each line that is too long one-by-one + while pos < output.len() { + let prefix_len = i.to_string().len() + 3; + let curr_line = output[pos].clone(); + if curr_line.len() + prefix_len < LEDGER_WIDTH { + // No need to split the line in this case + output[pos] = format!("{} | {}", i, curr_line); + pos += 1; + } else { + // Line is too long so split it up. Repeat the key on each line + let (mut key, mut value) = + curr_line.split_once(':').unwrap_or(("", &curr_line)); + key = key.trim(); + value = value.trim(); + if value.is_empty() { + value = "(none)" + } + + // First comput how many lines we will break the current one up into + let mut digits = 1; + let mut line_space; + let mut lines; + loop { + let prefix_len = prefix_len + 7 + 2 * digits + key.len(); + line_space = LEDGER_WIDTH - prefix_len; + lines = (value.len() + line_space - 1) / line_space; + if lines.to_string().len() <= digits { + break; + } else { + digits += 1; + } + } + + // Then break up this line according to the above plan + output.remove(pos); + for (idx, part) in + value.chars().chunks(line_space).into_iter().enumerate() + { + let line = format!( + "{} | {} [{}/{}] : {}", + i, + key, + idx + 1, + lines, + part.collect::(), + ); + output.insert(pos, line); + pos += 1; + } + } + i += 1; + } +} + +/// Converts the given transaction to the form that is displayed on the Ledger +/// device +pub async fn to_ledger_vector< + C: crate::ledger::queries::Client + Sync, + U: WalletUtils, +>( + client: &C, + wallet: &mut Wallet, + tx: &Tx, +) -> Result { + let init_account_hash = query_wasm_code_hash(client, TX_INIT_ACCOUNT_WASM) + .await + .unwrap(); + let init_validator_hash = + query_wasm_code_hash(client, TX_INIT_VALIDATOR_WASM) + .await + .unwrap(); + let init_proposal_hash = query_wasm_code_hash(client, TX_INIT_PROPOSAL) + .await + .unwrap(); + let vote_proposal_hash = query_wasm_code_hash(client, TX_VOTE_PROPOSAL) + .await + .unwrap(); + let reveal_pk_hash = + query_wasm_code_hash(client, TX_REVEAL_PK).await.unwrap(); + let update_vp_hash = query_wasm_code_hash(client, TX_UPDATE_VP_WASM) + .await + .unwrap(); + let transfer_hash = query_wasm_code_hash(client, TX_TRANSFER_WASM) + .await + .unwrap(); + let ibc_hash = query_wasm_code_hash(client, TX_IBC_WASM).await.unwrap(); + let bond_hash = query_wasm_code_hash(client, TX_BOND_WASM).await.unwrap(); + let unbond_hash = + query_wasm_code_hash(client, TX_UNBOND_WASM).await.unwrap(); + let withdraw_hash = query_wasm_code_hash(client, TX_WITHDRAW_WASM) + .await + .unwrap(); + let change_commission_hash = + query_wasm_code_hash(client, TX_CHANGE_COMMISSION_WASM) + .await + .unwrap(); + let user_hash = query_wasm_code_hash(client, VP_USER_WASM).await.unwrap(); + + // To facilitate lookups of human-readable token names + let tokens: HashMap = wallet + .get_addresses_with_vp_type(AddressVpType::Token) + .into_iter() + .map(|addr| { + let alias = match wallet.find_alias(&addr) { + Some(alias) => alias.to_string(), + None => addr.to_string(), + }; + (addr, alias) + }) + .collect(); + + let mut tv = LedgerVector { + blob: HEXLOWER + .encode(&tx.try_to_vec().expect("unable to serialize transaction")), + index: 0, + valid: true, + name: "Custom 0".to_string(), + ..Default::default() + }; + + let code_hash = tx + .get_section(tx.code_sechash()) + .expect("expected tx code section to be present") + .code_sec() + .expect("expected section to have code tag") + .code + .hash(); + tv.output_expert + .push(format!("Code hash : {}", HEXLOWER.encode(&code_hash.0))); + + if code_hash == init_account_hash { + let init_account = + InitAccount::try_from_slice(&tx.data().ok_or_else(|| { + std::io::Error::from(ErrorKind::InvalidData) + })?)?; + + tv.name = "Init Account 0".to_string(); + + let extra = tx + .get_section(&init_account.vp_code_hash) + .and_then(Section::extra_data_sec) + .expect("unable to load vp code") + .code + .hash(); + let vp_code = if extra == user_hash { + "User".to_string() + } else { + HEXLOWER.encode(&extra.0) + }; + + tv.output.extend(vec![ + format!("Type : Init Account"), + format!("Public key : {}", init_account.public_key), + format!("VP type : {}", vp_code), + ]); + + tv.output_expert.extend(vec![ + format!("Public key : {}", init_account.public_key), + format!("VP type : {}", HEXLOWER.encode(&extra.0)), + ]); + } else if code_hash == init_validator_hash { + let init_validator = + InitValidator::try_from_slice(&tx.data().ok_or_else(|| { + std::io::Error::from(ErrorKind::InvalidData) + })?)?; + + tv.name = "Init Validator 0".to_string(); + + let extra = tx + .get_section(&init_validator.validator_vp_code_hash) + .and_then(Section::extra_data_sec) + .expect("unable to load vp code") + .code + .hash(); + let vp_code = if extra == user_hash { + "User".to_string() + } else { + HEXLOWER.encode(&extra.0) + }; + + tv.output.extend(vec![ + format!("Type : Init Validator"), + format!("Account key : {}", init_validator.account_key), + format!("Consensus key : {}", init_validator.consensus_key), + format!("Protocol key : {}", init_validator.protocol_key), + format!("DKG key : {}", init_validator.dkg_key), + format!("Commission rate : {}", init_validator.commission_rate), + format!( + "Maximum commission rate change : {}", + init_validator.max_commission_rate_change + ), + format!("Validator VP type : {}", vp_code,), + ]); + + tv.output_expert.extend(vec![ + format!("Account key : {}", init_validator.account_key), + format!("Consensus key : {}", init_validator.consensus_key), + format!("Protocol key : {}", init_validator.protocol_key), + format!("DKG key : {}", init_validator.dkg_key), + format!("Commission rate : {}", init_validator.commission_rate), + format!( + "Maximum commission rate change : {}", + init_validator.max_commission_rate_change + ), + format!("Validator VP type : {}", HEXLOWER.encode(&extra.0)), + ]); + } else if code_hash == init_proposal_hash { + let init_proposal_data = + InitProposalData::try_from_slice(&tx.data().ok_or_else(|| { + std::io::Error::from(ErrorKind::InvalidData) + })?)?; + + tv.name = "Init Proposal 0".to_string(); + + let init_proposal_data_id = init_proposal_data + .id + .as_ref() + .map(u64::to_string) + .unwrap_or_else(|| "(none)".to_string()); + tv.output.extend(vec![ + format!("Type : Init proposal"), + format!("ID : {}", init_proposal_data_id), + format!("Author : {}", init_proposal_data.author), + format!( + "Voting start epoch : {}", + init_proposal_data.voting_start_epoch + ), + format!( + "Voting end epoch : {}", + init_proposal_data.voting_end_epoch + ), + format!("Grace epoch : {}", init_proposal_data.grace_epoch), + ]); + let content: BTreeMap = + BorshDeserialize::try_from_slice(&init_proposal_data.content)?; + if !content.is_empty() { + for (key, value) in &content { + tv.output.push(format!("Content {} : {}", key, value)); + } + } else { + tv.output.push("Content : (none)".to_string()); + } + + tv.output_expert.extend(vec![ + format!("ID : {}", init_proposal_data_id), + format!("Author : {}", init_proposal_data.author), + format!( + "Voting start epoch : {}", + init_proposal_data.voting_start_epoch + ), + format!( + "Voting end epoch : {}", + init_proposal_data.voting_end_epoch + ), + format!("Grace epoch : {}", init_proposal_data.grace_epoch), + ]); + if !content.is_empty() { + for (key, value) in content { + tv.output_expert + .push(format!("Content {} : {}", key, value)); + } + } else { + tv.output_expert.push("Content : none".to_string()); + } + } else if code_hash == vote_proposal_hash { + let vote_proposal = + VoteProposalData::try_from_slice(&tx.data().ok_or_else(|| { + std::io::Error::from(ErrorKind::InvalidData) + })?)?; + + tv.name = "Vote Proposal 0".to_string(); + + tv.output.extend(vec![ + format!("Type : Vote Proposal"), + format!("ID : {}", vote_proposal.id), + format!("Vote : {}", vote_proposal.vote), + format!("Voter : {}", vote_proposal.voter), + ]); + for delegation in &vote_proposal.delegations { + tv.output.push(format!("Delegations : {}", delegation)); + } + + tv.output_expert.extend(vec![ + format!("ID : {}", vote_proposal.id), + format!("Vote : {}", vote_proposal.vote), + format!("Voter : {}", vote_proposal.voter), + ]); + for delegation in vote_proposal.delegations { + tv.output_expert + .push(format!("Delegations : {}", delegation)); + } + } else if code_hash == reveal_pk_hash { + let public_key = common::PublicKey::try_from_slice( + &tx.data() + .ok_or_else(|| std::io::Error::from(ErrorKind::InvalidData))?, + )?; + + tv.name = "Init Account 0".to_string(); + + tv.output.extend(vec![ + format!("Type : Reveal PK"), + format!("Public key : {}", public_key), + ]); + + tv.output_expert + .extend(vec![format!("Public key : {}", public_key)]); + } else if code_hash == update_vp_hash { + let transfer = + UpdateVp::try_from_slice(&tx.data().ok_or_else(|| { + std::io::Error::from(ErrorKind::InvalidData) + })?)?; + + tv.name = "Update VP 0".to_string(); + + let extra = tx + .get_section(&transfer.vp_code_hash) + .and_then(Section::extra_data_sec) + .expect("unable to load vp code") + .code + .hash(); + let vp_code = if extra == user_hash { + "User".to_string() + } else { + HEXLOWER.encode(&extra.0) + }; + + tv.output.extend(vec![ + format!("Type : Update VP"), + format!("Address : {}", transfer.addr), + format!("VP type : {}", vp_code), + ]); + + tv.output_expert.extend(vec![ + format!("Address : {}", transfer.addr), + format!("VP type : {}", HEXLOWER.encode(&extra.0)), + ]); + } else if code_hash == transfer_hash { + let transfer = + Transfer::try_from_slice(&tx.data().ok_or_else(|| { + std::io::Error::from(ErrorKind::InvalidData) + })?)?; + // To facilitate lookups of MASP AssetTypes + let mut asset_types = HashMap::new(); + let builder = if let Some(shielded_hash) = transfer.shielded { + tx.sections.iter().find_map(|x| match x { + Section::MaspBuilder(builder) + if builder.target == shielded_hash => + { + for (addr, epoch) in &builder.asset_types { + asset_types.insert( + make_asset_type(*epoch, addr), + (addr.clone(), *epoch), + ); + } + Some(builder) + } + _ => None, + }) + } else { + None + }; + + tv.name = "Transfer 0".to_string(); + + tv.output.push("Type : Transfer".to_string()); + if transfer.source != masp() { + tv.output.push(format!("Sender : {}", transfer.source)); + if transfer.target == masp() { + make_ledger_amount_addr( + &tokens, + &mut tv.output, + transfer.amount, + &transfer.token, + "Sending ", + ); + } + } else if let Some(builder) = builder { + for input in builder.builder.sapling_inputs() { + let vk = ExtendedViewingKey::from(*input.key()); + tv.output.push(format!("Sender : {}", vk)); + make_ledger_amount_asset( + &tokens, + &mut tv.output, + input.value(), + &input.asset_type(), + &asset_types, + "Sending ", + ); + } + } + if transfer.target != masp() { + tv.output.push(format!("Destination : {}", transfer.target)); + if transfer.source == masp() { + make_ledger_amount_addr( + &tokens, + &mut tv.output, + transfer.amount, + &transfer.token, + "Receiving ", + ); + } + } else if let Some(builder) = builder { + for output in builder.builder.sapling_outputs() { + let pa = PaymentAddress::from(output.address()); + tv.output.push(format!("Destination : {}", pa)); + make_ledger_amount_asset( + &tokens, + &mut tv.output, + output.value(), + &output.asset_type(), + &asset_types, + "Receiving ", + ); + } + } + if transfer.source != masp() && transfer.target != masp() { + make_ledger_amount_addr( + &tokens, + &mut tv.output, + transfer.amount, + &transfer.token, + "", + ); + } + + tv.output_expert.extend(vec![ + format!("Source : {}", transfer.source), + format!("Target : {}", transfer.target), + format!("Token : {}", transfer.token), + format!("Amount : {}", transfer.amount), + ]); + } else if code_hash == ibc_hash { + let msg = Any::decode( + tx.data() + .ok_or_else(|| std::io::Error::from(ErrorKind::InvalidData))? + .as_ref(), + ) + .map_err(|x| std::io::Error::new(ErrorKind::Other, x))?; + + tv.name = "IBC 0".to_string(); + tv.output.push("Type : IBC".to_string()); + + match msg.type_url.as_str() { + MSG_TRANSFER_TYPE_URL => { + let transfer = MsgTransfer::try_from(msg).map_err(|_| { + std::io::Error::from(ErrorKind::InvalidData) + })?; + let transfer_token = format!( + "{} {}", + transfer.token.amount, transfer.token.denom + ); + tv.output.extend(vec![ + format!("Source port : {}", transfer.port_id_on_a), + format!("Source channel : {}", transfer.chan_id_on_a), + format!("Token : {}", transfer_token), + format!("Sender : {}", transfer.sender), + format!("Receiver : {}", transfer.receiver), + format!( + "Timeout height : {}", + transfer.timeout_height_on_b + ), + format!( + "Timeout timestamp : {}", + transfer.timeout_timestamp_on_b + ), + ]); + tv.output_expert.extend(vec![ + format!("Source port : {}", transfer.port_id_on_a), + format!("Source channel : {}", transfer.chan_id_on_a), + format!("Token : {}", transfer_token), + format!("Sender : {}", transfer.sender), + format!("Receiver : {}", transfer.receiver), + format!( + "Timeout height : {}", + transfer.timeout_height_on_b + ), + format!( + "Timeout timestamp : {}", + transfer.timeout_timestamp_on_b + ), + ]); + } + _ => { + for line in format!("{:#?}", msg).split('\n') { + let stripped = line.trim_start(); + tv.output.push(format!("Part : {}", stripped)); + tv.output_expert.push(format!("Part : {}", stripped)); + } + } + } + } else if code_hash == bond_hash { + let bond = + pos::Bond::try_from_slice(&tx.data().ok_or_else(|| { + std::io::Error::from(ErrorKind::InvalidData) + })?)?; + + tv.name = "Bond 0".to_string(); + + let bond_source = bond + .source + .as_ref() + .map(Address::to_string) + .unwrap_or_else(|| "(none)".to_string()); + tv.output.extend(vec![ + format!("Type : Bond"), + format!("Source : {}", bond_source), + format!("Validator : {}", bond.validator), + format!("Amount : {}", bond.amount), + ]); + + tv.output_expert.extend(vec![ + format!("Source : {}", bond_source), + format!("Validator : {}", bond.validator), + format!("Amount : {}", bond.amount), + ]); + } else if code_hash == unbond_hash { + let unbond = + pos::Unbond::try_from_slice(&tx.data().ok_or_else(|| { + std::io::Error::from(ErrorKind::InvalidData) + })?)?; + + tv.name = "Unbond 0".to_string(); + + let unbond_source = unbond + .source + .as_ref() + .map(Address::to_string) + .unwrap_or_else(|| "(none)".to_string()); + tv.output.extend(vec![ + format!("Code : Unbond"), + format!("Source : {}", unbond_source), + format!("Validator : {}", unbond.validator), + format!("Amount : {}", unbond.amount), + ]); + + tv.output_expert.extend(vec![ + format!("Source : {}", unbond_source), + format!("Validator : {}", unbond.validator), + format!("Amount : {}", unbond.amount), + ]); + } else if code_hash == withdraw_hash { + let withdraw = + pos::Withdraw::try_from_slice(&tx.data().ok_or_else(|| { + std::io::Error::from(ErrorKind::InvalidData) + })?)?; + + tv.name = "Withdraw 0".to_string(); + + let withdraw_source = withdraw + .source + .as_ref() + .map(Address::to_string) + .unwrap_or_else(|| "(none)".to_string()); + tv.output.extend(vec![ + format!("Type : Withdraw"), + format!("Source : {}", withdraw_source), + format!("Validator : {}", withdraw.validator), + ]); + + tv.output_expert.extend(vec![ + format!("Source : {}", withdraw_source), + format!("Validator : {}", withdraw.validator), + ]); + } else if code_hash == change_commission_hash { + let commission_change = pos::CommissionChange::try_from_slice( + &tx.data() + .ok_or_else(|| std::io::Error::from(ErrorKind::InvalidData))?, + )?; + + tv.name = "Change Commission 0".to_string(); + + tv.output.extend(vec![ + format!("Type : Change commission"), + format!("New rate : {}", commission_change.new_rate), + format!("Validator : {}", commission_change.validator), + ]); + + tv.output_expert.extend(vec![ + format!("New rate : {}", commission_change.new_rate), + format!("Validator : {}", commission_change.validator), + ]); + } + + if let Some(wrapper) = tx.header.wrapper() { + tv.output_expert.extend(vec![ + format!("Timestamp : {}", tx.header.timestamp.0), + format!("PK : {}", wrapper.pk), + format!("Epoch : {}", wrapper.epoch), + format!("Gas limit : {}", Amount::from(wrapper.gas_limit)), + format!("Fee token : {}", wrapper.fee.token), + ]); + if let Some(token) = tokens.get(&wrapper.fee.token) { + tv.output_expert + .push(format!("Fee amount : {} {}", token, wrapper.fee.amount)); + } else { + tv.output_expert + .push(format!("Fee amount : {}", wrapper.fee.amount)); + } + } + + // Finally, index each line and break those that are too long + format_outputs(&mut tv.output); + format_outputs(&mut tv.output_expert); + Ok(tv) +} diff --git a/shared/src/ledger/tx.rs b/shared/src/ledger/tx.rs index 8b2a9d85ed..b6445609d2 100644 --- a/shared/src/ledger/tx.rs +++ b/shared/src/ledger/tx.rs @@ -1,16 +1,26 @@ //! SDK functions to construct different types of transactions use std::borrow::Cow; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashSet}; use std::str::FromStr; use borsh::BorshSerialize; use itertools::Either::*; +use masp_primitives::asset_type::AssetType; use masp_primitives::transaction::builder; +use masp_primitives::transaction::builder::Builder; +use masp_primitives::transaction::components::sapling::fees::{ + ConvertView, InputView as SaplingInputView, OutputView as SaplingOutputView, +}; +use masp_primitives::transaction::components::transparent::fees::{ + InputView as TransparentInputView, OutputView as TransparentOutputView, +}; +use masp_primitives::transaction::components::Amount; use namada_core::types::address::{masp, masp_tx_key, Address}; use namada_proof_of_stake::parameters::PosParams; use namada_proof_of_stake::types::CommissionPair; use prost::EncodeError; use rust_decimal::Decimal; +use sha2::{Digest as Sha2Digest, Sha256}; use thiserror::Error; use tokio::time::Duration; @@ -28,18 +38,48 @@ use crate::ledger::masp::{ShieldedContext, ShieldedUtils}; use crate::ledger::rpc::{self, TxBroadcastData, TxResponse}; use crate::ledger::signing::{find_keypair, sign_tx, tx_signer, TxSigningKey}; use crate::ledger::wallet::{Wallet, WalletUtils}; -use crate::proto::Tx; +use crate::proto::{Code, Data, MaspBuilder, Section, Signature, Tx}; use crate::tendermint_rpc::endpoint::broadcast::tx_sync::Response; use crate::tendermint_rpc::error::Error as RpcError; +use crate::types::hash::Hash; use crate::types::key::*; use crate::types::masp::TransferTarget; use crate::types::storage::{Epoch, RESERVED_ADDRESS_PREFIX}; use crate::types::time::DateTimeUtc; -use crate::types::transaction::{pos, InitAccount, UpdateVp}; +use crate::types::transaction::decrypted::DecryptedTx; +use crate::types::transaction::{pos, InitAccount, TxType, UpdateVp}; use crate::types::{storage, token}; use crate::vm; use crate::vm::WasmValidationError; +/// Initialize account transaction WASM +pub const TX_INIT_ACCOUNT_WASM: &str = "tx_init_account.wasm"; +/// Initialize validator transaction WASM path +pub const TX_INIT_VALIDATOR_WASM: &str = "tx_init_validator.wasm"; +/// Initialize proposal transaction WASM path +pub const TX_INIT_PROPOSAL: &str = "tx_init_proposal.wasm"; +/// Vote transaction WASM path +pub const TX_VOTE_PROPOSAL: &str = "tx_vote_proposal.wasm"; +/// Reveal public key transaction WASM path +pub const TX_REVEAL_PK: &str = "tx_reveal_pk.wasm"; +/// Update validity predicate WASM path +pub const TX_UPDATE_VP_WASM: &str = "tx_update_vp.wasm"; +/// Transfer transaction WASM path +pub const TX_TRANSFER_WASM: &str = "tx_transfer.wasm"; +/// IBC transaction WASM path +pub const TX_IBC_WASM: &str = "tx_ibc.wasm"; +/// User validity predicate WASM path +pub const VP_USER_WASM: &str = "vp_user.wasm"; +/// Bond WASM path +pub const TX_BOND_WASM: &str = "tx_bond.wasm"; +/// Unbond WASM path +pub const TX_UNBOND_WASM: &str = "tx_unbond.wasm"; +/// Withdraw WASM path +pub const TX_WITHDRAW_WASM: &str = "tx_withdraw.wasm"; +/// Change commission WASM path +pub const TX_CHANGE_COMMISSION_WASM: &str = + "tx_change_validator_commission.wasm"; + /// Default timeout in seconds for requests to the `/accepted` /// and `/applied` ABCI query endpoints. const DEFAULT_NAMADA_EVENTS_MAX_WAIT_TIME_SECONDS: u64 = 60; @@ -61,7 +101,7 @@ pub enum Error { TxBroadcast(RpcError), /// Invalid comission rate set #[error("Invalid new commission rate, received {0}")] - InvalidCommisionRate(Decimal), + InvalidCommissionRate(Decimal), /// Invalid validator address #[error("The address {0} doesn't belong to any known validator account.")] InvalidValidatorAddress(Address), @@ -73,7 +113,7 @@ pub enum Error { TooLargeOfChange(Decimal), /// Error retrieving from storage #[error("Error retrieving from storage")] - Retrival, + Retrieval, /// No unbonded bonds ready to withdraw in the current epoch #[error( "There are no unbonded bonds ready to withdraw in the current epoch \ @@ -128,7 +168,7 @@ pub enum Error { ), /// No Balance found for token #[error("{0}")] - MaspError(builder::Error), + MaspError(builder::Error), /// Wasm validation failed #[error("Validity predicate code validation failed with {0}")] WasmValidationFailure(WasmValidationError), @@ -166,6 +206,26 @@ pub enum Error { Other(String), } +/// Capture the result of running a transaction +pub enum ProcessTxResponse { + /// Result of submitting a transaction to the blockchain + Applied(TxResponse), + /// Result of submitting a transaction to the mempool + Broadcast(Response), + /// Result of dry running transaction + DryRun, +} + +impl ProcessTxResponse { + /// Get the the accounts that were reported to be initialized + pub fn initialized_accounts(&self) -> Vec
{ + match self { + Self::Applied(result) => result.initialized_accounts.clone(), + _ => vec![], + } + } +} + /// Submit transaction and wait for result. Returns a list of addresses /// initialized in the transaction if any. In dry run, this is always empty. pub async fn process_tx< @@ -178,7 +238,7 @@ pub async fn process_tx< tx: Tx, default_signer: TxSigningKey, #[cfg(not(feature = "mainnet"))] requires_pow: bool, -) -> Result, Error> { +) -> Result { let to_broadcast = sign_tx::( client, wallet, @@ -200,7 +260,7 @@ pub async fn process_tx< // println!("HTTP request body: {}", request_body); if args.dry_run { - expect_dry_broadcast(to_broadcast, client, vec![]).await + expect_dry_broadcast(to_broadcast, client).await } else { // Either broadcast or submit transaction and collect result into // sum type @@ -212,8 +272,8 @@ pub async fn process_tx< // Return result based on executed operation, otherwise deal with // the encountered errors uniformly match result { - Right(Ok(result)) => Ok(result.initialized_accounts), - Left(Ok(_)) => Ok(Vec::default()), + Right(Ok(result)) => Ok(ProcessTxResponse::Applied(result)), + Left(Ok(result)) => Ok(ProcessTxResponse::Broadcast(result)), Right(Err(err)) => Err(err), Left(Err(err)) => Err(err), } @@ -281,34 +341,52 @@ pub async fn submit_reveal_pk_aux< wallet: &mut Wallet, public_key: &common::PublicKey, args: &args::Tx, -) -> Result<(), Error> { +) -> Result { let addr: Address = public_key.into(); println!("Submitting a tx to reveal the public key for address {addr}..."); let tx_data = public_key.try_to_vec().map_err(Error::EncodeKeyFailure)?; - let tx_code = args.tx_code_path.clone(); - let tx = Tx::new( - tx_code, - Some(tx_data), - args.chain_id.clone().expect("value should be there"), - args.expiration, - ); + + let tx_code_hash = query_wasm_code_hash( + client, + args.tx_reveal_code_path.to_str().unwrap(), + ) + .await + .unwrap(); + + let mut tx = Tx::new(TxType::Decrypted(DecryptedTx::Decrypted { + #[cfg(not(feature = "mainnet"))] + // To be able to dry-run testnet faucet withdrawal, pretend + // that we got a valid PoW + has_valid_pow: true, + })); + tx.header.chain_id = args.chain_id.clone().expect("value should be there"); + tx.header.expiration = args.expiration; + tx.set_data(Data::new(tx_data)); + tx.set_code(Code::from_hash(tx_code_hash)); // submit_tx without signing the inner tx let keypair = if let Some(signing_key) = &args.signing_key { Ok(signing_key.clone()) } else if let Some(signer) = args.signer.as_ref() { - let signer = signer; - find_keypair::(client, wallet, signer, args.password.clone()) - .await + find_keypair(client, wallet, signer, args.password.clone()).await } else { - find_keypair::(client, wallet, &addr, args.password.clone()).await + find_keypair(client, wallet, &addr, args.password.clone()).await }?; + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair, + ))); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &keypair, + ))); let epoch = rpc::query_epoch(client).await; let to_broadcast = if args.dry_run { TxBroadcastData::DryRun(tx) } else { super::signing::sign_wrapper( client, + wallet, args, epoch, tx, @@ -319,9 +397,8 @@ pub async fn submit_reveal_pk_aux< .await }; - // Logic is the same as process_tx if args.dry_run { - expect_dry_broadcast(to_broadcast, client, ()).await + expect_dry_broadcast(to_broadcast, client).await } else { // Either broadcast or submit transaction and collect result into // sum type @@ -335,7 +412,8 @@ pub async fn submit_reveal_pk_aux< match result { Right(Err(err)) => Err(err), Left(Err(err)) => Err(err), - _ => Ok(()), + Right(Ok(response)) => Ok(ProcessTxResponse::Applied(response)), + Left(Ok(response)) => Ok(ProcessTxResponse::Broadcast(response)), } } } @@ -517,7 +595,10 @@ pub async fn submit_validator_commission_change< ) -> Result<(), Error> { let epoch = rpc::query_epoch(client).await; - let tx_code = args.tx_code_path; + let tx_code_hash = + query_wasm_code_hash(client, args.tx_code_path.to_str().unwrap()) + .await + .unwrap(); // TODO: put following two let statements in its own function let params_key = crate::ledger::pos::params_key(); @@ -528,18 +609,11 @@ pub async fn submit_validator_commission_change< let validator = args.validator.clone(); if rpc::is_validator(client, &validator).await { if args.rate < Decimal::ZERO || args.rate > Decimal::ONE { - if args.tx.force { - eprintln!( - "Invalid new commission rate, received {}", - args.rate - ); - Ok(()) - } else { - Err(Error::InvalidCommisionRate(args.rate)) + eprintln!("Invalid new commission rate, received {}", args.rate); + if !args.tx.force { + return Err(Error::InvalidCommissionRate(args.rate)); } - } else { - Ok(()) - }?; + } let pipeline_epoch_minus_one = epoch + params.pipeline_len - 1; @@ -563,14 +637,14 @@ pub async fn submit_validator_commission_change< effect." ); if !args.tx.force { - return Err(Error::InvalidCommisionRate(args.rate)); + return Err(Error::InvalidCommissionRate(args.rate)); } } } None => { eprintln!("Error retrieving from storage"); if !args.tx.force { - return Err(Error::Retrival); + return Err(Error::Retrieval); } } } @@ -587,10 +661,12 @@ pub async fn submit_validator_commission_change< }; let data = data.try_to_vec().map_err(Error::EncodeTxFailure)?; - let chain_id = args.tx.chain_id.clone().unwrap(); - let expiration = args.tx.expiration; + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = args.tx.chain_id.clone().unwrap(); + tx.header.expiration = args.tx.expiration; + tx.set_data(Data::new(data)); + tx.set_code(Code::from_hash(tx_code_hash)); - let tx = Tx::new(tx_code, Some(data), chain_id, expiration); let default_signer = args.validator.clone(); process_tx::( client, @@ -621,7 +697,11 @@ pub async fn submit_withdraw< .await?; let source = args.source.clone(); - let tx_code = args.tx_code_path; + + let tx_code_hash = + query_wasm_code_hash(client, args.tx_code_path.to_str().unwrap()) + .await + .unwrap(); // Check the source's current unbond amount let bond_source = source.clone().unwrap_or_else(|| validator.clone()); @@ -650,10 +730,12 @@ pub async fn submit_withdraw< let data = pos::Withdraw { validator, source }; let data = data.try_to_vec().map_err(Error::EncodeTxFailure)?; - let chain_id = args.tx.chain_id.clone().unwrap(); - let expiration = args.tx.expiration; + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = args.tx.chain_id.clone().unwrap(); + tx.header.expiration = args.tx.expiration; + tx.set_data(Data::new(data)); + tx.set_code(Code::from_hash(tx_code_hash)); - let tx = Tx::new(tx_code, Some(data), chain_id, expiration); let default_signer = args.source.unwrap_or(args.validator); process_tx::( client, @@ -677,36 +759,44 @@ pub async fn submit_unbond< wallet: &mut Wallet, args: args::Unbond, ) -> Result<(), Error> { - let validator = + let source = args.source.clone(); + // Check the source's current bond amount + let bond_source = source.clone().unwrap_or_else(|| args.validator.clone()); + + let tx_code_hash = + query_wasm_code_hash(client, args.tx_code_path.to_str().unwrap()) + .await + .unwrap(); + + if !args.tx.force { known_validator_or_err(args.validator.clone(), args.tx.force, client) .await?; - let source = args.source.clone(); - let tx_code = args.tx_code_path; - // Check the source's current bond amount - let bond_source = source.clone().unwrap_or_else(|| validator.clone()); - let bond_amount = - rpc::query_bond(client, &bond_source, &validator, None).await; - println!("Bond amount available for unbonding: {} NAM", bond_amount); + let bond_amount = + rpc::query_bond(client, &bond_source, &args.validator, None).await; + println!("Bond amount available for unbonding: {} NAM", bond_amount); - if args.amount > bond_amount { - eprintln!( - "The total bonds of the source {} is lower than the amount to be \ - unbonded. Amount to unbond is {} and the total bonds is {}.", - bond_source, args.amount, bond_amount - ); - if !args.tx.force { - return Err(Error::LowerBondThanUnbond( - bond_source, - args.amount, - bond_amount, - )); + if args.amount > bond_amount { + eprintln!( + "The total bonds of the source {} is lower than the amount to \ + be unbonded. Amount to unbond is {} and the total bonds is \ + {}.", + bond_source, args.amount, bond_amount + ); + if !args.tx.force { + return Err(Error::LowerBondThanUnbond( + bond_source, + args.amount, + bond_amount, + )); + } } } // Query the unbonds before submitting the tx let unbonds = - rpc::query_unbond_with_slashing(client, &bond_source, &validator).await; + rpc::query_unbond_with_slashing(client, &bond_source, &args.validator) + .await; let mut withdrawable = BTreeMap::::new(); for ((_start_epoch, withdraw_epoch), amount) in unbonds.into_iter() { let to_withdraw = withdrawable.entry(withdraw_epoch).or_default(); @@ -715,17 +805,19 @@ pub async fn submit_unbond< let latest_withdrawal_pre = withdrawable.into_iter().last(); let data = pos::Unbond { - validator: validator.clone(), + validator: args.validator.clone(), amount: args.amount, source, }; let data = data.try_to_vec().map_err(Error::EncodeTxFailure)?; - let chain_id = args.tx.chain_id.clone().unwrap(); - let expiration = args.tx.expiration; + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = args.tx.chain_id.clone().unwrap(); + tx.header.expiration = args.tx.expiration; + tx.set_data(Data::new(data)); + tx.set_code(Code::from_hash(tx_code_hash)); - let tx = Tx::new(tx_code, Some(data), chain_id, expiration); - let default_signer = args.source.unwrap_or(args.validator); + let default_signer = args.source.unwrap_or_else(|| args.validator.clone()); process_tx::( client, wallet, @@ -739,7 +831,8 @@ pub async fn submit_unbond< // Query the unbonds post-tx let unbonds = - rpc::query_unbond_with_slashing(client, &bond_source, &validator).await; + rpc::query_unbond_with_slashing(client, &bond_source, &args.validator) + .await; let mut withdrawable = BTreeMap::::new(); for ((_start_epoch, withdraw_epoch), amount) in unbonds.into_iter() { let to_withdraw = withdrawable.entry(withdraw_epoch).or_default(); @@ -823,7 +916,11 @@ pub async fn submit_bond< ) .await?; - let tx_code = args.tx_code_path; + let tx_code_hash = + query_wasm_code_hash(client, args.tx_code_path.to_str().unwrap()) + .await + .unwrap(); + let bond = pos::Bond { validator, amount: args.amount, @@ -831,10 +928,12 @@ pub async fn submit_bond< }; let data = bond.try_to_vec().map_err(Error::EncodeTxFailure)?; - let chain_id = args.tx.chain_id.clone().unwrap(); - let expiration = args.tx.expiration; + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = args.tx.chain_id.clone().unwrap(); + tx.header.expiration = args.tx.expiration; + tx.set_data(Data::new(data)); + tx.set_code(Code::from_hash(tx_code_hash)); - let tx = Tx::new(tx_code, Some(data), chain_id, expiration); let default_signer = args.source.unwrap_or(args.validator); process_tx::( client, @@ -919,7 +1018,10 @@ pub async fn submit_ibc_transfer< ) .await?; - let tx_code = args.tx_code_path; + let tx_code_hash = + query_wasm_code_hash(client, args.tx_code_path.to_str().unwrap()) + .await + .unwrap(); let denom = match sub_prefix { // To parse IbcToken address, remove the address prefix @@ -965,10 +1067,12 @@ pub async fn submit_ibc_transfer< prost::Message::encode(&any_msg, &mut data) .map_err(Error::EncodeFailure)?; - let chain_id = args.tx.chain_id.clone().unwrap(); - let expiration = args.tx.expiration; + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = args.tx.chain_id.clone().unwrap(); + tx.header.expiration = args.tx.expiration; + tx.set_data(Data::new(data)); + tx.set_code(Code::from_hash(tx_code_hash)); - let tx = Tx::new(tx_code, Some(data), chain_id, expiration); process_tx::( client, wallet, @@ -982,6 +1086,79 @@ pub async fn submit_ibc_transfer< Ok(()) } +/// Try to decode the given asset type and add its decoding to the supplied set. +/// Returns true only if a new decoding has been added to the given set. +async fn add_asset_type< + C: crate::ledger::queries::Client + Sync, + U: ShieldedUtils, +>( + asset_types: &mut HashSet<(Address, Epoch)>, + shielded: &mut ShieldedContext, + client: &C, + asset_type: AssetType, +) -> bool { + if let Some(asset_type) = + shielded.decode_asset_type(client, asset_type).await + { + asset_types.insert(asset_type) + } else { + false + } +} + +/// Collect the asset types used in the given Builder and decode them. This +/// function provides the data necessary for offline wallets to present asset +/// type information. +async fn used_asset_types< + C: crate::ledger::queries::Client + Sync, + U: ShieldedUtils, + P, + R, + K, + N, +>( + shielded: &mut ShieldedContext, + client: &C, + builder: &Builder, +) -> Result, RpcError> { + let mut asset_types = HashSet::new(); + // Collect all the asset types used in the Sapling inputs + for input in builder.sapling_inputs() { + add_asset_type(&mut asset_types, shielded, client, input.asset_type()) + .await; + } + // Collect all the asset types used in the transparent inputs + for input in builder.transparent_inputs() { + add_asset_type( + &mut asset_types, + shielded, + client, + input.coin().asset_type(), + ) + .await; + } + // Collect all the asset types used in the Sapling outputs + for output in builder.sapling_outputs() { + add_asset_type(&mut asset_types, shielded, client, output.asset_type()) + .await; + } + // Collect all the asset types used in the transparent outputs + for output in builder.transparent_outputs() { + add_asset_type(&mut asset_types, shielded, client, output.asset_type()) + .await; + } + // Collect all the asset types used in the Sapling converts + for output in builder.sapling_converts() { + for (asset_type, _) in + Amount::from(output.conversion().clone()).components() + { + add_asset_type(&mut asset_types, shielded, client, *asset_type) + .await; + } + } + Ok(asset_types) +} + /// Submit an ordinary transfer pub async fn submit_transfer< C: crate::ledger::queries::Client + Sync, @@ -993,43 +1170,30 @@ pub async fn submit_transfer< shielded: &mut ShieldedContext, args: args::TxTransfer, ) -> Result<(), Error> { + let source = args.source.effective_address(); + let target = args.target.effective_address(); + let token = args.token.clone(); + // Check that the source address exists on chain - let force = args.tx.force; - let transfer_source = args.source.clone(); - let source = source_exists_or_err( - transfer_source.effective_address(), - force, - client, - ) - .await?; + source_exists_or_err(source.clone(), args.tx.force, client).await?; // Check that the target address exists on chain - let transfer_target = args.target.clone(); - let target = target_exists_or_err( - transfer_target.effective_address(), - force, - client, - ) - .await?; - + target_exists_or_err(target.clone(), args.tx.force, client).await?; // Check that the token address exists on chain - let token = - &(token_exists_or_err(args.token.clone(), force, client).await?); - + token_exists_or_err(token.clone(), args.tx.force, client).await?; // Check source balance let (sub_prefix, balance_key) = match &args.sub_prefix { Some(sub_prefix) => { let sub_prefix = storage::Key::parse(sub_prefix).unwrap(); - let prefix = token::multitoken_balance_prefix(token, &sub_prefix); + let prefix = token::multitoken_balance_prefix(&token, &sub_prefix); ( Some(sub_prefix), token::multitoken_balance_key(&prefix, &source), ) } - None => (None, token::balance_key(token, &source)), + None => (None, token::balance_key(&token, &source)), }; - - check_balance_too_low_err( - token, + check_balance_too_low_err::( + &token, &source, args.amount, balance_key, @@ -1038,7 +1202,6 @@ pub async fn submit_transfer< ) .await?; - let tx_code = args.tx_code_path.clone(); let masp_addr = masp(); // For MASP sources, use a special sentinel key recognized by VPs as default // signer. Also, if the transaction is shielded, redact the amount and token @@ -1060,9 +1223,9 @@ pub async fn submit_transfer< ) } else { ( - TxSigningKey::WalletAddress(source.clone()), + TxSigningKey::WalletAddress(args.source.effective_address()), args.amount, - token.clone(), + token, ) }; // If our chosen signer is the MASP sentinel key, then our shielded inputs @@ -1078,53 +1241,124 @@ pub async fn submit_transfer< _ => None, }; - let stx_result = shielded - .gen_shielded_transfer(client, args.clone(), shielded_gas) - .await; - let shielded = match stx_result { - Ok(stx) => Ok(stx.map(|x| x.0)), - Err(builder::Error::ChangeIsNegative(_)) => { - Err(Error::NegativeBalanceAfterTransfer( - source.clone(), - args.amount, - token.clone(), - args.tx.fee_amount, - args.tx.fee_token.clone(), - )) - } - Err(err) => Err(Error::MaspError(err)), - }?; + #[cfg(not(feature = "mainnet"))] + let is_source_faucet = rpc::is_faucet_account(client, &source).await; - let transfer = token::Transfer { - source: source.clone(), - target, - token, - sub_prefix, - amount, - key, - shielded, - }; - tracing::debug!("Transfer data {:?}", transfer); - let data = transfer.try_to_vec().map_err(Error::EncodeTxFailure)?; + let tx_code_hash = + query_wasm_code_hash(client, args.tx_code_path.to_str().unwrap()) + .await + .unwrap(); - let chain_id = args.tx.chain_id.clone().unwrap(); - let expiration = args.tx.expiration; + // Loop twice in case the first submission attempt fails + for _ in 0..2 { + // Construct the shielded part of the transaction, if any + let stx_result = shielded + .gen_shielded_transfer(client, args.clone(), shielded_gas) + .await; - #[cfg(not(feature = "mainnet"))] - let is_source_faucet = rpc::is_faucet_account(client, &source).await; + let shielded_parts = match stx_result { + Ok(stx) => Ok(stx), + Err(builder::Error::InsufficientFunds(_)) => { + Err(Error::NegativeBalanceAfterTransfer( + source.clone(), + args.amount, + token.clone(), + args.tx.fee_amount, + args.tx.fee_token.clone(), + )) + } + Err(err) => Err(Error::MaspError(err)), + }?; - let tx = Tx::new(tx_code, Some(data), chain_id, expiration); - let signing_address = TxSigningKey::WalletAddress(source); - process_tx::( - client, - wallet, - &args.tx, - tx, - signing_address, - #[cfg(not(feature = "mainnet"))] - is_source_faucet, - ) - .await?; + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = args.tx.chain_id.clone().unwrap(); + tx.header.expiration = args.tx.expiration; + // Add the MASP Transaction and its Builder to facilitate validation + let (masp_hash, shielded_tx_epoch) = if let Some(shielded_parts) = + shielded_parts + { + // Add a MASP Transaction section to the Tx + let masp_tx = tx.add_section(Section::MaspTx(shielded_parts.1)); + // Get the hash of the MASP Transaction section + let masp_hash = + Hash(masp_tx.hash(&mut Sha256::new()).finalize_reset().into()); + // Get the decoded asset types used in the transaction to give + // offline wallet users more information + let asset_types = + used_asset_types(shielded, client, &shielded_parts.0) + .await + .unwrap_or_default(); + // Add the MASP Transaction's Builder to the Tx + tx.add_section(Section::MaspBuilder(MaspBuilder { + asset_types, + // Store how the Info objects map to Descriptors/Outputs + metadata: shielded_parts.2, + // Store the data that was used to construct the Transaction + builder: shielded_parts.0, + // Link the Builder to the Transaction by hash code + target: masp_hash, + })); + // The MASP Transaction section hash will be used in Transfer + (Some(masp_hash), Some(shielded_parts.3)) + } else { + (None, None) + }; + // Construct the corresponding transparent Transfer object + let transfer = token::Transfer { + source: source.clone(), + target: target.clone(), + token: token.clone(), + sub_prefix: sub_prefix.clone(), + amount, + key: key.clone(), + // Link the Transfer to the MASP Transaction by hash code + shielded: masp_hash, + }; + tracing::debug!("Transfer data {:?}", transfer); + // Encode the Transfer and store it beside the MASP transaction + let data = transfer + .try_to_vec() + .expect("Encoding tx data shouldn't fail"); + tx.set_data(Data::new(data)); + // Finally store the Traansfer WASM code in the Tx + tx.set_code(Code::from_hash(tx_code_hash)); + + // Dry-run/broadcast/submit the transaction + let result = process_tx::( + client, + wallet, + &args.tx, + tx, + default_signer.clone(), + #[cfg(not(feature = "mainnet"))] + is_source_faucet, + ) + .await?; + + // Query the epoch in which the transaction was probably submitted + let submission_epoch = rpc::query_epoch(client).await; + + match result { + ProcessTxResponse::Applied(resp) if + // If a transaction is shielded + shielded_tx_epoch.is_some() && + // And it is rejected by a VP + resp.code == 1.to_string() && + // And the its submission epoch doesn't match construction epoch + shielded_tx_epoch.unwrap() != submission_epoch => + { + // Then we probably straddled an epoch boundary. Let's retry... + eprintln!( + "MASP transaction rejected and this may be due to the \ + epoch changing. Attempting to resubmit transaction.", + ); + continue; + }, + // Otherwise either the transaction was successful or it will not + // benefit from resubmission + _ => break, + } + } Ok(()) } @@ -1138,22 +1372,32 @@ pub async fn submit_init_account< args: args::TxInitAccount, ) -> Result<(), Error> { let public_key = args.public_key; - let vp_code = args.vp_code; - // Validate the VP code - validate_untrusted_code_err(&vp_code, args.tx.force)?; - let vp_code_path = String::from_utf8(args.vp_code_path).unwrap(); + let vp_code_hash = - query_wasm_code_hash(client, vp_code_path).await.unwrap(); + query_wasm_code_hash(client, args.vp_code_path.to_str().unwrap()) + .await + .unwrap(); - let tx_code = args.tx_code_path; + let tx_code_hash = + query_wasm_code_hash(client, args.tx_code_path.to_str().unwrap()) + .await + .unwrap(); + + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = args.tx.chain_id.clone().unwrap(); + tx.header.expiration = args.tx.expiration; + let extra = + tx.add_section(Section::ExtraData(Code::from_hash(vp_code_hash))); + let extra_hash = + Hash(extra.hash(&mut Sha256::new()).finalize_reset().into()); let data = InitAccount { public_key, - vp_code_hash, + vp_code_hash: extra_hash, }; let data = data.try_to_vec().map_err(Error::EncodeTxFailure)?; - let chain_id = args.tx.chain_id.clone().unwrap(); - let expiration = args.tx.expiration; - let tx = Tx::new(tx_code, Some(data), chain_id, expiration); + tx.set_data(Data::new(data)); + tx.set_code(Code::from_hash(tx_code_hash)); + // TODO Move unwrap to an either let initialized_accounts = process_tx::( client, @@ -1165,7 +1409,8 @@ pub async fn submit_init_account< false, ) .await - .unwrap(); + .unwrap() + .initialized_accounts(); save_initialized_accounts::(wallet, &args.tx, initialized_accounts) .await; Ok(()) @@ -1222,21 +1467,31 @@ pub async fn submit_update_vp< } }?; - let vp_code_path = String::from_utf8(args.vp_code_path).unwrap(); let vp_code_hash = - query_wasm_code_hash(client, vp_code_path).await.unwrap(); + query_wasm_code_hash(client, args.vp_code_path.to_str().unwrap()) + .await + .unwrap(); - let tx_code_path = String::from_utf8(args.tx_code_path).unwrap(); let tx_code_hash = - query_wasm_code_hash(client, tx_code_path).await.unwrap(); - - let data = UpdateVp { addr, vp_code_hash }; + query_wasm_code_hash(client, args.tx_code_path.to_str().unwrap()) + .await + .unwrap(); + + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = args.tx.chain_id.clone().unwrap(); + tx.header.expiration = args.tx.expiration; + let extra = + tx.add_section(Section::ExtraData(Code::from_hash(vp_code_hash))); + let extra_hash = + Hash(extra.hash(&mut Sha256::new()).finalize_reset().into()); + let data = UpdateVp { + addr, + vp_code_hash: extra_hash, + }; let data = data.try_to_vec().map_err(Error::EncodeTxFailure)?; + tx.set_data(Data::new(data)); + tx.set_code(Code::from_hash(tx_code_hash)); - let chain_id = args.tx.chain_id.clone().unwrap(); - let expiration = args.tx.expiration; - - let tx = Tx::new(tx_code_hash.to_vec(), Some(data), chain_id, expiration); process_tx::( client, wallet, @@ -1259,11 +1514,12 @@ pub async fn submit_custom< wallet: &mut Wallet, args: args::TxCustom, ) -> Result<(), Error> { - let tx_code = args.code_path; - let data = args.data_path; - let chain_id = args.tx.chain_id.clone().unwrap(); - let expiration = args.tx.expiration; - let tx = Tx::new(tx_code, data, chain_id, expiration); + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = args.tx.chain_id.clone().unwrap(); + tx.header.expiration = args.tx.expiration; + args.data_path.map(|data| tx.set_data(Data::new(data))); + tx.set_code(Code::new(args.code_path)); + let initialized_accounts = process_tx::( client, wallet, @@ -1273,21 +1529,21 @@ pub async fn submit_custom< #[cfg(not(feature = "mainnet"))] false, ) - .await?; + .await? + .initialized_accounts(); save_initialized_accounts::(wallet, &args.tx, initialized_accounts) .await; Ok(()) } -async fn expect_dry_broadcast( +async fn expect_dry_broadcast( to_broadcast: TxBroadcastData, client: &C, - ret: T, -) -> Result { +) -> Result { match to_broadcast { TxBroadcastData::DryRun(tx) => { rpc::dry_run_tx(client, tx.to_bytes()).await; - Ok(ret) + Ok(ProcessTxResponse::DryRun) } TxBroadcastData::Wrapper { tx, @@ -1463,6 +1719,7 @@ async fn check_balance_too_low_err( } } +#[allow(dead_code)] fn validate_untrusted_code_err( vp_code: &Vec, force: bool, diff --git a/shared/src/ledger/vp_host_fns.rs b/shared/src/ledger/vp_host_fns.rs index de1d7b8c75..5d89e4006d 100644 --- a/shared/src/ledger/vp_host_fns.rs +++ b/shared/src/ledger/vp_host_fns.rs @@ -3,7 +3,7 @@ use std::num::TryFromIntError; use namada_core::types::address::Address; -use namada_core::types::hash::{Hash, HASH_LENGTH}; +use namada_core::types::hash::Hash; use namada_core::types::storage::{ BlockHash, BlockHeight, Epoch, Header, Key, TxIndex, }; @@ -14,7 +14,7 @@ use crate::ledger::gas; use crate::ledger::gas::VpGasMeter; use crate::ledger::storage::write_log::WriteLog; use crate::ledger::storage::{self, write_log, Storage, StorageHasher}; -use crate::proto::Tx; +use crate::proto::{Section, Tx}; /// These runtime errors will abort VP execution immediately #[allow(missing_docs)] @@ -286,13 +286,11 @@ where pub fn get_tx_code_hash( gas_meter: &mut VpGasMeter, tx: &Tx, -) -> EnvResult { - let hash = if tx.code_or_hash.len() == HASH_LENGTH { - Hash::try_from(&tx.code_or_hash[..]) - .map_err(|_| RuntimeError::InvalidCodeHash)? - } else { - Hash(tx.code_hash()) - }; +) -> EnvResult> { + let hash = tx + .get_section(tx.code_sechash()) + .and_then(Section::code_sec) + .map(|x| x.code.hash()); add_gas(gas_meter, MIN_STORAGE_GAS)?; Ok(hash) } diff --git a/shared/src/ledger/wallet/derivation_path.rs b/shared/src/ledger/wallet/derivation_path.rs new file mode 100644 index 0000000000..7f639161d2 --- /dev/null +++ b/shared/src/ledger/wallet/derivation_path.rs @@ -0,0 +1,159 @@ +use core::fmt; +use std::str::FromStr; + +use derivation_path::{ChildIndex, DerivationPath as DerivationPathInner}; +use thiserror::Error; +use tiny_hderive::bip44::{ + DerivationPath as HDeriveDerivationPath, + IntoDerivationPath as IntoHDeriveDerivationPath, +}; +use tiny_hderive::Error as HDeriveError; + +use crate::types::key::SchemeType; + +const ETH_COIN_TYPE: u32 = 60; +const NAMADA_COIN_TYPE: u32 = 877; + +#[derive(Error, Debug)] +pub enum DerivationPathError { + #[error("invalid derivation path: {0}")] + InvalidDerivationPath(String), +} + +#[derive(Clone)] +pub struct DerivationPath(DerivationPathInner); + +impl DerivationPath { + fn new

(path: P) -> Self + where + P: Into>, + { + Self(DerivationPathInner::new(path)) + } + + pub fn is_compatible(&self, scheme: SchemeType) -> bool { + if let Some(coin_type) = self.0.as_ref().get(1) { + let coin_type = coin_type.to_u32(); + match scheme { + SchemeType::Ed25519 => coin_type == NAMADA_COIN_TYPE, + SchemeType::Secp256k1 => coin_type == ETH_COIN_TYPE, + _ => true, + } + } else { + true + } + } + + fn bip44_base_indexes_for_scheme(scheme: SchemeType) -> Vec { + vec![ + ChildIndex::Hardened(44), + match scheme { + SchemeType::Secp256k1 => ChildIndex::Hardened(ETH_COIN_TYPE), + SchemeType::Ed25519 => ChildIndex::Hardened(NAMADA_COIN_TYPE), + SchemeType::Common => unimplemented!("not implemented"), + }, + ] + } + + fn bip44( + scheme: SchemeType, + account: u32, + change: u32, + address: u32, + ) -> Self { + let mut indexes = Self::bip44_base_indexes_for_scheme(scheme); + indexes.push(ChildIndex::Hardened(account)); + indexes.push(ChildIndex::Normal(change)); + indexes.push(ChildIndex::Normal(address)); + Self::new(indexes) + } + + fn hardened(&self, scheme: SchemeType) -> Self { + Self::new( + self.0 + .into_iter() + .map(|idx| match scheme { + SchemeType::Ed25519 => ChildIndex::Hardened(idx.to_u32()), + _ => *idx, + }) + .collect::>(), + ) + } + + pub fn default_for_scheme(scheme: SchemeType) -> Self { + let path = Self::bip44(scheme, 0, 0, 0); + path.hardened(scheme) + } + + pub fn from_path_str( + scheme: SchemeType, + path: &str, + ) -> Result { + let inner = DerivationPathInner::from_str(path).map_err(|err| { + DerivationPathError::InvalidDerivationPath(err.to_string()) + })?; + Ok(Self(inner).hardened(scheme)) + } + + pub fn path(&self) -> &[ChildIndex] { + self.0.path() + } +} + +impl fmt::Display for DerivationPath { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +impl IntoHDeriveDerivationPath for DerivationPath { + fn into(self) -> Result { + HDeriveDerivationPath::from_str(&self.0.to_string()) + } +} + +#[cfg(test)] +mod tests { + use super::DerivationPath; + use crate::types::key::SchemeType; + + #[test] + fn path_is_compatible() { + let path_empty = + DerivationPath::from_path_str(SchemeType::Secp256k1, "m") + .expect("Path construction cannot fail."); + assert!(path_empty.is_compatible(SchemeType::Ed25519)); + assert!(path_empty.is_compatible(SchemeType::Secp256k1)); + assert!(path_empty.is_compatible(SchemeType::Common)); + + let path_one = + DerivationPath::from_path_str(SchemeType::Secp256k1, "m/44'") + .expect("Path construction cannot fail."); + assert!(path_one.is_compatible(SchemeType::Ed25519)); + assert!(path_one.is_compatible(SchemeType::Secp256k1)); + assert!(path_one.is_compatible(SchemeType::Common)); + + let path_two = DerivationPath::from_path_str( + SchemeType::Secp256k1, + "m/44'/99999'", + ) + .expect("Path construction cannot fail."); + assert!(!path_two.is_compatible(SchemeType::Ed25519)); + assert!(!path_two.is_compatible(SchemeType::Secp256k1)); + assert!(path_two.is_compatible(SchemeType::Common)); + + let path_eth = + DerivationPath::from_path_str(SchemeType::Secp256k1, "m/44'/60'") + .expect("Path construction cannot fail."); + assert!(!path_eth.is_compatible(SchemeType::Ed25519)); + assert!(path_eth.is_compatible(SchemeType::Secp256k1)); + assert!(path_eth.is_compatible(SchemeType::Common)); + + let path_nam = + DerivationPath::from_path_str(SchemeType::Ed25519, "m/44'/877'") + .expect("Path construction cannot fail."); + assert!(path_nam.is_compatible(SchemeType::Ed25519)); + assert!(!path_nam.is_compatible(SchemeType::Secp256k1)); + assert!(path_nam.is_compatible(SchemeType::Common)); + } +} diff --git a/shared/src/ledger/wallet/keys.rs b/shared/src/ledger/wallet/keys.rs index ce8c1769dc..d13ebbd07c 100644 --- a/shared/src/ledger/wallet/keys.rs +++ b/shared/src/ledger/wallet/keys.rs @@ -9,6 +9,7 @@ use data_encoding::HEXLOWER; use orion::{aead, kdf}; use serde::{Deserialize, Serialize}; use thiserror::Error; +use zeroize::Zeroizing; use crate::ledger::wallet::WalletUtils; @@ -152,7 +153,7 @@ where /// Construct a keypair for storage. If no password is provided, the keypair /// will be stored raw without encryption. Returns the key for storing and a /// reference-counting point to the raw key. - pub fn new(keypair: T, password: Option) -> (Self, T) { + pub fn new(keypair: T, password: Option>) -> (Self, T) { match password { Some(password) => ( Self::Encrypted(EncryptedKeypair::new(&keypair, password)), @@ -168,14 +169,13 @@ where pub fn get( &self, decrypt: bool, - password: Option, + password: Option>, ) -> Result { match self { StoredKeypair::Encrypted(encrypted_keypair) => { if decrypt { - let password = password.unwrap_or_else(|| { - U::read_password("Enter decryption password: ") - }); + let password = password + .unwrap_or_else(|| U::read_decryption_password()); let key = encrypted_keypair.decrypt(password)?; Ok(key) } else { @@ -197,9 +197,9 @@ where impl EncryptedKeypair { /// Encrypt a keypair and store it with its salt. - pub fn new(keypair: &T, password: String) -> Self { + pub fn new(keypair: &T, password: Zeroizing) -> Self { let salt = encryption_salt(); - let encryption_key = encryption_key(&salt, password); + let encryption_key = encryption_key(&salt, &password); let data = keypair .try_to_vec() @@ -214,14 +214,17 @@ impl EncryptedKeypair { } /// Decrypt an encrypted keypair - pub fn decrypt(&self, password: String) -> Result { + pub fn decrypt( + &self, + password: Zeroizing, + ) -> Result { let salt_len = encryption_salt().len(); let (raw_salt, cipher) = self.0.split_at(salt_len); let salt = kdf::Salt::from_slice(raw_salt) .map_err(|_| DecryptionError::BadSalt)?; - let encryption_key = encryption_key(&salt, password); + let encryption_key = encryption_key(&salt, &password); let decrypted_data = aead::open(&encryption_key, cipher) .map_err(|_| DecryptionError::DecryptionError)?; @@ -237,7 +240,7 @@ fn encryption_salt() -> kdf::Salt { } /// Make encryption secret key from a password. -fn encryption_key(salt: &kdf::Salt, password: String) -> kdf::SecretKey { +fn encryption_key(salt: &kdf::Salt, password: &str) -> kdf::SecretKey { kdf::Password::from_slice(password.as_bytes()) .and_then(|password| kdf::derive_key(&password, salt, 3, 1 << 17, 32)) .expect("Generation of encryption secret key shouldn't fail") diff --git a/shared/src/ledger/wallet/mod.rs b/shared/src/ledger/wallet/mod.rs index cf90aed460..7bbca76e87 100644 --- a/shared/src/ledger/wallet/mod.rs +++ b/shared/src/ledger/wallet/mod.rs @@ -1,21 +1,25 @@ //! Provides functionality for managing keys and addresses for a user pub mod alias; +mod derivation_path; mod keys; pub mod pre_genesis; pub mod store; use std::collections::{HashMap, HashSet}; use std::fmt::Display; -use std::marker::PhantomData; use std::str::FromStr; use alias::Alias; +use bip39::{Language, Mnemonic, MnemonicType, Seed}; use borsh::{BorshDeserialize, BorshSerialize}; use masp_primitives::zip32::ExtendedFullViewingKey; pub use pre_genesis::gen_key_to_store; -pub use store::{gen_sk, AddressVpType, Store}; +use rand_core::RngCore; +pub use store::{gen_sk_rng, AddressVpType, Store}; use thiserror::Error; +use zeroize::Zeroizing; +use self::derivation_path::{DerivationPath, DerivationPathError}; pub use self::keys::{DecryptionError, StoredKeypair}; pub use self::store::{ConfirmationResponse, ValidatorData, ValidatorKeys}; use crate::types::address::Address; @@ -24,49 +28,70 @@ use crate::types::masp::{ ExtendedSpendingKey, ExtendedViewingKey, PaymentAddress, }; +/// Errors of key generation / recovery +#[derive(Error, Debug)] +pub enum GenRestoreKeyError { + /// Derivation path parse error + #[error("Derivation path parse error")] + DerivationPathError(DerivationPathError), + /// Mnemonic generation error + #[error("Mnemonic generation error")] + MnemonicGenerationError, + /// Mnemonic input error + #[error("Mnemonic input error")] + MnemonicInputError, +} + /// Captures the interactive parts of the wallet's functioning pub trait WalletUtils { /// The location where the wallet is stored type Storage; - /// Read the password for encryption/decryption from the file/env/stdin. - /// Panics if all options are empty/invalid. - fn read_password(prompt_msg: &str) -> String; + /// Secure random number generator + type Rng: RngCore; + + /// Generates a random mnemonic of the given mnemonic type. + fn generate_mnemonic_code( + mnemonic_type: MnemonicType, + rng: &mut Self::Rng, + ) -> Result { + const BITS_PER_BYTE: usize = 8; + + // generate random mnemonic + let entropy_size = mnemonic_type.entropy_bits() / BITS_PER_BYTE; + let mut bytes = vec![0u8; entropy_size]; + rand::RngCore::fill_bytes(rng, &mut bytes); + let mnemonic = Mnemonic::from_entropy(&bytes, Language::English) + .expect("Mnemonic creation should not fail"); + + Ok(mnemonic) + } + + /// Read the password for decryption from the file/env/stdin. + fn read_decryption_password() -> Zeroizing; + + /// Read the password for encryption from the file/env/stdin. + /// If the password is read from stdin, the implementation is expected + /// to ask for a confirmation. + fn read_encryption_password() -> Zeroizing; /// Read an alias from the file/env/stdin. fn read_alias(prompt_msg: &str) -> String; + /// Read mnemonic code from the file/env/stdin. + fn read_mnemonic_code() -> Result; + + /// Read a mnemonic code from the file/env/stdin. + fn read_mnemonic_passphrase(confirm: bool) -> Zeroizing; + /// The given alias has been selected but conflicts with another alias in /// the store. Offer the user to either replace existing mapping, alter the - /// chosen alias to a name of their chosing, or cancel the aliasing. + /// chosen alias to a name of their choice, or cancel the aliasing. fn show_overwrite_confirmation( alias: &Alias, alias_for: &str, ) -> store::ConfirmationResponse; } -/// A degenerate implementation of wallet interactivity -pub struct SdkWalletUtils(PhantomData); - -impl WalletUtils for SdkWalletUtils { - type Storage = U; - - fn read_password(_prompt_msg: &str) -> String { - panic!("attempted to prompt for password in non-interactive mode"); - } - - fn read_alias(_prompt_msg: &str) -> String { - panic!("attempted to prompt for alias in non-interactive mode"); - } - - fn show_overwrite_confirmation( - _alias: &Alias, - _alias_for: &str, - ) -> store::ConfirmationResponse { - // Automatically replace aliases in non-interactive mode - store::ConfirmationResponse::Replace - } -} - /// The error that is produced when a given key cannot be obtained #[derive(Error, Debug)] pub enum FindKeyError { @@ -98,33 +123,150 @@ impl Wallet { } } + fn gen_and_store_key( + &mut self, + scheme: SchemeType, + alias: Option, + alias_force: bool, + seed_and_derivation_path: Option<(Seed, DerivationPath)>, + password: Option>, + ) -> Result<(String, common::SecretKey), GenRestoreKeyError> { + let (alias, key) = self.store.gen_key::( + scheme, + alias, + alias_force, + seed_and_derivation_path, + password, + ); + // Cache the newly added key + self.decrypted_key_cache.insert(alias.clone(), key.clone()); + Ok((alias.into(), key)) + } + + /// Restore a keypair from the user mnemonic code (read from stdin) using + /// a given BIP44 derivation path and derive an implicit address from its + /// public part and insert them into the store with the provided alias, + /// converted to lower case. If none provided, the alias will be the public + /// key hash (in lowercase too). + /// The key is encrypted with the provided password. If no password + /// provided, will prompt for password from stdin. + /// Stores the key in decrypted key cache and returns the alias of the key + /// and a reference-counting pointer to the key. + pub fn derive_key_from_user_mnemonic_code( + &mut self, + scheme: SchemeType, + alias: Option, + alias_force: bool, + derivation_path: Option, + password: Option>, + ) -> Result<(String, common::SecretKey), GenRestoreKeyError> { + let parsed_derivation_path = derivation_path + .map(|p| { + let is_default = p.eq_ignore_ascii_case("DEFAULT"); + if is_default { + Ok(DerivationPath::default_for_scheme(scheme)) + } else { + DerivationPath::from_path_str(scheme, &p) + .map_err(GenRestoreKeyError::DerivationPathError) + } + }) + .transpose()? + .unwrap_or_else(|| DerivationPath::default_for_scheme(scheme)); + if !parsed_derivation_path.is_compatible(scheme) { + println!( + "WARNING: the specified derivation path may be incompatible \ + with the chosen cryptography scheme." + ) + } + println!("Using HD derivation path {}", parsed_derivation_path); + let mnemonic = U::read_mnemonic_code()?; + let passphrase = U::read_mnemonic_passphrase(false); + let seed = Seed::new(&mnemonic, &passphrase); + + self.gen_and_store_key( + scheme, + alias, + alias_force, + Some((seed, parsed_derivation_path)), + password, + ) + } + /// Generate a new keypair and derive an implicit address from its public /// and insert them into the store with the provided alias, converted to /// lower case. If none provided, the alias will be the public key hash (in - /// lowercase too). If the key is to be encrypted, will prompt for - /// password from stdin. Stores the key in decrypted key cache and + /// lowercase too). If the alias already exists, optionally force overwrite + /// the keypair for the alias. + /// If no encryption password is provided, the keypair will be stored raw + /// without encryption. + /// Stores the key in decrypted key cache and /// returns the alias of the key and a reference-counting pointer to the /// key. + /// If a derivation path is specified, derive the key from a generated BIP39 + /// mnemonic code. Use provided rng for mnemonic code generation. pub fn gen_key( &mut self, scheme: SchemeType, alias: Option, - password: Option, - force_alias: bool, - ) -> (String, common::SecretKey) { - let (alias, key) = - self.store - .gen_key::(scheme, alias, password, force_alias); - // Cache the newly added key - self.decrypted_key_cache.insert(alias.clone(), key.clone()); - (alias.into(), key) + alias_force: bool, + password: Option>, + derivation_path_and_mnemonic_rng: Option<(String, &mut U::Rng)>, + ) -> Result<(String, common::SecretKey), GenRestoreKeyError> { + let parsed_path_and_rng = derivation_path_and_mnemonic_rng + .map(|(raw_derivation_path, rng)| { + let is_default = + raw_derivation_path.eq_ignore_ascii_case("DEFAULT"); + let parsed_derivation_path = if is_default { + Ok(DerivationPath::default_for_scheme(scheme)) + } else { + DerivationPath::from_path_str(scheme, &raw_derivation_path) + .map_err(GenRestoreKeyError::DerivationPathError) + }; + parsed_derivation_path.map(|p| (p, rng)) + }) + .transpose()?; + + // Check if the path is compatible with the selected scheme + if parsed_path_and_rng.is_some() { + let (parsed_derivation_path, _) = + parsed_path_and_rng.as_ref().unwrap(); + if !parsed_derivation_path.is_compatible(scheme) { + println!( + "WARNING: the specified derivation path may be \ + incompatible with the chosen cryptography scheme." + ) + } + println!("Using HD derivation path {}", parsed_derivation_path); + } + + let seed_and_derivation_path //: Option> + = parsed_path_and_rng.map(|(path, rng)| { + const MNEMONIC_TYPE: MnemonicType = MnemonicType::Words24; + let mnemonic = U::generate_mnemonic_code(MNEMONIC_TYPE, rng)?; + println!( + "Safely store your {} words mnemonic.", + MNEMONIC_TYPE.word_count() + ); + println!("{}", mnemonic.clone().into_phrase()); + + let passphrase = U::read_mnemonic_passphrase(true); + Ok((Seed::new(&mnemonic, &passphrase), path)) + }).transpose()?; + + self.gen_and_store_key( + scheme, + alias, + alias_force, + seed_and_derivation_path, + password, + ) } /// Generate a spending key and store it under the given alias in the wallet pub fn gen_spending_key( &mut self, alias: String, - password: Option, + password: Option>, force_alias: bool, ) -> (String, ExtendedSpendingKey) { let (alias, key) = @@ -163,7 +305,7 @@ impl Wallet { pub fn find_key( &mut self, alias_pkh_or_pk: impl AsRef, - password: Option, + password: Option>, ) -> Result { // Try cache first if let Some(cached_key) = self @@ -191,7 +333,7 @@ impl Wallet { pub fn find_spending_key( &mut self, alias: impl AsRef, - password: Option, + password: Option>, ) -> Result { // Try cache first if let Some(cached_key) = @@ -238,7 +380,7 @@ impl Wallet { pub fn find_key_by_pk( &mut self, pk: &common::PublicKey, - password: Option, + password: Option>, ) -> Result { // Try to look-up alias for the given pk. Otherwise, use the PKH string. let pkh: PublicKeyHash = pk.into(); @@ -270,7 +412,7 @@ impl Wallet { pub fn find_key_by_pkh( &mut self, pkh: &PublicKeyHash, - password: Option, + password: Option>, ) -> Result { // Try to look-up alias for the given pk. Otherwise, use the PKH string. let alias = self @@ -304,16 +446,15 @@ impl Wallet { decrypted_key_cache: &mut HashMap, stored_key: &StoredKeypair, alias: Alias, - password: Option, + password: Option>, ) -> Result where ::Err: Display, { match stored_key { StoredKeypair::Encrypted(encrypted) => { - let password = password.unwrap_or_else(|| { - U::read_password("Enter decryption password: ") - }); + let password = + password.unwrap_or_else(U::read_decryption_password); let key = encrypted .decrypt(password) .map_err(FindKeyError::KeyDecryptionError)?; @@ -455,7 +596,7 @@ impl Wallet { &mut self, alias: String, spend_key: ExtendedSpendingKey, - password: Option, + password: Option>, force_alias: bool, ) -> Option { self.store diff --git a/shared/src/ledger/wallet/pre_genesis.rs b/shared/src/ledger/wallet/pre_genesis.rs index 333a9f21e7..cd486db9a4 100644 --- a/shared/src/ledger/wallet/pre_genesis.rs +++ b/shared/src/ledger/wallet/pre_genesis.rs @@ -1,6 +1,7 @@ //! Provides functionality for managing validator keys use serde::{Deserialize, Serialize}; use thiserror::Error; +use zeroize::Zeroizing; use crate::ledger::wallet; use crate::ledger::wallet::{store, StoredKeypair}; @@ -67,10 +68,10 @@ impl ValidatorStore { /// Generate a key and then encrypt it pub fn gen_key_to_store( scheme: SchemeType, - password: &Option, + password: Option>, ) -> (StoredKeypair, common::SecretKey) { - let sk = store::gen_sk(scheme); - StoredKeypair::new(sk, password.clone()) + let sk = store::gen_sk_rng(scheme); + StoredKeypair::new(sk, password) } impl From for ReadError { diff --git a/shared/src/ledger/wallet/store.rs b/shared/src/ledger/wallet/store.rs index 5d33c9dea7..aeeb4dfa9b 100644 --- a/shared/src/ledger/wallet/store.rs +++ b/shared/src/ledger/wallet/store.rs @@ -5,12 +5,17 @@ use std::fmt::Display; use std::str::FromStr; use bimap::BiHashMap; +use bip39::Seed; +use itertools::Itertools; use masp_primitives::zip32::ExtendedFullViewingKey; #[cfg(feature = "masp-tx-gen")] use rand_core::RngCore; use serde::{Deserialize, Serialize}; +use slip10_ed25519; +use zeroize::Zeroizing; use super::alias::{self, Alias}; +use super::derivation_path::DerivationPath; use super::pre_genesis; use crate::ledger::wallet::{StoredKeypair, WalletUtils}; use crate::types::address::{Address, ImplicitAddress}; @@ -190,7 +195,7 @@ impl Store { keys } - /// Get all known addresses by their alias, paired with PKH, if known. + /// Get all known addresses by their alias. pub fn get_addresses(&self) -> &BiHashMap { &self.addresses } @@ -223,17 +228,31 @@ impl Store { /// Generate a new keypair and insert it into the store with the provided /// alias. If none provided, the alias will be the public key hash. - /// If no password is provided, the keypair will be stored raw without - /// encryption. Returns the alias of the key and a reference-counting - /// pointer to the key. + /// If the alias already exists, optionally force overwrite the keypair + /// for the alias. + /// If no encryption password is provided, the keypair will be stored raw + /// without encryption. + /// Optionally, use a given random seed and a BIP44 derivation path. + /// Returns the alias of the key and a reference-counting pointer to the + /// key. pub fn gen_key( &mut self, scheme: SchemeType, alias: Option, - password: Option, - force_alias: bool, + alias_force: bool, + seed_and_derivation_path: Option<(Seed, DerivationPath)>, + password: Option>, ) -> (Alias, common::SecretKey) { - let sk = gen_sk(scheme); + let sk = if let Some((seed, derivation_path)) = seed_and_derivation_path + { + gen_sk_from_seed_and_derivation_path( + scheme, + seed.as_bytes(), + derivation_path, + ) + } else { + gen_sk_rng(scheme) + }; let pkh: PublicKeyHash = PublicKeyHash::from(&sk.ref_to()); let (keypair_to_store, raw_keypair) = StoredKeypair::new(sk, password); let address = Address::Implicit(ImplicitAddress(pkh.clone())); @@ -243,14 +262,14 @@ impl Store { alias.clone(), keypair_to_store, pkh, - force_alias, + alias_force, ) .is_none() { panic!("Action cancelled, no changes persisted."); } if self - .insert_address::(alias.clone(), address, force_alias) + .insert_address::(alias.clone(), address, alias_force) .is_none() { panic!("Action cancelled, no changes persisted."); @@ -262,7 +281,7 @@ impl Store { pub fn gen_spending_key( &mut self, alias: String, - password: Option, + password: Option>, force_alias: bool, ) -> (Alias, ExtendedSpendingKey) { let spendkey = Self::generate_spending_key(); @@ -314,6 +333,12 @@ impl Store { pkh: PublicKeyHash, force: bool, ) -> Option { + // abort if the key already exists + if self.pkhs.contains_key(&pkh) { + println!("The key already exists."); + return None; + } + if alias.is_empty() { println!( "Empty alias given, defaulting to {}.", @@ -613,7 +638,7 @@ impl Store { } /// Generate a new secret key. -pub fn gen_sk(scheme: SchemeType) -> common::SecretKey { +pub fn gen_sk_rng(scheme: SchemeType) -> common::SecretKey { use rand::rngs::OsRng; let mut csprng = OsRng {}; match scheme { @@ -629,6 +654,43 @@ pub fn gen_sk(scheme: SchemeType) -> common::SecretKey { } } +/// Generate a new secret key from the seed. +pub fn gen_sk_from_seed_and_derivation_path( + scheme: SchemeType, + seed: &[u8], + derivation_path: DerivationPath, +) -> common::SecretKey { + match scheme { + SchemeType::Ed25519 => { + let indexes = derivation_path + .path() + .iter() + .map(|idx| idx.to_bits()) + .collect_vec(); + // SLIP10 Ed25519 key derivation function promotes all indexes to + // hardened indexes. + let sk = slip10_ed25519::derive_ed25519_private_key(seed, &indexes); + ed25519::SigScheme::from_bytes(sk).try_to_sk().unwrap() + } + SchemeType::Secp256k1 => { + let xpriv = tiny_hderive::bip32::ExtendedPrivKey::derive( + seed, + derivation_path, + ) + .expect("Secret key derivation should not fail."); + secp256k1::SigScheme::from_bytes(xpriv.secret()) + .try_to_sk() + .unwrap() + } + SchemeType::Common => { + panic!( + "Cannot generate common signing scheme. Must convert from \ + alternative scheme." + ) + } + } +} + impl Display for AddressVpType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -668,3 +730,162 @@ impl<'de> Deserialize<'de> for AddressVpType { Self::from_str(&raw).map_err(D::Error::custom) } } + +#[cfg(all(test, feature = "dev"))] +mod test_wallet { + use base58::{self, FromBase58}; + use bip39::{Language, Mnemonic}; + use hex; + + use super::super::derivation_path::DerivationPath; + use super::*; + + #[test] + fn gen_sk_from_mnemonic_code_secp256k1() { + const SCHEME: SchemeType = SchemeType::Secp256k1; + const MNEMONIC_CODE: &str = "cruise ball fame lucky fabric govern \ + length fruit permit tonight fame pear \ + horse park key chimney furnace lobster \ + foot example shoot dry fuel lawn"; + const SEED_EXPECTED: &str = "8601e9f639f995f856c8ecfa3cd298d292253d071d6fa75d50dd31f6ba9df743ec279354a73fe78e6011738027fb994e5d904c476c4679c7c35e82586e14297f"; + const PASSPHRASE: &str = "test"; + const DERIVATION_PATH: &str = "m/44'/60'/0'/0/0"; + const SK_EXPECTED: &str = + "9e426cc5f63cdbd5f362adb918a07c2b16c593b2bc1b244f33b9e2c3ac6b265a"; + + let mnemonic = Mnemonic::from_phrase(MNEMONIC_CODE, Language::English) + .expect("Mnemonic construction cannot fail."); + let seed = Seed::new(&mnemonic, PASSPHRASE); + assert_eq!(format!("{:x}", seed), SEED_EXPECTED); + + let derivation_path = + DerivationPath::from_path_str(SCHEME, DERIVATION_PATH) + .expect("Derivation path construction cannot fail"); + + let sk = gen_sk_from_seed_and_derivation_path( + SCHEME, + seed.as_bytes(), + derivation_path, + ); + + assert_eq!(&sk.to_string()[2..], SK_EXPECTED); + } + + #[test] + fn gen_sk_from_mnemonic_code_ed25519() { + const SCHEME: SchemeType = SchemeType::Ed25519; + const MNEMONIC_CODE: &str = "cruise ball fame lucky fabric govern \ + length fruit permit tonight fame pear \ + horse park key chimney furnace lobster \ + foot example shoot dry fuel lawn"; + const SEED_EXPECTED: &str = "8601e9f639f995f856c8ecfa3cd298d292253d071d6fa75d50dd31f6ba9df743ec279354a73fe78e6011738027fb994e5d904c476c4679c7c35e82586e14297f"; + const PASSPHRASE: &str = "test"; + const DERIVATION_PATH: &str = "m/44'/877'/0'/0/0"; + const DERIVATION_PATH_HARDENED: &str = "m/44'/877'/0'/0'/0'"; + + let mnemonic = Mnemonic::from_phrase(MNEMONIC_CODE, Language::English) + .expect("Mnemonic construction cannot fail."); + let seed = Seed::new(&mnemonic, PASSPHRASE); + assert_eq!(format!("{:x}", seed), SEED_EXPECTED); + + let derivation_path = + DerivationPath::from_path_str(SCHEME, DERIVATION_PATH) + .expect("Derivation path construction cannot fail"); + + let derivation_path_hardened = + DerivationPath::from_path_str(SCHEME, DERIVATION_PATH_HARDENED) + .expect("Derivation path construction cannot fail"); + + let sk = gen_sk_from_seed_and_derivation_path( + SCHEME, + seed.as_bytes(), + derivation_path, + ); + + let sk_hard = gen_sk_from_seed_and_derivation_path( + SCHEME, + seed.as_bytes(), + derivation_path_hardened, + ); + + // check that indexes are promoted to hardened + assert_eq!(&sk.to_string(), &sk_hard.to_string()); + } + + fn do_test_gen_sk_from_seed_and_derivation_path( + scheme: SchemeType, + seed: &str, + derivation_path: &str, + priv_key: &str, + ) { + let sk = gen_sk_from_seed_and_derivation_path( + scheme, + hex::decode(seed) + .expect("Seed parsing cannot fail.") + .as_slice(), + DerivationPath::from_path_str(scheme, derivation_path) + .expect("Derivation path construction cannot fail"), + ); + let sk_expected = if priv_key.starts_with("xprv") { + // this is an extended private key encoded in base58 + let xprv = + priv_key.from_base58().expect("XPRV parsing cannot fail."); + hex::encode(&xprv[46..78]) + } else { + priv_key.to_string() + }; + assert_eq!(&sk.to_string()[2..], sk_expected); + } + + #[test] + fn gen_sk_from_seed_secp256k1() { + const SCHEME: SchemeType = SchemeType::Secp256k1; + // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki + { + // Test vector 1 + const SEED: &str = "000102030405060708090a0b0c0d0e0f"; + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m", "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0'", "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0'/1", "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0'/1/2'", "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0'/1/2'/2", "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0'/1/2'/2/1000000000", "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76"); + } + { + // Test vector 2 + const SEED: &str = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"; + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m", "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0", "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0/2147483647'", "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0/2147483647'/1", "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0/2147483647'/1/2147483646'", "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0/2147483647'/1/2147483646'/2", "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"); + } + } + + #[test] + fn gen_sk_from_seed_ed25519() { + const SCHEME: SchemeType = SchemeType::Ed25519; + // https://github.com/satoshilabs/slips/blob/master/slip-0010.md + { + // Test vector 1 for ed15519 + const SEED: &str = "000102030405060708090a0b0c0d0e0f"; + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m", "2b4be7f19ee27bbf30c667b642d5f4aa69fd169872f8fc3059c08ebae2eb19e7"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0'", "68e0fe46dfb67e368c75379acec591dad19df3cde26e63b93a8e704f1dade7a3"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0'/1'", "b1d0bad404bf35da785a64ca1ac54b2617211d2777696fbffaf208f746ae84f2"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0'/1'/2'", "92a5b23c0b8a99e37d07df3fb9966917f5d06e02ddbd909c7e184371463e9fc9"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0'/1'/2'/2'", "30d1dc7e5fc04c31219ab25a27ae00b50f6fd66622f6e9c913253d6511d1e662"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0'/1'/2'/2'/1000000000'", "8f94d394a8e8fd6b1bc2f3f49f5c47e385281d5c17e65324b0f62483e37e8793"); + } + { + // Test vector 2 for ed15519 + const SEED: &str = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"; + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m", "171cb88b1b3c1db25add599712e36245d75bc65a1a5c9e18d76f9f2b1eab4012"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0'", "1559eb2bbec5790b0c65d8693e4d0875b1747f4970ae8b650486ed7470845635"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0'/2147483647'", "ea4f5bfe8694d8bb74b7b59404632fd5968b774ed545e810de9c32a4fb4192f4"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0'/2147483647'/1'", "3757c7577170179c7868353ada796c839135b3d30554bbb74a4b1e4a5a58505c"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0'/2147483647'/1'/2147483646'", "5837736c89570de861ebc173b1086da4f505d4adb387c6a1b1342d5e4ac9ec72"); + do_test_gen_sk_from_seed_and_derivation_path(SCHEME, SEED, "m/0'/2147483647'/1'/2147483646'/2'", "551d333177df541ad876a60ea71f00447931c0a9da16f227c11ea080d7391b8d"); + } + } +} diff --git a/shared/src/lib.rs b/shared/src/lib.rs index a35a29a61f..06838e79c2 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -10,6 +10,7 @@ pub use tendermint_rpc; #[cfg(feature = "tendermint-rpc-abcipp")] pub use tendermint_rpc_abcipp as tendermint_rpc; +pub use {bip39, namada_core as core, namada_proof_of_stake as proof_of_stake}; #[cfg(feature = "abciplus")] pub use {ibc, ibc_proto, tendermint, tendermint_proto}; #[cfg(feature = "abcipp")] @@ -18,7 +19,6 @@ pub use { tendermint_abcipp as tendermint, tendermint_proto_abcipp as tendermint_proto, }; -pub use {namada_core as core, namada_proof_of_stake as proof_of_stake}; pub mod ledger; pub use namada_core::proto; pub mod types; diff --git a/shared/src/vm/host_env.rs b/shared/src/vm/host_env.rs index 238ba58520..abd3f62551 100644 --- a/shared/src/vm/host_env.rs +++ b/shared/src/vm/host_env.rs @@ -22,13 +22,11 @@ use crate::types::address::{self, Address}; use crate::types::hash::Hash; use crate::types::ibc::IbcEvent; use crate::types::internal::HostEnvResult; -use crate::types::key::*; use crate::types::storage::{BlockHeight, Key, TxIndex}; use crate::vm::memory::VmMemory; use crate::vm::prefix_iter::{PrefixIteratorId, PrefixIterators}; use crate::vm::{HostRef, MutHostRef}; -const VERIFY_TX_SIG_GAS_COST: u64 = 1000; const WASM_VALIDATION_GAS_PER_BYTE: u64 = 1; /// These runtime errors will abort tx WASM execution immediately @@ -92,6 +90,8 @@ where pub iterators: MutHostRef<'a, &'a PrefixIterators<'a, DB>>, /// Transaction gas meter. pub gas_meter: MutHostRef<'a, &'a BlockGasMeter>, + /// The transaction code is used for signature verification + pub tx: HostRef<'a, &'a Tx>, /// The transaction index is used to identify a shielded transaction's /// parent pub tx_index: HostRef<'a, &'a TxIndex>, @@ -132,6 +132,7 @@ where write_log: &mut WriteLog, iterators: &mut PrefixIterators<'a, DB>, gas_meter: &mut BlockGasMeter, + tx: &Tx, tx_index: &TxIndex, verifiers: &mut BTreeSet

, result_buffer: &mut Option>, @@ -142,6 +143,7 @@ where let write_log = unsafe { MutHostRef::new(write_log) }; let iterators = unsafe { MutHostRef::new(iterators) }; let gas_meter = unsafe { MutHostRef::new(gas_meter) }; + let tx = unsafe { HostRef::new(tx) }; let tx_index = unsafe { HostRef::new(tx_index) }; let verifiers = unsafe { MutHostRef::new(verifiers) }; let result_buffer = unsafe { MutHostRef::new(result_buffer) }; @@ -154,6 +156,7 @@ where write_log, iterators, gas_meter, + tx, tx_index, verifiers, result_buffer, @@ -196,6 +199,7 @@ where write_log: self.write_log.clone(), iterators: self.iterators.clone(), gas_meter: self.gas_meter.clone(), + tx: self.tx.clone(), tx_index: self.tx_index.clone(), verifiers: self.verifiers.clone(), result_buffer: self.result_buffer.clone(), @@ -287,8 +291,8 @@ pub trait VpEvaluator { fn eval( &self, ctx: VpCtx<'static, Self::Db, Self::H, Self::Eval, Self::CA>, - vp_code: Vec, - input_data: Vec, + vp_code_hash: Hash, + input_data: Tx, ) -> HostEnvResult; } @@ -1481,9 +1485,9 @@ where Ok(height.0) } -/// Getting the block height function exposed to the wasm VM Tx -/// environment. The height is that of the block to which the current -/// transaction is being applied. +/// Getting the transaction index function exposed to the wasm VM Tx +/// environment. The index is that of the transaction being applied +/// in the current block. pub fn tx_get_tx_index( env: &TxVmEnv, ) -> TxResult @@ -1725,9 +1729,16 @@ where let gas_meter = unsafe { env.ctx.gas_meter.get() }; let tx = unsafe { env.ctx.tx.get() }; let hash = vp_host_fns::get_tx_code_hash(gas_meter, tx)?; + let mut result_bytes = vec![]; + if let Some(hash) = hash { + result_bytes.push(1); + result_bytes.extend(hash.0); + } else { + result_bytes.push(0); + }; let gas = env .memory - .write_bytes(result_ptr, hash.0) + .write_bytes(result_ptr, result_bytes) .map_err(|e| vp_host_fns::RuntimeError::MemoryError(Box::new(e)))?; vp_host_fns::add_gas(gas_meter, gas) } @@ -1751,43 +1762,6 @@ where Ok(epoch.0) } -/// Verify a transaction signature. -pub fn vp_verify_tx_signature( - env: &VpVmEnv, - pk_ptr: u64, - pk_len: u64, - sig_ptr: u64, - sig_len: u64, -) -> vp_host_fns::EnvResult -where - MEM: VmMemory, - DB: storage::DB + for<'iter> storage::DBIter<'iter>, - H: StorageHasher, - EVAL: VpEvaluator, - CA: WasmCacheAccess, -{ - let (pk, gas) = env - .memory - .read_bytes(pk_ptr, pk_len as _) - .map_err(|e| vp_host_fns::RuntimeError::MemoryError(Box::new(e)))?; - let gas_meter = unsafe { env.ctx.gas_meter.get() }; - vp_host_fns::add_gas(gas_meter, gas)?; - let pk: common::PublicKey = BorshDeserialize::try_from_slice(&pk) - .map_err(vp_host_fns::RuntimeError::EncodingError)?; - - let (sig, gas) = env - .memory - .read_bytes(sig_ptr, sig_len as _) - .map_err(|e| vp_host_fns::RuntimeError::MemoryError(Box::new(e)))?; - vp_host_fns::add_gas(gas_meter, gas)?; - let sig: common::Signature = BorshDeserialize::try_from_slice(&sig) - .map_err(vp_host_fns::RuntimeError::EncodingError)?; - - vp_host_fns::add_gas(gas_meter, VERIFY_TX_SIG_GAS_COST)?; - let tx = unsafe { env.ctx.tx.get() }; - Ok(HostEnvResult::from(tx.verify_sig(&pk, &sig).is_ok()).to_i64()) -} - /// Verify a ShieldedTransaction. pub fn vp_verify_masp( env: &VpVmEnv, @@ -1801,7 +1775,7 @@ where EVAL: VpEvaluator, CA: WasmCacheAccess, { - use crate::types::token::Transfer; + use masp_primitives::transaction::Transaction; let gas_meter = unsafe { env.ctx.gas_meter.get() }; let (tx_bytes, gas) = env @@ -1810,17 +1784,14 @@ where .map_err(|e| vp_host_fns::RuntimeError::MemoryError(Box::new(e)))?; vp_host_fns::add_gas(gas_meter, gas)?; - let full_tx: Transfer = + let shielded: Transaction = BorshDeserialize::try_from_slice(tx_bytes.as_slice()) .map_err(vp_host_fns::RuntimeError::EncodingError)?; - match full_tx.shielded { - Some(shielded_tx) => Ok(HostEnvResult::from( - crate::ledger::masp::verify_shielded_tx(&shielded_tx), - ) - .to_i64()), - None => Ok(HostEnvResult::Fail.to_i64()), - } + Ok( + HostEnvResult::from(crate::ledger::masp::verify_shielded_tx(&shielded)) + .to_i64(), + ) } /// Log a string from exposed to the wasm VM Tx environment. The message will be @@ -1893,10 +1864,10 @@ where EVAL: VpEvaluator, CA: WasmCacheAccess, { - let (vp_code, gas) = - env.memory - .read_bytes(vp_code_ptr, vp_code_len as _) - .map_err(|e| vp_host_fns::RuntimeError::MemoryError(Box::new(e)))?; + let (vp_code_hash, gas) = env + .memory + .read_bytes(vp_code_ptr, vp_code_len as _) + .map_err(|e| vp_host_fns::RuntimeError::MemoryError(Box::new(e)))?; let gas_meter = unsafe { env.ctx.gas_meter.get() }; vp_host_fns::add_gas(gas_meter, gas)?; @@ -1905,10 +1876,18 @@ where .read_bytes(input_data_ptr, input_data_len as _) .map_err(|e| vp_host_fns::RuntimeError::MemoryError(Box::new(e)))?; vp_host_fns::add_gas(gas_meter, gas)?; + let input_data: Tx = BorshDeserialize::try_from_slice(&input_data) + .map_err(vp_host_fns::RuntimeError::EncodingError)?; + let vp_code_hash = Hash(vp_code_hash.try_into().map_err(|e| { + vp_host_fns::RuntimeError::EncodingError(std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!("Not a valid hash: {:?}", e), + )) + })?); let eval_runner = unsafe { env.ctx.eval_runner.get() }; Ok(eval_runner - .eval(env.ctx.clone(), vp_code, input_data) + .eval(env.ctx.clone(), vp_code_hash, input_data) .to_i64()) } @@ -2002,6 +1981,7 @@ pub mod testing { iterators: &mut PrefixIterators<'static, DB>, verifiers: &mut BTreeSet
, gas_meter: &mut BlockGasMeter, + tx: &Tx, tx_index: &TxIndex, result_buffer: &mut Option>, #[cfg(feature = "wasm-runtime")] vp_wasm_cache: &mut VpCache, @@ -2018,6 +1998,7 @@ pub mod testing { write_log, iterators, gas_meter, + tx, tx_index, verifiers, result_buffer, diff --git a/shared/src/vm/types.rs b/shared/src/vm/types.rs index 16e525f4be..893496f1e6 100644 --- a/shared/src/vm/types.rs +++ b/shared/src/vm/types.rs @@ -11,6 +11,7 @@ use std::collections::BTreeSet; +use crate::proto::Tx; use crate::types::address::Address; use crate::types::storage; @@ -19,7 +20,7 @@ pub struct VpInput<'a> { /// The address of the validity predicate's owning account pub addr: &'a Address, /// The input data as arbitrary bytes - pub data: &'a [u8], + pub data: &'a Tx, /// The storage changed keys from the write log of storage updates /// performed by the transaction for the account associated with the VP pub keys_changed: &'a BTreeSet, diff --git a/shared/src/vm/wasm/compilation_cache/common.rs b/shared/src/vm/wasm/compilation_cache/common.rs index 6402918031..5d38a7a993 100644 --- a/shared/src/vm/wasm/compilation_cache/common.rs +++ b/shared/src/vm/wasm/compilation_cache/common.rs @@ -164,8 +164,7 @@ impl Cache { hash.to_string() ); // Put into cache, ignore result if it's full - let _ = - in_memory.put_with_weight(hash.clone(), module.clone()); + let _ = in_memory.put_with_weight(*hash, module.clone()); return Ok(Some((module, store))); } @@ -196,13 +195,12 @@ impl Cache { // Update progress let mut progress = self.progress.write().unwrap(); - progress.insert(hash.clone(), Compilation::Done); + progress.insert(*hash, Compilation::Done); // Put into cache, ignore the result (fails if the module // cannot fit into the cache) let mut in_memory = self.in_memory.write().unwrap(); - let _ = - in_memory.put_with_weight(hash.clone(), module.clone()); + let _ = in_memory.put_with_weight(*hash, module.clone()); return Ok(Some((module, store))); } @@ -304,7 +302,7 @@ impl Cache { drop(progress); return self.fetch(&hash); } - progress.insert(hash.clone(), Compilation::Compiling); + progress.insert(hash, Compilation::Compiling); drop(progress); tracing::info!("Compiling {} {}.", N::name(), hash.to_string()); @@ -317,7 +315,7 @@ impl Cache { // Update progress let mut progress = self.progress.write().unwrap(); - progress.insert(hash.clone(), Compilation::Done); + progress.insert(hash, Compilation::Done); // Put into cache, ignore result if it's full let mut in_memory = self.in_memory.write().unwrap(); @@ -364,7 +362,7 @@ impl Cache { progress.insert(hash, Compilation::Done); return; } - progress.insert(hash.clone(), Compilation::Compiling); + progress.insert(hash, Compilation::Compiling); drop(progress); let progress = self.progress.clone(); let code = code.as_ref().to_vec(); @@ -378,10 +376,8 @@ impl Cache { Ok((module, store)) => { let mut progress = progress.write().unwrap(); - progress.insert( - hash.clone(), - Compilation::Done, - ); + progress + .insert(hash, Compilation::Done); file_write_module(&dir, &module, &hash); (module, store) } diff --git a/shared/src/vm/wasm/host_env.rs b/shared/src/vm/wasm/host_env.rs index bf438ddfcc..3212649624 100644 --- a/shared/src/vm/wasm/host_env.rs +++ b/shared/src/vm/wasm/host_env.rs @@ -118,7 +118,6 @@ where "namada_vp_get_block_hash" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_block_hash), "namada_vp_get_tx_code_hash" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_tx_code_hash), "namada_vp_get_block_epoch" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_block_epoch), - "namada_vp_verify_tx_signature" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_verify_tx_signature), "namada_vp_verify_masp" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_verify_masp), "namada_vp_eval" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_eval), "namada_vp_get_native_token" => Function::new_native_with_env(wasm_store, env.clone(), host_env::vp_get_native_token), diff --git a/shared/src/vm/wasm/memory.rs b/shared/src/vm/wasm/memory.rs index 31f48fdbcd..7e79ab1be0 100644 --- a/shared/src/vm/wasm/memory.rs +++ b/shared/src/vm/wasm/memory.rs @@ -15,6 +15,7 @@ use wasmer_vm::{ MemoryStyle, TableStyle, VMMemoryDefinition, VMTableDefinition, }; +use crate::proto::Tx; use crate::vm::memory::VmMemory; use crate::vm::types::VpInput; @@ -81,10 +82,11 @@ pub struct TxCallInput { /// Write transaction inputs into wasm memory pub fn write_tx_inputs( memory: &wasmer::Memory, - tx_data_bytes: impl AsRef<[u8]>, + tx_data: &Tx, ) -> Result { let tx_data_ptr = 0; - let tx_data_len = tx_data_bytes.as_ref().len() as _; + let tx_data_bytes = tx_data.try_to_vec().map_err(Error::EncodingError)?; + let tx_data_len = tx_data_bytes.len() as _; write_memory_bytes(memory, tx_data_ptr, tx_data_bytes)?; @@ -129,8 +131,9 @@ pub fn write_vp_inputs( let addr_bytes = addr.try_to_vec().map_err(Error::EncodingError)?; let addr_len = addr_bytes.len() as _; + let data_bytes = data.try_to_vec().map_err(Error::EncodingError)?; let data_ptr = addr_ptr + addr_len; - let data_len = data.len() as _; + let data_len = data_bytes.len() as _; let keys_changed_bytes = keys_changed.try_to_vec().map_err(Error::EncodingError)?; @@ -144,7 +147,7 @@ pub fn write_vp_inputs( let bytes = [ &addr_bytes[..], - data, + &data_bytes[..], &keys_changed_bytes[..], &verifiers_bytes[..], ] diff --git a/shared/src/vm/wasm/run.rs b/shared/src/vm/wasm/run.rs index 35e7875af2..ae20b9e6d2 100644 --- a/shared/src/vm/wasm/run.rs +++ b/shared/src/vm/wasm/run.rs @@ -13,9 +13,9 @@ use super::TxCache; use crate::ledger::gas::{BlockGasMeter, VpGasMeter}; use crate::ledger::storage::write_log::WriteLog; use crate::ledger::storage::{self, Storage, StorageHasher}; -use crate::proto::Tx; +use crate::proto::{Commitment, Section, Tx}; use crate::types::address::Address; -use crate::types::hash::{Error as TxHashError, Hash, HASH_LENGTH}; +use crate::types::hash::{Error as TxHashError, Hash}; use crate::types::internal::HostEnvResult; use crate::types::storage::{Key, TxIndex}; use crate::vm::host_env::{TxVmEnv, VpCtx, VpEvaluator, VpVmEnv}; @@ -34,6 +34,8 @@ const WASM_STACK_LIMIT: u32 = u16::MAX as u32; #[allow(missing_docs)] #[derive(Error, Debug)] pub enum Error { + #[error("Missing wasm code error")] + MissingCode, #[error("Memory error: {0}")] MemoryError(memory::Error), #[error("Unable to inject gas meter")] @@ -84,8 +86,7 @@ pub fn tx( write_log: &mut WriteLog, gas_meter: &mut BlockGasMeter, tx_index: &TxIndex, - tx_code: impl AsRef<[u8]>, - tx_data: impl AsRef<[u8]>, + tx: &Tx, vp_wasm_cache: &mut VpCache, tx_wasm_cache: &mut TxCache, ) -> Result> @@ -94,15 +95,19 @@ where H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { - let (module, store) = if tx_code.as_ref().len() == HASH_LENGTH { - // we assume that there is no wasm code with HASH_LENGTH - let code_hash = - Hash::try_from(tx_code.as_ref()).map_err(Error::CodeHash)?; - fetch_or_compile(tx_wasm_cache, &code_hash, write_log, storage)? - } else { - match tx_wasm_cache.compile_or_fetch(tx_code)? { - Some((module, store)) => (module, store), - None => return Err(Error::NoCompiledWasmCode), + let tx_code = tx + .get_section(tx.code_sechash()) + .and_then(Section::code_sec) + .ok_or(Error::MissingCode)?; + let (module, store) = match tx_code.code { + Commitment::Hash(code_hash) => { + fetch_or_compile(tx_wasm_cache, &code_hash, write_log, storage)? + } + Commitment::Id(tx_code) => { + match tx_wasm_cache.compile_or_fetch(tx_code)? { + Some((module, store)) => (module, store), + None => return Err(Error::NoCompiledWasmCode), + } } }; @@ -116,6 +121,7 @@ where write_log, &mut iterators, gas_meter, + tx, tx_index, &mut verifiers, &mut result_buffer, @@ -140,7 +146,7 @@ where let memory::TxCallInput { tx_data_ptr, tx_data_len, - } = memory::write_tx_inputs(memory, tx_data).map_err(Error::MemoryError)?; + } = memory::write_tx_inputs(memory, tx).map_err(Error::MemoryError)?; // Get the module's entrypoint to be called let apply_tx = instance .exports @@ -187,11 +193,6 @@ where H: 'static + StorageHasher, CA: 'static + WasmCacheAccess, { - let input_data = match tx.data.as_ref() { - Some(data) => &data[..], - None => &[], - }; - // Compile the wasm module let (module, store) = fetch_or_compile(&mut vp_wasm_cache, vp_code_hash, write_log, storage)?; @@ -226,20 +227,13 @@ where memory::prepare_vp_memory(&store).map_err(Error::MemoryError)?; let imports = vp_imports(&store, initial_memory, env); - run_vp( - module, - imports, - input_data, - address, - keys_changed, - verifiers, - ) + run_vp(module, imports, tx, address, keys_changed, verifiers) } fn run_vp( module: wasmer::Module, vp_imports: wasmer::ImportObject, - input_data: &[u8], + input_data: &Tx, address: &Address, keys_changed: &BTreeSet, verifiers: &BTreeSet
, @@ -328,8 +322,8 @@ where fn eval( &self, ctx: VpCtx<'static, DB, H, Self, CA>, - vp_code_hash: Vec, - input_data: Vec, + vp_code_hash: Hash, + input_data: Tx, ) -> HostEnvResult { let vp_code_hash = match Hash::try_from(&vp_code_hash[..]) { Ok(hash) => hash, @@ -359,7 +353,7 @@ where &self, ctx: VpCtx<'static, DB, H, Self, CA>, vp_code_hash: Hash, - input_data: Vec, + input_data: Tx, ) -> Result { let address = unsafe { ctx.address.get() }; let keys_changed = unsafe { ctx.keys_changed.get() }; @@ -384,7 +378,7 @@ where run_vp( module, imports, - &input_data[..], + &input_data, address, keys_changed, verifiers, @@ -475,13 +469,15 @@ fn get_gas_rules() -> rules::Set { mod tests { use borsh::BorshSerialize; use itertools::Either; - use namada_core::types::chain::ChainId; use namada_test_utils::TestWasms; use test_log::test; use wasmer_vm::TrapCode; use super::*; use crate::ledger::storage::testing::TestStorage; + use crate::proto::{Code, Data}; + use crate::types::hash::Hash; + use crate::types::transaction::TxType; use crate::types::validity_predicate::EvalVp; use crate::vm::wasm; @@ -539,7 +535,7 @@ mod tests { // store the wasm code let code_hash = Hash::sha256(&tx_code); let key = Key::wasm_code(&code_hash); - write_log.write(&key, tx_code).unwrap(); + write_log.write(&key, tx_code.clone()).unwrap(); // Assuming 200 pages, 12.8 MiB limit assert_eq!(memory::TX_MEMORY_MAX_PAGES, 200); @@ -551,13 +547,15 @@ mod tests { wasm::compilation_cache::common::testing::cache(); let (mut tx_cache, _) = wasm::compilation_cache::common::testing::cache(); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.set_code(Code::new(tx_code.clone())); + outer_tx.set_data(Data::new(tx_data)); let result = tx( &storage, &mut write_log, &mut gas_meter, &tx_index, - &code_hash, - tx_data, + &outer_tx, &mut vp_cache, &mut tx_cache, ); @@ -566,13 +564,15 @@ mod tests { // Allocating `2^24` (16 MiB) should be above the memory limit and // should fail let tx_data = 2_usize.pow(24).try_to_vec().unwrap(); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.set_code(Code::new(tx_code)); + outer_tx.set_data(Data::new(tx_data)); let error = tx( &storage, &mut write_log, &mut gas_meter, &tx_index, - &code_hash, - tx_data, + &outer_tx, &mut vp_cache, &mut tx_cache, ) @@ -613,18 +613,24 @@ mod tests { // Allocating `2^23` (8 MiB) should be below the memory limit and // shouldn't fail let input = 2_usize.pow(23).try_to_vec().unwrap(); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(input)); let eval_vp = EvalVp { - vp_code_hash: limit_code_hash.clone(), - input, + vp_code_hash: limit_code_hash, + input: tx, }; let tx_data = eval_vp.try_to_vec().unwrap(); - let tx = Tx::new(vec![], Some(tx_data), storage.chain_id.clone(), None); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.header.chain_id = storage.chain_id.clone(); + outer_tx.set_code(Code::new(vec![])); + outer_tx.set_data(Data::new(tx_data)); let (vp_cache, _) = wasm::compilation_cache::common::testing::cache(); // When the `eval`ed VP doesn't run out of memory, it should return // `true` let passed = vp( &code_hash, - &tx, + &outer_tx, &tx_index, &addr, &storage, @@ -642,18 +648,24 @@ mod tests { // Allocating `2^24` (16 MiB) should be above the memory limit and // should fail let input = 2_usize.pow(24).try_to_vec().unwrap(); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(input)); let eval_vp = EvalVp { vp_code_hash: limit_code_hash, - input, + input: tx, }; let tx_data = eval_vp.try_to_vec().unwrap(); - let tx = Tx::new(vec![], Some(tx_data), storage.chain_id.clone(), None); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.header.chain_id = storage.chain_id.clone(); + outer_tx.set_data(Data::new(tx_data)); + outer_tx.set_code(Code::new(vec![])); // When the `eval`ed VP runs out of memory, its result should be // `false`, hence we should also get back `false` from the VP that // called `eval`. let passed = vp( &code_hash, - &tx, + &outer_tx, &tx_index, &addr, &storage, @@ -695,11 +707,14 @@ mod tests { // Allocating `2^23` (8 MiB) should be below the memory limit and // shouldn't fail let tx_data = 2_usize.pow(23).try_to_vec().unwrap(); - let tx = Tx::new(vec![], Some(tx_data), storage.chain_id.clone(), None); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.header.chain_id = storage.chain_id.clone(); + outer_tx.set_data(Data::new(tx_data)); + outer_tx.set_code(Code::new(vec![])); let (vp_cache, _) = wasm::compilation_cache::common::testing::cache(); let result = vp( &code_hash, - &tx, + &outer_tx, &tx_index, &addr, &storage, @@ -716,10 +731,12 @@ mod tests { // Allocating `2^24` (16 MiB) should be above the memory limit and // should fail let tx_data = 2_usize.pow(24).try_to_vec().unwrap(); - let tx = Tx::new(vec![], Some(tx_data), storage.chain_id.clone(), None); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.header.chain_id = storage.chain_id.clone(); + outer_tx.set_data(Data::new(tx_data)); let error = vp( &code_hash, - &tx, + &outer_tx, &tx_index, &addr, &storage, @@ -749,7 +766,7 @@ mod tests { // store the wasm code let code_hash = Hash::sha256(&tx_no_op); let key = Key::wasm_code(&code_hash); - write_log.write(&key, tx_no_op).unwrap(); + write_log.write(&key, tx_no_op.clone()).unwrap(); // Assuming 200 pages, 12.8 MiB limit assert_eq!(memory::TX_MEMORY_MAX_PAGES, 200); @@ -762,13 +779,15 @@ mod tests { wasm::compilation_cache::common::testing::cache(); let (mut tx_cache, _) = wasm::compilation_cache::common::testing::cache(); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.set_code(Code::new(tx_no_op)); + outer_tx.set_data(Data::new(tx_data)); let result = tx( &storage, &mut write_log, &mut gas_meter, &tx_index, - code_hash, - tx_data, + &outer_tx, &mut vp_cache, &mut tx_cache, ); @@ -816,11 +835,14 @@ mod tests { // limit and should fail let len = 2_usize.pow(24); let tx_data: Vec = vec![6_u8; len]; - let tx = Tx::new(vec![], Some(tx_data), storage.chain_id.clone(), None); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.header.chain_id = storage.chain_id.clone(); + outer_tx.set_data(Data::new(tx_data)); + outer_tx.set_code(Code::new(vec![])); let (vp_cache, _) = wasm::compilation_cache::common::testing::cache(); let result = vp( &code_hash, - &tx, + &outer_tx, &tx_index, &addr, &storage, @@ -867,7 +889,7 @@ mod tests { // store the wasm code let code_hash = Hash::sha256(&tx_read_key); let key = Key::wasm_code(&code_hash); - write_log.write(&key, tx_read_key).unwrap(); + write_log.write(&key, tx_read_key.clone()).unwrap(); // Allocating `2^24` (16 MiB) for a value in storage that the tx // attempts to read should be above the memory limit and should @@ -885,13 +907,15 @@ mod tests { wasm::compilation_cache::common::testing::cache(); let (mut tx_cache, _) = wasm::compilation_cache::common::testing::cache(); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.set_code(Code::new(tx_read_key)); + outer_tx.set_data(Data::new(tx_data)); let error = tx( &storage, &mut write_log, &mut gas_meter, &tx_index, - code_hash, - tx_data, + &outer_tx, &mut vp_cache, &mut tx_cache, ) @@ -931,11 +955,14 @@ mod tests { // Borsh. storage.write(&key, value.try_to_vec().unwrap()).unwrap(); let tx_data = key.try_to_vec().unwrap(); - let tx = Tx::new(vec![], Some(tx_data), storage.chain_id.clone(), None); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.header.chain_id = storage.chain_id.clone(); + outer_tx.set_data(Data::new(tx_data)); + outer_tx.set_code(Code::new(vec![])); let (vp_cache, _) = wasm::compilation_cache::common::testing::cache(); let error = vp( &code_hash, - &tx, + &outer_tx, &tx_index, &addr, &storage, @@ -991,16 +1018,22 @@ mod tests { // Borsh. storage.write(&key, value.try_to_vec().unwrap()).unwrap(); let input = 2_usize.pow(23).try_to_vec().unwrap(); + let mut tx = Tx::new(TxType::Raw); + tx.set_data(Data::new(input)); + tx.set_code(Code::new(vec![])); let eval_vp = EvalVp { vp_code_hash: read_code_hash, - input, + input: tx, }; let tx_data = eval_vp.try_to_vec().unwrap(); - let tx = Tx::new(vec![], Some(tx_data), storage.chain_id.clone(), None); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.header.chain_id = storage.chain_id.clone(); + outer_tx.set_data(Data::new(tx_data)); + outer_tx.set_code(Code::new(vec![])); let (vp_cache, _) = wasm::compilation_cache::common::testing::cache(); let passed = vp( &code_hash, - &tx, + &outer_tx, &tx_index, &addr, &storage, @@ -1060,18 +1093,22 @@ mod tests { wasm::compilation_cache::common::testing::cache(); let (mut tx_cache, _) = wasm::compilation_cache::common::testing::cache(); + // store the tx code let code_hash = Hash::sha256(&tx_code); let key = Key::wasm_code(&code_hash); write_log.write(&key, tx_code).unwrap(); + let mut outer_tx = Tx::new(TxType::Raw); + outer_tx.set_code(Code::from_hash(code_hash)); + outer_tx.set_data(Data::new(tx_data)); + tx( &storage, &mut write_log, &mut gas_meter, &tx_index, - code_hash, - tx_data, + &outer_tx, &mut vp_cache, &mut tx_cache, ) @@ -1104,9 +1141,9 @@ mod tests { (export "_validate_tx" (func $_validate_tx))) "#, loops).as_bytes(), ) - .expect("unexpected error converting wat2wasm").into_owned(); + .expect("unexpected error converting wat2wasm").into_owned(); - let tx = Tx::new(vec![], None, ChainId::default(), None); + let outer_tx = Tx::new(TxType::Raw); let tx_index = TxIndex::default(); let mut storage = TestStorage::default(); let addr = storage.address_gen.generate_address("rng seed"); @@ -1122,7 +1159,7 @@ mod tests { vp( &code_hash, - &tx, + &outer_tx, &tx_index, &addr, &storage, @@ -1154,10 +1191,10 @@ mod tests { assert!( // Universal engine error (currently used on mac) trap_code == - Either::Left(wasmer_vm::TrapCode::UnreachableCodeReached) || + Either::Left(wasmer_vm::TrapCode::UnreachableCodeReached) || // Dylib engine error (used elsewhere) - trap_code == - Either::Left(wasmer_vm::TrapCode::StackOverflow), + trap_code == + Either::Left(wasmer_vm::TrapCode::StackOverflow), ); } } diff --git a/tests/proptest-regressions/native_vp/pos.txt b/tests/proptest-regressions/native_vp/pos.txt index ad157e817b..c223b898a7 100644 --- a/tests/proptest-regressions/native_vp/pos.txt +++ b/tests/proptest-regressions/native_vp/pos.txt @@ -1,2 +1,2 @@ cc 65720acc67508ccd2fefc1ca42477075ae53a7d1e3c8f31324cfb8f06587457e -cc 45b2dd2ed9619ceef6135ee6ca34406621c8a6429ffa153bbda3ce79dd4e006c \ No newline at end of file +cc 45b2dd2ed9619ceef6135ee6ca34406621c8a6429ffa153bbda3ce79dd4e006c diff --git a/tests/src/e2e/wallet_tests.rs b/tests/src/e2e/wallet_tests.rs index c3d37f1a03..ca9d290f55 100644 --- a/tests/src/e2e/wallet_tests.rs +++ b/tests/src/e2e/wallet_tests.rs @@ -37,9 +37,7 @@ fn wallet_encrypted_key_cmds() -> Result<()> { cmd.exp_string("Enter your encryption password:")?; cmd.send_line(password)?; - cmd.exp_string( - "To confirm, please enter the same encryption password once more: ", - )?; + cmd.exp_string("Enter same passphrase again: ")?; cmd.send_line(password)?; cmd.exp_string(&format!( "Successfully added a key and an address with alias: \"{}\"", @@ -54,7 +52,7 @@ fn wallet_encrypted_key_cmds() -> Result<()> { Some(20), )?; - cmd.exp_string("Enter decryption password:")?; + cmd.exp_string("Enter your decryption password:")?; cmd.send_line(password)?; cmd.exp_string("Public key hash:")?; cmd.exp_string("Public key:")?; diff --git a/tests/src/native_vp/mod.rs b/tests/src/native_vp/mod.rs index 6baf214a3b..e46ee4b23c 100644 --- a/tests/src/native_vp/mod.rs +++ b/tests/src/native_vp/mod.rs @@ -58,9 +58,12 @@ impl TestNativeVpEnv { keys_changed: &self.keys_changed, verifiers: &self.verifiers, }; - let tx_data = self.tx_env.tx.data.as_ref().cloned().unwrap_or_default(); let native_vp = init_native_vp(ctx); - native_vp.validate_tx(&tx_data, &self.keys_changed, &self.verifiers) + native_vp.validate_tx( + &self.tx_env.tx, + &self.keys_changed, + &self.verifiers, + ) } } diff --git a/tests/src/vm_host_env/ibc.rs b/tests/src/vm_host_env/ibc.rs index 47ff3808fd..a442b5a6cd 100644 --- a/tests/src/vm_host_env/ibc.rs +++ b/tests/src/vm_host_env/ibc.rs @@ -105,7 +105,7 @@ pub struct TestIbcVp<'a> { impl<'a> TestIbcVp<'a> { pub fn validate( &self, - tx_data: &[u8], + tx_data: &Tx, ) -> std::result::Result { self.ibc.validate_tx( tx_data, @@ -122,7 +122,7 @@ pub struct TestIbcTokenVp<'a> { impl<'a> TestIbcTokenVp<'a> { pub fn validate( &self, - tx_data: &[u8], + tx_data: &Tx, ) -> std::result::Result { self.token.validate_tx( tx_data, @@ -164,7 +164,7 @@ pub fn validate_ibc_vp_from_tx<'a>( ); let ibc = Ibc { ctx }; - TestIbcVp { ibc }.validate(tx.data.as_ref().unwrap()) + TestIbcVp { ibc }.validate(tx) } /// Validate the native token VP for the given address @@ -200,7 +200,7 @@ pub fn validate_token_vp_from_tx<'a>( ); let token = IbcToken { ctx }; - TestIbcTokenVp { token }.validate(tx.data.as_ref().unwrap()) + TestIbcTokenVp { token }.validate(tx) } /// Initialize the test storage. Requires initialized [`tx_host_env::ENV`]. @@ -233,7 +233,7 @@ pub fn init_storage() -> (Address, Address) { }); // initialize a token - let token = tx::ctx().init_account(code_hash.clone()).unwrap(); + let token = tx::ctx().init_account(code_hash).unwrap(); // initialize an account let account = tx::ctx().init_account(code_hash).unwrap(); diff --git a/tests/src/vm_host_env/mod.rs b/tests/src/vm_host_env/mod.rs index 1ad4b22599..f4344e33b2 100644 --- a/tests/src/vm_host_env/mod.rs +++ b/tests/src/vm_host_env/mod.rs @@ -27,20 +27,18 @@ mod tests { get_dummy_header as tm_dummy_header, Error as IbcError, }; use namada::ledger::tx_env::TxEnv; - use namada::proto::{SignedTxData, Tx}; - use namada::types::chain::ChainId; + use namada::proto::{Code, Data, Section, Signature, Tx}; use namada::types::hash::Hash; use namada::types::key::*; use namada::types::storage::{self, BlockHash, BlockHeight, Key, KeySeg}; use namada::types::time::DateTimeUtc; use namada::types::token::{self, Amount}; + use namada::types::transaction::TxType; use namada::types::{address, key}; use namada_core::ledger::ibc::context::transfer_mod::testing::DummyTransferModule; use namada_core::ledger::ibc::Error as IbcActionError; use namada_test_utils::TestWasms; - use namada_tx_prelude::{ - BorshDeserialize, BorshSerialize, StorageRead, StorageWrite, - }; + use namada_tx_prelude::{BorshSerialize, StorageRead, StorageWrite}; use namada_vp_prelude::VpEnv; use prost::Message; use test_log::test; @@ -452,38 +450,42 @@ mod tests { let expiration = Some(DateTimeUtc::now()); for data in &[ // Tx with some arbitrary data - Some(vec![1, 2, 3, 4].repeat(10)), + vec![1, 2, 3, 4].repeat(10), // Tx without any data - None, + vec![], ] { let signed_tx_data = vp_host_env::with(|env| { - env.tx = Tx::new( - code.clone(), - data.clone(), - env.wl_storage.storage.chain_id.clone(), - expiration, - ) - .sign(&keypair); - let tx_data = env.tx.data.as_ref().expect("data should exist"); - - SignedTxData::try_from_slice(&tx_data[..]) - .expect("decoding signed data we just signed") + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = env.wl_storage.storage.chain_id.clone(); + tx.header.expiration = expiration; + tx.set_code(Code::new(code.clone())); + tx.set_data(Data::new(data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &keypair, + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair, + ))); + env.tx = tx; + env.tx.clone() }); - assert_eq!(&signed_tx_data.data, data); + assert_eq!(signed_tx_data.data().as_ref(), Some(data)); assert!( - vp::CTX - .verify_tx_signature(&pk, &signed_tx_data.sig) - .unwrap() + signed_tx_data + .verify_signature(&pk, signed_tx_data.data_sechash()) + .is_ok() ); let other_keypair = key::testing::keypair_2(); assert!( - !vp::CTX - .verify_tx_signature( + signed_tx_data + .verify_signature( &other_keypair.ref_to(), - &signed_tx_data.sig + signed_tx_data.data_sechash() ) - .unwrap() + .is_err() ); } } @@ -535,7 +537,18 @@ mod tests { // evaluating without any code should fail let empty_code = Hash::zero(); let input_data = vec![]; - let result = vp::CTX.eval(empty_code, input_data).unwrap(); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(input_data)); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); + let result = vp::CTX.eval(empty_code, tx).unwrap(); assert!(!result); // evaluating the VP template which always returns `true` should pass @@ -547,7 +560,18 @@ mod tests { env.wl_storage.storage.write(&key, code.clone()).unwrap(); }); let input_data = vec![]; - let result = vp::CTX.eval(code_hash, input_data).unwrap(); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(input_data)); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); + let result = vp::CTX.eval(code_hash, tx).unwrap(); assert!(result); // evaluating the VP template which always returns `false` shouldn't @@ -560,7 +584,18 @@ mod tests { env.wl_storage.storage.write(&key, code.clone()).unwrap(); }); let input_data = vec![]; - let result = vp::CTX.eval(code_hash, input_data).unwrap(); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(input_data)); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); + let result = vp::CTX.eval(code_hash, tx).unwrap(); assert!(!result); } @@ -575,14 +610,17 @@ mod tests { let msg = ibc::msg_create_client(); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // create a client with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -612,14 +650,17 @@ mod tests { let msg = ibc::msg_update_client(client_id); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // update the client with the message tx_host_env::ibc::ibc_actions(tx::ctx()) .execute(&tx_data) @@ -652,14 +693,17 @@ mod tests { let msg = ibc::msg_connection_open_init(client_id); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // init a connection with the message tx_host_env::ibc::ibc_actions(tx::ctx()) .execute(&tx_data) @@ -688,14 +732,17 @@ mod tests { let msg = ibc::msg_connection_open_ack(conn_id, client_state); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // open the connection with the message tx_host_env::ibc::ibc_actions(tx::ctx()) .execute(&tx_data) @@ -729,14 +776,17 @@ mod tests { let msg = ibc::msg_connection_open_try(client_id, client_state); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // open try a connection with the message tx_host_env::ibc::ibc_actions(tx::ctx()) .execute(&tx_data) @@ -765,14 +815,17 @@ mod tests { let msg = ibc::msg_connection_open_confirm(conn_id); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // open the connection with the mssage tx_host_env::ibc::ibc_actions(tx::ctx()) .execute(&tx_data) @@ -808,14 +861,17 @@ mod tests { let msg = ibc::msg_channel_open_init(port_id.clone(), conn_id); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // init a channel with the message tx_host_env::ibc::ibc_actions(tx::ctx()) .execute(&tx_data) @@ -844,14 +900,17 @@ mod tests { let msg = ibc::msg_channel_open_ack(port_id, channel_id); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // open the channle with the message tx_host_env::ibc::ibc_actions(tx::ctx()) .execute(&tx_data) @@ -887,14 +946,17 @@ mod tests { let msg = ibc::msg_channel_open_try(port_id.clone(), conn_id); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // try open a channel with the message tx_host_env::ibc::ibc_actions(tx::ctx()) .execute(&tx_data) @@ -923,14 +985,18 @@ mod tests { let msg = ibc::msg_channel_open_confirm(port_id, channel_id); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // open a channel with the message tx_host_env::ibc::ibc_actions(tx::ctx()) .execute(&tx_data) @@ -968,14 +1034,18 @@ mod tests { let msg = ibc::msg_channel_close_init(port_id, channel_id); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // close the channel with the message let mut actions = tx_host_env::ibc::ibc_actions(tx::ctx()); // the dummy module closes the channel @@ -1021,14 +1091,18 @@ mod tests { let msg = ibc::msg_channel_close_confirm(port_id, channel_id); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // close the channel with the message tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -1071,14 +1145,18 @@ mod tests { .to_any() .encode(&mut tx_data) .expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // send the token and a packet with the data tx_host_env::ibc::ibc_actions(tx::ctx()) .execute(&tx_data) @@ -1120,14 +1198,18 @@ mod tests { let msg = ibc::msg_packet_ack(packet); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // ack the packet with the message tx_host_env::ibc::ibc_actions(tx::ctx()) .execute(&tx_data) @@ -1196,14 +1278,18 @@ mod tests { let msg = ibc::msg_transfer(port_id, channel_id, hashed_denom, &sender); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // send the token and a packet with the data tx_host_env::ibc::ibc_actions(tx::ctx()) .execute(&tx_data) @@ -1272,14 +1358,18 @@ mod tests { let msg = ibc::msg_packet_recv(packet); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // receive a packet with the message tx_host_env::ibc::ibc_actions(tx::ctx()) .execute(&tx_data) @@ -1361,14 +1451,17 @@ mod tests { let msg = ibc::msg_packet_recv(packet); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // receive a packet with the message tx_host_env::ibc::ibc_actions(tx::ctx()) .execute(&tx_data) @@ -1453,14 +1546,18 @@ mod tests { let msg = ibc::msg_packet_recv(packet); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // receive a packet with the message tx_host_env::ibc::ibc_actions(tx::ctx()) .execute(&tx_data) @@ -1549,14 +1646,17 @@ mod tests { let msg = ibc::msg_timeout(packet, ibc::Sequence::from(1)); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // timeout the packet tx_host_env::ibc::ibc_actions(tx::ctx()) @@ -1635,14 +1735,17 @@ mod tests { let msg = ibc::msg_timeout_on_close(packet, ibc::Sequence::from(1)); let mut tx_data = vec![]; msg.to_any().encode(&mut tx_data).expect("encoding failed"); - let tx = Tx { - code_or_hash: vec![], - data: Some(tx_data.clone()), - timestamp: DateTimeUtc::now(), - chain_id: ChainId::default(), - expiration: None, - } - .sign(&key::testing::keypair_1()); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(vec![])); + tx.set_data(Data::new(tx_data.clone())); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key::testing::keypair_1(), + ))); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key::testing::keypair_1(), + ))); // timeout the packet tx_host_env::ibc::ibc_actions(tx::ctx()) diff --git a/tests/src/vm_host_env/tx.rs b/tests/src/vm_host_env/tx.rs index b67cac2416..c9ab18a08a 100644 --- a/tests/src/vm_host_env/tx.rs +++ b/tests/src/vm_host_env/tx.rs @@ -9,14 +9,15 @@ use namada::ledger::storage::write_log::WriteLog; use namada::ledger::storage::{Sha256Hasher, WlStorage}; use namada::proto::Tx; use namada::types::address::Address; +use namada::types::hash::Hash; use namada::types::storage::{Key, TxIndex}; use namada::types::time::DurationSecs; +pub use namada::types::transaction::TxType; use namada::types::{key, token}; use namada::vm::prefix_iter::PrefixIterators; use namada::vm::wasm::run::Error; use namada::vm::wasm::{self, TxCache, VpCache}; use namada::vm::{self, WasmCacheRwAccess}; -use namada_core::types::hash::Hash; use namada_tx_prelude::{BorshSerialize, Ctx}; use tempfile::TempDir; @@ -62,12 +63,12 @@ impl Default for TestTxEnv { wasm::compilation_cache::common::testing::cache(); let (tx_wasm_cache, tx_cache_dir) = wasm::compilation_cache::common::testing::cache(); - let wl_storage = WlStorage { storage: TestStorage::default(), write_log: WriteLog::default(), }; - let chain_id = wl_storage.storage.chain_id.clone(); + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = wl_storage.storage.chain_id.clone(); Self { wl_storage, iterators: PrefixIterators::default(), @@ -79,7 +80,7 @@ impl Default for TestTxEnv { vp_cache_dir, tx_wasm_cache, tx_cache_dir, - tx: Tx::new(vec![], None, chain_id, None), + tx, } } } @@ -208,14 +209,12 @@ impl TestTxEnv { /// Apply the tx changes to the write log. pub fn execute_tx(&mut self) -> Result<(), Error> { - let empty_data = vec![]; wasm::run::tx( &self.wl_storage.storage, &mut self.wl_storage.write_log, &mut self.gas_meter, &self.tx_index, - &self.tx.code_or_hash, - self.tx.data.as_ref().unwrap_or(&empty_data), + &self.tx, &mut self.vp_wasm_cache, &mut self.tx_wasm_cache, ) @@ -337,7 +336,7 @@ mod native_tx_host_env { vp_cache_dir: _, tx_wasm_cache, tx_cache_dir: _, - tx: _, + tx, }: &mut TestTxEnv| { let tx_env = vm::host_env::testing::tx_env( @@ -346,6 +345,7 @@ mod native_tx_host_env { iterators, verifiers, gas_meter, + tx, tx_index, result_buffer, vp_wasm_cache, @@ -376,7 +376,7 @@ mod native_tx_host_env { vp_cache_dir: _, tx_wasm_cache, tx_cache_dir: _, - tx: _, + tx, }: &mut TestTxEnv| { let tx_env = vm::host_env::testing::tx_env( @@ -385,6 +385,7 @@ mod native_tx_host_env { iterators, verifiers, gas_meter, + tx, tx_index, result_buffer, vp_wasm_cache, diff --git a/tests/src/vm_host_env/vp.rs b/tests/src/vm_host_env/vp.rs index 8220ce4c64..afad40a69d 100644 --- a/tests/src/vm_host_env/vp.rs +++ b/tests/src/vm_host_env/vp.rs @@ -8,6 +8,7 @@ use namada::ledger::storage::{Sha256Hasher, WlStorage}; use namada::proto::Tx; use namada::types::address::{self, Address}; use namada::types::storage::{self, Key, TxIndex}; +use namada::types::transaction::TxType; use namada::vm::prefix_iter::PrefixIterators; use namada::vm::wasm::{self, VpCache}; use namada::vm::{self, WasmCacheRwAccess}; @@ -68,13 +69,14 @@ impl Default for TestVpEnv { storage: TestStorage::default(), write_log: WriteLog::default(), }; - let chain_id = wl_storage.storage.chain_id.clone(); + let mut tx = Tx::new(TxType::Raw); + tx.header.chain_id = wl_storage.storage.chain_id.clone(); Self { addr: address::testing::established_address_1(), wl_storage, iterators: PrefixIterators::default(), gas_meter: VpGasMeter::default(), - tx: Tx::new(vec![], None, chain_id, None), + tx, tx_index: TxIndex::default(), keys_changed: BTreeSet::default(), verifiers: BTreeSet::default(), @@ -361,12 +363,6 @@ mod native_vp_host_env { native_host_fn!(vp_get_tx_code_hash(result_ptr: u64)); native_host_fn!(vp_get_block_epoch() -> u64); native_host_fn!(vp_get_native_token(result_ptr: u64)); - native_host_fn!(vp_verify_tx_signature( - pk_ptr: u64, - pk_len: u64, - sig_ptr: u64, - sig_len: u64, - ) -> i64); native_host_fn!(vp_eval( vp_code_ptr: u64, vp_code_len: u64, diff --git a/tx_prelude/Cargo.toml b/tx_prelude/Cargo.toml index 8e7cdb3f1b..71d4416b76 100644 --- a/tx_prelude/Cargo.toml +++ b/tx_prelude/Cargo.toml @@ -15,7 +15,8 @@ abciplus = [ ] [dependencies] -masp_primitives = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c" } +# branch = "murisi/namada-integration" +masp_primitives = { git = "https://github.com/anoma/masp", rev = "cfea8c95d3f73077ca3e25380fd27e5b46e828fd" } namada_core = {path = "../core", default-features = false} namada_macros = {path = "../macros"} namada_proof_of_stake = {path = "../proof_of_stake", default-features = false} diff --git a/tx_prelude/src/lib.rs b/tx_prelude/src/lib.rs index e4f991cad2..d9a92956a3 100644 --- a/tx_prelude/src/lib.rs +++ b/tx_prelude/src/lib.rs @@ -24,7 +24,7 @@ pub use namada_core::ledger::storage_api::{ ResultExt, StorageRead, StorageWrite, }; pub use namada_core::ledger::tx_env::TxEnv; -pub use namada_core::proto::{Signed, SignedTxData}; +pub use namada_core::proto::{Section, Tx}; pub use namada_core::types::address::Address; use namada_core::types::chain::CHAIN_ID_LENGTH; use namada_core::types::internal::HostEnvResult; diff --git a/tx_prelude/src/proof_of_stake.rs b/tx_prelude/src/proof_of_stake.rs index 22c00ca599..47d38e044c 100644 --- a/tx_prelude/src/proof_of_stake.rs +++ b/tx_prelude/src/proof_of_stake.rs @@ -1,5 +1,6 @@ //! Proof of Stake system integration with functions for transactions +use namada_core::types::hash::Hash; use namada_core::types::transaction::InitValidator; use namada_core::types::{key, token}; pub use namada_proof_of_stake::parameters::PosParams; @@ -72,12 +73,13 @@ impl Ctx { dkg_key, commission_rate, max_commission_rate_change, - validator_vp_code_hash, + validator_vp_code_hash: _, }: InitValidator, + validator_vp_code_hash: Hash, ) -> EnvResult
{ let current_epoch = self.get_block_epoch()?; // Init validator account - let validator_address = self.init_account(&validator_vp_code_hash)?; + let validator_address = self.init_account(validator_vp_code_hash)?; let pk_key = key::pk_key(&validator_address); self.write(&pk_key, &account_key)?; let protocol_pk_key = key::protocol_pk_key(&validator_address); diff --git a/tx_prelude/src/token.rs b/tx_prelude/src/token.rs index adf8fa92e9..251d9a21f9 100644 --- a/tx_prelude/src/token.rs +++ b/tx_prelude/src/token.rs @@ -1,5 +1,6 @@ use masp_primitives::transaction::Transaction; use namada_core::types::address::{Address, InternalAddress}; +use namada_core::types::hash::Hash; use namada_core::types::storage::KeySeg; use namada_core::types::token; pub use namada_core::types::token::*; @@ -16,6 +17,7 @@ pub fn transfer( sub_prefix: Option, amount: Amount, key: &Option, + shielded_hash: &Option, shielded: &Option, ) -> TxResult { if amount != Amount::default() { @@ -108,17 +110,16 @@ pub fn transfer( sub_prefix: None, amount, key: key.clone(), - shielded: Some(shielded.clone()), + shielded: *shielded_hash, }; - ctx.write( - ¤t_tx_key, - ( - ctx.get_block_epoch()?, - ctx.get_block_height()?, - ctx.get_tx_index()?, - transfer, - ), - )?; + let record: (Epoch, BlockHeight, TxIndex, Transfer, Transaction) = ( + ctx.get_block_epoch()?, + ctx.get_block_height()?, + ctx.get_tx_index()?, + transfer, + shielded.clone(), + ); + ctx.write(¤t_tx_key, record)?; ctx.write(&head_tx_key, current_tx_idx + 1)?; // If storage key has been supplied, then pin this transaction to it if let Some(key) = key { diff --git a/vm_env/Cargo.toml b/vm_env/Cargo.toml index 00141fcd8c..c56bd277c8 100644 --- a/vm_env/Cargo.toml +++ b/vm_env/Cargo.toml @@ -15,7 +15,6 @@ abciplus = [ [dependencies] namada_core = {path = "../core", default-features = false} borsh = "0.9.0" -#libmasp = { git = "https://github.com/anoma/masp", branch = "murisi/masp-incentive" } -masp_primitives = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c" } -masp_proofs = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c" } +# branch = "murisi/namada-integration" +masp_primitives = { git = "https://github.com/anoma/masp", rev = "cfea8c95d3f73077ca3e25380fd27e5b46e828fd" } hex = "0.4.3" diff --git a/vm_env/src/lib.rs b/vm_env/src/lib.rs index 9df39d6271..f51d92e62e 100644 --- a/vm_env/src/lib.rs +++ b/vm_env/src/lib.rs @@ -93,7 +93,7 @@ pub mod tx { // Get the current block epoch pub fn namada_tx_get_block_epoch() -> u64; - // Get the current tx id + // Get the current tx index pub fn namada_tx_get_tx_index() -> u32; // Get the native token address @@ -184,14 +184,6 @@ pub mod vp { // Get the native token address pub fn namada_vp_get_native_token(result_ptr: u64); - // Verify a transaction signature - pub fn namada_vp_verify_tx_signature( - pk_ptr: u64, - pk_len: u64, - sig_ptr: u64, - sig_len: u64, - ) -> i64; - // Requires a node running with "Info" log level pub fn namada_vp_log_string(str_ptr: u64, str_len: u64); diff --git a/vp_prelude/src/lib.rs b/vp_prelude/src/lib.rs index 57ac5a388f..8ec55ef7cd 100644 --- a/vp_prelude/src/lib.rs +++ b/vp_prelude/src/lib.rs @@ -23,12 +23,11 @@ pub use namada_core::ledger::storage_api::{ }; pub use namada_core::ledger::vp_env::VpEnv; pub use namada_core::ledger::{parameters, testnet_pow}; -pub use namada_core::proto::{Signed, SignedTxData}; +pub use namada_core::proto::{Section, Tx}; pub use namada_core::types::address::Address; use namada_core::types::chain::CHAIN_ID_LENGTH; use namada_core::types::hash::{Hash, HASH_LENGTH}; use namada_core::types::internal::HostEnvResult; -use namada_core::types::key::*; use namada_core::types::storage::{ BlockHash, BlockHeight, Epoch, Header, TxIndex, BLOCK_HASH_LENGTH, }; @@ -50,7 +49,9 @@ pub fn is_tx_whitelisted(ctx: &Ctx) -> VpResult { let whitelist: Vec = ctx.read_pre(&key)?.unwrap_or_default(); // if whitelist is empty, allow any transaction Ok(whitelist.is_empty() - || whitelist.contains(&tx_hash.to_string().to_lowercase())) + || (tx_hash.is_some() + && whitelist + .contains(&tx_hash.unwrap().to_string().to_lowercase()))) } pub fn is_vp_whitelisted(ctx: &Ctx, vp_hash: &[u8]) -> VpResult { @@ -82,11 +83,14 @@ pub fn is_proposal_accepted(ctx: &Ctx, proposal_id: u64) -> VpResult { /// - tx is whitelisted, or /// - tx is executed by an approved governance proposal (no need to be /// whitelisted) -pub fn is_valid_tx(ctx: &Ctx, tx_data: &[u8]) -> VpResult { +pub fn is_valid_tx(ctx: &Ctx, tx_data: &Tx) -> VpResult { if is_tx_whitelisted(ctx)? { accept() } else { - let proposal_id = u64::try_from_slice(tx_data).ok(); + let proposal_id = tx_data + .data() + .as_ref() + .and_then(|x| u64::try_from_slice(x).ok()); proposal_id.map_or(reject(), |id| is_proposal_accepted(ctx, id)) } @@ -272,44 +276,35 @@ impl<'view> VpEnv<'view> for Ctx { iter_prefix_pre_impl(prefix) } - fn eval(&self, vp_code: Hash, input_data: Vec) -> Result { + fn eval(&self, vp_code: Hash, input_data: Tx) -> Result { + let input_data_bytes = BorshSerialize::try_to_vec(&input_data).unwrap(); let result = unsafe { namada_vp_eval( - vp_code.as_ptr() as _, - vp_code.len() as _, - input_data.as_ptr() as _, - input_data.len() as _, + vp_code.0.as_ptr() as _, + vp_code.0.len() as _, + input_data_bytes.as_ptr() as _, + input_data_bytes.len() as _, ) }; Ok(HostEnvResult::is_success(result)) } - fn verify_tx_signature( - &self, - pk: &common::PublicKey, - sig: &common::Signature, - ) -> Result { - let pk = BorshSerialize::try_to_vec(pk).unwrap(); - let sig = BorshSerialize::try_to_vec(sig).unwrap(); - let valid = unsafe { - namada_vp_verify_tx_signature( - pk.as_ptr() as _, - pk.len() as _, - sig.as_ptr() as _, - sig.len() as _, - ) - }; - Ok(HostEnvResult::is_success(valid)) - } - - fn get_tx_code_hash(&self) -> Result { - let result = Vec::with_capacity(HASH_LENGTH); + fn get_tx_code_hash(&self) -> Result, Error> { + let result = Vec::with_capacity(HASH_LENGTH + 1); unsafe { namada_vp_get_tx_code_hash(result.as_ptr() as _); } let slice = - unsafe { slice::from_raw_parts(result.as_ptr(), HASH_LENGTH) }; - Ok(Hash::try_from(slice).expect("Cannot convert the hash")) + unsafe { slice::from_raw_parts(result.as_ptr(), HASH_LENGTH + 1) }; + Ok(if slice[0] == 1 { + Some(Hash( + slice[1..HASH_LENGTH + 1] + .try_into() + .expect("Cannot convert the hash"), + )) + } else { + None + }) } fn verify_masp(&self, tx: Vec) -> Result { diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 824f1bb078..a34089c555 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -19,11 +19,12 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aead" -version = "0.4.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "generic-array", + "crypto-common", + "generic-array 0.14.7", ] [[package]] @@ -33,9 +34,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ "cfg-if 1.0.0", - "cipher", + "cipher 0.3.0", "cpufeatures", - "opaque-debug", + "opaque-debug 0.3.0", ] [[package]] @@ -441,6 +442,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base58" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" + [[package]] name = "base64" version = "0.13.1" @@ -486,20 +493,26 @@ dependencies = [ "lazy_static", "log", "num_cpus", - "pairing", + "pairing 0.21.0", "rand_core 0.6.4", "rayon", - "subtle", + "subtle 2.4.1", ] [[package]] -name = "bigint" -version = "4.4.3" +name = "bellman" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0e8c8a600052b52482eff2cf4d810e462fdff1f656ac1ecb6232132a1ed7def" +checksum = "a4dd656ef4fdf7debb6d87d4dd92642fcbcdb78cbf6600c13e25c87e4d1a3807" dependencies = [ + "bitvec 1.0.1", + "blake2s_simd 1.0.1", "byteorder", - "crunchy 0.1.6", + "ff 0.12.1", + "group 0.12.1", + "pairing 0.22.0", + "rand_core 0.6.4", + "subtle 2.4.1", ] [[package]] @@ -557,7 +570,7 @@ checksum = "0694ea59225b0c5f3cb405ff3f670e4828358ed26aec49dc352f730f0cb1a8a3" dependencies = [ "bech32 0.9.1", "bitcoin_hashes", - "secp256k1 0.24.3", + "secp256k1", "serde", ] @@ -609,17 +622,6 @@ dependencies = [ "digest 0.10.6", ] -[[package]] -name = "blake2b_simd" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "constant_time_eq 0.1.5", -] - [[package]] name = "blake2b_simd" version = "1.0.1" @@ -667,13 +669,25 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding 0.1.5", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + [[package]] name = "block-buffer" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -682,7 +696,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -691,8 +705,17 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" dependencies = [ - "block-padding", - "cipher", + "block-padding 0.2.1", + "cipher 0.3.0", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", ] [[package]] @@ -724,9 +747,22 @@ checksum = "a829c821999c06be34de314eaeb7dd1b42be38661178bc26ad47a4eacebdb0f9" dependencies = [ "ff 0.11.1", "group 0.11.0", - "pairing", + "pairing 0.21.0", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", +] + +[[package]] +name = "bls12_381" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3c196a77437e7cc2fb515ce413a6401291578b5afc8ecb29a3c7ab957f05941" +dependencies = [ + "ff 0.12.1", + "group 0.12.1", + "pairing 0.22.0", + "rand_core 0.6.4", + "subtle 2.4.1", ] [[package]] @@ -788,6 +824,12 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "bytecheck" version = "0.6.10" @@ -887,20 +929,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" dependencies = [ "cfg-if 1.0.0", - "cipher", + "cipher 0.3.0", "cpufeatures", - "zeroize", ] [[package]] -name = "chacha20poly1305" +name = "chacha20" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if 1.0.0", + "cipher 0.4.4", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ "aead", - "chacha20", - "cipher", + "chacha20 0.9.1", + "cipher 0.4.4", "poly1305", "zeroize", ] @@ -923,7 +975,18 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array", + "generic-array 0.14.7", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", ] [[package]] @@ -1146,12 +1209,6 @@ dependencies = [ "cfg-if 1.0.0", ] -[[package]] -name = "crunchy" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" - [[package]] name = "crunchy" version = "0.2.2" @@ -1164,9 +1221,9 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ - "generic-array", + "generic-array 0.14.7", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -1176,43 +1233,38 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array", + "generic-array 0.14.7", "typenum", ] [[package]] name = "crypto-mac" -version = "0.8.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" dependencies = [ - "generic-array", - "subtle", + "generic-array 0.12.4", + "subtle 1.0.0", ] [[package]] name = "crypto-mac" -version = "0.11.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array", - "subtle", + "generic-array 0.14.7", + "subtle 2.4.1", ] [[package]] -name = "crypto_api" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f855e87e75a4799e18b8529178adcde6fd4f97c1449ff4821e747ff728bb102" - -[[package]] -name = "crypto_api_chachapoly" -version = "0.4.3" +name = "crypto-mac" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930b6a026ce9d358a17f9c9046c55d90b14bb847f36b6ebb6b19365d4feffb8" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "crypto_api", + "generic-array 0.14.7", + "subtle 2.4.1", ] [[package]] @@ -1249,7 +1301,7 @@ dependencies = [ "byteorder", "digest 0.9.0", "rand_core 0.5.1", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -1343,13 +1395,22 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -1360,7 +1421,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.4", "crypto-common", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -1529,11 +1590,11 @@ dependencies = [ "der", "digest 0.10.6", "ff 0.12.1", - "generic-array", + "generic-array 0.14.7", "group 0.12.1", "rand_core 0.6.4", "sec1", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -1578,15 +1639,6 @@ dependencies = [ "syn 2.0.15", ] -[[package]] -name = "equihash" -version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" -dependencies = [ - "blake2b_simd 1.0.1", - "byteorder", -] - [[package]] name = "erased-serde" version = "0.3.25" @@ -1642,6 +1694,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -1672,7 +1730,7 @@ dependencies = [ "ark-std", "bincode", "blake2", - "blake2b_simd 1.0.1", + "blake2b_simd", "borsh", "digest 0.10.6", "ed25519-dalek", @@ -1690,7 +1748,7 @@ dependencies = [ "serde_bytes", "serde_json", "subproductdomain", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -1715,7 +1773,7 @@ checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924" dependencies = [ "bitvec 0.22.3", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -1724,8 +1782,9 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" dependencies = [ + "bitvec 1.0.1", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -1778,7 +1837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd910db5f9ca4dc3116f8c46367825807aa2b942f72565f16b4be0b208a00a9e" dependencies = [ "block-modes", - "cipher", + "cipher 0.3.0", "libm", "num-bigint", "num-integer", @@ -1900,6 +1959,15 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1917,8 +1985,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if 1.0.0", + "js-sys", "libc", "wasi 0.9.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -1978,7 +2048,7 @@ dependencies = [ "byteorder", "ff 0.11.1", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -1988,8 +2058,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ "ff 0.12.1", + "memuse", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -2004,8 +2075,8 @@ dependencies = [ "ark-poly", "ark-serialize", "ark-std", - "blake2b_simd 1.0.1", - "chacha20", + "blake2b_simd", + "chacha20 0.8.2", "hex", "itertools", "miracl_core", @@ -2061,20 +2132,6 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" -[[package]] -name = "halo2" -version = "0.1.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f186b85ed81082fb1cf59d52b0111f02915e89a4ac61d292b38d075e570f3a9" -dependencies = [ - "blake2b_simd 0.5.11", - "ff 0.11.1", - "group 0.11.0", - "pasta_curves", - "rand 0.8.5", - "rayon", -] - [[package]] name = "hashbrown" version = "0.11.2" @@ -2154,6 +2211,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" +dependencies = [ + "crypto-mac 0.7.0", + "digest 0.8.1", +] + [[package]] name = "hmac" version = "0.8.1" @@ -2183,6 +2250,17 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "hmac-drbg" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" +dependencies = [ + "digest 0.8.1", + "generic-array 0.12.4", + "hmac 0.7.1", +] + [[package]] name = "hmac-drbg" version = "0.3.0" @@ -2190,10 +2268,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", - "generic-array", + "generic-array 0.14.7", "hmac 0.8.1", ] +[[package]] +name = "hmac-sha512" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e806677ce663d0a199541030c816847b36e8dc095f70dae4a4f4ad63da5383" + [[package]] name = "http" version = "0.2.9" @@ -2422,7 +2506,7 @@ dependencies = [ "ed25519-dalek-bip32", "flex-error", "futures", - "generic-array", + "generic-array 0.14.7", "hdpath", "hex", "http", @@ -2438,7 +2522,7 @@ dependencies = [ "regex", "retry", "ripemd", - "secp256k1 0.24.3", + "secp256k1", "semver 1.0.17", "serde", "serde_derive", @@ -2452,7 +2536,7 @@ dependencies = [ "tendermint-light-client-verifier 0.28.0", "tendermint-rpc 0.28.0", "thiserror", - "tiny-bip39", + "tiny-bip39 1.0.0", "tiny-keccak", "tokio", "toml", @@ -2554,9 +2638,9 @@ dependencies = [ [[package]] name = "incrementalmerkletree" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186fd3ab92aeac865d4b80b410de9a7b341d31ba8281373caed0b6d17b2b5e96" +checksum = "d5ad43a3f5795945459d577f6589cf62a476e92c79b75e70cd954364e14ce17b" dependencies = [ "serde", ] @@ -2587,6 +2671,15 @@ dependencies = [ "serde", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "instant" version = "0.1.12" @@ -2636,16 +2729,16 @@ dependencies = [ [[package]] name = "jubjub" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7baec19d4e83f9145d4891178101a604565edff9645770fc979804138b04c" +checksum = "a575df5f985fe1cd5b2b05664ff6accfc46559032b954529fd225a2168d27b0f" dependencies = [ - "bitvec 0.22.3", - "bls12_381", - "ff 0.11.1", - "group 0.11.0", + "bitvec 1.0.1", + "bls12_381 0.7.1", + "ff 0.12.1", + "group 0.12.1", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -2712,6 +2805,22 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +[[package]] +name = "libsecp256k1" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" +dependencies = [ + "arrayref", + "crunchy", + "digest 0.8.1", + "hmac-drbg 0.2.0", + "rand 0.7.3", + "sha2 0.8.2", + "subtle 2.4.1", + "typenum", +] + [[package]] name = "libsecp256k1" version = "0.7.0" @@ -2720,7 +2829,7 @@ dependencies = [ "arrayref", "base64 0.13.1", "digest 0.9.0", - "hmac-drbg", + "hmac-drbg 0.3.0", "libsecp256k1-core", "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", @@ -2735,9 +2844,9 @@ name = "libsecp256k1-core" version = "0.3.0" source = "git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9#bbb3bd44a49db361f21d9db80f9a087c194c0ae9" dependencies = [ - "crunchy 0.2.2", + "crunchy", "digest 0.9.0", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -2812,58 +2921,67 @@ dependencies = [ "libc", ] +[[package]] +name = "masp_note_encryption" +version = "0.2.0" +source = "git+https://github.com/anoma/masp?rev=cfea8c95d3f73077ca3e25380fd27e5b46e828fd#cfea8c95d3f73077ca3e25380fd27e5b46e828fd" +dependencies = [ + "borsh", + "chacha20 0.9.1", + "chacha20poly1305", + "cipher 0.4.4", + "rand_core 0.6.4", + "subtle 2.4.1", +] + [[package]] name = "masp_primitives" -version = "0.5.0" -source = "git+https://github.com/anoma/masp?rev=bee40fc465f6afbd10558d12fe96eb1742eee45c#bee40fc465f6afbd10558d12fe96eb1742eee45c" +version = "0.9.0" +source = "git+https://github.com/anoma/masp?rev=cfea8c95d3f73077ca3e25380fd27e5b46e828fd#cfea8c95d3f73077ca3e25380fd27e5b46e828fd" dependencies = [ "aes", "bip0039", - "bitvec 0.22.3", - "blake2b_simd 1.0.1", + "bitvec 1.0.1", + "blake2b_simd", "blake2s_simd 1.0.1", - "bls12_381", + "bls12_381 0.7.1", "borsh", "byteorder", - "chacha20poly1305", - "crypto_api_chachapoly", - "ff 0.11.1", + "ff 0.12.1", "fpe", - "group 0.11.0", + "group 0.12.1", "hex", "incrementalmerkletree", "jubjub", "lazy_static", + "masp_note_encryption", + "memuse", + "nonempty", "rand 0.8.5", "rand_core 0.6.4", - "ripemd160", - "secp256k1 0.20.3", - "serde", "sha2 0.9.9", - "subtle", + "subtle 2.4.1", "zcash_encoding", - "zcash_primitives", ] [[package]] name = "masp_proofs" -version = "0.5.0" -source = "git+https://github.com/anoma/masp?rev=bee40fc465f6afbd10558d12fe96eb1742eee45c#bee40fc465f6afbd10558d12fe96eb1742eee45c" +version = "0.9.0" +source = "git+https://github.com/anoma/masp?rev=cfea8c95d3f73077ca3e25380fd27e5b46e828fd#cfea8c95d3f73077ca3e25380fd27e5b46e828fd" dependencies = [ - "bellman", - "blake2b_simd 1.0.1", - "bls12_381", - "byteorder", + "bellman 0.13.1", + "blake2b_simd", + "bls12_381 0.7.1", "directories", - "ff 0.11.1", - "group 0.11.0", + "getrandom 0.2.9", + "group 0.12.1", "itertools", "jubjub", "lazy_static", "masp_primitives", "rand_core 0.6.4", - "zcash_primitives", - "zcash_proofs", + "redjubjub", + "tracing", ] [[package]] @@ -2941,9 +3059,12 @@ name = "memuse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2145869435ace5ea6ea3d35f59be559317ec9a0d04e1812d5f185a87b6d36f1a" -dependencies = [ - "nonempty", -] + +[[package]] +name = "memzero" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c0d11ac30a033511ae414355d80f70d9f29a44a49140face477117a1ee90db" [[package]] name = "merlin" @@ -3031,13 +3152,14 @@ version = "0.16.0" dependencies = [ "async-std", "async-trait", - "bellman", + "bellman 0.11.2", "bimap", - "bls12_381", + "bls12_381 0.6.1", "borsh", "circular-queue", "clru", "data-encoding", + "derivation-path", "derivative", "ibc", "ibc-proto 0.26.0", @@ -3056,16 +3178,20 @@ dependencies = [ "rand 0.8.5", "rand_core 0.6.4", "rayon", + "ripemd", "rust_decimal", "rust_decimal_macros", "serde", "serde_json", "sha2 0.9.9", + "slip10_ed25519", "tempfile", "tendermint 0.23.6", "tendermint-proto 0.23.6", "tendermint-rpc 0.23.6", "thiserror", + "tiny-bip39 0.8.2", + "tiny-hderive", "tokio", "toml", "tracing", @@ -3087,7 +3213,7 @@ dependencies = [ "ark-ec", "ark-serialize", "bech32 0.8.1", - "bellman", + "bellman 0.11.2", "borsh", "chrono", "data-encoding", @@ -3101,7 +3227,7 @@ dependencies = [ "ics23", "index-set", "itertools", - "libsecp256k1", + "libsecp256k1 0.7.0", "masp_primitives", "namada_macros", "proptest", @@ -3212,7 +3338,6 @@ dependencies = [ "borsh", "hex", "masp_primitives", - "masp_proofs", "namada_core", ] @@ -3236,7 +3361,6 @@ dependencies = [ "borsh", "getrandom 0.2.9", "masp_primitives", - "masp_proofs", "namada", "namada_test_utils", "namada_tests", @@ -3384,6 +3508,12 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -3396,33 +3526,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" -[[package]] -name = "orchard" -version = "0.1.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31f03b6d0aee6d993cac35388b818e04f076ded0ab284979e4d7cd5a8b3c2be" -dependencies = [ - "aes", - "arrayvec 0.7.2", - "bigint", - "bitvec 0.22.3", - "blake2b_simd 1.0.1", - "ff 0.11.1", - "fpe", - "group 0.11.0", - "halo2", - "incrementalmerkletree", - "lazy_static", - "memuse", - "nonempty", - "pasta_curves", - "rand 0.8.5", - "reddsa", - "serde", - "subtle", - "zcash_note_encryption 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "orion" version = "0.16.1" @@ -3431,7 +3534,7 @@ checksum = "c6624905ddd92e460ff0685567539ed1ac985b2dee4c92c7edcd64fce905b00c" dependencies = [ "ct-codecs", "getrandom 0.2.9", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -3444,6 +3547,15 @@ dependencies = [ "group 0.11.0", ] +[[package]] +name = "pairing" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135590d8bdba2b31346f9cd1fb2a912329f5135e832a4f422942eb6ead8b6b3b" +dependencies = [ + "group 0.12.1", +] + [[package]] name = "parity-scale-codec" version = "3.5.0" @@ -3513,22 +3625,7 @@ checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8" dependencies = [ "base64ct", "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "pasta_curves" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d647d91972bad78120fd61e06b225fcda117805c9bbf17676b51bd03a251278b" -dependencies = [ - "blake2b_simd 0.5.11", - "ff 0.11.1", - "group 0.11.0", - "lazy_static", - "rand 0.8.5", - "static_assertions", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -3537,6 +3634,15 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +[[package]] +name = "pbkdf2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +dependencies = [ + "crypto-mac 0.8.0", +] + [[package]] name = "pbkdf2" version = "0.9.0" @@ -3659,12 +3765,12 @@ dependencies = [ [[package]] name = "poly1305" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures", - "opaque-debug", + "opaque-debug 0.3.0", "universal-hash", ] @@ -4019,17 +4125,15 @@ dependencies = [ ] [[package]] -name = "reddsa" -version = "0.1.0" +name = "redjubjub" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90e2c94bca3445cae0d55dff7370e29c24885d2403a1665ce19c106c79455e6" +checksum = "6039ff156887caf92df308cbaccdc058c9d3155a913da046add6e48c4cdbd91d" dependencies = [ - "blake2b_simd 0.5.11", + "blake2b_simd", "byteorder", "digest 0.9.0", - "group 0.11.0", "jubjub", - "pasta_curves", "rand_core 0.6.4", "serde", "thiserror", @@ -4178,7 +4282,7 @@ checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" dependencies = [ "block-buffer 0.9.0", "digest 0.9.0", - "opaque-debug", + "opaque-debug 0.3.0", ] [[package]] @@ -4474,20 +4578,11 @@ checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ "base16ct", "der", - "generic-array", - "subtle", + "generic-array 0.14.7", + "subtle 2.4.1", "zeroize", ] -[[package]] -name = "secp256k1" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d03ceae636d0fed5bae6a7f4f664354c5f4fcedf6eef053fef17e49f837d0a" -dependencies = [ - "secp256k1-sys 0.4.2", -] - [[package]] name = "secp256k1" version = "0.24.3" @@ -4496,19 +4591,10 @@ checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" dependencies = [ "bitcoin_hashes", "rand 0.8.5", - "secp256k1-sys 0.6.1", + "secp256k1-sys", "serde", ] -[[package]] -name = "secp256k1-sys" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957da2573cde917463ece3570eab4a0b3f19de6f1646cde62e6fd3868f566036" -dependencies = [ - "cc", -] - [[package]] name = "secp256k1-sys" version = "0.6.1" @@ -4651,6 +4737,18 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + [[package]] name = "sha2" version = "0.9.9" @@ -4661,7 +4759,7 @@ dependencies = [ "cfg-if 1.0.0", "cpufeatures", "digest 0.9.0", - "opaque-debug", + "opaque-debug 0.3.0", ] [[package]] @@ -4749,6 +4847,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "slip10_ed25519" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be0ff28bf14f9610a342169084e87a4f435ad798ec528dc7579a3678fa9dc9a" +dependencies = [ + "hmac-sha512", +] + [[package]] name = "smallvec" version = "1.10.0" @@ -4829,6 +4936,12 @@ dependencies = [ "ark-std", ] +[[package]] +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" + [[package]] name = "subtle" version = "2.4.1" @@ -4930,7 +5043,7 @@ dependencies = [ "serde_repr", "sha2 0.9.9", "signature", - "subtle", + "subtle 2.4.1", "subtle-encoding", "tendermint-proto 0.23.6", "time", @@ -4960,7 +5073,7 @@ dependencies = [ "serde_repr", "sha2 0.9.9", "signature", - "subtle", + "subtle 2.4.1", "subtle-encoding", "tendermint-proto 0.28.0", "time", @@ -5129,7 +5242,7 @@ dependencies = [ "serde", "serde_bytes", "serde_json", - "subtle", + "subtle 2.4.1", "subtle-encoding", "tendermint 0.28.0", "tendermint-config 0.28.0", @@ -5240,6 +5353,24 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-bip39" +version = "0.8.2" +source = "git+https://github.com/anoma/tiny-bip39.git?rev=bf0f6d8713589b83af7a917366ec31f5275c0e57#bf0f6d8713589b83af7a917366ec31f5275c0e57" +dependencies = [ + "anyhow", + "hmac 0.8.1", + "once_cell", + "pbkdf2 0.4.0", + "rand 0.7.3", + "rustc-hash", + "sha2 0.9.9", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + [[package]] name = "tiny-bip39" version = "1.0.0" @@ -5259,13 +5390,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "tiny-hderive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01b874a4992538d4b2f4fbbac11b9419d685f4b39bdc3fed95b04e07bfd76040" +dependencies = [ + "base58", + "hmac 0.7.1", + "libsecp256k1 0.3.5", + "memzero", + "sha2 0.8.2", +] + [[package]] name = "tiny-keccak" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" dependencies = [ - "crunchy 0.2.2", + "crunchy", ] [[package]] @@ -5597,7 +5741,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ "byteorder", - "crunchy 0.2.2", + "crunchy", "hex", "static_assertions", ] @@ -5652,12 +5796,12 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "universal-hash" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" dependencies = [ - "generic-array", - "subtle", + "crypto-common", + "subtle 2.4.1", ] [[package]] @@ -6389,83 +6533,10 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" -dependencies = [ - "byteorder", - "nonempty", -] - -[[package]] -name = "zcash_note_encryption" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33f84ae538f05a8ac74c82527f06b77045ed9553a0871d9db036166a4c344e3a" -dependencies = [ - "chacha20", - "chacha20poly1305", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "zcash_note_encryption" -version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" -dependencies = [ - "chacha20", - "chacha20poly1305", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "zcash_primitives" -version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" +source = "git+https://github.com/zcash/librustzcash?rev=43c18d0#43c18d000fcbe45363b2d53585d5102841eff99e" dependencies = [ - "aes", - "bip0039", - "bitvec 0.22.3", - "blake2b_simd 1.0.1", - "blake2s_simd 1.0.1", - "bls12_381", "byteorder", - "chacha20poly1305", - "equihash", - "ff 0.11.1", - "fpe", - "group 0.11.0", - "hex", - "incrementalmerkletree", - "jubjub", - "lazy_static", - "memuse", "nonempty", - "orchard", - "rand 0.8.5", - "rand_core 0.6.4", - "sha2 0.9.9", - "subtle", - "zcash_encoding", - "zcash_note_encryption 0.1.0 (git+https://github.com/zcash/librustzcash/?rev=2425a08)", -] - -[[package]] -name = "zcash_proofs" -version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" -dependencies = [ - "bellman", - "blake2b_simd 1.0.1", - "bls12_381", - "byteorder", - "directories", - "ff 0.11.1", - "group 0.11.0", - "jubjub", - "lazy_static", - "rand_core 0.6.4", - "zcash_primitives", ] [[package]] diff --git a/wasm/checksums.json b/wasm/checksums.json index 544d711eb0..e42c1a4b7e 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,20 +1,20 @@ { - "tx_bond.wasm": "tx_bond.1ac4fc75655415a08a628bc07133d0141ade4086253b4f96673e9eb7da45726a.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.4dcd692eeb90658506d2eb284c7da90c08bb8d2a625f914573e99efebbfb94ed.wasm", - "tx_ibc.wasm": "tx_ibc.729f25abd2e77152eeec492c01a3d79db56c0ddb9ff235cb12590037387f4aa1.wasm", - "tx_init_account.wasm": "tx_init_account.53ef514d4f5eb4cb4a8a83894be08833777e4eb3aec7c7edbd147991b7d275f4.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.008bd96153886cdeff1ad68fc0978d35ad7a4edc19becf71d8f8c3e5d1f4b1a5.wasm", - "tx_init_validator.wasm": "tx_init_validator.d87634e95f47c4ed43806a23f2cdcea5e3c8c964987ab4059013a531a3cf91ae.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.0b1b66ec606b0ce85717e8a026872ef7f6edb936510d60abf0126d2936e34c5a.wasm", - "tx_transfer.wasm": "tx_transfer.57f62e5edc9ac962add374599cccf9de2d5b8341cfcbc6c11021f6947253da9a.wasm", - "tx_unbond.wasm": "tx_unbond.30f386ba0b79017c060e9e828f61b2f25590999c3e321ea33f06a7bcf2f0f63e.wasm", - "tx_update_vp.wasm": "tx_update_vp.24b8501c7df4ee482fbab8411a5e8666d36267302783998e6dd3ccc92a2eb802.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.e38b9a1e138aba5482e4429522218172a1c5ef91279e7b2c213fe2749c9220e7.wasm", - "tx_withdraw.wasm": "tx_withdraw.2d54fbffe9ab316cf63122ffe2f6c9b6f43e2da0145a5ea75dd99d5e86018fb0.wasm", - "vp_implicit.wasm": "vp_implicit.2057db599475eb1dc96e5b40c9f39eec8748f92bfd45713e85abea9c5b9a3021.wasm", - "vp_masp.wasm": "vp_masp.3f1f0b555faaf9260b19b04e8277fc52889a6cb0f8bd225b08c4777dba23a65d.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.05c6758273d11980f03bf58af4edeacb935a22c89aa915c9767825924b5bd00f.wasm", - "vp_token.wasm": "vp_token.61e54faa78931c6fd3cd4ef3d03495d735ed1052037f3cc395079d34db63de16.wasm", - "vp_user.wasm": "vp_user.f46b6efec59564fe82aa7e8fd1cfa503f4c86ae881b3704ee6080998fa0313dc.wasm", - "vp_validator.wasm": "vp_validator.78a51f16ad2ab2dcc10c77c66b31d3bd7f5e2c0d2181b1ef4bf52049f9469ab2.wasm" + "tx_bond.wasm": "tx_bond.9f4e8670ea75bb1247cc914dbde19f7023ca9a7d2b3261e16dfe10237364bc5f.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.3796cf236a36a62c1e3db0edaecbe7596277df8147ddd5988dab8ebab093dd6c.wasm", + "tx_ibc.wasm": "tx_ibc.63b144c47e0c5d60eb958b6cabbf61528297ae79ae9d9868fd19ad8d0b8cf0c5.wasm", + "tx_init_account.wasm": "tx_init_account.bc5961eac12b3e642e118f7574272badde9277e03e75d1b2a2741932ab04f9e0.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.ead98e083a2a42edf5df36672a28233cda2e9b017fa14f5d4f3b2943f52fadf7.wasm", + "tx_init_validator.wasm": "tx_init_validator.0aeeb155cc5da348556b27ba06af5d360d01416ad3b179938f8cee83c888c2c3.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.74d931b783331226de6fe561dcc0249a8a1e3a5e67de1196955d4980b79a51df.wasm", + "tx_transfer.wasm": "tx_transfer.c9822943cae9cdcf05f9a94ebe81d5c66e8d54c986cf5e4b8c7229c5713d0958.wasm", + "tx_unbond.wasm": "tx_unbond.1559850e450847375abb458a1c743a9636e00ccd922508e5c0a508b996a26f81.wasm", + "tx_update_vp.wasm": "tx_update_vp.6468db241190a691fb237bbb1720e9874960fc10890d4242761edd92578f237f.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.b192be3d87827d8a24fb3c7573c7df4f2211d72d0ccac6572e11625b7e701593.wasm", + "tx_withdraw.wasm": "tx_withdraw.a8feafdc8d8d38002ec02fe1561aa0ff0b77db2116237f35dd9134135886d578.wasm", + "vp_implicit.wasm": "vp_implicit.c8d26f0d3e6fb592535039ab4aac6b79be6c067b1c4caf776496e59d0b097674.wasm", + "vp_masp.wasm": "vp_masp.613ba0dfda6f5696f71343e07964ad10bbd851b41cdd85e8983d94c7ab0d78f6.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.8d33f39a948d49a354ae05fc82b73e6cb606cec3918ddb4b9a02d56ea5c5d74f.wasm", + "vp_token.wasm": "vp_token.c69c26c350e0807588177109bb38827a94964c7ecc02aa85641be3d42e3f1c05.wasm", + "vp_user.wasm": "vp_user.4f8e0b2f2ad5282f88ff637a84a3709d99ae35870a2b400aab03fdd8f9df2d81.wasm", + "vp_validator.wasm": "vp_validator.fd18e36a05424161df01e4dfde064a242384fdf4734d9f3e5587cf3fca06d7c8.wasm" } \ No newline at end of file diff --git a/wasm/tx_template/src/lib.rs b/wasm/tx_template/src/lib.rs index 473984aa31..4a14783325 100644 --- a/wasm/tx_template/src/lib.rs +++ b/wasm/tx_template/src/lib.rs @@ -1,7 +1,7 @@ use namada_tx_prelude::*; #[transaction] -fn apply_tx(_ctx: &mut Ctx, tx_data: Vec) -> TxResult { +fn apply_tx(_ctx: &mut Ctx, tx_data: Tx) -> TxResult { log_string(format!("apply_tx called with data: {:#?}", tx_data)); Ok(()) } @@ -19,8 +19,8 @@ mod tests { // The environment must be initialized first tx_host_env::init(); - let tx_data = vec![]; - apply_tx(ctx(), tx_data).unwrap(); + let tx = Tx::new(TxType::Raw); + apply_tx(ctx(), tx).unwrap(); let env = tx_host_env::take(); assert!(env.all_touched_storage_keys().is_empty()); diff --git a/wasm/vp_template/src/lib.rs b/wasm/vp_template/src/lib.rs index 35cdabd1c5..c4af9f31e0 100644 --- a/wasm/vp_template/src/lib.rs +++ b/wasm/vp_template/src/lib.rs @@ -3,7 +3,7 @@ use namada_vp_prelude::*; #[validity_predicate] fn validate_tx( ctx: &Ctx, - tx_data: Vec, + tx_data: Tx, addr: Address, keys_changed: BTreeSet, verifiers: BTreeSet
, diff --git a/wasm/wasm_source/Cargo.toml b/wasm/wasm_source/Cargo.toml index 85194462e6..5861d32d4f 100644 --- a/wasm/wasm_source/Cargo.toml +++ b/wasm/wasm_source/Cargo.toml @@ -25,7 +25,7 @@ tx_update_vp = ["namada_tx_prelude"] tx_vote_proposal = ["namada_tx_prelude"] tx_withdraw = ["namada_tx_prelude"] tx_change_validator_commission = ["namada_tx_prelude"] -vp_masp = ["namada_vp_prelude", "masp_proofs", "masp_primitives"] +vp_masp = ["namada_vp_prelude", "masp_primitives"] vp_implicit = ["namada_vp_prelude", "once_cell", "rust_decimal"] vp_testnet_faucet = ["namada_vp_prelude", "once_cell"] vp_token = ["namada_vp_prelude"] @@ -40,9 +40,9 @@ once_cell = {version = "1.8.0", optional = true} rust_decimal = {version = "=1.26.1", optional = true} wee_alloc = "0.4.5" getrandom = { version = "0.2", features = ["custom"] } -masp_proofs = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c", optional = true } -masp_primitives = { git = "https://github.com/anoma/masp", rev = "bee40fc465f6afbd10558d12fe96eb1742eee45c", optional = true } -ripemd = "0.1.3" +# branch = "murisi/namada-integration" +masp_primitives = { git = "https://github.com/anoma/masp", rev = "cfea8c95d3f73077ca3e25380fd27e5b46e828fd", optional = true } +ripemd = "0.1" [dev-dependencies] namada = {path = "../../shared"} diff --git a/wasm/wasm_source/src/tx_bond.rs b/wasm/wasm_source/src/tx_bond.rs index 8215045d5c..35e8ba7fac 100644 --- a/wasm/wasm_source/src/tx_bond.rs +++ b/wasm/wasm_source/src/tx_bond.rs @@ -3,11 +3,9 @@ use namada_tx_prelude::*; #[transaction] -fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { - let signed = SignedTxData::try_from_slice(&tx_data[..]) - .wrap_err("failed to decode SignedTxData") - .unwrap(); - let data = signed.data.ok_or_err_msg("Missing data").unwrap(); +fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { + let signed = tx_data; + let data = signed.data().ok_or_err_msg("Missing data")?; let bond = transaction::pos::Bond::try_from_slice(&data[..]) .wrap_err("failed to decode Bond") .unwrap(); @@ -24,9 +22,9 @@ mod tests { bond_handle, read_consensus_validator_set_addresses_with_stake, read_total_stake, read_validator_stake, }; - use namada::proto::Tx; - use namada::types::chain::ChainId; + use namada::proto::{Code, Data, Signature, Tx}; use namada::types::storage::Epoch; + use namada::types::transaction::TxType; use namada_tests::log::test; use namada_tests::native_vp::pos::init_pos; use namada_tests::native_vp::TestNativeVpEnv; @@ -100,9 +98,18 @@ mod tests { let tx_code = vec![]; let tx_data = bond.try_to_vec().unwrap(); - let tx = Tx::new(tx_code, Some(tx_data), ChainId::default(), None); - let signed_tx = tx.sign(&key); - let tx_data = signed_tx.data.unwrap(); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(tx_code)); + tx.set_data(Data::new(tx_data)); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key, + ))); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key, + ))); + let signed_tx = tx.clone(); // Ensure that the initial stake of the sole validator is equal to the // PoS account balance @@ -145,7 +152,7 @@ mod tests { ); } - apply_tx(ctx(), tx_data)?; + apply_tx(ctx(), signed_tx)?; // Read the data after the tx is executed. let mut epoched_total_stake_post: Vec = Vec::new(); diff --git a/wasm/wasm_source/src/tx_change_validator_commission.rs b/wasm/wasm_source/src/tx_change_validator_commission.rs index 30ae263269..938a57236b 100644 --- a/wasm/wasm_source/src/tx_change_validator_commission.rs +++ b/wasm/wasm_source/src/tx_change_validator_commission.rs @@ -4,10 +4,9 @@ use namada_tx_prelude::transaction::pos::CommissionChange; use namada_tx_prelude::*; #[transaction] -fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { - let signed = SignedTxData::try_from_slice(&tx_data[..]) - .wrap_err("failed to decode SignedTxData")?; - let data = signed.data.ok_or_err_msg("Missing data")?; +fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { + let signed = tx_data; + let data = signed.data().ok_or_err_msg("Missing data")?; let CommissionChange { validator, new_rate, @@ -22,9 +21,9 @@ mod tests { use namada::ledger::pos::{PosParams, PosVP}; use namada::proof_of_stake::validator_commission_rate_handle; - use namada::proto::Tx; - use namada::types::chain::ChainId; + use namada::proto::{Code, Data, Signature, Tx}; use namada::types::storage::Epoch; + use namada::types::transaction::TxType; use namada_tests::log::test; use namada_tests::native_vp::pos::init_pos; use namada_tests::native_vp::TestNativeVpEnv; @@ -79,9 +78,18 @@ mod tests { let tx_code = vec![]; let tx_data = commission_change.try_to_vec().unwrap(); - let tx = Tx::new(tx_code, Some(tx_data), ChainId::default(), None); - let signed_tx = tx.sign(&key); - let tx_data = signed_tx.data.unwrap(); + let mut tx = Tx::new(TxType::Raw); + tx.set_data(Data::new(tx_data)); + tx.set_code(Code::new(tx_code)); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key, + ))); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key, + ))); + let signed_tx = tx.clone(); // Read the data before the tx is executed let commission_rate_handle = @@ -98,7 +106,7 @@ mod tests { assert_eq!(commission_rates_pre[0], Some(initial_rate)); - apply_tx(ctx(), tx_data)?; + apply_tx(ctx(), signed_tx)?; // Read the data after the tx is executed diff --git a/wasm/wasm_source/src/tx_ibc.rs b/wasm/wasm_source/src/tx_ibc.rs index b9473170b2..08c7678b07 100644 --- a/wasm/wasm_source/src/tx_ibc.rs +++ b/wasm/wasm_source/src/tx_ibc.rs @@ -6,10 +6,9 @@ use namada_tx_prelude::*; #[transaction] -fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { - let signed = SignedTxData::try_from_slice(&tx_data[..]) - .wrap_err("failed to decode SignedTxData")?; - let data = signed.data.ok_or_err_msg("Missing data")?; +fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { + let signed = tx_data; + let data = signed.data().ok_or_err_msg("Missing data")?; ibc::ibc_actions(ctx).execute(&data).into_storage_result() } diff --git a/wasm/wasm_source/src/tx_init_account.rs b/wasm/wasm_source/src/tx_init_account.rs index b1041c40a2..2e85b70ae9 100644 --- a/wasm/wasm_source/src/tx_init_account.rs +++ b/wasm/wasm_source/src/tx_init_account.rs @@ -4,15 +4,21 @@ use namada_tx_prelude::*; #[transaction] -fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { - let signed = SignedTxData::try_from_slice(&tx_data[..]) - .wrap_err("failed to decode SignedTxData")?; - let data = signed.data.ok_or_err_msg("Missing data")?; +fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { + let signed = tx_data; + let data = signed.data().ok_or_err_msg("Missing data")?; let tx_data = transaction::InitAccount::try_from_slice(&data[..]) .wrap_err("failed to decode InitAccount")?; debug_log!("apply_tx called to init a new established account"); - let address = ctx.init_account(&tx_data.vp_code_hash)?; + let vp_code = signed + .get_section(&tx_data.vp_code_hash) + .ok_or_err_msg("vp code section not found")? + .extra_data_sec() + .ok_or_err_msg("vp code section must be tagged as extra")? + .code + .hash(); + let address = ctx.init_account(vp_code)?; let pk_key = key::pk_key(&address); ctx.write(&pk_key, &tx_data.public_key)?; Ok(()) diff --git a/wasm/wasm_source/src/tx_init_proposal.rs b/wasm/wasm_source/src/tx_init_proposal.rs index cb7fe9ffbb..2507a18d21 100644 --- a/wasm/wasm_source/src/tx_init_proposal.rs +++ b/wasm/wasm_source/src/tx_init_proposal.rs @@ -3,10 +3,9 @@ use namada_tx_prelude::*; #[transaction] -fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { - let signed = SignedTxData::try_from_slice(&tx_data[..]) - .wrap_err("failed to decode SignedTxData")?; - let data = signed.data.ok_or_err_msg("Missing data")?; +fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { + let signed = tx_data; + let data = signed.data().ok_or_err_msg("Missing data")?; let tx_data = transaction::governance::InitProposalData::try_from_slice(&data[..]) .wrap_err("failed to decode InitProposalData")?; diff --git a/wasm/wasm_source/src/tx_init_validator.rs b/wasm/wasm_source/src/tx_init_validator.rs index 6a823faf3f..0cd37da111 100644 --- a/wasm/wasm_source/src/tx_init_validator.rs +++ b/wasm/wasm_source/src/tx_init_validator.rs @@ -5,16 +5,23 @@ use namada_tx_prelude::transaction::InitValidator; use namada_tx_prelude::*; #[transaction] -fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { - let signed = SignedTxData::try_from_slice(&tx_data[..]) - .wrap_err("failed to decode SignedTxData")?; - let data = signed.data.ok_or_err_msg("Missing data")?; +fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { + let signed = tx_data; + let data = signed.data().ok_or_err_msg("Missing data")?; let init_validator = InitValidator::try_from_slice(&data[..]) .wrap_err("failed to decode InitValidator")?; debug_log!("apply_tx called to init a new validator account"); + // Get the validator vp code from the extra section + let validator_vp_code_hash = signed + .get_section(&init_validator.validator_vp_code_hash) + .ok_or_err_msg("validator vp section not found")? + .extra_data_sec() + .ok_or_err_msg("validator vp section must be tagged as extra")? + .code + .hash(); // Register the validator in PoS - match ctx.init_validator(init_validator) { + match ctx.init_validator(init_validator, validator_vp_code_hash) { Ok(validator_address) => { debug_log!("Created validator {}", validator_address.encode(),) } diff --git a/wasm/wasm_source/src/tx_reveal_pk.rs b/wasm/wasm_source/src/tx_reveal_pk.rs index be3bacce6d..e091b69a4c 100644 --- a/wasm/wasm_source/src/tx_reveal_pk.rs +++ b/wasm/wasm_source/src/tx_reveal_pk.rs @@ -7,8 +7,10 @@ use namada_tx_prelude::key::common; use namada_tx_prelude::*; #[transaction] -fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { - let pk = common::PublicKey::try_from_slice(&tx_data[..]) +fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { + let signed = tx_data; + let data = signed.data().ok_or_err_msg("Missing data")?; + let pk = common::PublicKey::try_from_slice(&data[..]) .wrap_err("failed to decode common::PublicKey from tx_data")?; debug_log!("tx_reveal_pk called with pk: {pk}"); key::reveal_pk(ctx, &pk) diff --git a/wasm/wasm_source/src/tx_transfer.rs b/wasm/wasm_source/src/tx_transfer.rs index 293b7207c3..1727035d2f 100644 --- a/wasm/wasm_source/src/tx_transfer.rs +++ b/wasm/wasm_source/src/tx_transfer.rs @@ -5,10 +5,9 @@ use namada_tx_prelude::*; #[transaction] -fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { - let signed = SignedTxData::try_from_slice(&tx_data[..]) - .wrap_err("failed to decode SignedTxData")?; - let data = signed.data.ok_or_err_msg("Missing data")?; +fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { + let signed = tx_data; + let data = signed.data().ok_or_err_msg("Missing data")?; let transfer = token::Transfer::try_from_slice(&data[..]) .wrap_err("failed to decode token::Transfer")?; debug_log!("apply_tx called with transfer: {:#?}", transfer); @@ -19,9 +18,26 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { sub_prefix, amount, key, - shielded, + shielded: shielded_hash, } = transfer; + let shielded = shielded_hash + .as_ref() + .map(|hash| { + signed + .get_section(hash) + .and_then(Section::masp_tx) + .ok_or_err_msg("unable to find shielded section") + }) + .transpose()?; token::transfer( - ctx, &source, &target, &token, sub_prefix, amount, &key, &shielded, + ctx, + &source, + &target, + &token, + sub_prefix, + amount, + &key, + &shielded_hash, + &shielded, ) } diff --git a/wasm/wasm_source/src/tx_unbond.rs b/wasm/wasm_source/src/tx_unbond.rs index 33cf9f56ea..49932fdcbf 100644 --- a/wasm/wasm_source/src/tx_unbond.rs +++ b/wasm/wasm_source/src/tx_unbond.rs @@ -4,10 +4,9 @@ use namada_tx_prelude::*; #[transaction] -fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { - let signed = SignedTxData::try_from_slice(&tx_data[..]) - .wrap_err("failed to decode SignedTxData")?; - let data = signed.data.ok_or_err_msg("Missing data")?; +fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { + let signed = tx_data; + let data = signed.data().ok_or_err_msg("Missing data")?; let unbond = transaction::pos::Unbond::try_from_slice(&data[..]) .wrap_err("failed to decode Unbond")?; @@ -24,9 +23,9 @@ mod tests { bond_handle, read_consensus_validator_set_addresses_with_stake, read_total_stake, read_validator_stake, unbond_handle, }; - use namada::proto::Tx; - use namada::types::chain::ChainId; + use namada::proto::{Code, Data, Signature, Tx}; use namada::types::storage::Epoch; + use namada::types::transaction::TxType; use namada_tests::log::test; use namada_tests::native_vp::pos::init_pos; use namada_tests::native_vp::TestNativeVpEnv; @@ -122,9 +121,18 @@ mod tests { let tx_code = vec![]; let tx_data = unbond.try_to_vec().unwrap(); - let tx = Tx::new(tx_code, Some(tx_data), ChainId::default(), None); - let signed_tx = tx.sign(&key); - let tx_data = signed_tx.data.unwrap(); + let mut tx = Tx::new(TxType::Raw); + tx.set_data(Data::new(tx_data)); + tx.set_code(Code::new(tx_code)); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key, + ))); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key, + ))); + let signed_tx = tx.clone(); let unbond_src = unbond .source @@ -182,7 +190,7 @@ mod tests { // dbg!(&epoched_bonds_pre); // Apply the unbond tx - apply_tx(ctx(), tx_data)?; + apply_tx(ctx(), signed_tx)?; // Read the data after the tx is executed. // The following storage keys should be updated: diff --git a/wasm/wasm_source/src/tx_update_vp.rs b/wasm/wasm_source/src/tx_update_vp.rs index fb0b80af40..7df5facde6 100644 --- a/wasm/wasm_source/src/tx_update_vp.rs +++ b/wasm/wasm_source/src/tx_update_vp.rs @@ -5,14 +5,19 @@ use namada_tx_prelude::*; #[transaction] -fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { - let signed = SignedTxData::try_from_slice(&tx_data[..]) - .wrap_err("failed to decode SignedTxData")?; - let data = signed.data.ok_or_err_msg("Missing data")?; +fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { + let signed = tx_data; + let data = signed.data().ok_or_err_msg("Missing data")?; let update_vp = transaction::UpdateVp::try_from_slice(&data[..]) .wrap_err("failed to decode UpdateVp")?; debug_log!("update VP for: {:#?}", update_vp.addr); - - ctx.update_validity_predicate(&update_vp.addr, update_vp.vp_code_hash) + let vp_code_hash = signed + .get_section(&update_vp.vp_code_hash) + .ok_or_err_msg("vp code section not found")? + .extra_data_sec() + .ok_or_err_msg("vp code section must be tagged as extra")? + .code + .hash(); + ctx.update_validity_predicate(&update_vp.addr, vp_code_hash) } diff --git a/wasm/wasm_source/src/tx_vote_proposal.rs b/wasm/wasm_source/src/tx_vote_proposal.rs index 92c7af4c7f..3be6685bdc 100644 --- a/wasm/wasm_source/src/tx_vote_proposal.rs +++ b/wasm/wasm_source/src/tx_vote_proposal.rs @@ -3,10 +3,9 @@ use namada_tx_prelude::*; #[transaction] -fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { - let signed = SignedTxData::try_from_slice(&tx_data[..]) - .wrap_err("failed to decode SignedTxData")?; - let data = signed.data.ok_or_err_msg("Missing data")?; +fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { + let signed = tx_data; + let data = signed.data().ok_or_err_msg("Missing data")?; let tx_data = transaction::governance::VoteProposalData::try_from_slice(&data[..]) .wrap_err("failed to decode VoteProposalData")?; diff --git a/wasm/wasm_source/src/tx_withdraw.rs b/wasm/wasm_source/src/tx_withdraw.rs index b00661261e..eaac9f57bf 100644 --- a/wasm/wasm_source/src/tx_withdraw.rs +++ b/wasm/wasm_source/src/tx_withdraw.rs @@ -4,10 +4,9 @@ use namada_tx_prelude::*; #[transaction] -fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { - let signed = SignedTxData::try_from_slice(&tx_data[..]) - .wrap_err("failed to decode SignedTxData")?; - let data = signed.data.ok_or_err_msg("Missing data")?; +fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { + let signed = tx_data; + let data = signed.data().ok_or_err_msg("Missing data")?; let withdraw = transaction::pos::Withdraw::try_from_slice(&data[..]) .wrap_err("failed to decode Withdraw")?; @@ -23,9 +22,9 @@ fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { mod tests { use namada::ledger::pos::{GenesisValidator, PosParams, PosVP}; use namada::proof_of_stake::unbond_handle; - use namada::proto::Tx; - use namada::types::chain::ChainId; + use namada::proto::{Code, Data, Signature, Tx}; use namada::types::storage::Epoch; + use namada::types::transaction::TxType; use namada_tests::log::test; use namada_tests::native_vp::pos::init_pos; use namada_tests::native_vp::TestNativeVpEnv; @@ -155,9 +154,18 @@ mod tests { let tx_code = vec![]; let tx_data = withdraw.try_to_vec().unwrap(); - let tx = Tx::new(tx_code, Some(tx_data), ChainId::default(), None); - let signed_tx = tx.sign(&key); - let tx_data = signed_tx.data.unwrap(); + let mut tx = Tx::new(TxType::Raw); + tx.set_code(Code::new(tx_code)); + tx.set_data(Data::new(tx_data)); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &key, + ))); + tx.add_section(Section::Signature(Signature::new( + tx.code_sechash(), + &key, + ))); + let signed_tx = tx.clone(); // Read data before we apply tx: let pos_balance_key = token::balance_key( @@ -180,7 +188,7 @@ mod tests { assert_eq!(unbond_pre, Some(unbonded_amount)); - apply_tx(ctx(), tx_data)?; + apply_tx(ctx(), signed_tx)?; // Read the data after the tx is executed let unbond_post = diff --git a/wasm/wasm_source/src/vp_implicit.rs b/wasm/wasm_source/src/vp_implicit.rs index 26a88d84ea..938c27be95 100644 --- a/wasm/wasm_source/src/vp_implicit.rs +++ b/wasm/wasm_source/src/vp_implicit.rs @@ -52,7 +52,7 @@ impl<'a> From<&'a storage::Key> for KeyType<'a> { #[validity_predicate] fn validate_tx( ctx: &Ctx, - tx_data: Vec, + tx_data: Tx, addr: Address, keys_changed: BTreeSet, verifiers: BTreeSet
, @@ -64,23 +64,14 @@ fn validate_tx( verifiers ); - let signed_tx_data = - Lazy::new(|| SignedTxData::try_from_slice(&tx_data[..])); - - let valid_sig = Lazy::new(|| match &*signed_tx_data { - Ok(signed_tx_data) => { - let pk = key::get(ctx, &addr); - match pk { - Ok(Some(pk)) => { - matches!( - ctx.verify_tx_signature(&pk, &signed_tx_data.sig), - Ok(true) - ) - } - _ => false, - } + let valid_sig = Lazy::new(|| { + let pk = key::get(ctx, &addr); + match pk { + Ok(Some(pk)) => tx_data + .verify_signature(&pk, tx_data.data_sechash()) + .is_ok(), + _ => false, } - _ => false, }); if !is_valid_tx(ctx, &tx_data)? { @@ -202,7 +193,9 @@ fn validate_tx( mod tests { // Use this as `#[test]` annotation to enable logging use namada::ledger::pos::{GenesisValidator, PosParams}; + use namada::proto::{Code, Data, Signature}; use namada::types::storage::Epoch; + use namada::types::transaction::TxType; use namada_test_utils::TestWasms; use namada_tests::log::test; use namada_tests::native_vp::pos::init_pos; @@ -219,7 +212,8 @@ mod tests { /// Test that no-op transaction (i.e. no storage modifications) accepted. #[test] fn test_no_op_transaction() { - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let addr: Address = address::testing::established_address_1(); let keys_changed: BTreeSet = BTreeSet::default(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -251,7 +245,8 @@ mod tests { }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -277,7 +272,8 @@ mod tests { }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -313,7 +309,8 @@ mod tests { }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -357,12 +354,14 @@ mod tests { amount, &None, &None, + &None, ) .unwrap(); }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -426,7 +425,8 @@ mod tests { }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -492,16 +492,19 @@ mod tests { }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&secret_key); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &secret_key, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } @@ -538,12 +541,14 @@ mod tests { amount, &None, &None, + &None, ) .unwrap(); }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -588,21 +593,25 @@ mod tests { amount, &None, &None, + &None, ) .unwrap(); }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&secret_key); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &secret_key, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } @@ -641,12 +650,14 @@ mod tests { amount, &None, &None, + &None, ) .unwrap(); }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -701,7 +712,8 @@ mod tests { }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -741,15 +753,15 @@ mod tests { }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&secret_key); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.add_section(Section::Signature(Signature::new(tx.data_sechash(), &secret_key))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); - assert!(validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers).unwrap()); + assert!(validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers).unwrap()); } } @@ -775,12 +787,13 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -820,21 +833,25 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&secret_key); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &secret_key, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - !validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + !validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } @@ -853,7 +870,7 @@ mod tests { tx_env.store_wasm_code(vp_code); // hardcoded hash of VP_ALWAYS_TRUE_WASM - tx_env.init_parameters(None, None, Some(vec!["E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855".to_string()])); + tx_env.init_parameters(None, None, Some(vec!["2AC0BCB5D9E2019180F99BEB84A77E32728CDABAAD8C4F0EF3762594EC836A9D".to_string()])); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); @@ -864,21 +881,26 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&secret_key); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_code(Code::from_hash(vp_hash)); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &secret_key, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } diff --git a/wasm/wasm_source/src/vp_masp.rs b/wasm/wasm_source/src/vp_masp.rs index 958501c96c..b3ccda6286 100644 --- a/wasm/wasm_source/src/vp_masp.rs +++ b/wasm/wasm_source/src/vp_masp.rs @@ -1,7 +1,6 @@ use std::cmp::Ordering; use masp_primitives::asset_type::AssetType; -use masp_primitives::legacy::TransparentAddress::{PublicKey, Script}; use masp_primitives::transaction::components::{Amount, TxOut}; /// Multi-asset shielded pool VP. use namada_vp_prelude::address::masp; @@ -71,7 +70,7 @@ fn convert_amount( #[validity_predicate] fn validate_tx( ctx: &Ctx, - tx_data: Vec, + tx_data: Tx, addr: Address, keys_changed: BTreeSet, verifiers: BTreeSet
, @@ -79,22 +78,30 @@ fn validate_tx( debug_log!( "vp_masp called with {} bytes data, address {}, keys_changed {:?}, \ verifiers {:?}", - tx_data.len(), + tx_data.data().as_ref().map(|x| x.len()).unwrap_or(0), addr, keys_changed, verifiers, ); - let signed = SignedTxData::try_from_slice(&tx_data[..]).unwrap(); - // Also get the data as bytes for the VM. - let data = signed.data.as_ref().unwrap().clone(); + let signed = tx_data; let transfer = - token::Transfer::try_from_slice(&signed.data.unwrap()[..]).unwrap(); - - if let Some(shielded_tx) = transfer.shielded { + token::Transfer::try_from_slice(&signed.data().unwrap()[..]).unwrap(); + + let shielded = transfer + .shielded + .as_ref() + .map(|hash| { + signed + .get_section(hash) + .and_then(Section::masp_tx) + .ok_or_err_msg("unable to find shielded section") + }) + .transpose()?; + if let Some(shielded_tx) = shielded { let mut transparent_tx_pool = Amount::zero(); // The Sapling value balance adds to the transparent tx pool - transparent_tx_pool += shielded_tx.value_balance.clone(); + transparent_tx_pool += shielded_tx.sapling_value_balance(); if transfer.source != masp() { // Handle transparent input @@ -116,13 +123,15 @@ fn validate_tx( // 2. the transparent transaction value pool's amount must equal the // containing wrapper transaction's fee amount // Satisfies 1. - if !shielded_tx.vin.is_empty() { - debug_log!( - "Transparent input to a transaction from the masp must be \ - 0 but is {}", - shielded_tx.vin.len() - ); - return reject(); + if let Some(transp_bundle) = shielded_tx.transparent_bundle() { + if !transp_bundle.vin.is_empty() { + debug_log!( + "Transparent input to a transaction from the masp \ + must be 0 but is {}", + transp_bundle.vin.len() + ); + return reject(); + } } } @@ -136,16 +145,20 @@ fn validate_tx( // 4. Public key must be the hash of the target // Satisfies 1. - if shielded_tx.vout.len() != 1 { + let transp_bundle = + shielded_tx.transparent_bundle().ok_or_err_msg( + "Expected transparent outputs in unshielding transaction", + )?; + if transp_bundle.vout.len() != 1 { debug_log!( - "Transparent output to a transaction to the masp must be \ - 1 but is {}", - shielded_tx.vin.len() + "Transparent output to a transaction from the masp must \ + be 1 but is {}", + transp_bundle.vin.len() ); return reject(); } - let out: &TxOut = &shielded_tx.vout[0]; + let out: &TxOut = &transp_bundle.vout[0]; let expected_asset_type: AssetType = asset_type_from_epoched_address( @@ -155,7 +168,10 @@ fn validate_tx( // Satisfies 2. and 3. if !(valid_asset_type(&expected_asset_type, &out.asset_type) - && valid_transfer_amount(out.value, u64::from(transfer.amount))) + && valid_transfer_amount( + out.value as u64, + u64::from(transfer.amount), + )) { return reject(); } @@ -170,38 +186,34 @@ fn validate_tx( transparent_tx_pool -= transp_amt; // Satisfies 4. - match out.script_pubkey.address() { - None | Some(Script(_)) => {} - Some(PublicKey(pub_bytes)) => { - let target_enc = transfer - .target - .try_to_vec() - .expect("target address encoding"); - - let hash = - Ripemd160::digest(sha256(&target_enc).as_slice()); - - if <[u8; 20]>::from(hash) != pub_bytes { - debug_log!( - "the public key of the output account does not \ - match the transfer target" - ); - return reject(); - } - } + let target_enc = transfer + .target + .try_to_vec() + .expect("target address encoding"); + + let hash = Ripemd160::digest(sha256(&target_enc).as_slice()); + + if <[u8; 20]>::from(hash) != out.address.0 { + debug_log!( + "the public key of the output account does not match the \ + transfer target" + ); + return reject(); } } else { // Handle shielded output // The following boundary conditions must be satisfied // 1. Zero transparent output // Satisfies 1. - if !shielded_tx.vout.is_empty() { - debug_log!( - "Transparent output to a transaction from the masp must \ - be 0 but is {}", - shielded_tx.vin.len() - ); - return reject(); + if let Some(transp_bundle) = shielded_tx.transparent_bundle() { + if !transp_bundle.vout.is_empty() { + debug_log!( + "Transparent output to a transaction from the masp \ + must be 0 but is {}", + transp_bundle.vin.len() + ); + return reject(); + } } } @@ -218,8 +230,9 @@ fn validate_tx( } _ => {} } + // Do the expensive proof verification in the VM at the end. + ctx.verify_masp(shielded_tx.try_to_vec().unwrap()) + } else { + reject() } - - // Do the expensive proof verification in the VM at the end. - ctx.verify_masp(data) } diff --git a/wasm/wasm_source/src/vp_testnet_faucet.rs b/wasm/wasm_source/src/vp_testnet_faucet.rs index e7067e664f..430cb55b21 100644 --- a/wasm/wasm_source/src/vp_testnet_faucet.rs +++ b/wasm/wasm_source/src/vp_testnet_faucet.rs @@ -12,7 +12,7 @@ use once_cell::unsync::Lazy; #[validity_predicate] fn validate_tx( ctx: &Ctx, - tx_data: Vec, + tx_data: Tx, addr: Address, keys_changed: BTreeSet, verifiers: BTreeSet
, @@ -25,23 +25,14 @@ fn validate_tx( verifiers ); - let signed_tx_data = - Lazy::new(|| SignedTxData::try_from_slice(&tx_data[..])); - - let valid_sig = Lazy::new(|| match &*signed_tx_data { - Ok(signed_tx_data) => { - let pk = key::get(ctx, &addr); - match pk { - Ok(Some(pk)) => { - matches!( - ctx.verify_tx_signature(&pk, &signed_tx_data.sig), - Ok(true) - ) - } - _ => false, - } + let valid_sig = Lazy::new(|| { + let pk = key::get(ctx, &addr); + match pk { + Ok(Some(pk)) => tx_data + .verify_signature(&pk, tx_data.data_sechash()) + .is_ok(), + _ => false, } - _ => false, }); if !is_valid_tx(ctx, &tx_data)? { @@ -109,6 +100,8 @@ fn validate_tx( #[cfg(test)] mod tests { use address::testing::arb_non_internal_address; + use namada::proto::{Data, Signature}; + use namada::types::transaction::TxType; use namada_test_utils::TestWasms; // Use this as `#[test]` annotation to enable logging use namada_tests::log::test; @@ -116,7 +109,7 @@ mod tests { use namada_tests::vp::vp_host_env::storage::Key; use namada_tests::vp::*; use namada_tx_prelude::{StorageWrite, TxEnv}; - use namada_vp_prelude::key::{RefTo, SigScheme}; + use namada_vp_prelude::key::RefTo; use proptest::prelude::*; use storage::testing::arb_account_storage_key_no_vp; @@ -128,7 +121,8 @@ mod tests { /// Test that no-op transaction (i.e. no storage modifications) accepted. #[test] fn test_no_op_transaction() { - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let addr: Address = address::testing::established_address_1(); let keys_changed: BTreeSet = BTreeSet::default(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -171,12 +165,14 @@ mod tests { amount, &None, &None, + &None, ) .unwrap(); }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -207,12 +203,13 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -247,21 +244,25 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&keypair); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } @@ -310,11 +311,12 @@ mod tests { // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Apply transfer in a transaction - tx_host_env::token::transfer(tx::ctx(), address, &target, &token, None, amount, &None, &None).unwrap(); + tx_host_env::token::transfer(tx::ctx(), address, &target, &token, None, amount, &None, &None, &None).unwrap(); }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -352,13 +354,6 @@ mod tests { let challenge = testnet_pow::Challenge::new(&mut tx_env.wl_storage, &vp_owner, target.clone()).unwrap(); let solution = challenge.solve(); let solution_bytes = solution.try_to_vec().unwrap(); - // The signature itself doesn't matter and is not being checked in this - // test, it's just used to construct `SignedTxData` - let sig = key::common::SigScheme::sign(&target_key, &solution_bytes); - let signed_solution = SignedTxData { - data: Some(solution_bytes), - sig, - }; // Initialize VP environment from a transaction vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { @@ -367,13 +362,15 @@ mod tests { let valid = solution.validate(tx::ctx(), address, target.clone()).unwrap(); assert!(valid); // Apply transfer in a transaction - tx_host_env::token::transfer(tx::ctx(), address, &target, &token, None, amount, &None, &None).unwrap(); + tx_host_env::token::transfer(tx::ctx(), address, &target, &token, None, amount, &None, &None, &None).unwrap(); }); let mut vp_env = vp_host_env::take(); // This is set by the protocol when the wrapper tx has a valid PoW vp_env.has_valid_pow = true; - let tx_data: Vec = signed_solution.try_to_vec().unwrap(); + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(solution_bytes)); + tx_data.add_section(Section::Signature(Signature::new(tx_data.data_sechash(), &target_key))); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -418,15 +415,16 @@ mod tests { }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&keypair); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new(tx.data_sechash(), &keypair))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); - assert!(validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers).unwrap()); + assert!(validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers).unwrap()); } } } diff --git a/wasm/wasm_source/src/vp_token.rs b/wasm/wasm_source/src/vp_token.rs index cc7aee3311..8ccc2c9053 100644 --- a/wasm/wasm_source/src/vp_token.rs +++ b/wasm/wasm_source/src/vp_token.rs @@ -10,7 +10,7 @@ use namada_vp_prelude::{storage, token, *}; #[validity_predicate] fn validate_tx( ctx: &Ctx, - tx_data: Vec, + tx_data: Tx, addr: Address, keys_changed: BTreeSet, verifiers: BTreeSet
, @@ -113,6 +113,8 @@ fn token_checks( mod tests { // Use this as `#[test]` annotation to enable logging use namada::core::ledger::storage_api::token; + use namada::proto::Data; + use namada::types::transaction::TxType; use namada_tests::log::test; use namada_tests::tx::{self, TestTxEnv}; use namada_tests::vp::*; @@ -150,7 +152,8 @@ mod tests { }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers = vp_env.get_verifiers(); @@ -203,7 +206,8 @@ mod tests { }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers = vp_env.get_verifiers(); @@ -255,7 +259,8 @@ mod tests { }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers = vp_env.get_verifiers(); diff --git a/wasm/wasm_source/src/vp_user.rs b/wasm/wasm_source/src/vp_user.rs index 0f5df00577..a09f1ab946 100644 --- a/wasm/wasm_source/src/vp_user.rs +++ b/wasm/wasm_source/src/vp_user.rs @@ -52,7 +52,7 @@ impl<'a> From<&'a storage::Key> for KeyType<'a> { #[validity_predicate] fn validate_tx( ctx: &Ctx, - tx_data: Vec, + tx_data: Tx, addr: Address, keys_changed: BTreeSet, verifiers: BTreeSet
, @@ -64,23 +64,14 @@ fn validate_tx( verifiers ); - let signed_tx_data = - Lazy::new(|| SignedTxData::try_from_slice(&tx_data[..])); - - let valid_sig = Lazy::new(|| match &*signed_tx_data { - Ok(signed_tx_data) => { - let pk = key::get(ctx, &addr); - match pk { - Ok(Some(pk)) => { - matches!( - ctx.verify_tx_signature(&pk, &signed_tx_data.sig), - Ok(true) - ) - } - _ => false, - } + let valid_sig = Lazy::new(|| { + let pk = key::get(ctx, &addr); + match pk { + Ok(Some(pk)) => tx_data + .verify_signature(&pk, tx_data.data_sechash()) + .is_ok(), + _ => false, } - _ => false, }); if !is_valid_tx(ctx, &tx_data)? { @@ -193,7 +184,9 @@ fn validate_tx( mod tests { use address::testing::arb_non_internal_address; use namada::ledger::pos::{GenesisValidator, PosParams}; + use namada::proto::{Code, Data, Signature}; use namada::types::storage::Epoch; + use namada::types::transaction::TxType; use namada_test_utils::TestWasms; // Use this as `#[test]` annotation to enable logging use namada_tests::log::test; @@ -211,7 +204,8 @@ mod tests { /// Test that no-op transaction (i.e. no storage modifications) accepted. #[test] fn test_no_op_transaction() { - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let addr: Address = address::testing::established_address_1(); let keys_changed: BTreeSet = BTreeSet::default(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -254,12 +248,14 @@ mod tests { amount, &None, &None, + &None, ) .unwrap(); }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -300,12 +296,14 @@ mod tests { amount, &None, &None, + &None, ) .unwrap(); }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -350,21 +348,26 @@ mod tests { amount, &None, &None, + &None, ) .unwrap(); }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&keypair); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } @@ -422,7 +425,8 @@ mod tests { }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -488,16 +492,20 @@ mod tests { }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&secret_key); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &secret_key, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } @@ -534,12 +542,14 @@ mod tests { amount, &None, &None, + &None, ) .unwrap(); }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -594,7 +604,8 @@ mod tests { }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -636,15 +647,16 @@ mod tests { }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&keypair); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new(tx.data_sechash(), &keypair))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); - assert!(validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers).unwrap()); + assert!(validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers).unwrap()); } } @@ -668,12 +680,13 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -709,21 +722,25 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&keypair); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } @@ -752,21 +769,25 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&keypair); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - !validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + !validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } @@ -796,21 +817,25 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&keypair); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } @@ -844,21 +869,25 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&keypair); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - !validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + !validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } @@ -877,7 +906,7 @@ mod tests { tx_env.store_wasm_code(vp_code); // hardcoded hash of VP_ALWAYS_TRUE_WASM - tx_env.init_parameters(None, None, Some(vec!["E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855".to_string()])); + tx_env.init_parameters(None, None, Some(vec!["2AC0BCB5D9E2019180F99BEB84A77E32728CDABAAD8C4F0EF3762594EC836A9D".to_string()])); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); @@ -888,21 +917,26 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&keypair); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_code(Code::from_hash(vp_hash)); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } diff --git a/wasm/wasm_source/src/vp_validator.rs b/wasm/wasm_source/src/vp_validator.rs index d57d09b304..c2142423f5 100644 --- a/wasm/wasm_source/src/vp_validator.rs +++ b/wasm/wasm_source/src/vp_validator.rs @@ -52,7 +52,7 @@ impl<'a> From<&'a storage::Key> for KeyType<'a> { #[validity_predicate] fn validate_tx( ctx: &Ctx, - tx_data: Vec, + tx_data: Tx, addr: Address, keys_changed: BTreeSet, verifiers: BTreeSet
, @@ -64,23 +64,14 @@ fn validate_tx( verifiers ); - let signed_tx_data = - Lazy::new(|| SignedTxData::try_from_slice(&tx_data[..])); - - let valid_sig = Lazy::new(|| match &*signed_tx_data { - Ok(signed_tx_data) => { - let pk = key::get(ctx, &addr); - match pk { - Ok(Some(pk)) => { - matches!( - ctx.verify_tx_signature(&pk, &signed_tx_data.sig), - Ok(true) - ) - } - _ => false, - } + let valid_sig = Lazy::new(|| { + let pk = key::get(ctx, &addr); + match pk { + Ok(Some(pk)) => tx_data + .verify_signature(&pk, tx_data.data_sechash()) + .is_ok(), + _ => false, } - _ => false, }); if !is_valid_tx(ctx, &tx_data)? { @@ -201,7 +192,9 @@ fn validate_tx( mod tests { use address::testing::arb_non_internal_address; use namada::ledger::pos::{GenesisValidator, PosParams}; + use namada::proto::{Code, Data, Signature}; use namada::types::storage::Epoch; + use namada::types::transaction::TxType; use namada_test_utils::TestWasms; // Use this as `#[test]` annotation to enable logging use namada_tests::log::test; @@ -220,7 +213,8 @@ mod tests { /// Test that no-op transaction (i.e. no storage modifications) accepted. #[test] fn test_no_op_transaction() { - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let addr: Address = address::testing::established_address_1(); let keys_changed: BTreeSet = BTreeSet::default(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -263,12 +257,14 @@ mod tests { amount, &None, &None, + &None, ) .unwrap(); }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -309,12 +305,14 @@ mod tests { amount, &None, &None, + &None, ) .unwrap(); }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -359,21 +357,26 @@ mod tests { amount, &None, &None, + &None, ) .unwrap(); }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&keypair); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } @@ -437,7 +440,8 @@ mod tests { }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -509,16 +513,20 @@ mod tests { }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&secret_key); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &secret_key, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } @@ -555,12 +563,14 @@ mod tests { amount, &None, &None, + &None, ) .unwrap(); }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -615,7 +625,8 @@ mod tests { }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -657,15 +668,16 @@ mod tests { }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&keypair); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new(tx.data_sechash(), &keypair))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); - assert!(validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers).unwrap()); + assert!(validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers).unwrap()); } } @@ -689,12 +701,13 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let vp_env = vp_host_env::take(); - let tx_data: Vec = vec![]; + let mut tx_data = Tx::new(TxType::Raw); + tx_data.set_data(Data::new(vec![])); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); @@ -730,21 +743,25 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&keypair); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } @@ -773,21 +790,25 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&keypair); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - !validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + !validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } @@ -817,21 +838,25 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&keypair); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } @@ -865,21 +890,25 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&keypair); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - !validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + !validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } @@ -898,7 +927,7 @@ mod tests { tx_env.store_wasm_code(vp_code); // hardcoded hash of VP_ALWAYS_TRUE_WASM - tx_env.init_parameters(None, None, Some(vec!["E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855".to_string()])); + tx_env.init_parameters(None, None, Some(vec!["2AC0BCB5D9E2019180F99BEB84A77E32728CDABAAD8C4F0EF3762594EC836A9D".to_string()])); // Spawn the accounts to be able to modify their storage tx_env.spawn_accounts([&vp_owner]); @@ -909,21 +938,26 @@ mod tests { vp_host_env::init_from_tx(vp_owner.clone(), tx_env, |address| { // Update VP in a transaction tx::ctx() - .update_validity_predicate(address, &vp_hash) + .update_validity_predicate(address, vp_hash) .unwrap(); }); let mut vp_env = vp_host_env::take(); - let tx = vp_env.tx.clone(); - let signed_tx = tx.sign(&keypair); - let tx_data: Vec = signed_tx.data.as_ref().cloned().unwrap(); - vp_env.tx = signed_tx; + let mut tx = vp_env.tx.clone(); + tx.set_code(Code::from_hash(vp_hash)); + tx.set_data(Data::new(vec![])); + tx.add_section(Section::Signature(Signature::new( + tx.data_sechash(), + &keypair, + ))); + let signed_tx = tx.clone(); + vp_env.tx = signed_tx.clone(); let keys_changed: BTreeSet = vp_env.all_touched_storage_keys(); let verifiers: BTreeSet
= BTreeSet::default(); vp_host_env::set(vp_env); assert!( - validate_tx(&CTX, tx_data, vp_owner, keys_changed, verifiers) + validate_tx(&CTX, signed_tx, vp_owner, keys_changed, verifiers) .unwrap() ); } diff --git a/wasm_for_tests/tx_memory_limit.wasm b/wasm_for_tests/tx_memory_limit.wasm index f25dd9a631..84fb2b57d0 100755 Binary files a/wasm_for_tests/tx_memory_limit.wasm and b/wasm_for_tests/tx_memory_limit.wasm differ diff --git a/wasm_for_tests/tx_mint_tokens.wasm b/wasm_for_tests/tx_mint_tokens.wasm index c8cb5ccb03..d0156bf1bd 100755 Binary files a/wasm_for_tests/tx_mint_tokens.wasm and b/wasm_for_tests/tx_mint_tokens.wasm differ diff --git a/wasm_for_tests/tx_no_op.wasm b/wasm_for_tests/tx_no_op.wasm index 160795df99..68b3a96f34 100755 Binary files a/wasm_for_tests/tx_no_op.wasm and b/wasm_for_tests/tx_no_op.wasm differ diff --git a/wasm_for_tests/tx_proposal_code.wasm b/wasm_for_tests/tx_proposal_code.wasm index b23e15125b..fb5d8d5d28 100755 Binary files a/wasm_for_tests/tx_proposal_code.wasm and b/wasm_for_tests/tx_proposal_code.wasm differ diff --git a/wasm_for_tests/tx_read_storage_key.wasm b/wasm_for_tests/tx_read_storage_key.wasm index d54ad5fcaf..06145bc7e8 100755 Binary files a/wasm_for_tests/tx_read_storage_key.wasm and b/wasm_for_tests/tx_read_storage_key.wasm differ diff --git a/wasm_for_tests/tx_write.wasm b/wasm_for_tests/tx_write.wasm index 87e1ff0a2b..cfbf02e639 100755 Binary files a/wasm_for_tests/tx_write.wasm and b/wasm_for_tests/tx_write.wasm differ diff --git a/wasm_for_tests/tx_write_storage_key.wasm b/wasm_for_tests/tx_write_storage_key.wasm new file mode 100755 index 0000000000..a0fb758ae9 Binary files /dev/null and b/wasm_for_tests/tx_write_storage_key.wasm differ diff --git a/wasm_for_tests/vp_always_false.wasm b/wasm_for_tests/vp_always_false.wasm index c5dcc4c5c9..a9e047cba4 100755 Binary files a/wasm_for_tests/vp_always_false.wasm and b/wasm_for_tests/vp_always_false.wasm differ diff --git a/wasm_for_tests/vp_always_true.wasm b/wasm_for_tests/vp_always_true.wasm index 4b74d80433..be671df8d0 100755 Binary files a/wasm_for_tests/vp_always_true.wasm and b/wasm_for_tests/vp_always_true.wasm differ diff --git a/wasm_for_tests/vp_eval.wasm b/wasm_for_tests/vp_eval.wasm index 9d9d653651..ffcc0a32b4 100755 Binary files a/wasm_for_tests/vp_eval.wasm and b/wasm_for_tests/vp_eval.wasm differ diff --git a/wasm_for_tests/vp_memory_limit.wasm b/wasm_for_tests/vp_memory_limit.wasm index 4ec8fbb8ba..1ef6e12489 100755 Binary files a/wasm_for_tests/vp_memory_limit.wasm and b/wasm_for_tests/vp_memory_limit.wasm differ diff --git a/wasm_for_tests/vp_read_storage_key.wasm b/wasm_for_tests/vp_read_storage_key.wasm index 2feb07793c..ee2e0fc3b0 100755 Binary files a/wasm_for_tests/vp_read_storage_key.wasm and b/wasm_for_tests/vp_read_storage_key.wasm differ diff --git a/wasm_for_tests/wasm_source/Cargo.lock b/wasm_for_tests/wasm_source/Cargo.lock index 18900018b0..f0e39809e5 100644 --- a/wasm_for_tests/wasm_source/Cargo.lock +++ b/wasm_for_tests/wasm_source/Cargo.lock @@ -19,11 +19,12 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aead" -version = "0.4.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "generic-array", + "crypto-common", + "generic-array 0.14.7", ] [[package]] @@ -33,9 +34,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ "cfg-if 1.0.0", - "cipher", + "cipher 0.3.0", "cpufeatures", - "opaque-debug", + "opaque-debug 0.3.0", ] [[package]] @@ -441,6 +442,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base58" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" + [[package]] name = "base64" version = "0.13.1" @@ -486,20 +493,26 @@ dependencies = [ "lazy_static", "log", "num_cpus", - "pairing", + "pairing 0.21.0", "rand_core 0.6.4", "rayon", - "subtle", + "subtle 2.4.1", ] [[package]] -name = "bigint" -version = "4.4.3" +name = "bellman" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0e8c8a600052b52482eff2cf4d810e462fdff1f656ac1ecb6232132a1ed7def" +checksum = "a4dd656ef4fdf7debb6d87d4dd92642fcbcdb78cbf6600c13e25c87e4d1a3807" dependencies = [ + "bitvec 1.0.1", + "blake2s_simd 1.0.1", "byteorder", - "crunchy 0.1.6", + "ff 0.12.1", + "group 0.12.1", + "pairing 0.22.0", + "rand_core 0.6.4", + "subtle 2.4.1", ] [[package]] @@ -557,7 +570,7 @@ checksum = "0694ea59225b0c5f3cb405ff3f670e4828358ed26aec49dc352f730f0cb1a8a3" dependencies = [ "bech32 0.9.1", "bitcoin_hashes", - "secp256k1 0.24.3", + "secp256k1", "serde", ] @@ -609,17 +622,6 @@ dependencies = [ "digest 0.10.6", ] -[[package]] -name = "blake2b_simd" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "constant_time_eq 0.1.5", -] - [[package]] name = "blake2b_simd" version = "1.0.1" @@ -667,13 +669,25 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding 0.1.5", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + [[package]] name = "block-buffer" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -682,7 +696,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -691,8 +705,17 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" dependencies = [ - "block-padding", - "cipher", + "block-padding 0.2.1", + "cipher 0.3.0", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", ] [[package]] @@ -724,9 +747,22 @@ checksum = "a829c821999c06be34de314eaeb7dd1b42be38661178bc26ad47a4eacebdb0f9" dependencies = [ "ff 0.11.1", "group 0.11.0", - "pairing", + "pairing 0.21.0", + "rand_core 0.6.4", + "subtle 2.4.1", +] + +[[package]] +name = "bls12_381" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3c196a77437e7cc2fb515ce413a6401291578b5afc8ecb29a3c7ab957f05941" +dependencies = [ + "ff 0.12.1", + "group 0.12.1", + "pairing 0.22.0", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -788,6 +824,12 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "bytecheck" version = "0.6.10" @@ -887,20 +929,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" dependencies = [ "cfg-if 1.0.0", - "cipher", + "cipher 0.3.0", "cpufeatures", - "zeroize", ] [[package]] -name = "chacha20poly1305" +name = "chacha20" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if 1.0.0", + "cipher 0.4.4", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ "aead", - "chacha20", - "cipher", + "chacha20 0.9.1", + "cipher 0.4.4", "poly1305", "zeroize", ] @@ -923,7 +975,18 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array", + "generic-array 0.14.7", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", ] [[package]] @@ -1146,12 +1209,6 @@ dependencies = [ "cfg-if 1.0.0", ] -[[package]] -name = "crunchy" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" - [[package]] name = "crunchy" version = "0.2.2" @@ -1164,9 +1221,9 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ - "generic-array", + "generic-array 0.14.7", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -1176,43 +1233,38 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array", + "generic-array 0.14.7", "typenum", ] [[package]] name = "crypto-mac" -version = "0.8.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" dependencies = [ - "generic-array", - "subtle", + "generic-array 0.12.4", + "subtle 1.0.0", ] [[package]] name = "crypto-mac" -version = "0.11.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array", - "subtle", + "generic-array 0.14.7", + "subtle 2.4.1", ] [[package]] -name = "crypto_api" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f855e87e75a4799e18b8529178adcde6fd4f97c1449ff4821e747ff728bb102" - -[[package]] -name = "crypto_api_chachapoly" -version = "0.4.3" +name = "crypto-mac" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930b6a026ce9d358a17f9c9046c55d90b14bb847f36b6ebb6b19365d4feffb8" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "crypto_api", + "generic-array 0.14.7", + "subtle 2.4.1", ] [[package]] @@ -1249,7 +1301,7 @@ dependencies = [ "byteorder", "digest 0.9.0", "rand_core 0.5.1", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -1343,13 +1395,22 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -1360,7 +1421,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.4", "crypto-common", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -1529,11 +1590,11 @@ dependencies = [ "der", "digest 0.10.6", "ff 0.12.1", - "generic-array", + "generic-array 0.14.7", "group 0.12.1", "rand_core 0.6.4", "sec1", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -1578,15 +1639,6 @@ dependencies = [ "syn 2.0.15", ] -[[package]] -name = "equihash" -version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" -dependencies = [ - "blake2b_simd 1.0.1", - "byteorder", -] - [[package]] name = "erased-serde" version = "0.3.25" @@ -1642,6 +1694,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -1672,7 +1730,7 @@ dependencies = [ "ark-std", "bincode", "blake2", - "blake2b_simd 1.0.1", + "blake2b_simd", "borsh", "digest 0.10.6", "ed25519-dalek", @@ -1690,7 +1748,7 @@ dependencies = [ "serde_bytes", "serde_json", "subproductdomain", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -1715,7 +1773,7 @@ checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924" dependencies = [ "bitvec 0.22.3", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -1724,8 +1782,9 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" dependencies = [ + "bitvec 1.0.1", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -1778,7 +1837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd910db5f9ca4dc3116f8c46367825807aa2b942f72565f16b4be0b208a00a9e" dependencies = [ "block-modes", - "cipher", + "cipher 0.3.0", "libm", "num-bigint", "num-integer", @@ -1900,6 +1959,15 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1917,8 +1985,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if 1.0.0", + "js-sys", "libc", "wasi 0.9.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -1978,7 +2048,7 @@ dependencies = [ "byteorder", "ff 0.11.1", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -1988,8 +2058,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ "ff 0.12.1", + "memuse", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -2004,8 +2075,8 @@ dependencies = [ "ark-poly", "ark-serialize", "ark-std", - "blake2b_simd 1.0.1", - "chacha20", + "blake2b_simd", + "chacha20 0.8.2", "hex", "itertools", "miracl_core", @@ -2061,20 +2132,6 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" -[[package]] -name = "halo2" -version = "0.1.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f186b85ed81082fb1cf59d52b0111f02915e89a4ac61d292b38d075e570f3a9" -dependencies = [ - "blake2b_simd 0.5.11", - "ff 0.11.1", - "group 0.11.0", - "pasta_curves", - "rand 0.8.5", - "rayon", -] - [[package]] name = "hashbrown" version = "0.11.2" @@ -2154,6 +2211,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" +dependencies = [ + "crypto-mac 0.7.0", + "digest 0.8.1", +] + [[package]] name = "hmac" version = "0.8.1" @@ -2183,6 +2250,17 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "hmac-drbg" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" +dependencies = [ + "digest 0.8.1", + "generic-array 0.12.4", + "hmac 0.7.1", +] + [[package]] name = "hmac-drbg" version = "0.3.0" @@ -2190,10 +2268,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", - "generic-array", + "generic-array 0.14.7", "hmac 0.8.1", ] +[[package]] +name = "hmac-sha512" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e806677ce663d0a199541030c816847b36e8dc095f70dae4a4f4ad63da5383" + [[package]] name = "http" version = "0.2.9" @@ -2343,7 +2427,7 @@ dependencies = [ [[package]] name = "ibc" version = "0.36.0" -source = "git+https://github.com/heliaxdev/cosmos-ibc-rs.git?rev=e71bc2cc79f8c2b32e970d95312f251398c93d9e#e71bc2cc79f8c2b32e970d95312f251398c93d9e" +source = "git+https://github.com/heliaxdev/cosmos-ibc-rs.git?rev=4ec09135f5ef669144ef48c0a111a6428164c9ed#4ec09135f5ef669144ef48c0a111a6428164c9ed" dependencies = [ "bytes", "cfg-if 1.0.0", @@ -2391,7 +2475,7 @@ dependencies = [ [[package]] name = "ibc-proto" version = "0.26.0" -source = "git+https://github.com/heliaxdev/ibc-proto-rs.git?rev=6f4038fcf4981f1ed70771d1cd89931267f917af#6f4038fcf4981f1ed70771d1cd89931267f917af" +source = "git+https://github.com/heliaxdev/ibc-proto-rs.git?rev=19ad1cdfda14e95bd45fba0e0c710c086f71d0bc#19ad1cdfda14e95bd45fba0e0c710c086f71d0bc" dependencies = [ "base64 0.13.1", "bytes", @@ -2422,7 +2506,7 @@ dependencies = [ "ed25519-dalek-bip32", "flex-error", "futures", - "generic-array", + "generic-array 0.14.7", "hdpath", "hex", "http", @@ -2438,7 +2522,7 @@ dependencies = [ "regex", "retry", "ripemd", - "secp256k1 0.24.3", + "secp256k1", "semver 1.0.17", "serde", "serde_derive", @@ -2452,7 +2536,7 @@ dependencies = [ "tendermint-light-client-verifier 0.28.0", "tendermint-rpc 0.28.0", "thiserror", - "tiny-bip39", + "tiny-bip39 1.0.0", "tiny-keccak", "tokio", "toml", @@ -2554,9 +2638,9 @@ dependencies = [ [[package]] name = "incrementalmerkletree" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186fd3ab92aeac865d4b80b410de9a7b341d31ba8281373caed0b6d17b2b5e96" +checksum = "d5ad43a3f5795945459d577f6589cf62a476e92c79b75e70cd954364e14ce17b" dependencies = [ "serde", ] @@ -2587,6 +2671,15 @@ dependencies = [ "serde", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "instant" version = "0.1.12" @@ -2636,16 +2729,16 @@ dependencies = [ [[package]] name = "jubjub" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7baec19d4e83f9145d4891178101a604565edff9645770fc979804138b04c" +checksum = "a575df5f985fe1cd5b2b05664ff6accfc46559032b954529fd225a2168d27b0f" dependencies = [ - "bitvec 0.22.3", - "bls12_381", - "ff 0.11.1", - "group 0.11.0", + "bitvec 1.0.1", + "bls12_381 0.7.1", + "ff 0.12.1", + "group 0.12.1", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -2712,6 +2805,22 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +[[package]] +name = "libsecp256k1" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" +dependencies = [ + "arrayref", + "crunchy", + "digest 0.8.1", + "hmac-drbg 0.2.0", + "rand 0.7.3", + "sha2 0.8.2", + "subtle 2.4.1", + "typenum", +] + [[package]] name = "libsecp256k1" version = "0.7.0" @@ -2720,7 +2829,7 @@ dependencies = [ "arrayref", "base64 0.13.1", "digest 0.9.0", - "hmac-drbg", + "hmac-drbg 0.3.0", "libsecp256k1-core", "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", @@ -2735,9 +2844,9 @@ name = "libsecp256k1-core" version = "0.3.0" source = "git+https://github.com/heliaxdev/libsecp256k1?rev=bbb3bd44a49db361f21d9db80f9a087c194c0ae9#bbb3bd44a49db361f21d9db80f9a087c194c0ae9" dependencies = [ - "crunchy 0.2.2", + "crunchy", "digest 0.9.0", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -2812,58 +2921,67 @@ dependencies = [ "libc", ] +[[package]] +name = "masp_note_encryption" +version = "0.2.0" +source = "git+https://github.com/anoma/masp?rev=cfea8c95d3f73077ca3e25380fd27e5b46e828fd#cfea8c95d3f73077ca3e25380fd27e5b46e828fd" +dependencies = [ + "borsh", + "chacha20 0.9.1", + "chacha20poly1305", + "cipher 0.4.4", + "rand_core 0.6.4", + "subtle 2.4.1", +] + [[package]] name = "masp_primitives" -version = "0.5.0" -source = "git+https://github.com/anoma/masp?rev=bee40fc465f6afbd10558d12fe96eb1742eee45c#bee40fc465f6afbd10558d12fe96eb1742eee45c" +version = "0.9.0" +source = "git+https://github.com/anoma/masp?rev=cfea8c95d3f73077ca3e25380fd27e5b46e828fd#cfea8c95d3f73077ca3e25380fd27e5b46e828fd" dependencies = [ "aes", "bip0039", - "bitvec 0.22.3", - "blake2b_simd 1.0.1", + "bitvec 1.0.1", + "blake2b_simd", "blake2s_simd 1.0.1", - "bls12_381", + "bls12_381 0.7.1", "borsh", "byteorder", - "chacha20poly1305", - "crypto_api_chachapoly", - "ff 0.11.1", + "ff 0.12.1", "fpe", - "group 0.11.0", + "group 0.12.1", "hex", "incrementalmerkletree", "jubjub", "lazy_static", + "masp_note_encryption", + "memuse", + "nonempty", "rand 0.8.5", "rand_core 0.6.4", - "ripemd160", - "secp256k1 0.20.3", - "serde", "sha2 0.9.9", - "subtle", + "subtle 2.4.1", "zcash_encoding", - "zcash_primitives", ] [[package]] name = "masp_proofs" -version = "0.5.0" -source = "git+https://github.com/anoma/masp?rev=bee40fc465f6afbd10558d12fe96eb1742eee45c#bee40fc465f6afbd10558d12fe96eb1742eee45c" +version = "0.9.0" +source = "git+https://github.com/anoma/masp?rev=cfea8c95d3f73077ca3e25380fd27e5b46e828fd#cfea8c95d3f73077ca3e25380fd27e5b46e828fd" dependencies = [ - "bellman", - "blake2b_simd 1.0.1", - "bls12_381", - "byteorder", + "bellman 0.13.1", + "blake2b_simd", + "bls12_381 0.7.1", "directories", - "ff 0.11.1", - "group 0.11.0", + "getrandom 0.2.9", + "group 0.12.1", "itertools", "jubjub", "lazy_static", "masp_primitives", "rand_core 0.6.4", - "zcash_primitives", - "zcash_proofs", + "redjubjub", + "tracing", ] [[package]] @@ -2941,9 +3059,12 @@ name = "memuse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2145869435ace5ea6ea3d35f59be559317ec9a0d04e1812d5f185a87b6d36f1a" -dependencies = [ - "nonempty", -] + +[[package]] +name = "memzero" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c0d11ac30a033511ae414355d80f70d9f29a44a49140face477117a1ee90db" [[package]] name = "merlin" @@ -3031,13 +3152,14 @@ version = "0.16.0" dependencies = [ "async-std", "async-trait", - "bellman", + "bellman 0.11.2", "bimap", - "bls12_381", + "bls12_381 0.6.1", "borsh", "circular-queue", "clru", "data-encoding", + "derivation-path", "derivative", "ibc", "ibc-proto 0.26.0", @@ -3056,16 +3178,20 @@ dependencies = [ "rand 0.8.5", "rand_core 0.6.4", "rayon", + "ripemd", "rust_decimal", "rust_decimal_macros", "serde", "serde_json", "sha2 0.9.9", + "slip10_ed25519", "tempfile", "tendermint 0.23.6", "tendermint-proto 0.23.6", "tendermint-rpc 0.23.6", "thiserror", + "tiny-bip39 0.8.2", + "tiny-hderive", "tokio", "toml", "tracing", @@ -3087,7 +3213,7 @@ dependencies = [ "ark-ec", "ark-serialize", "bech32 0.8.1", - "bellman", + "bellman 0.11.2", "borsh", "chrono", "data-encoding", @@ -3101,7 +3227,7 @@ dependencies = [ "ics23", "index-set", "itertools", - "libsecp256k1", + "libsecp256k1 0.7.0", "masp_primitives", "namada_macros", "proptest", @@ -3212,7 +3338,6 @@ dependencies = [ "borsh", "hex", "masp_primitives", - "masp_proofs", "namada_core", ] @@ -3375,6 +3500,12 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -3387,33 +3518,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" -[[package]] -name = "orchard" -version = "0.1.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31f03b6d0aee6d993cac35388b818e04f076ded0ab284979e4d7cd5a8b3c2be" -dependencies = [ - "aes", - "arrayvec 0.7.2", - "bigint", - "bitvec 0.22.3", - "blake2b_simd 1.0.1", - "ff 0.11.1", - "fpe", - "group 0.11.0", - "halo2", - "incrementalmerkletree", - "lazy_static", - "memuse", - "nonempty", - "pasta_curves", - "rand 0.8.5", - "reddsa", - "serde", - "subtle", - "zcash_note_encryption 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "orion" version = "0.16.1" @@ -3422,7 +3526,7 @@ checksum = "c6624905ddd92e460ff0685567539ed1ac985b2dee4c92c7edcd64fce905b00c" dependencies = [ "ct-codecs", "getrandom 0.2.9", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -3435,6 +3539,15 @@ dependencies = [ "group 0.11.0", ] +[[package]] +name = "pairing" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135590d8bdba2b31346f9cd1fb2a912329f5135e832a4f422942eb6ead8b6b3b" +dependencies = [ + "group 0.12.1", +] + [[package]] name = "parity-scale-codec" version = "3.5.0" @@ -3504,22 +3617,7 @@ checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8" dependencies = [ "base64ct", "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "pasta_curves" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d647d91972bad78120fd61e06b225fcda117805c9bbf17676b51bd03a251278b" -dependencies = [ - "blake2b_simd 0.5.11", - "ff 0.11.1", - "group 0.11.0", - "lazy_static", - "rand 0.8.5", - "static_assertions", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -3528,6 +3626,15 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +[[package]] +name = "pbkdf2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +dependencies = [ + "crypto-mac 0.8.0", +] + [[package]] name = "pbkdf2" version = "0.9.0" @@ -3650,12 +3757,12 @@ dependencies = [ [[package]] name = "poly1305" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures", - "opaque-debug", + "opaque-debug 0.3.0", "universal-hash", ] @@ -4010,17 +4117,15 @@ dependencies = [ ] [[package]] -name = "reddsa" -version = "0.1.0" +name = "redjubjub" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90e2c94bca3445cae0d55dff7370e29c24885d2403a1665ce19c106c79455e6" +checksum = "6039ff156887caf92df308cbaccdc058c9d3155a913da046add6e48c4cdbd91d" dependencies = [ - "blake2b_simd 0.5.11", + "blake2b_simd", "byteorder", "digest 0.9.0", - "group 0.11.0", "jubjub", - "pasta_curves", "rand_core 0.6.4", "serde", "thiserror", @@ -4169,7 +4274,7 @@ checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" dependencies = [ "block-buffer 0.9.0", "digest 0.9.0", - "opaque-debug", + "opaque-debug 0.3.0", ] [[package]] @@ -4465,20 +4570,11 @@ checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ "base16ct", "der", - "generic-array", - "subtle", + "generic-array 0.14.7", + "subtle 2.4.1", "zeroize", ] -[[package]] -name = "secp256k1" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d03ceae636d0fed5bae6a7f4f664354c5f4fcedf6eef053fef17e49f837d0a" -dependencies = [ - "secp256k1-sys 0.4.2", -] - [[package]] name = "secp256k1" version = "0.24.3" @@ -4487,19 +4583,10 @@ checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" dependencies = [ "bitcoin_hashes", "rand 0.8.5", - "secp256k1-sys 0.6.1", + "secp256k1-sys", "serde", ] -[[package]] -name = "secp256k1-sys" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957da2573cde917463ece3570eab4a0b3f19de6f1646cde62e6fd3868f566036" -dependencies = [ - "cc", -] - [[package]] name = "secp256k1-sys" version = "0.6.1" @@ -4642,6 +4729,18 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + [[package]] name = "sha2" version = "0.9.9" @@ -4652,7 +4751,7 @@ dependencies = [ "cfg-if 1.0.0", "cpufeatures", "digest 0.9.0", - "opaque-debug", + "opaque-debug 0.3.0", ] [[package]] @@ -4740,6 +4839,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "slip10_ed25519" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be0ff28bf14f9610a342169084e87a4f435ad798ec528dc7579a3678fa9dc9a" +dependencies = [ + "hmac-sha512", +] + [[package]] name = "smallvec" version = "1.10.0" @@ -4820,6 +4928,12 @@ dependencies = [ "ark-std", ] +[[package]] +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" + [[package]] name = "subtle" version = "2.4.1" @@ -4921,7 +5035,7 @@ dependencies = [ "serde_repr", "sha2 0.9.9", "signature", - "subtle", + "subtle 2.4.1", "subtle-encoding", "tendermint-proto 0.23.6", "time", @@ -4951,7 +5065,7 @@ dependencies = [ "serde_repr", "sha2 0.9.9", "signature", - "subtle", + "subtle 2.4.1", "subtle-encoding", "tendermint-proto 0.28.0", "time", @@ -5120,7 +5234,7 @@ dependencies = [ "serde", "serde_bytes", "serde_json", - "subtle", + "subtle 2.4.1", "subtle-encoding", "tendermint 0.28.0", "tendermint-config 0.28.0", @@ -5231,6 +5345,24 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-bip39" +version = "0.8.2" +source = "git+https://github.com/anoma/tiny-bip39.git?rev=bf0f6d8713589b83af7a917366ec31f5275c0e57#bf0f6d8713589b83af7a917366ec31f5275c0e57" +dependencies = [ + "anyhow", + "hmac 0.8.1", + "once_cell", + "pbkdf2 0.4.0", + "rand 0.7.3", + "rustc-hash", + "sha2 0.9.9", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + [[package]] name = "tiny-bip39" version = "1.0.0" @@ -5250,13 +5382,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "tiny-hderive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01b874a4992538d4b2f4fbbac11b9419d685f4b39bdc3fed95b04e07bfd76040" +dependencies = [ + "base58", + "hmac 0.7.1", + "libsecp256k1 0.3.5", + "memzero", + "sha2 0.8.2", +] + [[package]] name = "tiny-keccak" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" dependencies = [ - "crunchy 0.2.2", + "crunchy", ] [[package]] @@ -5577,7 +5722,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ "byteorder", - "crunchy 0.2.2", + "crunchy", "hex", "static_assertions", ] @@ -5632,12 +5777,12 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "universal-hash" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" dependencies = [ - "generic-array", - "subtle", + "crypto-common", + "subtle 2.4.1", ] [[package]] @@ -6358,83 +6503,10 @@ dependencies = [ [[package]] name = "zcash_encoding" version = "0.0.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" -dependencies = [ - "byteorder", - "nonempty", -] - -[[package]] -name = "zcash_note_encryption" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33f84ae538f05a8ac74c82527f06b77045ed9553a0871d9db036166a4c344e3a" -dependencies = [ - "chacha20", - "chacha20poly1305", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "zcash_note_encryption" -version = "0.1.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" -dependencies = [ - "chacha20", - "chacha20poly1305", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "zcash_primitives" -version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" +source = "git+https://github.com/zcash/librustzcash?rev=43c18d0#43c18d000fcbe45363b2d53585d5102841eff99e" dependencies = [ - "aes", - "bip0039", - "bitvec 0.22.3", - "blake2b_simd 1.0.1", - "blake2s_simd 1.0.1", - "bls12_381", "byteorder", - "chacha20poly1305", - "equihash", - "ff 0.11.1", - "fpe", - "group 0.11.0", - "hex", - "incrementalmerkletree", - "jubjub", - "lazy_static", - "memuse", "nonempty", - "orchard", - "rand 0.8.5", - "rand_core 0.6.4", - "sha2 0.9.9", - "subtle", - "zcash_encoding", - "zcash_note_encryption 0.1.0 (git+https://github.com/zcash/librustzcash/?rev=2425a08)", -] - -[[package]] -name = "zcash_proofs" -version = "0.5.0" -source = "git+https://github.com/zcash/librustzcash/?rev=2425a08#2425a0869098e3b0588ccd73c42716bcf418612c" -dependencies = [ - "bellman", - "blake2b_simd 1.0.1", - "bls12_381", - "byteorder", - "directories", - "ff 0.11.1", - "group 0.11.0", - "jubjub", - "lazy_static", - "rand_core 0.6.4", - "zcash_primitives", ] [[package]] diff --git a/wasm_for_tests/wasm_source/Cargo.toml b/wasm_for_tests/wasm_source/Cargo.toml index 417120eea2..4aa1e2960a 100644 --- a/wasm_for_tests/wasm_source/Cargo.toml +++ b/wasm_for_tests/wasm_source/Cargo.toml @@ -47,8 +47,8 @@ tendermint-testgen = {git = "https://github.com/heliaxdev/tendermint-rs.git", re tendermint-light-client-verifier = {git = "https://github.com/heliaxdev/tendermint-rs.git", rev = "02b256829e80f8cfecf3fa0d625c2a76c79cd043"} # patched to a commit on the `eth-bridge-integration` branch of our fork -ibc = {git = "https://github.com/heliaxdev/cosmos-ibc-rs.git", rev = "e71bc2cc79f8c2b32e970d95312f251398c93d9e"} -ibc-proto = {git = "https://github.com/heliaxdev/ibc-proto-rs.git", rev = "6f4038fcf4981f1ed70771d1cd89931267f917af"} +ibc = {git = "https://github.com/heliaxdev/cosmos-ibc-rs.git", rev = "4ec09135f5ef669144ef48c0a111a6428164c9ed"} +ibc-proto = {git = "https://github.com/heliaxdev/ibc-proto-rs.git", rev = "19ad1cdfda14e95bd45fba0e0c710c086f71d0bc"} # patched to the yanked 1.2.0 until masp updates bitvec funty = { git = "https://github.com/bitvecto-rs/funty/", rev = "7ef0d890fbcd8b3def1635ac1a877fc298488446" } diff --git a/wasm_for_tests/wasm_source/src/lib.rs b/wasm_for_tests/wasm_source/src/lib.rs index f271acc97a..a37039e9c4 100644 --- a/wasm_for_tests/wasm_source/src/lib.rs +++ b/wasm_for_tests/wasm_source/src/lib.rs @@ -4,7 +4,7 @@ pub mod main { use namada_tx_prelude::*; #[transaction] - fn apply_tx(_ctx: &mut Ctx, _tx_data: Vec) -> TxResult { + fn apply_tx(_ctx: &mut Ctx, _tx_data: Tx) -> TxResult { Ok(()) } } @@ -15,8 +15,8 @@ pub mod main { use namada_tx_prelude::*; #[transaction] - fn apply_tx(_ctx: &mut Ctx, tx_data: Vec) -> TxResult { - let len = usize::try_from_slice(&tx_data[..]).unwrap(); + fn apply_tx(_ctx: &mut Ctx, tx_data: Tx) -> TxResult { + let len = usize::try_from_slice(&tx_data.data().as_ref().unwrap()[..]).unwrap(); log_string(format!("allocate len {}", len)); let bytes: Vec = vec![6_u8; len]; // use the variable to prevent it from compiler optimizing it away @@ -31,7 +31,7 @@ pub mod main { use namada_tx_prelude::*; #[transaction] - fn apply_tx(ctx: &mut Ctx, _tx_data: Vec) -> TxResult { + fn apply_tx(ctx: &mut Ctx, _tx_data: Tx) -> TxResult { // governance let target_key = gov_storage::get_min_proposal_grace_epoch_key(); ctx.write(&target_key, 9_u64)?; @@ -49,9 +49,9 @@ pub mod main { use namada_tx_prelude::*; #[transaction] - fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { + fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { // Allocates a memory of size given from the `tx_data (usize)` - let key = storage::Key::try_from_slice(&tx_data[..]).unwrap(); + let key = storage::Key::try_from_slice(&tx_data.data().as_ref().unwrap()[..]).unwrap(); log_string(format!("key {}", key)); let _result: Vec = ctx.read(&key)?.unwrap(); Ok(()) @@ -64,8 +64,7 @@ pub mod main { use borsh::BorshDeserialize; use namada_test_utils::tx_data::TxWriteData; use namada_tx_prelude::{ - log_string, transaction, Ctx, ResultExt, SignedTxData, StorageRead, - StorageWrite, TxResult, + log_string, transaction, Ctx, StorageRead, StorageWrite, TxResult, Tx, }; const TX_NAME: &str = "tx_write"; @@ -85,10 +84,9 @@ pub mod main { } #[transaction] - fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { - let signed = SignedTxData::try_from_slice(&tx_data[..]) - .wrap_err("failed to decode SignedTxData")?; - let data = match signed.data { + fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { + let signed = tx_data; + let data = match signed.data() { Some(data) => { log(&format!("got data ({} bytes)", data.len())); data @@ -134,11 +132,10 @@ pub mod main { use namada_tx_prelude::*; #[transaction] - fn apply_tx(ctx: &mut Ctx, tx_data: Vec) -> TxResult { - let signed = SignedTxData::try_from_slice(&tx_data[..]) - .wrap_err("failed to decode SignedTxData")?; + fn apply_tx(ctx: &mut Ctx, tx_data: Tx) -> TxResult { + let signed = tx_data; let transfer = - token::Transfer::try_from_slice(&signed.data.unwrap()[..]).unwrap(); + token::Transfer::try_from_slice(&signed.data().unwrap()[..]).unwrap(); log_string(format!("apply_tx called to mint tokens: {:#?}", transfer)); let token::Transfer { source: _, @@ -166,7 +163,7 @@ pub mod main { #[validity_predicate] fn validate_tx( _ctx: &Ctx, - _tx_data: Vec, + _tx_data: Tx, _addr: Address, _keys_changed: BTreeSet, _verifiers: BTreeSet
, @@ -183,7 +180,7 @@ pub mod main { #[validity_predicate] fn validate_tx( _ctx: &Ctx, - _tx_data: Vec, + _tx_data: Tx, _addr: Address, _keys_changed: BTreeSet, _verifiers: BTreeSet
, @@ -201,14 +198,14 @@ pub mod main { #[validity_predicate] fn validate_tx( ctx: &Ctx, - tx_data: Vec, + tx_data: Tx, _addr: Address, _keys_changed: BTreeSet, _verifiers: BTreeSet
, ) -> VpResult { use validity_predicate::EvalVp; let EvalVp { vp_code_hash, input }: EvalVp = - EvalVp::try_from_slice(&tx_data[..]).unwrap(); + EvalVp::try_from_slice(&tx_data.data().as_ref().unwrap()[..]).unwrap(); ctx.eval(vp_code_hash, input) } } @@ -222,12 +219,12 @@ pub mod main { #[validity_predicate] fn validate_tx( _ctx: &Ctx, - tx_data: Vec, + tx_data: Tx, _addr: Address, _keys_changed: BTreeSet, _verifiers: BTreeSet
, ) -> VpResult { - let len = usize::try_from_slice(&tx_data[..]).unwrap(); + let len = usize::try_from_slice(&tx_data.data().as_ref().unwrap()[..]).unwrap(); log_string(format!("allocate len {}", len)); let bytes: Vec = vec![6_u8; len]; // use the variable to prevent it from compiler optimizing it away @@ -245,13 +242,13 @@ pub mod main { #[validity_predicate] fn validate_tx( ctx: &Ctx, - tx_data: Vec, + tx_data: Tx, _addr: Address, _keys_changed: BTreeSet, _verifiers: BTreeSet
, ) -> VpResult { // Allocates a memory of size given from the `tx_data (usize)` - let key = storage::Key::try_from_slice(&tx_data[..]).unwrap(); + let key = storage::Key::try_from_slice(&tx_data.data().as_ref().unwrap()[..]).unwrap(); log_string(format!("key {}", key)); let _result: Vec = ctx.read_pre(&key)?.unwrap(); accept()