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 .nullifiers
172 .into_iter()
173 .map(|d| {
174 let word: Word = d
175 .try_into()
176 .map_err(|e: RpcConversionError| RpcError::InvalidResponse(e.to_string()))?;
177 Ok(InputNoteCommitment::from(Nullifier::from_raw(word)))
178 })
179 .collect::<Result<Vec<_>, RpcError>>()?;
180 let input_notes = InputNotes::new_unchecked(note_commitments);
181
182 let output_notes = value
183 .output_notes
184 .into_iter()
185 .map(TryInto::try_into)
186 .collect::<Result<Vec<NoteHeader>, RpcError>>()?;
187
188 let transaction_header = TransactionHeader::new(
189 account_id.try_into()?,
190 initial_state_commitment.try_into()?,
191 final_state_commitment.try_into()?,
192 input_notes,
193 output_notes,
194 FungibleAsset::new(ACCOUNT_ID_NATIVE_ASSET_FAUCET.try_into().expect("is valid"), 0u64)
196 .unwrap(),
197 );
198 Ok(transaction_header)
199 }
200}
201
202impl TryFrom<proto::note::NoteSyncRecord> for NoteHeader {
203 type Error = RpcError;
204
205 fn try_from(value: proto::note::NoteSyncRecord) -> Result<Self, Self::Error> {
206 let note_id = value
207 .note_id
208 .ok_or(RpcConversionError::MissingFieldInProtobufRepresentation {
209 entity: "NoteSyncRecord",
210 field_name: "note_id",
211 })?
212 .try_into()?;
213
214 let note_metadata = value
215 .metadata
216 .ok_or(RpcConversionError::MissingFieldInProtobufRepresentation {
217 entity: "NoteSyncRecord",
218 field_name: "metadata",
219 })?
220 .try_into()?;
221
222 let note_header = Self::new(note_id, note_metadata);
223 Ok(note_header)
224 }
225}