miden_client/rpc/domain/
transaction.rs1use alloc::string::ToString;
2use alloc::vec::Vec;
3
4use miden_protocol::Word;
5use miden_protocol::account::AccountId;
6use miden_protocol::asset::FungibleAsset;
7use miden_protocol::block::BlockNumber;
8use miden_protocol::note::{NoteHeader, Nullifier};
9use miden_protocol::transaction::{
10 InputNoteCommitment,
11 InputNotes,
12 TransactionHeader,
13 TransactionId,
14};
15
16use crate::rpc::{RpcConversionError, RpcError, generated as proto};
17
18pub const ACCOUNT_ID_NATIVE_ASSET_FAUCET: u128 = 0xab00_0000_0000_cd20_0000_ac00_0000_de00_u128;
22
23impl TryFrom<proto::primitives::Digest> for TransactionId {
27 type Error = RpcConversionError;
28
29 fn try_from(value: proto::primitives::Digest) -> Result<Self, Self::Error> {
30 let word: Word = value.try_into()?;
31 Ok(Self::from_raw(word))
32 }
33}
34
35impl TryFrom<proto::transaction::TransactionId> for TransactionId {
36 type Error = RpcConversionError;
37
38 fn try_from(value: proto::transaction::TransactionId) -> Result<Self, Self::Error> {
39 value
40 .id
41 .ok_or(RpcConversionError::MissingFieldInProtobufRepresentation {
42 entity: "TransactionId",
43 field_name: "id",
44 })?
45 .try_into()
46 }
47}
48
49impl From<TransactionId> for proto::transaction::TransactionId {
50 fn from(value: TransactionId) -> Self {
51 Self { id: Some(value.as_word().into()) }
52 }
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct TransactionInclusion {
61 pub transaction_id: TransactionId,
63 pub block_num: BlockNumber,
65 pub account_id: AccountId,
67 pub initial_state_commitment: Word,
69}
70
71#[derive(Debug, Clone, PartialEq, Eq)]
76pub struct TransactionsInfo {
77 pub chain_tip: BlockNumber,
79 pub block_num: BlockNumber,
81 pub transaction_records: Vec<TransactionRecord>,
83}
84
85impl TryFrom<proto::rpc::SyncTransactionsResponse> for TransactionsInfo {
86 type Error = RpcError;
87
88 fn try_from(value: proto::rpc::SyncTransactionsResponse) -> Result<Self, Self::Error> {
89 let pagination_info = value.pagination_info.ok_or(
90 RpcConversionError::MissingFieldInProtobufRepresentation {
91 entity: "SyncTransactionsResponse",
92 field_name: "pagination_info",
93 },
94 )?;
95
96 let chain_tip = pagination_info.chain_tip.into();
97 let block_num = pagination_info.block_num.into();
98
99 let transaction_records = value
100 .transactions
101 .into_iter()
102 .map(TryInto::try_into)
103 .collect::<Result<Vec<TransactionRecord>, RpcError>>()?;
104
105 Ok(Self {
106 chain_tip,
107 block_num,
108 transaction_records,
109 })
110 }
111}
112
113#[derive(Debug, Clone, PartialEq, Eq)]
119pub struct TransactionRecord {
120 pub block_num: BlockNumber,
122 pub transaction_header: TransactionHeader,
124}
125
126impl TryFrom<proto::rpc::TransactionRecord> for TransactionRecord {
127 type Error = RpcError;
128
129 fn try_from(value: proto::rpc::TransactionRecord) -> Result<Self, Self::Error> {
130 let block_num = value.block_num.into();
131 let transaction_header =
132 value.header.ok_or(RpcConversionError::MissingFieldInProtobufRepresentation {
133 entity: "TransactionRecord",
134 field_name: "transaction_header",
135 })?;
136
137 Ok(Self {
138 block_num,
139 transaction_header: transaction_header.try_into()?,
140 })
141 }
142}
143
144impl TryFrom<proto::transaction::TransactionHeader> for TransactionHeader {
145 type Error = RpcError;
146
147 fn try_from(value: proto::transaction::TransactionHeader) -> Result<Self, Self::Error> {
148 let account_id =
149 value
150 .account_id
151 .ok_or(RpcConversionError::MissingFieldInProtobufRepresentation {
152 entity: "TransactionHeader",
153 field_name: "account_id",
154 })?;
155
156 let initial_state_commitment = value.initial_state_commitment.ok_or(
157 RpcConversionError::MissingFieldInProtobufRepresentation {
158 entity: "TransactionHeader",
159 field_name: "initial_state_commitment",
160 },
161 )?;
162
163 let final_state_commitment = value.final_state_commitment.ok_or(
164 RpcConversionError::MissingFieldInProtobufRepresentation {
165 entity: "TransactionHeader",
166 field_name: "final_state_commitment",
167 },
168 )?;
169
170 let note_commitments = value
171 .input_notes
172 .into_iter()
173 .map(|input_note| {
174 let nullifier_digest = input_note.nullifier.ok_or(
175 RpcConversionError::MissingFieldInProtobufRepresentation {
176 entity: "InputNoteCommitment",
177 field_name: "nullifier",
178 },
179 )?;
180 let word: Word = nullifier_digest
181 .try_into()
182 .map_err(|e: RpcConversionError| RpcError::InvalidResponse(e.to_string()))?;
183 Ok(InputNoteCommitment::from(Nullifier::from_raw(word)))
184 })
185 .collect::<Result<Vec<_>, RpcError>>()?;
186 let input_notes = InputNotes::new_unchecked(note_commitments);
187
188 let output_notes = value
189 .output_notes
190 .into_iter()
191 .map(NoteHeader::try_from)
192 .collect::<Result<Vec<NoteHeader>, RpcError>>()?;
193
194 let transaction_header = TransactionHeader::new(
195 account_id.try_into()?,
196 initial_state_commitment.try_into()?,
197 final_state_commitment.try_into()?,
198 input_notes,
199 output_notes,
200 FungibleAsset::new(ACCOUNT_ID_NATIVE_ASSET_FAUCET.try_into().expect("is valid"), 0u64)
202 .unwrap(),
203 );
204 Ok(transaction_header)
205 }
206}
207
208impl TryFrom<proto::note::NoteHeader> for NoteHeader {
209 type Error = RpcError;
210
211 fn try_from(value: proto::note::NoteHeader) -> Result<Self, Self::Error> {
212 let note_id = value
213 .note_id
214 .ok_or(RpcConversionError::MissingFieldInProtobufRepresentation {
215 entity: "NoteHeader",
216 field_name: "note_id",
217 })?
218 .try_into()?;
219
220 let note_metadata = value
221 .metadata
222 .ok_or(RpcConversionError::MissingFieldInProtobufRepresentation {
223 entity: "NoteHeader",
224 field_name: "metadata",
225 })?
226 .try_into()?;
227
228 Ok(Self::new(note_id, note_metadata))
229 }
230}
231
232impl TryFrom<proto::note::NoteSyncRecord> for NoteHeader {
233 type Error = RpcError;
234
235 fn try_from(value: proto::note::NoteSyncRecord) -> Result<Self, Self::Error> {
236 let note_id = value
237 .note_id
238 .ok_or(RpcConversionError::MissingFieldInProtobufRepresentation {
239 entity: "NoteSyncRecord",
240 field_name: "note_id",
241 })?
242 .try_into()?;
243
244 let note_metadata = value
245 .metadata
246 .ok_or(RpcConversionError::MissingFieldInProtobufRepresentation {
247 entity: "NoteSyncRecord",
248 field_name: "metadata",
249 })?
250 .try_into()?;
251
252 let note_header = Self::new(note_id, note_metadata);
253 Ok(note_header)
254 }
255}