diff --git a/c/generator.c b/c/generator.c index 2b509849a..c67e0742b 100644 --- a/c/generator.c +++ b/c/generator.c @@ -17,6 +17,7 @@ #define GW_SYS_STORE 3051 #define GW_SYS_LOAD 3052 #define GW_SYS_SET_RETURN_DATA 3061 +#define GW_SYS_CREATE 3071 /* internal syscall only for generator */ #define GW_SYS_LOAD_CALLCONTEXT 4051 #define GW_SYS_LOAD_BLOCKINFO 4052 @@ -182,6 +183,13 @@ int sys_call(void *ctx, uint32_t to_id, uint8_t *args, uint32_t args_len, return 0; } +int sys_create(void *ctx, + uint8_t *script, + uint32_t script_len, + gw_call_receipt_t *receipt) { + return syscall(GW_SYS_CREATE, script, script_len, 0, 0, 0, 0); +} + int main() { int ret; uint8_t code_buffer[CODE_SIZE] __attribute__((aligned(RISCV_PGSIZE))); @@ -194,6 +202,7 @@ int main() { context.sys_store = sys_store; context.sys_set_program_return_data = sys_set_program_return_data; context.sys_call = sys_call; + context.sys_create = sys_create; context.sys_get_account_id_by_script_hash = sys_get_account_id_by_script_hash; context.sys_get_script_hash_by_account_id = sys_get_script_hash_by_account_id; context.sys_get_account_script = sys_get_account_script; diff --git a/c/gw_def.h b/c/gw_def.h index 7a3c04f02..8f7006d85 100644 --- a/c/gw_def.h +++ b/c/gw_def.h @@ -26,28 +26,29 @@ typedef struct { /** * Load value by key from current contract account * - * @param ctx The godwoken context - * @param account_id - * @param args - * @param args_len - * @param receipt Receipt of this function call - * @return The status code, 0 is success + * @param ctx The godwoken context + * @param account_id The account id to call + * @param args The arguments data for the call + * @param args_len The length of the arguments data + * @param receipt Receipt of this function call + * @return The status code, 0 is success */ -typedef int (*gw_call_fn)(void *ctx, uint32_t account_id, uint8_t *args, - uint32_t args_len, gw_call_receipt_t *receipt); +typedef int (*gw_call_fn)(void *ctx, + uint32_t account_id, + uint8_t *args, + uint32_t args_len, + gw_call_receipt_t *receipt); /** * Create a new account * - * @param ctx The godwoken context - * @param script Contract's script - * @param len Length of script structure - * @param receipt Receipt of this constructor call - * @return The status code, 0 is success + * @param ctx The godwoken context + * @param script Contract's script (MUST be valid molecule format CKB Script) + * @param script_len Length of script structure + * @param receipt Receipt of this function call + * @return The status code, 0 is success */ -typedef int (*gw_create_fn)(void *ctx, uint8_t *script, - uint32_t len, gw_call_receipt_t *receipt); - +typedef int (*gw_create_fn)(void *ctx, uint8_t *script, uint32_t script_len, gw_call_receipt_t *receipt); /** * Load value by key from current contract account diff --git a/crates/generator/src/syscalls/mod.rs b/crates/generator/src/syscalls/mod.rs index ad4cdd370..5bd64f089 100644 --- a/crates/generator/src/syscalls/mod.rs +++ b/crates/generator/src/syscalls/mod.rs @@ -1,12 +1,24 @@ -use crate::traits::CodeStore; +use crate::traits::{CodeStore, StateExt}; use ckb_vm::{ memory::{Memory, FLAG_EXECUTABLE, FLAG_FREEZED}, registers::{A0, A1, A2, A3, A4, A7}, Error as VMError, Register, SupportMachine, Syscalls, }; -use gw_common::{state::State, H256}; +use gw_common::{ + state::{ + State, + GW_ACCOUNT_NONCE, + GW_ACCOUNT_SCRIPT_HASH, + build_account_key, + build_account_field_key, + build_script_hash_to_account_id_key, + }, + H256, + h256_ext::H256Ext, +}; use gw_types::{ - packed::{BlockInfo, CallContext}, + bytes::Bytes, + packed::{BlockInfo, CallContext, Script}, prelude::*, }; use std::cmp; @@ -19,6 +31,7 @@ const MAX_SET_RETURN_DATA_SIZE: u64 = 1024; const SYS_STORE: u64 = 3051; const SYS_LOAD: u64 = 3052; const SYS_SET_RETURN_DATA: u64 = 3061; +const SYS_CREATE: u64 = 3071; /* internal syscall numbers */ const SYS_LOAD_CALLCONTEXT: u64 = 4051; const SYS_LOAD_BLOCKINFO: u64 = 4052; @@ -39,6 +52,8 @@ pub struct RunResult { pub read_values: HashMap, pub write_values: HashMap, pub return_data: Vec, + pub account_count: Option, + pub new_scripts: HashMap>, } pub(crate) struct L2Syscalls<'a, S> { @@ -126,15 +141,7 @@ impl<'a, S: State, Mac: SupportMachine> Syscalls for L2Syscalls<'a, S> { let key_addr = machine.registers()[A0].to_u64(); let key = load_data_h256(machine, key_addr)?; let value_addr = machine.registers()[A1].to_u64(); - let value = match self.result.write_values.get(&key) { - Some(value) => *value, - None => { - let tree_value = - self.state.get_raw(&key).map_err(|_| VMError::Unexpected)?; - self.result.read_values.insert(key, tree_value); - tree_value - } - }; + let value = self.get_raw(&key)?; machine .memory_mut() .store_bytes(value_addr, &value.as_slice())?; @@ -152,6 +159,37 @@ impl<'a, S: State, Mac: SupportMachine> Syscalls for L2Syscalls<'a, S> { machine.set_register(A0, Mac::REG::from_u8(SUCCESS)); Ok(true) } + SYS_CREATE => { + let script_addr = machine.registers()[A0].to_u64(); + let script_len = machine.registers()[A1].to_u32(); + + let script_data = load_bytes(machine, script_addr, script_len as usize)?; + let script = Script::from_slice(&script_data[..]).map_err(|err| { + eprintln!("syscall error: invalid script to create : {:?}", err); + VMError::Unexpected + })?; + let script_hash = script.hash(); + + // Same logic from State::create_account() + let id = self.get_account_count()?; + self.result.write_values.insert( + build_account_field_key(id, GW_ACCOUNT_NONCE).into(), + H256::zero(), + ); + self.result.write_values.insert( + build_account_field_key(id, GW_ACCOUNT_SCRIPT_HASH).into(), + script_hash.into(), + ); + // script hash to id + self.result.write_values.insert( + build_script_hash_to_account_id_key(&script_hash[..]).into(), + H256::from_u32(id), + ); + self.result.new_scripts.insert(script_hash.into(), script.as_slice().to_vec()); + self.set_account_count(id+1); + machine.set_register(A0, Mac::REG::from_u8(SUCCESS)); + Ok(true) + } SYS_LOAD_BLOCKINFO => { let data = self.block_info.as_slice(); store_data(machine, data)?; @@ -169,10 +207,8 @@ impl<'a, S: State, Mac: SupportMachine> Syscalls for L2Syscalls<'a, S> { let account_id_addr = machine.registers()[A1].to_u64(); let script_hash = load_data_h256(machine, script_hash_addr)?; let account_id = self - .state .get_account_id_by_script_hash(&script_hash) .map_err(|err| { - eprintln!("syscall error: get account id by script hash : {:?}", err); VMError::Unexpected })? .ok_or_else(|| { @@ -188,10 +224,11 @@ impl<'a, S: State, Mac: SupportMachine> Syscalls for L2Syscalls<'a, S> { SYS_LOAD_SCRIPT_HASH_BY_ACCOUNT_ID => { let account_id = machine.registers()[A0].to_u32(); let script_hash_addr = machine.registers()[A1].to_u64(); - let script_hash = self.state.get_script_hash(account_id).map_err(|err| { - eprintln!("syscall error: get script hash by account id: {:?}", err); - VMError::Unexpected - })?; + let script_hash = self + .get_script_hash(account_id).map_err(|err| { + eprintln!("syscall error: get script hash by account id: {:?}", err); + VMError::Unexpected + })?; machine .memory_mut() .store_bytes(script_hash_addr, script_hash.as_slice())?; @@ -203,12 +240,12 @@ impl<'a, S: State, Mac: SupportMachine> Syscalls for L2Syscalls<'a, S> { let len_addr = machine.registers()[A1].to_u64(); let offset = machine.registers()[A2].to_u32() as usize; let script_addr = machine.registers()[A3].to_u64(); - let script_hash = self.state.get_script_hash(account_id).map_err(|err| { + let script_hash = self.get_script_hash(account_id).map_err(|err| { eprintln!("syscall error: get script hash by account id: {:?}", err); VMError::Unexpected })?; let len = load_data_u32(machine, len_addr)? as usize; - let script = self.code_store.get_script(&script_hash).ok_or_else(|| { + let script = self.get_script(&script_hash).ok_or_else(|| { eprintln!( "syscall error: script not found by script hash: {:?}", script_hash @@ -252,19 +289,79 @@ impl<'a, S: State, Mac: SupportMachine> Syscalls for L2Syscalls<'a, S> { } impl<'a, S: State> L2Syscalls<'a, S> { - fn load_program_as_code(&self, machine: &mut Mac) -> Result<(), VMError> { + fn get_raw(&mut self, key: &H256) -> Result { + let value = match self.result.write_values.get(&key) { + Some(value) => *value, + None => { + let tree_value = + self.state.get_raw(&key).map_err(|_| VMError::Unexpected)?; + self.result.read_values.insert(*key, tree_value); + tree_value + } + }; + Ok(value) + } + fn get_account_count(&self) -> Result { + if let Some(id) = self.result.account_count { + Ok(id) + } else { + self.state + .get_account_count() + .map_err(|err| { + eprintln!("syscall error: get account count : {:?}", err); + VMError::Unexpected + }) + } + } + fn set_account_count(&mut self, count: u32) -> Result<(), VMError> { + self.result.account_count = Some(count); + Ok(()) + } + fn get_script(&self, script_hash: &H256) -> Option