Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions ledger/block/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ workspace = true
[dependencies.snarkvm-ledger-puzzle]
workspace = true

[dependencies.snarkvm-synthesizer-error]
workspace = true

[dependencies.snarkvm-synthesizer-program]
workspace = true

Expand Down
89 changes: 56 additions & 33 deletions ledger/block/src/transactions/confirmed/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ mod bytes;
mod serialize;
mod string;

use crate::{Transaction, rejected::Rejected};
use crate::{Transaction, rejected::Rejected, rejected_reason::RejectedReason};
use console::{network::prelude::*, program::FINALIZE_ID_DEPTH, types::Field};
use snarkvm_synthesizer_program::FinalizeOperation;

Expand Down Expand Up @@ -220,6 +220,16 @@ impl<N: Network> ConfirmedTransaction<N> {
pub fn contains_unconfirmed_transaction_id(&self, unconfirmed_transaction_id: &N::TransactionID) -> bool {
self.to_unconfirmed_transaction_id().is_ok_and(|id| &id == unconfirmed_transaction_id)
}

/// Returns the rejected reason, if the confirmed transaction is rejected.
pub fn rejected_reason(&self) -> &Option<RejectedReason<N>> {
match self {
Self::AcceptedDeploy(..) | Self::AcceptedExecute(..) => &None,
Self::RejectedDeploy(_, _, rejected, _) | Self::RejectedExecute(_, _, rejected, _) => {
rejected.rejected_reason()
}
}
}
}

impl<N: Network> ConfirmedTransaction<N> {
Expand Down Expand Up @@ -409,6 +419,7 @@ pub mod test_helpers {
version: u8,
edition: u16,
is_fee_private: bool,
has_rejected_reason: bool,
rng: &mut TestRng,
) -> ConfirmedTransaction<CurrentNetwork> {
// Sample a fee transaction.
Expand All @@ -418,7 +429,13 @@ pub mod test_helpers {
};

// Extract the rejected deployment.
let rejected = crate::rejected::test_helpers::sample_rejected_deployment(version, edition, is_fee_private, rng);
let rejected = crate::rejected::test_helpers::sample_rejected_deployment(
version,
edition,
is_fee_private,
has_rejected_reason,
rng,
);

// Return the confirmed transaction.
ConfirmedTransaction::rejected_deploy(index, fee_transaction, rejected, vec![]).unwrap()
Expand All @@ -428,6 +445,7 @@ pub mod test_helpers {
pub(crate) fn sample_rejected_execute(
index: u32,
is_fee_private: bool,
has_rejected_reason: bool,
rng: &mut TestRng,
) -> ConfirmedTransaction<CurrentNetwork> {
// Sample a fee transaction.
Expand All @@ -437,7 +455,8 @@ pub mod test_helpers {
};

// Extract the rejected execution.
let rejected = crate::rejected::test_helpers::sample_rejected_execution(is_fee_private, rng);
let rejected =
crate::rejected::test_helpers::sample_rejected_execution(is_fee_private, has_rejected_reason, rng);

// Return the confirmed transaction.
ConfirmedTransaction::rejected_execute(index, fee_transaction, rejected, vec![]).unwrap()
Expand All @@ -454,24 +473,24 @@ pub mod test_helpers {
sample_accepted_deploy(0, 2, Uniform::rand(rng), true, rng),
sample_accepted_execute(1, true, rng),
sample_accepted_execute(1, false, rng),
sample_rejected_deploy(2, 1, Uniform::rand(rng), true, rng),
sample_rejected_deploy(2, 1, Uniform::rand(rng), true, rng),
sample_rejected_deploy(2, 2, Uniform::rand(rng), true, rng),
sample_rejected_deploy(2, 2, Uniform::rand(rng), true, rng),
sample_rejected_execute(3, true, rng),
sample_rejected_execute(3, false, rng),
sample_rejected_deploy(2, 1, Uniform::rand(rng), true, true, rng),
sample_rejected_deploy(2, 1, Uniform::rand(rng), true, false, rng),
sample_rejected_deploy(2, 2, Uniform::rand(rng), true, true, rng),
sample_rejected_deploy(2, 2, Uniform::rand(rng), true, false, rng),
sample_rejected_execute(3, true, true, rng),
sample_rejected_execute(3, true, false, rng),
sample_rejected_execute(3, false, true, rng),
sample_rejected_execute(3, false, false, rng),
sample_accepted_execute(Uniform::rand(rng), true, rng),
sample_accepted_execute(Uniform::rand(rng), false, rng),
sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), true, rng),
sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, rng),
sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), true, rng),
sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, rng),
sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), true, rng),
sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, rng),
sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), true, rng),
sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, rng),
sample_rejected_execute(Uniform::rand(rng), true, rng),
sample_rejected_execute(Uniform::rand(rng), false, rng),
sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), true, Uniform::rand(rng), rng),
sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, Uniform::rand(rng), rng),
sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), true, Uniform::rand(rng), rng),
sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, Uniform::rand(rng), rng),
sample_rejected_execute(Uniform::rand(rng), true, true, rng),
sample_rejected_execute(Uniform::rand(rng), true, false, rng),
sample_rejected_execute(Uniform::rand(rng), false, true, rng),
sample_rejected_execute(Uniform::rand(rng), false, false, rng),
]
}
}
Expand Down Expand Up @@ -549,22 +568,22 @@ mod test {

// Ensure that the unconfirmed transaction ID of a rejected deployment is not equivalent to its confirmed transaction ID.
let rejected_deploy =
test_helpers::sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), true, rng);
test_helpers::sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), true, false, rng);
check_contains_unconfirmed_transaction_id(rejected_deploy);
let rejected_deploy =
test_helpers::sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, rng);
test_helpers::sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, false, rng);
check_contains_unconfirmed_transaction_id(rejected_deploy);
let rejected_deploy =
test_helpers::sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), true, rng);
test_helpers::sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), true, false, rng);
check_contains_unconfirmed_transaction_id(rejected_deploy);
let rejected_deploy =
test_helpers::sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, rng);
test_helpers::sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, false, rng);
check_contains_unconfirmed_transaction_id(rejected_deploy);

// Ensure that the unconfirmed transaction ID of a rejected execute is not equivalent to its confirmed transaction ID.
let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), true, rng);
let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), true, false, rng);
check_contains_unconfirmed_transaction_id(rejected_execution);
let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), false, rng);
let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), false, false, rng);
check_contains_unconfirmed_transaction_id(rejected_execution);
}

Expand Down Expand Up @@ -594,22 +613,22 @@ mod test {

// Ensure that the unconfirmed transaction ID of a rejected deployment is not equivalent to its confirmed transaction ID.
let rejected_deploy =
test_helpers::sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), true, rng);
test_helpers::sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), true, false, rng);
assert_ne!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), rejected_deploy.id());
let rejected_deploy =
test_helpers::sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, rng);
test_helpers::sample_rejected_deploy(Uniform::rand(rng), 1, Uniform::rand(rng), false, false, rng);
assert_ne!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), rejected_deploy.id());
let rejected_deploy =
test_helpers::sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), true, rng);
test_helpers::sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), true, false, rng);
assert_ne!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), rejected_deploy.id());
let rejected_deploy =
test_helpers::sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, rng);
test_helpers::sample_rejected_deploy(Uniform::rand(rng), 2, Uniform::rand(rng), false, false, rng);
assert_ne!(rejected_deploy.to_unconfirmed_transaction_id().unwrap(), rejected_deploy.id());

// Ensure that the unconfirmed transaction ID of a rejected execute is not equivalent to its confirmed transaction ID.
let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), true, rng);
let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), true, false, rng);
assert_ne!(rejected_execution.to_unconfirmed_transaction_id().unwrap(), rejected_execution.id());
let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), false, rng);
let rejected_execution = test_helpers::sample_rejected_execute(Uniform::rand(rng), false, false, rng);
assert_ne!(rejected_execution.to_unconfirmed_transaction_id().unwrap(), rejected_execution.id());
}

Expand Down Expand Up @@ -643,6 +662,7 @@ mod test {
let rejected = Rejected::new_deployment(
*deployment_transaction.owner().unwrap(),
deployment_transaction.deployment().unwrap().clone(),
None,
);
let fee = Transaction::from_fee(deployment_transaction.fee_transition().unwrap()).unwrap();
let rejected_deploy = ConfirmedTransaction::rejected_deploy(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
Expand All @@ -654,6 +674,7 @@ mod test {
let rejected = Rejected::new_deployment(
*deployment_transaction.owner().unwrap(),
deployment_transaction.deployment().unwrap().clone(),
None,
);
let fee = Transaction::from_fee(deployment_transaction.fee_transition().unwrap()).unwrap();
let rejected_deploy = ConfirmedTransaction::rejected_deploy(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
Expand All @@ -665,6 +686,7 @@ mod test {
let rejected = Rejected::new_deployment(
*deployment_transaction.owner().unwrap(),
deployment_transaction.deployment().unwrap().clone(),
None,
);
let fee = Transaction::from_fee(deployment_transaction.fee_transition().unwrap()).unwrap();
let rejected_deploy = ConfirmedTransaction::rejected_deploy(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
Expand All @@ -676,6 +698,7 @@ mod test {
let rejected = Rejected::new_deployment(
*deployment_transaction.owner().unwrap(),
deployment_transaction.deployment().unwrap().clone(),
None,
);
let fee = Transaction::from_fee(deployment_transaction.fee_transition().unwrap()).unwrap();
let rejected_deploy = ConfirmedTransaction::rejected_deploy(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
Expand All @@ -685,7 +708,7 @@ mod test {
// Ensure that the unconfirmed transaction of a rejected execute is not equivalent to its confirmed transaction.
let execution_transaction =
crate::transaction::test_helpers::sample_execution_transaction_with_fee(true, rng, 0);
let rejected = Rejected::new_execution(execution_transaction.execution().unwrap().clone());
let rejected = Rejected::new_execution(execution_transaction.execution().unwrap().clone(), None);
let fee = Transaction::from_fee(execution_transaction.fee_transition().unwrap()).unwrap();
let rejected_execute =
ConfirmedTransaction::rejected_execute(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
Expand All @@ -694,7 +717,7 @@ mod test {

let execution_transaction =
crate::transaction::test_helpers::sample_execution_transaction_with_fee(false, rng, 0);
let rejected = Rejected::new_execution(execution_transaction.execution().unwrap().clone());
let rejected = Rejected::new_execution(execution_transaction.execution().unwrap().clone(), None);
let fee = Transaction::from_fee(execution_transaction.fee_transition().unwrap()).unwrap();
let rejected_execute =
ConfirmedTransaction::rejected_execute(Uniform::rand(rng), fee, rejected, vec![]).unwrap();
Expand Down
8 changes: 7 additions & 1 deletion ledger/block/src/transactions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ pub use confirmed::*;
pub mod rejected;
pub use rejected::*;

pub mod rejected_reason;
pub use rejected_reason::*;

mod bytes;
mod merkle;
mod serialize;
Expand All @@ -31,6 +34,8 @@ use console::{
Ciphertext,
FINALIZE_ID_DEPTH,
FINALIZE_OPERATIONS_DEPTH,
Identifier,
ProgramID,
Comment on lines +37 to +38
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These console::program imports (Identifier, ProgramID) appear unused in this module. Consider removing them to avoid unused-import warnings.

Suggested change
Identifier,
ProgramID,

Copilot uses AI. Check for mistakes.
ProgramOwner,
Record,
TRANSACTIONS_DEPTH,
Expand All @@ -41,7 +46,8 @@ use console::{
};
use snarkvm_ledger_committee::Committee;
use snarkvm_ledger_narwhal_batch_header::BatchHeader;
use snarkvm_synthesizer_program::FinalizeOperation;
use snarkvm_synthesizer_error::IndexedFinalizeError;
use snarkvm_synthesizer_program::{Command, FinalizeOperation};
Comment on lines +49 to +50
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These imports (IndexedFinalizeError, Command) appear unused in this module. Consider removing them to avoid unused-import warnings.

Suggested change
use snarkvm_synthesizer_error::IndexedFinalizeError;
use snarkvm_synthesizer_program::{Command, FinalizeOperation};
use snarkvm_synthesizer_program::FinalizeOperation;

Copilot uses AI. Check for mistakes.

use indexmap::IndexMap;

Expand Down
46 changes: 41 additions & 5 deletions ledger/block/src/transactions/rejected/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,33 @@ impl<N: Network> FromBytes for Rejected<N> {
// Read the deployment.
let deployment = Deployment::read_le(&mut reader)?;
// Return the rejected deployment.
Ok(Self::new_deployment(program_owner, deployment))
Ok(Self::new_deployment(program_owner, deployment, None))
}
1 => {
// Read the execution.
let execution = Execution::read_le(&mut reader)?;
// Return the rejected execution.
Ok(Self::new_execution(execution))
Ok(Self::new_execution(execution, None))
}
2.. => Err(error(format!("Failed to decode rejected transaction variant {variant}"))),
2 => {
// Read the program owner.
let program_owner = ProgramOwner::read_le(&mut reader)?;
// Read the deployment.
let deployment = Deployment::read_le(&mut reader)?;
// Read the rejected reason.
let rejected_reason = RejectedReason::read_le(&mut reader)?;
// Return the rejected deployment.
Ok(Self::new_deployment(program_owner, deployment, Some(rejected_reason)))
}
3 => {
// Read the execution.
let execution = Execution::read_le(&mut reader)?;
// Read the rejected reason.
let rejected_reason = RejectedReason::read_le(&mut reader)?;
// Return the rejected execution.
Ok(Self::new_execution(execution, Some(rejected_reason)))
}
4.. => Err(error(format!("Failed to decode rejected transaction variant {variant}"))),
}
}
}
Expand All @@ -43,20 +61,38 @@ impl<N: Network> ToBytes for Rejected<N> {
/// Writes the rejected transaction to a buffer.
fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
match self {
Self::Deployment(program_owner, deployment) => {
Self::Deployment(program_owner, deployment, None) => {
// Write the variant.
0u8.write_le(&mut writer)?;
// Write the program owner.
program_owner.write_le(&mut writer)?;
// Write the deployment.
deployment.write_le(&mut writer)
}
Self::Execution(execution) => {
Self::Execution(execution, None) => {
// Write the variant.
1u8.write_le(&mut writer)?;
// Write the execution.
execution.write_le(&mut writer)
}
Self::Deployment(program_owner, deployment, Some(rejected_reason)) => {
// Write the variant.
2u8.write_le(&mut writer)?;
// Write the program owner.
program_owner.write_le(&mut writer)?;
// Write the deployment.
deployment.write_le(&mut writer)?;
// Write the rejected reason.
rejected_reason.write_le(&mut writer)
}
Self::Execution(execution, Some(rejected_reason)) => {
// Write the variant.
3u8.write_le(&mut writer)?;
// Write the execution.
execution.write_le(&mut writer)?;
// Write the rejected reason.
rejected_reason.write_le(&mut writer)
}
}
}
}
Expand Down
Loading
Loading