Skip to main content

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}