Skip to content

Commit

Permalink
Merge pull request #2013 from radixdlt/tests/transaction-structure-tests
Browse files Browse the repository at this point in the history
tests: Added tests for each `SubintentStructureError`
  • Loading branch information
iamyulong authored Nov 26, 2024
2 parents 7a3e55d + 7e1c4e5 commit 603b3c1
Show file tree
Hide file tree
Showing 126 changed files with 3,470 additions and 1,076 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,25 @@ impl ManifestResourceConstraints {
/// * Panics if the constraint isn't valid for the resource address
/// * Panics if constraints have already been specified against the resource
pub fn with(
mut self,
self,
resource_address: ResourceAddress,
constraint: ManifestResourceConstraint,
) -> Self {
if !constraint.is_valid_for(&resource_address) {
panic!("Constraint isn't valid for the resource address");
}
self.with_unchecked(resource_address, constraint)
}

/// Unlike `with`, this does not validate the constraint.
///
/// ## Panics
/// * Panics if constraints have already been specified against the resource
pub fn with_unchecked(
mut self,
resource_address: ResourceAddress,
constraint: ManifestResourceConstraint,
) -> Self {
let replaced = self
.specified_resources
.insert(resource_address, constraint);
Expand Down
4 changes: 2 additions & 2 deletions radix-rust/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ macro_rules! assert_matches {
)
}
};
($expression:expr, $pattern:pat $(if $condition:expr)? $(,)? => $code:expr) => {
($expression:expr, $pattern:pat $(if $condition:expr)? => $code:expr $(,)?) => {
match $expression {
$pattern $(if $condition)? => $code,
ref expression => panic!(
Expand All @@ -67,7 +67,7 @@ macro_rules! assert_matches {
)
}
};
($expression:expr, $pattern:pat $(if $condition:expr)? $(,)? => $code:expr, $($arg:tt)+) => {
($expression:expr, $pattern:pat $(if $condition:expr)? => $code:expr, $($arg:tt)+) => {
match $expression {
$pattern $(if $condition)? => $code,
ref expression => panic!(
Expand Down
4 changes: 3 additions & 1 deletion radix-transactions/src/builder/manifest_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,9 @@ impl<M: BuildableManifest> ManifestBuilder<M> {
self
}

#[deprecated = "This should not be used apart from for test code purposefully constructing invalid manifests. Instead use the more-tailored instruction, or add_instruction_advanced."]
/// This should not be used apart from for test code purposefully constructing invalid manifests.
/// Instead use the more-tailored instruction, or add_instruction_advanced.
#[cfg(test)]
pub fn add_raw_instruction_ignoring_all_side_effects(
self,
instruction: impl Into<M::Instruction>,
Expand Down
2 changes: 0 additions & 2 deletions radix-transactions/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ use sbor::*;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HeaderValidationError {
UnknownVersion(u8),
InvalidEpochRange,
InvalidTimestampRange,
InvalidNetwork,
InvalidCostUnitLimit,
InvalidTip,
NoValidEpochRangeAcrossAllIntents,
NoValidTimestampRangeAcrossAllIntents,
Expand Down
5 changes: 2 additions & 3 deletions radix-transactions/src/manifest/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2262,14 +2262,13 @@ pub fn generator_error_diagnostics(
GeneratorErrorKind::ManifestBuildError(
ManifestBuildError::PreallocatedAddressesUnsupportedByManifestType,
) => {
let title =
format!("preallocated addresses are not supported in this manifest version");
let title = format!("preallocated addresses are not supported in this manifest type");
(title, "unsupported instruction".to_string())
}
GeneratorErrorKind::ManifestBuildError(
ManifestBuildError::ChildSubintentsUnsupportedByManifestType,
) => {
let title = format!("child subintents are not supported in this manifest version");
let title = format!("child subintents are not supported in this manifest type");
(title, "unsupported instruction".to_string())
}
GeneratorErrorKind::HeaderInstructionMustComeFirst => {
Expand Down
8 changes: 8 additions & 0 deletions radix-transactions/src/manifest/manifest_instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1341,6 +1341,14 @@ pub struct YieldToParent {
pub args: ManifestValue,
}

impl YieldToParent {
pub fn empty() -> Self {
Self {
args: ManifestValue::unit(),
}
}
}

impl ManifestInstruction for YieldToParent {
const IDENT: &'static str = "YIELD_TO_PARENT";
const ID: u8 = INSTRUCTION_YIELD_TO_PARENT_DISCRIMINATOR;
Expand Down
2 changes: 1 addition & 1 deletion radix-transactions/src/model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ Enum<3u8>(
assert_eq!(
validated,
Err(TransactionValidationError::PrepareError(
PrepareError::Other(format!("Unknown transaction payload discriminator byte: 4"))
PrepareError::UnexpectedTransactionDiscriminator { actual: Some(4) }
))
)
}
Expand Down
10 changes: 6 additions & 4 deletions radix-transactions/src/model/preparation/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ use sbor::*;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ValueType {
Blob,
Message,
Subintent,
ChildIntentConstraint,
IntentSignatures,
ChildSubintentSpecifier,
SubintentSignatureBatches,
// Too many signatures is captured at validation time
}

#[derive(Debug, Clone, Eq, PartialEq)]
Expand All @@ -22,7 +22,9 @@ pub enum PrepareError {
max: usize,
},
LengthOverflow,
Other(String),
UnexpectedTransactionDiscriminator {
actual: Option<u8>,
},
}

impl From<DecodeError> for PrepareError {
Expand Down
12 changes: 6 additions & 6 deletions radix-transactions/src/model/user_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,9 @@ impl PreparedTransaction for PreparedUserTransaction {
) -> Result<Self, PrepareError> {
let offset = decoder.get_offset();
let slice = decoder.get_input_slice();
let discriminator_byte = slice.get(offset + 1).ok_or(PrepareError::Other(
"Could not read transaction payload discriminator byte".to_string(),
))?;
let discriminator_byte = slice
.get(offset + 1)
.ok_or(PrepareError::UnexpectedTransactionDiscriminator { actual: None })?;

let prepared = match TransactionDiscriminator::from_repr(*discriminator_byte) {
Some(TransactionDiscriminator::V1Notarized) => PreparedUserTransaction::V1(
Expand All @@ -260,9 +260,9 @@ impl PreparedTransaction for PreparedUserTransaction {
PreparedNotarizedTransactionV2::prepare_from_transaction_enum(decoder)?,
),
_ => {
return Err(PrepareError::Other(format!(
"Unknown transaction payload discriminator byte: {discriminator_byte}"
)))
return Err(PrepareError::UnexpectedTransactionDiscriminator {
actual: Some(*discriminator_byte),
})
}
};

Expand Down
6 changes: 6 additions & 0 deletions radix-transactions/src/model/v1/blobs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ pub struct BlobsV1 {
pub blobs: Vec<BlobV1>,
}

impl BlobsV1 {
pub fn none() -> Self {
Self { blobs: Vec::new() }
}
}

impl From<IndexMap<Hash, Vec<u8>>> for BlobsV1 {
fn from(blobs: IndexMap<Hash, Vec<u8>>) -> Self {
let blobs = blobs
Expand Down
8 changes: 7 additions & 1 deletion radix-transactions/src/model/v2/child_subintent_hashes_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ impl ChildSubintentSpecifier {
}
}

impl From<SubintentHash> for ChildSubintentSpecifier {
fn from(hash: SubintentHash) -> Self {
Self { hash }
}
}

/// A new-type representing the index of a referenced intent.
/// The first few of these will be the children of the given intent.
///
Expand Down Expand Up @@ -86,7 +92,7 @@ impl TransactionPreparableFromValueBody for PreparedChildSubintentSpecifiersV2 {
let (hashes, summary) =
ConcatenatedDigest::prepare_from_sbor_array_value_body::<Vec<RawHash>>(
decoder,
ValueType::ChildIntentConstraint,
ValueType::ChildSubintentSpecifier,
max_child_subintents_per_intent,
)?;

Expand Down
14 changes: 13 additions & 1 deletion radix-transactions/src/model/v2/intent_signatures_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ pub struct IntentSignaturesV2 {
pub signatures: Vec<IntentSignatureV1>,
}

impl IntentSignaturesV2 {
pub fn none() -> Self {
Self {
signatures: Vec::new(),
}
}

pub fn new(signatures: Vec<IntentSignatureV1>) -> Self {
Self { signatures }
}
}

impl TransactionPartialPrepare for IntentSignaturesV2 {
type Prepared = PreparedIntentSignaturesV2;
}
Expand Down Expand Up @@ -34,7 +46,7 @@ impl TransactionPreparableFromValueBody for PreparedNonRootSubintentSignaturesV2
Vec<PreparedIntentSignaturesV2>,
>(
decoder,
ValueType::IntentSignatures,
ValueType::SubintentSignatureBatches,
max_subintents_per_transaction,
)?;

Expand Down
21 changes: 21 additions & 0 deletions radix-transactions/src/model/v2/transaction_manifest_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ impl BuildableManifest for TransactionManifestV2 {
impl BuildableManifestSupportingChildren for TransactionManifestV2 {}

impl TransactionManifestV2 {
pub fn empty() -> Self {
Self {
instructions: Default::default(),
blobs: Default::default(),
children: Default::default(),
object_names: ManifestObjectNames::Unknown,
}
}

pub fn from_intent_core(intent: &IntentCoreV2) -> Self {
Self {
instructions: intent.instructions.to_vec(),
Expand All @@ -93,6 +102,18 @@ impl TransactionManifestV2 {
}
}

pub fn to_intent_core(self, header: IntentHeaderV2, message: MessageV2) -> IntentCoreV2 {
IntentCoreV2 {
header,
blobs: self.blobs.into(),
message,
instructions: self.instructions.into(),
children: ChildSubintentSpecifiersV2 {
children: self.children,
},
}
}

pub fn for_intent(self) -> (InstructionsV2, BlobsV1, ChildSubintentSpecifiersV2) {
(
self.instructions.into(),
Expand Down
2 changes: 1 addition & 1 deletion radix-transactions/src/validation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ pub use transaction_structure_validator::*;
pub use transaction_validation_configuration::*;
pub use transaction_validator::*;
#[cfg(test)]
pub use validation_test_helpers::*;
pub(crate) use validation_test_helpers::*;
108 changes: 108 additions & 0 deletions radix-transactions/src/validation/signature_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -585,4 +585,112 @@ mod tests {
}
}
}

#[test]
fn too_many_signatures_should_be_rejected() {
fn validate_transaction(
root_signature_count: usize,
signature_counts: Vec<usize>,
) -> Result<ValidatedNotarizedTransactionV2, TransactionValidationError> {
TransactionV2Builder::new_with_test_defaults()
.add_children(
signature_counts
.iter()
.enumerate()
.map(|(i, signature_count)| {
create_leaf_partial_transaction(i as u64, *signature_count)
}),
)
.add_manifest_calling_each_child_once()
.multi_sign(
(0..root_signature_count)
.into_iter()
.map(|i| Secp256k1PrivateKey::from_u64((100 + i) as u64).unwrap()),
)
.default_notarize_and_validate()
}

assert_matches!(validate_transaction(1, vec![10]), Ok(_));
assert_matches!(
validate_transaction(1, vec![10, 20]),
Err(TransactionValidationError::SignatureValidationError(
TransactionValidationErrorLocation::NonRootSubintent(SubintentIndex(1), _),
SignatureValidationError::TooManySignatures {
total: 20,
limit: 16,
},
))
);
assert_matches!(
validate_transaction(17, vec![0, 3]),
Err(TransactionValidationError::SignatureValidationError(
TransactionValidationErrorLocation::RootTransactionIntent(_),
SignatureValidationError::TooManySignatures {
total: 17,
limit: 16,
},
))
);
assert_matches!(
validate_transaction(1, vec![10, 10, 10, 10, 10, 10, 10]),
Err(TransactionValidationError::SignatureValidationError(
TransactionValidationErrorLocation::AcrossTransaction,
SignatureValidationError::TooManySignatures {
total: 72, // 70 from subintent, 1 from transaction intent, 1 from notarization
limit: 64
},
))
);
}

#[test]
fn test_incorrect_number_of_subintent_signature_batches() {
// CASE 1: Too fee signatures
let validator = TransactionValidator::new_for_latest_simulator();

let mut transaction = TransactionV2Builder::new_with_test_defaults()
.add_children(vec![PartialTransactionV2Builder::new_with_test_defaults()
.add_trivial_manifest()
.build()])
.add_manifest_calling_each_child_once()
.default_notarize()
.build_minimal_no_validate();

// Remove one signature batch
let removed_signature_batch = transaction
.signed_transaction_intent
.non_root_subintent_signatures
.by_subintent
.pop()
.unwrap();

assert_matches!(
transaction.prepare_and_validate(&validator),
Err(TransactionValidationError::SignatureValidationError(
TransactionValidationErrorLocation::AcrossTransaction,
SignatureValidationError::IncorrectNumberOfSubintentSignatureBatches
))
);

// CASE 2: Too many signature batches
let mut transaction = TransactionV2Builder::new_with_test_defaults()
.add_trivial_manifest()
.default_notarize()
.build_minimal_no_validate();

// Add an extra signature batch
transaction
.signed_transaction_intent
.non_root_subintent_signatures
.by_subintent
.push(removed_signature_batch);

assert_matches!(
transaction.prepare_and_validate(&validator),
Err(TransactionValidationError::SignatureValidationError(
TransactionValidationErrorLocation::AcrossTransaction,
SignatureValidationError::IncorrectNumberOfSubintentSignatureBatches
))
);
}
}
Loading

0 comments on commit 603b3c1

Please sign in to comment.