snarkvm_ledger_block/transactions/rejected_reason/mod.rs
1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16mod bytes;
17mod serialize;
18mod string;
19
20use super::*;
21
22/// The reason a transaction was rejected.
23#[derive(Clone, PartialEq, Eq)]
24pub enum RejectedReason<N: Network> {
25 /// The transaction was rejected due to a duplicate program ID deployment in the same block.
26 DuplicateProgramID(ProgramID<N>),
27
28 /// The transaction was rejected due to a failed finalize command. (program ID, edition, resource, index, command).
29 /// Note: We do not log the actual error message from the finalize command, as it may contain
30 /// sensitive information or lead to DOS vectors by storing string representations of large structs.
31 Finalize { program_id: ProgramID<N>, edition: u16, resource: Identifier<N>, index: usize, command: Box<Command<N>> },
32
33 /// The transaction was rejected due to a VM error not captured by a finalize command.
34 /// The programID and resource are logged if they are available.
35 VM(Option<(ProgramID<N>, u16)>, Option<Identifier<N>>),
36}
37
38impl<N: Network> RejectedReason<N> {
39 /// Initializes the rejected reason from an indexed finalize error.
40 ///
41 /// `C` may be any type whose `Display` output is a valid `Command<N>` string (e.g. `Command<N>`
42 /// itself or `String`). If the command string cannot be re-parsed, the reason falls back to
43 /// `VM` so that a bad string never causes a panic in consensus code.
44 pub fn from_indexed_finalize_error<C: ToString>(error: IndexedFinalizeError<N, C>) -> Self {
45 let program_id = error.program_id;
46 let resource = error.resource;
47 match error.command.map(|b| *b) {
48 Some((index, command)) => {
49 // Parse the command from its display string. Falls back to VM on failure.
50 match (program_id, resource, command.to_string().parse::<Command<N>>()) {
51 (Some((program_id, edition)), Some(resource), Ok(command)) => {
52 Self::Finalize { program_id, edition, resource, index, command: Box::new(command) }
53 }
54 (program_id, resource, _) => Self::VM(program_id, resource),
55 }
56 }
57 None => Self::VM(program_id, resource),
58 }
59 }
60}
61
62#[cfg(test)]
63pub mod test_helpers {
64 use super::*;
65 use std::str::FromStr;
66
67 /// Returns one instance of each `RejectedReason` variant for testing.
68 pub(crate) fn sample_rejected_reasons<N: Network>() -> Vec<RejectedReason<N>> {
69 let program = ProgramID::<N>::from_str("dummy_program.aleo").unwrap();
70 let credits = ProgramID::<N>::from_str("credits.aleo").unwrap();
71 let transfer = Identifier::<N>::from_str("transfer_public").unwrap();
72 let bond = Identifier::<N>::from_str("bond_public").unwrap();
73 let command = Command::<N>::from_str("assert.eq r0 r1;").unwrap();
74 vec![
75 RejectedReason::DuplicateProgramID(program),
76 RejectedReason::Finalize {
77 program_id: credits,
78 edition: 1,
79 resource: transfer,
80 index: 3,
81 command: Box::new(command),
82 },
83 RejectedReason::VM(Some((credits, 0u16)), Some(bond)),
84 RejectedReason::VM(None, Some(bond)),
85 RejectedReason::VM(Some((credits, 0u16)), None),
86 RejectedReason::VM(None, None),
87 ]
88 }
89}