1use eyre::eyre;
5use fastcrypto::encoding::decode_bytes_hex;
6use serde::{Deserialize, Serialize};
7use serde_repr::Deserialize_repr;
8use serde_repr::Serialize_repr;
9use std::str::FromStr;
10
11pub const INTENT_PREFIX_LENGTH: usize = 3;
12
13#[derive(Serialize_repr, Deserialize_repr, Copy, Clone, PartialEq, Eq, Debug, Hash)]
17#[repr(u8)]
18pub enum IntentVersion {
19 V0 = 0,
20}
21
22impl TryFrom<u8> for IntentVersion {
23 type Error = eyre::Report;
24 fn try_from(value: u8) -> Result<Self, Self::Error> {
25 bcs::from_bytes(&[value]).map_err(|_| eyre!("Invalid IntentVersion"))
26 }
27}
28
29#[derive(Serialize_repr, Deserialize_repr, Copy, Clone, Default, PartialEq, Eq, Debug, Hash)]
34#[repr(u8)]
35pub enum AppId {
36 #[default]
37 Sui = 0,
38 Narwhal = 1,
39 Consensus = 2,
40}
41
42impl TryFrom<u8> for AppId {
44 type Error = eyre::Report;
45 fn try_from(value: u8) -> Result<Self, Self::Error> {
46 bcs::from_bytes(&[value]).map_err(|_| eyre!("Invalid AppId"))
47 }
48}
49
50#[derive(Serialize_repr, Deserialize_repr, Copy, Clone, PartialEq, Eq, Debug, Hash)]
54#[repr(u8)]
55pub enum IntentScope {
56 TransactionData = 0, TransactionEffects = 1, CheckpointSummary = 2, PersonalMessage = 3, SenderSignedTransaction = 4, ProofOfPossession = 5, HeaderDigest = 6, BridgeEventUnused = 7, ConsensusBlock = 8, DiscoveryPeers = 9, }
67
68impl TryFrom<u8> for IntentScope {
69 type Error = eyre::Report;
70 fn try_from(value: u8) -> Result<Self, Self::Error> {
71 bcs::from_bytes(&[value]).map_err(|_| eyre!("Invalid IntentScope"))
72 }
73}
74
75#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Hash)]
81pub struct Intent {
82 pub scope: IntentScope,
83 pub version: IntentVersion,
84 pub app_id: AppId,
85}
86
87impl Intent {
88 pub fn to_bytes(&self) -> [u8; INTENT_PREFIX_LENGTH] {
89 [self.scope as u8, self.version as u8, self.app_id as u8]
90 }
91
92 pub fn from_bytes(bytes: &[u8]) -> Result<Self, eyre::Report> {
93 if bytes.len() != INTENT_PREFIX_LENGTH {
94 return Err(eyre!("Invalid Intent"));
95 }
96 Ok(Self {
97 scope: bytes[0].try_into()?,
98 version: bytes[1].try_into()?,
99 app_id: bytes[2].try_into()?,
100 })
101 }
102}
103
104impl FromStr for Intent {
105 type Err = eyre::Report;
106 fn from_str(s: &str) -> Result<Self, Self::Err> {
107 let bytes: Vec<u8> = decode_bytes_hex(s).map_err(|_| eyre!("Invalid Intent"))?;
108 Self::from_bytes(bytes.as_slice())
109 }
110}
111
112impl Intent {
113 pub fn sui_app(scope: IntentScope) -> Self {
114 Self {
115 version: IntentVersion::V0,
116 scope,
117 app_id: AppId::Sui,
118 }
119 }
120
121 pub fn sui_transaction() -> Self {
122 Self {
123 scope: IntentScope::TransactionData,
124 version: IntentVersion::V0,
125 app_id: AppId::Sui,
126 }
127 }
128
129 pub fn personal_message() -> Self {
130 Self {
131 scope: IntentScope::PersonalMessage,
132 version: IntentVersion::V0,
133 app_id: AppId::Sui,
134 }
135 }
136
137 pub fn narwhal_app(scope: IntentScope) -> Self {
138 Self {
139 scope,
140 version: IntentVersion::V0,
141 app_id: AppId::Narwhal,
142 }
143 }
144
145 pub fn consensus_app(scope: IntentScope) -> Self {
146 Self {
147 scope,
148 version: IntentVersion::V0,
149 app_id: AppId::Consensus,
150 }
151 }
152}
153
154#[derive(Debug, PartialEq, Eq, Serialize, Clone, Hash, Deserialize)]
163pub struct IntentMessage<T> {
164 pub intent: Intent,
165 pub value: T,
166}
167
168impl<T> IntentMessage<T> {
169 pub fn new(intent: Intent, value: T) -> Self {
170 Self { intent, value }
171 }
172}
173
174#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
176pub struct PersonalMessage {
177 pub message: Vec<u8>,
178}
179
180pub trait SecureIntent: Serialize + private::SealedIntent {}
181
182pub(crate) mod private {
183 use super::IntentMessage;
184
185 pub trait SealedIntent {}
186 impl<T> SealedIntent for IntentMessage<T> {}
187}
188
189#[derive(Serialize_repr, Deserialize_repr, Copy, Clone, PartialEq, Eq, Debug, Hash)]
193#[repr(u8)]
194pub enum HashingIntentScope {
195 ChildObjectId = 0xf0,
196 RegularObjectId = 0xf1,
197}