-
Notifications
You must be signed in to change notification settings - Fork 155
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
allow transactions with "mixed" token types #1827
allow transactions with "mixed" token types #1827
Conversation
e6dcbaf
to
7e82974
Compare
9657189
to
e9f37ab
Compare
dbd5cc6
to
3e1cefe
Compare
330f86f
to
3e63fd7
Compare
Co-authored-by: sugargoat <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome - this is looking great. I'm going to take one more pass on the rct_bulletproofs
code and the transaction_builder
changes, but very close to approval from my perspective!
Co-authored-by: sugargoat <[email protected]>
Co-authored-by: sugargoat <[email protected]>
Co-authored-by: sugargoat <[email protected]>
Co-authored-by: sugargoat <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM once all of @sugargoat 's comments are addressed.
…tion#1827)" This reverts commit 36ed5152969bba4cfa892445f22696752853886e. These were the conflicts I resolved: ``` diff --cc Cargo.lock index f71361a4,fc9f5b6c..00000000 --- a/Cargo.lock +++ b/Cargo.lock diff --cc mobilecoind/src/payments.rs index c3b6f0ed,5e0d6ea4..00000000 --- a/mobilecoind/src/payments.rs +++ b/mobilecoind/src/payments.rs @@@ -21,10 -21,10 +21,10 @@@ use mc_transaction_core:: onetime_keys::recover_onetime_private_key, ring_signature::KeyImage, tx::{Tx, TxOut, TxOutConfirmationNumber, TxOutMembershipProof}, - Amount, BlockIndex, BlockVersion, TokenId, + BlockIndex, BlockVersion, TokenId, }; use mc_transaction_std::{ - ChangeDestination, EmptyMemoBuilder, InputCredentials, TransactionBuilder, + ChangeDestination, EmptyMemoBuilder, InputCredentials, MemoBuilder, TransactionBuilder, }; use mc_util_uri::FogUri; use rand::Rng; @@@ -876,17 -867,19 +876,33 @@@ impl<T: BlockchainConnection + UserTxCo fog_resolver_factory(&fog_uris).map_err(Error::Fog)? }; ++<<<<<<< HEAD + // Create tx_builder. + // TODO (GH #1522): Use RTH memo builder, optionally? + let memo_builder: Box<dyn MemoBuilder + Send + Sync> = + opt_memo_builder.unwrap_or_else(|| Box::new(EmptyMemoBuilder::default())); + + let fee_amount = Amount::new(fee, token_id); + let mut tx_builder = + TransactionBuilder::new_with_box(block_version, fee_amount, fog_resolver, memo_builder) + .map_err(|err| { + Error::TxBuild(format!("Error creating transaction builder: {}", err)) + })?; ++======= + // TODO: Use RTH memo builder, optionally? + + // Create tx_builder. + let mut tx_builder = TransactionBuilder::new( + block_version, + token_id, + fog_resolver, + EmptyMemoBuilder::default(), + ); + + tx_builder + .set_fee(fee) + .map_err(|err| Error::TxBuild(format!("Error setting fee: {}", err)))?; ++>>>>>>> parent of 36ed5152... allow transactions with "mixed" token types (#1827) // Unzip each vec of tuples into a tuple of vecs. let mut rings_and_proofs: Vec<(Vec<TxOut>, Vec<TxOutMembershipProof>)> = rings diff --cc transaction/core/src/ring_signature/rct_bulletproofs.rs index 9c844bd7,836799f3..00000000 --- a/transaction/core/src/ring_signature/rct_bulletproofs.rs +++ b/transaction/core/src/ring_signature/rct_bulletproofs.rs @@@ -830,11 -529,7 +529,15 @@@ mod rct_bulletproofs_tests let value = rng.next_u64(); let blinding = Scalar::random(rng); ++<<<<<<< HEAD + + let token_id = TokenId::from(token_ids[i % token_ids.len()]); + let generator = generator_cache.get(token_id); + + let commitment = CompressedCommitment::new(value, blinding, generator); ++======= + let commitment = CompressedCommitment::new(value, blinding, &generator); ++>>>>>>> parent of 36ed5152... allow transactions with "mixed" token types (#1827) let real_index = rng.next_u64() as usize % (num_mixins + 1); ring.insert(real_index, (onetime_public_key, commitment)); diff --cc transaction/core/src/tx_error.rs index 29e5c016,e6bf28be..00000000 --- a/transaction/core/src/tx_error.rs +++ b/transaction/core/src/tx_error.rs @@@ -65,14 -65,6 +65,17 @@@ pub enum NewMemoError OutputsAfterChange, /// Changing the fee after the change output is not supported FeeAfterChange, ++<<<<<<< HEAD + /// Invalid recipient address + InvalidRecipient, + /// Multiple outputs are not supported + MultipleOutputs, + /// Missing output + MissingOutput, + /// Mixed Token Ids are not supported in these memos + MixedTokenIds, ++======= ++>>>>>>> parent of 36ed5152... allow transactions with "mixed" token types (#1827) /// Other: {0} Other(String), } diff --cc transaction/core/src/validation/validate.rs index 27b55868,0e74d16e..00000000 --- a/transaction/core/src/validation/validate.rs +++ b/transaction/core/src/validation/validate.rs @@@ -487,13 -484,888 +488,901 @@@ fn check_unique<T: Eq + core::hash::Has Ok(()) } ++<<<<<<< HEAD +// NOTE: There are unit tests of every validation function, which appear in +// transaction/core/tests/validation.rs. +// +// The reason that these appear there is, +// many of the tests use `mc-transaction-core-test-utils` which itself depends +// on `mc-ledger-db` and `mc-transaction-core`, and this creates a circular +// dependency which leads to build problems, if the unit tests appear in-line +// here. +// +// Please add tests for any new validation functions there. Thank you! ++======= + #[cfg(test)] + mod tests { + use super::*; + + extern crate alloc; + + use alloc::vec::Vec; + + use crate::{ + constants::RING_SIZE, + tokens::Mob, + tx::{Tx, TxOutMembershipHash, TxOutMembershipProof}, + validation::error::TransactionValidationError, + Token, + }; + + use crate::membership_proofs::Range; + use mc_crypto_keys::{CompressedRistrettoPublic, ReprBytes}; + use mc_ledger_db::{Ledger, LedgerDB}; + use mc_transaction_core_test_utils::{ + create_ledger, create_transaction, create_transaction_with_amount_and_comparer, + initialize_ledger, AccountKey, InverseTxOutputsOrdering, INITIALIZE_LEDGER_AMOUNT, + }; + use mc_transaction_std::{DefaultTxOutputsOrdering, TxOutputsOrdering}; + use rand::{rngs::StdRng, SeedableRng}; + use serde::{de::DeserializeOwned, ser::Serialize}; + + // HACK: To test validation we need valid Tx objects. The code to create them is + // complicated, and a variant of it resides inside the + // `transaction_test_utils` crate. However,when we depend on it in our + // [dev-dependencies], it will compile and link against another copy of this + // crate, since cargo is weird like that. Relying in the fact that both data + // structures are actually the same, this hack lets us convert from the + // `transaction` crate being compiled by `transaction_test_utils` to the one + // compiled as part of building test tests. + // If we want to avoid this hack, we could move transaction validation into its + // own crate. + fn adapt_hack<Src: Serialize, Dst: DeserializeOwned>(src: &Src) -> Dst { + let bytes = mc_util_serial::serialize(src).unwrap(); + mc_util_serial::deserialize(&bytes).unwrap() + } + + fn create_test_tx(block_version: BlockVersion) -> (Tx, LedgerDB) { + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let sender = AccountKey::random(&mut rng); + let mut ledger = create_ledger(); + let n_blocks = 1; + initialize_ledger( + adapt_hack(&block_version), + &mut ledger, + n_blocks, + &sender, + &mut rng, + ); + + // Spend an output from the last block. + let block_contents = ledger.get_block_contents(n_blocks - 1).unwrap(); + let tx_out = block_contents.outputs[0].clone(); + + let recipient = AccountKey::random(&mut rng); + let tx = create_transaction( + adapt_hack(&block_version), + &mut ledger, + &tx_out, + &sender, + &recipient.default_subaddress(), + n_blocks + 1, + &mut rng, + ); + + (adapt_hack(&tx), ledger) + } + + fn create_test_tx_with_amount( + block_version: BlockVersion, + amount: u64, + fee: u64, + ) -> (Tx, LedgerDB) { + create_test_tx_with_amount_and_comparer::<DefaultTxOutputsOrdering>( + block_version, + amount, + fee, + ) + } + + fn create_test_tx_with_amount_and_comparer<O: TxOutputsOrdering>( + block_version: BlockVersion, + amount: u64, + fee: u64, + ) -> (Tx, LedgerDB) { + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let sender = AccountKey::random(&mut rng); + let mut ledger = create_ledger(); + let n_blocks = 1; + initialize_ledger( + adapt_hack(&block_version), + &mut ledger, + n_blocks, + &sender, + &mut rng, + ); + + // Spend an output from the last block. + let block_contents = ledger.get_block_contents(n_blocks - 1).unwrap(); + let tx_out = block_contents.outputs[0].clone(); + + let recipient = AccountKey::random(&mut rng); + let tx = create_transaction_with_amount_and_comparer::<_, _, O>( + adapt_hack(&block_version), + &mut ledger, + &tx_out, + &sender, + &recipient.default_subaddress(), + amount, + fee, + n_blocks + 1, + &mut rng, + ); + + (adapt_hack(&tx), ledger) + } + + #[test] + // Should return MissingMemo when memos are missing in an output + fn test_validate_memo_exists() { + let (tx, _) = create_test_tx(BlockVersion::ZERO); + let tx_out = tx.prefix.outputs.first().unwrap(); + + assert!(tx_out.e_memo.is_none()); + assert_eq!( + validate_memo_exists(tx_out), + Err(TransactionValidationError::MissingMemo) + ); + + let (tx, _) = create_test_tx(BlockVersion::ONE); + let tx_out = tx.prefix.outputs.first().unwrap(); + + assert!(tx_out.e_memo.is_some()); + assert_eq!(validate_memo_exists(tx_out), Ok(())); + } + + #[test] + // Should return MemosNotAllowed when memos are present in an output + fn test_validate_no_memo_exists() { + let (tx, _) = create_test_tx(BlockVersion::ZERO); + let tx_out = tx.prefix.outputs.first().unwrap(); + + assert!(tx_out.e_memo.is_none()); + assert_eq!(validate_no_memo_exists(tx_out), Ok(())); + + let (tx, _) = create_test_tx(BlockVersion::ONE); + let tx_out = tx.prefix.outputs.first().unwrap(); + + assert!(tx_out.e_memo.is_some()); + assert_eq!( + validate_no_memo_exists(tx_out), + Err(TransactionValidationError::MemosNotAllowed) + ); + } + + #[test] + // Should return MissingMaskedTokenId when masked_token_id are missing in an + // output + fn test_validate_masked_token_id_exists() { + let (tx, _) = create_test_tx(BlockVersion::ONE); + let tx_out = tx.prefix.outputs.first().unwrap(); + + assert!(tx_out.masked_amount.masked_token_id.is_empty()); + assert_eq!( + validate_masked_token_id_exists(tx_out), + Err(TransactionValidationError::MissingMaskedTokenId) + ); + + let (tx, _) = create_test_tx(BlockVersion::TWO); + let tx_out = tx.prefix.outputs.first().unwrap(); + + assert!(!tx_out.masked_amount.masked_token_id.is_empty()); + assert_eq!(validate_memo_exists(tx_out), Ok(())); + } + + #[test] + // Should return MemosNotAllowed when memos are present in an output + fn test_validate_no_masked_token_id_exists() { + let (tx, _) = create_test_tx(BlockVersion::ONE); + let tx_out = tx.prefix.outputs.first().unwrap(); + + assert!(tx_out.masked_amount.masked_token_id.is_empty()); + assert_eq!(validate_no_masked_token_id_exists(tx_out), Ok(())); + + let (tx, _) = create_test_tx(BlockVersion::TWO); + let tx_out = tx.prefix.outputs.first().unwrap(); + + assert!(!tx_out.masked_amount.masked_token_id.is_empty()); + assert_eq!( + validate_no_masked_token_id_exists(tx_out), + Err(TransactionValidationError::MaskedTokenIdNotAllowed) + ); + } + + #[test] + // Should return Ok(()) when the Tx's membership proofs are correct and agree + // with ledger. + fn test_validate_membership_proofs() { + for block_version in BlockVersion::iterator() { + let (tx, ledger) = create_test_tx(block_version); + + let highest_indices = tx.get_membership_proof_highest_indices(); + let root_proofs: Vec<TxOutMembershipProof> = adapt_hack( + &ledger + .get_tx_out_proof_of_memberships(&highest_indices) + .expect("failed getting proofs"), + ); + + // Validate the transaction prefix without providing the correct ledger context. + { + let mut broken_proofs = root_proofs.clone(); + broken_proofs[0].elements[0].hash = TxOutMembershipHash::from([1u8; 32]); + assert_eq!( + validate_membership_proofs(&tx.prefix, &broken_proofs), + Err(TransactionValidationError::InvalidTxOutMembershipProof) + ); + } + + // Validate the transaction prefix with the correct root proofs. + { + let highest_indices = tx.get_membership_proof_highest_indices(); + let root_proofs: Vec<TxOutMembershipProof> = adapt_hack( + &ledger + .get_tx_out_proof_of_memberships(&highest_indices) + .expect("failed getting proofs"), + ); + assert_eq!(validate_membership_proofs(&tx.prefix, &root_proofs), Ok(())); + } + } + } + + #[test] + // Should return InvalidRangeProof if a membership proof containing an invalid + // Range. + fn test_validate_membership_proofs_invalid_range_in_tx() { + for block_version in BlockVersion::iterator() { + let (mut tx, ledger) = create_test_tx(block_version); + + let highest_indices = tx.get_membership_proof_highest_indices(); + let root_proofs: Vec<TxOutMembershipProof> = adapt_hack( + &ledger + .get_tx_out_proof_of_memberships(&highest_indices) + .expect("failed getting proofs"), + ); + + // Modify tx to include an invalid Range. + let mut proof = tx.prefix.inputs[0].proofs[0].clone(); + let mut first_element = proof.elements[0].clone(); + first_element.range = Range { from: 7, to: 3 }; + proof.elements[0] = first_element; + tx.prefix.inputs[0].proofs[0] = proof; + + assert_eq!( + validate_membership_proofs(&tx.prefix, &root_proofs), + Err(TransactionValidationError::MembershipProofValidationError) + ); + } + } + + #[test] + // Should return InvalidRangeProof if a root proof containing an invalid Range. + fn test_validate_membership_proofs_invalid_range_in_root_proof() { + for block_version in BlockVersion::iterator() { + let (tx, ledger) = create_test_tx(block_version); + + let highest_indices = tx.get_membership_proof_highest_indices(); + let mut root_proofs: Vec<TxOutMembershipProof> = adapt_hack( + &ledger + .get_tx_out_proof_of_memberships(&highest_indices) + .expect("failed getting proofs"), + ); + + // Modify a root proof to include an invalid Range. + let mut proof = root_proofs[0].clone(); + let mut first_element = proof.elements[0].clone(); + first_element.range = Range { from: 7, to: 3 }; + proof.elements[0] = first_element; + root_proofs[0] = proof; + + assert_eq!( + validate_membership_proofs(&tx.prefix, &root_proofs), + Err(TransactionValidationError::MembershipProofValidationError) + ); + } + } + + #[test] + fn test_validate_number_of_inputs() { + for block_version in BlockVersion::iterator() { + let (orig_tx, _ledger) = create_test_tx(block_version); + let max_inputs = 25; + + for num_inputs in 0..100 { + let mut tx_prefix = orig_tx.prefix.clone(); + tx_prefix.inputs.clear(); + for _i in 0..num_inputs { + tx_prefix.inputs.push(orig_tx.prefix.inputs[0].clone()); + } + + let expected_result = if num_inputs == 0 { + Err(TransactionValidationError::NoInputs) + } else if num_inputs > max_inputs { + Err(TransactionValidationError::TooManyInputs) + } else { + Ok(()) + }; + + assert_eq!( + validate_number_of_inputs(&tx_prefix, max_inputs), + expected_result, + ); + } + } + } + + #[test] + fn test_validate_number_of_outputs() { + for block_version in BlockVersion::iterator() { + let (orig_tx, _ledger) = create_test_tx(block_version); + let max_outputs = 25; + + for num_outputs in 0..100 { + let mut tx_prefix = orig_tx.prefix.clone(); + tx_prefix.outputs.clear(); + for _i in 0..num_outputs { + tx_prefix.outputs.push(orig_tx.prefix.outputs[0].clone()); + } + + let expected_result = if num_outputs == 0 { + Err(TransactionValidationError::NoOutputs) + } else if num_outputs > max_outputs { + Err(TransactionValidationError::TooManyOutputs) + } else { + Ok(()) + }; + + assert_eq!( + validate_number_of_outputs(&tx_prefix, max_outputs), + expected_result, + ); + } + } + } + + #[test] + fn test_validate_ring_sizes() { + for block_version in BlockVersion::iterator() { + let (tx, _ledger) = create_test_tx(block_version); + assert_eq!(tx.prefix.inputs.len(), 1); + assert_eq!(tx.prefix.inputs[0].ring.len(), RING_SIZE); + + // A transaction with a single input containing RING_SIZE elements. + assert_eq!(validate_ring_sizes(&tx.prefix, RING_SIZE), Ok(())); + + // A single input containing zero elements. + { + let mut tx_prefix = tx.prefix.clone(); + tx_prefix.inputs[0].ring.clear(); + + assert_eq!( + validate_ring_sizes(&tx_prefix, RING_SIZE), + Err(TransactionValidationError::InsufficientRingSize), + ); + } + + // A single input containing too few elements. + { + let mut tx_prefix = tx.prefix.clone(); + tx_prefix.inputs[0].ring.pop(); + + assert_eq!( + validate_ring_sizes(&tx_prefix, RING_SIZE), + Err(TransactionValidationError::InsufficientRingSize), + ); + } + + // A single input containing too many elements. + { + let mut tx_prefix = tx.prefix.clone(); + let element = tx_prefix.inputs[0].ring[0].clone(); + tx_prefix.inputs[0].ring.push(element); + + assert_eq!( + validate_ring_sizes(&tx_prefix, RING_SIZE), + Err(TransactionValidationError::ExcessiveRingSize), + ); + } + + // Two inputs each containing RING_SIZE elements. + { + let mut tx_prefix = tx.prefix.clone(); + let input = tx_prefix.inputs[0].clone(); + tx_prefix.inputs.push(input); + + assert_eq!(validate_ring_sizes(&tx_prefix, RING_SIZE), Ok(())); + } + + // The second input contains too few elements. + { + let mut tx_prefix = tx.prefix.clone(); + let mut input = tx_prefix.inputs[0].clone(); + input.ring.pop(); + tx_prefix.inputs.push(input); + + assert_eq!( + validate_ring_sizes(&tx_prefix, RING_SIZE), + Err(TransactionValidationError::InsufficientRingSize), + ); + } + } + } + + #[test] + fn test_validate_ring_elements_are_unique() { + for block_version in BlockVersion::iterator() { + let (tx, _ledger) = create_test_tx(block_version); + assert_eq!(tx.prefix.inputs.len(), 1); + + // A transaction with a single input and unique ring elements. + assert_eq!(validate_ring_elements_are_unique(&tx.prefix), Ok(())); + + // A transaction with a single input and duplicate ring elements. + { + let mut tx_prefix = tx.prefix.clone(); + tx_prefix.inputs[0] + .ring + .push(tx.prefix.inputs[0].ring[0].clone()); + + assert_eq!( + validate_ring_elements_are_unique(&tx_prefix), + Err(TransactionValidationError::DuplicateRingElements) + ); + } + + // A transaction with a multiple inputs and unique ring elements. + { + let mut tx_prefix = tx.prefix.clone(); + tx_prefix.inputs.push(tx.prefix.inputs[0].clone()); + + for mut tx_out in tx_prefix.inputs[1].ring.iter_mut() { + let mut bytes = tx_out.target_key.to_bytes(); + bytes[0] = !bytes[0]; + tx_out.target_key = CompressedRistrettoPublic::from_bytes(&bytes).unwrap(); + } + + assert_eq!(validate_ring_elements_are_unique(&tx_prefix), Ok(())); + } + + // A transaction with a multiple inputs and duplicate ring elements in different + // rings. + { + let mut tx_prefix = tx.prefix.clone(); + tx_prefix.inputs.push(tx.prefix.inputs[0].clone()); + + assert_eq!( + validate_ring_elements_are_unique(&tx_prefix), + Err(TransactionValidationError::DuplicateRingElements) + ); + } + } + } + + #[test] + /// validate_ring_elements_are_sorted should reject an unsorted ring. + fn test_validate_ring_elements_are_sorted() { + for block_version in BlockVersion::iterator() { + let (mut tx, _ledger) = create_test_tx(block_version); + assert_eq!(validate_ring_elements_are_sorted(&tx.prefix), Ok(())); + + // Change the ordering of a ring. + tx.prefix.inputs[0].ring.swap(0, 3); + assert_eq!( + validate_ring_elements_are_sorted(&tx.prefix), + Err(TransactionValidationError::UnsortedRingElements) + ); + } + } + + #[test] + /// validate_inputs_are_sorted should reject unsorted inputs. + fn test_validate_inputs_are_sorted() { + for block_version in BlockVersion::iterator() { + let (tx, _ledger) = create_test_tx(block_version); + + // Add a second input to the transaction. + let mut tx_prefix = tx.prefix.clone(); + tx_prefix.inputs.push(tx.prefix.inputs[0].clone()); + + // By removing the first ring element of the second input we ensure the inputs + // are different, but remain sorted (since the ring elements are + // sorted). + tx_prefix.inputs[1].ring.remove(0); + + assert_eq!(validate_inputs_are_sorted(&tx_prefix), Ok(())); + + // Change the ordering of inputs. + tx_prefix.inputs.swap(0, 1); + assert_eq!( + validate_inputs_are_sorted(&tx_prefix), + Err(TransactionValidationError::UnsortedInputs) + ); + } + } + + #[test] + /// Should reject a transaction with unsorted outputs. + fn test_validate_outputs_are_sorted() { + for block_version in BlockVersion::iterator() { + let (tx, _ledger) = create_test_tx(block_version); + + let mut output_a = tx.prefix.outputs.get(0).unwrap().clone(); + output_a.public_key = CompressedRistrettoPublic::from(&[1u8; 32]); + + let mut output_b = output_a.clone(); + output_b.public_key = CompressedRistrettoPublic::from(&[2u8; 32]); + + assert!(output_a.public_key < output_b.public_key); + + { + let mut tx_prefix = tx.prefix.clone(); + // A single output is trivially sorted. + tx_prefix.outputs = vec![output_a.clone()]; + assert_eq!(validate_outputs_are_sorted(&tx_prefix), Ok(())); + } + + { + let mut tx_prefix = tx.prefix.clone(); + // Outputs sorted by public_key, ascending. + tx_prefix.outputs = vec![output_a.clone(), output_b.clone()]; + assert_eq!(validate_outputs_are_sorted(&tx_prefix), Ok(())); + } + + { + let mut tx_prefix = tx.prefix.clone(); + // Outputs are not correctly sorted. + tx_prefix.outputs = vec![output_b.clone(), output_a.clone()]; + assert_eq!( + validate_outputs_are_sorted(&tx_prefix), + Err(TransactionValidationError::UnsortedOutputs) + ); + } + } + } + + #[test] + /// validate_key_images_are_unique rejects duplicate key image. + fn test_validate_key_images_are_unique_rejects_duplicate() { + for block_version in BlockVersion::iterator() { + let (mut tx, _ledger) = create_test_tx(block_version); + // Tx only contains a single ring signature, which contains the key image. + // Duplicate the ring signature so that tx.key_images() returns a + // duplicate key image. + let ring_signature = tx.signature.ring_signatures[0].clone(); + tx.signature.ring_signatures.push(ring_signature); + + assert_eq!( + validate_key_images_are_unique(&tx), + Err(TransactionValidationError::DuplicateKeyImages) + ); + } + } + + #[test] + /// validate_key_images_are_unique returns Ok if all key images are unique. + fn test_validate_key_images_are_unique_ok() { + for block_version in BlockVersion::iterator() { + let (tx, _ledger) = create_test_tx(block_version); + assert_eq!(validate_key_images_are_unique(&tx), Ok(()),); + } + } + + #[test] + /// validate_outputs_public_keys_are_unique rejects duplicate public key. + fn test_validate_output_public_keys_are_unique_rejects_duplicate() { + for block_version in BlockVersion::iterator() { + let (mut tx, _ledger) = create_test_tx(block_version); + // Tx only contains a single output. Duplicate the + // output so that tx.output_public_keys() returns a duplicate public key. + let tx_out = tx.prefix.outputs[0].clone(); + tx.prefix.outputs.push(tx_out); + + assert_eq!( + validate_outputs_public_keys_are_unique(&tx), + Err(TransactionValidationError::DuplicateOutputPublicKey) + ); + } + } + + #[test] + /// validate_outputs_public_keys_are_unique returns Ok if all public keys + /// are unique. + fn test_validate_output_public_keys_are_unique_ok() { + for block_version in BlockVersion::iterator() { + let (tx, _ledger) = create_test_tx(block_version); + assert_eq!(validate_outputs_public_keys_are_unique(&tx), Ok(()),); + } + } + + #[test] + // `validate_signature` return OK for a valid transaction. + fn test_validate_signature_ok() { + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + + for block_version in BlockVersion::iterator() { + let (tx, _ledger) = create_test_tx(block_version); + assert_eq!(validate_signature(block_version, &tx, &mut rng), Ok(())); + } + } + + #[test] + // Should return InvalidTransactionSignature if an input is modified. + fn test_transaction_signature_err_modified_input() { + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + + for block_version in BlockVersion::iterator() { + let (mut tx, _ledger) = create_test_tx(block_version); + + // Remove an input. + tx.prefix.inputs[0].ring.pop(); + + match validate_signature(block_version, &tx, &mut rng) { + Err(TransactionValidationError::InvalidTransactionSignature(_e)) => {} // Expected. + Err(e) => { + panic!("Unexpected error {}", e); + } + Ok(()) => panic!("Unexpected success"), + } + } + } + + #[test] + // Should return InvalidTransactionSignature if an output is modified. + fn test_transaction_signature_err_modified_output() { + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + + for block_version in BlockVersion::iterator() { + let (mut tx, _ledger) = create_test_tx(block_version); + + // Add an output. + let output = tx.prefix.outputs.get(0).unwrap().clone(); + tx.prefix.outputs.push(output); + + match validate_signature(block_version, &tx, &mut rng) { + Err(TransactionValidationError::InvalidTransactionSignature(_e)) => {} // Expected. + Err(e) => { + panic!("Unexpected error {}", e); + } + Ok(()) => panic!("Unexpected success"), + } + } + } + + #[test] + // Should return InvalidTransactionSignature if the fee is modified. + fn test_transaction_signature_err_modified_fee() { + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + + for block_version in BlockVersion::iterator() { + let (mut tx, _ledger) = create_test_tx(block_version); + + tx.prefix.fee += 1; + + match validate_signature(block_version, &tx, &mut rng) { + Err(TransactionValidationError::InvalidTransactionSignature(_e)) => {} // Expected. + Err(e) => { + panic!("Unexpected error {}", e); + } + Ok(()) => panic!("Unexpected success"), + } + } + } + + #[test] + // Should return InvalidTransactionSignature if the token_id is modified + fn test_transaction_signature_err_modified_token_id() { + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + + for _ in 0..3 { + let (mut tx, _ledger) = create_test_tx(BlockVersion::TWO); + + tx.prefix.token_id += 1; + + match validate_signature(BlockVersion::TWO, &tx, &mut rng) { + Err(TransactionValidationError::InvalidTransactionSignature(_e)) => {} // Expected. + Err(e) => { + panic!("Unexpected error {}", e); + } + Ok(()) => panic!("Unexpected success"), + } + } + } + + #[test] + // Should return InvalidTransactionSignature if block v 1 is validated as 2 + fn test_transaction_signature_err_version_one_as_two() { + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + + for _ in 0..3 { + let (tx, _ledger) = create_test_tx(BlockVersion::ONE); + + match validate_signature(BlockVersion::TWO, &tx, &mut rng) { + Err(TransactionValidationError::InvalidTransactionSignature(_e)) => {} // Expected. + Err(e) => { + panic!("Unexpected error {}", e); + } + Ok(()) => panic!("Unexpected success"), + } + } + } + + #[test] + // Should return InvalidTransactionSignature if block v 2 is validated as 1 + fn test_transaction_signature_err_version_two_as_one() { + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + + for _ in 0..3 { + let (tx, _ledger) = create_test_tx(BlockVersion::TWO); + + match validate_signature(BlockVersion::ONE, &tx, &mut rng) { + Err(TransactionValidationError::InvalidTransactionSignature(_e)) => {} // Expected. + Err(e) => { + panic!("Unexpected error {}", e); + } + Ok(()) => panic!("Unexpected success"), + } + } + } + + #[test] + fn test_validate_transaction_fee() { + for block_version in BlockVersion::iterator() { + { + // Zero fees gets rejected + let (tx, _ledger) = + create_test_tx_with_amount(block_version, INITIALIZE_LEDGER_AMOUNT, 0); + assert_eq!( + validate_transaction_fee(&tx, 1000), + Err(TransactionValidationError::TxFeeError) + ); + } + + { + // Off by one fee gets rejected + let fee = Mob::MINIMUM_FEE - 1; + let (tx, _ledger) = + create_test_tx_with_amount(block_version, INITIALIZE_LEDGER_AMOUNT - fee, fee); + assert_eq!( + validate_transaction_fee(&tx, Mob::MINIMUM_FEE), + Err(TransactionValidationError::TxFeeError) + ); + } + + { + // Exact fee amount is okay + let (tx, _ledger) = create_test_tx_with_amount( + block_version, + INITIALIZE_LEDGER_AMOUNT - Mob::MINIMUM_FEE, + Mob::MINIMUM_FEE, + ); + assert_eq!(validate_transaction_fee(&tx, Mob::MINIMUM_FEE), Ok(())); + } + + { + // Overpaying fees is okay + let fee = Mob::MINIMUM_FEE + 1; + let (tx, _ledger) = + create_test_tx_with_amount(block_version, INITIALIZE_LEDGER_AMOUNT - fee, fee); + assert_eq!(validate_transaction_fee(&tx, Mob::MINIMUM_FEE), Ok(())); + } + } + } + + #[test] + /// Should return TombstoneBlockExceeded if the transaction has expired. + fn test_validate_tombstone_tombstone_block_exceeded() { + { + // The tombstone block is in the near future, so Ok. + let current_block_index = 888; + let tombstone_block_index = 889; + assert_eq!( + validate_tombstone(current_block_index, tombstone_block_index), + Ok(()) + ); + } + + { + // The tombstone block is the current block. + let current_block_index = 7; + let tombstone_block_index = 7; + assert_eq!( + validate_tombstone(current_block_index, tombstone_block_index), + Err(TransactionValidationError::TombstoneBlockExceeded) + ); + } + + { + // The tombstone block is in the past. + let current_block_index = 888; + let tombstone_block_index = 7; + assert_eq!( + validate_tombstone(current_block_index, tombstone_block_index), + Err(TransactionValidationError::TombstoneBlockExceeded) + ); + } + } + + #[test] + /// Should return TombstoneBlockTooFar if the tombstone is too far in the + /// future. + fn test_validate_tombstone_tombstone_block_too_far() { + { + // The tombstone block is in the near future, so Ok. + let current_block_index = 7; + let tombstone_block_index = current_block_index + 1; + assert_eq!( + validate_tombstone(current_block_index, tombstone_block_index), + Ok(()) + ); + } + + { + // Largest tombstone that is still Ok. + let current_block_index = 7; + let tombstone_block_index = current_block_index + MAX_TOMBSTONE_BLOCKS; + assert_eq!( + validate_tombstone(current_block_index, tombstone_block_index), + Ok(()) + ); + } + + { + // Tombstone is too far in the future. + let current_block_index = 7; + let tombstone_block_index = current_block_index + MAX_TOMBSTONE_BLOCKS + 1; + assert_eq!( + validate_tombstone(current_block_index, tombstone_block_index), + Err(TransactionValidationError::TombstoneBlockTooFar) + ); + } + } + + #[test] + fn test_global_validate_for_blocks_with_sorted_outputs() { + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let fee = Mob::MINIMUM_FEE + 1; + for block_version in BlockVersion::iterator() { + // for block version < 3 it doesn't matter + // for >= 3 it shall return an error about unsorted outputs + let (tx, _ledger) = create_test_tx_with_amount_and_comparer::<InverseTxOutputsOrdering>( + block_version, + INITIALIZE_LEDGER_AMOUNT - fee, + fee, + ); + + let highest_indices = tx.get_membership_proof_highest_indices(); + let root_proofs: Vec<TxOutMembershipProof> = adapt_hack( + &_ledger + .get_tx_out_proof_of_memberships(&highest_indices) + .expect("failed getting proofs"), + ); + + let result = validate( + &tx, + tx.prefix.tombstone_block - 1, + block_version, + &root_proofs, + 0, + &mut rng, + ); + + assert_eq!( + result, + match block_version.validate_transaction_outputs_are_sorted() { + true => Err(TransactionValidationError::UnsortedOutputs), + false => Ok(()), + } + ) + } + } + } ++>>>>>>> parent of 36ed5152... allow transactions with "mixed" token types (#1827) diff --cc transaction/std/src/transaction_builder.rs index 68cb805e,1ef00663..00000000 --- a/transaction/std/src/transaction_builder.rs +++ b/transaction/std/src/transaction_builder.rs @@@ -2697,548 -2590,4 +2593,551 @@@ pub mod transaction_builder_tests .is_err()); } } ++<<<<<<< HEAD + + #[test] + // Transaction builder with Burn Redemption memo builder + fn test_transaction_builder_burn_redemption_memos() { + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let block_version = BlockVersion::MAX; + let token_id = TokenId::from(5); + let fog_resolver = MockFogResolver::default(); + let sender = AccountKey::random(&mut rng); + let change_destination = ChangeDestination::from(&sender); + + // Adding an output that is not to the burn address is not allowed. + { + let memo_builder = BurnRedemptionMemoBuilder::new([2u8; 64]); + + let mut transaction_builder = TransactionBuilder::new( + block_version, + Amount::new(10, token_id), + fog_resolver.clone(), + memo_builder, + ) + .unwrap(); + + let recipient = AccountKey::random(&mut rng); + let result = transaction_builder.add_output( + Amount::new(100, token_id), + &recipient.default_subaddress(), + &mut rng, + ); + assert_matches!( + result, + Err(TxBuilderError::NewTx(NewTxError::Memo( + NewMemoError::InvalidRecipient + ))) + ); + } + + // Adding two burn outputs is not allowed. + { + let memo_builder = BurnRedemptionMemoBuilder::new([2u8; 64]); + + let mut transaction_builder = TransactionBuilder::new( + block_version, + Amount::new(10, token_id), + fog_resolver.clone(), + memo_builder, + ) + .unwrap(); + + transaction_builder + .add_output(Amount::new(100, token_id), &burn_address(), &mut rng) + .unwrap(); + + let result = transaction_builder.add_output( + Amount::new(100, token_id), + &burn_address(), + &mut rng, + ); + assert_matches!( + result, + Err(TxBuilderError::NewTx(NewTxError::Memo( + NewMemoError::MultipleOutputs + ))) + ); + } + + // Adding a change output before a burn output is not allowed. + { + let mut memo_builder = BurnRedemptionMemoBuilder::new([2u8; 64]); + memo_builder.enable_destination_memo(); + + let mut transaction_builder = TransactionBuilder::new( + block_version, + Amount::new(10, token_id), + fog_resolver.clone(), + memo_builder, + ) + .unwrap(); + + let result = transaction_builder.add_change_output( + Amount::new(10, token_id), + &change_destination, + &mut rng, + ); + + assert_matches!( + result, + Err(TxBuilderError::NewTx(NewTxError::Memo( + NewMemoError::MissingOutput + ))) + ); + } + + // Setting fee after change output has been written is not allowed. + { + let mut memo_builder = BurnRedemptionMemoBuilder::new([3u8; 64]); + memo_builder.enable_destination_memo(); + + let mut transaction_builder = TransactionBuilder::new( + block_version, + Amount::new(10, token_id), + fog_resolver.clone(), + memo_builder, + ) + .unwrap(); + + transaction_builder.set_fee(3).unwrap(); + + let input_credentials = get_input_credentials( + block_version, + Amount::new(113, token_id), + &AccountKey::random(&mut rng), + &fog_resolver, + &mut rng, + ); + transaction_builder.add_input(input_credentials); + + let (_burn_tx_out, _confirmation) = transaction_builder + .add_output(Amount::new(100, token_id), &burn_address(), &mut rng) + .unwrap(); + + transaction_builder + .add_change_output(Amount::new(10, token_id), &change_destination, &mut rng) + .unwrap(); + + let result = transaction_builder.set_fee(1235); + assert_matches!( + result, + Err(TxBuilderError::Memo(NewMemoError::FeeAfterChange)) + ); + } + + // Change in a different token is not allowed. + { + let mut memo_builder = BurnRedemptionMemoBuilder::new([3u8; 64]); + memo_builder.enable_destination_memo(); + + let mut transaction_builder = TransactionBuilder::new( + block_version, + Amount::new(10, Mob::ID), + fog_resolver.clone(), + memo_builder, + ) + .unwrap(); + + let (_burn_tx_out, _confirmation) = transaction_builder + .add_output(Amount::new(100, token_id), &burn_address(), &mut rng) + .unwrap(); + + let result = transaction_builder.add_change_output( + Amount::new(10, token_id), + &change_destination, + &mut rng, + ); + + assert_matches!( + result, + Err(TxBuilderError::NewTx(NewTxError::Memo( + NewMemoError::MixedTokenIds + ))) + ); + } + + // Happy flow without change + { + let mut memo_builder = BurnRedemptionMemoBuilder::new([2u8; 64]); + memo_builder.enable_destination_memo(); + + let mut transaction_builder = TransactionBuilder::new( + block_version, + Amount::new(10, token_id), + fog_resolver.clone(), + memo_builder, + ) + .unwrap(); + + transaction_builder.set_fee(3).unwrap(); + + let input_credentials = get_input_credentials( + block_version, + Amount::new(113, token_id), + &AccountKey::random(&mut rng), + &fog_resolver, + &mut rng, + ); + transaction_builder.add_input(input_credentials); + + let (burn_output, _confirmation) = transaction_builder + .add_output(Amount::new(110, token_id), &burn_address(), &mut rng) + .unwrap(); + + let tx = transaction_builder.build(&mut rng).expect("build tx"); + + assert_eq!(tx.prefix.outputs.len(), 1); + assert_eq!(burn_output, tx.prefix.outputs[0]); + + // Test that view key matching works with the burn tx out with burn address view + // key + let (amount, _) = burn_output + .view_key_match(&burn_address_view_private()) + .unwrap(); + assert_eq!(amount, Amount::new(110, token_id)); + + // Burn output should have a burn redemption memo + let ss = get_tx_out_shared_secret( + &burn_address_view_private(), + &RistrettoPublic::try_from(&burn_output.public_key).unwrap(), + ); + let memo = burn_output.e_memo.unwrap().decrypt(&ss); + match MemoType::try_from(&memo).expect("Couldn't decrypt memo") { + MemoType::BurnRedemption(memo) => { + assert_eq!(memo.memo_data(), &[2u8; 64],); + } + _ => { + panic!("unexpected memo type") + } + } + } + + // Happy flow with change + { + let mut memo_builder = BurnRedemptionMemoBuilder::new([3u8; 64]); + memo_builder.enable_destination_memo(); + + let mut transaction_builder = TransactionBuilder::new( + block_version, + Amount::new(10, token_id), + fog_resolver.clone(), + memo_builder, + ) + .unwrap(); + + transaction_builder.set_fee(3).unwrap(); + + let input_credentials = get_input_credentials( + block_version, + Amount::new(113, token_id), + &AccountKey::random(&mut rng), + &fog_resolver, + &mut rng, + ); + transaction_builder.add_input(input_credentials); + + let (burn_tx_out, _confirmation) = transaction_builder + .add_output(Amount::new(100, token_id), &burn_address(), &mut rng) + .unwrap(); + + transaction_builder + .add_change_output(Amount::new(10, token_id), &change_destination, &mut rng) + .unwrap(); + + let tx = transaction_builder.build(&mut rng).expect("build tx"); + + assert_eq!(tx.prefix.outputs.len(), 2); + + let burn_output = tx + .prefix + .outputs + .iter() + .find(|tx_out| tx_out.public_key == burn_tx_out.public_key) + .expect("Didn't find recipient's output"); + let change_output = tx + .prefix + .outputs + .iter() + .find(|tx_out| { + subaddress_matches_tx_out(&sender, CHANGE_SUBADDRESS_INDEX, tx_out).unwrap() + }) + .expect("Didn't find sender's output"); + + // Test that view key matching works with the burn tx out with burn address view + // key + let (amount, _) = burn_output + .view_key_match(&burn_address_view_private()) + .unwrap(); + assert_eq!(amount, Amount::new(100, token_id)); + + assert!(change_output + .view_key_match(&burn_address_view_private()) + .is_err()); + + // Test that view key matching works with the change tx out with sender's view + // key + let (amount, _) = change_output + .view_key_match(sender.view_private_key()) + .unwrap(); + assert_eq!(amount, Amount::new(10, token_id)); + + assert!(burn_output + .view_key_match(sender.view_private_key()) + .is_err()); + + // Burn output should have a burn redemption memo + let ss = get_tx_out_shared_secret( + &burn_address_view_private(), + &RistrettoPublic::try_from(&burn_output.public_key).unwrap(), + ); + let memo = burn_output.e_memo.unwrap().decrypt(&ss); + match MemoType::try_from(&memo).expect("Couldn't decrypt memo") { + MemoType::BurnRedemption(memo) => { + assert_eq!(memo.memo_data(), &[3u8; 64],); + } + _ => { + panic!("unexpected memo type") + } + } + + // Change output should have a destination memo + let ss = get_tx_out_shared_secret( + sender.view_private_key(), + &RistrettoPublic::try_from(&change_output.public_key).unwrap(), + ); + let memo = change_output.e_memo.unwrap().decrypt(&ss); + match MemoType::try_from(&memo).expect("Couldn't decrypt memo") { + MemoType::Destination(memo) => { + assert_eq!( + memo.get_address_hash(), + &ShortAddressHash::from(&burn_address()), + "lookup based on address hash failed" + ); + assert_eq!(memo.get_num_recipients(), 1); + assert_eq!(memo.get_fee(), 3); + assert_eq!( + memo.get_total_outlay(), + 103, + "outlay should be amount sent to recipient + fee" + ); + } + _ => { + panic!("unexpected memo type") + } + } + } + } + + #[test] + // Test that sending mixed transactions works + // + // This test uses inputs of two different token IDs, paying the fee and creating + // change with TokenID1, and "passing through" the second token ID with its + // full amount as output. + fn test_mixed_transactions() { + let mut rng: StdRng = SeedableRng::from_seed([18u8; 32]); + + let fog_resolver = MockFogResolver::default(); + let sender = AccountKey::random(&mut rng); + let sender_change_dest = ChangeDestination::from(&sender); + let recipient = AccountKey::random(&mut rng); + let recipient_addr = recipient.default_subaddress(); + + let amount1 = Amount::new(1475 * MILLIMOB_TO_PICOMOB, Mob::ID); + let change_amount = Amount::new(128 * MILLIMOB_TO_PICOMOB, Mob::ID); + let amount2 = Amount::new(999999, 2.into()); + + let tx_out1_right_amount = Amount::new( + amount1.value - change_amount.value - Mob::MINIMUM_FEE, + Mob::ID, + ); + + for block_version in 3..=*BlockVersion::MAX { + let block_version = BlockVersion::try_from(block_version).unwrap(); + let memo_builder = EmptyMemoBuilder::default(); + + let mut transaction_builder = TransactionBuilder::new( + block_version, + Amount::new(Mob::MINIMUM_FEE, Mob::ID), + fog_resolver.clone(), + memo_builder, + ) + .unwrap(); + + let input_credentials = + get_input_credentials(block_version, amount1, &sender, &fog_resolver, &mut rng); + transaction_builder.add_input(input_credentials); + + let input_credentials = + get_input_credentials(block_version, amount2, &sender, &fog_resolver, &mut rng); + transaction_builder.add_input(input_credentials); + + let (tx_out1, _confirmation) = transaction_builder + .add_output(tx_out1_right_amount, &recipient_addr, &mut rng) + .unwrap(); + + let (tx_out2, _confirmation) = transaction_builder + .add_output(amount2, &recipient_addr, &mut rng) + .unwrap(); + + transaction_builder + .add_change_output(change_amount, &sender_change_dest, &mut rng) + .unwrap(); + + let tx = transaction_builder.build(&mut rng).unwrap(); + + assert_eq!(tx.prefix.outputs.len(), 3); + let idx1 = tx + .prefix + .outputs + .iter() + .position(|tx_out| tx_out.public_key == tx_out1.public_key) + .unwrap(); + let idx2 = tx + .prefix + .outputs + .iter() + .position(|tx_out| tx_out.public_key == tx_out2.public_key) + .unwrap(); + let change_idx = (0..3).find(|x| *x != idx1 && *x != idx2).unwrap(); + + let change_tx_out = &tx.prefix.outputs[change_idx]; + + // Test that sender's change subaddress owns the change, and not the other tx + // outs + assert!( + !subaddress_matches_tx_out(&sender, CHANGE_SUBADDRESS_INDEX, &tx_out1).unwrap() + ); + assert!( + !subaddress_matches_tx_out(&sender, CHANGE_SUBADDRESS_INDEX, &tx_out2).unwrap() + ); + assert!( + subaddress_matches_tx_out(&sender, CHANGE_SUBADDRESS_INDEX, change_tx_out).unwrap() + ); + + // Test that recipients's default subaddress owns the correct output, and not + // the other tx outs + assert!( + subaddress_matches_tx_out(&recipient, DEFAULT_SUBADDRESS_INDEX, &tx_out1).unwrap() + ); + assert!( + subaddress_matches_tx_out(&recipient, DEFAULT_SUBADDRESS_INDEX, &tx_out2).unwrap() + ); + assert!(!subaddress_matches_tx_out( + &recipient, + DEFAULT_SUBADDRESS_INDEX, + change_tx_out + ) + .unwrap()); + + // Test that view key matching works with the two tx outs + let (amount, _) = tx_out1 + .view_key_match(recipient.view_private_key()) + .unwrap(); + assert_eq!( + amount.value, + amount1.value - change_amount.value - Mob::MINIMUM_FEE + ); + assert_eq!(amount.token_id, Mob::ID); + + let (amount, _) = tx_out2 + .view_key_match(recipient.view_private_key()) + .unwrap(); + assert_eq!(amount, amount2); + + assert!(change_tx_out + .view_key_match(recipient.view_private_key()) + .is_err()); + + // Test that view key matching works with the change tx out with sender's view + // key + let (amount, _) = change_tx_out + .view_key_match(sender.view_private_key()) + .unwrap(); + assert_eq!(amount.value, change_amount.value); + + assert!(tx_out1.view_key_match(sender.view_private_key()).is_err()); + + assert!(tx_out2.view_key_match(sender.view_private_key()).is_err()); + } + } + + #[test] + // Test mixed transactions expected failures (imbalanced transactions) + fn test_mixed_transactions_expected_failure_imbalanced_transactions() { + let mut rng: StdRng = SeedableRng::from_seed([18u8; 32]); + + let fog_resolver = MockFogResolver::default(); + let sender = AccountKey::random(&mut rng); + let sender_change_dest = ChangeDestination::from(&sender); + let recipient = AccountKey::random(&mut rng); + let recipient_addr = recipient.default_subaddress(); + + let amount1 = Amount::new(1475 * MILLIMOB_TO_PICOMOB, Mob::ID); + let change_amount = Amount::new(128 * MILLIMOB_TO_PICOMOB, Mob::ID); + let amount2 = Amount::new(999999, 2.into()); + + let tx_out1_right_amount = Amount::new( + amount1.value - change_amount.value - Mob::MINIMUM_FEE, + Mob::ID, + ); + + // Builds a transaction using a particular amount in place of tx_out1, returning + // result of `.build()` + let mut test_fn = |block_version, tx_out1_amount| -> Result<_, _> { + let memo_builder = EmptyMemoBuilder::default(); + + let mut transaction_builder = TransactionBuilder::new( + block_version, + Amount::new(Mob::MINIMUM_FEE, Mob::ID), + fog_resolver.clone(), + memo_builder, + ) + .unwrap(); + + let input_credentials = + get_input_credentials(block_version, amount1, &sender, &fog_resolver, &mut rng); + transaction_builder.add_input(input_credentials); + + let input_credentials = + get_input_credentials(block_version, amount2, &sender, &fog_resolver, &mut rng); + transaction_builder.add_input(input_credentials); + + let (_tx_out1, _confirmation) = transaction_builder + .add_output(tx_out1_amount, &recipient_addr, &mut rng) + .unwrap(); + + let (_tx_out2, _confirmation) = transaction_builder + .add_output(amount2, &recipient_addr, &mut rng) + .unwrap(); + + transaction_builder + .add_change_output(change_amount, &sender_change_dest, &mut rng) + .unwrap(); + + transaction_builder.build(&mut rng) + }; + + for block_version in 3..=*BlockVersion::MAX { + let block_version = BlockVersion::try_from(block_version).unwrap(); + + assert!(test_fn(block_version, tx_out1_right_amount).is_ok()); + + let mut tx_out1_wrong_amount = tx_out1_right_amount; + tx_out1_wrong_amount.value -= 1; + assert!(test_fn(block_version, tx_out1_wrong_amount).is_err()); + + tx_out1_wrong_amount.value += 2; + assert!(test_fn(block_version, tx_out1_wrong_amount).is_err()); + + tx_out1_wrong_amount.token_id = 99.into(); + assert!(test_fn(block_version, tx_out1_wrong_amount).is_err()); + + tx_out1_wrong_amount.value -= 1; + assert!(test_fn(block_version, tx_out1_wrong_amount).is_err()); + } + } ++======= ++>>>>>>> parent of 36ed5152... allow transactions with "mixed" token types (#1827) } ```
* add feature extended message digest (#1841) * Bump anyhow from 1.0.56 to 1.0.57 (#1837) Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.56 to 1.0.57. - [Release notes](https://github.com/dtolnay/anyhow/releases) - [Commits](https://github.com/dtolnay/anyhow/compare/1.0.56...1.0.57) --- updated-dependencies: - dependency-name: anyhow dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump cbindgen from 0.22.0 to 0.23.0 (#1836) Bumps [cbindgen](https://github.com/eqrion/cbindgen) from 0.22.0 to 0.23.0. - [Release notes](https://github.com/eqrion/cbindgen/releases) - [Changelog](https://github.com/eqrion/cbindgen/blob/master/CHANGES) - [Commits](https://github.com/eqrion/cbindgen/compare/v0.22.0...v0.23.0) --- updated-dependencies: - dependency-name: cbindgen dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix header file definition (#1796) Soundtrack of this PR: [Orlann Divo - Onde Anda O Meu Amor](https://youtu.be/fe-PXWEbCGg) ### Motivation Function parameter types do not match definition. ### In this PR * update to be correct. * add get_minting_trust_root to consensus enclave (#1845) * remove redundant field (#1843) * Slip10redo (#1689) * Adding chris's slip10 changes * mnemonics replacing slip10 * fix clippy * Removing slip10 where inappropriate * Addressing review comments * Update mobilecoind/api/proto/mobilecoind_api.proto Co-authored-by: Remoun Metyas <[email protected]> * Update util/keyfile/src/bin/main.rs Co-authored-by: Remoun Metyas <[email protected]> * Updating conformance and local network tests to pass report id * linting * Changing fog report id to empty string in conformace and local network tests * Adding back readme * Escaping the empty string in fog conformance test * Adding missing account index * lint * Update fog/sample-paykit/proto/remote_wallet.proto Co-authored-by: James Cape <[email protected]> * Update util/keyfile/src/mnemonic_acct.rs Co-authored-by: James Cape <[email protected]> * Update util/keyfile/src/error.rs Co-authored-by: James Cape <[email protected]> * Fixing prost * Update util/keyfile/src/bin/keygen_main.rs Co-authored-by: Remoun Metyas <[email protected]> * Update util/keyfile/src/bin/main.rs Co-authored-by: Remoun Metyas <[email protected]> * Update util/keyfile/src/bin/keygen_main.rs Co-authored-by: Remoun Metyas <[email protected]> * Update util/keyfile/src/bin/main.rs Co-authored-by: Remoun Metyas <[email protected]> * removing structopt from merge * clean up * fixing previous * addressing review comments * importing read and write directly instead of using prelude * Resolving lint Co-authored-by: Remoun Metyas <[email protected]> Co-authored-by: James Cape <[email protected]> * Android SDK Recoverable Transaction History (#1606) * Add android bindings for recoverable transaction history * Create android bindings for RTH * Format files * Fixing Memo Size in Test * Add Block Version to TransactionBuilder * Fix One-time Private Key Generation to Use Correct Subaddress * Review Action Items Removing some debug logs and unused imports * Updating Android Bindings Dependencies * Removing old comment * Fixing bad merge * Updating Cargo.lock * Removing comments * Removing unused binding * Updating Cargo.lock * Updating Cargo.lock * Removing unnecessary log * Removing redundant test * Cleaning up unneeded dependencies * Cargo lock updates * Updating android-bindings dependencies * Lint * Lint * Lint Co-authored-by: Sam <[email protected]> * Split LedgerDB impl (#1844) * Bump clap from 3.1.10 to 3.1.12 (#1847) Bumps [clap](https://github.com/clap-rs/clap) from 3.1.10 to 3.1.12. - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/v3.1.10...v3.1.12) --- updated-dependencies: - dependency-name: clap dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * GHA CI/CD workflow_dispatch placeholders. (#1846) * add workflow-dispatch placeholdes so we can run on other non-default branches * add delete dispatch * Add endpoint for exposing current consensus node configuration (#1858) * initial work on consensus config api endpoint * add governors signature to config * expose more configuration params * minor edits * try and fix go build * Update consensus/api/proto/consensus_config.proto Co-authored-by: Remoun Metyas <[email protected]> * Update consensus/api/src/conversions.rs Co-authored-by: Remoun Metyas <[email protected]> Co-authored-by: Remoun Metyas <[email protected]> * allow transactions with "mixed" token types (#1827) * allow transactions with "mixed" token types these are introduced at block version 3 the token type of every pseudo output and real output must be listed in `pseudo_output_token_ids` and `output_token_ids` in the `SignatureRctBulletproofs` object * Update mobilecoind-json/src/data_types.rs Co-authored-by: Remoun Metyas <[email protected]> * Update mobilecoind-json/src/data_types.rs Co-authored-by: Remoun Metyas <[email protected]> * Update mobilecoind-json/src/data_types.rs Co-authored-by: Remoun Metyas <[email protected]> * cargo fmt * fix mobilecoind-json conversions * review comments * fixups * add more test coverage on mixed transactions * add transaction builder support for mixed transactions, and tests * fix the world * don't, at this time, make mobilecoind start writing 0 value change this should happen at the time of adding RTH support to mobilecoind * eran comments * Update transaction/core/src/ring_signature/error.rs Co-authored-by: Remoun Metyas <[email protected]> * more review comments * Make a wrapper for u64 which serializes to json as string this addresses review comments * missing copyright * more review comments * more uses of `Amount::new` * cleanup in transaction builder around Amount * cleanup tx prefix around Amount * cleanup input secret and output secret around Amount * Update transaction/core/src/ring_signature/rct_bulletproofs.rs Co-authored-by: Remoun Metyas <[email protected]> * Update transaction/std/src/memo_builder/rth_memo_builder.rs Co-authored-by: Remoun Metyas <[email protected]> * Update transaction/std/src/transaction_builder.rs Co-authored-by: Remoun Metyas <[email protected]> * Update transaction/std/src/transaction_builder.rs Co-authored-by: Remoun Metyas <[email protected]> * Update transaction/std/src/transaction_builder.rs Co-authored-by: Remoun Metyas <[email protected]> * Update transaction/std/src/transaction_builder.rs Co-authored-by: Remoun Metyas <[email protected]> * Update transaction/std/src/transaction_builder.rs Co-authored-by: Remoun Metyas <[email protected]> * Update transaction/std/src/transaction_builder.rs Co-authored-by: Remoun Metyas <[email protected]> * Update consensus/service/src/validators.rs Co-authored-by: Remoun Metyas <[email protected]> * fix build * JsonTokenId -> JsonU64, and code shortening in lots of places * more JsonU64 * Update transaction/core/src/ring_signature/generator_cache.rs Co-authored-by: Remoun Metyas <[email protected]> * replace assert with error * Make `TransactionBuilder::new(` take the fee `Amount` this cleans up the constructor * fix build * fix clippy * replace assert with error in another place * fix a variable name, per review comments * add a code comment about balance proofing after this mixed transactions stuff * fixup code comment * Update transaction/core/src/blockchain/block_version.rs Co-authored-by: sugargoat <[email protected]> * Update transaction/core/src/ring_signature/rct_bulletproofs.rs Co-authored-by: sugargoat <[email protected]> * Update transaction/std/src/transaction_builder.rs Co-authored-by: sugargoat <[email protected]> * Update transaction/std/src/transaction_builder.rs Co-authored-by: sugargoat <[email protected]> * Update transaction/std/src/transaction_builder.rs Co-authored-by: sugargoat <[email protected]> * Update transaction/core/src/tx_error.rs Co-authored-by: sugargoat <[email protected]> * Update transaction/core/src/ring_signature/rct_bulletproofs.rs Co-authored-by: sugargoat <[email protected]> * improve code comment * add code comments in external.proto * add github issue references in sources * cargo format of code comments Co-authored-by: Remoun Metyas <[email protected]> Co-authored-by: sugargoat <[email protected]> * Create test blocks with custom content (#1864) Add ability to create simulated blocks with custom content for benchmark tests * Make sure all output token ids are actually configured (#1876) Fixes #1868 * move validation tests to be an integration test, remove adapt_hack (#1875) * move validation tests to be an integration test, remove adapt_hack This works because, when cargo compiles in-line unit tests, they are compiled into the module of the code under test, but when cargo compiles integration tests (in tests directory), that is considered a separate module (effectively a separate crate, just without its own cargo toml). This fixes the adapt hack issue because now we no longer need to compile the `mc-transaction-core` crate in test mode, which also references the `mc-transaction-core` crate which `mc-ledger-db` depends on. The types in the test code always refer to the types in the same version of `mc-transaction-core` that `mc-ledger-db` is using. * address eran comments * Initial take on implementing Burn Redemption Memo (#1862) * burn redemption memo type and builder * another test * misc fixes * update comment * Update transaction/std/src/memo_builder/burn_redemption_memo_builder.rs Co-authored-by: Mike Turner <[email protected]> * fmt * use const * Update transaction/std/src/memo_builder/burn_redemption_memo_builder.rs Co-authored-by: Nick Santana <[email protected]> * pr review fixes * merge fixes * lint * remove address from InvalidRecipient * review fixes Co-authored-by: Mike Turner <[email protected]> Co-authored-by: Nick Santana <[email protected]> * Add Clippy check to lint script (#1863) * Add --all-targets to clippy command in tools/lint.sh * Fix clippy findings. * Update Change Subaddress Index (#1880) Set Change Subaddress Index to u64::MAX - 1 * API for generating burn txs in mobilecoind (#1872) * add and impl generate_burn_redemption_tx * add tests * support destination memos * fmt * Update mobilecoind/src/service.rs Co-authored-by: Remoun Metyas <[email protected]> * Update mobilecoind/src/service.rs Co-authored-by: Remoun Metyas <[email protected]> * Update mobilecoind/api/proto/mobilecoind_api.proto Co-authored-by: Nick Santana <[email protected]> * change test to exercise off by one scenario Co-authored-by: Remoun Metyas <[email protected]> Co-authored-by: Nick Santana <[email protected]> * fix typo (#1883) * Update rust-toolchain to nightly-2022-04-29 (#1888) * Update rust-toolchain version * Fix clippy warnings * Write an integration test for mobilecoind-json (#1885) * Write an integration test for mobilecoind-json * sort * enable test * spaces * move wait_for_monitor_to_sync down * DRY * Update mobilecoind/strategies/test_client.py Co-authored-by: Remoun Metyas <[email protected]> * Update mobilecoind/strategies/drain-accounts.py Co-authored-by: Remoun Metyas <[email protected]> Co-authored-by: Remoun Metyas <[email protected]> * Fix/slip10errors (#1893) * Adding a default value for fog_report_id and adding info level logging * lint * Fix bug calling wrong method in accounts.py * Changing check in sample_keys_main to just be a clap default * Changing option to string reference * Bump libc from 0.2.124 to 0.2.125 (#1887) Bumps [libc](https://github.com/rust-lang/libc) from 0.2.124 to 0.2.125. - [Release notes](https://github.com/rust-lang/libc/releases) - [Commits](https://github.com/rust-lang/libc/compare/0.2.124...0.2.125) --- updated-dependencies: - dependency-name: libc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump syn from 1.0.91 to 1.0.92 (#1886) Bumps [syn](https://github.com/dtolnay/syn) from 1.0.91 to 1.0.92. - [Release notes](https://github.com/dtolnay/syn/releases) - [Commits](https://github.com/dtolnay/syn/compare/1.0.91...1.0.92) --- updated-dependencies: - dependency-name: syn dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump serde from 1.0.136 to 1.0.137 in /fog/ledger/enclave/trusted (#1894) Bumps [serde](https://github.com/serde-rs/serde) from 1.0.136 to 1.0.137. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.136...v1.0.137) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump serde from 1.0.136 to 1.0.137 in /fog/view/enclave/trusted (#1895) Bumps [serde](https://github.com/serde-rs/serde) from 1.0.136 to 1.0.137. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.136...v1.0.137) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump serde from 1.0.136 to 1.0.137 in /consensus/enclave/trusted (#1897) Bumps [serde](https://github.com/serde-rs/serde) from 1.0.136 to 1.0.137. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.136...v1.0.137) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump serde from 1.0.136 to 1.0.137 in /fog/ingest/enclave/trusted (#1901) Bumps [serde](https://github.com/serde-rs/serde) from 1.0.136 to 1.0.137. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.136...v1.0.137) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump serde from 1.0.136 to 1.0.137 (#1903) Bumps [serde](https://github.com/serde-rs/serde) from 1.0.136 to 1.0.137. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.136...v1.0.137) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump semver from 1.0.7 to 1.0.9 (#1900) Bumps [semver](https://github.com/dtolnay/semver) from 1.0.7 to 1.0.9. - [Release notes](https://github.com/dtolnay/semver/releases) - [Commits](https://github.com/dtolnay/semver/compare/1.0.7...1.0.9) --- updated-dependencies: - dependency-name: semver dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump zeroize from 1.5.4 to 1.5.5 in /fog/view/enclave/trusted (#1896) Bumps [zeroize](https://github.com/RustCrypto/utils) from 1.5.4 to 1.5.5. - [Release notes](https://github.com/RustCrypto/utils/releases) - [Commits](https://github.com/RustCrypto/utils/compare/zeroize-v1.5.4...zeroize-v1.5.5) --- updated-dependencies: - dependency-name: zeroize dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump zeroize from 1.5.4 to 1.5.5 in /consensus/enclave/trusted (#1898) Bumps [zeroize](https://github.com/RustCrypto/utils) from 1.5.4 to 1.5.5. - [Release notes](https://github.com/RustCrypto/utils/releases) - [Commits](https://github.com/RustCrypto/utils/compare/zeroize-v1.5.4...zeroize-v1.5.5) --- updated-dependencies: - dependency-name: zeroize dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump zeroize from 1.5.4 to 1.5.5 in /fog/ingest/enclave/trusted (#1899) Bumps [zeroize](https://github.com/RustCrypto/utils) from 1.5.4 to 1.5.5. - [Release notes](https://github.com/RustCrypto/utils/releases) - [Commits](https://github.com/RustCrypto/utils/compare/zeroize-v1.5.4...zeroize-v1.5.5) --- updated-dependencies: - dependency-name: zeroize dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Dependency updates (Part 1) (#1908) * Update regex in consensus enclave to 1.5.5 * Update crossbeam utils 0.8.5 to 0.8.8 * Update generic-array 0.12.3 to 0.12.4 * Update hyper from 0.12.35 to 0.12.36 * Update hyper 0.14.16 to 0.14.18 * Ran cargo update against protoc, somehow log is back to the right version. * Update log to 0.4.17. * Fix protoc again... * Switch to using our fork of opentelemetry. (#1918) This was needed since we had to upgrade opentelemetry-jaeger's dependency on thrift, which in turn depended on a broken verison of ordered-float. * Upgrade to rocket 0.5.0-rc (#1913) * mobilecoind-json uses new(er) rocket * admin gw uses newer rocket * fog-overseer-server uses newer rocket * Bump clap from 3.1.12 to 3.1.15 (#1904) Bumps [clap](https://github.com/clap-rs/clap) from 3.1.12 to 3.1.15. - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/v3.1.12...v3.1.15) --- updated-dependencies: - dependency-name: clap dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * no need for 10 subaddresses, and this also makes the test compatible with the drain_accounts.py script (#1919) * Bump libz-sys from 1.1.5 to 1.1.6 (#1873) Bumps [libz-sys](https://github.com/rust-lang/libz-sys) from 1.1.5 to 1.1.6. - [Release notes](https://github.com/rust-lang/libz-sys/releases) - [Commits](https://github.com/rust-lang/libz-sys/compare/1.1.5...1.1.6) --- updated-dependencies: - dependency-name: libz-sys dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * added extra configuration consensus service serve side. (#1800) * added extra configuration consensus service serve side. * fixed build errors * increased the allowed testime for testing * added one more method to set defaults * fixed compile error * linting issue * Update consensus/service/src/consensus_service.rs Co-authored-by: Remoun Metyas <[email protected]> Co-authored-by: Eugene Rata <[email protected]> Co-authored-by: Remoun Metyas <[email protected]> * Bump zeroize from 1.5.4 to 1.5.5 (#1902) * Bump serde_json from 1.0.79 to 1.0.81 (#1916) Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.79 to 1.0.81. - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.79...v1.0.81) --- updated-dependencies: - dependency-name: serde_json dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump crc from 2.1.0 to 3.0.0 (#1857) * Bump crc from 2.1.0 to 3.0.0 Bumps [crc](https://github.com/mrhooray/crc-rs) from 2.1.0 to 3.0.0. - [Release notes](https://github.com/mrhooray/crc-rs/releases) - [Commits](https://github.com/mrhooray/crc-rs/compare/2.1.0...3.0.0) --- updated-dependencies: - dependency-name: crc dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <[email protected]> * Update lock files Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Nick Santana <[email protected]> * Upgrade rusoto_s3 from 0.42 to 0.48. (#1912) Simplify type of retry result * Bump prost from 0.10.1 to 0.10.3 (#1930) * Bump prost from 0.10.1 to 0.10.3 Bumps [prost](https://github.com/tokio-rs/prost) from 0.10.1 to 0.10.3. - [Release notes](https://github.com/tokio-rs/prost/releases) - [Commits](https://github.com/tokio-rs/prost/compare/v0.10.1...v0.10.3) --- updated-dependencies: - dependency-name: prost dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> * Update util/keyfile/Cargo.toml Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Remoun Metyas <[email protected]> * Clean up mc-util-test-helper (#1925) * Use rand::distributions::Alphanumeric in random_string() * Use lazy_static to call get_seeds only once * Add cargo-sort to the pre-commit hook (#1921) * Update precommit hook to install/use cargo sort. * Redirect sort output to dev null. * Do version checks first, then do tests, pipe some junk to /dev/null. * Don't print "OK" for rustfmt check, version is sufficient. * Pipe stderr output when testing for cargo sort/fmt to dev null. * Update hooks/pre-commit Co-authored-by: Mike Turner <[email protected]> * GH-CLI Friendly PR Template (#1922) * Remove the "In this PR section", Soundtrack to end * Comment out the prompts * Update .github/pull_request_template.md Co-authored-by: Remoun Metyas <[email protected]> * Prevent docker from trashing the host target directory (#1935) * Make a transaction-std-test-utils module, to help code reuse (#1870) * Make a transaction-std-test-utils module, to help code reuse This will be useful for testing the signed contingent inputs * fix build and comments * fix another comment * fix a clippy * cargo fmt * Support Arbitrary Reserved Addresses (#1936) * Refactor to ChangeAddress to `ReservedAddress` accomodate all platform reserved addresses Originally the ChangeAddress object only held public address data for the change address. As more reserved subaddresses are becoming official on the mobilecoin platform, it's wise to make the object general to hold all reserved subaddresses. * allow providing minting trust root public key via a PEM file instead hex bytes (#1951) * allow providing minting trust root public key via a PEM file instead of hex bytes * update lock file * make fog-resolver and ingest-report serializable (#1944) * Feature/attest verifier mc seed bugfix (#1943) * Bump proc-macro2 from 1.0.37 to 1.0.38 (#1938) Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.37 to 1.0.38. - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.37...1.0.38) --- updated-dependencies: - dependency-name: proc-macro2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * reverting ledger-dist folder to work dir and work dir now in target dir (#1954) * Add proto schema for GiftCode and rust equivalent object (#1953) Add protobuf message enabling clients to TxOutGift codes between users and Rust equivalent object * Bump clap from 3.1.15 to 3.1.18 (#1957) Bumps [clap](https://github.com/clap-rs/clap) from 3.1.15 to 3.1.18. - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/v3.1.15...v3.1.18) --- updated-dependencies: - dependency-name: clap dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump syn from 1.0.92 to 1.0.93 (#1956) Bumps [syn](https://github.com/dtolnay/syn) from 1.0.92 to 1.0.93. - [Release notes](https://github.com/dtolnay/syn/releases) - [Commits](https://github.com/dtolnay/syn/compare/1.0.92...1.0.93) --- updated-dependencies: - dependency-name: syn dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump prost from 0.10.1 to 0.10.3 in /consensus/enclave/trusted (#1927) Bumps [prost](https://github.com/tokio-rs/prost) from 0.10.1 to 0.10.3. - [Release notes](https://github.com/tokio-rs/prost/releases) - [Commits](https://github.com/tokio-rs/prost/compare/v0.10.1...v0.10.3) --- updated-dependencies: - dependency-name: prost dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump prost from 0.10.1 to 0.10.3 in /fog/ledger/enclave/trusted (#1928) Bumps [prost](https://github.com/tokio-rs/prost) from 0.10.1 to 0.10.3. - [Release notes](https://github.com/tokio-rs/prost/releases) - [Commits](https://github.com/tokio-rs/prost/compare/v0.10.1...v0.10.3) --- updated-dependencies: - dependency-name: prost dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump prost from 0.10.1 to 0.10.3 in /fog/view/enclave/trusted (#1929) Bumps [prost](https://github.com/tokio-rs/prost) from 0.10.1 to 0.10.3. - [Release notes](https://github.com/tokio-rs/prost/releases) - [Commits](https://github.com/tokio-rs/prost/compare/v0.10.1...v0.10.3) --- updated-dependencies: - dependency-name: prost dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Feature/signed contingent inputs (#1871) * add signed input rules to TxIn and use in ring signatures * signed contingent input builder, and make tx builder able to use these inputs * fix dependency ordering add a test for removing input rules entirely from the signed input clippy clippy fixup Update transaction/std/src/transaction_builder.rs Co-authored-by: Remoun Metyas <[email protected]> Update transaction/std/src/transaction_builder.rs Co-authored-by: Remoun Metyas <[email protected]> Update transaction/core/src/signed_contingent_input.rs Co-authored-by: Remoun Metyas <[email protected]> Update api/src/convert/tx_in.rs Co-authored-by: Remoun Metyas <[email protected]> Update mobilecoind-json/src/data_types.rs Co-authored-by: Remoun Metyas <[email protected]> Update transaction/core/src/ring_signature/rct_bulletproofs.rs Co-authored-by: Eran Rundstein <[email protected]> Update transaction/std/src/signed_contingent_input_builder.rs Co-authored-by: wjuan-mob <[email protected]> Update transaction/std/src/signed_contingent_input_builder.rs Co-authored-by: wjuan-mob <[email protected]> Update transaction/std/src/signed_contingent_input_builder.rs Co-authored-by: Eran Rundstein <[email protected]> fix tests after rebase address remoun comment address remoun and sam comments fix bad rebase * remoun comment * add unit tests for input rules * enforce global index count * cargo fmt * fix nits, fix build * remove optional input rules from SignableRing for rct_bulletproofs signer There is no existing use-case for this, and so removing it reduces code complexity somewhat. Thanks to Eran for pointing this out. * eran nits, fix build * signed contingent input signs the entire TxIn not just the rules * enforce in the verifier that not all rigns can be presigned * add block version to the signed contingent input proto * clippy * improve code comments and variable names in signed contingent input builder * fix a william nit * fix fee setting with RTH on the signed contingent input builder * Update transaction/core/src/validation/validate.rs Co-authored-by: Eran Rundstein <[email protected]> * fix input credentials zeroize Co-authored-by: Eran Rundstein <[email protected]> * Bump prost from 0.10.1 to 0.10.3 in /fog/ingest/enclave/trusted (#1926) Bumps [prost](https://github.com/tokio-rs/prost) from 0.10.1 to 0.10.3. - [Release notes](https://github.com/tokio-rs/prost/releases) - [Commits](https://github.com/tokio-rs/prost/compare/v0.10.1...v0.10.3) --- updated-dependencies: - dependency-name: prost dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * MintTx/MintConfigTx tombstone validation bugfixes (#1964) * fix a bug that would cause MintTxs that depend on old MintConfigTxs to fail validation * fix off by one error in mint tx/mint config tx tombstone validation. also add a test for duplicate nonce * lint, fix tests * test boundary condiotion and update comment * Update consensus/service/src/mint_tx_manager/mod.rs Co-authored-by: Nick Santana <[email protected]> * Update consensus/service/src/mint_tx_manager/mod.rs Co-authored-by: Nick Santana <[email protected]> * make tombstone block check optional by passing an Option for the current block index, as suggested in the PR review Co-authored-by: Nick Santana <[email protected]> * mint-client: make the process exitcode reflect transaction status returned from consensus when a transaction is submitted (#1967) * mint-client: make the process exitcode reflect transaction status returned from consensus when a transaction is submitted * add missing import * Gift Code Memos (#1932) Add gift code memo objects for the 3 gift code types: * Gift Code Sender Memo 0x0002 * Gift Code Funding Memo 0x0201 * Gift Code Cancellation Memo 0x0202 * Update builder_install image (#1968) #1888 updated rust-toolchain without updating builder_install. * Remove bogus DATABASE_URL value * Add postgresql-client explicitly to init_debian.sh * Bump Dockerfile-version * Make fee map enforce divisibility by minimum fee. Also check this in enclave impl (#1965) * Make fee map enforce divisibility by minimum fee. Also check this in enclave impl * fix previous after cargo fmt * fix imports * fix build * clippy * fix consensus service tests * fix more tests * fix blockchain api tests * fix consensus enclave api tests * fix consensus enclave impl tests * fix more tests * New attempt at zip_exact utility (#1969) * New attempt at zip_exact utility This is intended to ease maintenance of the rct_bulletproofs module, since accidentally removing or wrongly implementing a length check no longer silently leads to transaction elements being skipped. Fixes #1869 --- This version seems less annoying than the previous version based on `ZipLongest`, because it doesn't cause as much syntax explosion. * cargo lock * review comments * Update Rust crates to Edition 2021 (#1960) * Update mc-crypto-digestible-derive-test to 2021 * Upgrade mc-fog-test-enclave-api to 2021 * Upgrade mc-fog-ingest-enclave-edl to 2021 * Upgrade mc-fog-ingest-enclave-impl to 2021 * Upgrade mc-fog-ingest-enclave-measurement to 2021 * Upgrade mc-fog-ingest-enclave-trusted to 2021 * Upgrade mc-fog-ingest-enclave to 2021 * Upgrade mc-fog-ingest-client to 2021 * Upgrade mc-fog-ingest-server to 2021 * Upgrade mc-fog-ledger-connection to 2021 * Upgrade mc-fog-ledger-enclave-api to 2021 * Upgrade mc-fog-ledger-enclave-edl to 2021 * Upgrade mc-fog-ledger-enclave-impl to 2021 * Upgrade mc-fog-ledger-enclave-measurement to 2021 * Upgrade mc-fog-ledger-enclave-trusted to 2021 * Upgrade mc-fog-ledger-enclave to 2021 * Upgrade mc-fog-ledger-server to 2021 * Upgrade mc-fog-ledger-test-infra to 2021 * Upgrade mc-fog-api to 2021 * Update mc-fog-distribution to 2021 * Update mc-foc-enclave-connection to 2021 * Update mc-fog-kex-rng to 2021 * Update mc-fog-load-testing to 2021 * Update mc-fog-ocall-oram-storage-edl to 2021 * Update mc-fog-ocall-oram-storage-testing 2021 * Update mc-fog-ocall-oram-storage-trusted to 2021 * Update mc-fog-ocall-oram-storage-untrusted to 2021 * Update mc-fog-overseer-server to 2021 * Update mc-fog-recovery-db-iface to 2021 * Update mc-fog-report-apit to 2021 * Update mc-fog-report-cli to 2021 * Update mc-fog-report-connection to 2021 * Update mc-fog-report-server to 2021 * Update mc-fog-report-types to 2021 * Update mc-fog-report-validation to 2021 * Update mc-fog-sample-paykit to 2021 * Update mc-fog-sig to 2021 * Update mc-fog-sql-recovery-db to 2021 * Update mc-fog-test-client to 2021 * Update mc-fog-test-infra to 2021 * Update mc-fog-types to 2021 * Update mc-fog-uri to 2021 * Update mc-fog-view-connection to 2021 * Update mc-fog-view-enclave to 2021 * Update mc-fog-view-load-test to 2021 * Update mc-fog-view-protocol to 2021 * Update mc-fog-view-server to 2021 * Update mc-fog-view-enclave to 2021 * Update missed mc-fog-report test-utils to 2021 * Update enclave rustfmt.toml to 2021 * Update mc-consensus to 2021 * Update mc-account-keys to 2021 * Update mc-admin-http-gateway to 2021 * Update mc-android-bindings to 2021 * Update mc-api to 2021 * Update mc-attest to 2021 * Update mc-common to 2021 * Update mc-connection to 2021 * Update mc-crypto to 2021 * Update mc-enclave-boundary to 2021 * Update libmobilecoin to 2021 * Update mc-mint-auditor to 2021 * Update mobilecoind to 2021 * Update mobilecoind-json to 2021 * Update mc-peers to 2021 * Update test vectors to 2021 * Update transaction to 2021 * Update sgx crates to 2021 * Update go-grpc testing crate to 2021 * Update libmobilecoin to 2021 (again) * Update ledger crates to 2021 * Update rustfmt rules to 2021 * Update mc-watcher to 2021 * Update util crates to 2021 * Cleaner solution for partial-capture-induced issues Co-authored-by: Andrew Wygle <[email protected]> * Create ResponderId out of host and port in PollingNetworkState (#1906) * Add responder_id query param in ConnectionUri test url * Create host and port ResponderId from ConnectionUri * Run format * Include error in panic message * Bump hashbrown from 0.11.2 to 0.12.1 (#1915) * Bump hashbrown from 0.11.2 to 0.12.1 Bumps [hashbrown](https://github.com/rust-lang/hashbrown) from 0.11.2 to 0.12.1. - [Release notes](https://github.com/rust-lang/hashbrown/releases) - [Changelog](https://github.com/rust-lang/hashbrown/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/hashbrown/compare/v0.11.2...v0.12.1) --- updated-dependencies: - dependency-name: hashbrown dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> * Updating lock files for enclave crates Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Nick Santana <[email protected]> * Fix failures when tests are run in parallel, and DRY up tests that start a fog-ingest server (#1931) * Fix concurrent test failures due to port conflicts. * consensus-service: Have gRPC server pick a port. * fog-ingest: update constants. * fog-view, mobilecoind: use portpicker helper. * impl Drop for OverseerService * Fix incorrect deny(missing_docs) * DRY up tests that start a fog-ingest-server. * Bump ed25519 from 1.4.1 to 1.5.0 (#1950) * Bump ed25519 from 1.4.1 to 1.5.0 Bumps [ed25519](https://github.com/RustCrypto/signatures) from 1.4.1 to 1.5.0. - [Release notes](https://github.com/RustCrypto/signatures/releases) - [Commits](https://github.com/RustCrypto/signatures/compare/ed25519/v1.4.1...ed25519/v1.5.0) --- updated-dependencies: - dependency-name: ed25519 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> * Update Cargo.lock for enclave crates Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Andrew Wygle <[email protected]> * Add a mint auditor integration test to be used in CD. (#1971) * Write a mint auditor integration test to be used in CD. accept command line args wip off by one debug * update comment * update fee amount * Update mint-auditor/tests/integration_test.py Co-authored-by: Remoun Metyas <[email protected]> * Update mint-auditor/tests/integration_test.py Co-authored-by: Remoun Metyas <[email protected]> Co-authored-by: Remoun Metyas <[email protected]> * Bump serde_with from 1.12.1 to 1.13.0 (#1882) Bumps [serde_with](https://github.com/jonasbb/serde_with) from 1.12.1 to 1.13.0. - [Release notes](https://github.com/jonasbb/serde_with/releases) - [Commits](https://github.com/jonasbb/serde_with/compare/v1.12.1...v1.13.0) --- updated-dependencies: - dependency-name: serde_with dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Mark fee_token_id as `omit_when = 0` (#1978) (#1980) The fee token id, which is a new field in 1.2, needs to be marked `omit_when = 0`, otherwise there is no way for clients to be compatible with 1.1 and 1.2. This needs to be done whenever we add a numeric field to a proto that is hashed into the blockchain, otherwise the schema evolution strategy for hashing doesn't work. (It really should have been the default for hashing integers, because it's so easy to miss this, but it's probably too late to change that now.) * Bump syn from 1.0.93 to 1.0.94 (#1976) Bumps [syn](https://github.com/dtolnay/syn) from 1.0.93 to 1.0.94. - [Release notes](https://github.com/dtolnay/syn/releases) - [Commits](https://github.com/dtolnay/syn/compare/1.0.93...1.0.94) --- updated-dependencies: - dependency-name: syn dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump mockall from 0.11.0 to 0.11.1 (#1986) Bumps [mockall](https://github.com/asomers/mockall) from 0.11.0 to 0.11.1. - [Release notes](https://github.com/asomers/mockall/releases) - [Changelog](https://github.com/asomers/mockall/blob/master/CHANGELOG.md) - [Commits](https://github.com/asomers/mockall/compare/v0.11.0...v0.11.1) --- updated-dependencies: - dependency-name: mockall dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Make the test client exercise the signed contingent input functionality (#1979) * Make the test client exercise the signed contingent input functionality * fix multiline displaydoc after format * fix review comments, make SCI builder clear the proofs, fix tests * clippy * Update fog/test-client/src/test_client.rs Co-authored-by: Eran Rundstein <[email protected]> * Update fog/test-client/src/test_client.rs Co-authored-by: Eran Rundstein <[email protected]> * Update fog/test-client/src/test_client.rs Co-authored-by: Eran Rundstein <[email protected]> * Update fog/sample-paykit/src/client.rs Co-authored-by: Remoun Metyas <[email protected]> * Update fog/sample-paykit/src/client.rs Co-authored-by: Remoun Metyas <[email protected]> * Update fog/sample-paykit/src/client.rs Co-authored-by: Remoun Metyas <[email protected]> * fix more review comments * fix signed contingent input builder tests (we made the builder delete merkle proofs) * test defaults with delimiter values * add use-value-delimiter config * add debug logging * fix an issue where SCI builder wasn't sorting global indices with input ring we realized that, the `InputCredentials` actually contains all the global indices, so we don't need to have a second source of truth for this, the SCI builder can just get the global indices from the membership proofs The input credentials already sorts the inputs and membership proofs together, so this simplification also fixes the issue * increase atomic swap amount * fix atomic swap fee calculation thing * fix expected balances after changes to swap amounts * fixup RTH tests in connection to atomic swaps * add a note about SCI expiry check being racy Co-authored-by: Eran Rundstein <[email protected]> Co-authored-by: Remoun Metyas <[email protected]> * Bump ed25519 from 1.5.0 to 1.5.2 (#1987) Bumps [ed25519](https://github.com/RustCrypto/signatures) from 1.5.0 to 1.5.2. - [Release notes](https://github.com/RustCrypto/signatures/releases) - [Commits](https://github.com/RustCrypto/signatures/compare/ed25519/v1.5.0...ed25519/v1.5.2) --- updated-dependencies: - dependency-name: ed25519 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump signal-hook from 0.3.13 to 0.3.14 (#1984) Bumps [signal-hook](https://github.com/vorner/signal-hook) from 0.3.13 to 0.3.14. - [Release notes](https://github.com/vorner/signal-hook/releases) - [Changelog](https://github.com/vorner/signal-hook/blob/master/CHANGELOG.md) - [Commits](https://github.com/vorner/signal-hook/compare/v0.3.13...v0.3.14) --- updated-dependencies: - dependency-name: signal-hook dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump rayon from 1.5.2 to 1.5.3 (#1985) Bumps [rayon](https://github.com/rayon-rs/rayon) from 1.5.2 to 1.5.3. - [Release notes](https://github.com/rayon-rs/rayon/releases) - [Changelog](https://github.com/rayon-rs/rayon/blob/master/RELEASES.md) - [Commits](https://github.com/rayon-rs/rayon/compare/v1.5.2...v1.5.3) --- updated-dependencies: - dependency-name: rayon dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump syn from 1.0.94 to 1.0.95 (#1989) Bumps [syn](https://github.com/dtolnay/syn) from 1.0.94 to 1.0.95. - [Release notes](https://github.com/dtolnay/syn/releases) - [Commits](https://github.com/dtolnay/syn/compare/1.0.94...1.0.95) --- updated-dependencies: - dependency-name: syn dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump libc from 0.2.125 to 0.2.126 (#1990) Bumps [libc](https://github.com/rust-lang/libc) from 0.2.125 to 0.2.126. - [Release notes](https://github.com/rust-lang/libc/releases) - [Commits](https://github.com/rust-lang/libc/compare/0.2.125...0.2.126) --- updated-dependencies: - dependency-name: libc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump rocket from 0.5.0-rc.1 to 0.5.0-rc.2 (#1949) * Bump rocket from 0.5.0-rc.1 to 0.5.0-rc.2 Bumps [rocket](https://github.com/SergioBenitez/Rocket) from 0.5.0-rc.1 to 0.5.0-rc.2. - [Release notes](https://github.com/SergioBenitez/Rocket/releases) - [Changelog](https://github.com/SergioBenitez/Rocket/blob/master/CHANGELOG.md) - [Commits](https://github.com/SergioBenitez/Rocket/compare/v0.5.0-rc.1...v0.5.0-rc.2) --- updated-dependencies: - dependency-name: rocket dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> * Update for Rocket v0.5.0-rc.2 Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Andrew Wygle <[email protected]> * Bump ed25519 from 1.5.0 to 1.5.2 in /fog/view/enclave/trusted (#1983) Bumps [ed25519](https://github.com/RustCrypto/signatures) from 1.5.0 to 1.5.2. - [Release notes](https://github.com/RustCrypto/signatures/releases) - [Commits](https://github.com/RustCrypto/signatures/compare/ed25519/v1.5.0...ed25519/v1.5.2) --- updated-dependencies: - dependency-name: ed25519 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump ed25519 from 1.5.0 to 1.5.2 in /consensus/enclave/trusted (#1982) Bumps [ed25519](https://github.com/RustCrypto/signatures) from 1.5.0 to 1.5.2. - [Release notes](https://github.com/RustCrypto/signatures/releases) - [Commits](https://github.com/RustCrypto/signatures/compare/ed25519/v1.5.0...ed25519/v1.5.2) --- updated-dependencies: - dependency-name: ed25519 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * make permission denied log level be info (#1997) Logging at error causes a sentry alert, and so if a client like fog distro is in a retry loop submitting transactions and getting this error, it can blow out our sentry quota * add an option to use an ipinfo token (#1996) (#1998) * Implement some improvements to the `mc-consensus-mint-client` utility (#1993) * add TxFile and dump subcommand * add signing command * Gift Code Memo Builder (#1933) Add memo builders for creating gift code memos and simulate gift code transaction flow within transaction builder. * Fix clippy::iter-overeager-cloned * Fix clippy::needless-arbitrary-self-type * Fix deprecated-where-clause-location * Fix lifetime issue * Fix error: use of `expect` followed by a function call * Fix clippy::needless-collect Co-authored-by: Chris Beck <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Adam Mork <[email protected]> Co-authored-by: Eran Rundstein <[email protected]> Co-authored-by: wjuan-mob <[email protected]> Co-authored-by: James Cape <[email protected]> Co-authored-by: Bernie Dolan <[email protected]> Co-authored-by: Sam <[email protected]> Co-authored-by: Jason Greathouse <[email protected]> Co-authored-by: sugargoat <[email protected]> Co-authored-by: Mike Turner <[email protected]> Co-authored-by: Nick Santana <[email protected]> Co-authored-by: James Cape <[email protected]> Co-authored-by: Eugen Rata <[email protected]> Co-authored-by: Eugene Rata <[email protected]> Co-authored-by: Diana <[email protected]> Co-authored-by: Brian Corbin <[email protected]> Co-authored-by: awygle <[email protected]> Co-authored-by: Andrew Wygle <[email protected]> Co-authored-by: Sam Dealy <[email protected]>
This allows transactions to involve inputs and outputs of different types, as long as they are balanced with respect to all of the types.
This is described in MCIP 31
TODO:
Future work: