diff --git a/.changelog/v0.15.4/bug-fixes/1405-bugfix-replay-prepare.md b/.changelog/v0.15.4/bug-fixes/1405-bugfix-replay-prepare.md new file mode 100644 index 0000000000..a32b40e2b3 --- /dev/null +++ b/.changelog/v0.15.4/bug-fixes/1405-bugfix-replay-prepare.md @@ -0,0 +1,3 @@ +- Fixed a bug in `prepare_proposal` causing the creation + of blocks containing already applied transactions. + ([#1405](https://github.com/anoma/namada/pull/1405)) \ No newline at end of file diff --git a/.changelog/v0.15.4/improvements/1399-consensus_params.md b/.changelog/v0.15.4/improvements/1399-consensus_params.md new file mode 100644 index 0000000000..5e2ee441c0 --- /dev/null +++ b/.changelog/v0.15.4/improvements/1399-consensus_params.md @@ -0,0 +1,2 @@ +- Make Tendermint consensus paramenters configurable via Namada configuration. + ([#1399](https://github.com/anoma/namada/pull/1399)) \ No newline at end of file diff --git a/.changelog/v0.15.4/improvements/1407-tx-validation-log.md b/.changelog/v0.15.4/improvements/1407-tx-validation-log.md new file mode 100644 index 0000000000..5d74828736 --- /dev/null +++ b/.changelog/v0.15.4/improvements/1407-tx-validation-log.md @@ -0,0 +1,2 @@ +- Improved error logs in `process_proposal` and added more info to + `InternalStats` ([#1407](https://github.com/anoma/namada/pull/1407)) \ No newline at end of file diff --git a/.changelog/v0.15.4/summary.md b/.changelog/v0.15.4/summary.md new file mode 100644 index 0000000000..c5f4bc8610 --- /dev/null +++ b/.changelog/v0.15.4/summary.md @@ -0,0 +1,2 @@ +Namada 0.15.4 is a maintenance release addressing the invalid creation of blocks due to missing replay protection checks during prepare +proposal. diff --git a/.changelog/v0.16.0/improvements/925-shared-sdk-integration.md b/.changelog/v0.16.0/improvements/925-shared-sdk-integration.md new file mode 100644 index 0000000000..49514ba8f7 --- /dev/null +++ b/.changelog/v0.16.0/improvements/925-shared-sdk-integration.md @@ -0,0 +1,3 @@ +- Provide Namada SDK (in particular, the `namada` +crate may now be usefully linked into user +applications). ([#925](https://github.com/anoma/namada/pull/925)) diff --git a/.changelog/v0.17.1/bug-fixes/1455-persist-epoch-update-delay.md b/.changelog/v0.17.1/bug-fixes/1455-persist-epoch-update-delay.md new file mode 100644 index 0000000000..7560aa3f4c --- /dev/null +++ b/.changelog/v0.17.1/bug-fixes/1455-persist-epoch-update-delay.md @@ -0,0 +1,3 @@ +- Persists a newly added storage field for epoch update blocks delay to be + available after node restart when not `None` which may break consensus. + ([\#1455](https://github.com/anoma/namada/pull/1455)) \ No newline at end of file diff --git a/.changelog/v0.17.1/bug-fixes/1456-fix-max-wait-tries.md b/.changelog/v0.17.1/bug-fixes/1456-fix-max-wait-tries.md new file mode 100644 index 0000000000..fdf18ff5ec --- /dev/null +++ b/.changelog/v0.17.1/bug-fixes/1456-fix-max-wait-tries.md @@ -0,0 +1,2 @@ +- Client: Fixed an off-by-one error to stop waiting for start or catch-up when + max tries are reached. ([\#1456](https://github.com/anoma/namada/pull/1456)) \ No newline at end of file diff --git a/.changelog/v0.17.1/bug-fixes/1474-fix-sdk-tx-with-hash.md b/.changelog/v0.17.1/bug-fixes/1474-fix-sdk-tx-with-hash.md new file mode 100644 index 0000000000..991a67f952 --- /dev/null +++ b/.changelog/v0.17.1/bug-fixes/1474-fix-sdk-tx-with-hash.md @@ -0,0 +1,2 @@ +- Include the wasm tx hash instead of the wasm blob when constructing a + transaction ([#1474](https://github.com/anoma/namada/pull/1474)) \ No newline at end of file diff --git a/.changelog/v0.17.1/bug-fixes/1534-fix-block-query.md b/.changelog/v0.17.1/bug-fixes/1534-fix-block-query.md new file mode 100644 index 0000000000..8bfe764e11 --- /dev/null +++ b/.changelog/v0.17.1/bug-fixes/1534-fix-block-query.md @@ -0,0 +1,2 @@ +- Fix a client block query to avoid seeing pre-committed blocks. + ([\#1534](https://github.com/anoma/namada/pull/1534)) \ No newline at end of file diff --git a/.changelog/v0.17.1/docs/889-gas-and-fee-specs.md b/.changelog/v0.17.1/docs/889-gas-and-fee-specs.md new file mode 100644 index 0000000000..e6a4021cf8 --- /dev/null +++ b/.changelog/v0.17.1/docs/889-gas-and-fee-specs.md @@ -0,0 +1 @@ +- Adds specs for gas and fee ([#889](https://github.com/anoma/namada/pull/889)) \ No newline at end of file diff --git a/.changelog/v0.17.1/features/1110-wallet-deterministic.md b/.changelog/v0.17.1/features/1110-wallet-deterministic.md new file mode 100644 index 0000000000..d9551b5ea6 --- /dev/null +++ b/.changelog/v0.17.1/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/v0.17.1/features/1344-find-validator-by-tm.md b/.changelog/v0.17.1/features/1344-find-validator-by-tm.md new file mode 100644 index 0000000000..f0e923c31a --- /dev/null +++ b/.changelog/v0.17.1/features/1344-find-validator-by-tm.md @@ -0,0 +1,3 @@ +- PoS: Added a client command `find-validator --tm-address
` + to find validator's Namada address by Tendermint address. + ([\#1344](https://github.com/anoma/namada/pull/1344)) \ No newline at end of file diff --git a/.changelog/v0.17.1/features/892-cubic-slashing.md b/.changelog/v0.17.1/features/892-cubic-slashing.md new file mode 100644 index 0000000000..cdd079bfc3 --- /dev/null +++ b/.changelog/v0.17.1/features/892-cubic-slashing.md @@ -0,0 +1,5 @@ +- The implementation of the cubic slashing system that touches virtually all + parts of the proof-of-stake system. Slashes tokens are currently kept in the + PoS address rather than being transferred to the Slash Pool address. This PR + also includes significant testing infrastructure, highlighted by the PoS state + machine test with slashing. ([#892](https://github.com/anoma/namada/pull/892)) \ No newline at end of file diff --git a/.changelog/v0.17.1/improvements/1093-signable-txs.md b/.changelog/v0.17.1/improvements/1093-signable-txs.md new file mode 100644 index 0000000000..e1e244bd0f --- /dev/null +++ b/.changelog/v0.17.1/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/v0.17.1/improvements/1238-optinal-multicore-feature.md b/.changelog/v0.17.1/improvements/1238-optinal-multicore-feature.md new file mode 100644 index 0000000000..ee1a0c1ba1 --- /dev/null +++ b/.changelog/v0.17.1/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/v0.17.1/improvements/1425-wallet-zeroize.md b/.changelog/v0.17.1/improvements/1425-wallet-zeroize.md new file mode 100644 index 0000000000..ab32b10f65 --- /dev/null +++ b/.changelog/v0.17.1/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/.changelog/v0.17.1/improvements/1432-missing-args-def.md b/.changelog/v0.17.1/improvements/1432-missing-args-def.md new file mode 100644 index 0000000000..9c49f92e49 --- /dev/null +++ b/.changelog/v0.17.1/improvements/1432-missing-args-def.md @@ -0,0 +1,2 @@ +- Added some missing cli option for cli wallet + ([#1432](https://github.com/anoma/namada/pull/1432)) \ No newline at end of file diff --git a/.changelog/v0.17.1/improvements/1434-improve-cli-commission-rate.md b/.changelog/v0.17.1/improvements/1434-improve-cli-commission-rate.md new file mode 100644 index 0000000000..e5adfe06db --- /dev/null +++ b/.changelog/v0.17.1/improvements/1434-improve-cli-commission-rate.md @@ -0,0 +1,2 @@ +- Improve logging error when submiting an invalid validator commission change tx + ([#1434](https://github.com/anoma/namada/pull/1434)) \ No newline at end of file diff --git a/.changelog/v0.17.1/improvements/1435-fix-err-typos.md b/.changelog/v0.17.1/improvements/1435-fix-err-typos.md new file mode 100644 index 0000000000..60e80421e5 --- /dev/null +++ b/.changelog/v0.17.1/improvements/1435-fix-err-typos.md @@ -0,0 +1,2 @@ +- Correct a typo in the error change commission error handling + ([#1435](https://github.com/anoma/namada/pull/1435)) \ No newline at end of file diff --git a/.changelog/v0.17.1/improvements/1436-rename-tx-reveal-arg.md b/.changelog/v0.17.1/improvements/1436-rename-tx-reveal-arg.md new file mode 100644 index 0000000000..3cb5fdc29b --- /dev/null +++ b/.changelog/v0.17.1/improvements/1436-rename-tx-reveal-arg.md @@ -0,0 +1,2 @@ +- Improve the reveal tx naming in cli + ([#1436](https://github.com/anoma/namada/pull/1436)) \ No newline at end of file diff --git a/.changelog/v0.17.1/improvements/1444-cse-in-inflation.md b/.changelog/v0.17.1/improvements/1444-cse-in-inflation.md new file mode 100644 index 0000000000..f5a89ac9b2 --- /dev/null +++ b/.changelog/v0.17.1/improvements/1444-cse-in-inflation.md @@ -0,0 +1,2 @@ +- Improve computations readability when calculating inflations + ([#1444](https://github.com/anoma/namada/pull/1444)) \ No newline at end of file diff --git a/.changelog/v0.17.1/improvements/1449-rm-abciplus-cargo-deps.md b/.changelog/v0.17.1/improvements/1449-rm-abciplus-cargo-deps.md new file mode 100644 index 0000000000..91527ebfb2 --- /dev/null +++ b/.changelog/v0.17.1/improvements/1449-rm-abciplus-cargo-deps.md @@ -0,0 +1 @@ +- Remove abci++ dependencies ([#1449](https://github.com/anoma/namada/pull/1449)) \ No newline at end of file diff --git a/.changelog/v0.17.1/improvements/1462-restructure-tx-draft.md b/.changelog/v0.17.1/improvements/1462-restructure-tx-draft.md new file mode 100644 index 0000000000..7fc3dca855 --- /dev/null +++ b/.changelog/v0.17.1/improvements/1462-restructure-tx-draft.md @@ -0,0 +1,2 @@ +- Reorganize the structure of transactions + ([#1462](https://github.com/anoma/namada/pull/1462)) \ No newline at end of file diff --git a/.changelog/v0.17.1/improvements/1509-improve-pos-logging.md b/.changelog/v0.17.1/improvements/1509-improve-pos-logging.md new file mode 100644 index 0000000000..010d881000 --- /dev/null +++ b/.changelog/v0.17.1/improvements/1509-improve-pos-logging.md @@ -0,0 +1,2 @@ +- Improved log entries related to PoS system. + ([\#1509](https://github.com/anoma/namada/pull/1509)) \ No newline at end of file diff --git a/.changelog/v0.17.1/summary.md b/.changelog/v0.17.1/summary.md new file mode 100644 index 0000000000..0491959a0a --- /dev/null +++ b/.changelog/v0.17.1/summary.md @@ -0,0 +1,2 @@ +Namada 0.17.0 is a scheduled minor release featuring several improvements to the slashing mechanism, +wallet address derivation, transaction structure and the ledger stability. diff --git a/.changelog/v0.17.2/bug-fixes/1504-wallet-address-add-fix.md b/.changelog/v0.17.2/bug-fixes/1504-wallet-address-add-fix.md new file mode 100644 index 0000000000..18420800a1 --- /dev/null +++ b/.changelog/v0.17.2/bug-fixes/1504-wallet-address-add-fix.md @@ -0,0 +1,2 @@ +- Do not add address if it already exists in the wallet. + ([\#1504](https://github.com/anoma/namada/issues/1504)) diff --git a/.changelog/v0.17.2/bug-fixes/1520-fix-sum-post-bonds-slashing.md b/.changelog/v0.17.2/bug-fixes/1520-fix-sum-post-bonds-slashing.md new file mode 100644 index 0000000000..bed4b5f534 --- /dev/null +++ b/.changelog/v0.17.2/bug-fixes/1520-fix-sum-post-bonds-slashing.md @@ -0,0 +1,5 @@ +- When processing slashes, bonds and unbonds that became active after + the infraction epoch must be properly accounted in order to properly + deduct stake that accounts for the precise slash amount. A bug + is fixed in the procedure that properly performs this accounting. + ([#1520](https://github.com/anoma/namada/pull/1520)) \ No newline at end of file diff --git a/.changelog/v0.17.2/bug-fixes/1522-client-fix-wait-msg.md b/.changelog/v0.17.2/bug-fixes/1522-client-fix-wait-msg.md new file mode 100644 index 0000000000..0a082cd560 --- /dev/null +++ b/.changelog/v0.17.2/bug-fixes/1522-client-fix-wait-msg.md @@ -0,0 +1,2 @@ +- Fix the message when a client is waiting for a node to sync on queries or + transactions. ([\#1522](https://github.com/anoma/namada/pull/1522)) \ No newline at end of file diff --git a/.changelog/v0.17.2/bug-fixes/1524-fix-dry-run-header-and-prover.md b/.changelog/v0.17.2/bug-fixes/1524-fix-dry-run-header-and-prover.md new file mode 100644 index 0000000000..d1baea14a6 --- /dev/null +++ b/.changelog/v0.17.2/bug-fixes/1524-fix-dry-run-header-and-prover.md @@ -0,0 +1,2 @@ +- This change will enable usage of the Namada SDK to create MASP transactions + from non-CLI clients. ([\#1524](https://github.com/anoma/namada/pull/1524)) \ No newline at end of file diff --git a/.changelog/v0.17.2/bug-fixes/1528-balances-fix-issue-758.md b/.changelog/v0.17.2/bug-fixes/1528-balances-fix-issue-758.md new file mode 100644 index 0000000000..3ab09e8210 --- /dev/null +++ b/.changelog/v0.17.2/bug-fixes/1528-balances-fix-issue-758.md @@ -0,0 +1,2 @@ +- Fixing how token balances are displayed in case of missing --token option. + ([#1528](https://github.com/anoma/namada/pull/1528)) \ No newline at end of file diff --git a/.changelog/v0.17.2/bug-fixes/1533-fix-slashing-client-query.md b/.changelog/v0.17.2/bug-fixes/1533-fix-slashing-client-query.md new file mode 100644 index 0000000000..24b3646264 --- /dev/null +++ b/.changelog/v0.17.2/bug-fixes/1533-fix-slashing-client-query.md @@ -0,0 +1,3 @@ +- The slashed token amounts contained inside the bond and unbond information + returned by the PoS library fn bonds_and_unbonds are fixed and properly + computed. ([#1533](https://github.com/anoma/namada/pull/1533)) \ No newline at end of file diff --git a/.changelog/v0.17.2/bug-fixes/1549-fix-init-validator-tm-mode.md b/.changelog/v0.17.2/bug-fixes/1549-fix-init-validator-tm-mode.md new file mode 100644 index 0000000000..33000a423d --- /dev/null +++ b/.changelog/v0.17.2/bug-fixes/1549-fix-init-validator-tm-mode.md @@ -0,0 +1,3 @@ +- PoS: Fixed the client to change configuration to validator + mode after a successful `init-validator` transaction. + ([\#1549](https://github.com/anoma/namada/pull/1549)) \ No newline at end of file diff --git a/.changelog/v0.17.2/bug-fixes/1553-fix-is-validator-fn.md b/.changelog/v0.17.2/bug-fixes/1553-fix-is-validator-fn.md new file mode 100644 index 0000000000..c66e587913 --- /dev/null +++ b/.changelog/v0.17.2/bug-fixes/1553-fix-is-validator-fn.md @@ -0,0 +1,3 @@ +- PoS: fixed a check for whether a given address belongs to a + validator account to work properly with newly created accounts. + ([\#1553](https://github.com/anoma/namada/pull/1553)) \ No newline at end of file diff --git a/.changelog/v0.17.2/bug-fixes/1558-fix-slash-logging.md b/.changelog/v0.17.2/bug-fixes/1558-fix-slash-logging.md new file mode 100644 index 0000000000..3b3df8cce3 --- /dev/null +++ b/.changelog/v0.17.2/bug-fixes/1558-fix-slash-logging.md @@ -0,0 +1,3 @@ +- Fixes the slash rate output in the query_slashes client + command and some redundancy in misbehavior reporting logs. + ([#1558](https://github.com/anoma/namada/pull/1558)) \ No newline at end of file diff --git a/.changelog/v0.17.2/improvements/1491-utils-base-dir.md b/.changelog/v0.17.2/improvements/1491-utils-base-dir.md new file mode 100644 index 0000000000..6ec06cb49a --- /dev/null +++ b/.changelog/v0.17.2/improvements/1491-utils-base-dir.md @@ -0,0 +1,4 @@ +- Add a command, `namadac utils default-base-dir`, to + print the default base directory the command + line would use were one not provided by the user. + ([#1491](https://github.com/anoma/namada/pull/1491)) diff --git a/.changelog/v0.17.2/improvements/1510-established-addr-bytes.md b/.changelog/v0.17.2/improvements/1510-established-addr-bytes.md new file mode 100644 index 0000000000..4f25dfff9f --- /dev/null +++ b/.changelog/v0.17.2/improvements/1510-established-addr-bytes.md @@ -0,0 +1,3 @@ +- Improve the established address in-memory representation + and use a full SHA-256 digest for their generation. + ([\#1510](https://github.com/anoma/namada/pull/1510)) \ No newline at end of file diff --git a/.changelog/v0.17.2/improvements/1512-implicit-addr-bytes.md b/.changelog/v0.17.2/improvements/1512-implicit-addr-bytes.md new file mode 100644 index 0000000000..250aa01d77 --- /dev/null +++ b/.changelog/v0.17.2/improvements/1512-implicit-addr-bytes.md @@ -0,0 +1,2 @@ +- Improve the implicit address and PKH in-memory representation. + ([\#1512](https://github.com/anoma/namada/pull/1512)) \ No newline at end of file diff --git a/.changelog/v0.17.2/improvements/1514-wallet-message-fix.md b/.changelog/v0.17.2/improvements/1514-wallet-message-fix.md new file mode 100644 index 0000000000..b50a9149f6 --- /dev/null +++ b/.changelog/v0.17.2/improvements/1514-wallet-message-fix.md @@ -0,0 +1,2 @@ +- Improve help message for address add command + ([\#1514](https://github.com/anoma/namada/issues/1514)) \ No newline at end of file diff --git a/.changelog/v0.17.2/improvements/1518-pos-bonds-query-reuse.md b/.changelog/v0.17.2/improvements/1518-pos-bonds-query-reuse.md new file mode 100644 index 0000000000..84f7a75e63 --- /dev/null +++ b/.changelog/v0.17.2/improvements/1518-pos-bonds-query-reuse.md @@ -0,0 +1,2 @@ +- PoS: make a re-usable bonds and unbonds details query. + ([\#1518](https://github.com/anoma/namada/pull/1518)) \ No newline at end of file diff --git a/.changelog/v0.17.2/summary.md b/.changelog/v0.17.2/summary.md new file mode 100644 index 0000000000..858698bd0b --- /dev/null +++ b/.changelog/v0.17.2/summary.md @@ -0,0 +1,2 @@ +Namada 0.17.2 is a minor release featuring improvements to the client stability. + diff --git a/.changelog/v0.17.3/bug-fixes/1345-key-parsing-fix.md b/.changelog/v0.17.3/bug-fixes/1345-key-parsing-fix.md new file mode 100644 index 0000000000..e7d8f19ee8 --- /dev/null +++ b/.changelog/v0.17.3/bug-fixes/1345-key-parsing-fix.md @@ -0,0 +1,2 @@ +- Correctly handle parsing storage key if they are empty. + ([#1345](https://github.com/anoma/namada/pull/1345)) \ No newline at end of file diff --git a/.changelog/v0.17.3/features/1570-tendermint-config.md b/.changelog/v0.17.3/features/1570-tendermint-config.md new file mode 100644 index 0000000000..888fda8928 --- /dev/null +++ b/.changelog/v0.17.3/features/1570-tendermint-config.md @@ -0,0 +1,2 @@ +- Enable users to change any tendermint config options via namada config. + ([#1570](https://github.com/anoma/namada/pull/1570)) \ No newline at end of file diff --git a/.changelog/v0.17.3/improvements/1404-ibc-event-query.md b/.changelog/v0.17.3/improvements/1404-ibc-event-query.md new file mode 100644 index 0000000000..8e1c096f11 --- /dev/null +++ b/.changelog/v0.17.3/improvements/1404-ibc-event-query.md @@ -0,0 +1,2 @@ +- Added query endpoint for IBC events replacing Tendermint index. + ([\#1404](https://github.com/anoma/namada/pull/1404)) \ No newline at end of file diff --git a/.changelog/v0.17.3/miscellaneous/1476-cometbft.md b/.changelog/v0.17.3/miscellaneous/1476-cometbft.md new file mode 100644 index 0000000000..a1a23432ac --- /dev/null +++ b/.changelog/v0.17.3/miscellaneous/1476-cometbft.md @@ -0,0 +1,2 @@ +- Switch from unreleased Tendermint fork to an official CometBFT release + v0.37.1. ([\#1476](https://github.com/anoma/namada/pull/1476)) \ No newline at end of file diff --git a/.changelog/v0.17.3/summary.md b/.changelog/v0.17.3/summary.md new file mode 100644 index 0000000000..2bfbb16476 --- /dev/null +++ b/.changelog/v0.17.3/summary.md @@ -0,0 +1 @@ +Namada 0.17.3 is a minor release switching from tendermint to cometbft. diff --git a/.changelog/v0.17.4/bug-fixes/1588-fix-masp-missing-await.md b/.changelog/v0.17.4/bug-fixes/1588-fix-masp-missing-await.md new file mode 100644 index 0000000000..79a6f32b21 --- /dev/null +++ b/.changelog/v0.17.4/bug-fixes/1588-fix-masp-missing-await.md @@ -0,0 +1,2 @@ +- Fix missing async awaits in MASP load and save calls. + ([\#1588](https://github.com/anoma/namada/pull/1588)) \ No newline at end of file diff --git a/.changelog/v0.17.4/summary.md b/.changelog/v0.17.4/summary.md new file mode 100644 index 0000000000..f2074f1311 --- /dev/null +++ b/.changelog/v0.17.4/summary.md @@ -0,0 +1 @@ +Namada 0.17.4 is a minor release improving the codebase by bumping the rust toolchain. diff --git a/.changelog/v0.17.5/improvements/1619-masp-checked.md b/.changelog/v0.17.5/improvements/1619-masp-checked.md new file mode 100644 index 0000000000..c862efa193 --- /dev/null +++ b/.changelog/v0.17.5/improvements/1619-masp-checked.md @@ -0,0 +1,2 @@ +- Check MASP parameters are correct in the ledger node. + ([#1619](https://github.com/anoma/namada/pull/1619)) \ No newline at end of file diff --git a/.changelog/v0.17.5/summary.md b/.changelog/v0.17.5/summary.md new file mode 100644 index 0000000000..f05b6a2973 --- /dev/null +++ b/.changelog/v0.17.5/summary.md @@ -0,0 +1,2 @@ +Namada 0.17.5 is a maintenance release chiefly addressing MASP +parameter validation. diff --git a/.github/workflows/build-and-test-bridge.yml b/.github/workflows/build-and-test-bridge.yml index 6520413e92..c6bf2ead2f 100644 --- a/.github/workflows/build-and-test-bridge.yml +++ b/.github/workflows/build-and-test-bridge.yml @@ -36,7 +36,7 @@ jobs: matrix: os: [ubuntu-20.04] wasm_cache_version: ["v2"] - mold_version: [1.7.0] + mold_version: [1.11.0] steps: - name: Checkout repo @@ -78,7 +78,7 @@ jobs: matrix: os: [ubuntu-20.04] wasm_cache_version: ["v2"] - mold_version: [1.7.0] + mold_version: [1.11.0] steps: - name: Checkout repo @@ -144,14 +144,13 @@ jobs: fail-fast: false matrix: os: [ubuntu-20.04] - nightly_version: [nightly-2022-11-03] - mold_version: [1.7.0] + nightly_version: [nightly-2023-06-01] + mold_version: [1.11.0] make: - name: ABCI suffix: '' cache_key: namada cache_version: v2 - tendermint_artifact: tendermint-unreleased-v0.1.4-abciplus env: CARGO_INCREMENTAL: 0 @@ -251,7 +250,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-20.04] - mold_version: [1.7.0] + mold_version: [1.11.0] make: - name: ABCI Release build suffix: '' @@ -324,7 +323,7 @@ jobs: - name: Build run: make build-release${{ matrix.make.suffix }} env: - RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" + RUSTFLAGS: "-C linker=clang -C debug_assertions=true -C link-arg=-fuse-ld=/usr/local/bin/mold" - name: Upload target binaries uses: actions/upload-artifact@v3 with: @@ -357,22 +356,20 @@ jobs: fail-fast: false matrix: os: [ubuntu-20.04] - nightly_version: [nightly-2022-11-03] - mold_version: [1.7.0] + nightly_version: [nightly-2023-06-01] + mold_version: [1.11.0] make: - name: e2e suffix: '' index: 0 cache_key: namada cache_version: v2 - tendermint_artifact: tendermint-unreleased-v0.1.4-abciplus wait_for: namada-release-eth (ubuntu-20.04, 1.7.0, ABCI Release build, namada-e2e-release, v2) - name: e2e suffix: '' index: 1 cache_key: namada cache_version: v2 - tendermint_artifact: tendermint-unreleased-v0.1.4-abciplus wait_for: namada-release-eth (ubuntu-20.04, 1.7.0, ABCI Release build, namada-e2e-release, v2) env: @@ -443,20 +440,6 @@ jobs: run: | wget -q -O- https://github.com/rui314/mold/releases/download/v${{ matrix.mold_version }}/mold-${{ matrix.mold_version }}-x86_64-linux.tar.gz | tar -xz mv mold-${{ matrix.mold_version }}-x86_64-linux/bin/mold /usr/local/bin - - name: Download tendermint binaries - uses: dawidd6/action-download-artifact@v2 - with: - github_token: ${{secrets.GITHUB_TOKEN}} - workflow: build-tendermint.yml - workflow_conclusion: success - name: ${{ matrix.make.tendermint_artifact }} - path: /usr/local/bin - - name: Download masp parameters - run: | - mkdir /home/runner/work/masp - curl -o /home/runner/work/masp/masp-spend.params -sLO https://github.com/anoma/masp/blob/ef0ef75e81696ff4428db775c654fbec1b39c21f/masp-spend.params?raw=true - curl -o /home/runner/work/masp/masp-output.params -sLO https://github.com/anoma/masp/blob/ef0ef75e81696ff4428db775c654fbec1b39c21f/masp-output.params?raw=true - curl -o /home/runner/work/masp/masp-convert.params -sLO https://github.com/anoma/masp/blob/ef0ef75e81696ff4428db775c654fbec1b39c21f/masp-convert.params?raw=true - name: Build run: make build env: @@ -483,6 +466,14 @@ jobs: with: name: binaries${{ matrix.make.suffix }}-${{ github.event.pull_request.head.sha || github.sha }} path: ./target/release/ + - name: Download tendermint & cometbft + run: | + curl -o cometbft.tar.gz -LO https://github.com/cometbft/cometbft/releases/download/v0.37.2/cometbft_0.37.2_linux_amd64.tar.gz + tar -xvzf cometbft.tar.gz + mv cometbft /usr/local/bin + curl -o tendermint.tar.gz -LO https://github.com/heliaxdev/tendermint/releases/download/v0.1.4-abciplus/tendermint_0.1.4-abciplus_linux_amd64.tar.gz + tar -xvzf tendermint.tar.gz + mv tendermint /usr/local/bin - name: Change permissions run: | chmod +x target/release/namada @@ -490,15 +481,22 @@ jobs: chmod +x target/release/namadan chmod +x target/release/namadac chmod +x /usr/local/bin/tendermint + chmod +x /usr/local/bin/cometbft - name: Run e2e test - run: python3 .github/workflows/scripts/schedule-e2e.py + run: | + mkdir -p /home/runner/work/masp-params + curl -o /home/runner/work/masp-params/masp-spend.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-spend.params?raw=true + curl -o /home/runner/work/masp-params/masp-output.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-output.params?raw=true + curl -o /home/runner/work/masp-params/masp-convert.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-convert.params?raw=true + ls -l /home/runner/work/masp-params + shasum /home/runner/work/masp-params/*.params + python3 .github/workflows/scripts/schedule-e2e.py env: - NAMADA_TENDERMINT_WEBSOCKET_TIMEOUT: 20 NAMADA_E2E_USE_PREBUILT_BINARIES: "true" NAMADA_E2E_KEEP_TEMP: "true" NAMADA_TM_STDOUT: "false" NAMADA_LOG_COLOR: "false" - NAMADA_MASP_PARAMS_DIR: "/home/runner/work/masp" + # NAMADA_MASP_PARAMS_DIR: "/home/runner/work/masp-params" NAMADA_LOG: "info" RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" INDEX: ${{ matrix.make.index }} diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index b07e2212e9..783509d291 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -37,7 +37,7 @@ jobs: matrix: os: [ubuntu-20.04] wasm_cache_version: ["v2"] - mold_version: [1.7.0] + mold_version: [1.11.0] steps: - name: Checkout repo @@ -79,7 +79,8 @@ jobs: matrix: os: [ubuntu-20.04] wasm_cache_version: ["v2"] - mold_version: [1.7.0] + nightly_version: [nightly-2023-06-01] + mold_version: [1.11.0] steps: - name: Checkout repo @@ -103,6 +104,11 @@ jobs: path: ./wasm env: RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" + - name: Setup rust nightly + uses: oxidecomputer/actions-rs_toolchain@ad3f86084a8a5acf2c09cb691421b31cf8af7a36 + with: + toolchain: ${{ matrix.nightly_version }} + profile: default - name: Test Wasm run: make test-wasm - name: Check wasm up-to-date @@ -146,8 +152,8 @@ jobs: fail-fast: false matrix: os: [ubuntu-20.04] - nightly_version: [nightly-2022-11-03] - mold_version: [1.7.0] + nightly_version: [nightly-2023-06-01] + mold_version: [1.11.0] make: - name: ABCI suffix: '' @@ -235,6 +241,8 @@ jobs: name: wasm-${{ github.event.pull_request.head.sha|| github.sha }} path: ./wasm - uses: taiki-e/install-action@cargo-llvm-cov + - name: Check crates build with default features + run: make check-crates - name: Run unit test run: make test-unit-coverage${{ matrix.make.suffix }} env: @@ -259,7 +267,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-20.04] - mold_version: [1.7.0] + mold_version: [1.11.0] make: - name: ABCI Release build suffix: '' @@ -338,7 +346,7 @@ jobs: - name: Build run: make build-release${{ matrix.make.suffix }} env: - RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" + RUSTFLAGS: "-C linker=clang -C debug_assertions=true -C link-arg=-fuse-ld=/usr/local/bin/mold" - name: Upload target binaries uses: actions/upload-artifact@v3 with: @@ -366,12 +374,12 @@ jobs: namada-e2e: runs-on: ${{ matrix.os }} - timeout-minutes: 80 + # timeout-minutes: 80 strategy: fail-fast: false matrix: os: [ubuntu-20.04] - nightly_version: [nightly-2022-11-03] + nightly_version: [nightly-2023-06-01] mold_version: [1.7.0] make: - name: e2e @@ -380,14 +388,21 @@ jobs: cache_key: namada cache_version: v2 tendermint_artifact: tendermint-unreleased-v0.1.4-abciplus - wait_for: namada-release (ubuntu-20.04, 1.7.0, ABCI Release build, namada-e2e-release, v2) + wait_for: namada-release (ubuntu-20.04, 1.11.0, ABCI Release build, namada-e2e-release, v2) - name: e2e suffix: '' index: 1 cache_key: namada cache_version: v2 tendermint_artifact: tendermint-unreleased-v0.1.4-abciplus - wait_for: namada-release (ubuntu-20.04, 1.7.0, ABCI Release build, namada-e2e-release, v2) + wait_for: namada-release (ubuntu-20.04, 1.11.0, ABCI Release build, namada-e2e-release, v2) + - name: e2e + suffix: '' + index: 2 + cache_key: namada + cache_version: v2 + tendermint_artifact: tendermint-unreleased-v0.1.4-abciplus + wait_for: namada-release (ubuntu-20.04, 1.11.0, ABCI Release build, namada-e2e-release, v2) env: CARGO_INCREMENTAL: 0 @@ -463,20 +478,6 @@ jobs: run: | wget -q -O- https://github.com/rui314/mold/releases/download/v${{ matrix.mold_version }}/mold-${{ matrix.mold_version }}-x86_64-linux.tar.gz | tar -xz mv mold-${{ matrix.mold_version }}-x86_64-linux/bin/mold /usr/local/bin - - name: Download tendermint binaries - uses: dawidd6/action-download-artifact@v2 - with: - github_token: ${{secrets.GITHUB_TOKEN}} - workflow: build-tendermint.yml - workflow_conclusion: success - name: ${{ matrix.make.tendermint_artifact }} - path: /usr/local/bin - - name: Download masp parameters - run: | - mkdir /home/runner/work/masp - curl -o /home/runner/work/masp/masp-spend.params -sLO https://github.com/anoma/masp/blob/ef0ef75e81696ff4428db775c654fbec1b39c21f/masp-spend.params?raw=true - curl -o /home/runner/work/masp/masp-output.params -sLO https://github.com/anoma/masp/blob/ef0ef75e81696ff4428db775c654fbec1b39c21f/masp-output.params?raw=true - curl -o /home/runner/work/masp/masp-convert.params -sLO https://github.com/anoma/masp/blob/ef0ef75e81696ff4428db775c654fbec1b39c21f/masp-convert.params?raw=true - name: Build run: make build env: @@ -503,6 +504,14 @@ jobs: with: name: binaries${{ matrix.make.suffix }}-${{ github.event.pull_request.head.sha || github.sha }} path: ./target/release/ + - name: Download tendermint & cometbft + run: | + curl -o cometbft.tar.gz -LO https://github.com/cometbft/cometbft/releases/download/v0.37.2/cometbft_0.37.2_linux_amd64.tar.gz + tar -xvzf cometbft.tar.gz + mv cometbft /usr/local/bin + curl -o tendermint.tar.gz -LO https://github.com/heliaxdev/tendermint/releases/download/v0.1.4-abciplus/tendermint_0.1.4-abciplus_linux_amd64.tar.gz + tar -xvzf tendermint.tar.gz + mv tendermint /usr/local/bin - name: Change permissions run: | chmod +x target/release/namada @@ -510,15 +519,23 @@ jobs: chmod +x target/release/namadan chmod +x target/release/namadac chmod +x /usr/local/bin/tendermint + chmod +x /usr/local/bin/cometbft - name: Run e2e test - run: python3 .github/workflows/scripts/schedule-e2e.py + run: | + mkdir -p /home/runner/work/masp-params + curl -o /home/runner/work/masp-params/masp-spend.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-spend.params?raw=true + curl -o /home/runner/work/masp-params/masp-output.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-output.params?raw=true + curl -o /home/runner/work/masp-params/masp-convert.params -LO https://github.com/anoma/masp-mpc/releases/download/namada-trusted-setup/masp-convert.params?raw=true + ls -l /home/runner/work/masp-params + shasum /home/runner/work/masp-params/*.params + python3 .github/workflows/scripts/schedule-e2e.py env: NAMADA_TENDERMINT_WEBSOCKET_TIMEOUT: 20 NAMADA_E2E_USE_PREBUILT_BINARIES: "true" NAMADA_E2E_KEEP_TEMP: "true" NAMADA_TM_STDOUT: "false" NAMADA_LOG_COLOR: "false" - NAMADA_MASP_PARAMS_DIR: "/home/runner/work/masp" + # NAMADA_MASP_PARAMS_DIR: "/home/runner/work/masp-params" NAMADA_LOG: "info" RUSTFLAGS: "-C linker=clang -C link-arg=-fuse-ld=/usr/local/bin/mold" INDEX: ${{ matrix.make.index }} diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 681cf1b475..a55bb1efed 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -27,12 +27,8 @@ jobs: fail-fast: false matrix: os: [ubuntu-20.04] - nightly_version: [nightly-2022-11-03] + nightly_version: [nightly-2023-06-01] make: - - name: Check ABCI++ - command: check-abcipp - cache_subkey: abcipp - cache_version: v1 - name: Clippy command: clippy cache_subkey: clippy diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index c3fcec07dd..2151916622 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -20,7 +20,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-20.04] - nightly_version: [nightly-2022-11-03] + nightly_version: [nightly-2023-06-01] make: - name: Audit command: audit diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 731bc9cc01..986e278b19 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -27,7 +27,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-20.04] - nightly_version: [nightly-2022-11-03] + nightly_version: [nightly-2023-06-01] mdbook_version: [rust-lang/mdbook@v0.4.18] mdbook_mermaid: [badboy/mdbook-mermaid@v0.11.1] mdbook_linkcheck: [Michael-F-Bryan/mdbook-linkcheck@v0.7.6] @@ -36,20 +36,6 @@ jobs: mdbook_katex: [lzanini/mdbook-katex@v0.4.0] mdbook_pagetoc: [slowsage/mdbook-pagetoc@v0.1.7] make: - - name: Build specs - folder: documentation/specs - bucket: namada-specs-static-website - command: cd documentation/specs && mdbook build - cache_subkey: specs - cache_version: v1 - distribution_id: E2Y9R2H4P5LYED - - name: Build docs - folder: documentation/docs - bucket: namada-docs-static-website - command: cd documentation/docs && mdbook build - cache_subkey: docs - cache_version: v1 - distribution_id: E2T9UML53913RV - name: Build development docs folder: documentation/dev bucket: namada-dev-static-website @@ -116,13 +102,13 @@ jobs: run: sccache --start-server - name: Install cargo tool run: | - curl https://i.jpillora.com/${{ matrix.mdbook_version }}! | bash - curl https://i.jpillora.com/${{ matrix.mdbook_mermaid }}! | bash - curl https://i.jpillora.com/${{ matrix.mdbook_linkcheck }}! | bash - curl https://i.jpillora.com/${{ matrix.mdbook_open_on_gh }}! | bash - curl https://i.jpillora.com/${{ matrix.mdbook_admonish }}! | bash - curl https://i.jpillora.com/${{ matrix.mdbook_katex }}! | bash - curl https://i.jpillora.com/${{ matrix.mdbook_pagetoc }}! | bash + curl -k https://installer.heliax.click/${{ matrix.mdbook_version }}! | bash + curl -k https://installer.heliax.click/${{ matrix.mdbook_mermaid }}! | bash + curl -k https://installer.heliax.click/${{ matrix.mdbook_linkcheck }}! | bash + curl -k https://installer.heliax.click/${{ matrix.mdbook_open_on_gh }}! | bash + curl -k https://installer.heliax.click/${{ matrix.mdbook_admonish }}! | bash + curl -k https://installer.heliax.click/${{ matrix.mdbook_katex }}! | bash + curl -k https://installer.heliax.click/${{ matrix.mdbook_pagetoc }}! | bash cd ${{ matrix.make.folder }} && mdbook-admonish install - name: ${{ matrix.make.name }} run: ${{ matrix.make.command }} diff --git a/.github/workflows/scripts/e2e.json b/.github/workflows/scripts/e2e.json index a6900183dd..df2b75369c 100644 --- a/.github/workflows/scripts/e2e.json +++ b/.github/workflows/scripts/e2e.json @@ -1,24 +1,24 @@ { "e2e::eth_bridge_tests::everything": 4, - "e2e::ibc_tests::run_ledger_ibc": 140, + "e2e::ibc_tests::run_ledger_ibc": 155, "e2e::ledger_tests::double_signing_gets_slashed": 12, - "e2e::ledger_tests::invalid_transactions": 8, - "e2e::ledger_tests::ledger_many_txs_in_a_block": 41, - "e2e::ledger_tests::ledger_txs_and_queries": 11, - "e2e::ledger_tests::masp_incentives": 581, - "e2e::ledger_tests::masp_pinned_txs": 73, - "e2e::ledger_tests::masp_txs_and_queries": 245, - "e2e::ledger_tests::pos_bonds": 19, - "e2e::ledger_tests::pos_init_validator": 15, - "e2e::ledger_tests::proposal_offline": 15, - "e2e::ledger_tests::pgf_governance_proposal": 35, - "e2e::ledger_tests::eth_governance_proposal": 35, - "e2e::ledger_tests::proposal_submission": 35, + "e2e::ledger_tests::invalid_transactions": 13, + "e2e::ledger_tests::ledger_many_txs_in_a_block": 55, + "e2e::ledger_tests::ledger_txs_and_queries": 30, + "e2e::ledger_tests::masp_incentives": 618, + "e2e::ledger_tests::masp_pinned_txs": 75, + "e2e::ledger_tests::masp_txs_and_queries": 282, + "e2e::ledger_tests::pos_bonds": 77, + "e2e::ledger_tests::pos_init_validator": 40, + "e2e::ledger_tests::proposal_offline": 21, + "e2e::ledger_tests::pgf_governance_proposal": 100, + "e2e::ledger_tests::eth_governance_proposal": 100, + "e2e::ledger_tests::proposal_submission": 200, "e2e::ledger_tests::run_ledger": 5, - "e2e::ledger_tests::run_ledger_load_state_and_reset": 5, + "e2e::ledger_tests::run_ledger_load_state_and_reset": 23, "e2e::ledger_tests::test_namada_shuts_down_if_tendermint_dies": 2, - "e2e::ledger_tests::test_genesis_validators": 9, - "e2e::ledger_tests::test_node_connectivity_and_consensus": 20, + "e2e::ledger_tests::test_genesis_validators": 14, + "e2e::ledger_tests::test_node_connectivity_and_consensus": 28, "e2e::wallet_tests::wallet_address_cmds": 1, "e2e::wallet_tests::wallet_encrypted_key_cmds": 1, "e2e::wallet_tests::wallet_encrypted_key_cmds_env_var": 1, diff --git a/.github/workflows/scripts/schedule-e2e.py b/.github/workflows/scripts/schedule-e2e.py index e32a76bf5f..31d06b56c0 100644 --- a/.github/workflows/scripts/schedule-e2e.py +++ b/.github/workflows/scripts/schedule-e2e.py @@ -3,14 +3,14 @@ import subprocess import sys -N_OF_MACHINES = 2 +N_OF_MACHINES = 3 NIGHTLY_VERSION = open("rust-nightly-version", "r").read().strip() E2E_FILE = ".github/workflows/scripts/e2e.json" -CARGO_TEST_COMMAND = "cargo +{} test {} -Z unstable-options -- --test-threads=1 -Z unstable-options --nocapture" +CARGO_TEST_COMMAND = "cargo +{} test {} -- --test-threads=1 --nocapture" -MACHINES = [{'tasks': [], 'total_time': 0} for _ in range(N_OF_MACHINES)] +MACHINES = [{'tasks': [], 'time': [], 'total_time': 0} for _ in range(N_OF_MACHINES)] CURRENT_MACHINE_INDEX = int(os.environ.get("INDEX", 1)) @@ -29,7 +29,10 @@ def find_freer_machine(): for task in sorted_task.items(): machine_index = find_freer_machine() MACHINES[machine_index]['total_time'] += task[1] - MACHINES[machine_index]['tasks'].append(task[0]) + MACHINES[machine_index]['tasks'].append({ + 'name': task[0], + 'time': task[1] + }) for index, machine in enumerate(MACHINES): print("Machine {}: {} tasks for a total of {}s".format(index, len(machine['tasks']), machine['total_time'])) @@ -41,17 +44,19 @@ def find_freer_machine(): test_results = {} has_failures = False -for test_name in tasks: +for task in tasks: try: - command = CARGO_TEST_COMMAND.format(NIGHTLY_VERSION, test_name) + command = CARGO_TEST_COMMAND.format(NIGHTLY_VERSION, task['name']) subprocess.check_call(command, shell=True, stdout=sys.stdout, stderr=subprocess.STDOUT) - test_results[test_name] = { + test_results[task['name']] = { 'status': 'ok', + 'time': task['time'], 'command': command } except: - test_results[test_name] = { + test_results[task['name']] = { 'status': 'fail', + 'time': task['time'], 'command': command } has_failures = True @@ -61,7 +66,8 @@ def find_freer_machine(): for test_name in test_results.keys(): test_status = test_results[test_name]['status'] - print("- Test {} -> status: {}".format(test_name, test_status)) + time = test_results[test_name]['time'] + print("- Test {} ({}s) -> status: {}".format(test_name, time, test_status)) if test_results[test_name]['status'] != 'ok': test_command = test_results[test_name]['command'] print(" Run locally with: {}".format(test_command)) diff --git a/CHANGELOG.md b/CHANGELOG.md index 139f1b5060..80cf588897 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,156 @@ # CHANGELOG +## v0.17.5 + +Namada 0.17.5 is a maintenance release chiefly addressing MASP +parameter validation. + +### IMPROVEMENTS + +- Check MASP parameters are correct in the ledger node. + ([#1619](https://github.com/anoma/namada/pull/1619)) + +## v0.17.4 + +Namada 0.17.4 is a minor release improving the codebase by bumping the rust toolchain. + +### BUG FIXES + +- Fix missing async awaits in MASP load and save calls. + ([\#1588](https://github.com/anoma/namada/pull/1588)) + +## v0.17.3 + +Namada 0.17.3 is a minor release switching from tendermint to cometbft. + +### BUG FIXES + +- Correctly handle parsing storage key if they are empty. + ([#1345](https://github.com/anoma/namada/pull/1345)) + +### FEATURES + +- Enable users to change any tendermint config options via namada config. + ([#1570](https://github.com/anoma/namada/pull/1570)) + +### IMPROVEMENTS + +- Added query endpoint for IBC events replacing Tendermint index. + ([\#1404](https://github.com/anoma/namada/pull/1404)) + +### MISCELLANEOUS + +- Switch from unreleased Tendermint fork to an official CometBFT release + v0.37.1. ([\#1476](https://github.com/anoma/namada/pull/1476)) + +## v0.17.2 + +Namada 0.17.2 is a minor release featuring improvements to the client stability. + +### BUG FIXES + +- Do not add address if it already exists in the wallet. + ([\#1504](https://github.com/anoma/namada/issues/1504)) +- When processing slashes, bonds and unbonds that became active after + the infraction epoch must be properly accounted in order to properly + deduct stake that accounts for the precise slash amount. A bug + is fixed in the procedure that properly performs this accounting. + ([#1520](https://github.com/anoma/namada/pull/1520)) +- Fix the message when a client is waiting for a node to sync on queries or + transactions. ([\#1522](https://github.com/anoma/namada/pull/1522)) +- This change will enable usage of the Namada SDK to create MASP transactions + from non-CLI clients. ([\#1524](https://github.com/anoma/namada/pull/1524)) +- Fixing how token balances are displayed in case of missing --token option. + ([#1528](https://github.com/anoma/namada/pull/1528)) +- The slashed token amounts contained inside the bond and unbond information + returned by the PoS library fn bonds_and_unbonds are fixed and properly + computed. ([#1533](https://github.com/anoma/namada/pull/1533)) +- PoS: Fixed the client to change configuration to validator + mode after a successful `init-validator` transaction. + ([\#1549](https://github.com/anoma/namada/pull/1549)) +- PoS: fixed a check for whether a given address belongs to a + validator account to work properly with newly created accounts. + ([\#1553](https://github.com/anoma/namada/pull/1553)) +- Fixes the slash rate output in the query_slashes client + command and some redundancy in misbehavior reporting logs. + ([#1558](https://github.com/anoma/namada/pull/1558)) + +### IMPROVEMENTS + +- Add a command, `namadac utils default-base-dir`, to + print the default base directory the command + line would use were one not provided by the user. + ([#1491](https://github.com/anoma/namada/pull/1491)) +- Improve the established address in-memory representation + and use a full SHA-256 digest for their generation. + ([\#1510](https://github.com/anoma/namada/pull/1510)) +- Improve the implicit address and PKH in-memory representation. + ([\#1512](https://github.com/anoma/namada/pull/1512)) +- Improve help message for address add command + ([\#1514](https://github.com/anoma/namada/issues/1514)) +- PoS: make a re-usable bonds and unbonds details query. + ([\#1518](https://github.com/anoma/namada/pull/1518)) + +## v0.17.1 + +Namada 0.17.0 is a scheduled minor release featuring several improvements to the slashing mechanism, +wallet address derivation, transaction structure and the ledger stability. + +### BUG FIXES + +- Persists a newly added storage field for epoch update blocks delay to be + available after node restart when not `None` which may break consensus. + ([\#1455](https://github.com/anoma/namada/pull/1455)) +- Client: Fixed an off-by-one error to stop waiting for start or catch-up when + max tries are reached. ([\#1456](https://github.com/anoma/namada/pull/1456)) +- Include the wasm tx hash instead of the wasm blob when constructing a + transaction ([#1474](https://github.com/anoma/namada/pull/1474)) +- Fix a client block query to avoid seeing pre-committed blocks. + ([\#1534](https://github.com/anoma/namada/pull/1534)) + +### DOCS + +- Adds specs for gas and fee ([#889](https://github.com/anoma/namada/pull/889)) + +### FEATURES + +- The implementation of the cubic slashing system that touches virtually all + parts of the proof-of-stake system. Slashes tokens are currently kept in the + PoS address rather than being transferred to the Slash Pool address. This PR + also includes significant testing infrastructure, highlighted by the PoS state + machine test with slashing. ([#892](https://github.com/anoma/namada/pull/892)) +- Implements HD wallet derivation / recovery from a given mnemonic code + ([\#1110](https://github.com/anoma/namada/pull/1110)) +- PoS: Added a client command `find-validator --tm-address
` + to find validator's Namada address by Tendermint address. + ([\#1344](https://github.com/anoma/namada/pull/1344)) + +### IMPROVEMENTS + +- Make Namada transactions signable on hardware constrained wallets by making + them smaller. ([#1093](https://github.com/anoma/namada/pull/1093)) +- 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)) +- Zeroizes memory containing passphrases in wallet. + ([\#1425](https://github.com/anoma/namada/issues/1425)) +- Added some missing cli option for cli wallet + ([#1432](https://github.com/anoma/namada/pull/1432)) +- Improve logging error when submiting an invalid validator commission change tx + ([#1434](https://github.com/anoma/namada/pull/1434)) +- Correct a typo in the error change commission error handling + ([#1435](https://github.com/anoma/namada/pull/1435)) +- Improve the reveal tx naming in cli + ([#1436](https://github.com/anoma/namada/pull/1436)) +- Improve computations readability when calculating inflations + ([#1444](https://github.com/anoma/namada/pull/1444)) +- Remove abci++ dependencies ([#1449](https://github.com/anoma/namada/pull/1449)) +- Reorganize the structure of transactions + ([#1462](https://github.com/anoma/namada/pull/1462)) +- Improved log entries related to PoS system. + ([\#1509](https://github.com/anoma/namada/pull/1509)) + ## v0.16.0 Namada 0.16.0 is a regular release focused on providing the Namada SDK @@ -12,9 +163,30 @@ to developers. ### IMPROVEMENTS +- Provide Namada SDK (in particular, the `namada` +crate may now be usefully linked into user +applications). ([#925](https://github.com/anoma/namada/pull/925)) - Bump RocksDB crate to 0.21.0 to address compilation errors on certain C++ toolchains. ([#1366](https://github.com/anoma/namada/pull/1366)) +## v0.15.4 + +Namada 0.15.4 is a maintenance release addressing the invalid creation of blocks due to missing replay protection checks during prepare +proposal. + +### BUG FIXES + +- Fixed a bug in `prepare_proposal` causing the creation + of blocks containing already applied transactions. + ([#1405](https://github.com/anoma/namada/pull/1405)) + +### IMPROVEMENTS + +- Make Tendermint consensus paramenters configurable via Namada configuration. + ([#1399](https://github.com/anoma/namada/pull/1399)) +- Improved error logs in `process_proposal` and added more info to + `InternalStats` ([#1407](https://github.com/anoma/namada/pull/1407)) + ## v0.15.3 Namada 0.15.3 is a maintenance release addressing the creation of diff --git a/Cargo.lock b/Cargo.lock index 68075542f3..1008afe7c8 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", ] @@ -46,18 +47,7 @@ checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ "getrandom 0.2.9", "once_cell", - "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", + "version_check", ] [[package]] @@ -84,7 +74,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -265,7 +255,7 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" dependencies = [ - "concurrent-queue 2.2.0", + "concurrent-queue", "event-listener", "futures-core", ] @@ -278,7 +268,7 @@ checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" dependencies = [ "async-lock", "async-task", - "concurrent-queue 2.2.0", + "concurrent-queue", "fastrand", "futures-lite", "slab", @@ -301,22 +291,22 @@ dependencies = [ [[package]] name = "async-io" -version = "1.9.0" -source = "git+https://github.com/heliaxdev/async-io.git?rev=9285dad39c9a37ecd0dbd498c5ce5b0e65b02489#9285dad39c9a37ecd0dbd498c5ce5b0e65b02489" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ - "autocfg 1.1.0", - "concurrent-queue 1.2.4", + "async-lock", + "autocfg", + "cfg-if 1.0.0", + "concurrent-queue", "futures-lite", - "libc", - "log 0.4.17", - "once_cell", + "log", "parking", "polling", - "rustversion", + "rustix", "slab", "socket2", "waker-fn", - "winapi 0.3.9", ] [[package]] @@ -330,27 +320,27 @@ dependencies = [ [[package]] name = "async-process" -version = "1.5.0" -source = "git+https://github.com/heliaxdev/async-process.git?rev=e42c527e87d937da9e01aaeb563c0b948580dc89#e42c527e87d937da9e01aaeb563c0b948580dc89" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" dependencies = [ "async-io", - "autocfg 1.1.0", + "async-lock", + "autocfg", "blocking", "cfg-if 1.0.0", "event-listener", "futures-lite", - "libc", - "once_cell", - "rustversion", + "rustix", "signal-hook", - "winapi 0.3.9", + "windows-sys 0.48.0", ] [[package]] name = "async-std" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52580991739c5cdb36cde8b2a516371c0a3b70dda36d916cc08b82372916808c" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" dependencies = [ "async-channel", "async-global-executor", @@ -364,9 +354,8 @@ dependencies = [ "futures-lite", "gloo-timers", "kv-log-macro", - "log 0.4.17", + "log", "memchr", - "num_cpus", "once_cell", "pin-project-lite", "pin-utils", @@ -393,7 +382,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -410,7 +399,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -421,7 +410,7 @@ checksum = "e00550829ef8e2c4115250d0ee43305649b0fa95f78a32ce5b07da0b73d95c5c" dependencies = [ "futures-io", "futures-util", - "log 0.4.17", + "log", "pin-project-lite", "tokio", "tokio-rustls 0.22.0", @@ -443,16 +432,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi 0.1.19", "libc", - "winapi 0.3.9", -] - -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.1.0", + "winapi", ] [[package]] @@ -470,16 +450,16 @@ dependencies = [ "async-trait", "axum-core", "bitflags", - "bytes 1.4.0", + "bytes", "futures-util", "http", "http-body", - "hyper 0.14.26", + "hyper", "itoa", "matchit", "memchr", - "mime 0.3.17", - "percent-encoding 2.2.0", + "mime", + "percent-encoding", "pin-project-lite", "rustversion", "serde 1.0.163", @@ -496,11 +476,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" dependencies = [ "async-trait", - "bytes 1.4.0", + "bytes", "futures-util", "http", "http-body", - "mime 0.3.17", + "mime", "rustversion", "tower-layer", "tower-service", @@ -528,23 +508,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" [[package]] -name = "base64" -version = "0.9.3" +name = "base58" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" -dependencies = [ - "byteorder", - "safemem", -] +checksum = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" [[package]] -name = "base64" -version = "0.10.1" +name = "base58" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -dependencies = [ - "byteorder", -] +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" [[package]] name = "base64" @@ -578,33 +551,23 @@ checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" [[package]] name = "bellman" -version = "0.11.2" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43473b34abc4b0b405efa0a250bac87eea888182b21687ee5c8115d279b0fda5" +checksum = "a4dd656ef4fdf7debb6d87d4dd92642fcbcdb78cbf6600c13e25c87e4d1a3807" dependencies = [ - "bitvec 0.22.3", - "blake2s_simd 0.5.11", + "bitvec", + "blake2s_simd", "byteorder", "crossbeam-channel 0.5.8", - "ff", - "group", + "ff 0.12.1", + "group 0.12.1", "lazy_static", - "log 0.4.17", + "log", "num_cpus", "pairing", "rand_core 0.6.4", "rayon", - "subtle", -] - -[[package]] -name = "bigint" -version = "4.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0e8c8a600052b52482eff2cf4d810e462fdff1f656ac1ecb6232132a1ed7def" -dependencies = [ - "byteorder", - "crunchy 0.1.6", + "subtle 2.4.1", ] [[package]] @@ -637,13 +600,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 +646,7 @@ checksum = "0694ea59225b0c5f3cb405ff3f670e4828358ed26aec49dc352f730f0cb1a8a3" dependencies = [ "bech32 0.9.1", "bitcoin_hashes", - "secp256k1 0.24.3", + "secp256k1", "serde 1.0.163", ] @@ -702,28 +665,16 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -[[package]] -name = "bitvec" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5237f00a8c86130a0cc317830e558b966dd7850d48a953d998c813f01a41b527" -dependencies = [ - "funty 1.2.0", - "radium 0.6.2", - "tap", - "wyz 0.4.0", -] - [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "funty 2.0.0", - "radium 0.7.0", + "funty", + "radium", "tap", - "wyz 0.5.1", + "wyz", ] [[package]] @@ -745,17 +696,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" @@ -764,18 +704,7 @@ checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" dependencies = [ "arrayref", "arrayvec 0.7.2", - "constant_time_eq 0.2.5", -] - -[[package]] -name = "blake2s_simd" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e461a7034e85b211a4acb57ee2e6730b32912b06c08cc242243c39fc21ae6a2" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "constant_time_eq 0.1.5", + "constant_time_eq", ] [[package]] @@ -786,7 +715,7 @@ checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" dependencies = [ "arrayref", "arrayvec 0.7.2", - "constant_time_eq 0.2.5", + "constant_time_eq", ] [[package]] @@ -799,7 +728,7 @@ dependencies = [ "arrayvec 0.7.2", "cc", "cfg-if 1.0.0", - "constant_time_eq 0.2.5", + "constant_time_eq", "digest 0.10.6", ] @@ -840,7 +769,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]] @@ -870,20 +799,20 @@ dependencies = [ "atomic-waker", "fastrand", "futures-lite", - "log 0.4.17", + "log", ] [[package]] name = "bls12_381" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a829c821999c06be34de314eaeb7dd1b42be38661178bc26ad47a4eacebdb0f9" +checksum = "a3c196a77437e7cc2fb515ce413a6401291578b5afc8ecb29a3c7ab957f05941" dependencies = [ - "ff", - "group", + "ff 0.12.1", + "group 0.12.1", "pairing", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -891,40 +820,17 @@ name = "borsh" version = "0.9.4" source = "git+https://github.com/heliaxdev/borsh-rs.git?rev=cd5223e5103c4f139e0c54cf8259b7ec5ec4073a#cd5223e5103c4f139e0c54cf8259b7ec5ec4073a" dependencies = [ - "borsh-derive 0.9.4", + "borsh-derive", "hashbrown 0.11.2", ] -[[package]] -name = "borsh" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" -dependencies = [ - "borsh-derive 0.10.3", - "hashbrown 0.13.2", -] - [[package]] name = "borsh-derive" 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", -] - -[[package]] -name = "borsh-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" -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 +846,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 +856,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" @@ -1051,16 +935,6 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "bytes" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -dependencies = [ - "byteorder", - "iovec", -] - [[package]] name = "bytes" version = "1.4.0" @@ -1078,12 +952,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "cache-padded" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "981520c98f422fcc584dc1a95c334e6953900b9106bc47a9839b81790009eb21" - [[package]] name = "camino" version = "1.1.4" @@ -1152,20 +1020,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", ] @@ -1179,7 +1057,7 @@ dependencies = [ "iana-time-zone", "num-integer", "num-traits 0.2.15", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -1197,13 +1075,24 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d34327ead1c743a10db339de35fb58957564b99d248a67985c55638b22c59b5" dependencies = [ - "version_check 0.9.4", + "version_check", ] [[package]] @@ -1234,15 +1123,6 @@ dependencies = [ "vec_map", ] -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - [[package]] name = "clru" version = "0.5.0" @@ -1285,15 +1165,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "concurrent-queue" -version = "1.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" -dependencies = [ - "cache-padded", -] - [[package]] name = "concurrent-queue" version = "2.2.0" @@ -1321,11 +1192,11 @@ dependencies = [ [[package]] name = "conpty" -version = "0.3.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "977baae4026273d7f9bb69a0a8eb4aed7ab9dac98799f742dce09173a9734754" +checksum = "b72b06487a0d4683349ad74d62e87ad639b09667082b3c495c5b6bab7d84b3da" dependencies = [ - "windows 0.29.0", + "windows 0.44.0", ] [[package]] @@ -1334,12 +1205,6 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "constant_time_eq" version = "0.2.5" @@ -1402,9 +1267,9 @@ dependencies = [ "cranelift-codegen-shared", "cranelift-entity", "gimli 0.25.0", - "log 0.4.17", + "log", "regalloc", - "smallvec 1.10.0", + "smallvec", "target-lexicon", ] @@ -1437,8 +1302,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "279afcc0d3e651b773f94837c3d581177b348c8d69e928104b2e9fccb226f921" dependencies = [ "cranelift-codegen", - "log 0.4.17", - "smallvec 1.10.0", + "log", + "smallvec", "target-lexicon", ] @@ -1488,7 +1353,7 @@ version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" dependencies = [ - "autocfg 1.1.0", + "autocfg", "cfg-if 1.0.0", "crossbeam-utils 0.8.15", "memoffset 0.8.0", @@ -1501,7 +1366,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg 1.1.0", + "autocfg", "cfg-if 0.1.10", "lazy_static", ] @@ -1515,12 +1380,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 +1394,7 @@ checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -1551,37 +1410,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 +1478,7 @@ dependencies = [ "byteorder", "digest 0.9.0", "rand_core 0.5.1", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -1661,7 +1515,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -1672,7 +1526,7 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" dependencies = [ "darling_core", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -1756,7 +1610,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.4", "crypto-common", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -1786,7 +1640,7 @@ checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -1797,7 +1651,7 @@ checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", "redox_users", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -1808,7 +1662,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -1935,12 +1789,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 +1845,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]] @@ -2039,7 +1884,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" dependencies = [ - "version_check 0.9.4", + "version_check", ] [[package]] @@ -2048,7 +1893,7 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5584ba17d7ab26a8a7284f13e5bd196294dd2f2d79773cff29b9e9edef601a6" dependencies = [ - "log 0.4.17", + "log", "once_cell", "serde 1.0.163", "serde_json", @@ -2062,12 +1907,12 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "expectrl" -version = "0.5.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2795e11f4ee3124984d454f25ac899515a5fa6d956562ef2b147fef6050b02f8" +checksum = "b36f34b0325008d05b0e9be8361bfa8a0fb905f10de0d951c2621c59e811cb91" dependencies = [ "conpty", - "nix 0.23.2", + "nix", "ptyprocess", "regex", ] @@ -2118,8 +1963,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 +1981,7 @@ dependencies = [ "serde_bytes", "serde_json", "subproductdomain", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -2159,9 +2004,19 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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", + "rand_core 0.6.4", + "subtle 2.4.1", ] [[package]] @@ -2180,7 +2035,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "547ebf393d987692a02b5d2be1c0b398b16a5b185c23a047c1d3fc3050d6d803" dependencies = [ - "log 0.4.17", + "log", "mime_guess", "tiny_http", ] @@ -2262,7 +2117,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "percent-encoding 2.2.0", + "percent-encoding", ] [[package]] @@ -2272,7 +2127,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", @@ -2285,45 +2140,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -dependencies = [ - "bitflags", - "fuchsia-zircon-sys", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" - -[[package]] -name = "funty" -version = "1.2.0" -source = "git+https://github.com/bitvecto-rs/funty/?rev=7ef0d890fbcd8b3def1635ac1a877fc298488446#7ef0d890fbcd8b3def1635ac1a877fc298488446" - [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" -[[package]] -name = "futures" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" - [[package]] name = "futures" version = "0.3.28" @@ -2395,7 +2217,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -2444,7 +2266,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -2454,8 +2276,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 +2289,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]] @@ -2495,10 +2321,10 @@ dependencies = [ "bitflags", "libc", "libgit2-sys", - "log 0.4.17", + "log", "openssl-probe", "openssl-sys", - "url 2.3.1", + "url", ] [[package]] @@ -2525,10 +2351,21 @@ version = "0.11.0" 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 +2380,8 @@ dependencies = [ "ark-poly", "ark-serialize", "ark-std", - "blake2b_simd 1.0.1", - "chacha20", + "blake2b_simd", + "chacha20 0.8.2", "hex", "itertools", "miracl_core", @@ -2581,7 +2418,7 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" dependencies = [ - "bytes 1.4.0", + "bytes", "fnv", "futures-core", "futures-sink", @@ -2600,27 +2437,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 +2452,14 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.6", + "ahash", ] [[package]] -name = "hashbrown" -version = "0.13.2" +name = "hdpath" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.3", -] - -[[package]] -name = "hdpath" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ae1615f843ce3981b47468f3f7c435ac17deb33c2261e64d7f1e87f5c11acc" +checksum = "dfa5bc9db2c17d2660f53ce217b778d06d68de13d1cd01c0f4e5de4b7918935f" dependencies = [ "byteorder", ] @@ -2668,11 +2482,11 @@ checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64 0.13.1", "bitflags", - "bytes 1.4.0", + "bytes", "headers-core", "http", "httpdate", - "mime 0.3.17", + "mime", "sha1", ] @@ -2721,6 +2535,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 +2574,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,13 +2596,19 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ - "bytes 1.4.0", + "bytes", "fnv", "itoa", ] @@ -2778,7 +2619,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "bytes 1.4.0", + "bytes", "http", "pin-project-lite", ] @@ -2811,32 +2652,13 @@ dependencies = [ "serde 1.0.163", ] -[[package]] -name = "hyper" -version = "0.10.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273" -dependencies = [ - "base64 0.9.3", - "httparse", - "language-tags", - "log 0.3.9", - "mime 0.2.6", - "num_cpus", - "time 0.1.43", - "traitobject", - "typeable", - "unicase 1.4.2", - "url 1.7.2", -] - [[package]] name = "hyper" version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ - "bytes 1.4.0", + "bytes", "futures-channel", "futures-core", "futures-util", @@ -2860,11 +2682,11 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc" dependencies = [ - "bytes 1.4.0", - "futures 0.3.28", + "bytes", + "futures", "headers", "http", - "hyper 0.14.26", + "hyper", "hyper-rustls", "rustls-native-certs 0.5.0", "tokio", @@ -2881,8 +2703,8 @@ checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" dependencies = [ "ct-logs", "futures-util", - "hyper 0.14.26", - "log 0.4.17", + "hyper", + "log", "rustls 0.19.1", "rustls-native-certs 0.5.0", "tokio", @@ -2897,7 +2719,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper 0.14.26", + "hyper", "pin-project-lite", "tokio", "tokio-io-timeout", @@ -2909,8 +2731,8 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes 1.4.0", - "hyper 0.14.26", + "bytes", + "hyper", "native-tls", "tokio", "tokio-native-tls", @@ -2942,50 +2764,18 @@ dependencies = [ [[package]] name = "ibc" version = "0.36.0" -source = "git+https://github.com/heliaxdev/cosmos-ibc-rs.git?rev=2d7edc16412b60cabf78163fe24a6264e11f77a9#2d7edc16412b60cabf78163fe24a6264e11f77a9" -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.git?rev=7e527b5b8c95d83351e93ceafc14ac853224283f)", - "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.6", - "tendermint-light-client-verifier 0.23.6", - "tendermint-proto 0.23.6", - "tendermint-testgen 0.23.6", - "time 0.3.17", - "tracing 0.1.37", - "uint", -] - -[[package]] -name = "ibc" -version = "0.36.0" -source = "git+https://github.com/heliaxdev/cosmos-ibc-rs?rev=db14744bfba6239cc5f58345ff90f8b7d42637d6#db14744bfba6239cc5f58345ff90f8b7d42637d6" +source = "git+https://github.com/heliaxdev/cosmos-ibc-rs.git?rev=e71bc2cc79f8c2b32e970d95312f251398c93d9e#e71bc2cc79f8c2b32e970d95312f251398c93d9e" dependencies = [ - "bytes 1.4.0", + "bytes", "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)", + "ibc-proto", "ics23", "num-traits 0.2.15", - "parking_lot 0.12.1", + "parking_lot", "primitive-types", "prost", "safe-regex", @@ -2994,11 +2784,11 @@ dependencies = [ "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", + "tendermint", + "tendermint-light-client-verifier", + "tendermint-proto", + "tendermint-testgen", + "time", "tracing 0.1.37", "uint", ] @@ -3006,46 +2796,29 @@ dependencies = [ [[package]] name = "ibc-proto" version = "0.26.0" -source = "git+https://github.com/heliaxdev/ibc-proto-rs.git?rev=7e527b5b8c95d83351e93ceafc14ac853224283f#7e527b5b8c95d83351e93ceafc14ac853224283f" +source = "git+https://github.com/heliaxdev/ibc-proto-rs.git?rev=6f4038fcf4981f1ed70771d1cd89931267f917af#6f4038fcf4981f1ed70771d1cd89931267f917af" dependencies = [ "base64 0.13.1", - "bytes 1.4.0", + "bytes", "flex-error", "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" -source = "git+https://github.com/heliaxdev/hermes.git?rev=8e2ff3479edc0653f34b22df450d451eedd2c2ab#8e2ff3479edc0653f34b22df450d451eedd2c2ab" +source = "git+https://github.com/heliaxdev/hermes.git?rev=a4ad1355fc0b05908881854aa27221cb2b878ac5#a4ad1355fc0b05908881854aa27221cb2b878ac5" dependencies = [ "anyhow", "async-stream", "bech32 0.9.1", "bitcoin", "bs58", - "bytes 1.4.0", + "bytes", "crossbeam-channel 0.5.8", "digest 0.10.6", "dirs-next", @@ -3053,14 +2826,14 @@ dependencies = [ "ed25519-dalek", "ed25519-dalek-bip32", "flex-error", - "futures 0.3.28", + "futures", "generic-array 0.14.7", "hdpath", "hex", "http", "humantime", "humantime-serde", - "ibc-proto 0.26.0 (git+https://github.com/heliaxdev/ibc-proto-rs.git?rev=7e527b5b8c95d83351e93ceafc14ac853224283f)", + "ibc-proto", "ibc-relayer-types", "itertools", "moka", @@ -3070,7 +2843,7 @@ dependencies = [ "regex", "retry", "ripemd", - "secp256k1 0.24.3", + "secp256k1", "semver 1.0.17", "serde 1.0.163", "serde_derive", @@ -3079,32 +2852,32 @@ 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]] name = "ibc-relayer-types" version = "0.22.0" -source = "git+https://github.com/heliaxdev/hermes.git?rev=8e2ff3479edc0653f34b22df450d451eedd2c2ab#8e2ff3479edc0653f34b22df450d451eedd2c2ab" +source = "git+https://github.com/heliaxdev/hermes.git?rev=a4ad1355fc0b05908881854aa27221cb2b878ac5#a4ad1355fc0b05908881854aa27221cb2b878ac5" dependencies = [ - "bytes 1.4.0", + "bytes", "derive_more", "dyn-clone", "erased-serde", "flex-error", - "ibc-proto 0.26.0 (git+https://github.com/heliaxdev/ibc-proto-rs.git?rev=7e527b5b8c95d83351e93ceafc14ac853224283f)", + "ibc-proto", "ics23", "itertools", "num-rational", @@ -3115,12 +2888,12 @@ 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", - "time 0.3.17", + "tendermint", + "tendermint-light-client-verifier", + "tendermint-proto", + "tendermint-rpc", + "tendermint-testgen", + "time", "uint", ] @@ -3131,7 +2904,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca44b684ce1859cff746ff46f5765ab72e12e3c06f76a8356db8f9a2ecf43f17" dependencies = [ "anyhow", - "bytes 1.4.0", + "bytes", "hex", "prost", "ripemd", @@ -3145,17 +2918,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.3.0" @@ -3197,9 +2959,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 +2977,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", ] @@ -3225,18 +2987,27 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "autocfg 1.1.0", + "autocfg", "hashbrown 0.12.3", "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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f97967975f448f1a7ddb12b0bc41069d09ed6a1c161a92687e057325db35d413" dependencies = [ - "bytes 1.4.0", + "bytes", ] [[package]] @@ -3262,15 +3033,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "iovec" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -dependencies = [ - "libc", -] - [[package]] name = "ipnet" version = "2.7.2" @@ -3303,25 +3065,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", + "bitvec", "bls12_381", - "ff", - "group", + "ff 0.12.1", + "group 0.12.1", "rand_core 0.6.4", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -3346,31 +3108,15 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - [[package]] name = "kv-log-macro" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" dependencies = [ - "log 0.4.17", + "log", ] -[[package]] -name = "language-tags" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" - [[package]] name = "lazy_static" version = "1.4.0" @@ -3429,14 +3175,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ "cfg-if 1.0.0", - "winapi 0.3.9", + "winapi", ] [[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 +3200,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 +3224,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 +3239,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]] @@ -3539,41 +3301,22 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" -[[package]] -name = "lock_api" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" -dependencies = [ - "scopeguard", -] - [[package]] name = "lock_api" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ - "autocfg 1.1.0", + "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" -dependencies = [ - "log 0.4.17", -] - -[[package]] -name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" dependencies = [ - "cfg-if 1.0.0", "value-bag", ] @@ -3621,60 +3364,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", - "blake2s_simd 1.0.1", + "bitvec", + "blake2b_simd", + "blake2s_simd", "bls12_381", - "borsh 0.9.4", + "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", + "blake2b_simd", "bls12_381", - "byteorder", "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]] @@ -3686,12 +3438,6 @@ dependencies = [ "regex-automata", ] -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - [[package]] name = "matchit" version = "0.7.0" @@ -3711,7 +3457,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56220900f1a0923789ecd6bf25fbae8af3b2f1ff3e9e297fc9b6b8674dd4d852" dependencies = [ "instant", - "log 0.4.17", + "log", ] [[package]] @@ -3735,7 +3481,16 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ - "autocfg 1.1.0", + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", ] [[package]] @@ -3744,7 +3499,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -3752,9 +3507,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" @@ -3768,15 +3526,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "mime" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" -dependencies = [ - "log 0.3.9", -] - [[package]] name = "mime" version = "0.3.17" @@ -3789,8 +3538,8 @@ version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" dependencies = [ - "mime 0.3.17", - "unicase 2.6.0", + "mime", + "unicase", ] [[package]] @@ -3823,32 +3572,13 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb6c6973f78ef55d0e5fc04fdb8f9ad67c87c9e86bca0ff77b6a3102b0eb36b7" dependencies = [ - "log 0.4.17", + "log", "once_cell", "rustls 0.20.8", "webpki 0.22.0", "webpki-roots 0.22.6", ] -[[package]] -name = "mio" -version = "0.6.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" -dependencies = [ - "cfg-if 0.1.10", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", - "libc", - "log 0.4.17", - "miow", - "net2", - "slab", - "winapi 0.2.8", -] - [[package]] name = "mio" version = "0.8.6" @@ -3856,23 +3586,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", - "log 0.4.17", + "log", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.45.0", ] -[[package]] -name = "miow" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" -dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", -] - [[package]] name = "miracl_core" version = "2.3.0" @@ -3890,16 +3608,16 @@ dependencies = [ "crossbeam-utils 0.8.15", "num_cpus", "once_cell", - "parking_lot 0.12.1", + "parking_lot", "quanta", "rustc_version 0.4.0", "scheduled-thread-pool", "skeptic", - "smallvec 1.10.0", + "smallvec", "tagptr", "thiserror", "triomphe", - "uuid 1.3.2", + "uuid 1.3.3", ] [[package]] @@ -3916,26 +3634,22 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "namada" -version = "0.16.0" +version = "0.17.5" dependencies = [ "assert_matches", "async-std", "async-trait", - "bellman", + "base58 0.2.0", "bimap", - "bls12_381", - "borsh 0.9.4", + "borsh", "byte-unit", "circular-queue", "clru", "data-encoding", + "derivation-path", "derivative", - "ibc 0.36.0 (git+https://github.com/heliaxdev/cosmos-ibc-rs.git?rev=2d7edc16412b60cabf78163fe24a6264e11f77a9)", - "ibc 0.36.0 (git+https://github.com/heliaxdev/cosmos-ibc-rs?rev=db14744bfba6239cc5f58345ff90f8b7d42637d6)", - "ibc-proto 0.26.0 (git+https://github.com/heliaxdev/ibc-proto-rs.git?rev=7e527b5b8c95d83351e93ceafc14ac853224283f)", - "ibc-proto 0.26.0 (git+https://github.com/heliaxdev/ibc-proto-rs?rev=dd8ba23110a144ffe2074a0b889676468266435a)", "itertools", - "libsecp256k1", + "libsecp256k1 0.7.0", "loupe", "masp_primitives", "masp_proofs", @@ -3952,17 +3666,19 @@ 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-rpc", "test-log", "thiserror", + "tiny-bip39 0.8.2", + "tiny-hderive", "tokio", "toml", "tracing 0.1.37", @@ -3979,7 +3695,7 @@ dependencies = [ [[package]] name = "namada_apps" -version = "0.16.0" +version = "0.17.5" dependencies = [ "ark-serialize", "ark-std", @@ -3991,7 +3707,7 @@ dependencies = [ "bimap", "bit-set", "blake2b-rs", - "borsh 0.9.4", + "borsh", "byte-unit", "byteorder", "clap", @@ -4006,7 +3722,7 @@ dependencies = [ "ferveo-common", "file-lock", "flate2", - "futures 0.3.28", + "futures", "git2", "itertools", "libc", @@ -4029,6 +3745,7 @@ dependencies = [ "rayon", "regex", "reqwest", + "ripemd", "rlimit", "rocksdb", "rpassword", @@ -4043,10 +3760,7 @@ dependencies = [ "sysinfo", "tar", "tempfile", - "tendermint 0.23.6", - "tendermint-config 0.23.6", - "tendermint-proto 0.23.6", - "tendermint-rpc 0.23.6", + "tendermint-config", "test-log", "thiserror", "tokio", @@ -4054,26 +3768,24 @@ dependencies = [ "toml", "tonic", "tower", - "tower-abci 0.1.0 (git+https://github.com/heliaxdev/tower-abci.git?rev=79069a441cee7d9955a3d826d29656a0fb16115c)", - "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", + "winapi", + "zeroize", ] [[package]] name = "namada_core" -version = "0.16.0" +version = "0.17.5" dependencies = [ "ark-bls12-381", "ark-ec", "ark-serialize", "assert_matches", "bech32 0.8.1", - "bellman", - "borsh 0.9.4", + "borsh", "chrono", "data-encoding", "derivative", @@ -4081,14 +3793,12 @@ dependencies = [ "ferveo", "ferveo-common", "group-threshold-cryptography", - "ibc 0.36.0 (git+https://github.com/heliaxdev/cosmos-ibc-rs.git?rev=2d7edc16412b60cabf78163fe24a6264e11f77a9)", - "ibc 0.36.0 (git+https://github.com/heliaxdev/cosmos-ibc-rs?rev=db14744bfba6239cc5f58345ff90f8b7d42637d6)", - "ibc-proto 0.26.0 (git+https://github.com/heliaxdev/ibc-proto-rs.git?rev=7e527b5b8c95d83351e93ceafc14ac853224283f)", - "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 +3814,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", @@ -4116,9 +3826,9 @@ dependencies = [ [[package]] name = "namada_encoding_spec" -version = "0.16.0" +version = "0.17.5" dependencies = [ - "borsh 0.9.4", + "borsh", "itertools", "lazy_static", "madato", @@ -4127,7 +3837,7 @@ dependencies = [ [[package]] name = "namada_macros" -version = "0.16.0" +version = "0.17.5" dependencies = [ "proc-macro2", "quote", @@ -4136,16 +3846,16 @@ dependencies = [ [[package]] name = "namada_proof_of_stake" -version = "0.16.0" +version = "0.17.5" dependencies = [ - "borsh 0.9.4", + "borsh", "data-encoding", "derivative", - "hex", "itertools", "namada_core", "once_cell", "proptest", + "proptest-state-machine", "rust_decimal", "rust_decimal_macros", "test-log", @@ -4156,19 +3866,19 @@ dependencies = [ [[package]] name = "namada_test_utils" -version = "0.16.0" +version = "0.17.5" dependencies = [ - "borsh 0.9.4", + "borsh", "namada_core", "strum", ] [[package]] name = "namada_tests" -version = "0.16.0" +version = "0.17.5" dependencies = [ "assert_cmd", - "borsh 0.9.4", + "borsh", "chrono", "color-eyre", "concat-idents", @@ -4188,8 +3898,10 @@ dependencies = [ "namada_test_utils", "namada_tx_prelude", "namada_vp_prelude", + "once_cell", "pretty_assertions", "proptest", + "proptest-state-machine", "prost", "rand 0.8.5", "regex", @@ -4198,10 +3910,6 @@ 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", "test-log", "tokio", "toml", @@ -4211,40 +3919,38 @@ dependencies = [ [[package]] name = "namada_tx_prelude" -version = "0.16.0" +version = "0.17.5" dependencies = [ - "borsh 0.9.4", + "borsh", "masp_primitives", "namada_core", "namada_macros", "namada_proof_of_stake", "namada_vm_env", "rust_decimal", - "sha2 0.10.6", + "sha2 0.9.9", "thiserror", ] [[package]] name = "namada_vm_env" -version = "0.16.0" +version = "0.17.5" dependencies = [ - "borsh 0.9.4", - "hex", + "borsh", "masp_primitives", - "masp_proofs", "namada_core", ] [[package]] name = "namada_vp_prelude" -version = "0.16.0" +version = "0.17.5" dependencies = [ - "borsh 0.9.4", + "borsh", "namada_core", "namada_macros", "namada_proof_of_stake", "namada_vm_env", - "sha2 0.10.6", + "sha2 0.9.9", "thiserror", ] @@ -4256,7 +3962,7 @@ checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", - "log 0.4.17", + "log", "openssl", "openssl-probe", "openssl-sys", @@ -4266,41 +3972,18 @@ dependencies = [ "tempfile", ] -[[package]] -name = "net2" -version = "0.2.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "nix" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77d9f3521ea8e0641a153b3cddaf008dcbf26acd4ed739a2517295e0760d12c7" -dependencies = [ - "bitflags", - "cc", - "cfg-if 1.0.0", - "libc", - "memoffset 0.6.5", -] - [[package]] name = "nix" -version = "0.23.2" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ "bitflags", - "cc", "cfg-if 1.0.0", "libc", - "memoffset 0.6.5", + "memoffset 0.7.1", + "pin-utils", + "static_assertions", ] [[package]] @@ -4311,7 +3994,7 @@ checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" dependencies = [ "lexical-core", "memchr", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -4336,7 +4019,7 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -4346,7 +4029,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ "overload", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -4369,7 +4052,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-integer", "num-traits 0.2.15", "serde 1.0.163", @@ -4401,7 +4084,7 @@ version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-traits 0.2.15", ] @@ -4411,7 +4094,7 @@ version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-integer", "num-traits 0.2.15", ] @@ -4422,7 +4105,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-bigint", "num-integer", "num-traits 0.2.15", @@ -4444,7 +4127,7 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ - "autocfg 1.1.0", + "autocfg", "libm", ] @@ -4520,7 +4203,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -4541,33 +4224,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 +4232,7 @@ checksum = "c6624905ddd92e460ff0685567539ed1ac985b2dee4c92c7edcd64fce905b00c" dependencies = [ "ct-codecs", "getrandom 0.2.9", - "subtle", + "subtle 2.4.1", "zeroize", ] @@ -4592,7 +4248,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -4609,11 +4265,11 @@ checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55" [[package]] name = "pairing" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2e415e349a3006dd7d9482cdab1c980a845bed1377777d768cb693a44540b42" +checksum = "135590d8bdba2b31346f9cd1fb2a912329f5135e832a4f422942eb6ead8b6b3b" dependencies = [ - "group", + "group 0.12.1", ] [[package]] @@ -4623,7 +4279,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ddb756ca205bd108aee3c62c6d3c994e1df84a59b9d6d4a5ea42ee1fd5a9a28" dependencies = [ "arrayvec 0.7.2", - "bitvec 1.0.1", + "bitvec", "byte-slice-cast", "impl-trait-for-tuples", "parity-scale-codec-derive", @@ -4654,40 +4310,14 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" -[[package]] -name = "parking_lot" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -dependencies = [ - "lock_api 0.3.4", - "parking_lot_core 0.6.3", - "rustc_version 0.2.3", -] - [[package]] name = "parking_lot" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ - "lock_api 0.4.9", - "parking_lot_core 0.9.7", -] - -[[package]] -name = "parking_lot_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66b810a62be75176a80873726630147a5ca780cd33921e0b5709033e66b0a" -dependencies = [ - "cfg-if 0.1.10", - "cloudabi", - "libc", - "redox_syscall 0.1.57", - "rustc_version 0.2.3", - "smallvec 0.6.14", - "winapi 0.3.9", + "lock_api", + "parking_lot_core", ] [[package]] @@ -4699,7 +4329,7 @@ dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall 0.2.16", - "smallvec 1.10.0", + "smallvec", "windows-sys 0.45.0", ] @@ -4711,22 +4341,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", - "group", - "lazy_static", - "rand 0.8.5", - "static_assertions", - "subtle", + "subtle 2.4.1", ] [[package]] @@ -4735,6 +4350,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" @@ -4787,12 +4411,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088" -[[package]] -name = "percent-encoding" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" - [[package]] name = "percent-encoding" version = "2.2.0" @@ -4836,7 +4454,7 @@ checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -4859,23 +4477,23 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "polling" -version = "2.3.0" -source = "git+https://github.com/heliaxdev/polling.git?rev=02a655775282879459a3460e2646b60c005bca2c#02a655775282879459a3460e2646b60c005bca2c" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" dependencies = [ - "autocfg 1.1.0", + "autocfg", "cfg-if 1.0.0", "libc", - "log 0.4.17", - "rustversion", + "log", "wepoll-ffi", - "winapi 0.3.9", + "windows-sys 0.42.0", ] [[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 +4557,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]] @@ -4988,7 +4606,7 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -4999,22 +4617,23 @@ checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", - "version_check 0.9.4", + "version_check", ] [[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", ] [[package]] name = "proptest" -version = "1.1.0" -source = "git+https://github.com/heliaxdev/proptest?rev=8f1b4abe7ebd35c0781bf9a00a4ee59833ffa2a1#8f1b4abe7ebd35c0781bf9a00a4ee59833ffa2a1" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" dependencies = [ "bit-set", "bitflags", @@ -5023,20 +4642,29 @@ dependencies = [ "num-traits 0.2.15", "rand 0.8.5", "rand_chacha 0.3.1", - "rand_xorshift 0.3.0", + "rand_xorshift", "regex-syntax 0.6.29", "rusty-fork", "tempfile", "unarray", ] +[[package]] +name = "proptest-state-machine" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b52a714915de2d16a5289616d2265a934780f50a9dd30359322b687403fa2ac2" +dependencies = [ + "proptest", +] + [[package]] name = "prost" version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ - "bytes 1.4.0", + "bytes", "prost-derive", ] @@ -5046,11 +4674,11 @@ version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ - "bytes 1.4.0", + "bytes", "heck", "itertools", "lazy_static", - "log 0.4.17", + "log", "multimap", "petgraph", "prettyplease 0.1.25", @@ -5106,11 +4734,11 @@ dependencies = [ [[package]] name = "ptyprocess" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69c28fcebfd842bfe19d69409fc321230ea8c1bebe31f274906485c761ce1917" +checksum = "7e05aef7befb11a210468a2d77d978dde2c6381a0381e33beb575e91f57fe8cf" dependencies = [ - "nix 0.21.2", + "nix", ] [[package]] @@ -5121,7 +4749,7 @@ checksum = "34f197a544b0c9ab3ae46c359a7ec9cbbb5c7bf97054266fecb7ead794a181d6" dependencies = [ "bitflags", "memchr", - "unicase 2.6.0", + "unicase", ] [[package]] @@ -5130,7 +4758,7 @@ version = "0.20.0" source = "git+https://github.com/heliaxdev/wasm-utils?tag=v0.20.0#782bfa7fb5e513b602e66af492cbc4cb1b06f2ba" dependencies = [ "byteorder", - "log 0.4.17", + "log", "parity-wasm", ] @@ -5147,7 +4775,7 @@ dependencies = [ "raw-cpuid", "wasi 0.10.2+wasi-snapshot-preview1", "web-sys", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -5165,37 +4793,12 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "radium" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" - [[package]] name = "radium" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.8", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc 0.1.0", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg", - "rand_xorshift 0.1.1", - "winapi 0.3.9", -] - [[package]] name = "rand" version = "0.7.3" @@ -5206,7 +4809,7 @@ dependencies = [ "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc 0.2.0", + "rand_hc", ] [[package]] @@ -5220,16 +4823,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", -] - [[package]] name = "rand_chacha" version = "0.2.2" @@ -5250,21 +4843,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.5.1" @@ -5283,15 +4861,6 @@ dependencies = [ "getrandom 0.2.9", ] -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "rand_hc" version = "0.2.0" @@ -5301,59 +4870,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi 0.3.9", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi 0.3.9", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "rand_xorshift" version = "0.3.0" @@ -5378,7 +4894,7 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" dependencies = [ - "autocfg 1.1.0", + "autocfg", "crossbeam-deque", "either", "rayon-core", @@ -5397,38 +4913,21 @@ dependencies = [ ] [[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - -[[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", "zeroize", ] -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - [[package]] name = "redox_syscall" version = "0.2.16" @@ -5464,9 +4963,9 @@ version = "0.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "571f7f397d61c4755285cd37853fe8e03271c243424a907415909379659381c5" dependencies = [ - "log 0.4.17", + "log", "rustc-hash", - "smallvec 1.10.0", + "smallvec", ] [[package]] @@ -5510,7 +5009,7 @@ dependencies = [ "bitflags", "libc", "mach", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -5524,27 +5023,27 @@ 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", + "bytes", "encoding_rs", "futures-core", "futures-util", "h2", "http", "http-body", - "hyper 0.14.26", + "hyper", "hyper-tls", "ipnet", "js-sys", - "log 0.4.17", - "mime 0.3.17", + "log", + "mime", "native-tls", "once_cell", - "percent-encoding 2.2.0", + "percent-encoding", "pin-project-lite", "serde 1.0.163", "serde_json", @@ -5552,7 +5051,7 @@ dependencies = [ "tokio", "tokio-native-tls", "tower-service", - "url 2.3.1", + "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -5588,7 +5087,7 @@ dependencies = [ "spin", "untrusted", "web-sys", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -5617,7 +5116,7 @@ version = "0.7.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" dependencies = [ - "bitvec 1.0.1", + "bitvec", "bytecheck", "hashbrown 0.12.3", "ptr_meta", @@ -5625,7 +5124,7 @@ dependencies = [ "rkyv_derive", "seahash", "tinyvec", - "uuid 1.3.2", + "uuid 1.3.3", ] [[package]] @@ -5665,7 +5164,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb" dependencies = [ "libc", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -5681,7 +5180,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", ] @@ -5714,15 +5213,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - [[package]] name = "rustc_version" version = "0.3.3" @@ -5762,7 +5252,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" dependencies = [ "base64 0.13.1", - "log 0.4.17", + "log", "ring", "sct 0.6.1", "webpki 0.21.4", @@ -5774,7 +5264,7 @@ version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ - "log 0.4.17", + "log", "ring", "sct 0.7.0", "webpki 0.22.0", @@ -5884,12 +5374,6 @@ dependencies = [ "safe-regex-compiler", ] -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - [[package]] name = "same-file" version = "1.0.6" @@ -5899,30 +5383,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" @@ -5938,7 +5398,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" dependencies = [ - "parking_lot 0.12.1", + "parking_lot", ] [[package]] @@ -5981,19 +5441,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 +5453,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" @@ -6047,22 +5489,13 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser 0.7.0", -] - [[package]] name = "semver" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ - "semver-parser 0.10.2", + "semver-parser", ] [[package]] @@ -6074,12 +5507,6 @@ dependencies = [ "serde 1.0.163", ] -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "semver-parser" version = "0.10.2" @@ -6143,7 +5570,7 @@ checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -6165,7 +5592,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -6192,18 +5619,6 @@ dependencies = [ "yaml-rust", ] -[[package]] -name = "sha-1" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - [[package]] name = "sha-1" version = "0.9.8" @@ -6228,6 +5643,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" @@ -6339,16 +5766,16 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] -name = "smallvec" -version = "0.6.14" +name = "slip10_ed25519" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" +checksum = "4be0ff28bf14f9610a342169084e87a4f435ad798ec528dc7579a3678fa9dc9a" dependencies = [ - "maybe-uninit", + "hmac-sha512", ] [[package]] @@ -6364,7 +5791,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -6373,7 +5800,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 +5865,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 +5905,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", @@ -6498,7 +5931,7 @@ dependencies = [ "libc", "ntapi", "once_cell", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -6543,45 +5976,17 @@ 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" -source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=4db3c5ea09fae4057008d22bf9e96bf541b55b35#4db3c5ea09fae4057008d22bf9e96bf541b55b35" +source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=02b256829e80f8cfecf3fa0d625c2a76c79cd043#02b256829e80f8cfecf3fa0d625c2a76c79cd043" dependencies = [ "async-trait", - "bytes 1.4.0", + "bytes", "ed25519", "ed25519-dalek", "flex-error", - "futures 0.3.28", + "futures", "k256", "num-traits 0.2.15", "once_cell", @@ -6594,108 +5999,65 @@ dependencies = [ "serde_repr", "sha2 0.9.9", "signature", - "subtle", + "subtle 2.4.1", "subtle-encoding", - "tendermint-proto 0.23.6", - "time 0.3.17", + "tendermint-proto", + "time", "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" -source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=4db3c5ea09fae4057008d22bf9e96bf541b55b35#4db3c5ea09fae4057008d22bf9e96bf541b55b35" +source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=02b256829e80f8cfecf3fa0d625c2a76c79cd043#02b256829e80f8cfecf3fa0d625c2a76c79cd043" dependencies = [ "flex-error", "serde 1.0.163", "serde_json", - "tendermint 0.23.6", + "tendermint", "toml", - "url 2.3.1", + "url", ] [[package]] name = "tendermint-light-client" version = "0.23.6" -source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=4db3c5ea09fae4057008d22bf9e96bf541b55b35#4db3c5ea09fae4057008d22bf9e96bf541b55b35" +source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=02b256829e80f8cfecf3fa0d625c2a76c79cd043#02b256829e80f8cfecf3fa0d625c2a76c79cd043" dependencies = [ "contracts", "crossbeam-channel 0.4.4", "derive_more", "flex-error", - "futures 0.3.28", + "futures", "serde 1.0.163", "serde_cbor", "serde_derive", "static_assertions", - "tendermint 0.23.6", - "tendermint-light-client-verifier 0.23.6", - "tendermint-rpc 0.23.6", - "time 0.3.17", + "tendermint", + "tendermint-light-client-verifier", + "tendermint-rpc", + "time", "tokio", ] [[package]] name = "tendermint-light-client-verifier" -version = "0.23.5" -source = "git+https://github.com/heliaxdev/tendermint-rs?rev=a3a0ad5f07d380976bbd5321239aec9cc3a8f916#a3a0ad5f07d380976bbd5321239aec9cc3a8f916" +version = "0.23.6" +source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=02b256829e80f8cfecf3fa0d625c2a76c79cd043#02b256829e80f8cfecf3fa0d625c2a76c79cd043" dependencies = [ "derive_more", "flex-error", "serde 1.0.163", - "tendermint 0.23.5", - "tendermint-rpc 0.23.5", - "time 0.3.17", + "tendermint", + "time", ] [[package]] -name = "tendermint-light-client-verifier" +name = "tendermint-proto" version = "0.23.6" -source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=4db3c5ea09fae4057008d22bf9e96bf541b55b35#4db3c5ea09fae4057008d22bf9e96bf541b55b35" +source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=02b256829e80f8cfecf3fa0d625c2a76c79cd043#02b256829e80f8cfecf3fa0d625c2a76c79cd043" 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", - "time 0.3.17", -] - -[[package]] -name = "tendermint-proto" -version = "0.23.6" -source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=4db3c5ea09fae4057008d22bf9e96bf541b55b35#4db3c5ea09fae4057008d22bf9e96bf541b55b35" -dependencies = [ - "bytes 1.4.0", + "bytes", "flex-error", "num-derive", "num-traits 0.2.15", @@ -6704,46 +6066,22 @@ dependencies = [ "serde 1.0.163", "serde_bytes", "subtle-encoding", - "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", + "time", ] [[package]] name = "tendermint-rpc" version = "0.23.6" -source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=4db3c5ea09fae4057008d22bf9e96bf541b55b35#4db3c5ea09fae4057008d22bf9e96bf541b55b35" +source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=02b256829e80f8cfecf3fa0d625c2a76c79cd043#02b256829e80f8cfecf3fa0d625c2a76c79cd043" dependencies = [ "async-trait", "async-tungstenite", - "bytes 1.4.0", + "bytes", "flex-error", - "futures 0.3.28", + "futures", "getrandom 0.2.9", "http", - "hyper 0.14.26", + "hyper", "hyper-proxy", "hyper-rustls", "peg", @@ -6752,37 +6090,22 @@ 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", + "time", "tokio", "tracing 0.1.37", - "url 2.3.1", + "url", "uuid 0.8.2", "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" -source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=4db3c5ea09fae4057008d22bf9e96bf541b55b35#4db3c5ea09fae4057008d22bf9e96bf541b55b35" +source = "git+https://github.com/heliaxdev/tendermint-rs.git?rev=02b256829e80f8cfecf3fa0d625c2a76c79cd043#02b256829e80f8cfecf3fa0d625c2a76c79cd043" dependencies = [ "ed25519-dalek", "gumdrop", @@ -6790,8 +6113,8 @@ dependencies = [ "serde_json", "simple-error", "tempfile", - "tendermint 0.23.6", - "time 0.3.17", + "tendermint", + "time", ] [[package]] @@ -6846,7 +6169,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -6869,16 +6192,6 @@ dependencies = [ "libc", ] -[[package]] -name = "time" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" -dependencies = [ - "libc", - "winapi 0.3.9", -] - [[package]] name = "time" version = "0.3.17" @@ -6905,6 +6218,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 +6255,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]] @@ -6942,7 +6286,7 @@ dependencies = [ "ascii", "chunked_transfer", "httpdate", - "log 0.4.17", + "log", ] [[package]] @@ -6966,12 +6310,12 @@ version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" dependencies = [ - "autocfg 1.1.0", - "bytes 1.4.0", + "autocfg", + "bytes", "libc", - "mio 0.8.6", + "mio", "num_cpus", - "parking_lot 0.12.1", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", @@ -6979,38 +6323,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "tokio-codec" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "tokio-io", -] - -[[package]] -name = "tokio-executor" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" -dependencies = [ - "crossbeam-utils 0.7.2", - "futures 0.1.31", -] - -[[package]] -name = "tokio-io" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "log 0.4.17", -] - [[package]] name = "tokio-io-timeout" version = "1.2.0" @@ -7029,7 +6341,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -7042,25 +6354,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-reactor" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" -dependencies = [ - "crossbeam-utils 0.7.2", - "futures 0.1.31", - "lazy_static", - "log 0.4.17", - "mio 0.6.23", - "num_cpus", - "parking_lot 0.9.0", - "slab", - "tokio-executor", - "tokio-io", - "tokio-sync", -] - [[package]] name = "tokio-rustls" version = "0.22.0" @@ -7094,30 +6387,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-sync" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" -dependencies = [ - "fnv", - "futures 0.1.31", -] - -[[package]] -name = "tokio-tcp" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "iovec", - "mio 0.6.23", - "tokio-io", - "tokio-reactor", -] - [[package]] name = "tokio-test" version = "0.4.2" @@ -7125,33 +6394,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53474327ae5e166530d17f2d956afcb4f8a004de581b3cae10f12006bc8163e3" dependencies = [ "async-stream", - "bytes 1.4.0", + "bytes", "futures-core", "tokio", "tokio-stream", ] -[[package]] -name = "tokio-tls" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "354b8cd83825b3c20217a9dc174d6a0c67441a2fae5c41bcb1ea6679f6ae0f7c" -dependencies = [ - "futures 0.1.31", - "native-tls", - "tokio-io", -] - [[package]] name = "tokio-util" version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ - "bytes 1.4.0", + "bytes", "futures-core", "futures-sink", - "log 0.4.17", + "log", "pin-project-lite", "tokio", ] @@ -7162,7 +6420,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ - "bytes 1.4.0", + "bytes", "futures-core", "futures-sink", "pin-project-lite", @@ -7181,15 +6439,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", @@ -7206,15 +6464,15 @@ dependencies = [ "async-trait", "axum", "base64 0.13.1", - "bytes 1.4.0", + "bytes", "futures-core", "futures-util", "h2", "http", "http-body", - "hyper 0.14.26", + "hyper", "hyper-timeout", - "percent-encoding 2.2.0", + "percent-encoding", "pin-project", "prost", "prost-derive", @@ -7268,31 +6526,13 @@ dependencies = [ [[package]] name = "tower-abci" version = "0.1.0" -source = "git+https://github.com/heliaxdev/tower-abci.git?rev=79069a441cee7d9955a3d826d29656a0fb16115c#79069a441cee7d9955a3d826d29656a0fb16115c" +source = "git+https://github.com/heliaxdev/tower-abci.git?rev=367d8d958b83c501ed2c09e9c4595f8bf75a0b01#367d8d958b83c501ed2c09e9c4595f8bf75a0b01" dependencies = [ - "bytes 1.4.0", - "futures 0.3.28", + "bytes", + "futures", "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", @@ -7340,7 +6580,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if 1.0.0", - "log 0.4.17", + "log", "pin-project-lite", "tracing-attributes 0.1.24", "tracing-core 0.1.31", @@ -7364,7 +6604,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] @@ -7421,7 +6661,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" dependencies = [ "lazy_static", - "log 0.4.17", + "log", "tracing-core 0.1.31", ] @@ -7459,7 +6699,6 @@ dependencies = [ "serde 1.0.163", "serde_json", "sharded-slab", - "smallvec 1.10.0", "thread_local", "tracing 0.1.37", "tracing-core 0.1.31", @@ -7472,7 +6711,7 @@ name = "tracing-tower" version = "0.1.0" source = "git+https://github.com/tokio-rs/tracing/?tag=tracing-0.1.30#df4ba17d857db8ba1b553f7b293ac8ba967a42f8" dependencies = [ - "futures 0.3.28", + "futures", "pin-project-lite", "tower-layer", "tower-make", @@ -7481,12 +6720,6 @@ dependencies = [ "tracing-futures 0.2.5 (git+https://github.com/tokio-rs/tracing/?tag=tracing-0.1.30)", ] -[[package]] -name = "traitobject" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" - [[package]] name = "triomphe" version = "0.1.8" @@ -7507,23 +6740,17 @@ checksum = "8ada8297e8d70872fa9a551d93250a9f407beb9f37ef86494eb20012a2ff7c24" dependencies = [ "base64 0.13.1", "byteorder", - "bytes 1.4.0", + "bytes", "http", "httparse", "input_buffer", - "log 0.4.17", + "log", "rand 0.8.5", - "sha-1 0.9.8", - "url 2.3.1", + "sha-1", + "url", "utf-8", ] -[[package]] -name = "typeable" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" - [[package]] name = "typenum" version = "1.16.0" @@ -7543,7 +6770,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ "byteorder", - "crunchy 0.2.2", + "crunchy", "hex", "static_assertions", ] @@ -7554,22 +6781,13 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" -[[package]] -name = "unicase" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" -dependencies = [ - "version_check 0.1.5", -] - [[package]] name = "unicase" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" dependencies = [ - "version_check 0.9.4", + "version_check", ] [[package]] @@ -7607,12 +6825,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]] @@ -7621,17 +6839,6 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" -[[package]] -name = "url" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" -dependencies = [ - "idna 0.1.5", - "matches", - "percent-encoding 1.0.1", -] - [[package]] name = "url" version = "2.3.1" @@ -7639,8 +6846,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", - "idna 0.3.0", - "percent-encoding 2.2.0", + "idna", + "percent-encoding", ] [[package]] @@ -7663,9 +6870,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", ] @@ -7678,13 +6885,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.0.0-alpha.9" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" -dependencies = [ - "ctor", - "version_check 0.9.4", -] +checksum = "a4d330786735ea358f3bc09eea4caa098569c1c93f342d9aca0514915022fe7e" [[package]] name = "vcpkg" @@ -7698,12 +6901,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" - [[package]] name = "version_check" version = "0.9.4" @@ -7791,7 +6988,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" dependencies = [ - "log 0.4.17", + "log", "try-lock", ] @@ -7815,9 +7012,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 +7022,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", + "log", "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 +7049,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 +7059,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", ] @@ -7911,7 +7108,7 @@ dependencies = [ "wasmer-types", "wasmer-vm", "wat", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -7937,7 +7134,7 @@ dependencies = [ "rkyv", "serde 1.0.163", "serde_bytes", - "smallvec 1.10.0", + "smallvec", "target-lexicon", "thiserror", "wasmer-types", @@ -7958,7 +7155,7 @@ dependencies = [ "loupe", "more-asserts", "rayon", - "smallvec 1.10.0", + "smallvec", "target-lexicon", "tracing 0.1.37", "wasmer-compiler", @@ -7979,7 +7176,7 @@ dependencies = [ "loupe", "more-asserts", "rayon", - "smallvec 1.10.0", + "smallvec", "wasmer-compiler", "wasmer-types", "wasmer-vm", @@ -8061,7 +7258,7 @@ dependencies = [ "wasmer-engine", "wasmer-types", "wasmer-vm", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -8109,7 +7306,7 @@ dependencies = [ "serde 1.0.163", "thiserror", "wasmer-types", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -8126,9 +7323,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 +7335,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", @@ -8193,47 +7390,6 @@ dependencies = [ "webpki 0.22.0", ] -[[package]] -name = "websocket" -version = "0.26.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92aacab060eea423e4036820ddd28f3f9003b2c4d8048cbda985e5a14e18038d" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "hyper 0.10.16", - "native-tls", - "rand 0.6.5", - "tokio-codec", - "tokio-io", - "tokio-reactor", - "tokio-tcp", - "tokio-tls", - "unicase 1.4.2", - "url 1.7.2", - "websocket-base", -] - -[[package]] -name = "websocket-base" -version = "0.26.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49aec794b07318993d1db16156d5a9c750120597a5ee40c6b928d416186cb138" -dependencies = [ - "base64 0.10.1", - "bitflags", - "byteorder", - "bytes 0.4.12", - "futures 0.1.31", - "native-tls", - "rand 0.6.5", - "sha-1 0.8.2", - "tokio-codec", - "tokio-io", - "tokio-tcp", - "tokio-tls", -] - [[package]] name = "wepoll-ffi" version = "0.1.2" @@ -8254,12 +7410,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - [[package]] name = "winapi" version = "0.3.9" @@ -8270,12 +7420,6 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -8288,7 +7432,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -8299,15 +7443,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.29.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac7fef12f4b59cd0a29339406cc9203ab44e440ddff6b3f5a41455349fa9cf3" +checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" dependencies = [ - "windows_aarch64_msvc 0.29.0", - "windows_i686_gnu 0.29.0", - "windows_i686_msvc 0.29.0", - "windows_x86_64_gnu 0.29.0", - "windows_x86_64_msvc 0.29.0", + "windows-targets 0.42.2", ] [[package]] @@ -8394,12 +7534,6 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" -[[package]] -name = "windows_aarch64_msvc" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d027175d00b01e0cbeb97d6ab6ebe03b12330a35786cbaca5252b1c4bf5d9b" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -8412,12 +7546,6 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" -[[package]] -name = "windows_i686_gnu" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8793f59f7b8e8b01eda1a652b2697d87b93097198ae85f823b969ca5b89bba58" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -8430,12 +7558,6 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" -[[package]] -name = "windows_i686_msvc" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8602f6c418b67024be2996c512f5f995de3ba417f4c75af68401ab8756796ae4" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -8448,12 +7570,6 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" -[[package]] -name = "windows_x86_64_gnu" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d615f419543e0bd7d2b3323af0d86ff19cbc4f816e6453f36a2c2ce889c354" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -8478,12 +7594,6 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" -[[package]] -name = "windows_x86_64_msvc" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d95421d9ed3672c280884da53201a5c46b7b2765ca6faf34b0d71cf34a3561" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -8511,26 +7621,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - -[[package]] -name = "wyz" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "129e027ad65ce1453680623c3fb5163cbf7107bfe1aa32257e7d0e63f9ced188" -dependencies = [ - "tap", + "winapi", ] [[package]] @@ -8563,83 +7654,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", - "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 +7677,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.16", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6c9c630d82..317dd335e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,40 +21,129 @@ exclude = [ "wasm_for_tests", ] +[workspace.package] +authors = ["Heliax AG "] +edition = "2021" +documentation = "https://docs.namada.net/" +homepage = "https://namada.net/" +keywords = ["blockchain", "privacy", "crypto", "protocol", "network"] +license = "GPL-3.0" +readme = "README.md" +repository = "https://github.com/anoma/namada" +version = "0.17.5" + +[workspace.dependencies] +ark-bls12-381 = {version = "0.3"} +ark-serialize = {version = "0.3"} +ark-std = "0.3.0" +# 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"]} +assert_cmd = "1.0.7" +assert_matches = "1.5.0" +async-std = "1.11.0" +async-trait = {version = "0.1.51"} +base58 = "0.2.0" +base64 = "0.13.0" +bech32 = "0.8.0" +bimap = {version = "0.6.2", features = ["serde"]} +bit-set = "0.5.2" +blake2b-rs = "0.2.0" +byte-unit = "4.0.13" +byteorder = "1.4.2" +borsh = "0.9.0" +chrono = {version = "0.4.22", default-features = false, features = ["clock", "std"]} +circular-queue = "0.2.6" +clap = {git = "https://github.com/clap-rs/clap/", tag = "v3.0.0-beta.2", default-features = false, features = ["std", "suggestions", "color", "cargo"]} +clru = {git = "https://github.com/marmeladema/clru-rs.git", rev = "71ca566"} +color-eyre = "0.5.10" +concat-idents = "1.1.2" +config = "0.11.0" +data-encoding = "2.3.2" +derivation-path = "0.2.0" +derivative = "2.2.0" +directories = "4.0.1" +ed25519-consensus = "1.2.0" +escargot = "0.5.7" +expectrl = "0.7.0" +eyre = "0.6.5" +ferveo = {git = "https://github.com/anoma/ferveo", rev = "e5abd0acc938da90140351a65a26472eb495ce4d"} +ferveo-common = {git = "https://github.com/anoma/ferveo", rev = "e5abd0acc938da90140351a65a26472eb495ce4d"} +file-lock = "2.0.2" +file-serve = "0.2.0" +flate2 = "1.0.22" +fs_extra = "1.2.0" +futures = "0.3" +git2 = "0.13.25" +ibc-relayer = {git = "https://github.com/heliaxdev/hermes.git", rev = "a4ad1355fc0b05908881854aa27221cb2b878ac5", default-features = false} +ibc-relayer-types = {git = "https://github.com/heliaxdev/hermes.git", rev = "a4ad1355fc0b05908881854aa27221cb2b878ac5", default-features = false} +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" +libc = "0.2.97" +libloading = "0.7.2" +libsecp256k1 = {git = "https://github.com/heliaxdev/libsecp256k1", rev = "bbb3bd44a49db361f21d9db80f9a087c194c0ae9", default-features = false, features = ["std", "static-context"]} +# 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"] } +num_cpus = "1.13.0" +num-derive = "0.3.3" +num-rational = "0.4.1" +num-traits = "0.2.14" +once_cell = "1.8.0" +orion = "0.16.0" +paste = "1.0.9" +pretty_assertions = "0.7.2" +proptest = "1.2.0" +proptest-state-machine = "0.1.0" +prost = "0.11.6" +prost-types = "0.11.6" +rand = {version = "0.8", default-features = false} +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" +rust_decimal = { version = "=1.26.1", features = ["borsh"] } +rust_decimal_macros = "=1.26.1" +serde = {version = "1.0.125", features = ["derive"]} +serde_bytes = "0.11.5" +serde_json = "1.0.62" +sha2 = "0.9.3" +signal-hook = "0.3.9" +slip10_ed25519 = "0.1.3" +# sysinfo with disabled multithread feature +sysinfo = {version = "=0.21.1", default-features = false} +tar = "0.4.37" +tempfile = {version = "3.2.0"} +tendermint-config = {git = "https://github.com/heliaxdev/tendermint-rs.git", rev = "02b256829e80f8cfecf3fa0d625c2a76c79cd043"} +test-log = {version = "0.2.7", default-features = false, features = ["trace"]} +tiny-bip39 = {git = "https://github.com/anoma/tiny-bip39.git", rev = "bf0f6d8713589b83af7a917366ec31f5275c0e57"} +tiny-hderive = "0.3.0" +thiserror = "1.0.38" +tokio = {version = "1.8.2", default-features = false} +tokio-test = "0.4.2" +toml = "0.5.8" +tonic = "0.8.3" +tonic-build = "0.8.4" +tower = "0.4" +# Also, using the same version of tendermint-rs as we do here. +tower-abci = {git = "https://github.com/heliaxdev/tower-abci.git", rev = "367d8d958b83c501ed2c09e9c4595f8bf75a0b01"} +tracing = "0.1.30" +tracing-log = "0.1.2" +tracing-subscriber = {version = "0.3.7", default-features = false, features = ["env-filter", "fmt"]} +wasmparser = "0.83.0" +winapi = "0.3.9" +zeroize = {version = "1.5.5", features = ["zeroize_derive"]} + [patch.crates-io] # TODO temp patch for , and more tba. borsh = {git = "https://github.com/heliaxdev/borsh-rs.git", rev = "cd5223e5103c4f139e0c54cf8259b7ec5ec4073a"} borsh-derive = {git = "https://github.com/heliaxdev/borsh-rs.git", rev = "cd5223e5103c4f139e0c54cf8259b7ec5ec4073a"} borsh-derive-internal = {git = "https://github.com/heliaxdev/borsh-rs.git", rev = "cd5223e5103c4f139e0c54cf8259b7ec5ec4073a"} borsh-schema-derive-internal = {git = "https://github.com/heliaxdev/borsh-rs.git", rev = "cd5223e5103c4f139e0c54cf8259b7ec5ec4073a"} -# The following 3 crates patch a work-around for https://github.com/smol-rs/polling/issues/38 breaking namada tooling build with nightly 2022-05-20 -polling = {git = "https://github.com/heliaxdev/polling.git", rev = "02a655775282879459a3460e2646b60c005bca2c"} -async-io = {git = "https://github.com/heliaxdev/async-io.git", rev = "9285dad39c9a37ecd0dbd498c5ce5b0e65b02489"} -async-process = {git = "https://github.com/heliaxdev/async-process.git", rev = "e42c527e87d937da9e01aaeb563c0b948580dc89"} -# borsh = {path = "../borsh-rs/borsh"} -# borsh-derive = {path = "../borsh-rs/borsh-derive"} -# borsh-derive-internal = {path = "../borsh-rs/borsh-derive-internal"} -# borsh-schema-derive-internal = {path = "../borsh-rs/borsh-schema-derive-internal"} - -# patched to a commit on the `eth-bridge-integration+consensus-timeout` branch of our fork -tendermint = {git = "https://github.com/heliaxdev/tendermint-rs.git", rev = "4db3c5ea09fae4057008d22bf9e96bf541b55b35"} -tendermint-config = {git = "https://github.com/heliaxdev/tendermint-rs.git", rev = "4db3c5ea09fae4057008d22bf9e96bf541b55b35"} -tendermint-proto = {git = "https://github.com/heliaxdev/tendermint-rs.git", rev = "4db3c5ea09fae4057008d22bf9e96bf541b55b35"} -tendermint-rpc = {git = "https://github.com/heliaxdev/tendermint-rs.git", rev = "4db3c5ea09fae4057008d22bf9e96bf541b55b35"} -tendermint-testgen = {git = "https://github.com/heliaxdev/tendermint-rs.git", rev = "4db3c5ea09fae4057008d22bf9e96bf541b55b35"} -tendermint-light-client-verifier = {git = "https://github.com/heliaxdev/tendermint-rs.git", rev = "4db3c5ea09fae4057008d22bf9e96bf541b55b35"} - -# patched to a commit on the `eth-bridge-integration` branch of our fork -ibc = {git = "https://github.com/heliaxdev/cosmos-ibc-rs.git", rev = "2d7edc16412b60cabf78163fe24a6264e11f77a9"} -ibc-proto = {git = "https://github.com/heliaxdev/ibc-proto-rs.git", rev = "7e527b5b8c95d83351e93ceafc14ac853224283f"} -ibc-relayer = {git = "https://github.com/heliaxdev/hermes.git", rev = "8e2ff3479edc0653f34b22df450d451eedd2c2ab"} -ibc-relayer-types = {git = "https://github.com/heliaxdev/hermes.git", rev = "8e2ff3479edc0653f34b22df450d451eedd2c2ab"} - -# patched to a commit on the `eth-bridge-integration` branch of our fork -tower-abci = {git = "https://github.com/heliaxdev/tower-abci.git", rev = "79069a441cee7d9955a3d826d29656a0fb16115c"} - -# patched to the yanked 1.2.0 until masp updates bitvec -funty = { git = "https://github.com/bitvecto-rs/funty/", rev = "7ef0d890fbcd8b3def1635ac1a877fc298488446" } [profile.release] lto = true diff --git a/Makefile b/Makefile index d42b3fe3ec..6a63da44ec 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,13 @@ package = namada +# Some env vars defaults if not specified +NAMADA_E2E_USE_PREBUILT_BINARIES ?= true +NAMADA_E2E_DEBUG ?= true +RUST_BACKTRACE ?= 1 + cargo := $(env) cargo rustup := $(env) rustup -debug-env := RUST_BACKTRACE=1 RUST_LOG=$(package)=debug +debug-env := RUST_BACKTRACE=$(RUST_BACKTRACE) RUST_LOG=$(package)=debug debug-cargo := $(env) $(debug-env) cargo # Nightly build is currently used for rustfmt and clippy. nightly := $(shell cat rust-nightly-version) @@ -16,11 +21,24 @@ wasm_templates := wasm/tx_template wasm/vp_template # TODO upgrade libp2p audit-ignores += RUSTSEC-2021-0076 +# Workspace crates +crates := namada_core +crates += namada +crates += namada_apps +crates += namada_encoding_spec +crates += namada_macros +crates += namada_proof_of_stake +crates += namada_test_utils +crates += namada_tests +crates += namada_tx_prelude +crates += namada_vm_env +crates += namada_vp_prelude + build: $(cargo) build build-test: - $(cargo) +$(nightly) build --tests -Z unstable-options + $(cargo) build --tests build-release: NAMADA_DEV=false $(cargo) build --release --package namada_apps --manifest-path Cargo.toml @@ -41,18 +59,13 @@ 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" +# Check that every crate can be built with default features +check-crates: + $(foreach p,$(crates), echo "Checking $(p)"; cargo +$(nightly) check -Z unstable-options --tests -p $(p) && ) true + clippy-wasm = $(cargo) +$(nightly) clippy --manifest-path $(wasm)/Cargo.toml --all-targets -- -D warnings clippy: @@ -61,37 +74,21 @@ 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 clippy-fix: $(cargo) +$(nightly) clippy --fix -Z unstable-options --all-targets --allow-dirty --allow-staged -install: tendermint - NAMADA_DEV=false $(cargo) install --path ./apps --locked - tendermint: ./scripts/get_tendermint.sh +install: cometbft + NAMADA_DEV=false $(cargo) install --path ./apps --locked + +cometbft: + ./scripts/get_cometbft.sh + run-ledger: # runs the node $(cargo) run --bin namadan -- ledger run @@ -109,56 +106,29 @@ audit: test: test-unit test-e2e test-wasm -# NOTE: `unstable-options` are used twice for all unit tests - 1st to compile -# with allowing to use unstable features in test, 2nd to run with `report-time` test-unit-coverage: $(cargo) +$(nightly) llvm-cov --output-dir target \ --features namada/testing \ --html \ - -Z unstable-options \ -- --skip e2e -Z unstable-options --report-time +# NOTE: `TEST_FILTER` is prepended with `e2e::`. Since filters in `cargo test` +# work with a substring search, TEST_FILTER only works if it contains a string +# that directly follows `e2e::`, e.g. `TEST_FILTER=multitoken_tests` would run +# all tests that start with `e2e::multitoken_tests`. test-e2e: - RUST_BACKTRACE=1 $(cargo) test e2e \ - -Z unstable-options \ - -- \ - --test-threads=1 \ - -Z unstable-options --report-time - -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 + NAMADA_E2E_USE_PREBUILT_BINARIES=$(NAMADA_E2E_USE_PREBUILT_BINARIES) \ + NAMADA_E2E_DEBUG=$(NAMADA_E2E_DEBUG) \ + RUST_BACKTRACE=$(RUST_BACKTRACE) \ + $(cargo) +$(nightly) test e2e::$(TEST_FILTER) \ + -Z unstable-options \ + -- \ + --test-threads=1 \ + -Z unstable-options --report-time test-unit: $(cargo) +$(nightly) test \ $(TEST_FILTER) \ - -Z unstable-options \ -- --skip e2e \ -Z unstable-options --report-time @@ -166,14 +136,12 @@ test-unit-mainnet: $(cargo) +$(nightly) test \ --features "mainnet" \ $(TEST_FILTER) \ - -Z unstable-options \ -- --skip e2e \ -Z unstable-options --report-time test-unit-debug: $(debug-cargo) +$(nightly) test \ $(TEST_FILTER) -- \ - -Z unstable-options \ -- --skip e2e \ --nocapture \ -Z unstable-options --report-time @@ -181,7 +149,7 @@ test-unit-debug: test-wasm: make -C $(wasms) test -test-wasm-template = $(cargo) test \ +test-wasm-template = $(cargo) +$(nightly) test \ --manifest-path $(wasm)/Cargo.toml \ -- \ -Z unstable-options --report-time @@ -189,8 +157,7 @@ test-wasm-templates: $(foreach wasm,$(wasm_templates),$(test-wasm-template) && ) true test-debug: - $(debug-cargo) test \ - -Z unstable-options \ + $(debug-cargo) +$(nightly) test \ -- \ --nocapture \ -Z unstable-options --report-time @@ -266,4 +233,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/README.md b/README.md index a3c45f712e..10b2230009 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,7 @@ interaction with the protocol. ## 📓 Docs -* user docs: built from [docs mdBook](./documentation/docs/) * dev docs: built from [dev mdBook](./documentation/dev/) -* specifications: built from [specs mdBook](./documentation/specs/) ## Warning diff --git a/apps/Cargo.toml b/apps/Cargo.toml index a6439fbb35..35b5483d7c 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -1,13 +1,17 @@ [package] -authors = ["Heliax AG "] -description = "Namada apps" -edition = "2021" -license = "GPL-3.0" name = "namada_apps" -readme = "../README.md" +description = "Namada CLI apps" resolver = "2" -version = "0.16.0" default-run = "namada" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -45,125 +49,95 @@ 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", - "tendermint", - "tendermint-config", - "tendermint-rpc", - "tendermint-proto", - "tower-abci", ] [dependencies] -namada = {path = "../shared", default-features = false, features = ["wasm-runtime", "ferveo-tpke", "masp-tx-gen"]} -ark-serialize = "0.3.0" -ark-std = "0.3.0" -# branch = "bat/arse-merkle-tree" -arse-merkle-tree = {package = "sparse-merkle-tree", git = "https://github.com/heliaxdev/sparse-merkle-tree", rev = "e086b235ed6e68929bf73f617dd61cd17b000a56", features = ["std", "borsh"]} -assert_matches = "1.5.0" -async-std = {version = "=1.11.0", features = ["unstable"]} -async-trait = "0.1.51" -base64 = "0.13.0" -bech32 = "0.8.0" -blake2b-rs = "0.2.0" -borsh = "0.9.0" -byte-unit = "4.0.13" -byteorder = "1.4.2" -# https://github.com/clap-rs/clap/issues/1037 -clap = {git = "https://github.com/clap-rs/clap/", tag = "v3.0.0-beta.2", default-features = false, features = ["std", "suggestions", "color", "cargo"]} -color-eyre = "0.5.10" -config = "0.11.0" -data-encoding = "2.3.2" -derivative = "2.2.0" -ed25519-consensus = "1.2.0" -ferveo = {git = "https://github.com/anoma/ferveo", rev = "e5abd0acc938da90140351a65a26472eb495ce4d"} -ferveo-common = {git = "https://github.com/anoma/ferveo", rev = "e5abd0acc938da90140351a65a26472eb495ce4d"} -eyre = "0.6.5" -flate2 = "1.0.22" -file-lock = "2.0.2" -futures = "0.3" -itertools = "0.10.1" -libc = "0.2.97" -libloading = "0.7.2" -num-derive = "0.3.3" -num-rational = "0.4.1" -num-traits = "0.2.14" -num_cpus = "1.13.0" -once_cell = "1.8.0" -orion = "0.16.0" -prost = "0.11.6" -prost-types = "0.11.6" -rand = {version = "0.8", default-features = false} -rand_core = {version = "0.6", default-features = false} -rayon = "=1.5.3" -regex = "1.4.5" -reqwest = "0.11.4" -rlimit = "0.5.4" -rocksdb = {version = "0.21.0", features = ['zstd', 'jemalloc'], default-features = false} -rpassword = "5.0.1" -serde = {version = "1.0.125", features = ["derive"]} -serde_bytes = "0.11.5" -serde_json = {version = "1.0.62", features = ["raw_value"]} -sha2 = "0.9.3" -signal-hook = "0.3.9" -# sysinfo with disabled multithread feature -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 = "4db3c5ea09fae4057008d22bf9e96bf541b55b35", optional = true} -tendermint-config-abcipp = {package = "tendermint-config", git = "https://github.com/heliaxdev/tendermint-rs", rev = "4db3c5ea09fae4057008d22bf9e96bf541b55b35", optional = true} -tendermint-proto-abcipp = {package = "tendermint-proto", git = "https://github.com/heliaxdev/tendermint-rs", rev = "4db3c5ea09fae4057008d22bf9e96bf541b55b35", optional = true} -tendermint-rpc-abcipp = {package = "tendermint-rpc", git = "https://github.com/heliaxdev/tendermint-rs", rev = "4db3c5ea09fae4057008d22bf9e96bf541b55b35", 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} -tendermint-rpc = {version = "0.23.6", features = ["http-client", "websocket-client"], optional = true} -thiserror = "1.0.38" -tokio = {version = "1.8.2", features = ["full"]} -toml = "0.5.8" -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"] } -bimap = {version = "0.6.2", features = ["serde"]} -rust_decimal = "=1.26.1" -rust_decimal_macros = "=1.26.1" -directories = "4.0.1" +namada = {path = "../shared", features = ["ferveo-tpke", "masp-tx-gen", "multicore", "http-client"]} +ark-serialize.workspace = true +ark-std.workspace = true +arse-merkle-tree = { workspace = true, features = ["blake2b"] } +assert_matches.workspace = true +async-std = {workspace = true, features = ["unstable"]} +async-trait.workspace = true +base64.workspace = true +bech32.workspace = true +bimap.workspace = true +blake2b-rs.workspace = true +borsh.workspace = true +byte-unit.workspace = true +byteorder.workspace = true +clap.workspace = true +color-eyre.workspace = true +config.workspace = true +data-encoding.workspace = true +derivative.workspace = true +directories.workspace = true +ed25519-consensus.workspace = true +eyre.workspace = true +ferveo-common.workspace = true +ferveo.workspace = true +file-lock.workspace = true +flate2.workspace = true +futures.workspace = true +itertools.workspace = true +libc.workspace = true +libloading.workspace = true +masp_primitives = { workspace = true, features = ["transparent-inputs"] } +masp_proofs = { workspace = true, features = ["bundled-prover", "download-params"] } +num_cpus.workspace = true +num-derive.workspace = true +num-rational.workspace = true +num-traits.workspace = true +once_cell.workspace = true +orion.workspace = true +prost-types.workspace = true +prost.workspace = true +rand_core.workspace = true +rand.workspace = true +rayon.workspace = true +regex.workspace = true +reqwest.workspace = true +ripemd.workspace = true +rlimit.workspace = true +rocksdb.workspace = true +rpassword.workspace = true +rust_decimal_macros.workspace = true +rust_decimal.workspace = true +serde_bytes.workspace = true +serde_json = {workspace = true, features = ["raw_value"]} +serde.workspace = true +sha2.workspace = true +signal-hook.workspace = true +sysinfo.workspace = true +tar.workspace = true +tendermint-config.workspace = true +thiserror.workspace = true +tokio = {workspace = true, features = ["full"]} +toml.workspace = true +tonic.workspace = true +tower-abci.workspace = true +tower.workspace = true +tracing-log.workspace = true +tracing-subscriber = { workspace = true, features = ["std", "json", "ansi", "tracing-log"]} +tracing.workspace = true +winapi.workspace = true +zeroize.workspace = true [dev-dependencies] namada = {path = "../shared", default-features = false, features = ["testing", "wasm-runtime"]} namada_test_utils = {path = "../test_utils"} -bit-set = "0.5.2" -# A fork with state machime testing -proptest = {git = "https://github.com/heliaxdev/proptest", rev = "8f1b4abe7ebd35c0781bf9a00a4ee59833ffa2a1"} -tempfile = "3.2.0" -test-log = {version = "0.2.7", default-features = false, features = ["trace"]} -tokio-test = "0.4.2" +bit-set.workspace = true +proptest.workspace = true +tempfile.workspace = true +test-log.workspace = true +tokio-test.workspace = true [build-dependencies] -git2 = "0.13.25" +git2.workspace = true diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index 10316cb6bf..bf9921a527 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -253,6 +253,13 @@ pub async fn main() -> Result<()> { rpc::query_delegations(&client, &mut ctx.wallet, args) .await; } + Sub::QueryFindValidator(QueryFindValidator(args)) => { + let client = + HttpClient::new(args.query.ledger_address.clone()) + .unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::query_find_validator(&client, args).await; + } Sub::QueryResult(QueryResult(args)) => { wait_until_node_is_synched(&args.query.ledger_address) .await; @@ -319,6 +326,9 @@ pub async fn main() -> Result<()> { Utils::PkToTmAddress(PkToTmAddress(args)) => { utils::pk_to_tm_address(global_args, args) } + Utils::DefaultBaseDir(DefaultBaseDir(args)) => { + utils::default_base_dir(global_args, args) + } }, } Ok(()) @@ -341,7 +351,7 @@ async fn wait_until_node_is_synched(ledger_address: &TendermintAddress) { if is_at_least_height_one && !is_catching_up { return; } else { - if try_count > MAX_TRIES { + if try_count == MAX_TRIES { println!( "Node is still catching up, wait for it to finish \ synching." @@ -351,9 +361,9 @@ async fn wait_until_node_is_synched(ledger_address: &TendermintAddress) { println!( " Waiting for {} ({}/{} tries)...", if is_at_least_height_one { - "a first block" - } else { "node to sync" + } else { + "a first block" }, try_count + 1, MAX_TRIES diff --git a/apps/src/bin/namada-node/cli.rs b/apps/src/bin/namada-node/cli.rs index 240e81f90c..6499a34e9e 100644 --- a/apps/src/bin/namada-node/cli.rs +++ b/apps/src/bin/namada-node/cli.rs @@ -7,15 +7,11 @@ use namada_apps::node::ledger; pub fn main() -> Result<()> { let (cmd, mut ctx) = cli::namada_node_cli()?; - if let Some(mode) = ctx.global_args.mode.clone() { - ctx.config.ledger.tendermint.tendermint_mode = mode; - } match cmd { cmds::NamadaNode::Ledger(sub) => match sub { cmds::Ledger::Run(cmds::LedgerRun(args)) => { let wasm_dir = ctx.wasm_dir(); sleep_until(args.start_time); - ctx.config.ledger.tendermint.tx_index = args.tx_index; ledger::run(ctx.config.ledger, wasm_dir); } cmds::Ledger::RunUntil(cmds::LedgerRunUntil(args)) => { diff --git a/apps/src/bin/namada-wallet/cli.rs b/apps/src/bin/namada-wallet/cli.rs index 2ec7a3a31c..685ed7f116 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,45 @@ 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) + }) + .unwrap_or_else(|| { + println!("No changes are persisted. Exiting."); + cli::safe_exit(0); + }); + 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 +362,31 @@ 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); + }) + .unwrap_or_else(|| { + println!("No changes are persisted. Exiting."); + cli::safe_exit(0); + }); 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..b3078037ff 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -175,6 +175,7 @@ pub mod cmds { .subcommand(QueryBondedStake::def().display_order(3)) .subcommand(QuerySlashes::def().display_order(3)) .subcommand(QueryDelegations::def().display_order(3)) + .subcommand(QueryFindValidator::def().display_order(3)) .subcommand(QueryResult::def().display_order(3)) .subcommand(QueryRawBytes::def().display_order(3)) .subcommand(QueryProposal::def().display_order(3)) @@ -213,6 +214,8 @@ pub mod cmds { let query_slashes = Self::parse_with_ctx(matches, QuerySlashes); let query_delegations = Self::parse_with_ctx(matches, QueryDelegations); + let query_find_validator = + Self::parse_with_ctx(matches, QueryFindValidator); let query_result = Self::parse_with_ctx(matches, QueryResult); let query_raw_bytes = Self::parse_with_ctx(matches, QueryRawBytes); let query_proposal = Self::parse_with_ctx(matches, QueryProposal); @@ -242,6 +245,7 @@ pub mod cmds { .or(query_bonded_stake) .or(query_slashes) .or(query_delegations) + .or(query_find_validator) .or(query_result) .or(query_raw_bytes) .or(query_proposal) @@ -306,6 +310,7 @@ pub mod cmds { QueryCommissionRate(QueryCommissionRate), QuerySlashes(QuerySlashes), QueryDelegations(QueryDelegations), + QueryFindValidator(QueryFindValidator), QueryRawBytes(QueryRawBytes), QueryProposal(QueryProposal), QueryProposalResult(QueryProposalResult), @@ -359,6 +364,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 +377,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 +392,7 @@ pub mod cmds { look-up keys.", ) .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand(KeyRestore::def()) .subcommand(KeyGen::def()) .subcommand(KeyFind::def()) .subcommand(KeyList::def()) @@ -392,6 +400,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 +441,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 +673,7 @@ pub mod cmds { #[derive(Clone, Debug)] pub enum WalletAddress { Gen(AddressGen), + Restore(AddressRestore), Find(AddressOrAliasFind), List(AddressList), Add(AddressAdd), @@ -651,10 +685,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 +701,7 @@ pub mod cmds { ) .setting(AppSettings::SubcommandRequiredElseHelp) .subcommand(AddressGen::def()) + .subcommand(AddressRestore::def()) .subcommand(AddressOrAliasFind::def()) .subcommand(AddressList::def()) .subcommand(AddressAdd::def()) @@ -688,7 +724,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 +732,31 @@ 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 HD \ + derivation path 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); @@ -782,7 +843,6 @@ pub mod cmds { // The `run` command is the default if no sub-command given .or(Some(Self::Run(LedgerRun(args::LedgerRun { start_time: None, - tx_index: false, })))) }) } @@ -1406,6 +1466,28 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct QueryFindValidator(pub args::QueryFindValidator); + + impl SubCmd for QueryFindValidator { + const CMD: &'static str = "find-validator"; + + fn parse(matches: &ArgMatches) -> Option + where + Self: Sized, + { + matches.subcommand_matches(Self::CMD).map(|matches| { + QueryFindValidator(args::QueryFindValidator::parse(matches)) + }) + } + + fn def() -> App { + App::new(Self::CMD) + .about("Find a PoS validator by its Tendermint address.") + .add_args::>() + } + } + #[derive(Clone, Debug)] pub struct QueryRawBytes(pub args::QueryRawBytes); @@ -1506,6 +1588,7 @@ pub mod cmds { InitNetwork(InitNetwork), InitGenesisValidator(InitGenesisValidator), PkToTmAddress(PkToTmAddress), + DefaultBaseDir(DefaultBaseDir), } impl SubCmd for Utils { @@ -1522,11 +1605,14 @@ pub mod cmds { SubCmd::parse(matches).map(Self::InitGenesisValidator); let pk_to_tm_address = SubCmd::parse(matches).map(Self::PkToTmAddress); + let default_base_dir = + SubCmd::parse(matches).map(Self::DefaultBaseDir); join_network .or(fetch_wasms) .or(init_network) .or(init_genesis) .or(pk_to_tm_address) + .or(default_base_dir) }) } @@ -1538,6 +1624,7 @@ pub mod cmds { .subcommand(InitNetwork::def()) .subcommand(InitGenesisValidator::def()) .subcommand(PkToTmAddress::def()) + .subcommand(DefaultBaseDir::def()) .setting(AppSettings::SubcommandRequiredElseHelp) } } @@ -1643,6 +1730,29 @@ pub mod cmds { .add_args::() } } + + #[derive(Clone, Debug)] + pub struct DefaultBaseDir(pub args::DefaultBaseDir); + + impl SubCmd for DefaultBaseDir { + const CMD: &'static str = "default-base-dir"; + + fn parse(matches: &ArgMatches) -> Option { + matches + .subcommand_matches(Self::CMD) + .map(|matches| Self(args::DefaultBaseDir::parse(matches))) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Print the default base directory that would be used if \ + --base-dir or NAMADA_BASE_DIR were not used to set the \ + base directory.", + ) + .add_args::() + } + } } pub mod args { @@ -1666,7 +1776,7 @@ pub mod args { use super::context::*; use super::utils::*; use super::{ArgGroup, ArgMatches}; - use crate::config::{self, Action, ActionAtHeight, TendermintMode}; + use crate::config::{self, Action, ActionAtHeight}; use crate::facade::tendermint::Timeout; use crate::facade::tendermint_config::net::Address as TendermintAddress; @@ -1684,6 +1794,7 @@ pub mod args { pub const TX_WITHDRAW_WASM: &str = "tx_withdraw.wasm"; pub const TX_CHANGE_COMMISSION_WASM: &str = "tx_change_validator_commission.wasm"; + pub const TX_UNJAIL_VALIDATOR_WASM: &str = "tx_unjail_validator.wasm"; pub const ADDRESS: Arg = arg("address"); pub const ALIAS_OPT: ArgOpt = ALIAS.opt(); @@ -1734,6 +1845,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 \ @@ -1749,7 +1863,6 @@ pub mod args { pub const MASP_VALUE: Arg = arg("value"); pub const MAX_COMMISSION_RATE_CHANGE: Arg = arg("max-commission-rate-change"); - pub const MODE: ArgOpt = arg_opt("mode"); pub const NET_ADDRESS: Arg = arg("net-address"); pub const NAMADA_START_TIME: ArgOpt = arg_opt("time"); pub const NO_CONVERSIONS: ArgFlag = flag("no-conversions"); @@ -1786,9 +1899,9 @@ pub mod args { pub const STORAGE_KEY: Arg = arg("storage-key"); pub const SUB_PREFIX: ArgOpt = arg_opt("sub-prefix"); pub const SUSPEND_ACTION: ArgFlag = flag("suspend"); - pub const TENDERMINT_TX_INDEX: ArgFlag = flag("tx-index"); pub const TIMEOUT_HEIGHT: ArgOpt = arg_opt("timeout-height"); pub const TIMEOUT_SEC_OFFSET: ArgOpt = arg_opt("timeout-sec-offset"); + pub const TM_ADDRESS: Arg = arg("tm-address"); pub const TOKEN_OPT: ArgOpt = TOKEN.opt(); pub const TOKEN: Arg = arg("token"); pub const TRANSFER_SOURCE: Arg = arg("source"); @@ -1816,7 +1929,6 @@ pub mod args { pub chain_id: Option, pub base_dir: PathBuf, pub wasm_dir: Option, - pub mode: Option, } impl Global { @@ -1825,12 +1937,10 @@ pub mod args { let chain_id = CHAIN_ID_OPT.parse(matches); let base_dir = BASE_DIR.parse(matches); let wasm_dir = WASM_DIR.parse(matches); - let mode = MODE.parse(matches).map(TendermintMode::from); Global { chain_id, base_dir, wasm_dir, - mode, } } @@ -1854,27 +1964,18 @@ pub mod args { `NAMADA_WASM_DIR` environment variable, but the argument \ takes precedence, if specified.", )) - .arg(MODE.def().about( - "The mode in which to run Namada. Options are \n\t * \ - Validator (default)\n\t * Full\n\t * Seed", - )) } } #[derive(Clone, Debug)] pub struct LedgerRun { pub start_time: Option, - pub tx_index: bool, } impl Args for LedgerRun { fn parse(matches: &ArgMatches) -> Self { let start_time = NAMADA_START_TIME.parse(matches); - let tx_index = TENDERMINT_TX_INDEX.parse(matches); - Self { - start_time, - tx_index, - } + Self { start_time } } fn def(app: App) -> App { @@ -1886,11 +1987,6 @@ pub mod args { equivalent:\n2023-01-20T12:12:12Z\n2023-01-20 \ 12:12:12Z\n2023- 01-20T12: 12:12Z", )) - .arg( - TENDERMINT_TX_INDEX - .def() - .about("Enable Tendermint tx indexing."), - ) } } @@ -2054,7 +2150,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 +2205,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 +2266,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 +2280,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 +2321,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 +2407,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 +2450,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 +2490,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 +2537,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 +2547,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 +2598,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 +2609,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 +2834,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 +3066,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(), } } } @@ -3015,7 +3086,7 @@ pub mod args { } fn def(app: App) -> App { - app.add_args::>() + app.add_args::>() .arg(VALIDATOR.def().about( "The validator's address whose commission rate to change.", )) @@ -3027,6 +3098,43 @@ pub mod args { } } + impl CliToSdk> for TxUnjailValidator { + fn to_sdk(self, ctx: &mut Context) -> TxUnjailValidator { + TxUnjailValidator { + tx: self.tx.to_sdk(ctx), + validator: ctx.get(&self.validator), + tx_code_path: self + .tx_code_path + .as_path() + .to_str() + .unwrap() + .to_string() + .into_bytes(), + } + } + } + + impl Args for TxUnjailValidator { + fn parse(matches: &ArgMatches) -> Self { + let tx = Tx::parse(matches); + let validator = VALIDATOR.parse(matches); + let tx_code_path = PathBuf::from(TX_UNJAIL_VALIDATOR_WASM); + Self { + tx, + validator, + tx_code_path, + } + } + + fn def(app: App) -> App { + app.add_args::>().arg( + VALIDATOR.def().about( + "The address of the jailed validator to re-activate.", + ), + ) + } + } + impl CliToSdk> for QueryCommissionRate { fn to_sdk(self, ctx: &mut Context) -> QueryCommissionRate { QueryCommissionRate:: { @@ -3111,6 +3219,31 @@ pub mod args { } } + impl Args for QueryFindValidator { + fn parse(matches: &ArgMatches) -> Self { + let query = Query::parse(matches); + let tm_addr = TM_ADDRESS.parse(matches); + Self { query, tm_addr } + } + + fn def(app: App) -> App { + app.add_args::>().arg( + TM_ADDRESS + .def() + .about("The address of the validator in Tendermint."), + ) + } + } + + impl CliToSdk> for QueryFindValidator { + fn to_sdk(self, ctx: &mut Context) -> QueryFindValidator { + QueryFindValidator:: { + query: self.query.to_sdk(ctx), + tm_addr: self.tm_addr, + } + } + } + impl CliToSdk> for QueryRawBytes { fn to_sdk(self, ctx: &mut Context) -> QueryRawBytes { QueryRawBytes:: { @@ -3165,7 +3298,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 +3334,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 +3387,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 +3404,7 @@ pub mod args { expiration, signing_key, signer, - tx_code_path, + tx_reveal_code_path, password, chain_id, } @@ -3318,6 +3454,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 +3525,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 +3536,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 +3608,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 +3788,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() @@ -3646,6 +3851,19 @@ pub mod args { } } + #[derive(Clone, Debug)] + pub struct DefaultBaseDir {} + + impl Args for DefaultBaseDir { + fn parse(_matches: &ArgMatches) -> Self { + Self {} + } + + fn def(app: App) -> App { + app + } + } + #[derive(Clone, Debug)] pub struct FetchWasms { pub chain_id: ChainId, diff --git a/apps/src/lib/cli/context.rs b/apps/src/lib/cli/context.rs index 42c1bb6c94..dbe1cec667 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}; @@ -89,7 +89,7 @@ impl Context { let mut config = Config::load( &global_args.base_dir, &global_config.default_chain_id, - global_args.mode.clone(), + None, ); let chain_dir = global_args @@ -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..8aa466420e 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; @@ -33,7 +32,9 @@ use namada::ledger::pos::{ self, BondId, BondsAndUnbondsDetail, CommissionPair, PosParams, Slash, }; use namada::ledger::queries::RPC; -use namada::ledger::rpc::{query_epoch, TxResponse}; +use namada::ledger::rpc::{ + enriched_bonds_and_unbonds, query_epoch, TxResponse, +}; use namada::ledger::storage::ConversionState; use namada::ledger::wallet::{AddressVpType, Wallet}; use namada::proof_of_stake::types::WeightedValidator; @@ -78,8 +79,19 @@ pub async fn query_and_print_epoch< /// Query the last committed block pub async fn query_block( client: &C, -) -> crate::facade::tendermint_rpc::endpoint::block::Response { - namada::ledger::rpc::query_block(client).await +) { + let block = namada::ledger::rpc::query_block(client).await; + match block { + Some(block) => { + println!( + "Last committed block ID: {}, height: {}, time: {}", + block.hash, block.height, block.time + ); + } + None => { + println!("No block has been committed yet."); + } + } } /// Query the results of the last committed block @@ -107,7 +119,7 @@ pub async fn query_transfers< || Either::Right(wallet.get_addresses().into_values().collect()), Either::Left, ); - let _ = shielded.load(); + let _ = shielded.load().await; // Obtain the effects of all shielded and transparent transactions let transfers = shielded .query_tx_deltas( @@ -304,7 +316,8 @@ pub async fn query_transparent_balance< } (None, Some(owner)) => { for token in tokens { - let prefix = token.to_db_key().into(); + let prefix = + token::balance_key(&token, &owner.address().unwrap()); let balances = query_storage_prefix::(client, &prefix) .await; @@ -319,7 +332,7 @@ pub async fn query_transparent_balance< } } (Some(token), None) => { - let prefix = token.to_db_key().into(); + let prefix = token::balance_prefix(&token); let balances = query_storage_prefix::(client, &prefix).await; if let Some(balances) = balances { @@ -368,7 +381,7 @@ pub async fn query_pinned_balance< .values() .map(|fvk| ExtendedFullViewingKey::from(*fvk).fvk.vk) .collect(); - let _ = shielded.load(); + let _ = shielded.load().await; // Print the token balances by payment address for owner in owners { let mut balance = Err(PinnedBalanceError::InvalidViewingKey); @@ -693,14 +706,14 @@ pub async fn query_shielded_balance< Some(viewing_key) => vec![viewing_key], None => wallet.get_viewing_keys().values().copied().collect(), }; - let _ = shielded.load(); + let _ = shielded.load().await; let fvks: Vec<_> = viewing_keys .iter() .map(|fvk| ExtendedFullViewingKey::from(*fvk).fvk.vk) .collect(); shielded.fetch(client, &[], &fvks).await; // Save the update state so that future fetches can be short-circuited - let _ = shielded.save(); + let _ = shielded.save().await; // The epoch is required to identify timestamped tokens let epoch = query_and_print_epoch(client).await; // Map addresses to token names @@ -1253,7 +1266,7 @@ pub async fn query_bonds( _wallet: &mut Wallet, args: args::QueryBonds, ) -> std::io::Result<()> { - let _epoch = query_and_print_epoch(client).await; + let epoch = query_and_print_epoch(client).await; let source = args.owner; let validator = args.validator; @@ -1261,21 +1274,10 @@ pub async fn query_bonds( let stdout = io::stdout(); let mut w = stdout.lock(); - let bonds_and_unbonds: pos::types::BondsAndUnbondsDetails = - unwrap_client_response::( - RPC.vp() - .pos() - .bonds_and_unbonds(client, &source, &validator) - .await, - ); - let mut bonds_total: token::Amount = 0.into(); - let mut bonds_total_slashed: token::Amount = 0.into(); - let mut unbonds_total: token::Amount = 0.into(); - let mut unbonds_total_slashed: token::Amount = 0.into(); - let mut total_withdrawable: token::Amount = 0.into(); - for (bond_id, details) in bonds_and_unbonds { - let mut total: token::Amount = 0.into(); - let mut total_slashed: token::Amount = 0.into(); + let bonds_and_unbonds = + enriched_bonds_and_unbonds(client, epoch, &source, &validator).await; + + for (bond_id, details) in &bonds_and_unbonds.data { let bond_type = if bond_id.source == bond_id.validator { format!("Self-bonds from {}", bond_id.validator) } else { @@ -1285,74 +1287,66 @@ pub async fn query_bonds( ) }; writeln!(w, "{}:", bond_type)?; - for bond in details.bonds { + for bond in &details.data.bonds { writeln!( w, " Remaining active bond from epoch {}: Δ {}", bond.start, bond.amount )?; - total += bond.amount; - total_slashed += bond.slashed_amount.unwrap_or_default(); } - if total_slashed != token::Amount::default() { + if details.bonds_total_slashed != token::Amount::default() { writeln!( w, "Active (slashed) bonds total: {}", - total - total_slashed + details.bonds_total_active() )?; } - writeln!(w, "Bonds total: {}", total)?; + writeln!(w, "Bonds total: {}", details.bonds_total)?; writeln!(w)?; - bonds_total += total; - bonds_total_slashed += total_slashed; - let mut withdrawable = token::Amount::default(); - if !details.unbonds.is_empty() { - let mut total: token::Amount = 0.into(); - let mut total_slashed: token::Amount = 0.into(); + if !details.data.unbonds.is_empty() { let bond_type = if bond_id.source == bond_id.validator { format!("Unbonded self-bonds from {}", bond_id.validator) } else { format!("Unbonded delegations from {}", bond_id.source) }; writeln!(w, "{}:", bond_type)?; - for unbond in details.unbonds { - total += unbond.amount; - total_slashed += unbond.slashed_amount.unwrap_or_default(); + for unbond in &details.data.unbonds { writeln!( w, " Withdrawable from epoch {} (active from {}): Δ {}", unbond.withdraw, unbond.start, unbond.amount )?; } - withdrawable = total - total_slashed; - writeln!(w, "Unbonded total: {}", total)?; - - unbonds_total += total; - unbonds_total_slashed += total_slashed; - total_withdrawable += withdrawable; + writeln!(w, "Unbonded total: {}", details.unbonds_total)?; } - writeln!(w, "Withdrawable total: {}", withdrawable)?; + writeln!(w, "Withdrawable total: {}", details.total_withdrawable)?; writeln!(w)?; } - if bonds_total != bonds_total_slashed { + if bonds_and_unbonds.bonds_total != bonds_and_unbonds.bonds_total_slashed { writeln!( w, "All bonds total active: {}", - bonds_total - bonds_total_slashed + bonds_and_unbonds.bonds_total_active() )?; } - writeln!(w, "All bonds total: {}", bonds_total)?; + writeln!(w, "All bonds total: {}", bonds_and_unbonds.bonds_total)?; - if unbonds_total != unbonds_total_slashed { + if bonds_and_unbonds.unbonds_total + != bonds_and_unbonds.unbonds_total_slashed + { writeln!( w, "All unbonds total active: {}", - unbonds_total - unbonds_total_slashed + bonds_and_unbonds.unbonds_total_active() )?; } - writeln!(w, "All unbonds total: {}", unbonds_total)?; - writeln!(w, "All unbonds total withdrawable: {}", total_withdrawable)?; + writeln!(w, "All unbonds total: {}", bonds_and_unbonds.unbonds_total)?; + writeln!( + w, + "All unbonds total withdrawable: {}", + bonds_and_unbonds.total_withdrawable + )?; Ok(()) } @@ -1502,9 +1496,7 @@ pub async fn query_slashes( writeln!( w, "Slash epoch {}, type {}, rate {}", - slash.epoch, - slash.r#type, - slash.r#type.get_slash_rate(¶ms) + slash.epoch, slash.r#type, slash.rate ) .unwrap(); } @@ -1562,6 +1554,30 @@ pub async fn query_delegations( } } +pub async fn query_find_validator( + client: &C, + args: args::QueryFindValidator, +) { + let args::QueryFindValidator { query: _, tm_addr } = args; + if tm_addr.len() != 40 { + eprintln!( + "Expected 40 characters in Tendermint address, got {}", + tm_addr.len() + ); + cli::safe_exit(1); + } + let tm_addr = tm_addr.to_ascii_uppercase(); + let validator = unwrap_client_response::( + RPC.vp().pos().validator_by_tm_addr(client, &tm_addr).await, + ); + match validator { + Some(address) => println!("Found validator address \"{address}\"."), + None => { + println!("No validator with Tendermint address {tm_addr} found.") + } + } +} + /// Dry run a transaction pub async fn dry_run_tx( client: &C, 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..237cef3c92 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; @@ -14,30 +15,37 @@ use namada::ledger::governance::storage as gov_storage; 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::ledger::{masp, pos, tx}; +use namada::proof_of_stake::parameters::PosParams; +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 tendermint_rpc::HttpClient; +use sha2::{Digest as Sha2Digest, Sha256}; use super::rpc; 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::config::TendermintMode; use crate::facade::tendermint_rpc::endpoint::broadcast::tx_sync::Response; +use crate::facade::tendermint_rpc::HttpClient; 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 +119,18 @@ 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.") + .expect("No existing alias expected.") .1 .ref_to() }); @@ -133,15 +145,19 @@ 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.") + .expect("No existing alias expected.") .1 }); @@ -160,11 +176,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 +209,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 +222,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, @@ -270,10 +292,26 @@ pub async fn submit_init_validator< crate::wallet::save(&ctx.wallet) .unwrap_or_else(|err| eprintln!("{}", err)); - let tendermint_home = ctx.config.ledger.tendermint_dir(); + let tendermint_home = ctx.config.ledger.cometbft_dir(); tendermint_node::write_validator_key(&tendermint_home, &consensus_key); tendermint_node::write_validator_state(tendermint_home); + // Write Namada config stuff or figure out how to do the above + // tendermint_node things two epochs in the future!!! + ctx.config.ledger.shell.tendermint_mode = TendermintMode::Validator; + ctx.config + .write( + &ctx.config.ledger.shell.base_dir, + &ctx.config.ledger.chain_id, + true, + ) + .unwrap(); + + let key = pos::params_key(); + let pos_params = rpc::query_storage_value::(client, &key) + .await + .expect("Pos parameter should be defined."); + println!(); println!( "The validator's addresses and keys were stored in the wallet:" @@ -285,6 +323,11 @@ pub async fn submit_init_validator< "The ledger node has been setup to use this validator's address \ and consensus key." ); + println!( + "Your validator will be active in {} epochs. Be sure to restart \ + your node for the changes to take effect!", + pos_params.pipeline_len + ); } else { println!("Transaction dry run. No addresses have been saved.") } @@ -315,7 +358,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,8 +379,9 @@ impl Default for CLIShieldedUtils { } } +#[async_trait(?Send)] impl masp::ShieldedUtils for CLIShieldedUtils { - type C = tendermint_rpc::HttpClient; + type C = crate::facade::tendermint_rpc::HttpClient; fn local_tx_prover(&self) -> LocalTxProver { if let Ok(params_dir) = env::var(masp::ENV_VAR_MASP_PARAMS_DIR) { @@ -354,7 +398,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 +411,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 +481,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 +574,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 +595,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, @@ -599,7 +642,7 @@ pub async fn submit_vote_proposal( "yay" => { if let Some(pgf) = args.proposal_pgf { let splits = pgf.trim().split_ascii_whitespace(); - let address_iter = splits.clone().into_iter().step_by(2); + let address_iter = splits.clone().step_by(2); let cap_iter = splits.into_iter().skip(1).step_by(2); let mut set = HashSet::new(); for (address, cap) in @@ -806,8 +849,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 +926,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() @@ -990,6 +1043,20 @@ pub async fn submit_validator_commission_change< .await } +pub async fn submit_unjail_validator< + C: namada::ledger::queries::Client + Sync, +>( + client: &C, + mut ctx: Context, + mut args: args::TxUnjailValidator, +) -> Result<(), tx::Error> { + args.tx.chain_id = args + .tx + .chain_id + .or_else(|| Some(ctx.config.ledger.chain_id.clone())); + tx::submit_unjail_validator::(client, &mut ctx.wallet, args).await +} + /// Submit transaction and wait for result. Returns a list of addresses /// initialized in the transaction if any. In dry run, this is always empty. async fn process_tx( @@ -1001,7 +1068,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 +1083,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..ee63ecb1d2 100644 --- a/apps/src/lib/client/utils.rs +++ b/apps/src/lib/client/utils.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::env; use std::fs::{self, File, OpenOptions}; use std::io::Write; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::net::SocketAddr; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -27,11 +27,13 @@ use crate::config::genesis::genesis_config::{ self, GenesisConfig, HexString, ValidatorPreGenesisConfig, }; use crate::config::global::GlobalConfig; -use crate::config::{self, Config, TendermintMode}; +use crate::config::{self, get_default_namada_folder, 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"; @@ -231,11 +233,7 @@ pub async fn join_network( let base_dir = base_dir.clone(); let chain_id = chain_id.clone(); tokio::task::spawn_blocking(move || { - let mut config = Config::load( - &base_dir, - &chain_id, - global_args.mode.clone(), - ); + let mut config = Config::load(&base_dir, &chain_id, None); config.wasm_dir = wasm_dir; config.write(&base_dir, &chain_id, true).unwrap(); }) @@ -288,7 +286,7 @@ pub async fn join_network( }) .clone(); - let tm_home_dir = chain_dir.join("tendermint"); + let tm_home_dir = chain_dir.join("cometbft"); // Write consensus key to tendermint home tendermint_node::write_validator_key( @@ -321,27 +319,20 @@ pub async fn join_network( let chain_id = chain_id.clone(); tokio::task::spawn_blocking(move || { let mut config = Config::load(&base_dir, &chain_id, None); + config.ledger.shell.tendermint_mode = TendermintMode::Validator; - config.ledger.tendermint.tendermint_mode = - TendermintMode::Validator; - // Validator node should turned off peer exchange reactor - config.ledger.tendermint.p2p_pex = false; // Remove self from persistent peers - config - .ledger - .tendermint - .p2p_persistent_peers - .retain(|peer| { - if let TendermintAddress::Tcp { - peer_id: Some(peer_id), - .. - } = peer - { - node_id != *peer_id - } else { - true - } - }); + config.ledger.cometbft.p2p.persistent_peers.retain(|peer| { + if let TendermintAddress::Tcp { + peer_id: Some(peer_id), + .. + } = peer + { + node_id != *peer_id + } else { + true + } + }); config.write(&base_dir, &chain_id, true).unwrap(); }) .await @@ -454,7 +445,7 @@ pub fn init_network( let validator_dir = accounts_dir.join(name); let chain_dir = validator_dir.join(&accounts_temp_dir); - let tm_home_dir = chain_dir.join("tendermint"); + let tm_home_dir = chain_dir.join("cometbft"); // Find or generate tendermint node key let node_pk = try_parse_public_key( @@ -491,7 +482,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 +492,12 @@ 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.") + .expect("No existing alias expected."); // Write consensus key for Tendermint tendermint_node::write_validator_key(&tm_home_dir, &keypair); @@ -522,13 +512,12 @@ 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.") + .expect("No existing alias expected."); keypair.ref_to() }); @@ -539,13 +528,12 @@ 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.") + .expect("No existing alias expected."); keypair.ref_to() }); @@ -593,7 +581,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 +614,18 @@ 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.") + .expect("No existing alias expected."); let public_key = genesis_config::HexString(keypair.ref_to().to_string()); config.public_key = Some(public_key); @@ -739,7 +733,7 @@ pub fn init_network( // directories. config.ledger.shell.base_dir = config::DEFAULT_BASE_DIR.into(); // Add a ledger P2P persistent peers - config.ledger.tendermint.p2p_persistent_peers = persistent_peers + config.ledger.cometbft.p2p.persistent_peers = persistent_peers .iter() .enumerate() .filter_map(|(index, peer)| @@ -750,37 +744,39 @@ pub fn init_network( None }) .collect(); - config.ledger.tendermint.consensus_timeout_commit = + + config.ledger.cometbft.consensus.timeout_commit = consensus_timeout_commit; - config.ledger.tendermint.p2p_allow_duplicate_ip = - allow_duplicate_ip; - config.ledger.tendermint.p2p_addr_book_strict = !localhost; + config.ledger.cometbft.p2p.allow_duplicate_ip = allow_duplicate_ip; + config.ledger.cometbft.p2p.addr_book_strict = !localhost; // Clear the net address from the config and use it to set ports let net_address = validator_config.net_address.take().unwrap(); + let ip = SocketAddr::from_str(&net_address).unwrap().ip(); let first_port = SocketAddr::from_str(&net_address).unwrap().port(); if !localhost { - config - .ledger - .tendermint - .p2p_address - .set_ip(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))); + config.ledger.cometbft.p2p.laddr = TendermintAddress::from_str( + &format!("0.0.0.0:{}", first_port), + ) + .unwrap(); } - config.ledger.tendermint.p2p_address.set_port(first_port); + config.ledger.cometbft.p2p.laddr = + TendermintAddress::from_str(&format!("{}:{}", ip, first_port)) + .unwrap(); if !localhost { - config - .ledger - .tendermint - .rpc_address - .set_ip(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))); + config.ledger.cometbft.rpc.laddr = TendermintAddress::from_str( + &format!("0.0.0.0:{}", first_port + 1), + ) + .unwrap(); } - config - .ledger - .tendermint - .rpc_address - .set_port(first_port + 1); - config.ledger.shell.ledger_address.set_port(first_port + 2); - // Validator node should turned off peer exchange reactor - config.ledger.tendermint.p2p_pex = false; + config.ledger.cometbft.rpc.laddr = TendermintAddress::from_str( + &format!("{}:{}", ip, first_port + 1), + ) + .unwrap(); + + config.ledger.cometbft.proxy_app = TendermintAddress::from_str( + &format!("{}:{}", ip, first_port + 2), + ) + .unwrap(); config.write(&validator_dir, &chain_id, true).unwrap(); }, @@ -788,19 +784,15 @@ pub fn init_network( // Update the ledger config persistent peers and save it let mut config = Config::load(&global_args.base_dir, &chain_id, None); - config.ledger.tendermint.p2p_persistent_peers = persistent_peers; - config.ledger.tendermint.consensus_timeout_commit = - consensus_timeout_commit; - config.ledger.tendermint.p2p_allow_duplicate_ip = allow_duplicate_ip; + config.ledger.cometbft.p2p.persistent_peers = persistent_peers; + config.ledger.cometbft.consensus.timeout_commit = consensus_timeout_commit; + config.ledger.cometbft.p2p.allow_duplicate_ip = allow_duplicate_ip; // Open P2P address if !localhost { - config - .ledger - .tendermint - .p2p_address - .set_ip(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))); + config.ledger.cometbft.p2p.laddr = + TendermintAddress::from_str("0.0.0.0:26656").unwrap(); } - config.ledger.tendermint.p2p_addr_book_strict = !localhost; + config.ledger.cometbft.p2p.addr_book_strict = !localhost; config.ledger.genesis_time = genesis.genesis_time.into(); config .write(&global_args.base_dir, &chain_id, true) @@ -877,13 +869,18 @@ 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.") + .expect("No existing alias expected."); let public_key = genesis_config::HexString(keypair.ref_to().to_string()); config.public_key = Some(public_key); @@ -901,6 +898,18 @@ pub fn pk_to_tm_address( println!("{tm_addr}"); } +pub fn default_base_dir( + _global_args: args::Global, + _args: args::DefaultBaseDir, +) { + println!( + "{}", + get_default_namada_folder().to_str().expect( + "expected a default namada folder to be possible to determine" + ) + ); +} + /// Initialize genesis validator's address, consensus key and validator account /// key and use it in the ledger's node. pub fn init_genesis_validator( diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 6b029b6407..527893057d 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -1,8 +1,6 @@ //! The parameters used for the chain's genesis use std::collections::HashMap; -#[cfg(not(feature = "dev"))] -use std::path::Path; use borsh::{BorshDeserialize, BorshSerialize}; use derivative::Derivative; @@ -12,8 +10,6 @@ use namada::ledger::governance::parameters::GovParams; use namada::ledger::parameters::EpochDuration; use namada::ledger::pos::{GenesisValidator, PosParams}; use namada::types::address::Address; -#[cfg(not(feature = "dev"))] -use namada::types::chain::ChainId; use namada::types::chain::ProposalBytes; use namada::types::key::dkg_session_keys::DkgPublicKey; use namada::types::key::*; @@ -308,6 +304,9 @@ pub mod genesis_config { // light client attack. // XXX: u64 doesn't work with toml-rs! pub light_client_attack_min_slash_rate: Decimal, + /// Number of epochs above and below (separately) the current epoch to + /// consider when doing cubic slashing + pub cubic_slashing_window_length: u64, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -653,6 +652,7 @@ pub mod genesis_config { target_staked_ratio, duplicate_vote_min_slash_rate, light_client_attack_min_slash_rate, + cubic_slashing_window_length, } = pos_params; let pos_params = PosParams { max_validator_slots, @@ -665,6 +665,7 @@ pub mod genesis_config { target_staked_ratio, duplicate_vote_min_slash_rate, light_client_attack_min_slash_rate, + cubic_slashing_window_length, }; let mut genesis = Genesis { @@ -880,14 +881,17 @@ pub struct Parameters { pub wrapper_tx_fees: Option, } -#[cfg(not(feature = "dev"))] -pub fn genesis(base_dir: impl AsRef, chain_id: &ChainId) -> Genesis { +#[cfg(not(any(test, feature = "dev")))] +pub fn genesis( + base_dir: impl AsRef, + chain_id: &namada::types::chain::ChainId, +) -> Genesis { let path = base_dir .as_ref() .join(format!("{}.toml", chain_id.as_str())); genesis_config::read_genesis_config(path) } -#[cfg(feature = "dev")] +#[cfg(any(test, feature = "dev"))] pub fn genesis(num_validators: u64) -> Genesis { use namada::types::address::{self}; use rust_decimal_macros::dec; diff --git a/apps/src/lib/config/mod.rs b/apps/src/lib/config/mod.rs index 2df988e298..4a6be155aa 100644 --- a/apps/src/lib/config/mod.rs +++ b/apps/src/lib/config/mod.rs @@ -6,9 +6,7 @@ pub mod utils; use std::fs::{create_dir_all, File}; use std::io::Write; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::path::{Path, PathBuf}; -use std::str::FromStr; use directories::ProjectDirs; use namada::types::chain::ChainId; @@ -18,8 +16,9 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; use crate::cli; -use crate::facade::tendermint::Timeout; -use crate::facade::tendermint_config::net::Address as TendermintAddress; +use crate::facade::tendermint_config::{ + TendermintConfig, TxIndexConfig, TxIndexer, +}; /// Base directory contains global config and chain directories. pub const DEFAULT_BASE_DIR: &str = ".namada"; @@ -30,8 +29,8 @@ pub const DEFAULT_WASM_DIR: &str = "wasm"; pub const DEFAULT_WASM_CHECKSUMS_FILE: &str = "checksums.json"; /// Chain-specific Namada configuration. Nested in chain dirs. pub const FILENAME: &str = "config.toml"; -/// Chain-specific Tendermint configuration. Nested in chain dirs. -pub const TENDERMINT_DIR: &str = "tendermint"; +/// Chain-specific CometBFT configuration. Nested in chain dirs. +pub const COMETBFT_DIR: &str = "cometbft"; /// Chain-specific Namada DB. Nested in chain dirs. pub const DB_DIR: &str = "db"; @@ -95,13 +94,13 @@ pub struct Ledger { pub genesis_time: Rfc3339String, pub chain_id: ChainId, pub shell: Shell, - pub tendermint: Tendermint, + pub cometbft: TendermintConfig, } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Shell { pub base_dir: PathBuf, - pub ledger_address: SocketAddr, + // pub ledger_address: SocketAddr, /// RocksDB block cache maximum size in bytes. /// When not set, defaults to 1/3 of the available memory. pub block_cache_bytes: Option, @@ -116,35 +115,12 @@ pub struct Shell { pub storage_read_past_height_limit: Option, /// Use the [`Ledger::db_dir()`] method to read the value. db_dir: PathBuf, - /// Use the [`Ledger::tendermint_dir()`] method to read the value. - tendermint_dir: PathBuf, + /// Use the [`Ledger::cometbft_dir()`] method to read the value. + cometbft_dir: PathBuf, /// An optional action to take when a given blockheight is reached. pub action_at_height: Option, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Tendermint { - pub rpc_address: SocketAddr, - pub p2p_address: SocketAddr, - /// The persistent peers addresses must include node ID - pub p2p_persistent_peers: Vec, - /// Turns the peer exchange reactor on or off. Validator node will want the - /// pex turned off. - pub p2p_pex: bool, - /// Toggle to disable guard against peers connecting from the same IP - pub p2p_allow_duplicate_ip: bool, - /// Set `true` for strict address routability rules - /// Set `false` for private or local networks - pub p2p_addr_book_strict: bool, - /// How long we wait after committing a block, before starting on the new - /// height - pub consensus_timeout_commit: Timeout, + /// Specify if tendermint is started as validator, fullnode or seednode pub tendermint_mode: TendermintMode, - pub instrumentation_prometheus: bool, - pub instrumentation_prometheus_listen_addr: SocketAddr, - pub instrumentation_namespace: String, - /// Toggle to enable tx indexing - pub tx_index: bool, } impl Ledger { @@ -153,47 +129,28 @@ impl Ledger { chain_id: ChainId, mode: TendermintMode, ) -> Self { + let mut tendermint_config = + TendermintConfig::parse_toml(DEFAULT_COMETBFT_CONFIG).unwrap(); + tendermint_config.instrumentation.namespace = "namada_tm".to_string(); + tendermint_config.tx_index = TxIndexConfig { + indexer: TxIndexer::Null, + }; Self { genesis_time: Rfc3339String("1970-01-01T00:00:00Z".to_owned()), chain_id, shell: Shell { base_dir: base_dir.as_ref().to_owned(), - ledger_address: SocketAddr::new( - IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), - 26658, - ), block_cache_bytes: None, vp_wasm_compilation_cache_bytes: None, tx_wasm_compilation_cache_bytes: None, // Default corresponds to 1 hour of past blocks at 1 block/sec storage_read_past_height_limit: Some(3600), db_dir: DB_DIR.into(), - tendermint_dir: TENDERMINT_DIR.into(), + cometbft_dir: COMETBFT_DIR.into(), action_at_height: None, - }, - tendermint: Tendermint { - rpc_address: SocketAddr::new( - IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), - 26657, - ), - p2p_address: SocketAddr::new( - IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), - 26656, - ), - p2p_persistent_peers: vec![], - p2p_pex: true, - p2p_allow_duplicate_ip: false, - p2p_addr_book_strict: true, - consensus_timeout_commit: Timeout::from_str("1s").unwrap(), tendermint_mode: mode, - instrumentation_prometheus: false, - instrumentation_prometheus_listen_addr: SocketAddr::new( - IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), - 26661, - ), - instrumentation_namespace: "namadan_tm".to_string(), - tx_index: false, }, + cometbft: tendermint_config, } } @@ -208,8 +165,8 @@ impl Ledger { } /// Get the directory path to Tendermint - pub fn tendermint_dir(&self) -> PathBuf { - self.shell.tendermint_dir(&self.chain_id) + pub fn cometbft_dir(&self) -> PathBuf { + self.shell.cometbft_dir(&self.chain_id) } } @@ -220,10 +177,10 @@ impl Shell { } /// Get the directory path to Tendermint - pub fn tendermint_dir(&self, chain_id: &ChainId) -> PathBuf { + pub fn cometbft_dir(&self, chain_id: &ChainId) -> PathBuf { self.base_dir .join(chain_id.as_str()) - .join(&self.tendermint_dir) + .join(&self.cometbft_dir) } } @@ -407,3 +364,490 @@ And this is correct nested:Nested, } "#; + +pub const DEFAULT_COMETBFT_CONFIG: &str = r#" + +# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +# NOTE: Any path below can be absolute (e.g. "/var/myawesomeapp/data") or +# relative to the home directory (e.g. "data"). The home directory is +# "$HOME/.cometbft" by default, but could be changed via $CMTHOME env variable +# or --home cmd flag. + +####################################################################### +### Main Base Config Options ### +####################################################################### + +# TCP or UNIX socket address of the ABCI application, +# or the name of an ABCI application compiled in with the CometBFT binary +proxy_app = "tcp://127.0.0.1:26658" + +# A custom human readable name for this node +moniker = "technodrome" + +# If this node is many blocks behind the tip of the chain, BlockSync +# allows them to catchup quickly by downloading blocks in parallel +# and verifying their commits +# +# Deprecated: this key will be removed and BlockSync will be enabled +# unconditionally in the next major release. +block_sync = true + +# Database backend: goleveldb | cleveldb | boltdb | rocksdb | badgerdb +# * goleveldb (github.com/syndtr/goleveldb - most popular implementation) +# - pure go +# - stable +# * cleveldb (uses levigo wrapper) +# - fast +# - requires gcc +# - use cleveldb build tag (go build -tags cleveldb) +# * boltdb (uses etcd's fork of bolt - github.com/etcd-io/bbolt) +# - EXPERIMENTAL +# - may be faster is some use-cases (random reads - indexer) +# - use boltdb build tag (go build -tags boltdb) +# * rocksdb (uses github.com/tecbot/gorocksdb) +# - EXPERIMENTAL +# - requires gcc +# - use rocksdb build tag (go build -tags rocksdb) +# * badgerdb (uses github.com/dgraph-io/badger) +# - EXPERIMENTAL +# - use badgerdb build tag (go build -tags badgerdb) +db_backend = "goleveldb" + +# Database directory +db_dir = "data" + +# Output level for logging, including package level options +log_level = "info" + +# Output format: 'plain' (colored text) or 'json' +log_format = "plain" + +##### additional base config options ##### + +# Path to the JSON file containing the initial validator set and other meta data +genesis_file = "config/genesis.json" + +# Path to the JSON file containing the private key to use as a validator in the consensus protocol +priv_validator_key_file = "config/priv_validator_key.json" + +# Path to the JSON file containing the last sign state of a validator +priv_validator_state_file = "data/priv_validator_state.json" + +# TCP or UNIX socket address for CometBFT to listen on for +# connections from an external PrivValidator process +priv_validator_laddr = "" + +# Path to the JSON file containing the private key to use for node authentication in the p2p protocol +node_key_file = "config/node_key.json" + +# Mechanism to connect to the ABCI application: socket | grpc +abci = "socket" + +# If true, query the ABCI app on connecting to a new peer +# so the app can decide if we should keep the connection or not +filter_peers = false + + +####################################################################### +### Advanced Configuration Options ### +####################################################################### + +####################################################### +### RPC Server Configuration Options ### +####################################################### +[rpc] + +# TCP or UNIX socket address for the RPC server to listen on +laddr = "tcp://127.0.0.1:26657" + +# A list of origins a cross-domain request can be executed from +# Default value '[]' disables cors support +# Use '["*"]' to allow any origin +cors_allowed_origins = [] + +# A list of methods the client is allowed to use with cross-domain requests +cors_allowed_methods = ["HEAD", "GET", "POST", ] + +# A list of non simple headers the client is allowed to use with cross-domain requests +cors_allowed_headers = ["Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time", ] + +# TCP or UNIX socket address for the gRPC server to listen on +# NOTE: This server only supports /broadcast_tx_commit +grpc_laddr = "" + +# Maximum number of simultaneous connections. +# Does not include RPC (HTTP&WebSocket) connections. See max_open_connections +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} +# 1024 - 40 - 10 - 50 = 924 = ~900 +grpc_max_open_connections = 900 + +# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool +unsafe = false + +# Maximum number of simultaneous connections (including WebSocket). +# Does not include gRPC connections. See grpc_max_open_connections +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} +# 1024 - 40 - 10 - 50 = 924 = ~900 +max_open_connections = 900 + +# Maximum number of unique clientIDs that can /subscribe +# If you're using /broadcast_tx_commit, set to the estimated maximum number +# of broadcast_tx_commit calls per block. +max_subscription_clients = 100 + +# Maximum number of unique queries a given client can /subscribe to +# If you're using GRPC (or Local RPC client) and /broadcast_tx_commit, set to +# the estimated # maximum number of broadcast_tx_commit calls per block. +max_subscriptions_per_client = 5 + +# Experimental parameter to specify the maximum number of events a node will +# buffer, per subscription, before returning an error and closing the +# subscription. Must be set to at least 100, but higher values will accommodate +# higher event throughput rates (and will use more memory). +experimental_subscription_buffer_size = 200 + +# Experimental parameter to specify the maximum number of RPC responses that +# can be buffered per WebSocket client. If clients cannot read from the +# WebSocket endpoint fast enough, they will be disconnected, so increasing this +# parameter may reduce the chances of them being disconnected (but will cause +# the node to use more memory). +# +# Must be at least the same as "experimental_subscription_buffer_size", +# otherwise connections could be dropped unnecessarily. This value should +# ideally be somewhat higher than "experimental_subscription_buffer_size" to +# accommodate non-subscription-related RPC responses. +experimental_websocket_write_buffer_size = 200 + +# If a WebSocket client cannot read fast enough, at present we may +# silently drop events instead of generating an error or disconnecting the +# client. +# +# Enabling this experimental parameter will cause the WebSocket connection to +# be closed instead if it cannot read fast enough, allowing for greater +# predictability in subscription behavior. +experimental_close_on_slow_client = false + +# How long to wait for a tx to be committed during /broadcast_tx_commit. +# WARNING: Using a value larger than 10s will result in increasing the +# global HTTP write timeout, which applies to all connections and endpoints. +# See https://github.com/tendermint/tendermint/issues/3435 +timeout_broadcast_tx_commit = "10s" + +# Maximum size of request body, in bytes +max_body_bytes = 1000000 + +# Maximum size of request header, in bytes +max_header_bytes = 1048576 + +# The path to a file containing certificate that is used to create the HTTPS server. +# Might be either absolute path or path related to CometBFT's config directory. +# If the certificate is signed by a certificate authority, +# the certFile should be the concatenation of the server's certificate, any intermediates, +# and the CA's certificate. +# NOTE: both tls_cert_file and tls_key_file must be present for CometBFT to create HTTPS server. +# Otherwise, HTTP server is run. +tls_cert_file = "" + +# The path to a file containing matching private key that is used to create the HTTPS server. +# Might be either absolute path or path related to CometBFT's config directory. +# NOTE: both tls-cert-file and tls-key-file must be present for CometBFT to create HTTPS server. +# Otherwise, HTTP server is run. +tls_key_file = "" + +# pprof listen address (https://golang.org/pkg/net/http/pprof) +pprof_laddr = "" + +####################################################### +### P2P Configuration Options ### +####################################################### +[p2p] + +# Address to listen for incoming connections +laddr = "tcp://0.0.0.0:26656" + +# Address to advertise to peers for them to dial +# If empty, will use the same port as the laddr, +# and will introspect on the listener or use UPnP +# to figure out the address. ip and port are required +# example: 159.89.10.97:26656 +external_address = "" + +# Comma separated list of seed nodes to connect to +seeds = "" + +# Comma separated list of nodes to keep persistent connections to +persistent_peers = "" + +# UPNP port forwarding +upnp = false + +# Path to address book +addr_book_file = "config/addrbook.json" + +# Set true for strict address routability rules +# Set false for private or local networks +addr_book_strict = true + +# Maximum number of inbound peers +max_num_inbound_peers = 40 + +# Maximum number of outbound peers to connect to, excluding persistent peers +max_num_outbound_peers = 10 + +# List of node IDs, to which a connection will be (re)established ignoring any existing limits +unconditional_peer_ids = "" + +# Maximum pause when redialing a persistent peer (if zero, exponential backoff is used) +persistent_peers_max_dial_period = "0s" + +# Time to wait before flushing messages out on the connection +flush_throttle_timeout = "100ms" + +# Maximum size of a message packet payload, in bytes +max_packet_msg_payload_size = 1024 + +# Rate at which packets can be sent, in bytes/second +send_rate = 5120000 + +# Rate at which packets can be received, in bytes/second +recv_rate = 5120000 + +# Set true to enable the peer-exchange reactor +pex = true + +# Seed mode, in which node constantly crawls the network and looks for +# peers. If another node asks it for addresses, it responds and disconnects. +# +# Does not work if the peer-exchange reactor is disabled. +seed_mode = false + +# Comma separated list of peer IDs to keep private (will not be gossiped to other peers) +private_peer_ids = "" + +# Toggle to disable guard against peers connecting from the same ip. +allow_duplicate_ip = false + +# Peer connection configuration. +handshake_timeout = "20s" +dial_timeout = "3s" + +####################################################### +### Mempool Configuration Option ### +####################################################### +[mempool] + +# Mempool version to use: +# 1) "v0" - (default) FIFO mempool. +# 2) "v1" - prioritized mempool (deprecated; will be removed in the next release). +version = "v0" + +recheck = true +broadcast = true +wal_dir = "" + +# Maximum number of transactions in the mempool +size = 5000 + +# Limit the total size of all txs in the mempool. +# This only accounts for raw transactions (e.g. given 1MB transactions and +# max_txs_bytes=5MB, mempool will only accept 5 transactions). +max_txs_bytes = 1073741824 + +# Size of the cache (used to filter transactions we saw earlier) in transactions +cache_size = 10000 + +# Do not remove invalid transactions from the cache (default: false) +# Set to true if it's not possible for any invalid transaction to become valid +# again in the future. +keep-invalid-txs-in-cache = false + +# Maximum size of a single transaction. +# NOTE: the max size of a tx transmitted over the network is {max_tx_bytes}. +max_tx_bytes = 1048576 + +# Maximum size of a batch of transactions to send to a peer +# Including space needed by encoding (one varint per transaction). +# XXX: Unused due to https://github.com/tendermint/tendermint/issues/5796 +max_batch_bytes = 0 + +# ttl-duration, if non-zero, defines the maximum amount of time a transaction +# can exist for in the mempool. +# +# Note, if ttl-num-blocks is also defined, a transaction will be removed if it +# has existed in the mempool at least ttl-num-blocks number of blocks or if it's +# insertion time into the mempool is beyond ttl-duration. +ttl-duration = "0s" + +# ttl-num-blocks, if non-zero, defines the maximum number of blocks a transaction +# can exist for in the mempool. +# +# Note, if ttl-duration is also defined, a transaction will be removed if it +# has existed in the mempool at least ttl-num-blocks number of blocks or if +# it's insertion time into the mempool is beyond ttl-duration. +ttl-num-blocks = 0 + +####################################################### +### State Sync Configuration Options ### +####################################################### +[statesync] +# State sync rapidly bootstraps a new node by discovering, fetching, and restoring a state machine +# snapshot from peers instead of fetching and replaying historical blocks. Requires some peers in +# the network to take and serve state machine snapshots. State sync is not attempted if the node +# has any local state (LastBlockHeight > 0). The node will have a truncated block history, +# starting from the height of the snapshot. +enable = false + +# RPC servers (comma-separated) for light client verification of the synced state machine and +# retrieval of state data for node bootstrapping. Also needs a trusted height and corresponding +# header hash obtained from a trusted source, and a period during which validators can be trusted. +# +# For Cosmos SDK-based chains, trust_period should usually be about 2/3 of the unbonding time (~2 +# weeks) during which they can be financially punished (slashed) for misbehavior. +rpc_servers = "" +trust_height = 0 +trust_hash = "" +trust_period = "168h0m0s" + +# Time to spend discovering snapshots before initiating a restore. +discovery_time = "15s" + +# Temporary directory for state sync snapshot chunks, defaults to the OS tempdir (typically /tmp). +# Will create a new, randomly named directory within, and remove it when done. +temp_dir = "" + +# The timeout duration before re-requesting a chunk, possibly from a different +# peer (default: 1 minute). +chunk_request_timeout = "10s" + +# The number of concurrent chunk fetchers to run (default: 1). +chunk_fetchers = "4" + +####################################################### +### Block Sync Configuration Options ### +####################################################### +[blocksync] + +# Block Sync version to use: +# +# In v0.37, v1 and v2 of the block sync protocols were deprecated. +# Please use v0 instead. +# +# 1) "v0" - the default block sync implementation +version = "v0" + +####################################################### +### Consensus Configuration Options ### +####################################################### +[consensus] + +wal_file = "data/cs.wal/wal" + +# How long we wait for a proposal block before prevoting nil +timeout_propose = "3s" +# How much timeout_propose increases with each round +timeout_propose_delta = "500ms" +# How long we wait after receiving +2/3 prevotes for “anything” (ie. not a single block or nil) +timeout_prevote = "1s" +# How much the timeout_prevote increases with each round +timeout_prevote_delta = "500ms" +# How long we wait after receiving +2/3 precommits for “anything” (ie. not a single block or nil) +timeout_precommit = "1s" +# How much the timeout_precommit increases with each round +timeout_precommit_delta = "500ms" +# How long we wait after committing a block, before starting on the new +# height (this gives us a chance to receive some more precommits, even +# though we already have +2/3). +timeout_commit = "1s" + +# How many blocks to look back to check existence of the node's consensus votes before joining consensus +# When non-zero, the node will panic upon restart +# if the same consensus key was used to sign {double_sign_check_height} last blocks. +# So, validators should stop the state machine, wait for some blocks, and then restart the state machine to avoid panic. +double_sign_check_height = 0 + +# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) +skip_timeout_commit = false + +# EmptyBlocks mode and possible interval between empty blocks +create_empty_blocks = true +create_empty_blocks_interval = "0s" + +# Reactor sleep duration parameters +peer_gossip_sleep_duration = "100ms" +peer_query_maj23_sleep_duration = "2s" + +####################################################### +### Storage Configuration Options ### +####################################################### +[storage] + +# Set to true to discard ABCI responses from the state store, which can save a +# considerable amount of disk space. Set to false to ensure ABCI responses are +# persisted. ABCI responses are required for /block_results RPC queries, and to +# reindex events in the command-line tool. +discard_abci_responses = false + +####################################################### +### Transaction Indexer Configuration Options ### +####################################################### +[tx_index] + +# What indexer to use for transactions +# +# The application will set which txs to index. In some cases a node operator will be able +# to decide which txs to index based on configuration set in the application. +# +# Options: +# 1) "null" +# 2) "kv" (default) - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). +# - When "kv" is chosen "tx.height" and "tx.hash" will always be indexed. +# 3) "psql" - the indexer services backed by PostgreSQL. +# When "kv" or "psql" is chosen "tx.height" and "tx.hash" will always be indexed. +indexer = "kv" + +# The PostgreSQL connection configuration, the connection format: +# postgresql://:@:/? +psql-conn = "" + +####################################################### +### Instrumentation Configuration Options ### +####################################################### +[instrumentation] + +# When true, Prometheus metrics are served under /metrics on +# PrometheusListenAddr. +# Check out the documentation for the list of available metrics. +prometheus = false + +# Address to listen for Prometheus collector(s) connections +prometheus_listen_addr = ":26660" + +# Maximum number of simultaneous connections. +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +max_open_connections = 3 + +# Instrumentation namespace +namespace = "cometbft" + +"#; + +#[cfg(test)] +mod tests { + use super::DEFAULT_COMETBFT_CONFIG; + use crate::facade::tendermint_config::TendermintConfig; + + #[test] + fn test_default_cometbft_config() { + assert!(TendermintConfig::parse_toml(DEFAULT_COMETBFT_CONFIG).is_ok()); + } +} diff --git a/apps/src/lib/config/utils.rs b/apps/src/lib/config/utils.rs index 19b2648d3b..ca038dcb42 100644 --- a/apps/src/lib/config/utils.rs +++ b/apps/src/lib/config/utils.rs @@ -1,11 +1,13 @@ //! Configuration utilities +use std::net::{SocketAddr, ToSocketAddrs}; use std::str::FromStr; use std::{cmp, env}; use itertools::Either; use crate::cli; +use crate::facade::tendermint_config::net::Address as TendermintAddress; /// Find how many threads to use from an environment variable if it's set and /// valid (>= 1). If the environment variable is invalid, exits the process with @@ -45,6 +47,24 @@ fn num_of_threads_aux( } } +// fixme: Handle this gracefully with either an Option or a Result. Ensure that +// hostname resolution works. +pub fn convert_tm_addr_to_socket_addr( + tm_addr: &TendermintAddress, +) -> SocketAddr { + let tm_addr = tm_addr.clone(); + match tm_addr { + TendermintAddress::Tcp { + peer_id: _, + host, + port, + } => (host, port).to_socket_addrs().unwrap().next().unwrap(), + TendermintAddress::Unix { path: _ } => { + panic!("Unix addresses aren't currently supported.") + } + } +} + #[cfg(test)] mod test { use std::panic; diff --git a/apps/src/lib/mod.rs b/apps/src/lib/mod.rs index 65d0472e9e..7df31ea2ea 100644 --- a/apps/src/lib/mod.rs +++ b/apps/src/lib/mod.rs @@ -23,8 +23,8 @@ pub mod facade { #[cfg(not(feature = "abcipp"))] pub use { - tendermint, tendermint_config, tendermint_proto, tendermint_rpc, - tower_abci, + namada::tendermint, namada::tendermint_proto, namada::tendermint_rpc, + tendermint_config, tower_abci, }; #[cfg(feature = "abcipp")] pub use { diff --git a/apps/src/lib/node/ledger/broadcaster.rs b/apps/src/lib/node/ledger/broadcaster.rs index 199ab953c1..915a7eee67 100644 --- a/apps/src/lib/node/ledger/broadcaster.rs +++ b/apps/src/lib/node/ledger/broadcaster.rs @@ -1,3 +1,5 @@ +use std::net::SocketAddr; + use tokio::sync::mpsc::UnboundedReceiver; use crate::facade::tendermint_rpc::{Client, HttpClient}; @@ -13,7 +15,7 @@ pub struct Broadcaster { impl Broadcaster { /// Create a new broadcaster that will send Http messages /// over the given url. - pub fn new(url: &str, receiver: UnboundedReceiver>) -> Self { + pub fn new(url: SocketAddr, receiver: UnboundedReceiver>) -> Self { Self { client: HttpClient::new(format!("http://{}", url).as_str()) .unwrap(), diff --git a/apps/src/lib/node/ledger/mod.rs b/apps/src/lib/node/ledger/mod.rs index 39a8460ac2..710df318bf 100644 --- a/apps/src/lib/node/ledger/mod.rs +++ b/apps/src/lib/node/ledger/mod.rs @@ -23,7 +23,7 @@ use tower::ServiceBuilder; use self::abortable::AbortableSpawner; use self::shims::abcipp_shim::AbciService; use crate::cli::args; -use crate::config::utils::num_of_threads; +use crate::config::utils::{convert_tm_addr_to_socket_addr, num_of_threads}; use crate::config::TendermintMode; use crate::facade::tendermint_proto::abci::CheckTxType; use crate::facade::tower_abci::{response, split, Server}; @@ -92,7 +92,7 @@ impl Shell { tracing::debug!("Request InitChain"); self.init_chain( init, - #[cfg(feature = "dev")] + #[cfg(any(test, feature = "dev"))] 1, ) .map(Response::InitChain) @@ -408,7 +408,8 @@ fn start_abci_broadcaster_shell( task::JoinHandle<()>, thread::JoinHandle<()>, ) { - let rpc_address = config.tendermint.rpc_address.to_string(); + let rpc_address = + convert_tm_addr_to_socket_addr(&config.cometbft.rpc.laddr); let RunAuxSetup { vp_wasm_compilation_cache, tx_wasm_compilation_cache, @@ -421,41 +422,40 @@ fn start_abci_broadcaster_shell( tokio::sync::mpsc::unbounded_channel(); // Start broadcaster - let broadcaster = if matches!( - config.tendermint.tendermint_mode, - TendermintMode::Validator - ) { - let (bc_abort_send, bc_abort_recv) = - tokio::sync::oneshot::channel::<()>(); - - spawner - .spawn_abortable("Broadcaster", move |aborter| async move { - // Construct a service for broadcasting protocol txs from the - // ledger - let mut broadcaster = - Broadcaster::new(&rpc_address, broadcaster_receiver); - broadcaster.run(bc_abort_recv).await; - tracing::info!("Broadcaster is no longer running."); - - drop(aborter); - }) - .with_cleanup(async move { - let _ = bc_abort_send.send(()); - }) - } else { - spawn_dummy_task(()) - }; + let broadcaster = + if matches!(config.shell.tendermint_mode, TendermintMode::Validator) { + let (bc_abort_send, bc_abort_recv) = + tokio::sync::oneshot::channel::<()>(); + + spawner + .spawn_abortable("Broadcaster", move |aborter| async move { + // Construct a service for broadcasting protocol txs from + // the ledger + let mut broadcaster = + Broadcaster::new(rpc_address, broadcaster_receiver); + broadcaster.run(bc_abort_recv).await; + tracing::info!("Broadcaster is no longer running."); + + drop(aborter); + }) + .with_cleanup(async move { + let _ = bc_abort_send.send(()); + }) + } else { + spawn_dummy_task(()) + }; // Setup DB cache, it must outlive the DB instance that's in the shell let db_cache = rocksdb::Cache::new_lru_cache(db_block_cache_size_bytes as usize); // Construct our ABCI application. - let tendermint_mode = config.tendermint.tendermint_mode.clone(); - let ledger_address = config.shell.ledger_address; - #[cfg(not(feature = "dev"))] + let tendermint_mode = config.shell.tendermint_mode.clone(); + let proxy_app_address = + convert_tm_addr_to_socket_addr(&config.cometbft.proxy_app); + #[cfg(not(any(test, feature = "dev")))] let genesis = genesis::genesis(&config.shell.base_dir, &config.chain_id); - #[cfg(feature = "dev")] + #[cfg(any(test, feature = "dev"))] let genesis = genesis::genesis(1); let (shell, abci_service, service_handle) = AbcippShim::new( config, @@ -476,7 +476,7 @@ fn start_abci_broadcaster_shell( let res = run_abci( abci_service, service_handle, - ledger_address, + proxy_app_address, abci_abort_recv, ) .await; @@ -513,7 +513,7 @@ fn start_abci_broadcaster_shell( async fn run_abci( abci_service: AbciService, service_handle: tokio::sync::broadcast::Sender<()>, - ledger_address: SocketAddr, + proxy_app_address: SocketAddr, abort_recv: tokio::sync::oneshot::Receiver<()>, ) -> shell::Result<()> { // Split it into components. @@ -541,7 +541,7 @@ async fn run_abci( .unwrap(); tokio::select! { // Run the server with the ABCI service - status = server.listen(ledger_address) => { + status = server.listen(proxy_app_address) => { status.map_err(|err| Error::TowerServer(err.to_string())) }, resp_sender = abort_recv => { @@ -566,17 +566,17 @@ fn start_tendermint( spawner: &mut AbortableSpawner, config: &config::Ledger, ) -> task::JoinHandle> { - let tendermint_dir = config.tendermint_dir(); + let tendermint_dir = config.cometbft_dir(); let chain_id = config.chain_id.clone(); - let ledger_address = config.shell.ledger_address.to_string(); - let tendermint_config = config.tendermint.clone(); + let proxy_app_address = config.cometbft.proxy_app.to_string(); + let config = config.clone(); let genesis_time = config .genesis_time .clone() .try_into() .expect("expected RFC3339 genesis_time"); - // Channel for signalling shut down to Tendermint process + // Channel for signalling shut down to cometbft process let (tm_abort_send, tm_abort_recv) = tokio::sync::oneshot::channel::>(); @@ -586,8 +586,8 @@ fn start_tendermint( tendermint_dir, chain_id, genesis_time, - ledger_address, - tendermint_config, + proxy_app_address, + config, tm_abort_recv, ) .map_err(Error::Tendermint) diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index 27ff2955ac..c5f0fc6807 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -75,10 +75,15 @@ where Some(EPOCH_SWITCH_BLOCKS_DELAY) ); - tracing::debug!( - "Block height: {height}, epoch: {current_epoch}, new epoch: \ + tracing::info!( + "Block height: {height}, epoch: {current_epoch}, is new epoch: \ {new_epoch}." ); + tracing::debug!( + "New epoch block delay for updating the Tendermint validator set: \ + {:?}", + self.wl_storage.storage.update_epoch_blocks_delay + ); if new_epoch { namada::ledger::storage::update_allowed_conversions( @@ -103,7 +108,10 @@ where // Invariant: This has to be applied after // `copy_validator_sets_and_positions` if we're starting a new epoch - self.slash(); + self.record_slashes_from_evidence(); + if new_epoch { + self.process_slashes(); + } let wrapper_fees = self.get_wrapper_tx_fees(); let mut stats = InternalStats::default(); @@ -127,26 +135,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 +157,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 +166,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 +181,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 +189,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,23 +202,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) => { - let mut tx_event = Event::new_tx_event(&tx_type, height.0); + stats.increment_wrapper_txs(); + 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![]) @@ -275,8 +282,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, }); @@ -291,20 +298,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(); @@ -312,7 +322,7 @@ where } (event, Some(wrapper_hash)) } - TxType::Raw(_) => { + TxType::Raw => { tracing::error!( "Internal logic error: FinalizeBlock received a \ TxType::Raw transaction" @@ -329,7 +339,7 @@ where }; match protocol::apply_tx( - tx_type, + tx.clone(), tx_length, TxIndex( tx_index @@ -364,7 +374,9 @@ where } for ibc_event in &result.ibc_events { // Add the IBC event besides the tx_event - let event = Event::from(ibc_event.clone()); + let mut event = Event::from(ibc_event.clone()); + // Add the height for IBC event query + event["height"] = height.to_string(); response.events.push(event); } match serde_json::to_string( @@ -417,8 +429,8 @@ where .storage .delete(&tx_hash_key) .expect( - "Error while deleting tx hash key from storage", - ); + "Error while deleting tx hash key from storage", + ); } } @@ -526,7 +538,7 @@ where hash: BlockHash, byzantine_validators: Vec, ) -> (BlockHeight, bool) { - let height = self.wl_storage.storage.last_height + 1; + let height = self.wl_storage.storage.get_last_block_height() + 1; self.gas_meter.reset(); @@ -600,7 +612,7 @@ where /// executed while finalizing the first block of a new epoch and is applied /// with respect to the previous epoch. fn apply_inflation(&mut self, current_epoch: Epoch) -> Result<()> { - let last_epoch = current_epoch - 1; + let last_epoch = current_epoch.prev(); // Get input values needed for the PD controller for PoS and MASP. // Run the PD controllers to calculate new rates. // @@ -885,32 +897,44 @@ fn pos_votes_from_abci( #[cfg(test)] mod test_finalize_block { use std::collections::{BTreeMap, BTreeSet}; - use std::str::FromStr; use data_encoding::HEXUPPER; use namada::ledger::parameters::EpochDuration; use namada::ledger::storage_api; use namada::proof_of_stake::btree_set::BTreeSetShims; - use namada::proof_of_stake::types::WeightedValidator; + use namada::proof_of_stake::parameters::PosParams; + use namada::proof_of_stake::storage::{ + is_validator_slashes_key, slashes_prefix, + }; + use namada::proof_of_stake::types::{ + decimal_mult_amount, BondId, SlashType, ValidatorState, + WeightedValidator, + }; use namada::proof_of_stake::{ + enqueued_slashes_handle, get_num_consensus_validators, read_consensus_validator_set_addresses_with_stake, - rewards_accumulator_handle, validator_consensus_key_handle, - validator_rewards_products_handle, + rewards_accumulator_handle, unjail_validator, + validator_consensus_key_handle, validator_rewards_products_handle, + validator_slashes_handle, validator_state_handle, write_pos_params, }; + 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; use namada::types::time::DurationSecs; + use namada::types::token::Amount; 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; use super::*; - use crate::facade::tendermint_proto::abci::{Validator, VoteInfo}; + use crate::facade::tendermint_proto::abci::{ + Misbehavior, Validator, VoteInfo, + }; use crate::node::ledger::shell::test_utils::*; use crate::node::ledger::shims::abcipp_shim_types::shim::request::{ FinalizeBlock, ProcessedTx, @@ -938,31 +962,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"), @@ -998,10 +1022,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); @@ -1015,13 +1038,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(), @@ -1029,25 +1046,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 @@ -1072,13 +1092,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(), @@ -1087,23 +1101,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 @@ -1147,37 +1158,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(), @@ -1186,35 +1195,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(), @@ -1260,10 +1267,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); @@ -1334,12 +1340,11 @@ mod test_finalize_block { // Collect all storage key-vals into a sorted map let store_block_state = |shell: &TestShell| -> BTreeMap<_, _> { - let prefix: Key = FromStr::from_str("").unwrap(); shell .wl_storage .storage .db - .iter_prefix(&prefix) + .iter_optional_prefix(None) .map(|(key, val, _gas)| (key, val)) .collect() }; @@ -1505,7 +1510,7 @@ mod test_finalize_block { // FINALIZE BLOCK 1. Tell Namada that val1 is the block proposer. We // won't receive votes from TM since we receive votes at a 1-block // delay, so votes will be empty here - next_block_for_inflation(&mut shell, pkh1.clone(), vec![]); + next_block_for_inflation(&mut shell, pkh1.clone(), vec![], None); assert!( rewards_accumulator_handle() .is_empty(&shell.wl_storage) @@ -1515,7 +1520,7 @@ mod test_finalize_block { // FINALIZE BLOCK 2. Tell Namada that val1 is the block proposer. // Include votes that correspond to block 1. Make val2 the next block's // proposer. - next_block_for_inflation(&mut shell, pkh2.clone(), votes.clone()); + next_block_for_inflation(&mut shell, pkh2.clone(), votes.clone(), None); assert!(rewards_prod_1.is_empty(&shell.wl_storage).unwrap()); assert!(rewards_prod_2.is_empty(&shell.wl_storage).unwrap()); assert!(rewards_prod_3.is_empty(&shell.wl_storage).unwrap()); @@ -1538,7 +1543,7 @@ mod test_finalize_block { ); // FINALIZE BLOCK 3, with val1 as proposer for the next block. - next_block_for_inflation(&mut shell, pkh1.clone(), votes); + next_block_for_inflation(&mut shell, pkh1.clone(), votes, None); assert!(rewards_prod_1.is_empty(&shell.wl_storage).unwrap()); assert!(rewards_prod_2.is_empty(&shell.wl_storage).unwrap()); assert!(rewards_prod_3.is_empty(&shell.wl_storage).unwrap()); @@ -1590,7 +1595,7 @@ mod test_finalize_block { // FINALIZE BLOCK 4. The next block proposer will be val1. Only val1, // val2, and val3 vote on this block. - next_block_for_inflation(&mut shell, pkh1.clone(), votes.clone()); + next_block_for_inflation(&mut shell, pkh1.clone(), votes.clone(), None); assert!(rewards_prod_1.is_empty(&shell.wl_storage).unwrap()); assert!(rewards_prod_2.is_empty(&shell.wl_storage).unwrap()); assert!(rewards_prod_3.is_empty(&shell.wl_storage).unwrap()); @@ -1623,7 +1628,12 @@ mod test_finalize_block { get_rewards_acc(&shell.wl_storage), get_rewards_sum(&shell.wl_storage), ); - next_block_for_inflation(&mut shell, pkh1.clone(), votes.clone()); + next_block_for_inflation( + &mut shell, + pkh1.clone(), + votes.clone(), + None, + ); } assert!( rewards_accumulator_handle() @@ -1678,12 +1688,26 @@ mod test_finalize_block { shell: &mut TestShell, proposer_address: Vec, votes: Vec, + byzantine_validators: Option>, ) { - let req = FinalizeBlock { + // Let the header time be always ahead of the next epoch min start time + let header = Header { + time: shell + .wl_storage + .storage + .next_epoch_min_start_time + .next_second(), + ..Default::default() + }; + let mut req = FinalizeBlock { + header, proposer_address, votes, ..Default::default() }; + if let Some(byz_vals) = byzantine_validators { + req.byzantine_validators = byz_vals; + } shell.finalize_block(req).unwrap(); shell.commit(); } @@ -1699,29 +1723,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 @@ -1729,12 +1759,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(), @@ -1764,4 +1789,1226 @@ mod test_finalize_block { // .0 // ) } + + #[test] + fn test_ledger_slashing() -> storage_api::Result<()> { + let num_validators = 7_u64; + let (mut shell, _) = setup(num_validators); + let mut params = read_pos_params(&shell.wl_storage).unwrap(); + params.unbonding_len = 4; + write_pos_params(&mut shell.wl_storage, params.clone())?; + + let validator_set: Vec = + read_consensus_validator_set_addresses_with_stake( + &shell.wl_storage, + Epoch::default(), + ) + .unwrap() + .into_iter() + .collect(); + + let val1 = validator_set[0].clone(); + let val2 = validator_set[1].clone(); + + let initial_stake = val1.bonded_stake; + let total_initial_stake = num_validators * initial_stake; + + let get_pkh = |address, epoch| { + let ck = validator_consensus_key_handle(&address) + .get(&shell.wl_storage, epoch, ¶ms) + .unwrap() + .unwrap(); + let hash_string = tm_consensus_key_raw_hash(&ck); + HEXUPPER.decode(hash_string.as_bytes()).unwrap() + }; + + let mut all_pkhs: Vec> = Vec::new(); + let mut behaving_pkhs: Vec> = Vec::new(); + for (idx, validator) in validator_set.iter().enumerate() { + // Every validator should be in the consensus set + assert_eq!( + validator_state_handle(&validator.address) + .get(&shell.wl_storage, Epoch::default(), ¶ms) + .unwrap(), + Some(ValidatorState::Consensus) + ); + all_pkhs.push(get_pkh(validator.address.clone(), Epoch::default())); + if idx > 1_usize { + behaving_pkhs + .push(get_pkh(validator.address.clone(), Epoch::default())); + } + } + + let pkh1 = all_pkhs[0].clone(); + let pkh2 = all_pkhs[1].clone(); + + // Finalize block 1 (no votes since this is the first block) + next_block_for_inflation(&mut shell, pkh1.clone(), vec![], None); + + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); + assert!(!votes.is_empty()); + assert_eq!(votes.len(), 7_usize); + + // For block 2, include the evidences found for block 1. + // NOTE: Only the type, height, and validator address fields from the + // Misbehavior struct are used in Namada + let byzantine_validators = vec![ + Misbehavior { + r#type: 1, + validator: Some(Validator { + address: pkh1.clone(), + power: Default::default(), + }), + height: 1, + time: Default::default(), + total_voting_power: Default::default(), + }, + Misbehavior { + r#type: 2, + validator: Some(Validator { + address: pkh2, + power: Default::default(), + }), + height: 1, + time: Default::default(), + total_voting_power: Default::default(), + }, + ]; + next_block_for_inflation( + &mut shell, + pkh1.clone(), + votes, + Some(byzantine_validators), + ); + + let processing_epoch = shell.wl_storage.storage.block.epoch + + params.unbonding_len + + 1_u64 + + params.cubic_slashing_window_length; + + // Check that the ValidatorState, enqueued slashes, and validator sets + // are properly updated + assert_eq!( + validator_state_handle(&val1.address) + .get(&shell.wl_storage, Epoch::default(), ¶ms) + .unwrap(), + Some(ValidatorState::Consensus) + ); + assert_eq!( + validator_state_handle(&val2.address) + .get(&shell.wl_storage, Epoch::default(), ¶ms) + .unwrap(), + Some(ValidatorState::Consensus) + ); + assert!( + enqueued_slashes_handle() + .at(&Epoch::default()) + .is_empty(&shell.wl_storage)? + ); + assert_eq!( + get_num_consensus_validators(&shell.wl_storage, Epoch::default()) + .unwrap(), + 7_u64 + ); + for epoch in Epoch::default().next().iter_range(params.pipeline_len) { + assert_eq!( + validator_state_handle(&val1.address) + .get(&shell.wl_storage, epoch, ¶ms) + .unwrap(), + Some(ValidatorState::Jailed) + ); + assert_eq!( + validator_state_handle(&val2.address) + .get(&shell.wl_storage, epoch, ¶ms) + .unwrap(), + Some(ValidatorState::Jailed) + ); + assert!( + enqueued_slashes_handle() + .at(&epoch) + .is_empty(&shell.wl_storage)? + ); + assert_eq!( + get_num_consensus_validators(&shell.wl_storage, epoch).unwrap(), + 5_u64 + ); + } + assert!( + !enqueued_slashes_handle() + .at(&processing_epoch) + .is_empty(&shell.wl_storage)? + ); + + // Advance to the processing epoch + let votes = get_default_true_votes(&shell.wl_storage, Epoch::default()); + loop { + next_block_for_inflation( + &mut shell, + pkh1.clone(), + votes.clone(), + None, + ); + // println!( + // "Block {} epoch {}", + // shell.wl_storage.storage.block.height, + // shell.wl_storage.storage.block.epoch + // ); + if shell.wl_storage.storage.block.epoch == processing_epoch { + // println!("Reached processing epoch"); + break; + } else { + assert!( + enqueued_slashes_handle() + .at(&shell.wl_storage.storage.block.epoch) + .is_empty(&shell.wl_storage)? + ); + let stake1 = read_validator_stake( + &shell.wl_storage, + ¶ms, + &val1.address, + shell.wl_storage.storage.block.epoch, + )? + .unwrap(); + let stake2 = read_validator_stake( + &shell.wl_storage, + ¶ms, + &val2.address, + shell.wl_storage.storage.block.epoch, + )? + .unwrap(); + let total_stake = read_total_stake( + &shell.wl_storage, + ¶ms, + shell.wl_storage.storage.block.epoch, + )?; + assert_eq!(stake1, initial_stake); + assert_eq!(stake2, initial_stake); + assert_eq!(total_stake, total_initial_stake); + } + } + + let num_slashes = storage_api::iter_prefix_bytes( + &shell.wl_storage, + &slashes_prefix(), + )? + .filter(|kv_res| { + let (k, _v) = kv_res.as_ref().unwrap(); + is_validator_slashes_key(k).is_some() + }) + .count(); + + assert_eq!(num_slashes, 2); + assert_eq!( + validator_slashes_handle(&val1.address) + .len(&shell.wl_storage) + .unwrap(), + 1_u64 + ); + assert_eq!( + validator_slashes_handle(&val2.address) + .len(&shell.wl_storage) + .unwrap(), + 1_u64 + ); + + let slash1 = validator_slashes_handle(&val1.address) + .get(&shell.wl_storage, 0)? + .unwrap(); + let slash2 = validator_slashes_handle(&val2.address) + .get(&shell.wl_storage, 0)? + .unwrap(); + + assert_eq!(slash1.r#type, SlashType::DuplicateVote); + assert_eq!(slash2.r#type, SlashType::LightClientAttack); + assert_eq!(slash1.epoch, Epoch::default()); + assert_eq!(slash2.epoch, Epoch::default()); + + // Each validator has equal weight in this test, and two have been + // slashed + let frac = dec!(2) / dec!(7); + let cubic_rate = dec!(9) * frac * frac; + + assert_eq!(slash1.rate, cubic_rate); + assert_eq!(slash2.rate, cubic_rate); + + // Check that there are still 5 consensus validators and the 2 + // misbehaving ones are still jailed + for epoch in shell + .wl_storage + .storage + .block + .epoch + .iter_range(params.pipeline_len + 1) + { + assert_eq!( + validator_state_handle(&val1.address) + .get(&shell.wl_storage, epoch, ¶ms) + .unwrap(), + Some(ValidatorState::Jailed) + ); + assert_eq!( + validator_state_handle(&val2.address) + .get(&shell.wl_storage, epoch, ¶ms) + .unwrap(), + Some(ValidatorState::Jailed) + ); + assert_eq!( + get_num_consensus_validators(&shell.wl_storage, epoch).unwrap(), + 5_u64 + ); + } + + // Check that the deltas at the pipeline epoch are slashed + let pipeline_epoch = + shell.wl_storage.storage.block.epoch + params.pipeline_len; + let stake1 = read_validator_stake( + &shell.wl_storage, + ¶ms, + &val1.address, + pipeline_epoch, + )? + .unwrap(); + let stake2 = read_validator_stake( + &shell.wl_storage, + ¶ms, + &val2.address, + pipeline_epoch, + )? + .unwrap(); + let total_stake = + read_total_stake(&shell.wl_storage, ¶ms, pipeline_epoch)?; + + let expected_slashed = decimal_mult_amount(cubic_rate, initial_stake); + assert_eq!(stake1, initial_stake - expected_slashed); + assert_eq!(stake2, initial_stake - expected_slashed); + assert_eq!(total_stake, total_initial_stake - 2 * expected_slashed); + + // Unjail one of the validators + let current_epoch = shell.wl_storage.storage.block.epoch; + unjail_validator(&mut shell.wl_storage, &val1.address, current_epoch)?; + let pipeline_epoch = current_epoch + params.pipeline_len; + + // Check that the state is the same until the pipeline epoch, at which + // point one validator is unjailed + for epoch in shell + .wl_storage + .storage + .block + .epoch + .iter_range(params.pipeline_len) + { + assert_eq!( + validator_state_handle(&val1.address) + .get(&shell.wl_storage, epoch, ¶ms) + .unwrap(), + Some(ValidatorState::Jailed) + ); + assert_eq!( + validator_state_handle(&val2.address) + .get(&shell.wl_storage, epoch, ¶ms) + .unwrap(), + Some(ValidatorState::Jailed) + ); + assert_eq!( + get_num_consensus_validators(&shell.wl_storage, epoch).unwrap(), + 5_u64 + ); + } + assert_eq!( + validator_state_handle(&val1.address) + .get(&shell.wl_storage, pipeline_epoch, ¶ms) + .unwrap(), + Some(ValidatorState::Consensus) + ); + assert_eq!( + validator_state_handle(&val2.address) + .get(&shell.wl_storage, pipeline_epoch, ¶ms) + .unwrap(), + Some(ValidatorState::Jailed) + ); + assert_eq!( + get_num_consensus_validators(&shell.wl_storage, pipeline_epoch) + .unwrap(), + 6_u64 + ); + + Ok(()) + } + + /// NOTE: must call `get_default_true_votes` before every call to + /// `next_block_for_inflation` + #[test] + fn test_multiple_misbehaviors() -> storage_api::Result<()> { + for num_validators in 4u64..10u64 { + println!("NUM VALIDATORS = {}", num_validators); + test_multiple_misbehaviors_by_num_vals(num_validators)?; + } + Ok(()) + } + + /// Current test procedure (prefixed by epoch in which the event occurs): + /// 0) Validator initial stake of 200_000 + /// 1) Delegate 67_231 to validator + /// 1) Self-unbond 154_654 + /// 2) Unbond delegation of 18_000 + /// 3) Self-bond 9_123 + /// 4) Self-unbond 15_000 + /// 5) Delegate 8_144 to validator + /// 6) Discover misbehavior in epoch 3 + /// 7) Discover misbehavior in epoch 3 + /// 7) Discover misbehavior in epoch 4 + fn test_multiple_misbehaviors_by_num_vals( + num_validators: u64, + ) -> storage_api::Result<()> { + // Setup the network with pipeline_len = 2, unbonding_len = 4 + // let num_validators = 8_u64; + let (mut shell, _) = setup(num_validators); + let mut params = read_pos_params(&shell.wl_storage).unwrap(); + params.unbonding_len = 4; + params.max_validator_slots = 4; + write_pos_params(&mut shell.wl_storage, params.clone())?; + + // Slash pool balance + let nam_address = shell.wl_storage.storage.native_token.clone(); + let slash_balance_key = token::balance_key( + &nam_address, + &namada_proof_of_stake::SLASH_POOL_ADDRESS, + ); + let slash_pool_balance_init: token::Amount = shell + .wl_storage + .read(&slash_balance_key) + .expect("must be able to read") + .unwrap_or_default(); + debug_assert_eq!(slash_pool_balance_init, token::Amount::default()); + + let consensus_set: Vec = + read_consensus_validator_set_addresses_with_stake( + &shell.wl_storage, + Epoch::default(), + ) + .unwrap() + .into_iter() + .collect(); + + let val1 = consensus_set[0].clone(); + let pkh1 = get_pkh_from_address( + &shell.wl_storage, + ¶ms, + val1.address.clone(), + Epoch::default(), + ); + + let initial_stake = val1.bonded_stake; + let total_initial_stake = num_validators * initial_stake; + + // Finalize block 1 + next_block_for_inflation(&mut shell, pkh1.clone(), vec![], None); + + let votes = get_default_true_votes(&shell.wl_storage, Epoch::default()); + assert!(!votes.is_empty()); + + // Advance to epoch 1 and + // 1. Delegate 67231 NAM to validator + // 2. Validator self-unbond 154654 NAM + let current_epoch = advance_epoch(&mut shell, &pkh1, &votes, None); + assert_eq!(shell.wl_storage.storage.block.epoch.0, 1_u64); + + // Make an account with balance and delegate some tokens + let delegator = address::testing::gen_implicit_address(); + let del_1_amount = token::Amount::whole(67_231); + let staking_token = shell.wl_storage.storage.native_token.clone(); + credit_tokens( + &mut shell.wl_storage, + &staking_token, + &delegator, + token::Amount::whole(200_000), + ) + .unwrap(); + namada_proof_of_stake::bond_tokens( + &mut shell.wl_storage, + Some(&delegator), + &val1.address, + del_1_amount, + current_epoch, + ) + .unwrap(); + + // Self-unbond + let self_unbond_1_amount = token::Amount::whole(154_654); + namada_proof_of_stake::unbond_tokens( + &mut shell.wl_storage, + None, + &val1.address, + self_unbond_1_amount, + current_epoch, + ) + .unwrap(); + + let val_stake = namada_proof_of_stake::read_validator_stake( + &shell.wl_storage, + ¶ms, + &val1.address, + current_epoch + params.pipeline_len, + ) + .unwrap() + .unwrap_or_default(); + + let total_stake = namada_proof_of_stake::read_total_stake( + &shell.wl_storage, + ¶ms, + current_epoch + params.pipeline_len, + ) + .unwrap(); + + assert_eq!( + val_stake, + initial_stake + del_1_amount - self_unbond_1_amount + ); + assert_eq!( + total_stake, + total_initial_stake + del_1_amount - self_unbond_1_amount + ); + + // Advance to epoch 2 and + // 1. Unbond 18000 NAM from delegation + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); + let current_epoch = advance_epoch(&mut shell, &pkh1, &votes, None); + println!("\nUnbonding in epoch 2"); + let del_unbond_1_amount = token::Amount::whole(18_000); + namada_proof_of_stake::unbond_tokens( + &mut shell.wl_storage, + Some(&delegator), + &val1.address, + del_unbond_1_amount, + current_epoch, + ) + .unwrap(); + + let val_stake = namada_proof_of_stake::read_validator_stake( + &shell.wl_storage, + ¶ms, + &val1.address, + current_epoch + params.pipeline_len, + ) + .unwrap() + .unwrap_or_default(); + let total_stake = namada_proof_of_stake::read_total_stake( + &shell.wl_storage, + ¶ms, + current_epoch + params.pipeline_len, + ) + .unwrap(); + assert_eq!( + val_stake, + initial_stake + del_1_amount + - self_unbond_1_amount + - del_unbond_1_amount + ); + assert_eq!( + total_stake, + total_initial_stake + del_1_amount + - self_unbond_1_amount + - del_unbond_1_amount + ); + + // Advance to epoch 3 and + // 1. Validator self-bond 9123 NAM + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); + let current_epoch = advance_epoch(&mut shell, &pkh1, &votes, None); + println!("\nBonding in epoch 3"); + + let self_bond_1_amount = token::Amount::whole(9_123); + namada_proof_of_stake::bond_tokens( + &mut shell.wl_storage, + None, + &val1.address, + self_bond_1_amount, + current_epoch, + ) + .unwrap(); + + // Advance to epoch 4 + // 1. Validator self-unbond 15000 NAM + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); + let current_epoch = advance_epoch(&mut shell, &pkh1, &votes, None); + assert_eq!(current_epoch.0, 4_u64); + + let self_unbond_2_amount = token::Amount::whole(15_000); + namada_proof_of_stake::unbond_tokens( + &mut shell.wl_storage, + None, + &val1.address, + self_unbond_2_amount, + current_epoch, + ) + .unwrap(); + + // Advance to epoch 5 and + // Delegate 8144 NAM to validator + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); + let current_epoch = advance_epoch(&mut shell, &pkh1, &votes, None); + assert_eq!(current_epoch.0, 5_u64); + println!("Delegating in epoch 5"); + + // Delegate + let del_2_amount = token::Amount::whole(8_144); + namada_proof_of_stake::bond_tokens( + &mut shell.wl_storage, + Some(&delegator), + &val1.address, + del_2_amount, + current_epoch, + ) + .unwrap(); + + println!("Advancing to epoch 6"); + + // Advance to epoch 6 + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); + let current_epoch = advance_epoch(&mut shell, &pkh1, &votes, None); + assert_eq!(current_epoch.0, 6_u64); + + // Discover a misbehavior committed in epoch 3 + // NOTE: Only the type, height, and validator address fields from the + // Misbehavior struct are used in Namada + let misbehavior_epoch = Epoch(3_u64); + let height = shell + .wl_storage + .storage + .block + .pred_epochs + .first_block_heights[misbehavior_epoch.0 as usize]; + let misbehaviors = vec![Misbehavior { + r#type: 1, + validator: Some(Validator { + address: pkh1.clone(), + power: Default::default(), + }), + height: height.0 as i64, + time: Default::default(), + total_voting_power: Default::default(), + }]; + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); + next_block_for_inflation( + &mut shell, + pkh1.clone(), + votes.clone(), + Some(misbehaviors), + ); + + // Assertions + assert_eq!(current_epoch.0, 6_u64); + let processing_epoch = misbehavior_epoch + + params.unbonding_len + + 1_u64 + + params.cubic_slashing_window_length; + let enqueued_slash = enqueued_slashes_handle() + .at(&processing_epoch) + .at(&val1.address) + .front(&shell.wl_storage) + .unwrap() + .unwrap(); + assert_eq!(enqueued_slash.epoch, misbehavior_epoch); + assert_eq!(enqueued_slash.r#type, SlashType::DuplicateVote); + assert_eq!(enqueued_slash.rate, Decimal::ZERO); + let last_slash = + namada_proof_of_stake::read_validator_last_slash_epoch( + &shell.wl_storage, + &val1.address, + ) + .unwrap(); + assert_eq!(last_slash, Some(misbehavior_epoch)); + assert!( + namada_proof_of_stake::validator_slashes_handle(&val1.address) + .is_empty(&shell.wl_storage) + .unwrap() + ); + + println!("Advancing to epoch 7"); + + // Advance to epoch 7 + let current_epoch = advance_epoch(&mut shell, &pkh1, &votes, None); + + // Discover two more misbehaviors, one committed in epoch 3, one in + // epoch 4 + let height4 = shell + .wl_storage + .storage + .block + .pred_epochs + .first_block_heights[4]; + let misbehaviors = vec![ + Misbehavior { + r#type: 1, + validator: Some(Validator { + address: pkh1.clone(), + power: Default::default(), + }), + height: height.0 as i64, + time: Default::default(), + total_voting_power: Default::default(), + }, + Misbehavior { + r#type: 2, + validator: Some(Validator { + address: pkh1.clone(), + power: Default::default(), + }), + height: height4.0 as i64, + time: Default::default(), + total_voting_power: Default::default(), + }, + ]; + next_block_for_inflation( + &mut shell, + pkh1.clone(), + votes.clone(), + Some(misbehaviors), + ); + assert_eq!(current_epoch.0, 7_u64); + let enqueued_slashes_8 = enqueued_slashes_handle() + .at(&processing_epoch) + .at(&val1.address); + let enqueued_slashes_9 = enqueued_slashes_handle() + .at(&processing_epoch.next()) + .at(&val1.address); + + assert_eq!(enqueued_slashes_8.len(&shell.wl_storage).unwrap(), 2_u64); + assert_eq!(enqueued_slashes_9.len(&shell.wl_storage).unwrap(), 1_u64); + let last_slash = + namada_proof_of_stake::read_validator_last_slash_epoch( + &shell.wl_storage, + &val1.address, + ) + .unwrap(); + assert_eq!(last_slash, Some(Epoch(4))); + assert!( + namada_proof_of_stake::is_validator_frozen( + &shell.wl_storage, + &val1.address, + current_epoch, + ¶ms + ) + .unwrap() + ); + assert!( + namada_proof_of_stake::validator_slashes_handle(&val1.address) + .is_empty(&shell.wl_storage) + .unwrap() + ); + + let pre_stake_10 = namada_proof_of_stake::read_validator_stake( + &shell.wl_storage, + ¶ms, + &val1.address, + Epoch(10), + ) + .unwrap() + .unwrap_or_default(); + assert_eq!( + pre_stake_10, + initial_stake + del_1_amount + - self_unbond_1_amount + - del_unbond_1_amount + + self_bond_1_amount + - self_unbond_2_amount + + del_2_amount + ); + + println!("\nNow processing the infractions\n"); + + // Advance to epoch 9, where the infractions committed in epoch 3 will + // be processed + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); + let _ = advance_epoch(&mut shell, &pkh1, &votes, None); + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); + let current_epoch = advance_epoch(&mut shell, &pkh1, &votes, None); + assert_eq!(current_epoch.0, 9_u64); + + let val_stake_3 = namada_proof_of_stake::read_validator_stake( + &shell.wl_storage, + ¶ms, + &val1.address, + Epoch(3), + ) + .unwrap() + .unwrap_or_default(); + let val_stake_4 = namada_proof_of_stake::read_validator_stake( + &shell.wl_storage, + ¶ms, + &val1.address, + Epoch(4), + ) + .unwrap() + .unwrap_or_default(); + + let tot_stake_3 = namada_proof_of_stake::read_total_stake( + &shell.wl_storage, + ¶ms, + Epoch(3), + ) + .unwrap(); + let tot_stake_4 = namada_proof_of_stake::read_total_stake( + &shell.wl_storage, + ¶ms, + Epoch(4), + ) + .unwrap(); + + let vp_frac_3 = Decimal::from(val_stake_3) / Decimal::from(tot_stake_3); + let vp_frac_4 = Decimal::from(val_stake_4) / Decimal::from(tot_stake_4); + let tot_frac = dec!(2) * vp_frac_3 + vp_frac_4; + let cubic_rate = + std::cmp::min(Decimal::ONE, dec!(9) * tot_frac * tot_frac); + dbg!(&cubic_rate); + + let equal_enough = |rate1: Decimal, rate2: Decimal| -> bool { + let tolerance = dec!(0.000000001); + (rate1 - rate2).abs() < tolerance + }; + + // There should be 2 slashes processed for the validator, each with rate + // equal to the cubic slashing rate + let val_slashes = + namada_proof_of_stake::validator_slashes_handle(&val1.address); + assert_eq!(val_slashes.len(&shell.wl_storage).unwrap(), 2u64); + let is_rate_good = val_slashes + .iter(&shell.wl_storage) + .unwrap() + .all(|s| equal_enough(s.unwrap().rate, cubic_rate)); + assert!(is_rate_good); + + // Check the amount of stake deducted from the futuremost epoch while + // processing the slashes + let post_stake_10 = namada_proof_of_stake::read_validator_stake( + &shell.wl_storage, + ¶ms, + &val1.address, + Epoch(10), + ) + .unwrap() + .unwrap_or_default(); + // The amount unbonded after the infraction that affected the deltas + // before processing is `del_unbond_1_amount + self_bond_1_amount - + // self_unbond_2_amount` (since this self-bond was enacted then unbonded + // all after the infraction). Thus, the additional deltas to be + // deducted is the (infraction stake - this) * rate + let slash_rate_3 = std::cmp::min(Decimal::ONE, dec!(2) * cubic_rate); + let exp_slashed_during_processing_9 = decimal_mult_amount( + slash_rate_3, + initial_stake + del_1_amount + - self_unbond_1_amount + - del_unbond_1_amount + + self_bond_1_amount + - self_unbond_2_amount, + ); + assert_eq!( + pre_stake_10 - post_stake_10, + exp_slashed_during_processing_9 + ); + + // Check that we can compute the stake at the pipeline epoch + // NOTE: may be off. by 1 namnam due to rounding; + let exp_pipeline_stake = decimal_mult_amount( + Decimal::ONE - slash_rate_3, + initial_stake + del_1_amount + - self_unbond_1_amount + - del_unbond_1_amount + + self_bond_1_amount + - self_unbond_2_amount, + ) + del_2_amount; + assert!( + (exp_pipeline_stake.change() - post_stake_10.change()).abs() <= 1 + ); + + // Check the balance of the Slash Pool + // TODO: finish once implemented + // let slash_pool_balance: token::Amount = shell + // .wl_storage + // .read(&slash_balance_key) + // .expect("must be able to read") + // .unwrap_or_default(); + // let exp_slashed_3 = decimal_mult_amount( + // std::cmp::min(Decimal::TWO * cubic_rate, Decimal::ONE), + // val_stake_3 - del_unbond_1_amount + self_bond_1_amount + // - self_unbond_2_amount, + // ); + // assert_eq!(slash_pool_balance, exp_slashed_3); + + let _pre_stake_11 = namada_proof_of_stake::read_validator_stake( + &shell.wl_storage, + ¶ms, + &val1.address, + Epoch(10), + ) + .unwrap() + .unwrap_or_default(); + + // Advance to epoch 10, where the infraction committed in epoch 4 will + // be processed + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); + let current_epoch = advance_epoch(&mut shell, &pkh1, &votes, None); + assert_eq!(current_epoch.0, 10_u64); + + // Check the balance of the Slash Pool + // TODO: finish once implemented + // let slash_pool_balance: token::Amount = shell + // .wl_storage + // .read(&slash_balance_key) + // .expect("must be able to read") + // .unwrap_or_default(); + + // let exp_slashed_4 = if dec!(2) * cubic_rate >= Decimal::ONE { + // token::Amount::default() + // } else if dec!(3) * cubic_rate >= Decimal::ONE { + // decimal_mult_amount( + // Decimal::ONE - dec!(2) * cubic_rate, + // val_stake_4 + self_bond_1_amount - self_unbond_2_amount, + // ) + // } else { + // decimal_mult_amount( + // std::cmp::min(cubic_rate, Decimal::ONE), + // val_stake_4 + self_bond_1_amount - self_unbond_2_amount, + // ) + // }; + // dbg!(slash_pool_balance, exp_slashed_3 + exp_slashed_4); + // assert!( + // (slash_pool_balance.change() + // - (exp_slashed_3 + exp_slashed_4).change()) + // .abs() + // <= 1 + // ); + + let val_stake = read_validator_stake( + &shell.wl_storage, + ¶ms, + &val1.address, + current_epoch + params.pipeline_len, + )? + .unwrap_or_default(); + + let post_stake_11 = namada_proof_of_stake::read_validator_stake( + &shell.wl_storage, + ¶ms, + &val1.address, + Epoch(10), + ) + .unwrap() + .unwrap_or_default(); + + assert_eq!(post_stake_11, val_stake); + // dbg!(&val_stake); + // dbg!(pre_stake_10 - post_stake_10); + + // dbg!(&exp_slashed_during_processing_9); + // TODO: finish once implemented + // assert!( + // ((pre_stake_11 - post_stake_11).change() - + // exp_slashed_4.change()) .abs() + // <= 1 + // ); + + // dbg!(&val_stake, &exp_stake); + // dbg!(exp_slashed_during_processing_8 + + // exp_slashed_during_processing_9); dbg!( + // val_stake_3 + // - (exp_slashed_during_processing_8 + + // exp_slashed_during_processing_9) + // ); + + // let exp_stake = val_stake_3 - del_unbond_1_amount + + // self_bond_1_amount + // - self_unbond_2_amount + // + del_2_amount + // - exp_slashed_3 + // - exp_slashed_4; + + // assert!((exp_stake.change() - post_stake_11.change()).abs() <= 1); + + for _ in 0..2 { + let votes = get_default_true_votes( + &shell.wl_storage, + shell.wl_storage.storage.block.epoch, + ); + let _ = advance_epoch(&mut shell, &pkh1, &votes, None); + } + let current_epoch = shell.wl_storage.storage.block.epoch; + assert_eq!(current_epoch.0, 12_u64); + + println!("\nCHECK BOND AND UNBOND DETAILS"); + let details = namada_proof_of_stake::bonds_and_unbonds( + &shell.wl_storage, + None, + None, + ) + .unwrap(); + + let del_id = BondId { + source: delegator.clone(), + validator: val1.address.clone(), + }; + let self_id = BondId { + source: val1.address.clone(), + validator: val1.address.clone(), + }; + + let del_details = details.get(&del_id).unwrap(); + let self_details = details.get(&self_id).unwrap(); + // dbg!(del_details, self_details); + + // Check slashes + assert_eq!(del_details.slashes, self_details.slashes); + assert_eq!(del_details.slashes.len(), 3); + assert_eq!(del_details.slashes[0].epoch, Epoch(3)); + assert!(equal_enough(del_details.slashes[0].rate, cubic_rate)); + assert_eq!(del_details.slashes[1].epoch, Epoch(3)); + assert!(equal_enough(del_details.slashes[1].rate, cubic_rate)); + assert_eq!(del_details.slashes[2].epoch, Epoch(4)); + assert!(equal_enough(del_details.slashes[2].rate, cubic_rate)); + + // Check delegations + assert_eq!(del_details.bonds.len(), 2); + assert_eq!(del_details.bonds[0].start, Epoch(3)); + assert_eq!( + del_details.bonds[0].amount, + del_1_amount - del_unbond_1_amount + ); + // TODO: decimal mult issues should be resolved with PR 1282 + assert!( + (del_details.bonds[0].slashed_amount.unwrap().change() + - decimal_mult_amount( + std::cmp::min(Decimal::ONE, dec!(3) * cubic_rate), + del_1_amount - del_unbond_1_amount + ) + .change()) + .abs() + <= 2 + ); + assert_eq!(del_details.bonds[1].start, Epoch(7)); + assert_eq!(del_details.bonds[1].amount, del_2_amount); + assert_eq!(del_details.bonds[1].slashed_amount, None); + + // Check self-bonds + assert_eq!(self_details.bonds.len(), 1); + assert_eq!(self_details.bonds[0].start, Epoch(0)); + assert_eq!( + self_details.bonds[0].amount, + initial_stake - self_unbond_1_amount + self_bond_1_amount + - self_unbond_2_amount + ); + // TODO: not sure why this is correct??? (with + self_bond_1_amount - + // self_unbond_2_amount) + // TODO: Make sure this is sound and what we expect + assert_eq!( + self_details.bonds[0].slashed_amount, + Some(decimal_mult_amount( + std::cmp::min(Decimal::ONE, dec!(3) * cubic_rate), + initial_stake - self_unbond_1_amount + self_bond_1_amount + - self_unbond_2_amount + )) + ); + + // Check delegation unbonds + assert_eq!(del_details.unbonds.len(), 1); + assert_eq!(del_details.unbonds[0].start, Epoch(3)); + assert_eq!(del_details.unbonds[0].withdraw, Epoch(9)); + assert_eq!(del_details.unbonds[0].amount, del_unbond_1_amount); + assert!( + (del_details.unbonds[0].slashed_amount.unwrap().change() + - decimal_mult_amount( + std::cmp::min(Decimal::ONE, dec!(2) * cubic_rate), + del_unbond_1_amount + ) + .change()) + .abs() + <= 1 + ); + + // Check self-unbonds + assert_eq!(self_details.unbonds.len(), 3); + assert_eq!(self_details.unbonds[0].start, Epoch(0)); + assert_eq!(self_details.unbonds[0].withdraw, Epoch(8)); + assert_eq!(self_details.unbonds[1].start, Epoch(0)); + assert_eq!(self_details.unbonds[1].withdraw, Epoch(11)); + assert_eq!(self_details.unbonds[2].start, Epoch(5)); + assert_eq!(self_details.unbonds[2].withdraw, Epoch(11)); + assert_eq!(self_details.unbonds[0].amount, self_unbond_1_amount); + assert_eq!(self_details.unbonds[0].slashed_amount, None); + assert_eq!( + self_details.unbonds[1].amount, + self_unbond_2_amount - self_bond_1_amount + ); + assert_eq!( + self_details.unbonds[1].slashed_amount, + Some(decimal_mult_amount( + std::cmp::min(Decimal::ONE, dec!(3) * cubic_rate), + self_unbond_2_amount - self_bond_1_amount + )) + ); + assert_eq!(self_details.unbonds[2].amount, self_bond_1_amount); + assert_eq!(self_details.unbonds[2].slashed_amount, None); + + println!("\nWITHDRAWING DELEGATION UNBOND"); + // let slash_pool_balance_pre_withdraw = slash_pool_balance; + // Withdraw the delegation unbonds, which total to 18_000. This should + // only be affected by the slashes in epoch 3 + let del_withdraw = namada_proof_of_stake::withdraw_tokens( + &mut shell.wl_storage, + Some(&delegator), + &val1.address, + current_epoch, + ) + .unwrap(); + + let exp_del_withdraw_slashed_amount = + decimal_mult_amount(slash_rate_3, del_unbond_1_amount); + assert_eq!( + del_withdraw, + del_unbond_1_amount - exp_del_withdraw_slashed_amount + ); + + // TODO: finish once implemented + // Check the balance of the Slash Pool + // let slash_pool_balance: token::Amount = shell + // .wl_storage + // .read(&slash_balance_key) + // .expect("must be able to read") + // .unwrap_or_default(); + // dbg!(del_withdraw, slash_pool_balance); + // assert_eq!( + // slash_pool_balance - slash_pool_balance_pre_withdraw, + // exp_del_withdraw_slashed_amount + // ); + + // println!("\nWITHDRAWING SELF UNBOND"); + // Withdraw the self unbonds, which total 154_654 + 15_000 - 9_123. Only + // the (15_000 - 9_123) tokens are slashable. + // let self_withdraw = namada_proof_of_stake::withdraw_tokens( + // &mut shell.wl_storage, + // None, + // &val1.address, + // current_epoch, + // ) + // .unwrap(); + + // let exp_self_withdraw_slashed_amount = decimal_mult_amount( + // std::cmp::min(dec!(3) * cubic_rate, Decimal::ONE), + // self_unbond_2_amount - self_bond_1_amount, + // ); + // Check the balance of the Slash Pool + // let slash_pool_balance: token::Amount = shell + // .wl_storage + // .read(&slash_balance_key) + // .expect("must be able to read") + // .unwrap_or_default(); + + // dbg!(self_withdraw, slash_pool_balance); + // dbg!( + // decimal_mult_amount(dec!(2) * cubic_rate, val_stake_3) + // + decimal_mult_amount(cubic_rate, val_stake_4) + // ); + + // assert_eq!( + // exp_self_withdraw_slashed_amount, + // slash_pool_balance + // - slash_pool_balance_pre_withdraw + // - exp_del_withdraw_slashed_amount + // ); + + Ok(()) + } + + fn get_default_true_votes(storage: &S, epoch: Epoch) -> Vec + where + S: StorageRead, + { + let params = read_pos_params(storage).unwrap(); + read_consensus_validator_set_addresses_with_stake(storage, epoch) + .unwrap() + .into_iter() + .map(|val| { + let pkh = get_pkh_from_address( + storage, + ¶ms, + val.address.clone(), + epoch, + ); + VoteInfo { + validator: Some(Validator { + address: pkh, + power: u64::from(val.bonded_stake) as i64, + }), + signed_last_block: true, + } + }) + .collect::>() + } + + fn advance_epoch( + shell: &mut TestShell, + proposer_address: &[u8], + consensus_votes: &[VoteInfo], + misbehaviors: Option>, + ) -> Epoch { + let current_epoch = shell.wl_storage.storage.block.epoch; + loop { + next_block_for_inflation( + shell, + proposer_address.to_owned(), + consensus_votes.to_owned(), + misbehaviors.clone(), + ); + if shell.wl_storage.storage.block.epoch == current_epoch.next() { + break; + } + } + shell.wl_storage.storage.block.epoch + } + + fn get_pkh_from_address( + storage: &S, + params: &PosParams, + address: Address, + epoch: Epoch, + ) -> Vec + where + S: StorageRead, + { + let ck = validator_consensus_key_handle(&address) + .get(storage, epoch, params) + .unwrap() + .unwrap(); + let hash_string = tm_consensus_key_raw_hash(&ck); + HEXUPPER.decode(hash_string.as_bytes()).unwrap() + } } 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/init_chain.rs b/apps/src/lib/node/ledger/shell/init_chain.rs index 58891c3b4e..5c77171277 100644 --- a/apps/src/lib/node/ledger/shell/init_chain.rs +++ b/apps/src/lib/node/ledger/shell/init_chain.rs @@ -33,7 +33,7 @@ where pub fn init_chain( &mut self, init: request::InitChain, - #[cfg(feature = "dev")] num_validators: u64, + #[cfg(any(test, feature = "dev"))] num_validators: u64, ) -> Result { let mut response = response::InitChain::default(); let (current_chain_id, _) = self.wl_storage.storage.get_chain_id(); @@ -43,10 +43,10 @@ where current_chain_id, init.chain_id ))); } - #[cfg(not(feature = "dev"))] + #[cfg(not(any(test, feature = "dev")))] let genesis = genesis::genesis(&self.base_dir, &self.wl_storage.storage.chain_id); - #[cfg(not(feature = "dev"))] + #[cfg(not(any(test, feature = "dev")))] { let genesis_bytes = genesis.try_to_vec().unwrap(); let errors = @@ -58,7 +58,7 @@ where errors.into_iter().format(". ") ); } - #[cfg(feature = "dev")] + #[cfg(any(test, feature = "dev"))] let genesis = genesis::genesis(num_validators); let ts: protobuf::Timestamp = init.time.expect("Missing genesis time"); @@ -480,11 +480,9 @@ where #[cfg(test)] mod test { use std::collections::BTreeMap; - use std::str::FromStr; use namada::ledger::storage::DBIter; use namada::types::chain::ChainId; - use namada::types::storage; use crate::facade::tendermint_proto::abci::RequestInitChain; use crate::facade::tendermint_proto::google::protobuf::Timestamp; @@ -498,12 +496,11 @@ mod test { // Collect all storage key-vals into a sorted map let store_block_state = |shell: &TestShell| -> BTreeMap<_, _> { - let prefix: storage::Key = FromStr::from_str("").unwrap(); shell .wl_storage .storage .db - .iter_prefix(&prefix) + .iter_optional_prefix(None) .map(|(key, val, _gas)| (key, val)) .collect() }; diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index a83059edac..7c90997fce 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -30,15 +30,15 @@ use namada::ledger::pos::namada_proof_of_stake::types::{ }; use namada::ledger::storage::write_log::WriteLog; use namada::ledger::storage::{ - DBIter, Sha256Hasher, Storage, StorageHasher, WlStorage, DB, + DBIter, Sha256Hasher, Storage, StorageHasher, TempWlStorage, WlStorage, DB, }; -use namada::ledger::storage_api::{self, StorageRead}; +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::proof_of_stake::{self, process_slashes, read_pos_params, slash}; +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}; @@ -46,7 +46,7 @@ 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, + hash_tx, verify_decrypted_correctly, AffineCurve, DecryptedTx, EllipticCurve, PairingEngine, TxType, }; use namada::types::{address, hash}; @@ -112,6 +112,8 @@ pub enum Error { LoadingWasm(String), #[error("Error reading from or writing to storage: {0}")] StorageApi(#[from] storage_api::Error), + #[error("Transaction replay attempt: {0}")] + ReplayAttempt(String), } impl From for TxResult { @@ -184,8 +186,7 @@ pub fn reset(config: config::Ledger) -> Result<()> { res => res.map_err(Error::RemoveDB)?, }; // reset Tendermint state - tendermint_node::reset(config.tendermint_dir()) - .map_err(Error::Tendermint)?; + tendermint_node::reset(config.cometbft_dir()).map_err(Error::Tendermint)?; Ok(()) } @@ -193,7 +194,7 @@ pub fn rollback(config: config::Ledger) -> Result<()> { // Rollback Tendermint state tracing::info!("Rollback Tendermint state"); let tendermint_block_height = - tendermint_node::rollback(config.tendermint_dir()) + tendermint_node::rollback(config.cometbft_dir()) .map_err(Error::Tendermint)?; // Rollback Namada state @@ -293,7 +294,7 @@ where let chain_id = config.chain_id; let db_path = config.shell.db_dir(&chain_id); let base_dir = config.shell.base_dir; - let mode = config.tendermint.tendermint_mode; + let mode = config.shell.tendermint_mode; let storage_read_past_height_limit = config.shell.storage_read_past_height_limit; if !Path::new(&base_dir).is_dir() { @@ -411,7 +412,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() } @@ -491,7 +492,7 @@ where } /// Apply PoS slashes from the evidence - fn slash(&mut self) { + fn record_slashes_from_evidence(&mut self) { if !self.byzantine_validators.is_empty() { let byzantine_validators = mem::take(&mut self.byzantine_validators); @@ -499,6 +500,7 @@ where let pos_params = read_pos_params(&self.wl_storage).unwrap(); let current_epoch = self.wl_storage.storage.block.epoch; for evidence in byzantine_validators { + // dbg!(&evidence); tracing::info!("Processing evidence {evidence:?}."); let evidence_height = match u64::try_from(evidence.height) { Ok(height) => height, @@ -526,7 +528,12 @@ where continue; } }; - if evidence_epoch + pos_params.unbonding_len <= current_epoch { + // Disregard evidences that should have already been processed + // at this time + if evidence_epoch + pos_params.slash_processing_epoch_offset() + - pos_params.cubic_slashing_window_length + <= current_epoch + { tracing::info!( "Skipping outdated evidence from epoch \ {evidence_epoch}" @@ -585,11 +592,13 @@ where } }; tracing::info!( - "Slashing {} for {} in epoch {}, block height {}", + "Slashing {} for {} in epoch {}, block height {} (current \ + epoch = {})", validator, slash_type, evidence_epoch, - evidence_height + evidence_height, + current_epoch ); if let Err(err) = slash( &mut self.wl_storage, @@ -606,6 +615,19 @@ where } } + /// Process and apply slashes that have already been recorded for the + /// current epoch + fn process_slashes(&mut self) { + let current_epoch = self.wl_storage.storage.block.epoch; + if let Err(err) = process_slashes(&mut self.wl_storage, current_epoch) { + tracing::error!( + "Error while processing slashes queued for epoch {}: {}", + current_epoch, + err + ); + } + } + /// INVARIANT: This method must be stateless. #[cfg(feature = "abcipp")] pub fn extend_vote( @@ -642,12 +664,62 @@ where tracing::info!( "Committed block hash: {}, height: {}", root, - self.wl_storage.storage.last_height, + self.wl_storage.storage.get_last_block_height(), ); response.data = root.0; response } + /// Checks that neither the wrapper nor the inner transaction have already + /// been applied. Requires a [`TempWlStorage`] to perform the check during + /// block construction and validation + pub fn replay_protection_checks( + &self, + wrapper: &Tx, + tx_bytes: &[u8], + temp_wl_storage: &mut TempWlStorage, + ) -> Result<()> { + 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", + &inner_tx_hash, + ))); + } + + // Write inner hash to WAL + temp_wl_storage + .write(&inner_hash_key, ()) + .expect("Couldn't write inner transaction hash to write log"); + + let tx = + Tx::try_from(tx_bytes).expect("Deserialization shouldn't fail"); + let wrapper_hash = tx.header_hash(); + let wrapper_hash_key = + replay_protection::get_tx_hash_key(&wrapper_hash); + if temp_wl_storage + .has_key(&wrapper_hash_key) + .expect("Error while checking wrapper tx hash key in storage") + { + return Err(Error::ReplayAttempt(format!( + "Wrapper transaction hash {} already in storage", + wrapper_hash + ))); + } + + // Write wrapper hash to WAL + temp_wl_storage + .write(&wrapper_hash_key, ()) + .expect("Couldn't write wrapper tx hash to write log"); + + Ok(()) + } + /// Validate a transaction request. On success, the transaction will /// included in the mempool and propagated to peers, otherwise it will be /// rejected. @@ -676,17 +748,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 { @@ -700,8 +772,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(); @@ -710,10 +782,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 @@ -725,14 +800,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 @@ -796,13 +871,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(), @@ -933,6 +1001,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::address::init_token_storage; use namada::types::chain::ChainId; use namada::types::hash::Hash; @@ -1042,7 +1111,7 @@ mod test_utils { pub fn init_chain( &mut self, req: RequestInitChain, - #[cfg(feature = "dev")] num_validators: u64, + num_validators: u64, ) { self.shell .init_chain(req, num_validators) @@ -1090,16 +1159,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, + }); } } @@ -1174,13 +1239,7 @@ mod test_utils { init_token_storage(&mut shell.wl_storage, 60); 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, @@ -1188,12 +1247,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, @@ -1231,7 +1293,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; @@ -1244,41 +1306,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(), @@ -1299,67 +1343,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(), @@ -1379,12 +1389,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(), @@ -1402,14 +1409,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(), @@ -1417,27 +1417,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 @@ -1469,13 +1468,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 @@ -1488,7 +1488,7 @@ mod test_mempool_validate { result.log, format!( "Inner transaction hash {} already in storage, replay attempt", - tx_type.tx_hash + inner_tx_hash ) ); @@ -1501,7 +1501,7 @@ mod test_mempool_validate { result.log, format!( "Inner transaction hash {} already in storage, replay attempt", - tx_type.tx_hash + inner_tx_hash ) ) } @@ -1514,13 +1514,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(), @@ -1543,13 +1544,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 d48d5cfcec..9a9aa58491 100644 --- a/apps/src/lib/node/ledger/shell/prepare_proposal.rs +++ b/apps/src/lib/node/ledger/shell/prepare_proposal.rs @@ -1,14 +1,15 @@ //! Implementation of the [`RequestPrepareProposal`] ABCI++ method for the Shell use namada::core::hints; -use namada::ledger::storage::{DBIter, StorageHasher, DB}; +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,9 @@ 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::shims::abcipp_shim_types::shim::{response, TxBytes}; impl Shell @@ -42,33 +44,30 @@ where &self, req: RequestPrepareProposal, ) -> response::PrepareProposal { - 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, &req.txs, &req.time); - let mut txs = encrypted_txs; - - // decrypt the wrapper txs included in the previous block - let (mut decrypted_txs, alloc) = self.build_decrypted_txs(alloc); - txs.append(&mut decrypted_txs); - - // add vote extension protocol txs - let mut protocol_txs = self.build_protocol_txs( - alloc, - #[cfg(feature = "abcipp")] - req.local_last_commit, - #[cfg(not(feature = "abcipp"))] - &req.txs, - ); - txs.append(&mut protocol_txs); + // 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, + TempWlStorage::new(&self.wl_storage.storage), + &req.txs, + &req.time, + ); + let mut txs = encrypted_txs; - txs - } else { - vec![] - }; + // decrypt the wrapper txs included in the previous block + let (mut decrypted_txs, alloc) = self.build_decrypted_txs(alloc); + txs.append(&mut decrypted_txs); + + // add vote extension protocol txs + let mut protocol_txs = self.build_protocol_txs( + alloc, + #[cfg(feature = "abcipp")] + req.local_last_commit, + #[cfg(not(feature = "abcipp"))] + &req.txs, + ); + txs.append(&mut protocol_txs); tracing::info!( height = req.height, @@ -119,6 +118,7 @@ where fn build_encrypted_txs( &self, mut alloc: EncryptedTxBatchAllocator, + mut temp_wl_storage: TempWlStorage, txs: &[TxBytes], block_time: &Option, ) -> (Vec, BlockSpaceAllocator) { @@ -135,10 +135,10 @@ 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(_)) = process_tx(tx) { + 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()); } } @@ -195,7 +195,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 @@ -203,20 +202,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 @@ -271,7 +280,9 @@ where mod test_prepare_proposal { use borsh::BorshSerialize; + use namada::ledger::replay_protection; use namada::proof_of_stake::Epoch; + use namada::proto::{Code, Data, Header, Section, Signature}; use namada::types::transaction::{Fee, WrapperTx}; use super::*; @@ -283,12 +294,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() @@ -303,36 +312,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()], @@ -358,18 +354,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(), @@ -377,54 +362,161 @@ 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 expired wrapper transactions are not included in the block + /// Test that if the unsigned wrapper tx hash is known (replay attack), the + /// transaction is not included in the block #[test] - fn test_expired_wrapper_tx() { + fn test_wrapper_tx_hash() { + let (mut shell, _) = test_utils::setup(1); + + let keypair = crate::wallet::defaults::daewon_keypair(); + 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, + )))); + 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 = wrapper.header_hash(); + let hash_key = + replay_protection::get_tx_hash_key(&wrapper_unsigned_hash); + shell + .wl_storage + .storage + .write(&hash_key, vec![]) + .expect("Test failed"); + + let req = RequestPrepareProposal { + txs: vec![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() + .expect("Test failed") + }); + assert_eq!(received.len(), 0); + } + + /// Test that if two identical wrapper txs are proposed for this block, only + /// one gets accepted + #[test] + fn test_wrapper_tx_hash_same_block() { 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(), + + let keypair = crate::wallet::defaults::daewon_keypair(); + 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, - ); - let wrapper_tx = WrapperTx::new( + )))); + 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![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() + .expect("Test failed") + }); + assert_eq!(received.len(), 1); + } + + /// Test that if the unsigned inner tx hash is known (replay attack), the + /// transaction is not included in the block + #[test] + fn test_inner_tx_hash() { + let (mut shell, _) = test_utils::setup(1); + + let keypair = crate::wallet::defaults::daewon_keypair(); + let mut wrapper = Tx::new(TxType::Wrapper(Box::new(WrapperTx::new( Fee { amount: 0.into(), token: shell.wl_storage.storage.native_token.clone(), @@ -432,15 +524,138 @@ 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(), Some(tx_time)) + )))); + 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); + shell + .wl_storage + .storage + .write(&hash_key, vec![]) .expect("Test failed"); + let req = RequestPrepareProposal { + txs: vec![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() + .expect("Test failed") + }); + assert_eq!(received.len(), 0); + } + + /// Test that if two identical decrypted txs are proposed for this block, + /// only one gets accepted + #[test] + fn test_inner_tx_hash_same_block() { + let (shell, _) = test_utils::setup(1); + + let keypair = crate::wallet::defaults::daewon_keypair(); + let keypair_2 = crate::wallet::defaults::daewon_keypair(); + 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, + )))); + 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 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![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() + .expect("Test failed") + }); + assert_eq!(received.len(), 1); + } + + /// Test that expired wrapper transactions are not included in the block + #[test] + fn test_expired_wrapper_tx() { + let (shell, _) = test_utils::setup(1); + let keypair = gen_keypair(); + let tx_time = DateTimeUtc::now(); + 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, + ))); + wrapper_tx.encrypt(&Default::default()); + let time = DateTimeUtc::now(); let block_time = namada::core::tendermint_proto::google::protobuf::Timestamp { @@ -448,13 +663,13 @@ 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() }; let result = shell.prepare_proposal(req); eprintln!("Proposal: {:?}", result.txs); - assert!(result.txs.is_empty()); + assert_eq!(result.txs.len(), 0); } } diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index acc57e5979..2837ac6600 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -4,10 +4,9 @@ use data_encoding::HEXUPPER; use namada::core::hints; use namada::core::ledger::storage::WlStorage; -use namada::core::types::hash::Hash; 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; @@ -149,11 +148,16 @@ where &mut temp_wl_storage, block_time, ); - if let ErrorCodes::Ok = - ErrorCodes::from_u32(result.code).unwrap() - { + let error_code = ErrorCodes::from_u32(result.code).unwrap(); + if let ErrorCodes::Ok = error_code { temp_wl_storage.write_log.commit_tx(); } else { + tracing::info!( + "Process proposal rejected an invalid tx. Error code: \ + {:?}, info: {}", + error_code, + result.info + ); temp_wl_storage.write_log.drop_tx(); } result @@ -189,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, @@ -225,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 { @@ -246,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" @@ -286,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 \ @@ -298,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(), @@ -419,7 +434,7 @@ where } // validate the ciphertext via Ferveo - if !wrapper.validate_ciphertext() { + if !tx.validate_ciphertext() { TxResult { code: ErrorCodes::InvalidTx.into(), info: format!( @@ -429,54 +444,17 @@ where } } else { // Replay protection checks - let inner_hash_key = - replay_protection::get_tx_hash_key(&wrapper.tx_hash); - if temp_wl_storage.has_key(&inner_hash_key).expect( - "Error while checking inner tx hash key in storage", + if let Err(e) = self.replay_protection_checks( + &tx, + tx_bytes, + temp_wl_storage, ) { return TxResult { code: ErrorCodes::ReplayTx.into(), - info: format!( - "Inner transaction hash {} already in \ - storage, replay attempt", - &wrapper.tx_hash - ), + info: e.to_string(), }; } - // Write inner hash to WAL - temp_wl_storage - .write_log - .write(&inner_hash_key, vec![]) - .expect( - "Couldn't write inner transaction hash to write \ - log", - ); - - let tx = Tx::try_from(tx_bytes) - .expect("Deserialization shouldn't fail"); - let wrapper_hash = Hash(tx.unsigned_hash()); - let wrapper_hash_key = - replay_protection::get_tx_hash_key(&wrapper_hash); - if temp_wl_storage.has_key(&wrapper_hash_key).expect( - "Error while checking wrapper tx hash key in storage", - ) { - return TxResult { - code: ErrorCodes::ReplayTx.into(), - info: format!( - "Wrapper transaction hash {} already in \ - storage, replay attempt", - wrapper_hash - ), - }; - } - - // Write wrapper hash to WAL - temp_wl_storage - .write_log - .write(&wrapper_hash_key, vec![]) - .expect("Couldn't write wrapper tx hash to write log"); - // If the public key corresponds to the MASP sentinel // transaction key, then the fee payer is effectively // the MASP, otherwise derive @@ -537,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::{ @@ -559,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(), @@ -573,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()], @@ -599,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." + ) ); } } @@ -611,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(), @@ -626,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()], @@ -679,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) @@ -709,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(), @@ -723,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) { @@ -777,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(), @@ -791,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) { @@ -829,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 { @@ -892,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(), @@ -906,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()], }; @@ -947,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(), @@ -961,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()], }; @@ -997,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(), @@ -1009,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) @@ -1042,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()], @@ -1083,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()], }; @@ -1121,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(), @@ -1135,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 @@ -1156,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)) => { @@ -1168,8 +1065,8 @@ mod test_process_proposal { assert_eq!( response[0].result.info, format!( - "Wrapper transaction hash {} already in storage, \ - replay attempt", + "Transaction replay attempt: Wrapper transaction hash \ + {} already in storage", wrapper_unsigned_hash ) ); @@ -1195,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(), @@ -1209,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"), @@ -1236,9 +1130,12 @@ mod test_process_proposal { assert_eq!( response[1].result.info, format!( - "Inner transaction hash {} already in storage, replay \ - attempt", - wrapper.tx_hash + "Transaction replay attempt: Inner transaction hash \ + {} already in storage", + wrapper + .clone() + .update_header(TxType::Raw) + .header_hash(), ) ); } @@ -1253,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(), @@ -1267,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); @@ -1287,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"), @@ -1299,8 +1194,8 @@ mod test_process_proposal { assert_eq!( response[0].result.info, format!( - "Inner transaction hash {} already in storage, replay \ - attempt", + "Transaction replay attempt: Inner transaction hash \ + {} already in storage", inner_unsigned_hash ) ); @@ -1339,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(), @@ -1353,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(), @@ -1371,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"), @@ -1395,8 +1289,8 @@ mod test_process_proposal { assert_eq!( response[1].result.info, format!( - "Inner transaction hash {} already in storage, replay \ - attempt", + "Transaction replay attempt: Inner transaction hash \ + {} already in storage", inner_unsigned_hash ) ); @@ -1407,17 +1301,11 @@ mod test_process_proposal { /// Test that a wrapper or protocol transaction with a mismatching chain id /// causes the entire block to be rejected #[test] - fn test_wong_chain_id() { + fn test_wrong_chain_id() { 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(), @@ -1425,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"), @@ -1469,24 +1364,12 @@ mod test_process_proposal { /// Test that a decrypted transaction with a mismatching chain id gets /// rejected without rejecting the entire block #[test] - fn test_decrypted_wong_chain_id() { + fn test_decrypted_wrong_chain_id() { let (mut shell, _) = test_utils::setup(1); 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(), @@ -1494,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, }; @@ -1507,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) { @@ -1535,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(), @@ -1549,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"), @@ -1580,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(), @@ -1600,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, }; @@ -1613,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/shell/stats.rs b/apps/src/lib/node/ledger/shell/stats.rs index f4c548c231..0a677ed576 100644 --- a/apps/src/lib/node/ledger/shell/stats.rs +++ b/apps/src/lib/node/ledger/shell/stats.rs @@ -9,6 +9,7 @@ pub struct InternalStats { vp_cache_size: (usize, usize), tx_cache_size: (usize, usize), tx_executed: HashMap, + wrapper_txs: u64, } impl InternalStats { @@ -50,15 +51,21 @@ impl InternalStats { info.strip_suffix(", ").unwrap().to_string() } } + + pub fn increment_wrapper_txs(&mut self) { + self.wrapper_txs += 1; + } } impl Display for InternalStats { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "Applied {} transactions, successful txs: {}, rejected txs: {}, \ - errored txs: {}, vp cache size: {} - {}, tx cache size {} - {}", + "Applied {} transactions. Wrappers: {}, successful inner txs: {}, \ + rejected inner txs: {}, errored inner txs: {}, vp cache size: {} \ + - {}, tx cache size {} - {}", self.successful_tx + self.rejected_txs + self.errored_txs, + self.wrapper_txs, self.successful_tx, self.rejected_txs, self.errored_txs, 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/shims/abcipp_shim_types.rs b/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs index c94ba2e1d9..d1cd190e1d 100644 --- a/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs +++ b/apps/src/lib/node/ledger/shims/abcipp_shim_types.rs @@ -1,22 +1,12 @@ -#[cfg(not(feature = "abcipp"))] -use tower_abci::{Request, Response}; #[cfg(feature = "abcipp")] use tower_abci_abcipp::{Request, Response}; +#[cfg(not(feature = "abcipp"))] +use crate::facade::tower_abci::{Request, Response}; + pub mod shim { use std::convert::TryFrom; - #[cfg(not(feature = "abcipp"))] - use tendermint_proto::abci::{ - RequestApplySnapshotChunk, RequestCheckTx, RequestCommit, RequestEcho, - RequestFlush, RequestInfo, RequestInitChain, RequestListSnapshots, - RequestLoadSnapshotChunk, RequestOfferSnapshot, RequestPrepareProposal, - RequestProcessProposal, RequestQuery, ResponseApplySnapshotChunk, - ResponseCheckTx, ResponseCommit, ResponseEcho, ResponseEndBlock, - ResponseFlush, ResponseInfo, ResponseInitChain, ResponseListSnapshots, - ResponseLoadSnapshotChunk, ResponseOfferSnapshot, - ResponsePrepareProposal, ResponseQuery, VoteInfo, - }; #[cfg(feature = "abcipp")] use tendermint_proto_abcipp::abci::{ RequestApplySnapshotChunk, RequestCheckTx, RequestCommit, RequestEcho, @@ -33,6 +23,17 @@ pub mod shim { use thiserror::Error; use super::{Request as Req, Response as Resp}; + #[cfg(not(feature = "abcipp"))] + use crate::facade::tendermint_proto::abci::{ + RequestApplySnapshotChunk, RequestCheckTx, RequestCommit, RequestEcho, + RequestFlush, RequestInfo, RequestInitChain, RequestListSnapshots, + RequestLoadSnapshotChunk, RequestOfferSnapshot, RequestPrepareProposal, + RequestProcessProposal, RequestQuery, ResponseApplySnapshotChunk, + ResponseCheckTx, ResponseCommit, ResponseEcho, ResponseEndBlock, + ResponseFlush, ResponseInfo, ResponseInitChain, ResponseListSnapshots, + ResponseLoadSnapshotChunk, ResponseOfferSnapshot, + ResponsePrepareProposal, ResponseQuery, VoteInfo, + }; use crate::node::ledger::shell; pub type TxBytes = Vec; @@ -199,14 +200,14 @@ pub mod shim { use namada::types::hash::Hash; use namada::types::storage::{BlockHash, Header}; use namada::types::time::DateTimeUtc; - #[cfg(not(feature = "abcipp"))] - use tendermint_proto::abci::Misbehavior as Evidence; #[cfg(feature = "abcipp")] use tendermint_proto_abcipp::abci::{ Misbehavior as Evidence, RequestFinalizeBlock, }; use super::VoteInfo; + #[cfg(not(feature = "abcipp"))] + use crate::facade::tendermint_proto::abci::Misbehavior as Evidence; pub struct VerifyHeader; @@ -423,7 +424,9 @@ pub mod shim { } #[cfg(not(feature = "abcipp"))] - impl From for tendermint_proto::abci::ResponseEndBlock { + impl From + for crate::facade::tendermint_proto::abci::ResponseEndBlock + { fn from(resp: FinalizeBlock) -> Self { Self { events: resp diff --git a/apps/src/lib/node/ledger/storage/mod.rs b/apps/src/lib/node/ledger/storage/mod.rs index 95f947a4b7..e6d060e533 100644 --- a/apps/src/lib/node/ledger/storage/mod.rs +++ b/apps/src/lib/node/ledger/storage/mod.rs @@ -248,7 +248,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) = @@ -356,14 +356,16 @@ mod tests { let is_last_write = blocks_write_value.last().unwrap().1; // The upper bound is arbitrary. - for height in storage.last_height.0..storage.last_height.0 + 10 { + for height in storage.get_last_block_height().0 + ..storage.get_last_block_height().0 + 10 + { let height = BlockHeight::from(height); let (value_bytes, _gas) = storage.read_with_height(&key, height)?; if is_last_write { let value_bytes = value_bytes.expect("Should have been written"); let value: BlockHeight = types::decode(value_bytes).unwrap(); - assert_eq!(value, storage.last_height); + assert_eq!(value, storage.get_last_block_height()); } else if value_bytes.is_some() { let value: BlockHeight = types::decode(value_bytes.unwrap()).unwrap(); diff --git a/apps/src/lib/node/ledger/storage/rocksdb.rs b/apps/src/lib/node/ledger/storage/rocksdb.rs index 9d54bc6de3..6108cdf289 100644 --- a/apps/src/lib/node/ledger/storage/rocksdb.rs +++ b/apps/src/lib/node/ledger/storage/rocksdb.rs @@ -23,6 +23,7 @@ //! - `root`: root hash //! - `store`: the tree's store //! - `hash`: block hash +//! - `time`: block time //! - `epoch`: block epoch //! - `address_gen`: established address generator //! - `header`: block's header @@ -412,9 +413,8 @@ impl RocksDB { let batch = Mutex::new(batch); tracing::info!("Restoring previous hight subspace diffs"); - self.iter_prefix(&Key::default()) - .par_bridge() - .try_for_each(|(key, _value, _gas)| -> Result<()> { + self.iter_optional_prefix(None).par_bridge().try_for_each( + |(key, _value, _gas)| -> Result<()> { // Restore previous height diff if present, otherwise delete the // subspace key let subspace_cf = self.get_column_family(SUBSPACE_CF)?; @@ -432,7 +432,8 @@ impl RocksDB { } Ok(()) - })?; + }, + )?; tracing::info!("Deleting keys prepended with the last height"); let mut batch = batch.into_inner().unwrap(); @@ -537,6 +538,19 @@ impl DB for RocksDB { return Ok(None); } }; + let update_epoch_blocks_delay: Option = match self + .0 + .get_cf(state_cf, "update_epoch_blocks_delay") + .map_err(|e| Error::DBError(e.into_string()))? + { + Some(bytes) => types::decode(bytes).map_err(Error::CodingError)?, + None => { + tracing::error!( + "Couldn't load epoch update block delay from the DB" + ); + return Ok(None); + } + }; let tx_queue: TxQueue = match self .0 .get_cf(state_cf, "tx_queue") @@ -557,6 +571,7 @@ impl DB for RocksDB { read_opts.set_iterate_upper_bound(next_height_prefix); let mut merkle_tree_stores = MerkleTreeStoresRead::default(); let mut hash = None; + let mut time = None; let mut epoch = None; let mut pred_epochs = None; let mut address_gen = None; @@ -605,6 +620,11 @@ impl DB for RocksDB { types::decode(bytes).map_err(Error::CodingError)?, ) } + "time" => { + time = Some( + types::decode(bytes).map_err(Error::CodingError)?, + ) + } "epoch" => { epoch = Some( types::decode(bytes).map_err(Error::CodingError)?, @@ -625,21 +645,27 @@ impl DB for RocksDB { None => unknown_key_error(path)?, } } - match (hash, epoch, pred_epochs, address_gen) { - (Some(hash), Some(epoch), Some(pred_epochs), Some(address_gen)) => { - Ok(Some(BlockStateRead { - merkle_tree_stores, - hash, - height, - epoch, - pred_epochs, - results, - next_epoch_min_start_height, - next_epoch_min_start_time, - address_gen, - tx_queue, - })) - } + match (hash, time, epoch, pred_epochs, address_gen) { + ( + Some(hash), + Some(time), + Some(epoch), + Some(pred_epochs), + Some(address_gen), + ) => Ok(Some(BlockStateRead { + merkle_tree_stores, + hash, + height, + time, + epoch, + pred_epochs, + results, + next_epoch_min_start_height, + next_epoch_min_start_time, + update_epoch_blocks_delay, + address_gen, + tx_queue, + })), _ => Err(Error::Temporary { error: "Essential data couldn't be read from the DB" .to_string(), @@ -658,12 +684,14 @@ impl DB for RocksDB { header, hash, height, + time, epoch, pred_epochs, - results, next_epoch_min_start_height, next_epoch_min_start_time, + update_epoch_blocks_delay, address_gen, + results, tx_queue, }: BlockStateWrite = state; @@ -704,6 +732,24 @@ impl DB for RocksDB { "next_epoch_min_start_time", types::encode(&next_epoch_min_start_time), ); + if let Some(current_value) = self + .0 + .get_cf(state_cf, "update_epoch_blocks_delay") + .map_err(|e| Error::DBError(e.into_string()))? + { + // Write the predecessor value for rollback + batch.0.put_cf( + state_cf, + "pred/update_epoch_blocks_delay", + current_value, + ); + } + batch.0.put_cf( + state_cf, + "update_epoch_blocks_delay", + types::encode(&update_epoch_blocks_delay), + ); + // Tx queue if let Some(pred_tx_queue) = self .0 @@ -770,6 +816,15 @@ impl DB for RocksDB { .0 .put_cf(block_cf, key.to_string(), types::encode(&hash)); } + // Block time + { + let key = prefix_key + .push(&"time".to_owned()) + .map_err(Error::KeyError)?; + batch + .0 + .put_cf(block_cf, key.to_string(), types::encode(&time)); + } // Block epoch { let key = prefix_key @@ -1188,9 +1243,9 @@ impl DB for RocksDB { impl<'iter> DBIter<'iter> for RocksDB { type PrefixIter = PersistentPrefixIterator<'iter>; - fn iter_prefix( + fn iter_optional_prefix( &'iter self, - prefix: &Key, + prefix: Option<&Key>, ) -> PersistentPrefixIterator<'iter> { iter_subspace_prefix(self, prefix) } @@ -1228,13 +1283,17 @@ impl<'iter> DBIter<'iter> for RocksDB { fn iter_subspace_prefix<'iter>( db: &'iter RocksDB, - prefix: &Key, + prefix: Option<&Key>, ) -> PersistentPrefixIterator<'iter> { let subspace_cf = db .get_column_family(SUBSPACE_CF) .expect("{SUBSPACE_CF} column family should exist"); let db_prefix = "".to_owned(); - iter_prefix(db, subspace_cf, db_prefix, prefix.to_string()) + let prefix_string = match prefix { + Some(prefix) => prefix.to_string(), + None => "".to_string(), + }; + iter_prefix(db, subspace_cf, db_prefix, prefix_string) } fn iter_diffs_prefix( @@ -1407,11 +1466,13 @@ mod test { let merkle_tree = MerkleTree::::default(); let merkle_tree_stores = merkle_tree.stores(); let hash = BlockHash::default(); + let time = DateTimeUtc::now(); let epoch = Epoch::default(); let pred_epochs = Epochs::default(); let height = BlockHeight::default(); let next_epoch_min_start_height = BlockHeight::default(); let next_epoch_min_start_time = DateTimeUtc::now(); + let update_epoch_blocks_delay = None; let address_gen = EstablishedAddressGen::new("whatever"); let tx_queue = TxQueue::default(); let results = BlockResults::default(); @@ -1420,11 +1481,13 @@ mod test { header: None, hash: &hash, height, + time, epoch, results: &results, pred_epochs: &pred_epochs, next_epoch_min_start_height, next_epoch_min_start_time, + update_epoch_blocks_delay, address_gen: &address_gen, tx_queue: &tx_queue, }; diff --git a/apps/src/lib/node/ledger/tendermint_node.rs b/apps/src/lib/node/ledger/tendermint_node.rs index 63c9cd40c2..2bd5168ffa 100644 --- a/apps/src/lib/node/ledger/tendermint_node.rs +++ b/apps/src/lib/node/ledger/tendermint_node.rs @@ -9,8 +9,6 @@ use namada::types::key::*; use namada::types::storage::BlockHeight; use namada::types::time::DateTimeUtc; use serde_json::json; -#[cfg(feature = "abciplus")] -use tendermint::Moniker; #[cfg(feature = "abcipp")] use tendermint_abcipp::Moniker; use thiserror::Error; @@ -20,32 +18,33 @@ use tokio::process::Command; use crate::cli::namada_version; use crate::config; +#[cfg(feature = "abciplus")] +use crate::facade::tendermint::Moniker; use crate::facade::tendermint::{block, Genesis}; -use crate::facade::tendermint_config::net::Address as TendermintAddress; use crate::facade::tendermint_config::{ - Error as TendermintError, TendermintConfig, TxIndexConfig, TxIndexer, + Error as TendermintError, TendermintConfig, }; /// Env. var to output Tendermint log to stdout -pub const ENV_VAR_TM_STDOUT: &str = "NAMADA_TM_STDOUT"; +pub const ENV_VAR_TM_STDOUT: &str = "NAMADA_CMT_STDOUT"; #[derive(Error, Debug)] pub enum Error { - #[error("Failed to initialize Tendermint: {0}")] + #[error("Failed to initialize CometBFT: {0}")] Init(std::io::Error), - #[error("Failed to load Tendermint config file: {0}")] + #[error("Failed to load CometBFT config file: {0}")] LoadConfig(TendermintError), - #[error("Failed to open Tendermint config for writing: {0}")] + #[error("Failed to open CometBFT config for writing: {0}")] OpenWriteConfig(std::io::Error), - #[error("Failed to serialize Tendermint config TOML to string: {0}")] + #[error("Failed to serialize CometBFT config TOML to string: {0}")] ConfigSerializeToml(toml::ser::Error), - #[error("Failed to write Tendermint config: {0}")] + #[error("Failed to write CometBFT config: {0}")] WriteConfig(std::io::Error), - #[error("Failed to start up Tendermint node: {0}")] + #[error("Failed to start up CometBFT node: {0}")] StartUp(std::io::Error), #[error("{0}")] Runtime(String), - #[error("Failed to rollback tendermint state: {0}")] + #[error("Failed to rollback CometBFT state: {0}")] RollBack(String), #[error("Failed to convert to String: {0:?}")] TendermintPath(std::ffi::OsString), @@ -53,17 +52,17 @@ pub enum Error { pub type Result = std::result::Result; -/// Check if the TENDERMINT env var has been set and use that as the -/// location of the tendermint binary. Otherwise, assume it is on path +/// Check if the COMET env var has been set and use that as the +/// location of the COMET binary. Otherwise, assume it is on path /// /// Returns an error if the env var is defined but not a valid Unicode. fn from_env_or_default() -> Result { - match std::env::var("TENDERMINT") { + match std::env::var("COMETBFT") { Ok(path) => { - tracing::info!("Using tendermint path from env variable: {}", path); + tracing::info!("Using CometBFT path from env variable: {}", path); Ok(path) } - Err(std::env::VarError::NotPresent) => Ok(String::from("tendermint")), + Err(std::env::VarError::NotPresent) => Ok(String::from("cometbft")), Err(std::env::VarError::NotUnicode(msg)) => { Err(Error::TendermintPath(msg)) } @@ -75,15 +74,15 @@ pub async fn run( home_dir: PathBuf, chain_id: ChainId, genesis_time: DateTimeUtc, - ledger_address: String, - config: config::Tendermint, + proxy_app_address: String, + config: config::Ledger, abort_recv: tokio::sync::oneshot::Receiver< tokio::sync::oneshot::Sender<()>, >, ) -> Result<()> { let home_dir_string = home_dir.to_string_lossy().to_string(); let tendermint_path = from_env_or_default()?; - let mode = config.tendermint_mode.to_str().to_owned(); + let mode = config.shell.tendermint_mode.to_str().to_owned(); #[cfg(feature = "dev")] // This has to be checked before we run tendermint init @@ -115,13 +114,13 @@ pub async fn run( #[cfg(not(feature = "abcipp"))] write_tm_genesis(&home_dir, chain_id, genesis_time).await; - update_tendermint_config(&home_dir, config).await?; + update_tendermint_config(&home_dir, config.cometbft).await?; let mut tendermint_node = Command::new(&tendermint_path); tendermint_node.args([ "start", "--proxy_app", - &ledger_address, + &proxy_app_address, "--home", &home_dir_string, ]); @@ -138,7 +137,7 @@ pub async fn run( .kill_on_drop(true) .spawn() .map_err(Error::StartUp)?; - tracing::info!("Tendermint node started"); + tracing::info!("CometBFT node started"); tokio::select! { status = tendermint_node.wait() => { @@ -348,24 +347,16 @@ pub fn write_validator_state(home_dir: impl AsRef) { async fn update_tendermint_config( home_dir: impl AsRef, - tendermint_config: config::Tendermint, + config: TendermintConfig, ) -> Result<()> { let home_dir = home_dir.as_ref(); let path = home_dir.join("config").join("config.toml"); - let mut config = - TendermintConfig::load_toml_file(&path).map_err(Error::LoadConfig)?; + let mut config = config.clone(); config.moniker = Moniker::from_str(&format!("{}-{}", config.moniker, namada_version())) .expect("Invalid moniker"); - config.p2p.laddr = - TendermintAddress::from_str(&tendermint_config.p2p_address.to_string()) - .unwrap(); - config.p2p.persistent_peers = tendermint_config.p2p_persistent_peers; - config.p2p.pex = tendermint_config.p2p_pex; - config.p2p.allow_duplicate_ip = tendermint_config.p2p_allow_duplicate_ip; - // In "dev", only produce blocks when there are txs or when the AppHash // changes config.consensus.create_empty_blocks = true; // !cfg!(feature = "dev"); @@ -375,36 +366,10 @@ async fn update_tendermint_config( // again in the future. config.mempool.keep_invalid_txs_in_cache = false; - config.rpc.laddr = - TendermintAddress::from_str(&tendermint_config.rpc_address.to_string()) - .unwrap(); // Bumped from the default `1_000_000`, because some WASMs can be // quite large config.rpc.max_body_bytes = 2_000_000; - config.instrumentation.prometheus = - tendermint_config.instrumentation_prometheus; - config.instrumentation.prometheus_listen_addr = tendermint_config - .instrumentation_prometheus_listen_addr - .to_string(); - config.instrumentation.namespace = - tendermint_config.instrumentation_namespace; - - #[cfg(feature = "abciplus")] - { - config.consensus.timeout_commit = - tendermint_config.consensus_timeout_commit; - } - - let indexer = if tendermint_config.tx_index { - TxIndexer::Kv - } else { - TxIndexer::Null - }; - #[cfg(feature = "abcipp")] - let indexer = [indexer]; - config.tx_index = TxIndexConfig { indexer }; - let mut file = OpenOptions::new() .write(true) .truncate(true) @@ -474,8 +439,8 @@ async fn write_tm_genesis( ) }); let data = serde_json::to_vec_pretty(&genesis) - .expect("Couldn't encode the Tendermint genesis file"); + .expect("Couldn't encode the CometBFT genesis file"); file.write_all(&data[..]) .await - .expect("Couldn't write the Tendermint genesis file"); + .expect("Couldn't write the CometBFT genesis file"); } diff --git a/apps/src/lib/wallet/defaults.rs b/apps/src/lib/wallet/defaults.rs index 95d1fe035b..c9d37c9e86 100644 --- a/apps/src/lib/wallet/defaults.rs +++ b/apps/src/lib/wallet/defaults.rs @@ -1,6 +1,6 @@ //! Default addresses and keys. -#[cfg(feature = "dev")] +#[cfg(any(test, feature = "dev"))] pub use dev::{ addresses, albert_address, albert_keypair, bertha_address, bertha_keypair, christel_address, christel_keypair, daewon_address, daewon_keypair, keys, @@ -68,7 +68,7 @@ pub fn addresses_from_genesis(genesis: GenesisConfig) -> Vec<(Alias, Address)> { addresses } -#[cfg(feature = "dev")] +#[cfg(any(test, feature = "dev"))] mod dev { use std::collections::HashMap; 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 0e98344216..45e1bc7976 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,13 +1,19 @@ [package] -authors = ["Heliax AG "] -edition = "2021" -license = "GPL-3.0" name = "namada_core" +description = "Namada core" resolver = "2" -version = "0.16.0" +authors.workspace = true +edition.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true [features] -default = [] +default = ["abciplus", "ferveo-tpke"] mainnet = [] ferveo-tpke = [ "ferveo", @@ -25,12 +31,6 @@ secp256k1-sign-verify = [ "libsecp256k1/hmac", ] -abcipp = [ - "ibc-proto-abcipp", - "ibc-abcipp", - "tendermint-abcipp", - "tendermint-proto-abcipp", -] abciplus = [ "ibc", "ibc-proto", @@ -42,10 +42,6 @@ ibc-mocks = [ "ibc/mocks", "ibc/std", ] -ibc-mocks-abcipp = [ - "ibc-abcipp/mocks", - "ibc-abcipp/std", -] # for integration tests and test utilies testing = [ @@ -56,61 +52,53 @@ testing = [ [dependencies] namada_macros = {path = "../macros"} -ark-bls12-381 = {version = "0.3"} +ark-bls12-381.workspace = true ark-ec = {version = "0.3", optional = true} -ark-serialize = {version = "0.3"} -# We switch off "blake2b" because it cannot be compiled to wasm -# 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" -borsh = "0.9.0" -chrono = {version = "0.4.22", default-features = false, features = ["clock", "std"]} -data-encoding = "2.3.2" -derivative = "2.2.0" -ed25519-consensus = "1.2.0" +ark-serialize.workspace = true +arse-merkle-tree.workspace = true +bech32.workspace = true +borsh.workspace = true +chrono.workspace = true +data-encoding.workspace = true +derivative.workspace = true +ed25519-consensus.workspace = true ferveo = {optional = true, git = "https://github.com/anoma/ferveo", rev = "e5abd0acc938da90140351a65a26472eb495ce4d"} ferveo-common = {git = "https://github.com/anoma/ferveo", rev = "e5abd0acc938da90140351a65a26472eb495ce4d"} tpke = {package = "group-threshold-cryptography", optional = true, git = "https://github.com/anoma/ferveo", rev = "e5abd0acc938da90140351a65a26472eb495ce4d"} # 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" } -proptest = {git = "https://github.com/heliaxdev/proptest", rev = "8f1b4abe7ebd35c0781bf9a00a4ee59833ffa2a1", optional = true} -prost = "0.11.6" -prost-types = "0.11.6" +ibc = {git = "https://github.com/heliaxdev/cosmos-ibc-rs.git", rev = "e71bc2cc79f8c2b32e970d95312f251398c93d9e", default-features = false, features = ["serde"], optional = true} +ibc-proto = {git = "https://github.com/heliaxdev/ibc-proto-rs.git", rev = "6f4038fcf4981f1ed70771d1cd89931267f917af", default-features = false, optional = true} +ics23.workspace = true +index-set.workspace = true +itertools.workspace = true +libsecp256k1.workspace = true +masp_primitives.workspace = true +proptest = {version = "1.2.0", optional = true} +prost.workspace = true +prost-types.workspace = true rand = {version = "0.8", optional = true} rand_core = {version = "0.6", optional = true} rayon = {version = "=1.5.3", optional = true} -rust_decimal = { version = "=1.26.1", features = ["borsh"] } -rust_decimal_macros = "=1.26.1" -serde = {version = "1.0.125", features = ["derive"]} -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 = "4db3c5ea09fae4057008d22bf9e96bf541b55b35", optional = true} -tendermint-proto-abcipp = {package = "tendermint-proto", git = "https://github.com/heliaxdev/tendermint-rs", rev = "4db3c5ea09fae4057008d22bf9e96bf541b55b35", optional = true} -thiserror = "1.0.38" -tracing = "0.1.30" -zeroize = {version = "1.5.5", features = ["zeroize_derive"]} +rust_decimal.workspace = true +rust_decimal_macros.workspace = true +serde.workspace = true +serde_json.workspace = true +sha2.workspace = true +tendermint = {git = "https://github.com/heliaxdev/tendermint-rs.git", rev = "02b256829e80f8cfecf3fa0d625c2a76c79cd043", optional = true} +tendermint-proto = {git = "https://github.com/heliaxdev/tendermint-rs.git", rev = "02b256829e80f8cfecf3fa0d625c2a76c79cd043", optional = true} +thiserror.workspace = true +tracing.workspace = true +zeroize.workspace = true [dev-dependencies] -assert_matches = "1.5.0" -libsecp256k1 = {git = "https://github.com/heliaxdev/libsecp256k1", rev = "bbb3bd44a49db361f21d9db80f9a087c194c0ae9"} -pretty_assertions = "0.7.2" -# A fork with state machine testing -proptest = {git = "https://github.com/heliaxdev/proptest", rev = "8f1b4abe7ebd35c0781bf9a00a4ee59833ffa2a1"} -rand = {version = "0.8"} -rand_core = {version = "0.6"} -test-log = {version = "0.2.7", default-features = false, features = ["trace"]} -tracing-subscriber = {version = "0.3.7", default-features = false, features = ["env-filter", "fmt"]} +assert_matches.workspace = true +libsecp256k1 = {workspace = true, features = ["hmac"]} +pretty_assertions.workspace = true +proptest.workspace = true +rand.workspace = true +rand_core.workspace = true +test-log.workspace = true +tracing-subscriber.workspace = true [build-dependencies] -tonic-build = "0.8.4" +tonic-build.workspace = true diff --git a/core/proptest-regressions/ledger/storage/mod.txt b/core/proptest-regressions/ledger/storage/mod.txt new file mode 100644 index 0000000000..5e452257c0 --- /dev/null +++ b/core/proptest-regressions/ledger/storage/mod.txt @@ -0,0 +1,2 @@ +cc 2c5330b824e07348ee588e53138f5df5895149db6b11f98343380b663c8c344c + diff --git a/core/src/ledger/ibc/storage.rs b/core/src/ledger/ibc/storage.rs index fab224e755..bee874bb0e 100644 --- a/core/src/ledger/ibc/storage.rs +++ b/core/src/ledger/ibc/storage.rs @@ -16,7 +16,7 @@ use crate::ibc::core::ics24_host::path::{ ReceiptPath, SeqAckPath, SeqRecvPath, SeqSendPath, }; use crate::ibc::core::ics24_host::Path; -use crate::types::address::{Address, InternalAddress, HASH_LEN}; +use crate::types::address::{Address, InternalAddress, HASH_HEX_LEN}; use crate::types::storage::{self, DbKeySeg, Key, KeySeg}; const CLIENTS_COUNTER: &str = "clients/counter"; @@ -500,7 +500,7 @@ pub fn token_hash_from_denom(denom: impl AsRef) -> Result> { pub fn calc_hash(denom: impl AsRef) -> String { let mut hasher = Sha256::new(); hasher.update(denom.as_ref()); - format!("{:.width$x}", hasher.finalize(), width = HASH_LEN) + format!("{:.width$x}", hasher.finalize(), width = HASH_HEX_LEN) } /// Key's prefix of the received token over IBC diff --git a/core/src/ledger/inflation.rs b/core/src/ledger/inflation.rs index e2248cc851..16baee9694 100644 --- a/core/src/ledger/inflation.rs +++ b/core/src/ledger/inflation.rs @@ -105,10 +105,11 @@ impl RewardsController { let control_val = p_gain * error - d_gain * delta_error; let last_inflation_amount = Decimal::from(last_inflation_amount); - let inflation = if last_inflation_amount + control_val > max_inflation { + let new_inflation_amount = last_inflation_amount + control_val; + let inflation = if new_inflation_amount > max_inflation { max_inflation - } else if last_inflation_amount + control_val > dec!(0.0) { - last_inflation_amount + control_val + } else if new_inflation_amount > dec!(0.0) { + new_inflation_amount } else { dec!(0.0) }; 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/mockdb.rs b/core/src/ledger/storage/mockdb.rs index 16e28d2759..e53178b157 100644 --- a/core/src/ledger/storage/mockdb.rs +++ b/core/src/ledger/storage/mockdb.rs @@ -83,6 +83,13 @@ impl DB for MockDB { } None => return Ok(None), }; + let update_epoch_blocks_delay: Option = + match self.0.borrow().get("update_epoch_blocks_delay") { + Some(bytes) => { + types::decode(bytes).map_err(Error::CodingError)? + } + None => return Ok(None), + }; #[cfg(feature = "ferveo-tpke")] let tx_queue: TxQueue = match self.0.borrow().get("tx_queue") { Some(bytes) => types::decode(bytes).map_err(Error::CodingError)?, @@ -94,6 +101,7 @@ impl DB for MockDB { let upper_prefix = format!("{}/", height.next_height().raw()); let mut merkle_tree_stores = MerkleTreeStoresRead::default(); let mut hash = None; + let mut time = None; let mut epoch = None; let mut pred_epochs = None; let mut address_gen = None; @@ -130,6 +138,11 @@ impl DB for MockDB { types::decode(bytes).map_err(Error::CodingError)?, ) } + "time" => { + time = Some( + types::decode(bytes).map_err(Error::CodingError)?, + ) + } "epoch" => { epoch = Some( types::decode(bytes).map_err(Error::CodingError)?, @@ -150,22 +163,28 @@ impl DB for MockDB { None => unknown_key_error(path)?, } } - match (hash, epoch, pred_epochs, address_gen) { - (Some(hash), Some(epoch), Some(pred_epochs), Some(address_gen)) => { - Ok(Some(BlockStateRead { - merkle_tree_stores, - hash, - height, - epoch, - pred_epochs, - next_epoch_min_start_height, - next_epoch_min_start_time, - address_gen, - results, - #[cfg(feature = "ferveo-tpke")] - tx_queue, - })) - } + match (hash, time, epoch, pred_epochs, address_gen) { + ( + Some(hash), + Some(time), + Some(epoch), + Some(pred_epochs), + Some(address_gen), + ) => Ok(Some(BlockStateRead { + merkle_tree_stores, + hash, + height, + time, + epoch, + pred_epochs, + next_epoch_min_start_height, + next_epoch_min_start_time, + update_epoch_blocks_delay, + address_gen, + results, + #[cfg(feature = "ferveo-tpke")] + tx_queue, + })), _ => Err(Error::Temporary { error: "Essential data couldn't be read from the DB" .to_string(), @@ -183,11 +202,13 @@ impl DB for MockDB { merkle_tree_stores, header, hash, + time, height, epoch, pred_epochs, next_epoch_min_start_height, next_epoch_min_start_time, + update_epoch_blocks_delay, address_gen, results, #[cfg(feature = "ferveo-tpke")] @@ -203,6 +224,10 @@ impl DB for MockDB { "next_epoch_min_start_time".into(), types::encode(&next_epoch_min_start_time), ); + self.0.borrow_mut().insert( + "update_epoch_blocks_delay".into(), + types::encode(&update_epoch_blocks_delay), + ); #[cfg(feature = "ferveo-tpke")] { self.0 @@ -257,6 +282,15 @@ impl DB for MockDB { .borrow_mut() .insert(key.to_string(), types::encode(&hash)); } + // Block time + { + let key = prefix_key + .push(&"time".to_owned()) + .map_err(Error::KeyError)?; + self.0 + .borrow_mut() + .insert(key.to_string(), types::encode(&time)); + } // Block epoch { let key = prefix_key @@ -480,9 +514,21 @@ impl DB for MockDB { impl<'iter> DBIter<'iter> for MockDB { type PrefixIter = MockPrefixIterator; - fn iter_prefix(&'iter self, prefix: &Key) -> MockPrefixIterator { + fn iter_optional_prefix( + &'iter self, + prefix: Option<&Key>, + ) -> MockPrefixIterator { let db_prefix = "subspace/".to_owned(); - let prefix = format!("{}{}", db_prefix, prefix); + let prefix = format!( + "{}{}", + db_prefix, + match prefix { + None => "".to_string(), + Some(prefix) => { + prefix.to_string() + } + } + ); let iter = self.0.borrow().clone().into_iter(); MockPrefixIterator::new(MockIterator { prefix, iter }, db_prefix) } diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index e9bdf93de9..935a677a1f 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -13,6 +13,7 @@ pub mod write_log; use core::fmt::Debug; use std::cmp::Ordering; +use borsh::{BorshDeserialize, BorshSerialize}; pub use merkle_tree::{ MembershipProof, MerkleTree, MerkleTreeStoresRead, MerkleTreeStoresWrite, StoreType, @@ -75,9 +76,8 @@ where /// going to be committed. After a block is committed, this is reset to /// `None` until the next `FinalizeBlock` phase is reached. pub header: Option
, - /// The height of the most recently committed block, or `BlockHeight(0)` if - /// no block has been committed for this chain yet. - pub last_height: BlockHeight, + /// The most recently committed block, if any. + pub last_block: Option, /// The epoch of the most recently committed block. If it is `Epoch(0)`, /// then no block may have been committed for this chain yet. pub last_epoch: Epoch, @@ -104,6 +104,17 @@ where pub storage_read_past_height_limit: Option, } +/// Last committed block +#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] +pub struct LastBlock { + /// Block height + pub height: BlockHeight, + /// Block hash + pub hash: BlockHash, + /// Block time + pub time: DateTimeUtc, +} + /// The block storage data #[derive(Debug)] pub struct BlockStorage { @@ -160,6 +171,8 @@ pub struct BlockStateRead { pub hash: BlockHash, /// Height of the block pub height: BlockHeight, + /// Time of the block + pub time: DateTimeUtc, /// Epoch of the block pub epoch: Epoch, /// Predecessor block epochs @@ -168,6 +181,8 @@ pub struct BlockStateRead { pub next_epoch_min_start_height: BlockHeight, /// Minimum block time at which the next epoch may start pub next_epoch_min_start_time: DateTimeUtc, + /// Update epoch delay + pub update_epoch_blocks_delay: Option, /// Established address generator pub address_gen: EstablishedAddressGen, /// Results of applying transactions @@ -187,6 +202,8 @@ pub struct BlockStateWrite<'a> { pub hash: &'a BlockHash, /// Height of the block pub height: BlockHeight, + /// Time of the block + pub time: DateTimeUtc, /// Epoch of the block pub epoch: Epoch, /// Predecessor block epochs @@ -195,6 +212,8 @@ pub struct BlockStateWrite<'a> { pub next_epoch_min_start_height: BlockHeight, /// Minimum block time at which the next epoch may start pub next_epoch_min_start_time: DateTimeUtc, + /// Update epoch delay + pub update_epoch_blocks_delay: Option, /// Established address generator pub address_gen: &'a EstablishedAddressGen, /// Results of applying transactions @@ -321,7 +340,20 @@ pub trait DBIter<'iter> { /// /// Read account subspace key value pairs with the given prefix from the DB, /// ordered by the storage keys. - fn iter_prefix(&'iter self, prefix: &Key) -> Self::PrefixIter; + fn iter_prefix(&'iter self, prefix: &Key) -> Self::PrefixIter { + self.iter_optional_prefix(Some(prefix)) + } + + /// Iterate over all subspace keys + fn iter_all(&'iter self) -> Self::PrefixIter { + self.iter_optional_prefix(None) + } + + /// Iterate over subspace keys, with optional prefix + fn iter_optional_prefix( + &'iter self, + prefix: Option<&Key>, + ) -> Self::PrefixIter; /// Read results subspace key value pairs from the DB fn iter_results(&'iter self) -> Self::PrefixIter; @@ -362,7 +394,7 @@ where chain_id, block, header: None, - last_height: BlockHeight(0), + last_block: None, last_epoch: Epoch::default(), next_epoch_min_start_height: BlockHeight::default(), next_epoch_min_start_time: DateTimeUtc::now(), @@ -386,25 +418,28 @@ where merkle_tree_stores, hash, height, + time, epoch, pred_epochs, next_epoch_min_start_height, next_epoch_min_start_time, + update_epoch_blocks_delay, results, address_gen, #[cfg(feature = "ferveo-tpke")] tx_queue, }) = self.db.read_last_block()? { - self.block.hash = hash; + self.block.hash = hash.clone(); self.block.height = height; self.block.epoch = epoch; self.block.results = results; self.block.pred_epochs = pred_epochs; - self.last_height = height; + self.last_block = Some(LastBlock { height, hash, time }); self.last_epoch = epoch; self.next_epoch_min_start_height = next_epoch_min_start_height; self.next_epoch_min_start_time = next_epoch_min_start_time; + self.update_epoch_blocks_delay = update_epoch_blocks_delay; self.address_gen = address_gen; // Rebuild Merkle tree self.block.tree = MerkleTree::new(merkle_tree_stores) @@ -452,24 +487,51 @@ where // All states are written only when the first height or a new epoch let is_full_commit = self.block.height.0 == 1 || self.last_epoch != self.block.epoch; + + // For convenience in tests, fill-in a header if it's missing. + // Normally, the header is added in `FinalizeBlock`. + #[cfg(any(test, feature = "testing"))] + { + if self.header.is_none() { + self.header = Some(Header { + hash: Hash::default(), + time: DateTimeUtc::now(), + next_validators_hash: Hash::default(), + }); + } + } + let state = BlockStateWrite { merkle_tree_stores: self.block.tree.stores(), header: self.header.as_ref(), hash: &self.block.hash, height: self.block.height, + time: self + .header + .as_ref() + .expect("Must have a block header on commit") + .time, epoch: self.block.epoch, results: &self.block.results, pred_epochs: &self.block.pred_epochs, next_epoch_min_start_height: self.next_epoch_min_start_height, next_epoch_min_start_time: self.next_epoch_min_start_time, + update_epoch_blocks_delay: self.update_epoch_blocks_delay, address_gen: &self.address_gen, #[cfg(feature = "ferveo-tpke")] tx_queue: &self.tx_queue, }; self.db.write_block(state, &mut batch, is_full_commit)?; - self.last_height = self.block.height; + let header = self + .header + .take() + .expect("Must have a block header on commit"); + self.last_block = Some(LastBlock { + height: self.block.height, + hash: header.hash.into(), + time: header.time, + }); self.last_epoch = self.block.epoch; - self.header = None; if is_full_commit { // prune old merkle tree stores self.prune_merkle_tree_stores(&mut batch)?; @@ -512,13 +574,13 @@ where key: &Key, height: BlockHeight, ) -> Result<(Option>, u64)> { - if height >= self.last_height { + if height >= self.get_last_block_height() { self.read(key) } else { match self.db.read_subspace_val_with_height( key, height, - self.last_height, + self.get_last_block_height(), )? { Some(v) => { let gas = key.len() + v.len(); @@ -538,7 +600,10 @@ where &self, prefix: &Key, ) -> (>::PrefixIter, u64) { - (self.db.iter_prefix(prefix), prefix.len() as _) + ( + self.db.iter_optional_prefix(Some(prefix)), + prefix.len() as _, + ) } /// Returns a prefix iterator and the gas cost @@ -722,7 +787,7 @@ where ) -> Result { use std::array; - if height > self.last_height { + if height > self.get_last_block_height() { Err(Error::Temporary { error: format!( "The block at the height {} hasn't committed yet", @@ -747,7 +812,7 @@ where key: &Key, height: BlockHeight, ) -> Result { - if height > self.last_height { + if height > self.get_last_block_height() { Err(Error::Temporary { error: format!( "The block at the height {} hasn't committed yet", @@ -898,11 +963,11 @@ where batch: &mut D::WriteBatch, ) -> Result<()> { if let Some(limit) = self.storage_read_past_height_limit { - if self.last_height.0 <= limit { + if self.get_last_block_height().0 <= limit { return Ok(()); } - let min_height = (self.last_height.0 - limit).into(); + let min_height = (self.get_last_block_height().0 - limit).into(); if let Some(epoch) = self.block.pred_epochs.get_epoch(min_height) { if epoch.0 == 0 { return Ok(()); @@ -919,9 +984,17 @@ where } } } - Ok(()) } + + /// Get the height of the last committed block or 0 if no block has been + /// committed yet. The first block is at height 1. + pub fn get_last_block_height(&self) -> BlockHeight { + self.last_block + .as_ref() + .map(|b| b.height) + .unwrap_or_default() + } } impl From for Error { @@ -971,7 +1044,7 @@ pub mod testing { chain_id, block, header: None, - last_height: BlockHeight(0), + last_block: None, last_epoch: Epoch::default(), next_epoch_min_start_height: BlockHeight::default(), next_epoch_min_start_time: DateTimeUtc::now(), @@ -1256,7 +1329,6 @@ mod tests { let time_of_update = time_of_update + Duration::seconds(1); wl_storage.update_epoch(height_of_update, time_of_update).unwrap(); assert_eq!(wl_storage.storage.block.epoch, epoch_before.next()); - assert!(wl_storage.storage.update_epoch_blocks_delay.is_none()); } } } diff --git a/core/src/ledger/storage/wl_storage.rs b/core/src/ledger/storage/wl_storage.rs index 82bb799f39..4ca76aa379 100644 --- a/core/src/ledger/storage/wl_storage.rs +++ b/core/src/ledger/storage/wl_storage.rs @@ -236,7 +236,7 @@ where D: DB + for<'iter_> DBIter<'iter_>, H: StorageHasher, { - let storage_iter = storage.db.iter_prefix(prefix).peekable(); + let storage_iter = storage.db.iter_optional_prefix(Some(prefix)).peekable(); let write_log_iter = write_log.iter_prefix_pre(prefix).peekable(); ( PrefixIter { @@ -261,7 +261,7 @@ where D: DB + for<'iter_> DBIter<'iter_>, H: StorageHasher, { - let storage_iter = storage.db.iter_prefix(prefix).peekable(); + let storage_iter = storage.db.iter_optional_prefix(Some(prefix)).peekable(); let write_log_iter = write_log.iter_prefix_post(prefix).peekable(); ( PrefixIter { 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/storage_api/collections/lazy_map.rs b/core/src/ledger/storage_api/collections/lazy_map.rs index c1e8ae6dbf..5870fd7b3f 100644 --- a/core/src/ledger/storage_api/collections/lazy_map.rs +++ b/core/src/ledger/storage_api/collections/lazy_map.rs @@ -740,4 +740,119 @@ mod test { Ok(()) } + + #[test] + fn test_nested_map_basics() -> storage_api::Result<()> { + let mut storage = TestWlStorage::default(); + let key = storage::Key::parse("testing").unwrap(); + + // A nested map from u32 -> String -> u32 + let nested_map = NestedMap::>::open(key); + + assert!(nested_map.is_empty(&storage)?); + assert!(nested_map.iter(&storage)?.next().is_none()); + + // Insert a value + nested_map + .at(&0) + .insert(&mut storage, "string1".to_string(), 100)?; + + assert!(!nested_map.is_empty(&storage)?); + assert!(nested_map.iter(&storage)?.next().is_some()); + assert_eq!( + nested_map.at(&0).get(&storage, &"string1".to_string())?, + Some(100) + ); + assert_eq!( + nested_map.at(&0).get(&storage, &"string2".to_string())?, + None + ); + + // Insert more values + nested_map + .at(&1) + .insert(&mut storage, "string1".to_string(), 200)?; + nested_map + .at(&0) + .insert(&mut storage, "string2".to_string(), 300)?; + + let mut it = nested_map.iter(&storage)?; + let ( + NestedSubKey::Data { + key, + nested_sub_key: SubKey::Data(inner_key), + }, + inner_val, + ) = it.next().unwrap()?; + assert_eq!(key, 0); + assert_eq!(inner_key, "string1".to_string()); + assert_eq!(inner_val, 100); + + let ( + NestedSubKey::Data { + key, + nested_sub_key: SubKey::Data(inner_key), + }, + inner_val, + ) = it.next().unwrap()?; + assert_eq!(key, 0); + assert_eq!(inner_key, "string2".to_string()); + assert_eq!(inner_val, 300); + + let ( + NestedSubKey::Data { + key, + nested_sub_key: SubKey::Data(inner_key), + }, + inner_val, + ) = it.next().unwrap()?; + assert_eq!(key, 1); + assert_eq!(inner_key, "string1".to_string()); + assert_eq!(inner_val, 200); + + // Next element should be None + assert!(it.next().is_none()); + drop(it); + + // Start removing elements + let rem = nested_map + .at(&0) + .remove(&mut storage, &"string2".to_string())?; + assert_eq!(rem, Some(300)); + assert_eq!( + nested_map.at(&0).get(&storage, &"string2".to_string())?, + None + ); + assert_eq!(nested_map.at(&0).len(&storage)?, 1_u64); + assert_eq!(nested_map.at(&1).len(&storage)?, 1_u64); + assert_eq!(nested_map.iter(&storage)?.count(), 2); + + // Start removing elements + let rem = nested_map + .at(&0) + .remove(&mut storage, &"string1".to_string())?; + assert_eq!(rem, Some(100)); + assert_eq!( + nested_map.at(&0).get(&storage, &"string1".to_string())?, + None + ); + assert_eq!(nested_map.at(&0).len(&storage)?, 0_u64); + assert_eq!(nested_map.at(&1).len(&storage)?, 1_u64); + assert_eq!(nested_map.iter(&storage)?.count(), 1); + + // Start removing elements + let rem = nested_map + .at(&1) + .remove(&mut storage, &"string1".to_string())?; + assert_eq!(rem, Some(200)); + assert_eq!( + nested_map.at(&1).get(&storage, &"string1".to_string())?, + None + ); + assert_eq!(nested_map.at(&0).len(&storage)?, 0_u64); + assert_eq!(nested_map.at(&1).len(&storage)?, 0_u64); + assert!(nested_map.is_empty(&storage)?); + + Ok(()) + } } diff --git a/core/src/ledger/storage_api/collections/lazy_vec.rs b/core/src/ledger/storage_api/collections/lazy_vec.rs index 1e83456814..d21ca1c515 100644 --- a/core/src/ledger/storage_api/collections/lazy_vec.rs +++ b/core/src/ledger/storage_api/collections/lazy_vec.rs @@ -407,7 +407,7 @@ impl LazyVec { // `LazyVec` methods with borsh encoded values `T` impl LazyVec where - T: BorshSerialize + BorshDeserialize + 'static, + T: BorshSerialize + BorshDeserialize + 'static + Debug, { /// Appends an element to the back of a collection. pub fn push(&self, storage: &mut S, val: T) -> Result<()> @@ -470,6 +470,23 @@ where storage.read(&self.get_data_key(index)) } + /// Read the first element + pub fn front(&self, storage: &S) -> Result> + where + S: StorageRead, + { + self.get(storage, 0) + } + + /// Read the last element + pub fn back(&self, storage: &S) -> Result> + where + S: StorageRead, + { + let len = self.len(storage)?; + self.get(storage, len - 1) + } + /// An iterator visiting all elements. The iterator element type is /// `Result`, because iterator's call to `next` may fail with e.g. out of /// gas or data decoding error. diff --git a/core/src/ledger/storage_api/collections/mod.rs b/core/src/ledger/storage_api/collections/mod.rs index 6301d151be..ff1136acd5 100644 --- a/core/src/ledger/storage_api/collections/mod.rs +++ b/core/src/ledger/storage_api/collections/mod.rs @@ -60,7 +60,7 @@ pub trait LazyCollection { type SubKeyWithData: Debug; /// A type of a value in the inner-most collection - type Value: BorshDeserialize; + type Value: BorshDeserialize + Debug; /// Create or use an existing vector with the given storage `key`. fn open(key: storage::Key) -> Self; 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/address.rs b/core/src/types/address.rs index 33221ca931..1b4c12652c 100644 --- a/core/src/types/address.rs +++ b/core/src/types/address.rs @@ -8,6 +8,7 @@ use std::str::FromStr; use bech32::{self, FromBase32, ToBase32, Variant}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use data_encoding::HEXUPPER; use rust_decimal_macros::dec; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; @@ -21,7 +22,7 @@ use crate::types::key::PublicKeyHash; use crate::types::{key, token}; /// The length of an established [`Address`] encoded with Borsh. -pub const ESTABLISHED_ADDRESS_BYTES_LEN: usize = 45; +pub const ESTABLISHED_ADDRESS_BYTES_LEN: usize = 21; /// The length of [`Address`] encoded with Bech32m. pub const ADDRESS_LEN: usize = 79 + ADDRESS_HRP.len(); @@ -31,7 +32,23 @@ pub const ADDRESS_LEN: usize = 79 + ADDRESS_HRP.len(); const ADDRESS_HRP: &str = "atest"; /// We're using "Bech32m" variant pub const BECH32M_VARIANT: bech32::Variant = Variant::Bech32m; -pub(crate) const HASH_LEN: usize = 40; + +/// Length of a hash of an address as a hexadecimal string +pub(crate) const HASH_HEX_LEN: usize = 40; + +/// Length of a trimmed hash of an address. +pub(crate) const HASH_LEN: usize = 20; + +/// SHA-256 hash len +/// +/// ``` +/// use sha2::Digest; +/// assert_eq!( +/// sha2::Sha256::output_size(), +/// namada_core::types::address::SHA_HASH_LEN +/// ); +/// ``` +pub const SHA_HASH_LEN: usize = 32; /// An address string before bech32m encoding must be this size. pub const FIXED_LEN_STRING_BYTES: usize = 45; @@ -170,10 +187,16 @@ impl Address { /// Try to get a raw hash of an address, only defined for established and /// implicit addresses. - pub fn raw_hash(&self) -> Option<&str> { + pub fn raw_hash(&self) -> Option { match self { - Address::Established(established) => Some(&established.hash), - Address::Implicit(ImplicitAddress(implicit)) => Some(&implicit.0), + Address::Established(established) => { + let hash_hex = HEXUPPER.encode(&established.hash); + Some(hash_hex) + } + Address::Implicit(ImplicitAddress(implicit)) => { + let hash_hex = HEXUPPER.encode(&implicit.0); + Some(hash_hex) + } Address::Internal(_) => None, } } @@ -182,7 +205,10 @@ impl Address { fn to_fixed_len_string(&self) -> Vec { let mut string = match self { Address::Established(EstablishedAddress { hash }) => { - format!("{}::{}", PREFIX_ESTABLISHED, hash) + // The bech32m's data is a hex of the first 40 chars of the hash + let hash_hex = HEXUPPER.encode(hash); + debug_assert_eq!(hash_hex.len(), HASH_HEX_LEN); + format!("{}::{}", PREFIX_ESTABLISHED, hash_hex) } Address::Implicit(ImplicitAddress(pkh)) => { format!("{}::{}", PREFIX_IMPLICIT, pkh) @@ -237,10 +263,24 @@ impl Address { } match string.split_once("::") { Some((PREFIX_ESTABLISHED, hash)) => { - if hash.len() == HASH_LEN { - Ok(Address::Established(EstablishedAddress { - hash: hash.to_string(), - })) + if hash.len() == HASH_HEX_LEN { + let raw = + HEXUPPER.decode(hash.as_bytes()).map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::InvalidInput, + e, + ) + })?; + if raw.len() != HASH_LEN { + return Err(Error::new( + ErrorKind::InvalidData, + "Established address hash must be 40 characters \ + long", + )); + } + let mut hash: [u8; HASH_LEN] = Default::default(); + hash.copy_from_slice(&raw); + Ok(Address::Established(EstablishedAddress { hash })) } else { Err(Error::new( ErrorKind::InvalidData, @@ -289,7 +329,7 @@ impl Address { internal::IBC_MINT => { Ok(Address::Internal(InternalAddress::IbcMint)) } - _ if raw.len() == HASH_LEN => Ok(Address::Internal( + _ if raw.len() == HASH_HEX_LEN => Ok(Address::Internal( InternalAddress::IbcToken(raw.to_string()), )), _ => Err(Error::new( @@ -393,20 +433,20 @@ impl TryFrom for Address { Deserialize, )] pub struct EstablishedAddress { - hash: String, + hash: [u8; HASH_LEN], } /// A generator of established addresses #[derive(Debug, Clone, PartialEq, BorshSerialize, BorshDeserialize)] pub struct EstablishedAddressGen { - last_hash: String, + last_hash: [u8; SHA_HASH_LEN], } impl EstablishedAddressGen { /// Initialize a new address generator with a given randomness seed. pub fn new(seed: impl AsRef) -> Self { Self { - last_hash: seed.as_ref().to_owned(), + last_hash: Sha256::digest(seed.as_ref().as_bytes()).into(), } } @@ -420,12 +460,12 @@ impl EstablishedAddressGen { let gen_bytes = self .try_to_vec() .expect("Encoding established addresses generator shouldn't fail"); - let mut hasher = Sha256::new(); let bytes = [&gen_bytes, rng_source.as_ref()].concat(); - hasher.update(bytes); - // hex of the first 40 chars of the hash - let hash = format!("{:.width$X}", hasher.finalize(), width = HASH_LEN); - self.last_hash = hash.clone(); + let full_hash = Sha256::digest(&bytes); + // take first 20 bytes of the hash + let mut hash: [u8; HASH_LEN] = Default::default(); + hash.copy_from_slice(&full_hash[..HASH_LEN]); + self.last_hash = full_hash.into(); Address::Established(EstablishedAddress { hash }) } } @@ -511,7 +551,8 @@ impl InternalAddress { let mut hasher = Sha256::new(); let s = format!("{}/{}/{}", port_id, channel_id, token); hasher.update(&s); - let hash = format!("{:.width$x}", hasher.finalize(), width = HASH_LEN); + let hash = + format!("{:.width$x}", hasher.finalize(), width = HASH_HEX_LEN); InternalAddress::IbcToken(hash) } } @@ -884,7 +925,7 @@ pub mod testing { ); hasher.update(&s); let hash = - format!("{:.width$x}", hasher.finalize(), width = HASH_LEN); + format!("{:.width$x}", hasher.finalize(), width = HASH_HEX_LEN); InternalAddress::IbcToken(hash) }) } 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..159a39ceb7 100644 --- a/core/src/types/key/ed25519.rs +++ b/core/src/types/key/ed25519.rs @@ -86,7 +86,7 @@ impl BorshSchema for PublicKey { } } -#[allow(clippy::derive_hash_xor_eq)] +#[allow(clippy::derived_hash_with_manual_eq)] impl Hash for PublicKey { fn hash(&self, state: &mut H) { self.0.to_bytes().hash(state); @@ -291,7 +291,7 @@ impl BorshSchema for Signature { } } -#[allow(clippy::derive_hash_xor_eq)] +#[allow(clippy::derived_hash_with_manual_eq)] impl Hash for Signature { fn hash(&self, state: &mut H) { self.0.to_bytes().hash(state); @@ -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..ad5917aa68 100644 --- a/core/src/types/key/mod.rs +++ b/core/src/types/key/mod.rs @@ -14,7 +14,7 @@ use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use data_encoding::HEXUPPER; #[cfg(feature = "rand")] use rand::{CryptoRng, RngCore}; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use sha2::{Digest, Sha256}; use thiserror::Error; @@ -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, @@ -299,23 +301,45 @@ pub trait SigScheme: Eq + Ord + Debug + Serialize + Default { PartialOrd, Ord, Hash, - Serialize, - Deserialize, )] -#[serde(transparent)] -pub struct PublicKeyHash(pub(crate) String); +pub struct PublicKeyHash(pub(crate) [u8; address::HASH_LEN]); -const PKH_HASH_LEN: usize = address::HASH_LEN; +impl serde::Serialize for PublicKeyHash { + fn serialize( + &self, + serializer: S, + ) -> std::result::Result + where + S: serde::Serializer, + { + let encoded = self.to_string(); + serde::Serialize::serialize(&encoded, serializer) + } +} + +impl<'de> serde::Deserialize<'de> for PublicKeyHash { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + use serde::de::Error; + let encoded: String = serde::Deserialize::deserialize(deserializer)?; + Self::from_str(&encoded).map_err(D::Error::custom) + } +} + +const PKH_HEX_LEN: usize = address::HASH_HEX_LEN; +const PKH_LEN: usize = address::HASH_LEN; impl From for String { fn from(pkh: PublicKeyHash) -> Self { - pkh.0 + pkh.to_string() } } impl Display for PublicKeyHash { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) + write!(f, "{}", HEXUPPER.encode(&self.0)) } } @@ -323,32 +347,36 @@ impl FromStr for PublicKeyHash { type Err = PkhFromStringError; fn from_str(s: &str) -> Result { - if s.len() != PKH_HASH_LEN { + if s.len() != PKH_HEX_LEN { return Err(Self::Err::UnexpectedLen(s.len())); } - Ok(Self(s.to_owned())) + let raw_bytes = HEXUPPER + .decode(s.as_bytes()) + .map_err(Self::Err::DecodeUpperHex)?; + let mut bytes: [u8; PKH_LEN] = Default::default(); + bytes.copy_from_slice(&raw_bytes); + Ok(Self(bytes)) } } #[allow(missing_docs)] #[derive(Error, Debug)] pub enum PkhFromStringError { - #[error("Wrong PKH len. Expected {PKH_HASH_LEN}, got {0}")] + #[error("Wrong PKH len. Expected {PKH_HEX_LEN}, got {0}")] UnexpectedLen(usize), + #[error("Failed decoding upper hex with {0}")] + DecodeUpperHex(data_encoding::DecodeError), } impl From<&PK> for PublicKeyHash { fn from(pk: &PK) -> Self { let pk_bytes = pk.try_to_vec().expect("Public key encoding shouldn't fail"); - let mut hasher = Sha256::new(); - hasher.update(pk_bytes); - // hex of the first 40 chars of the hash - PublicKeyHash(format!( - "{:.width$X}", - hasher.finalize(), - width = PKH_HASH_LEN - )) + let full_hash = Sha256::digest(&pk_bytes); + // take first 20 bytes of the hash + let mut hash: [u8; PKH_LEN] = Default::default(); + hash.copy_from_slice(&full_hash[..PKH_LEN]); + PublicKeyHash(hash) } } @@ -367,16 +395,11 @@ impl PublicKeyTmRawHash for common::PublicKey { /// Convert validator's consensus key into address raw hash that is compatible /// with Tendermint pub fn tm_consensus_key_raw_hash(pk: &common::PublicKey) -> String { - match pk { - common::PublicKey::Ed25519(pk) => { - let pkh = PublicKeyHash::from(pk); - pkh.0 - } - common::PublicKey::Secp256k1(pk) => { - let pkh = PublicKeyHash::from(pk); - pkh.0 - } - } + let pkh = match pk { + common::PublicKey::Ed25519(pk) => PublicKeyHash::from(pk), + common::PublicKey::Secp256k1(pk) => PublicKeyHash::from(pk), + }; + pkh.to_string() } /// Convert Tendermint validator's raw hash bytes to Namada raw hash string diff --git a/core/src/types/key/secp256k1.rs b/core/src/types/key/secp256k1.rs index d901e46d25..b40ceb3e50 100644 --- a/core/src/types/key/secp256k1.rs +++ b/core/src/types/key/secp256k1.rs @@ -90,7 +90,7 @@ impl BorshSchema for PublicKey { } } -#[allow(clippy::derive_hash_xor_eq)] +#[allow(clippy::derived_hash_with_manual_eq)] impl Hash for PublicKey { fn hash(&self, state: &mut H) { self.0.serialize_compressed().hash(state); @@ -401,7 +401,7 @@ impl BorshSchema for Signature { } } -#[allow(clippy::derive_hash_xor_eq)] +#[allow(clippy::derived_hash_with_manual_eq)] impl Hash for Signature { fn hash(&self, state: &mut H) { self.0.serialize().hash(state); @@ -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..487dddf3e3 100644 --- a/core/src/types/masp.rs +++ b/core/src/types/masp.rs @@ -9,7 +9,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use sha2::{Digest, Sha256}; use crate::types::address::{ - masp, Address, DecodeError, BECH32M_VARIANT, HASH_LEN, + masp, Address, DecodeError, BECH32M_VARIANT, HASH_HEX_LEN, }; /// human-readable part of Bech32m encoded address @@ -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 @@ -153,18 +153,18 @@ impl PaymentAddress { let mut hasher = Sha256::new(); hasher.update(bytes); // hex of the first 40 chars of the hash - format!("{:.width$X}", hasher.finalize(), width = HASH_LEN) + format!("{:.width$X}", hasher.finalize(), width = HASH_HEX_LEN) } } -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/named_address.rs b/core/src/types/named_address.rs deleted file mode 100644 index ec26721471..0000000000 --- a/core/src/types/named_address.rs +++ /dev/null @@ -1,285 +0,0 @@ -//! This module is currently unused and not included in the module tree. -//! It implements named addresses as described in [Archived -//! page](docs/src/archive/domain-name-addresses.md). - -use std::collections::HashSet; -use std::fmt::{Debug, Display}; -use std::hash::Hash; -use std::iter::FromIterator; -use std::str::FromStr; -use std::string; - -use bech32::{self, FromBase32, ToBase32, Variant}; -use borsh::{BorshDeserialize, BorshSerialize}; -use sha2::{Digest, Sha256}; -use thiserror::Error; - -const MAX_RAW_ADDRESS_LEN: usize = 255; -const MIN_RAW_ADDRESS_LEN: usize = 3; -const MAX_LABEL_LEN: usize = 64; - -const HASH_LEN: usize = 64; -/// human-readable part of Bech32m encoded address -const ADDRESS_HRP: &str = "a"; -const ADDRESS_BECH32_VARIANT: bech32::Variant = Variant::Bech32m; - -#[derive(Error, Debug)] -pub enum Error { - #[error("Address must be at least {MIN_RAW_ADDRESS_LEN} characters long")] - AddressTooShort, - #[error("Address must be at most {MAX_RAW_ADDRESS_LEN} characters long")] - AddressTooLong, - #[error("Address must not contain non-ASCII characters")] - AddressNonAscii, - #[error( - "Address can only contain ASCII alphanumeric characters, hyphens and \ - full stops" - )] - AddressContainsInvalidCharacter, - #[error("Address label cannot be be empty")] - EmptyLabel, - #[error("Address label must be at most {MAX_LABEL_LEN} characters long")] - LabelTooLong, - #[error("Address label cannot begin with hyphen")] - LabelStartsWithHyphen, - #[error("Address label cannot end with hyphen")] - LabelEndsWithHyphen, - #[error("Address label cannot begin with a digit")] - LabelStartsWithDigit, - #[error("Error decoding address from Bech32m: {0}")] - DecodeBech32(bech32::Error), - #[error("Error decoding address from base32: {0}")] - DecodeBase32(bech32::Error), - #[error( - "Unexpected Bech32m human-readable part {0}, expected {ADDRESS_HRP}" - )] - UnexpectedBech32Prefix(String), - #[error( - "Unexpected Bech32m variant {0:?}, expected {ADDRESS_BECH32_VARIANT:?}" - )] - UnexpectedBech32Variant(bech32::Variant), - #[error("Unexpected address hash length {0}, expected {HASH_LEN}")] - UnexpectedHashLength(usize), - #[error("Address must be encoded with utf-8")] - NonUtf8Address(string::FromUtf8Error), -} - -pub type Result = std::result::Result; - -#[derive( - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - BorshSerialize, - BorshDeserialize, - Hash, -)] -pub struct Address { - pub hash: String, -} - -/// invariant, the raw string is equal to labels.join("."). -#[derive( - Clone, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - BorshSerialize, - BorshDeserialize, -)] -pub struct RawAddress { - pub raw: String, - labels: Vec