Skip to content

Commit

Permalink
Merge remote-tracking branch 'namada/grarco/replay-protection-impl' i…
Browse files Browse the repository at this point in the history
…nto draft-0.15

evil: wl_storage gains a 'static

* namada/grarco/replay-protection-impl:
  [ci] wasm checksums update
  Updates fees in replay protection specs
  Brings back sig check in `finalize_block`
  Fixes fee error code
  [ci] wasm checksums update
  Fixes fee in unit tests
  Fmt
  Refactors replay protection logic
  Removes wal from replay protection specs
  Removes unnecessary clones
  Fixes tx unsigned hash
  Replay protection VP always rejects
  Fixes typos
  changelog: add #1017
  [ci] wasm checksums update
  Clippy
  Fmt
  Updates `process_proposal` unit tests
  Fixes `finalize_block` and adds unit test
  Updates replay protection specs with protocol txs
  Updates `process_proposal` unit tests
  Removes tx hash from storage in `finalize_block`
  Fixes error codes
  Refactors `process_proposal`
  Replay protection checks in `process_proposal`
  Fixes replay protection specs
  Refactors `unsigned_hash_tx`
  Unit test `mempool_validate`
  Wrapper commit hash on unsigned inner tx
  Adds tx hash check in mempool validate
  Updates replay protections specs with governance and unsigned inner hash
  Adds replay protection internal address and vp
  Updates replay protection specs
  changelog: add #1051
  core: added `TempWlStorage` for ABCI++ prepare/process proposal
  • Loading branch information
juped committed Mar 16, 2023
2 parents d1296f4 + 48bcb8c commit 62bc281
Show file tree
Hide file tree
Showing 21 changed files with 2,165 additions and 503 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Adds hash-based replay protection
([#1017](https://github.com/anoma/namada/pull/1017))
3 changes: 3 additions & 0 deletions .changelog/unreleased/improvements/1051-temp-wl-storage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Added a TempWlStorage for storage_api::StorageRead/Write
in ABCI++ prepare/process proposal handler.
([#1051](https://github.com/anoma/namada/pull/1051))
151 changes: 142 additions & 9 deletions apps/src/lib/node/ledger/shell/finalize_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
use namada::ledger::pos::namada_proof_of_stake;
use namada::ledger::pos::types::into_tm_voting_power;
use namada::ledger::protocol;
use namada::ledger::storage_api::StorageRead;
use namada::ledger::{protocol, replay_protection};
use namada::types::hash;
use namada::types::storage::{BlockHash, BlockResults, Header};
use namada::types::token::Amount;

Expand Down Expand Up @@ -145,17 +146,49 @@ where
tx_event["gas_used"] = "0".into();
response.events.push(tx_event);
// if the rejected tx was decrypted, remove it
// from the queue of txs to be processed
// from the queue of txs to be processed and remove the hash
// from storage
if let TxType::Decrypted(_) = &tx_type {
self.wl_storage.storage.tx_queue.pop();
let tx_hash = self
.wl_storage
.storage
.tx_queue
.pop()
.expect("Missing wrapper tx in queue")
.tx
.tx_hash;
let tx_hash_key =
replay_protection::get_tx_hash_key(&tx_hash);
self.wl_storage
.storage
.delete(&tx_hash_key)
.expect("Error while deleting tx hash from storage");
}
continue;
}

let mut tx_event = match &tx_type {
let (mut tx_event, tx_unsigned_hash) = match &tx_type {
TxType::Wrapper(wrapper) => {
let mut tx_event = Event::new_tx_event(&tx_type, height.0);

// Writes both txs hash to storage
let tx = Tx::try_from(processed_tx.tx.as_ref()).unwrap();
let wrapper_tx_hash_key =
replay_protection::get_tx_hash_key(&hash::Hash(
tx.unsigned_hash(),
));
self.wl_storage
.storage
.write(&wrapper_tx_hash_key, vec![])
.expect("Error while writing tx hash to storage");

let inner_tx_hash_key =
replay_protection::get_tx_hash_key(&wrapper.tx_hash);
self.wl_storage
.storage
.write(&inner_tx_hash_key, vec![])
.expect("Error while writing tx hash to storage");

#[cfg(not(feature = "mainnet"))]
let has_valid_pow =
self.invalidate_pow_solution_if_valid(wrapper);
Expand Down Expand Up @@ -216,11 +249,18 @@ where
#[cfg(not(feature = "mainnet"))]
has_valid_pow,
});
tx_event
(tx_event, None)
}
TxType::Decrypted(inner) => {
// We remove the corresponding wrapper tx from the queue
self.wl_storage.storage.tx_queue.pop();
let wrapper_hash = self
.wl_storage
.storage
.tx_queue
.pop()
.expect("Missing wrapper tx in queue")
.tx
.tx_hash;
let mut event = Event::new_tx_event(&tx_type, height.0);

match inner {
Expand All @@ -239,8 +279,7 @@ where
event["code"] = ErrorCodes::Undecryptable.into();
}
}

event
(event, Some(wrapper_hash))
}
TxType::Raw(_) => {
tracing::error!(
Expand Down Expand Up @@ -333,6 +372,25 @@ where
msg
);
stats.increment_errored_txs();

// If transaction type is Decrypted and failed because of
// out of gas, remove its hash from storage to allow
// rewrapping it
if let Some(hash) = tx_unsigned_hash {
if let Error::TxApply(protocol::Error::GasError(namada::ledger::gas::Error::TransactionGasExceededError)) =
msg
{
let tx_hash_key =
replay_protection::get_tx_hash_key(&hash);
self.wl_storage
.storage
.delete(&tx_hash_key)
.expect(
"Error while deleting tx hash key from storage",
);
}
}

self.wl_storage.drop_tx();
tx_event["gas_used"] = self
.gas_meter
Expand Down Expand Up @@ -691,7 +749,7 @@ mod test_finalize_block {
let mut processed_txs = vec![];
let mut valid_txs = vec![];

// Add unshielded balance for fee paymenty
// Add unshielded balance for fee payment
let balance_key = token::balance_key(
&shell.wl_storage.storage.native_token,
&Address::from(&keypair.ref_to()),
Expand Down Expand Up @@ -915,4 +973,79 @@ mod test_finalize_block {
last_storage_state = store_block_state(&shell);
}
}

/// Test that if a decrypted transaction fails because of out-of-gas, its
/// hash is removed from storage to allow rewrapping it
#[test]
fn test_remove_tx_hash() {
let (mut shell, _) = setup();
let keypair = gen_keypair();

let mut wasm_path = top_level_directory();
wasm_path.push("wasm_for_tests/tx_no_op.wasm");
let tx_code = std::fs::read(wasm_path)
.expect("Expected a file at given code path");
let raw_tx = Tx::new(
tx_code,
Some("Encrypted transaction data".as_bytes().to_owned()),
);
let wrapper_tx = WrapperTx::new(
Fee {
amount: 0.into(),
token: shell.wl_storage.storage.native_token.clone(),
},
&keypair,
Epoch(0),
0.into(),
raw_tx.clone(),
Default::default(),
#[cfg(not(feature = "mainnet"))]
None,
);

// Write inner hash in storage
let inner_hash_key =
replay_protection::get_tx_hash_key(&wrapper_tx.tx_hash);
shell
.wl_storage
.storage
.write(&inner_hash_key, vec![])
.expect("Test failed");

let processed_tx = ProcessedTx {
tx: Tx::from(TxType::Decrypted(DecryptedTx::Decrypted {
tx: raw_tx,
#[cfg(not(feature = "mainnet"))]
has_valid_pow: false,
}))
.to_bytes(),
result: TxResult {
code: ErrorCodes::Ok.into(),
info: "".into(),
},
};
shell.enqueue_tx(wrapper_tx);

let _event = &shell
.finalize_block(FinalizeBlock {
txs: vec![processed_tx],
..Default::default()
})
.expect("Test failed")[0];

// FIXME: uncomment when proper gas metering is in place
// // Check inner tx hash has been removed from storage
// assert_eq!(event.event_type.to_string(), String::from("applied"));
// let code = event.attributes.get("code").expect("Test
// failed").as_str(); assert_eq!(code,
// String::from(ErrorCodes::WasmRuntimeError).as_str());

// assert!(
// !shell
// .storage
// .has_key(&inner_hash_key)
// .expect("Test failed")
// .0
// )
}
}
Loading

0 comments on commit 62bc281

Please sign in to comment.