mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-14 02:45:50 +00:00
Introduce basic skeleton for Polkadot runtime. (#32)
* Introduce basic skeleton for Polkador runtime. * Clean up the runtime skeleton. * Make initial runtime skeleton compile. * Compile polkadot-runtime both for Wasm ad native, allowing for testing and direct usage. * More fleshing out on runtime. * Update native support. * Fix warning. * Update gitignore * Update path. * Fix path. * Remove accidentally committed files. * Add wasm binaries. * Fix test. * Native storage support API. * Add environmental module * Add native environment to make native source-code compatible with wasm. Also tests. * Finish up & polish environment stuff. * Avoid using reentrancy issues. * Add some docs and a test. * Remove unneeded function. * Documentation * Tweak docs * Remove TODOs. * Balance transfers + util methods. * Rejig tests and ensure authorities are addressed consistently. * Add marshaller for xfer function * Transaction dispatch test. * Minor fix. * Add test for ser/de transaction. * Add ser/de for header. * Add tests for header ser/de * Introduce basic block decoding/execution framework. * Introduce block decoding/execution framework (p2) * Big refactor. * Split out joiner. * Hide away support modules. * Fix up wasm runtime. * use externalities for chain_id * Clean up (Test)Externalities. * Repot and introduce keccak-256 external. * Signing with crypto. * fix unsafety hole in environmental using function * Introduce Ed25519 crypto. * Repotting. * Add ed25519_verify external. * Introduce Ed25519 verify as an external. * fix unsafety hole around unwinding * Compile fixes. * use new environmental API * Tests for ed25519 verify. * Polish * Introduce UncheckedTransaction & test. * Implement basic block and tx processing * Introduce static hex and valid signature for block test. * Repot session. * comments. * Refactor and timestamp test * Remove fluff * Remove fluff. * Staking eras and tests. * Implement sessions. * Polish * Test sessions. * Introduce better hashing. - Blake2 for secure hashing - XX for fast hashing * Fix tests. * Introduce staking. * Tests for simple staking system. * Build fix for wasm. * Fix tests. * Repotting and docs. * Docs and licence. * Documentation. * Remove superfluous code. * Remove dummy key. * Remove other superfluous file. * Optimise with swap_remove
This commit is contained in:
committed by
Arkadiy Paronyan
parent
28d84d8ac4
commit
3402f169a7
@@ -0,0 +1,78 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Environment API: Allows certain information to be accessed throughout the runtime.
|
||||
|
||||
use runtime_support::{Rc, RefCell, transmute, Box};
|
||||
use primitives::{BlockNumber, Digest};
|
||||
|
||||
#[derive(Default)]
|
||||
/// The information that can be accessed globally.
|
||||
pub struct Environment {
|
||||
/// The current block number.
|
||||
pub block_number: BlockNumber,
|
||||
/// The current block digest.
|
||||
pub digest: Digest,
|
||||
/// The number of log items in this block that have been accounted for so far.
|
||||
pub next_log_index: usize,
|
||||
}
|
||||
|
||||
/// Do something with the environment and return its value. Keep the function short.
|
||||
pub fn with_env<T, F: FnOnce(&mut Environment) -> T>(f: F) -> T {
|
||||
let e = env();
|
||||
let mut eb = e.borrow_mut();
|
||||
f(&mut *eb)
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
fn env() -> Rc<RefCell<Environment>> {
|
||||
// Initialize it to a null value
|
||||
static mut SINGLETON: *const Rc<RefCell<Environment>> = 0 as *const Rc<RefCell<Environment>>;
|
||||
|
||||
unsafe {
|
||||
if SINGLETON == 0 as *const Rc<RefCell<Environment>> {
|
||||
// Make it
|
||||
let singleton: Rc<RefCell<Environment>> = Rc::new(RefCell::new(Default::default()));
|
||||
|
||||
// Put it in the heap so it can outlive this call
|
||||
SINGLETON = transmute(Box::new(singleton));
|
||||
}
|
||||
|
||||
// Now we give out a copy of the data that is safe to use concurrently.
|
||||
(*SINGLETON).clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn env() -> Rc<RefCell<Environment>> {
|
||||
// Initialize it to a null value
|
||||
thread_local!{
|
||||
static SINGLETON: RefCell<*const Rc<RefCell<Environment>>> = RefCell::new(0 as *const Rc<RefCell<Environment>>);
|
||||
}
|
||||
|
||||
SINGLETON.with(|s| unsafe {
|
||||
if *s.borrow() == 0 as *const Rc<RefCell<Environment>> {
|
||||
// Make it
|
||||
let singleton: Rc<RefCell<Environment>> = Rc::new(RefCell::new(Default::default()));
|
||||
|
||||
// Put it in the heap so it can outlive this call
|
||||
*s.borrow_mut() = transmute(Box::new(singleton));
|
||||
}
|
||||
|
||||
// Now we give out a copy of the data that is safe to use concurrently.
|
||||
(**s.borrow()).clone()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Function data: This describes a function that can be called from an external transaction.
|
||||
|
||||
use primitives::AccountID;
|
||||
use streamreader::StreamReader;
|
||||
use runtime::{staking, session, timestamp};
|
||||
|
||||
/// The functions that a transaction can call (and be dispatched to).
|
||||
#[cfg_attr(test, derive(PartialEq, Debug))]
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Function {
|
||||
StakingStake,
|
||||
StakingUnstake,
|
||||
StakingTransfer,
|
||||
SessionSetKey,
|
||||
TimestampSet,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
/// Derive `Some` value from a `u8`, or `None` if it's invalid.
|
||||
pub fn from_u8(value: u8) -> Option<Function> {
|
||||
match value {
|
||||
x if x == Function::StakingStake as u8 => Some(Function::StakingStake),
|
||||
x if x == Function::StakingUnstake as u8 => Some(Function::StakingUnstake),
|
||||
x if x == Function::StakingTransfer as u8 => Some(Function::StakingTransfer),
|
||||
x if x == Function::SessionSetKey as u8 => Some(Function::SessionSetKey),
|
||||
x if x == Function::TimestampSet as u8 => Some(Function::TimestampSet),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Function {
|
||||
/// Dispatch the function.
|
||||
pub fn dispatch(&self, transactor: &AccountID, data: &[u8]) {
|
||||
let mut params = StreamReader::new(data);
|
||||
match *self {
|
||||
Function::StakingStake => {
|
||||
staking::stake(transactor);
|
||||
}
|
||||
Function::StakingUnstake => {
|
||||
staking::unstake(transactor);
|
||||
}
|
||||
Function::StakingTransfer => {
|
||||
let dest = params.read().unwrap();
|
||||
let value = params.read().unwrap();
|
||||
staking::transfer(transactor, &dest, value);
|
||||
}
|
||||
Function::SessionSetKey => {
|
||||
let session = params.read().unwrap();
|
||||
session::set_key(transactor, &session);
|
||||
}
|
||||
Function::TimestampSet => {
|
||||
let t = params.read().unwrap();
|
||||
timestamp::set(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Support code for the runtime.
|
||||
|
||||
pub mod primitives;
|
||||
pub mod function;
|
||||
pub mod environment;
|
||||
pub mod storable;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod statichex;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
pub mod testing;
|
||||
@@ -0,0 +1,515 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Primitive types.
|
||||
|
||||
use runtime_support::Vec;
|
||||
use streamreader::StreamReader;
|
||||
use joiner::Joiner;
|
||||
use slicable::{Slicable, NonTrivialSlicable};
|
||||
use function::Function;
|
||||
use runtime_support::{size_of, blake2_256, twox_128, twox_256, ed25519_verify};
|
||||
|
||||
#[cfg(test)]
|
||||
use std::fmt;
|
||||
|
||||
/// The Ed25519 pubkey that identifies an account.
|
||||
pub type AccountID = [u8; 32];
|
||||
/// The Ed25519 pub key of an session that belongs to an authority. This is used as what the
|
||||
/// external environment/consensus algorithm calls an "authority".
|
||||
pub type SessionKey = AccountID;
|
||||
/// Indentifier for a chain.
|
||||
pub type ChainID = u64;
|
||||
/// Index of a block in the chain.
|
||||
pub type BlockNumber = u64;
|
||||
/// Index of a transaction.
|
||||
pub type TxOrder = u64;
|
||||
/// A hash of some data.
|
||||
pub type Hash = [u8; 32];
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
#[cfg_attr(test, derive(PartialEq, Debug))]
|
||||
/// The digest of a block, useful for light-clients.
|
||||
pub struct Digest {
|
||||
/// All logs that have happened in the block.
|
||||
pub logs: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(test, derive(PartialEq, Debug))]
|
||||
/// The header for a block.
|
||||
pub struct Header {
|
||||
/// The parent block's "hash" (actually the Blake2-256 hash of its serialised header).
|
||||
pub parent_hash: Hash,
|
||||
/// The block's number (how many ancestors does it have?).
|
||||
pub number: BlockNumber,
|
||||
/// The root of the trie that represents this block's final storage map.
|
||||
pub state_root: Hash,
|
||||
/// The root of the trie that represents this block's transactions, indexed by a 32-bit integer.
|
||||
pub transaction_root: Hash,
|
||||
/// The digest for this block.
|
||||
pub digest: Digest,
|
||||
}
|
||||
|
||||
impl Slicable for Header {
|
||||
fn from_slice(value: &[u8]) -> Option<Self> {
|
||||
let mut reader = StreamReader::new(value);
|
||||
Some(Header {
|
||||
parent_hash: reader.read()?,
|
||||
number: reader.read()?,
|
||||
state_root: reader.read()?,
|
||||
transaction_root: reader.read()?,
|
||||
digest: Digest { logs: reader.read()?, },
|
||||
})
|
||||
}
|
||||
|
||||
fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(_fill_slice: F) -> Option<Self> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn to_vec(&self) -> Vec<u8> {
|
||||
Vec::new()
|
||||
.join(&self.parent_hash)
|
||||
.join(&self.number)
|
||||
.join(&self.state_root)
|
||||
.join(&self.transaction_root)
|
||||
.join(&self.digest.logs)
|
||||
}
|
||||
|
||||
fn size_of(data: &[u8]) -> Option<usize> {
|
||||
let first_part = size_of::<Hash>() + size_of::<BlockNumber>() + size_of::<Hash>() + size_of::<Hash>();
|
||||
let second_part = <Vec<Vec<u8>>>::size_of(&data[first_part..])?;
|
||||
Some(first_part + second_part)
|
||||
}
|
||||
}
|
||||
|
||||
impl NonTrivialSlicable for Header {}
|
||||
|
||||
#[cfg_attr(test, derive(PartialEq, Debug))]
|
||||
/// A vetted and verified transaction from the external world.
|
||||
pub struct Transaction {
|
||||
/// Who signed it (note this is not a signature).
|
||||
pub signed: AccountID,
|
||||
/// The number of transactions have come before from the same signer.
|
||||
pub nonce: TxOrder,
|
||||
/// The function that should be called.
|
||||
pub function: Function,
|
||||
/// Serialised input data to the function.
|
||||
pub input_data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Slicable for Transaction {
|
||||
fn from_slice(value: &[u8]) -> Option<Self> {
|
||||
let mut reader = StreamReader::new(value);
|
||||
Some(Transaction {
|
||||
signed: reader.read()?,
|
||||
nonce: reader.read()?,
|
||||
function: Function::from_u8(reader.read()?)?,
|
||||
input_data: reader.read()?,
|
||||
})
|
||||
}
|
||||
|
||||
fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(_fill_slice: F) -> Option<Self> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn to_vec(&self) -> Vec<u8> {
|
||||
Vec::new()
|
||||
.join(&self.signed)
|
||||
.join(&self.nonce)
|
||||
.join(&(self.function as u8))
|
||||
.join(&self.input_data)
|
||||
}
|
||||
|
||||
fn size_of(data: &[u8]) -> Option<usize> {
|
||||
let first_part = size_of::<AccountID>() + size_of::<TxOrder>() + size_of::<u8>();
|
||||
let second_part = <Vec<u8>>::size_of(&data[first_part..])?;
|
||||
Some(first_part + second_part)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Hashable: Sized {
|
||||
fn blake2_256(&self) -> [u8; 32];
|
||||
fn twox_128(&self) -> [u8; 16];
|
||||
fn twox_256(&self) -> [u8; 32];
|
||||
}
|
||||
|
||||
impl<T: Slicable> Hashable for T {
|
||||
fn blake2_256(&self) -> [u8; 32] {
|
||||
blake2_256(&self.to_vec())
|
||||
}
|
||||
fn twox_128(&self) -> [u8; 16] {
|
||||
twox_128(&self.to_vec())
|
||||
}
|
||||
fn twox_256(&self) -> [u8; 32] {
|
||||
twox_256(&self.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl NonTrivialSlicable for Transaction {}
|
||||
|
||||
/// A transactions right from the external world. Unchecked.
|
||||
pub struct UncheckedTransaction {
|
||||
/// The actual transaction information.
|
||||
pub transaction: Transaction,
|
||||
/// The signature; should be an Ed25519 signature applied to the serialised `transaction` field.
|
||||
pub signature: [u8; 64],
|
||||
}
|
||||
|
||||
impl UncheckedTransaction {
|
||||
/// Verify the signature.
|
||||
pub fn ed25519_verify(&self) -> bool {
|
||||
let msg = self.transaction.to_vec();
|
||||
ed25519_verify(&self.signature, &msg, &self.transaction.signed)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl PartialEq for UncheckedTransaction {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.signature.iter().eq(other.signature.iter()) && self.transaction == other.transaction
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl fmt::Debug for UncheckedTransaction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "UncheckedTransaction({:?})", self.transaction)
|
||||
}
|
||||
}
|
||||
|
||||
impl Slicable for UncheckedTransaction {
|
||||
fn from_slice(value: &[u8]) -> Option<Self> {
|
||||
let mut reader = StreamReader::new(value);
|
||||
Some(UncheckedTransaction {
|
||||
signature: reader.read()?,
|
||||
transaction: reader.read()?,
|
||||
})
|
||||
}
|
||||
|
||||
fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(_fill_slice: F) -> Option<Self> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn to_vec(&self) -> Vec<u8> {
|
||||
Vec::new()
|
||||
.join(&self.signature)
|
||||
.join(&self.transaction)
|
||||
}
|
||||
|
||||
fn size_of(data: &[u8]) -> Option<usize> {
|
||||
let first_part = size_of::<[u8; 64]>();
|
||||
let second_part = <Transaction>::size_of(&data[first_part..])?;
|
||||
Some(first_part + second_part)
|
||||
}
|
||||
}
|
||||
|
||||
impl NonTrivialSlicable for UncheckedTransaction {}
|
||||
|
||||
#[cfg_attr(test, derive(PartialEq, Debug))]
|
||||
/// A Polkadot relay chain block.
|
||||
pub struct Block {
|
||||
/// The header of the block.
|
||||
pub header: Header,
|
||||
/// All transactions.
|
||||
pub transactions: Vec<UncheckedTransaction>,
|
||||
}
|
||||
|
||||
impl Slicable for Block {
|
||||
fn from_slice(value: &[u8]) -> Option<Self> {
|
||||
let mut reader = StreamReader::new(value);
|
||||
Some(Block {
|
||||
header: reader.read()?,
|
||||
transactions: reader.read()?,
|
||||
})
|
||||
}
|
||||
|
||||
fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(_fill_slice: F) -> Option<Self> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn to_vec(&self) -> Vec<u8> {
|
||||
Vec::new()
|
||||
.join(&self.header)
|
||||
.join(&self.transactions)
|
||||
}
|
||||
|
||||
fn size_of(data: &[u8]) -> Option<usize> {
|
||||
let first_part = Header::size_of(data)?;
|
||||
let second_part = <Vec<Transaction>>::size_of(&data[first_part..])?;
|
||||
Some(first_part + second_part)
|
||||
}
|
||||
}
|
||||
|
||||
impl NonTrivialSlicable for Block {}
|
||||
|
||||
impl<T: Slicable> NonTrivialSlicable for Vec<T> where Vec<T>: Slicable {}
|
||||
|
||||
impl<T: NonTrivialSlicable> Slicable for Vec<T> {
|
||||
fn from_slice(value: &[u8]) -> Option<Self> {
|
||||
let len = Self::size_of(&value[0..4])?;
|
||||
let mut off = 4;
|
||||
let mut r = Vec::new();
|
||||
while off < len {
|
||||
let element_len = T::size_of(&value[off..])?;
|
||||
r.push(T::from_slice(&value[off..off + element_len])?);
|
||||
off += element_len;
|
||||
}
|
||||
Some(r)
|
||||
}
|
||||
|
||||
fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(_fill_slice: F) -> Option<Self> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn to_vec(&self) -> Vec<u8> {
|
||||
let vecs = self.iter().map(Slicable::to_vec).collect::<Vec<_>>();
|
||||
let len = vecs.iter().fold(0, |mut a, v| {a += v.len(); a});
|
||||
let mut r = Vec::new().join(&(len as u32));
|
||||
vecs.iter().for_each(|v| r.extend_from_slice(v));
|
||||
r
|
||||
}
|
||||
|
||||
fn size_of(data: &[u8]) -> Option<usize> {
|
||||
u32::from_slice(&data[0..4]).map(|i| (i + 4) as usize)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use joiner::Joiner;
|
||||
use function::Function;
|
||||
|
||||
#[test]
|
||||
fn serialise_transaction_works() {
|
||||
let one: AccountID = [1u8; 32];
|
||||
let two: AccountID = [2u8; 32];
|
||||
let tx = Transaction {
|
||||
signed: one.clone(),
|
||||
nonce: 69,
|
||||
function: Function::StakingTransfer,
|
||||
input_data: Vec::new().join(&two).join(&69u64),
|
||||
};
|
||||
let serialised = tx.to_vec();
|
||||
assert_eq!(serialised, vec![
|
||||
1u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
69, 0, 0, 0, 0, 0, 0, 0,
|
||||
2,
|
||||
40, 0, 0, 0,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
69, 0, 0, 0, 0, 0, 0, 0
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialise_transaction_works() {
|
||||
let one: AccountID = [1u8; 32];
|
||||
let two: AccountID = [2u8; 32];
|
||||
let tx = Transaction {
|
||||
signed: one.clone(),
|
||||
nonce: 69,
|
||||
function: Function::StakingTransfer,
|
||||
input_data: Vec::new().join(&two).join(&69u64),
|
||||
};
|
||||
let data = [
|
||||
1u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
69, 0, 0, 0, 0, 0, 0, 0,
|
||||
2,
|
||||
40, 0, 0, 0,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
69, 0, 0, 0, 0, 0, 0, 0
|
||||
];
|
||||
let deserialised = Transaction::from_slice(&data).unwrap();
|
||||
assert_eq!(deserialised, tx);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialise_header_works() {
|
||||
let h = Header {
|
||||
parent_hash: [4u8; 32],
|
||||
number: 42,
|
||||
state_root: [5u8; 32],
|
||||
transaction_root: [6u8; 32],
|
||||
digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], },
|
||||
};
|
||||
let serialised = h.to_vec();
|
||||
assert_eq!(serialised, vec![
|
||||
4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
42, 0, 0, 0, 0, 0, 0, 0,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
26, 0, 0, 0,
|
||||
7, 0, 0, 0,
|
||||
111, 110, 101, 32, 108, 111, 103,
|
||||
11, 0, 0, 0,
|
||||
97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialise_header_works() {
|
||||
let h = Header {
|
||||
parent_hash: [4u8; 32],
|
||||
number: 42,
|
||||
state_root: [5u8; 32],
|
||||
transaction_root: [6u8; 32],
|
||||
digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], },
|
||||
};
|
||||
let data = [
|
||||
4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
42, 0, 0, 0, 0, 0, 0, 0,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
26, 0, 0, 0,
|
||||
7, 0, 0, 0,
|
||||
111, 110, 101, 32, 108, 111, 103,
|
||||
11, 0, 0, 0,
|
||||
97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103
|
||||
];
|
||||
let deserialised = Header::from_slice(&data).unwrap();
|
||||
assert_eq!(deserialised, h);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialise_block_works() {
|
||||
let one: AccountID = [1u8; 32];
|
||||
let two: AccountID = [2u8; 32];
|
||||
let tx1 = UncheckedTransaction {
|
||||
transaction: Transaction {
|
||||
signed: one.clone(),
|
||||
nonce: 69,
|
||||
function: Function::StakingTransfer,
|
||||
input_data: Vec::new().join(&two).join(&69u64),
|
||||
},
|
||||
signature: [1u8; 64],
|
||||
};
|
||||
let tx2 = UncheckedTransaction {
|
||||
transaction: Transaction {
|
||||
signed: two.clone(),
|
||||
nonce: 42,
|
||||
function: Function::StakingStake,
|
||||
input_data: Vec::new(),
|
||||
},
|
||||
signature: [2u8; 64],
|
||||
};
|
||||
let h = Header {
|
||||
parent_hash: [4u8; 32],
|
||||
number: 42,
|
||||
state_root: [5u8; 32],
|
||||
transaction_root: [6u8; 32],
|
||||
digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], },
|
||||
};
|
||||
let b = Block {
|
||||
header: h,
|
||||
transactions: vec![tx1, tx2],
|
||||
};
|
||||
let serialised = b.to_vec();
|
||||
assert_eq!(serialised, vec![
|
||||
// header
|
||||
4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
42, 0, 0, 0, 0, 0, 0, 0,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
26, 0, 0, 0,
|
||||
7, 0, 0, 0,
|
||||
111, 110, 101, 32, 108, 111, 103,
|
||||
11, 0, 0, 0,
|
||||
97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103,
|
||||
// transactions
|
||||
2, 1, 0, 0,
|
||||
// tx1
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
69, 0, 0, 0, 0, 0, 0, 0,
|
||||
2,
|
||||
40, 0, 0, 0,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
69, 0, 0, 0, 0, 0, 0, 0,
|
||||
// tx2
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
42, 0, 0, 0, 0, 0, 0, 0,
|
||||
0,
|
||||
0, 0, 0, 0
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialise_block_works() {
|
||||
let one: AccountID = [1u8; 32];
|
||||
let two: AccountID = [2u8; 32];
|
||||
let tx1 = UncheckedTransaction {
|
||||
transaction: Transaction {
|
||||
signed: one.clone(),
|
||||
nonce: 69,
|
||||
function: Function::StakingTransfer,
|
||||
input_data: Vec::new().join(&two).join(&69u64),
|
||||
},
|
||||
signature: [1u8; 64],
|
||||
};
|
||||
let tx2 = UncheckedTransaction {
|
||||
transaction: Transaction {
|
||||
signed: two.clone(),
|
||||
nonce: 42,
|
||||
function: Function::StakingStake,
|
||||
input_data: Vec::new(),
|
||||
},
|
||||
signature: [2u8; 64],
|
||||
};
|
||||
let h = Header {
|
||||
parent_hash: [4u8; 32],
|
||||
number: 42,
|
||||
state_root: [5u8; 32],
|
||||
transaction_root: [6u8; 32],
|
||||
digest: Digest { logs: vec![ b"one log".to_vec(), b"another log".to_vec() ], },
|
||||
};
|
||||
let b = Block {
|
||||
header: h,
|
||||
transactions: vec![tx1, tx2],
|
||||
};
|
||||
let data = [
|
||||
// header
|
||||
4u8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
42, 0, 0, 0, 0, 0, 0, 0,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
26, 0, 0, 0,
|
||||
7, 0, 0, 0,
|
||||
111, 110, 101, 32, 108, 111, 103,
|
||||
11, 0, 0, 0,
|
||||
97, 110, 111, 116, 104, 101, 114, 32, 108, 111, 103,
|
||||
// transactions
|
||||
2, 1, 0, 0,
|
||||
// tx1
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
69, 0, 0, 0, 0, 0, 0, 0,
|
||||
2,
|
||||
40, 0, 0, 0,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
69, 0, 0, 0, 0, 0, 0, 0,
|
||||
// tx2
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
42, 0, 0, 0, 0, 0, 0, 0,
|
||||
0,
|
||||
0, 0, 0, 0
|
||||
];
|
||||
let deserialised = Block::from_slice(&data).unwrap();
|
||||
assert_eq!(deserialised, b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Interpret a static string of hex as a desired type.
|
||||
|
||||
use rustc_hex::FromHex;
|
||||
|
||||
/// Trait to allow conversion from a static hex string to an instance.
|
||||
pub trait StaticHexConversion: Sized {
|
||||
/// Convert the static str into Self. Use just like `From::from`.
|
||||
fn from_static_hex(hex: &'static str) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! impl_sizes {
|
||||
( $( $t:expr ),* ) => { $(
|
||||
impl StaticHexConversion for [u8; $t] {
|
||||
fn from_static_hex(hex: &'static str) -> Self {
|
||||
let mut r = [0u8; $t];
|
||||
r.copy_from_slice(&FromHex::from_hex(hex).unwrap());
|
||||
r
|
||||
}
|
||||
}
|
||||
)* }
|
||||
}
|
||||
|
||||
impl_sizes!(1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128);
|
||||
|
||||
/// Trait to allow converting from itself (only implemented for a static str) into some useful
|
||||
/// type (which must implement `StaticHexConversion`).
|
||||
pub trait StaticHexInto {
|
||||
/// Convert self (i.e. a static str) into the appropriate type. Use just like `Into::into`.
|
||||
fn convert<T: StaticHexConversion>(self) -> T;
|
||||
}
|
||||
|
||||
impl StaticHexInto for &'static str {
|
||||
fn convert<T: StaticHexConversion>(self) -> T {
|
||||
T::from_static_hex(self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Stuff to do with the runtime's storage.
|
||||
|
||||
use slicable::Slicable;
|
||||
use endiansensitive::EndianSensitive;
|
||||
use keyedvec::KeyedVec;
|
||||
use runtime_support::{self, twox_128, Vec};
|
||||
|
||||
/// Trait for a value which may be stored in the storage DB.
|
||||
pub trait Storable {
|
||||
/// Lookup the value in storage and deserialise, giving a default value if not found.
|
||||
fn lookup_default(key: &[u8]) -> Self where Self: Sized + Default { Self::lookup(key).unwrap_or_else(Default::default) }
|
||||
/// Lookup `Some` value in storage and deserialise; `None` if it's not there.
|
||||
fn lookup(_key: &[u8]) -> Option<Self> where Self: Sized { unimplemented!() }
|
||||
/// Place the value in storage under `key`.
|
||||
fn store(&self, key: &[u8]);
|
||||
}
|
||||
|
||||
// TODO: consider using blake256 to avoid possible eclipse attack.
|
||||
|
||||
/// Remove `key` from storage.
|
||||
pub fn kill(key: &[u8]) { runtime_support::set_storage(&twox_128(key)[..], b""); }
|
||||
|
||||
impl<T: Default + Sized + EndianSensitive> Storable for T {
|
||||
fn lookup(key: &[u8]) -> Option<Self> {
|
||||
Slicable::set_as_slice(|out| runtime_support::read_storage(&twox_128(key)[..], out) == out.len())
|
||||
}
|
||||
fn store(&self, key: &[u8]) {
|
||||
self.as_slice_then(|slice| runtime_support::set_storage(&twox_128(key)[..], slice));
|
||||
}
|
||||
}
|
||||
|
||||
impl Storable for [u8] {
|
||||
fn store(&self, key: &[u8]) {
|
||||
runtime_support::set_storage(&twox_128(key)[..], self)
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait to conveniently store a vector of storable data.
|
||||
// TODO: add iterator support
|
||||
pub trait StorageVec {
|
||||
type Item: Default + Sized + Storable;
|
||||
const PREFIX: &'static [u8];
|
||||
|
||||
/// Get the current set of items.
|
||||
fn items() -> Vec<Self::Item> {
|
||||
(0..Self::count()).into_iter().map(Self::item).collect()
|
||||
}
|
||||
|
||||
/// Set the current set of items.
|
||||
fn set_items(items: &[Self::Item]) {
|
||||
Self::set_count(items.len() as u32);
|
||||
items.iter().enumerate().for_each(|(v, ref i)| Self::set_item(v as u32, i));
|
||||
}
|
||||
|
||||
fn set_item(index: u32, item: &Self::Item) {
|
||||
item.store(&index.to_keyed_vec(Self::PREFIX));
|
||||
}
|
||||
|
||||
fn item(index: u32) -> Self::Item {
|
||||
Storable::lookup_default(&index.to_keyed_vec(Self::PREFIX))
|
||||
}
|
||||
|
||||
fn set_count(count: u32) {
|
||||
(count..Self::count()).for_each(|i| Self::set_item(i, &Self::Item::default()));
|
||||
count.store(&b"len".to_keyed_vec(Self::PREFIX));
|
||||
}
|
||||
|
||||
fn count() -> u32 {
|
||||
Storable::lookup_default(&b"len".to_keyed_vec(Self::PREFIX))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Polkadot is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Testing helpers.
|
||||
|
||||
use runtime_support::{NoError, Externalities};
|
||||
use std::collections::HashMap;
|
||||
use primitives::AccountID;
|
||||
use statichex::StaticHexInto;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
/// Simple externaties implementation.
|
||||
pub struct TestExternalities {
|
||||
/// The storage map.
|
||||
pub storage: HashMap<Vec<u8>, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Externalities for TestExternalities {
|
||||
type Error = NoError;
|
||||
|
||||
fn storage(&self, key: &[u8]) -> Result<&[u8], NoError> {
|
||||
Ok(self.storage.get(&key.to_vec()).map_or(&[] as &[u8], Vec::as_slice))
|
||||
}
|
||||
|
||||
fn set_storage(&mut self, key: Vec<u8>, value: Vec<u8>) {
|
||||
self.storage.insert(key, value);
|
||||
}
|
||||
|
||||
fn chain_id(&self) -> u64 { 42 }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! map {
|
||||
($( $name:expr => $value:expr ),*) => (
|
||||
vec![ $( ( $name, $value ) ),* ].into_iter().collect()
|
||||
)
|
||||
}
|
||||
|
||||
/// One account (to which we know the secret key).
|
||||
pub fn one() -> AccountID {
|
||||
"2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee".convert()
|
||||
}
|
||||
/// Another account (secret key known).
|
||||
pub fn two() -> AccountID {
|
||||
"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a".convert()
|
||||
}
|
||||
|
||||
/// Hex display, this time for no_std. See main codebase for documentation.
|
||||
pub struct HexDisplay<'a>(&'a [u8]);
|
||||
|
||||
impl<'a> HexDisplay<'a> {
|
||||
/// See main codebase for documentation.
|
||||
pub fn from(d: &'a AsBytesRef) -> Self { HexDisplay(d.as_bytes_ref()) }
|
||||
}
|
||||
|
||||
impl<'a> ::std::fmt::Display for HexDisplay<'a> {
|
||||
fn fmt(&self, fmtr: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
|
||||
for byte in self.0 {
|
||||
try!( fmtr.write_fmt(format_args!("{:02x}", byte)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// See main codebase for documentation.
|
||||
pub trait AsBytesRef {
|
||||
/// See main codebase for documentation.
|
||||
fn as_bytes_ref(&self) -> &[u8];
|
||||
}
|
||||
|
||||
impl AsBytesRef for [u8] {
|
||||
fn as_bytes_ref(&self) -> &[u8] { &self }
|
||||
}
|
||||
|
||||
impl<'a> AsBytesRef for &'a[u8] {
|
||||
fn as_bytes_ref(&self) -> &[u8] { self }
|
||||
}
|
||||
|
||||
impl AsBytesRef for Vec<u8> {
|
||||
fn as_bytes_ref(&self) -> &[u8] { &self[..] }
|
||||
}
|
||||
|
||||
macro_rules! impl_non_endians {
|
||||
( $( $t:ty ),* ) => { $(
|
||||
impl AsBytesRef for $t {
|
||||
fn as_bytes_ref(&self) -> &[u8] { &self[..] }
|
||||
}
|
||||
)* }
|
||||
}
|
||||
|
||||
impl_non_endians!([u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8],
|
||||
[u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40],
|
||||
[u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128]);
|
||||
Reference in New Issue
Block a user