freenet_stdlib/contract_interface/
key.rs1use std::borrow::Borrow;
8use std::fmt::Display;
9use std::hash::{Hash, Hasher};
10use std::ops::Deref;
11use std::str::FromStr;
12
13use blake3::{traits::digest::Digest, Hasher as Blake3};
14use serde::{Deserialize, Serialize};
15use serde_with::serde_as;
16
17use crate::client_api::{TryFromFbs, WsApiError};
18use crate::code_hash::CodeHash;
19use crate::common_generated::common::ContractKey as FbsContractKey;
20use crate::parameters::Parameters;
21
22use super::code::ContractCode;
23use super::CONTRACT_KEY_SIZE;
24
25#[serde_as]
27#[derive(PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Hash)]
28#[cfg_attr(
29 any(feature = "testing", all(test, any(unix, windows))),
30 derive(arbitrary::Arbitrary)
31)]
32#[repr(transparent)]
33pub struct ContractInstanceId(#[serde_as(as = "[_; CONTRACT_KEY_SIZE]")] [u8; CONTRACT_KEY_SIZE]);
34
35impl ContractInstanceId {
36 pub fn from_params_and_code<'a>(
37 params: impl Borrow<Parameters<'a>>,
38 code: impl Borrow<ContractCode<'a>>,
39 ) -> Self {
40 generate_id(params.borrow(), code.borrow())
41 }
42
43 pub const fn new(key: [u8; CONTRACT_KEY_SIZE]) -> Self {
44 Self(key)
45 }
46
47 pub fn encode(&self) -> String {
49 bs58::encode(self.0)
50 .with_alphabet(bs58::Alphabet::BITCOIN)
51 .into_string()
52 }
53
54 pub fn as_bytes(&self) -> &[u8] {
55 self.0.as_slice()
56 }
57
58 pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, bs58::decode::Error> {
60 let mut spec = [0; CONTRACT_KEY_SIZE];
61 bs58::decode(bytes)
62 .with_alphabet(bs58::Alphabet::BITCOIN)
63 .onto(&mut spec)?;
64 Ok(Self(spec))
65 }
66}
67
68impl Deref for ContractInstanceId {
69 type Target = [u8; CONTRACT_KEY_SIZE];
70
71 fn deref(&self) -> &Self::Target {
72 &self.0
73 }
74}
75
76impl FromStr for ContractInstanceId {
77 type Err = bs58::decode::Error;
78
79 fn from_str(s: &str) -> Result<Self, Self::Err> {
80 ContractInstanceId::from_bytes(s)
81 }
82}
83
84impl TryFrom<String> for ContractInstanceId {
85 type Error = bs58::decode::Error;
86
87 fn try_from(s: String) -> Result<Self, Self::Error> {
88 ContractInstanceId::from_bytes(s)
89 }
90}
91
92impl Display for ContractInstanceId {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 write!(f, "{}", self.encode())
95 }
96}
97
98impl std::fmt::Debug for ContractInstanceId {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 f.debug_tuple("ContractInstanceId")
101 .field(&self.encode())
102 .finish()
103 }
104}
105
106#[serde_as]
108#[derive(Debug, Eq, Copy, Clone, Serialize, Deserialize)]
109#[cfg_attr(
110 any(feature = "testing", all(test, any(unix, windows))),
111 derive(arbitrary::Arbitrary)
112)]
113pub struct ContractKey {
114 instance: ContractInstanceId,
115 code: Option<CodeHash>,
116}
117
118impl ContractKey {
119 pub fn from_params_and_code<'a>(
120 params: impl Borrow<Parameters<'a>>,
121 wasm_code: impl Borrow<ContractCode<'a>>,
122 ) -> Self {
123 let code = wasm_code.borrow();
124 let id = generate_id(params.borrow(), code);
125 let code_hash = code.hash();
126 Self {
127 instance: id,
128 code: Some(*code_hash),
129 }
130 }
131
132 pub fn from_id(instance: impl Into<String>) -> Result<Self, bs58::decode::Error> {
134 let instance = ContractInstanceId::try_from(instance.into())?;
135 Ok(Self {
136 instance,
137 code: None,
138 })
139 }
140
141 pub fn as_bytes(&self) -> &[u8] {
143 self.instance.0.as_ref()
144 }
145
146 pub fn code_hash(&self) -> Option<&CodeHash> {
148 self.code.as_ref()
149 }
150
151 pub fn encoded_code_hash(&self) -> Option<String> {
153 self.code.as_ref().map(|c| {
154 bs58::encode(c.0)
155 .with_alphabet(bs58::Alphabet::BITCOIN)
156 .into_string()
157 })
158 }
159
160 pub fn from_params(
163 code_hash: impl Into<String>,
164 parameters: Parameters,
165 ) -> Result<Self, bs58::decode::Error> {
166 let mut code_key = [0; CONTRACT_KEY_SIZE];
167 bs58::decode(code_hash.into())
168 .with_alphabet(bs58::Alphabet::BITCOIN)
169 .onto(&mut code_key)?;
170
171 let mut hasher = Blake3::new();
172 hasher.update(code_key.as_slice());
173 hasher.update(parameters.as_ref());
174 let full_key_arr = hasher.finalize();
175
176 let mut spec = [0; CONTRACT_KEY_SIZE];
177 spec.copy_from_slice(&full_key_arr);
178 Ok(Self {
179 instance: ContractInstanceId(spec),
180 code: Some(CodeHash(code_key)),
181 })
182 }
183
184 pub fn encoded_contract_id(&self) -> String {
186 self.instance.encode()
187 }
188
189 pub fn id(&self) -> &ContractInstanceId {
190 &self.instance
191 }
192}
193
194impl PartialEq for ContractKey {
195 fn eq(&self, other: &Self) -> bool {
196 self.instance == other.instance
197 }
198}
199
200impl std::hash::Hash for ContractKey {
201 fn hash<H: Hasher>(&self, state: &mut H) {
202 self.instance.0.hash(state);
203 }
204}
205
206impl From<ContractInstanceId> for ContractKey {
207 fn from(instance: ContractInstanceId) -> Self {
208 Self {
209 instance,
210 code: None,
211 }
212 }
213}
214
215impl From<ContractKey> for ContractInstanceId {
216 fn from(key: ContractKey) -> Self {
217 key.instance
218 }
219}
220
221impl Deref for ContractKey {
222 type Target = [u8; CONTRACT_KEY_SIZE];
223
224 fn deref(&self) -> &Self::Target {
225 &self.instance.0
226 }
227}
228
229impl std::fmt::Display for ContractKey {
230 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
231 self.instance.fmt(f)
232 }
233}
234
235impl<'a> TryFromFbs<&FbsContractKey<'a>> for ContractKey {
236 fn try_decode_fbs(key: &FbsContractKey<'a>) -> Result<Self, WsApiError> {
237 let key_bytes: [u8; CONTRACT_KEY_SIZE] = key.instance().data().bytes().try_into().unwrap();
238 let instance = ContractInstanceId::new(key_bytes);
239 let code = key
240 .code()
241 .map(|code_hash| CodeHash::from_code(code_hash.bytes()));
242 Ok(ContractKey { instance, code })
243 }
244}
245
246fn generate_id<'a>(
247 parameters: &Parameters<'a>,
248 code_data: &ContractCode<'a>,
249) -> ContractInstanceId {
250 let contract_hash = code_data.hash();
251
252 let mut hasher = Blake3::new();
253 hasher.update(contract_hash.0.as_slice());
254 hasher.update(parameters.as_ref());
255 let full_key_arr = hasher.finalize();
256
257 debug_assert_eq!(full_key_arr[..].len(), CONTRACT_KEY_SIZE);
258 let mut spec = [0; CONTRACT_KEY_SIZE];
259 spec.copy_from_slice(&full_key_arr);
260 ContractInstanceId(spec)
261}
262
263#[inline]
264pub(super) fn internal_fmt_key(
265 key: &[u8; CONTRACT_KEY_SIZE],
266 f: &mut std::fmt::Formatter<'_>,
267) -> std::fmt::Result {
268 let r = bs58::encode(key)
269 .with_alphabet(bs58::Alphabet::BITCOIN)
270 .into_string();
271 write!(f, "{}", &r[..8])
272}