diff --git a/.changelog/unreleased/improvements/2131-validate-tx-bytes-len.md b/.changelog/unreleased/improvements/2131-validate-tx-bytes-len.md new file mode 100644 index 00000000000..13754266877 --- /dev/null +++ b/.changelog/unreleased/improvements/2131-validate-tx-bytes-len.md @@ -0,0 +1,2 @@ +- Tighten security around potential P2P issues + ([\#2131](https://github.com/anoma/namada/pull/2131)) \ No newline at end of file diff --git a/apps/src/lib/config/genesis.rs b/apps/src/lib/config/genesis.rs index 7e6998eea5e..b1fac85e811 100644 --- a/apps/src/lib/config/genesis.rs +++ b/apps/src/lib/config/genesis.rs @@ -153,7 +153,7 @@ pub struct ImplicitAccount { BorshDeserialize, )] pub struct Parameters { - // Max payload size, in bytes, for a tx batch proposal. + /// Max payload size, in bytes, for a tx batch proposal. pub max_proposal_bytes: ProposalBytes, /// Max block gas pub max_block_gas: u64, diff --git a/apps/src/lib/config/genesis/chain.rs b/apps/src/lib/config/genesis/chain.rs index bab430d4b1e..d63cca70103 100644 --- a/apps/src/lib/config/genesis/chain.rs +++ b/apps/src/lib/config/genesis/chain.rs @@ -262,6 +262,7 @@ impl Finalized { fee_unshielding_descriptions_limit, max_block_gas, minimum_gas_price, + max_tx_bytes, .. } = self.parameters.parameters.clone(); @@ -291,6 +292,7 @@ impl Finalized { let pos_inflation_amount = 0; namada::ledger::parameters::Parameters { + max_tx_bytes, epoch_duration, max_expected_time_per_block, vp_whitelist, diff --git a/apps/src/lib/config/genesis/templates.rs b/apps/src/lib/config/genesis/templates.rs index d670cb2916e..eb22bb1a62d 100644 --- a/apps/src/lib/config/genesis/templates.rs +++ b/apps/src/lib/config/genesis/templates.rs @@ -242,6 +242,9 @@ pub struct Parameters { Eq, )] pub struct ChainParams { + /// Max payload size, in bytes, for a tx decided through + /// the consensus protocol. + pub max_tx_bytes: u32, /// Name of the native token - this must one of the tokens from /// `tokens.toml` file pub native_token: Alias, @@ -296,6 +299,7 @@ impl ChainParams { tokens: &Tokens, ) -> eyre::Result> { let ChainParams { + max_tx_bytes, native_token, min_num_of_blocks, max_expected_time_per_block, @@ -342,6 +346,7 @@ impl ChainParams { } Ok(ChainParams { + max_tx_bytes, native_token, min_num_of_blocks, max_expected_time_per_block, diff --git a/apps/src/lib/node/ledger/shell/mod.rs b/apps/src/lib/node/ledger/shell/mod.rs index bc47f53c189..4a242a3e2ac 100644 --- a/apps/src/lib/node/ledger/shell/mod.rs +++ b/apps/src/lib/node/ledger/shell/mod.rs @@ -48,6 +48,7 @@ use namada::ledger::storage::{ DBIter, Sha256Hasher, Storage, StorageHasher, TempWlStorage, WlStorage, DB, EPOCH_SWITCH_BLOCKS_DELAY, }; +use namada::ledger::storage_api::tx::validate_tx_bytes; use namada::ledger::storage_api::{self, StorageRead}; use namada::ledger::{parameters, pos, protocol}; use namada::proof_of_stake::{self, process_slashes, read_pos_params, slash}; @@ -154,6 +155,7 @@ pub enum ErrorCodes { TxGasLimit = 11, FeeError = 12, InvalidVoteExtension = 13, + TooLarge = 14, } impl ErrorCodes { @@ -167,7 +169,8 @@ impl ErrorCodes { Ok | WasmRuntimeError => true, InvalidTx | InvalidSig | InvalidOrder | ExtraTxs | Undecryptable | AllocationError | ReplayTx | InvalidChainId - | ExpiredTx | TxGasLimit | FeeError | InvalidVoteExtension => false, + | ExpiredTx | TxGasLimit | FeeError | InvalidVoteExtension + | TooLarge => false, } } } @@ -1073,6 +1076,18 @@ where const VALID_MSG: &str = "Mempool validation passed"; const INVALID_MSG: &str = "Mempool validation failed"; + // check tx bytes + // + // NB: always keep this as the first tx check, + // as it is a pretty cheap one + if !validate_tx_bytes(&self.wl_storage, tx_bytes.len()) + .expect("Failed to get max tx bytes param from storage") + { + response.code = ErrorCodes::TooLarge.into(); + response.log = format!("{INVALID_MSG}: Tx too large"); + return response; + } + // Tx format check let tx = match Tx::try_from(tx_bytes).map_err(Error::TxDecoding) { Ok(t) => t, @@ -2091,6 +2106,7 @@ mod test_utils { .new_epoch(BlockHeight(1)); // initialize parameter storage let params = Parameters { + max_tx_bytes: 1024 * 1024, epoch_duration: EpochDuration { min_num_of_blocks: 1, min_duration: DurationSecs(3600), @@ -2945,4 +2961,56 @@ mod shell_tests { ); assert_eq!(result.code, ErrorCodes::FeeError.into()); } + + /// Test max tx bytes parameter in CheckTx + #[test] + fn test_max_tx_bytes_check_tx() { + let (shell, _recv, _, _) = test_utils::setup(); + + let max_tx_bytes: u32 = { + let key = parameters::storage::get_max_tx_bytes_key(); + shell + .wl_storage + .read(&key) + .expect("Failed to read from storage") + .expect("Max tx bytes should have been written to storage") + }; + + let new_tx = |size: u32| { + let keypair = super::test_utils::gen_keypair(); + let mut wrapper = Tx::from_type(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount_per_gas_unit: 100.into(), + token: shell.wl_storage.storage.native_token.clone(), + }, + keypair.ref_to(), + Epoch(0), + GAS_LIMIT_MULTIPLIER.into(), + None, + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new(vec![0; size as usize])); + wrapper.add_section(Section::Signature(Signature::new( + wrapper.sechashes(), + [(0, keypair)].into_iter().collect(), + None, + ))); + return wrapper; + }; + + // test a small tx + let result = shell.mempool_validate( + new_tx(50).to_bytes().as_ref(), + MempoolTxType::NewTransaction, + ); + assert!(result.code != ErrorCodes::TooLarge.into()); + + // max tx bytes + 1, on the other hand, is not + let result = shell.mempool_validate( + new_tx(max_tx_bytes + 1).to_bytes().as_ref(), + MempoolTxType::NewTransaction, + ); + assert_eq!(result.code, ErrorCodes::TooLarge.into()); + } } diff --git a/apps/src/lib/node/ledger/shell/process_proposal.rs b/apps/src/lib/node/ledger/shell/process_proposal.rs index 785eabb04f0..c04d77113df 100644 --- a/apps/src/lib/node/ledger/shell/process_proposal.rs +++ b/apps/src/lib/node/ledger/shell/process_proposal.rs @@ -7,6 +7,7 @@ use namada::core::ledger::storage::WlStorage; use namada::ledger::pos::PosQueries; use namada::ledger::protocol::get_fee_unshielding_transaction; use namada::ledger::storage::TempWlStorage; +use namada::ledger::storage_api::tx::validate_tx_bytes; use namada::proof_of_stake::find_validator_by_raw_hash; use namada::types::internal::TxInQueue; use namada::types::transaction::protocol::{ @@ -266,6 +267,19 @@ where where CA: 'static + WasmCacheAccess + Sync, { + // check tx bytes + // + // NB: always keep this as the first tx check, + // as it is a pretty cheap one + if !validate_tx_bytes(&self.wl_storage, tx_bytes.len()) + .expect("Failed to get max tx bytes param from storage") + { + return TxResult { + code: ErrorCodes::TooLarge.into(), + info: "Tx too large".into(), + }; + } + // try to allocate space for this tx if let Err(e) = metadata.txs_bin.try_dump(tx_bytes) { return TxResult { @@ -2039,4 +2053,68 @@ mod test_process_proposal { ); } } + + /// Test max tx bytes parameter in ProcessProposal + #[test] + fn test_max_tx_bytes_process_proposal() { + use namada::ledger::parameters::storage::get_max_tx_bytes_key; + let (shell, _recv, _, _) = test_utils::setup_at_height(3u64); + + let max_tx_bytes: u32 = { + let key = get_max_tx_bytes_key(); + shell + .wl_storage + .read(&key) + .expect("Failed to read from storage") + .expect("Max tx bytes should have been written to storage") + }; + + let new_tx = |size: u32| { + let keypair = super::test_utils::gen_keypair(); + let mut wrapper = Tx::from_type(TxType::Wrapper(Box::new(WrapperTx::new( + Fee { + amount_per_gas_unit: 100.into(), + token: shell.wl_storage.storage.native_token.clone(), + }, + keypair.ref_to(), + Epoch(0), + GAS_LIMIT_MULTIPLIER.into(), + None, + )))); + wrapper.header.chain_id = shell.chain_id.clone(); + wrapper.set_code(Code::new("wasm_code".as_bytes().to_owned())); + wrapper.set_data(Data::new(vec![0; size as usize])); + wrapper.add_section(Section::Signature(Signature::new( + wrapper.sechashes(), + [(0, keypair)].into_iter().collect(), + None, + ))); + return wrapper; + }; + + let request = ProcessProposal { + txs: vec![new_tx(max_tx_bytes + 1).to_bytes()], + }; + match shell.process_proposal(request) { + Ok(_) => panic!("Test failed"), + Err(TestError::RejectProposal(response)) => { + assert_eq!( + response[0].result.code, + u32::from(ErrorCodes::TooLarge) + ); + } + } + + let request = ProcessProposal { + txs: vec![new_tx(0).to_bytes()], + }; + match shell.process_proposal(request) { + Ok(_) => panic!("Test failed"), + Err(TestError::RejectProposal(response)) => { + assert!( + response[0].result.code != u32::from(ErrorCodes::TooLarge) + ); + } + } + } } diff --git a/apps/src/lib/node/ledger/storage/mod.rs b/apps/src/lib/node/ledger/storage/mod.rs index 5e57fe8a2d4..de761d98d69 100644 --- a/apps/src/lib/node/ledger/storage/mod.rs +++ b/apps/src/lib/node/ledger/storage/mod.rs @@ -146,6 +146,7 @@ mod tests { let mut wl_storage = WlStorage::new(WriteLog::default(), storage); // initialize parameter storage let params = Parameters { + max_tx_bytes: 1024 * 1024, epoch_duration: EpochDuration { min_num_of_blocks: 1, min_duration: DurationSecs(3600), diff --git a/apps/src/lib/node/ledger/tendermint_node.rs b/apps/src/lib/node/ledger/tendermint_node.rs index f57b8434921..9845b65c50b 100644 --- a/apps/src/lib/node/ledger/tendermint_node.rs +++ b/apps/src/lib/node/ledger/tendermint_node.rs @@ -347,11 +347,10 @@ pub fn id_from_pk(pk: &common::PublicKey) -> TendermintNodeId { async fn update_tendermint_config( home_dir: impl AsRef, - config: TendermintConfig, + mut config: TendermintConfig, ) -> Result<()> { let home_dir = home_dir.as_ref(); let path = home_dir.join("config").join("config.toml"); - let mut config = config.clone(); config.moniker = Moniker::from_str(&format!("{}-{}", config.moniker, namada_version())) @@ -359,10 +358,34 @@ async fn update_tendermint_config( config.consensus.create_empty_blocks = true; - // We set this to true as we don't want any invalid tx be re-applied. This - // also implies that it's not possible for an invalid tx to become valid - // again in the future. - config.mempool.keep_invalid_txs_in_cache = false; + // mempool config + // https://forum.cosmos.network/t/our-understanding-of-the-cosmos-hub-mempool-issues/12040 + { + // We set this to true as we don't want any invalid tx be re-applied. + // This also implies that it's not possible for an invalid tx to + // become valid again in the future. + config.mempool.keep_invalid_txs_in_cache = false; + + // Drop txs from the mempool that are larger than 1 MiB + // + // The application (Namada) can assign arbitrary max tx sizes, + // which are subject to consensus. Either way, nodes are able to + // configure their local mempool however they please. + // + // 1 MiB is a reasonable value that allows governance proposal txs + // containing wasm code to be proposed by a leading validator + // during some round's start + config.mempool.max_tx_bytes = 1024 * 1024; + + // Hold 50x the max amount of txs in a block + // + // 6 MiB is the default Namada max proposal size governance + // parameter -> 50 * 6 MiB + config.mempool.max_txs_bytes = 50 * 6 * 1024 * 1024; + + // Hold up to 4k txs in the mempool + config.mempool.size = 4000; + } // Bumped from the default `1_000_000`, because some WASMs can be // quite large @@ -408,11 +431,11 @@ async fn write_tm_genesis( .try_into() .expect("Couldn't convert DateTimeUtc to Tendermint Time"); let size = block::Size { - // maximum size of a serialized Tendermint block - // cannot go over 100 MiB - max_bytes: (100 << 20) - 1, /* unsure if we are dealing with an open - * range, so it's better to subtract one, - * here */ + // maximum size of a serialized Tendermint block. + // on Namada, we have a hard-cap of 16 MiB (6 MiB max + // txs in a block + 10 MiB reserved for evidence data, + // block headers and protobuf serialization overhead) + max_bytes: 16 * 1024 * 1024, // gas is metered app-side, so we disable it // at the Tendermint level max_gas: -1, diff --git a/core/src/ledger/parameters/mod.rs b/core/src/ledger/parameters/mod.rs index 0ad6d5a9184..f8f33d97684 100644 --- a/core/src/ledger/parameters/mod.rs +++ b/core/src/ledger/parameters/mod.rs @@ -35,6 +35,8 @@ pub const ADDRESS: Address = Address::Internal(InternalAddress::Parameters); BorshSchema, )] pub struct Parameters { + /// Max payload size, in bytes, for a mempool tx. + pub max_tx_bytes: u32, /// Epoch duration (read only) pub epoch_duration: EpochDuration, /// Maximum expected time per block (read only) @@ -117,6 +119,7 @@ impl Parameters { S: StorageRead + StorageWrite, { let Self { + max_tx_bytes, epoch_duration, max_expected_time_per_block, max_proposal_bytes, @@ -135,6 +138,10 @@ impl Parameters { fee_unshielding_descriptions_limit, } = self; + // write max tx bytes parameter + let max_tx_bytes_key = storage::get_max_tx_bytes_key(); + storage.write(&max_tx_bytes_key, max_tx_bytes)?; + // write max proposal bytes parameter let max_proposal_bytes_key = storage::get_max_proposal_bytes_key(); storage.write(&max_proposal_bytes_key, max_proposal_bytes)?; @@ -542,7 +549,15 @@ where .ok_or(ReadError::ParametersMissing) .into_storage_result()?; + // read max tx bytes + let max_tx_bytes_key = storage::get_max_tx_bytes_key(); + let value = storage.read(&max_tx_bytes_key)?; + let max_tx_bytes = value + .ok_or(ReadError::ParametersMissing) + .into_storage_result()?; + Ok(Parameters { + max_tx_bytes, epoch_duration, max_expected_time_per_block, max_proposal_bytes, diff --git a/core/src/ledger/parameters/storage.rs b/core/src/ledger/parameters/storage.rs index 31fac1abd4c..fd7f4fabadd 100644 --- a/core/src/ledger/parameters/storage.rs +++ b/core/src/ledger/parameters/storage.rs @@ -40,6 +40,7 @@ struct Keys { tx_whitelist: &'static str, vp_whitelist: &'static str, max_proposal_bytes: &'static str, + max_tx_bytes: &'static str, max_block_gas: &'static str, minimum_gas_price: &'static str, fee_unshielding_gas_limit: &'static str, @@ -120,6 +121,11 @@ pub fn is_max_proposal_bytes_key(key: &Key) -> bool { is_max_proposal_bytes_key_at_addr(key, &ADDRESS) } +/// Returns if the key is the max tx bytes key. +pub fn is_max_tx_bytes_key(key: &Key) -> bool { + is_max_tx_bytes_key_at_addr(key, &ADDRESS) +} + /// Storage key used for epoch parameter. pub fn get_epoch_duration_storage_key() -> Key { get_epoch_duration_key_at_addr(ADDRESS) @@ -185,6 +191,11 @@ pub fn get_max_proposal_bytes_key() -> Key { get_max_proposal_bytes_key_at_addr(ADDRESS) } +/// Storage key used for the max tx bytes. +pub fn get_max_tx_bytes_key() -> Key { + get_max_tx_bytes_key_at_addr(ADDRESS) +} + /// Storage key used for the max block gas. pub fn get_max_block_gas_key() -> Key { get_max_block_gas_key_at_addr(ADDRESS) diff --git a/core/src/ledger/storage/mod.rs b/core/src/ledger/storage/mod.rs index 2479f9adfa1..7a0f9333cb8 100644 --- a/core/src/ledger/storage/mod.rs +++ b/core/src/ledger/storage/mod.rs @@ -1400,6 +1400,7 @@ mod tests { ..Default::default() }; let mut parameters = Parameters { + max_tx_bytes: 1024 * 1024, max_proposal_bytes: Default::default(), max_block_gas: 20_000_000, epoch_duration: epoch_duration.clone(), diff --git a/core/src/ledger/storage_api/mod.rs b/core/src/ledger/storage_api/mod.rs index 3ec75843b1c..ad239b2f4cc 100644 --- a/core/src/ledger/storage_api/mod.rs +++ b/core/src/ledger/storage_api/mod.rs @@ -8,6 +8,7 @@ pub mod governance; pub mod key; pub mod pgf; pub mod token; +pub mod tx; pub mod validation; use borsh::{BorshDeserialize, BorshSerialize}; diff --git a/core/src/ledger/storage_api/tx.rs b/core/src/ledger/storage_api/tx.rs new file mode 100644 index 00000000000..0fdd56b34e0 --- /dev/null +++ b/core/src/ledger/storage_api/tx.rs @@ -0,0 +1,19 @@ +//! Tx storage_api functions + +use super::StorageRead; +use crate::ledger::parameters::storage::get_max_tx_bytes_key; +use crate::ledger::storage_api; + +/// Validate the size of a tx. +pub fn validate_tx_bytes( + storage: &S, + tx_size: usize, +) -> storage_api::Result +where + S: StorageRead, +{ + let max_tx_bytes: u32 = storage + .read(&get_max_tx_bytes_key())? + .expect("The max tx bytes param should be present in storage"); + Ok(tx_size <= max_tx_bytes as usize) +} diff --git a/core/src/types/chain.rs b/core/src/types/chain.rs index 26f82a5aa9d..2b22a138129 100644 --- a/core/src/types/chain.rs +++ b/core/src/types/chain.rs @@ -131,23 +131,19 @@ impl ProposalBytes { }; /// The (raw) default value for a [`ProposalBytes`]. /// - /// This value must be within the range `[1 B, 90 MiB]`. - const RAW_DEFAULT: NonZeroU64 = unsafe { - // SAFETY: We are constructing a greater than zero - // value, so the API contract is never violated. - // Moreover, 21 MiB <= 90 MiB. - NonZeroU64::new_unchecked(21 << 20) - }; + /// This value must be within the range `[1 B, RAW_MAX MiB]`. + const RAW_DEFAULT: NonZeroU64 = Self::RAW_MAX; /// The (raw) upper bound of a [`ProposalBytes`] value. /// /// The maximum space a serialized Tendermint block can /// occupy is 100 MiB. We reserve 10 MiB for serialization - /// overhead, evidence and header data, and 90 MiB for - /// tx data. + /// overhead, evidence and header data. For P2P safety + /// reasons (i.e. DoS protection) we hardcap the size of + /// tx data to 6 MiB. const RAW_MAX: NonZeroU64 = unsafe { // SAFETY: We are constructing a greater than zero // value, so the API contract is never violated. - NonZeroU64::new_unchecked(90 << 20) + NonZeroU64::new_unchecked(6 * 1024 * 1024) }; } diff --git a/genesis/localnet/parameters.toml b/genesis/localnet/parameters.toml index 41fb316f34a..d1b88f1f6fe 100644 --- a/genesis/localnet/parameters.toml +++ b/genesis/localnet/parameters.toml @@ -5,8 +5,10 @@ native_token = "NAM" min_num_of_blocks = 4 # Maximum expected time per block (in seconds). max_expected_time_per_block = 30 +# Max payload size, in bytes, for a tx. +max_tx_bytes = 1048576 # Max payload size, in bytes, for a tx batch proposal. -max_proposal_bytes = 22020096 +max_proposal_bytes = 6291456 # vp whitelist vp_whitelist = [] # tx whitelist diff --git a/genesis/starter/parameters.toml b/genesis/starter/parameters.toml index 86714f827c5..e4fbe38d904 100644 --- a/genesis/starter/parameters.toml +++ b/genesis/starter/parameters.toml @@ -5,8 +5,10 @@ native_token = "NAM" min_num_of_blocks = 4 # Maximum expected time per block (in seconds). max_expected_time_per_block = 30 +# Max payload size, in bytes, for a tx. +max_tx_bytes = 1048576 # Max payload size, in bytes, for a tx batch proposal. -max_proposal_bytes = 22020096 +max_proposal_bytes = 6291456 # vp whitelist vp_whitelist = [] # tx whitelist diff --git a/test_fixtures/masp_proofs/04E60A2C785353DA8A6374713205EBD81CB1534BEE3E2613C9D7B0741457379B.bin b/test_fixtures/masp_proofs/04E60A2C785353DA8A6374713205EBD81CB1534BEE3E2613C9D7B0741457379B.bin new file mode 100644 index 00000000000..a31b6369002 Binary files /dev/null and b/test_fixtures/masp_proofs/04E60A2C785353DA8A6374713205EBD81CB1534BEE3E2613C9D7B0741457379B.bin differ diff --git a/test_fixtures/masp_proofs/3685852CFE6B81E86B0E034A7B9CA78BCBDBA3D419DC41DD1C11400AEEFBA239.bin b/test_fixtures/masp_proofs/3685852CFE6B81E86B0E034A7B9CA78BCBDBA3D419DC41DD1C11400AEEFBA239.bin new file mode 100644 index 00000000000..3eb3dd257c4 Binary files /dev/null and b/test_fixtures/masp_proofs/3685852CFE6B81E86B0E034A7B9CA78BCBDBA3D419DC41DD1C11400AEEFBA239.bin differ diff --git a/test_fixtures/masp_proofs/60076AE3CE4C2200DABBF29508A41215EC575F801750DBBEBC7D25A8D8E15184.bin b/test_fixtures/masp_proofs/60076AE3CE4C2200DABBF29508A41215EC575F801750DBBEBC7D25A8D8E15184.bin new file mode 100644 index 00000000000..cf70e87f570 Binary files /dev/null and b/test_fixtures/masp_proofs/60076AE3CE4C2200DABBF29508A41215EC575F801750DBBEBC7D25A8D8E15184.bin differ diff --git a/test_fixtures/masp_proofs/794948CAF0698D563A2AB53690F93287C48ED4CFD159FE745D533B6FF369D838.bin b/test_fixtures/masp_proofs/794948CAF0698D563A2AB53690F93287C48ED4CFD159FE745D533B6FF369D838.bin new file mode 100644 index 00000000000..1aa50361aa0 Binary files /dev/null and b/test_fixtures/masp_proofs/794948CAF0698D563A2AB53690F93287C48ED4CFD159FE745D533B6FF369D838.bin differ diff --git a/test_fixtures/masp_proofs/7B8B510CCE25BF342C06E88A3139D72A66040F886F2CB30958BDB58427F87422.bin b/test_fixtures/masp_proofs/7B8B510CCE25BF342C06E88A3139D72A66040F886F2CB30958BDB58427F87422.bin new file mode 100644 index 00000000000..832a21f80c7 Binary files /dev/null and b/test_fixtures/masp_proofs/7B8B510CCE25BF342C06E88A3139D72A66040F886F2CB30958BDB58427F87422.bin differ diff --git a/test_fixtures/masp_proofs/BD9F1BE421A756E1BF5886DFAD8DC93EC8768ACE63234127EE44F3951E971C05.bin b/test_fixtures/masp_proofs/BD9F1BE421A756E1BF5886DFAD8DC93EC8768ACE63234127EE44F3951E971C05.bin new file mode 100644 index 00000000000..db805d4c2f2 Binary files /dev/null and b/test_fixtures/masp_proofs/BD9F1BE421A756E1BF5886DFAD8DC93EC8768ACE63234127EE44F3951E971C05.bin differ diff --git a/test_fixtures/masp_proofs/DB20322760CCA05AC835F1F8190106E9EC9F13BE8E633E5AEAE894B5B166EEF2.bin b/test_fixtures/masp_proofs/DB20322760CCA05AC835F1F8190106E9EC9F13BE8E633E5AEAE894B5B166EEF2.bin new file mode 100644 index 00000000000..016b618e7d6 Binary files /dev/null and b/test_fixtures/masp_proofs/DB20322760CCA05AC835F1F8190106E9EC9F13BE8E633E5AEAE894B5B166EEF2.bin differ diff --git a/test_fixtures/masp_proofs/E9B02EB4E717627A13301634043991638F92173468DB8B30D09887313462DC3B.bin b/test_fixtures/masp_proofs/E9B02EB4E717627A13301634043991638F92173468DB8B30D09887313462DC3B.bin new file mode 100644 index 00000000000..eeeb2652c15 Binary files /dev/null and b/test_fixtures/masp_proofs/E9B02EB4E717627A13301634043991638F92173468DB8B30D09887313462DC3B.bin differ diff --git a/test_fixtures/masp_proofs/EB02A8490E5F6064629EB2F5C6A0219764C42FA314904F266135C6AAE0776A75.bin b/test_fixtures/masp_proofs/EB02A8490E5F6064629EB2F5C6A0219764C42FA314904F266135C6AAE0776A75.bin new file mode 100644 index 00000000000..459e01a0a34 Binary files /dev/null and b/test_fixtures/masp_proofs/EB02A8490E5F6064629EB2F5C6A0219764C42FA314904F266135C6AAE0776A75.bin differ diff --git a/test_fixtures/masp_proofs/EFF502561CFD3DD0F3EAEBFB8D070A55C56FDC98CA764B2C999D0E26753FCC63.bin b/test_fixtures/masp_proofs/EFF502561CFD3DD0F3EAEBFB8D070A55C56FDC98CA764B2C999D0E26753FCC63.bin new file mode 100644 index 00000000000..04e6d4b29e9 Binary files /dev/null and b/test_fixtures/masp_proofs/EFF502561CFD3DD0F3EAEBFB8D070A55C56FDC98CA764B2C999D0E26753FCC63.bin differ diff --git a/test_fixtures/masp_proofs/FBC60F842BA5ECAFEF0892D67ECD4E375F9716294B2D7513AF9ECDCCEFE220D2.bin b/test_fixtures/masp_proofs/FBC60F842BA5ECAFEF0892D67ECD4E375F9716294B2D7513AF9ECDCCEFE220D2.bin new file mode 100644 index 00000000000..5317275a42c Binary files /dev/null and b/test_fixtures/masp_proofs/FBC60F842BA5ECAFEF0892D67ECD4E375F9716294B2D7513AF9ECDCCEFE220D2.bin differ