1use crate::stack_item::StackItem;
6use sha2::{Digest, Sha256};
7
8const MAX_INPUT_SIZE: usize = 1024 * 1024;
10
11pub trait NativeContract {
13 fn hash(&self) -> [u8; 20];
14 fn invoke(&self, method: &str, args: Vec<StackItem>) -> Result<StackItem, String>;
15}
16
17#[derive(Debug, Default)]
19pub struct StdLib;
20
21impl StdLib {
22 #[inline]
23 pub fn new() -> Self {
24 Self
25 }
26
27 #[inline]
28 fn serialize(&self, args: Vec<StackItem>) -> Result<StackItem, String> {
29 if args.is_empty() {
30 return Err("serialize requires 1 argument".to_string());
31 }
32 let bytes = bincode::serialize(&args[0]).map_err(|e| e.to_string())?;
33 Ok(StackItem::ByteString(bytes))
34 }
35
36 fn deserialize(&self, args: Vec<StackItem>) -> Result<StackItem, String> {
37 if let Some(StackItem::ByteString(bytes)) = args.first() {
38 if bytes.len() > MAX_INPUT_SIZE {
39 return Err(format!(
40 "deserialize input exceeds maximum size of {} bytes",
41 MAX_INPUT_SIZE
42 ));
43 }
44 bincode::deserialize(bytes).map_err(|e| format!("deserialize failed: {}", e))
45 } else {
46 Err("deserialize requires ByteString argument".to_string())
47 }
48 }
49
50 #[inline]
51 fn json_serialize(&self, args: Vec<StackItem>) -> Result<StackItem, String> {
52 if args.is_empty() {
53 return Err("jsonSerialize requires 1 argument".to_string());
54 }
55 let json = serde_json::to_string(&args[0]).map_err(|e| e.to_string())?;
56 if json.len() > MAX_INPUT_SIZE {
57 return Err(format!(
58 "jsonSerialize output exceeds maximum size of {} bytes",
59 MAX_INPUT_SIZE
60 ));
61 }
62 Ok(StackItem::ByteString(json.into_bytes()))
63 }
64}
65
66impl StdLib {
67 #[inline]
68 fn base64_encode(&self, args: Vec<StackItem>) -> Result<StackItem, String> {
69 if let Some(StackItem::ByteString(bytes)) = args.first() {
70 if bytes.len() > MAX_INPUT_SIZE {
71 return Err(format!(
72 "base64Encode input exceeds maximum size of {} bytes",
73 MAX_INPUT_SIZE
74 ));
75 }
76 use base64::{engine::general_purpose::STANDARD, Engine};
77 let encoded = STANDARD.encode(bytes);
78 Ok(StackItem::ByteString(encoded.into_bytes()))
79 } else {
80 Err("base64Encode requires ByteString".to_string())
81 }
82 }
83
84 #[inline]
85 fn base64_decode(&self, args: Vec<StackItem>) -> Result<StackItem, String> {
86 if let Some(StackItem::ByteString(bytes)) = args.first() {
87 if bytes.len() > MAX_INPUT_SIZE {
88 return Err(format!(
89 "base64Decode input exceeds maximum size of {} bytes",
90 MAX_INPUT_SIZE
91 ));
92 }
93 use base64::{engine::general_purpose::STANDARD, Engine};
94 let s = String::from_utf8_lossy(bytes);
95 let decoded = STANDARD.decode(s.as_ref()).map_err(|e| e.to_string())?;
96 Ok(StackItem::ByteString(decoded))
97 } else {
98 Err("base64Decode requires ByteString".to_string())
99 }
100 }
101}
102
103impl StdLib {
104 #[inline]
105 fn itoa(&self, args: Vec<StackItem>) -> Result<StackItem, String> {
106 if let Some(StackItem::Integer(n)) = args.first() {
107 let base = args
108 .get(1)
109 .and_then(|i| {
110 if let StackItem::Integer(b) = i {
111 Some(*b as u32)
112 } else {
113 None
114 }
115 })
116 .unwrap_or(10);
117 if base != 2 && base != 10 && base != 16 {
118 return Err(format!(
119 "Unsupported base {}. Supported bases: 2 (binary), 10 (decimal), 16 (hexadecimal)",
120 base
121 ));
122 }
123 let s = match base {
124 2 => format!("{:b}", n),
125 10 => format!("{}", n),
126 16 => format!("{:x}", n),
127 _ => unreachable!(),
128 };
129 Ok(StackItem::ByteString(s.into_bytes()))
130 } else {
131 Err("itoa requires Integer".to_string())
132 }
133 }
134
135 #[inline]
136 fn atoi(&self, args: Vec<StackItem>) -> Result<StackItem, String> {
137 if let Some(StackItem::ByteString(bytes)) = args.first() {
138 if bytes.len() > MAX_INPUT_SIZE {
139 return Err(format!(
140 "atoi input exceeds maximum size of {} bytes",
141 MAX_INPUT_SIZE
142 ));
143 }
144 let s = String::from_utf8_lossy(bytes);
145 let base = args
146 .get(1)
147 .and_then(|i| {
148 if let StackItem::Integer(b) = i {
149 Some(*b as u32)
150 } else {
151 None
152 }
153 })
154 .unwrap_or(10);
155 if base != 2 && base != 10 && base != 16 {
156 return Err(format!(
157 "Unsupported base {}. Supported bases: 2 (binary), 10 (decimal), 16 (hexadecimal)",
158 base
159 ));
160 }
161 let n = i128::from_str_radix(s.trim(), base).map_err(|e| e.to_string())?;
162 Ok(StackItem::Integer(n))
163 } else {
164 Err("atoi requires ByteString".to_string())
165 }
166 }
167}
168
169impl NativeContract for StdLib {
170 #[inline]
171 fn hash(&self) -> [u8; 20] {
172 [
173 0xac, 0xce, 0x6f, 0xd8, 0x0d, 0x44, 0xe1, 0xa3, 0x92, 0x6d, 0xe2, 0x1c, 0xcf, 0x30,
174 0x96, 0x9a, 0x22, 0x4b, 0xc0, 0x6b,
175 ]
176 }
177
178 #[inline]
179 fn invoke(&self, method: &str, args: Vec<StackItem>) -> Result<StackItem, String> {
180 match method {
181 "serialize" => self.serialize(args),
182 "deserialize" => self.deserialize(args),
183 "jsonSerialize" => self.json_serialize(args),
184 "base64Encode" => self.base64_encode(args),
185 "base64Decode" => self.base64_decode(args),
186 "itoa" => self.itoa(args),
187 "atoi" => self.atoi(args),
188 _ => Err(format!("Unknown method: {}", method)),
189 }
190 }
191}
192
193#[derive(Debug, Default)]
195pub struct CryptoLib;
196
197impl CryptoLib {
198 pub fn new() -> Self {
199 Self
200 }
201}
202
203impl NativeContract for CryptoLib {
204 #[inline]
205 fn hash(&self) -> [u8; 20] {
206 [
207 0x72, 0x6c, 0xb6, 0xe0, 0xcd, 0x8b, 0x0a, 0xc3, 0x3c, 0xe1, 0xde, 0xc0, 0xd4, 0x7e,
208 0x5c, 0x3c, 0x4a, 0x6b, 0x8a, 0x0d,
209 ]
210 }
211
212 #[inline]
213 fn invoke(&self, method: &str, args: Vec<StackItem>) -> Result<StackItem, String> {
214 match method {
215 "sha256" => self.sha256(args),
216 "ripemd160" => self.ripemd160(args),
217 "verifyWithECDsa" => self.verify_ecdsa(args),
218 _ => Err(format!("Unknown method: {}", method)),
219 }
220 }
221}
222
223impl CryptoLib {
224 #[inline]
225 fn sha256(&self, args: Vec<StackItem>) -> Result<StackItem, String> {
226 if let Some(StackItem::ByteString(data)) = args.first() {
227 if data.len() > MAX_INPUT_SIZE {
228 return Err(format!(
229 "sha256 input exceeds maximum size of {} bytes",
230 MAX_INPUT_SIZE
231 ));
232 }
233 let hash = Sha256::digest(data);
234 Ok(StackItem::ByteString(hash.to_vec()))
235 } else {
236 Err("sha256 requires ByteString".to_string())
237 }
238 }
239
240 #[inline]
241 fn ripemd160(&self, args: Vec<StackItem>) -> Result<StackItem, String> {
242 if let Some(StackItem::ByteString(data)) = args.first() {
243 if data.len() > MAX_INPUT_SIZE {
244 return Err(format!(
245 "ripemd160 input exceeds maximum size of {} bytes",
246 MAX_INPUT_SIZE
247 ));
248 }
249 use ripemd::Ripemd160;
250 let hash = Ripemd160::digest(data);
251 Ok(StackItem::ByteString(hash.to_vec()))
252 } else {
253 Err("ripemd160 requires ByteString".to_string())
254 }
255 }
256
257 #[inline]
258 fn verify_ecdsa(&self, args: Vec<StackItem>) -> Result<StackItem, String> {
259 use k256::ecdsa::{signature::Verifier, Signature, VerifyingKey};
260
261 if args.len() < 2 {
262 return Err("verify_ecdsa requires at least 2 arguments".to_string());
263 }
264
265 let message = match &args[0] {
266 StackItem::ByteString(msg) => msg.as_slice(),
267 _ => return Err("verify_ecdsa: first argument must be ByteString".to_string()),
268 };
269
270 let signature = match &args[1] {
271 StackItem::ByteString(sig) => sig.as_slice(),
272 _ => return Err("verify_ecdsa: second argument must be ByteString".to_string()),
273 };
274
275 let pubkey = if args.len() >= 3 {
276 match &args[2] {
277 StackItem::ByteString(pk) => pk.as_slice(),
278 _ => return Err("verify_ecdsa: third argument must be ByteString".to_string()),
279 }
280 } else {
281 return Err("verify_ecdsa: public key required".to_string());
282 };
283
284 if message.len() > MAX_INPUT_SIZE {
285 return Err(format!(
286 "verify_ecdsa message exceeds maximum size of {} bytes",
287 MAX_INPUT_SIZE
288 ));
289 }
290
291 let signature = Signature::from_slice(signature)
292 .map_err(|_| "Invalid ECDSA signature format".to_string())?;
293 let verifying_key = VerifyingKey::from_sec1_bytes(pubkey)
294 .map_err(|_| "Invalid public key format".to_string())?;
295
296 Ok(StackItem::Boolean(
297 verifying_key.verify(message, &signature).is_ok(),
298 ))
299 }
300}
301
302#[derive(Default)]
304pub struct NativeRegistry {
305 stdlib: StdLib,
306 cryptolib: CryptoLib,
307}
308
309impl NativeRegistry {
310 #[inline]
311 pub fn new() -> Self {
312 Self {
313 stdlib: StdLib::new(),
314 cryptolib: CryptoLib::new(),
315 }
316 }
317
318 #[inline]
319 pub fn invoke(
320 &self,
321 hash: &[u8; 20],
322 method: &str,
323 args: Vec<StackItem>,
324 ) -> Result<StackItem, String> {
325 if *hash == self.stdlib.hash() {
326 self.stdlib.invoke(method, args)
327 } else if *hash == self.cryptolib.hash() {
328 self.cryptolib.invoke(method, args)
329 } else {
330 Err("Unknown native contract".to_string())
331 }
332 }
333}