From 1f183d688a8621ec638c851e710fc9ae99928835 Mon Sep 17 00:00:00 2001 From: Gav <gavin@parity.io> Date: Mon, 8 Jan 2018 17:13:44 +0100 Subject: [PATCH] Introduce basic skeleton for Polkador runtime. --- substrate/executor/src/wasm_executor.rs | 28 ++- substrate/runtime/polkadot/src/lib.rs | 222 ++++++++++++++++++++++-- substrate/runtime/support/src/lib.rs | 87 ++++------ substrate/runtime/test/src/lib.rs | 53 +++++- 4 files changed, 308 insertions(+), 82 deletions(-) diff --git a/substrate/executor/src/wasm_executor.rs b/substrate/executor/src/wasm_executor.rs index f52b2cbf715..4c3ed5b7916 100644 --- a/substrate/executor/src/wasm_executor.rs +++ b/substrate/executor/src/wasm_executor.rs @@ -76,7 +76,7 @@ impl WritePrimitive<u32> for MemoryInstance { } impl_function_executor!(this: FunctionExecutor<'e, E>, - ext_print(utf8_data: *const u8, utf8_len: i32) => { + ext_print(utf8_data: *const u8, utf8_len: u32) => { if let Ok(utf8) = this.memory.get(utf8_data, utf8_len as usize) { if let Ok(message) = String::from_utf8(utf8) { println!("Runtime: {}", message); @@ -96,7 +96,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, println!("memmove {} from {}, {} bytes", dest, src, count); dest }, - ext_memset(dest: *mut u8, val: i32, count: usize) -> *mut u8 => { + ext_memset(dest: *mut u8, val: u32, count: usize) -> *mut u8 => { let _ = this.memory.clear(dest as usize, val as u8, count as usize); println!("memset {} with {}, {} bytes", dest, val, count); dest @@ -110,12 +110,12 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, this.heap.deallocate(addr); println!("free {}", addr) }, - ext_set_storage(key_data: *const u8, key_len: i32, value_data: *const u8, value_len: i32) => { + ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32) => { if let (Ok(key), Ok(value)) = (this.memory.get(key_data, key_len as usize), this.memory.get(value_data, value_len as usize)) { this.ext.set_storage(key, value); } }, - ext_get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8 => { + ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8 => { let (offset, written) = if let Ok(key) = this.memory.get(key_data, key_len as usize) { if let Ok(value) = this.ext.storage(&key) { let offset = this.heap.allocate(value.len() as u32) as u32; @@ -126,6 +126,18 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, this.memory.write_primitive(written_out, written); offset as u32 + }, + ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32) -> u32 => { + if let Ok(key) = this.memory.get(key_data, key_len as usize) { + if let Ok(value) = this.ext.storage(&key) { + let written = ::std::cmp::min(value_len as usize, value.len()); + let _ = this.memory.set(value_data, &value[0..written]); + written as u32 + } else { 0 } + } else { 0 } + }, + ext_deposit_log(_log_data: *const u8, _log_len: u32) { + unimplemented!() } => <'e, E: Externalities + 'e> ); @@ -163,8 +175,8 @@ impl CodeExecutor for WasmExecutor { let returned = program .params_with_external("env", &mut fec) .map(|p| p - .add_argument(I32(offset as i32)) - .add_argument(I32(size as i32))) + .add_argument(I32(offset as u32)) + .add_argument(I32(size as u32))) .and_then(|p| module.execute_export(method, p)) .map_err(|_| -> Error { ErrorKind::Runtime.into() })?; @@ -221,8 +233,8 @@ mod tests { let returned = program .params_with_external("env", &mut fec) .map(|p| p - .add_argument(I32(offset as i32)) - .add_argument(I32(size as i32))) + .add_argument(I32(offset as u32)) + .add_argument(I32(size as u32))) .and_then(|p| module.execute_export("test_data_in", p)) .map_err(|_| -> Error { ErrorKind::Runtime.into() }).expect("function should be callable"); diff --git a/substrate/runtime/polkadot/src/lib.rs b/substrate/runtime/polkadot/src/lib.rs index ec3fabb89cd..da82db8fa2c 100644 --- a/substrate/runtime/polkadot/src/lib.rs +++ b/substrate/runtime/polkadot/src/lib.rs @@ -8,29 +8,215 @@ use alloc::vec::Vec; #[macro_use] extern crate runtime_support; -use runtime_support::{set_storage, code, set_code, storage, validators, set_validators, print}; +use runtime_support::{set_storage, storage, storage_into, print, Value20}; -impl_stub!(test_data_in); -fn test_data_in(input: Vec<u8>) -> Vec<u8> { - print(b"set_storage" as &[u8]); - set_storage(b"input", &input); +/// The hash of an ECDSA pub key which is used to identify an external transactor. +type AccountID = [u8; 32]; +/// The ECDSA pub key of an authority. This is what the external environment/consensus algorithm +/// refers to as a "authority". +type SessionKey = [u8; 65]; +type Balance = u64; +type ChainID = u64; +type Hash = [u8; 32]; +type BlockNumber = u64; +/// A proportion (rational number). +struct Proportion { nom: u64, denom: u64, }; +type Timestamp = u64; +type TxOrder = u64; +/// Statistics concerning consensus. +// TODO. +struct Statistics; +/// A report of bad behaviour. +// TODO. +struct Complaint; - print(b"code" as &[u8]); - set_storage(b"code", &code()); +/// The state of a parachain. +/*struct ParachainState { + head_data: Vec<u8>, + balance: Balance, + user_balances: HashMap<AccountID, Balance>, + balance_downloads: HashMap<AccountID, ( Balance, Vec<u8> ), + egress_roots: Vec<Hash> +}*/ +//struct CandidateReceipt; - print(b"set_code" as &[u8]); - set_code(&input); +// TODO: include RLP implementation +// TODO: add keccak256 (or some better hashing scheme) & ECDSA-recover (or some better sig scheme) - print(b"storage" as &[u8]); - let copy = storage(b"input"); +impl_stub!(execute_block); +fn execute_block(_input: Vec<u8>) -> Vec<u8> { + // TODO: decode block and ensure valid + // TODO: iterate through transactions amd decode/dispatch them + // TODO: progress to next session if it's time + // TODO: progress to next era if it's time + Vec::new() +} + +impl_stub!(execute_transaction); +fn execute_transaction(tx: Vec<u8>) -> Vec<u8> { + environment::execute_transaction(&tx) +} + +/// The current relay chain identifier. +fn chain_id() -> ChainID { unimplemented!() } // TODO: retrieve from external + +mod environment { + /// The current block number being processed. Set by `execute_block`. + pub fn block_number() -> BlockNumber { unimplemented!() } + + /// Get the block hash of a given block. + pub fn block_hash(_number: BlockNumber) -> Hash { unimplemented!() } + + /// ? + fn set_digest(_preserialised_rlp_digest: &[u8]) { unimplemented!() } + + /// Get the current user's ID + pub fn current_user() -> AccountID { unimplemented!() } + + /// Execute a given transaction. + pub fn execute_transaction(_tx: &[u8]) -> Vec<u8> { + // TODO: decode data and ensure valid + // TODO: ensure signature valid and recover id + // TODO: ensure target_function valid + // TODO: decode parameters + // TODO: make call + // TODO: encode any return + Vec::new() + } + + /// Set the new code. + pub fn set_code(new: &[u8]) { + set_storage(b"\0code", new) + } + + /// ? + fn set_active_parachains(_data: &[u8]) { unimplemented!() } +} + +mod consensus { + fn value_vec(mut value: usize, initial: Vec<u8>) -> Vec<u8> { + let mut acc = initial; + while value > 0 { + acc.push(value as u8); + value /= 256; + } + acc + } + + fn set_authority(index: usize, authority: AccountID) { + set_storage(&value_vec(index, b"\0authority".to_vec()), &authority[..]); + } + + fn authority(index: usize) -> AccountID { + storage_into::<Value20>(&value_vec(index, b"\0authority".to_vec())) + } + + fn set_authority_count(count: usize) { + (count..authority_count()).for_each(|i| set_authority(i, &[])); + set_storage(b"\0authority_count", &value_vec(count, Vec::new())); + } + + fn authority_count() -> usize { + storage(b"\0authority_count").into_iter().rev().fold(0, |acc, i| (acc << 8) + (i as usize)) + } + + /// Get the current set of authorities. These are the session keys. + pub fn authorities() -> Vec<AccountID> { + (0..authority_count()).into_iter().map(authority).map.collect() + } - print(b"validators" as &[u8]); - let mut v = validators(); - v.push(copy); + /// Set the current set of authorities' session keys. + /// + /// Called by `next_session` only. + fn set_authorities(authorities: &[AccountID]) { + set_authority_count(authorities.len()); + authorities.iter().enumerate().for_each(|(v, i)| set_authority(v, i)); + } - print(b"set_validators" as &[u8]); - set_validators(&v.iter().map(Vec::as_slice).collect::<Vec<_>>()); + /// Get the current set of validators. These are the long-term identifiers for the validators + /// and will be mapped to a session key with the most recent `set_next_session_key`. + pub fn validators() -> Vec<AccountID> { + unimplemented!() + } - print(b"finished!" as &[u8]); - b"all ok!".to_vec() + /// Set the current set of validators. + /// + /// Called by staking::next_era() only. + pub fn set_validators(_new: &[AccountID]) { + unimplemented!() + } + + /// Flush out any statistics. + pub fn flush_statistics() -> Statistics { unimplemented!() } + + /// Sets the session key of `_validator` to `_session`. This doesn't take effect until the next + /// session. + pub fn set_session_key(_validator: AccountID, _session: AccountID) { + unimplemented!() + } + + /// Move onto next session: register the new authority set. + pub fn next_session() { + // TODO: Call set_authorities(). + unimplemented!() + } +} + +mod staking { + /// The length of a staking era in blocks. + fn era_length() -> BlockNumber { unimplemented!() } + + /// The era has changed - enact new staking set. + /// + /// NOTE: This is always a session change. + fn next_era() { unimplemented!() } + + /// The balance of a given account. + fn balance(_who: AccountID) -> Balance { unimplemented!() } + + /// User-level function to move funds onto a parachain. Calls `parachains::credit_parachain`. + fn move_to_parachain(chain_id: ChainID, value: Balance) { unimplemented!() } + + /// System-level function to be called only by Parachains object when funds have left that + /// object and are to be credited here. + fn credit_staker(value: Balance) { unimplemented!() } + + /// Declare the desire to stake under the requirement that under flawless operation, each era + /// should return `minimum_era_return` on the amount staked. + /// + /// Effects will be felt at the beginning of the next era. + fn stake(minimum_era_return: Proportion) { unimplemented!() } + + /// Retract the desire to stake. + /// + /// Effects will be felt at the beginning of the next era. + fn unstake() { unimplemented!() } + + /// Report invalid behaviour by a staking participant. + fn complain(complaint: Complaint) { unimplemented!() } +} + +/* +mod parachains { + fn chain_ids(self) -> [ ChainID ]; + fn validation_function(self, chain_id: ChainID) -> Fn(consolidated_ingress: [ ( ChainID, bytes ) ], balance_downloads: [ ( AccountID, Balance ) ], block_data: bytes, previous_head_data: bytes) -> (head_data: bytes, egress_queues: [ [ bytes ] ], balance_uploads: [ ( AccountID, Balance ) ]); + fn validate_and_calculate_fees_function(self, chain_id: ChainID) -> Fn(egress_queues: [ [ bytes ] ], balance_uploads: [ ( AccountID, Balance ) ]) -> Balance; + fn balance(self, chain_id: ChainID, id: AccountID) -> Balance; + fn verify_and_consolidate_queues(self, unprocessed_ingress: [ [ [ bytes ] ] ]) -> [ (chain_id: ChainID, message: bytes) ]; + fn chain_state(self, chain_id: ChainID) -> ParachainState; + fn move_to_staking(mut self, chain_id: ChainID, value: Balance); + fn credit_parachain(mut self, chain_id: ChainID, value: Balance); + fn download(mut self, chain_id: ChainID, value: Balance, instruction: bytes); + fn update_heads(mut self, candidate_receipts: &[ ( ChainID, CandidateReceipt ) ]); +} + +mod authentication { + fn validate_signature(self, tx: Transaction) -> ( AccountID, TxOrder ); + fn nonce(self, id: AccountID) -> TxOrder; + fn authenticate(mut self, tx: Transaction) -> AccountID; +} +*/ +mod timestamp { + fn timestamp() -> Timestamp { unimplemented!() } + fn set_timestamp(Timestamp) { unimplemented!() } } diff --git a/substrate/runtime/support/src/lib.rs b/substrate/runtime/support/src/lib.rs index 472497d2845..4430f7249fa 100644 --- a/substrate/runtime/support/src/lib.rs +++ b/substrate/runtime/support/src/lib.rs @@ -17,70 +17,57 @@ pub fn panic_fmt() -> ! { } extern "C" { - fn ext_print(utf8_data: *const u8, utf8_len: i32); + fn ext_print(utf8_data: *const u8, utf8_len: u32); fn ext_print_num(value: u64); - fn ext_set_storage(key_data: *const u8, key_len: i32, value_data: *const u8, value_len: i32); - fn ext_get_allocated_storage(key_data: *const u8, key_len: i32, written_out: *mut i32) -> *mut u8; + fn ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32); + fn ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8; + fn ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32) -> u32; + fn ext_deposit_log(log_data: *const u8, log_len: u32); } pub fn storage(key: &[u8]) -> Vec<u8> { - let mut length: i32 = 0; + let mut length: u32 = 0; unsafe { - let ptr = ext_get_allocated_storage(&key[0], key.len() as i32, &mut length); + let ptr = ext_get_allocated_storage(&key[0], key.len() as u32, &mut length); Vec::from_raw_parts(ptr, length as usize, length as usize) } } -pub fn set_storage(key: &[u8], value: &[u8]) { - unsafe { - ext_set_storage( - &key[0] as *const u8, key.len() as i32, - &value[0] as *const u8, value.len() as i32 - ); - } -} - -pub fn code() -> Vec<u8> { - storage(b"\0code") +pub trait IsValue { + const value: usize; } -pub fn set_code(new: &[u8]) { - set_storage(b"\0code", new) -} +pub struct Value20; impl IsValue for Value20 { const value = 20usize; } +pub struct Value32; impl IsValue for Value32 { const value = 32usize; } +pub struct Value64; impl IsValue for Value64 { const value = 64usize; } +pub struct Value65; impl IsValue for Value65 { const value = 65usize; } -fn value_vec(mut value: usize, initial: Vec<u8>) -> Vec<u8> { - let mut acc = initial; - while value > 0 { - acc.push(value as u8); - value /= 256; +pub fn storage_into<T: IsValue>(key: &[u8]) -> Option<[u8; T::value]> { + let mut result = [0u8; T::value]; + let written = unsafe { + ext_get_storage_into(&key[0], key.len() as u32, &result[0], result.len()) + }; + match written { + T::value => Some(result), + _ => None, } - acc -} - -pub fn set_validator(index: usize, validator: &[u8]) { - set_storage(&value_vec(index, b"\0validator".to_vec()), validator); -} - -pub fn validator(index: usize) -> Vec<u8> { - storage(&value_vec(index, b"\0validator".to_vec())) } -pub fn set_validator_count(count: usize) { - (count..validator_count()).for_each(|i| set_validator(i, &[])); - set_storage(b"\0validator_count", &value_vec(count, Vec::new())); -} - -pub fn validator_count() -> usize { - storage(b"\0validator_count").into_iter().rev().fold(0, |acc, i| (acc << 8) + (i as usize)) -} - -pub fn validators() -> Vec<Vec<u8>> { - (0..validator_count()).into_iter().map(validator).collect() +pub fn set_storage(key: &[u8], value: &[u8]) { + unsafe { + ext_set_storage( + &key[0] as *const u8, key.len() as u32, + &value[0] as *const u8, value.len() as u32 + ); + } } -pub fn set_validators(validators: &[&[u8]]) { - set_validator_count(validators.len()); - validators.iter().enumerate().for_each(|(v, i)| set_validator(v, i)); +pub fn deposit_log(log: &[u8]) { + unsafe { + ext_deposit_log( + &log[0] as *const u8, log.len() as u32, + ) + } } pub trait Printable { @@ -90,7 +77,7 @@ pub trait Printable { impl<'a> Printable for &'a [u8] { fn print(self) { unsafe { - ext_print(&self[0] as *const u8, self.len() as i32); + ext_print(&self[0] as *const u8, self.len() as u32); } } } @@ -109,12 +96,10 @@ pub fn print<T: Printable + Sized>(value: T) { macro_rules! impl_stub { ($name:ident) => { pub mod _internal { - extern crate alloc; - #[no_mangle] pub fn $name(input_data: *mut u8, input_len: usize) -> u64 { let input = unsafe { - ::alloc::vec::Vec::from_raw_parts(input_data, input_len, input_len) + super::alloc::vec::Vec::from_raw_parts(input_data, input_len, input_len) }; let output = super::$name(input); diff --git a/substrate/runtime/test/src/lib.rs b/substrate/runtime/test/src/lib.rs index ec3fabb89cd..550e06c05dd 100644 --- a/substrate/runtime/test/src/lib.rs +++ b/substrate/runtime/test/src/lib.rs @@ -8,7 +8,50 @@ use alloc::vec::Vec; #[macro_use] extern crate runtime_support; -use runtime_support::{set_storage, code, set_code, storage, validators, set_validators, print}; +use runtime_support::{set_storage, storage, print}; + +pub fn code() -> Vec<u8> { + storage(b"\0code") +} + +pub fn set_code(new: &[u8]) { + set_storage(b"\0code", new) +} + +fn value_vec(mut value: usize, initial: Vec<u8>) -> Vec<u8> { + let mut acc = initial; + while value > 0 { + acc.push(value as u8); + value /= 256; + } + acc +} + +pub fn set_authority(index: usize, authority: &[u8]) { + set_storage(&value_vec(index, b"\0authority".to_vec()), authority); +} + +pub fn authority(index: usize) -> Vec<u8> { + storage(&value_vec(index, b"\0authority".to_vec())) +} + +pub fn set_authority_count(count: usize) { + (count..authority_count()).for_each(|i| set_authority(i, &[])); + set_storage(b"\0authority_count", &value_vec(count, Vec::new())); +} + +pub fn authority_count() -> usize { + storage(b"\0authority_count").into_iter().rev().fold(0, |acc, i| (acc << 8) + (i as usize)) +} + +pub fn authorities() -> Vec<Vec<u8>> { + (0..authority_count()).into_iter().map(authority).collect() +} + +pub fn set_authorities(authorities: &[&[u8]]) { + set_authority_count(authorities.len()); + authorities.iter().enumerate().for_each(|(v, i)| set_authority(v, i)); +} impl_stub!(test_data_in); fn test_data_in(input: Vec<u8>) -> Vec<u8> { @@ -24,12 +67,12 @@ fn test_data_in(input: Vec<u8>) -> Vec<u8> { print(b"storage" as &[u8]); let copy = storage(b"input"); - print(b"validators" as &[u8]); - let mut v = validators(); + print(b"authorities" as &[u8]); + let mut v = authorities(); v.push(copy); - print(b"set_validators" as &[u8]); - set_validators(&v.iter().map(Vec::as_slice).collect::<Vec<_>>()); + print(b"set_authorities" as &[u8]); + set_authorities(&v.iter().map(Vec::as_slice).collect::<Vec<_>>()); print(b"finished!" as &[u8]); b"all ok!".to_vec() -- GitLab