From 50afd40ca0bfbfda7e76c88f59dad410d3e9510a Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Mon, 7 Nov 2022 09:39:48 +0000 Subject: [PATCH 01/41] Add a new Makefile target to print stack traces after running failing unit tests --- Makefile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Makefile b/Makefile index b2d0393df9..bf7bb44c10 100644 --- a/Makefile +++ b/Makefile @@ -133,6 +133,13 @@ test-unit: --skip e2e \ -Z unstable-options --report-time +test-unit-debug: + $(debug-cargo) test \ + $(TEST_FILTER) -- \ + --skip e2e \ + --nocapture \ + -Z unstable-options --report-time + test-wasm: make -C $(wasms) test From 498e79ea739924f0bd76be4028821605a3323000 Mon Sep 17 00:00:00 2001 From: yito88 Date: Thu, 17 Nov 2022 17:39:34 +0100 Subject: [PATCH 02/41] add ibc e2e test to the list --- .github/workflows/scripts/e2e.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/scripts/e2e.json b/.github/workflows/scripts/e2e.json index fd8939ba9a..9450dbcbcf 100644 --- a/.github/workflows/scripts/e2e.json +++ b/.github/workflows/scripts/e2e.json @@ -1,5 +1,6 @@ { "e2e::eth_bridge_tests::everything": 4, + "e2e::ibc_tests::run_ledger_ibc": 140, "e2e::ledger_tests::double_signing_gets_slashed": 12, "e2e::ledger_tests::invalid_transactions": 8, "e2e::ledger_tests::ledger_many_txs_in_a_block": 41, From 6e89a9d710c15c7b1abd1f17e925a91255bc720e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 21 Nov 2022 17:23:13 +0100 Subject: [PATCH 03/41] PoS: allow 0 amount bond/unbond in tx --- proof_of_stake/src/lib.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index e6cf06dcf4..8d6a98e5cf 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -1074,8 +1074,6 @@ pub enum BondError { InactiveValidator(Address), #[error("Voting power overflow: {0}")] VotingPowerOverflow(TryFromIntError), - #[error("Given zero amount to bond")] - ZeroAmount, } #[allow(missing_docs)] @@ -1093,8 +1091,6 @@ pub enum UnbondError { ValidatorHasNoVotingPower(Address), #[error("Voting power overflow: {0}")] VotingPowerOverflow(TryFromIntError), - #[error("Given zero amount to unbond")] - ZeroAmount, } #[allow(missing_docs)] @@ -1646,9 +1642,6 @@ where + BorshSerialize + BorshSchema, { - if amount == TokenAmount::default() { - return Err(BondError::ZeroAmount); - } // Check the validator state match validator_state { None => { @@ -1826,9 +1819,6 @@ where + BorshSerialize + BorshSchema, { - if amount == TokenAmount::default() { - return Err(UnbondError::ZeroAmount); - } // We can unbond tokens that are bonded for a future epoch (not yet // active), hence we check the total at the pipeline offset let unbondable_amount = bond From 5801b4fad3c772a47d4108093d3b7607cfb7339f Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Mon, 5 Sep 2022 15:59:34 +0200 Subject: [PATCH 04/41] Replay protection specs first draft --- documentation/specs/src/SUMMARY.md | 1 + .../src/base-ledger/replay-protection.md | 98 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 documentation/specs/src/base-ledger/replay-protection.md diff --git a/documentation/specs/src/SUMMARY.md b/documentation/specs/src/SUMMARY.md index b7537eb315..94c6390400 100644 --- a/documentation/specs/src/SUMMARY.md +++ b/documentation/specs/src/SUMMARY.md @@ -8,6 +8,7 @@ - [Default account](./base-ledger/default-account.md) - [Multisignature account](./base-ledger/multisignature.md) - [Fungible token](./base-ledger/fungible-token.md) + - [Replay protection](./base-ledger/replay-protection.md) - [Multi-asset shielded pool](./masp.md) - [Ledger integration](./masp/ledger-integration.md) - [Asset type](./masp/asset-type.md) diff --git a/documentation/specs/src/base-ledger/replay-protection.md b/documentation/specs/src/base-ledger/replay-protection.md new file mode 100644 index 0000000000..2b94c1e4f1 --- /dev/null +++ b/documentation/specs/src/base-ledger/replay-protection.md @@ -0,0 +1,98 @@ +# Replay Protection + +Namada supports a replay protection mechanism to prevent the execution of already processed transactions. + +## Tendermint + +[Tendermint]((https://docs.tendermint.com/v0.33/app-dev/app-development.html#replay-protection)) provides a first layer of protection against replay attacks in its mempool. The mechanism is based on a cache of previously seen transactions. This check though, like all the checks performed in `CheckTx`, is weak, since a malicious validator could always propose a block containing invalid transactions. There's therefore the need for a more robust replay protection mechanism implemented directly in the application. + +## WrapperTx + +`WrapperTx` is the only type of transaction currently accepted by the ledger. It must be protected from replay attacks because, if it wasn't, a malicious user could replay the transaction as is. Even if the inner transaction implemented replay protection or, for any reason, wasn't accepted, the signer of the wrapper would still pay for gas and fees, effectively suffering economic damage. + +To protect this transaction we can implement an in-protocol mechanism that requires us to expand the current definition of the struct to include a transaction counter: + +```rust +pub struct WrapperTx { + /// The fee to be payed for including the tx + pub fee: Fee, + /// Used to determine an implicit account of the fee payer + pub pk: common::PublicKey, + /// The epoch in which the tx is to be submitted. This determines + /// which decryption key will be used + pub epoch: Epoch, + /// Max amount of gas that can be used when executing the inner tx + pub gas_limit: GasLimit, + /// Transaction counter for replay-protection + pub tx_counter: u32, + /// the encrypted payload + pub inner_tx: EncryptedTx, + /// sha-2 hash of the inner transaction acting as a commitment + /// the contents of the encrypted payload + pub tx_hash: Hash, +} +``` + +In addition, we need a counter in the storage subspace of every implict address: + +``` +/$Address/tx_counter: u32 +``` + +In `process_proposal` we check that `tx_counter` field in `WrapperTx` is greater or equal to the value currently in storage; if this doesn't hold, the transaction is considered invalid. + +At last, in `finalize_block`, the protocol updates the counter key in storage, increasing its value by one. Now, if a malicious user tried to replay this transaction, the `tx_counter` in the struct would no longer be greater or equal to the one in storage and the transaction would be deemed invalid. + +Note that the address whose counter is involved in this process is the one signing the transaction (the same as the `pk` field of the struct). + +## InnerTx + +The `EncryptedTx` incapsulated inside `WrapperTx` should be protected too. That's because, if it wasn't, an attacker could extract the inner tx, rewrap it, and replay it.\ +To simplify this check we will perform it entirely in Wasm: the check of the counter will be carryed out by the validity predicates while the actual writing of the counter in storage will be done by the transactions themselves. + +To do so, the `SignedTxData` attached to the transaction will hold the current value of the counter in storage. The transaction will simply read the value from storage and increase its value by one. The VP will then check the validity of the signature and, if it's deemed valid, will proceed to checking if the pre value of the counter in storage was less or equal than the one contained in the transaction data and if the post value of the key in storage has been incremented by one. + +In the specific case of a transfer, since MASP already comes with replay protection as part of the Zcash design (see the [MASP specs](https://specs.namada.net/masp.html) and [Zcash protocol specs](https://zips.z.cash/protocol/protocol.pdf)), the counter in `SignedTxData` should be optional. + +An alternative implementation could place the protection for the inner tx in protocol, just like the wrapper one. In this case we would need to extend the `Tx` struct to hold an additional optional field (again, because of MASP) for the transaction counter and the address involved in replay protection, like so: + +```rust +pub struct Tx { + pub code: Vec, + pub data: Option>, + pub timestamp: DateTimeUtc, + pub tx_counter: Option<(Address, u32)> +} +``` + +The check would run in `process_proposal` and the update in `finalize_block`, just like for the wrapper transaction. The drawback of this implementation is that it implies the need for an hard fork in case of a modification of the replay protection mechanism. + +### VPs and Txs + +To implement replay protection for the inner transaction we will need to update all the VPs checking the transaction's signature to include the check on the transaction counter: at the moment the `vp_user` validity predicate is the only one to update. + +In addition, all the transactions involving `SignedTxData` should increment the counter. + +## Single counter + +The mechanisms to prevent replay attacks for both transactions (wrapper and inner) will share the same `tx_counter` key in storage. + +This could be an issue when the signer of the `WrapperTx` is the same of the `InnerTx` (default behaviour of the client): in this case, if both transactions expect the same counter in storage, the wrapper transaction will pass validation but the inner one will fail. To cope with this, the client will be responsible for producing a valid `WrapperTx` in which `tx_counter` will be set to the current value of the counter in storage, call it `storage_ctr`, while the inner transaction will have `storage_ctr + 1` in its data. + +## Storage Interface + +Replay protection will require interaction with the storage from both the protocol and Wasm. To do so we can take advantage of the `StorageRead` and `StorageWrite` traits to work with a single interface. + +## Batching + +The implementation proposed in this document doesn't support batching of multiple transactions from a same address in a single block. This is because the order in which transactions will be included in the block by the proposer is not guarateed. An out of order execution of multiple transaction would lead to the failure of some of them (in the worst case, the failure of all of them but the first one executed, in the best case, the failure of only the last transaction). This problem will be amplified by the introduction of Ferveo for DKG which will be able to reorder transactions. + +The Wasm implementation of replay protection can't cope with this problem because every wasm run (of either a transaction or a validity predicate) is only aware of its context, meaning the wasm bytecode and the serialized transaction data. The lack of awareness of the other transactions makes it impossible to develop a replay protection mechanism supporting batching in wasm. + +To address this issue there could be two ways: + +- Keep the proposed replay protection mechanism and implement a batching mechanism in the client to embed more than one transaction in a single `Tx` struct +- Implement replay protection in protocol for the inner transaction (as discolsed in section [InnerTx](#InnerTx)) + +Following the second option, the ledger would be able to analyze the validity of the counters, of all the transcations relating to a single address, against the value in storage at the beginning of the block. +Finally, it could increment the counter in storage a single time by the correct amount (given by the amount of transactions that were executed with success). From 9fd2bfe7f5a44185f5adc786f671ec340448c074 Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Mon, 5 Sep 2022 18:13:18 +0200 Subject: [PATCH 05/41] Review batching --- documentation/specs/src/base-ledger/replay-protection.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/documentation/specs/src/base-ledger/replay-protection.md b/documentation/specs/src/base-ledger/replay-protection.md index 2b94c1e4f1..e976d29a08 100644 --- a/documentation/specs/src/base-ledger/replay-protection.md +++ b/documentation/specs/src/base-ledger/replay-protection.md @@ -91,8 +91,10 @@ The Wasm implementation of replay protection can't cope with this problem becaus To address this issue there could be two ways: -- Keep the proposed replay protection mechanism and implement a batching mechanism in the client to embed more than one transaction in a single `Tx` struct +- Keep the proposed replay protection in Wasm and implement a batching mechanism in both the client and the ledger to embed more than one transaction in a single `Tx` struct - Implement replay protection in protocol for the inner transaction (as discolsed in section [InnerTx](#InnerTx)) Following the second option, the ledger would be able to analyze the validity of the counters, of all the transcations relating to a single address, against the value in storage at the beginning of the block. Finally, it could increment the counter in storage a single time by the correct amount (given by the amount of transactions that were executed with success). + +The first option, though, seems to have more benefits. In addition to allowing batching, it's more flexible and it also enables the in-order execution of the transactions included in the batch, which may come in handy in certain cases. From 7b152671ab766ba1fd1284119db3c4c8a2dd8317 Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Tue, 6 Sep 2022 11:38:02 +0200 Subject: [PATCH 06/41] Fixes typos, rephrase batching --- documentation/specs/src/base-ledger/replay-protection.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/specs/src/base-ledger/replay-protection.md b/documentation/specs/src/base-ledger/replay-protection.md index e976d29a08..8e9ddea036 100644 --- a/documentation/specs/src/base-ledger/replay-protection.md +++ b/documentation/specs/src/base-ledger/replay-protection.md @@ -85,7 +85,7 @@ Replay protection will require interaction with the storage from both the protoc ## Batching -The implementation proposed in this document doesn't support batching of multiple transactions from a same address in a single block. This is because the order in which transactions will be included in the block by the proposer is not guarateed. An out of order execution of multiple transaction would lead to the failure of some of them (in the worst case, the failure of all of them but the first one executed, in the best case, the failure of only the last transaction). This problem will be amplified by the introduction of Ferveo for DKG which will be able to reorder transactions. +The implementation proposed in this document doesn't support batching of multiple transactions from a same address in a single block. More specifically, the transactions will all succeed only if they are executed in the intended order, but the order in which transactions will be included in the block by the proposer is not guaranteed. An out of order execution of multiple transactions would lead to the failure of some of them (in the worst case, the failure of all of them but the first one executed, in the best case, the failure of only the last transaction). This problem will be amplified by the introduction of Ferveo for DKG which will be able to reorder transactions. The Wasm implementation of replay protection can't cope with this problem because every wasm run (of either a transaction or a validity predicate) is only aware of its context, meaning the wasm bytecode and the serialized transaction data. The lack of awareness of the other transactions makes it impossible to develop a replay protection mechanism supporting batching in wasm. @@ -94,7 +94,7 @@ To address this issue there could be two ways: - Keep the proposed replay protection in Wasm and implement a batching mechanism in both the client and the ledger to embed more than one transaction in a single `Tx` struct - Implement replay protection in protocol for the inner transaction (as discolsed in section [InnerTx](#InnerTx)) -Following the second option, the ledger would be able to analyze the validity of the counters, of all the transcations relating to a single address, against the value in storage at the beginning of the block. +Following the second option, the ledger would be able to analyze the validity of the counters, of all the transactions relating to a single address, against the value in storage at the beginning of the block. Finally, it could increment the counter in storage a single time by the correct amount (given by the amount of transactions that were executed with success). The first option, though, seems to have more benefits. In addition to allowing batching, it's more flexible and it also enables the in-order execution of the transactions included in the batch, which may come in handy in certain cases. From eb3a937a5113d840c147d6874383f1b01ff2c5f8 Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Wed, 7 Sep 2022 13:08:19 +0200 Subject: [PATCH 07/41] Fixes `tx_counter` type, check logic and drawbacks --- .../src/base-ledger/replay-protection.md | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/documentation/specs/src/base-ledger/replay-protection.md b/documentation/specs/src/base-ledger/replay-protection.md index 8e9ddea036..cef66e549d 100644 --- a/documentation/specs/src/base-ledger/replay-protection.md +++ b/documentation/specs/src/base-ledger/replay-protection.md @@ -24,7 +24,7 @@ pub struct WrapperTx { /// Max amount of gas that can be used when executing the inner tx pub gas_limit: GasLimit, /// Transaction counter for replay-protection - pub tx_counter: u32, + pub tx_counter: u64, /// the encrypted payload pub inner_tx: EncryptedTx, /// sha-2 hash of the inner transaction acting as a commitment @@ -36,12 +36,12 @@ pub struct WrapperTx { In addition, we need a counter in the storage subspace of every implict address: ``` -/$Address/tx_counter: u32 +/$Address/tx_counter: u64 ``` -In `process_proposal` we check that `tx_counter` field in `WrapperTx` is greater or equal to the value currently in storage; if this doesn't hold, the transaction is considered invalid. +In `process_proposal` we check that `tx_counter` field in `WrapperTx` is equal to the value currently in storage; if this doesn't hold, the transaction is considered invalid. -At last, in `finalize_block`, the protocol updates the counter key in storage, increasing its value by one. Now, if a malicious user tried to replay this transaction, the `tx_counter` in the struct would no longer be greater or equal to the one in storage and the transaction would be deemed invalid. +At last, in `finalize_block`, the protocol updates the counter key in storage, increasing its value by one. Now, if a malicious user tried to replay this transaction, the `tx_counter` in the struct would no longer be equal to the one in storage and the transaction would be deemed invalid. Note that the address whose counter is involved in this process is the one signing the transaction (the same as the `pk` field of the struct). @@ -50,7 +50,7 @@ Note that the address whose counter is involved in this process is the one signi The `EncryptedTx` incapsulated inside `WrapperTx` should be protected too. That's because, if it wasn't, an attacker could extract the inner tx, rewrap it, and replay it.\ To simplify this check we will perform it entirely in Wasm: the check of the counter will be carryed out by the validity predicates while the actual writing of the counter in storage will be done by the transactions themselves. -To do so, the `SignedTxData` attached to the transaction will hold the current value of the counter in storage. The transaction will simply read the value from storage and increase its value by one. The VP will then check the validity of the signature and, if it's deemed valid, will proceed to checking if the pre value of the counter in storage was less or equal than the one contained in the transaction data and if the post value of the key in storage has been incremented by one. +To do so, the `SignedTxData` attached to the transaction will hold the current value of the counter in storage. The transaction will simply read the value from storage and increase its value by one. The VP will then check the validity of the signature and, if it's deemed valid, will proceed to checking if the pre value of the counter in storage was equal to the one contained in the transaction data and if the post value of the key in storage has been incremented by one. In the specific case of a transfer, since MASP already comes with replay protection as part of the Zcash design (see the [MASP specs](https://specs.namada.net/masp.html) and [Zcash protocol specs](https://zips.z.cash/protocol/protocol.pdf)), the counter in `SignedTxData` should be optional. @@ -61,11 +61,14 @@ pub struct Tx { pub code: Vec, pub data: Option>, pub timestamp: DateTimeUtc, - pub tx_counter: Option<(Address, u32)> + pub tx_counter: Option<(Address, u64)> } ``` -The check would run in `process_proposal` and the update in `finalize_block`, just like for the wrapper transaction. The drawback of this implementation is that it implies the need for an hard fork in case of a modification of the replay protection mechanism. +The check would run in `process_proposal` and the update in `finalize_block`, just like for the wrapper transaction. This implementation, though, shows two drawbacks: + +- it implies the need for an hard fork in case of a modification of the replay protection mechanism +- it's not clear who's the source of the inner transaction from the outside, as that depends on the specific code of the transaction itself. We could use specific whitelisted txs set to define when it requires a counter (would not work for future programmable transactions), but still, we have no way to define which address should be targeted for replay protection (**blocking issue**) ### VPs and Txs @@ -85,7 +88,7 @@ Replay protection will require interaction with the storage from both the protoc ## Batching -The implementation proposed in this document doesn't support batching of multiple transactions from a same address in a single block. More specifically, the transactions will all succeed only if they are executed in the intended order, but the order in which transactions will be included in the block by the proposer is not guaranteed. An out of order execution of multiple transactions would lead to the failure of some of them (in the worst case, the failure of all of them but the first one executed, in the best case, the failure of only the last transaction). This problem will be amplified by the introduction of Ferveo for DKG which will be able to reorder transactions. +The implementation proposed in this document doesn't support batching of multiple transactions from a same address in a single block. More specifically, the transactions will all succeed only if they are executed in the intended order, but the order in which transactions will be included in the block by the proposer is not guaranteed. An out of order execution of multiple transactions would lead to the failure of some of them (in the worst case, the failure of all of them but one, in the best case, the failure of only one). This problem will be amplified by the introduction of Ferveo for DKG which will be able to reorder transactions. The Wasm implementation of replay protection can't cope with this problem because every wasm run (of either a transaction or a validity predicate) is only aware of its context, meaning the wasm bytecode and the serialized transaction data. The lack of awareness of the other transactions makes it impossible to develop a replay protection mechanism supporting batching in wasm. @@ -95,6 +98,6 @@ To address this issue there could be two ways: - Implement replay protection in protocol for the inner transaction (as discolsed in section [InnerTx](#InnerTx)) Following the second option, the ledger would be able to analyze the validity of the counters, of all the transactions relating to a single address, against the value in storage at the beginning of the block. -Finally, it could increment the counter in storage a single time by the correct amount (given by the amount of transactions that were executed with success). +Finally, it could increment the counter in storage a single time by the correct amount (given by the amount of transactions that were executed with success). As already mentioned, though, this implementation suffers from some serious issues and seems to not be feasible. -The first option, though, seems to have more benefits. In addition to allowing batching, it's more flexible and it also enables the in-order execution of the transactions included in the batch, which may come in handy in certain cases. +The first option, instead, carries more benefits, since in addition to allowing batching, it's also more flexible. From d03f2bb1c835cc427e1e4b5d589d2a8217f3ec89 Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Fri, 30 Sep 2022 18:27:33 +0200 Subject: [PATCH 08/41] Updates replay protection specs --- .../src/base-ledger/replay-protection.md | 258 +++++++++++++++--- 1 file changed, 220 insertions(+), 38 deletions(-) diff --git a/documentation/specs/src/base-ledger/replay-protection.md b/documentation/specs/src/base-ledger/replay-protection.md index cef66e549d..5cfb5d36fa 100644 --- a/documentation/specs/src/base-ledger/replay-protection.md +++ b/documentation/specs/src/base-ledger/replay-protection.md @@ -1,16 +1,24 @@ # Replay Protection -Namada supports a replay protection mechanism to prevent the execution of already processed transactions. +Replay protection is a mechanism to prevent _replay attacks_, which consist of a malicious user resubmitting an already executed transaction (also mentioned as tx in this document) to the ledger. -## Tendermint +A replay attack causes the state of the machine to deviate from the intended one (from the perspective of the parties involved in the original transaction) and causes economic damage to the fee payer of the original transaction, who finds himself paying more than once. Further economic damage is caused if the transaction involved the moving of value in some form (e.g. a transfer of tokens) with the sender being deprived of more value than intended. -[Tendermint]((https://docs.tendermint.com/v0.33/app-dev/app-development.html#replay-protection)) provides a first layer of protection against replay attacks in its mempool. The mechanism is based on a cache of previously seen transactions. This check though, like all the checks performed in `CheckTx`, is weak, since a malicious validator could always propose a block containing invalid transactions. There's therefore the need for a more robust replay protection mechanism implemented directly in the application. +Since the original transaction was already well formatted for the protocol's rules, the attacker doesn't need to rework it, making this attack relatively easy. -## WrapperTx +Of course, a replay attack makes sense only if the attacker differs from the _source_ of the original transaction, as a user will always be able to generate another semantically identical transaction to submit without the need to replay the same one. -`WrapperTx` is the only type of transaction currently accepted by the ledger. It must be protected from replay attacks because, if it wasn't, a malicious user could replay the transaction as is. Even if the inner transaction implemented replay protection or, for any reason, wasn't accepted, the signer of the wrapper would still pay for gas and fees, effectively suffering economic damage. +To prevent this scenario, Namada supports a replay protection mechanism to prevent the execution of already processed transactions. + +## Context -To protect this transaction we can implement an in-protocol mechanism that requires us to expand the current definition of the struct to include a transaction counter: +This section will illustrate the pre-existing context in which we are going to implement the replay protection mechanism. + +### Encryption-Authentication + +The current implementation of Namada is built on top of Tendermint which provides an encrypted and authenticated communication channel between every two nodes to prevent a _man-in-the-middle_ attack (see the detailed [spec](https://github.com/tendermint/tendermint/blob/29e5fbcc648510e4763bd0af0b461aed92c21f30/spec/p2p/peer.md)). + +The Namada protocol relies on this substrate to exchange transactions (messages) that will define the state transition of the ledger. More specifically, a transaction is composed of two parts: a `WrapperTx` and an inner `Tx` ```rust pub struct WrapperTx { @@ -23,81 +31,255 @@ pub struct WrapperTx { pub epoch: Epoch, /// Max amount of gas that can be used when executing the inner tx pub gas_limit: GasLimit, - /// Transaction counter for replay-protection - pub tx_counter: u64, /// the encrypted payload pub inner_tx: EncryptedTx, /// sha-2 hash of the inner transaction acting as a commitment /// the contents of the encrypted payload pub tx_hash: Hash, } + +pub struct Tx { + pub code: Vec, + pub data: Option>, + pub timestamp: DateTimeUtc, +} ``` -In addition, we need a counter in the storage subspace of every implict address: +The wrapper transaction is composed of some metadata, the encrypted inner transaction itself and the hash of this. The inner `Tx` transaction carries the Wasm code to be executed and the associated data. + +A transaction is constructed as follows: + +1. The struct `Tx` is produced +2. The hash of this transaction gets signed by the author, producing another `Tx` where the data field holds the concatenation of the original data and the signature (`SignedTxData`) +3. The produced transaction is encrypted and embedded in a `WrapperTx`. The encryption step is there for a future implementation of DKG (see [Ferveo](https://github.com/anoma/ferveo)) +4. Finally, the `WrapperTx` gets converted to a `Tx` struct, signed over its hash (same as step 2, relying on `SignedTxData`), and submitted to the network + +Note that the signer of the `WrapperTx` and that of the inner one don't need to coincide, but the signer of the wrapper will be charged with gas and fees. +In the execution steps: + +1. The `WrapperTx` signature is verified and, only if valid, the tx is processed +2. In the following height the proposer decrypts the inner tx, checks that the hash matches that of the `tx_hash` field and, if everything went well, includes the decrypted tx in the proposed block +3. The inner tx will then be executed by the Wasm runtime +4. After the execution, the affected validity predicates (also mentioned as VP in this document) will check the storage changes and (if relevant) the signature of the transaction: if the signature is not valid, the VP will deem the transaction invalid and the changes won't be applied to the storage + +The signature checks effectively prevent any tampering with the transaction data because that would cause the checks to fail and the transaction to be rejected. +For a more in-depth view, please refer to the [Namada execution spec](https://specs.namada.net/base-ledger/execution.html). + +### Tendermint replay protection + +The underlying consensus engine, [Tendermint](https://github.com/tendermint/tendermint/blob/29e5fbcc648510e4763bd0af0b461aed92c21f30/spec/abci/apps.md), provides a first layer of protection in its mempool which is based on a cache of previously seen transactions. This mechanism is actually aimed at preventing a block proposer from including an already processed transaction in the next block, which can happen when the transaction has been received late. Of course, this also acts as a countermeasure against intentional replay attacks. This check though, like all the checks performed in `CheckTx`, is weak, since a malicious validator could always propose a block containing invalid transactions. There's therefore the need for a more robust replay protection mechanism implemented directly in the application. + +## Implementation + +Namada replay protection consists of three parts: the in-Wasm solution for `EncryptedTx` (also called the `InnerTx`), an in-protocol mechanism for `WrapperTx` and a way to mitigate replay attacks in case of a fork. + +### InnerTx + +The actual Wasm code and data for the transaction are encapsulated inside a struct `Tx`, which gets encrypted as an `EncryptedTx` and wrapped inside a `WrapperTx` (see the [relative](#encryption-authentication) section). This inner transaction must be protected from replay attacks because it carries the actual semantics of the state transition. Moreover, even if the wrapper transaction was protected from replay attacks, an attacker could extract the inner transaction, rewrap it, and replay it. Note that for this attack to work, the attacker will need to sign the outer transaction himself and pay gas and fees for that, but this could still cause much greater damage to the parties involved in the inner transaction. + +We will implement the protection entirely in Wasm: the check of the counter will be carried out by the validity predicates while the actual writing of the counter in storage will be done by the transactions themselves. + +To do so, the `SignedTxData` attached to the transaction will hold the current value of the counter in storage: + +```rust +pub struct SignedTxData { + /// The original tx data bytes, if any + pub data: Option>, + /// The optional transaction counter for replay protection + pub tx_counter: Option, + /// The signature is produced on the tx data concatenated with the tx code + /// and the timestamp. + pub sig: common::Signature, +} +``` + +The counter must reside in `SignedTxData` and not in the data itself because this must be checked by the validity predicate which is not aware of the specific transaction that took place but only of the changes in the storage; therefore, the VP is not able to correctly deserialize the data of the transactions since it doesn't know what type of data the bytes represent. + +The counter will be signed as well to protect it from tampering and grant it the same guarantees explained at the [beginning](#encryption-authentication) of this document. + +The wasm transaction will simply read the value from storage and increase its value by one. The target key in storage will be the following: + +``` +/$Address/inner_tx_counter: u64 +``` + +The VP of the _source_ address will then check the validity of the signature and, if it's deemed valid, will proceed to check if the pre-value of the counter in storage was equal to the one contained in the `SignedTxData` struct and if the post-value of the key in storage has been incremented by one: if any of these conditions doesn't hold the VP will discard the transactions and prevent the changes from being applied to the storage. + +In the specific case of a transfer, since MASP already comes with replay protection as part of the Zcash design (see the [MASP specs](https://specs.namada.net/masp.html) and [Zcash protocol specs](https://zips.z.cash/protocol/protocol.pdf)), the counter in `SignedTxData` is not required and therefore should be optional. + +To implement replay protection for the inner transaction we will need to update all the VPs checking the transaction's signature to include the check on the transaction counter: at the moment the `vp_user` validity predicate is the only one to update. In addition, all the transactions involving `SignedTxData` should increment the counter. + +### WrapperTx + +`WrapperTx` is the only type of transaction currently accepted by the ledger. It must be protected from replay attacks because, if it wasn't, a malicious user could replay the transaction as is. Even if the inner transaction implemented replay protection (as explained in the [previous](#innertx) section) or, for any reason, wasn't accepted, the signer of the wrapper would still pay for gas and fees, effectively suffering economic damage. + +To protect this transaction we can implement an in-protocol mechanism. Since the wrapper transaction gets signed before being submitted to the network, we can leverage the `tx_counter` field of the `SignedTxData` already introduced for the inner tx. + +In addition, we need another counter in the storage subspace of every address: ``` -/$Address/tx_counter: u64 +/$Address/wrapper_tx_counter: u64 ``` -In `process_proposal` we check that `tx_counter` field in `WrapperTx` is equal to the value currently in storage; if this doesn't hold, the transaction is considered invalid. +where `$Address` is the one signing the transaction (the same implied by the `pk` field of the `WrapperTx` struct). -At last, in `finalize_block`, the protocol updates the counter key in storage, increasing its value by one. Now, if a malicious user tried to replay this transaction, the `tx_counter` in the struct would no longer be equal to the one in storage and the transaction would be deemed invalid. +The check will consist of a signature check first followed by a check on the counter that will make sure that the counter attached to the transaction matches the one in storage for the signing address. This will be done in the `process_proposal` function so that validators can decide whether the transaction is valid or not; if it's not, then they will discard the transaction and skip to the following one. -Note that the address whose counter is involved in this process is the one signing the transaction (the same as the `pk` field of the struct). +At last, in `finalize_block`, the ledger will update the counter key in storage, increasing its value by one. This will happen when the following conditions are met: -## InnerTx +- `process_proposal` has accepted the tx by validating its signature and transaction counter +- The tx was correctly applied in `finalize_block` (for `WrapperTx` this simply means inclusion in the block and gas accounting) -The `EncryptedTx` incapsulated inside `WrapperTx` should be protected too. That's because, if it wasn't, an attacker could extract the inner tx, rewrap it, and replay it.\ -To simplify this check we will perform it entirely in Wasm: the check of the counter will be carryed out by the validity predicates while the actual writing of the counter in storage will be done by the transactions themselves. +Now, if a malicious user tried to replay this transaction, the `tx_counter` in the struct would no longer be equal to the one in storage and the transaction would be deemed invalid. -To do so, the `SignedTxData` attached to the transaction will hold the current value of the counter in storage. The transaction will simply read the value from storage and increase its value by one. The VP will then check the validity of the signature and, if it's deemed valid, will proceed to checking if the pre value of the counter in storage was equal to the one contained in the transaction data and if the post value of the key in storage has been incremented by one. +### Forks -In the specific case of a transfer, since MASP already comes with replay protection as part of the Zcash design (see the [MASP specs](https://specs.namada.net/masp.html) and [Zcash protocol specs](https://zips.z.cash/protocol/protocol.pdf)), the counter in `SignedTxData` should be optional. +In the case of a fork, the transaction counters are not enough to prevent replay attacks. Transactions, in fact, could still be replayed on the other branch as long as their format is kept unchanged and the counters in storage match. -An alternative implementation could place the protection for the inner tx in protocol, just like the wrapper one. In this case we would need to extend the `Tx` struct to hold an additional optional field (again, because of MASP) for the transaction counter and the address involved in replay protection, like so: +To mitigate this problem, transactions will need to carry a `ChainId` identifier to tie them to a specific fork. This field needs to be added to the `Tx` struct so that it applies to both `WrapperTx` and `EncryptedTx` (for the same reason explained in [InnerTx](#InnerTx) about the double transaction counter): ```rust pub struct Tx { pub code: Vec, pub data: Option>, pub timestamp: DateTimeUtc, - pub tx_counter: Option<(Address, u64)> + pub chain_id: ChainId } ``` -The check would run in `process_proposal` and the update in `finalize_block`, just like for the wrapper transaction. This implementation, though, shows two drawbacks: +This new field will be signed just like the other ones and is therefore subject to the same guarantees explained in the [initial](#encrypted-authenticated-fixme-better-name-for-this-section) section. +The validity of this identifier will be checked in `process_proposal` for both the outer and inner tx: if a transaction carries an unexpected chain id, it won't be applied, meaning that the counter in storage won't be updated and no other modifications will be applied to storage. + +## Implementation details + +In this section we'll talk about some details of the replay protection mechanism that derive from the solution proposed in the previous section. + +### Storage counters + +Replay protection will require interaction with the storage from both the protocol and Wasm. To do so we can take advantage of the `StorageRead` and `StorageWrite` traits to work with a single interface. + +The proposed implementation requires two transaction counters in storage for every address, so that the storage subspace of a given address looks like the following: + +``` +/$Address/wrapper_tx_counter: u64 +/$Address/inner_tx_counter: u64 +``` + +An implementation requiring a single counter in storage has been taken into consideration and discarded because that would not support batching; see the [relative section](#single-counter-in-storage) for a more in-depth explanation. + +For both the wrapper and inner transaction, the increase of the counter in storage is an important step that must be correctly executed. More specifically, we want to increase the counter as soon as we verify that the signature, the chain id and the passed-in transaction counter are valid. The increase should happen immediately after the checks because of two reasons: + +- Prevent replay attack of a transaction in the same block +- Update the transaction counter even in case the transaction fails, to prevent a possible replay attack in the future (since a transaction invalid at state Sx could become valid at state Sn where `n > x`) + +For `WrapperTx`, the counter increase and fee accounting will per performed in `finalize_block` (as stated in the [relative](#wrappertx) section). + +For `InnerTx`, instead, the logic is not straightforward. The transaction code will be executed in a Wasm environment ([Wasmer](https://wasmer.io)) till it eventually completes or raises an exception. In case of success, the counter in storage will be updated correctly but, in case of failure, the protocol will discard all of the changes brought by the transactions to the write-ahead-log, including the updated transaction counter. This is a problem because the transaction could be successfully replayed in the future if it will become valid. + +The ideal solution would be to interrupt the execution of the Wasm code after the transaction counter (if any) has been increased. This would allow performing a first run of the involved VPs and, if all of them accept the changes, let the protocol commit these changes before any possible failure. After that, the protocol would resume the execution of the transaction from the previous interrupt point until completion or failure, after which a second pass of the VPs is initiated to validate the remaining state modifications. In case of a VP rejection after the counter increase there would be no need to resume execution and the transaction could be immediately deemed invalid so that the protocol could skip to the next tx to be executed. With this solution, the counter update would be committed to storage regardless of a failure of the transaction itself. + +Unfortunately, at the moment, Wasmer doesn't allow [yielding](https://github.com/wasmerio/wasmer/issues/1127) from the execution. For now, the responsibility will be up to the user to provide a valid inner transaction, and, in case of an invalid one, to take actions to prevent a possible replay attack: in essence, the user will be required to submit a new valid transaction to invalidate the counter of the previous one. + +### Batching and transaction ordering + +The proposed replay protection technique supports the execution of multiple transactions with the same address as _source_ in a single block. Actually, the presence of the transaction counters and the checks performed on them now impose a strict ordering on the execution sequence (which can be an added value for some use cases). The correct execution of more than one transaction per source address in the same block is preserved as long as: + +1. The wrapper transactions are inserted in the block with the correct ascending order +2. No hole is present in the counters' sequence +3. The counter of the first transaction included in the block matches the expected one in storage + +The conditions are enforced by the block proposer who has an interest in maximizing the amount of fees extracted by the proposed block. To support this incentive, we will charge gas and fees at the same moment in which we perform the counter increase explained in the [storage counters](#storage-counters) section: this way we can avoid charging fees and gas if the transaction is invalid (invalid signature, wrong counter or wrong chain id), effectively incentivizing the block proposer to include only valid transactions and correctly reorder them to maximize the fees (see the [block rejection](#block-rejection) section for an alternative solution that was discarded in favor of this). + +In case of a missing transaction causes a hole in the sequence of transaction counters, the block proposer will include in the block all the transactions up to the missing one and discard all the ones following that one, effectively preserving the correct ordering. + +Correctly ordering the transactions is not enough to guarantee the correct execution. As already mentioned in the [WrapperTx](#wrappertx) section, the block proposer and the validators also need to access the storage to check that the first transaction counter of a sequence is actually the expected one. + +The entire counter ordering is only done on the `WrapperTx`: if the inner counter is wrong then the inner transaction will fail and the signer of the corresponding wrapper will be charged with fees. This incentivizes submitters to produce valid transactions and discourages malicious user from rewrapping and resubmitting old transactions. + +### Mempool checks + +As a form of optimization to prevent mempool spamming, some of the checks that have been introduced in this document will also be brought to the `mempool_validate` function. Of course, we always refer to checks on the `WrapperTx` only. More specifically: + +- Check the `ChainId` field +- Check the signature of the transaction against the `pk` field of the `WrapperTx` +- Perform a limited check on the transaction counter + +Regarding the last point, `mempool_validate` will check if the counter in the transaction is `>=` than the one in storage for the address signing the `WrapperTx`. A complete check (checking for strict equality) is not feasible, as described in the [relative](#mempool-counter-validation) section. + +## Alternatives considered + +In this last section we list some possible solutions that were taken into consideration during the writing of this spec but were eventually discarded. + +### Mempool counter validation + +The idea of performing a complete validation of the transaction counters in the `mempool_validate` function was discarded because of a possible flaw. + +Suppose a client sends five transactions (counters from 1 to 5). The mempool of the next block proposer is not guaranteed to receive them in order: something on the network could shuffle the transactions up so that they arrive in the following order: 2-3-4-5-1. Now, since we validate every single transaction to be included in the mempool in the exact order in which we receive them, we would discard the first four transactions and only accept the last one, that with counter 1. Now the next block proposer might have the four discarded transactions in its mempool (since those were not added to the previous block and therefore not evicted from the other mempools, at least they shouldn't, see [block rejection](#block-rejection)) and could therefore include them in the following block. But still, a process that could have ended in a single block actually took two blocks. Moreover, there are two more issues: + +- The next block proposer might have the remaining transactions out of order in his mempool as well, effectively propagating the same issue down to the next block proposer +- The next block proposer might not have these transactions in his mempool at all + +Finally, transactions that are not allowed into the mempool don't get propagated to the other peers, making their inclusion in a block even harder. +It is instead better to avoid a complete filter on the transactions based on their order in the mempool: instead we are going to perform a simpler check and then let the block proposer rearrange them correctly when proposing the block. + +### In-protocol protection for InnerTx + +An alternative implementation could place the protection for the inner tx in protocol, just like the wrapper one, based on the transaction counter inside `SignedTxData`. The check would run in `process_proposal` and the update in `finalize_block`, just like for the wrapper transaction. This implementation, though, shows two drawbacks: - it implies the need for an hard fork in case of a modification of the replay protection mechanism - it's not clear who's the source of the inner transaction from the outside, as that depends on the specific code of the transaction itself. We could use specific whitelisted txs set to define when it requires a counter (would not work for future programmable transactions), but still, we have no way to define which address should be targeted for replay protection (**blocking issue**) -### VPs and Txs +### In-protocol counter increase for InnerTx + +In the [storage counter](#storage-counters) section we mentioned the issue of increasing the transaction counter for an inner tx even in case of failure. A possible solution that we took in consideration and discarded was to increase the counter from protocol in case of a failure. -To implement replay protection for the inner transaction we will need to update all the VPs checking the transaction's signature to include the check on the transaction counter: at the moment the `vp_user` validity predicate is the only one to update. +This is technically feasible since the protocol is aware of the keys modified by the transaction and also of the results of the validity predicates (useful in case the transaction updated more than one counter in storage). It is then possible to recover the value and reapply the change directly from protocol. This logic though, is quite dispersive, since it effectively splits the management of the counter for the `InnerTx` among Wasm and protocol, while our initial intent was to keep it completely in Wasm. -In addition, all the transactions involving `SignedTxData` should increment the counter. +### Single counter in storage -## Single counter +We can't use a single transaction counter in storage because this would prevent batching. -The mechanisms to prevent replay attacks for both transactions (wrapper and inner) will share the same `tx_counter` key in storage. +As an example, if a client (with a current counter in storage holding value 5) generates two transactions to be included in the same block, signing both the outer and the inner (default behavior of the client), it would need to generate the following transaction counters: -This could be an issue when the signer of the `WrapperTx` is the same of the `InnerTx` (default behaviour of the client): in this case, if both transactions expect the same counter in storage, the wrapper transaction will pass validation but the inner one will fail. To cope with this, the client will be responsible for producing a valid `WrapperTx` in which `tx_counter` will be set to the current value of the counter in storage, call it `storage_ctr`, while the inner transaction will have `storage_ctr + 1` in its data. +``` +[ + T1: (WrapperCtr: 5, InnerCtr: 6), + T2: (WrapperCtr: 7, InnerCtr: 8) +] +``` -## Storage Interface +Now, the current execution model of Namada includes the `WrapperTx` in a block first to then decrypt and execute the inner tx in the following block (respecting the committed order of the transactions). That would mean that the outer tx of `T1` would pass validation and immediately increase the counter to 6 to prevent a replay attack in the same block. Now, the outer tx of `T2` will be processed but it won't pass validation because it carries a counter with value 7 while the ledger expects 6. -Replay protection will require interaction with the storage from both the protocol and Wasm. To do so we can take advantage of the `StorageRead` and `StorageWrite` traits to work with a single interface. +To fix this, one could think to set the counters as follows: + +``` +[ + T1: (WrapperCtr: 5, InnerCtr: 7), + T2: (WrapperCtr: 6, InnerCtr: 8) +] +``` -## Batching +This way both the transactions will be considered valid and executed. The issue is that, if the second transaction is not included in the block (for any reason), than the first transaction (the only one remaining at this point) will fail. In fact, after the outer tx has correctly increased the counter in storage to value 6 the block will be accepted. In the next block the inner transaction will be decrypted and executed but this last step will fail since the counter in `SignedTxData` carries a value of 7 and the counter in storage has a value of 6. + +To cope with this there are two possible ways. The first one is that, instead of checking the exact value of the counter in storage and increasing its value by one, we could check that the transaction carries a counter `>=` than the one in storage and write this one (not increase) to storage. The problem with this is that it the lack of support for strict ordering of execution. + +The second option is to keep the usual increase strategy of the counter (increase by one and check for strict equality) and simply use two different counters in storage for each address. The transaction will then look like this: + +``` +[ + T1: (WrapperCtr: 5, InnerCtr: 5), + T2: (WrapperCtr: 6, InnerCtr: 6) +] +``` -The implementation proposed in this document doesn't support batching of multiple transactions from a same address in a single block. More specifically, the transactions will all succeed only if they are executed in the intended order, but the order in which transactions will be included in the block by the proposer is not guaranteed. An out of order execution of multiple transactions would lead to the failure of some of them (in the worst case, the failure of all of them but one, in the best case, the failure of only one). This problem will be amplified by the introduction of Ferveo for DKG which will be able to reorder transactions. +Since the order of inclusion of the `WrapperTxs` forces the same order of the execution for the inner ones, both transactions can be correctly executed and the correctness will be maintained even in case `T2` didn't make it to the block (note that the counter for an inner tx and the corresponding wrapper one don't need to coincide). -The Wasm implementation of replay protection can't cope with this problem because every wasm run (of either a transaction or a validity predicate) is only aware of its context, meaning the wasm bytecode and the serialized transaction data. The lack of awareness of the other transactions makes it impossible to develop a replay protection mechanism supporting batching in wasm. +### Block rejection -To address this issue there could be two ways: +The implementation proposed in this document has one flaw when it comes to discontinuous transactions. If, for example, for a given address, the counter in storage for the `WrapperTx` is 5 and the block proposer receives, in order, transactions 6, 5 and 8, the proposer will have an incentive to correctly order transactions 5 and 6 to gain the fees that he would otherwise lose. Transaction 8 will never be accepted by the validators no matter the ordering (since they will expect tx 7 which got lost): this effectively means that the block proposer has no incentive to include this transaction in the block because it would gain him no fees but, at the same time, he doesn't really have a disincentive to not include it, since in this case the validators will simply discard the invalid tx but accept the rest of the block granting the proposer his fees on all the other transactions. -- Keep the proposed replay protection in Wasm and implement a batching mechanism in both the client and the ledger to embed more than one transaction in a single `Tx` struct -- Implement replay protection in protocol for the inner transaction (as discolsed in section [InnerTx](#InnerTx)) +A similar scenario happens in the case of a single transaction that is not the expected one (e.g. tx 5 when 4 is expected), or for a different type of inconsistencies, like a wrong `ChainId` or an invalid signature. -Following the second option, the ledger would be able to analyze the validity of the counters, of all the transactions relating to a single address, against the value in storage at the beginning of the block. -Finally, it could increment the counter in storage a single time by the correct amount (given by the amount of transactions that were executed with success). As already mentioned, though, this implementation suffers from some serious issues and seems to not be feasible. +It is up to the block proposer then, whether to include or not these kinds of transactions: a malicious proposer could do so to spam the block without suffering any penalty. The lack of fees could be a strong enough measure to prevent proposers from applying this behavior, together with the fact that the only damage caused to the chain would be spamming the blocks. -The first option, instead, carries more benefits, since in addition to allowing batching, it's also more flexible. +If one wanted to completely prevent this scenario, the solution would be to reject the entire block: this way the proposer would have an incentive to behave correctly (by not including these transactions into the block) to gain the block fees. This would allow to shrink the size of the blocks in case of unfair block proposers but it would also cause the slow down of the block creation process, since after a block rejection a new Tendermint round has to be initiated. From 806c23d112eee39ac87fb7fe63e2c4b249d0e101 Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Tue, 18 Oct 2022 12:33:14 +0200 Subject: [PATCH 09/41] Updates replay protection specs with hash strategy --- .../src/base-ledger/replay-protection.md | 298 +++++++++++++++--- 1 file changed, 258 insertions(+), 40 deletions(-) diff --git a/documentation/specs/src/base-ledger/replay-protection.md b/documentation/specs/src/base-ledger/replay-protection.md index 5cfb5d36fa..7ebeb847f4 100644 --- a/documentation/specs/src/base-ledger/replay-protection.md +++ b/documentation/specs/src/base-ledger/replay-protection.md @@ -71,12 +71,127 @@ The underlying consensus engine, [Tendermint](https://github.com/tendermint/tend ## Implementation -Namada replay protection consists of three parts: the in-Wasm solution for `EncryptedTx` (also called the `InnerTx`), an in-protocol mechanism for `WrapperTx` and a way to mitigate replay attacks in case of a fork. +Namada replay protection consists of three parts: the hash-based solution for both `EncryptedTx` (also called the `InnerTx`) and `WrapperTx`, a way to mitigate replay attacks in case of a fork and a concept of a lifetime for the transactions. -### InnerTx +### Hash register The actual Wasm code and data for the transaction are encapsulated inside a struct `Tx`, which gets encrypted as an `EncryptedTx` and wrapped inside a `WrapperTx` (see the [relative](#encryption-authentication) section). This inner transaction must be protected from replay attacks because it carries the actual semantics of the state transition. Moreover, even if the wrapper transaction was protected from replay attacks, an attacker could extract the inner transaction, rewrap it, and replay it. Note that for this attack to work, the attacker will need to sign the outer transaction himself and pay gas and fees for that, but this could still cause much greater damage to the parties involved in the inner transaction. +`WrapperTx` is the only type of transaction currently accepted by the ledger. It must be protected from replay attacks because, if it wasn't, a malicious user could replay the transaction as is. Even if the inner transaction implemented replay protection or, for any reason, wasn't accepted, the signer of the wrapper would still pay for gas and fees, effectively suffering economic damage. + +To prevent the replay of both these transactions we will rely on a set of already processed transactions' digests that will be kept in storage. These digests will be computed on the **signed** transactions. To support this, we'll need a subspace in storage headed by a `ReplayProtection` internal address: + +``` +/$ReplayProtectionAddress/$tx0_hash: None +/$ReplayProtectionAddress/$tx1_hash: None +/$ReplayProtectionAddress/$tx2_hash: None +... +``` + +The hashes will form the last part of the path to allow for a fast storage lookup. + +The consistency of the storage subspace is of critical importance for the correct working of the replay protection mechanism. To protect it, a validity predicate will check that no changes to this subspace are applied by any wasm transaction, as those should only be available from protocol. + +Both in `mempool_validation` and `process_proposal` we will perform a check (together with others, see the [relative](#wrapper-checks) section) on both the digests against the storage to check that neither of the transactions has already been executed: if this doesn't hold, the `WrapperTx` will not be included into the mempool/block respectively. If both checks pass then the transaction is included in the block and executed. In the `finalize_block` function we will add the transaction's hash to storage to prevent re-executions. We will first add the hash of the wrapper transaction. After that, in the following block, we deserialize the inner transaction, check the correct order of the transactions in the block and execute the tx: if it runs out of gas then we'll avoid storing its hash to allow rewrapping and executing the transaction, otherwise we'll add the hash in storage (both in case of success or failure of the tx). + +### Forks + +In the case of a fork, the transaction hash is not enough to prevent replay attacks. Transactions, in fact, could still be replayed on the other branch as long as their format is kept unchanged and the counters in storage match. + +To mitigate this problem, transactions will need to carry a `ChainId` identifier to tie them to a specific fork. This field needs to be added to the `Tx` struct so that it applies to both `WrapperTx` and `EncryptedTx`: + +```rust +pub struct Tx { + pub code: Vec, + pub data: Option>, + pub timestamp: DateTimeUtc, + pub chain_id: ChainId +} +``` + +This new field will be signed just like the other ones and is therefore subject to the same guarantees explained in the [initial](#encryption-authentication) section. The validity of this identifier will be checked in `process_proposal` for both the outer and inner tx: if a transaction carries an unexpected chain id, it won't be applied, meaning that no modifications will be applied to storage. + +### Transaction lifetime + +In general, a transaction is valid at the moment of submission, but after that, a series of external factors (ledger state, etc.) might change the mind of the submitter who's now not interested in the execution of the transaction anymore. + +We have to introduce the concept of a lifetime (or timeout) for the transactions: basically, the `Tx` struct will hold an extra field called `expiration` stating the maximum block height up until which the submitter is willing to see the transaction executed. After the specified block height, the transaction will be considered invalid and discarded regardless of all the other checks. + +By introducing this new field we are setting a new constraint in the transaction's contract, where the ledger will make sure to prevent the execution of the transaction after the deadline and, on the other side, the submitter commits himself to the result of the execution at least until its expiration. If the expiration is reached and the transaction has not been executed the submitter can decide to submit a new transaction if he's still interested in the changes carried by it. + +In our design, the `expiration` will hold until the transaction is executed: once it's executed, either in case of success or failure, the tx hash will be written to storage and the transaction will not be replayable. In essence, the transaction submitter commits himself to one of these three conditions: + +- Transaction is invalid regardless of the specific state +- Transaction is executed (either with success or not) and the transaction hash is saved in the storage +- Expiration block height has passed + +The first condition satisfied will invalidate further executions of the same tx. + +In anticipation of DKG implementation, the current struct `WrapperTx` holds a field `epoch` stating the epoch in which the tx should be executed. This is because Ferveo will produce a new public key each epoch, effectively limiting the lifetime of the transaction (see section 2.2.2 of the [documentation](https://eprint.iacr.org/2022/898.pdf)). Unfortunately, for replay protection, a resolution of 1 epoch (~ 1 day) is too low for the possible needs of the submitters, therefore we need the `expiration` field to hold a maximum `BlockHeight` to increase resolution down to a single block (~ 10 seconds). + +```rust +pub struct Tx { + pub code: Vec, + pub data: Option>, + pub timestamp: DateTimeUtc, + pub chain_id: ChainId, + /// Lifetime of the transaction, also determines which decryption key will be used + pub expiration: BlockHeight, +} + +pub struct WrapperTx { + /// The fee to be payed for including the tx + pub fee: Fee, + /// Used to determine an implicit account of the fee payer + pub pk: common::PublicKey, + /// Max amount of gas that can be used when executing the inner tx + pub gas_limit: GasLimit, + /// the encrypted payload + pub inner_tx: EncryptedTx, + /// sha-2 hash of the inner transaction acting as a commitment + /// the contents of the encrypted payload + pub tx_hash: Hash, +} +``` + +Since we now have more detailed information about the desired lifetime of the transaction, we can remove the `epoch` field and rely solely on `expiration`. Now, the producer of the inner transaction should make sure to set a sensible value for this field, in the sense that it should not span more than one epoch. If this happens, then the transaction will be correctly decrypted only in a subset of the desired lifetime (the one expecting the actual key used for the encryption), while, in the following epochs, the transaction will fail decryption and won't be executed. In essence, the `expiration` parameter can only restrict the implicit lifetime within the current epoch, it can not surpass it as that would make the transaction fail in the decryption phase. + +The subject encrypting the inner transaction will also be responsible for using the appropriate public key for encryption relative to the targeted block height. + +The wrapper transaction will match the `expiration` of the inner for correct execution. Note that we need this field also for the wrapper to anticipate the check at mempool/proposal evaluation time, but also to prevent someone from inserting a wrapper transaction after the corresponding inner has expired forcing the wrapper signer to pay for the fees. + +### Wrapper checks + +In `mempool_validation` and `process_proposal` we will perform some checks on the wrapper tx to validate it. These will involve: + +- Valid signature +- Enough funds to pay the fee +- Valid chainId +- Valid transaction hash +- Valid expiration + +These checks can all be done before executing the transactions themselves (the check on the gas cannot be done ahead of time). If any of these fails, the transaction should be considered invalid and the action to take will be one of the followings: + +1. If the checks fail on the signature, chainId, expiration or transaction hash, then this transaction will be forever invalid, regardless of the possible evolution of the ledger's state. There's no need to include the transaction in the block. Moreover, we **cannot** include this transaction in the block to charge a fee (as a sort of punishment) because these errors may not depend on the signer of the tx (could be due to malicious users or simply a delay in the tx inclusion in the block) +2. If the checks fail _only_ because of an insufficient balance, the wrapper should be kept in mempool for a future play in case the funds should become available +3. If all the checks pass validation we will include the transaction in the block to store the hash and charge the fee + +The `expiration` parameter also justifies step 2 of the previous bullet points which states that if the validity checks fail only because of an insufficient balance to pay for fees then the transaction should be kept in mempool for future execution. Without it, the transaction could be potentially executed at any future moment, possibly going against the mutated interests of the submitter. With the expiration parameter, now, the submitter commits himself to accept the execution of the transaction up to the specified block height: it's going to be his responsibility to provide a sensible value for this parameter. Given this constraint the transaction will be kept in memepool up until the expiration (since it would become invalid after that in any case), to prevent the mempool from increasing too much in size. + +This mechanism can also be applied to another scenario. Suppose a transaction was not propagated to the network by a node (or a group of colluding nodes). Now, this tx might be valid, but it doesn't get inserted into a block. Without an expiration, this tx can be replayed (better, applied, since it was never executed in the first place) at a future moment in time when the submitter might not be willing to execute it anymore. + +## Possible optimizations + +In this section we describe two alternative solutions that come with some optimizations. + +### Transaction counter + +Instead of relying on a hash (32 bytes) we could use a 64 bits (8 bytes) transaction counter as nonce for the wrapper and inner transactions. The advantage is that it would only use 25% of the storage space required by the proposed solution. On the other hand, the handling of the counter for the inner transaction will be performed entirely in wasm (transactions and VPs) making it a bit less efficient. + +**NOTE**: this solution requires the ability to [yield](https://github.com/wasmerio/wasmer/issues/1127) execution from Wasmer which is not implemented yet. + +#### InnerTx + We will implement the protection entirely in Wasm: the check of the counter will be carried out by the validity predicates while the actual writing of the counter in storage will be done by the transactions themselves. To do so, the `SignedTxData` attached to the transaction will hold the current value of the counter in storage: @@ -105,13 +220,11 @@ The wasm transaction will simply read the value from storage and increase its va The VP of the _source_ address will then check the validity of the signature and, if it's deemed valid, will proceed to check if the pre-value of the counter in storage was equal to the one contained in the `SignedTxData` struct and if the post-value of the key in storage has been incremented by one: if any of these conditions doesn't hold the VP will discard the transactions and prevent the changes from being applied to the storage. -In the specific case of a transfer, since MASP already comes with replay protection as part of the Zcash design (see the [MASP specs](https://specs.namada.net/masp.html) and [Zcash protocol specs](https://zips.z.cash/protocol/protocol.pdf)), the counter in `SignedTxData` is not required and therefore should be optional. +In the specific case of a shielded transfer, since MASP already comes with replay protection as part of the Zcash design (see the [MASP specs](https://specs.namada.net/masp.html) and [Zcash protocol specs](https://zips.z.cash/protocol/protocol.pdf)), the counter in `SignedTxData` is not required and therefore should be optional. To implement replay protection for the inner transaction we will need to update all the VPs checking the transaction's signature to include the check on the transaction counter: at the moment the `vp_user` validity predicate is the only one to update. In addition, all the transactions involving `SignedTxData` should increment the counter. -### WrapperTx - -`WrapperTx` is the only type of transaction currently accepted by the ledger. It must be protected from replay attacks because, if it wasn't, a malicious user could replay the transaction as is. Even if the inner transaction implemented replay protection (as explained in the [previous](#innertx) section) or, for any reason, wasn't accepted, the signer of the wrapper would still pay for gas and fees, effectively suffering economic damage. +#### WrapperTx To protect this transaction we can implement an in-protocol mechanism. Since the wrapper transaction gets signed before being submitted to the network, we can leverage the `tx_counter` field of the `SignedTxData` already introduced for the inner tx. @@ -132,33 +245,15 @@ At last, in `finalize_block`, the ledger will update the counter key in storage, Now, if a malicious user tried to replay this transaction, the `tx_counter` in the struct would no longer be equal to the one in storage and the transaction would be deemed invalid. -### Forks - -In the case of a fork, the transaction counters are not enough to prevent replay attacks. Transactions, in fact, could still be replayed on the other branch as long as their format is kept unchanged and the counters in storage match. +#### Implementation details -To mitigate this problem, transactions will need to carry a `ChainId` identifier to tie them to a specific fork. This field needs to be added to the `Tx` struct so that it applies to both `WrapperTx` and `EncryptedTx` (for the same reason explained in [InnerTx](#InnerTx) about the double transaction counter): +In this section we'll talk about some details of the replay protection mechanism that derive from the solution proposed in this section. -```rust -pub struct Tx { - pub code: Vec, - pub data: Option>, - pub timestamp: DateTimeUtc, - pub chain_id: ChainId -} -``` - -This new field will be signed just like the other ones and is therefore subject to the same guarantees explained in the [initial](#encrypted-authenticated-fixme-better-name-for-this-section) section. -The validity of this identifier will be checked in `process_proposal` for both the outer and inner tx: if a transaction carries an unexpected chain id, it won't be applied, meaning that the counter in storage won't be updated and no other modifications will be applied to storage. - -## Implementation details - -In this section we'll talk about some details of the replay protection mechanism that derive from the solution proposed in the previous section. - -### Storage counters +##### Storage counters Replay protection will require interaction with the storage from both the protocol and Wasm. To do so we can take advantage of the `StorageRead` and `StorageWrite` traits to work with a single interface. -The proposed implementation requires two transaction counters in storage for every address, so that the storage subspace of a given address looks like the following: +This implementation requires two transaction counters in storage for every address, so that the storage subspace of a given address looks like the following: ``` /$Address/wrapper_tx_counter: u64 @@ -167,7 +262,7 @@ The proposed implementation requires two transaction counters in storage for eve An implementation requiring a single counter in storage has been taken into consideration and discarded because that would not support batching; see the [relative section](#single-counter-in-storage) for a more in-depth explanation. -For both the wrapper and inner transaction, the increase of the counter in storage is an important step that must be correctly executed. More specifically, we want to increase the counter as soon as we verify that the signature, the chain id and the passed-in transaction counter are valid. The increase should happen immediately after the checks because of two reasons: +For both the wrapper and inner transaction, the increase of the counter in storage is an important step that must be correctly executed. First, the implementation will `panic!` in case of a counter overflow to prevent wrapping, since this would allow for the replay of previous transactions. Also, we want to increase the counter as soon as we verify that the signature, the chain id and the passed-in transaction counter are valid. The increase should happen immediately after the checks because of two reasons: - Prevent replay attack of a transaction in the same block - Update the transaction counter even in case the transaction fails, to prevent a possible replay attack in the future (since a transaction invalid at state Sx could become valid at state Sn where `n > x`) @@ -178,11 +273,13 @@ For `InnerTx`, instead, the logic is not straightforward. The transaction code w The ideal solution would be to interrupt the execution of the Wasm code after the transaction counter (if any) has been increased. This would allow performing a first run of the involved VPs and, if all of them accept the changes, let the protocol commit these changes before any possible failure. After that, the protocol would resume the execution of the transaction from the previous interrupt point until completion or failure, after which a second pass of the VPs is initiated to validate the remaining state modifications. In case of a VP rejection after the counter increase there would be no need to resume execution and the transaction could be immediately deemed invalid so that the protocol could skip to the next tx to be executed. With this solution, the counter update would be committed to storage regardless of a failure of the transaction itself. -Unfortunately, at the moment, Wasmer doesn't allow [yielding](https://github.com/wasmerio/wasmer/issues/1127) from the execution. For now, the responsibility will be up to the user to provide a valid inner transaction, and, in case of an invalid one, to take actions to prevent a possible replay attack: in essence, the user will be required to submit a new valid transaction to invalidate the counter of the previous one. +Unfortunately, at the moment, Wasmer doesn't allow [yielding](https://github.com/wasmerio/wasmer/issues/1127) from the execution. -### Batching and transaction ordering +In case the transaction went out of gas (given the `gas_limit` field of the wrapper), all the changes applied will be discarded from the WAL and will not affect the state of the storage. The inner transaction could then be rewrapped with a correct gas limit and replayed until the `expiration` block has been reached. -The proposed replay protection technique supports the execution of multiple transactions with the same address as _source_ in a single block. Actually, the presence of the transaction counters and the checks performed on them now impose a strict ordering on the execution sequence (which can be an added value for some use cases). The correct execution of more than one transaction per source address in the same block is preserved as long as: +##### Batching and transaction ordering + +This replay protection technique supports the execution of multiple transactions with the same address as _source_ in a single block. Actually, the presence of the transaction counters and the checks performed on them now impose a strict ordering on the execution sequence (which can be an added value for some use cases). The correct execution of more than one transaction per source address in the same block is preserved as long as: 1. The wrapper transactions are inserted in the block with the correct ascending order 2. No hole is present in the counters' sequence @@ -196,7 +293,7 @@ Correctly ordering the transactions is not enough to guarantee the correct execu The entire counter ordering is only done on the `WrapperTx`: if the inner counter is wrong then the inner transaction will fail and the signer of the corresponding wrapper will be charged with fees. This incentivizes submitters to produce valid transactions and discourages malicious user from rewrapping and resubmitting old transactions. -### Mempool checks +##### Mempool checks As a form of optimization to prevent mempool spamming, some of the checks that have been introduced in this document will also be brought to the `mempool_validate` function. Of course, we always refer to checks on the `WrapperTx` only. More specifically: @@ -206,11 +303,11 @@ As a form of optimization to prevent mempool spamming, some of the checks that h Regarding the last point, `mempool_validate` will check if the counter in the transaction is `>=` than the one in storage for the address signing the `WrapperTx`. A complete check (checking for strict equality) is not feasible, as described in the [relative](#mempool-counter-validation) section. -## Alternatives considered +#### Alternatives considered -In this last section we list some possible solutions that were taken into consideration during the writing of this spec but were eventually discarded. +In this section we list some possible solutions that were taken into consideration during the writing of this solution but were eventually discarded. -### Mempool counter validation +##### Mempool counter validation The idea of performing a complete validation of the transaction counters in the `mempool_validate` function was discarded because of a possible flaw. @@ -222,20 +319,20 @@ Suppose a client sends five transactions (counters from 1 to 5). The mempool of Finally, transactions that are not allowed into the mempool don't get propagated to the other peers, making their inclusion in a block even harder. It is instead better to avoid a complete filter on the transactions based on their order in the mempool: instead we are going to perform a simpler check and then let the block proposer rearrange them correctly when proposing the block. -### In-protocol protection for InnerTx +##### In-protocol protection for InnerTx An alternative implementation could place the protection for the inner tx in protocol, just like the wrapper one, based on the transaction counter inside `SignedTxData`. The check would run in `process_proposal` and the update in `finalize_block`, just like for the wrapper transaction. This implementation, though, shows two drawbacks: - it implies the need for an hard fork in case of a modification of the replay protection mechanism - it's not clear who's the source of the inner transaction from the outside, as that depends on the specific code of the transaction itself. We could use specific whitelisted txs set to define when it requires a counter (would not work for future programmable transactions), but still, we have no way to define which address should be targeted for replay protection (**blocking issue**) -### In-protocol counter increase for InnerTx +##### In-protocol counter increase for InnerTx In the [storage counter](#storage-counters) section we mentioned the issue of increasing the transaction counter for an inner tx even in case of failure. A possible solution that we took in consideration and discarded was to increase the counter from protocol in case of a failure. This is technically feasible since the protocol is aware of the keys modified by the transaction and also of the results of the validity predicates (useful in case the transaction updated more than one counter in storage). It is then possible to recover the value and reapply the change directly from protocol. This logic though, is quite dispersive, since it effectively splits the management of the counter for the `InnerTx` among Wasm and protocol, while our initial intent was to keep it completely in Wasm. -### Single counter in storage +##### Single counter in storage We can't use a single transaction counter in storage because this would prevent batching. @@ -274,7 +371,7 @@ The second option is to keep the usual increase strategy of the counter (increas Since the order of inclusion of the `WrapperTxs` forces the same order of the execution for the inner ones, both transactions can be correctly executed and the correctness will be maintained even in case `T2` didn't make it to the block (note that the counter for an inner tx and the corresponding wrapper one don't need to coincide). -### Block rejection +##### Block rejection The implementation proposed in this document has one flaw when it comes to discontinuous transactions. If, for example, for a given address, the counter in storage for the `WrapperTx` is 5 and the block proposer receives, in order, transactions 6, 5 and 8, the proposer will have an incentive to correctly order transactions 5 and 6 to gain the fees that he would otherwise lose. Transaction 8 will never be accepted by the validators no matter the ordering (since they will expect tx 7 which got lost): this effectively means that the block proposer has no incentive to include this transaction in the block because it would gain him no fees but, at the same time, he doesn't really have a disincentive to not include it, since in this case the validators will simply discard the invalid tx but accept the rest of the block granting the proposer his fees on all the other transactions. @@ -283,3 +380,124 @@ A similar scenario happens in the case of a single transaction that is not the e It is up to the block proposer then, whether to include or not these kinds of transactions: a malicious proposer could do so to spam the block without suffering any penalty. The lack of fees could be a strong enough measure to prevent proposers from applying this behavior, together with the fact that the only damage caused to the chain would be spamming the blocks. If one wanted to completely prevent this scenario, the solution would be to reject the entire block: this way the proposer would have an incentive to behave correctly (by not including these transactions into the block) to gain the block fees. This would allow to shrink the size of the blocks in case of unfair block proposers but it would also cause the slow down of the block creation process, since after a block rejection a new Tendermint round has to be initiated. + +### Wrapper-bound InnerTx + +The solution is to tie an `InnerTx` to the corresponding `WrapperTx`. By doing so, it becomes impossible to rewrap an inner transaction and, therefore, all the attacks related to this practice would be unfeasible. This mechanism requires eight times less space in storage (only a 64 bit counter for the wrapper tx) and only one check on the wrapper counter in protocol. As a con, it requires communication between the signer of the inner transaction and that of the wrapper during the transaction construction. + +To do so we will have to change the current definition of the two tx structs to the following: + +```rust +pub struct WrapperTx { + /// The fee to be payed for including the tx + pub fee: Fee, + /// Used to determine an implicit account of the fee payer + pub pk: common::PublicKey, + /// Max amount of gas that can be used when executing the inner tx + pub gas_limit: GasLimit, + /// Lifetime of the transaction, also determines which decryption key will be used + pub expiration: BlockHeight, + /// Chain identifier for replay protection + pub chain_id: ChainId, + /// Transaction counter for replay protection + pub tx_counter: u64, + /// the encrypted payload + pub inner_tx: EncryptedTx, +} + +pub struct Tx { + pub code: Vec, + pub data: Option>, + pub timestamp: DateTimeUtc, + pub wrapper_commit: Option, +} +``` + +The Wrapper transaction no longer holds the inner transaction hash while the inner one now holds a commit to the corresponding wrapper tx in the form of the hash of a `WrapperCommit` struct, defined as: + +```rust +pub struct WrapperCommit { + pub pk: common::PublicKey, + pub tx_counter: u64, + pub expiration: BlockHeight, + pub chain_id: ChainId, +} +``` + +The `pk-tx_counter` couple contained in this struct, uniquely identifies a single `WrapperTx` (since a valid tx_counter is unique given the address) so that the inner one is now bound to this specific wrapper. The remaining fields, `expiration` and `chain_id`, will tie these two values given their importance in terms of safety (see the [relative](#wrappertx-checks) section). Note that the `wrapper_commit` field must be optional because the `WrapperTx` struct itself gets converted to a `Tx` struct before submission but it doesn't need any commitment. + +Both the inner and wrapper tx get signed on their hash, as usual, to prevent tampering with data. When a wrapper gets processed by the ledger, we first check the validity of the signature, checking that none of the fields were modified: this means that the inner tx embedded within the wrapper is, in fact, the intended one. This last statement means that no external attacker has tampered data, but the tampering could still have been performed by the signer of the wrapper before signing the wrapper transaction. + +If this check (and others, explained later in the [checks](#wrappertx-checks) section) passes, then the inner tx gets decrypted in the following block proposal process. At this time we check that the order in which the inner txs are inserted in the block matches that of the corresponding wrapper txs in the previous block. To do so, we rely on an in-storage queue holding the hash of the `WrapperCommit` struct computed from the wrapper tx. From the inner tx we extract the `WrapperCommit` hash and check that it matches that in the queue: if they don't it means that the inner tx has been reordered or rewrapped and we reject the block. Note that, since we have already checked the wrapper at this point, the only way to rewrap the inner tx would be to also modify its commitment (need to change at least the `tx_counter` field), otherwise the checks on the wrapper would have spotted the inconsistency and rejected the tx. + +If this check passes then we can send the inner transaction to the wasm environment for execution: if the transaction is signed, then at least one VP will check its signature to spot possible tampering of the data (especially by the wrapper signer, since this specific case cannot be checked before this step) and, if this is the case, will reject this transaction and no storage modifications will be applied. + +In summary: + +- The `InnerTx` carries a unique identifier of the `WrapperTx` embedding it +- Both the inner and wrapper txs are signed on all of their data +- The signature check on the wrapper tx ensures that the inner transaction is the intended one and that this wrapper has not been used to wrap a different inner tx. It also verifies that no tampering happened with the inner transaction by a third party. Finally, it ensures that the public key is the one of the signer +- The check on the `WrapperCommit` ensures that the inner tx has not been reordered nor rewrapped (this last one is a non-exhaustive check, inner tx data could have been tampered with by the wrapper signer) +- The signature check of the inner tx performed in Vp grants that no data of the inner tx has been tampered with, effectively verifying the correctness of the previous check (`WrapperCommit`) + +This sequence of controls makes it no longer possible to rewrap an `InnerTx` which is now bound to its wrapper. This implies that replay protection is only needed on the `WrapperTx` since there's no way to extract the inner one, rewrap it and replay it. + +#### WrapperTx checks + +In `mempool_validation` and `process_proposal` we will perform some checks on the wrapper tx to validate it. These will involve: + +- Valid signature +- Enough funds to pay for the fee +- Valid chainId +- Valid transaction counter +- Valid expiration + +These checks can all be done before executing the transactions themselves. The check on the gas cannot be done ahead of time and we'll deal with it later. If any of these fails, the transaction should be considered invalid and the action to take will be one of the followings: + +1. If the checks fail on the signature, chainId, expiration or transaction counter, then this transaction will be forever invalid, regardless of the possible evolution of the ledger's state. There's no need to include the transaction in the block nor to increase the transaction counter. Moreover, we **cannot** include this transaction in the block to charge a fee (as a sort of punishment) because these errors may not depend on the signer of the tx (could be due to malicious users or simply a delay in the tx inclusion in the block) +2. If the checks fail _only_ because of an insufficient balance, the wrapper should be kept in mempool for a future play in case the funds should become available +3. If all the checks pass validation we will include the transaction in the block to increase the counter and charge the fee + +Note that, regarding point one, there's a distinction to be made about an invalid `tx_counter` which could be invalid because of being old or being in advance. To solve this last issue (counter greater than the expected one), we have to introduce the concept of a lifetime (or timeout) for the transactions: basically, the `WrapperTx` will hold an extra field called `expiration` stating the maximum block height up until which the submitter is willing to see the transaction executed. After the specified block height the transaction will be considered invalid and discarded regardless of all the other checks. This way, in case of a transaction with a counter greater than expected, it is sufficient to wait till after the expiration to submit more transactions, so that the counter in storage is not modified (kept invalid for the transaction under observation) and replaying that tx would result in a rejection. + +This actually generalizes to a more broad concept. In general, a transaction is valid at the moment of submission, but after that, a series of external factors (ledger state, etc.) might change the mind of the submitter who's now not interested in the execution of the transaction anymore. By introducing this new field we are introducing a new constraint in the transaction's contract, where the ledger will make sure to prevent the execution of the transaction after the deadline and, on the other side, the submitter commits himself to the result of the execution at least until its expiration. If the expiration is reached and the transaction has not been executed the submitter can decide to submit a new, identical transaction if he's still interested in the changes carried by it. + +In our design, the `expiration` will hold until the transaction is executed, once it's executed, either in case of success or failure, the `tx_counter` will be increased and the transaction will not be replayable. In essence, the transaction submitter commits himself to one of these three conditions: + +- Transaction is invalid regardless of the specific state +- Transaction is executed (either with success or not) and the transaction counter is increased +- Expiration block height has passed + +The first condition satisfied will invalidate further executions of the same tx. + +The `expiration` parameter also justifies step 2 of the previous bullet points which states that if the validity checks fail only because of an insufficient balance to pay for fees than the transaction should be kept in mempool for a future execution. Without it, the transaction could be potentially executed at any future moment (provided that the counter is still valid), possibily going against the mutated interests of the submitter. With the expiration parameter, now, the submitter commits himself to accepting the execution of the transaction up to the specified block height: it's going to be his responsibility to provide a sensible value for this parameter. Given this constraint the transaction will be kept in memepool up until the expiration (since it would become invalid after that in any case), to prevent the mempool from increasing too much in size. + +This mechanism can also be applied to another scenario. Suppose a transaction was not propagated to the network by a node (or a group of colluding nodes). Now, this tx might be valid, but it doesn't get inserted into a block. Without an expiration, if the submitter doesn't submit any other transaction (which gets included in a block to increase the transaction counter), this tx can be replayed (better, applied, since it was never executed in the first place) at a future moment in time when the submitter might not be willing to execute it any more. + +Since the signer of the wrapper may be different from the one of the inner we also need to include this `expiration` field in the `WrapperCommit` struct, to prevent the signer of the wrapper from setting a lifetime which is in conflict with the interests of the inner signer. Note that adding a separate lifetime for the wrapper alone (which would require two separate checks) doesn't carry any benefit: a wrapper with a lifetime greater than the inner would have no sense since the inner would fail. Restricting the lifetime would work but it also means that the wrapper could prevent a valid inner transaction from being executed. We will then keep a single `expiration` field specifying the wrapper tx max block height (the inner one will actually be executed one block later because of the execution mechanism of Namada). + +To prevent the signer of the wrapper from submitting the transaction to a different chain, the `ChainId` field should also be included in the commit. + +Finally, in case the transaction run out of gas (based on the provided `gas_limit` field of the wrapper) we don't need to take any action: by this time the transaction counter will have already been incremented and the tx is not replayable anymore. In theory, we don't even need to increment the counter since the only way this transaction could become valid is a change in the way gas is accounted, which might require a fork anyway, and consequently a change in the required `ChainId`. However, since we can't tell the gas consumption before the inner tx has been executed, we cannot anticipate this check. + +#### WrapperCommit + +The fields of `WrapperTx` not included in `WrapperCommit` are at the discretion of the `WrapperTx` producer. These fields are not included in the commit because of one of these two reasons: + +- They depend on the specific state of the wrapper signer and cannot be forced (like `fee`, since the wrapper signer must have enough funds to pay for those) +- They are not a threat (in terms of replay attacks) to the signer of the inner transaction in case of failure of the transaction + +In a certain way, the `WrapperCommit` not only binds an `InnerTx` no a wrapper, but effectively allows the inner to control the wrapper by requesting some specific parameters for its creation and bind these parameters among the two transactions: this allows us to apply the same constraints to both txs while performing the checks on the wrapper only. + +#### Transaction creation process + +To craft a transaction, the process will now be the following (optional steps are only required if the signer of the inner differs from that of the wrapper): + +- (**Optional**) the `InnerTx` constructor request, to the wrapper signer, his public key and the `tx_counter` to be used +- The `InnerTx` is constructed in its entirety with also the `wrapper_commit` field to define the constraints of the future wrapper +- The produced `Tx` struct get signed over all of its data (with `SignedTxData`) producing a new struct `Tx` +- (**Optional**) The inner tx produced is sent to the `WrapperTx` producer together with the `WrapperCommit` struct (required since the inner tx only holds the hash of it) +- The signer of the wrapper constructs a `WrapperTx` compliant with the `WrapperCommit` fields +- The produced `WrapperTx` gets signed over all of its fields + +Compared to a solution not binding the inner tx to the wrapper one, this solution requires the exchange of 3 messages (request `tx_counter`, receive `tx_counter`, send `InnerTx`) between the two signers (in case they differ), instead of one. However, it allows the signer of the inner to send the `InnerTx` to the wrapper signer already encrypted, guaranteeing a higher level of safety: only the `WrapperCommit` struct should be sent clear, but this doesn't reveal any sensitive information about the inner transaction itself. From 3fd7a3a13833708262f84c12d7bf84add6a55fcb Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Tue, 18 Oct 2022 13:07:21 +0200 Subject: [PATCH 10/41] Removes panic --- documentation/specs/src/base-ledger/replay-protection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/specs/src/base-ledger/replay-protection.md b/documentation/specs/src/base-ledger/replay-protection.md index 7ebeb847f4..b2fb6e7f81 100644 --- a/documentation/specs/src/base-ledger/replay-protection.md +++ b/documentation/specs/src/base-ledger/replay-protection.md @@ -262,7 +262,7 @@ This implementation requires two transaction counters in storage for every addre An implementation requiring a single counter in storage has been taken into consideration and discarded because that would not support batching; see the [relative section](#single-counter-in-storage) for a more in-depth explanation. -For both the wrapper and inner transaction, the increase of the counter in storage is an important step that must be correctly executed. First, the implementation will `panic!` in case of a counter overflow to prevent wrapping, since this would allow for the replay of previous transactions. Also, we want to increase the counter as soon as we verify that the signature, the chain id and the passed-in transaction counter are valid. The increase should happen immediately after the checks because of two reasons: +For both the wrapper and inner transaction, the increase of the counter in storage is an important step that must be correctly executed. First, the implementation will return an error in case of a counter overflow to prevent wrapping, since this would allow for the replay of previous transactions. Also, we want to increase the counter as soon as we verify that the signature, the chain id and the passed-in transaction counter are valid. The increase should happen immediately after the checks because of two reasons: - Prevent replay attack of a transaction in the same block - Update the transaction counter even in case the transaction fails, to prevent a possible replay attack in the future (since a transaction invalid at state Sx could become valid at state Sn where `n > x`) From aba665bc81a4af69c77ed210b4e2d1950aca2e33 Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Tue, 18 Oct 2022 14:24:43 +0200 Subject: [PATCH 11/41] Mentions strict ordering of txs --- documentation/specs/src/base-ledger/replay-protection.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/specs/src/base-ledger/replay-protection.md b/documentation/specs/src/base-ledger/replay-protection.md index b2fb6e7f81..ebfcda3f83 100644 --- a/documentation/specs/src/base-ledger/replay-protection.md +++ b/documentation/specs/src/base-ledger/replay-protection.md @@ -186,7 +186,7 @@ In this section we describe two alternative solutions that come with some optimi ### Transaction counter -Instead of relying on a hash (32 bytes) we could use a 64 bits (8 bytes) transaction counter as nonce for the wrapper and inner transactions. The advantage is that it would only use 25% of the storage space required by the proposed solution. On the other hand, the handling of the counter for the inner transaction will be performed entirely in wasm (transactions and VPs) making it a bit less efficient. +Instead of relying on a hash (32 bytes) we could use a 64 bits (8 bytes) transaction counter as nonce for the wrapper and inner transactions. The advantage is that it would only use 25% of the storage space required by the proposed solution. On the other hand, the handling of the counter for the inner transaction will be performed entirely in wasm (transactions and VPs) making it a bit less efficient. This solution also imposes a strict ordering on the transactions issued by a same address. **NOTE**: this solution requires the ability to [yield](https://github.com/wasmerio/wasmer/issues/1127) execution from Wasmer which is not implemented yet. @@ -383,7 +383,7 @@ If one wanted to completely prevent this scenario, the solution would be to reje ### Wrapper-bound InnerTx -The solution is to tie an `InnerTx` to the corresponding `WrapperTx`. By doing so, it becomes impossible to rewrap an inner transaction and, therefore, all the attacks related to this practice would be unfeasible. This mechanism requires eight times less space in storage (only a 64 bit counter for the wrapper tx) and only one check on the wrapper counter in protocol. As a con, it requires communication between the signer of the inner transaction and that of the wrapper during the transaction construction. +The solution is to tie an `InnerTx` to the corresponding `WrapperTx`. By doing so, it becomes impossible to rewrap an inner transaction and, therefore, all the attacks related to this practice would be unfeasible. This mechanism requires eight times less space in storage (only a 64 bit counter for the wrapper tx) and only one check on the wrapper counter in protocol. As a con, it requires communication between the signer of the inner transaction and that of the wrapper during the transaction construction. This solution also imposes a strict ordering on the wrapper transactions issued by a same address. To do so we will have to change the current definition of the two tx structs to the following: From 72e696cb1f719be72ddcc1c98930da2f4555fdac Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Tue, 18 Oct 2022 16:52:28 +0200 Subject: [PATCH 12/41] Fixes optimizations stats --- documentation/specs/src/base-ledger/replay-protection.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/specs/src/base-ledger/replay-protection.md b/documentation/specs/src/base-ledger/replay-protection.md index ebfcda3f83..dae2cce3ff 100644 --- a/documentation/specs/src/base-ledger/replay-protection.md +++ b/documentation/specs/src/base-ledger/replay-protection.md @@ -186,7 +186,7 @@ In this section we describe two alternative solutions that come with some optimi ### Transaction counter -Instead of relying on a hash (32 bytes) we could use a 64 bits (8 bytes) transaction counter as nonce for the wrapper and inner transactions. The advantage is that it would only use 25% of the storage space required by the proposed solution. On the other hand, the handling of the counter for the inner transaction will be performed entirely in wasm (transactions and VPs) making it a bit less efficient. This solution also imposes a strict ordering on the transactions issued by a same address. +Instead of relying on a hash (32 bytes) we could use a 64 bits (8 bytes) transaction counter as nonce for the wrapper and inner transactions. The advantage is that the space required would be much less since we only need two 8 bytes values in storage for every address which is signing transactions. On the other hand, the handling of the counter for the inner transaction will be performed entirely in wasm (transactions and VPs) making it a bit less efficient. This solution also imposes a strict ordering on the transactions issued by a same address. **NOTE**: this solution requires the ability to [yield](https://github.com/wasmerio/wasmer/issues/1127) execution from Wasmer which is not implemented yet. @@ -383,7 +383,7 @@ If one wanted to completely prevent this scenario, the solution would be to reje ### Wrapper-bound InnerTx -The solution is to tie an `InnerTx` to the corresponding `WrapperTx`. By doing so, it becomes impossible to rewrap an inner transaction and, therefore, all the attacks related to this practice would be unfeasible. This mechanism requires eight times less space in storage (only a 64 bit counter for the wrapper tx) and only one check on the wrapper counter in protocol. As a con, it requires communication between the signer of the inner transaction and that of the wrapper during the transaction construction. This solution also imposes a strict ordering on the wrapper transactions issued by a same address. +The solution is to tie an `InnerTx` to the corresponding `WrapperTx`. By doing so, it becomes impossible to rewrap an inner transaction and, therefore, all the attacks related to this practice would be unfeasible. This mechanism requires even less space in storage (only a 64 bit counter for every address signing wrapper transactions) and only one check on the wrapper counter in protocol. As a con, it requires communication between the signer of the inner transaction and that of the wrapper during the transaction construction. This solution also imposes a strict ordering on the wrapper transactions issued by a same address. To do so we will have to change the current definition of the two tx structs to the following: From 901c05c6739878a290e2ae21b6d2ae3c8d72a8f8 Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Thu, 20 Oct 2022 17:29:24 +0200 Subject: [PATCH 13/41] Fixes internal docs references --- documentation/specs/src/base-ledger/replay-protection.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/specs/src/base-ledger/replay-protection.md b/documentation/specs/src/base-ledger/replay-protection.md index dae2cce3ff..28aa98bc72 100644 --- a/documentation/specs/src/base-ledger/replay-protection.md +++ b/documentation/specs/src/base-ledger/replay-protection.md @@ -63,7 +63,7 @@ In the execution steps: 4. After the execution, the affected validity predicates (also mentioned as VP in this document) will check the storage changes and (if relevant) the signature of the transaction: if the signature is not valid, the VP will deem the transaction invalid and the changes won't be applied to the storage The signature checks effectively prevent any tampering with the transaction data because that would cause the checks to fail and the transaction to be rejected. -For a more in-depth view, please refer to the [Namada execution spec](https://specs.namada.net/base-ledger/execution.html). +For a more in-depth view, please refer to the [Namada execution spec](./execution.md). ### Tendermint replay protection @@ -220,7 +220,7 @@ The wasm transaction will simply read the value from storage and increase its va The VP of the _source_ address will then check the validity of the signature and, if it's deemed valid, will proceed to check if the pre-value of the counter in storage was equal to the one contained in the `SignedTxData` struct and if the post-value of the key in storage has been incremented by one: if any of these conditions doesn't hold the VP will discard the transactions and prevent the changes from being applied to the storage. -In the specific case of a shielded transfer, since MASP already comes with replay protection as part of the Zcash design (see the [MASP specs](https://specs.namada.net/masp.html) and [Zcash protocol specs](https://zips.z.cash/protocol/protocol.pdf)), the counter in `SignedTxData` is not required and therefore should be optional. +In the specific case of a shielded transfer, since MASP already comes with replay protection as part of the Zcash design (see the [MASP specs](../masp.md) and [Zcash protocol specs](https://zips.z.cash/protocol/protocol.pdf)), the counter in `SignedTxData` is not required and therefore should be optional. To implement replay protection for the inner transaction we will need to update all the VPs checking the transaction's signature to include the check on the transaction counter: at the moment the `vp_user` validity predicate is the only one to update. In addition, all the transactions involving `SignedTxData` should increment the counter. From 42b313b5c196f2f72256062d8c86593ba1d43074 Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Mon, 21 Nov 2022 12:05:48 +0100 Subject: [PATCH 14/41] Changes expiration block to expiration datetime --- .../src/base-ledger/replay-protection.md | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/documentation/specs/src/base-ledger/replay-protection.md b/documentation/specs/src/base-ledger/replay-protection.md index 28aa98bc72..24e44e9c59 100644 --- a/documentation/specs/src/base-ledger/replay-protection.md +++ b/documentation/specs/src/base-ledger/replay-protection.md @@ -115,7 +115,7 @@ This new field will be signed just like the other ones and is therefore subject In general, a transaction is valid at the moment of submission, but after that, a series of external factors (ledger state, etc.) might change the mind of the submitter who's now not interested in the execution of the transaction anymore. -We have to introduce the concept of a lifetime (or timeout) for the transactions: basically, the `Tx` struct will hold an extra field called `expiration` stating the maximum block height up until which the submitter is willing to see the transaction executed. After the specified block height, the transaction will be considered invalid and discarded regardless of all the other checks. +We have to introduce the concept of a lifetime (or timeout) for the transactions: basically, the `Tx` struct will hold an extra field called `expiration` stating the maximum `DateTimeUtc` up until which the submitter is willing to see the transaction executed. After the specified time, the transaction will be considered invalid and discarded regardless of all the other checks. By introducing this new field we are setting a new constraint in the transaction's contract, where the ledger will make sure to prevent the execution of the transaction after the deadline and, on the other side, the submitter commits himself to the result of the execution at least until its expiration. If the expiration is reached and the transaction has not been executed the submitter can decide to submit a new transaction if he's still interested in the changes carried by it. @@ -123,11 +123,11 @@ In our design, the `expiration` will hold until the transaction is executed: onc - Transaction is invalid regardless of the specific state - Transaction is executed (either with success or not) and the transaction hash is saved in the storage -- Expiration block height has passed +- Expiration time has passed The first condition satisfied will invalidate further executions of the same tx. -In anticipation of DKG implementation, the current struct `WrapperTx` holds a field `epoch` stating the epoch in which the tx should be executed. This is because Ferveo will produce a new public key each epoch, effectively limiting the lifetime of the transaction (see section 2.2.2 of the [documentation](https://eprint.iacr.org/2022/898.pdf)). Unfortunately, for replay protection, a resolution of 1 epoch (~ 1 day) is too low for the possible needs of the submitters, therefore we need the `expiration` field to hold a maximum `BlockHeight` to increase resolution down to a single block (~ 10 seconds). +In anticipation of DKG implementation, the current struct `WrapperTx` holds a field `epoch` stating the epoch in which the tx should be executed. This is because Ferveo will produce a new public key each epoch, effectively limiting the lifetime of the transaction (see section 2.2.2 of the [documentation](https://eprint.iacr.org/2022/898.pdf)). Unfortunately, for replay protection, a resolution of 1 epoch (~ 1 day) is too low for the possible needs of the submitters, therefore we need the `expiration` field to hold a maximum `DateTimeUtc` to increase resolution down to a single block (~ 10 seconds). ```rust pub struct Tx { @@ -136,7 +136,7 @@ pub struct Tx { pub timestamp: DateTimeUtc, pub chain_id: ChainId, /// Lifetime of the transaction, also determines which decryption key will be used - pub expiration: BlockHeight, + pub expiration: DateTimeUtc, } pub struct WrapperTx { @@ -156,7 +156,7 @@ pub struct WrapperTx { Since we now have more detailed information about the desired lifetime of the transaction, we can remove the `epoch` field and rely solely on `expiration`. Now, the producer of the inner transaction should make sure to set a sensible value for this field, in the sense that it should not span more than one epoch. If this happens, then the transaction will be correctly decrypted only in a subset of the desired lifetime (the one expecting the actual key used for the encryption), while, in the following epochs, the transaction will fail decryption and won't be executed. In essence, the `expiration` parameter can only restrict the implicit lifetime within the current epoch, it can not surpass it as that would make the transaction fail in the decryption phase. -The subject encrypting the inner transaction will also be responsible for using the appropriate public key for encryption relative to the targeted block height. +The subject encrypting the inner transaction will also be responsible for using the appropriate public key for encryption relative to the targeted time. The wrapper transaction will match the `expiration` of the inner for correct execution. Note that we need this field also for the wrapper to anticipate the check at mempool/proposal evaluation time, but also to prevent someone from inserting a wrapper transaction after the corresponding inner has expired forcing the wrapper signer to pay for the fees. @@ -176,7 +176,7 @@ These checks can all be done before executing the transactions themselves (the c 2. If the checks fail _only_ because of an insufficient balance, the wrapper should be kept in mempool for a future play in case the funds should become available 3. If all the checks pass validation we will include the transaction in the block to store the hash and charge the fee -The `expiration` parameter also justifies step 2 of the previous bullet points which states that if the validity checks fail only because of an insufficient balance to pay for fees then the transaction should be kept in mempool for future execution. Without it, the transaction could be potentially executed at any future moment, possibly going against the mutated interests of the submitter. With the expiration parameter, now, the submitter commits himself to accept the execution of the transaction up to the specified block height: it's going to be his responsibility to provide a sensible value for this parameter. Given this constraint the transaction will be kept in memepool up until the expiration (since it would become invalid after that in any case), to prevent the mempool from increasing too much in size. +The `expiration` parameter also justifies step 2 of the previous bullet points which states that if the validity checks fail only because of an insufficient balance to pay for fees then the transaction should be kept in mempool for future execution. Without it, the transaction could be potentially executed at any future moment, possibly going against the mutated interests of the submitter. With the expiration parameter, now, the submitter commits himself to accept the execution of the transaction up to the specified time: it's going to be his responsibility to provide a sensible value for this parameter. Given this constraint the transaction will be kept in memepool up until the expiration (since it would become invalid after that in any case), to prevent the mempool from increasing too much in size. This mechanism can also be applied to another scenario. Suppose a transaction was not propagated to the network by a node (or a group of colluding nodes). Now, this tx might be valid, but it doesn't get inserted into a block. Without an expiration, this tx can be replayed (better, applied, since it was never executed in the first place) at a future moment in time when the submitter might not be willing to execute it anymore. @@ -275,7 +275,7 @@ The ideal solution would be to interrupt the execution of the Wasm code after th Unfortunately, at the moment, Wasmer doesn't allow [yielding](https://github.com/wasmerio/wasmer/issues/1127) from the execution. -In case the transaction went out of gas (given the `gas_limit` field of the wrapper), all the changes applied will be discarded from the WAL and will not affect the state of the storage. The inner transaction could then be rewrapped with a correct gas limit and replayed until the `expiration` block has been reached. +In case the transaction went out of gas (given the `gas_limit` field of the wrapper), all the changes applied will be discarded from the WAL and will not affect the state of the storage. The inner transaction could then be rewrapped with a correct gas limit and replayed until the `expiration` time has been reached. ##### Batching and transaction ordering @@ -396,7 +396,7 @@ pub struct WrapperTx { /// Max amount of gas that can be used when executing the inner tx pub gas_limit: GasLimit, /// Lifetime of the transaction, also determines which decryption key will be used - pub expiration: BlockHeight, + pub expiration: DateTimeUtc, /// Chain identifier for replay protection pub chain_id: ChainId, /// Transaction counter for replay protection @@ -419,7 +419,7 @@ The Wrapper transaction no longer holds the inner transaction hash while the inn pub struct WrapperCommit { pub pk: common::PublicKey, pub tx_counter: u64, - pub expiration: BlockHeight, + pub expiration: DateTimeUtc, pub chain_id: ChainId, } ``` @@ -458,7 +458,7 @@ These checks can all be done before executing the transactions themselves. The c 2. If the checks fail _only_ because of an insufficient balance, the wrapper should be kept in mempool for a future play in case the funds should become available 3. If all the checks pass validation we will include the transaction in the block to increase the counter and charge the fee -Note that, regarding point one, there's a distinction to be made about an invalid `tx_counter` which could be invalid because of being old or being in advance. To solve this last issue (counter greater than the expected one), we have to introduce the concept of a lifetime (or timeout) for the transactions: basically, the `WrapperTx` will hold an extra field called `expiration` stating the maximum block height up until which the submitter is willing to see the transaction executed. After the specified block height the transaction will be considered invalid and discarded regardless of all the other checks. This way, in case of a transaction with a counter greater than expected, it is sufficient to wait till after the expiration to submit more transactions, so that the counter in storage is not modified (kept invalid for the transaction under observation) and replaying that tx would result in a rejection. +Note that, regarding point one, there's a distinction to be made about an invalid `tx_counter` which could be invalid because of being old or being in advance. To solve this last issue (counter greater than the expected one), we have to introduce the concept of a lifetime (or timeout) for the transactions: basically, the `WrapperTx` will hold an extra field called `expiration` stating the maximum time up until which the submitter is willing to see the transaction executed. After the specified time the transaction will be considered invalid and discarded regardless of all the other checks. This way, in case of a transaction with a counter greater than expected, it is sufficient to wait till after the expiration to submit more transactions, so that the counter in storage is not modified (kept invalid for the transaction under observation) and replaying that tx would result in a rejection. This actually generalizes to a more broad concept. In general, a transaction is valid at the moment of submission, but after that, a series of external factors (ledger state, etc.) might change the mind of the submitter who's now not interested in the execution of the transaction anymore. By introducing this new field we are introducing a new constraint in the transaction's contract, where the ledger will make sure to prevent the execution of the transaction after the deadline and, on the other side, the submitter commits himself to the result of the execution at least until its expiration. If the expiration is reached and the transaction has not been executed the submitter can decide to submit a new, identical transaction if he's still interested in the changes carried by it. @@ -466,15 +466,15 @@ In our design, the `expiration` will hold until the transaction is executed, onc - Transaction is invalid regardless of the specific state - Transaction is executed (either with success or not) and the transaction counter is increased -- Expiration block height has passed +- Expiration time has passed The first condition satisfied will invalidate further executions of the same tx. -The `expiration` parameter also justifies step 2 of the previous bullet points which states that if the validity checks fail only because of an insufficient balance to pay for fees than the transaction should be kept in mempool for a future execution. Without it, the transaction could be potentially executed at any future moment (provided that the counter is still valid), possibily going against the mutated interests of the submitter. With the expiration parameter, now, the submitter commits himself to accepting the execution of the transaction up to the specified block height: it's going to be his responsibility to provide a sensible value for this parameter. Given this constraint the transaction will be kept in memepool up until the expiration (since it would become invalid after that in any case), to prevent the mempool from increasing too much in size. +The `expiration` parameter also justifies step 2 of the previous bullet points which states that if the validity checks fail only because of an insufficient balance to pay for fees than the transaction should be kept in mempool for a future execution. Without it, the transaction could be potentially executed at any future moment (provided that the counter is still valid), possibily going against the mutated interests of the submitter. With the expiration parameter, now, the submitter commits himself to accepting the execution of the transaction up to the specified time: it's going to be his responsibility to provide a sensible value for this parameter. Given this constraint the transaction will be kept in memepool up until the expiration (since it would become invalid after that in any case), to prevent the mempool from increasing too much in size. This mechanism can also be applied to another scenario. Suppose a transaction was not propagated to the network by a node (or a group of colluding nodes). Now, this tx might be valid, but it doesn't get inserted into a block. Without an expiration, if the submitter doesn't submit any other transaction (which gets included in a block to increase the transaction counter), this tx can be replayed (better, applied, since it was never executed in the first place) at a future moment in time when the submitter might not be willing to execute it any more. -Since the signer of the wrapper may be different from the one of the inner we also need to include this `expiration` field in the `WrapperCommit` struct, to prevent the signer of the wrapper from setting a lifetime which is in conflict with the interests of the inner signer. Note that adding a separate lifetime for the wrapper alone (which would require two separate checks) doesn't carry any benefit: a wrapper with a lifetime greater than the inner would have no sense since the inner would fail. Restricting the lifetime would work but it also means that the wrapper could prevent a valid inner transaction from being executed. We will then keep a single `expiration` field specifying the wrapper tx max block height (the inner one will actually be executed one block later because of the execution mechanism of Namada). +Since the signer of the wrapper may be different from the one of the inner we also need to include this `expiration` field in the `WrapperCommit` struct, to prevent the signer of the wrapper from setting a lifetime which is in conflict with the interests of the inner signer. Note that adding a separate lifetime for the wrapper alone (which would require two separate checks) doesn't carry any benefit: a wrapper with a lifetime greater than the inner would have no sense since the inner would fail. Restricting the lifetime would work but it also means that the wrapper could prevent a valid inner transaction from being executed. We will then keep a single `expiration` field specifying the wrapper tx max time (the inner one will actually be executed one block later because of the execution mechanism of Namada). To prevent the signer of the wrapper from submitting the transaction to a different chain, the `ChainId` field should also be included in the commit. From 3a43f50410dd0a3ab23cfa734d5b3d2b4bbc24bf Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Mon, 21 Nov 2022 12:21:55 +0100 Subject: [PATCH 15/41] changelog: add #440 --- .changelog/unreleased/docs/440-replay-protection-specs.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/docs/440-replay-protection-specs.md diff --git a/.changelog/unreleased/docs/440-replay-protection-specs.md b/.changelog/unreleased/docs/440-replay-protection-specs.md new file mode 100644 index 0000000000..b05bb443b3 --- /dev/null +++ b/.changelog/unreleased/docs/440-replay-protection-specs.md @@ -0,0 +1,2 @@ +- Adds specs for replay protection + ([#440](https://github.com/anoma/namada/pull/440)) \ No newline at end of file From c83325bb6fb291f692efe66c49a5e04cacddde61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 21 Nov 2022 19:08:05 +0100 Subject: [PATCH 16/41] wasm/tx_{bond,unbond,withdraw}: avoid zero-amount action for test inputs --- shared/src/types/token.rs | 8 ++++++++ wasm/wasm_source/src/tx_bond.rs | 2 +- wasm/wasm_source/src/tx_unbond.rs | 28 ++++++++++++++-------------- wasm/wasm_source/src/tx_withdraw.rs | 11 ++++++----- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/shared/src/types/token.rs b/shared/src/types/token.rs index b19642b85a..3c11d6824c 100644 --- a/shared/src/types/token.rs +++ b/shared/src/types/token.rs @@ -476,4 +476,12 @@ pub mod testing { pub fn arb_amount_ceiled(max: u64) -> impl Strategy { (0..=max).prop_map(Amount::from) } + + /// Generate an arbitrary non-zero token amount up to and including given + /// `max` value + pub fn arb_amount_non_zero_ceiled( + max: u64, + ) -> impl Strategy { + (1..=max).prop_map(Amount::from) + } } diff --git a/wasm/wasm_source/src/tx_bond.rs b/wasm/wasm_source/src/tx_bond.rs index 875c15e752..ae0b854001 100644 --- a/wasm/wasm_source/src/tx_bond.rs +++ b/wasm/wasm_source/src/tx_bond.rs @@ -345,7 +345,7 @@ mod tests { ( arb_established_address(), prop::option::of(arb_non_internal_address()), - token::testing::arb_amount_ceiled(max_amount), + token::testing::arb_amount_non_zero_ceiled(max_amount), ) .prop_map(|(validator, source, amount)| { transaction::pos::Bond { diff --git a/wasm/wasm_source/src/tx_unbond.rs b/wasm/wasm_source/src/tx_unbond.rs index fa59670a56..c9e20610e2 100644 --- a/wasm/wasm_source/src/tx_unbond.rs +++ b/wasm/wasm_source/src/tx_unbond.rs @@ -220,24 +220,24 @@ mod tests { epoch {epoch}" ); } - let start_epoch = match &unbond.source { - Some(_) => { - // This bond was a delegation - namada_tx_prelude::proof_of_stake::types::Epoch::from( - pos_params.pipeline_len, - ) - } - None => { - // This bond was a genesis validator self-bond - namada_tx_prelude::proof_of_stake::types::Epoch::default() - } + let start_epoch = if is_delegation { + // This bond was a delegation + namada_tx_prelude::proof_of_stake::types::Epoch::from( + pos_params.pipeline_len, + ) + } else { + // This bond was a genesis validator self-bond + namada_tx_prelude::proof_of_stake::types::Epoch::default() }; let end_epoch = namada_tx_prelude::proof_of_stake::types::Epoch::from( pos_params.unbonding_len - 1, ); - let expected_unbond = - HashMap::from_iter([((start_epoch, end_epoch), unbond.amount)]); + let expected_unbond = if unbond.amount == token::Amount::default() { + HashMap::new() + } else { + HashMap::from_iter([((start_epoch, end_epoch), unbond.amount)]) + }; let actual_unbond: Unbond = unbonds_post.get(pos_params.unbonding_len).unwrap(); assert_eq!( @@ -403,7 +403,7 @@ mod tests { ( address::testing::arb_established_address(), prop::option::of(address::testing::arb_non_internal_address()), - token::testing::arb_amount_ceiled(max_amount), + token::testing::arb_amount_non_zero_ceiled(max_amount), ) .prop_map(|(validator, source, amount)| { let validator = Address::Established(validator); diff --git a/wasm/wasm_source/src/tx_withdraw.rs b/wasm/wasm_source/src/tx_withdraw.rs index dc054fcd6f..4584f0aa4c 100644 --- a/wasm/wasm_source/src/tx_withdraw.rs +++ b/wasm/wasm_source/src/tx_withdraw.rs @@ -201,16 +201,17 @@ mod tests { fn arb_initial_stake_and_unbonded_amount() -> impl Strategy { // Generate initial stake - token::testing::arb_amount_ceiled((i64::MAX / 8) as u64).prop_flat_map( - |initial_stake| { + token::testing::arb_amount_non_zero_ceiled((i64::MAX / 8) as u64) + .prop_flat_map(|initial_stake| { // Use the initial stake to limit the unbonded amount from the // stake let unbonded_amount = - token::testing::arb_amount_ceiled(initial_stake.into()); + token::testing::arb_amount_non_zero_ceiled( + initial_stake.into(), + ); // Use the generated initial stake too too (Just(initial_stake), unbonded_amount) - }, - ) + }) } fn arb_withdraw() -> impl Strategy { From abde61ba8979879a9121c6c005b7b1ccfe508f2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Mon, 21 Nov 2022 19:16:38 +0100 Subject: [PATCH 17/41] wasm/tx_unbond: add regressions file --- wasm/wasm_source/proptest-regressions/tx_unbond.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 wasm/wasm_source/proptest-regressions/tx_unbond.txt diff --git a/wasm/wasm_source/proptest-regressions/tx_unbond.txt b/wasm/wasm_source/proptest-regressions/tx_unbond.txt new file mode 100644 index 0000000000..8c589d1abd --- /dev/null +++ b/wasm/wasm_source/proptest-regressions/tx_unbond.txt @@ -0,0 +1 @@ +cc f22e874350910b197cb02a4a07ec5bef18e16c0d1a39eaabaee43d1fc05ce11d From 36f4eb363b6cc5436be904e7248a64d35ce5ba70 Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Tue, 25 Oct 2022 14:48:03 +0200 Subject: [PATCH 18/41] Adds multisignature specs --- .../specs/src/base-ledger/multisignature.md | 58 ++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/documentation/specs/src/base-ledger/multisignature.md b/documentation/specs/src/base-ledger/multisignature.md index a32b8da81e..aa70f5afc6 100644 --- a/documentation/specs/src/base-ledger/multisignature.md +++ b/documentation/specs/src/base-ledger/multisignature.md @@ -1,3 +1,57 @@ -## k-of-n multisignature +# k-of-n multisignature -The k-of-n multisignature validity predicate authorises transactions on the basis of k out of n parties approving them. \ No newline at end of file +The k-of-n multisignature validity predicate authorises transactions on the basis of k out of n parties approving them. This document targets the encrypted wasm transactions: at the moment there's no support for multisignature on wrapper or protocol transactions. + +## Protocol + +Namada transactions get signed before being delivered to the network. This signature is then checked by the VPs to determine the validity of the transaction. To support multisignature we need to extend the current `SignedTxData` struct to the following: + +```rust +pub enum Signature { + Sig(common::Signature), + MultiSig(Vec) +} + +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: Signature, +} +``` + +This struct will now hold either a signature or multiple signatures over the data carried by the transaction. The different enum variants allow for a quick check of the correct signature type at validation time. + +## VPs + +To support multisig we provide a new `vp_multisig` wasm validity predicate that can be used instead of the usual `vp_user` for `implicit addresses` (see [spec](./default-account.md)). This new vp will be generic, it will allow for arbitrary actions on the account as long as the signatures are valid. + +Moreover, `established` and `internal` addresses may want a multi-signature scheme on top of their validation process. Among the internal ones, `PGF` will require multisignature for its council (see the [relative](../economics/public-goods-funding.md) spec). + +To support the validity checks, the VP will need to access two types of information: + +1. The multisig threshold +2. A list of valid signers' public keys + +This data defines the requirements of a valid transaction operating on the multisignature address and it will be written in storage when the account is first created: + +``` +/\$Address/multisig/threshold/: u8 +/\$Address/multisig/pubkeys/: Vec +``` + +To verify the correctness of the signatures, these VPs will proceed with a four-steps verification process: + +1. Check that the type of the signature is `MultiSig` +2. Check to have enough **unique** signatures for the given threshold +3. Validate the signatures +4. Check to have enough **valid** signatures for the given threshold + +Steps 1 and 2 allow to short-circuit the validation process and avoid unnecessary processing and storage access. The signatures will be validated against the list of predefined public keys: a signature will be rejected if it's not valid for any of these public keys. Step 4 will halt as soon as it retrieves enough valid signatures to match the threshold, meaning that the remaining signatures will not be verified. + +## Transaction construction + +To craft a multisigned transaction, the involved parties will need to coordinate. More specifically, the transaction will be constructed by one entity which will then distribute it to the signers and collect their signatures: note that the constructing party doesn't necessarily need to be one of the signers. Finally, these signatures will be inserted in the `SignedTxData` struct so that they can be encrypted, wrapped and submitted to the network. + +Namada does not provide a layer to support this process, so the involved parties will need to rely on an external communication mechanism. From 1f3b0be93c5463aa6cbdff0fa03570401ebaa7bc Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Tue, 22 Nov 2022 14:38:30 +0100 Subject: [PATCH 19/41] Updates multisig specs --- .../specs/src/base-ledger/multisignature.md | 65 +++++++++++++++---- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/documentation/specs/src/base-ledger/multisignature.md b/documentation/specs/src/base-ledger/multisignature.md index aa70f5afc6..2ba2f78112 100644 --- a/documentation/specs/src/base-ledger/multisignature.md +++ b/documentation/specs/src/base-ledger/multisignature.md @@ -9,7 +9,7 @@ Namada transactions get signed before being delivered to the network. This signa ```rust pub enum Signature { Sig(common::Signature), - MultiSig(Vec) + MultiSig(Vec<(u8, common::Signature)) } pub struct SignedTxData { @@ -21,37 +21,76 @@ pub struct SignedTxData { } ``` -This struct will now hold either a signature or multiple signatures over the data carried by the transaction. The different enum variants allow for a quick check of the correct signature type at validation time. +The `MultiSig` variant holds a vector of tuples where the first element is an 8-bit integer and a the second one is a signature. The integer serves as an index to match a specific signature to one of the public keys in the list of accepted ones. This way, we can improve the verification algorithm and check each signature only against the public key at the provided index (linear in time complexity), without the need to cycle on all of them which would be $\mathcal{O}(n^2)$. ## VPs -To support multisig we provide a new `vp_multisig` wasm validity predicate that can be used instead of the usual `vp_user` for `implicit addresses` (see [spec](./default-account.md)). This new vp will be generic, it will allow for arbitrary actions on the account as long as the signatures are valid. +To support multisig we provide a new generic `vp_multisig` wasm validity predicate that will allow for arbitrary actions on the account as long as the signatures are valid. Note that these modifications can affect the multisig account parameters themselves. -Moreover, `established` and `internal` addresses may want a multi-signature scheme on top of their validation process. Among the internal ones, `PGF` will require multisignature for its council (see the [relative](../economics/public-goods-funding.md) spec). - -To support the validity checks, the VP will need to access two types of information: +To perform the validity checks, the VP will need to access two types of information: 1. The multisig threshold 2. A list of valid signers' public keys -This data defines the requirements of a valid transaction operating on the multisignature address and it will be written in storage when the account is first created: +This data defines the requirements of a valid transaction operating on the multisignature address and it will be written in storage when the account is created: ``` /\$Address/multisig/threshold/: u8 -/\$Address/multisig/pubkeys/: Vec +/\$Address/multisig/pubkeys/: LazyVec ``` -To verify the correctness of the signatures, these VPs will proceed with a four-steps verification process: +The `LazyVec` struct will split all of its element on different subkeys in storage so that we won't need to load the entire vector of public keys in memory for validation but just the ones pointed by the indexes in the `SignedTxData` struct. + +To verify the correctness of the signatures, these VPs will proceed with a three-steps verification process: 1. Check that the type of the signature is `MultiSig` 2. Check to have enough **unique** signatures for the given threshold -3. Validate the signatures -4. Check to have enough **valid** signatures for the given threshold +3. Check to have enough **valid** signatures for the given threshold + +Steps 1 and 2 allow to short-circuit the validation process and avoid unnecessary processing and storage access. Each signature will be validated **only** against the public key found in list at the specified index. Step 3 will halt as soon as it retrieves enough valid signatures to match the threshold, meaning that the remaining signatures will not be verified. + +In the transaction initiating the established address, the submitter will be able to provide a custom VP if the provided one doesn't fully satisfy the desired requirements. + +## Addresses + +The multisig vp introduced in the previous section is available for `established` addresses. To generate a multisig account we provide a new `tx_init_multisig_account` wasm transaction to be used instead of the already available `tx_init_account`. A multisig account can be created by anyone and the creator is responsible for providing the correct data, represented by the following struct: + +```rust +pub struct InitMultiSigAccount { + /// The VP code + pub vp_code: Vec, + /// Multisig threshold for k-of-n + pub threshold: u8, + /// Multisig signers' pubkeys + pub pubkeys: Vec +} +``` -Steps 1 and 2 allow to short-circuit the validation process and avoid unnecessary processing and storage access. The signatures will be validated against the list of predefined public keys: a signature will be rejected if it's not valid for any of these public keys. Step 4 will halt as soon as it retrieves enough valid signatures to match the threshold, meaning that the remaining signatures will not be verified. +Finally, the tx performs the following writes to storage: + +- The multisig vp +- The threshold +- The list of public keys of the signers + +No checks will be run on these parameters, meaning that the creator could provide wrong data: to perform validation on this data we would need an internal VP managing the creation of every multisig account. This VP, though, is not required since in case of an error the creator can simply submit a new transaction to generate the correct account. On the other side, the participants of a multisig account can refuse to sign transactions if they don't agree on the parameters defining the account itself. + +`Internal` addresses may want a multi-signature scheme on top of their validation process as well. Among the internal ones, `PGF` will require multisignature for its council (see the [relative](../economics/public-goods-funding.md) spec). The storage data necessary for the correct working of the multisig for an internal address are written in the genesis file: these keys can be later modified through governance. + +`Implicit` addresses are not generated by a transaction and therefore are not suitable for a multisignature scheme since there would be no way to properly construct them. More specifically, an implicit address doesn't allow for: + +- A custom, modifiable VP +- An initial transaction to be used as an initializer for the relevant data ## Transaction construction -To craft a multisigned transaction, the involved parties will need to coordinate. More specifically, the transaction will be constructed by one entity which will then distribute it to the signers and collect their signatures: note that the constructing party doesn't necessarily need to be one of the signers. Finally, these signatures will be inserted in the `SignedTxData` struct so that they can be encrypted, wrapped and submitted to the network. +To craft a multisigned transaction, the involved parties will need to coordinate. More specifically, the transaction will be constructed by one entity which will then distribute it to the signers and collect their signatures: note that the constructing party doesn't necessarily need to be one of the signers. Finally, these signatures will be inserted in the `SignedTxData` struct so that it can be encrypted, wrapped and submitted to the network. Namada does not provide a layer to support this process, so the involved parties will need to rely on an external communication mechanism. + +## Replay protection + +The [replay protection](./replay-protection.md) mechanism of Namada will prevent third-party malicious users from replaying a transaction having a multisig account as the source. This mechanism, though, is not enough to completely protect multisigned transactions: in this case, in fact, the threat could come from the members of the account themselves. + +With the current hash-based replay protection mechanism, once the transaction has been executed, its hash gets stored to prevent a replay. The issue, with a multisig transaction, is that the same transaction with a different set or amount of signatures would have a different hash, meaning that it can be replayed. + +There's no way to mitigate this scenario with the current implementation, so the members of a multisignature account will be responsible for behaving honestly towards each other and, possibly, punish malicious users by excluding them from the account. From 9f9f799651fc3cc1fa46ba4429150a070dc66683 Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Wed, 23 Nov 2022 15:28:45 +0100 Subject: [PATCH 20/41] Updates multisig specs checks --- .../specs/src/base-ledger/multisignature.md | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/documentation/specs/src/base-ledger/multisignature.md b/documentation/specs/src/base-ledger/multisignature.md index 2ba2f78112..0a274fd6e0 100644 --- a/documentation/specs/src/base-ledger/multisignature.md +++ b/documentation/specs/src/base-ledger/multisignature.md @@ -1,6 +1,6 @@ # k-of-n multisignature -The k-of-n multisignature validity predicate authorises transactions on the basis of k out of n parties approving them. This document targets the encrypted wasm transactions: at the moment there's no support for multisignature on wrapper or protocol transactions. +The k-of-n multisignature validity predicate authorizes transactions on the basis of k out of n parties approving them. This document targets the encrypted wasm transactions: at the moment there's no support for multisignature on wrapper or protocol transactions. ## Protocol @@ -21,7 +21,7 @@ pub struct SignedTxData { } ``` -The `MultiSig` variant holds a vector of tuples where the first element is an 8-bit integer and a the second one is a signature. The integer serves as an index to match a specific signature to one of the public keys in the list of accepted ones. This way, we can improve the verification algorithm and check each signature only against the public key at the provided index (linear in time complexity), without the need to cycle on all of them which would be $\mathcal{O}(n^2)$. +The `MultiSig` variant holds a vector of tuples where the first element is an 8-bit integer and the second one is a signature. The integer serves as an index to match a specific signature to one of the public keys in the list of accepted ones. This way, we can improve the verification algorithm and check each signature only against the public key at the provided index (linear in time complexity), without the need to cycle on all of them which would be $\mathcal{O}(n^2)$. ## VPs @@ -39,17 +39,17 @@ This data defines the requirements of a valid transaction operating on the multi /\$Address/multisig/pubkeys/: LazyVec ``` -The `LazyVec` struct will split all of its element on different subkeys in storage so that we won't need to load the entire vector of public keys in memory for validation but just the ones pointed by the indexes in the `SignedTxData` struct. +The `LazyVec` struct will split all of its elements on different subkeys in storage so that we won't need to load the entire vector of public keys in memory for validation but just the ones pointed by the indexes in the `SignedTxData` struct. -To verify the correctness of the signatures, these VPs will proceed with a three-steps verification process: +To verify the correctness of the signatures, these VPs will proceed with a three-step verification process: 1. Check that the type of the signature is `MultiSig` 2. Check to have enough **unique** signatures for the given threshold 3. Check to have enough **valid** signatures for the given threshold -Steps 1 and 2 allow to short-circuit the validation process and avoid unnecessary processing and storage access. Each signature will be validated **only** against the public key found in list at the specified index. Step 3 will halt as soon as it retrieves enough valid signatures to match the threshold, meaning that the remaining signatures will not be verified. +Steps 1 and 2 allow us to short-circuit the validation process and avoid unnecessary processing and storage access. Each signature will be validated **only** against the public key found in the list at the specified index. Step 3 will halt as soon as it retrieves enough valid signatures to match the threshold, meaning that the remaining signatures will not be verified. -In the transaction initiating the established address, the submitter will be able to provide a custom VP if the provided one doesn't fully satisfy the desired requirements. +In the transaction initiating the established address, the submitter will be able to provide a custom VP if the provided one doesn't impose the desired constraints. ## Addresses @@ -72,15 +72,33 @@ Finally, the tx performs the following writes to storage: - The threshold - The list of public keys of the signers -No checks will be run on these parameters, meaning that the creator could provide wrong data: to perform validation on this data we would need an internal VP managing the creation of every multisig account. This VP, though, is not required since in case of an error the creator can simply submit a new transaction to generate the correct account. On the other side, the participants of a multisig account can refuse to sign transactions if they don't agree on the parameters defining the account itself. - `Internal` addresses may want a multi-signature scheme on top of their validation process as well. Among the internal ones, `PGF` will require multisignature for its council (see the [relative](../economics/public-goods-funding.md) spec). The storage data necessary for the correct working of the multisig for an internal address are written in the genesis file: these keys can be later modified through governance. -`Implicit` addresses are not generated by a transaction and therefore are not suitable for a multisignature scheme since there would be no way to properly construct them. More specifically, an implicit address doesn't allow for: +`Implicit` addresses are not generated by a transaction and, therefore, are not suitable for a multisignature scheme since there would be no way to properly construct them. More specifically, an implicit address doesn't allow for: - A custom, modifiable VP - An initial transaction to be used as an initializer for the relevant data +## Multisig account init validation + +Since the VP of an established account does not get triggered at account creation, no checks will be run on the multisig parameters, meaning that the creator could provide wrong data. + +To perform validation at account creation time we could: + +1. Write in storage the addresses together with the public keys to trigger their VPs +2. Manually trigger the multisig VP even at creation time +3. Create an internal VP managing the creation of every multisig account + +All of these solutions would require the init transaction to become a multisigned one. + +Solution 1 actually exhibits a problem: in case the members of the account would like to exclude one of them from the account, the target account could refuse to sign the multisig transaction carrying this modification. At validation time, his private VP will be triggered and, since there's no signature matching his own public key in the transaction, it would reject it effectively preventing the multisig account to operate on itself even with enough signatures to match the threshold. This goes against the principle that a multisig account should be self-sufficient and controlled by its own VP and not those of its members. + +Solution 2 would perform just a partial check since the logic of the VP will revolve around the threshold. + +Finally, solution 3 would require an internal VP dedicated to the management of multisig addresses' parameters both at creation and modification time. This could implement a logic based on the threshold or a logic requiring a signature by all the members to initialize/modify a multisig account's parameters. The former effectively collapses to the VP of the account itself (making the internal VP redundant), while the latter has the same problem as solution 1. + +In the end, we don't implement any of these checks and will leave the responsibility to the signer of the transaction creating the address: in case of an error he can simply submit a new transaction to generate the correct account. On the other side, the participants of a multisig account can refuse to sign transactions if they don't agree on the parameters defining the account itself. + ## Transaction construction To craft a multisigned transaction, the involved parties will need to coordinate. More specifically, the transaction will be constructed by one entity which will then distribute it to the signers and collect their signatures: note that the constructing party doesn't necessarily need to be one of the signers. Finally, these signatures will be inserted in the `SignedTxData` struct so that it can be encrypted, wrapped and submitted to the network. From fea2fc9a257af0614c0bf752d4d0d659fe9c8ccb Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Mon, 28 Nov 2022 13:05:47 +0000 Subject: [PATCH 21/41] Enable sign extend wasm opcodes --- shared/Cargo.toml | 4 ++-- shared/src/vm/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 1af1162f92..3745bc7719 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -107,13 +107,13 @@ ics23 = "0.7.0" itertools = "0.10.0" loupe = {version = "0.1.3", optional = true} libsecp256k1 = {git = "https://github.com/heliaxdev/libsecp256k1", rev = "bbb3bd44a49db361f21d9db80f9a087c194c0ae9", default-features = false, features = ["std", "static-context"]} -parity-wasm = {version = "0.42.2", optional = true} +parity-wasm = {version = "0.45.0", features = ["sign_ext"], optional = true} paste = "1.0.9" # A fork with state machine testing proptest = {git = "https://github.com/heliaxdev/proptest", branch = "tomas/sm", optional = true} prost = "0.9.0" prost-types = "0.9.0" -pwasm-utils = {version = "0.18.0", optional = true} +pwasm-utils = {git = "https://github.com/heliaxdev/wasm-utils", tag = "v0.20.0", features = ["sign_ext"], optional = true} rand = {version = "0.8", optional = true} # TODO proptest rexports the RngCore trait but the re-implementations only work for version `0.8`. *sigh* rand_core = {version = "0.6", optional = true} diff --git a/shared/src/vm/mod.rs b/shared/src/vm/mod.rs index 2e14666f81..70fae7a3d4 100644 --- a/shared/src/vm/mod.rs +++ b/shared/src/vm/mod.rs @@ -28,7 +28,7 @@ const UNTRUSTED_WASM_FEATURES: WasmFeatures = WasmFeatures { memory64: false, mutable_global: false, saturating_float_to_int: false, - sign_extension: false, + sign_extension: true, relaxed_simd: false, extended_const: false, }; From 577ebf18f2e61d9fdce77a8fd9316ca1215e5ca2 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Mon, 28 Nov 2022 13:21:05 +0000 Subject: [PATCH 22/41] Update Cargo.lock files --- Cargo.lock | 9 ++++----- wasm/Cargo.lock | 9 ++++----- wasm_for_tests/wasm_source/Cargo.lock | 9 ++++----- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d72635662f..6792cb13b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4281,9 +4281,9 @@ dependencies = [ [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "parking" @@ -4730,9 +4730,8 @@ dependencies = [ [[package]] name = "pwasm-utils" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "880b3384fb00b8f6ecccd5d358b93bd2201900ae3daad213791d1864f6441f5c" +version = "0.20.0" +source = "git+https://github.com/heliaxdev/wasm-utils?tag=v0.20.0#782bfa7fb5e513b602e66af492cbc4cb1b06f2ba" dependencies = [ "byteorder", "log 0.4.17", diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 3967368aaa..1444f2fc82 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -2738,9 +2738,9 @@ dependencies = [ [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "parking_lot" @@ -3076,9 +3076,8 @@ dependencies = [ [[package]] name = "pwasm-utils" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "880b3384fb00b8f6ecccd5d358b93bd2201900ae3daad213791d1864f6441f5c" +version = "0.20.0" +source = "git+https://github.com/heliaxdev/wasm-utils?tag=v0.20.0#782bfa7fb5e513b602e66af492cbc4cb1b06f2ba" dependencies = [ "byteorder", "log", diff --git a/wasm_for_tests/wasm_source/Cargo.lock b/wasm_for_tests/wasm_source/Cargo.lock index a43b493805..6943d2156d 100644 --- a/wasm_for_tests/wasm_source/Cargo.lock +++ b/wasm_for_tests/wasm_source/Cargo.lock @@ -2730,9 +2730,9 @@ dependencies = [ [[package]] name = "parity-wasm" -version = "0.42.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5e13c266502aadf83426d87d81a0f5d1ef45b8027f5a471c360abfe4bfae92" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] name = "parking_lot" @@ -3068,9 +3068,8 @@ dependencies = [ [[package]] name = "pwasm-utils" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "880b3384fb00b8f6ecccd5d358b93bd2201900ae3daad213791d1864f6441f5c" +version = "0.20.0" +source = "git+https://github.com/heliaxdev/wasm-utils?tag=v0.20.0#782bfa7fb5e513b602e66af492cbc4cb1b06f2ba" dependencies = [ "byteorder", "log", From 4378fb02b54f52559010684f73572226a08eadf9 Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Tue, 29 Nov 2022 15:01:00 +0100 Subject: [PATCH 23/41] Merges normal and multisig accounts into one. Misc updates --- .../specs/src/base-ledger/multisignature.md | 48 +++++++------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/documentation/specs/src/base-ledger/multisignature.md b/documentation/specs/src/base-ledger/multisignature.md index 0a274fd6e0..7be36c156a 100644 --- a/documentation/specs/src/base-ledger/multisignature.md +++ b/documentation/specs/src/base-ledger/multisignature.md @@ -1,31 +1,28 @@ # k-of-n multisignature -The k-of-n multisignature validity predicate authorizes transactions on the basis of k out of n parties approving them. This document targets the encrypted wasm transactions: at the moment there's no support for multisignature on wrapper or protocol transactions. +The k-of-n multisignature validity predicate authorizes transactions on the basis of k out of n parties approving them. This document targets the encrypted wasm transactions: there won't be support for multisignature on wrapper or protocol transactions. ## Protocol -Namada transactions get signed before being delivered to the network. This signature is then checked by the VPs to determine the validity of the transaction. To support multisignature we need to extend the current `SignedTxData` struct to the following: +Namada transactions get signed before being delivered to the network. This signature is then checked by the VPs to determine the validity of the transaction. To support multisignature we need to modify the current `SignedTxData` struct to the following: ```rust -pub enum Signature { - Sig(common::Signature), - MultiSig(Vec<(u8, common::Signature)) -} - 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: Signature, + pub sig: Vec<(u8, common::Signature)>, } ``` -The `MultiSig` variant holds a vector of tuples where the first element is an 8-bit integer and the second one is a signature. The integer serves as an index to match a specific signature to one of the public keys in the list of accepted ones. This way, we can improve the verification algorithm and check each signature only against the public key at the provided index (linear in time complexity), without the need to cycle on all of them which would be $\mathcal{O}(n^2)$. +The `sig` field now holds a vector of tuples where the first element is an 8-bit integer and the second one is a signature. The integer serves as an index to match a specific signature to one of the public keys in the list of accepted ones. This way, we can improve the verification algorithm and check each signature only against the public key at the provided index (linear in time complexity), without the need to cycle on all of them which would be $\mathcal{O}(n^2)$. + +This means that non-multisig addresses will now be seen as 1-of-1 multisig accounts. ## VPs -To support multisig we provide a new generic `vp_multisig` wasm validity predicate that will allow for arbitrary actions on the account as long as the signatures are valid. Note that these modifications can affect the multisig account parameters themselves. +Since all the addresses will be multisig ones, we will keep using the already available `vp_user` as the default validity predicate. The only modification required is the signature check which must happen on a set of signatures instead of a single one. To perform the validity checks, the VP will need to access two types of information: @@ -35,33 +32,32 @@ To perform the validity checks, the VP will need to access two types of informat This data defines the requirements of a valid transaction operating on the multisignature address and it will be written in storage when the account is created: ``` -/\$Address/multisig/threshold/: u8 -/\$Address/multisig/pubkeys/: LazyVec +/\$Address/threshold/: u8 +/\$Address/pubkeys/: LazyVec ``` The `LazyVec` struct will split all of its elements on different subkeys in storage so that we won't need to load the entire vector of public keys in memory for validation but just the ones pointed by the indexes in the `SignedTxData` struct. -To verify the correctness of the signatures, these VPs will proceed with a three-step verification process: - -1. Check that the type of the signature is `MultiSig` -2. Check to have enough **unique** signatures for the given threshold -3. Check to have enough **valid** signatures for the given threshold +To verify the correctness of the signatures, this VP will proceed with a two-step verification process: -Steps 1 and 2 allow us to short-circuit the validation process and avoid unnecessary processing and storage access. Each signature will be validated **only** against the public key found in the list at the specified index. Step 3 will halt as soon as it retrieves enough valid signatures to match the threshold, meaning that the remaining signatures will not be verified. +1. Check to have enough **unique** signatures for the given threshold +2. Check to have enough **valid** signatures for the given threshold -In the transaction initiating the established address, the submitter will be able to provide a custom VP if the provided one doesn't impose the desired constraints. +Step 1 allows us to short-circuit the validation process and avoid unnecessary processing and storage access. Each signature will be validated **only** against the public key found in the list at the specified index. Step 2 will halt as soon as it retrieves enough valid signatures to match the threshold, meaning that the remaining signatures will not be verified. ## Addresses -The multisig vp introduced in the previous section is available for `established` addresses. To generate a multisig account we provide a new `tx_init_multisig_account` wasm transaction to be used instead of the already available `tx_init_account`. A multisig account can be created by anyone and the creator is responsible for providing the correct data, represented by the following struct: +The vp introduced in the previous section is available for `established` addresses. To generate a multisig account we need to modify the `InitAccount` struct to support multiple public keys and a threshold, as follows: ```rust -pub struct InitMultiSigAccount { +pub struct InitAccount { /// The VP code pub vp_code: Vec, /// Multisig threshold for k-of-n pub threshold: u8, - /// Multisig signers' pubkeys + /// Multisig signers' pubkeys to be written into the account's storage. This can be used + /// for signature verification of transactions for the newly created + /// account. pub pubkeys: Vec } ``` @@ -104,11 +100,3 @@ In the end, we don't implement any of these checks and will leave the responsibi To craft a multisigned transaction, the involved parties will need to coordinate. More specifically, the transaction will be constructed by one entity which will then distribute it to the signers and collect their signatures: note that the constructing party doesn't necessarily need to be one of the signers. Finally, these signatures will be inserted in the `SignedTxData` struct so that it can be encrypted, wrapped and submitted to the network. Namada does not provide a layer to support this process, so the involved parties will need to rely on an external communication mechanism. - -## Replay protection - -The [replay protection](./replay-protection.md) mechanism of Namada will prevent third-party malicious users from replaying a transaction having a multisig account as the source. This mechanism, though, is not enough to completely protect multisigned transactions: in this case, in fact, the threat could come from the members of the account themselves. - -With the current hash-based replay protection mechanism, once the transaction has been executed, its hash gets stored to prevent a replay. The issue, with a multisig transaction, is that the same transaction with a different set or amount of signatures would have a different hash, meaning that it can be replayed. - -There's no way to mitigate this scenario with the current implementation, so the members of a multisignature account will be responsible for behaving honestly towards each other and, possibly, punish malicious users by excluding them from the account. From f239661b849852d2a57744271d0c96c1154ce530 Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Tue, 29 Nov 2022 15:34:04 +0100 Subject: [PATCH 24/41] Fixes hash on unsigned txs --- documentation/specs/src/base-ledger/replay-protection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/specs/src/base-ledger/replay-protection.md b/documentation/specs/src/base-ledger/replay-protection.md index 24e44e9c59..1094460cad 100644 --- a/documentation/specs/src/base-ledger/replay-protection.md +++ b/documentation/specs/src/base-ledger/replay-protection.md @@ -79,7 +79,7 @@ The actual Wasm code and data for the transaction are encapsulated inside a stru `WrapperTx` is the only type of transaction currently accepted by the ledger. It must be protected from replay attacks because, if it wasn't, a malicious user could replay the transaction as is. Even if the inner transaction implemented replay protection or, for any reason, wasn't accepted, the signer of the wrapper would still pay for gas and fees, effectively suffering economic damage. -To prevent the replay of both these transactions we will rely on a set of already processed transactions' digests that will be kept in storage. These digests will be computed on the **signed** transactions. To support this, we'll need a subspace in storage headed by a `ReplayProtection` internal address: +To prevent the replay of both these transactions we will rely on a set of already processed transactions' digests that will be kept in storage. These digests will be computed on the **unsigned** transactions, to support replay protection even for [multisigned](multisignature.md) transactions: in this case, if hashes were taken from the signed transactions, a different set of signatures on the same tx would produce a different hash, effectively allowing for a replay. To support this, we'll need a subspace in storage headed by a `ReplayProtection` internal address: ``` /$ReplayProtectionAddress/$tx0_hash: None From 71b348bd713034803d96a3a935670b0c19d4e88d Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Wed, 30 Nov 2022 04:33:53 -0500 Subject: [PATCH 25/41] changelog: add #833 --- .../unreleased/improvements/833-wasm-vm-allow-sign-extend.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/833-wasm-vm-allow-sign-extend.md diff --git a/.changelog/unreleased/improvements/833-wasm-vm-allow-sign-extend.md b/.changelog/unreleased/improvements/833-wasm-vm-allow-sign-extend.md new file mode 100644 index 0000000000..b6efeba444 --- /dev/null +++ b/.changelog/unreleased/improvements/833-wasm-vm-allow-sign-extend.md @@ -0,0 +1,2 @@ +- Allow sign extension opcodes in WASM + ([#833](https://github.com/anoma/namada/pull/833)) \ No newline at end of file From f5040f201e0cdd78d22bfd1d9528a6b284d10f04 Mon Sep 17 00:00:00 2001 From: satan Date: Wed, 30 Nov 2022 14:22:20 +0100 Subject: [PATCH 26/41] [feat]: Removed MerkleValue type --- shared/src/ledger/queries/shell.rs | 4 ++-- shared/src/ledger/storage/merkle_tree.rs | 26 +++++++++++------------ shared/src/ledger/storage/mod.rs | 4 ++-- shared/src/ledger/storage/traits.rs | 27 ++++++++++-------------- shared/src/types/storage.rs | 21 ------------------ 5 files changed, 28 insertions(+), 54 deletions(-) diff --git a/shared/src/ledger/queries/shell.rs b/shared/src/ledger/queries/shell.rs index 4662381499..4e87eac5ab 100644 --- a/shared/src/ledger/queries/shell.rs +++ b/shared/src/ledger/queries/shell.rs @@ -207,7 +207,7 @@ where .storage .get_existence_proof( &storage_key, - value.clone().into(), + value.clone(), request.height, ) .into_storage_result()?; @@ -264,7 +264,7 @@ where for PrefixValue { key, value } in &data { let mut proof = ctx .storage - .get_existence_proof(key, value.clone().into(), request.height) + .get_existence_proof(key, value.clone(), request.height) .into_storage_result()?; ops.append(&mut proof.ops); } diff --git a/shared/src/ledger/storage/merkle_tree.rs b/shared/src/ledger/storage/merkle_tree.rs index 49afb3dcfc..e7f9a33703 100644 --- a/shared/src/ledger/storage/merkle_tree.rs +++ b/shared/src/ledger/storage/merkle_tree.rs @@ -22,8 +22,7 @@ use crate::tendermint::merkle::proof::{Proof, ProofOp}; use crate::types::address::{Address, InternalAddress}; use crate::types::hash::Hash; use crate::types::storage::{ - DbKeySeg, Error as StorageError, Key, MembershipProof, MerkleValue, - StringKey, TreeBytes, + DbKeySeg, Error as StorageError, Key, MembershipProof, StringKey, TreeBytes, }; #[allow(missing_docs)] @@ -52,6 +51,9 @@ pub enum Error { /// Result for functions that may fail type Result = std::result::Result; +/// Type alias for bytes to be put into the Merkle storage +pub(super) type StorageBytes = Vec; + /// Type aliases for the different merkle trees and backing stores pub type SmtStore = DefaultStore; pub type AmtStore = DefaultStore; @@ -278,9 +280,11 @@ impl MerkleTree { &mut self, store_type: &StoreType, key: &Key, - value: MerkleValue, + value: impl AsRef<[u8]>, ) -> Result<()> { - let sub_root = self.tree_mut(store_type).subtree_update(key, value)?; + let sub_root = self + .tree_mut(store_type) + .subtree_update(key, value.as_ref())?; // update the base tree with the updated sub root without hashing if *store_type != StoreType::Base { let base_key = H::hash(store_type.to_string()); @@ -296,13 +300,9 @@ impl MerkleTree { } /// Update the tree with the given key and value - pub fn update( - &mut self, - key: &Key, - value: impl Into, - ) -> Result<()> { + pub fn update(&mut self, key: &Key, value: impl AsRef<[u8]>) -> Result<()> { let (store_type, sub_key) = StoreType::sub_key(key)?; - self.update_tree(&store_type, &sub_key, value.into()) + self.update_tree(&store_type, &sub_key, value) } /// Delete the value corresponding to the given key @@ -335,7 +335,7 @@ impl MerkleTree { pub fn get_sub_tree_existence_proof( &self, keys: &[Key], - values: Vec, + values: Vec, ) -> Result { let first_key = keys.iter().next().ok_or_else(|| { Error::InvalidMerkleKey( @@ -675,7 +675,7 @@ mod test { let MembershipProof::ICS23(proof) = tree .get_sub_tree_existence_proof( std::array::from_ref(&ibc_key), - vec![ibc_val.clone().into()], + vec![ibc_val.clone()], ) .unwrap(); let proof = tree.get_tendermint_proof(&ibc_key, proof).unwrap(); @@ -730,7 +730,7 @@ mod test { let MembershipProof::ICS23(proof) = tree .get_sub_tree_existence_proof( std::array::from_ref(&pos_key), - vec![pos_val.clone().into()], + vec![pos_val.clone()], ) .unwrap(); let proof = tree.get_tendermint_proof(&pos_key, proof).unwrap(); diff --git a/shared/src/ledger/storage/mod.rs b/shared/src/ledger/storage/mod.rs index 571d33b4ab..233854d7e7 100644 --- a/shared/src/ledger/storage/mod.rs +++ b/shared/src/ledger/storage/mod.rs @@ -46,7 +46,7 @@ use crate::types::chain::{ChainId, CHAIN_ID_LENGTH}; use crate::types::storage::TxQueue; use crate::types::storage::{ BlockHash, BlockHeight, BlockResults, Epoch, Epochs, Header, Key, KeySeg, - MembershipProof, MerkleValue, TxIndex, BLOCK_HASH_LENGTH, + MembershipProof, TxIndex, BLOCK_HASH_LENGTH, }; use crate::types::time::DateTimeUtc; use crate::types::token; @@ -625,7 +625,7 @@ where pub fn get_existence_proof( &self, key: &Key, - value: MerkleValue, + value: Vec, height: BlockHeight, ) -> Result { if height >= self.get_block_height().0 { diff --git a/shared/src/ledger/storage/traits.rs b/shared/src/ledger/storage/traits.rs index e382f34d73..a488c58786 100644 --- a/shared/src/ledger/storage/traits.rs +++ b/shared/src/ledger/storage/traits.rs @@ -8,13 +8,12 @@ use arse_merkle_tree::{Key as TreeKey, H256}; use ics23::commitment_proof::Proof as Ics23Proof; use ics23::{CommitmentProof, ExistenceProof}; use sha2::{Digest, Sha256}; +use crate::ledger::storage::merkle_tree::StorageBytes; use super::merkle_tree::{Amt, Error, Smt}; use super::{ics23_specs, IBC_KEY_LIMIT}; use crate::types::hash::Hash; -use crate::types::storage::{ - Key, MembershipProof, MerkleValue, StringKey, TreeBytes, -}; +use crate::types::storage::{Key, MembershipProof, StringKey, TreeBytes}; /// Trait for reading from a merkle tree that is a sub-tree /// of the global merkle tree. @@ -25,7 +24,7 @@ pub trait SubTreeRead { fn subtree_membership_proof( &self, keys: &[Key], - values: Vec, + values: Vec, ) -> Result; } @@ -36,7 +35,7 @@ pub trait SubTreeWrite { fn subtree_update( &mut self, key: &Key, - value: MerkleValue, + value: &[u8], ) -> Result; /// Delete a key from the sub-tree fn subtree_delete(&mut self, key: &Key) -> Result; @@ -53,13 +52,13 @@ impl<'a, H: StorageHasher + Default> SubTreeRead for &'a Smt { fn subtree_membership_proof( &self, keys: &[Key], - mut values: Vec, + mut values: Vec, ) -> Result { if keys.len() != 1 || values.len() != 1 { return Err(Error::Ics23MultiLeaf); } let key: &Key = &keys[0]; - let MerkleValue::Bytes(value) = values.remove(0); + let value = values.remove(0); let cp = self.membership_proof(&H::hash(key.to_string()).into())?; // Replace the values and the leaf op for the verification match cp.proof.expect("The proof should exist") { @@ -82,11 +81,9 @@ impl<'a, H: StorageHasher + Default> SubTreeWrite for &'a mut Smt { fn subtree_update( &mut self, key: &Key, - value: MerkleValue, + value: &[u8], ) -> Result { - let value = match value { - MerkleValue::Bytes(bytes) => H::hash(bytes.as_slice()), - }; + let value = H::hash(value); self.update(H::hash(key.to_string()).into(), value.into()) .map(Hash::from) .map_err(|err| Error::MerkleTree(err.to_string())) @@ -112,7 +109,7 @@ impl<'a, H: StorageHasher + Default> SubTreeRead for &'a Amt { fn subtree_membership_proof( &self, keys: &[Key], - _: Vec, + _: Vec, ) -> Result { if keys.len() != 1 { return Err(Error::Ics23MultiLeaf); @@ -139,12 +136,10 @@ impl<'a, H: StorageHasher + Default> SubTreeWrite for &'a mut Amt { fn subtree_update( &mut self, key: &Key, - value: MerkleValue, + value: &[u8], ) -> Result { let key = StringKey::try_from_bytes(key.to_string().as_bytes())?; - let value = match value { - MerkleValue::Bytes(bytes) => TreeBytes::from(bytes), - }; + let value = TreeBytes::from(value.as_ref().to_owned()); self.update(key, value) .map(Into::into) .map_err(|err| Error::MerkleTree(err.to_string())) diff --git a/shared/src/types/storage.rs b/shared/src/types/storage.rs index bec847e8aa..a96032086a 100644 --- a/shared/src/types/storage.rs +++ b/shared/src/types/storage.rs @@ -356,27 +356,6 @@ impl FromStr for Key { } } -/// An enum representing the different types of values -/// that can be passed into Anoma's storage. -/// -/// This is a multi-store organized as -/// several Merkle trees, each of which is -/// responsible for understanding how to parse -/// this value. -pub enum MerkleValue { - /// raw bytes - Bytes(Vec), -} - -impl From for MerkleValue -where - T: AsRef<[u8]>, -{ - fn from(bytes: T) -> Self { - Self::Bytes(bytes.as_ref().to_owned()) - } -} - /// Storage keys that are utf8 encoded strings #[derive(Eq, PartialEq, Copy, Clone, Hash)] pub struct StringKey { From 7b50cac9ce29a0696eeee12438aa77cb83b45784 Mon Sep 17 00:00:00 2001 From: satan Date: Thu, 1 Dec 2022 10:37:41 +0100 Subject: [PATCH 27/41] [fix]: Changed Vec to &[u8] for getting merkle proofs --- shared/src/ledger/queries/shell.rs | 8 ++------ shared/src/ledger/storage/merkle_tree.rs | 6 +++--- shared/src/ledger/storage/mod.rs | 4 ++-- shared/src/ledger/storage/traits.rs | 10 +++++----- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/shared/src/ledger/queries/shell.rs b/shared/src/ledger/queries/shell.rs index 4e87eac5ab..d973180541 100644 --- a/shared/src/ledger/queries/shell.rs +++ b/shared/src/ledger/queries/shell.rs @@ -205,11 +205,7 @@ where let proof = if request.prove { let proof = ctx .storage - .get_existence_proof( - &storage_key, - value.clone(), - request.height, - ) + .get_existence_proof(&storage_key, &value, request.height) .into_storage_result()?; Some(proof) } else { @@ -264,7 +260,7 @@ where for PrefixValue { key, value } in &data { let mut proof = ctx .storage - .get_existence_proof(key, value.clone(), request.height) + .get_existence_proof(key, value, request.height) .into_storage_result()?; ops.append(&mut proof.ops); } diff --git a/shared/src/ledger/storage/merkle_tree.rs b/shared/src/ledger/storage/merkle_tree.rs index e7f9a33703..bd26259a41 100644 --- a/shared/src/ledger/storage/merkle_tree.rs +++ b/shared/src/ledger/storage/merkle_tree.rs @@ -52,7 +52,7 @@ pub enum Error { type Result = std::result::Result; /// Type alias for bytes to be put into the Merkle storage -pub(super) type StorageBytes = Vec; +pub(super) type StorageBytes<'a> = &'a [u8]; /// Type aliases for the different merkle trees and backing stores pub type SmtStore = DefaultStore; @@ -675,7 +675,7 @@ mod test { let MembershipProof::ICS23(proof) = tree .get_sub_tree_existence_proof( std::array::from_ref(&ibc_key), - vec![ibc_val.clone()], + vec![&ibc_val], ) .unwrap(); let proof = tree.get_tendermint_proof(&ibc_key, proof).unwrap(); @@ -730,7 +730,7 @@ mod test { let MembershipProof::ICS23(proof) = tree .get_sub_tree_existence_proof( std::array::from_ref(&pos_key), - vec![pos_val.clone()], + vec![&pos_val], ) .unwrap(); let proof = tree.get_tendermint_proof(&pos_key, proof).unwrap(); diff --git a/shared/src/ledger/storage/mod.rs b/shared/src/ledger/storage/mod.rs index 233854d7e7..87cb49ec75 100644 --- a/shared/src/ledger/storage/mod.rs +++ b/shared/src/ledger/storage/mod.rs @@ -31,7 +31,7 @@ use super::storage_api::{ResultExt, StorageRead, StorageWrite}; use crate::ledger::gas::MIN_STORAGE_GAS; use crate::ledger::parameters::EpochDuration; use crate::ledger::storage::merkle_tree::{ - Error as MerkleTreeError, MerkleRoot, + Error as MerkleTreeError, MerkleRoot, StorageBytes, }; pub use crate::ledger::storage::merkle_tree::{ MerkleTree, MerkleTreeStoresRead, MerkleTreeStoresWrite, StoreType, @@ -625,7 +625,7 @@ where pub fn get_existence_proof( &self, key: &Key, - value: Vec, + value: StorageBytes, height: BlockHeight, ) -> Result { if height >= self.get_block_height().0 { diff --git a/shared/src/ledger/storage/traits.rs b/shared/src/ledger/storage/traits.rs index a488c58786..dfb820a96b 100644 --- a/shared/src/ledger/storage/traits.rs +++ b/shared/src/ledger/storage/traits.rs @@ -8,10 +8,10 @@ use arse_merkle_tree::{Key as TreeKey, H256}; use ics23::commitment_proof::Proof as Ics23Proof; use ics23::{CommitmentProof, ExistenceProof}; use sha2::{Digest, Sha256}; -use crate::ledger::storage::merkle_tree::StorageBytes; use super::merkle_tree::{Amt, Error, Smt}; use super::{ics23_specs, IBC_KEY_LIMIT}; +use crate::ledger::storage::merkle_tree::StorageBytes; use crate::types::hash::Hash; use crate::types::storage::{Key, MembershipProof, StringKey, TreeBytes}; @@ -35,7 +35,7 @@ pub trait SubTreeWrite { fn subtree_update( &mut self, key: &Key, - value: &[u8], + value: StorageBytes, ) -> Result; /// Delete a key from the sub-tree fn subtree_delete(&mut self, key: &Key) -> Result; @@ -65,7 +65,7 @@ impl<'a, H: StorageHasher + Default> SubTreeRead for &'a Smt { Ics23Proof::Exist(ep) => Ok(CommitmentProof { proof: Some(Ics23Proof::Exist(ExistenceProof { key: key.to_string().as_bytes().to_vec(), - value, + value: value.to_vec(), leaf: Some(ics23_specs::leaf_spec::()), ..ep })), @@ -81,7 +81,7 @@ impl<'a, H: StorageHasher + Default> SubTreeWrite for &'a mut Smt { fn subtree_update( &mut self, key: &Key, - value: &[u8], + value: StorageBytes, ) -> Result { let value = H::hash(value); self.update(H::hash(key.to_string()).into(), value.into()) @@ -136,7 +136,7 @@ impl<'a, H: StorageHasher + Default> SubTreeWrite for &'a mut Amt { fn subtree_update( &mut self, key: &Key, - value: &[u8], + value: StorageBytes, ) -> Result { let key = StringKey::try_from_bytes(key.to_string().as_bytes())?; let value = TreeBytes::from(value.as_ref().to_owned()); From a9393e95243419a042bc3df863be17da70da9093 Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli <> Date: Thu, 1 Dec 2022 10:52:03 +0100 Subject: [PATCH 28/41] fix: ci rename --- .github/workflows/build-and-test-bridge.yml | 8 ++++---- .github/workflows/build-and-test.yml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-and-test-bridge.yml b/.github/workflows/build-and-test-bridge.yml index 11fe0f6994..b9b157dc75 100644 --- a/.github/workflows/build-and-test-bridge.yml +++ b/.github/workflows/build-and-test-bridge.yml @@ -148,7 +148,7 @@ jobs: make: - name: ABCI suffix: '' - cache_key: anoma + cache_key: namada cache_version: v2 tendermint_artifact: tendermint-unreleased-ad825dcadbd4b98c3f91ce5a711e4fb36a69c377 @@ -250,7 +250,7 @@ jobs: make: - name: ABCI Release build suffix: '' - cache_key: anoma-e2e-release + cache_key: namada-e2e-release cache_version: "v2" env: @@ -354,14 +354,14 @@ jobs: - name: e2e suffix: '' index: 0 - cache_key: anoma + cache_key: namada cache_version: v2 tendermint_artifact: tendermint-unreleased-ad825dcadbd4b98c3f91ce5a711e4fb36a69c377 wait_for: namada-release-eth (ubuntu-latest, 1.7.0, ABCI Release build, namada-e2e-release, v2) - name: e2e suffix: '' index: 1 - cache_key: anoma + cache_key: namada cache_version: v2 tendermint_artifact: tendermint-unreleased-ad825dcadbd4b98c3f91ce5a711e4fb36a69c377 wait_for: namada-release-eth (ubuntu-latest, 1.7.0, ABCI Release build, namada-e2e-release, v2) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index d2be12f726..97513e705c 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -151,7 +151,7 @@ jobs: make: - name: ABCI suffix: '' - cache_key: anoma + cache_key: namada cache_version: v2 tendermint_artifact: tendermint-unreleased-ad825dcadbd4b98c3f91ce5a711e4fb36a69c377 @@ -253,7 +253,7 @@ jobs: make: - name: ABCI Release build suffix: '' - cache_key: anoma-e2e-release + cache_key: namada-e2e-release cache_version: "v2" env: @@ -357,14 +357,14 @@ jobs: - name: e2e suffix: '' index: 0 - cache_key: anoma + cache_key: namada cache_version: v2 tendermint_artifact: tendermint-unreleased-ad825dcadbd4b98c3f91ce5a711e4fb36a69c377 wait_for: namada-release (ubuntu-latest, 1.7.0, ABCI Release build, namada-e2e-release, v2) - name: e2e suffix: '' index: 1 - cache_key: anoma + cache_key: namada cache_version: v2 tendermint_artifact: tendermint-unreleased-ad825dcadbd4b98c3f91ce5a711e4fb36a69c377 wait_for: namada-release (ubuntu-latest, 1.7.0, ABCI Release build, namada-e2e-release, v2) From c6265e14e58b7649aa4bb38c5f2da09a12319427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Thu, 1 Dec 2022 12:54:22 +0100 Subject: [PATCH 29/41] docs: update PoS user docs for 0.11.0 - replace `voting-power` cmd with `bonded-stake` - added 2 new args for `init-validator` for their commission rate - removed staking reward accounts - the rewards will be instead auto-bonded in the validator account --- .../docs/src/user-guide/ledger/pos.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/documentation/docs/src/user-guide/ledger/pos.md b/documentation/docs/src/user-guide/ledger/pos.md index e925269d35..8448dd48bb 100644 --- a/documentation/docs/src/user-guide/ledger/pos.md +++ b/documentation/docs/src/user-guide/ledger/pos.md @@ -86,10 +86,10 @@ Upon success, the withdrawn tokens will be credited back your account and debite ### Validators' Voting Power -To see all validators and their voting power, you can query: +To see all validators and their voting power, which is exactly equal to the amount of staked NAM tokens from their self-bonds and delegations, you can query: ```shell -namada client voting-power +namada client bonded-stake ``` With this command, you can specify `--epoch` to find the voting powers at some future epoch. Note that only the voting powers for the current and the next epoch are final. @@ -103,21 +103,21 @@ To register a new validator account, run: ```shell namada client init-validator \ --alias my-validator \ - --source my-new-acc + --source my-new-acc \ + --commission-rate + --max-commission-rate-change ``` +The commission rate charged by the validator for delegation rewards and the maximum change per epoch in the commission rate charged by the validator for delegation rewards. Both are expressed as a decimal between 0 and 1. *Staking rewards are not yet implemented*. + This command will generate the keys required for running a validator: - Consensus key, which is used in [signing blocks in Tendermint](https://docs.tendermint.com/master/nodes/validators.html#validator-keys). - Validator account key for signing transactions on the validator account, such as token self-bonding, unbonding and withdrawal, validator keys, validity predicate, state and metadata updates. -- Staking reward account key for signing transactions on the staking reward accounts. More on this account below. - -Then, it submits a transaction to the ledger that generates two new accounts with established addresses: -- A validator account with the main validator address, which can be used to receive new delegations -- A staking reward account, which will receive rewards for participation in the PoS system. In the future, the validity predicate of this account will be able to control how the rewards are to be distributed to the validator's delegators. *Staking rewards are not yet implemented*. +Then, it submits a transaction to the ledger that generates the new validator account with established address, which can be used to receive new delegations. -These keys and aliases of the addresses will be saved in your wallet. Your local ledger node will also be setup to run this validator, you just have to shut it down with e.g. `Ctrl + C`, then start it again with the same command: +The keys and the alias of the address will be saved in your wallet. Your local ledger node will also be setup to run this validator, you just have to shut it down with e.g. `Ctrl + C`, then start it again with the same command: ```shell namada ledger @@ -145,7 +145,7 @@ namada client bond \ ### Determine your voting power -A validator's voting power is determined by the sum of all their active self-bonds and delegations of tokens, with slashes applied, if any, divided by `1000` (PoS `votes_per_token` parameter, with the current value set to `10‱` in parts per ten thousand). +A validator's voting power is determined by the sum of all their active self-bonds and delegations of tokens, with slashes applied, if any. The same rules apply to delegations. When you self-bond tokens, the bonded amount won't count towards your validator's stake (which in turn determines your power) until the beginning of epoch `n + 2` in the current epoch `n`. The bonded amount of tokens will be deducted from the validator's account immediately and will be credited to the PoS system's account. From 7e72eb85bab73f95f4aaf4b3dddcfd9a7c51bbd2 Mon Sep 17 00:00:00 2001 From: Marco Granelli Date: Thu, 1 Dec 2022 14:55:43 +0100 Subject: [PATCH 30/41] changelog: add #680 --- .changelog/unreleased/docs/680-multisig-specs.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/docs/680-multisig-specs.md diff --git a/.changelog/unreleased/docs/680-multisig-specs.md b/.changelog/unreleased/docs/680-multisig-specs.md new file mode 100644 index 0000000000..8c2abf92ee --- /dev/null +++ b/.changelog/unreleased/docs/680-multisig-specs.md @@ -0,0 +1,2 @@ +- Adds specs for multisig accounts + ([#680](https://github.com/anoma/namada/pull/680)) \ No newline at end of file From 1c1579af33e146b2275d6ae22d808b3b89defc52 Mon Sep 17 00:00:00 2001 From: Tiago Carvalho Date: Fri, 2 Dec 2022 15:48:30 +0000 Subject: [PATCH 31/41] Reduce serialized size of BlockResults --- Cargo.lock | 11 ++- .../lib/node/ledger/shell/finalize_block.rs | 2 +- core/Cargo.toml | 2 +- core/src/types/storage.rs | 74 ++++++------------- wasm/Cargo.lock | 11 ++- wasm_for_tests/wasm_source/Cargo.lock | 11 ++- 6 files changed, 56 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b8c8cfbc5..4c711b025a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2947,6 +2947,15 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "index-set" +version = "0.7.1" +source = "git+https://github.com/heliaxdev/index-set?tag=v0.7.1#dc24cdbbe3664514d59f1a4c4031863fc565f1c2" +dependencies = [ + "borsh", + "serde 1.0.147", +] + [[package]] name = "indexmap" version = "1.9.1" @@ -3777,7 +3786,6 @@ dependencies = [ "assert_matches", "bech32", "bellman", - "bit-vec", "borsh", "chrono", "data-encoding", @@ -3791,6 +3799,7 @@ dependencies = [ "ibc-proto 0.17.1 (git+https://github.com/heliaxdev/ibc-rs?rev=9fcc1c8c19db6af50806ffe5b2f6c214adcbfd5d)", "ibc-proto 0.17.1 (git+https://github.com/heliaxdev/ibc-rs.git?rev=f4703dfe2c1f25cc431279ab74f10f3e0f6827e2)", "ics23", + "index-set", "itertools", "libsecp256k1", "masp_primitives", diff --git a/apps/src/lib/node/ledger/shell/finalize_block.rs b/apps/src/lib/node/ledger/shell/finalize_block.rs index aa0d542857..d762ee91d8 100644 --- a/apps/src/lib/node/ledger/shell/finalize_block.rs +++ b/apps/src/lib/node/ledger/shell/finalize_block.rs @@ -52,7 +52,7 @@ where } // Tracks the accepted transactions - self.storage.block.results = BlockResults::with_len(req.txs.len()); + self.storage.block.results = BlockResults::default(); for (tx_index, processed_tx) in req.txs.iter().enumerate() { let tx = if let Ok(tx) = Tx::try_from(processed_tx.tx.as_ref()) { tx diff --git a/core/Cargo.toml b/core/Cargo.toml index 5749522136..db5489078d 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -60,7 +60,6 @@ ark-serialize = {version = "0.3"} arse-merkle-tree = {package = "sparse-merkle-tree", git = "https://github.com/heliaxdev/sparse-merkle-tree", rev = "04ad1eeb28901b57a7599bbe433b3822965dabe8", default-features = false, features = ["std", "borsh"]} bech32 = "0.8.0" bellman = "0.11.2" -bit-vec = "0.6.3" borsh = "0.9.0" chrono = {version = "0.4.22", default-features = false, features = ["clock", "std"]} data-encoding = "2.3.2" @@ -75,6 +74,7 @@ ibc-proto = {version = "0.17.1", default-features = false, optional = true} ibc-abcipp = {package = "ibc", git = "https://github.com/heliaxdev/ibc-rs", rev = "9fcc1c8c19db6af50806ffe5b2f6c214adcbfd5d", default-features = false, optional = true} ibc-proto-abcipp = {package = "ibc-proto", git = "https://github.com/heliaxdev/ibc-rs", rev = "9fcc1c8c19db6af50806ffe5b2f6c214adcbfd5d", default-features = false, optional = true} ics23 = "0.7.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" } diff --git a/core/src/types/storage.rs b/core/src/types/storage.rs index 099fd2ba51..d0cd584bf4 100644 --- a/core/src/types/storage.rs +++ b/core/src/types/storage.rs @@ -8,10 +8,10 @@ use std::str::FromStr; use arse_merkle_tree::traits::Value; use arse_merkle_tree::{InternalKey, Key as TreeKey}; -use bit_vec::BitVec; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use data_encoding::BASE32HEX_NOPAD; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use index_set::vec::VecIndexSet; +use serde::{Deserialize, Serialize}; use thiserror::Error; use crate::bytes::ByteBuf; @@ -92,24 +92,8 @@ impl From for u32 { } } -fn serialize_bitvec(x: &BitVec, s: S) -> std::result::Result -where - S: Serializer, -{ - Serialize::serialize(&x.to_bytes(), s) -} - -fn deserialize_bitvec<'de, D>( - deserializer: D, -) -> std::result::Result -where - D: Deserializer<'de>, -{ - let s: Vec = Deserialize::deserialize(deserializer)?; - Ok(BitVec::from_bytes(&s)) -} - -/// Represents the accepted transactions in a block +/// Represents the indices of the accepted transactions +/// in a block. #[derive( Clone, PartialEq, @@ -120,46 +104,36 @@ where Debug, Serialize, Deserialize, + BorshSerialize, + BorshDeserialize, Default, )] -pub struct BlockResults( - #[serde(serialize_with = "serialize_bitvec")] - #[serde(deserialize_with = "deserialize_bitvec")] - BitVec, -); +pub struct BlockResults(VecIndexSet); impl BlockResults { - /// Create `len` rejection results - pub fn with_len(len: usize) -> Self { - BlockResults(BitVec::from_elem(len, true)) + /// Accept the tx at the given position. + #[inline] + pub fn accept(&mut self, index: usize) { + self.0.remove(index) } - /// Accept the tx at the given position - pub fn accept(&mut self, idx: usize) { - self.0.set(idx, false) + /// Reject the tx at the given position. + #[inline] + pub fn reject(&mut self, index: usize) { + self.0.insert(index) } - /// Reject the tx at the given position - pub fn reject(&mut self, idx: usize) { - self.0.set(idx, true) + /// Check if the tx at the given position is accepted. + #[inline] + pub fn is_accepted(&self, index: usize) -> bool { + !self.0.contains(index) } - /// Check if the tx at the given position is accepted - pub fn is_accepted(&self, idx: usize) -> bool { - !self.0[idx] - } -} - -impl BorshSerialize for BlockResults { - fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - BorshSerialize::serialize(&self.0.to_bytes(), writer) - } -} - -impl BorshDeserialize for BlockResults { - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let vec: Vec<_> = BorshDeserialize::deserialize(buf)?; - Ok(Self(BitVec::from_bytes(&vec))) + /// Return an iterator over the removed txs + /// in this [`BlockResults`] instance. + #[inline] + pub fn iter_removed(&self) -> impl Iterator + '_ { + self.0.iter() } } diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 0eaff87a0b..5309dd35ed 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -2047,6 +2047,15 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "index-set" +version = "0.7.1" +source = "git+https://github.com/heliaxdev/index-set?tag=v0.7.1#dc24cdbbe3664514d59f1a4c4031863fc565f1c2" +dependencies = [ + "borsh", + "serde", +] + [[package]] name = "indexmap" version = "1.9.1" @@ -2497,7 +2506,6 @@ dependencies = [ "ark-serialize", "bech32", "bellman", - "bit-vec", "borsh", "chrono", "data-encoding", @@ -2507,6 +2515,7 @@ dependencies = [ "ibc", "ibc-proto", "ics23", + "index-set", "itertools", "libsecp256k1", "masp_primitives", diff --git a/wasm_for_tests/wasm_source/Cargo.lock b/wasm_for_tests/wasm_source/Cargo.lock index e3e5e8a2a9..187bb6284b 100644 --- a/wasm_for_tests/wasm_source/Cargo.lock +++ b/wasm_for_tests/wasm_source/Cargo.lock @@ -2047,6 +2047,15 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "index-set" +version = "0.7.1" +source = "git+https://github.com/heliaxdev/index-set?tag=v0.7.1#dc24cdbbe3664514d59f1a4c4031863fc565f1c2" +dependencies = [ + "borsh", + "serde", +] + [[package]] name = "indexmap" version = "1.9.1" @@ -2497,7 +2506,6 @@ dependencies = [ "ark-serialize", "bech32", "bellman", - "bit-vec", "borsh", "chrono", "data-encoding", @@ -2507,6 +2515,7 @@ dependencies = [ "ibc", "ibc-proto", "ics23", + "index-set", "itertools", "libsecp256k1", "masp_primitives", From 06df888595289c87227d66349516a10b81530327 Mon Sep 17 00:00:00 2001 From: Gianmarco Fraccaroli <> Date: Wed, 7 Dec 2022 10:02:13 +0100 Subject: [PATCH 32/41] ci: use specific ubuntu version --- .github/workflows/automation.yml | 2 +- .github/workflows/build-and-test-bridge.yml | 28 ++++++++++----------- .github/workflows/build-and-test.yml | 28 ++++++++++----------- .github/workflows/build-tendermint.yml | 2 +- .github/workflows/checks.yml | 2 +- .github/workflows/cron.yml | 2 +- .github/workflows/docker.yml | 2 +- .github/workflows/docs.yml | 12 ++++----- .github/workflows/release.yml | 8 +++--- 9 files changed, 43 insertions(+), 43 deletions(-) diff --git a/.github/workflows/automation.yml b/.github/workflows/automation.yml index 8cc686b888..48accef46c 100644 --- a/.github/workflows/automation.yml +++ b/.github/workflows/automation.yml @@ -21,7 +21,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] make: - name: Update wasm comment: pls update wasm diff --git a/.github/workflows/build-and-test-bridge.yml b/.github/workflows/build-and-test-bridge.yml index f638e50250..6470c61698 100644 --- a/.github/workflows/build-and-test-bridge.yml +++ b/.github/workflows/build-and-test-bridge.yml @@ -34,7 +34,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] wasm_cache_version: ["v2"] mold_version: [1.7.0] @@ -76,7 +76,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] wasm_cache_version: ["v2"] mold_version: [1.7.0] @@ -117,7 +117,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] steps: - name: Configure AWS Credentials @@ -143,7 +143,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] nightly_version: [nightly-2022-11-03] mold_version: [1.7.0] make: @@ -175,8 +175,8 @@ jobs: with: role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master aws-region: eu-west-1 - - name: Install sccache (ubuntu-latest) - if: matrix.os == 'ubuntu-latest' + - name: Install sccache (ubuntu-20.04) + if: matrix.os == 'ubuntu-20.04' env: LINK: https://github.com/mozilla/sccache/releases/download SCCACHE_VERSION: v0.3.0 @@ -246,7 +246,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] mold_version: [1.7.0] make: - name: ABCI Release build @@ -276,8 +276,8 @@ jobs: with: role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master aws-region: eu-west-1 - - name: Install sccache (ubuntu-latest) - if: matrix.os == 'ubuntu-latest' + - name: Install sccache (ubuntu-20.04) + if: matrix.os == 'ubuntu-20.04' env: LINK: https://github.com/mozilla/sccache/releases/download SCCACHE_VERSION: v0.3.0 @@ -348,7 +348,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] nightly_version: [nightly-2022-05-20] mold_version: [1.7.0] make: @@ -358,14 +358,14 @@ jobs: cache_key: namada cache_version: v2 tendermint_artifact: tendermint-unreleased-ad825dcadbd4b98c3f91ce5a711e4fb36a69c377 - wait_for: namada-release-eth (ubuntu-latest, 1.7.0, ABCI Release build, namada-e2e-release, v2) + 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-ad825dcadbd4b98c3f91ce5a711e4fb36a69c377 - wait_for: namada-release-eth (ubuntu-latest, 1.7.0, ABCI Release build, namada-e2e-release, v2) + wait_for: namada-release-eth (ubuntu-20.04, 1.7.0, ABCI Release build, namada-e2e-release, v2) env: CARGO_INCREMENTAL: 0 @@ -389,8 +389,8 @@ jobs: with: role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master aws-region: eu-west-1 - - name: Install sccache (ubuntu-latest) - if: matrix.os == 'ubuntu-latest' + - name: Install sccache (ubuntu-20.04) + if: matrix.os == 'ubuntu-20.04' env: LINK: https://github.com/mozilla/sccache/releases/download SCCACHE_VERSION: v0.3.0 diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 97513e705c..0789e8f880 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -35,7 +35,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] wasm_cache_version: ["v2"] mold_version: [1.7.0] @@ -77,7 +77,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] wasm_cache_version: ["v2"] mold_version: [1.7.0] @@ -119,7 +119,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] steps: - name: Configure AWS Credentials @@ -145,7 +145,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] nightly_version: [nightly-2022-11-03] mold_version: [1.7.0] make: @@ -177,8 +177,8 @@ jobs: with: role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master aws-region: eu-west-1 - - name: Install sccache (ubuntu-latest) - if: matrix.os == 'ubuntu-latest' + - name: Install sccache (ubuntu-20.04) + if: matrix.os == 'ubuntu-20.04' env: LINK: https://github.com/mozilla/sccache/releases/download SCCACHE_VERSION: v0.3.0 @@ -248,7 +248,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] mold_version: [1.7.0] make: - name: ABCI Release build @@ -278,8 +278,8 @@ jobs: with: role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master aws-region: eu-west-1 - - name: Install sccache (ubuntu-latest) - if: matrix.os == 'ubuntu-latest' + - name: Install sccache (ubuntu-20.04) + if: matrix.os == 'ubuntu-20.04' env: LINK: https://github.com/mozilla/sccache/releases/download SCCACHE_VERSION: v0.3.0 @@ -350,7 +350,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] nightly_version: [nightly-2022-05-20] mold_version: [1.7.0] make: @@ -360,14 +360,14 @@ jobs: cache_key: namada cache_version: v2 tendermint_artifact: tendermint-unreleased-ad825dcadbd4b98c3f91ce5a711e4fb36a69c377 - wait_for: namada-release (ubuntu-latest, 1.7.0, ABCI Release build, namada-e2e-release, v2) + wait_for: namada-release (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-ad825dcadbd4b98c3f91ce5a711e4fb36a69c377 - wait_for: namada-release (ubuntu-latest, 1.7.0, ABCI Release build, namada-e2e-release, v2) + wait_for: namada-release (ubuntu-20.04, 1.7.0, ABCI Release build, namada-e2e-release, v2) env: CARGO_INCREMENTAL: 0 @@ -391,8 +391,8 @@ jobs: with: role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master aws-region: eu-west-1 - - name: Install sccache (ubuntu-latest) - if: matrix.os == 'ubuntu-latest' + - name: Install sccache (ubuntu-20.04) + if: matrix.os == 'ubuntu-20.04' env: LINK: https://github.com/mozilla/sccache/releases/download SCCACHE_VERSION: v0.3.0 diff --git a/.github/workflows/build-tendermint.yml b/.github/workflows/build-tendermint.yml index 7914a39a49..1e78f1d986 100644 --- a/.github/workflows/build-tendermint.yml +++ b/.github/workflows/build-tendermint.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] make: - name: tendermint-unreleased repository: heliaxdev/tendermint diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 7364a477fa..dd62a78d6a 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -26,7 +26,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] nightly_version: [nightly-2022-11-03] make: - name: Clippy diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 260b0de4c6..aa6b779897 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] nightly_version: [nightly-2022-11-03] make: - name: Audit diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index d2e41e2084..1e284e8584 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -27,7 +27,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] make: - name: Build & Push WASM docker image image: wasm diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index b015bbd7cc..6bd95c514e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -26,7 +26,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] nightly_version: [nightly-2022-11-03] mdbook_version: [rust-lang/mdbook@v0.4.18] mdbook_mermaid: [badboy/mdbook-mermaid@v0.11.1] @@ -78,8 +78,8 @@ jobs: with: role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master aws-region: eu-west-1 - - name: Install sccache (ubuntu-latest) - if: matrix.os == 'ubuntu-latest' + - name: Install sccache (ubuntu-20.04) + if: matrix.os == 'ubuntu-20.04' env: LINK: https://github.com/mozilla/sccache/releases/download SCCACHE_VERSION: v0.3.0 @@ -145,7 +145,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] env: CARGO_INCREMENTAL: 0 @@ -168,8 +168,8 @@ jobs: with: role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master aws-region: eu-west-1 - - name: Install sccache (ubuntu-latest) - if: matrix.os == 'ubuntu-latest' + - name: Install sccache (ubuntu-20.04) + if: matrix.os == 'ubuntu-20.04' env: LINK: https://github.com/mozilla/sccache/releases/download SCCACHE_VERSION: v0.3.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 51bd8ea3e8..114ca0455d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest] + os: [ubuntu-20.04, macos-latest] namada_cache_version: [v1] make: - name: Build package @@ -41,8 +41,8 @@ jobs: with: role-to-assume: arn:aws:iam::375643557360:role/anoma-github-action-ci-master aws-region: eu-west-1 - - name: Install sccache (ubuntu-latest) - if: matrix.os == 'ubuntu-latest' + - name: Install sccache (ubuntu-20.04) + if: matrix.os == 'ubuntu-20.04' env: LINK: https://github.com/mozilla/sccache/releases/download SCCACHE_VERSION: v0.3.0 @@ -94,7 +94,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-20.04] steps: - id: get_version From 3250496531220007ed8ea30fea8d20ea586a908a Mon Sep 17 00:00:00 2001 From: James Hiew Date: Fri, 25 Nov 2022 13:25:36 +0000 Subject: [PATCH 33/41] Check code compiles with abcipp feature enabled in CI --- .github/workflows/checks.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 7364a477fa..e99288a0d2 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -29,6 +29,10 @@ jobs: os: [ubuntu-latest] nightly_version: [nightly-2022-11-03] make: + - name: Check ABCI++ + command: check-abcipp + cache_subkey: abcipp + cache_version: v1 - name: Clippy command: clippy cache_subkey: clippy From eac60ac97aa12d303fa1b2251a86a81d1a667f86 Mon Sep 17 00:00:00 2001 From: James Hiew Date: Fri, 25 Nov 2022 13:42:03 +0000 Subject: [PATCH 34/41] Add changelog --- .changelog/unreleased/ci/824-abcipp-ci.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 .changelog/unreleased/ci/824-abcipp-ci.md diff --git a/.changelog/unreleased/ci/824-abcipp-ci.md b/.changelog/unreleased/ci/824-abcipp-ci.md new file mode 100644 index 0000000000..541b5a3590 --- /dev/null +++ b/.changelog/unreleased/ci/824-abcipp-ci.md @@ -0,0 +1 @@ +- Run `make check-abcipp` in CI ([#824](https://github.com/anoma/namada/pull/824)) \ No newline at end of file From ff9e899c966a966c7f025d52704f201069660a84 Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Tue, 13 Dec 2022 00:53:28 -0500 Subject: [PATCH 35/41] changelog: add #813 --- .../unreleased/testing/813-fix-pos-tx-test-input-lower-bound.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/testing/813-fix-pos-tx-test-input-lower-bound.md diff --git a/.changelog/unreleased/testing/813-fix-pos-tx-test-input-lower-bound.md b/.changelog/unreleased/testing/813-fix-pos-tx-test-input-lower-bound.md new file mode 100644 index 0000000000..d33ceccc68 --- /dev/null +++ b/.changelog/unreleased/testing/813-fix-pos-tx-test-input-lower-bound.md @@ -0,0 +1,2 @@ +- Allow size zero bonds in PoS for testing. + ([#813](https://github.com/anoma/namada/pull/813)) \ No newline at end of file From 8465fb9b54cf3a354f50ed2fa0802f9f52998370 Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Tue, 13 Dec 2022 00:56:39 -0500 Subject: [PATCH 36/41] changelog: add #846 --- .changelog/unreleased/improvements/846-remove-merkle-values.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/846-remove-merkle-values.md diff --git a/.changelog/unreleased/improvements/846-remove-merkle-values.md b/.changelog/unreleased/improvements/846-remove-merkle-values.md new file mode 100644 index 0000000000..7d117a3974 --- /dev/null +++ b/.changelog/unreleased/improvements/846-remove-merkle-values.md @@ -0,0 +1,2 @@ +- Remove the MerkleValue type and just use byte slices for Merkle tree values. + ([#846](https://github.com/anoma/namada/pull/846)) \ No newline at end of file From 685125b802e54fcf6e1b596b4c84ddf7225634bd Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Tue, 13 Dec 2022 01:20:07 -0500 Subject: [PATCH 37/41] changelog: add #859 --- .../unreleased/improvements/859-index-set-block-results.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/859-index-set-block-results.md diff --git a/.changelog/unreleased/improvements/859-index-set-block-results.md b/.changelog/unreleased/improvements/859-index-set-block-results.md new file mode 100644 index 0000000000..993f2b5422 --- /dev/null +++ b/.changelog/unreleased/improvements/859-index-set-block-results.md @@ -0,0 +1,2 @@ +- Use index-set to reduce serialized size of block results. + ([#859](https://github.com/anoma/namada/pull/859)) \ No newline at end of file From f6262aa208fb604fd6807adc398b23c1ae926d05 Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Tue, 13 Dec 2022 01:54:58 -0500 Subject: [PATCH 38/41] Namada 0.12.0 --- .../ci/824-abcipp-ci.md | 0 .../ci/834-ethbridge-ci-stacked-prs.md | 0 .../docs/440-replay-protection-specs.md | 0 .../docs/680-multisig-specs.md | 0 .../833-wasm-vm-allow-sign-extend.md | 0 .../improvements/846-remove-merkle-values.md | 0 .../859-index-set-block-results.md | 0 .changelog/v0.12.0/summary.md | 1 + .../813-fix-pos-tx-test-input-lower-bound.md | 0 CHANGELOG.md | 31 +++++++++++++++ Cargo.lock | 20 +++++----- apps/Cargo.toml | 2 +- core/Cargo.toml | 2 +- encoding_spec/Cargo.toml | 2 +- macros/Cargo.toml | 2 +- proof_of_stake/Cargo.toml | 2 +- shared/Cargo.toml | 2 +- tests/Cargo.toml | 2 +- tx_prelude/Cargo.toml | 2 +- vm_env/Cargo.toml | 2 +- vp_prelude/Cargo.toml | 2 +- wasm/Cargo.lock | 22 +++++------ wasm/checksums.json | 36 +++++++++--------- wasm/tx_template/Cargo.toml | 2 +- wasm/vp_template/Cargo.toml | 2 +- wasm/wasm_source/Cargo.toml | 2 +- wasm_for_tests/tx_memory_limit.wasm | Bin 133398 -> 133398 bytes wasm_for_tests/tx_mint_tokens.wasm | Bin 352856 -> 352858 bytes wasm_for_tests/tx_proposal_code.wasm | Bin 201328 -> 201332 bytes wasm_for_tests/tx_read_storage_key.wasm | Bin 152564 -> 152564 bytes wasm_for_tests/tx_write_storage_key.wasm | Bin 226646 -> 226795 bytes wasm_for_tests/vp_always_false.wasm | Bin 156489 -> 156489 bytes wasm_for_tests/vp_always_true.wasm | Bin 156489 -> 156489 bytes wasm_for_tests/vp_eval.wasm | Bin 157426 -> 157426 bytes wasm_for_tests/vp_memory_limit.wasm | Bin 158937 -> 158937 bytes wasm_for_tests/vp_read_storage_key.wasm | Bin 170817 -> 170817 bytes wasm_for_tests/wasm_source/Cargo.lock | 18 ++++----- wasm_for_tests/wasm_source/Cargo.toml | 2 +- 38 files changed, 94 insertions(+), 62 deletions(-) rename .changelog/{unreleased => v0.12.0}/ci/824-abcipp-ci.md (100%) rename .changelog/{unreleased => v0.12.0}/ci/834-ethbridge-ci-stacked-prs.md (100%) rename .changelog/{unreleased => v0.12.0}/docs/440-replay-protection-specs.md (100%) rename .changelog/{unreleased => v0.12.0}/docs/680-multisig-specs.md (100%) rename .changelog/{unreleased => v0.12.0}/improvements/833-wasm-vm-allow-sign-extend.md (100%) rename .changelog/{unreleased => v0.12.0}/improvements/846-remove-merkle-values.md (100%) rename .changelog/{unreleased => v0.12.0}/improvements/859-index-set-block-results.md (100%) create mode 100644 .changelog/v0.12.0/summary.md rename .changelog/{unreleased => v0.12.0}/testing/813-fix-pos-tx-test-input-lower-bound.md (100%) diff --git a/.changelog/unreleased/ci/824-abcipp-ci.md b/.changelog/v0.12.0/ci/824-abcipp-ci.md similarity index 100% rename from .changelog/unreleased/ci/824-abcipp-ci.md rename to .changelog/v0.12.0/ci/824-abcipp-ci.md diff --git a/.changelog/unreleased/ci/834-ethbridge-ci-stacked-prs.md b/.changelog/v0.12.0/ci/834-ethbridge-ci-stacked-prs.md similarity index 100% rename from .changelog/unreleased/ci/834-ethbridge-ci-stacked-prs.md rename to .changelog/v0.12.0/ci/834-ethbridge-ci-stacked-prs.md diff --git a/.changelog/unreleased/docs/440-replay-protection-specs.md b/.changelog/v0.12.0/docs/440-replay-protection-specs.md similarity index 100% rename from .changelog/unreleased/docs/440-replay-protection-specs.md rename to .changelog/v0.12.0/docs/440-replay-protection-specs.md diff --git a/.changelog/unreleased/docs/680-multisig-specs.md b/.changelog/v0.12.0/docs/680-multisig-specs.md similarity index 100% rename from .changelog/unreleased/docs/680-multisig-specs.md rename to .changelog/v0.12.0/docs/680-multisig-specs.md diff --git a/.changelog/unreleased/improvements/833-wasm-vm-allow-sign-extend.md b/.changelog/v0.12.0/improvements/833-wasm-vm-allow-sign-extend.md similarity index 100% rename from .changelog/unreleased/improvements/833-wasm-vm-allow-sign-extend.md rename to .changelog/v0.12.0/improvements/833-wasm-vm-allow-sign-extend.md diff --git a/.changelog/unreleased/improvements/846-remove-merkle-values.md b/.changelog/v0.12.0/improvements/846-remove-merkle-values.md similarity index 100% rename from .changelog/unreleased/improvements/846-remove-merkle-values.md rename to .changelog/v0.12.0/improvements/846-remove-merkle-values.md diff --git a/.changelog/unreleased/improvements/859-index-set-block-results.md b/.changelog/v0.12.0/improvements/859-index-set-block-results.md similarity index 100% rename from .changelog/unreleased/improvements/859-index-set-block-results.md rename to .changelog/v0.12.0/improvements/859-index-set-block-results.md diff --git a/.changelog/v0.12.0/summary.md b/.changelog/v0.12.0/summary.md new file mode 100644 index 0000000000..56982aa446 --- /dev/null +++ b/.changelog/v0.12.0/summary.md @@ -0,0 +1 @@ +Namada 0.12.0 is a scheduled minor release. diff --git a/.changelog/unreleased/testing/813-fix-pos-tx-test-input-lower-bound.md b/.changelog/v0.12.0/testing/813-fix-pos-tx-test-input-lower-bound.md similarity index 100% rename from .changelog/unreleased/testing/813-fix-pos-tx-test-input-lower-bound.md rename to .changelog/v0.12.0/testing/813-fix-pos-tx-test-input-lower-bound.md diff --git a/CHANGELOG.md b/CHANGELOG.md index c11e94955a..9aaab24c50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,36 @@ # CHANGELOG +## v0.12.0 + +Namada 0.12.0 is a scheduled minor release. + +### CI + +- Run `make check-abcipp` in CI ([#824](https://github.com/anoma/namada/pull/824)) +- Run Ethereum bridge CI against more branches + ([#834](https://github.com/anoma/namada/pull/834)) + +### DOCS + +- Adds specs for replay protection + ([#440](https://github.com/anoma/namada/pull/440)) +- Adds specs for multisig accounts + ([#680](https://github.com/anoma/namada/pull/680)) + +### IMPROVEMENTS + +- Allow sign extension opcodes in WASM + ([#833](https://github.com/anoma/namada/pull/833)) +- Remove the MerkleValue type and just use byte slices for Merkle tree values. + ([#846](https://github.com/anoma/namada/pull/846)) +- Use index-set to reduce serialized size of block results. + ([#859](https://github.com/anoma/namada/pull/859)) + +### TESTING + +- Allow size zero bonds in PoS for testing. + ([#813](https://github.com/anoma/namada/pull/813)) + ## v0.11.0 Namada 0.11.0 is a scheduled minor release. diff --git a/Cargo.lock b/Cargo.lock index 0479a3215a..1a0a9ae50b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3635,7 +3635,7 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "namada" -version = "0.11.0" +version = "0.12.0" dependencies = [ "assert_matches", "async-trait", @@ -3692,7 +3692,7 @@ dependencies = [ [[package]] name = "namada_apps" -version = "0.11.0" +version = "0.12.0" dependencies = [ "ark-serialize", "ark-std", @@ -3778,7 +3778,7 @@ dependencies = [ [[package]] name = "namada_core" -version = "0.11.0" +version = "0.12.0" dependencies = [ "ark-bls12-381", "ark-ec", @@ -3830,7 +3830,7 @@ dependencies = [ [[package]] name = "namada_encoding_spec" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "itertools", @@ -3841,7 +3841,7 @@ dependencies = [ [[package]] name = "namada_macros" -version = "0.11.0" +version = "0.12.0" dependencies = [ "quote", "syn", @@ -3849,7 +3849,7 @@ dependencies = [ [[package]] name = "namada_proof_of_stake" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "derivative", @@ -3863,7 +3863,7 @@ dependencies = [ [[package]] name = "namada_tests" -version = "0.11.0" +version = "0.12.0" dependencies = [ "assert_cmd", "borsh", @@ -3907,7 +3907,7 @@ dependencies = [ [[package]] name = "namada_tx_prelude" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "masp_primitives", @@ -3922,7 +3922,7 @@ dependencies = [ [[package]] name = "namada_vm_env" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "hex", @@ -3933,7 +3933,7 @@ dependencies = [ [[package]] name = "namada_vp_prelude" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "namada_core", diff --git a/apps/Cargo.toml b/apps/Cargo.toml index 8df89fbcb9..bba5ca030e 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -6,7 +6,7 @@ license = "GPL-3.0" name = "namada_apps" readme = "../README.md" resolver = "2" -version = "0.11.0" +version = "0.12.0" default-run = "namada" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/core/Cargo.toml b/core/Cargo.toml index db5489078d..7754310669 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_core" resolver = "2" -version = "0.11.0" +version = "0.12.0" [features] default = [] diff --git a/encoding_spec/Cargo.toml b/encoding_spec/Cargo.toml index 3c798c53cd..2fc6c2cc18 100644 --- a/encoding_spec/Cargo.toml +++ b/encoding_spec/Cargo.toml @@ -6,7 +6,7 @@ license = "GPL-3.0" name = "namada_encoding_spec" readme = "../README.md" resolver = "2" -version = "0.11.0" +version = "0.12.0" [features] default = ["abciplus"] diff --git a/macros/Cargo.toml b/macros/Cargo.toml index e188483bb9..4bb96bbe49 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_macros" resolver = "2" -version = "0.11.0" +version = "0.12.0" [lib] proc-macro = true diff --git a/proof_of_stake/Cargo.toml b/proof_of_stake/Cargo.toml index d5dea7aa95..822ef2fabb 100644 --- a/proof_of_stake/Cargo.toml +++ b/proof_of_stake/Cargo.toml @@ -6,7 +6,7 @@ license = "GPL-3.0" name = "namada_proof_of_stake" readme = "../README.md" resolver = "2" -version = "0.11.0" +version = "0.12.0" [features] default = ["abciplus"] diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 0156a40d40..efcd0e81a0 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada" resolver = "2" -version = "0.11.0" +version = "0.12.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 147f23ba42..4403453dc9 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_tests" resolver = "2" -version = "0.11.0" +version = "0.12.0" [features] default = ["abciplus", "wasm-runtime"] diff --git a/tx_prelude/Cargo.toml b/tx_prelude/Cargo.toml index 9eaac06748..945f14ad7b 100644 --- a/tx_prelude/Cargo.toml +++ b/tx_prelude/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_tx_prelude" resolver = "2" -version = "0.11.0" +version = "0.12.0" [features] default = ["abciplus"] diff --git a/vm_env/Cargo.toml b/vm_env/Cargo.toml index 6a56c8ee13..cf4e6a7d9f 100644 --- a/vm_env/Cargo.toml +++ b/vm_env/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_vm_env" resolver = "2" -version = "0.11.0" +version = "0.12.0" [features] default = ["abciplus"] diff --git a/vp_prelude/Cargo.toml b/vp_prelude/Cargo.toml index 05f0b33f21..a0d76feadc 100644 --- a/vp_prelude/Cargo.toml +++ b/vp_prelude/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_vp_prelude" resolver = "2" -version = "0.11.0" +version = "0.12.0" [features] default = ["abciplus"] diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index a5b1ac403f..bd82ea3be8 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -2456,7 +2456,7 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "namada" -version = "0.11.0" +version = "0.12.0" dependencies = [ "async-trait", "bellman", @@ -2500,7 +2500,7 @@ dependencies = [ [[package]] name = "namada_core" -version = "0.11.0" +version = "0.12.0" dependencies = [ "ark-bls12-381", "ark-serialize", @@ -2541,7 +2541,7 @@ dependencies = [ [[package]] name = "namada_macros" -version = "0.11.0" +version = "0.12.0" dependencies = [ "quote", "syn", @@ -2549,7 +2549,7 @@ dependencies = [ [[package]] name = "namada_proof_of_stake" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "derivative", @@ -2563,7 +2563,7 @@ dependencies = [ [[package]] name = "namada_tests" -version = "0.11.0" +version = "0.12.0" dependencies = [ "chrono", "concat-idents", @@ -2592,7 +2592,7 @@ dependencies = [ [[package]] name = "namada_tx_prelude" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "masp_primitives", @@ -2607,7 +2607,7 @@ dependencies = [ [[package]] name = "namada_vm_env" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "hex", @@ -2618,7 +2618,7 @@ dependencies = [ [[package]] name = "namada_vp_prelude" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "namada_core", @@ -2631,7 +2631,7 @@ dependencies = [ [[package]] name = "namada_wasm" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "getrandom 0.2.8", @@ -4601,7 +4601,7 @@ dependencies = [ [[package]] name = "tx_template" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "getrandom 0.2.8", @@ -4738,7 +4738,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vp_template" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "getrandom 0.2.8", diff --git a/wasm/checksums.json b/wasm/checksums.json index e36da05ea8..b13508300c 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,20 +1,20 @@ { - "tx_bond.wasm": "tx_bond.aec5b289d8450c4e65939c5883f2868f9f77389c8dbf59a445e72e33faefc919.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.1edf31292f2e5693e6193ca7d9cce7d004d726d972ceea0df7f7a437689939f9.wasm", - "tx_ibc.wasm": "tx_ibc.4fd06fe6f9edc763c9d860bc0d61947a5c2d6fb35bb8eb5110e476cf8d6c79b7.wasm", - "tx_init_account.wasm": "tx_init_account.9b74ae5630bf2fbf76a1225f5e0e09ff9caf136bfe8652bc4f5d5a03058e14f1.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.c65e1c614ca8389a1c9a8fcb0610fb79df6466b646d8a7881fe9031140a8dae0.wasm", - "tx_init_validator.wasm": "tx_init_validator.c1db05715b899c8137783a0e8a6d53da61821337f66da5af5e6cd8072a7f601f.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.f0014d214e63424a433c6be6db6c79e74287cdc4b2549fd15d93e8f30ce42381.wasm", - "tx_transfer.wasm": "tx_transfer.a01e7c3b87edb1e60f0916b3037a2f966ccdf3a7d05ececa865375bc7630002f.wasm", - "tx_unbond.wasm": "tx_unbond.47e7f9e63d29062ef5f6053bf46d1c270574c63b7f3bacf6ca57a64a457ec432.wasm", - "tx_update_vp.wasm": "tx_update_vp.e6ed544a61534b5f317b5707e82217acf6a613a90f477988ddb8c1578f762997.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.885ead3f51aa2b697730ad645470b024963d4412c0a3d31a233ea2d2d3bfeef1.wasm", - "tx_withdraw.wasm": "tx_withdraw.646065e1c698e99c9e97fdd2532370575d416fcbb38c8e5d985e7e3bf914a883.wasm", - "vp_implicit.wasm": "vp_implicit.751ce0bbe7cd7ec4a6a4a6429f6324c40fe4630a39a1cc416501f911d19cc7e4.wasm", - "vp_masp.wasm": "vp_masp.3dd1b4906712ad66d784b39c9fad127d4a2a221533f706b8309db797d050053c.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.bf5b09d4fe21e1e9a1da59114a55c93772cf1f34f9de59a6c92781be5974c765.wasm", - "vp_token.wasm": "vp_token.6fe84584802e659d87d1839fa326a5208d738c14db85f29a44c6ac2f05371f98.wasm", - "vp_user.wasm": "vp_user.2bc988fa034a998c9e948a7f1bd8ca9681e8366646055e125117185a43b229ba.wasm", - "vp_validator.wasm": "vp_validator.42913ba7b50073ac862b2fcc10fc8fa04216abdd7fff12614bffaae464edccf4.wasm" + "tx_bond.wasm": "tx_bond.be9c75f96b3b4880b7934d42ee218582b6304f6326a4588d1e6ac1ea4cc61c49.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.cd861e0e82f4934be6d8382d6fff98286b4fadbc20ab826b9e817f6666021273.wasm", + "tx_ibc.wasm": "tx_ibc.13daeb0c88abba264d3052129eda0713bcf1a71f6f69bf37ec2494d0d9119f1f.wasm", + "tx_init_account.wasm": "tx_init_account.e21cfd7e96802f8e841613fb89f1571451401d002a159c5e9586855ac1374df5.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.b9a77bc9e416f33f1e715f25696ae41582e1b379422f7a643549884e0c73e9de.wasm", + "tx_init_validator.wasm": "tx_init_validator.1e9732873861c625f239e74245f8c504a57359c06614ba40387a71811ca4a097.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.47bc922a8be5571620a647ae442a1af7d03d05d29bef95f0b32cdfe00b11fee9.wasm", + "tx_transfer.wasm": "tx_transfer.bbd1ef5d9461c78f0288986de046baad77e10671addc5edaf3c68ea1ae4ecc99.wasm", + "tx_unbond.wasm": "tx_unbond.c0a690d0ad43a94294a6405bae3327f638a657446c74dc61dbb3a4d2ce488b5e.wasm", + "tx_update_vp.wasm": "tx_update_vp.ee2e9b882c4accadf4626e87d801c9ac8ea8c61ccea677e0532fc6c1ee7db6a2.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.263fd9f4cb40f283756f394d86bdea3417e9ecd0568d6582c07a5b6bd14287d6.wasm", + "tx_withdraw.wasm": "tx_withdraw.6ce8faf6a32340178ddeaeb91a9b40e7f0433334e5c1f357964bf8e11d0077f1.wasm", + "vp_implicit.wasm": "vp_implicit.17f5c2af947ccfadce22d0fffecde1a1b4bc4ca3acd5dd8b459c3dce4afcb4e8.wasm", + "vp_masp.wasm": "vp_masp.5620cb6e555161641337d308851c760fbab4f9d3693cfd378703aa55e285249d.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.362584b063cc4aaf8b72af0ed8af8d05a179ebefec596b6ab65e0ca255ec3c80.wasm", + "vp_token.wasm": "vp_token.a289723dd182fe0206e6c4cf1f426a6100787b20e2653d2fad6031e8106157f3.wasm", + "vp_user.wasm": "vp_user.b83b2d0616bb2244c8a92021665a0be749282a53fe1c493e98c330a6ed983833.wasm", + "vp_validator.wasm": "vp_validator.59e3e7729e14eeacc17d76b736d1760d59a1a6e9d6acbc9a870e1835438f524a.wasm" } \ No newline at end of file diff --git a/wasm/tx_template/Cargo.toml b/wasm/tx_template/Cargo.toml index e2f7c461f1..345ec86237 100644 --- a/wasm/tx_template/Cargo.toml +++ b/wasm/tx_template/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "tx_template" resolver = "2" -version = "0.11.0" +version = "0.12.0" [lib] crate-type = ["cdylib"] diff --git a/wasm/vp_template/Cargo.toml b/wasm/vp_template/Cargo.toml index e735645a20..07ae7758db 100644 --- a/wasm/vp_template/Cargo.toml +++ b/wasm/vp_template/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "vp_template" resolver = "2" -version = "0.11.0" +version = "0.12.0" [lib] crate-type = ["cdylib"] diff --git a/wasm/wasm_source/Cargo.toml b/wasm/wasm_source/Cargo.toml index 08b9f55130..36731a505b 100644 --- a/wasm/wasm_source/Cargo.toml +++ b/wasm/wasm_source/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_wasm" resolver = "2" -version = "0.11.0" +version = "0.12.0" [lib] crate-type = ["cdylib"] diff --git a/wasm_for_tests/tx_memory_limit.wasm b/wasm_for_tests/tx_memory_limit.wasm index f40653e3c7617d7aae1dadbb427441c9d5310e44..9c4ea2abccbcef666288b7ccb0a12e07dd9d4c27 100755 GIT binary patch delta 516 zcmbO>iDTL%jtOg+nOT@7u92B|x>P|`McUZJ!jj+0+J@0wMnqK1RGgPzLW)OHRzpi$ zM^|ri0wV`Iqx|MfjuWhm%A1Y&K5#JVPgWMUXEfNHEPjEJ(QvYfWD=v{=IN5h*(O(8 zXm5^FxX;L_FxgJ&5~Jc|LFEh}S*2VLB!4NlFe+}YS5anSlUHQmVd0)!XP*vKDWIOM zpa2wL;#OdAY+!Vp(0YJDK#2i`w|TaD2_vJ@W&uq@CP58e1_f3}jx5KGEe9AJnFKU8 z2WiVPGHOgN&?yFzPjntLYHU8CyM>WaZgQ=@0-qevEEY!v#}Cth2Fp!etv`>^XtI}q zHKWnyHiN&+%4#6H6c`;D6xbD51Pno{1(YFT3Tz6DAhF5IOs+DjO|CKRWK^EaW~Rxg zKH0#`3rJR*%>;&rsJRs|JOa$M87D8Zuw;~<{JQi>ns9VUe5Ccm(3 zX4IUVV3D4(&`a#VfZ!I;oV>yI6i{gCe4r33P&+SBsKf3h hD>(Efzp|H_Ji$H`Xu&i4B7y^O*hYUus2VA1`; z6=rz!Yrski)TA++5lFvNC+TM)qRtj40pj$igrPWp_ezv|UABtTPN@eaS|~T6q&CaH zJf_)qN00*j=rxM{Z3F*mIDpXC=tK1rnDlUb1&}JW9AxNBZIZcByT`?}>n;~_t)2&( z!aQv>9M1ctG0%BVIhU9(omjXZQu`N~zBx-^)0`V)-gc+JR=e&CEo94`9tZVvJpsd} vXUE^LwElo4AD;(Vx^v>YFToXh=|?L@oz`hK*lk_jx7MY__VdLy^tS&1fnbbq diff --git a/wasm_for_tests/tx_mint_tokens.wasm b/wasm_for_tests/tx_mint_tokens.wasm index c043a68c426df5b22ac83f304e90d949ca1a7047..72e1e8f075a1bc7c0f7061848deb59e2bbb37dd9 100755 GIT binary patch delta 3925 zcmbtWc~}%z67PCF$gP9o2u6kx5C#yzs1W1XiUc*@ibuFnkwF1bqXZE&9tg0Pi zbQK(iL1R7&TpqsxGBXCwO)r6BppXwmPzZTY3Jf;G4)_epp$xXbAvg)2LzLz;+yrl5 zzvuzdxCs*@CrzFb7CUdLi|3Nd;Bf^Oe{voQU^k?G1v_CoY=m4`xF0GZUIqmYz?X0k zPQe*C3rFD?d;uq*8vX#sAv~rABF;et)I%*?gbT0_>fi?a3BHC)@JILtZovb%3iseX zT!(9L2mTCqVX0>W+=j>S9sCs@!Xx+=Y%a<}#m*i4FKww2`~_i{Cx^-H+!_#fl@Z&Zo$*wJQiETK^qL|EyKDF)NI+>AB(QuvQ)>X}ELn z$Vga8caHo7R?7Ndm2wY>glxJhBptHlUqfQKSIp=LSVvEf&VY6DkTKuezzS-dWs| zR6rtcmY&AU@=>i~NT#kI{R+vlK0F*SIz1u+I~pP~QO=B{$ov?&1nsz}VwC@hN=2Cv zU4-&gG}>}f3h(z&%fvDy0Njpacz=ZC-P{cV18Z`Oil3zC|wLiA}&;@Jl$^NG$)UJE(2 zbIM%jO<=I_6E=Z8YAH)JNhem;2+}*+99oo84V&orh5gWT)xswz6I0z$7NnlQ3}Y63 zjPmTF>W*d}Sx-!OFB`OGId^eS4c}u2mu2zo9FQipdn23E)YEYbB*yJM_vnHPp_$2aZtLT!s~n+a4xn(Hi!k5uE6$?eDcaXRjS<&e$DO+a3Cy8Y;W( zGytijGYXofkI)Ibqp|v`-C^h} zTkY9n$v-1i2L{2&!p${8AmLy5-XB0 zp_mS4%ph5@YpaNkK0l<(+=Z@E2h9Gu;EbRhFI6;S{ARzQsyJnVF8GV_hC0S68>F0C zsMF1Xh8C)hQzCj9Tc~A&?qmsXixZdSbk{;sVtRGUJA0p zo9CZ5e&V;;)0?5NSMGo7B|s(hZ_tsfP4t5Xf3B7_)O9G`gt=eKBkvp&;amFiy-@sS zjJS{YqP*vRUvN58g5B=Y!;LuiJq25-8OzTfd`9e3a5cb4G=73snQYIXPI%k^lEQ`(B z4I#`agSaaF>OpS>pDL5DLJ+`eCFdHHIm2Vc<0+U4$pTjQBV=+t>KS;!3T44F{1rui zmqsu;q7KoJi+YT6a3@#nu>UXqq}~Enu!;d>Fsi>bDUU!B&_sH&^gyCRyEu?!pwb2r z{BW=dL1YN34MD2ksV4H5ATk+KPJEBl0qjw9?-PI2EMyRgMzwbkS&M9+!DPDFAzNK$ zHYtJhRjP-P5>HmNp6J=A`Q)rJaW1)K!IqT}7kmTO=f*-7kU3;8F#SeSja+ph2~a*> zOuiSD@+5NCf<-SQE8Ba{m^O{@GpdYBBMjHARsf491Zu4`AS8x};Y{o#ML6yhUBV}tZj5u|q;_$ihx_^{s+GYY|x(iZ3 zq8stx2N4gIchiv+c^Hv?V4p811I5CU8urQ;hi#cb0y>!aezR9+id2=blnhb^->_|c zvAhH6WDfYSF3ZUjTRVKc5zjYhPb+&fiB7|;SCC+3^BO{lGB}$o0+L$SoNWJh8PX5l_!zpj1I)Vyios3WjN zvH+v#Wz&e>P6(}XSd3BZ(gn@d-l2%uVxwqwD9zCd993b{VJl@uZ!y=txt1$fiv@e( zCHk^+-lE6Aqgb;Cy0uOg)doi+=G;T{+x&~7loY)4>XO+I9KkQ?7eZ}cEk2h!q pSM(6`*^u7i`xfK#wL*7xwYTWykklq{+Bz#g5t~M_^0SyY;J+gX#y0=} delta 3988 zcmbtWdstOf7C&p9%UkH8fKY-LM7SUbWk8CiN6Y~$%NLsRMnJC0LunKVfg%Nz=SDXo ziV{+qhIMF8V@^7#NsiBQ6wA;lo6jkS>@lB>gU+md0YBm|Y3}!}$M1L6T6^uYALo94 z+`03(^TB+0M29la4`nB_W#teu8m$Cn{-Msw%oC6VVPyppyAEp#z9vEeyArTutjEMm^i%F_L|1lRM<}b++bo&Gth)zn$gS#o+!1D(1?* zwR>`4G2NhEbjq)L(J9o+^TW)=%7(=ZIkhp@i&Z+fiK)!Bzx98ltNR?DkfE4;=@?5> zQ6~?-1cXL7y(CbE(?%F_ZEqJbD>c#O-@kcvK6YO|aM*`h*aVNhh21~o)W+OEJw9VU zoOY1YB*~f#z1H_fHx0Sa27`I(#|-}f@%rBJ$^fKw_VDf)Sn8qnhVxi+skvjPi1n!Q znMwMx&tkFNCiuPt4XoUE)4-w+^^Pj1y0BQ|Sg~vT0=?80qX)nn?1W!3lrTU4ZYWV* z$F+gj*=GaH8^AB{Wf9RhJ|2qLf5+c}BK1yC4-)nB6Y5EV*DT0~9ZVlRoBa6bV$x^QN3#D!2f$8dT=+EMw1trrad6=R zbA%`JU6gE&@?r-TdBRTh+@e)L#HPg_EMc*~*{gvp=CR~HWU1~kF+kbtVq@t+XKVrK zMRAOnTX8RwpA=t1`d{&@NH0sMCVe-7e060a6Ez`OJjh8drG)#*&yoM^QjyTLG>7!O zluY7}r94YMq>W@gsp;fZrDmCN8tL@38KmDa>z~r@QgrvSIAY?X-I*ypiM%uEBHPV0 zmGn!deA00lW7u7jXjgc7%lo~aKF^!2UlG&y^!|$erv)qfyqb(&r;0+p&k)7KGsU|t z%iI8^%rPs~Z3`GI0;R2BPgW_EP0FF7c1d|mU&^Yo4#O7qeD(y2EXuw~+O%p6>55f{ zs6y20-;+MN`mjTz$m~E99VUamQ7y~y)QNp{U~Qq;!a=#pkT-8*uK7e<&E0AB$WL|c zl2MXE>6W)sZr7=$MW6lZvauR;{aOvWeyzmyyQ#!nu%qNzYrI~^oU30D9op3RvhwN> z(dF^#2&iS&HRhW!t)>YG+13ig(rfES+@U6v#|*O4X=74qw-IPoUA8cr-~rzx7}N+1 zY6Pf}_N^DdnRV|ppG)V)vVm{rVe;A8Mllg)x2px-W$*3UMr}T~J6g2aynD`&t9dt> zt0gwgA9CsqbE@k3h5_()4XgHXV=>JE!YykS_X+!^d7euNqc`=|?siE(Lb9XnF>Sd4 z^zX9SdlIPc);*$c)vBe%N_<3G4}?R+;rezd6vg*Fz@+Sd0K82~!viqNq-=fwoJ>j& zEk|$8FacO#>vSZO-I^$$u5ag6e(1)!TU`pyi)FK<(mj<&CH-vWQa`58bsu$AP!<$I z|1SEdzX{5gDAxzncm0Wm2hrMK{2dnvOd%=gyzP%6$IsB)+kOVne^gfs% zYwQ3VcjMVpakMtM13awZRc*<62m^RcV;5kX8+@e&eh0e{3i!EyLIIfWHLda7~K0r zpdF!@Pv)^Oqz?pN9EMZK)PXh;j9xtqW2U68OzFL@IhI9kjg97|Ymn<;iW**KDT z65&5XB66)sM7Ief194&>*U^`oL^zqGFPH0$vL7$^lWqC;{<1#=@JGkV6C9UP#3&L` zF@+?6$BmO$Kb}hNz(&&s;bntxI>!Jx2R0Um7P AfB*mh diff --git a/wasm_for_tests/tx_proposal_code.wasm b/wasm_for_tests/tx_proposal_code.wasm index 8964af7efcd0efdd7a9882708f0040d4b1381822..584ebfdcb192dc64c52f445596312cd0127c8d51 100755 GIT binary patch delta 29110 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 BDS`k1 delta 29295 zcmajn3A_$<|Nrr8uCw8s<6PG4u4CV2FH4NQQc5drgm8##Ns9|fL$Xa}Dv4|(6dh$0 zqEwSq+DBAcXh-s$BJ2UTDzcbgIbDUg}b9_b4{K{Er zsZ=U9Fuow!alxRf83W@}ro7{g9-ee}3f_PReD^r>i3w~;3?6RvFwT=FE7o&EM z^o*=TndVIzb*^i+?|I>M*PnC4jW-SGUZraFdUYDNYSXq`yYtS!=#oB{Uflc2e%V)b z>CyM{u9qdwxuU|=*IYaFmSKYi4=Htcb9{t7>aM9qE&Kbs+LtUB%TA`5$$~oevDSAP zY4-3owJQDDAQ+=&A7)#9#HV7^t{ z$pZ6(;;vg@24|g9D%)&o8VQXm!`x?{ZAzvzYwkY-GEA}_DQ>cs6gOF`ikqypXPr~B zSXP3peMkSjT3;4-q9A1+DL#O#pNgBTQ^ifzUuT_DqF7cQS?kU=6>Y*z#m#DMEpDQA z9o<>;pKVUop5h~r_1;nYlJhG4*PdT-)Sh@=vS`njOZl+^cK5jjiOig=VvCc@5`1~& z->ZJ4xLKT^ikrnbRopDiwEtR~nP;7oaV9Fo(UU{grsC6b^lU9|vUU|WS$m3`tPhKu zHQ#sEIq7Gzh{`7F{S(I?0J2ULH(9?IH(7t5HD~NUXGKY})}L(xtk(0zP1d&JCh9eD>dYBS*4xELAnX0J z=7j$zs|2&3dbX)3%PMZN<`*|vPH_{p^o&bNZ$Z||&~DaYwo#&RONUB^QPMuxsfqnp z$4**RwEK1He0tdSPWf70S-V6sy+Qo4T3PbZyP#I486MslYY>aIr31>OA2W{~wy(LM zzER3fUC=T9{b76C1t;aBu=D1tynA=V3L3;?iR>P^^pxqQ(LP4sm@LsYJ`l}6zB8u1 zBsJ$p+xWEu7O>>@3W53*`V&xp^4%0AaR1Q@Rhg_DKG(G!= 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 Da9oOd delta 421 zcmeyeob$_a&JBW$e9Vjtj8MeF#5Gx-agwl)gT0f8n4_Ptud|D*o4bdnm-pr)j9ko& z3pSr;;bR0+_gLA1)Z}}t;+y}nZD(YV~)CGbvA_5E^3=9k# zfLIKOWhOgM>jo*lFwKZbKy0yCl9U!d> Q#Cpx*)3=LHXS}-y0H)Q0;s5{u diff --git a/wasm_for_tests/tx_write_storage_key.wasm b/wasm_for_tests/tx_write_storage_key.wasm index 2da7e6066aa3f1886988b69ea29c9335086c1e4d..2831315a06674278bd7e2bc8a224b3fa4ffc7634 100755 GIT binary patch delta 12163 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 N%wrsfbF@m{{|n4DJpTXy diff --git a/wasm_for_tests/vp_always_false.wasm b/wasm_for_tests/vp_always_false.wasm index 524ebec9cd9248695894844416d1095afc6cf680..db0729c87c2ed342252a61f6337ec3f576657a79 100755 GIT binary patch delta 532 zcmX?kjPv9%&IubtSecob7#JCunHhm36Bi=~3p4Y?BU5;tOwFvASzYXz?5u5UH>Wc0 zGMW6;V#(xlHeZ0$<|DS#7@2k~njB-l z8zd^}un#19(?On*@yX<$j&4BG!6^!)VvduW$e%?A7#z(Nm>h2~WI1kG4CG8$GWna+ zQK0eE!Fmob2=GqUba!K1u(`nf4~A4}%?H9O({oG`Hgp#w^D(5C=@|i*#W$ntVC(H!w)f zM|m01Nd}U&G4+g&oBzgWfP~cIf`DXY+zLkL%}VhHKthibrUT`x6K{h!;mN^3 z&g$e{TtLIp@;)#+Ob*Z22a@giVT=x&@8v&d0qQ(fvVaxHORt*61qw)q5Yf#Nt&ZxG zMb}C&YE0H!t2~)^4bSA%HB)$x2nsNGGcYim<78mqoLsk7dNSu)6($Rz$@*)>CO6J@ OYQDX8`|Y)id0PR4n8dgM delta 576 zcmX?kjPv9%&Iubtm|2;b7#JCunVA`Z6e9-h2~WI6r-ajq<${LSeo zP=51XXGuno-Wx9Wz$VUdlA3(U%~T0R&zFT@JqH*Bcp11k859^C8O)d_%e%WV-B~jE zzNF~p8uw?6Fm{*cX=aEogqRr^xp^3zW<5Ic>*Ve;GuXMmO_mFA-2B`(ksai0hY*R$ zNuh5*)(3>Kft)cpAWUp?Uie|KQ;Z|sVNU0E+`*XTcn0E($$gP7jE0jhNB#x|%=su! zMn;Fpk}=6ZvNook(P8u77!8n+T3is2tc+X1=(JfW{s2hmal&+-$4VBk0(t3Gv$#OP=@25iS)$cZeKPMF zp2-W>OyTVi6kzaXU|=}M$-uxl*=vnDY*eXEG e55z1`tO}&nfLI-fCpXS^YQDX8`|Y)id0PP-qR7|) diff --git a/wasm_for_tests/vp_always_true.wasm b/wasm_for_tests/vp_always_true.wasm index a2a0a9767141dcc0dacdbb13da0454d523844e61..e363102995e585675b5ab7ce79f135aff90d887a 100755 GIT binary patch delta 547 zcmX?kjPv9%&IubtSecob7#JCunHhm3BL@qR%Qf-H6kZ2YGb?6RCl@AXYa83msf@c! zCVw?MJz3st0;Az%M+^7Khs^81tfP#Eo7pXoGBcf6GWnd%7a+Cyi0w2+rX7nW$Jp-% ziAp-`1Bu>rkY{9kGWn;Y8<2ExiUO&aWVryx&Ch+!m^Rx4@qnG~5F#--DfA7MWgHsep#T+L$kpqhkFgThkFge~}$a4Gv;#^rg`J2;G zp#0{&&XSBEy*FI$flZv_BsKYxo2dkfo-Yf*dJZrM@J{aVm7T2U?!t6u$>jTzqMK{n zpE1JNm7b@WA?|1VHhGz^<7T!1Gp5Z;edU=p+XV4|-R}@0F*zyp4amlTFgB3QlLNxU zHs^&O2HS2N=?-%(x8n}REXOktn_WUqL$IbWhpR)iBI99TN7064kQsQKEo7~l`&*7%P?dXs#;JW!>vjf<< z4k4nOC0ZTTC-bi1nY?h#6y6R&0S0dd28MH-3=Eu;z1FBRKABv!MtO4N8VLghApr(O qpqvsAD+4iztpddSK+FQgsz6!|h}D63a^q~L=G$wx-(Jg@w-o>=Ak3Tq diff --git a/wasm_for_tests/vp_eval.wasm b/wasm_for_tests/vp_eval.wasm index 2960abf9f2c0d46143e069101bf5e9fcb9cf123a..8711cfca76723154a25f603751343bff408550e6 100755 GIT binary patch delta 627 zcmex#mh;nD&IuPpSeclZ85kLvm>7X1GZP~R3lr1CCsTQy&CIP?*j(M1?QLxBHn%bE zGMOx9etNQl*#t(T$rsIBCqFcAfU=fb09l(gERQlXomevYz0DUOwfU*-G)AT!ize6F z?*@rlIqU<8{&J9KWPCDN%E=8#COSodRP1$f6Zx~~0E45s0+ZtnhAhV|i-DX8OD2mu z9|g*9zUM5-2-5r0Is5Ul3_g8<*;6i+v%1xqGLiEcjV`HT_5 zWCnV2a)$eKkXsl%9)qZ79u1rKdCM_R_Hma5`pa#ylD`+kM`D}|3Ji`6W=ss+JPb~= z9v%60a`%}T?3=BE6qp#lO|}Vg+{_wa#9iLK(3>%-rI44v32I=MOGBb2$h zJMswF&4JPGFh6oR?qJMvJOgp>rX1e(@U@PW~Ba&4hL zkX&0B#^|`2rKp1oNcGkzaWcA3-q7mE=rH+Ht2U#zLuvxn?yV^TAZf;}T zWipxDd;+8KW9lO4=XPkw0L0Oq+*UTIOsXuMg&@+dRYm&KFc+k631o1fZFV`SQ} zWOA+jZjh*z!##ubyLoZNt9qEi$|#a<^jkpqhkFgThkFge~}$a4Gv;#^rg zS={+3P=510XGuno-k&b_z$Wf>lA8S4%~Tmh&zFT@JqH*B_!zi3859^C8O)d%Chzc* zn>^2R8ps7^UXMYPw0FZ~CwCX7J4+@>iEf_j{)`dIob4^g4D_PgWEp=ih&Kg*0mIG1 z;56&ekzXfwpP9kF`C(us)8@ne@=TkfLU_Qwo!BZqxjy_Y$mEHwqLZ5=K0=wByCaW) zof{bK4)Y$D;||6w$1@P8PF@r3!e})4WAtxe;JuCUWMp)nY#x^kBxlFfGdgdUj@JMQ zxg-PuInxs|nSdO||UTZ zHgD{3RG+N3VF7Q4pa6q60|Uc3P6h_f$*(u6Gd`Kjy+L{MuZxq zIq3*LS$u%O(OiMa@diVdgM^n;tf1d8%Ii$KbO zdIb0;C%U^ac5GhZ{)f@HW9b0~pn)7&0=$YKhC-GKg8~D$3?pN`0)rzttpG2uUtKvkfo3=|m@#e6 z2wcey@|gb-h3=U10?i5F$hRHC9Pm|+x$A|07z&}%5Hs5S-mKpjnQrL>m-@Yi!0QHL7@wD+vbPeff|$FZINJ_AUK(0tJ36s zn|UVNZJDB~BP77!&A`BLj+21_h!_Nbm<5W}fV4UgYfQesMV)DZ(BzP95zSk+Zr`$% HQFS{2=fJ}{ delta 547 zcmcb4lk?_H&IwnAnOT{c7#JCunVA`Z6e9};XUSwX+b=+B^B>!3j7&3@PM%`F8zdU&un#0E=qS&~IAOA(lN*q%aEbz{ z_~0fr`JI!F@QTF;7#z(Nm>h2~WH~+narR8sbv_1EviYmCBqK<>fa^W5p*l~9!5 zSp-tPrL z@4~qGjZYL4(0^`|F9di&e6u+#a5+23#fs6QlQkmWLzrTd^`kz3T%Z_z6YPRNG43!2 zb2;u{%yQfUcERMZSXV}q$#Y_V14Cp+oF^lr%j8G#$w1OAp`OuY^RWaCkkI?YARy_K zw1Uxn^XsGoAfYuW(}D6XsnFryzYB7o*8$^`dw-M)%3DlVmn8u22_doH1D; zT5R*f?m&&nd0Q6neh?I3@Md6OILFDrz&Y7^sXCL7&}5%2N|U{oN{Ds{2{1?iB_)Aa X3Wz~$>B-VtrJIYkZZF!(n7$nV>u|hp diff --git a/wasm_for_tests/vp_read_storage_key.wasm b/wasm_for_tests/vp_read_storage_key.wasm index 2729f15bf6d18e2c849fe005537f0f27ed7356a4..9bf70703e9321d30adc58bb57689f8bf4244d69a 100755 GIT binary patch delta 678 zcmX|6TS(JU7(d_H&77HP)4>`JM0g{l44zLL>2^k@(>)kSPLw z2&EJ#5CE}^Km3A7M#U7haJ^7(kgAJHbO(;R;+k7dD3Vh)Ym_^4^LFjtQ+~*3k;XOH zB(78M9obP>lK3a?rAi)+JCH zOE(g^iVC13^(v|oX{eoh{s3OJ?Wg_#duhv|^v*N}-!hfG>t0e$hG}esqxhFOY`5@rqhOb;=J&mnE!$Y`u!!ddmjQwp-#i%;=9=^Iz3vlCp#DVx)3k_atXO|?etozC z3*8R9u-8H(gZR^}UH^O4X%F9O;OzM7R^GK>8uq#~6B0p!zJaKSKs1!dmvO8^U^m()#CXM|z&=wPhnRJqUjZ<&wl1**>R9Kc$wc&Y zZ9St+w@jk wh$_+XIt2@6dQ;44Nu-<*GR%?AVNS!y%xzxojH+o-daUM}boP~W7W8lb0ql170{{R3 delta 692 zcmX|6ZAg<*6u#%~)h*}e@*BmW7L{L>I&-frU(>mqYko}pnwpuG5s3yNArYhuf{!ql3BxO96 z9hG5zY_D1b>ha*FGX@JCwIJcJBNOs)+3}6b%w5x=*icvdN2$`(J@UIp>t_|UsbOAG z6C0C&`ktZ5S)#yzv;Kd_YUszDrWJ)ib0#dK`1~X@w5X_3B({Ft9~_UlZFbEeq9+nD zQ2gAUggqI>s!yH;+_FXDMq9G~`>B|Jt&=Y{cxB$d*zE%cUl4!99ATD@7XsV6E`m-) zKpRYo9)DljeKY%RSgqa0xtkIjxUnqo4aX*vSy%TipwSh^dT(tDP{q1>S3>!d=*oaR z*6C^pG## xB&Ny_xOY;{rkhbXx)zPQaxlJ?V`)OPLZhO%P1uh#j_Euy?(fJmJ2I5L{s&Ec_y+(0 diff --git a/wasm_for_tests/wasm_source/Cargo.lock b/wasm_for_tests/wasm_source/Cargo.lock index 90b87bc01e..1abb89a73c 100644 --- a/wasm_for_tests/wasm_source/Cargo.lock +++ b/wasm_for_tests/wasm_source/Cargo.lock @@ -2456,7 +2456,7 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "namada" -version = "0.11.0" +version = "0.12.0" dependencies = [ "async-trait", "bellman", @@ -2500,7 +2500,7 @@ dependencies = [ [[package]] name = "namada_core" -version = "0.11.0" +version = "0.12.0" dependencies = [ "ark-bls12-381", "ark-serialize", @@ -2541,7 +2541,7 @@ dependencies = [ [[package]] name = "namada_macros" -version = "0.11.0" +version = "0.12.0" dependencies = [ "quote", "syn", @@ -2549,7 +2549,7 @@ dependencies = [ [[package]] name = "namada_proof_of_stake" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "derivative", @@ -2563,7 +2563,7 @@ dependencies = [ [[package]] name = "namada_tests" -version = "0.11.0" +version = "0.12.0" dependencies = [ "chrono", "concat-idents", @@ -2592,7 +2592,7 @@ dependencies = [ [[package]] name = "namada_tx_prelude" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "masp_primitives", @@ -2607,7 +2607,7 @@ dependencies = [ [[package]] name = "namada_vm_env" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "hex", @@ -2618,7 +2618,7 @@ dependencies = [ [[package]] name = "namada_vp_prelude" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "namada_core", @@ -2631,7 +2631,7 @@ dependencies = [ [[package]] name = "namada_wasm_for_tests" -version = "0.11.0" +version = "0.12.0" dependencies = [ "borsh", "getrandom 0.2.8", diff --git a/wasm_for_tests/wasm_source/Cargo.toml b/wasm_for_tests/wasm_source/Cargo.toml index a42b4e1e62..dd17f4c0dc 100644 --- a/wasm_for_tests/wasm_source/Cargo.toml +++ b/wasm_for_tests/wasm_source/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_wasm_for_tests" resolver = "2" -version = "0.11.0" +version = "0.12.0" [lib] crate-type = ["cdylib"] From 459dbbb7cfa556099864b5c7c28dee5e778b4dbf Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Wed, 21 Dec 2022 00:56:25 -0500 Subject: [PATCH 39/41] vp_verify_masp: avoid panicking unwrap()s Malformed transactions can cause the node to panic because this function unwraps values. Return the proper error, or a failure if asked to verify a transaction with no shielded part. --- shared/src/vm/host_env.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/shared/src/vm/host_env.rs b/shared/src/vm/host_env.rs index 90e6e4405f..655774080f 100644 --- a/shared/src/vm/host_env.rs +++ b/shared/src/vm/host_env.rs @@ -1822,22 +1822,26 @@ where EVAL: VpEvaluator, CA: WasmCacheAccess, { - use masp_primitives::transaction::Transaction; - use crate::types::token::Transfer; + let gas_meter = unsafe { env.ctx.gas_meter.get() }; let (tx_bytes, gas) = env .memory .read_bytes(tx_ptr, tx_len as _) .map_err(|e| vp_host_fns::RuntimeError::MemoryError(Box::new(e)))?; vp_host_fns::add_gas(gas_meter, gas)?; + let full_tx: Transfer = - BorshDeserialize::try_from_slice(tx_bytes.as_slice()).unwrap(); - let shielded_tx: Transaction = full_tx.shielded.unwrap(); - Ok(HostEnvResult::from(crate::ledger::masp::verify_shielded_tx( - &shielded_tx, - )) - .to_i64()) + BorshDeserialize::try_from_slice(tx_bytes.as_slice()) + .map_err(vp_host_fns::RuntimeError::EncodingError)?; + + match full_tx.shielded { + Some(shielded_tx) => Ok(HostEnvResult::from( + crate::ledger::masp::verify_shielded_tx(&shielded_tx), + ) + .to_i64()), + None => Ok(HostEnvResult::Fail.to_i64()), + } } /// Log a string from exposed to the wasm VM Tx environment. The message will be From 97934a7042c5dc8119154fb4a5a8ffe5edb2d6c0 Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Wed, 21 Dec 2022 00:59:31 -0500 Subject: [PATCH 40/41] changelog: add #942 --- .changelog/unreleased/bug-fixes/942-vp-verify-masp-failure.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/bug-fixes/942-vp-verify-masp-failure.md diff --git a/.changelog/unreleased/bug-fixes/942-vp-verify-masp-failure.md b/.changelog/unreleased/bug-fixes/942-vp-verify-masp-failure.md new file mode 100644 index 0000000000..4c3cfdee3c --- /dev/null +++ b/.changelog/unreleased/bug-fixes/942-vp-verify-masp-failure.md @@ -0,0 +1,2 @@ +- Avoid panicking unwrap()s in vp_verify_masp, to prevent crashing the node on + malformed transactions. ([#942](https://github.com/anoma/namada/pull/942)) \ No newline at end of file From 6920cbcb5b21eadf65aadd21b943247250c47abd Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Wed, 21 Dec 2022 01:01:11 -0500 Subject: [PATCH 41/41] Namada 0.12.1 --- .../bug-fixes/942-vp-verify-masp-failure.md | 0 .changelog/v0.12.1/summary.md | 2 ++ CHANGELOG.md | 10 ++++++ Cargo.lock | 20 +++++------ apps/Cargo.toml | 2 +- core/Cargo.toml | 2 +- encoding_spec/Cargo.toml | 2 +- macros/Cargo.toml | 2 +- proof_of_stake/Cargo.toml | 2 +- shared/Cargo.toml | 2 +- tests/Cargo.toml | 2 +- tx_prelude/Cargo.toml | 2 +- vm_env/Cargo.toml | 2 +- vp_prelude/Cargo.toml | 2 +- wasm/Cargo.lock | 22 ++++++------ wasm/checksums.json | 34 +++++++++--------- wasm/tx_template/Cargo.toml | 2 +- wasm/vp_template/Cargo.toml | 2 +- wasm/wasm_source/Cargo.toml | 2 +- wasm_for_tests/tx_mint_tokens.wasm | Bin 352858 -> 352849 bytes wasm_for_tests/tx_proposal_code.wasm | Bin 201332 -> 203872 bytes wasm_for_tests/tx_read_storage_key.wasm | Bin 152564 -> 152560 bytes wasm_for_tests/tx_write_storage_key.wasm | Bin 226795 -> 226642 bytes wasm_for_tests/vp_always_false.wasm | Bin 156489 -> 156489 bytes wasm_for_tests/vp_always_true.wasm | Bin 156489 -> 156489 bytes wasm_for_tests/vp_eval.wasm | Bin 157426 -> 157426 bytes wasm_for_tests/vp_memory_limit.wasm | Bin 158937 -> 158937 bytes wasm_for_tests/vp_read_storage_key.wasm | Bin 170817 -> 168265 bytes wasm_for_tests/wasm_source/Cargo.lock | 18 +++++----- wasm_for_tests/wasm_source/Cargo.toml | 2 +- 30 files changed, 73 insertions(+), 61 deletions(-) rename .changelog/{unreleased => v0.12.1}/bug-fixes/942-vp-verify-masp-failure.md (100%) create mode 100644 .changelog/v0.12.1/summary.md diff --git a/.changelog/unreleased/bug-fixes/942-vp-verify-masp-failure.md b/.changelog/v0.12.1/bug-fixes/942-vp-verify-masp-failure.md similarity index 100% rename from .changelog/unreleased/bug-fixes/942-vp-verify-masp-failure.md rename to .changelog/v0.12.1/bug-fixes/942-vp-verify-masp-failure.md diff --git a/.changelog/v0.12.1/summary.md b/.changelog/v0.12.1/summary.md new file mode 100644 index 0000000000..330b094aec --- /dev/null +++ b/.changelog/v0.12.1/summary.md @@ -0,0 +1,2 @@ +Namada 0.12.1 is a hotfix release, fixing a node crash on malformed +transactions to the MASP. diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aaab24c50..c43936dd31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # CHANGELOG +## v0.12.1 + +Namada 0.12.1 is a hotfix release, fixing a node crash on malformed +transactions to the MASP. + +### BUG FIXES + +- Avoid panicking unwrap()s in vp_verify_masp, to prevent crashing the node on + malformed transactions. ([#942](https://github.com/anoma/namada/pull/942)) + ## v0.12.0 Namada 0.12.0 is a scheduled minor release. diff --git a/Cargo.lock b/Cargo.lock index 1a0a9ae50b..569ef32e37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3635,7 +3635,7 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "namada" -version = "0.12.0" +version = "0.12.1" dependencies = [ "assert_matches", "async-trait", @@ -3692,7 +3692,7 @@ dependencies = [ [[package]] name = "namada_apps" -version = "0.12.0" +version = "0.12.1" dependencies = [ "ark-serialize", "ark-std", @@ -3778,7 +3778,7 @@ dependencies = [ [[package]] name = "namada_core" -version = "0.12.0" +version = "0.12.1" dependencies = [ "ark-bls12-381", "ark-ec", @@ -3830,7 +3830,7 @@ dependencies = [ [[package]] name = "namada_encoding_spec" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "itertools", @@ -3841,7 +3841,7 @@ dependencies = [ [[package]] name = "namada_macros" -version = "0.12.0" +version = "0.12.1" dependencies = [ "quote", "syn", @@ -3849,7 +3849,7 @@ dependencies = [ [[package]] name = "namada_proof_of_stake" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "derivative", @@ -3863,7 +3863,7 @@ dependencies = [ [[package]] name = "namada_tests" -version = "0.12.0" +version = "0.12.1" dependencies = [ "assert_cmd", "borsh", @@ -3907,7 +3907,7 @@ dependencies = [ [[package]] name = "namada_tx_prelude" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "masp_primitives", @@ -3922,7 +3922,7 @@ dependencies = [ [[package]] name = "namada_vm_env" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "hex", @@ -3933,7 +3933,7 @@ dependencies = [ [[package]] name = "namada_vp_prelude" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "namada_core", diff --git a/apps/Cargo.toml b/apps/Cargo.toml index bba5ca030e..7bbe802b58 100644 --- a/apps/Cargo.toml +++ b/apps/Cargo.toml @@ -6,7 +6,7 @@ license = "GPL-3.0" name = "namada_apps" readme = "../README.md" resolver = "2" -version = "0.12.0" +version = "0.12.1" default-run = "namada" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/core/Cargo.toml b/core/Cargo.toml index 7754310669..5f5381b38f 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_core" resolver = "2" -version = "0.12.0" +version = "0.12.1" [features] default = [] diff --git a/encoding_spec/Cargo.toml b/encoding_spec/Cargo.toml index 2fc6c2cc18..a493ea3dc9 100644 --- a/encoding_spec/Cargo.toml +++ b/encoding_spec/Cargo.toml @@ -6,7 +6,7 @@ license = "GPL-3.0" name = "namada_encoding_spec" readme = "../README.md" resolver = "2" -version = "0.12.0" +version = "0.12.1" [features] default = ["abciplus"] diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 4bb96bbe49..ab2dea5b9e 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_macros" resolver = "2" -version = "0.12.0" +version = "0.12.1" [lib] proc-macro = true diff --git a/proof_of_stake/Cargo.toml b/proof_of_stake/Cargo.toml index 822ef2fabb..a7b1417608 100644 --- a/proof_of_stake/Cargo.toml +++ b/proof_of_stake/Cargo.toml @@ -6,7 +6,7 @@ license = "GPL-3.0" name = "namada_proof_of_stake" readme = "../README.md" resolver = "2" -version = "0.12.0" +version = "0.12.1" [features] default = ["abciplus"] diff --git a/shared/Cargo.toml b/shared/Cargo.toml index efcd0e81a0..0683648271 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada" resolver = "2" -version = "0.12.0" +version = "0.12.1" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 4403453dc9..fd8c123bb5 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_tests" resolver = "2" -version = "0.12.0" +version = "0.12.1" [features] default = ["abciplus", "wasm-runtime"] diff --git a/tx_prelude/Cargo.toml b/tx_prelude/Cargo.toml index 945f14ad7b..ae0e303939 100644 --- a/tx_prelude/Cargo.toml +++ b/tx_prelude/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_tx_prelude" resolver = "2" -version = "0.12.0" +version = "0.12.1" [features] default = ["abciplus"] diff --git a/vm_env/Cargo.toml b/vm_env/Cargo.toml index cf4e6a7d9f..df7f726863 100644 --- a/vm_env/Cargo.toml +++ b/vm_env/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_vm_env" resolver = "2" -version = "0.12.0" +version = "0.12.1" [features] default = ["abciplus"] diff --git a/vp_prelude/Cargo.toml b/vp_prelude/Cargo.toml index a0d76feadc..3489f62172 100644 --- a/vp_prelude/Cargo.toml +++ b/vp_prelude/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_vp_prelude" resolver = "2" -version = "0.12.0" +version = "0.12.1" [features] default = ["abciplus"] diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index bd82ea3be8..52b7029972 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -2456,7 +2456,7 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "namada" -version = "0.12.0" +version = "0.12.1" dependencies = [ "async-trait", "bellman", @@ -2500,7 +2500,7 @@ dependencies = [ [[package]] name = "namada_core" -version = "0.12.0" +version = "0.12.1" dependencies = [ "ark-bls12-381", "ark-serialize", @@ -2541,7 +2541,7 @@ dependencies = [ [[package]] name = "namada_macros" -version = "0.12.0" +version = "0.12.1" dependencies = [ "quote", "syn", @@ -2549,7 +2549,7 @@ dependencies = [ [[package]] name = "namada_proof_of_stake" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "derivative", @@ -2563,7 +2563,7 @@ dependencies = [ [[package]] name = "namada_tests" -version = "0.12.0" +version = "0.12.1" dependencies = [ "chrono", "concat-idents", @@ -2592,7 +2592,7 @@ dependencies = [ [[package]] name = "namada_tx_prelude" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "masp_primitives", @@ -2607,7 +2607,7 @@ dependencies = [ [[package]] name = "namada_vm_env" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "hex", @@ -2618,7 +2618,7 @@ dependencies = [ [[package]] name = "namada_vp_prelude" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "namada_core", @@ -2631,7 +2631,7 @@ dependencies = [ [[package]] name = "namada_wasm" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "getrandom 0.2.8", @@ -4601,7 +4601,7 @@ dependencies = [ [[package]] name = "tx_template" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "getrandom 0.2.8", @@ -4738,7 +4738,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vp_template" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "getrandom 0.2.8", diff --git a/wasm/checksums.json b/wasm/checksums.json index b13508300c..bf6efb760c 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,20 +1,20 @@ { - "tx_bond.wasm": "tx_bond.be9c75f96b3b4880b7934d42ee218582b6304f6326a4588d1e6ac1ea4cc61c49.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.cd861e0e82f4934be6d8382d6fff98286b4fadbc20ab826b9e817f6666021273.wasm", - "tx_ibc.wasm": "tx_ibc.13daeb0c88abba264d3052129eda0713bcf1a71f6f69bf37ec2494d0d9119f1f.wasm", - "tx_init_account.wasm": "tx_init_account.e21cfd7e96802f8e841613fb89f1571451401d002a159c5e9586855ac1374df5.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.b9a77bc9e416f33f1e715f25696ae41582e1b379422f7a643549884e0c73e9de.wasm", - "tx_init_validator.wasm": "tx_init_validator.1e9732873861c625f239e74245f8c504a57359c06614ba40387a71811ca4a097.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.47bc922a8be5571620a647ae442a1af7d03d05d29bef95f0b32cdfe00b11fee9.wasm", - "tx_transfer.wasm": "tx_transfer.bbd1ef5d9461c78f0288986de046baad77e10671addc5edaf3c68ea1ae4ecc99.wasm", - "tx_unbond.wasm": "tx_unbond.c0a690d0ad43a94294a6405bae3327f638a657446c74dc61dbb3a4d2ce488b5e.wasm", + "tx_bond.wasm": "tx_bond.c80510dae9aa8785a33301a16747b358da20cb125edae01e330eba2153ca28f4.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.655b344c2d9cc602f9250cf390112ff2c9293537cdc1eb4a52c58ee622ccb538.wasm", + "tx_ibc.wasm": "tx_ibc.de66b4cc33061f503c63964debda8061ab9e0f1294e6c5c510d4b94a409a2edf.wasm", + "tx_init_account.wasm": "tx_init_account.87940d07eac068e03cfe5cd8b491032b3f1814d36060cbc761beb528f1c27b46.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.2464b3de1c3aa4ed25cf592366df3b744ec3d2030ce952ea18f38066d4091693.wasm", + "tx_init_validator.wasm": "tx_init_validator.6a27ed0de01ab555628129ac9f05801d5ac812824633bd36e43b1d67b6360db6.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.d22e9b3310bad29fea038b42b4120f12b11ffe0ec4012ddf2d709aed490cac97.wasm", + "tx_transfer.wasm": "tx_transfer.3f6d8d0c103bd631c7cd12b58663e026aa9743f8552b14919a1b5bcd9fd31741.wasm", + "tx_unbond.wasm": "tx_unbond.6b801584c4d01f7b52988e5ecf971050e1575f1e15f818f3e2694604b402ebd8.wasm", "tx_update_vp.wasm": "tx_update_vp.ee2e9b882c4accadf4626e87d801c9ac8ea8c61ccea677e0532fc6c1ee7db6a2.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.263fd9f4cb40f283756f394d86bdea3417e9ecd0568d6582c07a5b6bd14287d6.wasm", - "tx_withdraw.wasm": "tx_withdraw.6ce8faf6a32340178ddeaeb91a9b40e7f0433334e5c1f357964bf8e11d0077f1.wasm", - "vp_implicit.wasm": "vp_implicit.17f5c2af947ccfadce22d0fffecde1a1b4bc4ca3acd5dd8b459c3dce4afcb4e8.wasm", - "vp_masp.wasm": "vp_masp.5620cb6e555161641337d308851c760fbab4f9d3693cfd378703aa55e285249d.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.362584b063cc4aaf8b72af0ed8af8d05a179ebefec596b6ab65e0ca255ec3c80.wasm", - "vp_token.wasm": "vp_token.a289723dd182fe0206e6c4cf1f426a6100787b20e2653d2fad6031e8106157f3.wasm", - "vp_user.wasm": "vp_user.b83b2d0616bb2244c8a92021665a0be749282a53fe1c493e98c330a6ed983833.wasm", - "vp_validator.wasm": "vp_validator.59e3e7729e14eeacc17d76b736d1760d59a1a6e9d6acbc9a870e1835438f524a.wasm" + "tx_vote_proposal.wasm": "tx_vote_proposal.37269505f1526de9680b619413478a071ed2f92b22924e6fbb3b7127f88da41a.wasm", + "tx_withdraw.wasm": "tx_withdraw.e5affdbd26cdd08aff6dfc37c5b3d92a8784547e28d039b863a6a95d5ac1cc97.wasm", + "vp_implicit.wasm": "vp_implicit.2f0b200b9e0f3db5151e0b86f5e66ae1b7e43e01d8cdc852db1337c744fac4b6.wasm", + "vp_masp.wasm": "vp_masp.da15ab8670750f400fc12616921dc3c959386bdb5d2df4f455f2299847c257ee.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.13f1911794bc69e4d7dcebd30b048740398b5d45f7efee95a07a627b1a46fa47.wasm", + "vp_token.wasm": "vp_token.497a346ebcc1b7e7b844a8aab644a8d43ab9399006a95c7ca411035b8966b05e.wasm", + "vp_user.wasm": "vp_user.59c397bd2356d01cea5379de64882423cf1e2bb361301651573c1e3619d43551.wasm", + "vp_validator.wasm": "vp_validator.0b13e2f48407640ca35414c5b855f3e20f3393f60ff987aaa3b0febbb0d64e51.wasm" } \ No newline at end of file diff --git a/wasm/tx_template/Cargo.toml b/wasm/tx_template/Cargo.toml index 345ec86237..708225ef63 100644 --- a/wasm/tx_template/Cargo.toml +++ b/wasm/tx_template/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "tx_template" resolver = "2" -version = "0.12.0" +version = "0.12.1" [lib] crate-type = ["cdylib"] diff --git a/wasm/vp_template/Cargo.toml b/wasm/vp_template/Cargo.toml index 07ae7758db..31cb9374b0 100644 --- a/wasm/vp_template/Cargo.toml +++ b/wasm/vp_template/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "vp_template" resolver = "2" -version = "0.12.0" +version = "0.12.1" [lib] crate-type = ["cdylib"] diff --git a/wasm/wasm_source/Cargo.toml b/wasm/wasm_source/Cargo.toml index 36731a505b..0e57b8aec1 100644 --- a/wasm/wasm_source/Cargo.toml +++ b/wasm/wasm_source/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_wasm" resolver = "2" -version = "0.12.0" +version = "0.12.1" [lib] crate-type = ["cdylib"] diff --git a/wasm_for_tests/tx_mint_tokens.wasm b/wasm_for_tests/tx_mint_tokens.wasm index 72e1e8f075a1bc7c0f7061848deb59e2bbb37dd9..c5bdd46d33bd84919b65a81c87c723ecb8a5ea79 100755 GIT binary patch delta 7985 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 delta 8033 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 diff --git a/wasm_for_tests/tx_proposal_code.wasm b/wasm_for_tests/tx_proposal_code.wasm index 584ebfdcb192dc64c52f445596312cd0127c8d51..a8e7a81527f8c5135a439d61cb53e7660571ef9c 100755 GIT binary patch delta 30762 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 delta 28243 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 OD4I{K&2dVR=Kllp&}~Qn diff --git a/wasm_for_tests/tx_read_storage_key.wasm b/wasm_for_tests/tx_read_storage_key.wasm index 8f62c8211e4f1d4561e7e8aac1b45c676b4a8bb0..1b5b368833822bec0478e5d20d7974aaa235a550 100755 GIT binary patch delta 3122 zcmbVOYfMx}6rMB7UV&ZU!h+&1%7R$kpyHzwrVxiKgCy~wZ77xxr^)IuL@yj<~!%i`R1IN zIddO*BOZ7od=-S+=cF07>Ep&vm^@`_&WF>m2`<5Q zy_EL=C_#^D7;MKMO#6m3o$#{^#zr&dG-;|^t0(MYY?Oh+-P$P`AIGw=YZ1jmwC7J>@3~ofh*f4A<|R0=BsLm~^!>3}0F~Gp zcR^mE`Hiz<_>s~CLf2$4}C!#l@bwDwNsikXm zMSiBL%%a9Ji`L};b%@i{tRL*zUu>g0(S;9t4W;CzCQdf+JYMa^8|DQ3HPHj-aaHft zN!w5P*+9-Rxs{J~GAGbcw7NCFPLA3!%)WA1a7b94n_{wP!rsXTa%(1MR-x@r3S)EnZWj82>Rdf(fb zTWdEiiT+huzcA=Prz?e*9Kn`%lMq~e;SX7`!1ckbnr}kYfC;M8GNx+LAmc>0c0tBY zw{}&={oPtC-gDVEZIPwqb%%_}YnhD6>pmHi*Ml-9uig;0Mp(4L8mLMtn;4}!nbvls zYXH{>Z|baXOeFKRY(QLhWlUU;WlUVpLf9HEam5fzJtF+=8<>8;2cF&tWT z&xkY*r}5m#e&EBKBh#QsS28_dJ5SGomQE#0)RB&(1Oiw)YC``GT8|pnSy0QXN#0L8 zMQTxGwO1JJS=r#nxmlB`{W@z7wU4us!#iv813l?IUp)Br%hRi(saux2(FDc%y#)b) zVr=+$Hpy%YGfhyZ=j1hW@aeY2Pl3Pi>e24!qQ^(evUO*E8}Rny80{W~&Pn~z?OvkS zFU^Dj?Z-vcr8}ucrig*lokmyCLZ40+D;u}(R@1YKXInfyaP0C)(1P2R4^Fxw-ik0X zO>%3|>L6p*!1tNB=I1c5Jf-g$D(M3m%QWsXU5{GAlyLFgM5h5`RwP3sW~?{@&3fC4 z+W>WVcGb|>b3wO8d5BR5X^r?|RR!!sEC}fg$~e;P#@2!@@RL4wbv%bE%rA5(yT!Yr z;)=qaN|6}GiXl8if1v|ziB<`mBypT!(IAFF3mu29aqw?X;={FeWuK56PWY~qsI9ld z=XhX^L;0tcowR|Xjd-Pkd@6yfFH-MchF}AwTdppMilh=3GpHHQCop6>lZo7>q#jZda_fw25x$V zwNfE0#heYI$mVaDuNY6ZA1`m%*g4c^eKs!)e%6m|ZlJRkkNL$jL|0$Dl8$A@)>spq z)3t54BP0C1RVM!Ym{C@Vfiks8|!1G9_odrsf?fcflyr16h# delta 3128 zcmbVOeNa?Y6u;*#`&`%s9xNd4^09n4vV)3WAtjT%GEGV+r*Rm|8B?>V9W+cD{7NWI za&(k9fIDUeC9Msli;sdCV8n5qOxL2K!iLGw$r^K}fzm%r&CY#G>&3s4o!$5D@7!~L zzkANT_uL)wMPBzs`V%3F0nj(X6vZ(e+;9*V!prujisotwNl!1bFMjOtc~8t=@Z`c` z48S?qqu20p05#|}O@}>r)^uR%nNNFJ9%FMDbD1>NqxBMYGdA167LV2`V?WkL6vB1< zI^yty4s0@ufj^tyfAE2J>{i^y;PLt$r30W9m&ZD>&oaf{+0x6j*)Fbm!J{2)A`nD1 zGIcB(>mr4QH?m?ZgP)0_!TRQ?c>r~I!n%y6IKlSxn3=H8wsb6mUy2sfsEN)5KH$S> zOEC=K-I(P@!U{(xYse>(rMJY^L)zO%!+XH2(VE}6x8`Q;1~!@PxF*qwd*WhXhyGq% z0YD@6$A2Nu(0t$CF?giCW7hlz&!F86JFqul0;VVAkAPDWp1|DxvKxoTPov~zCND7XGT!RLi{?c9DcK8W zal?eyQ}(p?vK-Fxxs4aOm)FiByWSFd)@OSYXZp-J)23B$x zmV9n;aXNpzT&qE!Bb7GdX~%q8N}KZ+Ma~l3mu3*lC8ZfC&1so*()8yuFN%XQC;ORu z>ps9d+JJFL^h4QM5io=LZ8CwsJW zGIn{i?_@lY)bS6uqjINgCDy$%Ce{WS6YC)v6YIw^Cf1fPwnbXCDH^CsCVL`UbusNi z^XLFB5w0AmZ%icfs_Z~o!!jnVn=&S?f5O;ik+fn-t2Nw3fWm5*F-digV3H>X(hA59 zq!pAgX?=rvxs%{9mgHto&EAwdwer&8(cxT`F-Z)|m=tcxm=xZ;Phn>m+ssllHkx0% zJd~p8COj&00oj4Hf-)wpei>6V7sA*UA!%7jt2*2j%G^E~Q&{^)a5!@x$PT3ClQC(v z;#ZR$l|OwnI$M`zOtintm?HR7#>D!!jEVJb7~8lMK_pH3({NWPg3kz#%2to;Kw76| zOj_qK2sbnC@y&Tweg?JwU1UUfu|@!W#cDiWo6Sv+b~hG@7v*>uFL8bGCs zU@NeOD<04C<-tZUY}|MQdrHYGaQbxE%QV%dVhzoYlkti^}FhA6A!5N%>aX z77;|2;?ZK%$p&xEBTO9hlh{|5k=8>+J%_P;<22I&`HV27T>NvQ{eZFS)8RDct#5{) z{{8wZ0NvQNVOreD5Uo)lV%5poX}rCm0rnx5hx-i4c4cBW?K2W(U+_)g~w3 zX?XHmenHLu^5fbH2mFph)O9gr>o;CXkDmy}8+vVi!OokFmx7`r#JoE+{0{bQcJkq4 zhS%j|c(THwe0_|(>_!3tShU4Se=O*!bO^H%1>Jwb>_b024`6W1YNxlGI>m!7?M$wL zhwfpmP8jR3c&jM0rCXm@j7vL!=eKSfY3fVfSQY_4>Md`c0L~Jzbo*lQ>fZhuZOptK zaV9vaYt>hxA_EgtCO!eot37C*vUcSw>+oEy!(14rvRV4IS|=p_=dw3mWt?1JS+$xq zqO@S9LjP<@phpfdr7fQBY^LAYGg1PHoxWSHP diff --git a/wasm_for_tests/tx_write_storage_key.wasm b/wasm_for_tests/tx_write_storage_key.wasm index 2831315a06674278bd7e2bc8a224b3fa4ffc7634..471bceb93b60337506a2a63bfd2f5dc4e63fb9b0 100755 GIT binary patch delta 14726 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 V{E)5Mxxct8`RiPbbJX&_{|9R`CmsL* diff --git a/wasm_for_tests/vp_always_false.wasm b/wasm_for_tests/vp_always_false.wasm index db0729c87c2ed342252a61f6337ec3f576657a79..f5d1881c27d8b93065907d0e793ed70c5281c2cc 100755 GIT binary patch delta 257 zcmX?kjPv9%&Iubtm|2;b7#JCunHhm33o{cV2Q$~iBU8BSOwFvAS?yhz95yF0?lqZw zz`TLcXtJY)>t=bgXhtyeD3D~ge936I`J<&ZACS`Z@L`<1!bg^ulR<&Ok-?0Kft#6u zadW4qJoDsNW^R+YeZ3%N@bNG>&3bg?*U8;yX0UUAn=BXLxcRy7Yi>s4$(JL4Ga79^ zALYr&=rma}CK*W9#?&)9ZT=gh0TNP+3j%T~<1(3moW}{%ft>2Z+aQ_n^3Jc?lsx` z)GV5j(QvY(h5KYhvpO*AD3D~ge936M`J<&ZACS`Z@L?2SW?3utnVA`ZBnuN4BL_1x^TZKd&W(k?jtL}%)rRa!{9XQ(UD&#cb}QTzPZ9v zo_X>svui-?Hu;y&tj$Y(*;pCPH?Il$!p3Mmd10g>qtoWgk*-XPj*}&0l7VDxOg*FH z=D#r-j6jZBTo90~j9bCzyjdy!07&R@!gQc~b>eLhCpwZL p2S$g<;raSNvOPbH(P8tw{O2q{{l`icurfL{r&n!HuVQ@k0s#0ZXV(A# delta 342 zcmX?kjPv9%&Iy}@n3)(D8JU?Gfg~db3o|nl*Tf?Yist%W-mE^pejvFf!IFFgUVg zX)rM;F|s%)FaQ|}jE;-~T-=l2dAm>M_MIib%)rRa!{9XQ(UD&#cb}QTzIll+J1e8b z<~2cI*cdG)FN`!~G}?SQ(v^wPezIgtGLWo|sb{p`{5M8}5y(-C3j&graVr=dH!H;- z00})#m=2V$PP`4`geM0BIjfVqxPXS4<>@mTO-{@Ez-Tu)JYOG3w&#a2+HJm<|C|M= V|5(WaRz|z#^s4RYRg8~b006ZZYl#2= diff --git a/wasm_for_tests/vp_eval.wasm b/wasm_for_tests/vp_eval.wasm index 8711cfca76723154a25f603751343bff408550e6..4aec2531643b31cec121ab865efd0a2f5dde4bd8 100755 GIT binary patch delta 171 zcmex#mh;nD&IuPpm{^&Z85kLvm>7X13lk#;6BF~qCsVoX&CIP?*j(J0T{kx~?lqbG z(7b`sc=8-`*Ub)Q(TrfG`{b1tbwHMe6 Qi89R`<=Z*R8DG5z0Da#ytN;K2 delta 173 zcmex#mh;nD&IuPpSeclZ85kLvm>7X1GZP~R3lr1CCsVnc&CIP?*j(M1?Kd|w?lsxW zW**JRXf*ktnd@W+vj!+@xdn)&Vfm8LWV4W!H6M`j^YCG02Aa5eo2LxxnupPN^2g}kj7FQ^#ssl2x@>kx^yX(Y-rQ3V&&KF7*&$J; NnWKC=M>*rG_W&qIGa&!~ diff --git a/wasm_for_tests/vp_memory_limit.wasm b/wasm_for_tests/vp_memory_limit.wasm index d7d10f8a3601729dc55a246c024fdd57aef0b55a..0cbbe237616e5738290f3a5614ddb64b8aa56a3f 100755 GIT binary patch delta 1218 zcmah|Z%kWN6o2QvS61lg9yls3ZFh@hKoJ2St}<5ug~W)8m9Q)vpMb z^@a6d7gLU(030~z{1$RB;_4>6;u<3KyGJ2M%(_p?V8>marzz=*rYPdGd}$`70$-q;&-3~(@JWgLzJw)OBR0n0u?SI;=UVV zDGc-w9xA_X)zi&XaCDN&xiC^O0&e`Sq7ZWNPQ^onty|BKKucR|NFet%2Pk%n6YCN= zs%0J&d?QkY<<;Z3r*Z*jtCwIK{?OvZtNu7tU}4P+@n34XNt|$PKZ)~2ZQrUm@)~jO zVIsOcDnpGpw<9UT17eVW4#esjV>(MTH_OHyC#imCaMq|_Dq^i?6byv@bkT75FkQ3~ zeui*=#9>H(Pjv~Gcm1U{q*L^>P_`K?%)~%zz_ieZ(14$W8VT=&f|y$sz{SWUt>0ru zG|i+)VEF@kqg*V+$P8}0*w!X`qHpP2B{qi8ei;0szujk|E~g&-2EdO!`^unMOzoqV zIsUx=AYq_mi12F1SwnY?+4%H(hVIWDc%A~k9e9MWAwFt#(X(VRjen_!J=k~D22El) zJ_i)P_{5upO`VVB=w8s6ncppY+TYH;z@p@2-YX?^n$6xaJa`ark)OQ9L_kE}pFW)GAS1_#;+ z-yJw_%#+FN|IQ<#&t|B`5H^;%W(Zu4JH@%9EvnJ{#dl`RoPv5$@X{Wj zzK4gxDt*|+k+bsuw~wdBrlD0l{A!Vs+RD=S4XH;9;?VJWaEZC&;|k>A?g delta 1215 zcmah|T})GF7(VYgZ7r78!my*ioOZN8EA0?LpfdR?l)4paZD29ep+#3@*+g-f#5h=B zER&y5Uz2HMI^(j$ZHY@h31lOV3!`&m30Y!hceBOaXf&C-GBY#TdybOG#j=~{oacMr zyg$$Pe7Rg+E|)ho17dcjq)01Cq5vRCK&Sz|@|(STwA|vBpL?;YVSRI3yDqUU`TQDl zK{eCpTGvWN#on^=3V)!kzVW5aO*jr0p@i3p5A?7O%M6>Ll4lG{070xXrXk4B82`!F z+6?6u#qS?z$6;1#zuJiX3e(L`NynG~Z z8NiD5m9oi7wKPm-b7!cq6*HA*A%wbM7(zS}d@T7rN-r6hAg_`&=Ad9;4=+;T0Fja> zAz3Gb62krKZ<>^JJrx|8qH->bR}Vu8E>%~+3;3w|Wx|#Xr%9#XHZ+h*wi+8qHiH8j zvvw@rxQJgTqPVzi3RAV$@Dt+@?yc*FjrcfPiudc%P>YHBdE)sEJz7;HY>#ipO<}uM zQAolo@ub3*^%7U;^$g?hhLa*};x`&|B9!tAP2T~r?np{uv&}7{+H->XjkR7;`-S*G z?6icPZI7S@lkFbD!|j&{OA}iNdlE4$jMsw=72?K35^`@TbEa0WYFSs+g zn%_>H0#fE^$0+YjdPMr2NOd4|9)K`E*y-2OB-5|`1Q5pFT~$!Sr+3j?$pS9?Du zY~Huiu2{e<>)9POOT9qL6XL>e_kBP{VQ>6OxsFVVurCuOugzxu(z?k2Z2Wz4Lpi%l zAH@*ts=~|IeU6-(f~w1Zzb(yCF1FIqXgb$NL0F=hC%W781fi_@% diff --git a/wasm_for_tests/vp_read_storage_key.wasm b/wasm_for_tests/vp_read_storage_key.wasm index 9bf70703e9321d30adc58bb57689f8bf4244d69a..247eb8a4a4ab8bf1402590e5b73c54b9d496b474 100755 GIT binary patch delta 27697 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/U{QoQKH6r(a)*Mk&kMI6(9O&N=0o^Ec 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 RR|^Hg^;4R1uEp;l{Xe)#b{hZy diff --git a/wasm_for_tests/wasm_source/Cargo.lock b/wasm_for_tests/wasm_source/Cargo.lock index 1abb89a73c..5bcad8929f 100644 --- a/wasm_for_tests/wasm_source/Cargo.lock +++ b/wasm_for_tests/wasm_source/Cargo.lock @@ -2456,7 +2456,7 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "namada" -version = "0.12.0" +version = "0.12.1" dependencies = [ "async-trait", "bellman", @@ -2500,7 +2500,7 @@ dependencies = [ [[package]] name = "namada_core" -version = "0.12.0" +version = "0.12.1" dependencies = [ "ark-bls12-381", "ark-serialize", @@ -2541,7 +2541,7 @@ dependencies = [ [[package]] name = "namada_macros" -version = "0.12.0" +version = "0.12.1" dependencies = [ "quote", "syn", @@ -2549,7 +2549,7 @@ dependencies = [ [[package]] name = "namada_proof_of_stake" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "derivative", @@ -2563,7 +2563,7 @@ dependencies = [ [[package]] name = "namada_tests" -version = "0.12.0" +version = "0.12.1" dependencies = [ "chrono", "concat-idents", @@ -2592,7 +2592,7 @@ dependencies = [ [[package]] name = "namada_tx_prelude" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "masp_primitives", @@ -2607,7 +2607,7 @@ dependencies = [ [[package]] name = "namada_vm_env" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "hex", @@ -2618,7 +2618,7 @@ dependencies = [ [[package]] name = "namada_vp_prelude" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "namada_core", @@ -2631,7 +2631,7 @@ dependencies = [ [[package]] name = "namada_wasm_for_tests" -version = "0.12.0" +version = "0.12.1" dependencies = [ "borsh", "getrandom 0.2.8", diff --git a/wasm_for_tests/wasm_source/Cargo.toml b/wasm_for_tests/wasm_source/Cargo.toml index dd17f4c0dc..96a000968c 100644 --- a/wasm_for_tests/wasm_source/Cargo.toml +++ b/wasm_for_tests/wasm_source/Cargo.toml @@ -4,7 +4,7 @@ edition = "2021" license = "GPL-3.0" name = "namada_wasm_for_tests" resolver = "2" -version = "0.12.0" +version = "0.12.1" [lib] crate-type = ["cdylib"]