1use ed25519_dalek::Verifier;
9use pchain_types::{
10 blockchain::{Command, Log},
11 runtime::CallInput,
12 serialization::{Deserializable, Serializable},
13};
14use pchain_world_state::{
15 keys::AppKey, network::constants::NETWORK_ADDRESS, storage::WorldStateStorage,
16};
17use ripemd::Ripemd160;
18use sha2::{Digest as sha256_digest, Sha256};
19use tiny_keccak::{Hasher as _, Keccak};
20
21use crate::{
22 contract::{ContractBinaryInterface, FuncError},
23 cost::CostChange,
24 execution::{self},
25 gas::{self},
26 types::DeferredCommand,
27 wasmer::wasmer_env::Env,
28};
29
30pub(crate) struct ContractBinaryFunctions {}
33impl<S> ContractBinaryInterface<Env<S>> for ContractBinaryFunctions
34where
35 S: WorldStateStorage + Sync + Send + Clone,
36{
37 fn set(
38 env: &Env<S>,
39 key_ptr: u32,
40 key_len: u32,
41 val_ptr: u32,
42 val_len: u32,
43 ) -> Result<(), FuncError> {
44 let app_key = env.read_bytes(key_ptr, key_len)?;
45 let app_key = AppKey::new(app_key);
46
47 let new_value = env.read_bytes(val_ptr, val_len)?;
48
49 let target_address = env.call_tx.target;
50
51 let cost_change =
52 env.context
53 .lock()
54 .unwrap()
55 .set_app_data(target_address, app_key, new_value);
56 env.consume_non_wasm_gas(cost_change);
57 Ok(())
58 }
59
60 fn get(env: &Env<S>, key_ptr: u32, key_len: u32, val_ptr_ptr: u32) -> Result<i64, FuncError> {
61 let app_key = env.read_bytes(key_ptr, key_len)?;
62 let app_key = AppKey::new(app_key);
63
64 let tx_ctx_lock = env.context.lock().unwrap();
65 let (value, cost_change) = tx_ctx_lock.app_data(env.call_tx.target, app_key);
66 drop(tx_ctx_lock);
67
68 env.consume_non_wasm_gas(cost_change);
69
70 let ret_val = match value {
71 Some(value) => env.write_bytes(value, val_ptr_ptr)? as i64,
72 None => -1,
73 };
74
75 Ok(ret_val)
76 }
77
78 fn get_network_storage(
79 env: &Env<S>,
80 key_ptr: u32,
81 key_len: u32,
82 val_ptr_ptr: u32,
83 ) -> Result<i64, FuncError> {
84 let app_key = env.read_bytes(key_ptr, key_len)?;
85 let app_key = AppKey::new(app_key);
86
87 let tx_ctx_lock = env.context.lock().unwrap();
88 let (value, cost_change) = tx_ctx_lock.app_data(NETWORK_ADDRESS, app_key);
89 drop(tx_ctx_lock);
90
91 env.consume_non_wasm_gas(cost_change);
92
93 let ret_val = match value {
94 Some(value) => env.write_bytes(value, val_ptr_ptr)? as i64,
95 None => -1,
96 };
97
98 Ok(ret_val)
99 }
100
101 fn balance(env: &Env<S>) -> Result<u64, FuncError> {
102 let (balance, cost_change) = env.context.lock().unwrap().balance(env.call_tx.target);
103 env.consume_non_wasm_gas(cost_change);
104 Ok(balance)
105 }
106
107 fn block_height(env: &Env<S>) -> Result<u64, FuncError> {
108 Ok(env.params_from_blockchain.this_block_number)
109 }
110 fn block_timestamp(env: &Env<S>) -> Result<u32, FuncError> {
111 Ok(env.params_from_blockchain.timestamp)
112 }
113 fn prev_block_hash(env: &Env<S>, hash_ptr_ptr: u32) -> Result<(), FuncError> {
114 env.write_bytes(
115 env.params_from_blockchain.prev_block_hash.to_vec(),
116 hash_ptr_ptr,
117 )?;
118 Ok(())
119 }
120
121 fn calling_account(env: &Env<S>, address_ptr_ptr: u32) -> Result<(), FuncError> {
122 env.write_bytes(env.call_tx.signer.to_vec(), address_ptr_ptr)?;
123 Ok(())
124 }
125 fn current_account(env: &Env<S>, address_ptr_ptr: u32) -> Result<(), FuncError> {
126 env.write_bytes(env.call_tx.target.to_vec(), address_ptr_ptr)?;
127 Ok(())
128 }
129
130 fn method(env: &Env<S>, method_ptr_ptr: u32) -> Result<u32, FuncError> {
131 env.write_bytes(env.call_tx.method.as_bytes().to_vec(), method_ptr_ptr)
132 }
133
134 fn arguments(env: &Env<S>, arguments_ptr_ptr: u32) -> Result<u32, FuncError> {
135 match &env.call_tx.arguments {
136 Some(arguments) => {
137 let arguments = <Vec<Vec<u8>> as Serializable>::serialize(arguments);
138 env.write_bytes(arguments, arguments_ptr_ptr)
139 }
140 None => Ok(0),
141 }
142 }
143
144 fn amount(env: &Env<S>) -> Result<u64, FuncError> {
145 Ok(env.call_tx.amount.map_or(0, std::convert::identity))
146 }
147
148 fn is_internal_call(env: &Env<S>) -> Result<i32, FuncError> {
149 Ok(i32::from(env.call_counter != 0))
150 }
151
152 fn transaction_hash(env: &Env<S>, hash_ptr_ptr: u32) -> Result<(), FuncError> {
153 env.write_bytes(env.call_tx.hash.to_vec(), hash_ptr_ptr)?;
154 Ok(())
155 }
156
157 fn log(env: &Env<S>, log_ptr: u32, log_len: u32) -> Result<(), FuncError> {
158 let serialized_log = env.read_bytes(log_ptr, log_len)?;
159 let log = Log::deserialize(&serialized_log).map_err(|e| FuncError::Runtime(e.into()))?;
160
161 let cost_change =
162 CostChange::deduct(gas::blockchain_log_cost(log.topic.len(), log.value.len()));
163 let mut tx_ctx_lock = env.context.lock().unwrap();
164 tx_ctx_lock.receipt_write_gas += cost_change;
165 drop(tx_ctx_lock);
166
167 env.consume_non_wasm_gas(cost_change);
170 if env.get_wasmer_remaining_points() == 0 {
171 return Err(FuncError::GasExhaustionError);
172 }
173
174 env.context.lock().unwrap().logs.push(log);
175
176 Ok(())
177 }
178
179 fn return_value(env: &Env<S>, value_ptr: u32, value_len: u32) -> Result<(), FuncError> {
180 let value = env.read_bytes(value_ptr, value_len)?;
181
182 let cost_change = CostChange::deduct(gas::blockchain_return_values_cost(value.len()));
183 let mut tx_ctx_lock = env.context.lock().unwrap();
184 tx_ctx_lock.receipt_write_gas += cost_change;
185 drop(tx_ctx_lock);
186
187 env.consume_non_wasm_gas(cost_change);
190 if env.get_wasmer_remaining_points() == 0 {
191 return Err(FuncError::GasExhaustionError);
192 }
193
194 env.context.lock().unwrap().return_value =
195 if value.is_empty() { None } else { Some(value) };
196
197 Ok(())
198 }
199
200 fn call(
201 env: &Env<S>,
202 call_input_ptr: u32,
203 call_input_len: u32,
204 return_ptr_ptr: u32,
205 ) -> Result<u32, FuncError> {
206 let call_command_bytes = env.read_bytes(call_input_ptr, call_input_len)?;
207 let call_command =
208 Command::deserialize(&call_command_bytes).map_err(|e| FuncError::Runtime(e.into()))?;
209
210 let (target, method, arguments, amount) = match call_command {
211 Command::Call(CallInput {
212 target,
213 method,
214 arguments,
215 amount,
216 }) => (target, method, arguments, amount),
217 _ => return Err(FuncError::Internal),
218 };
219
220 if env.is_view && amount.is_some() {
222 return Err(FuncError::Internal);
223 }
224
225 let signer = env.call_tx.target;
227 let gas_limit = env.get_wasmer_remaining_points();
229
230 let mut call_tx = env.call_tx.clone();
232 call_tx.signer = signer;
233 call_tx.gas_limit = gas_limit;
234
235 call_tx.target = target;
236 call_tx.method = method;
237 call_tx.arguments = arguments;
238 call_tx.amount = amount;
239
240 let result = execution::internal::call_from_contract(
241 call_tx,
242 env.params_from_blockchain.clone(),
243 env.context.clone(),
244 env.call_counter.saturating_add(1),
245 env.is_view,
246 );
247 env.consume_non_wasm_gas(result.non_wasmer_gas);
248 env.consume_wasm_gas(result.exec_gas); match result.error {
251 None => {
252 let mut tx_ctx_locked = env.context.lock().unwrap();
253 let res = tx_ctx_locked.return_value.clone();
254
255 tx_ctx_locked.return_value = None;
257
258 if let Some(res) = res {
259 return env.write_bytes(res, return_ptr_ptr);
260 }
261 }
262 Some(e) => {
263 if env.get_wasmer_remaining_points() == 0 {
264 return Err(FuncError::GasExhaustionError);
265 }
266 return Err(e);
267 }
268 }
269
270 Ok(0)
271 }
272
273 fn transfer(env: &Env<S>, transfer_input_ptr: u32) -> Result<(), FuncError> {
274 let transfer_bytes =
275 env.read_bytes(transfer_input_ptr, std::mem::size_of::<[u8; 40]>() as u32)?;
276
277 let (recipient, amount_bytes) = transfer_bytes.split_at(32);
278 let recipient = recipient.try_into().unwrap();
279 let amount = u64::from_le_bytes(amount_bytes.try_into().unwrap());
280
281 let result = execution::internal::transfer_from_contract(
282 env.call_tx.target, amount,
284 recipient,
285 env.context.clone(),
286 );
287 env.consume_non_wasm_gas(result.non_wasmer_gas);
288
289 match result.error {
290 None => Ok(()),
291 Some(e) => Err(e),
292 }
293 }
294
295 fn defer_create_deposit(
296 env: &Env<S>,
297 create_deposit_input_ptr: u32,
298 create_deposit_input_len: u32,
299 ) -> Result<(), FuncError> {
300 let serialized_command =
301 env.read_bytes(create_deposit_input_ptr, create_deposit_input_len)?;
302 let command =
303 Command::deserialize(&serialized_command).map_err(|e| FuncError::Runtime(e.into()))?;
304
305 if !matches!(command, Command::CreateDeposit { .. }) {
306 return Err(FuncError::Internal);
307 }
308
309 env.context.lock().unwrap().commands.push(DeferredCommand {
310 command,
311 contract_address: env.call_tx.target,
312 });
313
314 Ok(())
315 }
316
317 fn defer_set_deposit_settings(
318 env: &Env<S>,
319 set_deposit_settings_input_ptr: u32,
320 set_deposit_settings_input_len: u32,
321 ) -> Result<(), FuncError> {
322 let serialized_command = env.read_bytes(
323 set_deposit_settings_input_ptr,
324 set_deposit_settings_input_len,
325 )?;
326 let command =
327 Command::deserialize(&serialized_command).map_err(|e| FuncError::Runtime(e.into()))?;
328
329 if !matches!(command, Command::SetDepositSettings { .. }) {
330 return Err(FuncError::Internal);
331 }
332
333 env.context.lock().unwrap().commands.push(DeferredCommand {
334 command,
335 contract_address: env.call_tx.target,
336 });
337
338 Ok(())
339 }
340
341 fn defer_topup_deposit(
342 env: &Env<S>,
343 top_up_deposit_input_ptr: u32,
344 top_up_deposit_input_len: u32,
345 ) -> Result<(), FuncError> {
346 let serialized_command =
347 env.read_bytes(top_up_deposit_input_ptr, top_up_deposit_input_len)?;
348 let command =
349 Command::deserialize(&serialized_command).map_err(|e| FuncError::Runtime(e.into()))?;
350
351 if !matches!(command, Command::TopUpDeposit { .. }) {
352 return Err(FuncError::Internal);
353 }
354
355 env.context.lock().unwrap().commands.push(DeferredCommand {
356 command,
357 contract_address: env.call_tx.target,
358 });
359
360 Ok(())
361 }
362
363 fn defer_withdraw_deposit(
364 env: &Env<S>,
365 withdraw_deposit_input_ptr: u32,
366 withdraw_deposit_input_len: u32,
367 ) -> Result<(), FuncError> {
368 let serialized_command =
369 env.read_bytes(withdraw_deposit_input_ptr, withdraw_deposit_input_len)?;
370 let command =
371 Command::deserialize(&serialized_command).map_err(|e| FuncError::Runtime(e.into()))?;
372
373 if !matches!(command, Command::WithdrawDeposit { .. }) {
374 return Err(FuncError::Internal);
375 }
376
377 env.context.lock().unwrap().commands.push(DeferredCommand {
378 command,
379 contract_address: env.call_tx.target,
380 });
381
382 Ok(())
383 }
384
385 fn defer_stake_deposit(
386 env: &Env<S>,
387 stake_deposit_input_ptr: u32,
388 stake_deposit_input_len: u32,
389 ) -> Result<(), FuncError> {
390 let serialized_command =
391 env.read_bytes(stake_deposit_input_ptr, stake_deposit_input_len)?;
392 let command =
393 Command::deserialize(&serialized_command).map_err(|e| FuncError::Runtime(e.into()))?;
394
395 if !matches!(command, Command::StakeDeposit { .. }) {
396 return Err(FuncError::Internal);
397 }
398
399 env.context.lock().unwrap().commands.push(DeferredCommand {
400 command,
401 contract_address: env.call_tx.target,
402 });
403
404 Ok(())
405 }
406
407 fn defer_unstake_deposit(
408 env: &Env<S>,
409 unstake_deposit_input_ptr: u32,
410 unstake_deposit_input_len: u32,
411 ) -> Result<(), FuncError> {
412 let serialized_command =
413 env.read_bytes(unstake_deposit_input_ptr, unstake_deposit_input_len)?;
414 let command =
415 Command::deserialize(&serialized_command).map_err(|e| FuncError::Runtime(e.into()))?;
416
417 if !matches!(command, Command::UnstakeDeposit { .. }) {
418 return Err(FuncError::Internal);
419 }
420
421 env.context.lock().unwrap().commands.push(DeferredCommand {
422 command,
423 contract_address: env.call_tx.target,
424 });
425
426 Ok(())
427 }
428
429 fn sha256(
430 env: &Env<S>,
431 msg_ptr: u32,
432 msg_len: u32,
433 digest_ptr_ptr: u32,
434 ) -> Result<(), FuncError> {
435 let input_bytes = env.read_bytes(msg_ptr, msg_len)?;
436
437 env.consume_wasm_gas(gas::CRYPTO_SHA256_PER_BYTE * input_bytes.len() as u64);
438
439 let mut hasher = Sha256::new();
440 sha2::Digest::update(&mut hasher, input_bytes);
441 let digest = hasher.finalize().to_vec();
442
443 env.write_bytes(digest, digest_ptr_ptr)?;
444 Ok(())
445 }
446
447 fn keccak256(
448 env: &Env<S>,
449 msg_ptr: u32,
450 msg_len: u32,
451 digest_ptr_ptr: u32,
452 ) -> Result<(), FuncError> {
453 let input_bytes = env.read_bytes(msg_ptr, msg_len)?;
454 let mut output_bytes = [0u8; 32];
455
456 env.consume_wasm_gas(gas::CRYPTO_KECCAK256_PER_BYTE * input_bytes.len() as u64);
457
458 let mut keccak = Keccak::v256();
459 keccak.update(&input_bytes);
460 keccak.finalize(&mut output_bytes);
461 let digest = output_bytes.to_vec();
462
463 env.write_bytes(digest, digest_ptr_ptr)?;
464 Ok(())
465 }
466
467 fn ripemd(
468 env: &Env<S>,
469 msg_ptr: u32,
470 msg_len: u32,
471 digest_ptr_ptr: u32,
472 ) -> Result<(), FuncError> {
473 let input_bytes = env.read_bytes(msg_ptr, msg_len)?;
474
475 env.consume_wasm_gas(gas::CRYPTO_RIPEMD160_PER_BYTE * input_bytes.len() as u64);
476
477 let mut hasher = Ripemd160::new();
478 hasher.update(&input_bytes);
479 let digest = hasher.finalize().to_vec();
480
481 env.write_bytes(digest, digest_ptr_ptr)?;
482 Ok(())
483 }
484
485 fn verify_ed25519_signature(
486 env: &Env<S>,
487 msg_ptr: u32,
488 msg_len: u32,
489 signature_ptr: u32,
490 address_ptr: u32,
491 ) -> Result<i32, FuncError> {
492 let message = env.read_bytes(msg_ptr, msg_len)?;
493
494 let signature = env.read_bytes(signature_ptr, 64)?;
495
496 let address = env.read_bytes(address_ptr, 32)?;
497
498 env.consume_wasm_gas(gas::crypto_verify_ed25519_signature_cost(message.len()));
499
500 let public_key =
501 ed25519_dalek::PublicKey::from_bytes(&address).map_err(|_| FuncError::Internal)?;
502
503 let dalek_signature =
504 ed25519_dalek::Signature::from_bytes(&signature).map_err(|_| FuncError::Internal)?;
505
506 let result = public_key.verify(&message, &dalek_signature).is_ok();
507
508 Ok(result as i32)
509 }
510}