freenet_stdlib/contract_interface/
code.rs1use std::borrow::Cow;
7use std::fs::File;
8use std::io::Read;
9use std::path::Path;
10
11use blake3::{traits::digest::Digest, Hasher as Blake3};
12use serde::{Deserialize, Serialize};
13use serde_with::serde_as;
14
15use crate::code_hash::CodeHash;
16
17use super::key::internal_fmt_key;
18use super::CONTRACT_KEY_SIZE;
19
20#[serde_as]
25#[derive(Serialize, Deserialize, Clone)]
26#[cfg_attr(
27 any(feature = "testing", all(test, any(unix, windows))),
28 derive(arbitrary::Arbitrary)
29)]
30pub struct ContractCode<'a> {
31 #[serde_as(as = "serde_with::Bytes")]
33 #[serde(borrow)]
34 pub(crate) data: Cow<'a, [u8]>,
35 pub(crate) code_hash: CodeHash,
37}
38
39impl ContractCode<'static> {
40 pub fn load_raw(path: &Path) -> Result<Self, std::io::Error> {
42 let contract_data = Self::load_bytes(path)?;
43 Ok(ContractCode::from(contract_data))
44 }
45
46 pub(crate) fn load_bytes(path: &Path) -> Result<Vec<u8>, std::io::Error> {
47 let mut contract_file = File::open(path)?;
48 let mut contract_data = if let Ok(md) = contract_file.metadata() {
49 Vec::with_capacity(md.len() as usize)
50 } else {
51 Vec::new()
52 };
53 contract_file.read_to_end(&mut contract_data)?;
54 Ok(contract_data)
55 }
56}
57
58impl ContractCode<'_> {
59 pub fn hash(&self) -> &CodeHash {
61 &self.code_hash
62 }
63
64 pub fn hash_str(&self) -> String {
66 Self::encode_hash(&self.code_hash.0)
67 }
68
69 pub fn data(&self) -> &[u8] {
71 &self.data
72 }
73
74 pub fn into_bytes(self) -> Vec<u8> {
76 self.data.to_vec()
77 }
78
79 pub fn encode_hash(hash: &[u8; CONTRACT_KEY_SIZE]) -> String {
81 bs58::encode(hash)
82 .with_alphabet(bs58::Alphabet::BITCOIN)
83 .into_string()
84 }
85
86 pub fn into_owned(self) -> ContractCode<'static> {
88 ContractCode {
89 data: self.data.into_owned().into(),
90 code_hash: self.code_hash,
91 }
92 }
93
94 fn gen_hash(data: &[u8]) -> CodeHash {
95 let mut hasher = Blake3::new();
96 hasher.update(data);
97 let key_arr = hasher.finalize();
98 debug_assert_eq!(key_arr[..].len(), CONTRACT_KEY_SIZE);
99 let mut key = [0; CONTRACT_KEY_SIZE];
100 key.copy_from_slice(&key_arr);
101 CodeHash(key)
102 }
103}
104
105impl From<Vec<u8>> for ContractCode<'static> {
106 fn from(data: Vec<u8>) -> Self {
107 let key = ContractCode::gen_hash(&data);
108 ContractCode {
109 data: Cow::from(data),
110 code_hash: key,
111 }
112 }
113}
114
115impl<'a> From<&'a [u8]> for ContractCode<'a> {
116 fn from(data: &'a [u8]) -> ContractCode<'a> {
117 let hash = ContractCode::gen_hash(data);
118 ContractCode {
119 data: Cow::from(data),
120 code_hash: hash,
121 }
122 }
123}
124
125impl PartialEq for ContractCode<'_> {
126 fn eq(&self, other: &Self) -> bool {
127 self.code_hash == other.code_hash
128 }
129}
130
131impl Eq for ContractCode<'_> {}
132
133impl std::fmt::Display for ContractCode<'_> {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 write!(f, "Contract( key: ")?;
136 internal_fmt_key(&self.code_hash.0, f)?;
137 let data: String = if self.data.len() > 8 {
138 self.data[..4]
139 .iter()
140 .map(|b| char::from(*b))
141 .chain("...".chars())
142 .chain(self.data[4..].iter().map(|b| char::from(*b)))
143 .collect()
144 } else {
145 self.data.iter().copied().map(char::from).collect()
146 };
147 write!(f, ", data: [{data}])")
148 }
149}
150
151impl std::fmt::Debug for ContractCode<'_> {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 f.debug_struct("ContractCode")
154 .field("hash", &self.code_hash);
155 Ok(())
156 }
157}