1#[cfg(feature = "serde")]
6use std::str::FromStr;
7
8use crate::TreeDisplay;
9
10pub const INTENT_PREFIX_LENGTH: usize = 3;
11
12#[derive(Debug, thiserror::Error)]
13#[non_exhaustive]
14pub enum IntentError {
15 #[error("invalid bytes for Intent")]
16 Bytes,
17 #[error("invalid hex string for Intent")]
18 Hex,
19 #[error("invalid Scope for Intent")]
20 Scope,
21 #[error("invalid Version for Intent")]
22 Version,
23 #[error("invalid AppId for Intent")]
24 AppId,
25}
26
27#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
47#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
48#[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))]
49pub struct Intent {
50 pub scope: IntentScope,
51 pub version: IntentVersion,
52 pub app_id: IntentAppId,
53}
54
55impl Intent {
56 pub fn new(scope: IntentScope, version: IntentVersion, app_id: IntentAppId) -> Self {
57 Self {
58 scope,
59 version,
60 app_id,
61 }
62 }
63
64 pub fn scope(self) -> IntentScope {
65 self.scope
66 }
67
68 pub fn version(self) -> IntentVersion {
69 self.version
70 }
71
72 pub fn app_id(self) -> IntentAppId {
73 self.app_id
74 }
75
76 pub fn iota_app(scope: IntentScope) -> Self {
77 Self {
78 scope,
79 version: IntentVersion::V0,
80 app_id: IntentAppId::Iota,
81 }
82 }
83
84 pub const fn iota_transaction() -> Self {
85 Self {
86 scope: IntentScope::TransactionData,
87 version: IntentVersion::V0,
88 app_id: IntentAppId::Iota,
89 }
90 }
91
92 pub const fn personal_message() -> Self {
93 Self {
94 scope: IntentScope::PersonalMessage,
95 version: IntentVersion::V0,
96 app_id: IntentAppId::Iota,
97 }
98 }
99
100 pub const fn consensus_app(scope: IntentScope) -> Self {
101 Self {
102 scope,
103 version: IntentVersion::V0,
104 app_id: IntentAppId::Consensus,
105 }
106 }
107
108 pub fn to_bytes(self) -> [u8; INTENT_PREFIX_LENGTH] {
109 [self.scope as u8, self.version as u8, self.app_id as u8]
110 }
111
112 #[cfg(feature = "serde")]
113 pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, IntentError> {
114 let bytes = bytes.as_ref();
115 if bytes.len() != INTENT_PREFIX_LENGTH {
116 return Err(IntentError::Bytes);
117 }
118 Ok(Self {
119 scope: bytes[0].try_into()?,
120 version: bytes[1].try_into()?,
121 app_id: bytes[2].try_into()?,
122 })
123 }
124}
125
126impl crate::TreeDisplay for Intent {
127 fn fmt_tree(&self, w: &mut crate::TreeWriter<'_, '_>) -> std::fmt::Result {
128 w.header("Intent")?;
129 w.leaf("Scope", &self.scope, false)?;
130 w.leaf("Version", &self.version, false)?;
131 w.leaf("App ID", &self.app_id, true)
132 }
133}
134
135crate::impl_tree_display!(Intent);
136
137#[cfg(feature = "serde")]
138impl FromStr for Intent {
139 type Err = IntentError;
140
141 fn from_str(s: &str) -> Result<Self, Self::Err> {
142 let bytes: Vec<u8> =
143 hex::decode(s.strip_prefix("0x").unwrap_or(s)).map_err(|_| IntentError::Hex)?;
144 Self::from_bytes(bytes.as_slice())
145 }
146}
147
148#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, strum::Display)]
162#[cfg_attr(
163 feature = "serde",
164 derive(serde_repr::Deserialize_repr, serde_repr::Serialize_repr)
165)]
166#[repr(u8)]
167#[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))]
168#[non_exhaustive]
169pub enum IntentScope {
170 TransactionData = 0, TransactionEffects = 1, CheckpointSummary = 2, PersonalMessage = 3, SenderSignedTransaction = 4, ProofOfPossession = 5, BridgeEventDeprecated = 6, ConsensusBlock = 7, DiscoveryPeers = 8, AuthorityCapabilities = 9, }
183
184impl IntentScope {
185 crate::def_is!(
186 TransactionData,
187 TransactionEffects,
188 CheckpointSummary,
189 PersonalMessage,
190 SenderSignedTransaction,
191 ProofOfPossession,
192 BridgeEventDeprecated,
193 ConsensusBlock,
194 DiscoveryPeers,
195 AuthorityCapabilities,
196 );
197}
198
199#[cfg(feature = "serde")]
200impl TryFrom<u8> for IntentScope {
201 type Error = IntentError;
202
203 fn try_from(value: u8) -> Result<Self, Self::Error> {
204 bcs::from_bytes(&[value]).map_err(|_| IntentError::Scope)
205 }
206}
207
208#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, strum::Display)]
222#[cfg_attr(
223 feature = "serde",
224 derive(serde_repr::Deserialize_repr, serde_repr::Serialize_repr)
225)]
226#[repr(u8)]
227#[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))]
228#[non_exhaustive]
229pub enum IntentVersion {
230 V0 = 0,
231}
232
233impl IntentVersion {
234 crate::def_is!(V0);
235}
236
237#[cfg(feature = "serde")]
238impl TryFrom<u8> for IntentVersion {
239 type Error = IntentError;
240
241 fn try_from(value: u8) -> Result<Self, Self::Error> {
242 bcs::from_bytes(&[value]).map_err(|_| IntentError::Version)
243 }
244}
245
246#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, strum::Display)]
262#[cfg_attr(
263 feature = "serde",
264 derive(serde_repr::Deserialize_repr, serde_repr::Serialize_repr)
265)]
266#[repr(u8)]
267#[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))]
268#[non_exhaustive]
269pub enum IntentAppId {
270 Iota = 0,
271 Consensus = 1,
272}
273
274impl IntentAppId {
275 crate::def_is!(Iota, Consensus);
276}
277
278#[cfg(feature = "serde")]
279impl TryFrom<u8> for IntentAppId {
280 type Error = IntentError;
281
282 fn try_from(value: u8) -> Result<Self, Self::Error> {
283 bcs::from_bytes(&[value]).map_err(|_| IntentError::AppId)
284 }
285}
286
287#[derive(Clone, Debug, Eq, Hash, PartialEq)]
296#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
297pub struct IntentMessage<T> {
298 pub intent: Intent,
299 pub value: T,
300}
301
302impl<T> IntentMessage<T> {
303 pub fn new(intent: Intent, value: T) -> Self {
304 Self { intent, value }
305 }
306}
307
308impl<T: std::fmt::Display> crate::TreeDisplay for IntentMessage<T> {
309 fn fmt_tree(&self, w: &mut crate::TreeWriter<'_, '_>) -> std::fmt::Result {
310 w.header("Intent Message")?;
311 w.child("Intent", &self.intent, false)?;
312 w.leaf("Value", &self.value, true)
313 }
314}
315
316impl<T: std::fmt::Display> std::fmt::Display for IntentMessage<T> {
317 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
318 let mut w = crate::TreeWriter::new(f);
319 self.fmt_tree(&mut w)
320 }
321}
322
323#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, strum::Display)]
328#[cfg_attr(
329 feature = "serde",
330 derive(serde_repr::Deserialize_repr, serde_repr::Serialize_repr)
331)]
332#[repr(u8)]
333#[non_exhaustive]
334pub enum HashingIntentScope {
335 ChildObjectId = 0xf0,
336 RegularObjectId = 0xf1,
337}
338
339#[derive(Clone, Debug, Eq, PartialEq)]
341#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
342pub struct PersonalMessage<'a>(pub std::borrow::Cow<'a, [u8]>);
343
344impl std::fmt::Display for PersonalMessage<'_> {
345 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
346 write!(f, "PersonalMessage({})", hex::encode(&self.0))
347 }
348}