sumchain_primitives/
receipt.rs1use serde::{Deserialize, Serialize};
7
8use crate::{Balance, BlockHeight, Hash};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12pub enum TxStatus {
13 Success,
15 InvalidSignature,
17 InvalidNonce,
19 InsufficientBalance,
21 InvalidChainId,
23 Failed(u32),
25}
26
27impl TxStatus {
28 pub fn is_success(&self) -> bool {
30 matches!(self, TxStatus::Success)
31 }
32
33 pub fn description(&self) -> &'static str {
69 match self {
70 TxStatus::Success => "success",
71 TxStatus::InvalidSignature => "invalid signature",
72 TxStatus::InvalidNonce => "invalid nonce",
73 TxStatus::InsufficientBalance => "insufficient balance",
74 TxStatus::InvalidChainId => "invalid chain id",
75 TxStatus::Failed(22) => "low-order x25519 public key rejected",
76 TxStatus::Failed(30) => "RegisterFilePendingV2 validity check failed",
77 TxStatus::Failed(31) => "AbandonFileV2 validity check failed",
78 TxStatus::Failed(32) => "V2 storage op not yet implemented",
79 TxStatus::Failed(33) => "AcceptAssignmentV2 validity check failed",
80 TxStatus::Failed(34) => "ActivateFileV2 validity check failed",
81 TxStatus::Failed(35) => "V2 access op validity check failed",
82 TxStatus::Failed(40) => "V2 storage protocol not enabled at this height",
83 TxStatus::Failed(50) => "OmniNode subprotocol not enabled at this block height",
85 TxStatus::Failed(51) => "duplicate InferenceAttestation for (session_id, verifier)",
86 TxStatus::Failed(52) => "invalid OmniNode Stage 6 verifier signature",
87 TxStatus::Failed(53) => "tx sender does not match verifier address (Ed25519 pubkey hash)",
88 TxStatus::Failed(70) => "education subprotocol not enabled at this block height",
90 TxStatus::Failed(71) => "malformed education payload",
91 TxStatus::Failed(72) => "unsupported education operation",
92 TxStatus::Failed(73) => "catalog entry not found",
93 TxStatus::Failed(74) => "catalog entry in wrong state for operation",
94 TxStatus::Failed(75) => "offering not found",
95 TxStatus::Failed(76) => "offering in wrong state for operation",
96 TxStatus::Failed(77) => "assessment not found or wrong kind",
97 TxStatus::Failed(78) => "assessment submission window closed",
98 TxStatus::Failed(79) => "student commitment not enrolled in offering",
99 TxStatus::Failed(80) => "submission attempts exhausted",
100 TxStatus::Failed(81) => "duplicate education record",
101 TxStatus::Failed(82) => "invalid reference (enrollment/employment/catalog)",
102 TxStatus::Failed(83) => "not authorized for education operation",
103 TxStatus::Failed(84) => "insufficient balance for education fee",
104 TxStatus::Failed(_) => "failed",
105 }
106 }
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
111pub struct Receipt {
112 pub tx_hash: Hash,
114 pub block_height: BlockHeight,
116 pub tx_index: u32,
118 pub status: TxStatus,
120 pub fee_paid: Balance,
122}
123
124impl Receipt {
125 pub fn new(
127 tx_hash: Hash,
128 block_height: BlockHeight,
129 tx_index: u32,
130 status: TxStatus,
131 fee_paid: Balance,
132 ) -> Self {
133 Self {
134 tx_hash,
135 block_height,
136 tx_index,
137 status,
138 fee_paid,
139 }
140 }
141
142 pub fn success(
144 tx_hash: Hash,
145 block_height: BlockHeight,
146 tx_index: u32,
147 fee_paid: Balance,
148 ) -> Self {
149 Self::new(tx_hash, block_height, tx_index, TxStatus::Success, fee_paid)
150 }
151
152 pub fn is_success(&self) -> bool {
154 self.status.is_success()
155 }
156
157 pub fn to_bytes(&self) -> Vec<u8> {
159 bincode::serialize(self).expect("Receipt serialization should not fail")
160 }
161
162 pub fn from_bytes(bytes: &[u8]) -> Result<Self, bincode::Error> {
164 bincode::deserialize(bytes)
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn test_status_is_success() {
174 assert!(TxStatus::Success.is_success());
175 assert!(!TxStatus::InvalidNonce.is_success());
176 assert!(!TxStatus::InsufficientBalance.is_success());
177 }
178
179 #[test]
180 fn test_receipt_serialization() {
181 let receipt = Receipt::success(Hash::hash(b"tx"), 100, 0, 10);
182 let bytes = receipt.to_bytes();
183 let receipt2 = Receipt::from_bytes(&bytes).unwrap();
184 assert_eq!(receipt, receipt2);
185 }
186}