Skip to content

Commit

Permalink
Merge pull request #2010 from radixdlt/tests/further-cuttlefish-funct…
Browse files Browse the repository at this point in the history
…ional-tests

tests: Add further subintent and verify parent auth tests
  • Loading branch information
dhedey authored Nov 22, 2024
2 parents c3aa433 + ba43323 commit 4733ad7
Show file tree
Hide file tree
Showing 13 changed files with 289 additions and 11 deletions.
1 change: 1 addition & 0 deletions radix-engine-tests/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Provides assets paths to benchmarks.
pub mod common;
pub mod pool_stubs;
pub mod prelude;
2 changes: 2 additions & 0 deletions radix-engine-tests/src/prelude.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub use crate::common::PackageLoader;
pub use scrypto_test::prelude::*;
4 changes: 4 additions & 0 deletions radix-engine-tests/tests/application_folder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@
// * Have this X_folder.rs file which gets loaded by the test loader
// * Use a mod definition to point to `X/mod.rs` where tests are defined
mod application;

pub mod prelude {
pub use radix_engine_tests::prelude::*;
}
4 changes: 4 additions & 0 deletions radix-engine-tests/tests/blueprints_folder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@
// * Have this X_folder.rs file which gets loaded by the test loader
// * Use a mod definition to point to `X/mod.rs` where tests are defined
mod blueprints;

pub mod prelude {
pub use radix_engine_tests::prelude::*;
}
4 changes: 4 additions & 0 deletions radix-engine-tests/tests/db_folder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@
// * Have this X_folder.rs file which gets loaded by the test loader
// * Use a mod definition to point to `X/mod.rs` where tests are defined
mod db;

pub mod prelude {
pub use radix_engine_tests::prelude::*;
}
4 changes: 4 additions & 0 deletions radix-engine-tests/tests/flash_folder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@
// * Have this X_folder.rs file which gets loaded by the test loader
// * Use a mod definition to point to `X/mod.rs` where tests are defined
mod flash;

pub mod prelude {
pub use radix_engine_tests::prelude::*;
}
4 changes: 4 additions & 0 deletions radix-engine-tests/tests/kernel_folder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@
// * Have this X_folder.rs file which gets loaded by the test loader
// * Use a mod definition to point to `X/mod.rs` where tests are defined
mod kernel;

pub mod prelude {
pub use radix_engine_tests::prelude::*;
}
4 changes: 4 additions & 0 deletions radix-engine-tests/tests/protocol_folder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@
// * Have this X_folder.rs file which gets loaded by the test loader
// * Use a mod definition to point to `X/mod.rs` where tests are defined
mod protocol;

pub mod prelude {
pub use radix_engine_tests::prelude::*;
}
140 changes: 138 additions & 2 deletions radix-engine-tests/tests/system/subintent_auth.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use scrypto_test::prelude::*;
use crate::prelude::*;

#[test]
fn should_not_be_able_to_use_root_auth_in_subintent() {
fn subintent_should_not_be_able_to_use_proofs_from_transaction_intent() {
// Arrange
let mut ledger = LedgerSimulatorBuilder::new().build();
let (public_key, _, account) = ledger.new_allocated_account();
Expand Down Expand Up @@ -38,6 +38,88 @@ fn should_not_be_able_to_use_root_auth_in_subintent() {
});
}

#[test]
fn subintent_should_not_be_able_to_use_proofs_from_other_subintents() {
// Arrange
let mut ledger = LedgerSimulatorBuilder::new().build();

let assert_access_rule_component_address = {
let package_address = ledger.publish_package_simple(PackageLoader::get("role_assignment"));

let manifest = ManifestBuilder::new()
.lock_fee_from_faucet()
.call_function(package_address, "AssertAccessRule", "new", manifest_args!())
.build();

let receipt = ledger.execute_manifest(manifest, []);
receipt.expect_commit_success();

receipt.expect_commit(true).new_component_addresses()[0]
};

// We will create a complex transaction with lots of intents.
// Naming convention: subintent XXX will have children XXXa, XXXb, XXXc, etc.
// Each intent will be signed by its own key.

let keys = indexmap! {
"aaa" => ledger.new_allocated_account().0,
"aaba" => ledger.new_allocated_account().0,
"aab" => ledger.new_allocated_account().0,
"aac" => ledger.new_allocated_account().0,
"aa" => ledger.new_allocated_account().0,
"ab" => ledger.new_allocated_account().0,
"a" => ledger.new_allocated_account().0,
"ba" => ledger.new_allocated_account().0,
"b" => ledger.new_allocated_account().0,
"c" => ledger.new_allocated_account().0,
"ROOT" => ledger.new_allocated_account().0,
};

// We will set all their manifests to be simple/trivial, except the AAB manifest
// which will be set to call "assert access rule" with each of the keys.
// The transaction should only pass if the AAB key is used.
for (key_name_to_assert, key_to_assert) in keys.iter() {
let mut builder = TestTransaction::new_v2_builder(ledger.next_transaction_nonce());

let aaa = builder.add_simple_subintent([], [keys["aaa"].signature_proof()]);
let aaba = builder.add_simple_subintent([], [keys["aaba"].signature_proof()]);
let aab = builder.add_tweaked_simple_subintent(
[aaba],
[keys["aab"].signature_proof()],
|builder| {
builder.call_method(
assert_access_rule_component_address,
"assert_access_rule",
(rule!(require(key_to_assert.signature_proof())),),
)
},
);
let aac = builder.add_simple_subintent([], [keys["aac"].signature_proof()]);
let aa = builder.add_simple_subintent([aaa, aab, aac], [keys["aa"].signature_proof()]);
let ab = builder.add_simple_subintent([], [keys["ab"].signature_proof()]);
let a = builder.add_simple_subintent([aa, ab], [keys["a"].signature_proof()]);
let ba = builder.add_simple_subintent([], [keys["ba"].signature_proof()]);
let b = builder.add_simple_subintent([ba], [keys["b"].signature_proof()]);
let c = builder.add_simple_subintent([], [keys["c"].signature_proof()]);
let transaction =
builder.finish_with_simple_root_intent([a, b, c], [keys["ROOT"].signature_proof()]);

let receipt = ledger.execute_test_transaction(transaction);

// ASSERT
if *key_name_to_assert == "aab" {
receipt.expect_commit_success();
} else {
receipt.expect_specific_failure(|e| {
matches!(
e,
RuntimeError::SystemError(SystemError::AssertAccessRuleFailed)
)
});
}
}
}

#[test]
fn should_be_able_to_use_separate_auth_in_subintent() {
// Arrange
Expand Down Expand Up @@ -72,6 +154,60 @@ fn should_be_able_to_use_separate_auth_in_subintent() {
receipt.expect_commit_success();
}

#[test]
fn subintent_processor_uses_transaction_processor_global_caller() {
// Arrange
let mut ledger = LedgerSimulatorBuilder::new().build();

let assert_access_rule_component_address = {
let package_address = ledger.publish_package_simple(PackageLoader::get("role_assignment"));

let manifest = ManifestBuilder::new()
.lock_fee_from_faucet()
.call_function(package_address, "AssertAccessRule", "new", manifest_args!())
.build();

let receipt = ledger.execute_manifest(manifest, []);
receipt.expect_commit_success();

receipt.expect_commit(true).new_component_addresses()[0]
};

// Act
let mut builder = TestTransaction::new_v2_builder(ledger.next_transaction_nonce());

let transaction_processor = BlueprintId {
package_address: TRANSACTION_PROCESSOR_PACKAGE,
blueprint_name: TRANSACTION_PROCESSOR_BLUEPRINT.to_string(),
};

let child = builder.add_subintent(
ManifestBuilder::new_subintent_v2()
.call_method(
assert_access_rule_component_address,
"assert_access_rule",
(rule!(require(global_caller(transaction_processor))),),
)
.yield_to_parent(())
.build(),
[],
);

let transaction = builder.finish_with_root_intent(
ManifestBuilder::new_v2()
.use_child("child", child)
.lock_fee_from_faucet()
.yield_to_child("child", ())
.build(),
[],
);

let receipt = ledger.execute_test_transaction(transaction);

// Assert
receipt.expect_commit_success();
}

#[derive(Debug, Eq, PartialEq, ManifestSbor)]
pub struct ManifestTransactionProcessorRunInput {
pub manifest_encoded_instructions: Vec<u8>,
Expand Down
74 changes: 65 additions & 9 deletions radix-engine-tests/tests/system/subintent_verify_parent.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
use radix_common::constants::XRD;
use radix_common::crypto::HasPublicKeyHash;
use radix_engine::errors::{IntentError, RuntimeError, SystemError};
use radix_engine_interface::macros::dec;
use radix_engine_interface::prelude::{require, require_amount, AccessRule};
use radix_engine_interface::rule;
use radix_transactions::builder::ManifestBuilder;
use radix_transactions::model::TestTransaction;
use scrypto_test::ledger_simulator::LedgerSimulatorBuilder;
use crate::prelude::*;

#[test]
fn should_not_be_able_to_use_subintent_when_verify_parent_access_rule_not_met() {
Expand Down Expand Up @@ -198,3 +190,67 @@ fn should_be_able_to_use_subintent_when_verify_parent_access_rule_is_met_on_seco
// Assert
receipt.expect_commit_success();
}

#[test]
fn verify_parent_should_only_work_against_proofs_in_parent_intent() {
// Arrange
let mut ledger = LedgerSimulatorBuilder::new().build();

// We will create a complex transaction with lots of intents.
// Naming convention: subintent XXX will have children XXXa, XXXb, XXXc, etc.
// Each intent will be signed by its own key.

let keys = indexmap! {
"aaa" => ledger.new_allocated_account().0,
"aaba" => ledger.new_allocated_account().0,
"aab" => ledger.new_allocated_account().0,
"aac" => ledger.new_allocated_account().0,
"aa" => ledger.new_allocated_account().0,
"ab" => ledger.new_allocated_account().0,
"a" => ledger.new_allocated_account().0,
"ba" => ledger.new_allocated_account().0,
"b" => ledger.new_allocated_account().0,
"c" => ledger.new_allocated_account().0,
"ROOT" => ledger.new_allocated_account().0,
};

// We will set all their manifests to be simple/trivial, except the AAB manifest
// which will be set to call "verify parent" with each of the keys.
// The transaction should only pass if the AA key is used (its parent).
for (key_name_to_assert, key_to_assert) in keys.iter() {
let mut builder = TestTransaction::new_v2_builder(ledger.next_transaction_nonce());

let aaa = builder.add_simple_subintent([], [keys["aaa"].signature_proof()]);
let aaba = builder.add_simple_subintent([], [keys["aaba"].signature_proof()]);
let aab = builder.add_tweaked_simple_subintent(
[aaba],
[keys["aab"].signature_proof()],
|builder| builder.verify_parent(rule!(require(key_to_assert.signature_proof()))),
);
let aac = builder.add_simple_subintent([], [keys["aac"].signature_proof()]);
let aa = builder.add_simple_subintent([aaa, aab, aac], [keys["aa"].signature_proof()]);
let ab = builder.add_simple_subintent([], [keys["ab"].signature_proof()]);
let a = builder.add_simple_subintent([aa, ab], [keys["a"].signature_proof()]);
let ba = builder.add_simple_subintent([], [keys["ba"].signature_proof()]);
let b = builder.add_simple_subintent([ba], [keys["b"].signature_proof()]);
let c = builder.add_simple_subintent([], [keys["c"].signature_proof()]);
let transaction =
builder.finish_with_simple_root_intent([a, b, c], [keys["ROOT"].signature_proof()]);

let receipt = ledger.execute_test_transaction(transaction);

// ASSERT
if *key_name_to_assert == "aa" {
receipt.expect_commit_success();
} else {
receipt.expect_specific_failure(|e| {
matches!(
e,
RuntimeError::SystemError(SystemError::IntentError(
IntentError::VerifyParentFailed
))
)
});
}
}
}
4 changes: 4 additions & 0 deletions radix-engine-tests/tests/system_folder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@
// * Have this X_folder.rs file which gets loaded by the test loader
// * Use a mod definition to point to `X/mod.rs` where tests are defined
mod system;

mod prelude {
pub use radix_engine_tests::prelude::*;
}
4 changes: 4 additions & 0 deletions radix-engine-tests/tests/vm_folder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@
// * Have this X_folder.rs file which gets loaded by the test loader
// * Use a mod definition to point to `X/mod.rs` where tests are defined
mod vm;

pub mod prelude {
pub use radix_engine_tests::prelude::*;
}
51 changes: 51 additions & 0 deletions radix-transactions/src/model/test_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,39 @@ impl TestTransactionV2Builder {
}
}

/// Yields to each child exactly once with empty arguments.
pub fn add_simple_subintent(
&mut self,
children: impl IntoIterator<Item = SubintentHash>,
proofs: impl IntoIterator<Item = NonFungibleGlobalId>,
) -> SubintentHash {
let mut manifest_builder = ManifestBuilder::new_subintent_v2();
for (child_index, child_hash) in children.into_iter().enumerate() {
let child_name = format!("child_{child_index}");
manifest_builder = manifest_builder.use_child(&child_name, child_hash);
manifest_builder = manifest_builder.yield_to_child(child_name, ());
}
let manifest = manifest_builder.yield_to_parent(()).build();
self.add_subintent(manifest, proofs)
}

pub fn add_tweaked_simple_subintent(
&mut self,
children: impl IntoIterator<Item = SubintentHash>,
proofs: impl IntoIterator<Item = NonFungibleGlobalId>,
addition: impl FnOnce(SubintentManifestV2Builder) -> SubintentManifestV2Builder,
) -> SubintentHash {
let mut manifest_builder = ManifestBuilder::new_subintent_v2();
for (child_index, child_hash) in children.into_iter().enumerate() {
let child_name = format!("child_{child_index}");
manifest_builder = manifest_builder.use_child(&child_name, child_hash);
manifest_builder = manifest_builder.yield_to_child(child_name, ());
}
manifest_builder = addition(manifest_builder);
let manifest = manifest_builder.yield_to_parent(()).build();
self.add_subintent(manifest, proofs)
}

pub fn add_subintent(
&mut self,
manifest: SubintentManifestV2,
Expand All @@ -34,6 +67,24 @@ impl TestTransactionV2Builder {
SubintentHash(hash)
}

/// Uses the faucet and yields to each child exactly once with empty arguments.
pub fn finish_with_simple_root_intent(
self,
children: impl IntoIterator<Item = SubintentHash>,
proofs: impl IntoIterator<Item = NonFungibleGlobalId>,
) -> TestTransaction {
let mut manifest_builder = ManifestBuilder::new_v2();
manifest_builder = manifest_builder.lock_fee_from_faucet();
for (child_index, child_hash) in children.into_iter().enumerate() {
let child_name = format!("child_{child_index}");
// In the manifest builder, we allow USE_CHILD later than in a written manifest
manifest_builder = manifest_builder.use_child(&child_name, child_hash);
manifest_builder = manifest_builder.yield_to_child(child_name, ());
}
let manifest = manifest_builder.build();
self.finish_with_root_intent(manifest, proofs)
}

pub fn finish_with_root_intent(
self,
manifest: TransactionManifestV2,
Expand Down

0 comments on commit 4733ad7

Please sign in to comment.