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}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
74pub struct TransactionsInfo {
75 pub chain_tip: BlockNumber,
77 pub block_num: BlockNumber,
79 pub transaction_records: Vec<TransactionRecord>,
81}
82
83impl TryFrom<proto::rpc::SyncTransactionsResponse> for TransactionsInfo {
84 type Error = RpcError;
85
86 fn try_from(value: proto::rpc::SyncTransactionsResponse) -> Result<Self, Self::Error> {
87 let pagination_info = value.pagination_info.ok_or(
88 RpcConversionError::MissingFieldInProtobufRepresentation {
89 entity: "SyncTransactionsResponse",
90 field_name: "pagination_info",
91 },
92 )?;
93
94 let chain_tip = pagination_info.chain_tip.into();
95 let block_num = pagination_info.block_num.into();
96
97 let transaction_records = value
98 .transactions
99 .into_iter()
100 .map(TryInto::try_into)
101 .collect::<Result<Vec<TransactionRecord>, RpcError>>()?;
102
103 Ok(Self {
104 chain_tip,
105 block_num,
106 transaction_records,
107 })
108 }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq)]
117pub struct TransactionRecord {
118 pub block_num: BlockNumber,
120 pub transaction_header: TransactionHeader,
122}
123
124impl TryFrom<proto::rpc::TransactionRecord> for TransactionRecord {
125 type Error = RpcError;
126
127 fn try_from(value: proto::rpc::TransactionRecord) -> Result<Self, Self::Error> {
128 let block_num = value.block_num.into();
129 let transaction_header =
130 value.header.ok_or(RpcConversionError::MissingFieldInProtobufRepresentation {
131 entity: "TransactionRecord",
132 field_name: "transaction_header",
133 })?;
134
135 Ok(Self {
136 block_num,
137 transaction_header: transaction_header.try_into()?,
138 })
139 }
140}
141
142impl TryFrom<proto::transaction::TransactionHeader> for TransactionHeader {
143 type Error = RpcError;
144
145 fn try_from(value: proto::transaction::TransactionHeader) -> Result<Self, Self::Error> {
146 let account_id =
147 value
148 .account_id
149 .ok_or(RpcConversionError::MissingFieldInProtobufRepresentation {
150 entity: "TransactionHeader",
151 field_name: "account_id",
152 })?;
153
154 let initial_state_commitment = value.initial_state_commitment.ok_or(
155 RpcConversionError::MissingFieldInProtobufRepresentation {
156 entity: "TransactionHeader",
157 field_name: "initial_state_commitment",
158 },
159 )?;
160
161 let final_state_commitment = value.final_state_commitment.ok_or(
162 RpcConversionError::MissingFieldInProtobufRepresentation {
163 entity: "TransactionHeader",
164 field_name: "final_state_commitment",
165 },
166 )?;
167
168 let note_commitments = value
169 .nullifiers
170 .into_iter()
171 .map(|d| {
172 Nullifier::from_hex(&d.to_string())
173 .map(InputNoteCommitment::from)
174 .map_err(|e| RpcError::InvalidResponse(e.to_string()))
175 })
176 .collect::<Result<Vec<_>, _>>()?;
177 let input_notes = InputNotes::new_unchecked(note_commitments);
178
179 let output_notes = value
180 .output_notes
181 .into_iter()
182 .map(TryInto::try_into)
183 .collect::<Result<Vec<NoteHeader>, RpcError>>()?;
184
185 let transaction_header = TransactionHeader::new(
186 account_id.try_into()?,
187 initial_state_commitment.try_into()?,
188 final_state_commitment.try_into()?,
189 input_notes,
190 output_notes,
191 FungibleAsset::new(ACCOUNT_ID_NATIVE_ASSET_FAUCET.try_into().expect("is valid"), 0u64)
193 .unwrap(),
194 );
195 Ok(transaction_header)
196 }
197}
198
199impl TryFrom<proto::note::NoteSyncRecord> for NoteHeader {
200 type Error = RpcError;
201
202 fn try_from(value: proto::note::NoteSyncRecord) -> Result<Self, Self::Error> {
203 let note_id = value
204 .note_id
205 .ok_or(RpcConversionError::MissingFieldInProtobufRepresentation {
206 entity: "NoteSyncRecord",
207 field_name: "note_id",
208 })?
209 .try_into()?;
210
211 let note_metadata = value
212 .metadata
213 .ok_or(RpcConversionError::MissingFieldInProtobufRepresentation {
214 entity: "NoteSyncRecord",
215 field_name: "metadata",
216 })?
217 .try_into()?;
218
219 let note_header = Self::new(note_id, note_metadata);
220 Ok(note_header)
221 }
222}