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]
113#[derive(Debug, Eq, Copy, Clone, Serialize, Deserialize)]
114#[cfg_attr(
115 any(feature = "testing", all(test, any(unix, windows))),
116 derive(arbitrary::Arbitrary)
117)]
118pub struct ContractKey {
119 instance: ContractInstanceId,
120 code: CodeHash,
121}
122
123impl ContractKey {
124 pub fn from_params_and_code<'a>(
125 params: impl Borrow<Parameters<'a>>,
126 wasm_code: impl Borrow<ContractCode<'a>>,
127 ) -> Self {
128 let code = wasm_code.borrow();
129 let id = generate_id(params.borrow(), code);
130 let code_hash = *code.hash();
131 Self {
132 instance: id,
133 code: code_hash,
134 }
135 }
136
137 pub fn as_bytes(&self) -> &[u8] {
139 self.instance.0.as_ref()
140 }
141
142 pub fn code_hash(&self) -> &CodeHash {
144 &self.code
145 }
146
147 pub fn encoded_code_hash(&self) -> String {
149 bs58::encode(self.code.0)
150 .with_alphabet(bs58::Alphabet::BITCOIN)
151 .into_string()
152 }
153
154 pub fn from_params(
157 code_hash: impl Into<String>,
158 parameters: Parameters,
159 ) -> Result<Self, bs58::decode::Error> {
160 let mut code_key = [0; CONTRACT_KEY_SIZE];
161 bs58::decode(code_hash.into())
162 .with_alphabet(bs58::Alphabet::BITCOIN)
163 .onto(&mut code_key)?;
164
165 let mut hasher = Blake3::new();
166 hasher.update(code_key.as_slice());
167 hasher.update(parameters.as_ref());
168 let full_key_arr = hasher.finalize();
169
170 let mut spec = [0; CONTRACT_KEY_SIZE];
171 spec.copy_from_slice(&full_key_arr);
172 Ok(Self {
173 instance: ContractInstanceId(spec),
174 code: CodeHash(code_key),
175 })
176 }
177
178 pub fn encoded_contract_id(&self) -> String {
180 self.instance.encode()
181 }
182
183 pub fn id(&self) -> &ContractInstanceId {
184 &self.instance
185 }
186
187 pub fn from_id_and_code(instance_id: ContractInstanceId, code_hash: CodeHash) -> Self {
193 Self {
194 instance: instance_id,
195 code: code_hash,
196 }
197 }
198}
199
200impl PartialEq for ContractKey {
201 fn eq(&self, other: &Self) -> bool {
202 self.instance == other.instance
203 }
204}
205
206impl std::hash::Hash for ContractKey {
207 fn hash<H: Hasher>(&self, state: &mut H) {
208 self.instance.0.hash(state);
209 }
210}
211
212
213impl From<ContractKey> for ContractInstanceId {
214 fn from(key: ContractKey) -> Self {
215 key.instance
216 }
217}
218
219impl Deref for ContractKey {
220 type Target = [u8; CONTRACT_KEY_SIZE];
221
222 fn deref(&self) -> &Self::Target {
223 &self.instance.0
224 }
225}
226
227impl std::fmt::Display for ContractKey {
228 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229 self.instance.fmt(f)
230 }
231}
232
233impl<'a> TryFromFbs<&FbsContractKey<'a>> for ContractKey {
234 fn try_decode_fbs(key: &FbsContractKey<'a>) -> Result<Self, WsApiError> {
235 let key_bytes: [u8; CONTRACT_KEY_SIZE] = key.instance().data().bytes().try_into().unwrap();
236 let instance = ContractInstanceId::new(key_bytes);
237 let code = key
238 .code()
239 .map(|code_hash| CodeHash::from_code(code_hash.bytes()))
240 .ok_or_else(|| WsApiError::deserialization("ContractKey missing code hash".into()))?;
241 Ok(ContractKey { instance, code })
242 }
243}
244
245fn generate_id<'a>(
246 parameters: &Parameters<'a>,
247 code_data: &ContractCode<'a>,
248) -> ContractInstanceId {
249 let contract_hash = code_data.hash();
250
251 let mut hasher = Blake3::new();
252 hasher.update(contract_hash.0.as_slice());
253 hasher.update(parameters.as_ref());
254 let full_key_arr = hasher.finalize();
255
256 debug_assert_eq!(full_key_arr[..].len(), CONTRACT_KEY_SIZE);
257 let mut spec = [0; CONTRACT_KEY_SIZE];
258 spec.copy_from_slice(&full_key_arr);
259 ContractInstanceId(spec)
260}
261
262#[inline]
263pub(super) fn internal_fmt_key(
264 key: &[u8; CONTRACT_KEY_SIZE],
265 f: &mut std::fmt::Formatter<'_>,
266) -> std::fmt::Result {
267 let r = bs58::encode(key)
268 .with_alphabet(bs58::Alphabet::BITCOIN)
269 .into_string();
270 write!(f, "{}", &r[..8])
271}