Skip to content
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

block-version-based protocol evolution #1468

Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ commands:
parameters:
test_command:
type: string
default: cargo test --workspace --exclude "mc-fog-*" --frozen --target "$HOST_TARGET_TRIPLE" --no-fail-fast --tests -j 4
default: cargo test --workspace --exclude "mc-fog-*" --exclude "mc-consensus-*" --frozen --target "$HOST_TARGET_TRIPLE" --no-fail-fast --tests -j 4
steps:
- run:
name: Run mobilecoin tests
Expand Down Expand Up @@ -368,6 +368,22 @@ commands:
- store_artifacts:
path: /tmp/core_dumps

run-consensus-tests:
steps:
- run:
name: Run consensus tests
command: |
# tell the operating system to remove the file size limit on core dump files
ulimit -c unlimited
cargo test --package "mc-consensus-*" -j 4 --frozen --no-fail-fast
- run:
command: |
mkdir -p /tmp/core_dumps
cp core.* /tmp/core_dumps
when: on_fail
- store_artifacts:
path: /tmp/core_dumps

# FIXME: Figure out why the parallel tests stuff using cargo2junit isn't working in the cloud for fog, maybe a memory limit issue?
run-fog-tests:
steps:
Expand Down Expand Up @@ -499,6 +515,20 @@ jobs:
- post-build
- post-mc-test

# Run consensus tests on a single container
run-consensus-tests:
executor: build-executor
environment:
<<: *default-build-environment
steps:
- prepare-for-build
- run-consensus-tests
- check-dirty-git
- when:
condition: { equal: [ << pipeline.git.branch >>, master ] }
steps: [ save-sccache-cache ]
- post-build

# Run fog tests on a single container
run-fog-tests:
executor: build-executor
Expand Down Expand Up @@ -654,6 +684,10 @@ workflows:
- run-mc-tests:
filters: { branches: { ignore: /^deploy\/.*/ } }

# Run consensus tests on a single container
- run-consensus-tests:
filters: { branches: { ignore: /^deploy\/.*/ } }

# Run fog tests on a single container
- run-fog-tests:
filters: { branches: { ignore: /^deploy\/.*/ } }
Expand Down
19 changes: 14 additions & 5 deletions android-bindings/src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ use mc_transaction_core::{
},
ring_signature::KeyImage,
tx::{Tx, TxOut, TxOutConfirmationNumber, TxOutMembershipProof},
Amount, CompressedCommitment,
Amount, BlockVersion, CompressedCommitment,
};
use mc_transaction_std::{InputCredentials, NoMemoBuilder, TransactionBuilder};
use mc_transaction_std::{InputCredentials, RTHMemoBuilder, TransactionBuilder};
use mc_util_from_random::FromRandom;
use mc_util_uri::FogUri;
use protobuf::Message;
Expand Down Expand Up @@ -1166,9 +1166,18 @@ pub unsafe extern "C" fn Java_com_mobilecoin_lib_TransactionBuilder_init_1jni(
jni_ffi_call(&env, |env| {
let fog_resolver: MutexGuard<FogResolver> =
env.get_rust_field(fog_resolver, RUST_OBJ_FIELD)?;
// TODO: After servers that support memos are deployed, use RTHMemoBuilder here
let memo_builder = NoMemoBuilder::default();
let tx_builder = TransactionBuilder::new(fog_resolver.clone(), memo_builder);
// FIXME: block version should be a parameter, it should be the latest
// version that fog ledger told us about, or that we got from ledger-db
let block_version = BlockVersion::ONE;
// Note: RTHMemoBuilder can be selected here, but we will only actually
// write memos if block_version is large enough that memos are supported.
// If block version is < 2, then transaction builder will filter out memos.
let mut memo_builder = RTHMemoBuilder::default();
// FIXME: we need to pass the source account key to build sender memo
// credentials memo_builder.set_sender_credential(SenderMemoCredential::
// from(source_account_key));
memo_builder.enable_destination_memo();
let tx_builder = TransactionBuilder::new(block_version, fog_resolver.clone(), memo_builder);
Ok(env.set_rust_field(obj, RUST_OBJ_FIELD, tx_builder)?)
})
}
Expand Down
4 changes: 2 additions & 2 deletions api/src/convert/archive_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ mod tests {
membership_proofs::Range,
ring_signature::KeyImage,
tx::{TxOut, TxOutMembershipElement, TxOutMembershipHash},
Amount, Block, BlockContents, BlockData, BlockID, BlockSignature,
Amount, Block, BlockContents, BlockData, BlockID, BlockSignature, BlockVersion,
};
use mc_util_from_random::FromRandom;
use rand::{rngs::StdRng, SeedableRng};
Expand All @@ -148,7 +148,7 @@ mod tests {

let block_contents = BlockContents::new(vec![key_image.clone()], vec![tx_out.clone()]);
let block = Block::new(
1,
BlockVersion::ONE,
&parent_block_id,
99 + block_idx,
400 + block_idx,
Expand Down
168 changes: 89 additions & 79 deletions api/src/convert/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ mod tests {
use mc_transaction_core::{
onetime_keys::recover_onetime_private_key,
tx::{Tx, TxOut, TxOutMembershipProof},
BlockVersion,
};
use mc_transaction_core_test_utils::MockFogResolver;
use mc_transaction_std::{EmptyMemoBuilder, InputCredentials, TransactionBuilder};
Expand All @@ -46,87 +47,96 @@ mod tests {
// transaction_builder.rs::test_simple_transaction
let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]);

let alice = AccountKey::random(&mut rng);
let bob = AccountKey::random(&mut rng);
let charlie = AccountKey::random(&mut rng);

let minted_outputs: Vec<TxOut> = {
// Mint an initial collection of outputs, including one belonging to
// `sender_account`.
let mut recipient_and_amounts: Vec<(PublicAddress, u64)> = Vec::new();
recipient_and_amounts.push((alice.default_subaddress(), 65536));

// Some outputs belonging to this account will be used as mix-ins.
recipient_and_amounts.push((charlie.default_subaddress(), 65536));
recipient_and_amounts.push((charlie.default_subaddress(), 65536));
mc_transaction_core_test_utils::get_outputs(&recipient_and_amounts, &mut rng)
};

let mut transaction_builder =
TransactionBuilder::new(MockFogResolver::default(), EmptyMemoBuilder::default());

let ring: Vec<TxOut> = minted_outputs.clone();
let public_key = RistrettoPublic::try_from(&minted_outputs[0].public_key).unwrap();
let onetime_private_key = recover_onetime_private_key(
&public_key,
alice.view_private_key(),
&alice.default_subaddress_spend_private(),
);

let membership_proofs: Vec<TxOutMembershipProof> = ring
.iter()
.map(|_tx_out| {
// TransactionBuilder does not validate membership proofs, but does require one
// for each ring member.
TxOutMembershipProof::new(0, 0, Default::default())
})
.collect();

let input_credentials = InputCredentials::new(
ring.clone(),
membership_proofs,
0,
onetime_private_key,
*alice.view_private_key(),
)
.unwrap();

transaction_builder.add_input(input_credentials);
transaction_builder.set_fee(0).unwrap();
transaction_builder
.add_output(65536, &bob.default_subaddress(), &mut rng)
for block_version in BlockVersion::iterator() {
let alice = AccountKey::random(&mut rng);
let bob = AccountKey::random(&mut rng);
let charlie = AccountKey::random(&mut rng);

let minted_outputs: Vec<TxOut> = {
// Mint an initial collection of outputs, including one belonging to
// `sender_account`.
let mut recipient_and_amounts: Vec<(PublicAddress, u64)> = Vec::new();
recipient_and_amounts.push((alice.default_subaddress(), 65536));

// Some outputs belonging to this account will be used as mix-ins.
recipient_and_amounts.push((charlie.default_subaddress(), 65536));
recipient_and_amounts.push((charlie.default_subaddress(), 65536));
mc_transaction_core_test_utils::get_outputs(
block_version,
&recipient_and_amounts,
&mut rng,
)
};

let mut transaction_builder = TransactionBuilder::new(
block_version,
MockFogResolver::default(),
EmptyMemoBuilder::default(),
);

let ring: Vec<TxOut> = minted_outputs.clone();
let public_key = RistrettoPublic::try_from(&minted_outputs[0].public_key).unwrap();
let onetime_private_key = recover_onetime_private_key(
&public_key,
alice.view_private_key(),
&alice.default_subaddress_spend_private(),
);

let membership_proofs: Vec<TxOutMembershipProof> = ring
.iter()
.map(|_tx_out| {
// TransactionBuilder does not validate membership proofs, but does require one
// for each ring member.
TxOutMembershipProof::new(0, 0, Default::default())
})
.collect();

let input_credentials = InputCredentials::new(
ring.clone(),
membership_proofs,
0,
onetime_private_key,
*alice.view_private_key(),
)
.unwrap();

let tx = transaction_builder.build(&mut rng).unwrap();

// decode(encode(tx)) should be the identity function.
{
let bytes = mc_util_serial::encode(&tx);
let recovered_tx = mc_util_serial::decode(&bytes).unwrap();
assert_eq!(tx, recovered_tx);
}

// Converting mc_transaction_core::Tx -> external::Tx -> mc_transaction_core::Tx
// should be the identity function.
{
let external_tx: external::Tx = external::Tx::from(&tx);
let recovered_tx: Tx = Tx::try_from(&external_tx).unwrap();
assert_eq!(tx, recovered_tx);
}

// Encoding with prost, decoding with protobuf should be the identity function.
{
let bytes = mc_util_serial::encode(&tx);
let recovered_tx = external::Tx::parse_from_bytes(&bytes).unwrap();
assert_eq!(recovered_tx, external::Tx::from(&tx));
}

// Encoding with protobuf, decoding with prost should be the identity function.
{
let external_tx: external::Tx = external::Tx::from(&tx);
let bytes = external_tx.write_to_bytes().unwrap();
let recovered_tx: Tx = mc_util_serial::decode(&bytes).unwrap();
assert_eq!(tx, recovered_tx);
transaction_builder.add_input(input_credentials);
transaction_builder.set_fee(0).unwrap();
transaction_builder
.add_output(65536, &bob.default_subaddress(), &mut rng)
.unwrap();

let tx = transaction_builder.build(&mut rng).unwrap();

// decode(encode(tx)) should be the identity function.
{
let bytes = mc_util_serial::encode(&tx);
let recovered_tx = mc_util_serial::decode(&bytes).unwrap();
assert_eq!(tx, recovered_tx);
}

// Converting mc_transaction_core::Tx -> external::Tx -> mc_transaction_core::Tx
// should be the identity function.
{
let external_tx: external::Tx = external::Tx::from(&tx);
let recovered_tx: Tx = Tx::try_from(&external_tx).unwrap();
assert_eq!(tx, recovered_tx);
}

// Encoding with prost, decoding with protobuf should be the identity function.
{
let bytes = mc_util_serial::encode(&tx);
let recovered_tx = external::Tx::parse_from_bytes(&bytes).unwrap();
assert_eq!(recovered_tx, external::Tx::from(&tx));
}

// Encoding with protobuf, decoding with prost should be the identity function.
{
let external_tx: external::Tx = external::Tx::from(&tx);
let bytes = external_tx.write_to_bytes().unwrap();
let recovered_tx: Tx = mc_util_serial::decode(&bytes).unwrap();
assert_eq!(tx, recovered_tx);
}
}
}
}
Loading