diff --git a/Cargo.lock.bip39-fix b/Cargo.lock.bip39-fix deleted file mode 100644 index d471b1fd0d..0000000000 --- a/Cargo.lock.bip39-fix +++ /dev/null @@ -1,30 +0,0 @@ -# This file is temporarily used to fix the compilation error due to the version mismatch -# The bug is fixed upstream, but our current revision does not incorporate this fix -# TODO: must be deleted as soon as our version is updated -# and the Makefile also needs to be updated - -[[package]] -name = "substrate-bip39" -version = "0.2.2" -source = "git+https://github.com/paritytech/substrate-bip39#e4ade6da714c836268430bf9b8f1f01c73404003" -dependencies = [ - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "substrate-bip39" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum substrate-bip39 0.2.2 (git+https://github.com/paritytech/substrate-bip39)" = "" -"checksum substrate-bip39 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d69ace596e9ca97837cc41f8edcfc4e0a997f227d5fc153d1010b60a0fe9acda" diff --git a/Cargo.toml b/Cargo.toml index 360125bae1..f5c4c91a98 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,15 @@ members = [ "worker/worker-api", "substratee-node-calls", "client", + "stf", + "application-crypto" ] [patch.'https://github.com/paritytech/substrate'] -sr-io = { git = "https://github.com/scs/substraTEE-node", tag = "M4" } -substrate-executor = { git = "https://github.com/scs/substraTEE-node", tag = "M4" } +sr-io = { git = "https://github.com/scs/substraTEE-node", branch = "brenzi-private-tx", default-features=false } +substrate-executor = { git = "https://github.com/scs/substraTEE-node", branch = "brenzi-private-tx", default-features=false } +substrate-primitives = { git = "https://github.com/scs/substrate-api-client", branch = "no_std_rstd"} +substrate-application-crypto = { path = "application-crypto"} + +[patch.crates-io] +ed25519-dalek = { git = "https://github.com/scs/ed25519-dalek.git", branch = "no_std_sgx"} \ No newline at end of file diff --git a/Makefile b/Makefile index a81e2dc601..7945f6e20c 100755 --- a/Makefile +++ b/Makefile @@ -138,12 +138,9 @@ RustEnclave_Link_Flags := $(SGX_COMMON_CFLAGS) -Wl,--no-undefined -nostdlib -nod RustEnclave_Name := enclave/enclave.so Signed_RustEnclave_Name := bin/enclave.signed.so -######## WASM settings ######## -Wasm_Name := bin/worker_enclave.compact.wasm - ######## Targets ######## .PHONY: all -all: $(Wasm_Name) $(Client_Name) $(Worker_Name) $(Signed_RustEnclave_Name) +all: $(Client_Name) $(Worker_Name) $(Signed_RustEnclave_Name) worker: $(Worker_Name) client: $(Client_Name) @@ -174,8 +171,8 @@ $(Worker_Name): $(Worker_Enclave_u_Object) $(Worker_SRC_Files) $(Client_Name): $(Client_SRC_Files) @echo @echo "Building the substraTEE-client" - @cp Cargo.lock.bip39-fix Cargo.lock - @cd $(Client_SRC_Path) && cargo build $(Client_Rust_Flags) + # remove next line when https://github.com/libp2p/rust-libp2p/issues/1259 is fixed + @cd $(Client_SRC_Path) && cargo update -p protobuf --precise 2.8.1 && cargo build $(Client_Rust_Flags) @echo "Cargo => $@" cp $(Client_Rust_Path)/$(Client_Binary) ./bin @@ -195,12 +192,6 @@ $(Signed_RustEnclave_Name): $(RustEnclave_Name) @$(SGX_ENCLAVE_SIGNER) sign -key enclave/Enclave_private.pem -enclave $(RustEnclave_Name) -out $@ -config enclave/Enclave.config.xml @echo "SIGN => $@" -######## Wasm objects ######## -.PHONY: $(Wasm_Name) -$(Wasm_Name): - @echo - @echo "Building the WASM" - @cd enclave/wasm && SGX_DEBUG=$(SGX_DEBUG) ./build.sh .PHONY: enclave enclave: diff --git a/README.md b/README.md index 3bd3b7c1e8..d32a90b32b 100644 --- a/README.md +++ b/README.md @@ -5,4 +5,93 @@ This is part of [substraTEE](https://github.com/scs/substraTEE) **Supports Rust nightly-2019-07-15** -**Enclave is compiled with nightly-2019-05-22** +**Enclave is compiled with nightly-2019-08-01** + +## Private-tx demo +To run a demo for private tokens do the following: + +Assumptions: +* your machine has SGX support +* Intel SGX SDK installed. +* rust toolchain is ready to build substrate + +in terminal 1 run a substraTEE-node +``` +git clone https://github.com/scs/substraTEE-node +cd substraTEE-node +git checkout brenzi-private-tx +cargo build --release +./target/release/substratee-node --dev --ws-port 9979 --rpc-port 9969 +``` + +in terminal 2, run the worker +``` +git clone https://github.com/scs/substraTEE-worker +cd substraTEE-worker +git checkout brenzi-private-tx +make +cd bin +RUST_LOG=info ./substratee_worker -p 9979 worker +``` + +in terminal 3, run the client +``` +cd substraTEE-worker/bin +./substratee_client --node-ws-port 9979 +``` + +Then you should see this in terminal 3: +``` +*** Getting the amount of the registered workers +[<] Found 1 workers + +[>] Getting the first worker's from the substraTEE-node +[<] Got first worker's coordinates: + W1's public key : "5Gkzji8EtE1hTjVzTmZXWqrs6sqcHcbCooGqVH7iRRuxdnar" + W1's url: "127.0.0.1:2000" + +[>] Get the shielding key from W1 (=5Gkzji8EtE1hTjVzTmZXWqrs6sqcHcbCooGqVH7iRRuxdnar) +[<] Got worker shielding key Rsa3072KeyPair: { n:CF67550FB00AB959A76219EA35188B360380037123FCAA77B683A791A1F980331F9D9E11D04C7F5FB3B63787F8AAB579FDFFE1DCE79A29B6ACED2628635C8463965D5D839BD58072AA77B8CAE124E40562955FE9936DAE2976CD57B41A2DE89EEDBF9DA77C155365E8BB45DCA1E0EC3B32604D9489712762BE63B3D1F04801D796887F70115BFDD440450A04BFE81DEE7BE718F56F766E6B0C2D9DE270583C4DFBA64FD59B4DE39C07977F1FD2956588DDBF73987EECB5BB303AF2115C4E72879C5EC69B7CD5C00DAEF9F9B062B40ADA16984C574246C8AB882A79D2E1C2F597C1017FBA69D7449BAD85ADE822D92A775DB1766F21C886E762C3E260390B72C82515F1D48FD190059B419C639E3688BCC2070E9CDB6BDDD49202B7296EA2AB01EA2D3AC2990C5078446582A4C03194BBF8D7E557B4503FF4645C053D7288398C79781F642F3A8D399195E6D2E6F74B434791D881BC97BAA0F0B228BD031C40E357BC61644E68CC40F3E08BDCBBD92E306FA9353FAEA05FDCBFCF4729FECC008C, e:01000001 } + +[+] Alice's Incognito Pubkey: 5Dt1Wg85pXGLstt36t6TDdvXXoCtG6zkUL17KkyVaPYSrzGH + +[+] Bob's Incognito Pubkey: 5GTTq4EnvMk4oTXYJp2kqTd2T9hbnARuee5awLiKFKsxWgRy + +[+] pre-funding Alice's Incognito account (ROOT call) +[+] Subscribed, waiting for event... + +[+] Received confirm call from 5Gkzji8EtE1hTjVzTmZXWqrs6sqcHcbCooGqVH7iRRuxdnar +[+] query Alice's Incognito account balance + got getter response from worker: Ok("State is 1000000") +[+] query Bob's Incognito account balance + got getter response from worker: Ok("State is 0") + +*** incognito transfer from Alice to Bob + +[+] Subscribed, waiting for event... + +[+] Received confirm call from 5Gkzji8EtE1hTjVzTmZXWqrs6sqcHcbCooGqVH7iRRuxdnar +[+] query Alice's Incognito account balance + got getter response from worker: Ok("State is 900000") +[+] query Bob's Incognito account balance + got getter response from worker: Ok("State is 100000") + +``` + +### So, what happens here? + +Alice wants to transfer 100k tokens privately to Bob. She doesn't use her substraTEE-node account for this as the transfer would be publicly visible. + +Instead, she creates an *incognito* account and she keeps her account secret (also the public key). This account will never hit the substraTEE-node blockchain transparently. + +The *Demo God* then gives Alice some initial Balance of 1M. + +Bob also creates an *incognito* account and tells Alice (and only her) his public key. + +Alice now uses SubstraTEE's *shielded transaction* feature to send 100k to Bob. + +### under the hood + +TODO: +* block diagram +* sequence diagram \ No newline at end of file diff --git a/application-crypto/Cargo.toml b/application-crypto/Cargo.toml new file mode 100644 index 0000000000..b524dc6127 --- /dev/null +++ b/application-crypto/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "substrate-application-crypto" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" +description = "Provides facilities for generating application specific crypto wrapper types." + +[dependencies] +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true, features = ["derive"] } +runtime-io = { package = "sr-io", git = "https://github.com/scs/substraTEE-node", branch = "brenzi-private-tx", default-features = false} + +[dependencies.rstd] +git = 'https://github.com/paritytech/substrate' +rev = '9b08e7ff938a45dbec7fcdb854063202e2b0cb48' +package = "sr-std" +default-features = false + +[dependencies.primitives] +git = "https://github.com/scs/substrate-api-client" +package = "substrate-primitives" +branch = "no_std_rstd" +default-features = false + +[dev-dependencies] +#test-client = { package = "substrate-test-runtime-client", path = "../test-runtime/client" } +sr-primitives = { git = 'https://github.com/paritytech/substrate', rev = '9b08e7ff938a45dbec7fcdb854063202e2b0cb48'} + + +[features] +default = [ "std" ] +std = [ "primitives/std", "codec/std", "serde", "rstd/std", "runtime-io/std" ] diff --git a/application-crypto/src/ed25519.rs b/application-crypto/src/ed25519.rs new file mode 100644 index 0000000000..eb58cad714 --- /dev/null +++ b/application-crypto/src/ed25519.rs @@ -0,0 +1,75 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Ed25519 crypto types. + +use crate::{RuntimePublic, KeyTypeId}; + +pub use primitives::ed25519::*; + +mod app { + use crate::Vec; + use crate::key_types::ED25519; + crate::app_crypto!(super, ED25519); +} + +pub use app::Public as AppPublic; +pub use app::Signature as AppSignature; +pub use app::Pair as AppPair; + +impl RuntimePublic for Public { + type Signature = Signature; + + fn all(key_type: KeyTypeId) -> crate::Vec { + runtime_io::ed25519_public_keys(key_type) + } + + fn generate_pair(key_type: KeyTypeId, seed: Option<&str>) -> Self { + runtime_io::ed25519_generate(key_type, seed) + } + + fn sign>(&self, key_type: KeyTypeId, msg: &M) -> Option { + runtime_io::ed25519_sign(key_type, self, msg) + } + + fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { + runtime_io::ed25519_verify(&signature, msg.as_ref(), self) + } +} + +#[cfg(test)] +mod tests { + use sr_primitives::{generic::BlockId, traits::ProvideRuntimeApi}; + use primitives::{testing::KeyStore, crypto::Pair, traits::BareCryptoStore as _}; + use test_client::{ + TestClientBuilder, DefaultTestClientBuilderExt, TestClientBuilderExt, + runtime::{TestAPI, app_crypto::ed25519::{AppPair, AppPublic}}, + }; + + #[test] + fn ed25519_works_in_runtime() { + let keystore = KeyStore::new(); + let test_client = TestClientBuilder::new().set_keystore(keystore.clone()).build(); + let (signature, public) = test_client.runtime_api() + .test_ed25519_crypto(&BlockId::Number(0)) + .expect("Tests `ed25519` crypto."); + + let key_pair = keystore.read().ed25519_key_pair(crate::key_types::ED25519, &public.as_ref()) + .expect("There should be at a `ed25519` key in the keystore for the given public key."); + + assert!(AppPair::verify(&signature, "ed25519", &AppPublic::from(key_pair.public()))); + } +} diff --git a/application-crypto/src/lib.rs b/application-crypto/src/lib.rs new file mode 100644 index 0000000000..30176e835b --- /dev/null +++ b/application-crypto/src/lib.rs @@ -0,0 +1,321 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Traits and macros for constructing application specific strongly typed crypto wrappers. + +#![warn(missing_docs)] + +#![cfg_attr(not(feature = "std"), no_std)] + +#[doc(hidden)] +pub use primitives::{self, crypto::{CryptoType, Public, Derive, IsWrappedBy, Wraps}}; +#[doc(hidden)] +pub use primitives::crypto::{SecretStringError, DeriveJunction, Ss58Codec, Pair}; +pub use primitives::{crypto::{KeyTypeId, key_types}}; + +#[doc(hidden)] +pub use codec; +#[doc(hidden)] +#[cfg(feature = "std")] +pub use serde; +#[doc(hidden)] +pub use rstd::{ops::Deref, vec::Vec}; + +pub mod ed25519; +pub mod sr25519; +mod traits; + +pub use traits::*; + +/// Declares Public, Pair, Signature types which are functionally equivalent to `$pair`, but are new +/// Application-specific types whose identifier is `$key_type`. +/// +/// ```rust +///# use substrate_application_crypto::{app_crypto, wrap, ed25519, KeyTypeId}; +/// // Declare a new set of crypto types using Ed25519 logic that identifies as `KeyTypeId` +/// // of value `b"fuba"`. +/// app_crypto!(ed25519, KeyTypeId(*b"_uba")); +/// ``` + + +#[macro_export] +macro_rules! app_crypto { + + ($module:ident, $key_type:expr) => { + $crate::app_crypto!($module::Pair, $module::Public, $module::Signature, $key_type); + }; + ($pair:ty, $public:ty, $sig:ty, $key_type:expr) => { + + $crate::app_crypto!($public, $sig, $key_type); + + $crate::wrap!{ + /// A generic `AppPublic` wrapper type over $pair crypto; this has no specific App. + #[derive(Clone)] + pub struct Pair($pair); + } + + impl $crate::CryptoType for Pair { + type Pair = Pair; + } + + impl $crate::Pair for Pair { + type Public = Public; + type Seed = <$pair as $crate::Pair>::Seed; + type Signature = Signature; + type DeriveError = <$pair as $crate::Pair>::DeriveError; + + #[cfg(feature = "std")] + fn generate_with_phrase(password: Option<&str>) -> (Self, String, Self::Seed) { + let r = <$pair>::generate_with_phrase(password); + (Self(r.0), r.1, r.2) + } + #[cfg(feature = "std")] + fn from_phrase(phrase: &str, password: Option<&str>) + -> Result<(Self, Self::Seed), $crate::SecretStringError> + { + <$pair>::from_phrase(phrase, password).map(|r| (Self(r.0), r.1)) + } + fn derive< + Iter: Iterator + >(&self, path: Iter) -> Result { + self.0.derive(path).map(Self) + } + fn from_seed(seed: &Self::Seed) -> Self { Self(<$pair>::from_seed(seed)) } + fn from_seed_slice(seed: &[u8]) -> Result { + <$pair>::from_seed_slice(seed).map(Self) + } + #[cfg(feature = "std")] + fn from_standard_components< + I: Iterator + >( + seed: &str, + password: Option<&str>, + path: I, + ) -> Result { + <$pair>::from_standard_components::(seed, password, path).map(Self) + } + fn sign(&self, msg: &[u8]) -> Self::Signature { + Signature(self.0.sign(msg)) + } + fn verify>( + sig: &Self::Signature, + message: M, + pubkey: &Self::Public, + ) -> bool { + <$pair>::verify(&sig.0, message, pubkey.as_ref()) + } + fn verify_weak, M: AsRef<[u8]>>( + sig: &[u8], + message: M, + pubkey: P, + ) -> bool { + <$pair>::verify_weak(sig, message, pubkey) + } + fn public(&self) -> Self::Public { Public(self.0.public()) } + fn to_raw_vec(&self) -> Vec { self.0.to_raw_vec() } + } + impl $crate::AppKey for Pair { + type UntypedGeneric = $pair; + type Public = Public; + type Pair = Pair; + type Signature = Signature; + const ID: $crate::KeyTypeId = $key_type; + } + impl $crate::AppPair for Pair { + type Generic = $pair; + } + }; + ($public:ty, $sig:ty, $key_type:expr) => { + $crate::wrap!{ + /// A generic `AppPublic` wrapper type over $public crypto; this has no specific App. + #[derive( + Clone, Default, Eq, PartialEq, Ord, PartialOrd, $crate::codec::Encode, + $crate::codec::Decode, + )] + #[cfg_attr(feature = "std", derive(Debug, Hash))] + #[cfg_attr(not(feature = "std"), derive(Hash))] + pub struct Public($public); + } + + impl $crate::Derive for Public { + #[cfg(feature = "std")] + fn derive>(&self, + path: Iter + ) -> Option { + self.0.derive(path).map(Self) + } + } + + #[cfg(feature = "std")] + impl std::fmt::Display for Public { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use $crate::Ss58Codec; + write!(f, "{}", self.0.to_ss58check()) + } + } + #[cfg(feature = "std")] + impl $crate::serde::Serialize for Public { + fn serialize(&self, serializer: S) -> std::result::Result where + S: $crate::serde::Serializer + { + use $crate::Ss58Codec; + serializer.serialize_str(&self.to_ss58check()) + } + } + #[cfg(feature = "std")] + impl<'de> $crate::serde::Deserialize<'de> for Public { + fn deserialize(deserializer: D) -> std::result::Result where + D: $crate::serde::Deserializer<'de> + { + use $crate::Ss58Codec; + Public::from_ss58check(&String::deserialize(deserializer)?) + .map_err(|e| $crate::serde::de::Error::custom(format!("{:?}", e))) + } + } + + impl AsRef<[u8]> for Public { + fn as_ref(&self) -> &[u8] { self.0.as_ref() } + } + + impl AsMut<[u8]> for Public { + fn as_mut(&mut self) -> &mut [u8] { self.0.as_mut() } + } + + impl $crate::CryptoType for Public { + type Pair = Pair; + } + + impl $crate::Public for Public { + fn from_slice(x: &[u8]) -> Self { Self(<$public>::from_slice(x)) } + } + + impl $crate::AppKey for Public { + type UntypedGeneric = $public; + type Public = Public; + type Pair = Pair; + type Signature = Signature; + const ID: $crate::KeyTypeId = $key_type; + } + + impl $crate::AppPublic for Public { + type Generic = $public; + } + + impl $crate::RuntimeAppPublic for Public where $public: $crate::RuntimePublic { + type Signature = Signature; + + fn all() -> $crate::Vec { + <$public as $crate::RuntimePublic>::all($key_type).into_iter().map(Self).collect() + } + + fn generate_pair(seed: Option<&str>) -> Self { + Self(<$public as $crate::RuntimePublic>::generate_pair($key_type, seed)) + } + + fn sign>(&self, msg: &M) -> Option { + <$public as $crate::RuntimePublic>::sign( + self.as_ref(), + $key_type, + msg, + ).map(Signature) + } + + fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { + <$public as $crate::RuntimePublic>::verify(self.as_ref(), msg, &signature.as_ref()) + } + } + + $crate::wrap! { + /// A generic `AppPublic` wrapper type over $public crypto; this has no specific App. + #[derive(Clone, Default, Eq, PartialEq, $crate::codec::Encode, $crate::codec::Decode)] + #[cfg_attr(feature = "std", derive(Debug, Hash))] + #[cfg_attr(not(feature = "std"), derive(Hash))] + pub struct Signature($sig); + } + + impl $crate::Deref for Signature { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { self.0.as_ref() } + } + + impl AsRef<[u8]> for Signature { + fn as_ref(&self) -> &[u8] { self.0.as_ref() } + } + + impl $crate::CryptoType for Signature { + type Pair = Pair; + } + + impl $crate::AppKey for Signature { + type UntypedGeneric = $sig; + type Public = Public; + type Pair = Pair; + type Signature = Signature; + const ID: $crate::KeyTypeId = $key_type; + } + + impl $crate::AppSignature for Signature { + type Generic = $sig; + } + } +} + +/// Implement bidirectional `From` and on-way `AsRef`/`AsMut` for two types, `$inner` and `$outer`. +/// +/// ```rust +/// substrate_application_crypto::wrap! { +/// pub struct Wrapper(u32); +/// } +/// ``` +#[macro_export] +macro_rules! wrap { + ($( #[ $attr:meta ] )* struct $outer:ident($inner:ty);) => { + $( #[ $attr ] )* + struct $outer( $inner ); + $crate::wrap!($inner, $outer); + }; + ($( #[ $attr:meta ] )* pub struct $outer:ident($inner:ty);) => { + $( #[ $attr ] )* + pub struct $outer( $inner ); + $crate::wrap!($inner, $outer); + }; + ($inner:ty, $outer:ty) => { + impl $crate::Wraps for $outer { + type Inner = $inner; + } + impl From<$inner> for $outer { + fn from(inner: $inner) -> Self { + Self(inner) + } + } + impl From<$outer> for $inner { + fn from(outer: $outer) -> Self { + outer.0 + } + } + impl AsRef<$inner> for $outer { + fn as_ref(&self) -> &$inner { + &self.0 + } + } + impl AsMut<$inner> for $outer { + fn as_mut(&mut self) -> &mut $inner { + &mut self.0 + } + } + } +} diff --git a/application-crypto/src/sr25519.rs b/application-crypto/src/sr25519.rs new file mode 100644 index 0000000000..eaae8de8c8 --- /dev/null +++ b/application-crypto/src/sr25519.rs @@ -0,0 +1,75 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Sr25519 crypto types. + +use crate::{RuntimePublic, KeyTypeId}; + +pub use primitives::sr25519::*; + +mod app { + use crate::Vec; + use crate::key_types::SR25519; + crate::app_crypto!(super, SR25519); +} + +pub use app::Public as AppPublic; +pub use app::Signature as AppSignature; +pub use app::Pair as AppPair; + +impl RuntimePublic for Public { + type Signature = Signature; + + fn all(key_type: KeyTypeId) -> crate::Vec { + runtime_io::sr25519_public_keys(key_type) + } + + fn generate_pair(key_type: KeyTypeId, seed: Option<&str>) -> Self { + runtime_io::sr25519_generate(key_type, seed) + } + + fn sign>(&self, key_type: KeyTypeId, msg: &M) -> Option { + runtime_io::sr25519_sign(key_type, self, msg) + } + + fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { + runtime_io::sr25519_verify(&signature, msg.as_ref(), self) + } +} + +#[cfg(test)] +mod tests { + use sr_primitives::{generic::BlockId, traits::ProvideRuntimeApi}; + use primitives::{testing::KeyStore, crypto::Pair, traits::BareCryptoStore as _}; + use test_client::{ + TestClientBuilder, DefaultTestClientBuilderExt, TestClientBuilderExt, + runtime::{TestAPI, app_crypto::sr25519::{AppPair, AppPublic}}, + }; + + #[test] + fn sr25519_works_in_runtime() { + let keystore = KeyStore::new(); + let test_client = TestClientBuilder::new().set_keystore(keystore.clone()).build(); + let (signature, public) = test_client.runtime_api() + .test_sr25519_crypto(&BlockId::Number(0)) + .expect("Tests `sr25519` crypto."); + + let key_pair = keystore.read().sr25519_key_pair(crate::key_types::SR25519, public.as_ref()) + .expect("There should be at a `sr25519` key in the keystore for the given public key."); + + assert!(AppPair::verify(&signature, "sr25519", &AppPublic::from(key_pair.public()))); + } +} diff --git a/application-crypto/src/traits.rs b/application-crypto/src/traits.rs new file mode 100644 index 0000000000..146eb7d932 --- /dev/null +++ b/application-crypto/src/traits.rs @@ -0,0 +1,121 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +use primitives::crypto::{KeyTypeId, CryptoType, IsWrappedBy, Public}; +use primitives::crypto::Pair; +use codec::Codec; + +/// An application-specific key. +pub trait AppKey: 'static + Send + Sync + Sized + CryptoType + Clone { + /// The corresponding type as a generic crypto type. + type UntypedGeneric: IsWrappedBy; + + /// The corresponding public key type in this application scheme. + type Public: AppPublic; + + /// The corresponding key pair type in this application scheme. + type Pair: AppPair; + + /// The corresponding signature type in this application scheme. + type Signature: AppSignature; + + /// An identifier for this application-specific key type. + const ID: KeyTypeId; +} + +/// Type which implements Debug and Hash in std, not when no-std (std variant). +#[cfg(feature = "std")] +pub trait MaybeDebugHash: std::fmt::Debug + std::hash::Hash {} +#[cfg(feature = "std")] +impl MaybeDebugHash for T {} + +/// Type which implements Debug and Hash in std, not when no-std (no-std variant). +#[cfg(not(feature = "std"))] +//pub trait MaybeDebugHash {} +pub trait MaybeDebugHash: core::hash::Hash {} +#[cfg(not(feature = "std"))] +//impl MaybeDebugHash for T {} +impl MaybeDebugHash for T {} + + +/// A application's public key. +pub trait AppPublic: AppKey + Public + Ord + PartialOrd + Eq + PartialEq + MaybeDebugHash + codec::Codec { + /// The wrapped type which is just a plain instance of `Public`. + type Generic: + IsWrappedBy + Public + Ord + PartialOrd + Eq + PartialEq + MaybeDebugHash + codec::Codec; +} + +/// A application's key pair. +pub trait AppPair: AppKey + Pair::Public> { + /// The wrapped type which is just a plain instance of `Pair`. + type Generic: IsWrappedBy + Pair::Public as AppPublic>::Generic>; +} + +/// A application's signature. +pub trait AppSignature: AppKey + Eq + PartialEq + MaybeDebugHash { + /// The wrapped type which is just a plain instance of `Signature`. + type Generic: IsWrappedBy + Eq + PartialEq + MaybeDebugHash; +} + +/// A runtime interface for a public key. +pub trait RuntimePublic: Sized { + /// The signature that will be generated when signing with the corresponding private key. + type Signature: Codec + MaybeDebugHash + Eq + PartialEq + Clone; + + /// Returns all public keys for the given key type in the keystore. + fn all(key_type: KeyTypeId) -> crate::Vec; + + /// Generate a public/private pair for the given key type and store it in the keystore. + /// + /// Returns the generated public key. + fn generate_pair(key_type: KeyTypeId, seed: Option<&str>) -> Self; + + /// Sign the given message with the corresponding private key of this public key. + /// + /// The private key will be requested from the keystore using the given key type. + /// + /// Returns the signature or `None` if the private key could not be found or some other error + /// occurred. + fn sign>(&self, key_type: KeyTypeId, msg: &M) -> Option; + + /// Verify that the given signature matches the given message using this public key. + fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool; +} + +/// A runtime interface for an application's public key. +pub trait RuntimeAppPublic: Sized { + /// The signature that will be generated when signing with the corresponding private key. + type Signature: Codec + MaybeDebugHash + Eq + PartialEq + Clone; + + /// Returns all public keys for this application in the keystore. + fn all() -> crate::Vec; + + /// Generate a public/private pair and store it in the keystore. + /// + /// Returns the generated public key. + fn generate_pair(seed: Option<&str>) -> Self; + + /// Sign the given message with the corresponding private key of this public key. + /// + /// The private key will be requested from the keystore. + /// + /// Returns the signature or `None` if the private key could not be found or some other error + /// occurred. + fn sign>(&self, msg: &M) -> Option; + + /// Verify that the given signature matches the given message using this public key. + fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool; +} diff --git a/client/Cargo.toml b/client/Cargo.toml index f9b801d712..215fa57185 100755 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -10,7 +10,7 @@ hex = "0.3" log = "0.4" rand = "0.6" env_logger = "0.6" -schnorrkel = "0.1" +schnorrkel = { version = "0.8.4", features = ["preaudit_deprecated"] } serde = "1.0" serde_json = "1.0" tiny-bip39 = "0.6.0" @@ -20,22 +20,22 @@ substrate-bip39 = "0.2.2" clap = { version = "2.33", features = [ "yaml" ] } blake2-rfc = { version = "0.2.18", default-features = false} -parity-codec = { version = "3.0", default-features = false } -primitive-types = { version = "0.2", default-features = false, features = ["codec"] } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +primitive-types = { version = "0.5.0", default-features = false, features = ["codec"] } sgx_types = { rev = "v1.0.9",git = "https://github.com/baidu/rust-sgx-sdk" } sgx_crypto_helper = { rev = "v1.0.9",git = "https://github.com/baidu/rust-sgx-sdk", default-features = false } [dependencies.my_node_runtime] git = "https://github.com/scs/substraTEE-node" -tag = "M4" +branch = "brenzi-private-tx" package = "substratee-node-runtime" features = ["no_global_allocator"] [dependencies.substrate-api-client] git = "https://github.com/scs/substrate-api-client" -rev = "61bc5233582952a9ff13ab24fb93b08396758bf1" -default-features = false +branch = "no_std_rstd" +#default-features = false [dependencies.substratee-node-calls] path = "../substratee-node-calls" @@ -45,24 +45,33 @@ path = "../worker/worker-api" [dependencies.system] git = "https://github.com/paritytech/substrate" -rev = "56b0273fd48b4e742e9da7317bc8e132a740b684" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" package = "srml-system" [dependencies.primitives] git = "https://github.com/paritytech/substrate" -rev = "56b0273fd48b4e742e9da7317bc8e132a740b684" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" package = "substrate-primitives" [dependencies.node-primitives] git = "https://github.com/paritytech/substrate" -rev = "56b0273fd48b4e742e9da7317bc8e132a740b684" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" [dependencies.substrate-keyring] git = "https://github.com/paritytech/substrate" -rev = "56b0273fd48b4e742e9da7317bc8e132a740b684" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" [dependencies.runtime_primitives] git = "https://github.com/paritytech/substrate" -rev = "56b0273fd48b4e742e9da7317bc8e132a740b684" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" package = "sr-primitives" default-features = false + +[dependencies.indices] +git = 'https://github.com/paritytech/substrate' +rev = '9b08e7ff938a45dbec7fcdb854063202e2b0cb48' +package = "srml-indices" + +[dependencies.substratee-stf] +path = "../stf" +default-features = false diff --git a/client/src/cli.yml b/client/src/cli.yml index 3b905a745f..956e1f6fbc 100644 --- a/client/src/cli.yml +++ b/client/src/cli.yml @@ -10,25 +10,20 @@ settings: # All Args must be defined in the 'args:' list where the name of the arg, is the # key to a Hash object args: - - node-port: + - node-ws-port: short: p - long: node-port - help: Set the websocket port to connect to substraTEE-node + long: node-ws-port + help: Set the websocket port to connect to substraTEE-node RPC takes_value: true - - node-server: - short: s - long: node-server - help: Set the websocket server address to connect to substraTEE-node + - node-addr: + short: a + long: node-addr + help: Set the websocket server IP address to connect to substraTEE-node takes_value: true - - message: - short: m - long: message - help: Set the message to be sent to the substraTEE-worker - takes_value: true - - wasm-path: - short: w - long: wasm-path - help: Path to the WASM binary that should be running in the enclave + - request: + short: r + long: request + help: Send custom request to the substraTEE-worker takes_value: true subcommands: diff --git a/client/src/lib.rs b/client/src/lib.rs index d7fa97ef85..c0b4d88507 100755 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -28,26 +28,39 @@ use my_node_runtime::{ Event, Hash, SubstraTEERegistryCall, - UncheckedExtrinsic, }; -use parity_codec::{Compact, Decode, Encode}; +use codec::{Compact, Decode, Encode}; use primitive_types::U256; use primitives::{ blake2_256, crypto::Ss58Codec, - ed25519, + ed25519, sr25519, hexdisplay::HexDisplay, Pair, }; use runtime_primitives::generic::Era; -use substrate_api_client::{Api, hexstr_to_u256, hexstr_to_vec}; +use substrate_api_client::{Api, compose_extrinsic, crypto::{AccountKey, CryptoKind}, + extrinsic, utils::{hexstr_to_u256, hexstr_to_vec}, +}; +use substratee_stf::{TrustedCall, TrustedGetter}; +use log::*; +use sgx_crypto_helper::rsa3072::Rsa3072PubKey; +use blake2_rfc::blake2s::blake2s; + +use substratee_worker_api::Api as WorkerApi; +// FIXME: most of these functions are redundant with substrate-api-client +// but first resolve this: https://github.com/scs/substrate-api-client/issues/27 pub static ECC_PUB_KEY: &str = "./bin/ecc_pubkey.txt"; pub fn pair_from_suri(suri: &str, password: Option<&str>) -> ed25519::Pair { ed25519::Pair::from_string(suri, password).expect("Invalid phrase") } +pub fn pair_from_suri_sr(suri: &str, password: Option<&str>) -> sr25519::Pair { + sr25519::Pair::from_string(suri, password).expect("Invalid phrase") +} + pub fn user_to_pubkey(user: &str) -> ed25519::Public { ed25519::Public::from_string(user).ok() .or_else(|| ed25519::Pair::from_string(user, Some("")).ok().map(|p| p.public())) @@ -59,7 +72,7 @@ pub fn get_from_storage(api: &Api, user: &str, category: &str, item: &str) -> U2 let accountid = user_to_pubkey(user); let result_str = api.get_storage(category, item, Some(accountid.encode())).unwrap(); - let result = hexstr_to_u256(result_str); + let result = hexstr_to_u256(result_str).unwrap(); println!("[<] {}'s {} is {}", user, item, result); println!(); result @@ -79,81 +92,40 @@ pub fn get_account_nonce(api: &Api, user: &str) -> U256 { pub fn fund_account(api: &Api, user: &str, amount: u128, nonce: U256, genesis_hash: Hash) { println!("[>] Fund {}'s account with {}", user, amount); - // build the extrinsic for funding - let xt = extrinsic_fund(user, user, amount, amount, nonce, genesis_hash); - - // encode as hex - let mut xthex = hex::encode(xt.encode()); - xthex.insert_str(0, "0x"); + let xt = compose_extrinsic!( + api.clone(), + "Balances", + "set_balance", + GenericAddress::from(AccountKey::public_from_suri(user, Some(""), CryptoKind::Ed25519)), + Compact(amount), + Compact(amount) + ); + let tx_hash = api.send_extrinsic(xt.hex_encode()).unwrap(); - // send the extrinsic - let tx_hash = api.send_extrinsic(xthex).unwrap(); println!("[+] Transaction got finalized. Hash: {:?}", tx_hash); println!("[<] Fund completed"); println!(); } -pub fn compose_extrinsic(from: &str, function: Call, index: U256, genesis_hash: Hash) -> UncheckedExtrinsic { - let signer = pair_from_suri(from, Some("")); - let era = Era::immortal(); - - let index = index.low_u64(); - let raw_payload = (Compact(index), function, era, genesis_hash); - - let signature = raw_payload.using_encoded(|payload| if payload.len() > 256 { - signer.sign(&blake2_256(payload)[..]) - } else { - info!("signing {}", HexDisplay::from(&payload)); - signer.sign(payload) - }); - - UncheckedExtrinsic::new_signed( - index, - raw_payload.1, - signer.public().into(), - signature, - era, - ) -} - -// function to compose the extrinsic for a Balance::set_balance call -pub fn extrinsic_fund(from: &str, to: &str, free: u128, reserved: u128, index: U256, genesis_hash: Hash) -> UncheckedExtrinsic { - let to = user_to_pubkey(to); - let function = Call::Balances(BalancesCall::set_balance(to.into(), free, reserved)); - compose_extrinsic(from, function, index, genesis_hash) -} - -pub fn transfer_amount(api: &Api, from: &str, to: ed25519::Public, amount: U256, nonce: U256, genesis_hash: Hash) { +pub fn transfer_amount(api: &Api, from: &str, to: ed25519::Public, amount: U256) { println!("[>] Transfer {} from '{}' to '{}'", amount, from, to); // build the extrinsic for transfer - let xt = extrinsic_transfer(from, to, amount, nonce, genesis_hash); - - // encode as hex - let mut xthex = hex::encode(xt.encode()); - xthex.insert_str(0, "0x"); + let xt = compose_extrinsic!( + api.clone(), + "Balances", + "transfer", + GenericAddress::from(to.0), + Compact(amount.low_u128()) + ); // send the extrinsic - let tx_hash = api.send_extrinsic(xthex).unwrap(); + let tx_hash = api.send_extrinsic(xt.hex_encode()).unwrap(); println!("[+] Transaction got finalized. Hash: {:?}", tx_hash); println!("[<] Transfer completed"); println!(); } -// function to compose the extrinsic for a Balance::transfer call -pub fn extrinsic_transfer(from: &str, to: ed25519::Public, amount: U256, index: U256, genesis_hash: Hash) -> UncheckedExtrinsic { - let amount = amount.low_u128(); - let function = Call::Balances(BalancesCall::transfer(to.into(), amount)); - compose_extrinsic(from, function, index, genesis_hash) -} - -// function to compose the extrinsic for a SubstraTEERegistry::call_worker call -pub fn compose_extrinsic_substratee_call_worker(from: &str, payload_encrypted: Vec, index: U256, genesis_hash: Hash) -> UncheckedExtrinsic { - let payload_encrypted_str = payload_encrypted; - let function = Call::SubstraTEERegistry(SubstraTEERegistryCall::call_worker(payload_encrypted_str)); - compose_extrinsic(from, function, index, genesis_hash) -} - // subscribes to he substratee_registry events of type CallConfirmed pub fn subscribe_to_call_confirmed(api: Api) -> Vec{ let (events_in, events_out) = channel(); @@ -169,10 +141,10 @@ pub fn subscribe_to_call_confirmed(api: Api) -> Vec{ loop { let event_str = events_out.recv().unwrap(); - let _unhex = hexstr_to_vec(event_str); + let _unhex = hexstr_to_vec(event_str).unwrap(); let mut _er_enc = _unhex.as_slice(); let _events = Vec::>::decode(&mut _er_enc); - if let Some(evts) = _events { + if let Ok(evts) = _events { for evr in &evts { if let Event::substratee_registry(pe) = &evr.event { if let my_node_runtime::substratee_registry::RawEvent::CallConfirmed(sender, payload) = &pe { @@ -205,3 +177,34 @@ pub fn get_wasm_hash(path: &str) -> Vec { .map(|s| s.to_string()) .collect() } + + +pub fn call_trusted_stf(api: &Api, call: TrustedCall, rsa_pubkey: Rsa3072PubKey) { + let call_encoded = call.encode(); + let mut call_encrypted: Vec = Vec::new(); + rsa_pubkey.encrypt_buffer(&call_encoded, &mut call_encrypted).unwrap(); + + let xt = compose_extrinsic!( + api.clone(), + "SubstraTEERegistry", + "call_worker", + call_encrypted.clone() + ); + + // send and watch extrinsic until finalized + let tx_hash = api.send_extrinsic(xt.hex_encode()).unwrap(); + info!("stf call extrinsic got finalized. Hash: {:?}", tx_hash); + info!("waiting for confirmation of stf call"); + let act_hash = subscribe_to_call_confirmed(api.clone()); + info!("callConfirmed event received"); + debug!("Expected stf call Hash: {:?}", blake2s(32, &[0; 32], &call_encrypted).as_bytes()); + debug!("confirmation stf call Hash: {:?}", act_hash); + +} + +pub fn get_trusted_stf_state(workerapi: &WorkerApi, getter: TrustedGetter) { + //TODO: encrypt and sign request + let ret = workerapi.get_stf_state(getter); + println!(" got getter response from worker: {:?}", ret); + //TODO: decrypt response and verify signature +} \ No newline at end of file diff --git a/client/src/main.rs b/client/src/main.rs index 2263b2b30b..900b8c3181 100755 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -21,7 +21,7 @@ extern crate env_logger; extern crate hex_literal; extern crate log; extern crate my_node_runtime; -extern crate parity_codec; +extern crate codec; extern crate primitives; extern crate runtime_primitives; extern crate serde; @@ -34,17 +34,25 @@ extern crate substrate_api_client; use blake2_rfc::blake2s::blake2s; use clap::App; -use parity_codec::Encode; +use codec::Encode; use primitive_types::U256; -use primitives::Pair; +use primitives::{Pair, Public, crypto::Ss58Codec}; use sgx_types::*; -use substrate_api_client::Api; +use substrate_api_client::{Api, compose_extrinsic, extrinsic, + utils::{hexstr_to_vec, hexstr_to_u256}, + crypto::{AccountKey, CryptoKind}, + extrinsic::{balances::transfer, xt_primitives::GenericAddress}, + }; -use substratee_client::*; +use substratee_client::{get_account_nonce, subscribe_to_call_confirmed, pair_from_suri_sr, + transfer_amount, fund_account, get_free_balance, pair_from_suri, call_trusted_stf, get_trusted_stf_state}; use substratee_node_calls::{get_worker_amount, get_worker_info}; use substratee_worker_api::Api as WorkerApi; +use substratee_stf::{TrustedCall, TrustedGetter}; +use log::*; -const WASM_FILE: &str = "worker_enclave.compact.wasm"; +use runtime_primitives::{AnySignature, traits::Verify}; +type AccountId = ::Signer; fn main() { // message structure @@ -60,11 +68,15 @@ fn main() { let yml = load_yaml!("cli.yml"); let matches = App::from_yaml(yml).get_matches(); - let port = matches.value_of("node-port").unwrap_or("9944"); - let server = matches.value_of("node-server").unwrap_or("127.0.0.1"); - let mut api: substrate_api_client::Api = Api::new(format!("ws://{}:{}", server, port)); - api.init(); - + let port = matches.value_of("node-ws-port").unwrap_or("9944"); + let server = matches.value_of("node-addr").unwrap_or("127.0.0.1"); + + info!("initializing ws api to node"); + let alice = primitives::sr25519::Pair::from_string("//Alice", None).unwrap(); + let alicekey = AccountKey::Sr(alice.clone()); + info!("use Alice account as signer = {}", alice.public().to_ss58check()); + let mut api: substrate_api_client::Api = Api::new(format!("ws://{}:{}", server, port)) + .set_signer(alicekey.clone()); println!("*** Getting the amount of the registered workers"); let worker = match get_worker_amount(&api) { @@ -83,8 +95,11 @@ fn main() { println!(" W1's url: {:?}\n", worker.url); let worker_api = WorkerApi::new(worker.url.clone()); - + + //FIXME: this is outdated if let Some(_matches) = matches.subcommand_matches("getcounter") { + panic!("outdated implementation!"); + /* let user = pair_from_suri("//Alice", Some("")); println!("*** Getting the counter value of //Alice = {:?} from the substraTEE-worker", user.public().to_string()); let sign = user.sign(user.public().as_slice()); @@ -92,56 +107,48 @@ fn main() { println!("[<] Received MSG: {}", value); return; + */ } - let wasm_path = matches.value_of("wasm-path").unwrap_or(WASM_FILE); - let hash_hex = get_wasm_hash(wasm_path); - println!("[>] Calculating WASM hash of {:?}", wasm_path); - println!("[<] WASM Hash: {:?}\n", hash_hex[0]); - let hash = hex::decode(hash_hex[0].clone()).unwrap(); - let sha256: sgx_sha256_hash_t = slice_to_hash(&hash); + info!("getting free_balance for Alice"); + let result_str = api.get_storage("Balances", "FreeBalance", Some(AccountId::from(alice.public()).encode())).unwrap(); + let funds = hexstr_to_u256(result_str).unwrap(); + info!("Alice free balance = {:?}", funds); + info!("Alice's Account Nonce is {}", api.get_nonce().unwrap()); - // get Alice's free balance - get_free_balance(&api, "//Alice"); + // compose extrinsic with encrypted payload + println!("[>] Get the shielding key from W1 (={})", worker.pubkey.to_string()); + let shielding_pubkey = worker_api.get_rsa_pubkey().unwrap(); + println!("[<] Got worker shielding key {:?}\n", shielding_pubkey); - // get Alice's account nonce - let mut nonce = get_account_nonce(&api, "//Alice"); + let alice_incognito_pair = pair_from_suri_sr("//AliceIncognito", Some("")); + println!("[+] Alice's Incognito Pubkey: {}\n", alice_incognito_pair.public()); - // fund the account of Alice - fund_account(&api, "//Alice", 1_000_000, nonce, api.genesis_hash.unwrap()); + let bob_incognito_pair = pair_from_suri_sr("//BobIncognito", Some("")); + println!("[+] Bob's Incognito Pubkey: {}\n", bob_incognito_pair.public()); - // transfer from Alice to TEE - nonce = get_account_nonce(&api, "//Alice"); - transfer_amount(&api, "//Alice", worker.pubkey.clone(), U256::from(1000), nonce, api.genesis_hash.unwrap()); + println!("[+] pre-funding Alice's Incognito account (ROOT call)"); + let call = TrustedCall::balance_set_balance(alice_incognito_pair.public(), 1_000_000, 0); + call_trusted_stf(&api, call, shielding_pubkey); + + println!("[+] query Alice's Incognito account balance"); + let getter = TrustedGetter::free_balance(alice_incognito_pair.public()); + get_trusted_stf_state(&worker_api, getter); + + println!("[+] query Bob's Incognito account balance"); + let getter = TrustedGetter::free_balance(bob_incognito_pair.public()); + get_trusted_stf_state(&worker_api, getter); + + println!("*** incognito transfer from Alice to Bob"); + let call = TrustedCall::balance_transfer(alice_incognito_pair.public(), bob_incognito_pair.public(), 100_000); + call_trusted_stf(&api, call, shielding_pubkey); + + println!("[+] query Alice's Incognito account balance"); + let getter = TrustedGetter::free_balance(alice_incognito_pair.public()); + get_trusted_stf_state(&worker_api, getter); + + println!("[+] query Bob's Incognito account balance"); + let getter = TrustedGetter::free_balance(bob_incognito_pair.public()); + get_trusted_stf_state(&worker_api, getter); - // compose extrinsic with encrypted payload - println!("[>] Get the encryption key from W1 (={})", worker.pubkey.to_string()); - let rsa_pubkey = worker_api.get_rsa_pubkey().unwrap(); - println!("[<] Got worker shielding key {:?}\n", rsa_pubkey); - - let account = user_to_pubkey("//Alice").to_string(); - println!("[+] //Alice's Pubkey: {}\n", account); - let amount = value_t!(matches.value_of("amount"), u32).unwrap_or(42); - let message = Message { account, amount, sha256 }; - let plaintext = serde_json::to_vec(&message).unwrap(); - let mut payload_encrypted: Vec = Vec::new(); - rsa_pubkey.encrypt_buffer(&plaintext, &mut payload_encrypted).unwrap(); - println!("[>] Sending message '{:?}' to substraTEE-worker.\n", message); - nonce = get_account_nonce(&api, "//Alice"); - let xt = compose_extrinsic_substratee_call_worker("//Alice", payload_encrypted, nonce, api.genesis_hash.unwrap()); - let mut _xthex = hex::encode(xt.encode()); - _xthex.insert_str(0, "0x"); - - // send and watch extrinsic until finalized - let tx_hash = api.send_extrinsic(_xthex).unwrap(); - println!("[+] Transaction got finalized. Hash: {:?}", tx_hash); - println!("[<] Message sent successfully"); - println!(); - - // subsribe to callConfirmed event - println!("[>] Subscribe to callConfirmed event"); - let act_hash = subscribe_to_call_confirmed(api); - println!("[<] callConfirmed event received"); - println!("[+] Expected Hash: {:?}", blake2s(32, &[0; 32], &plaintext).as_bytes()); - println!("[+] Actual Hash: {:?}", act_hash); } diff --git a/enclave/Cargo.toml b/enclave/Cargo.toml index 58ebe49617..8e48c47aa1 100755 --- a/enclave/Cargo.toml +++ b/enclave/Cargo.toml @@ -4,7 +4,8 @@ version = "4.0.0" authors = ["Supercomputing Systems AG "] [workspace] -members = ["wasm"] +members = [ +] [lib] name = "substratee_worker_enclave" @@ -27,12 +28,11 @@ sgx_tunittest = { rev = "v1.0.9", git = "https://github.com/baidu/rust-sgx-sdk" [dependencies] aes = { version = "0.3.2" } ofb = { version = "0.1.1" } -parity-codec = { version = "3.0", default-features = false } -primitive-types = { version = "0.2", default-features = false, features = ["codec"] } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +primitive-types = { version = "0.5.0", default-features = false, features = ["codec"] } sgx-crypto-helper = { rev = "v1.0.9", git = "https://github.com/baidu/rust-sgx-sdk", package = "sgx_tcrypto_helper" } log = { version = "0.4.7", git = "https://github.com/mesalock-linux/log-sgx" } env_logger = { version = "0.6.2", git = "https://github.com/mesalock-linux/env_logger-sgx" } -rust-crypto = { rev = "c379c41663889758d7a851f8a7bd3eb26f271827", git = "https://github.com/mesalock-linux/rust-crypto-sgx" } # for wasm code wasmi = { version = "0.5.0", git = "https://github.com/mesalock-linux/wasmi-sgx" } @@ -49,61 +49,75 @@ serde_derive = { version = "1.0.97", git = "https://github.com/mesalock-linux httparse = { version = "1.3.2", default-features = false } itertools = { version = "0.7.8", default-features = false, features = []} bit-vec = { version = "0.6", default-features = false } -rust-base58 = { rev = "3fe87eafe81d1ff5e6cf2fb63c6976fc4e06ac44", git = "https://github.com/mesalock-linux/rust-base58-sgx" } +rust-base58 = { rev = "3fe87eafe81d1ff5e6cf2fb63c6976fc4e06ac44", git = "https://github.com/mesalock-linux/rust-base58-sgx", default-features = false, features=["mesalock_sgx"] } [dependencies.webpki] git = "https://github.com/mesalock-linux/webpki" branch = "mesalock_sgx" version = "0.21.0" -default-features = false -features = ["mesalock_sgx"] [dependencies.webpki-roots] git = "https://github.com/mesalock-linux/webpki-roots" branch = "mesalock_sgx" version = "0.17.0" -# use renamed blake2-rfc -[dependencies.blake2-no-std] -git = "https://github.com/cesarb/blake2-rfc" -package = "blake2-rfc" -version = "0.2.18" -default-features = false - [dependencies.yasna] git = "https://github.com/mesalock-linux/yasna.rs-sgx" rev = "29f0707027ce3b06ddb7daf4086ef673dbcb14ff" -# rev = "d4619dfa4a5ff73b8bf6e8c3bd4e977fc1634a19" # fixing to this revision to have buf public -features = ["bit-vec", "num-bigint", "chrono"] +default-features = false +features = ["bit-vec", "num-bigint", "chrono", "mesalock_sgx"] [dependencies.rustls] git = "https://github.com/mesalock-linux/rustls" -rev = "36995713775439e1a60a01cfa327e045a61020a6" -# branch = "mesalock_sgx" +rev = "fe19b470f6acbb4c6c39fe1f83556bc434840fd8" features = ["dangerous_configuration"] -[dependencies.my_node_runtime] +[dependencies.sr-io] git = "https://github.com/scs/substraTEE-node" -tag = "M4" -package = "substratee-node-runtime" -default-features = false -features = ["no_global_allocator", "no_panic_handler", "no_oom"] +package = 'sr-io' +branch = "brenzi-private-tx" +default-features=false +features = ["no_oom", "no_panic_handler", "sgx", "debug"] [dependencies.sgx_serialize_derive] git = "https://github.com/baidu/rust-sgx-sdk" rev = "v1.0.9" [dependencies.primitives] -git = 'https://github.com/paritytech/substrate' -rev = "56b0273fd48b4e742e9da7317bc8e132a740b684" +git = "https://github.com/scs/substrate-api-client" package = "substrate-primitives" +branch = "no_std_rstd" default-features = false [dependencies.runtime_primitives] git = 'https://github.com/paritytech/substrate' -rev = "56b0273fd48b4e742e9da7317bc8e132a740b684" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" package = "sr-primitives" default-features = false +[dependencies.rstd] +git = 'https://github.com/paritytech/substrate' +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" +package = "sr-std" +features = ["no_global_allocator"] +default-features = false + +[dependencies.substrate-api-client] +git = "https://github.com/scs/substrate-api-client" +branch = "no_std_rstd" +default-features = false + +[dependencies.substratee-stf] +path = "../stf" +default-features = false +features = ["sgx"] + + [patch.'https://github.com/paritytech/substrate'] -sr-io = { git = "https://github.com/scs/substraTEE-node", tag = "M4"} +sr-io = { git = "https://github.com/scs/substraTEE-node", branch = "brenzi-private-tx"} +substrate-primitives = { git = "https://github.com/scs/substrate-api-client", branch = "no_std_rstd"} +substrate-application-crypto = { path = "../application-crypto"} +srml-balances = { path = "./balances" } + +[patch.crates-io] +ed25519-dalek = { git = "https://github.com/scs/ed25519-dalek.git", branch = "no_std_sgx"} diff --git a/enclave/Enclave.edl b/enclave/Enclave.edl index 46331a3a6c..b4767ab7f9 100755 --- a/enclave/Enclave.edl +++ b/enclave/Enclave.edl @@ -30,27 +30,26 @@ enclave { trusted { /* define ECALLs here. */ - public sgx_status_t call_counter_wasm( - [in, size=req_len] const uint8_t* req_bin, size_t req_len, - [in, size=ciphertext_size] uint8_t* ciphertext, uint32_t ciphertext_size, - [in, size=hash_size] uint8_t* hash, uint32_t hash_size, + public sgx_status_t init( + ); + + public sgx_status_t execute_stf( + [in, size=request_encrypted_size] uint8_t* request_encrypted, uint32_t request_encrypted_size, + [in, size=genesis_hash_size] uint8_t* genesis_hash, uint32_t genesis_hash_size, [in, size=nonce_size] uint8_t* nonce, uint32_t nonce_size, - [in, size=wasm_hash_size] uint8_t* wasm_hash, uint32_t wasm_hash_size, [out, size=unchecked_extrinsic_size] uint8_t* unchecked_extrinsic, uint32_t unchecked_extrinsic_size ); - public sgx_status_t get_counter( - [in, size=account_size] uint8_t* account, uint32_t account_size, - [out] uint32_t* counter_value); + public sgx_status_t get_state( + [in, size=getter_size] uint8_t* getter, uint32_t getter_size, + [out, size=value_size] uint8_t* value, uint32_t value_size + ); public sgx_status_t get_rsa_encryption_pubkey( [out, size=pubkey_size] uint8_t* pubkey, uint32_t pubkey_size); public sgx_status_t get_ecc_signing_pubkey( [out, size=pubkey_size] uint8_t* pubkey, uint32_t pubkey_size); - - public sgx_status_t sgxwasm_init(); - public sgx_status_t perform_ra( [in, size=genesis_hash_size] uint8_t* genesis_hash, uint32_t genesis_hash_size, [in, size=nonce_size] uint8_t* nonce, uint32_t nonce_size, diff --git a/enclave/balances/Cargo.toml b/enclave/balances/Cargo.toml new file mode 100644 index 0000000000..57efb9b867 --- /dev/null +++ b/enclave/balances/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "srml-balances" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +serde = { version = "1.0", optional = true } +safe-mix = { version = "1.0", default-features = false} +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } + +sgx_tstd = { rev = "v1.0.9", git = "https://github.com/baidu/rust-sgx-sdk", features = ["untrusted_fs","net","backtrace"]} +sgx_types = { rev = "v1.0.9", git = "https://github.com/baidu/rust-sgx-sdk"} +sgx_log = { package = "log", version = "0.4.7", git = "https://github.com/mesalock-linux/log-sgx"} + +[dependencies.rstd] +git = 'https://github.com/paritytech/substrate' +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" +package = "sr-std" +features = ["no_global_allocator"] +default-features = false + +[dependencies.sr-primitives] +git = 'https://github.com/paritytech/substrate' +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" +package = "sr-primitives" +default-features = false + +[dependencies.support] +default-features = false +git = "https://github.com/paritytech/substrate" +package = "srml-support" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" + +[dependencies.system] +default-features = false +git = "https://github.com/paritytech/substrate" +package = "srml-system" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" + +[dev-dependencies] +#runtime-io ={ package = "sr-io", path = "../../core/sr-io", default-features = false } +#primitives = { package = "substrate-primitives", path = "../../core/primitives" } + +[features] +default = ["std"] +std = [ + "serde", + "safe-mix/std", + "codec/std", + "rstd/std", + "support/std", + "sr-primitives/std", + "system/std", +] diff --git a/enclave/balances/src/lib.rs b/enclave/balances/src/lib.rs new file mode 100644 index 0000000000..daac0e70d5 --- /dev/null +++ b/enclave/balances/src/lib.rs @@ -0,0 +1,1310 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! # Balances Module +//! +//! The Balances module provides functionality for handling accounts and balances. +//! +//! - [`balances::Trait`](./trait.Trait.html) +//! - [`Call`](./enum.Call.html) +//! - [`Module`](./struct.Module.html) +//! +//! ## Overview +//! +//! The Balances module provides functions for: +//! +//! - Getting and setting free balances. +//! - Retrieving total, reserved and unreserved balances. +//! - Repatriating a reserved balance to a beneficiary account that exists. +//! - Transferring a balance between accounts (when not reserved). +//! - Slashing an account balance. +//! - Account creation and removal. +//! - Managing total issuance. +//! - Setting and managing locks. +//! +//! ### Terminology +//! +//! - **Existential Deposit:** The minimum balance required to create or keep an account open. This prevents +//! "dust accounts" from filling storage. +//! - **Total Issuance:** The total number of units in existence in a system. +//! - **Reaping an account:** The act of removing an account by resetting its nonce. Happens after its balance is set +//! to zero. +//! - **Free Balance:** The portion of a balance that is not reserved. The free balance is the only balance that matters +//! for most operations. When this balance falls below the existential deposit, most functionality of the account is +//! removed. When both it and the reserved balance are deleted, then the account is said to be dead. +//! - **Reserved Balance:** Reserved balance still belongs to the account holder, but is suspended. Reserved balance +//! can still be slashed, but only after all the free balance has been slashed. If the reserved balance falls below the +//! existential deposit then it and any related functionality will be deleted. When both it and the free balance are +//! deleted, then the account is said to be dead. +//! - **Imbalance:** A condition when some funds were credited or debited without equal and opposite accounting +//! (i.e. a difference between total issuance and account balances). Functions that result in an imbalance will +//! return an object of the `Imbalance` trait that can be managed within your runtime logic. (If an imbalance is +//! simply dropped, it should automatically maintain any book-keeping such as total issuance.) +//! - **Lock:** A freeze on a specified amount of an account's free balance until a specified block number. Multiple +//! locks always operate over the same funds, so they "overlay" rather than "stack". +//! - **Vesting:** Similar to a lock, this is another, but independent, liquidity restriction that reduces linearly +//! over time. +//! +//! ### Implementations +//! +//! The Balances module provides implementations for the following traits. If these traits provide the functionality +//! that you need, then you can avoid coupling with the Balances module. +//! +//! - [`Currency`](../srml_support/traits/trait.Currency.html): Functions for dealing with a +//! fungible assets system. +//! - [`ReservableCurrency`](../srml_support/traits/trait.ReservableCurrency.html): +//! Functions for dealing with assets that can be reserved from an account. +//! - [`LockableCurrency`](../srml_support/traits/trait.LockableCurrency.html): Functions for +//! dealing with accounts that allow liquidity restrictions. +//! - [`Imbalance`](../srml_support/traits/trait.Imbalance.html): Functions for handling +//! imbalances between total issuance in the system and account balances. Must be used when a function +//! creates new funds (e.g. a reward) or destroys some funds (e.g. a system fee). +//! - [`IsDeadAccount`](../srml_system/trait.IsDeadAccount.html): Determiner to say whether a +//! given account is unused. +//! +//! ## Interface +//! +//! ### Dispatchable Functions +//! +//! - `transfer` - Transfer some liquid free balance to another account. +//! - `set_balance` - Set the balances of a given account. The origin of this call must be root. +//! +//! ### Public Functions +//! +//! - `vesting_balance` - Get the amount that is currently being vested and cannot be transferred out of this account. +//! +//! ### Signed Extensions +//! +//! The balances module defines the following extensions: +//! +//! - [`TakeFees`]: Consumes fees proportional to the length and weight of the transaction. +//! Additionally, it can contain a single encoded payload as a `tip`. The inclusion priority +//! is increased proportional to the tip. +//! +//! Lookup the runtime aggregator file (e.g. `node/runtime`) to see the full list of signed +//! extensions included in a chain. +//! +//! ## Usage +//! +//! The following examples show how to use the Balances module in your custom module. +//! +//! ### Examples from the SRML +//! +//! The Contract module uses the `Currency` trait to handle gas payment, and its types inherit from `Currency`: +//! +//! ``` +//! use support::traits::Currency; +//! # pub trait Trait: system::Trait { +//! # type Currency: Currency; +//! # } +//! +//! pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +//! pub type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; +//! +//! # fn main() {} +//! ``` +//! +//! The Staking module uses the `LockableCurrency` trait to lock a stash account's funds: +//! +//! ``` +//! use support::traits::{WithdrawReasons, LockableCurrency}; +//! use sr_primitives::traits::Bounded; +//! pub trait Trait: system::Trait { +//! type Currency: LockableCurrency; +//! } +//! # struct StakingLedger { +//! # stash: ::AccountId, +//! # total: <::Currency as support::traits::Currency<::AccountId>>::Balance, +//! # phantom: std::marker::PhantomData, +//! # } +//! # const STAKING_ID: [u8; 8] = *b"staking "; +//! +//! fn update_ledger( +//! controller: &T::AccountId, +//! ledger: &StakingLedger +//! ) { +//! T::Currency::set_lock( +//! STAKING_ID, +//! &ledger.stash, +//! ledger.total, +//! T::BlockNumber::max_value(), +//! WithdrawReasons::all() +//! ); +//! // >::insert(controller, ledger); // Commented out as we don't have access to Staking's storage here. +//! } +//! # fn main() {} +//! ``` +//! +//! ## Genesis config +//! +//! The Balances module depends on the [`GenesisConfig`](./struct.GenesisConfig.html). +//! +//! ## Assumptions +//! +//! * Total issued balanced of all accounts should be less than `Trait::Balance::max_value()`. + +#![cfg_attr(not(feature = "std"), no_std)] + +use rstd::prelude::*; +use rstd::{cmp, result, mem}; + +use sgx_log::*; + +use codec::{Codec, Encode, Decode}; +use support::{ + StorageValue, StorageMap, Parameter, decl_event, decl_storage, decl_module, + traits::{ + UpdateBalanceOutcome, Currency, OnFreeBalanceZero, OnUnbalanced, + WithdrawReason, WithdrawReasons, LockIdentifier, LockableCurrency, ExistenceRequirement, + Imbalance, SignedImbalance, ReservableCurrency, Get, + }, + dispatch::Result, +}; +use sr_primitives::{ + transaction_validity::{ + TransactionPriority, ValidTransaction, InvalidTransaction, TransactionValidityError, + TransactionValidity, + }, + traits::{ + Zero, SimpleArithmetic, StaticLookup, Member, CheckedAdd, CheckedSub, MaybeSerializeDebug, + Saturating, Bounded, SignedExtension, SaturatedConversion, Convert, + }, + weights::{DispatchInfo, SimpleDispatchInfo, Weight}, +}; +use system::{IsDeadAccount, OnNewAccount, ensure_signed, ensure_root}; + +mod mock; +mod tests; + +pub use self::imbalances::{PositiveImbalance, NegativeImbalance}; + +pub trait Subtrait: system::Trait { + /// The balance of an account. + type Balance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy + + MaybeSerializeDebug + From; + + /// A function that is invoked when the free-balance has fallen below the existential deposit and + /// has been reduced to zero. + /// + /// Gives a chance to clean up resources associated with the given account. + type OnFreeBalanceZero: OnFreeBalanceZero; + + /// Handler for when a new account is created. + type OnNewAccount: OnNewAccount; + + /// The minimum amount required to keep an account open. + type ExistentialDeposit: Get; + + /// The fee required to make a transfer. + type TransferFee: Get; + + /// The fee required to create an account. + type CreationFee: Get; + + /// The fee to be paid for making a transaction; the base. + type TransactionBaseFee: Get; + + /// The fee to be paid for making a transaction; the per-byte portion. + type TransactionByteFee: Get; + + /// Convert a weight value into a deductible fee based on the currency type. + type WeightToFee: Convert; +} + +pub trait Trait: system::Trait { + /// The balance of an account. + type Balance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy + + MaybeSerializeDebug + From; + + /// A function that is invoked when the free-balance has fallen below the existential deposit and + /// has been reduced to zero. + /// + /// Gives a chance to clean up resources associated with the given account. + type OnFreeBalanceZero: OnFreeBalanceZero; + + /// Handler for when a new account is created. + type OnNewAccount: OnNewAccount; + + /// Handler for the unbalanced reduction when taking transaction fees. + type TransactionPayment: OnUnbalanced>; + + /// Handler for the unbalanced reduction when taking fees associated with balance + /// transfer (which may also include account creation). + type TransferPayment: OnUnbalanced>; + + /// Handler for the unbalanced reduction when removing a dust account. + type DustRemoval: OnUnbalanced>; + + /// The overarching event type. + type Event: From> + Into<::Event>; + + /// The minimum amount required to keep an account open. + type ExistentialDeposit: Get; + + /// The fee required to make a transfer. + type TransferFee: Get; + + /// The fee required to create an account. + type CreationFee: Get; + + /// The fee to be paid for making a transaction; the base. + type TransactionBaseFee: Get; + + /// The fee to be paid for making a transaction; the per-byte portion. + type TransactionByteFee: Get; + + /// Convert a weight value into a deductible fee based on the currency type. + type WeightToFee: Convert; +} + +impl, I: Instance> Subtrait for T { + type Balance = T::Balance; + type OnFreeBalanceZero = T::OnFreeBalanceZero; + type OnNewAccount = T::OnNewAccount; + type ExistentialDeposit = T::ExistentialDeposit; + type TransferFee = T::TransferFee; + type CreationFee = T::CreationFee; + type TransactionBaseFee = T::TransactionBaseFee; + type TransactionByteFee = T::TransactionByteFee; + type WeightToFee = T::WeightToFee; +} + +decl_event!( + pub enum Event where + ::AccountId, + >::Balance + { + /// A new account was created. + NewAccount(AccountId, Balance), + /// An account was reaped. + ReapedAccount(AccountId), + /// Transfer succeeded (from, to, value, fees). + Transfer(AccountId, AccountId, Balance, Balance), + } +); + +/// Struct to encode the vesting schedule of an individual account. +#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct VestingSchedule { + /// Locked amount at genesis. + pub locked: Balance, + /// Amount that gets unlocked every block after `starting_block`. + pub per_block: Balance, + /// Starting block for unlocking(vesting). + pub starting_block: BlockNumber, +} + +impl VestingSchedule { + /// Amount locked at block `n`. + pub fn locked_at(&self, n: BlockNumber) -> Balance + where Balance: From + { + // Number of blocks that count toward vesting + // Saturating to 0 when n < starting_block + let vested_block_count = n.saturating_sub(self.starting_block); + // Return amount that is still locked in vesting + if let Some(x) = Balance::from(vested_block_count).checked_mul(&self.per_block) { + self.locked.max(x) - x + } else { + Zero::zero() + } + } +} + +#[derive(Encode, Decode, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct BalanceLock { + pub id: LockIdentifier, + pub amount: Balance, + pub until: BlockNumber, + pub reasons: WithdrawReasons, +} + +decl_storage! { + trait Store for Module, I: Instance=DefaultInstance> as Balances { + /// The total units issued in the system. + pub TotalIssuance get(total_issuance) build(|config: &GenesisConfig| { + config.balances.iter().fold(Zero::zero(), |acc: T::Balance, &(_, n)| acc + n) + }): T::Balance; + + /// Information regarding the vesting of a given account. + pub Vesting get(vesting) build(|config: &GenesisConfig| { + // Generate initial vesting configuration + // * who - Account which we are generating vesting configuration for + // * begin - Block when the account will start to vest + // * length - Number of blocks from `begin` until fully vested + // * liquid - Number of units which can be spent before vesting begins + config.vesting.iter().filter_map(|&(ref who, begin, length, liquid)| { + let length = >::from(length); + + config.balances.iter() + .find(|&&(ref w, _)| w == who) + .map(|&(_, balance)| { + // Total genesis `balance` minus `liquid` equals funds locked for vesting + let locked = balance.saturating_sub(liquid); + // Number of units unlocked per block after `begin` + let per_block = locked / length.max(sr_primitives::traits::One::one()); + + (who.clone(), VestingSchedule { + locked: locked, + per_block: per_block, + starting_block: begin + }) + }) + }).collect::>() + }): map T::AccountId => Option>; + + /// The 'free' balance of a given account. + /// + /// This is the only balance that matters in terms of most operations on tokens. It + /// alone is used to determine the balance when in the contract execution environment. When this + /// balance falls below the value of `ExistentialDeposit`, then the 'current account' is + /// deleted: specifically `FreeBalance`. Further, the `OnFreeBalanceZero` callback + /// is invoked, giving a chance to external modules to clean up data associated with + /// the deleted account. + /// + /// `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets + /// collapsed to zero if it ever becomes less than `ExistentialDeposit`. + pub FreeBalance get(free_balance) + build(|config: &GenesisConfig| config.balances.clone()): + map T::AccountId => T::Balance; + + /// The amount of the balance of a given account that is externally reserved; this can still get + /// slashed, but gets slashed last of all. + /// + /// This balance is a 'reserve' balance that other subsystems use in order to set aside tokens + /// that are still 'owned' by the account holder, but which are suspendable. + /// + /// When this balance falls below the value of `ExistentialDeposit`, then this 'reserve account' + /// is deleted: specifically, `ReservedBalance`. + /// + /// `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets + /// collapsed to zero if it ever becomes less than `ExistentialDeposit`.) + pub ReservedBalance get(reserved_balance): map T::AccountId => T::Balance; + + /// Any liquidity locks on some account balances. + pub Locks get(locks): map T::AccountId => Vec>; + } + add_extra_genesis { + config(balances): Vec<(T::AccountId, T::Balance)>; + config(vesting): Vec<(T::AccountId, T::BlockNumber, T::BlockNumber, T::Balance)>; + // ^^ begin, length, amount liquid at genesis + } +} + +decl_module! { + pub struct Module, I: Instance = DefaultInstance> for enum Call where origin: T::Origin { + /// The minimum amount required to keep an account open. + const ExistentialDeposit: T::Balance = T::ExistentialDeposit::get(); + + /// The fee required to make a transfer. + const TransferFee: T::Balance = T::TransferFee::get(); + + /// The fee required to create an account. + const CreationFee: T::Balance = T::CreationFee::get(); + + /// The fee to be paid for making a transaction; the base. + const TransactionBaseFee: T::Balance = T::TransactionBaseFee::get(); + + /// The fee to be paid for making a transaction; the per-byte portion. + const TransactionByteFee: T::Balance = T::TransactionByteFee::get(); + + fn deposit_event() = default; + + /// Transfer some liquid free balance to another account. + /// + /// `transfer` will set the `FreeBalance` of the sender and receiver. + /// It will decrease the total issuance of the system by the `TransferFee`. + /// If the sender's account is below the existential deposit as a result + /// of the transfer, the account will be reaped. + /// + /// The dispatch origin for this call must be `Signed` by the transactor. + /// + /// # + /// - Dependent on arguments but not critical, given proper implementations for + /// input config types. See related functions below. + /// - It contains a limited number of reads and writes internally and no complex computation. + /// + /// Related functions: + /// + /// - `ensure_can_withdraw` is always called internally but has a bounded complexity. + /// - Transferring balances to accounts that did not exist before will cause + /// `T::OnNewAccount::on_new_account` to be called. + /// - Removing enough funds from an account will trigger + /// `T::DustRemoval::on_unbalanced` and `T::OnFreeBalanceZero::on_free_balance_zero`. + /// + /// # + #[weight = SimpleDispatchInfo::FixedNormal(1_000_000)] + pub fn transfer( + origin, + dest: ::Source, + #[compact] value: T::Balance + ) { + debug!("balances::transfer() called. checking signature"); + let transactor = ensure_signed(origin)?; + let dest = T::Lookup::lookup(dest)?; + >::transfer(&transactor, &dest, value)?; + } + + /// Set the balances of a given account. + /// + /// This will alter `FreeBalance` and `ReservedBalance` in storage. it will + /// also decrease the total issuance of the system (`TotalIssuance`). + /// If the new free or reserved balance is below the existential deposit, + /// it will reset the account nonce (`system::AccountNonce`). + /// + /// The dispatch origin for this call is `root`. + /// + /// # + /// - Independent of the arguments. + /// - Contains a limited number of reads and writes. + /// # + #[weight = SimpleDispatchInfo::FixedOperational(50_000)] + fn set_balance( + origin, + who: ::Source, + #[compact] new_free: T::Balance, + #[compact] new_reserved: T::Balance + ) { + debug!("set_balance called"); + ensure_root(origin)?; + let who = T::Lookup::lookup(who)?; + debug!("query FreeBalance"); + let current_free = >::get(&who); + if new_free > current_free { + mem::drop(PositiveImbalance::::new(new_free - current_free)); + } else if new_free < current_free { + mem::drop(NegativeImbalance::::new(current_free - new_free)); + } + Self::set_free_balance(&who, new_free); + + let current_reserved = >::get(&who); + if new_reserved > current_reserved { + mem::drop(PositiveImbalance::::new(new_reserved - current_reserved)); + } else if new_reserved < current_reserved { + mem::drop(NegativeImbalance::::new(current_reserved - new_reserved)); + } + Self::set_reserved_balance(&who, new_reserved); + } + + /// Exactly as `transfer`, except the origin must be root and the source account may be + /// specified. + #[weight = SimpleDispatchInfo::FixedNormal(1_000_000)] + pub fn force_transfer( + origin, + source: ::Source, + dest: ::Source, + #[compact] value: T::Balance + ) { + ensure_root(origin)?; + let source = T::Lookup::lookup(source)?; + let dest = T::Lookup::lookup(dest)?; + >::transfer(&source, &dest, value)?; + } + } +} + +impl, I: Instance> Module { + + // PUBLIC IMMUTABLES + + /// Get the amount that is currently being vested and cannot be transferred out of this account. + pub fn vesting_balance(who: &T::AccountId) -> T::Balance { + if let Some(v) = Self::vesting(who) { + Self::free_balance(who) + .min(v.locked_at(>::block_number())) + } else { + Zero::zero() + } + } + + // PRIVATE MUTABLES + + /// Set the reserved balance of an account to some new value. Will enforce `ExistentialDeposit` + /// law, annulling the account as needed. + /// + /// Doesn't do any preparatory work for creating a new account, so should only be used when it + /// is known that the account already exists. + /// + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + fn set_reserved_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { + debug!("set_reserved_balance called"); + if balance < T::ExistentialDeposit::get() { + debug!("below existential deposit"); + debug!("calling ReservedBalance::insert"); + >::insert(who, balance); + Self::on_reserved_too_low(who); + UpdateBalanceOutcome::AccountKilled + } else { + debug!("calling ReservedBalance::insert"); + >::insert(who, balance); + UpdateBalanceOutcome::Updated + } + } + + /// Set the free balance of an account to some new value. Will enforce `ExistentialDeposit` + /// law, annulling the account as needed. + /// + /// Doesn't do any preparatory work for creating a new account, so should only be used when it + /// is known that the account already exists. + /// + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + fn set_free_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { + // Commented out for now - but consider it instructive. + // assert!(!Self::total_balance(who).is_zero()); + // assert!(Self::free_balance(who) > T::ExistentialDeposit::get()); + debug!("set_free_balance called"); + if balance < T::ExistentialDeposit::get() { + debug!("below existential deposit"); + debug!("calling FreeBalance::insert"); + >::insert(who, balance); + Self::on_free_too_low(who); + UpdateBalanceOutcome::AccountKilled + } else { + debug!("calling FreeBalance::insert"); + >::insert(who, balance); + UpdateBalanceOutcome::Updated + } + } + + /// Register a new account (with existential balance). + /// + /// This just calls appropriate hooks. It doesn't (necessarily) make any state changes. + fn new_account(who: &T::AccountId, balance: T::Balance) { + T::OnNewAccount::on_new_account(&who); + Self::deposit_event(RawEvent::NewAccount(who.clone(), balance.clone())); + } + + /// Unregister an account. + /// + /// This just removes the nonce and leaves an event. + fn reap_account(who: &T::AccountId) { + >::remove(who); + Self::deposit_event(RawEvent::ReapedAccount(who.clone())); + } + + /// Account's free balance has dropped below existential deposit. Kill its + /// free side and the account completely if its reserved size is already dead. + /// + /// Will maintain total issuance. + fn on_free_too_low(who: &T::AccountId) { + debug!("on_free_too_low called"); + debug!("FreeBalance::take"); + let dust = >::take(who); + >::remove(who); + + // underflow should never happen, but if it does, there's not much we can do about it. + if !dust.is_zero() { + T::DustRemoval::on_unbalanced(NegativeImbalance::new(dust)); + } + + T::OnFreeBalanceZero::on_free_balance_zero(who); + + if Self::reserved_balance(who).is_zero() { + Self::reap_account(who); + } + } + + /// Account's reserved balance has dropped below existential deposit. Kill its + /// reserved side and the account completely if its free size is already dead. + /// + /// Will maintain total issuance. + fn on_reserved_too_low(who: &T::AccountId) { + debug!("on_reserved_too_low called"); + debug!("ReservedBalance::take"); + let dust = >::take(who); + + // underflow should never happen, but it if does, there's nothing to be done here. + if !dust.is_zero() { + T::DustRemoval::on_unbalanced(NegativeImbalance::new(dust)); + } + + if Self::free_balance(who).is_zero() { + Self::reap_account(who); + } + } +} + +// wrapping these imbalances in a private module is necessary to ensure absolute privacy +// of the inner member. +mod imbalances { + use super::{ + result, Subtrait, DefaultInstance, Imbalance, Trait, Zero, Instance, Saturating, + StorageValue, + }; + use rstd::mem; + use sgx_log::*; + + /// Opaque, move-only struct with private fields that serves as a token denoting that + /// funds have been created without any equal and opposite accounting. + #[must_use] + pub struct PositiveImbalance, I: Instance=DefaultInstance>(T::Balance); + + impl, I: Instance> PositiveImbalance { + /// Create a new positive imbalance from a balance. + pub fn new(amount: T::Balance) -> Self { + PositiveImbalance(amount) + } + } + + /// Opaque, move-only struct with private fields that serves as a token denoting that + /// funds have been destroyed without any equal and opposite accounting. + #[must_use] + pub struct NegativeImbalance, I: Instance=DefaultInstance>(T::Balance); + + impl, I: Instance> NegativeImbalance { + /// Create a new negative imbalance from a balance. + pub fn new(amount: T::Balance) -> Self { + NegativeImbalance(amount) + } + } + + impl, I: Instance> Imbalance for PositiveImbalance { + type Opposite = NegativeImbalance; + + fn zero() -> Self { + Self(Zero::zero()) + } + fn drop_zero(self) -> result::Result<(), Self> { + if self.0.is_zero() { + Ok(()) + } else { + Err(self) + } + } + fn split(self, amount: T::Balance) -> (Self, Self) { + let first = self.0.min(amount); + let second = self.0 - first; + + mem::forget(self); + (Self(first), Self(second)) + } + fn merge(mut self, other: Self) -> Self { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + + self + } + fn subsume(&mut self, other: Self) { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + } + fn offset(self, other: Self::Opposite) -> result::Result { + let (a, b) = (self.0, other.0); + mem::forget((self, other)); + + if a >= b { + Ok(Self(a - b)) + } else { + Err(NegativeImbalance::new(b - a)) + } + } + fn peek(&self) -> T::Balance { + self.0.clone() + } + } + + impl, I: Instance> Imbalance for NegativeImbalance { + type Opposite = PositiveImbalance; + + fn zero() -> Self { + Self(Zero::zero()) + } + fn drop_zero(self) -> result::Result<(), Self> { + if self.0.is_zero() { + Ok(()) + } else { + Err(self) + } + } + fn split(self, amount: T::Balance) -> (Self, Self) { + let first = self.0.min(amount); + let second = self.0 - first; + + mem::forget(self); + (Self(first), Self(second)) + } + fn merge(mut self, other: Self) -> Self { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + + self + } + fn subsume(&mut self, other: Self) { + self.0 = self.0.saturating_add(other.0); + mem::forget(other); + } + fn offset(self, other: Self::Opposite) -> result::Result { + let (a, b) = (self.0, other.0); + mem::forget((self, other)); + + if a >= b { + Ok(Self(a - b)) + } else { + Err(PositiveImbalance::new(b - a)) + } + } + fn peek(&self) -> T::Balance { + self.0.clone() + } + } + + impl, I: Instance> Drop for PositiveImbalance { + /// Basic drop handler will just square up the total issuance. + fn drop(&mut self) { + debug!("drop() changing TotalIssuance PositiveImbalance"); + , I>>::mutate( + |v| *v = v.saturating_add(self.0) + ); + } + } + + impl, I: Instance> Drop for NegativeImbalance { + /// Basic drop handler will just square up the total issuance. + fn drop(&mut self) { + debug!("drop() changing TotalIssuance NegativeImbalance"); + , I>>::mutate( + |v| *v = v.saturating_sub(self.0) + ); + } + } +} + +// TODO: #2052 +// Somewhat ugly hack in order to gain access to module's `increase_total_issuance_by` +// using only the Subtrait (which defines only the types that are not dependent +// on Positive/NegativeImbalance). Subtrait must be used otherwise we end up with a +// circular dependency with Trait having some types be dependent on PositiveImbalance +// and PositiveImbalance itself depending back on Trait for its Drop impl (and thus +// its type declaration). +// This works as long as `increase_total_issuance_by` doesn't use the Imbalance +// types (basically for charging fees). +// This should eventually be refactored so that the three type items that do +// depend on the Imbalance type (TransactionPayment, TransferPayment, DustRemoval) +// are placed in their own SRML module. +struct ElevatedTrait, I: Instance>(T, I); +impl, I: Instance> Clone for ElevatedTrait { + fn clone(&self) -> Self { unimplemented!() } +} +impl, I: Instance> PartialEq for ElevatedTrait { + fn eq(&self, _: &Self) -> bool { unimplemented!() } +} +impl, I: Instance> Eq for ElevatedTrait {} +impl, I: Instance> system::Trait for ElevatedTrait { + type Origin = T::Origin; + type Call = T::Call; + type Index = T::Index; + type BlockNumber = T::BlockNumber; + type Hash = T::Hash; + type Hashing = T::Hashing; + type AccountId = T::AccountId; + type Lookup = T::Lookup; + type Header = T::Header; + type WeightMultiplierUpdate = T::WeightMultiplierUpdate; + type Event = (); + type BlockHashCount = T::BlockHashCount; + type MaximumBlockWeight = T::MaximumBlockWeight; + type MaximumBlockLength = T::MaximumBlockLength; + type AvailableBlockRatio = T::AvailableBlockRatio; + type Version = T::Version; +} +impl, I: Instance> Trait for ElevatedTrait { + type Balance = T::Balance; + type OnFreeBalanceZero = T::OnFreeBalanceZero; + type OnNewAccount = T::OnNewAccount; + type Event = (); + type TransactionPayment = (); + type TransferPayment = (); + type DustRemoval = (); + type ExistentialDeposit = T::ExistentialDeposit; + type TransferFee = T::TransferFee; + type CreationFee = T::CreationFee; + type TransactionBaseFee = T::TransactionBaseFee; + type TransactionByteFee = T::TransactionByteFee; + type WeightToFee = T::WeightToFee; +} + +impl, I: Instance> Currency for Module +where + T::Balance: MaybeSerializeDebug +{ + type Balance = T::Balance; + type PositiveImbalance = PositiveImbalance; + type NegativeImbalance = NegativeImbalance; + + fn total_balance(who: &T::AccountId) -> Self::Balance { + Self::free_balance(who) + Self::reserved_balance(who) + } + + fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool { + Self::free_balance(who) >= value + } + + fn total_issuance() -> Self::Balance { + >::get() + } + + fn minimum_balance() -> Self::Balance { + T::ExistentialDeposit::get() + } + + fn free_balance(who: &T::AccountId) -> Self::Balance { + >::get(who) + } + + fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance { + debug!("burn() called"); + >::mutate(|issued| { + *issued = issued.checked_sub(&amount).unwrap_or_else(|| { + amount = *issued; + Zero::zero() + }); + }); + PositiveImbalance::new(amount) + } + + fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance { + debug!("issue() called"); + >::mutate(|issued| + *issued = issued.checked_add(&amount).unwrap_or_else(|| { + amount = Self::Balance::max_value() - *issued; + Self::Balance::max_value() + }) + ); + NegativeImbalance::new(amount) + } + + // # + // Despite iterating over a list of locks, they are limited by the number of + // lock IDs, which means the number of runtime modules that intend to use and create locks. + // # + fn ensure_can_withdraw( + who: &T::AccountId, + _amount: T::Balance, + reason: WithdrawReason, + new_balance: T::Balance, + ) -> Result { + match reason { + WithdrawReason::Reserve | WithdrawReason::Transfer if Self::vesting_balance(who) > new_balance => + return Err("vesting balance too high to send value"), + _ => {} + } + let locks = Self::locks(who); + if locks.is_empty() { + return Ok(()) + } + + let now = >::block_number(); + if locks.into_iter() + .all(|l| + now >= l.until + || new_balance >= l.amount + || !l.reasons.contains(reason) + ) + { + Ok(()) + } else { + Err("account liquidity restrictions prevent withdrawal") + } + } + + fn transfer(transactor: &T::AccountId, dest: &T::AccountId, value: Self::Balance) -> Result { + debug!("balances::transfer() called"); + debug!("get from_balance"); + let from_balance = Self::free_balance(transactor); + debug!("get to_balance"); + let to_balance = Self::free_balance(dest); + let would_create = to_balance.is_zero(); + debug!("get fee"); + let fee = if would_create { T::CreationFee::get() } else { T::TransferFee::get() }; + let liability = match value.checked_add(&fee) { + Some(l) => l, + None => return Err("got overflow after adding a fee to value"), + }; + + let new_from_balance = match from_balance.checked_sub(&liability) { + None => return Err("balance too low to send value"), + Some(b) => b, + }; + if would_create && value < T::ExistentialDeposit::get() { + return Err("value too low to create account"); + } + Self::ensure_can_withdraw(transactor, value, WithdrawReason::Transfer, new_from_balance)?; + + // NOTE: total stake being stored in the same type means that this could never overflow + // but better to be safe than sorry. + let new_to_balance = match to_balance.checked_add(&value) { + Some(b) => b, + None => return Err("destination balance too high to receive value"), + }; + debug!("setting new balances"); + if transactor != dest { + Self::set_free_balance(transactor, new_from_balance); + if !>::exists(dest) { + Self::new_account(dest, new_to_balance); + } + Self::set_free_balance(dest, new_to_balance); + T::TransferPayment::on_unbalanced(NegativeImbalance::new(fee)); + Self::deposit_event(RawEvent::Transfer(transactor.clone(), dest.clone(), value, fee)); + } + + Ok(()) + } + + fn withdraw( + who: &T::AccountId, + value: Self::Balance, + reason: WithdrawReason, + liveness: ExistenceRequirement, + ) -> result::Result { + debug!("withdraw() called"); + if let Some(new_balance) = Self::free_balance(who).checked_sub(&value) { + if liveness == ExistenceRequirement::KeepAlive && new_balance < T::ExistentialDeposit::get() { + return Err("payment would kill account") + } + Self::ensure_can_withdraw(who, value, reason, new_balance)?; + Self::set_free_balance(who, new_balance); + Ok(NegativeImbalance::new(value)) + } else { + Err("too few free funds in account") + } + } + + fn slash( + who: &T::AccountId, + value: Self::Balance + ) -> (Self::NegativeImbalance, Self::Balance) { + let free_balance = Self::free_balance(who); + let free_slash = cmp::min(free_balance, value); + Self::set_free_balance(who, free_balance - free_slash); + let remaining_slash = value - free_slash; + // NOTE: `slash()` prefers free balance, but assumes that reserve balance can be drawn + // from in extreme circumstances. `can_slash()` should be used prior to `slash()` to avoid having + // to draw from reserved funds, however we err on the side of punishment if things are inconsistent + // or `can_slash` wasn't used appropriately. + if !remaining_slash.is_zero() { + let reserved_balance = Self::reserved_balance(who); + let reserved_slash = cmp::min(reserved_balance, remaining_slash); + Self::set_reserved_balance(who, reserved_balance - reserved_slash); + (NegativeImbalance::new(free_slash + reserved_slash), remaining_slash - reserved_slash) + } else { + (NegativeImbalance::new(value), Zero::zero()) + } + } + + fn deposit_into_existing( + who: &T::AccountId, + value: Self::Balance + ) -> result::Result { + if Self::total_balance(who).is_zero() { + return Err("beneficiary account must pre-exist"); + } + Self::set_free_balance(who, Self::free_balance(who) + value); + Ok(PositiveImbalance::new(value)) + } + + fn deposit_creating( + who: &T::AccountId, + value: Self::Balance, + ) -> Self::PositiveImbalance { + let (imbalance, _) = Self::make_free_balance_be(who, Self::free_balance(who) + value); + if let SignedImbalance::Positive(p) = imbalance { + p + } else { + // Impossible, but be defensive. + Self::PositiveImbalance::zero() + } + } + + fn make_free_balance_be(who: &T::AccountId, balance: Self::Balance) -> ( + SignedImbalance, + UpdateBalanceOutcome + ) { + let original = Self::free_balance(who); + if balance < T::ExistentialDeposit::get() && original.is_zero() { + // If we're attempting to set an existing account to less than ED, then + // bypass the entire operation. It's a no-op if you follow it through, but + // since this is an instance where we might account for a negative imbalance + // (in the dust cleaner of set_free_balance) before we account for its actual + // equal and opposite cause (returned as an Imbalance), then in the + // instance that there's no other accounts on the system at all, we might + // underflow the issuance and our arithmetic will be off. + return ( + SignedImbalance::Positive(Self::PositiveImbalance::zero()), + UpdateBalanceOutcome::AccountKilled, + ) + } + let imbalance = if original <= balance { + SignedImbalance::Positive(PositiveImbalance::new(balance - original)) + } else { + SignedImbalance::Negative(NegativeImbalance::new(original - balance)) + }; + // If the balance is too low, then the account is reaped. + // NOTE: There are two balances for every account: `reserved_balance` and + // `free_balance`. This contract subsystem only cares about the latter: whenever + // the term "balance" is used *here* it should be assumed to mean "free balance" + // in the rest of the module. + // Free balance can never be less than ED. If that happens, it gets reduced to zero + // and the account information relevant to this subsystem is deleted (i.e. the + // account is reaped). + let outcome = if balance < T::ExistentialDeposit::get() { + Self::set_free_balance(who, balance); + UpdateBalanceOutcome::AccountKilled + } else { + if !>::exists(who) { + Self::new_account(&who, balance); + } + Self::set_free_balance(who, balance); + UpdateBalanceOutcome::Updated + }; + (imbalance, outcome) + } +} + +impl, I: Instance> ReservableCurrency for Module +where + T::Balance: MaybeSerializeDebug +{ + fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool { + Self::free_balance(who) + .checked_sub(&value) + .map_or(false, |new_balance| + Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve, new_balance).is_ok() + ) + } + + fn reserved_balance(who: &T::AccountId) -> Self::Balance { + >::get(who) + } + + fn reserve(who: &T::AccountId, value: Self::Balance) -> result::Result<(), &'static str> { + let b = Self::free_balance(who); + if b < value { + return Err("not enough free funds") + } + let new_balance = b - value; + Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve, new_balance)?; + Self::set_reserved_balance(who, Self::reserved_balance(who) + value); + Self::set_free_balance(who, new_balance); + Ok(()) + } + + fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance { + let b = Self::reserved_balance(who); + let actual = cmp::min(b, value); + Self::set_free_balance(who, Self::free_balance(who) + actual); + Self::set_reserved_balance(who, b - actual); + value - actual + } + + fn slash_reserved( + who: &T::AccountId, + value: Self::Balance + ) -> (Self::NegativeImbalance, Self::Balance) { + let b = Self::reserved_balance(who); + let slash = cmp::min(b, value); + // underflow should never happen, but it if does, there's nothing to be done here. + Self::set_reserved_balance(who, b - slash); + (NegativeImbalance::new(slash), value - slash) + } + + fn repatriate_reserved( + slashed: &T::AccountId, + beneficiary: &T::AccountId, + value: Self::Balance, + ) -> result::Result { + if Self::total_balance(beneficiary).is_zero() { + return Err("beneficiary account must pre-exist"); + } + let b = Self::reserved_balance(slashed); + let slash = cmp::min(b, value); + Self::set_free_balance(beneficiary, Self::free_balance(beneficiary) + slash); + Self::set_reserved_balance(slashed, b - slash); + Ok(value - slash) + } +} + +impl, I: Instance> LockableCurrency for Module +where + T::Balance: MaybeSerializeDebug +{ + type Moment = T::BlockNumber; + + fn set_lock( + id: LockIdentifier, + who: &T::AccountId, + amount: T::Balance, + until: T::BlockNumber, + reasons: WithdrawReasons, + ) { + let now = >::block_number(); + let mut new_lock = Some(BalanceLock { id, amount, until, reasons }); + let mut locks = Self::locks(who).into_iter().filter_map(|l| + if l.id == id { + new_lock.take() + } else if l.until > now { + Some(l) + } else { + None + }).collect::>(); + if let Some(lock) = new_lock { + locks.push(lock) + } + >::insert(who, locks); + } + + fn extend_lock( + id: LockIdentifier, + who: &T::AccountId, + amount: T::Balance, + until: T::BlockNumber, + reasons: WithdrawReasons, + ) { + let now = >::block_number(); + let mut new_lock = Some(BalanceLock { id, amount, until, reasons }); + let mut locks = Self::locks(who).into_iter().filter_map(|l| + if l.id == id { + new_lock.take().map(|nl| { + BalanceLock { + id: l.id, + amount: l.amount.max(nl.amount), + until: l.until.max(nl.until), + reasons: l.reasons | nl.reasons, + } + }) + } else if l.until > now { + Some(l) + } else { + None + }).collect::>(); + if let Some(lock) = new_lock { + locks.push(lock) + } + >::insert(who, locks); + } + + fn remove_lock( + id: LockIdentifier, + who: &T::AccountId, + ) { + let now = >::block_number(); + let locks = Self::locks(who).into_iter().filter_map(|l| + if l.until > now && l.id != id { + Some(l) + } else { + None + }).collect::>(); + >::insert(who, locks); + } +} + +/// Require the transactor pay for themselves and maybe include a tip to gain additional priority +/// in the queue. +#[derive(Encode, Decode, Clone, Eq, PartialEq)] +pub struct TakeFees, I: Instance = DefaultInstance>(#[codec(compact)] T::Balance); + +impl, I: Instance> TakeFees { + /// utility constructor. Used only in client/factory code. + pub fn from(fee: T::Balance) -> Self { + Self(fee) + } + + /// Compute the final fee value for a particular transaction. + /// + /// The final fee is composed of: + /// - _length-fee_: This is the amount paid merely to pay for size of the transaction. + /// - _weight-fee_: This amount is computed based on the weight of the transaction. Unlike + /// size-fee, this is not input dependent and reflects the _complexity_ of the execution + /// and the time it consumes. + /// - (optional) _tip_: if included in the transaction, it will be added on top. Only signed + /// transactions can have a tip. + fn compute_fee(len: usize, info: DispatchInfo, tip: T::Balance) -> T::Balance { + debug!("compute_fee() alled"); + let len_fee = if info.pay_length_fee() { + let len = T::Balance::from(len as u32); + let base = T::TransactionBaseFee::get(); + let per_byte = T::TransactionByteFee::get(); + base.saturating_add(per_byte.saturating_mul(len)) + } else { + Zero::zero() + }; + + let weight_fee = { + // cap the weight to the maximum defined in runtime, otherwise it will be the `Bounded` + // maximum of its data type, which is not desired. + let capped_weight = info.weight.min(::MaximumBlockWeight::get()); + let weight_update = >::next_weight_multiplier(); + let adjusted_weight = weight_update.apply_to(capped_weight); + T::WeightToFee::convert(adjusted_weight) + }; + + len_fee.saturating_add(weight_fee).saturating_add(tip) + } +} + +#[cfg(feature = "std")] +impl, I: Instance> rstd::fmt::Debug for TakeFees { + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + self.0.fmt(f) + } +} + +impl, I: Instance + Clone + Eq> SignedExtension for TakeFees { + type AccountId = T::AccountId; + type Call = T::Call; + type AdditionalSigned = (); + type Pre = (); + fn additional_signed(&self) -> rstd::result::Result<(), TransactionValidityError> { Ok(()) } + + fn validate( + &self, + who: &Self::AccountId, + _call: &Self::Call, + info: DispatchInfo, + len: usize, + ) -> TransactionValidity { + // pay any fees. + let fee = Self::compute_fee(len, info, self.0); + let imbalance = match >::withdraw( + who, + fee, + WithdrawReason::TransactionPayment, + ExistenceRequirement::KeepAlive, + ) { + Ok(imbalance) => imbalance, + Err(_) => return InvalidTransaction::Payment.into(), + }; + T::TransactionPayment::on_unbalanced(imbalance); + + let mut r = ValidTransaction::default(); + // NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which + // will be a bit more than setting the priority to tip. For now, this is enough. + r.priority = fee.saturated_into::(); + Ok(r) + } +} + +impl, I: Instance> IsDeadAccount for Module +where + T::Balance: MaybeSerializeDebug +{ + fn is_dead_account(who: &T::AccountId) -> bool { + Self::total_balance(who).is_zero() + } +} diff --git a/enclave/balances/src/mock.rs b/enclave/balances/src/mock.rs new file mode 100644 index 0000000000..12a49fc90d --- /dev/null +++ b/enclave/balances/src/mock.rs @@ -0,0 +1,220 @@ +// Copyright 2018-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Test utilities + +#![cfg(test)] + +use sr_primitives::{Perbill, traits::{Convert, IdentityLookup}, testing::Header, + weights::{DispatchInfo, Weight}}; +use primitives::{H256, Blake2Hasher}; +use runtime_io; +use support::{impl_outer_origin, parameter_types}; +use support::traits::Get; +use std::cell::RefCell; +use crate::{GenesisConfig, Module, Trait}; + +impl_outer_origin!{ + pub enum Origin for Runtime {} +} + +thread_local! { + static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); + static TRANSFER_FEE: RefCell = RefCell::new(0); + static CREATION_FEE: RefCell = RefCell::new(0); + static TRANSACTION_BASE_FEE: RefCell = RefCell::new(0); + static TRANSACTION_BYTE_FEE: RefCell = RefCell::new(1); + static TRANSACTION_WEIGHT_FEE: RefCell = RefCell::new(1); + static WEIGHT_TO_FEE: RefCell = RefCell::new(1); +} + +pub struct ExistentialDeposit; +impl Get for ExistentialDeposit { + fn get() -> u64 { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow()) } +} + +pub struct TransferFee; +impl Get for TransferFee { + fn get() -> u64 { TRANSFER_FEE.with(|v| *v.borrow()) } +} + +pub struct CreationFee; +impl Get for CreationFee { + fn get() -> u64 { CREATION_FEE.with(|v| *v.borrow()) } +} + +pub struct TransactionBaseFee; +impl Get for TransactionBaseFee { + fn get() -> u64 { TRANSACTION_BASE_FEE.with(|v| *v.borrow()) } +} + +pub struct TransactionByteFee; +impl Get for TransactionByteFee { + fn get() -> u64 { TRANSACTION_BYTE_FEE.with(|v| *v.borrow()) } +} + +pub struct WeightToFee(u64); +impl Convert for WeightToFee { + fn convert(t: Weight) -> u64 { + WEIGHT_TO_FEE.with(|v| *v.borrow() * (t as u64)) + } +} + +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Runtime; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} +impl system::Trait for Runtime { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Call = (); + type Hash = H256; + type Hashing = ::sr_primitives::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type WeightMultiplierUpdate = (); + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); +} +impl Trait for Runtime { + type Balance = u64; + type OnFreeBalanceZero = (); + type OnNewAccount = (); + type Event = (); + type TransactionPayment = (); + type DustRemoval = (); + type TransferPayment = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; + type CreationFee = CreationFee; + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; + type WeightToFee = WeightToFee; +} + +pub struct ExtBuilder { + transaction_base_fee: u64, + transaction_byte_fee: u64, + weight_to_fee: u64, + existential_deposit: u64, + transfer_fee: u64, + creation_fee: u64, + monied: bool, + vesting: bool, +} +impl Default for ExtBuilder { + fn default() -> Self { + Self { + transaction_base_fee: 0, + transaction_byte_fee: 0, + weight_to_fee: 0, + existential_deposit: 0, + transfer_fee: 0, + creation_fee: 0, + monied: false, + vesting: false, + } + } +} +impl ExtBuilder { + pub fn transaction_fees(mut self, base_fee: u64, byte_fee: u64, weight_fee: u64) -> Self { + self.transaction_base_fee = base_fee; + self.transaction_byte_fee = byte_fee; + self.weight_to_fee = weight_fee; + self + } + pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { + self.existential_deposit = existential_deposit; + self + } + #[allow(dead_code)] + pub fn transfer_fee(mut self, transfer_fee: u64) -> Self { + self.transfer_fee = transfer_fee; + self + } + pub fn creation_fee(mut self, creation_fee: u64) -> Self { + self.creation_fee = creation_fee; + self + } + pub fn monied(mut self, monied: bool) -> Self { + self.monied = monied; + if self.existential_deposit == 0 { + self.existential_deposit = 1; + } + self + } + pub fn vesting(mut self, vesting: bool) -> Self { + self.vesting = vesting; + self + } + pub fn set_associated_consts(&self) { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + TRANSFER_FEE.with(|v| *v.borrow_mut() = self.transfer_fee); + CREATION_FEE.with(|v| *v.borrow_mut() = self.creation_fee); + TRANSACTION_BASE_FEE.with(|v| *v.borrow_mut() = self.transaction_base_fee); + TRANSACTION_BYTE_FEE.with(|v| *v.borrow_mut() = self.transaction_byte_fee); + WEIGHT_TO_FEE.with(|v| *v.borrow_mut() = self.weight_to_fee); + } + pub fn build(self) -> runtime_io::TestExternalities { + self.set_associated_consts(); + let mut t = system::GenesisConfig::default().build_storage::().unwrap(); + GenesisConfig:: { + balances: if self.monied { + vec![ + (1, 10 * self.existential_deposit), + (2, 20 * self.existential_deposit), + (3, 30 * self.existential_deposit), + (4, 40 * self.existential_deposit), + (12, 10 * self.existential_deposit) + ] + } else { + vec![] + }, + vesting: if self.vesting && self.monied { + vec![ + (1, 0, 10, 5 * self.existential_deposit), + (2, 10, 20, 0), + (12, 10, 20, 5 * self.existential_deposit) + ] + } else { + vec![] + }, + }.assimilate_storage(&mut t).unwrap(); + t.into() + } +} + +pub type System = system::Module; +pub type Balances = Module; + + +pub const CALL: &::Call = &(); + +/// create a transaction info struct from weight. Handy to avoid building the whole struct. +pub fn info_from_weight(w: Weight) -> DispatchInfo { + DispatchInfo { weight: w, ..Default::default() } +} diff --git a/enclave/balances/src/tests.rs b/enclave/balances/src/tests.rs new file mode 100644 index 0000000000..8e9f6acdd8 --- /dev/null +++ b/enclave/balances/src/tests.rs @@ -0,0 +1,825 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Tests for the module. + +#![cfg(test)] + +use super::*; +use mock::{Balances, ExtBuilder, Runtime, System, info_from_weight, CALL}; +use runtime_io::with_externalities; +use support::{ + assert_noop, assert_ok, assert_err, + traits::{LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons, + Currency, ReservableCurrency} +}; +use system::RawOrigin; + +const ID_1: LockIdentifier = *b"1 "; +const ID_2: LockIdentifier = *b"2 "; +const ID_3: LockIdentifier = *b"3 "; + +#[test] +fn basic_locking_should_work() { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + assert_eq!(Balances::free_balance(&1), 10); + Balances::set_lock(ID_1, &1, 9, u64::max_value(), WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 5), + "account liquidity restrictions prevent withdrawal" + ); + }); +} + +#[test] +fn partial_locking_should_work() { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + Balances::set_lock(ID_1, &1, 5, u64::max_value(), WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1)); + }); +} + +#[test] +fn lock_removal_should_work() { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + Balances::set_lock(ID_1, &1, u64::max_value(), u64::max_value(), WithdrawReasons::all()); + Balances::remove_lock(ID_1, &1); + assert_ok!(>::transfer(&1, &2, 1)); + }); +} + +#[test] +fn lock_replacement_should_work() { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + Balances::set_lock(ID_1, &1, u64::max_value(), u64::max_value(), WithdrawReasons::all()); + Balances::set_lock(ID_1, &1, 5, u64::max_value(), WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1)); + }); +} + +#[test] +fn double_locking_should_work() { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + Balances::set_lock(ID_1, &1, 5, u64::max_value(), WithdrawReasons::all()); + Balances::set_lock(ID_2, &1, 5, u64::max_value(), WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1)); + }); +} + +#[test] +fn combination_locking_should_work() { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + Balances::set_lock(ID_1, &1, u64::max_value(), 0, WithdrawReasons::none()); + Balances::set_lock(ID_2, &1, 0, u64::max_value(), WithdrawReasons::none()); + Balances::set_lock(ID_3, &1, 0, 0, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1)); + }); +} + +#[test] +fn lock_value_extension_should_work() { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + Balances::set_lock(ID_1, &1, 5, u64::max_value(), WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6), + "account liquidity restrictions prevent withdrawal" + ); + Balances::extend_lock(ID_1, &1, 2, u64::max_value(), WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6), + "account liquidity restrictions prevent withdrawal" + ); + Balances::extend_lock(ID_1, &1, 8, u64::max_value(), WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 3), + "account liquidity restrictions prevent withdrawal" + ); + }); +} + +#[test] +fn lock_reasons_should_work() { + with_externalities( + &mut ExtBuilder::default() + .existential_deposit(1) + .monied(true).transaction_fees(0, 1, 0) + .build(), + || { + Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::Transfer.into()); + assert_noop!( + >::transfer(&1, &2, 1), + "account liquidity restrictions prevent withdrawal" + ); + assert_ok!(>::reserve(&1, 1)); + // NOTE: this causes a fee payment. + assert!( as SignedExtension>::pre_dispatch( + TakeFees::from(1), + &1, + CALL, + info_from_weight(1), + 0, + ).is_ok()); + + Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::Reserve.into()); + assert_ok!(>::transfer(&1, &2, 1)); + assert_noop!( + >::reserve(&1, 1), + "account liquidity restrictions prevent withdrawal" + ); + assert!( as SignedExtension>::pre_dispatch( + TakeFees::from(1), + &1, + CALL, + info_from_weight(1), + 0, + ).is_ok()); + + Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::TransactionPayment.into()); + assert_ok!(>::transfer(&1, &2, 1)); + assert_ok!(>::reserve(&1, 1)); + assert!( as SignedExtension>::pre_dispatch( + TakeFees::from(1), + &1, + CALL, + info_from_weight(1), + 0, + ).is_err()); + } + ); +} + +#[test] +fn lock_block_number_should_work() { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + Balances::set_lock(ID_1, &1, 10, 2, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 1), + "account liquidity restrictions prevent withdrawal" + ); + + System::set_block_number(2); + assert_ok!(>::transfer(&1, &2, 1)); + }); +} + +#[test] +fn lock_block_number_extension_should_work() { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + Balances::set_lock(ID_1, &1, 10, 2, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6), + "account liquidity restrictions prevent withdrawal" + ); + Balances::extend_lock(ID_1, &1, 10, 1, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6), + "account liquidity restrictions prevent withdrawal" + ); + System::set_block_number(2); + Balances::extend_lock(ID_1, &1, 10, 8, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 3), + "account liquidity restrictions prevent withdrawal" + ); + }); +} + +#[test] +fn lock_reasons_extension_should_work() { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { + Balances::set_lock(ID_1, &1, 10, 10, WithdrawReason::Transfer.into()); + assert_noop!( + >::transfer(&1, &2, 6), + "account liquidity restrictions prevent withdrawal" + ); + Balances::extend_lock(ID_1, &1, 10, 10, WithdrawReasons::none()); + assert_noop!( + >::transfer(&1, &2, 6), + "account liquidity restrictions prevent withdrawal" + ); + Balances::extend_lock(ID_1, &1, 10, 10, WithdrawReason::Reserve.into()); + assert_noop!( + >::transfer(&1, &2, 6), + "account liquidity restrictions prevent withdrawal" + ); + }); +} + +#[test] +fn default_indexing_on_new_accounts_should_not_work2() { + with_externalities( + &mut ExtBuilder::default() + .existential_deposit(10) + .creation_fee(50) + .monied(true) + .build(), + || { + + assert_eq!(Balances::is_dead_account(&5), true); // account 5 should not exist + // ext_deposit is 10, value is 9, not satisfies for ext_deposit + assert_noop!( + Balances::transfer(Some(1).into(), 5, 9), + "value too low to create account", + ); + assert_eq!(Balances::is_dead_account(&5), true); // account 5 should not exist + assert_eq!(Balances::free_balance(&1), 100); + }, + ); +} + +#[test] +fn reserved_balance_should_prevent_reclaim_count() { + with_externalities( + &mut ExtBuilder::default() + .existential_deposit(256 * 1) + .monied(true) + .build(), + || { + System::inc_account_nonce(&2); + assert_eq!(Balances::is_dead_account(&2), false); + assert_eq!(Balances::is_dead_account(&5), true); + assert_eq!(Balances::total_balance(&2), 256 * 20); + + assert_ok!(Balances::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved + assert_eq!(Balances::free_balance(&2), 0); // "free" account deleted." + assert_eq!(Balances::total_balance(&2), 256 * 19 + 1); // reserve still exists. + assert_eq!(Balances::is_dead_account(&2), false); + assert_eq!(System::account_nonce(&2), 1); + + // account 4 tries to take index 1 for account 5. + assert_ok!(Balances::transfer(Some(4).into(), 5, 256 * 1 + 0x69)); + assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69); + assert_eq!(Balances::is_dead_account(&5), false); + + assert!(Balances::slash(&2, 256 * 18 + 2).1.is_zero()); // account 2 gets slashed + // "reserve" account reduced to 255 (below ED) so account deleted + assert_eq!(Balances::total_balance(&2), 0); + assert_eq!(System::account_nonce(&2), 0); // nonce zero + assert_eq!(Balances::is_dead_account(&2), true); + + // account 4 tries to take index 1 again for account 6. + assert_ok!(Balances::transfer(Some(4).into(), 6, 256 * 1 + 0x69)); + assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69); + assert_eq!(Balances::is_dead_account(&6), false); + }, + ); +} + + +#[test] +fn reward_should_work() { + with_externalities(&mut ExtBuilder::default().monied(true).build(), || { + assert_eq!(Balances::total_balance(&1), 10); + assert_ok!(Balances::deposit_into_existing(&1, 10).map(drop)); + assert_eq!(Balances::total_balance(&1), 20); + assert_eq!(>::get(), 120); + }); +} + +#[test] +fn dust_account_removal_should_work() { + with_externalities( + &mut ExtBuilder::default() + .existential_deposit(100) + .monied(true) + .build(), + || { + System::inc_account_nonce(&2); + assert_eq!(System::account_nonce(&2), 1); + assert_eq!(Balances::total_balance(&2), 2000); + + assert_ok!(Balances::transfer(Some(2).into(), 5, 1901)); // index 1 (account 2) becomes zombie + assert_eq!(Balances::total_balance(&2), 0); + assert_eq!(Balances::total_balance(&5), 1901); + assert_eq!(System::account_nonce(&2), 0); + }, + ); +} + +#[test] +fn dust_account_removal_should_work2() { + with_externalities( + &mut ExtBuilder::default() + .existential_deposit(100) + .creation_fee(50) + .monied(true) + .build(), + || { + System::inc_account_nonce(&2); + assert_eq!(System::account_nonce(&2), 1); + assert_eq!(Balances::total_balance(&2), 2000); + // index 1 (account 2) becomes zombie for 256*10 + 50(fee) < 256 * 10 (ext_deposit) + assert_ok!(Balances::transfer(Some(2).into(), 5, 1851)); + assert_eq!(Balances::total_balance(&2), 0); + assert_eq!(Balances::total_balance(&5), 1851); + assert_eq!(System::account_nonce(&2), 0); + }, + ); +} + +#[test] +fn balance_works() { + with_externalities(&mut ExtBuilder::default().build(), || { + let _ = Balances::deposit_creating(&1, 42); + assert_eq!(Balances::free_balance(&1), 42); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::free_balance(&2), 0); + assert_eq!(Balances::reserved_balance(&2), 0); + assert_eq!(Balances::total_balance(&2), 0); + }); +} + +#[test] +fn balance_transfer_works() { + with_externalities(&mut ExtBuilder::default().build(), || { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::transfer(Some(1).into(), 2, 69)); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::total_balance(&2), 69); + }); +} + +#[test] +fn force_transfer_works() { + with_externalities(&mut ExtBuilder::default().build(), || { + let _ = Balances::deposit_creating(&1, 111); + assert_noop!( + Balances::force_transfer(Some(2).into(), 1, 2, 69), + "RequireRootOrigin", + ); + assert_ok!(Balances::force_transfer(RawOrigin::Root.into(), 1, 2, 69)); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::total_balance(&2), 69); + }); +} + +#[test] +fn reserving_balance_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + let _ = Balances::deposit_creating(&1, 111); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(&1), 111); + assert_eq!(Balances::reserved_balance(&1), 0); + + assert_ok!(Balances::reserve(&1, 69)); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(&1), 42); + assert_eq!(Balances::reserved_balance(&1), 69); + }); +} + +#[test] +fn balance_transfer_when_reserved_should_not_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert_noop!( + Balances::transfer(Some(1).into(), 2, 69), + "balance too low to send value", + ); + }); +} + +#[test] +fn deducting_balance_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert_eq!(Balances::free_balance(&1), 42); + }); +} + +#[test] +fn refunding_balance_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + let _ = Balances::deposit_creating(&1, 42); + Balances::set_reserved_balance(&1, 69); + Balances::unreserve(&1, 69); + assert_eq!(Balances::free_balance(&1), 111); + assert_eq!(Balances::reserved_balance(&1), 0); + }); +} + +#[test] +fn slashing_balance_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert!(Balances::slash(&1, 69).1.is_zero()); + assert_eq!(Balances::free_balance(&1), 0); + assert_eq!(Balances::reserved_balance(&1), 42); + assert_eq!(>::get(), 42); + }); +} + +#[test] +fn slashing_incomplete_balance_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + let _ = Balances::deposit_creating(&1, 42); + assert_ok!(Balances::reserve(&1, 21)); + assert_eq!(Balances::slash(&1, 69).1, 27); + assert_eq!(Balances::free_balance(&1), 0); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(>::get(), 0); + }); +} + +#[test] +fn unreserving_balance_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 111)); + Balances::unreserve(&1, 42); + assert_eq!(Balances::reserved_balance(&1), 69); + assert_eq!(Balances::free_balance(&1), 42); + }); +} + +#[test] +fn slashing_reserved_balance_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 111)); + assert_eq!(Balances::slash_reserved(&1, 42).1, 0); + assert_eq!(Balances::reserved_balance(&1), 69); + assert_eq!(Balances::free_balance(&1), 0); + assert_eq!(>::get(), 69); + }); +} + +#[test] +fn slashing_incomplete_reserved_balance_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 42)); + assert_eq!(Balances::slash_reserved(&1, 69).1, 27); + assert_eq!(Balances::free_balance(&1), 69); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(>::get(), 69); + }); +} + +#[test] +fn transferring_reserved_balance_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 1); + assert_ok!(Balances::reserve(&1, 110)); + assert_ok!(Balances::repatriate_reserved(&1, &2, 41), 0); + assert_eq!(Balances::reserved_balance(&1), 69); + assert_eq!(Balances::free_balance(&1), 0); + assert_eq!(Balances::reserved_balance(&2), 0); + assert_eq!(Balances::free_balance(&2), 42); + }); +} + +#[test] +fn transferring_reserved_balance_to_nonexistent_should_fail() { + with_externalities(&mut ExtBuilder::default().build(), || { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 111)); + assert_noop!(Balances::repatriate_reserved(&1, &2, 42), "beneficiary account must pre-exist"); + }); +} + +#[test] +fn transferring_incomplete_reserved_balance_should_work() { + with_externalities(&mut ExtBuilder::default().build(), || { + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 1); + assert_ok!(Balances::reserve(&1, 41)); + assert_ok!(Balances::repatriate_reserved(&1, &2, 69), 28); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(Balances::free_balance(&1), 69); + assert_eq!(Balances::reserved_balance(&2), 0); + assert_eq!(Balances::free_balance(&2), 42); + }); +} + +#[test] +fn transferring_too_high_value_should_not_panic() { + with_externalities(&mut ExtBuilder::default().build(), || { + >::insert(1, u64::max_value()); + >::insert(2, 1); + + assert_err!( + Balances::transfer(Some(1).into(), 2, u64::max_value()), + "destination balance too high to receive value", + ); + + assert_eq!(Balances::free_balance(&1), u64::max_value()); + assert_eq!(Balances::free_balance(&2), 1); + }); +} + +#[test] +fn account_create_on_free_too_low_with_other() { + with_externalities( + &mut ExtBuilder::default().existential_deposit(100).build(), + || { + let _ = Balances::deposit_creating(&1, 100); + assert_eq!(>::get(), 100); + + // No-op. + let _ = Balances::deposit_creating(&2, 50); + assert_eq!(Balances::free_balance(&2), 0); + assert_eq!(>::get(), 100); + } + ) +} + + +#[test] +fn account_create_on_free_too_low() { + with_externalities( + &mut ExtBuilder::default().existential_deposit(100).build(), + || { + // No-op. + let _ = Balances::deposit_creating(&2, 50); + assert_eq!(Balances::free_balance(&2), 0); + assert_eq!(>::get(), 0); + } + ) +} + +#[test] +fn account_removal_on_free_too_low() { + with_externalities( + &mut ExtBuilder::default().existential_deposit(100).build(), + || { + assert_eq!(>::get(), 0); + + // Setup two accounts with free balance above the existential threshold. + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 110); + + assert_eq!(Balances::free_balance(&1), 110); + assert_eq!(Balances::free_balance(&2), 110); + assert_eq!(>::get(), 220); + + // Transfer funds from account 1 of such amount that after this transfer + // the balance of account 1 will be below the existential threshold. + // This should lead to the removal of all balance of this account. + assert_ok!(Balances::transfer(Some(1).into(), 2, 20)); + + // Verify free balance removal of account 1. + assert_eq!(Balances::free_balance(&1), 0); + assert_eq!(Balances::free_balance(&2), 130); + + // Verify that TotalIssuance tracks balance removal when free balance is too low. + assert_eq!(>::get(), 130); + }, + ); +} + +#[test] +fn transfer_overflow_isnt_exploitable() { + with_externalities( + &mut ExtBuilder::default().creation_fee(50).build(), + || { + // Craft a value that will overflow if summed with `creation_fee`. + let evil_value = u64::max_value() - 49; + + assert_err!( + Balances::transfer(Some(1).into(), 5, evil_value), + "got overflow after adding a fee to value", + ); + } + ); +} + +#[test] +fn check_vesting_status() { + with_externalities( + &mut ExtBuilder::default() + .existential_deposit(256) + .monied(true) + .vesting(true) + .build(), + || { + assert_eq!(System::block_number(), 1); + let user1_free_balance = Balances::free_balance(&1); + let user2_free_balance = Balances::free_balance(&2); + let user12_free_balance = Balances::free_balance(&12); + assert_eq!(user1_free_balance, 256 * 10); // Account 1 has free balance + assert_eq!(user2_free_balance, 256 * 20); // Account 2 has free balance + assert_eq!(user12_free_balance, 256 * 10); // Account 12 has free balance + let user1_vesting_schedule = VestingSchedule { + locked: 256 * 5, + per_block: 128, // Vesting over 10 blocks + starting_block: 0, + }; + let user2_vesting_schedule = VestingSchedule { + locked: 256 * 20, + per_block: 256, // Vesting over 20 blocks + starting_block: 10, + }; + let user12_vesting_schedule = VestingSchedule { + locked: 256 * 5, + per_block: 64, // Vesting over 20 blocks + starting_block: 10, + }; + assert_eq!(Balances::vesting(&1), Some(user1_vesting_schedule)); // Account 1 has a vesting schedule + assert_eq!(Balances::vesting(&2), Some(user2_vesting_schedule)); // Account 2 has a vesting schedule + assert_eq!(Balances::vesting(&12), Some(user12_vesting_schedule)); // Account 12 has a vesting schedule + + // Account 1 has only 128 units vested from their illiquid 256 * 5 units at block 1 + assert_eq!(Balances::vesting_balance(&1), 128 * 9); + // Account 2 has their full balance locked + assert_eq!(Balances::vesting_balance(&2), user2_free_balance); + // Account 12 has only their illiquid funds locked + assert_eq!(Balances::vesting_balance(&12), user12_free_balance - 256 * 5); + + System::set_block_number(10); + assert_eq!(System::block_number(), 10); + + // Account 1 has fully vested by block 10 + assert_eq!(Balances::vesting_balance(&1), 0); + // Account 2 has started vesting by block 10 + assert_eq!(Balances::vesting_balance(&2), user2_free_balance); + // Account 12 has started vesting by block 10 + assert_eq!(Balances::vesting_balance(&12), user12_free_balance - 256 * 5); + + System::set_block_number(30); + assert_eq!(System::block_number(), 30); + + assert_eq!(Balances::vesting_balance(&1), 0); // Account 1 is still fully vested, and not negative + assert_eq!(Balances::vesting_balance(&2), 0); // Account 2 has fully vested by block 30 + assert_eq!(Balances::vesting_balance(&12), 0); // Account 2 has fully vested by block 30 + + } + ); +} + +#[test] +fn unvested_balance_should_not_transfer() { + with_externalities( + &mut ExtBuilder::default() + .existential_deposit(10) + .monied(true) + .vesting(true) + .build(), + || { + assert_eq!(System::block_number(), 1); + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 100); // Account 1 has free balance + // Account 1 has only 5 units vested at block 1 (plus 50 unvested) + assert_eq!(Balances::vesting_balance(&1), 45); + assert_noop!( + Balances::transfer(Some(1).into(), 2, 56), + "vesting balance too high to send value", + ); // Account 1 cannot send more than vested amount + } + ); +} + +#[test] +fn vested_balance_should_transfer() { + with_externalities( + &mut ExtBuilder::default() + .existential_deposit(10) + .monied(true) + .vesting(true) + .build(), + || { + assert_eq!(System::block_number(), 1); + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 100); // Account 1 has free balance + // Account 1 has only 5 units vested at block 1 (plus 50 unvested) + assert_eq!(Balances::vesting_balance(&1), 45); + assert_ok!(Balances::transfer(Some(1).into(), 2, 55)); + } + ); +} + +#[test] +fn extra_balance_should_transfer() { + with_externalities( + &mut ExtBuilder::default() + .existential_deposit(10) + .monied(true) + .vesting(true) + .build(), + || { + assert_eq!(System::block_number(), 1); + assert_ok!(Balances::transfer(Some(3).into(), 1, 100)); + assert_ok!(Balances::transfer(Some(3).into(), 2, 100)); + + let user1_free_balance = Balances::free_balance(&1); + assert_eq!(user1_free_balance, 200); // Account 1 has 100 more free balance than normal + + let user2_free_balance = Balances::free_balance(&2); + assert_eq!(user2_free_balance, 300); // Account 2 has 100 more free balance than normal + + // Account 1 has only 5 units vested at block 1 (plus 150 unvested) + assert_eq!(Balances::vesting_balance(&1), 45); + assert_ok!(Balances::transfer(Some(1).into(), 3, 155)); // Account 1 can send extra units gained + + // Account 2 has no units vested at block 1, but gained 100 + assert_eq!(Balances::vesting_balance(&2), 200); + assert_ok!(Balances::transfer(Some(2).into(), 3, 100)); // Account 2 can send extra units gained + } + ); +} + +#[test] +fn liquid_funds_should_transfer_with_delayed_vesting() { + with_externalities( + &mut ExtBuilder::default() + .existential_deposit(256) + .monied(true) + .vesting(true) + .build(), + || { + assert_eq!(System::block_number(), 1); + let user12_free_balance = Balances::free_balance(&12); + + assert_eq!(user12_free_balance, 2560); // Account 12 has free balance + // Account 12 has liquid funds + assert_eq!(Balances::vesting_balance(&12), user12_free_balance - 256 * 5); + + // Account 12 has delayed vesting + let user12_vesting_schedule = VestingSchedule { + locked: 256 * 5, + per_block: 64, // Vesting over 20 blocks + starting_block: 10, + }; + assert_eq!(Balances::vesting(&12), Some(user12_vesting_schedule)); + + // Account 12 can still send liquid funds + assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5)); + } + ); +} + +#[test] +fn signed_extension_take_fees_work() { + with_externalities( + &mut ExtBuilder::default() + .existential_deposit(10) + .transaction_fees(10, 1, 5) + .monied(true) + .build(), + || { + let len = 10; + assert!(TakeFees::::from(0).pre_dispatch(&1, CALL, info_from_weight(5), len).is_ok()); + assert_eq!(Balances::free_balance(&1), 100 - 20 - 25); + assert!(TakeFees::::from(5 /* tipped */).pre_dispatch(&1, CALL, info_from_weight(3), len).is_ok()); + assert_eq!(Balances::free_balance(&1), 100 - 20 - 25 - 20 - 5 - 15); + } + ); +} + +#[test] +fn signed_extension_take_fees_is_bounded() { + with_externalities( + &mut ExtBuilder::default() + .existential_deposit(1000) + .transaction_fees(0, 0, 1) + .monied(true) + .build(), + || { + use sr_primitives::weights::Weight; + + // maximum weight possible + assert!(TakeFees::::from(0).pre_dispatch(&1, CALL, info_from_weight(Weight::max_value()), 10).is_ok()); + // fee will be proportional to what is the actual maximum weight in the runtime. + assert_eq!( + Balances::free_balance(&1), + (10000 - ::MaximumBlockWeight::get()) as u64 + ); + } + ); +} + +#[test] +fn burn_must_work() { + with_externalities( + &mut ExtBuilder::default() + .monied(true) + .build(), + || { + let init_total_issuance = Balances::total_issuance(); + let imbalance = Balances::burn(10); + assert_eq!(Balances::total_issuance(), init_total_issuance - 10); + drop(imbalance); + assert_eq!(Balances::total_issuance(), init_total_issuance); + } + ); +} diff --git a/enclave/src/attestation.rs b/enclave/src/attestation.rs index c4a0919d7c..cb19d3942e 100755 --- a/enclave/src/attestation.rs +++ b/enclave/src/attestation.rs @@ -46,17 +46,21 @@ use core::default::Default; use runtime_primitives::generic::Era; use primitive_types::U256; -use parity_codec::{Decode, Encode, Compact}; +use codec::{Decode, Encode, Compact}; use utils::{hash_from_slice}; use utils::get_ecc_seed; -use my_node_runtime::{ - UncheckedExtrinsic, - Call, - SubstraTEERegistryCall -}; -use primitives::{ed25519}; -use crypto::ed25519::{keypair, signature}; -use utils::blake2_256; +use substrate_api_client::{ + extrinsic::xt_primitives::{UncheckedExtrinsicV3, GenericAddress, GenericExtra, SignedPayload}, + crypto::AccountKey, + extrinsic}; +use primitives::{ed25519, Pair}; +//use my_node_runtime::{UncheckedExtrinsic,Call,SubstraTEERegistryCall}; +/*use substrate_api_client::{compose_extrinsic, crypto::{AccountKey, CryptoKind}, + extrinsic, +};*/ + +//use crypto::ed25519::{keypair, signature}; +//use utils::blake2_256; use constants::{RA_SPID, RA_API_KEY}; @@ -576,8 +580,6 @@ pub unsafe extern "C" fn perform_ra( unchecked_extrinsic: * mut u8, unchecked_extrinsic_size: u32 ) -> sgx_status_t { - // initialize the logging environment in the enclave - env_logger::init(); // our certificate is unlinkable let sign_type = sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE; @@ -592,43 +594,35 @@ pub unsafe extern "C" fn perform_ra( let mut nonce_slice = slice::from_raw_parts(nonce, nonce_size as usize); let url_slice = slice::from_raw_parts(url, url_size as usize); let extrinsic_slice = slice::from_raw_parts_mut(unchecked_extrinsic, unchecked_extrinsic_size as usize); - let seed = match get_ecc_seed() { + let seedvec = match get_ecc_seed() { Ok(seed) => seed, Err(status) => return status, }; - let nonce = U256::decode(&mut nonce_slice).unwrap(); + let mut seed = [0u8; 32]; + let seedvec = &seedvec[..seed.len()]; // panics if not enough data + seed.copy_from_slice(seedvec); + let signer = AccountKey::Ed(ed25519::Pair::from_seed(&seed)); + info!("Restored ECC pubkey: {:?}", signer.public()); + + let nonce = u32::decode(&mut nonce_slice).unwrap(); let genesis_hash = hash_from_slice(genesis_hash_slice); let era = Era::immortal(); - let function = Call::SubstraTEERegistry(SubstraTEERegistryCall::register_enclave(cert_der.to_vec(), url_slice.to_vec())); - let index = nonce.low_u64(); - - let raw_payload = (Compact(index), function, era, genesis_hash); - - let (privkey, pubkey) = keypair(&seed); - - let sign = raw_payload.using_encoded(|payload| if payload.len() > 256 { - // include the blake2 hash of the payload - signature(&blake2_256(payload)[..], &privkey) - } else { - //println!("signing {}", HexDisplay::from(&payload)); - signature(payload, &privkey) - }); + //FIXME: define constants + let call = [7u8,0u8]; - // convert to valid signatures - let signerpub = ed25519::Public::from_raw(pubkey); - let signature = ed25519::Signature::from_raw(sign); + //FIXME: define constant at client + let spec_version = 4; - // build the extrinsic - let ex = UncheckedExtrinsic::new_signed( - index, - raw_payload.1, - signerpub.into(), - signature, - era, - ); + let xt = compose_extrinsic_offline!( + signer, + (call, cert_der.to_vec(), url_slice.to_vec()), + nonce, + genesis_hash, + spec_version + ); - let encoded = ex.encode(); + let encoded = xt.encode(); debug!(" [Enclave] Encoded extrinsic = {:?}", encoded); // split the extrinsic_slice at the length of the encoded extrinsic diff --git a/enclave/src/constants.rs b/enclave/src/constants.rs index 462e10f19c..05390faddd 100755 --- a/enclave/src/constants.rs +++ b/enclave/src/constants.rs @@ -16,8 +16,8 @@ */ pub const RSA3072_SEALED_KEY_FILE: &str = "rsa3072_key_sealed.bin"; -pub const ED25519_SEALED_KEY_FILE: &str = "ed25519_key_sealed.bin"; -pub const ENCRYPTED_STATE_FILE: &str = "sealed_counter_state.bin"; +pub const SEALED_SIGNER_SEED_FILE: &str = "ed25519_key_sealed.bin"; +pub const ENCRYPTED_STATE_FILE: &str = "sealed_stf_state.bin"; pub const AES_KEY_FILE_AND_INIT_V: &str = "aes_key_sealed.bin"; pub static RA_SPID: &str = "../bin/spid.txt"; diff --git a/enclave/src/hex.rs b/enclave/src/hex.rs index ff2ca6376b..9955d0aab8 100755 --- a/enclave/src/hex.rs +++ b/enclave/src/hex.rs @@ -68,7 +68,7 @@ pub fn encode_hex(bytes: &[u8]) -> String { .iter() .map(|byte| encode_hex_byte(*byte).iter().copied().collect()) .collect(); - strs.join(" ") + strs.join("") } #[cfg(test)] @@ -83,7 +83,7 @@ mod test { assert_eq!(decode_hex("00"), [0x00u8].to_vec()); assert_eq!(decode_hex("ff"), [0xffu8].to_vec()); assert_eq!(decode_hex("AB"), [0xabu8].to_vec()); - assert_eq!(decode_hex("fa 19"), [0xfau8, 0x19].to_vec()); + assert_eq!(decode_hex("fa19"), [0xfau8, 0x19].to_vec()); } #[test] @@ -92,7 +92,7 @@ mod test { assert_eq!("00".to_string(), encode_hex(&[0x00])); assert_eq!("ab".to_string(), encode_hex(&[0xab])); assert_eq!( - "01 a2 1a fe".to_string(), + "01a21afe".to_string(), encode_hex(&[0x01, 0xa2, 0x1a, 0xfe]) ); } diff --git a/enclave/src/lib.rs b/enclave/src/lib.rs index 0c3aeec8dc..79a9a936b2 100755 --- a/enclave/src/lib.rs +++ b/enclave/src/lib.rs @@ -14,6 +14,10 @@ limitations under the License. */ +#![feature(structural_match)] +#![feature(rustc_attrs)] +#![feature(core_intrinsics)] +#![feature(derive_eq)] #![crate_name = "substratee_worker_enclave"] #![crate_type = "staticlib"] @@ -23,18 +27,15 @@ extern crate base64; extern crate bit_vec; -extern crate blake2_no_std; extern crate chrono; -extern crate crypto; extern crate env_logger; extern crate httparse; extern crate itertools; extern crate lazy_static; #[macro_use] extern crate log; -extern crate my_node_runtime; extern crate num_bigint; -extern crate parity_codec; +extern crate codec; extern crate primitive_types; extern crate primitives; extern crate runtime_primitives; @@ -57,17 +58,22 @@ extern crate sgx_tseal; extern crate sgx_tstd as std; extern crate sgx_tunittest; extern crate sgx_types; -extern crate sgxwasm; -extern crate wasmi; extern crate webpki; extern crate webpki_roots; extern crate yasna; +#[macro_use] +extern crate substrate_api_client; +extern crate substratee_stf; + +use substratee_stf::{Stf, TrustedCall, TrustedGetter, State}; +use substrate_api_client::{ + extrinsic::xt_primitives::{UncheckedExtrinsicV3, GenericAddress, GenericExtra, SignedPayload}, + crypto::AccountKey, + extrinsic}; -use crypto::ed25519::{keypair, signature}; -use my_node_runtime::{Call, Hash, SubstraTEERegistryCall, UncheckedExtrinsic}; -use parity_codec::{Compact, Decode, Encode}; +use codec::{Compact, Decode, Encode}; use primitive_types::U256; -use primitives::ed25519; +use primitives::{ed25519, Pair, hashing::{blake2_256}}; use runtime_primitives::generic::Era; use rust_base58::ToBase58; use sgx_crypto_helper::rsa3072::Rsa3072KeyPair; @@ -77,7 +83,7 @@ use sgx_tcrypto::rsgx_sha256_slice; use sgx_tunittest::*; use sgx_types::{sgx_sha256_hash_t, sgx_status_t, size_t}; -use constants::{ED25519_SEALED_KEY_FILE, ENCRYPTED_STATE_FILE, RSA3072_SEALED_KEY_FILE}; +use constants::{SEALED_SIGNER_SEED_FILE, ENCRYPTED_STATE_FILE, RSA3072_SEALED_KEY_FILE}; use std::collections::HashMap; use std::sgxfs::SgxFile; use std::slice; @@ -87,28 +93,56 @@ use std::vec::Vec; mod constants; mod utils; -mod wasm; mod attestation; pub mod cert; pub mod hex; pub mod tls_ra; + pub const CERTEXPIRYDAYS: i64 = 90i64; +pub type Hash = primitives::H256; + #[no_mangle] -pub unsafe extern "C" fn get_rsa_encryption_pubkey(pubkey: *mut u8, pubkey_size: u32) -> sgx_status_t { +pub unsafe extern "C" fn init() -> sgx_status_t { + // initialize the logging environment in the enclave + env_logger::init(); -// If this is called when already initialised, the enclave panics -// initialize the logging environment in the enclaved -// env_logger::init(); + match SgxFile::open(SEALED_SIGNER_SEED_FILE) { + Ok(_k) => (), + Err(x) => { + info!("[Enclave] Keyfile not found, creating new! {}", x); + if let Err(status) = utils::create_sealed_ed25519_seed() { + return status; + } + }, + } + let seedvec = match utils::get_ecc_seed() { + Ok(seed) => seed, + Err(status) => return status, + }; + let mut seed = [0u8; 32]; + let seedvec = &seedvec[..seed.len()]; // panics if not enough data + //FIXME remove this leak! + info!("[Enclave initialized] Ed25519 seed : 0x{}", hex::encode_hex(&seedvec)); + seed.copy_from_slice(seedvec); + let signer_prim = ed25519::Pair::from_seed(&seed); + info!("[Enclave initialized] Ed25519 prim raw : {:?}", signer_prim.public().0); + + //create RSA keypair if not existing if let Err(x) = SgxFile::open(RSA3072_SEALED_KEY_FILE) { info!("[Enclave] Keyfile not found, creating new! {}", x); if let Err(status) = create_sealed_rsa3072_keypair() { return status } } + sgx_status_t::SGX_SUCCESS +} + +#[no_mangle] +pub unsafe extern "C" fn get_rsa_encryption_pubkey(pubkey: *mut u8, pubkey_size: u32) -> sgx_status_t { let rsa_pubkey = match utils::read_rsa_pubkey() { Ok(key) => key, @@ -141,13 +175,12 @@ fn create_sealed_rsa3072_keypair() -> Result { utils::write_file(rsa_key_json.as_bytes(), RSA3072_SEALED_KEY_FILE) } + + #[no_mangle] pub unsafe extern "C" fn get_ecc_signing_pubkey(pubkey: * mut u8, pubkey_size: u32) -> sgx_status_t { - // initialize the logging environment in the enclave - env_logger::init(); - - match SgxFile::open(ED25519_SEALED_KEY_FILE) { + match SgxFile::open(SEALED_SIGNER_SEED_FILE) { Ok(_k) => (), Err(x) => { info!("[Enclave] Keyfile not found, creating new! {}", x); @@ -157,38 +190,37 @@ pub unsafe extern "C" fn get_ecc_signing_pubkey(pubkey: * mut u8, pubkey_size: u }, } - let _seed = match utils::get_ecc_seed() { + let seedvec = match utils::get_ecc_seed() { Ok(seed) => seed, Err(status) => return status, }; + let mut seed = [0u8; 32]; + let seedvec = &seedvec[..seed.len()]; // panics if not enough data + seed.copy_from_slice(seedvec); + let signer = AccountKey::Ed(ed25519::Pair::from_seed(&seed)); - let (_privkey, _pubkey) = keypair(&_seed); - info!("[Enclave] Restored ECC pubkey: {:?}", _pubkey.to_base58()); + info!("[Enclave] Restored ECC pubkey: {:?}", signer.public()); let pubkey_slice = slice::from_raw_parts_mut(pubkey, pubkey_size as usize); - pubkey_slice.clone_from_slice(&_pubkey); + pubkey_slice.clone_from_slice(&signer.public()); sgx_status_t::SGX_SUCCESS } #[no_mangle] -pub unsafe extern "C" fn call_counter_wasm( - req_bin: *const u8, - req_length: usize, - ciphertext: *mut u8, - ciphertext_size: u32, - hash: *const u8, - hash_size: u32, +pub unsafe extern "C" fn execute_stf( + request_encrypted: *mut u8, + request_encrypted_size: u32, + genesis_hash: *const u8, + genesis_hash_size: u32, nonce: *const u8, nonce_size: u32, - wasm_hash: *const u8, - wasm_hash_size: u32, unchecked_extrinsic: *mut u8, unchecked_extrinsic_size: u32 ) -> sgx_status_t { - let ciphertext_slice = slice::from_raw_parts(ciphertext, ciphertext_size as usize); - let hash_slice = slice::from_raw_parts(hash, hash_size as usize); + let request_encrypted_slice = slice::from_raw_parts(request_encrypted, request_encrypted_size as usize); + let genesis_hash_slice = slice::from_raw_parts(genesis_hash, genesis_hash_size as usize); let mut nonce_slice = slice::from_raw_parts(nonce, nonce_size as usize); let extrinsic_slice = slice::from_raw_parts_mut(unchecked_extrinsic, unchecked_extrinsic_size as usize); @@ -200,77 +232,75 @@ pub unsafe extern "C" fn call_counter_wasm( debug!("[Enclave] Read RSA keypair done"); - // decode the payload - println!(" [Enclave] Decode the payload"); - let plaintext_vec = utils::decode_payload(&ciphertext_slice, &rsa_keypair); - let plaintext_string = String::from_utf8(plaintext_vec.clone()).unwrap(); - let msg: Message = serde_json::from_str(&plaintext_string).unwrap(); - - println!(" [Enclave] Message decoded:"); - println!(" [Enclave] account = {}", msg.account); - println!(" [Enclave] increment = {}", msg.amount); - println!(" [Enclave] sha256 = {:?}", msg.sha256); + // decrypt the payload + debug!(" [Enclave] Decode the payload"); + let request_vec = utils::decrypt_payload(&request_encrypted_slice, &rsa_keypair); + let stf_call = TrustedCall::decode(&mut request_vec.as_slice()).unwrap(); - // get the calculated SHA256 hash - let wasm_hash_slice = slice::from_raw_parts(wasm_hash, wasm_hash_size as usize); - let wasm_hash_calculated: sgx_sha256_hash_t = serde_json::from_slice(wasm_hash_slice).unwrap(); - - if let Err(status) = wasm::compare_hashes(wasm_hash_calculated, msg.sha256) { - return status; - } - - let state = match utils::read_state_from_file(ENCRYPTED_STATE_FILE) { + // load last state + let state_enc = match utils::read_state_from_file(ENCRYPTED_STATE_FILE) { Ok(state) => state, Err(status) => return status, }; - let mut counter: AllCounts = match state.len() { - 0 => AllCounts { entries: HashMap::new() }, + let mut state : State = match state_enc.len() { + 0 => Stf::init_state(), _ => { debug!(" [Enclave] State read, deserializing..."); - let helper = DeSerializeHelper::::new(state); + let helper = DeSerializeHelper::::new(state_enc); helper.decode().unwrap() } }; - // get the current counter value of the account or initialize with 0 - let counter_value_old: u32 = *counter.entries.entry(msg.account.to_string()).or_insert(0); - info!(" [Enclave] Current counter state of '{}' = {}", msg.account, counter_value_old); - - println!(" [Enclave] Executing WASM code"); - let req_slice = slice::from_raw_parts(req_bin, req_length); - let action_req: sgxwasm::SgxWasmAction = serde_json::from_slice(req_slice).unwrap(); - - if let Err(status) = wasm::invoke_wasm_action(action_req, msg, &mut counter) { - return status; - } + debug!(" [Enclave] executing STF..."); + Stf::execute(&mut state, stf_call); // write the counter state and return - let enc_state = match encrypt_counter_state(counter) { + let enc_state = match encrypt_state(state) { Ok(s) => s, Err(sgx_status) => return sgx_status, }; - println!(" [Enclave] Updated encrypted state: {:?}", enc_state.to_vec()); let state_hash = rsgx_sha256_slice(&enc_state).unwrap(); + debug!(" [Enclave] Updated encrypted state. hash=0x{}", hex::encode_hex(&state_hash)); + if let Err(status) = utils::write_plaintext(&enc_state, ENCRYPTED_STATE_FILE) { return status } // get information for composing the extrinsic - let _seed = match utils::get_ecc_seed() { + let seedvec = match utils::get_ecc_seed() { Ok(seed) => seed, Err(status) => return status, }; - let nonce = U256::decode(&mut nonce_slice).unwrap(); - let genesis_hash = utils::hash_from_slice(hash_slice); - let call_hash = utils::blake2s(&plaintext_vec); - debug!("[Enclave]: Call hash {:?}", call_hash); + let mut seed = [0u8; 32]; + let seedvec = &seedvec[..seed.len()]; // panics if not enough data + seed.copy_from_slice(seedvec); + let signer = AccountKey::Ed(ed25519::Pair::from_seed(&seed)); + debug!("Restored ECC pubkey: {:?}", signer.public()); + + let nonce = u32::decode(&mut nonce_slice).unwrap(); + debug!("using nonce for confirmation extrinsic: {:?}", nonce); + let genesis_hash = utils::hash_from_slice(genesis_hash_slice); + let call_hash = blake2_256(&request_vec); + debug!("[Enclave]: Call hash 0x{}", hex::encode_hex(&call_hash)); + + let xt_call = [7u8,3u8]; + + //FIXME: define constant at client + let spec_version = 4; + + let xt = compose_extrinsic_offline!( + signer, + (xt_call, call_hash.to_vec(), state_hash.to_vec()), + nonce, + genesis_hash, + spec_version + ); - let ex = confirm_call_extrinsic(_seed, &call_hash, &state_hash, nonce, genesis_hash); - let encoded = ex.encode(); + let encoded = xt.encode(); // split the extrinsic_slice at the length of the encoded extrinsic // and fill the right side with whitespace @@ -281,83 +311,56 @@ pub unsafe extern "C" fn call_counter_wasm( sgx_status_t::SGX_SUCCESS } -#[no_mangle] -pub unsafe extern "C" fn get_counter(account: *const u8, account_size: u32, value: *mut u32) -> sgx_status_t { - let account_slice = slice::from_raw_parts(account, account_size as usize); - let acc_str = std::str::from_utf8(account_slice).unwrap(); - let state = match utils::read_state_from_file(ENCRYPTED_STATE_FILE) { +#[no_mangle] +pub unsafe extern "C" fn get_state( + getter: *const u8, + getter_size: u32, + value: *mut u8, + value_size: u32 + ) -> sgx_status_t { + + let mut getter_slice = slice::from_raw_parts(getter, getter_size as usize); + let value_slice = slice::from_raw_parts_mut(value, value_size as usize); + + // load last state + let state_enc = match utils::read_state_from_file(ENCRYPTED_STATE_FILE) { Ok(state) => state, Err(status) => return status, }; - let mut counter: AllCounts = match state.len() { - 0 => AllCounts { entries: HashMap::new() }, + let mut state : State = match state_enc.len() { + 0 => Stf::init_state(), _ => { debug!(" [Enclave] State read, deserializing..."); - let helper = DeSerializeHelper::::new(state); + let helper = DeSerializeHelper::::new(state_enc); helper.decode().unwrap() } }; + let _getter = TrustedGetter::decode(&mut getter_slice).unwrap(); + let value_vec = match Stf::get_state(&mut state, _getter) { + Some(val) => val, + None => vec!(0), + }; - let ref_mut = &mut *value; - *ref_mut = *counter.entries.entry(acc_str.to_string()).or_insert(0); + // split the extrinsic_slice at the length of the encoded extrinsic + // and fill the right side with whitespace + let (left, right) = value_slice.split_at_mut(value_vec.len()); + left.clone_from_slice(&value_vec); + //FIXME: now implicitly assuming we pass unsigned integer vecs, not strings terminated by 0x20 + //FIXME: we should really pass an Option> + right.iter_mut().for_each(|x| *x = 0x00); + //right.iter_mut().for_each(|x| *x = 0x20); sgx_status_t::SGX_SUCCESS } -fn encrypt_counter_state(value: AllCounts) -> Result, sgx_status_t> { +fn encrypt_state(value: State) -> Result, sgx_status_t> { let helper = SerializeHelper::new(); let mut c = helper.encode(value).unwrap(); utils::aes_de_or_encrypt(&mut c)?; Ok(c) } -#[derive(Serializable, DeSerializable, Debug)] -pub struct AllCounts { - entries: HashMap -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Message { - account: String, - amount: u32, - sha256: sgx_sha256_hash_t -} - -pub fn confirm_call_extrinsic(seed: Vec, call_hash: &[u8], state_hash: &[u8], nonce: U256, genesis_hash: Hash) -> UncheckedExtrinsic { - let function = Call::SubstraTEERegistry(SubstraTEERegistryCall::confirm_call(call_hash.to_vec(), state_hash.to_vec())); - compose_extrinsic(seed, function, nonce, genesis_hash) -} - -pub fn compose_extrinsic(seed: Vec, function: Call, nonce: U256, genesis_hash: Hash) -> UncheckedExtrinsic { - let (_privkey, _pubkey) = keypair(&seed); - - let era = Era::immortal(); - - let index = nonce.low_u64(); - let raw_payload = (Compact(index), function, era, genesis_hash); - - let sign = raw_payload.using_encoded(|payload| if payload.len() > 256 { - // should not be thrown as we calculate a 32 byte hash ourselves - error!("unsupported payload size"); - signature(&[0u8; 64], &_privkey) - } else { - //println!("signing {}", HexDisplay::from(&payload)); - signature(payload, &_privkey) - }); - - let signerpub = ed25519::Public::from_raw(_pubkey); - let signature = ed25519::Signature::from_raw(sign); - - UncheckedExtrinsic::new_signed( - index, - raw_payload.1, - signerpub.into(), - signature, - era, - ) -} - extern "C" { pub fn ocall_read_ipfs( ret_val : *mut sgx_status_t, diff --git a/enclave/src/utils.rs b/enclave/src/utils.rs index 521a0b6368..dc8a4bae9c 100755 --- a/enclave/src/utils.rs +++ b/enclave/src/utils.rs @@ -18,16 +18,16 @@ extern crate aes; extern crate ofb; extern crate sgx_types; -use blake2_no_std::blake2b::blake2b; -use crypto::blake2s::Blake2s; +//use blake2_no_std::blake2b::blake2b; +//use crypto::blake2s::Blake2s; use log::*; -use my_node_runtime::Hash; +use crate::Hash; use sgx_crypto_helper::rsa3072::{Rsa3072KeyPair, Rsa3072PubKey}; use sgx_crypto_helper::RsaKeyPair; use sgx_rand::{Rng, StdRng}; use sgx_types::*; -use constants::{AES_KEY_FILE_AND_INIT_V, ED25519_SEALED_KEY_FILE, RSA3072_SEALED_KEY_FILE}; +use constants::{AES_KEY_FILE_AND_INIT_V, SEALED_SIGNER_SEED_FILE, RSA3072_SEALED_KEY_FILE}; use std::fs::File; use std::io::{Read, Write}; use std::sgxfs::SgxFile; @@ -58,7 +58,7 @@ pub fn store_rsa_key_pair(pair: &[u8]) -> SgxResult { } pub fn get_ecc_seed() -> SgxResult> { - read_file(ED25519_SEALED_KEY_FILE) + read_file(SEALED_SIGNER_SEED_FILE) } pub fn create_sealed_ed25519_seed() -> SgxResult { @@ -69,7 +69,7 @@ pub fn create_sealed_ed25519_seed() -> SgxResult { }; rand.fill_bytes(&mut seed); - write_file(&seed, ED25519_SEALED_KEY_FILE) + write_file(&seed, SEALED_SIGNER_SEED_FILE) } pub fn read_or_create_aes_key_iv() -> SgxResult<(Vec, Vec)> { @@ -180,7 +180,7 @@ pub fn read_plaintext(filepath: &str) -> SgxResult> { match File::open(filepath) { Ok(mut f) => match f.read_to_end(&mut state_vec) { Ok(len) => { - info!("[Enclave] Read {} bytes from counter file", len); + info!("[Enclave] Read {} bytes from state file", len); Ok(state_vec) } Err(x) => { @@ -214,7 +214,7 @@ pub fn write_plaintext(bytes: &[u8], filepath: &str) -> SgxResult } } -pub fn decode_payload(ciphertext_slice: &[u8], rsa_pair: &Rsa3072KeyPair) -> Vec { +pub fn decrypt_payload(ciphertext_slice: &[u8], rsa_pair: &Rsa3072KeyPair) -> Vec { let mut decrypted_buffer = Vec::new(); rsa_pair.decrypt_buffer(ciphertext_slice, &mut decrypted_buffer).unwrap(); decrypted_buffer @@ -226,6 +226,7 @@ pub fn hash_from_slice(hash_slize: &[u8]) -> Hash { Hash::from(&mut g) } +/* pub fn blake2s(plaintext: &[u8]) -> [u8; 32] { let mut call_hash: [u8; 32] = Default::default(); Blake2s::blake2s(&mut call_hash, &plaintext[..], &[0; 32]); @@ -244,7 +245,7 @@ pub fn blake2_256(data: &[u8]) -> [u8; 32] { blake2_256_into(data, &mut r); r } - +*/ pub fn test_encrypted_state_io_works() { let path = "test_state_file.bin"; let plaintext = b"The quick brown fox jumps over the lazy dog."; diff --git a/enclave/wasm/Cargo.toml b/enclave/wasm/Cargo.toml deleted file mode 100644 index 35412f1dc2..0000000000 --- a/enclave/wasm/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "substratee_worker_enclave_wasm" -version = "0.1.0" -authors = ["Supercomputing Systems AG "] -edition = "2018" - -[lib] -crate-type =["cdylib"] - -[dependencies] - diff --git a/enclave/wasm/build.sh b/enclave/wasm/build.sh deleted file mode 100755 index 1ab5b5ea38..0000000000 --- a/enclave/wasm/build.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -if [[ "${SGX_DEBUG}" == 0 ]]; then - BUILD_TARGET="--release" - OUTPUT_PATH="release" -else - BUILD_TARGET="" - OUTPUT_PATH="debug" -fi - -cargo build --target wasm32-unknown-unknown ${BUILD_TARGET} - -wasm-gc ../target/wasm32-unknown-unknown/${OUTPUT_PATH}/substratee_worker_enclave_wasm.wasm ../target/wasm32-unknown-unknown/${OUTPUT_PATH}/worker_enclave.compact.wasm -cp ../target/wasm32-unknown-unknown/${OUTPUT_PATH}/worker_enclave.compact.wasm ../../bin diff --git a/enclave/wasm/src/lib.rs b/enclave/wasm/src/lib.rs deleted file mode 100644 index c05e340a64..0000000000 --- a/enclave/wasm/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[no_mangle] -pub extern fn update_counter(x: u32, y: u32) -> u32 { - x + y + 10 -} diff --git a/rust-sgx-sdk/version b/rust-sgx-sdk/version index fd64d05973..58b603a9b8 100644 --- a/rust-sgx-sdk/version +++ b/rust-sgx-sdk/version @@ -1 +1 @@ -de01b631af9dd7561cb52d7810311308bda43efc +5dd83887fc2be6e3cf27094b5eff50ebb674a1ef diff --git a/stf/Cargo.toml b/stf/Cargo.toml new file mode 100644 index 0000000000..619f19eb3b --- /dev/null +++ b/stf/Cargo.toml @@ -0,0 +1,89 @@ +[package] +name = "substratee-stf" +version = "0.1.0" +authors = ["Supercomputing Systems AG "] +edition = "2018" + +[features] +default = [] +sgx = [ + "sgx_tstd", + "env_logger", + "log", + "sr-io/sgx", + ] + +[dependencies] +log = { version = "0.4.7", git = "https://github.com/mesalock-linux/log-sgx", optional=true } +env_logger = { version = "0.6.2", git = "https://github.com/mesalock-linux/env_logger-sgx", optional=true } + +[dependencies.sgx_tstd] +rev = "v1.0.9" +git = "https://github.com/baidu/rust-sgx-sdk" +features = ["untrusted_fs","net","backtrace"] +optional = true + +[dependencies.codec] +package = "parity-scale-codec" +version = "1.0.0" +default-features = false +features = ["derive"] + +[dependencies.primitives] +git = "https://github.com/scs/substrate-api-client" +package = "substrate-primitives" +branch = "no_std_rstd" +default-features = false + +[dependencies.runtime_primitives] +git = 'https://github.com/paritytech/substrate' +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" +package = "sr-primitives" +default-features = false + +[dependencies.balances] +default-features = false +git = "https://github.com/paritytech/substrate" +package = "srml-balances" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" + +[dependencies.timestamp] +default-features = false +git = "https://github.com/paritytech/substrate" +package = "srml-timestamp" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" + +[dependencies.system] +default-features = false +git = "https://github.com/paritytech/substrate" +package = "srml-system" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" + +[dependencies.srml-support] +default-features = false +git = "https://github.com/paritytech/substrate" +package = "srml-support" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" + +[dependencies.indices] +default-features = false +git = "https://github.com/paritytech/substrate" +package = "srml-indices" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" + +[dependencies.version] +default-features = false +git = "https://github.com/paritytech/substrate" +package = "sr-version" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" + +[dependencies.sr-io] +git = "https://github.com/scs/substraTEE-node" +package = 'sr-io' +branch = "brenzi-private-tx" +default-features=false +features = ["no_oom", "no_panic_handler", "debug"] + + + + diff --git a/stf/src/lib.rs b/stf/src/lib.rs new file mode 100644 index 0000000000..764b2a23da --- /dev/null +++ b/stf/src/lib.rs @@ -0,0 +1,200 @@ +/* + Copyright 2019 Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +///////////////////////////////////////////////////////////////////////////// +#![feature(structural_match)] +#![feature(rustc_attrs)] +#![feature(core_intrinsics)] +#![feature(derive_eq)] + +#![cfg_attr(not(target_env = "sgx"), no_std)] +#![cfg_attr(target_env = "sgx", feature(rustc_private))] + +extern crate balances; +extern crate srml_support; +extern crate version; +extern crate sr_io; +extern crate alloc; + +#[cfg(feature = "sgx")] +#[macro_use] +extern crate log; +#[cfg(feature = "sgx")] +extern crate env_logger; + +#[cfg(feature = "sgx")] +mod runtime_wrapper; + +pub mod tests; + +#[cfg(feature = "sgx")] +#[macro_use] +extern crate sgx_tstd as std; + +#[cfg(feature = "sgx")] +use std::prelude::v1::*; + +#[cfg(feature = "sgx")] +use runtime_wrapper::Runtime; + +#[cfg(feature = "sgx")] +use sr_io::SgxExternalities; + +#[cfg(feature = "sgx")] +use std::backtrace::{self, PrintFormat}; +#[cfg(feature = "sgx")] +use std::panic; + +use codec::{Compact, Decode, Encode}; +use primitives::hashing::{blake2_256, twox_128}; + +use runtime_primitives::traits::Dispatchable; +use runtime_primitives::{AnySignature, traits::Verify}; +pub type Signature = AnySignature; +pub type AuthorityId = ::Signer; +pub type AccountId = ::Signer; +pub type Hash = primitives::H256; +pub type Balance = u128; + +#[cfg(feature = "sgx")] +pub type State = SgxExternalities; + +#[derive(Encode, Decode)] +pub enum TrustedCall { + balance_set_balance(AccountId, Balance, Balance), + balance_transfer(AccountId, AccountId, Balance), +} + +#[derive(Encode, Decode)] +pub enum TrustedGetter { + free_balance(AccountId), + reserved_balance(AccountId), +} + +#[cfg(feature = "sgx")] +pub struct Stf { +} + +#[cfg(feature = "sgx")] +impl Stf { + pub fn init_state() -> State { + debug!("initializing stf state"); + let mut ext = State::new(); + sr_io::with_externalities(&mut ext, || { + // write Genesis + info!("Prepare some Genesis values"); + sr_io::set_storage(&storage_key_bytes("Balances", "TotalIssuance", None), &11u128.encode()); + sr_io::set_storage(&storage_key_bytes("Balances", "CreationFee", None), &1u128.encode()); + sr_io::set_storage(&storage_key_bytes("Balances", "TransferFee", None), &1u128.encode()); + sr_io::set_storage(&storage_key_bytes("Balances", "TransactionBaseFee", None), &1u128.encode()); + sr_io::set_storage(&storage_key_bytes("Balances", "TransfactionByteFee", None), &1u128.encode()); + sr_io::set_storage(&storage_key_bytes("Balances", "ExistentialDeposit", None), &1u128.encode()); + }); + ext + } + pub fn execute(mut ext: &mut State, call: TrustedCall) { + sr_io::with_externalities(&mut ext, || { + let result = match call { + TrustedCall::balance_set_balance(who, free_balance, reserved_balance ) => + runtime_wrapper::balancesCall::::set_balance(indices::Address::::Id(who.clone()), free_balance, reserved_balance).dispatch(runtime_wrapper::Origin::ROOT), + TrustedCall::balance_transfer(from, to, value) => { + //FIXME: here would be a good place to really verify a signature + let origin = runtime_wrapper::Origin::signed(from.clone()); + runtime_wrapper::balancesCall::::transfer(indices::Address::::Id(to.clone()), value).dispatch(origin) + }, + _ => { + Err("Call not recognized")} + }; + }); + } + + pub fn get_state(mut ext: &mut State, getter: TrustedGetter) -> Option> { + //FIXME: only account owner may get its own data. introduce signature verification! + sr_io::with_externalities(&mut ext, || { + let result = match getter { + TrustedGetter::free_balance(who) => + sr_io::storage(&storage_key_bytes("Balances", "FreeBalance", Some(who.encode()))), + TrustedGetter::reserved_balance(who) => + sr_io::storage(&storage_key_bytes("Balances", "ReservedBalance", Some(who.encode()))), + _ => None + }; + debug!("get_state result: {:?}", result); + result + }) + + } +} + +#[cfg(feature = "sgx")] +pub fn storage_key_bytes(module: &str, storage_key_name: &str, param: Option>) -> Vec { + use primitives::twox_128; + let mut key = [module, storage_key_name].join(" ").as_bytes().to_vec(); + let mut keyhash; + debug!("storage_key_hash for: module: {} key: {} (and params?) ", module, storage_key_name); + match param { + Some(par) => { + key.append(&mut par.clone()); + keyhash = blake2_256(&key).to_vec(); + }, + _ => { + keyhash = twox_128(&key).to_vec(); + }, + } + //debug!(" is 0x{}", hex::encode_hex(&keyhash)); + keyhash +} + +/* +pub fn init_runtime() { + info!("[??] asking runtime out"); + + let mut ext = SgxExternalities::new(); + + let tina = AccountId::default(); + let origin_tina = runtime_wrapper::Origin::signed(tina.clone()); + //let origin = runtime_wrapper::Origin::ROOT; + + let address = indices::Address::::default(); + + sr_io::with_externalities(&mut ext, || { + // write Genesis + info!("Prepare some Genesis values"); + sr_io::set_storage(&storage_key_bytes("Balances", "TotalIssuance", None), &11u128.encode()); + sr_io::set_storage(&storage_key_bytes("Balances", "CreationFee", None), &1u128.encode()); + sr_io::set_storage(&storage_key_bytes("Balances", "TransferFee", None), &1u128.encode()); + sr_io::set_storage(&storage_key_bytes("Balances", "TransactionBaseFee", None), &1u128.encode()); + sr_io::set_storage(&storage_key_bytes("Balances", "TransfactionByteFee", None), &1u128.encode()); + sr_io::set_storage(&storage_key_bytes("Balances", "ExistentialDeposit", None), &1u128.encode()); + // prefund Tina + sr_io::set_storage(&storage_key_bytes("Balances", "FreeBalance", Some(tina.clone().encode())), & 13u128.encode()); + + // read storage + let _creation_fee = sr_io::storage(&storage_key_bytes("Balances", "ExistentialDeposit", None)); + debug!("reading genesis storage ExistentialDeposit = {:?}", _creation_fee); + + const MILLICENTS: u128 = 1_000_000_000; + const CENTS: u128 = 1_000 * MILLICENTS; // assume this is worth about a cent. + + info!("re-funding tina: call set_balance"); + let res = runtime_wrapper::balancesCall::::set_balance(indices::Address::::Id(tina.clone()), 42, 43).dispatch(runtime_wrapper::Origin::ROOT); + info!("reading Tina's FreeBalance"); + let tina_balance = sr_io::storage(&storage_key_bytes("Balances", "FreeBalance", Some(tina.clone().encode()))); + info!("Tina's FreeBalance is {:?}", tina_balance); + }); + info!("[++] finished playing with runtime"); +} +*/ \ No newline at end of file diff --git a/stf/src/runtime_wrapper.rs b/stf/src/runtime_wrapper.rs new file mode 100644 index 0000000000..95dc127855 --- /dev/null +++ b/stf/src/runtime_wrapper.rs @@ -0,0 +1,731 @@ +/* + Copyright 2019 Supercomputing Systems AG + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +//! This file has been semi-manually derived from node-template +//! +//! go to your node-template ./runtime and run +//! `cargo expand --no-default-features > node-runtime-expanded.rs` +//! +//! then extract all definitions for +//! Runtime, Log, InternalLog +//! and put them here +//! +//! you might have to repeat this procedure for runtime updates + +//use node_runtime::{Balances, AccountId, Indices, Hash, Nonce, opaque, Block, BlockNumber, AuthorityId, AuthoritySignature, Event, Call, Origin}; +use runtime_primitives::{ + generic, + traits::{BlakeTwo256, ConvertInto, Block as BlockT}, + Permill, Perbill, + weights::Weight +}; +use version::RuntimeVersion; + +pub use runtime_primitives::OpaqueExtrinsic as UncheckedExtrinsic; + +use crate::{Signature, AuthorityId, AccountId, Hash}; +pub type Call = [u8; 2]; +pub type Index = u32; +pub type BlockNumber = u32; +pub type Header = generic::Header; +pub type Block = generic::Block; + +use srml_support::traits::Currency; +use std::vec::Vec; + +/* +pub trait Trait: system::Trait { + /// The overarching event type. + //type Event: From> + Into<::Event>; +} +*/ + +pub type System = system::Module; +pub type Timestamp = timestamp::Module; +pub type Indices = indices::Module; +pub type Balances = balances::Module; + +pub use balances::Call as balancesCall; + + +#[structural_match] +#[rustc_copy_clone_marker] +pub struct Runtime; +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for Runtime { + #[inline] + fn clone(&self) -> Runtime { { *self } } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::marker::Copy for Runtime { } +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for Runtime { + #[inline] + fn eq(&self, other: &Runtime) -> bool { + match *other { Runtime => match *self { Runtime => true, }, } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for Runtime { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { { } } +} +impl ::srml_support::sr_primitives::traits::GetNodeBlockType for Runtime + { + type NodeBlock = Block; //dummy +} +impl ::srml_support::sr_primitives::traits::GetRuntimeBlockType for + Runtime { + type RuntimeBlock = Block; +} + +impl balances::Trait for Runtime { + /// The type for recording an account's balance. + type Balance = u128; + /// What to do if an account's free balance gets zeroed. + type OnFreeBalanceZero = (); + /// What to do if a new account is created. + type OnNewAccount = Indices; + /// The uniquitous event type. + type Event = Event; + + type TransactionPayment = (); + type DustRemoval = (); + type TransferPayment = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; + type CreationFee = CreationFee; + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; + type WeightToFee = ConvertInto; +} + + +impl system::Trait for Runtime { + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type Call = Call; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = Indices; + /// The index type for storing how many extrinsics an account has signed. + type Index = Index; + /// The index type for blocks. + type BlockNumber = BlockNumber; + /// The type for hashing blocks and tries + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = BlakeTwo256; + /// The header digest type. + //type Digest = generic::Digest; + /// The header type. + type Header = generic::Header; + /// The ubiquitous event type. + type Event = Event; + /// Update weight (to fee) multiplier per-block. + type WeightMultiplierUpdate = (); + /// The ubiquitous log type. + //type Log = Log; + /// The ubiquitous origin type. + type Origin = Origin; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// Maximum weight of each block. With a default weight system of 1byte == 1weight, 4mb is ok. + type MaximumBlockWeight = MaximumBlockWeight; + /// Maximum size of all encoded transactions (in bytes) that are allowed in one block. + type MaximumBlockLength = MaximumBlockLength; + /// Portion of the block weight that is available to all normal transactions. + type AvailableBlockRatio = AvailableBlockRatio; + type Version = Version; +} + +impl timestamp::Trait for Runtime { + /// A timestamp: seconds since the unix epoch. + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; +} + +impl indices::Trait for Runtime { + /// The type for recording indexing into the account enumeration. If this ever overflows, there + /// will be problems! + type AccountIndex = u32; + /// Use the standard means of resolving an index hint from an id. + type ResolveHint = indices::SimpleResolveHint; + /// Determine whether an account is dead. + type IsDeadAccount = Balances; + /// The uniquitous event type. + type Event = Event; +} + +#[allow(non_camel_case_types)] +#[structural_match] +pub enum Origin { + system(system::Origin), + #[allow(dead_code)] + Void(::srml_support::Void), +} +#[automatically_derived] +#[allow(unused_qualifications)] +#[allow(non_camel_case_types)] +impl ::core::clone::Clone for Origin { + #[inline] + fn clone(&self) -> Origin { + match (&*self,) { + (&Origin::system(ref __self_0),) => + Origin::system(::core::clone::Clone::clone(&(*__self_0))), + (&Origin::Void(ref __self_0),) => + Origin::Void(::core::clone::Clone::clone(&(*__self_0))), + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +#[allow(non_camel_case_types)] +impl ::core::cmp::PartialEq for Origin { + #[inline] + fn eq(&self, other: &Origin) -> bool { + { + let __self_vi = + unsafe { ::core::intrinsics::discriminant_value(&*self) } as + isize; + let __arg_1_vi = + unsafe { ::core::intrinsics::discriminant_value(&*other) } as + isize; + if true && __self_vi == __arg_1_vi { + match (&*self, &*other) { + (&Origin::system(ref __self_0), + &Origin::system(ref __arg_1_0)) => + (*__self_0) == (*__arg_1_0), + (&Origin::Void(ref __self_0), + &Origin::Void(ref __arg_1_0)) => + (*__self_0) == (*__arg_1_0), + _ => unsafe { ::core::intrinsics::unreachable() } + } + } else { false } + } + } + #[inline] + fn ne(&self, other: &Origin) -> bool { + { + let __self_vi = + unsafe { ::core::intrinsics::discriminant_value(&*self) } as + isize; + let __arg_1_vi = + unsafe { ::core::intrinsics::discriminant_value(&*other) } as + isize; + if true && __self_vi == __arg_1_vi { + match (&*self, &*other) { + (&Origin::system(ref __self_0), + &Origin::system(ref __arg_1_0)) => + (*__self_0) != (*__arg_1_0), + (&Origin::Void(ref __self_0), + &Origin::Void(ref __arg_1_0)) => + (*__self_0) != (*__arg_1_0), + _ => unsafe { ::core::intrinsics::unreachable() } + } + } else { true } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +#[allow(non_camel_case_types)] +impl ::core::cmp::Eq for Origin { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq>; + let _: ::core::cmp::AssertParamIsEq<::srml_support::Void>; + } + } +} +#[allow(dead_code)] +impl Origin { + pub const NONE: Self = Origin::system(system::RawOrigin::None); + pub const ROOT: Self = Origin::system(system::RawOrigin::Root); + pub fn signed(by: ::AccountId) -> Self { + Origin::system(system::RawOrigin::Signed(by)) + } +} +impl From> for Origin { + fn from(x: system::Origin) -> Self { Origin::system(x) } +} +impl Into<::srml_support::rstd::result::Result, + Origin>> for Origin { + fn into(self) + -> ::srml_support::rstd::result::Result, Self> { + if let Origin::system(l) = self { Ok(l) } else { Err(self) } + } +} +impl From::AccountId>> for Origin { + fn from(x: Option<::AccountId>) -> Self { + >::from(x).into() + } +} + +pub struct ExistentialDeposit; +impl ExistentialDeposit { + pub fn get() -> u128 { 500 } +} +impl > ::srml_support::traits::Get for ExistentialDeposit { + fn get() -> I { I::from(500) } +} +pub struct TransferFee; +impl TransferFee { + pub fn get() -> u128 { 0 } +} +impl > ::srml_support::traits::Get for TransferFee { + fn get() -> I { I::from(0) } +} +pub struct CreationFee; +impl CreationFee { + pub fn get() -> u128 { 0 } +} +impl > ::srml_support::traits::Get for CreationFee { + fn get() -> I { I::from(0) } +} +pub struct TransactionBaseFee; +impl TransactionBaseFee { + pub fn get() -> u128 { 0 } +} +impl > ::srml_support::traits::Get for TransactionBaseFee { + fn get() -> I { I::from(0) } +} +pub struct TransactionByteFee; +impl TransactionByteFee { + pub fn get() -> u128 { 1 } +} +impl > ::srml_support::traits::Get for TransactionByteFee { + fn get() -> I { I::from(1) } +} + +pub const MILLISECS_PER_BLOCK: u64 = 6000; +pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; +pub const EPOCH_DURATION_IN_BLOCKS: u32 = 10 * MINUTES; +pub const MINUTES: BlockNumber = + 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; +pub const DAYS: BlockNumber = HOURS * 24; +pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4); +pub struct BlockHashCount; +impl BlockHashCount { + pub fn get() -> BlockNumber { 250 } +} +impl > ::srml_support::traits::Get for BlockHashCount + { + fn get() -> I { I::from(250) } +} +pub struct MaximumBlockWeight; +impl MaximumBlockWeight { + pub fn get() -> Weight { 1_000_000 } +} +impl > ::srml_support::traits::Get for MaximumBlockWeight { + fn get() -> I { I::from(1_000_000) } +} +pub struct AvailableBlockRatio; +impl AvailableBlockRatio { + pub fn get() -> Perbill { Perbill::from_percent(75) } +} +impl > ::srml_support::traits::Get for AvailableBlockRatio + { + fn get() -> I { I::from(Perbill::from_percent(75)) } +} +pub struct MaximumBlockLength; +impl MaximumBlockLength { + pub fn get() -> u32 { 5 * 1024 * 1024 } +} +impl > ::srml_support::traits::Get for MaximumBlockLength { + fn get() -> I { I::from(5 * 1024 * 1024) } +} + +pub struct Version; +impl Version { + pub fn get() -> RuntimeVersion { VERSION } +} +impl > ::srml_support::traits::Get for Version { + fn get() -> I { I::from(VERSION) } +} + +pub struct MinimumPeriod; +impl MinimumPeriod { + pub fn get() -> u64 { 5000 } +} +impl > ::srml_support::traits::Get for MinimumPeriod { + fn get() -> I { I::from(5000) } +} + +// dummy runtime API versions +type ApiId = [u8; 8]; +type ApisVec = &'static [(ApiId, u32)]; +const RUNTIME_API_VERSIONS: ApisVec = &[([0u8;8],0u32)]; +/// This runtime version. +pub const VERSION: RuntimeVersion = + RuntimeVersion{spec_name: { "sgx-trusted" }, + impl_name: { "sgx-trusted" }, + authoring_version: 3, + spec_version: 1, + impl_version: 1, + apis: RUNTIME_API_VERSIONS,}; + + +#[allow(non_camel_case_types)] +#[structural_match] +pub enum Event { + system(system::Event), + indices(indices::Event), + balances(balances::Event), +} +#[automatically_derived] +#[allow(unused_qualifications)] +#[allow(non_camel_case_types)] +impl ::core::clone::Clone for Event { + #[inline] + fn clone(&self) -> Event { + match (&*self,) { + (&Event::system(ref __self_0),) => + Event::system(::core::clone::Clone::clone(&(*__self_0))), + (&Event::indices(ref __self_0),) => + Event::indices(::core::clone::Clone::clone(&(*__self_0))), + (&Event::balances(ref __self_0),) => + Event::balances(::core::clone::Clone::clone(&(*__self_0))), + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +#[allow(non_camel_case_types)] +impl ::core::cmp::PartialEq for Event { + #[inline] + fn eq(&self, other: &Event) -> bool { + { + let __self_vi = + unsafe { ::core::intrinsics::discriminant_value(&*self) } as + isize; + let __arg_1_vi = + unsafe { ::core::intrinsics::discriminant_value(&*other) } as + isize; + if true && __self_vi == __arg_1_vi { + match (&*self, &*other) { + (&Event::system(ref __self_0), + &Event::system(ref __arg_1_0)) => + (*__self_0) == (*__arg_1_0), + (&Event::indices(ref __self_0), + &Event::indices(ref __arg_1_0)) => + (*__self_0) == (*__arg_1_0), + (&Event::balances(ref __self_0), + &Event::balances(ref __arg_1_0)) => + (*__self_0) == (*__arg_1_0), + _ => unsafe { ::core::intrinsics::unreachable() } + } + } else { false } + } + } + #[inline] + fn ne(&self, other: &Event) -> bool { + { + let __self_vi = + unsafe { ::core::intrinsics::discriminant_value(&*self) } as + isize; + let __arg_1_vi = + unsafe { ::core::intrinsics::discriminant_value(&*other) } as + isize; + if true && __self_vi == __arg_1_vi { + match (&*self, &*other) { + (&Event::system(ref __self_0), + &Event::system(ref __arg_1_0)) => + (*__self_0) != (*__arg_1_0), + (&Event::indices(ref __self_0), + &Event::indices(ref __arg_1_0)) => + (*__self_0) != (*__arg_1_0), + (&Event::balances(ref __self_0), + &Event::balances(ref __arg_1_0)) => + (*__self_0) != (*__arg_1_0), + _ => unsafe { ::core::intrinsics::unreachable() } + } + } else { true } + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +#[allow(non_camel_case_types)] +impl ::core::cmp::Eq for Event { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq>; + let _: ::core::cmp::AssertParamIsEq>; + } + } +} +#[allow(non_upper_case_globals , unused_attributes , unused_qualifications)] +const _IMPL_ENCODE_FOR_Event: () = + { + #[allow(unknown_lints)] + #[allow(rust_2018_idioms)] + extern crate codec as _codec; + impl _codec::Encode for Event { + fn encode_to(&self, + dest: + &mut EncOut) { + match *self { + Event::system(ref aa) => { + dest.push_byte(0usize as u8); + dest.push(aa); + } + Event::indices(ref aa) => { + dest.push_byte(1usize as u8); + dest.push(aa); + } + Event::balances(ref aa) => { + dest.push_byte(2usize as u8); + dest.push(aa); + } + _ => (), + } + } + } + impl _codec::EncodeLike for Event { } + }; +#[allow(non_upper_case_globals , unused_attributes , unused_qualifications)] +const _IMPL_DECODE_FOR_Event: () = + { + #[allow(unknown_lints)] + #[allow(rust_2018_idioms)] + extern crate codec as _codec; + impl _codec::Decode for Event { + fn decode(input: &mut DecIn) + -> core::result::Result { + match input.read_byte()? { + x if x == 0usize as u8 => { + Ok(Event::system({ + let res = + _codec::Decode::decode(input); + match res { + Err(_) => + return Err("Error decoding field Event :: system.0".into()), + Ok(a) => a, + } + })) + } + x if x == 1usize as u8 => { + Ok(Event::indices({ + let res = + _codec::Decode::decode(input); + match res { + Err(_) => + return Err("Error decoding field Event :: indices.0".into()), + Ok(a) => a, + } + })) + } + x if x == 2usize as u8 => { + Ok(Event::balances({ + let res = + _codec::Decode::decode(input); + match res { + Err(_) => + return Err("Error decoding field Event :: balances.0".into()), + Ok(a) => a, + } + })) + } + x => Err("No such variant in enum Event".into()), + } + } + } + }; +impl From for Event { + fn from(x: system::Event) -> Self { Event::system(x) } +} +impl From> for Event { + fn from(x: indices::Event) -> Self { Event::indices(x) } +} +impl ::srml_support::rstd::convert::TryInto> for Event + { + type + Error + = + (); + fn try_into(self) + -> + ::srml_support::rstd::result::Result, + Self::Error> { + match self { Self::indices(evt) => Ok(evt), _ => Err(()), } + } +} +impl From> for Event { + fn from(x: balances::Event) -> Self { Event::balances(x) } +} +impl ::srml_support::rstd::convert::TryInto> for + Event { + type + Error + = + (); + fn try_into(self) + -> + ::srml_support::rstd::result::Result, + Self::Error> { + match self { Self::balances(evt) => Ok(evt), _ => Err(()), } + } +} + + + + +/* +pub type Event = RawEvent<::AccountId>; +/// Events for this module. +/// +#[structural_match] +pub enum RawEvent { SomethingStored(u32, AccountId), } +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::clone::Clone for + RawEvent { + #[inline] + fn clone(&self) -> RawEvent { + match (&*self,) { + (&RawEvent::SomethingStored(ref __self_0, ref __self_1),) => + RawEvent::SomethingStored(::core::clone::Clone::clone(&(*__self_0)), + ::core::clone::Clone::clone(&(*__self_1))), + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::PartialEq for + RawEvent { + #[inline] + fn eq(&self, other: &RawEvent) -> bool { + match (&*self, &*other) { + (&RawEvent::SomethingStored(ref __self_0, ref __self_1), + &RawEvent::SomethingStored(ref __arg_1_0, ref __arg_1_1)) => + (*__self_0) == (*__arg_1_0) && (*__self_1) == (*__arg_1_1), + } + } + #[inline] + fn ne(&self, other: &RawEvent) -> bool { + match (&*self, &*other) { + (&RawEvent::SomethingStored(ref __self_0, ref __self_1), + &RawEvent::SomethingStored(ref __arg_1_0, ref __arg_1_1)) => + (*__self_0) != (*__arg_1_0) || (*__self_1) != (*__arg_1_1), + } + } +} +#[automatically_derived] +#[allow(unused_qualifications)] +impl ::core::cmp::Eq for RawEvent + { + #[inline] + #[doc(hidden)] + fn assert_receiver_is_total_eq(&self) -> () { + { + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + } + } +} +#[allow(non_upper_case_globals , unused_attributes , + unused_qualifications)] +const _IMPL_ENCODE_FOR_RawEvent: () = + { + #[allow(unknown_lints)] + #[allow(rust_2018_idioms)] + extern crate codec as _codec; + impl _codec::Encode for + RawEvent where AccountId: _codec::Encode, + AccountId: _codec::Encode { + fn encode_to(&self, + dest: + &mut EncOut) { + match *self { + RawEvent::SomethingStored(ref aa, ref ba) => { + dest.push_byte(0usize as u8); + dest.push(aa); + dest.push(ba); + } + _ => (), + } + } + } + impl _codec::EncodeLike for + RawEvent where AccountId: _codec::Encode, + AccountId: _codec::Encode { + } + }; +#[allow(non_upper_case_globals , unused_attributes , + unused_qualifications)] +const _IMPL_DECODE_FOR_RawEvent: () = + { + #[allow(unknown_lints)] + #[allow(rust_2018_idioms)] + extern crate codec as _codec; + impl _codec::Decode for + RawEvent where AccountId: _codec::Decode, + AccountId: _codec::Decode { + fn decode(input: + &mut DecIn) + -> core::result::Result { + match input.read_byte()? { + x if x == 0usize as u8 => { + Ok(RawEvent::SomethingStored({ + let res = + _codec::Decode::decode(input); + match res { + Err(_) => + return Err("Error decoding field RawEvent :: SomethingStored.0".into()), + Ok(a) => a, + } + }, + { + let res = + _codec::Decode::decode(input); + match res { + Err(_) => + return Err("Error decoding field RawEvent :: SomethingStored.1".into()), + Ok(a) => a, + } + })) + } + x => Err("No such variant in enum RawEvent".into()), + } + } + } + }; +impl From> for () { + fn from(_: RawEvent) -> () { () } +} +impl RawEvent { + #[allow(dead_code)] + pub fn metadata() -> &'static [::srml_support::event::EventMetadata] { + &[::srml_support::event::EventMetadata{name: + ::srml_support::event::DecodeDifferent::Encode("SomethingStored"), + arguments: + ::srml_support::event::DecodeDifferent::Encode(&["u32", + "AccountId"]), + documentation: + ::srml_support::event::DecodeDifferent::Encode(&[]),}] + } +} +*/ \ No newline at end of file diff --git a/stf/src/tests.rs b/stf/src/tests.rs new file mode 100644 index 0000000000..19f7be138a --- /dev/null +++ b/stf/src/tests.rs @@ -0,0 +1,28 @@ +/* + Copyright 2019 Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use alloc::prelude::v1::Vec; +use crate::{TrustedCall, TrustedGetter, AccountId}; +use codec::{Compact, Decode, Encode}; + + +pub fn get_test_balance_set_balance_call() -> Vec { + TrustedCall::balance_set_balance(AccountId::default(), 33,44).encode() +} + +pub fn get_test_getter_free_balance() -> Vec { + TrustedGetter::free_balance(AccountId::default()).encode() +} \ No newline at end of file diff --git a/substratee-node-calls/Cargo.toml b/substratee-node-calls/Cargo.toml index b1d5c9630b..e993eead81 100644 --- a/substratee-node-calls/Cargo.toml +++ b/substratee-node-calls/Cargo.toml @@ -6,22 +6,26 @@ edition = "2018" [dependencies] log = "0.4" -parity-codec = { version = "3.0", default-features = false } -primitive-types = { version = "0.2", default-features = false, features = ["codec"] } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +primitive-types = { version = "0.5.0", default-features = false, features = ["codec"] } regex = "1" [dependencies.substrate-api-client] git = "https://github.com/scs/substrate-api-client" -rev = "61bc5233582952a9ff13ab24fb93b08396758bf1" -default-features = false +branch = "no_std_rstd" +#default-features = false -[dependencies.my_node_runtime] -git = "https://github.com/scs/substraTEE-node" -tag = "M4" -package = "substratee-node-runtime" -features = ["no_global_allocator"] +#[dependencies.my_node_runtime] +#git = "https://github.com/scs/substraTEE-node" +#branch = "brenzi-private-tx" +#package = "substratee-node-runtime" +#features = ["no_global_allocator"] [dependencies.primitives] git = "https://github.com/paritytech/substrate" -rev = "56b0273fd48b4e742e9da7317bc8e132a740b684" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" package = "substrate-primitives" + +[dependencies.srml-support] +git = "https://github.com/paritytech/substrate" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" \ No newline at end of file diff --git a/substratee-node-calls/src/lib.rs b/substratee-node-calls/src/lib.rs index 9e6308d9e6..9457bef5ff 100644 --- a/substratee-node-calls/src/lib.rs +++ b/substratee-node-calls/src/lib.rs @@ -1,10 +1,11 @@ extern crate regex; use log::*; -use parity_codec::{Decode, Encode}; -use substrate_api_client::{hexstr_to_vec}; +use codec::{Decode, Encode}; +use substrate_api_client::utils::{hexstr_to_vec, hexstr_to_u64}; use primitives::ed25519; use regex::Regex; +use srml_support::traits::Len; pub fn get_worker_info(api: &substrate_api_client::Api, index: u64) -> Enclave { info!("[>] Get worker's URL"); @@ -19,14 +20,18 @@ pub fn get_worker_info(api: &substrate_api_client::Api, index: u64) -> Enclave { pub fn get_worker_amount(api: &substrate_api_client::Api) -> u64 { let result_str = api.get_storage("substraTEERegistry", "EnclaveCount", None).unwrap(); - let amount = hexstr_to_u64(result_str); + info!("get_worker_amount() ret {:?}", result_str); + let amount = match result_str.as_str() { + "null" => 0u64, + _ => hexstr_to_u64(result_str).unwrap(), + }; info!("[+]: Amount of Registered Workers {:?}", amount); amount } pub fn get_latest_state(api: &substrate_api_client::Api) -> Option<[u8; 46]> { let result_str = api.get_storage("substraTEERegistry", "LatestIPFSHash", None).unwrap(); - let unhex = hexstr_to_vec(result_str); + let unhex = hexstr_to_vec(result_str).unwrap(); info!("State hash vec: {:?}", unhex); let mut h : [u8; 46] = [0; 46]; @@ -43,7 +48,7 @@ pub fn get_latest_state(api: &substrate_api_client::Api) -> Option<[u8; 46]> { } fn hexstr_to_enclave(hexstr: String) -> Enclave { - let mut unhex = hexstr_to_vec(hexstr); + let mut unhex = hexstr_to_vec(hexstr).unwrap(); let (h, url) = unhex.split_at_mut(32 as usize); let mut raw: [u8; 32] = Default::default(); raw.copy_from_slice(&h); @@ -60,14 +65,6 @@ fn hexstr_to_enclave(hexstr: String) -> Enclave { } } -pub fn hexstr_to_u64(hexstr: String) -> u64 { - let unhex = hexstr_to_vec(hexstr); - let mut gh: [u8; 8] = Default::default(); - gh.copy_from_slice(&unhex); - - u64::from_le_bytes(gh) -} - #[derive(Encode, Decode, Default, Clone, PartialEq)] #[cfg_attr(feature = "std", derive(Debug))] pub struct Enclave { diff --git a/worker/Cargo.toml b/worker/Cargo.toml index afdac8b8d0..b3e98c340e 100755 --- a/worker/Cargo.toml +++ b/worker/Cargo.toml @@ -30,8 +30,8 @@ multihash = "0.8" cid = "0.3" sha2 = { version = "0.7", default-features = false } -parity-codec = { version = "3.0", default-features = false } -primitive-types = { version = "0.2", default-features = false, features = ["codec"] } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +primitive-types = { version = "0.5.0", default-features = false, features = ["codec"] } sgx_urts = { rev = "v1.0.9", git = "https://github.com/baidu/rust-sgx-sdk" } sgx_types = { rev = "v1.0.9", git = "https://github.com/baidu/rust-sgx-sdk" } @@ -41,7 +41,7 @@ sgx_crypto_helper = { rev = "v1.0.9", git = "https://github.com/baidu/rust-sgx [dependencies.substrate-api-client] git = "https://github.com/scs/substrate-api-client" -rev = "61bc5233582952a9ff13ab24fb93b08396758bf1" +branch = "no_std_rstd" [dependencies.substratee-node-calls] path = "../substratee-node-calls" @@ -51,39 +51,48 @@ path = "worker-api" [dependencies.my_node_runtime] git = "https://github.com/scs/substraTEE-node" -tag = "M4" +branch = "brenzi-private-tx" package = "substratee-node-runtime" default-features = false features = ["no_global_allocator"] +[dependencies.runtime_primitives] +git = 'https://github.com/paritytech/substrate' +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" +package = "sr-primitives" + [dependencies.balances] git = "https://github.com/paritytech/substrate" -rev = "56b0273fd48b4e742e9da7317bc8e132a740b684" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" package = "srml-balances" [dependencies.node-primitives] git = "https://github.com/paritytech/substrate" -rev = "56b0273fd48b4e742e9da7317bc8e132a740b684" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" [dependencies.primitives] git = "https://github.com/paritytech/substrate" -rev = "56b0273fd48b4e742e9da7317bc8e132a740b684" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" package = "substrate-primitives" [dependencies.substrate-keyring] git = "https://github.com/paritytech/substrate" -rev = "56b0273fd48b4e742e9da7317bc8e132a740b684" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" [dependencies.system] git = "https://github.com/paritytech/substrate" -rev = "56b0273fd48b4e742e9da7317bc8e132a740b684" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" package = "srml-system" +[dependencies.substratee-stf] +path = "../stf" +default-features = false + [features] default = ["std"] std = [ "primitives/std", - "parity-codec/std", + "codec/std", "my_node_runtime/std", ] diff --git a/worker/src/enclave_api.rs b/worker/src/enclave_api.rs index a6dbdf494a..f732c9f3b9 100755 --- a/worker/src/enclave_api.rs +++ b/worker/src/enclave_api.rs @@ -20,29 +20,33 @@ extern crate sgx_types; use sgx_types::*; extern { - pub fn call_counter_wasm( + pub fn init( eid: sgx_enclave_id_t, retval: *mut sgx_status_t, - req_bin : *const u8, - req_len: usize, - ciphertext: *mut u8, - ciphertext_size: u32, + ) -> sgx_status_t; + + pub fn execute_stf( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + request_encrypted: *mut u8, + request_encrypted_size: u32, hash: *const u8, hash_size: u32, nonce: *const u8, nonce_size: u32, - wasm_hash: *const u8, - wasm_hash_size: u32, unchecked_extrinsic: *mut u8, unchecked_extrinsic_size: u32 ) -> sgx_status_t; - pub fn get_counter( + + + pub fn get_state( eid: sgx_enclave_id_t, retval: *mut sgx_status_t, - account: *const u8, - account_size: u32, - counter_value: *mut u32 + getter: *const u8, + getter_size: u32, + value: *mut u8, + value_size: u32 ) -> sgx_status_t; pub fn get_rsa_encryption_pubkey( diff --git a/worker/src/enclave_wrappers.rs b/worker/src/enclave_wrappers.rs index dcb9999271..b8d66d66db 100644 --- a/worker/src/enclave_wrappers.rs +++ b/worker/src/enclave_wrappers.rs @@ -24,141 +24,23 @@ use constants::*; use enclave_api::*; use init_enclave::init_enclave; -use primitives::{ed25519}; +use primitives::{ed25519, sr25519}; +use primitives::crypto::Ss58Codec; -use substrate_api_client::{Api, hexstr_to_u256}; +use substrate_api_client::{Api, extrinsic::xt_primitives::GenericAddress, + utils::hexstr_to_u256}; use my_node_runtime::{UncheckedExtrinsic, Call, SubstraTEERegistryCall}; -use parity_codec::{Decode, Encode}; +use codec::{Decode, Encode}; use primitive_types::U256; -use wasm::SgxWasmAction; - use crypto::*; -// function to get the account nonce of a user -pub fn get_account_nonce(api: &Api, user: [u8; 32]) -> U256 { - info!("[>] Get account nonce"); - - let accountid = ed25519::Public::from_raw(user); - let result_str = api.get_storage("System", "AccountNonce", Some(accountid.encode())).unwrap(); - let nonce = hexstr_to_u256(result_str); - - info!("[<] Account nonce of {:?} is {}\n", accountid, nonce); - nonce -} - -// decrypt and process the payload (in the enclave) -// then compose the extrinsic (in the enclave) -// and send an extrinsic back to the substraTEE-node -pub fn process_forwarded_payload( - eid: sgx_enclave_id_t, - ciphertext: Vec, - retval: &mut sgx_status_t, - node_url: &str) { - - let mut api = Api::new(format!("ws://{}", node_url)); - api.init(); - - let mut unchecked_extrinsic = UncheckedExtrinsic::new_unsigned(Call::SubstraTEERegistry(SubstraTEERegistryCall::confirm_call(vec![0; 32], vec![0; 46]))); +use runtime_primitives::{AnySignature, traits::Verify}; - // decrypt and process the payload. we will get an extrinsic back - let result = decrypt_and_process_payload(eid, ciphertext, &mut unchecked_extrinsic, retval, &api); +type AccountId = ::Signer; - match result { - sgx_status_t::SGX_SUCCESS => { - let mut _xthex = hex::encode(unchecked_extrinsic.encode()); - _xthex.insert_str(0, "0x"); - - // sending the extrinsic - println!(); - println!("[>] Confirm processing (send the extrinsic)"); - let tx_hash = api.send_extrinsic(_xthex).unwrap(); - println!("[<] Extrinsic got finalized. Hash: {:?}\n", tx_hash); - }, - _ => { - println!(); - error!("Payload not processed due to errors."); - } - } -} - -pub fn decrypt_and_process_payload( - eid: sgx_enclave_id_t, - mut ciphertext: Vec, - ue: &mut UncheckedExtrinsic, - retval: &mut sgx_status_t, - api: &Api) -> sgx_status_t { - println!("[>] Decrypt and process the payload"); - - let genesis_hash = api.genesis_hash.unwrap().as_bytes().to_vec(); - - // get the public signing key of the TEE - let mut key = [0; 32]; - let ecc_key = fs::read(ECC_PUB_KEY).expect("Unable to open ECC public key file"); - key.copy_from_slice(&ecc_key[..]); - info!("[+] Got ECC public key of TEE = {:?}", key); - - // get enclaves's account nonce - let nonce = get_account_nonce(&api, key); - let nonce_bytes = U256::encode(&nonce); - - // read wasm file to string - let module = include_bytes!("../../bin/worker_enclave.compact.wasm").to_vec(); - - // calculate the SHA256 of the WASM - let wasm_hash = rsgx_sha256_slice(&module).unwrap(); - let wasm_hash_str = serde_json::to_string(&wasm_hash).unwrap(); - - // prepare the request - let req = SgxWasmAction::Call { - module : Some(module), - function : "update_counter".to_string(), - }; - debug!("Request for WASM = {:?}", req); - let req_str = serde_json::to_string(&req).unwrap(); - - // update the counter and compose the extrinsic - // the extrinsic size will be determined in the function call_counter_wasm - let unchecked_extrinsic_size = 500; - let mut unchecked_extrinsic : Vec = vec![0u8; unchecked_extrinsic_size as usize]; - - let result = unsafe { - call_counter_wasm(eid, - retval, - req_str.as_ptr() as * const u8, - req_str.len(), - ciphertext.as_mut_ptr(), - ciphertext.len() as u32, - genesis_hash.as_ptr(), - genesis_hash.len() as u32, - nonce_bytes.as_ptr(), - nonce_bytes.len() as u32, - wasm_hash_str.as_ptr(), - wasm_hash_str.len() as u32, - unchecked_extrinsic.as_mut_ptr(), - unchecked_extrinsic_size as u32 - ) - }; - - match result { - sgx_status_t::SGX_SUCCESS => debug!("[+] ECALL Enclave successful"), - _ => { - error!("[-] ECALL Enclave Failed {}!", result.as_str()); - } - } - - match retval { - sgx_status_t::SGX_SUCCESS => { - println!("[<] Message decoded and processed in the enclave"); - *ue = UncheckedExtrinsic::decode(&mut unchecked_extrinsic.as_slice()).unwrap(); - sgx_status_t::SGX_SUCCESS - }, - _ => { - error!("[<] Error processing message in the enclave"); - sgx_status_t::SGX_ERROR_UNEXPECTED - } - } -} +// FIXME: most of these functions use redundant code with is provided by substrate-api-client +// but first resolve this: https://github.com/scs/substrate-api-client/issues/27 pub fn get_signing_key_tee() { println!(); @@ -173,6 +55,19 @@ pub fn get_signing_key_tee() { return; }, }; + let mut status = sgx_status_t::SGX_SUCCESS; + println!("*** call enclave init()"); + let result = unsafe { + init( + enclave.geteid(), + &mut status, + ) + }; + + if result != sgx_status_t::SGX_SUCCESS || status != sgx_status_t::SGX_SUCCESS { + println!("[-] init() failed.\n"); + return; + } // request the key println!(); @@ -180,21 +75,18 @@ pub fn get_signing_key_tee() { let pubkey_size = 32; let mut pubkey = [0u8; 32]; - let mut retval = sgx_status_t::SGX_SUCCESS; + let mut status = sgx_status_t::SGX_SUCCESS; let result = unsafe { get_ecc_signing_pubkey(enclave.geteid(), - &mut retval, + &mut status, pubkey.as_mut_ptr(), pubkey_size ) }; - match result { - sgx_status_t::SGX_SUCCESS => {}, - _ => { - error!("[-] ECALL Enclave Failed {}!", result.as_str()); - return; - } + if result != sgx_status_t::SGX_SUCCESS || status != sgx_status_t::SGX_SUCCESS { + error!("[-] ECALL Enclave Failed {} / status {}!", result.as_str(), status.as_str()); + return; } println!("[+] Signing key: {:?}", pubkey); @@ -222,6 +114,19 @@ pub fn get_public_key_tee() return; }, }; + let mut status = sgx_status_t::SGX_SUCCESS; + println!("*** call enclave init()"); + let result = unsafe { + init( + enclave.geteid(), + &mut status, + ) + }; + + if result != sgx_status_t::SGX_SUCCESS || status != sgx_status_t::SGX_SUCCESS { + println!("[-] init() failed.\n"); + return; + } // request the key println!(); @@ -229,21 +134,18 @@ pub fn get_public_key_tee() let pubkey_size = 8192; let mut pubkey = vec![0u8; pubkey_size as usize]; - let mut retval = sgx_status_t::SGX_SUCCESS; + let mut status = sgx_status_t::SGX_SUCCESS; let result = unsafe { get_rsa_encryption_pubkey(enclave.geteid(), - &mut retval, + &mut status, pubkey.as_mut_ptr(), pubkey_size ) }; - match result { - sgx_status_t::SGX_SUCCESS => {}, - _ => { - error!("[-] ECALL Enclave Failed {}!", result.as_str()); - return; - } + if result != sgx_status_t::SGX_SUCCESS || status != sgx_status_t::SGX_SUCCESS { + error!("[-] ECALL Enclave Failed {} / status {}!", result.as_str(), status.as_str()); + return; } let rsa_pubkey: Rsa3072PubKey = serde_json::from_slice(&pubkey[..]).unwrap(); @@ -258,3 +160,74 @@ pub fn get_public_key_tee() _ => { println!("[+] File '{}' written successfully", RSA_PUB_KEY); } } } + +pub fn process_request( + eid: sgx_enclave_id_t, + request: Vec, + node_url: &str +) { + + // new api client (the other on is busy listening to events) + let mut _api = Api::new(format!("ws://{}", node_url)); + let mut status = sgx_status_t::SGX_SUCCESS; + // FIXME: refactor to function + println!("*** Ask the signing key from the TEE"); + let tee_pubkey_size = 32; + let mut tee_pubkey = [0u8; 32]; + + let mut status = sgx_status_t::SGX_SUCCESS; + let result = unsafe { + get_ecc_signing_pubkey(eid, + &mut status, + tee_pubkey.as_mut_ptr(), + tee_pubkey_size + ) + }; + if result != sgx_status_t::SGX_SUCCESS || status != sgx_status_t::SGX_SUCCESS { + error!("[-] ECALL Enclave Failed {} / status {}!", result.as_str(), status.as_str()); + return; + } + + // Attention: this HAS to be sr25519, although its a ed25519 key! + let tee_public = sr25519::Public::from_raw(tee_pubkey); + info!("[+] Got ed25519 account of TEE = {}", tee_public.to_ss58check()); + let tee_accountid = AccountId::from(tee_public); + + let result_str = _api.get_storage("System", "AccountNonce", Some(tee_accountid.encode())).unwrap(); + + let genesis_hash = _api.genesis_hash.as_bytes().to_vec(); + + let nonce = hexstr_to_u256(result_str).unwrap().low_u32(); + info!("Enclave nonce = {:?}", nonce); + let nonce_bytes = nonce.encode(); + + let unchecked_extrinsic_size = 500; + let mut unchecked_extrinsic : Vec = vec![0u8; unchecked_extrinsic_size as usize]; + + let result = unsafe { + execute_stf(eid, + &mut status, + request.to_vec().as_mut_ptr(), + request.len() as u32, + genesis_hash.as_ptr(), + genesis_hash.len() as u32, + nonce_bytes.as_ptr(), + nonce_bytes.len() as u32, + unchecked_extrinsic.as_mut_ptr(), + unchecked_extrinsic_size as u32 + ) + }; + if result != sgx_status_t::SGX_SUCCESS || status != sgx_status_t::SGX_SUCCESS { + error!("[-] ECALL Enclave Failed {} / status {}!", result.as_str(), status.as_str()); + return; + } + + println!("[<] Message decoded and processed in the enclave"); + let ue = UncheckedExtrinsic::decode(&mut unchecked_extrinsic.as_slice()).unwrap(); + let mut _xthex = hex::encode(ue.encode()); + _xthex.insert_str(0, "0x"); + println!("[>] Confirm processing (send the extrinsic)"); + let tx_hash = _api.send_extrinsic(_xthex).unwrap(); + println!("[<] Extrinsic got finalized. Hash: {:?}\n", tx_hash); + +} \ No newline at end of file diff --git a/worker/src/main.rs b/worker/src/main.rs index fdb2b7f0d6..df99956803 100644 --- a/worker/src/main.rs +++ b/worker/src/main.rs @@ -22,7 +22,8 @@ extern crate log; extern crate my_node_runtime; extern crate nan_preserving_float; extern crate node_primitives; -extern crate parity_codec; +extern crate runtime_primitives; +extern crate codec; extern crate primitive_types; extern crate primitives; extern crate rust_base58; @@ -34,7 +35,9 @@ extern crate sgx_crypto_helper; extern crate sgx_types; extern crate sgx_ucrypto as crypto; extern crate sgx_urts; +#[macro_use] extern crate substrate_api_client; +extern crate substratee_stf; extern crate substratee_node_calls; extern crate substratee_worker_api; extern crate substrate_keyring; @@ -51,27 +54,35 @@ extern crate sha2; use clap::App; use constants::*; -use enclave_api::{perform_ra}; -use enclave_wrappers::*; +use enclave_api::{init, perform_ra, get_ecc_signing_pubkey, execute_stf}; +use enclave_wrappers::{process_request, get_signing_key_tee, get_public_key_tee }; use init_enclave::init_enclave; use log::*; use my_node_runtime::{Event, Hash}; use my_node_runtime::UncheckedExtrinsic; -use parity_codec::Decode; -use parity_codec::Encode; +use codec::Decode; +use codec::Encode; use primitive_types::U256; use sgx_types::*; use std::fs; use std::str; use std::sync::mpsc::channel; use std::thread; -use substrate_api_client::{Api, hexstr_to_vec}; +use substrate_api_client::{Api, utils::hexstr_to_vec, + extrinsic::{balances::{transfer, set_balance}, xt_primitives::GenericAddress}, + crypto::{AccountKey, CryptoKind}, + utils::hexstr_to_u256}; + use utils::{check_files, get_first_worker_that_is_not_equal_to_self}; -use wasm::sgx_enclave_wasm_init; use ws_server::start_ws_server; use enclave_tls_ra::{Mode, run_enclave_server, run_enclave_client}; use substratee_node_calls::get_worker_amount; use substratee_worker_api::Api as WorkerApi; +use primitives::{Pair, crypto::Ss58Codec, ed25519, sr25519}; + +use runtime_primitives::{AnySignature, traits::Verify}; + +type AccountId = ::Signer; mod utils; mod constants; @@ -80,7 +91,6 @@ mod init_enclave; mod ws_server; mod enclave_wrappers; mod enclave_tls_ra; -mod wasm; mod attestation_ocalls; mod ipfs; mod tests; @@ -160,18 +170,17 @@ fn worker(node_url: &str, w_ip: &str, w_port: &str, mu_ra_port: &str) { }, }; + println!("*** call enclave init()"); + let result = unsafe { + init( + enclave.geteid(), + &mut status, + ) + }; - // ------------------------------------------------------------------------ - // initialize the sgxwasm specific driver engine - let result = sgx_enclave_wasm_init(enclave.geteid()); - match result { - Ok(_r) => { - println!("[+] Init Wasm in enclave successful\n"); - }, - Err(x) => { - error!("[-] Init Wasm in enclave failed {}!\n", x.as_str()); - return; - }, + if result != sgx_status_t::SGX_SUCCESS || status != sgx_status_t::SGX_SUCCESS { + println!("[-] init() failed.\n"); + return; } // ------------------------------------------------------------------------ @@ -188,31 +197,86 @@ fn worker(node_url: &str, w_ip: &str, w_port: &str, mu_ra_port: &str) { // ------------------------------------------------------------------------ // start the substrate-api-client to communicate with the node - let mut api = Api::new(format!("ws://{}", node_url)); - api.init(); + let alice = primitives::sr25519::Pair::from_string("//Alice", None).unwrap(); + println!(" Alice's account = {}", alice.public().to_ss58check()); + let alicekey = AccountKey::Sr(alice.clone()); + // alice is validator + let mut api = Api::new(format!("ws://{}", node_url)) + .set_signer(alicekey.clone()); + + info!("encoding Alice public = {:?}", alice.public().0.encode()); + info!("encoding Alice AccountId = {:?}", AccountId::from(alice.public()).encode()); + + let result_str = api.get_storage("Balances", "FreeBalance", Some(AccountId::from(alice.public()).encode())).unwrap(); + let funds = hexstr_to_u256(result_str).unwrap(); + println!(" Alice's free balance = {:?}", funds); + let result_str = api.get_storage("System", "AccountNonce", Some(AccountId::from(alice.public()).encode())).unwrap(); + let result = hexstr_to_u256(result_str).unwrap(); + println!(" Alice's Account Nonce is {}", result.low_u32()); + // ------------------------------------------------------------------------ // get required fields for the extrinsic - let genesis_hash = api.genesis_hash.unwrap().as_bytes().to_vec(); + let genesis_hash = api.genesis_hash.as_bytes().to_vec(); // get the public signing key of the TEE - let mut key = [0; 32]; - let ecc_key = fs::read(ECC_PUB_KEY).expect("Unable to open ECC public key file"); - key.copy_from_slice(&ecc_key[..]); - info!("[+] Got ECC public key of TEE = {:?}", key); + println!("*** Ask the signing key from the TEE"); + let tee_pubkey_size = 32; + let mut tee_pubkey = [0u8; 32]; + + let mut status = sgx_status_t::SGX_SUCCESS; + let result = unsafe { + get_ecc_signing_pubkey(enclave.geteid(), + &mut status, + tee_pubkey.as_mut_ptr(), + tee_pubkey_size + ) + }; + match result { + sgx_status_t::SGX_SUCCESS => {}, + _ => { + error!("[-] ECALL Enclave Failed {}!", result.as_str()); + return; + } + } + // Attention: this HAS to be sr25519, although its a ed25519 key! + let tee_public = sr25519::Public::from_raw(tee_pubkey); + info!("[+] Got ed25519 account of TEE = {}", tee_public.to_ss58check()); + //info!("[+] Got ed25519 public raw of TEE = {:?}", tee_pubkey); + let tee_accountid = AccountId::from(tee_public); + + // check the enclave's account balance + let result_str = api.get_storage("Balances", "FreeBalance", Some(tee_accountid.encode())).unwrap(); + let funds = hexstr_to_u256(result_str).unwrap(); + info!("TEE's free balance = {:?}", funds); + + if funds < U256::from(10) { + println!("[+] bootstrap funding Enclave form Alice's funds"); + let xt = transfer(api.clone(), GenericAddress::from(tee_pubkey), 1000000); + let xt_hash = api.send_extrinsic(xt.hex_encode()).unwrap(); + info!("[<] Extrinsic got finalized. Hash: {:?}\n", xt_hash); + + //verify funds have arrived + let result_str = api.get_storage("Balances", "FreeBalance", Some(tee_accountid.encode())).unwrap(); + let funds = hexstr_to_u256(result_str).unwrap(); + info!("TEE's NEW free balance = {:?}", funds); + } + + + // ------------------------------------------------------------------------ + // perform a remote attestation and get an unchecked extrinsic back // get enclaves's account nonce - let nonce = get_account_nonce(&api, key); - let nonce_bytes = U256::encode(&nonce); + let result_str = api.get_storage("System", "AccountNonce", Some(tee_accountid.encode())).unwrap(); + let nonce = hexstr_to_u256(result_str).unwrap().low_u32(); info!("Enclave nonce = {:?}", nonce); + let nonce_bytes = nonce.encode(); // prepare the unchecked extrinsic // the size is determined in the enclave let unchecked_extrinsic_size = 5000; let mut unchecked_extrinsic : Vec = vec![0u8; unchecked_extrinsic_size as usize]; - // ------------------------------------------------------------------------ - // perform a remote attestation and get an unchecked extrinsic back println!("*** Perform a remote attestation of the enclave"); let result = unsafe { perform_ra( @@ -258,7 +322,7 @@ fn worker(node_url: &str, w_ip: &str, w_port: &str, mu_ra_port: &str) { }, _ => { println!("*** There are already workers registered, fetching keys from first one..."); - let w1 = get_first_worker_that_is_not_equal_to_self(&api, ecc_key).unwrap(); + let w1 = get_first_worker_that_is_not_equal_to_self(&api, tee_pubkey.to_vec()).unwrap(); let w_api = WorkerApi::new(w1.url.clone()); let ra_port = w_api.get_mu_ra_port().unwrap(); @@ -285,14 +349,15 @@ fn worker(node_url: &str, w_ip: &str, w_port: &str, mu_ra_port: &str) { println!("[+] Subscribed, waiting for event..."); println!(); + loop { let event_str = events_out.recv().unwrap(); - let _unhex = hexstr_to_vec(event_str); + let _unhex = hexstr_to_vec(event_str).unwrap(); let mut _er_enc = _unhex.as_slice(); let _events = Vec::>::decode(&mut _er_enc); match _events { - Some(evts) => { + Ok(evts) => { for evr in &evts { debug!("Decoded: phase = {:?}, event = {:?}", evr.phase, evr.event); match &evr.event { @@ -322,14 +387,12 @@ fn worker(node_url: &str, w_ip: &str, w_port: &str, mu_ra_port: &str) { println!(" Registered URL: {:?}", str::from_utf8(worker_url).unwrap()); println!(); }, - my_node_runtime::substratee_registry::RawEvent::Forwarded(sender, payload) => { + my_node_runtime::substratee_registry::RawEvent::Forwarded(sender, request) => { println!("[+] Received Forwarded event"); debug!(" From: {:?}", sender); - debug!(" Payload: {:?}", hex::encode(payload)); + debug!(" Request: {:?}", hex::encode(request)); println!(); - - // process the payload and send extrinsic - process_forwarded_payload(enclave.geteid(), payload.to_vec(), &mut status, node_url); + process_request(enclave.geteid(), request.to_vec(), node_url); }, my_node_runtime::substratee_registry::RawEvent::CallConfirmed(sender, payload) => { println!("[+] Received CallConfirmed event"); @@ -349,7 +412,7 @@ fn worker(node_url: &str, w_ip: &str, w_port: &str, mu_ra_port: &str) { } } } - None => error!("Couldn't decode event record list") + Err(_) => error!("Couldn't decode event record list") } } } diff --git a/worker/src/tests/commons.rs b/worker/src/tests/commons.rs index 5fc0686461..8fb49a3258 100644 --- a/worker/src/tests/commons.rs +++ b/worker/src/tests/commons.rs @@ -18,7 +18,7 @@ use enclave_api::*; //use enclave_wrappers::get_account_nonce; use log::*; -//use parity_codec::{Compact, Encode}; +//use codec::{Compact, Encode}; //use primitive_types::U256; use primitives::{ed25519, Pair}; use serde_json; @@ -27,6 +27,7 @@ use sgx_types::*; use std::str; use substrate_api_client::Api; use utils; +use substratee_stf; #[derive(Debug, Serialize, Deserialize)] pub struct Message { @@ -63,32 +64,19 @@ pub fn get_encrypted_msg(eid: sgx_enclave_id_t) -> Vec { } pub fn encrypt_msg(rsa_pubkey: Rsa3072PubKey) -> Vec { - let hash: Vec = utils::get_wasm_hash(); - println!("Wasm Hash: {:?}", hash[0]); - println!("Wasm Binary : {:?}", hash[1]); - - let sha = hex::decode(hash[0].clone()).unwrap(); - let sha256: sgx_sha256_hash_t = from_slice(&sha); - - let account: String = "Alice".to_string(); - let amount = 42; - - let message = Message { account, amount, sha256 }; - let plaintext = serde_json::to_vec(&message).unwrap(); + let payload = substratee_stf::tests::get_test_balance_set_balance_call(); let mut payload_encrypted: Vec = Vec::new(); - - rsa_pubkey.encrypt_buffer(&plaintext, &mut payload_encrypted).unwrap(); + rsa_pubkey.encrypt_buffer(&payload, &mut payload_encrypted).unwrap(); payload_encrypted } #[allow(dead_code)] pub fn register_enclave(port: &str) { let mut api = Api::new(format!("ws://127.0.0.1:{}", port)); - api.init(); let tee_ecc_seed = [244, 96, 170, 60, 77, 239, 28, 64, 51, 180, 208, 145, 76, 154, 198, 174, 236, 162, 18, 135, 190, 84, 216, 155, 142, 175, 237, 238, 60, 219, 134, 184]; - let _pair = ed25519::Pair::from_seed(tee_ecc_seed); + let _pair = ed25519::Pair::from_seed(&tee_ecc_seed); // let tx_hash = api.send_extrinsic(xthex).unwrap(); } diff --git a/worker/src/tests/ecalls.rs b/worker/src/tests/ecalls.rs index 0e5beaf77c..e9467757df 100644 --- a/worker/src/tests/ecalls.rs +++ b/worker/src/tests/ecalls.rs @@ -18,65 +18,59 @@ use crypto::rsgx_sha256_slice; use enclave_api::*; use log::*; -use parity_codec::Encode; +use codec::Encode; use primitive_types::U256; use sgx_types::*; use tests::commons::*; -use wasm::SgxWasmAction; +use substratee_stf; -pub fn get_counter_works(eid: sgx_enclave_id_t) { +// TODO: test get_ecc_signing_pubkey +// TODO: test get_rsa_encryption_pubkey + +pub fn get_state_works(eid: sgx_enclave_id_t) { let mut retval = sgx_status_t::SGX_SUCCESS; let account ="Alice"; - let mut value = 0u32; + let value_size = 16; //u128 + let mut value: Vec = vec![0u8; value_size as usize]; + + let getter = substratee_stf::tests::get_test_getter_free_balance(); + + let result = sgx_status_t::SGX_ERROR_UNEXPECTED; let result = unsafe { - get_counter(eid, + get_state(eid, &mut retval, - account.as_ptr(), - account.len() as u32, - &mut value) + getter.as_ptr(), + getter.len() as u32, + value.as_mut_ptr(), + value_size as u32 + ) }; - - println!("{} value: {}", account, value); + println!("{} value: {:?}", account, value); evaluate_result(result); } -pub fn call_counter_wasm_works(eid: sgx_enclave_id_t) { +pub fn execute_stf_works(eid: sgx_enclave_id_t) { let mut retval = sgx_status_t::SGX_SUCCESS; - let mut payload_encrypted = get_encrypted_msg(eid); - let module = include_bytes!("../../../bin/worker_enclave.compact.wasm").to_vec(); - let wasm_hash = rsgx_sha256_slice(&module).unwrap(); - let wasm_hash_str = serde_json::to_string(&wasm_hash).unwrap(); - - // prepare the request - let req = SgxWasmAction::Call { - module: Some(module), - function: "update_counter".to_string(), - }; - debug!("Request for WASM = {:?}", req); - let req_str = serde_json::to_string(&req).unwrap(); + let mut request_encrypted = get_encrypted_msg(eid); let unchecked_extrinsic_size = 500; let mut unchecked_extrinsic: Vec = vec![0u8; unchecked_extrinsic_size as usize]; let nonce_bytes = U256::encode(&U256::from("1")); let genesis_hash: [u8; 32] = [0; 32]; - + //TODO: new payload let result = unsafe { - call_counter_wasm(eid, + execute_stf(eid, &mut retval, - req_str.as_ptr() as *const u8, - req_str.len(), - payload_encrypted.as_mut_ptr(), - payload_encrypted.len() as u32, + request_encrypted.as_mut_ptr(), + request_encrypted.len() as u32, genesis_hash.as_ptr(), genesis_hash.len() as u32, nonce_bytes.as_ptr(), nonce_bytes.len() as u32, - wasm_hash_str.as_ptr(), - wasm_hash_str.len() as u32, unchecked_extrinsic.as_mut_ptr(), unchecked_extrinsic_size as u32 ) diff --git a/worker/src/tests/integration_tests.rs b/worker/src/tests/integration_tests.rs index f35953917a..0005d51a18 100644 --- a/worker/src/tests/integration_tests.rs +++ b/worker/src/tests/integration_tests.rs @@ -18,22 +18,21 @@ use constants::*; use enclave_api::*; use enclave_wrappers::*; -use enclave_wrappers::get_account_nonce; use log::*; -use parity_codec::Encode; +use codec::Encode; use primitive_types::U256; use sgx_types::*; use std::fs; -use substrate_api_client::Api; +use substrate_api_client::{Api, extrinsic::xt_primitives::GenericAddress, + utils::hexstr_to_u256}; use tests::commons::*; pub fn perform_ra_works(eid: sgx_enclave_id_t, port: &str) { // start the substrate-api-client to communicate with the node let mut api = Api::new(format!("ws://127.0.0.1:{}", port)); - api.init(); - + let w_url = "ws://127.0.0.1:2001"; - let genesis_hash = api.genesis_hash.unwrap().as_bytes().to_vec(); + let genesis_hash = api.genesis_hash.as_bytes().to_vec(); // get the public signing key of the TEE let mut key = [0; 32]; @@ -42,8 +41,10 @@ pub fn perform_ra_works(eid: sgx_enclave_id_t, port: &str) { debug!("[+] Got ECC public key of TEE = {:?}", key); // get enclaves's account nonce - let nonce = get_account_nonce(&api, key); - let nonce_bytes = U256::encode(&nonce); + let result_str = api.get_storage("System", "AccountNonce", Some(GenericAddress::from(key).encode())).unwrap(); + let nonce = hexstr_to_u256(result_str).unwrap().low_u32(); + debug!(" TEE nonce is {}", nonce); + let nonce_bytes = nonce.encode(); debug!("Enclave nonce = {:?}", nonce); // prepare the unchecked extrinsic @@ -74,7 +75,5 @@ pub fn perform_ra_works(eid: sgx_enclave_id_t, port: &str) { pub fn process_forwarded_payload_works(eid: sgx_enclave_id_t, port: &str) { let payload_encrypted = get_encrypted_msg(eid); - let mut retval = sgx_status_t::SGX_SUCCESS; - process_forwarded_payload(eid, payload_encrypted, &mut retval, port); - evaluate_result(retval); + process_request(eid, payload_encrypted, port); } diff --git a/worker/src/tests/mod.rs b/worker/src/tests/mod.rs index 7d1635c067..14ded0ea5f 100644 --- a/worker/src/tests/mod.rs +++ b/worker/src/tests/mod.rs @@ -19,7 +19,6 @@ use clap::ArgMatches; use enclave_api::*; use init_enclave::init_enclave; use sgx_types::*; -use wasm::sgx_enclave_wasm_init; use self::ecalls::*; use self::integration_tests::*; @@ -31,7 +30,6 @@ pub mod integration_tests; pub fn run_enclave_tests(matches: &ArgMatches, port: &str) { println!("*** Starting Test enclave"); let enclave = init_enclave().unwrap(); - sgx_enclave_wasm_init(enclave.geteid()).unwrap(); if matches.is_present("all") || matches.is_present("unit") { println!("Running unit Tests"); @@ -74,8 +72,12 @@ fn run_enclave_unit_tests(eid: sgx_enclave_id_t) { pub fn run_ecalls(eid: sgx_enclave_id_t) { - get_counter_works(eid); - call_counter_wasm_works(eid); + println!(" testing execute_stf()"); + execute_stf_works(eid); + println!(" testing get_state()"); + get_state_works(eid); + + println!("[+] Ecall tests ended!"); } diff --git a/worker/src/ws_server.rs b/worker/src/ws_server.rs index 2a31fecbd2..1300abd014 100644 --- a/worker/src/ws_server.rs +++ b/worker/src/ws_server.rs @@ -22,12 +22,15 @@ use std::str; use std::thread; use log::*; -use primitives::{ed25519, Pair}; +use primitives::{ed25519, Pair, Public}; use sgx_crypto_helper::rsa3072::Rsa3072PubKey; use sgx_types::*; use ws::{CloseCode, Handler, listen, Message, Result, Sender}; - -use enclave_api::{get_counter, get_rsa_encryption_pubkey}; +use primitive_types::U256; +use substratee_stf::TrustedGetter; +use substrate_api_client::utils::hexstr_to_u256; +//use enclave_api::{get_counter, get_rsa_encryption_pubkey}; +use enclave_api::{get_rsa_encryption_pubkey, get_state}; use substratee_worker_api::requests::*; pub fn start_ws_server(eid: sgx_enclave_id_t, addr: String, mu_ra_port: String) { @@ -48,7 +51,7 @@ pub fn start_ws_server(eid: sgx_enclave_id_t, addr: String, mu_ra_port: String) let answer = match args[0] { MSG_GET_PUB_KEY_WORKER => get_worker_pub_key(self.eid), MSG_GET_MU_RA_PORT => Message::text(self.mu_ra_port.clone()), - MSG_GET_COUNTER => handle_get_counter_msg(self.eid, args[1], args[2]), + MSG_GET_STF_STATE => handle_get_stf_state_msg(self.eid, args[1]), _ => Message::text("[WS Server]: unrecognized msg pattern"), }; @@ -68,42 +71,38 @@ pub fn start_ws_server(eid: sgx_enclave_id_t, addr: String, mu_ra_port: String) }); } -fn handle_get_counter_msg(eid: sgx_enclave_id_t, account: &str, signature: &str) -> Message { - - println!(" [WS Server] Getting counter of: {}", account); - debug!("Signature: {}", signature); +fn handle_get_stf_state_msg(eid: sgx_enclave_id_t, getter_str: &str) -> Message { - let sig_vec: Vec = serde_json::from_str(signature).unwrap(); - let mut sig_arr = [0u8; 64]; - sig_arr.clone_from_slice(&sig_vec); - - let pubkey: ed25519::Public = serde_json::from_str(account).unwrap(); - let sign: ed25519::Signature = ed25519::Signature::from_raw(sig_arr); - - if ed25519::Pair::verify(&sign, pubkey.clone().as_slice(), &pubkey.clone()) { - println!(" [WS Server]: signature supplied in getcounter request is valid!"); - } else { - error!("[WS Server]: INVALID signature supplied in get counter!"); - return Message::text("Invalid Signature"); - } + info!(" [WS Server] Getting STF state"); + // FIXME: its good to check the signature here, but it should also be verified inside the enclave again! + let getter_vec = hex::decode(getter_str).unwrap(); let mut retval = sgx_status_t::SGX_SUCCESS; - let mut value = 0u32; + // FIXME: will not always be u128!! + let value_size = 16; //u128 + let mut value: Vec = vec![0u8; value_size as usize]; + + let result = sgx_status_t::SGX_ERROR_UNEXPECTED; let result = unsafe { - get_counter(eid, + get_state(eid, &mut retval, - pubkey.to_string().as_ptr(), - pubkey.to_string().len() as u32, - &mut value) + getter_vec.as_ptr(), + getter_vec.len() as u32, + value.as_mut_ptr(), + value_size as u32 + ) }; - match result { sgx_status_t::SGX_SUCCESS => {}, _ => { error!("[-] ECALL Enclave failed {}!", result.as_str()) } } + debug!("get_state result: {:?}", value); + //let val = hexstr_to_u256(String::from_utf8(value).unwrap()).unwrap(); + let val = U256::from_little_endian(&value); + println!("decoded value value: {}", val); - Message::text(format!("Counter of {} = {}", account, value)) + Message::text(format!("State is {}", val)) } fn get_worker_pub_key(eid: sgx_enclave_id_t) -> Message { diff --git a/worker/worker-api/Cargo.toml b/worker/worker-api/Cargo.toml index 75a31163b5..ed2a43ad55 100644 --- a/worker/worker-api/Cargo.toml +++ b/worker/worker-api/Cargo.toml @@ -6,11 +6,17 @@ authors = ["Supercomputing Systems AG "] [dependencies] ws = "0.7" log = "0.4" +hex = "0.3" serde_json = "1.0" serde_derive = "1.0" sgx_crypto_helper = { rev = "v1.0.9", git = "https://github.com/baidu/rust-sgx-sdk", default-features = false} +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } [dependencies.primitives] git = "https://github.com/paritytech/substrate" -rev = "56b0273fd48b4e742e9da7317bc8e132a740b684" +rev = "9b08e7ff938a45dbec7fcdb854063202e2b0cb48" package = "substrate-primitives" + +[dependencies.substratee-stf] +path = "../../stf" +default-features = false diff --git a/worker/worker-api/src/lib.rs b/worker/worker-api/src/lib.rs index 133c96e019..b478c62ce0 100644 --- a/worker/worker-api/src/lib.rs +++ b/worker/worker-api/src/lib.rs @@ -21,6 +21,8 @@ extern crate serde_derive; extern crate serde_json; extern crate sgx_crypto_helper; extern crate ws; +extern crate substratee_stf; +extern crate codec; use client::WsClient; use log::*; @@ -30,10 +32,13 @@ use std::sync::mpsc::channel; use std::thread; use ws::connect; use primitives::ed25519; +use substratee_stf::TrustedGetter; +use codec::Encode; pub mod client; pub mod requests; +#[derive(Clone)] pub struct Api { url: String, } @@ -58,11 +63,9 @@ impl Api { Ok(rsa_pubkey) } - pub fn get_counter(&self, account: ed25519::Public, signature: ed25519::Signature) -> Result { - let acc_str = serde_json::to_string(&account).unwrap(); - let sign_str = serde_json::to_string::<[u8]>(signature.as_ref()).unwrap(); - - let request = format!("{}::{}::{}", MSG_GET_COUNTER, acc_str, sign_str); + pub fn get_stf_state(&self, getter: TrustedGetter) -> Result { + let getter_str = hex::encode(getter.encode()); + let request = format!("{}::{}", MSG_GET_STF_STATE, getter_str); Self::get(&self, &request) } diff --git a/worker/worker-api/src/requests.rs b/worker/worker-api/src/requests.rs index 585ba68702..6c50f36fec 100644 --- a/worker/worker-api/src/requests.rs +++ b/worker/worker-api/src/requests.rs @@ -17,4 +17,4 @@ pub const MSG_GET_PUB_KEY_WORKER: &str = "get_pub_key_worker"; pub const MSG_GET_MU_RA_PORT: &str = "get_mu_ra_port"; -pub const MSG_GET_COUNTER: &str = "get_counter"; +pub const MSG_GET_STF_STATE: &str = "get_stf_state";