├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── accumulator-client ├── Cargo.lock ├── Cargo.toml └── src │ └── lib.rs ├── accumulator ├── Cargo.toml └── src │ ├── lib.rs │ ├── proofs.rs │ ├── subroutines.rs │ └── witnesses.rs ├── build.rs ├── client ├── .circleci │ └── config.yml ├── .env ├── LICENSE ├── README.md ├── config-overrides.js ├── package-lock.json ├── package.json ├── public │ ├── Substrate-Logo.png │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── AccountSelector.js │ ├── Accounts.js │ ├── App.js │ ├── Events.js │ ├── Mint.js │ ├── State.js │ ├── Transaction.js │ ├── Witness.js │ ├── __tests__ │ │ └── App.js │ ├── config │ │ ├── common.json │ │ ├── development.json │ │ ├── index.js │ │ ├── production.json │ │ └── test.json │ ├── index.js │ └── substrate-lib │ │ ├── SubstrateContext.js │ │ ├── components │ │ ├── DeveloperConsole.js │ │ ├── TxButton.js │ │ └── index.js │ │ ├── index.js │ │ └── useSubstrate.js ├── yarn-error.log └── yarn.lock ├── runtime ├── Cargo.toml ├── build.rs └── src │ ├── lib.rs │ └── stateless.rs ├── scripts └── init.sh ├── src ├── chain_spec.rs ├── cli.rs ├── main.rs └── service.rs ├── substrate-node-rename.sh └── vector-commitment ├── Cargo.lock ├── Cargo.toml └── src ├── binary.rs ├── lib.rs └── vc.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | **/target/ 4 | # These are backup files generated by rustfmt 5 | **/*.rs.bk 6 | 7 | .DS_Store -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [build-dependencies] 2 | vergen = '3' 3 | [profile.release] 4 | panic = 'unwind' 5 | 6 | [workspace] 7 | members = ['runtime'] 8 | 9 | [dependencies] 10 | derive_more = '0.14.0' 11 | exit-future = '0.1' 12 | futures = '0.1' 13 | log = '0.4' 14 | parking_lot = '0.9.0' 15 | tokio = '0.1' 16 | trie-root = '0.15.2' 17 | 18 | [dependencies.babe] 19 | git = 'https://github.com/paritytech/substrate.git' 20 | package = 'substrate-consensus-babe' 21 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 22 | 23 | [dependencies.babe-primitives] 24 | git = 'https://github.com/paritytech/substrate.git' 25 | package = 'substrate-consensus-babe-primitives' 26 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 27 | 28 | [dependencies.basic-authorship] 29 | git = 'https://github.com/paritytech/substrate.git' 30 | package = 'substrate-basic-authorship' 31 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 32 | 33 | [dependencies.codec] 34 | package = 'parity-scale-codec' 35 | version = '1.0.0' 36 | 37 | [dependencies.ctrlc] 38 | features = ['termination'] 39 | version = '3.0' 40 | 41 | [dependencies.grandpa] 42 | git = 'https://github.com/paritytech/substrate.git' 43 | package = 'substrate-finality-grandpa' 44 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 45 | 46 | [dependencies.grandpa-primitives] 47 | git = 'https://github.com/paritytech/substrate.git' 48 | package = 'substrate-finality-grandpa-primitives' 49 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 50 | 51 | [dependencies.inherents] 52 | git = 'https://github.com/paritytech/substrate.git' 53 | package = 'substrate-inherents' 54 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 55 | 56 | [dependencies.network] 57 | git = 'https://github.com/paritytech/substrate.git' 58 | package = 'substrate-network' 59 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 60 | 61 | [dependencies.stateless-blockchain-runtime] 62 | path = 'runtime' 63 | 64 | [dependencies.primitives] 65 | git = 'https://github.com/paritytech/substrate.git' 66 | package = 'substrate-primitives' 67 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 68 | 69 | [dependencies.sr-io] 70 | git = 'https://github.com/paritytech/substrate.git' 71 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 72 | 73 | [dependencies.substrate-cli] 74 | git = 'https://github.com/paritytech/substrate.git' 75 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 76 | 77 | [dependencies.substrate-client] 78 | git = 'https://github.com/paritytech/substrate.git' 79 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 80 | 81 | [dependencies.substrate-executor] 82 | git = 'https://github.com/paritytech/substrate.git' 83 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 84 | 85 | [dependencies.substrate-service] 86 | git = 'https://github.com/paritytech/substrate.git' 87 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 88 | 89 | [dependencies.transaction-pool] 90 | git = 'https://github.com/paritytech/substrate.git' 91 | package = 'substrate-transaction-pool' 92 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 93 | 94 | [package] 95 | authors = ['andrewtam'] 96 | build = 'build.rs' 97 | edition = '2018' 98 | name = 'stateless-blockchain' 99 | version = '2.0.0' 100 | 101 | [[bin]] 102 | name = 'stateless-blockchain' 103 | path = 'src/main.rs' 104 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stateless Blockchain Experiment 2 | 3 | ## Disclaimer 4 | This repository is purely experimental and is not production quality. The design choices made in this project 5 | are impractical from both a security and usability standpoint. Additionally, the following code has not been checked for 6 | correctness, style, or efficiency. 7 | 8 | ## Testing the Project 9 | 10 | In the root directory: 11 | 12 | * To build the project, run `cargo build --release` 13 | * To start the chain, run `./target/release/stateless-blockchain --dev --execution-block-construction=Native` 14 | * If you need to reset the chain, run `./target/release/stateless-blockchain purge-chain --dev` 15 | * If you would like to execute tests, run `cargo test -p stateless-blockchain-runtime --release` 16 | 17 | In the accumulator-client directory (you must use nightly Rust): 18 | 19 | * Install `wasm-pack` if you don't already have it `cargo install wasm-pack` 20 | * Run `wasm-pack build` to compile the crate to WASM. 21 | * Inside "pkg", run `npm link` 22 | 23 | In the client directory: 24 | 25 | * Run `npm install` to install all necessary dependencies. 26 | * Run `npm link accumulator-client` to link to the generated WASM files. 27 | * Run `yarn start` to start a local server on localhost:8000. 28 | 29 | ## Overview and Background 30 | This project implements a UTXO-based stateless blockchain on Substrate using an RSA accumulator. A cryptographic accumulator is 31 | a short commitment to a set that includes small witnesses(a Merkle Tree is a type of accumulator). However, compared 32 | to Merkle Trees, accumulators have constant size inclusion proofs, can efficiently add and delete(dynamic), and can support 33 | both membership and non-membership proofs(universal). These features make accumulators a suitable fit for the stateless 34 | validation model. 35 | 36 | In this scheme, validating parties only need to track a single dynamic accumulator that represents a commitment to 37 | the state instead of actually having to store the entire state(such as the UTXO set in Bitcoin). Similarly, users only need to 38 | store their own UTXOs and membership proofs. However, since users must constantly watch for updates to the accumulator 39 | in order to update their proofs, a data service provider who handles witnesses on behalf of users is likely to be used in 40 | practice. This particular implementation includes batching and aggregation techniques from this paper: 41 | https://eprint.iacr.org/2018/1188.pdf. 42 | 43 | ## Details 44 | 45 | ### Setup 46 | The accumulator can be instantiated with a group of unknown order such as an RSA group. Any RSA number up to RSA-2048 47 | is supported(see the "accumulator" crate root). Accumulators can be instantiated with no trusted setup using class groups 48 | but remain mostly a research topic at the moment. 49 | 50 | ### Mechanics 51 | The workflow of a stateless blockchain is as follows: 52 | 53 | 1. A mechanism like Proof-of-Work or some other minting mechanism can be used to add new coins to the accumulator. 54 | 2. To spend a coin, users construct transactions that include their UTXO, the membership witness for that UTXO, and a 55 | valid transaction output. 56 | 3. At the finalization of a block, the miner/validator aggregates all of the inclusion proofs from the spent UTXOs and 57 | uses it to batch delete them from the accumulator. Similarly, the miner/validator batch adds the newly created UTXOs 58 | to the accumulator. At each step, the miner/validator outputs a proof of correctness that the deletion/addition was 59 | executed correctly. 60 | 61 | ### Structure 62 | The base of this project is a simple Substrate runtime. However, the core accumulator logic is stored in the "accumulator" 63 | crate and includes all of the integer specific functions, succinct proofs of exponentiation, and functions for creating 64 | or updating membership witnesses. 65 | 66 | The front-end for this project is stored in the "client" directory and implements a simple React page based on the 67 | Substrate Front-End template. 68 | 69 | "accumulator-client" is a wrapper crate for "accumulator" that uses wasm-bindgen to export several accumulator 70 | functions to WASM. This is necessary so that the front-end can interact with the accumulator. 71 | 72 | ## Limitations 73 | 74 | Since this is an experimental project, there exists numerous limitations. 75 | 76 | * Instead of using a Proof-of-Work module, this runtime allows users to trivially mint new coins. 77 | * The "UTXOs" that are created more closely resemble non-fungible tokens and are not explicitly value bearing(only contain 78 | identifier and owner). 79 | * Users can only submit one transaction per block and each transaction is limited to one input and one output. 80 | * Instead of aggregating inclusion proofs in memory, the "blockchain" must temporarily write the details of each incoming 81 | transaction to storage (but are erased at the end of the block). This is currently the only viable method for processing 82 | incoming extrinsics without modifying Substrate itself. 83 | * The scheme does not verify any signatures. Signatures could be aggregated within a block using BLS signatures. 84 | 85 | ## Miscellaneous 86 | 87 | The primary computational bottleneck occurs when a UTXO is hashed to a prime representation. Although this implementation 88 | uses a deterministic variant of the Miller-Rabin primality test that is "efficient", since we have used a small 89 | lambda value for testing, it can still be a limiting factor. Page 24 of https://eprint.iacr.org/2018/1188.pdf presents 90 | a modification to the inclusion proofs such that the verifier only needs to perform one round of primality checking 91 | instead of rederiving the hash representation(which involves about log(lambda) rounds). If transactions are taking too 92 | long to process, the block time can be modified by changing "MinimumPeriod" in the crate root of the runtime. 93 | 94 | With regard to semantics, it is important to note that this implementation is not *actually* a stateless blockchain since 95 | the runtime still utilizes the underlying storage trie of Substrate as well as multiple SRML components. However, the 96 | storage requirements are still fairly minimal. 97 | 98 | ## Vector Commitments 99 | 100 | The directory titled "vector-commitment" is a crate that implements both binary and general vector commitments using 101 | the RSA accumulator. A vector commitment is a primitive that allows for the commitment of an ordered list of elements 102 | where elements can only be opened at their corresponding indices(position binding). Particularly, one can use a vector 103 | commitment to commit to a binary vector by mapping the indices of elements that are 1 to primes and then batch adding 104 | them to an accumulator. A subset of those indices can be opened with constant sized openings by utilizing the batching 105 | functionality from the accumulator. This scheme can be further generalized to accumulate a key-value store using a large 106 | sparse vector and finally used to build an account-based stateless blockchain. 107 | 108 | ## Future Work 109 | 110 | Here is a non-comprehensive list of potential future steps. 111 | 112 | * Implementing more complex UTXO logic. 113 | * Integrating a Proof-of-Work module. 114 | * Creating a UX friendly front-end. 115 | * Creating a data service provider. 116 | * Investigating class groups. 117 | * Signature aggregation. 118 | * Explore accumulator unions and multiset accumulators. 119 | * Creating an account-based stateless blockchain runtime. 120 | 121 | ## Reading List 122 | 123 | * https://eprint.iacr.org/2018/1188.pdf 124 | * https://www.zeroknowledge.fm/88 125 | * https://blog.goodaudience.com/deep-dive-on-rsa-accumulators-230bc84144d9 126 | * http://diyhpl.us/wiki/transcripts/stanford-blockchain-conference/2019/accumulators/ 127 | * https://scalingbitcoin.org/transcript/tokyo2018/accumulators 128 | * https://ethresear.ch/t/accumulators-scalability-of-utxo-blockchains-and-data-availability/176 129 | * https://www.youtube.com/watch?v=tqqsbsAHJzs 130 | * https://ethresear.ch/t/rsa-accumulators-for-plasma-cash-history-reduction/3739 131 | * https://crypto.stackexchange.com/questions/66396/cryptographic-accumulators-accumulator-size-vs-max-number-of-set-members 132 | 133 | Note: This repository will no longer be maintained by its original owner after November 2019. -------------------------------------------------------------------------------- /accumulator-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ['andrewtam'] 3 | edition = '2018' 4 | name = 'accumulator-client' 5 | version = '1.0.0' 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [dependencies] 11 | uint = { version="0.8", default-features=false } 12 | wasm-bindgen = { version="0.2.51" } 13 | clear_on_drop = { version="0.2.3", features=["nightly"] } 14 | 15 | [dependencies.codec] 16 | default-features = false 17 | features = ['derive'] 18 | package = 'parity-scale-codec' 19 | version = '1.0.0' 20 | 21 | [dependencies.primitive-types] 22 | version = "0.5.0" 23 | default-features = false 24 | features = ["codec"] 25 | 26 | [dependencies.accumulator] 27 | path = "../accumulator" 28 | 29 | [workspace] 30 | -------------------------------------------------------------------------------- /accumulator-client/src/lib.rs: -------------------------------------------------------------------------------- 1 | use accumulator::*; 2 | use wasm_bindgen::prelude::*; 3 | use codec::{Encode, Decode}; 4 | use primitive_types::H256; 5 | 6 | #[wasm_bindgen] 7 | #[derive(Encode, Decode)] 8 | pub struct UTXO { 9 | pub_key: H256, 10 | id: u64, 11 | } 12 | 13 | #[wasm_bindgen] 14 | pub fn create_utxo(pub_key: &[u8], id: u64) -> UTXO { 15 | let result = UTXO { 16 | pub_key: H256::from_slice(pub_key), 17 | id, 18 | }; 19 | return result; 20 | } 21 | 22 | #[wasm_bindgen] 23 | pub fn get_utxo_elem(pub_key: &[u8], id: u64) -> Vec { 24 | return create_utxo(pub_key, id).encode(); 25 | } 26 | 27 | #[wasm_bindgen] 28 | pub fn hash_to_prime(elem: &[u8]) -> Vec { 29 | let mut result: [u8; 256] = [0; 256]; // Change this constant 30 | subroutines::hash_to_prime(elem).to_little_endian(&mut result); 31 | return result.to_vec(); 32 | } 33 | 34 | #[wasm_bindgen] 35 | pub fn get_witness(old_state: &[u8], agg: &[u8], elem: &[u8]) -> Vec { 36 | let mut result: [u8; 256] = [0; 256]; 37 | witnesses::mem_wit_create(U2048::from_little_endian(old_state), U2048::from_little_endian(agg), 38 | U2048::from_little_endian(elem)).unwrap().to_little_endian(&mut result); 39 | return result.to_vec(); 40 | } 41 | 42 | #[wasm_bindgen] 43 | pub fn update_witness(elem: &[u8], witness: &[u8], new_state: &[u8], added: &[u8], deleted: &[u8]) -> Vec { 44 | let mut result: [u8; 256] = [0; 256]; 45 | witnesses::update_mem_wit(U2048::from_little_endian(elem), U2048::from_little_endian(witness), U2048::from_little_endian(new_state), 46 | U2048::from_little_endian(added), U2048::from_little_endian(deleted)).to_little_endian(&mut result); 47 | return result.to_vec(); 48 | } 49 | 50 | #[cfg(test)] 51 | mod tests { 52 | use super::*; 53 | use accumulator::*; 54 | 55 | #[test] 56 | fn test_hash_to_prime() { 57 | let utxo = UTXO { 58 | pub_key: H256::from_slice(hex::decode("d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d").unwrap()), 59 | id: 0, 60 | }; 61 | assert_eq!(subroutines::hash_to_prime(&utxo.encode()), U2048::from_dec_str("2882671871935824533").unwrap()); 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /accumulator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ['andrewtam'] 3 | edition = '2018' 4 | name = 'accumulator' 5 | version = '1.0.0' 6 | 7 | [dependencies] 8 | uint = { version="0.8", default-features=false } 9 | 10 | [dependencies.codec] 11 | default-features = false 12 | features = ['derive'] 13 | package = 'parity-scale-codec' 14 | version = '1.0.0' 15 | 16 | [dependencies.rstd] 17 | default_features = false 18 | git = 'https://github.com/paritytech/substrate.git' 19 | package = 'sr-std' 20 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 21 | 22 | [dependencies.runtime-io] 23 | default_features = false 24 | git = 'https://github.com/paritytech/substrate.git' 25 | package = 'sr-io' 26 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 27 | 28 | [dependencies.serde] 29 | features = ['derive'] 30 | optional = true 31 | version = '1.0.101' 32 | 33 | [features] 34 | default = ['std'] 35 | no_std = [] 36 | std = [ 37 | 'codec/std', 38 | 'rstd/std', 39 | 'runtime-io/std', 40 | 'uint/std', 41 | 'serde', 42 | ] 43 | -------------------------------------------------------------------------------- /accumulator/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | use codec::{Encode, Decode}; 4 | use rstd::vec::Vec; 5 | extern crate alloc; 6 | use alloc::borrow::ToOwned; 7 | #[macro_use] 8 | extern crate uint; 9 | 10 | pub mod subroutines; 11 | pub mod proofs; 12 | pub mod witnesses; 13 | 14 | /// Construct BigInt type. 15 | construct_uint! { 16 | #[derive(Encode, Decode)] 17 | pub struct U2048(32); 18 | } 19 | 20 | /// Defines the RSA group. Arbitrary set at MODULUS = 13 for testing. 21 | /// Example (insecure) modulus -> RSA 100: "1522605027922533360535618378132637429718068114961380688657908494580122963258952897654000350692006139" 22 | pub const MODULUS: &str = "13"; 23 | 24 | /// Security parameter that represents the size of elements added to the accumulator. 25 | pub const LAMBDA: u32 = u32::max_value(); 26 | 27 | /// A witness can either be a membership witness or a non-membership witness. 28 | #[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, Debug)] 29 | pub enum Witness { 30 | MemWit(U2048), 31 | NonMemWit((U2048,bool, U2048)), 32 | } 33 | 34 | // Cannot derive the Default trait for Enums so this is the only option 35 | impl Default for Witness { 36 | fn default() -> Self { 37 | Witness::MemWit(U2048::from(0)) 38 | } 39 | } 40 | 41 | /// A Bezout coefficient pair. This is a temporary workaround due to the lack of support for 42 | /// signed BigInts(coefficients can be negative). 43 | #[derive(Clone, Copy, PartialEq, Debug)] 44 | pub struct BezoutPair { 45 | coefficient_a: U2048, 46 | coefficient_b: U2048, 47 | sign_a: bool, // True indicates negative and false indicates positive 48 | sign_b: bool, 49 | } 50 | 51 | /// Add a single element to an accumulator. 52 | pub fn add(state: U2048, elem: U2048) -> U2048 { 53 | return subroutines::mod_exp(state, elem, U2048::from_dec_str(MODULUS).unwrap()); 54 | } 55 | 56 | /// Delete an element from the accumulator given a membership proof. 57 | pub fn delete(state: U2048, elem: U2048, proof: U2048) -> Option { 58 | if subroutines::mod_exp(proof, elem, U2048::from_dec_str(MODULUS).unwrap()) == state { 59 | return Some(proof); 60 | } 61 | return None; 62 | } 63 | 64 | /// Aggregates a set of accumulator elements + witnesses and batch deletes them from the accumulator. 65 | /// Returns the state after deletion, the product of the deleted elements, and a proof of exponentiation. 66 | pub fn batch_delete(state: U2048, elems: &Vec<(U2048, U2048)>) -> (U2048, U2048, U2048) { 67 | let (mut x_agg, mut new_state) = elems[0]; 68 | for i in 1..elems.len() { 69 | let (x, witness) = elems[i]; 70 | new_state = subroutines::shamir_trick(new_state, witness, x_agg, x).unwrap(); 71 | x_agg *= x; 72 | } 73 | let proof = proofs::poe(new_state, x_agg, state); 74 | return (new_state, x_agg, proof); 75 | } 76 | 77 | /// Aggregates a set of accumulator elements + witnesses and batch adds them to the accumulator. 78 | /// Returns the state after addition, the product of the added elements, and a proof of exponentiation. 79 | pub fn batch_add(state: U2048, elems: &Vec) -> (U2048, U2048, U2048) { 80 | let mut x_agg = U2048::from(1); 81 | for i in 0..elems.len() { 82 | x_agg *= elems[i]; 83 | } 84 | 85 | let new_state = subroutines::mod_exp(state, x_agg, U2048::from_dec_str(MODULUS).unwrap()); 86 | let proof = proofs::poe(state, x_agg, new_state); 87 | return (new_state, x_agg, proof); 88 | } 89 | -------------------------------------------------------------------------------- /accumulator/src/proofs.rs: -------------------------------------------------------------------------------- 1 | /// Succinct Non-interactive Proofs of Exponentiation. 2 | 3 | use runtime_io::blake2_256; 4 | use codec::{Encode}; 5 | use crate::subroutines; 6 | use super::U2048; 7 | 8 | /// Generates proof of exponentiation that u^x = w (based on Wesolowski). Protocol is only useful 9 | /// if the verifier can compute the residue r = x mod l faster than computing u^x. 10 | /// To investigate: Security parameter should be larger than that of accumulator elements. 11 | pub fn poe(u: U2048, x: U2048, w: U2048) -> U2048 { 12 | let l = subroutines::hash_to_prime(&(u, x, w).encode()); 13 | let q = x / l; 14 | return subroutines::mod_exp(u, q, U2048::from_dec_str(super::MODULUS).unwrap()); 15 | } 16 | 17 | /// Verifies proof of exponentiation. 18 | pub fn verify_poe(u: U2048, x: U2048, w: U2048, Q: U2048) -> bool { 19 | let l = subroutines::hash_to_prime(&(u, x, w).encode()); 20 | let r = x % l; 21 | let lhs = subroutines::mul_mod(subroutines::mod_exp(Q, l, U2048::from_dec_str(super::MODULUS).unwrap()), subroutines::mod_exp(u, r, U2048::from_dec_str(super::MODULUS).unwrap()), 22 | U2048::from_dec_str(super::MODULUS).unwrap()); 23 | return lhs == w; 24 | } 25 | 26 | /// Generates proof of knowledge of exponentiation that u^x = w. We will assume that the generator 27 | /// g = 2 is a group element of unknown order. 28 | /// To investigate: Security parameter should be larger than that of accumulator elements. 29 | pub fn poke(u: U2048, x: U2048, w: U2048) -> (U2048, U2048, U2048) { 30 | let z = subroutines::mod_exp(U2048::from(2), x, U2048::from_dec_str(super::MODULUS).unwrap()); 31 | let l = subroutines::hash_to_prime(&(u, w, z).encode()); 32 | let alpha = U2048::from_little_endian(&blake2_256(&(u, w, z, l).encode())); 33 | let q = x / l; 34 | let r = x % l; 35 | let Q = subroutines::mod_exp(subroutines::mul_mod(u, subroutines::mod_exp(U2048::from(2), alpha, U2048::from_dec_str(super::MODULUS).unwrap()), 36 | U2048::from_dec_str(super::MODULUS).unwrap()), q, U2048::from_dec_str(super::MODULUS).unwrap()); 37 | let pi = (z, Q, r); 38 | return pi; 39 | } 40 | 41 | /// Verifies proof of knowledge of exponentiation. 42 | pub fn verify_poke(u: U2048, w: U2048, z: U2048, Q: U2048, r: U2048) -> bool { 43 | let l = subroutines::hash_to_prime(&(u, w, z).encode()); 44 | let alpha = U2048::from_little_endian(&blake2_256(&(u, w, z, l).encode())); 45 | let lhs = subroutines::mul_mod(subroutines::mod_exp(Q, l, U2048::from_dec_str(super::MODULUS).unwrap()), 46 | subroutines::mod_exp(subroutines::mul_mod(u, subroutines::mod_exp(U2048::from(2), alpha, U2048::from_dec_str(super::MODULUS).unwrap()), 47 | U2048::from_dec_str(super::MODULUS).unwrap()), r, U2048::from_dec_str(super::MODULUS).unwrap()), U2048::from_dec_str(super::MODULUS).unwrap()); 48 | let rhs = subroutines::mul_mod(w, subroutines::mod_exp(z, alpha, U2048::from_dec_str(super::MODULUS).unwrap()), U2048::from_dec_str(super::MODULUS).unwrap()); 49 | return lhs == rhs; 50 | } 51 | 52 | #[cfg(test)] 53 | mod tests { 54 | use super::*; 55 | 56 | #[test] 57 | fn test_poe() { 58 | let mut proof = poe(U2048::from(2), U2048::from(6), U2048::from(12)); 59 | assert_eq!(verify_poe(U2048::from(2), U2048::from(6), U2048::from(12), proof), true); 60 | 61 | proof = poe(U2048::from(121314), U2048::from(14123), U2048::from(6)); 62 | assert_eq!(verify_poe(U2048::from(121314), U2048::from(14123), U2048::from(6), proof), true); 63 | 64 | // Fake proof 65 | assert_eq!(verify_poe(U2048::from(2), U2048::from(6), U2048::from(12), U2048::from(3)), false); 66 | assert_eq!(verify_poe(U2048::from(4), U2048::from(12), U2048::from(7), U2048::from(1)), false); 67 | } 68 | 69 | #[test] 70 | fn test_poke() { 71 | let (z, Q, r) = poke(U2048::from(2), U2048::from(6), U2048::from(12)); 72 | assert_eq!(verify_poke(U2048::from(2), U2048::from(12), z, Q, r), true); 73 | 74 | let (z, Q, r) = poke(U2048::from(121314), U2048::from(14123), U2048::from(6)); 75 | assert_eq!(verify_poke(U2048::from(121314), U2048::from(6), z, Q, r), true); 76 | 77 | // Fake proof 78 | assert_eq!(verify_poke(U2048::from(121314), U2048::from(7), z, Q, r), false); 79 | assert_eq!(verify_poke(U2048::from(2), U2048::from(12), U2048::from(4), U2048::from(1), U2048::from(2)), false); 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /accumulator/src/subroutines.rs: -------------------------------------------------------------------------------- 1 | /// Integer Subroutines for Accumulator Functions. 2 | 3 | use runtime_io::blake2_256; 4 | use rstd::prelude::Vec; 5 | use super::U2048; 6 | use crate::BezoutPair; 7 | 8 | /// Implements fast modular exponentiation. Algorithm inspired by https://github.com/pwoolcoc/mod_exp-rs/blob/master/src/lib.rs 9 | /// NOTE: Possible overflow error occurs when size of result exceeds U2048. 10 | pub fn mod_exp(mut base: U2048, mut exp: U2048, modulus: U2048) -> U2048 { 11 | let mut result: U2048 = U2048::from(1); 12 | base = base % modulus; 13 | while exp > U2048::from(0) { 14 | if exp % U2048::from(2) == U2048::from(1) { 15 | result = mul_mod(result, base, modulus); 16 | } 17 | 18 | if exp == U2048::from(1) { 19 | return result; 20 | } 21 | 22 | exp = exp >> U2048::from(1); 23 | base = mul_mod(base, base, modulus); 24 | } 25 | return result; 26 | } 27 | 28 | /// Defines the multiplication operation for the group. Idea courtesy of: 29 | /// https://www.geeksforgeeks.org/how-to-avoid-overflow-in-modular-multiplication/ 30 | pub fn mul_mod(mut a: U2048, mut b: U2048, modulus: U2048) -> U2048 { 31 | let mut result = U2048::from(0); 32 | a = a % modulus; 33 | while b > U2048::from(0) { 34 | if b % U2048::from(2) == U2048::from(1) { 35 | result = (result + a) % modulus; 36 | } 37 | 38 | a = (a * U2048::from(2)) % modulus; 39 | b /= U2048::from(2); 40 | } 41 | return result % modulus; 42 | } 43 | 44 | /// Given the xth root of g and yth root of g, finds the xyth root. If the roots are invalid or 45 | /// x and y are not coprime, None is returned. Otherwise, the function performs relevant modular 46 | /// inverse operations on the Bezout coefficients and finds the xyth root. 47 | pub fn shamir_trick(mut xth_root: U2048, mut yth_root: U2048, x: U2048, y: U2048) -> Option { 48 | // Check if the inputs are valid. 49 | if mod_exp(xth_root, x, U2048::from_dec_str(super::MODULUS).unwrap()) 50 | != mod_exp(yth_root, y, U2048::from_dec_str(super::MODULUS).unwrap()) { 51 | return None; 52 | } 53 | 54 | match bezout(x, y) { 55 | None => { 56 | return None; 57 | }, 58 | Some(coefficients) => { 59 | // Receive coefficient 60 | let pair = coefficients; 61 | 62 | // Calculate relevant modular inverses to allow for exponentiation later on. 63 | if pair.sign_b { 64 | xth_root = mod_inverse(xth_root); 65 | } 66 | 67 | if pair.sign_a { 68 | yth_root = mod_inverse(yth_root); 69 | } 70 | 71 | let combined_root: U2048 = (mod_exp(xth_root, U2048::from(pair.coefficient_b), U2048::from_dec_str(super::MODULUS).unwrap()) 72 | * mod_exp(yth_root, U2048::from(pair.coefficient_a), U2048::from_dec_str(super::MODULUS).unwrap())) % U2048::from_dec_str(super::MODULUS).unwrap(); 73 | return Some(combined_root); 74 | }, 75 | } 76 | } 77 | 78 | /// Computes the modular multiplicative inverse. 79 | /// NOTE: Does not check if gcd != 1(none exists if so). 80 | pub fn mod_inverse(elem: U2048) -> U2048 { 81 | let (_, pair) = extended_gcd(elem, U2048::from_dec_str(super::MODULUS).unwrap()); 82 | 83 | // Accommodate for negative x coefficient 84 | if pair.sign_a { 85 | // Since we're assuming that the U2048::from(super::MODULUS) will always be larger than than coefficient in 86 | // absolute value, we simply subtract x from the U2048::from(super::MODULUS) to get a positive value mod N. 87 | let pos_a = U2048::from_dec_str(super::MODULUS).unwrap() - pair.coefficient_a; 88 | return pos_a % U2048::from_dec_str(super::MODULUS).unwrap(); 89 | } 90 | return U2048::from(pair.coefficient_a) % U2048::from_dec_str(super::MODULUS).unwrap(); 91 | } 92 | 93 | /// Returns Bezout coefficients. Acts as a wrapper for extended_gcd. 94 | pub fn bezout(a: U2048, b: U2048) -> Option { 95 | let (gcd, pair) = extended_gcd(a, b); 96 | // Check if a and b are coprime 97 | if gcd != U2048::from(1) { 98 | return None; 99 | } 100 | else { 101 | return Some(pair); 102 | } 103 | } 104 | 105 | /// Implements the Extended Euclidean Algorithm (https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm). 106 | /// IMPORTANT NOTE: Instead of representing the coefficients as signed integers, I have represented 107 | /// them as (|a|, sign of a) and (|b|, sign of b). This is because the current project lacks 108 | /// support for signed BigInts. 109 | pub fn extended_gcd(a: U2048, b: U2048) -> (U2048, BezoutPair) { 110 | let (mut s, mut old_s): (U2048, U2048) = (U2048::from(0), U2048::from(1)); 111 | let (mut t, mut old_t): (U2048, U2048) = (U2048::from(1), U2048::from(0)); 112 | let (mut r, mut old_r): (U2048, U2048) = (b, a); 113 | 114 | let (mut prev_sign_s, mut prev_sign_t): (bool, bool) = (false, false); 115 | let (mut sign_s, mut sign_t): (bool, bool) = (false, false); 116 | 117 | while r != U2048::from(0) { 118 | let quotient = old_r/r; 119 | let new_r = old_r - U2048::from(quotient) * r; 120 | old_r = r; 121 | r = new_r; 122 | 123 | // Hacky workaround to track the coefficient "a" as (|a|, sign of a) 124 | let mut new_s = quotient * s; 125 | if prev_sign_s == sign_s && new_s > old_s { 126 | new_s = new_s - old_s; 127 | if !sign_s { sign_s = true; } 128 | else { sign_s = false; } 129 | } 130 | else if prev_sign_s != sign_s { 131 | new_s = old_s + new_s; 132 | prev_sign_s = sign_s; 133 | sign_s = !sign_s; 134 | } 135 | else { new_s = old_s - new_s; } 136 | old_s = s; 137 | s = new_s; 138 | 139 | // Hacky workaround to track the coefficient "b" as (|b|, sign of b) 140 | let mut new_t = quotient * t; 141 | if prev_sign_t == sign_t && new_t > old_t { 142 | new_t = new_t - old_t; 143 | if !sign_t { sign_t = true; } 144 | else { sign_t = false; } 145 | } 146 | else if prev_sign_t != sign_t { 147 | new_t = old_t + new_t; 148 | prev_sign_t = sign_t; 149 | sign_t = !sign_t; 150 | } 151 | else { new_t = old_t - new_t; } 152 | old_t = t; 153 | t = new_t; 154 | } 155 | 156 | let pair = BezoutPair { 157 | coefficient_a: old_s, 158 | coefficient_b: old_t, 159 | sign_a: prev_sign_s, 160 | sign_b: prev_sign_t, 161 | }; 162 | 163 | return (old_r, pair); 164 | } 165 | 166 | /// Continuously hashes the input until the result is prime. Assumes input values are transcoded in 167 | /// little endian(uses parity-scale-codec). 168 | /// Consideration: Currently unclear about the impact of Lambda on the security of the scheme. 169 | pub fn hash_to_prime(elem: &[u8]) -> U2048 { 170 | let mut hash = blake2_256(elem); 171 | let mut result = U2048::from_little_endian(&hash) % U2048::from(super::LAMBDA); 172 | 173 | // While the resulting hash is not a prime, keep trying 174 | while !miller_rabin(result) { 175 | hash = blake2_256(&hash); 176 | result = U2048::from_little_endian(&hash) % U2048::from(super::LAMBDA); 177 | } 178 | 179 | return result; 180 | } 181 | 182 | /// Implements a deterministic variant of the Miller-Rabin primality test for u64/u32 integers based 183 | /// on the algorithm from the following link: https://en.wikipedia.org/wiki/Miller–Rabin_primality_test 184 | /// Complexity of the algorithm is O((log n)^4) in soft-O notation. 185 | pub fn miller_rabin(n: U2048) -> bool { 186 | // Find r and d such that 2^r * d + 1 = n 187 | let r = (n-U2048::from(1)).trailing_zeros(); 188 | let d = (n-U2048::from(1)) >> U2048::from(r); 189 | 190 | // See https://stackoverflow.com/questions/7594307/simple-deterministic-primality-testing-for-small-numbers 191 | //let bases = [2,3,5,7,11,13,17]; // Deterministic for 64 bit integers 192 | let bases = [2, 7, 61]; // Deterministic for 32 bit integers 193 | 194 | 'outer: for &a in bases.iter() { 195 | // Annoying edge case to make sure a is within [2, n-2] for small n 196 | if n-U2048::from(2) < U2048::from(a) { break; } 197 | 198 | let mut x = mod_exp(U2048::from(a), d, n); 199 | 200 | if x == U2048::from(1) || x == (n-U2048::from(1)) { 201 | continue; 202 | } 203 | for _ in 1..r { 204 | x = mod_exp(x, U2048::from(2), n); 205 | if x == (n-U2048::from(1)) { 206 | continue 'outer; 207 | } 208 | } 209 | return false; 210 | } 211 | return true; 212 | } 213 | 214 | /// Given an element g and a set of elements x, computes the xith root of g^x for each element 215 | /// in the set. Runs in O(n log(n)). 216 | pub fn root_factor(g: U2048, elems: &[U2048]) -> Vec { 217 | if elems.len() == 1 { 218 | let mut ret = Vec::new(); 219 | ret.push(g); 220 | return ret; 221 | } 222 | 223 | let n_prime = elems.len()/2; 224 | 225 | let mut g_left = g; 226 | for i in 0..n_prime { 227 | g_left = mod_exp(g_left, elems[i], U2048::from_dec_str(super::MODULUS).unwrap()); 228 | } 229 | 230 | let mut g_right = g; 231 | for i in n_prime..elems.len() { 232 | g_right = mod_exp(g_right, elems[i], U2048::from_dec_str(super::MODULUS).unwrap()); 233 | } 234 | 235 | let mut left = root_factor(g_right, &elems[0..n_prime]); 236 | let mut right = root_factor(g_left, &elems[n_prime..]); 237 | left.append(&mut right); 238 | return left; 239 | } 240 | 241 | /// Short helper function that calculates the product of elements in the vector. 242 | pub fn prime_product(elems: &[U2048]) -> U2048 { 243 | let mut result: U2048 = U2048::from(1); 244 | for &elem in elems.iter() { 245 | result *= elem; 246 | } 247 | return result; 248 | } 249 | 250 | #[cfg(test)] 251 | mod tests { 252 | use super::*; 253 | use crate::MODULUS; 254 | 255 | #[test] 256 | fn test_mul_mod() { 257 | assert_eq!(mul_mod(U2048::from(121), U2048::from(12314), U2048::from_dec_str(MODULUS).unwrap()), 258 | U2048::from(12)); 259 | assert_eq!(mul_mod(U2048::from(128), U2048::from(23), U2048::from(75)), 260 | U2048::from(19)); 261 | } 262 | 263 | #[test] 264 | fn test_mod_exp() { 265 | assert_eq!(mod_exp(U2048::from(2), U2048::from(7), U2048::from_dec_str(MODULUS).unwrap()), U2048::from(11)); 266 | assert_eq!(mod_exp(U2048::from(7), U2048::from(15), U2048::from_dec_str(MODULUS).unwrap()), U2048::from(5)); 267 | } 268 | 269 | #[test] 270 | fn test_extended_gcd() { 271 | assert_eq!(extended_gcd(U2048::from(180), U2048::from(150)), (U2048::from(30), 272 | BezoutPair {coefficient_a: U2048::from(1), coefficient_b: U2048::from(1), sign_a: false, sign_b: true})); 273 | assert_eq!(extended_gcd(U2048::from(13), U2048::from(17)), (U2048::from(1), 274 | BezoutPair {coefficient_a: U2048::from(4), coefficient_b: U2048::from(3), sign_a: false, sign_b: true})); 275 | } 276 | 277 | #[test] 278 | fn test_bezout() { 279 | assert_eq!(bezout(U2048::from(4), U2048::from(10)), None); 280 | assert_eq!(bezout(U2048::from(3434), U2048::from(2423)), 281 | Some (BezoutPair {coefficient_a: U2048::from(997), coefficient_b: U2048::from(1413), sign_a: true, sign_b: false})); 282 | } 283 | 284 | #[test] 285 | fn test_shamir_trick() { 286 | assert_eq!(shamir_trick(U2048::from(11), U2048::from(6), U2048::from(7), U2048::from(5)), Some(U2048::from(7))); 287 | assert_eq!(shamir_trick(U2048::from(11), U2048::from(7), U2048::from(7), U2048::from(11),), Some(U2048::from(6))); 288 | assert_eq!(shamir_trick(U2048::from(6), U2048::from(7), U2048::from(5), U2048::from(11)), Some(U2048::from(11))); 289 | assert_eq!(shamir_trick(U2048::from(12), U2048::from(7), U2048::from(7), U2048::from(11)), None); 290 | } 291 | 292 | #[test] 293 | fn test_mod_inverse() { 294 | assert_eq!(mod_inverse(U2048::from(9)), U2048::from(3)); 295 | assert_eq!(mod_inverse(U2048::from(6)), U2048::from(11)); 296 | } 297 | 298 | #[test] 299 | fn test_miller_rabin() { 300 | assert_eq!(miller_rabin(U2048::from(5)), true); 301 | assert_eq!(miller_rabin(U2048::from(7)), true); 302 | assert_eq!(miller_rabin(U2048::from(241)), true); 303 | assert_eq!(miller_rabin(U2048::from(7919)), true); 304 | assert_eq!(miller_rabin(U2048::from(48131)), true); 305 | assert_eq!(miller_rabin(U2048::from(76463)), true); 306 | assert_eq!(miller_rabin(U2048::from(4222234741u64)), true); 307 | assert_eq!(miller_rabin(U2048::from(187278659180417234321u128)), true); 308 | 309 | assert_eq!(miller_rabin(U2048::from(21)), false); 310 | assert_eq!(miller_rabin(U2048::from(87)), false); 311 | assert_eq!(miller_rabin(U2048::from(155)), false); 312 | assert_eq!(miller_rabin(U2048::from(9167)), false); 313 | assert_eq!(miller_rabin(U2048::from(102398)), false); 314 | assert_eq!(miller_rabin(U2048::from(801435)), false); 315 | assert_eq!(miller_rabin(U2048::from(51456119958243u128)), false); 316 | } 317 | 318 | #[test] 319 | fn test_hash_to_prime() { 320 | //assert_eq!(hash_to_prime(&[7, 10]), U2048::from(...)); 321 | // Key values checked: 0, 1, 2 322 | } 323 | 324 | #[test] 325 | fn test_root_factor() { 326 | assert_eq!(root_factor(U2048::from(2), &vec![U2048::from(3), U2048::from(5), U2048::from(7), U2048::from(11)]), 327 | vec![U2048::from(2), U2048::from(8), U2048::from(5), U2048::from(5)]); 328 | } 329 | 330 | #[test] 331 | fn test_prime_product() { 332 | let elems = vec![U2048::from(2), U2048::from(3), U2048::from(4)]; 333 | assert_eq!(prime_product(&elems), U2048::from(24)); 334 | } 335 | 336 | 337 | } -------------------------------------------------------------------------------- /accumulator/src/witnesses.rs: -------------------------------------------------------------------------------- 1 | /// Membership Witness Management 2 | 3 | use crate::subroutines; 4 | use crate::proofs; 5 | use rstd::prelude::Vec; 6 | use super::U2048; 7 | 8 | /// Given an old state, the product of a set of elements that have been added, and a single element from that 9 | /// set, returns the witness for that element. 10 | /// NOTE: "old_state" represents the state *before* the elements are added. 11 | /// This function will likely be used by an online user. 12 | pub fn mem_wit_create(old_state: U2048, agg: U2048, elem: U2048) -> Option { 13 | if agg % elem != U2048::from(0) { 14 | return None; 15 | } 16 | let quotient = agg / elem; 17 | return Some(subroutines::mod_exp(old_state, quotient, U2048::from_dec_str(super::MODULUS).unwrap())); 18 | } 19 | 20 | /// Verify the witness of an element. 21 | pub fn verify_mem_wit(state: U2048, witness: U2048, elem: U2048) -> bool { 22 | let result = subroutines::mod_exp(witness, elem, U2048::from_dec_str(super::MODULUS).unwrap()); 23 | return result == state; 24 | } 25 | 26 | /// Updates a membership witness based on untracked additions and deletions. Algorithm is based on 27 | /// section 3.2 of the paper titled "Dynamic Accumulators and Applications to Efficient Revocation of 28 | /// Anonymous Credentials". Note that "additions" represent the product of the added elements 29 | /// and "deletions" represents the product of the deleted elements. 30 | /// NOTE: Does not do any error checking on unwrap. 31 | pub fn update_mem_wit(elem: U2048, mut witness: U2048, new_state: U2048, additions: U2048, deletions: U2048) -> U2048 { 32 | // Handle added elems 33 | witness = subroutines::mod_exp(witness, additions, U2048::from_dec_str(super::MODULUS).unwrap()); 34 | 35 | // Handle deleted elems 36 | witness = subroutines::shamir_trick(witness, new_state, elem, deletions).unwrap(); 37 | return witness; 38 | } 39 | 40 | 41 | /// Takes two elements + membership witnesses and returns the aggregated witness and aggregated proof. 42 | /// NOTE: Does very little error checking (Ex: Does not do any error checking on unwrap). 43 | pub fn agg_mem_wit(state: U2048, witness_x: U2048, witness_y: U2048, x: U2048, y: U2048) -> (U2048, U2048) { 44 | let aggregated = subroutines::shamir_trick(witness_x, witness_y, x, y).unwrap(); 45 | let proof = proofs::poe(aggregated, subroutines::mul_mod(x, y, U2048::from_dec_str(super::MODULUS).unwrap()), state); 46 | return (aggregated, proof); 47 | } 48 | 49 | /// Verifies that a membership witness + proof for a set of accumulator elements are valid. Acts as a 50 | /// wrapper for the proof of exponentiation verifier. 51 | pub fn verify_agg_mem_wit(state: U2048, agg_elems: U2048, witness: U2048, proof: U2048) -> bool { 52 | return proofs::verify_poe(witness, agg_elems, state, proof); 53 | } 54 | 55 | /// Creates individual membership witnesses. Acts as a wrapper for the RootFactor subroutine. 56 | /// NOTE: "old_state" represents the state *before* the elements are added. 57 | /// This function will most likely be used by a service provider. 58 | pub fn create_all_mem_wit(old_state: U2048, new_elems: &[U2048]) -> Vec { 59 | return subroutines::root_factor(old_state, new_elems); 60 | } 61 | 62 | /// Below contains all of the non-membership witness functions required for vector commitments. 63 | /// It is important to note that these functions allow one to specify a reference generator(typically 64 | /// as "old_state") for the non-membership proof since the accumulated set may be too large to be 65 | /// inputted. 66 | 67 | /// Creates a non-membership witness relative to some previous state. The current state should equal "old_state" 68 | /// raised to the "agg_elems" power(represents product of added elements). The second value of the 69 | /// tuple is the sign of the first value since the Bezout coefficient may be negative. 70 | /// NOTE: Function assumes that "elem" is not contained in "agg_elems" 71 | pub fn non_mem_wit_create(mut old_state: U2048, agg_elems: U2048, elem: U2048) -> (U2048, bool, U2048) { 72 | let pair = subroutines::bezout(agg_elems, elem).unwrap(); 73 | 74 | if pair.sign_b { 75 | old_state = subroutines::mod_inverse(old_state); 76 | } 77 | 78 | let B = subroutines::mod_exp(old_state, U2048::from(pair.coefficient_b), U2048::from_dec_str(super::MODULUS).unwrap()); 79 | return (pair.coefficient_a, pair.sign_a, B); 80 | } 81 | 82 | /// Verifies a non-membership witness. "state" represents the current state. 83 | pub fn verify_non_mem_wit(old_state: U2048, mut state: U2048, witness: (U2048, bool, U2048), elem: U2048) -> bool { 84 | let (a, sign_a, B) = witness; 85 | 86 | if sign_a { 87 | state = subroutines::mod_inverse(state); 88 | } 89 | 90 | let exp_1 = subroutines::mod_exp(state, U2048::from(a), U2048::from_dec_str(super::MODULUS).unwrap()); 91 | let exp_2 = subroutines::mod_exp(B, elem, U2048::from_dec_str(super::MODULUS).unwrap()); 92 | 93 | return subroutines::mul_mod(exp_1, exp_2, U2048::from_dec_str(super::MODULUS).unwrap()) == old_state; 94 | } 95 | 96 | pub fn update_non_mem_wit() {} 97 | 98 | /// OPTIONAL FUNCTION. 99 | /// Given the current state, the previous state, the product of the added elements, and a subset of 100 | /// those elements, creates a witness for thoise elements. 101 | pub fn mem_wit_create_star(cur_state: U2048, old_state: U2048, agg: U2048, new_elems: Vec) -> (U2048, U2048) { 102 | let product = subroutines::prime_product(&new_elems); 103 | let witness = mem_wit_create(old_state, agg, product).unwrap(); 104 | let proof = proofs::poe(witness, product, cur_state); 105 | return (witness, proof); 106 | } 107 | 108 | #[cfg(test)] 109 | mod tests { 110 | use super::*; 111 | use crate::batch_add; 112 | 113 | #[test] 114 | fn test_mem_wit_create() { 115 | assert_eq!(mem_wit_create(U2048::from(2), U2048::from(1155), U2048::from(3)).unwrap(), U2048::from(2)); 116 | assert_eq!(mem_wit_create(U2048::from(2), U2048::from(1155), U2048::from(5)).unwrap(), U2048::from(8)); 117 | assert_eq!(mem_wit_create(U2048::from(2), U2048::from(1155), U2048::from(7)).unwrap(), U2048::from(5)); 118 | assert_eq!(mem_wit_create(U2048::from(2), U2048::from(1155),U2048::from(11)).unwrap(), U2048::from(5)); 119 | assert_eq!(mem_wit_create(U2048::from(2), U2048::from(1155),U2048::from(4)).is_none(), true); 120 | } 121 | 122 | #[test] 123 | fn test_agg_mem_wit() { 124 | let (aggregate, proof) = agg_mem_wit(U2048::from(8), U2048::from(6), U2048::from(8),U2048::from(3), U2048::from(5)); 125 | assert_eq!(aggregate, U2048::from(2)); 126 | assert_eq!(verify_agg_mem_wit(U2048::from(8), U2048::from(15), aggregate, proof), true); 127 | } 128 | 129 | #[test] 130 | fn test_verify_agg_mem_wit() { 131 | let proof = proofs::poe(U2048::from(2), U2048::from(12123), U2048::from(8)); 132 | assert_eq!(verify_agg_mem_wit(U2048::from(8), U2048::from(12123), U2048::from(2), proof), true); 133 | assert_eq!(verify_agg_mem_wit(U2048::from(7), U2048::from(12123), U2048::from(2), proof), false); 134 | } 135 | 136 | #[test] 137 | fn test_update_mem_wit() { 138 | let deletions = U2048::from(15); 139 | let additions = U2048::from(77); 140 | 141 | let elem = U2048::from(12131); 142 | let witness = U2048::from(8); 143 | let new_state = U2048::from(11); 144 | 145 | assert_eq!(update_mem_wit(elem, witness, new_state, additions, deletions), U2048::from(6)); 146 | } 147 | 148 | #[test] 149 | fn test_create_all_mem_wit() { 150 | assert_eq!(create_all_mem_wit(U2048::from(2), &vec![U2048::from(3), U2048::from(5), U2048::from(7), U2048::from(11)]), 151 | vec![U2048::from(2), U2048::from(8), U2048::from(5), U2048::from(5)]); 152 | } 153 | 154 | // Begin tests for non-membership witnesses. 155 | 156 | #[test] 157 | fn test_non_mem_wit() { 158 | let (a, sign_a, B) = non_mem_wit_create(U2048::from(2), U2048::from(105), U2048::from(11)); 159 | 160 | assert_eq!(verify_non_mem_wit(U2048::from(2), U2048::from(5), (a, sign_a, B), U2048::from(11)), true); 161 | assert_eq!(verify_non_mem_wit(U2048::from(2), U2048::from(6), (a, sign_a, B), U2048::from(11)), false); 162 | assert_eq!(verify_non_mem_wit(U2048::from(2), U2048::from(5), (a, sign_a, B), U2048::from(5)), false); 163 | } 164 | 165 | #[test] 166 | fn test_mem_wit_create_star() { 167 | let old_state = U2048::from(2); 168 | let new_elems = vec![U2048::from(3), U2048::from(5), U2048::from(7), U2048::from(11), U2048::from(17)]; 169 | let (new_state, agg, _) = batch_add(old_state, &new_elems); 170 | 171 | let subset = vec![U2048::from(5), U2048::from(11), U2048::from(17)]; 172 | let subset_product = subroutines::prime_product(&subset); 173 | let (witness, proof) = mem_wit_create_star(new_state, old_state, agg, subset); 174 | 175 | assert_eq!(witness, U2048::from(5)); 176 | assert_eq!(proofs::verify_poe(witness, subset_product, new_state, proof), true); 177 | } 178 | 179 | 180 | } -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::{env, path::PathBuf}; 2 | 3 | use vergen::{ConstantsFlags, generate_cargo_keys}; 4 | 5 | const ERROR_MSG: &str = "Failed to generate metadata files"; 6 | 7 | fn main() { 8 | generate_cargo_keys(ConstantsFlags::SHA_SHORT).expect(ERROR_MSG); 9 | 10 | let mut manifest_dir = PathBuf::from( 11 | env::var("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` is always set by cargo.") 12 | ); 13 | 14 | while manifest_dir.parent().is_some() { 15 | if manifest_dir.join(".git/HEAD").exists() { 16 | println!("cargo:rerun-if-changed={}", manifest_dir.join(".git/HEAD").display()); 17 | return 18 | } 19 | 20 | manifest_dir.pop(); 21 | } 22 | 23 | println!("cargo:warning=Could not find `.git/HEAD` from manifest dir!"); 24 | } 25 | -------------------------------------------------------------------------------- /client/.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | 6 | aliases: 7 | - &filter-non-gh-pages 8 | branches: 9 | ignore: 10 | - gh-pages 11 | - &filter-only-master 12 | branches: 13 | only: 14 | - master 15 | 16 | version: 2 17 | jobs: 18 | test: 19 | docker: 20 | - image: circleci/node:12.10 21 | working_directory: ~/repo 22 | steps: 23 | - checkout 24 | # Download and cache dependencies 25 | - restore_cache: 26 | keys: 27 | - v1-dependencies-{{ checksum "package.json" }} 28 | # fallback to using the latest cache if no exact match is found 29 | - v1-dependencies- 30 | - run: 31 | name: Install 32 | command: yarn install 33 | - save_cache: 34 | paths: 35 | - node_modules 36 | key: v1-dependencies-{{ checksum "package.json" }} 37 | - run: 38 | name: Syntax Lint 39 | command: yarn lint:ci 40 | - run: 41 | name: Test 42 | command: yarn test 43 | - persist_to_workspace: 44 | # relative to working_directory 45 | root: "./" 46 | paths: 47 | - "./" 48 | 49 | deploy: 50 | docker: 51 | - image: circleci/node:12.10 52 | working_directory: ~/repo 53 | steps: 54 | - attach_workspace: 55 | at: ~/repo 56 | - run: 57 | name: Build & Deploy 58 | # added skip host key checking to avoid the manual prompt 59 | command: | 60 | mkdir ~/.ssh/ && echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config 61 | git config --global user.email "devhub-deploy@users.noreply.github.com" 62 | git config --global user.name "Devhub Deployer" 63 | yarn deploy 64 | 65 | workflows: 66 | version: 2 67 | test_deploy: 68 | jobs: 69 | - test: 70 | filters: *filter-non-gh-pages 71 | - deploy: 72 | filters: *filter-only-master 73 | requires: 74 | - test 75 | -------------------------------------------------------------------------------- /client/.env: -------------------------------------------------------------------------------- 1 | EXTEND_ESLINT=true 2 | -------------------------------------------------------------------------------- /client/LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # Substrate Front End Template 2 | 3 | ## Purpose 4 | 5 | The purpose of this front-end template is to allow you to quickly create a 6 | front-end application that connects to a [Substrate](https://github.com/paritytech/substrate) 7 | node without much configuration and simplify the underlying connection and 8 | handling of [Polkadot-JS](https://polkadot.js.org/api/). 9 | 10 | The encapsulation is handled by components inside `substrate-lib`. Substrate 11 | connection information is exposed out via `substrate-lib/useSubstrate.js` 12 | [custom hook](https://reactjs.org/docs/hooks-custom.html). 13 | 14 | ## Configuration 15 | 16 | The app configuration can be set in the `src/config` folder, with 17 | [common.json](./src/config/common.json) 18 | loaded in both development and production environments, and then the environment 19 | specific JSON file (with higher priority). More on React enviornment variables 20 | can be seen [here](https://create-react-app.dev/docs/adding-custom-environment-variables). 21 | 22 | When writing and deploying an front end with your own node, you likely want to change: 23 | 24 | - `CUSTOM_TYPES` in `src/config/common.json`. See 25 | [Extending types](https://polkadot.js.org/api/start/types.extend.html). 26 | 27 | - `PROVIDER_SOCKET` in `src/config/production.json` pointing to your own 28 | deployed node. 29 | 30 | - `DEVELOPMENT_KEYRING` in `src/config/common.json` be set to `false`. 31 | See [Keyring](https://polkadot.js.org/api/start/keyring.html). 32 | 33 | 34 | ## Install and Start 35 | ```bash 36 | cd ./substrate-front-end-template 37 | yarn install 38 | yarn start 39 | ``` 40 | 41 | ## Component Details 42 | 43 | ### useSubstrate Custom Hook 44 | 45 | The `useSubstrate` custom hook exposes this object: 46 | 47 | ```js 48 | { 49 | socket, 50 | types, 51 | keyring, 52 | keyringState, 53 | api, 54 | apiState, 55 | } 56 | ``` 57 | 58 | - `socket` - The remote provider socket it is connecting to. 59 | - `types` - The custom type used in the connected node. 60 | - `keyring` - Keyring of the connected node. 61 | - `keyringState` - One of [`READY`, `ERROR`] states. `keyring` is valid 62 | only when `keyringState` === `READY`. 63 | - `api` - The remote api to the connected node. 64 | - `apiState` - One of [`CONNECTING`, `READY`, `ERROR`] states. `api` is valid 65 | only when `apiState` === `READY`. 66 | 67 | 68 | ### TxButton Component 69 | 70 | There is a `TxButton` component in 71 | [src/substrate-lib/components/TxButton.js](./src/substrate-lib/components/TxButton.js). 72 | It handles a basic [query](https://polkadot.js.org/api/start/api.query.html) 73 | and [transaction](https://polkadot.js.org/api/start/api.tx.html) requests to the 74 | connected node, and app developers can reuse this component by passing the 75 | right parameters in. See [src/Transfer.js](./src/Transfer.js) for a transaction 76 | example and [src/ChainState.js](./src/ChainState.js) for a query example. 77 | 78 | ## Further Learning 79 | 80 | - To learn more about Substrate front-end development, go to our 81 | [Substrate Front-End Tutorial](https://substrate.dev/docs/en/tutorials/substrate-front-end/). 82 | 83 | - To try an in-depth tutorial for building a custom Substrate runtime as well as a 84 | user interface, goto our [Substrate Collectables Workshop](https://substrate-developer-hub.github.io/substrate-collectables-workshop/). 85 | 86 | - To learn how the underlying Polkadot-JS API library works, go to 87 | [polkadot-js/api](https://polkadot.js.org/api/). 88 | -------------------------------------------------------------------------------- /client/config-overrides.js: -------------------------------------------------------------------------------- 1 | // From: https://prestonrichey.com/blog/react-rust-wasm/ 2 | 3 | const path = require('path'); 4 | 5 | module.exports = function override(config, env) { 6 | const wasmExtensionRegExp = /\.wasm$/; 7 | 8 | config.resolve.extensions.push('.wasm'); 9 | 10 | config.module.rules.forEach(rule => { 11 | (rule.oneOf || []).forEach(oneOf => { 12 | if (oneOf.loader && oneOf.loader.indexOf('file-loader') >= 0) { 13 | // Make file-loader ignore WASM files 14 | oneOf.exclude.push(wasmExtensionRegExp); 15 | } 16 | }); 17 | }); 18 | 19 | // Add a dedicated loader for WASM 20 | config.module.rules.push({ 21 | test: wasmExtensionRegExp, 22 | include: path.resolve(__dirname, 'src'), 23 | use: [{ loader: require.resolve('wasm-loader'), options: {} }] 24 | }); 25 | 26 | return config; 27 | }; -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "substrate-front-end-template", 3 | "version": "0.1.1", 4 | "private": true, 5 | "author": "Parity Technologies ", 6 | "license": "Unlicense", 7 | "dependencies": { 8 | "@polkadot/api": "^0.94.0-beta.11", 9 | "@polkadot/extension-dapp": "^0.9.1", 10 | "@polkadot/keyring": "^1.1.1", 11 | "@polkadot/react-identicon": "^0.42.1", 12 | "@polkadot/ui-keyring": "^0.42.1", 13 | "@polkadot/util": "^1.5.1", 14 | "@polkadot/util-crypto": "^1.2.1", 15 | "@polkadot/util-rlp": "^1.5.1", 16 | "big-integer": "^1.6.47", 17 | "prop-types": "^15.7.2", 18 | "react": "^16.9.0", 19 | "react-dom": "^16.9.0", 20 | "react-scripts": "3.1.1", 21 | "semantic-ui-css": "^2.4.1", 22 | "semantic-ui-react": "^0.88.0" 23 | }, 24 | "scripts": { 25 | "start": "PORT=8000 react-app-rewired start", 26 | "build": "react-app-rewired build", 27 | "test": "CI=true react-app-rewired test --env=jsdom", 28 | "eject": "react-scripts eject", 29 | "lint": "eslint src/**", 30 | "lint:ci": "eslint src/** --max-warnings=0", 31 | "lint:fix": "eslint --fix src/**", 32 | "predeploy": "yarn build", 33 | "deploy": "gh-pages -d build -m '[ci skip] Updates'" 34 | }, 35 | "eslintConfig": { 36 | "extends": [ 37 | "react-app", 38 | "standard-warn" 39 | ], 40 | "rules": { 41 | "semi": [ 42 | 1, 43 | "always" 44 | ], 45 | "no-extra-semi": 1 46 | } 47 | }, 48 | "browserslist": { 49 | "production": [ 50 | ">0.2%", 51 | "not dead", 52 | "not op_mini all" 53 | ], 54 | "development": [ 55 | "last 1 chrome version", 56 | "last 1 firefox version", 57 | "last 1 safari version" 58 | ] 59 | }, 60 | "homepage": "https://substrate-developer-hub.github.io/substrate-front-end-template", 61 | "bugs": { 62 | "url": "https://github.com/substrate-developer-hub/substrate-front-end-template/issues" 63 | }, 64 | "keywords": [ 65 | "substrate", 66 | "substrate-ui", 67 | "polkadot-js" 68 | ], 69 | "devDependencies": { 70 | "eslint": "6.x", 71 | "eslint-config-standard-warn": "^0.0.3", 72 | "eslint-plugin-node": "^10.0.0", 73 | "eslint-plugin-promise": "^4.2.1", 74 | "eslint-plugin-standard": "^4.0.1", 75 | "gh-pages": "^2.1.1", 76 | "react-app-rewired": "^2.1.3", 77 | "wasm-loader": "^1.3.0" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /client/public/Substrate-Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/stateless-blockchain/bb0181ba8b08bc418e4ab1159ec79c74af9b7f49/client/public/Substrate-Logo.png -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paritytech/stateless-blockchain/bb0181ba8b08bc418e4ab1159ec79c74af9b7f49/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 | Substrate Front End Template 23 | 24 | 25 | 26 |
27 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Substrate Front End", 3 | "name": "Substrate Front End Template", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 48x48 32x32 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /client/src/AccountSelector.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | import { 4 | Menu, 5 | Dropdown, 6 | Container, 7 | Icon, 8 | Image, 9 | Label 10 | } from 'semantic-ui-react'; 11 | 12 | import { useSubstrate } from './substrate-lib'; 13 | 14 | export default function AccountSelector (props) { 15 | const { api, keyring } = useSubstrate(); 16 | const { setAccountAddress } = props; 17 | const [accountSelected, setAccountSelected] = useState(''); 18 | 19 | // Get the list of accounts we possess the private key for 20 | const keyringOptions = keyring.getPairs().map(account => ({ 21 | key: account.address, 22 | value: account.address, 23 | text: account.meta.name.toUpperCase(), 24 | icon: 'user' 25 | })); 26 | 27 | const initialAddress = 28 | keyringOptions.length > 0 ? keyringOptions[0].value : ''; 29 | 30 | // Set the initial address 31 | useEffect(() => { 32 | setAccountSelected(initialAddress); 33 | setAccountAddress(initialAddress); 34 | }, [setAccountAddress, initialAddress]); 35 | 36 | const onChange = address => { 37 | // Update state with new account address 38 | setAccountAddress(address); 39 | setAccountSelected(address); 40 | }; 41 | 42 | return ( 43 | 53 | 54 | 55 | 56 | 57 | 58 | 64 | { onChange(dropdown.value); }} 71 | value={accountSelected} 72 | /> 73 | {api.query.balances && api.query.balances.freeBalance 74 | ? 75 | : null} 76 | 77 | 78 | 79 | ); 80 | } 81 | 82 | function BalanceAnnotation (props) { 83 | const { accountSelected } = props; 84 | const { api } = useSubstrate(); 85 | const [accountBalance, setAccountBalance] = useState(0); 86 | 87 | // When account address changes, update subscriptions 88 | useEffect(() => { 89 | let unsubscribe; 90 | 91 | // If the user has selected an address, create a new subscription 92 | accountSelected && 93 | api.query.balances.freeBalance(accountSelected, balance => { 94 | setAccountBalance(balance.toString()); 95 | }).then(unsub => { 96 | unsubscribe = unsub; 97 | }).catch(console.error); 98 | 99 | return () => unsubscribe && unsubscribe(); 100 | }, [accountSelected, api.query.balances]); 101 | 102 | return accountSelected 103 | ? 110 | : null; 111 | } 112 | -------------------------------------------------------------------------------- /client/src/Accounts.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Table, Grid } from 'semantic-ui-react'; 3 | 4 | import { useSubstrate } from './substrate-lib'; 5 | 6 | export default function Accounts (props) { 7 | const { keyring } = useSubstrate(); 8 | const accounts = keyring.getPairs(); 9 | 10 | return ( 11 | 12 |

Accounts

13 | 14 | {accounts.map(account => 15 | 16 | {account.meta.name} 17 | {account.address} 18 | 19 | )} 20 | 21 |
22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, createRef } from 'react'; 2 | import { Container, Dimmer, Loader, Grid, Sticky, Divider, List } from 'semantic-ui-react'; 3 | import 'semantic-ui-css/semantic.min.css'; 4 | import { SubstrateContextProvider, useSubstrate } from './substrate-lib'; 5 | import { DeveloperConsole } from './substrate-lib/components'; 6 | import AccountSelector from './AccountSelector'; 7 | import Accounts from './Accounts'; 8 | import Events from './Events'; 9 | import Transaction from './Transaction'; 10 | import State from './State'; 11 | import Mint from './Mint'; 12 | import Witness from './Witness'; 13 | 14 | const wasmObj = import('accumulator-client'); 15 | 16 | function Main () { 17 | const [accountAddress, setAccountAddress] = useState(null); 18 | const [wasm, setWasm] = useState(null); 19 | 20 | const { apiState, keyring, keyringState } = useSubstrate(); 21 | const accountPair = 22 | accountAddress && 23 | keyringState === 'READY' && 24 | keyring.getPair(accountAddress); 25 | 26 | const loader = text => ( 27 | 28 | {text} 29 | 30 | ); 31 | 32 | if (apiState === 'ERROR') return loader('Error connecting to the blockchain'); 33 | else if (apiState !== 'READY') return loader('Connecting to the blockchain'); 34 | 35 | if (keyringState !== 'READY') { 36 | return loader( 37 | "Loading accounts (please review any extension's authorization)" 38 | ); 39 | } 40 | 41 | const contextRef = createRef(); 42 | 43 | wasmObj.then(wasm => { 44 | setWasm(wasm); 45 | }); 46 | 47 | return ( 48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 |

Instructions

56 | 57 | 58 | Mint a coin. 59 | 60 | Enter a 64 bit number as the coin ID. 61 | Get the hash prime representation of the coin. 62 | The witness associated with this coin is the current accumulator value. 63 | Add the coin to the accumulator. 64 | 65 | 66 | 67 | Send a coin. 68 | 69 | Enter the ID of the coin. Since we are dealing with "non-fungible" tokens this 70 | must be the same as before. 71 | Enter an output address from the given accounts table. You cannot send a coin to yourself. 72 | Enter the witness for the coin. 73 | Create a transaction and submit it to the blockchain. 74 | 75 | 76 | 77 | Receive or update a witness. 78 | 79 | To receive the witness for a coin after either minting or receiving it, enter 80 | the hash representation of the coin, the state before the coin was added, and the product 81 | of any other added elements (you should be able to read this value from the events log). 82 | To update a witness, enter the hash representation of the coin, the current witness, 83 | the product of any added elements, the product of any deleted elements, and the current state of the 84 | accumulator. Again, you should be able to read these values from the events log. 85 | 86 | 87 | 88 | 89 |
90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 |
109 | 110 |
111 |
112 | ); 113 | } 114 | 115 | export default function App () { 116 | return ( 117 | 118 |
119 | 120 | ); 121 | } 122 | -------------------------------------------------------------------------------- /client/src/Events.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Feed, Grid } from 'semantic-ui-react'; 3 | 4 | import { useSubstrate } from './substrate-lib'; 5 | import { hexToBn } from '@polkadot/util'; 6 | 7 | export default function Events (props) { 8 | const { api } = useSubstrate(); 9 | const [eventFeed, setEventFeed] = useState([]); 10 | 11 | useEffect(() => { 12 | // Filter some event from feed 13 | const filter = [ 14 | 'system:ExtrinsicSuccess:: (phase={"ApplyExtrinsic":0})', 15 | 'system:ExtrinsicSuccess:: (phase={"ApplyExtrinsic":1})' 16 | ]; 17 | 18 | api.query.system.events(events => { 19 | // loop through the Vec 20 | events.forEach(record => { 21 | // extract the phase, event and the event types 22 | const { event, phase } = record; 23 | const types = event.typeDef; 24 | 25 | // show what we are busy with 26 | const eventName = `${event.section}:${ 27 | event.method 28 | }:: (phase=${phase.toString()})`; 29 | 30 | if (filter.includes(eventName)) return; 31 | 32 | // loop through each of the parameters, displaying the type and data 33 | const params = event.data.map((data, index) => 34 | `${types[index].type}: ${hexToBn(data.toString(), { isLe: true, isNegative: false })}` 35 | ); 36 | 37 | setEventFeed(e => [{ 38 | icon: 'bell', 39 | date: 'X Blocks Ago', 40 | summary: `${eventName}-${e.length}`, 41 | extraText: event.meta.documentation.join(', ').toString(), 42 | content: params.join(', ') 43 | }, ...e]); 44 | }); 45 | }); 46 | }, [api.query.system]); 47 | 48 | return ( 49 | 50 |

Events

51 | 52 |
53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /client/src/Mint.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Grid, Form, Input, Button } from 'semantic-ui-react'; 3 | import { useSubstrate } from './substrate-lib'; 4 | import { TxButton } from './substrate-lib/components'; 5 | import { u8aToBn } from '@polkadot/util'; 6 | import keyring from '@polkadot/ui-keyring'; 7 | 8 | export default function Mint (props) { 9 | const { api } = useSubstrate(); 10 | const [status, setStatus] = useState(null); 11 | const { accountPair, wasm } = props; 12 | 13 | const [UTXO, setUTXO] = useState({ 14 | ID: '', 15 | elem: '' 16 | }); 17 | 18 | const { ID, elem } = UTXO; 19 | 20 | const onChange = (_, data) => 21 | setUTXO(UTXO => ({ ...UTXO, [data.name]: data.value })); 22 | 23 | function createUTXO () { 24 | const utxo = wasm.get_utxo_elem(keyring.decodeAddress(accountPair.address, true), BigInt(ID)); 25 | const hash = BigInt(u8aToBn(wasm.hash_to_prime(new Uint8Array(utxo)))); 26 | setUTXO(UTXO => ({ ...UTXO, elem: hash })); 27 | alert('The value of the coin is: ' + hash); 28 | } 29 | 30 | return ( 31 | 32 |

Mint Coin

33 |
34 | 35 | 43 | 44 | 45 | 52 | 53 | 54 | 64 | 65 |
{status}
66 |
67 |
68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /client/src/State.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Card, Statistic, Grid } from 'semantic-ui-react'; 3 | import { useSubstrate } from './substrate-lib'; 4 | 5 | import { u8aToBn } from '@polkadot/util'; 6 | 7 | export default function State (props) { 8 | const { api } = useSubstrate(); 9 | 10 | // The currently stored value 11 | const [currentValue, setCurrentValue] = useState(''); 12 | 13 | useEffect(() => { 14 | api.query.stateless.state(value => { 15 | setCurrentValue(Number(BigInt(u8aToBn(value)))); 16 | }); 17 | }, [api.query.stateless]); 18 | 19 | return ( 20 | 21 |

Accumulator Value: {currentValue}

22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /client/src/Transaction.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Grid, Form, Input, Button } from 'semantic-ui-react'; 3 | import { useSubstrate } from './substrate-lib'; 4 | import { TxButton } from './substrate-lib/components'; 5 | import keyring from '@polkadot/ui-keyring'; 6 | import { bnToU8a } from '@polkadot/util'; 7 | import { U8a } from '@polkadot/types/codec'; 8 | 9 | export default function Transaction (props) { 10 | const { api } = useSubstrate(); 11 | const [status, setStatus] = useState(null); 12 | const { accountPair } = props; 13 | 14 | const [formState, setFormState] = useState({ 15 | ID: '', 16 | address: '', 17 | witness: '', 18 | transaction: '' 19 | }); 20 | 21 | const { ID, address, witness, transaction } = formState; 22 | 23 | const onChange = (_, data) => 24 | setFormState(formState => ({ ...formState, [data.name]: data.value })); 25 | 26 | useEffect(() => { 27 | console.log(transaction); 28 | }, [transaction]); 29 | 30 | function createTransaction () { 31 | const sender = keyring.decodeAddress(accountPair.address, true); 32 | const receiver = keyring.decodeAddress(address, true); 33 | const idNum = BigInt(ID); 34 | 35 | const input = { sender, idNum }; 36 | const output = { receiver, idNum }; 37 | 38 | const newWitness = new U8a(bnToU8a(BigInt(witness), 2048, true)); 39 | 40 | const signature = accountPair.sign(idNum); 41 | 42 | const tx = { input, output, newWitness, signature }; 43 | setFormState(formState => ({ ...formState, transaction: tx })); 44 | alert('Transaction created! Ready to submit to the blockchain.'); 45 | } 46 | 47 | return ( 48 | 49 |

Spend Coin

50 |
51 | 52 | 60 | 61 | 62 | 70 | 71 | 72 | 80 | 81 | 82 | 89 | 90 | 91 | 101 | 102 |
{status}
103 |
104 |
105 | ); 106 | } 107 | -------------------------------------------------------------------------------- /client/src/Witness.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Grid, Form, Input, Button } from 'semantic-ui-react'; 3 | import { u8aToBn, bnToU8a } from '@polkadot/util'; 4 | 5 | export default function Witness (props) { 6 | const [status, setStatus] = useState(null); 7 | const { wasm } = props; 8 | 9 | const [formState, setFormState] = useState({ 10 | elem: '', 11 | oldState: '', 12 | newState: '', 13 | added: '', 14 | deleted: '', 15 | witness: '' 16 | }); 17 | 18 | const { elem, oldState, newState, added, deleted, witness } = formState; 19 | 20 | const onChange = (_, data) => 21 | setFormState(formState => ({ ...formState, [data.name]: data.value })); 22 | 23 | function getWitness () { 24 | const newWitness = BigInt(u8aToBn(wasm.get_witness(bnToU8a(oldState), bnToU8a(added), bnToU8a(elem)))); 25 | alert('The witness is: ' + newWitness); 26 | } 27 | 28 | function updateWitness () { 29 | const updatedWitness = BigInt(u8aToBn(wasm.update_witness(bnToU8a(elem), bnToU8a(witness), bnToU8a(newState), bnToU8a(added), bnToU8a(deleted)))); 30 | alert('The witness is: ' + updatedWitness); 31 | } 32 | 33 | return ( 34 | 35 | 36 |

Receive Witness

37 |
38 | 39 | 47 | 48 | 49 | 57 | 58 | 59 | 67 | 68 | 69 | 76 | 77 |
{status}
78 |
79 |
80 | 81 |

Update Witness

82 |
83 | 84 | 92 | 93 | 94 | 102 | 103 | 104 | 112 | 113 | 114 | 122 | 123 | 124 | 132 | 133 | 134 | 141 | 142 |
{status}
143 |
144 |
145 |
146 | ); 147 | } 148 | -------------------------------------------------------------------------------- /client/src/__tests__/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from '../App'; 4 | 5 | describe('App Test Suite', () => { 6 | it('renders without crashing', () => { 7 | const div = document.createElement('div'); 8 | ReactDOM.render(, div); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /client/src/config/common.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_NAME": "substrate-front-end-tutorial", 3 | "DEVELOPMENT_KEYRING": true, 4 | "CUSTOM_TYPES": { 5 | "U2048": "[u8; 256]", 6 | "UTXO": { 7 | "pub_key": "Hash", 8 | "id": "u64" 9 | }, 10 | "Transaction": { 11 | "input": "UTXO", 12 | "output": "UTXO", 13 | "witness": "Vec" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/src/config/development.json: -------------------------------------------------------------------------------- 1 | { 2 | "PROVIDER_SOCKET": "ws://127.0.0.1:9944" 3 | } 4 | -------------------------------------------------------------------------------- /client/src/config/index.js: -------------------------------------------------------------------------------- 1 | const configCommon = require('./common.json'); 2 | const configEnv = require(`./${process.env.NODE_ENV}.json`); 3 | const config = { ...configCommon, ...configEnv }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /client/src/config/production.json: -------------------------------------------------------------------------------- 1 | { 2 | "PROVIDER_SOCKET": "wss://dev-node.substrate.dev:9944" 3 | } 4 | -------------------------------------------------------------------------------- /client/src/config/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "PROVIDER_SOCKET": "wss://dev-node.substrate.dev:9944" 3 | } 4 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import App from './App'; 5 | 6 | ReactDOM.render(, 7 | document.getElementById('root') 8 | ); 9 | -------------------------------------------------------------------------------- /client/src/substrate-lib/SubstrateContext.js: -------------------------------------------------------------------------------- 1 | import React, { useReducer } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import config from '../config'; 4 | 5 | const INIT_STATE = { 6 | socket: config.PROVIDER_SOCKET, 7 | types: config.CUSTOM_TYPES, 8 | keyring: null, 9 | keyringState: null, 10 | api: null, 11 | apiState: null 12 | }; 13 | 14 | const reducer = (state, action) => { 15 | let socket = null; 16 | 17 | switch (action.type) { 18 | case 'RESET_SOCKET': 19 | socket = action.payload || state.socket; 20 | return { ...state, socket, api: null, apiState: null }; 21 | 22 | case 'CONNECT': 23 | return { ...state, api: action.payload, apiState: 'CONNECTING' }; 24 | 25 | case 'CONNECT_SUCCESS': 26 | return { ...state, apiState: 'READY' }; 27 | 28 | case 'CONNECT_ERROR': 29 | return { ...state, apiState: 'ERROR' }; 30 | 31 | case 'SET_KEYRING': 32 | return { ...state, keyring: action.payload, keyringState: 'READY' }; 33 | 34 | case 'KEYRING_ERROR': 35 | return { ...state, keyring: null, keyringState: 'ERROR' }; 36 | 37 | default: 38 | throw new Error(`Unknown type: ${action.type}`); 39 | } 40 | }; 41 | 42 | const SubstrateContext = React.createContext(); 43 | 44 | const SubstrateContextProvider = (props) => { 45 | // filtering props and merge with default param value 46 | const initState = { ...INIT_STATE }; 47 | const neededPropNames = ['socket', 'types']; 48 | neededPropNames.forEach(key => { 49 | initState[key] = (typeof props[key] === 'undefined' ? initState[key] : props[key]); 50 | }); 51 | const [state, dispatch] = useReducer(reducer, initState); 52 | 53 | return ( 54 | 55 | {props.children} 56 | 57 | ); 58 | }; 59 | 60 | // prop typechecking 61 | SubstrateContextProvider.propTypes = { 62 | socket: PropTypes.string, 63 | types: PropTypes.object 64 | }; 65 | 66 | export { SubstrateContext, SubstrateContextProvider }; 67 | -------------------------------------------------------------------------------- /client/src/substrate-lib/components/DeveloperConsole.js: -------------------------------------------------------------------------------- 1 | // This component will simply add utility functions to your developer console. 2 | import { useSubstrate } from '../'; 3 | 4 | export default function DeveloperConsole (props) { 5 | const { api } = useSubstrate(); 6 | window.api = api; 7 | window.util = require('@polkadot/util'); 8 | window.util_crypto = require('@polkadot/util-crypto'); 9 | window.keyring = require('@polkadot/keyring'); 10 | 11 | return null; 12 | } 13 | -------------------------------------------------------------------------------- /client/src/substrate-lib/components/TxButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button } from 'semantic-ui-react'; 3 | import { web3FromSource } from '@polkadot/extension-dapp'; 4 | 5 | import { useSubstrate } from '../'; 6 | 7 | export default function TxButton ({ 8 | accountPair = null, 9 | label, 10 | setStatus, 11 | style = null, 12 | type = null, 13 | attrs = null, 14 | disabled = false 15 | }) { 16 | const { api } = useSubstrate(); 17 | const { params = null, sudo = false, tx = null } = attrs; 18 | const isQuery = () => type === 'QUERY'; 19 | 20 | const transaction = async () => { 21 | const { 22 | address, 23 | meta: { source, isInjected } 24 | } = accountPair; 25 | let fromParam; 26 | 27 | // set the signer 28 | if (isInjected) { 29 | const injected = await web3FromSource(source); 30 | fromParam = address; 31 | api.setSigner(injected.signer); 32 | } else { 33 | fromParam = accountPair; 34 | } 35 | setStatus('Sending...'); 36 | 37 | let txExecute; 38 | try { 39 | // Check if tx has params 40 | if (!params) { 41 | txExecute = !sudo ? tx() : tx.sudo(); 42 | } else { 43 | txExecute = !sudo ? tx(...params) : tx.sudo(...params); 44 | } 45 | } catch (e) { 46 | console.error('ERROR forming transaction:', e); 47 | setStatus(e.toString()); 48 | } 49 | 50 | if (txExecute) { 51 | txExecute 52 | .signAndSend(fromParam, ({ status }) => { 53 | status.isFinalized 54 | ? setStatus( 55 | `Completed at block hash #${status.asFinalized.toString()}` 56 | ) 57 | : setStatus(`Current transaction status: ${status.type}`); 58 | }) 59 | .catch(e => { 60 | setStatus(':( transaction failed'); 61 | console.error('ERROR transaction:', e); 62 | }); 63 | } 64 | }; 65 | 66 | const query = async () => { 67 | try { 68 | const result = await tx(...params); 69 | setStatus(result.toString()); 70 | } catch (e) { 71 | console.error('ERROR query:', e); 72 | setStatus(e.toString()); 73 | } 74 | }; 75 | 76 | return ( 77 | 86 | ); 87 | } 88 | -------------------------------------------------------------------------------- /client/src/substrate-lib/components/index.js: -------------------------------------------------------------------------------- 1 | import DeveloperConsole from './DeveloperConsole'; 2 | import TxButton from './TxButton'; 3 | 4 | export { DeveloperConsole, TxButton }; 5 | -------------------------------------------------------------------------------- /client/src/substrate-lib/index.js: -------------------------------------------------------------------------------- 1 | import useSubstrate from './useSubstrate'; 2 | import { 3 | SubstrateContext, SubstrateContextProvider 4 | } from './SubstrateContext'; 5 | 6 | export { useSubstrate, SubstrateContext, SubstrateContextProvider }; 7 | -------------------------------------------------------------------------------- /client/src/substrate-lib/useSubstrate.js: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect, useCallback } from 'react'; 2 | import { ApiPromise, WsProvider } from '@polkadot/api'; 3 | import { web3Accounts, web3Enable } from '@polkadot/extension-dapp'; 4 | import keyring from '@polkadot/ui-keyring'; 5 | 6 | import config from '../config'; 7 | import { SubstrateContext } from './SubstrateContext'; 8 | 9 | const useSubstrate = () => { 10 | const [state, dispatch] = useContext(SubstrateContext); 11 | 12 | // `useCallback` so that returning memoized function and not created 13 | // everytime, and thus re-render. 14 | const { api, socket, types } = state; 15 | const connect = useCallback(async () => { 16 | if (api) return; 17 | 18 | const provider = new WsProvider(socket); 19 | 20 | try { 21 | const _api = await ApiPromise.create({ provider, types }); 22 | dispatch({ type: 'CONNECT', payload: _api }); 23 | await _api.isReady; 24 | dispatch({ type: 'CONNECT_SUCCESS' }); 25 | } catch (e) { 26 | console.log(e); 27 | dispatch({ type: 'CONNECT_ERROR' }); 28 | } 29 | }, [api, socket, types, dispatch]); 30 | 31 | // hook to get injected accounts 32 | const { keyringState } = state; 33 | const loadAccounts = useCallback(async () => { 34 | // Ensure the method only run once. 35 | if (keyringState) return; 36 | 37 | try { 38 | await web3Enable(config.APP_NAME); 39 | let allAccounts = await web3Accounts(); 40 | allAccounts = allAccounts.map(({ address, meta }) => 41 | ({ address, meta: { ...meta, name: `${meta.name} (${meta.source})` } })); 42 | 43 | keyring.loadAll({ isDevelopment: config.DEVELOPMENT_KEYRING }, allAccounts); 44 | dispatch({ type: 'SET_KEYRING', payload: keyring }); 45 | } catch (e) { 46 | console.log(e); 47 | dispatch({ type: 'KEYRING_ERROR' }); 48 | } 49 | }, [keyringState, dispatch]); 50 | 51 | useEffect(() => { 52 | connect(); 53 | }, [connect]); 54 | 55 | useEffect(() => { 56 | loadAccounts(); 57 | }, [loadAccounts]); 58 | 59 | return { ...state, dispatch }; 60 | }; 61 | 62 | export default useSubstrate; 63 | -------------------------------------------------------------------------------- /runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ['andrewtam'] 3 | edition = '2018' 4 | name = 'stateless-blockchain-runtime' 5 | version = '2.0.0' 6 | 7 | [dependencies.accumulator] 8 | path = "../accumulator" 9 | default-features = false 10 | 11 | [dependencies.vector-commitment] 12 | path = "../vector-commitment" 13 | default_features = false 14 | 15 | [dependencies.babe] 16 | default-features = false 17 | git = 'https://github.com/paritytech/substrate.git' 18 | package = 'srml-babe' 19 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 20 | 21 | [dependencies.primitive-types] 22 | version = "0.5.0" 23 | default-features = false 24 | features = ["codec"] 25 | 26 | [dependencies.babe-primitives] 27 | default-features = false 28 | git = 'https://github.com/paritytech/substrate.git' 29 | package = 'substrate-consensus-babe-primitives' 30 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 31 | 32 | [dependencies.balances] 33 | default_features = false 34 | git = 'https://github.com/paritytech/substrate.git' 35 | package = 'srml-balances' 36 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 37 | 38 | [dependencies.client] 39 | default_features = false 40 | git = 'https://github.com/paritytech/substrate.git' 41 | package = 'substrate-client' 42 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 43 | 44 | [dependencies.codec] 45 | default-features = false 46 | features = ['derive'] 47 | package = 'parity-scale-codec' 48 | version = '1.0.0' 49 | 50 | [dependencies.executive] 51 | default_features = false 52 | git = 'https://github.com/paritytech/substrate.git' 53 | package = 'srml-executive' 54 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 55 | 56 | [dependencies.grandpa] 57 | default-features = false 58 | git = 'https://github.com/paritytech/substrate.git' 59 | package = 'srml-grandpa' 60 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 61 | 62 | [dependencies.indices] 63 | default_features = false 64 | git = 'https://github.com/paritytech/substrate.git' 65 | package = 'srml-indices' 66 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 67 | 68 | [dependencies.offchain-primitives] 69 | default-features = false 70 | git = 'https://github.com/paritytech/substrate.git' 71 | package = 'substrate-offchain-primitives' 72 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 73 | 74 | [dependencies.primitives] 75 | default_features = false 76 | git = 'https://github.com/paritytech/substrate.git' 77 | package = 'substrate-primitives' 78 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 79 | 80 | [dependencies.rstd] 81 | default_features = false 82 | git = 'https://github.com/paritytech/substrate.git' 83 | package = 'sr-std' 84 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 85 | 86 | [dependencies.runtime-io] 87 | default_features = false 88 | git = 'https://github.com/paritytech/substrate.git' 89 | package = 'sr-io' 90 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 91 | 92 | [dependencies.safe-mix] 93 | default-features = false 94 | version = '1.0' 95 | 96 | [dependencies.serde] 97 | features = ['derive'] 98 | optional = true 99 | version = '1.0' 100 | 101 | [dependencies.sr-primitives] 102 | default_features = false 103 | git = 'https://github.com/paritytech/substrate.git' 104 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 105 | 106 | [dependencies.substrate-session] 107 | default-features = false 108 | git = 'https://github.com/paritytech/substrate.git' 109 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 110 | 111 | [dependencies.sudo] 112 | default_features = false 113 | git = 'https://github.com/paritytech/substrate.git' 114 | package = 'srml-sudo' 115 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 116 | 117 | [dependencies.support] 118 | default_features = false 119 | git = 'https://github.com/paritytech/substrate.git' 120 | package = 'srml-support' 121 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 122 | 123 | [dependencies.system] 124 | default_features = false 125 | git = 'https://github.com/paritytech/substrate.git' 126 | package = 'srml-system' 127 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 128 | 129 | [dependencies.timestamp] 130 | default_features = false 131 | git = 'https://github.com/paritytech/substrate.git' 132 | package = 'srml-timestamp' 133 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 134 | 135 | [dependencies.version] 136 | default_features = false 137 | git = 'https://github.com/paritytech/substrate.git' 138 | package = 'sr-version' 139 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 140 | [build-dependencies.wasm-builder-runner] 141 | package = 'substrate-wasm-builder-runner' 142 | version = '1.0.2' 143 | 144 | [features] 145 | default = ['std'] 146 | no_std = [] 147 | std = [ 148 | 'codec/std', 149 | 'client/std', 150 | 'rstd/std', 151 | 'runtime-io/std', 152 | 'support/std', 153 | 'balances/std', 154 | 'babe/std', 155 | 'babe-primitives/std', 156 | 'executive/std', 157 | 'indices/std', 158 | 'grandpa/std', 159 | 'primitives/std', 160 | 'sr-primitives/std', 161 | 'system/std', 162 | 'timestamp/std', 163 | 'sudo/std', 164 | 'version/std', 165 | 'serde', 166 | 'safe-mix/std', 167 | 'offchain-primitives/std', 168 | 'substrate-session/std', 169 | 'vector-commitment/std' 170 | ] 171 | -------------------------------------------------------------------------------- /runtime/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Parity Technologies (UK) Ltd. 2 | // This file is part of Substrate. 3 | 4 | // Substrate is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | 9 | // Substrate is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | 14 | // You should have received a copy of the GNU General Public License 15 | // along with Substrate. If not, see . 16 | 17 | use wasm_builder_runner::{build_current_project_with_rustflags, WasmBuilderSource}; 18 | 19 | fn main() { 20 | build_current_project_with_rustflags( 21 | "wasm_binary.rs", 22 | WasmBuilderSource::Crates("1.0.5"), 23 | // This instructs LLD to export __heap_base as a global variable, which is used by the 24 | // external memory allocator. 25 | "-Clink-arg=--export=__heap_base", 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /runtime/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm. 2 | 3 | #![cfg_attr(not(feature = "std"), no_std)] 4 | // `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. 5 | #![recursion_limit="256"] 6 | 7 | // Make the WASM binary available. 8 | #[cfg(feature = "std")] 9 | include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); 10 | 11 | use rstd::prelude::*; 12 | use primitives::{OpaqueMetadata, crypto::key_types}; 13 | use sr_primitives::{ 14 | ApplyResult, transaction_validity::TransactionValidity, generic, create_runtime_str, 15 | impl_opaque_keys, AnySignature 16 | }; 17 | use sr_primitives::traits::{NumberFor, BlakeTwo256, Block as BlockT, DigestFor, StaticLookup, Verify, ConvertInto}; 18 | use sr_primitives::weights::Weight; 19 | use babe::{AuthorityId as BabeId}; 20 | use grandpa::{AuthorityId as GrandpaId, AuthorityWeight as GrandpaWeight}; 21 | use grandpa::fg_primitives::{self, ScheduledChange}; 22 | use client::{ 23 | block_builder::api::{CheckInherentsResult, InherentData, self as block_builder_api}, 24 | runtime_api as client_api, impl_runtime_apis 25 | }; 26 | use version::RuntimeVersion; 27 | #[cfg(feature = "std")] 28 | use version::NativeVersion; 29 | 30 | // A few exports that help ease life for downstream crates. 31 | #[cfg(any(feature = "std", test))] 32 | pub use sr_primitives::BuildStorage; 33 | pub use timestamp::Call as TimestampCall; 34 | pub use balances::Call as BalancesCall; 35 | pub use sr_primitives::{Permill, Perbill}; 36 | pub use support::{StorageValue, construct_runtime, parameter_types}; 37 | 38 | /// An index to a block. 39 | pub type BlockNumber = u32; 40 | 41 | /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. 42 | pub type Signature = AnySignature; 43 | 44 | /// Some way of identifying an account on the chain. We intentionally make it equivalent 45 | /// to the public key of our transaction signing scheme. 46 | pub type AccountId = ::Signer; 47 | 48 | /// The type for looking up accounts. We don't expect more than 4 billion of them, but you 49 | /// never know... 50 | pub type AccountIndex = u32; 51 | 52 | /// Balance of an account. 53 | pub type Balance = u128; 54 | 55 | /// Index of a transaction in the chain. 56 | pub type Index = u32; 57 | 58 | /// A hash of some data used by the chain. 59 | pub type Hash = primitives::H256; 60 | 61 | /// Digest item type. 62 | pub type DigestItem = generic::DigestItem; 63 | 64 | /// Used for the module template in `./stateless.rs` 65 | mod stateless; 66 | 67 | /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know 68 | /// the specifics of the runtime. They can then be made to be agnostic over specific formats 69 | /// of data like extrinsics, allowing for them to continue syncing the network through upgrades 70 | /// to even the core datastructures. 71 | pub mod opaque { 72 | use super::*; 73 | 74 | pub use sr_primitives::OpaqueExtrinsic as UncheckedExtrinsic; 75 | 76 | /// Opaque block header type. 77 | pub type Header = generic::Header; 78 | /// Opaque block type. 79 | pub type Block = generic::Block; 80 | /// Opaque block identifier type. 81 | pub type BlockId = generic::BlockId; 82 | 83 | pub type SessionHandlers = (Grandpa, Babe); 84 | 85 | impl_opaque_keys! { 86 | pub struct SessionKeys { 87 | #[id(key_types::GRANDPA)] 88 | pub grandpa: GrandpaId, 89 | #[id(key_types::BABE)] 90 | pub babe: BabeId, 91 | } 92 | } 93 | } 94 | 95 | /// This runtime version. 96 | pub const VERSION: RuntimeVersion = RuntimeVersion { 97 | spec_name: create_runtime_str!("stateless-blockchain"), 98 | impl_name: create_runtime_str!("stateless-blockchain"), 99 | authoring_version: 3, 100 | spec_version: 4, 101 | impl_version: 4, 102 | apis: RUNTIME_API_VERSIONS, 103 | }; 104 | 105 | /// Constants for Babe. 106 | 107 | /// Since BABE is probabilistic this is the average expected block time that 108 | /// we are targetting. Blocks will be produced at a minimum duration defined 109 | /// by `SLOT_DURATION`, but some slots will not be allocated to any 110 | /// authority and hence no block will be produced. We expect to have this 111 | /// block time on average following the defined slot duration and the value 112 | /// of `c` configured for BABE (where `1 - c` represents the probability of 113 | /// a slot being empty). 114 | /// This value is only used indirectly to define the unit constants below 115 | /// that are expressed in blocks. The rest of the code should use 116 | /// `SLOT_DURATION` instead (like the timestamp module for calculating the 117 | /// minimum period). 118 | /// 119 | pub const MILLISECS_PER_BLOCK: u64 = 60000; 120 | 121 | pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; 122 | 123 | pub const EPOCH_DURATION_IN_BLOCKS: u32 = 10 * MINUTES; 124 | 125 | // These time units are defined in number of blocks. 126 | pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); 127 | pub const HOURS: BlockNumber = MINUTES * 60; 128 | pub const DAYS: BlockNumber = HOURS * 24; 129 | 130 | // 1 in 4 blocks (on average, not counting collisions) will be primary babe blocks. 131 | pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4); 132 | 133 | /// The version infromation used to identify this runtime when compiled natively. 134 | #[cfg(feature = "std")] 135 | pub fn native_version() -> NativeVersion { 136 | NativeVersion { 137 | runtime_version: VERSION, 138 | can_author_with: Default::default(), 139 | } 140 | } 141 | 142 | parameter_types! { 143 | pub const BlockHashCount: BlockNumber = 250; 144 | pub const MaximumBlockWeight: Weight = 1_000_000; 145 | pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); 146 | pub const MaximumBlockLength: u32 = 5 * 1024 * 1024; 147 | pub const Version: RuntimeVersion = VERSION; 148 | } 149 | 150 | impl system::Trait for Runtime { 151 | /// The identifier used to distinguish between accounts. 152 | type AccountId = AccountId; 153 | /// The aggregated dispatch type that is available for extrinsics. 154 | type Call = Call; 155 | /// The lookup mechanism to get account ID from whatever is passed in dispatchers. 156 | type Lookup = Indices; 157 | /// The index type for storing how many extrinsics an account has signed. 158 | type Index = Index; 159 | /// The index type for blocks. 160 | type BlockNumber = BlockNumber; 161 | /// The type for hashing blocks and tries. 162 | type Hash = Hash; 163 | /// The hashing algorithm used. 164 | type Hashing = BlakeTwo256; 165 | /// The header type. 166 | type Header = generic::Header; 167 | /// The ubiquitous event type. 168 | type Event = Event; 169 | /// Update weight (to fee) multiplier per-block. 170 | type WeightMultiplierUpdate = (); 171 | /// The ubiquitous origin type. 172 | type Origin = Origin; 173 | /// Maximum number of block number to block hash mappings to keep (oldest pruned first). 174 | type BlockHashCount = BlockHashCount; 175 | /// Maximum weight of each block. With a default weight system of 1byte == 1weight, 4mb is ok. 176 | type MaximumBlockWeight = MaximumBlockWeight; 177 | /// Maximum size of all encoded transactions (in bytes) that are allowed in one block. 178 | type MaximumBlockLength = MaximumBlockLength; 179 | /// Portion of the block weight that is available to all normal transactions. 180 | type AvailableBlockRatio = AvailableBlockRatio; 181 | type Version = Version; 182 | } 183 | 184 | parameter_types! { 185 | pub const EpochDuration: u64 = EPOCH_DURATION_IN_BLOCKS as u64; 186 | pub const ExpectedBlockTime: u64 = MILLISECS_PER_BLOCK; 187 | } 188 | 189 | impl babe::Trait for Runtime { 190 | type EpochDuration = EpochDuration; 191 | type ExpectedBlockTime = ExpectedBlockTime; 192 | } 193 | 194 | impl grandpa::Trait for Runtime { 195 | type Event = Event; 196 | } 197 | 198 | impl indices::Trait for Runtime { 199 | /// The type for recording indexing into the account enumeration. If this ever overflows, there 200 | /// will be problems! 201 | type AccountIndex = u32; 202 | /// Use the standard means of resolving an index hint from an id. 203 | type ResolveHint = indices::SimpleResolveHint; 204 | /// Determine whether an account is dead. 205 | type IsDeadAccount = Balances; 206 | /// The ubiquitous event type. 207 | type Event = Event; 208 | } 209 | 210 | parameter_types! { 211 | pub const MinimumPeriod: u64 = 15000; 212 | } 213 | 214 | impl timestamp::Trait for Runtime { 215 | /// A timestamp: milliseconds since the unix epoch. 216 | type Moment = u64; 217 | type OnTimestampSet = Babe; 218 | type MinimumPeriod = MinimumPeriod; 219 | } 220 | 221 | parameter_types! { 222 | pub const ExistentialDeposit: u128 = 500; 223 | pub const TransferFee: u128 = 0; 224 | pub const CreationFee: u128 = 0; 225 | pub const TransactionBaseFee: u128 = 0; 226 | pub const TransactionByteFee: u128 = 1; 227 | } 228 | 229 | impl balances::Trait for Runtime { 230 | /// The type for recording an account's balance. 231 | type Balance = Balance; 232 | /// What to do if an account's free balance gets zeroed. 233 | type OnFreeBalanceZero = (); 234 | /// What to do if a new account is created. 235 | type OnNewAccount = Indices; 236 | /// The ubiquitous event type. 237 | type Event = Event; 238 | 239 | type TransactionPayment = (); 240 | type DustRemoval = (); 241 | type TransferPayment = (); 242 | type ExistentialDeposit = ExistentialDeposit; 243 | type TransferFee = TransferFee; 244 | type CreationFee = CreationFee; 245 | type TransactionBaseFee = TransactionBaseFee; 246 | type TransactionByteFee = TransactionByteFee; 247 | type WeightToFee = ConvertInto; 248 | } 249 | 250 | impl sudo::Trait for Runtime { 251 | type Event = Event; 252 | type Proposal = Call; 253 | } 254 | 255 | parameter_types! { 256 | pub const KeySpace: u8 = 255; 257 | } 258 | 259 | /// Used for the module template in `./stateless.rs` 260 | impl stateless::Trait for Runtime { 261 | type Event = Event; 262 | } 263 | 264 | impl vector_commitment::Trait for Runtime { 265 | type Event = Event; 266 | type KeySpace = KeySpace; 267 | } 268 | 269 | construct_runtime!( 270 | pub enum Runtime where 271 | Block = Block, 272 | NodeBlock = opaque::Block, 273 | UncheckedExtrinsic = UncheckedExtrinsic 274 | { 275 | System: system::{Module, Call, Storage, Config, Event}, 276 | Timestamp: timestamp::{Module, Call, Storage, Inherent}, 277 | Babe: babe::{Module, Call, Storage, Config, Inherent(Timestamp)}, 278 | Grandpa: grandpa::{Module, Call, Storage, Config, Event}, 279 | Indices: indices::{default, Config}, 280 | Balances: balances, 281 | Sudo: sudo, 282 | Stateless: stateless::{Module, Call, Storage, Event}, 283 | StatelessAccounts: vector_commitment::{Module, Call, Storage, Event}, 284 | 285 | } 286 | ); 287 | 288 | /// The address format for describing accounts. 289 | pub type Address = ::Source; 290 | /// Block header type as expected by this runtime. 291 | pub type Header = generic::Header; 292 | /// Block type as expected by this runtime. 293 | pub type Block = generic::Block; 294 | /// A Block signed with a Justification 295 | pub type SignedBlock = generic::SignedBlock; 296 | /// BlockId type as expected by this runtime. 297 | pub type BlockId = generic::BlockId; 298 | /// The SignedExtension to the basic transaction logic. 299 | pub type SignedExtra = ( 300 | system::CheckVersion, 301 | system::CheckGenesis, 302 | system::CheckEra, 303 | system::CheckNonce, 304 | system::CheckWeight, 305 | balances::TakeFees 306 | ); 307 | /// Unchecked extrinsic type as expected by this runtime. 308 | pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; 309 | /// Extrinsic type that has already been checked. 310 | pub type CheckedExtrinsic = generic::CheckedExtrinsic; 311 | /// Executive: handles dispatch to the various modules. 312 | pub type Executive = executive::Executive, Runtime, AllModules>; 313 | 314 | impl_runtime_apis! { 315 | impl client_api::Core for Runtime { 316 | fn version() -> RuntimeVersion { 317 | VERSION 318 | } 319 | 320 | fn execute_block(block: Block) { 321 | Executive::execute_block(block) 322 | } 323 | 324 | fn initialize_block(header: &::Header) { 325 | Executive::initialize_block(header) 326 | } 327 | } 328 | 329 | impl client_api::Metadata for Runtime { 330 | fn metadata() -> OpaqueMetadata { 331 | Runtime::metadata().into() 332 | } 333 | } 334 | 335 | impl block_builder_api::BlockBuilder for Runtime { 336 | fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyResult { 337 | Executive::apply_extrinsic(extrinsic) 338 | } 339 | 340 | fn finalize_block() -> ::Header { 341 | Executive::finalize_block() 342 | } 343 | 344 | fn inherent_extrinsics(data: InherentData) -> Vec<::Extrinsic> { 345 | data.create_extrinsics() 346 | } 347 | 348 | fn check_inherents(block: Block, data: InherentData) -> CheckInherentsResult { 349 | data.check_extrinsics(&block) 350 | } 351 | 352 | fn random_seed() -> ::Hash { 353 | System::random_seed() 354 | } 355 | } 356 | 357 | impl client_api::TaggedTransactionQueue for Runtime { 358 | fn validate_transaction(tx: ::Extrinsic) -> TransactionValidity { 359 | Executive::validate_transaction(tx) 360 | } 361 | } 362 | 363 | impl offchain_primitives::OffchainWorkerApi for Runtime { 364 | fn offchain_worker(number: NumberFor) { 365 | Executive::offchain_worker(number) 366 | } 367 | } 368 | 369 | impl fg_primitives::GrandpaApi for Runtime { 370 | fn grandpa_pending_change(digest: &DigestFor) 371 | -> Option>> 372 | { 373 | Grandpa::pending_change(digest) 374 | } 375 | 376 | fn grandpa_forced_change(digest: &DigestFor) 377 | -> Option<(NumberFor, ScheduledChange>)> 378 | { 379 | Grandpa::forced_change(digest) 380 | } 381 | 382 | fn grandpa_authorities() -> Vec<(GrandpaId, GrandpaWeight)> { 383 | Grandpa::grandpa_authorities() 384 | } 385 | } 386 | 387 | impl babe_primitives::BabeApi for Runtime { 388 | fn startup_data() -> babe_primitives::BabeConfiguration { 389 | // The choice of `c` parameter (where `1 - c` represents the 390 | // probability of a slot being empty), is done in accordance to the 391 | // slot duration and expected target block time, for safely 392 | // resisting network delays of maximum two seconds. 393 | // 394 | babe_primitives::BabeConfiguration { 395 | median_required_blocks: 1000, 396 | slot_duration: Babe::slot_duration(), 397 | c: PRIMARY_PROBABILITY, 398 | } 399 | } 400 | 401 | fn epoch() -> babe_primitives::Epoch { 402 | babe_primitives::Epoch { 403 | start_slot: Babe::epoch_start_slot(), 404 | authorities: Babe::authorities(), 405 | epoch_index: Babe::epoch_index(), 406 | randomness: Babe::randomness(), 407 | duration: EpochDuration::get(), 408 | secondary_slots: Babe::secondary_slots().0, 409 | } 410 | } 411 | } 412 | 413 | impl substrate_session::SessionKeys for Runtime { 414 | fn generate_session_keys(seed: Option>) -> Vec { 415 | let seed = seed.as_ref().map(|s| rstd::str::from_utf8(&s).expect("Seed is an utf8 string")); 416 | opaque::SessionKeys::generate(seed) 417 | } 418 | } 419 | } 420 | -------------------------------------------------------------------------------- /runtime/src/stateless.rs: -------------------------------------------------------------------------------- 1 | /// PROJECT: Stateless Blockchain Experiment. 2 | /// 3 | /// DESCRIPTION: This repository implements a UTXO-based stateless blockchain on Substrate using an 4 | /// RSA accumulator. In this scheme, validators only need to track a single accumulator value and 5 | /// users only need to store their own UTXOs and membership proofs. Unless a data service provider is 6 | /// used, users must constantly watch for updates to the accumulator in order to update their proofs. 7 | /// This particular implementation includes batching and aggregation techniques from this paper: 8 | /// https://eprint.iacr.org/2018/1188.pdf. 9 | /// 10 | /// NOTE: This repository is experimental and is not meant to be used in production. The design choices 11 | /// made in this runtime are impractical from both a security and usability standpoint. Additionally, 12 | /// the following code has not been checked for correctness nor has been optimized for efficiency. 13 | 14 | use support::{decl_module, decl_storage, decl_event, ensure, StorageValue, dispatch::Result, traits::Get}; 15 | use system::ensure_signed; 16 | use primitive_types::H256; 17 | use rstd::prelude::Vec; 18 | use rstd::vec; 19 | use codec::{Encode, Decode}; 20 | use accumulator::*; 21 | 22 | /// At the moment, this particular struct resembles more closely an NFT. 23 | #[cfg_attr(feature = "std", derive(Debug))] 24 | #[derive(Default, Clone, Encode, Decode, PartialEq, Eq, Copy)] 25 | pub struct UTXO { 26 | pub_key: H256, 27 | id: u64, 28 | } 29 | 30 | /// Primitive transaction model with one input and one output. 31 | #[cfg_attr(feature = "std", derive(Debug))] 32 | #[derive(Default, Clone, Encode, Decode, PartialEq, Eq)] 33 | pub struct Transaction { 34 | input: UTXO, 35 | output: UTXO, 36 | witness: Vec, 37 | // Would in practice include a signature here. 38 | } 39 | 40 | pub trait Trait: system::Trait { 41 | type Event: From + Into<::Event>; 42 | } 43 | 44 | decl_storage! { 45 | trait Store for Module as Stateless { 46 | State get(get_state): U2048 = U2048::from(2); // Use 2 as an arbitrary generator with "unknown" order. 47 | SpentCoins get(get_spent_coins): Vec<(U2048, U2048)>; 48 | NewCoins get(get_new_coins): Vec 49 | } 50 | } 51 | 52 | decl_event!( 53 | pub enum Event { 54 | Deletion(U2048, U2048, U2048), 55 | Addition(U2048, U2048, U2048), 56 | } 57 | ); 58 | 59 | decl_module! { 60 | /// The module declaration. 61 | pub struct Module for enum Call where origin: T::Origin { 62 | // Initialize generic event 63 | fn deposit_event() = default; 64 | 65 | /// Receive request to execute a transaction. 66 | /// Verify the contents of a transaction and temporarily add it to a queue of verified transactions. 67 | /// NOTE: Only works if one transaction per user per block is submitted. 68 | pub fn addTransaction(origin, transaction: Transaction) -> Result { 69 | ensure_signed(origin)?; 70 | // Arbitrarily cap the number of pending transactions to 100 71 | ensure!(SpentCoins::get().len() < 100, "Transaction queue full. Please try again next block."); 72 | // Also verify that the user is not spending to themselves 73 | ensure!(transaction.input.pub_key != transaction.output.pub_key, "Cannot send coin to yourself."); 74 | 75 | // Verify witness 76 | let spent_elem = subroutines::hash_to_prime(&transaction.input.encode()); 77 | let witness = U2048::from_little_endian(&transaction.witness); 78 | ensure!(witnesses::verify_mem_wit(State::get(), witness, spent_elem), "Witness is invalid"); 79 | 80 | let new_elem = subroutines::hash_to_prime(&transaction.output.encode()); 81 | 82 | // Update storage items. 83 | SpentCoins::append(&vec![(spent_elem, witness)]); 84 | 85 | Ok(()) 86 | } 87 | 88 | /// Arbitrary replacement for Proof-of-Work to create new coins. 89 | pub fn mint(origin, elem: u64) -> Result { 90 | ensure_signed(origin)?; 91 | let state = subroutines::mod_exp(Self::get_state(), U2048::from(elem), U2048::from_dec_str(MODULUS).unwrap()); 92 | State::put(state); 93 | Ok(()) 94 | } 95 | 96 | /// Batch delete spent coins and add new coins on block finalization 97 | fn on_finalize() { 98 | // Clause here to protect against empty blocks 99 | if Self::get_spent_coins().len() > 0 { 100 | // Delete spent coins from aggregator and distribute proof 101 | let (state, agg, proof) = accumulator::batch_delete(State::get(), &Self::get_spent_coins()); 102 | Self::deposit_event(Event::Deletion(state, agg, proof)); 103 | 104 | // Add new coins to aggregator and distribute proof 105 | let (state, agg, proof) = accumulator::batch_add(state, &Self::get_new_coins()); 106 | Self::deposit_event(Event::Addition(state, agg, proof)); 107 | 108 | // Update state 109 | State::put(state); 110 | } 111 | 112 | // Clear storage 113 | SpentCoins::kill(); 114 | NewCoins::kill(); 115 | } 116 | } 117 | } 118 | 119 | /// tests for this module 120 | #[cfg(test)] 121 | mod tests { 122 | use super::*; 123 | 124 | use runtime_io::with_externalities; 125 | use primitives::{H256, Blake2Hasher}; 126 | use support::{impl_outer_origin, parameter_types}; 127 | use sr_primitives::{traits::{BlakeTwo256, IdentityLookup, OnFinalize}, testing::Header}; 128 | use sr_primitives::weights::Weight; 129 | use sr_primitives::Perbill; 130 | 131 | impl_outer_origin! { 132 | pub enum Origin for Test {} 133 | } 134 | 135 | // For testing the module, we construct most of a mock runtime. This means 136 | // first constructing a configuration type (`Test`) which `impl`s each of the 137 | // configuration traits of modules we want to use. 138 | #[derive(Clone, Eq, PartialEq)] 139 | pub struct Test; 140 | parameter_types! { 141 | pub const BlockHashCount: u64 = 250; 142 | pub const MaximumBlockWeight: Weight = 1024; 143 | pub const MaximumBlockLength: u32 = 2 * 1024; 144 | pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); 145 | } 146 | 147 | impl system::Trait for Test { 148 | type Origin = Origin; 149 | type Call = (); 150 | type Index = u64; 151 | type BlockNumber = u64; 152 | type Hash = H256; 153 | type Hashing = BlakeTwo256; 154 | type AccountId = u64; 155 | type Lookup = IdentityLookup; 156 | type Header = Header; 157 | type WeightMultiplierUpdate = (); 158 | type Event = (); 159 | type BlockHashCount = BlockHashCount; 160 | type MaximumBlockWeight = MaximumBlockWeight; 161 | type MaximumBlockLength = MaximumBlockLength; 162 | type AvailableBlockRatio = AvailableBlockRatio; 163 | type Version = (); 164 | } 165 | 166 | impl Trait for Test { 167 | type Event = (); 168 | } 169 | 170 | type Stateless = Module; 171 | type System = system::Module; 172 | 173 | // This function basically just builds a genesis storage key/value store according to 174 | // our desired mockup. 175 | fn new_test_ext() -> runtime_io::TestExternalities { 176 | system::GenesisConfig::default().build_storage::().unwrap().into() 177 | } 178 | 179 | #[test] 180 | fn test_add() { 181 | with_externalities(&mut new_test_ext(), || { 182 | let elems = vec![U2048::from(3), U2048::from(5), U2048::from(7)]; 183 | let (state, _, _) = accumulator::batch_add(Stateless::get_state(), &elems); 184 | assert_eq!(state, U2048::from(5)); 185 | }); 186 | } 187 | 188 | #[test] 189 | fn test_del() { 190 | with_externalities(&mut new_test_ext(), || { 191 | let elems = vec![U2048::from(3), U2048::from(5), U2048::from(7)]; 192 | // Collect witnesses for the added elements 193 | let witnesses = witnesses::create_all_mem_wit(Stateless::get_state(), &elems); 194 | 195 | // Add elements 196 | let (state, _, _) = accumulator::batch_add(Stateless::get_state(), &elems); 197 | assert_eq!(state, U2048::from(5)); 198 | 199 | // Delete elements 200 | let deletions = vec![(elems[0], witnesses[0]), (elems[1], witnesses[1]), (elems[2], witnesses[2])]; 201 | let (state, _, _) = accumulator::batch_delete(Stateless::get_state(), &deletions); 202 | assert_eq!(state, U2048::from(2)); 203 | }); 204 | } 205 | 206 | #[test] 207 | fn test_block() { 208 | with_externalities(&mut new_test_ext(), || { 209 | // 1. Construct UTXOs. 210 | let utxo_0 = UTXO { 211 | pub_key: H256::from_low_u64_be(0), 212 | id: 0, 213 | }; 214 | 215 | let utxo_1 = UTXO { 216 | pub_key: H256::from_low_u64_be(1), 217 | id: 1, 218 | }; 219 | 220 | let utxo_2 = UTXO { 221 | pub_key: H256::from_low_u64_be(2), 222 | id: 2, 223 | }; 224 | 225 | // 2. Hash each UTXO to a prime. 226 | let elem_0 = subroutines::hash_to_prime(&utxo_0.encode()); 227 | let elem_1 = subroutines::hash_to_prime(&utxo_1.encode()); 228 | let elem_2 = subroutines::hash_to_prime(&utxo_2.encode()); 229 | let elems = vec![elem_0, elem_1, elem_2]; 230 | 231 | // 3. Produce witnesses for the added elements. 232 | let witnesses = witnesses::create_all_mem_wit(Stateless::get_state(), &elems); 233 | 234 | // 4. Add elements to the accumulator. 235 | let (state, _, _) = accumulator::batch_add(Stateless::get_state(), &elems); 236 | State::put(state); 237 | 238 | // 5. Construct new UTXOs and derive integer representations. 239 | let utxo_3 = UTXO { 240 | pub_key: H256::from_low_u64_be(1), 241 | id: 0, 242 | }; 243 | 244 | let utxo_4 = UTXO { 245 | pub_key: H256::from_low_u64_be(2), 246 | id: 1, 247 | }; 248 | 249 | let utxo_5 = UTXO { 250 | pub_key: H256::from_low_u64_be(0), 251 | id: 2, 252 | }; 253 | 254 | let elem_3 = subroutines::hash_to_prime(&utxo_3.encode()); 255 | let elem_4 = subroutines::hash_to_prime(&utxo_4.encode()); 256 | let elem_5 = subroutines::hash_to_prime(&utxo_5.encode()); 257 | 258 | // 6. Construct transactions. 259 | let mut witness_0: [u8; 256] = [0; 256]; 260 | witnesses[0].to_little_endian(&mut witness_0); 261 | let tx_0 = Transaction { 262 | input: utxo_0, 263 | output: utxo_3, 264 | witness: witness_0.to_vec(), 265 | }; 266 | 267 | let mut witness_1: [u8; 256] = [0; 256]; 268 | witnesses[1].to_little_endian(&mut witness_1); 269 | let tx_1 = Transaction { 270 | input: utxo_1, 271 | output: utxo_4, 272 | witness: witness_1.to_vec(), 273 | }; 274 | 275 | let mut witness_2: [u8; 256] = [0; 256]; 276 | witnesses[2].to_little_endian(&mut witness_2); 277 | let tx_2 = Transaction { 278 | input: utxo_2, 279 | output: utxo_5, 280 | witness: witness_2.to_vec(), 281 | }; 282 | 283 | // 7. Verify transactions. Note that this logic will eventually be executed automatically 284 | // by the block builder API eventually. 285 | Stateless::addTransaction(Origin::signed(1), tx_0); 286 | Stateless::addTransaction(Origin::signed(1), tx_1); 287 | Stateless::addTransaction(Origin::signed(1), tx_2); 288 | 289 | // 8. Finalize the block. 290 | Stateless::on_finalize(System::block_number()); 291 | 292 | assert_eq!(Stateless::get_state(), 293 | subroutines::mod_exp(U2048::from(2), elem_3 * elem_4 * elem_5, U2048::from_dec_str(MODULUS).unwrap())); 294 | 295 | }); 296 | } 297 | 298 | #[test] 299 | fn test_mint() { 300 | with_externalities(&mut new_test_ext(), || { 301 | Stateless::mint(Origin::signed(1), 3); 302 | assert_eq!(Stateless::get_state(), U2048::from(8)); 303 | }); 304 | } 305 | 306 | } -------------------------------------------------------------------------------- /scripts/init.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | echo "*** Initializing WASM build environment" 6 | 7 | if [ -z $CI_PROJECT_NAME ] ; then 8 | rustup update nightly 9 | rustup update stable 10 | fi 11 | 12 | rustup target add wasm32-unknown-unknown --toolchain nightly 13 | 14 | # Install wasm-gc. It's useful for stripping slimming down wasm binaries. 15 | command -v wasm-gc || \ 16 | cargo +nightly install --git https://github.com/alexcrichton/wasm-gc --force 17 | -------------------------------------------------------------------------------- /src/chain_spec.rs: -------------------------------------------------------------------------------- 1 | use primitives::{Pair, Public}; 2 | use stateless_blockchain_runtime::{ 3 | AccountId, BabeConfig, BalancesConfig, GenesisConfig, GrandpaConfig, 4 | SudoConfig, IndicesConfig, SystemConfig, WASM_BINARY, 5 | }; 6 | use babe_primitives::{AuthorityId as BabeId}; 7 | use grandpa_primitives::{AuthorityId as GrandpaId}; 8 | use substrate_service; 9 | 10 | // Note this is the URL for the telemetry server 11 | //const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; 12 | 13 | /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. 14 | pub type ChainSpec = substrate_service::ChainSpec; 15 | 16 | /// The chain specification option. This is expected to come in from the CLI and 17 | /// is little more than one of a number of alternatives which can easily be converted 18 | /// from a string (`--chain=...`) into a `ChainSpec`. 19 | #[derive(Clone, Debug)] 20 | pub enum Alternative { 21 | /// Whatever the current runtime is, with just Alice as an auth. 22 | Development, 23 | /// Whatever the current runtime is, with simple Alice/Bob auths. 24 | LocalTestnet, 25 | } 26 | 27 | /// Helper function to generate a crypto pair from seed 28 | pub fn get_from_seed(seed: &str) -> ::Public { 29 | TPublic::Pair::from_string(&format!("//{}", seed), None) 30 | .expect("static values are valid; qed") 31 | .public() 32 | } 33 | 34 | /// Helper function to generate stash, controller and session key from seed 35 | pub fn get_authority_keys_from_seed(seed: &str) -> (AccountId, AccountId, GrandpaId, BabeId) { 36 | ( 37 | get_from_seed::(&format!("{}//stash", seed)), 38 | get_from_seed::(seed), 39 | get_from_seed::(seed), 40 | get_from_seed::(seed), 41 | ) 42 | } 43 | 44 | impl Alternative { 45 | /// Get an actual chain config from one of the alternatives. 46 | pub(crate) fn load(self) -> Result { 47 | Ok(match self { 48 | Alternative::Development => ChainSpec::from_genesis( 49 | "Development", 50 | "dev", 51 | || testnet_genesis(vec![ 52 | get_authority_keys_from_seed("Alice"), 53 | ], 54 | get_from_seed::("Alice"), 55 | vec![ 56 | get_from_seed::("Alice"), 57 | get_from_seed::("Bob"), 58 | get_from_seed::("Alice//stash"), 59 | get_from_seed::("Bob//stash"), 60 | ], 61 | true), 62 | vec![], 63 | None, 64 | None, 65 | None, 66 | None 67 | ), 68 | Alternative::LocalTestnet => ChainSpec::from_genesis( 69 | "Local Testnet", 70 | "local_testnet", 71 | || testnet_genesis(vec![ 72 | get_authority_keys_from_seed("Alice"), 73 | get_authority_keys_from_seed("Bob"), 74 | ], 75 | get_from_seed::("Alice"), 76 | vec![ 77 | get_from_seed::("Alice"), 78 | get_from_seed::("Bob"), 79 | get_from_seed::("Charlie"), 80 | get_from_seed::("Dave"), 81 | get_from_seed::("Eve"), 82 | get_from_seed::("Ferdie"), 83 | get_from_seed::("Alice//stash"), 84 | get_from_seed::("Bob//stash"), 85 | get_from_seed::("Charlie//stash"), 86 | get_from_seed::("Dave//stash"), 87 | get_from_seed::("Eve//stash"), 88 | get_from_seed::("Ferdie//stash"), 89 | ], 90 | true), 91 | vec![], 92 | None, 93 | None, 94 | None, 95 | None 96 | ), 97 | }) 98 | } 99 | 100 | pub(crate) fn from(s: &str) -> Option { 101 | match s { 102 | "dev" => Some(Alternative::Development), 103 | "" | "local" => Some(Alternative::LocalTestnet), 104 | _ => None, 105 | } 106 | } 107 | } 108 | 109 | fn testnet_genesis(initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId)>, 110 | root_key: AccountId, 111 | endowed_accounts: Vec, 112 | _enable_println: bool) -> GenesisConfig { 113 | GenesisConfig { 114 | system: Some(SystemConfig { 115 | code: WASM_BINARY.to_vec(), 116 | changes_trie_config: Default::default(), 117 | }), 118 | indices: Some(IndicesConfig { 119 | ids: endowed_accounts.clone(), 120 | }), 121 | balances: Some(BalancesConfig { 122 | balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(), 123 | vesting: vec![], 124 | }), 125 | sudo: Some(SudoConfig { 126 | key: root_key, 127 | }), 128 | babe: Some(BabeConfig { 129 | authorities: initial_authorities.iter().map(|x| (x.3.clone(), 1)).collect(), 130 | }), 131 | grandpa: Some(GrandpaConfig { 132 | authorities: initial_authorities.iter().map(|x| (x.2.clone(), 1)).collect(), 133 | }), 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use crate::service; 2 | use futures::{future, Future, sync::oneshot}; 3 | use std::cell::RefCell; 4 | use tokio::runtime::Runtime; 5 | pub use substrate_cli::{VersionInfo, IntoExit, error}; 6 | use substrate_cli::{informant, parse_and_prepare, ParseAndPrepare, NoCustom}; 7 | use substrate_service::{AbstractService, Roles as ServiceRoles}; 8 | use crate::chain_spec; 9 | use log::info; 10 | 11 | /// Parse command line arguments into service configuration. 12 | pub fn run(args: I, exit: E, version: VersionInfo) -> error::Result<()> where 13 | I: IntoIterator, 14 | T: Into + Clone, 15 | E: IntoExit, 16 | { 17 | match parse_and_prepare::(&version, "substrate-node", args) { 18 | ParseAndPrepare::Run(cmd) => cmd.run::<(), _, _, _, _>(load_spec, exit, 19 | |exit, _cli_args, _custom_args, config| { 20 | info!("{}", version.name); 21 | info!(" version {}", config.full_version()); 22 | info!(" by {}, 2017, 2018", version.author); 23 | info!("Chain specification: {}", config.chain_spec.name()); 24 | info!("Node name: {}", config.name); 25 | info!("Roles: {:?}", config.roles); 26 | let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?; 27 | match config.roles { 28 | ServiceRoles::LIGHT => run_until_exit( 29 | runtime, 30 | service::new_light(config).map_err(|e| format!("{:?}", e))?, 31 | exit 32 | ), 33 | _ => run_until_exit( 34 | runtime, 35 | service::new_full(config).map_err(|e| format!("{:?}", e))?, 36 | exit 37 | ), 38 | }.map_err(|e| format!("{:?}", e)) 39 | }), 40 | ParseAndPrepare::BuildSpec(cmd) => cmd.run(load_spec), 41 | ParseAndPrepare::ExportBlocks(cmd) => cmd.run_with_builder::<(), _, _, _, _, _>(|config| 42 | Ok(new_full_start!(config).0), load_spec, exit), 43 | ParseAndPrepare::ImportBlocks(cmd) => cmd.run_with_builder::<(), _, _, _, _, _>(|config| 44 | Ok(new_full_start!(config).0), load_spec, exit), 45 | ParseAndPrepare::PurgeChain(cmd) => cmd.run(load_spec), 46 | ParseAndPrepare::RevertChain(cmd) => cmd.run_with_builder::<(), _, _, _, _>(|config| 47 | Ok(new_full_start!(config).0), load_spec), 48 | ParseAndPrepare::CustomCommand(_) => Ok(()) 49 | }?; 50 | 51 | Ok(()) 52 | } 53 | 54 | fn load_spec(id: &str) -> Result, String> { 55 | Ok(match chain_spec::Alternative::from(id) { 56 | Some(spec) => Some(spec.load()?), 57 | None => None, 58 | }) 59 | } 60 | 61 | fn run_until_exit( 62 | mut runtime: Runtime, 63 | service: T, 64 | e: E, 65 | ) -> error::Result<()> 66 | where 67 | T: AbstractService, 68 | E: IntoExit, 69 | { 70 | let (exit_send, exit) = exit_future::signal(); 71 | 72 | let informant = informant::build(&service); 73 | runtime.executor().spawn(exit.until(informant).map(|_| ())); 74 | 75 | // we eagerly drop the service so that the internal exit future is fired, 76 | // but we need to keep holding a reference to the global telemetry guard 77 | let _telemetry = service.telemetry(); 78 | 79 | let service_res = { 80 | let exit = e.into_exit().map_err(|_| error::Error::Other("Exit future failed.".into())); 81 | let service = service.map_err(|err| error::Error::Service(err)); 82 | let select = service.select(exit).map(|_| ()).map_err(|(err, _)| err); 83 | runtime.block_on(select) 84 | }; 85 | 86 | exit_send.fire(); 87 | 88 | // TODO [andre]: timeout this future #1318 89 | let _ = runtime.shutdown_on_idle().wait(); 90 | 91 | service_res 92 | } 93 | 94 | // handles ctrl-c 95 | pub struct Exit; 96 | impl IntoExit for Exit { 97 | type Exit = future::MapErr, fn(oneshot::Canceled) -> ()>; 98 | fn into_exit(self) -> Self::Exit { 99 | // can't use signal directly here because CtrlC takes only `Fn`. 100 | let (exit_send, exit) = oneshot::channel(); 101 | 102 | let exit_send_cell = RefCell::new(Some(exit_send)); 103 | ctrlc::set_handler(move || { 104 | let exit_send = exit_send_cell.try_borrow_mut().expect("signal handler not reentrant; qed").take(); 105 | if let Some(exit_send) = exit_send { 106 | exit_send.send(()).expect("Error sending exit notification"); 107 | } 108 | }).expect("Error setting Ctrl-C handler"); 109 | 110 | exit.map_err(drop) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | //! Substrate Node Template CLI library. 2 | 3 | #![warn(missing_docs)] 4 | #![warn(unused_extern_crates)] 5 | 6 | mod chain_spec; 7 | #[macro_use] 8 | mod service; 9 | mod cli; 10 | 11 | pub use substrate_cli::{VersionInfo, IntoExit, error}; 12 | 13 | fn main() { 14 | let version = VersionInfo { 15 | name: "Substrate Node", 16 | commit: env!("VERGEN_SHA_SHORT"), 17 | version: env!("CARGO_PKG_VERSION"), 18 | executable_name: "stateless-blockchain", 19 | author: "andrewtam", 20 | description: "stateless-blockchain", 21 | support_url: "support.anonymous.an", 22 | }; 23 | 24 | if let Err(e) = cli::run(::std::env::args(), cli::Exit, version) { 25 | eprintln!("Fatal error: {}\n\n{:?}", e, e); 26 | std::process::exit(1) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/service.rs: -------------------------------------------------------------------------------- 1 | //! Service and ServiceFactory implementation. Specialized wrapper over substrate service. 2 | 3 | use std::sync::Arc; 4 | use std::time::Duration; 5 | use substrate_client::LongestChain; 6 | use babe::{import_queue, start_babe, Config}; 7 | use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider}; 8 | use futures::prelude::*; 9 | use stateless_blockchain_runtime::{self, GenesisConfig, opaque::Block, RuntimeApi, WASM_BINARY}; 10 | use substrate_service::{error::{Error as ServiceError}, AbstractService, Configuration, ServiceBuilder}; 11 | use transaction_pool::{self, txpool::{Pool as TransactionPool}}; 12 | use inherents::InherentDataProviders; 13 | use network::construct_simple_protocol; 14 | use substrate_executor::native_executor_instance; 15 | pub use substrate_executor::NativeExecutor; 16 | 17 | // Our native executor instance. 18 | native_executor_instance!( 19 | pub Executor, 20 | stateless_blockchain_runtime::api::dispatch, 21 | stateless_blockchain_runtime::native_version 22 | ); 23 | 24 | construct_simple_protocol! { 25 | /// Demo protocol attachment for substrate. 26 | pub struct NodeProtocol where Block = Block { } 27 | } 28 | 29 | /// Starts a `ServiceBuilder` for a full service. 30 | /// 31 | /// Use this macro if you don't actually need the full service, but just the builder in order to 32 | /// be able to perform chain operations. 33 | macro_rules! new_full_start { 34 | ($config:expr) => {{ 35 | let mut import_setup = None; 36 | let inherent_data_providers = inherents::InherentDataProviders::new(); 37 | let mut tasks_to_spawn = None; 38 | 39 | let builder = substrate_service::ServiceBuilder::new_full::< 40 | stateless_blockchain_runtime::opaque::Block, stateless_blockchain_runtime::RuntimeApi, crate::service::Executor 41 | >($config)? 42 | .with_select_chain(|_config, client| { 43 | #[allow(deprecated)] 44 | Ok(substrate_client::LongestChain::new(client.backend().clone())) 45 | })? 46 | .with_transaction_pool(|config, client| 47 | Ok(transaction_pool::txpool::Pool::new(config, transaction_pool::ChainApi::new(client))) 48 | )? 49 | .with_import_queue(|_config, client, mut select_chain, transaction_pool| { 50 | let select_chain = select_chain.take() 51 | .ok_or_else(|| substrate_service::Error::SelectChainRequired)?; 52 | let (block_import, link_half) = 53 | grandpa::block_import::<_, _, _, stateless_blockchain_runtime::RuntimeApi, _, _>( 54 | client.clone(), client.clone(), select_chain 55 | )?; 56 | let justification_import = block_import.clone(); 57 | 58 | let (import_queue, babe_link, babe_block_import, pruning_task) = babe::import_queue( 59 | babe::Config::get_or_compute(&*client)?, 60 | block_import, 61 | Some(Box::new(justification_import)), 62 | None, 63 | client.clone(), 64 | client, 65 | inherent_data_providers.clone(), 66 | Some(transaction_pool) 67 | )?; 68 | 69 | import_setup = Some((babe_block_import.clone(), link_half, babe_link)); 70 | tasks_to_spawn = Some(vec![Box::new(pruning_task)]); 71 | 72 | Ok(import_queue) 73 | })?; 74 | 75 | (builder, import_setup, inherent_data_providers, tasks_to_spawn) 76 | }} 77 | } 78 | 79 | /// Builds a new service for a full client. 80 | pub fn new_full(config: Configuration) 81 | -> Result 82 | { 83 | 84 | let (builder, mut import_setup, inherent_data_providers, mut tasks_to_spawn) = new_full_start!(config); 85 | 86 | let service = builder.with_network_protocol(|_| Ok(NodeProtocol::new()))? 87 | .with_finality_proof_provider(|client| 88 | Ok(Arc::new(GrandpaFinalityProofProvider::new(client.clone(), client)) as _) 89 | )? 90 | .build()?; 91 | 92 | let (block_import, link_half, babe_link) = 93 | import_setup.take() 94 | .expect("Link Half and Block Import are present for Full Services or setup failed before. qed"); 95 | 96 | // spawn any futures that were created in the previous setup steps 97 | if let Some(tasks) = tasks_to_spawn.take() { 98 | for task in tasks { 99 | service.spawn_task( 100 | task.select(service.on_exit()) 101 | .map(|_| ()) 102 | .map_err(|_| ()) 103 | ); 104 | } 105 | } 106 | 107 | if service.config().roles.is_authority() { 108 | let proposer = basic_authorship::ProposerFactory { 109 | client: service.client(), 110 | transaction_pool: service.transaction_pool(), 111 | }; 112 | 113 | let client = service.client(); 114 | let select_chain = service.select_chain() 115 | .ok_or(ServiceError::SelectChainRequired)?; 116 | 117 | let babe_config = babe::BabeParams { 118 | config: Config::get_or_compute(&*client)?, 119 | keystore: service.keystore(), 120 | client, 121 | select_chain, 122 | block_import, 123 | env: proposer, 124 | sync_oracle: service.network(), 125 | inherent_data_providers: inherent_data_providers.clone(), 126 | force_authoring: service.config().force_authoring, 127 | time_source: babe_link, 128 | }; 129 | 130 | let babe = start_babe(babe_config)?; 131 | let select = babe.select(service.on_exit()).then(|_| Ok(())); 132 | 133 | // the BABE authoring task is considered infallible, i.e. if it 134 | // fails we take down the service with it. 135 | service.spawn_essential_task(select); 136 | } 137 | 138 | let config = grandpa::Config { 139 | // FIXME #1578 make this available through chainspec 140 | gossip_duration: Duration::from_millis(333), 141 | justification_period: 4096, 142 | name: Some(service.config().name.clone()), 143 | keystore: Some(service.keystore()), 144 | }; 145 | 146 | match (service.config().roles.is_authority(), service.config().disable_grandpa) { 147 | (false, false) => { 148 | // start the lightweight GRANDPA observer 149 | service.spawn_task(Box::new(grandpa::run_grandpa_observer( 150 | config, 151 | link_half, 152 | service.network(), 153 | service.on_exit(), 154 | )?)); 155 | }, 156 | (true, false) => { 157 | // start the full GRANDPA voter 158 | let grandpa_config = grandpa::GrandpaParams { 159 | config: config, 160 | link: link_half, 161 | network: service.network(), 162 | inherent_data_providers: inherent_data_providers.clone(), 163 | on_exit: service.on_exit(), 164 | telemetry_on_connect: Some(service.telemetry_on_connect_stream()), 165 | }; 166 | 167 | // the GRANDPA voter task is considered infallible, i.e. 168 | // if it fails we take down the service with it. 169 | service.spawn_essential_task(grandpa::run_grandpa_voter(grandpa_config)?); 170 | }, 171 | (_, true) => { 172 | grandpa::setup_disabled_grandpa( 173 | service.client(), 174 | &inherent_data_providers, 175 | service.network(), 176 | )?; 177 | }, 178 | } 179 | 180 | Ok(service) 181 | } 182 | 183 | /// Builds a new service for a light client. 184 | pub fn new_light(config: Configuration) 185 | -> Result 186 | { 187 | let inherent_data_providers = InherentDataProviders::new(); 188 | 189 | ServiceBuilder::new_light::(config)? 190 | .with_select_chain(|_config, client| { 191 | #[allow(deprecated)] 192 | Ok(LongestChain::new(client.backend().clone())) 193 | })? 194 | .with_transaction_pool(|config, client| 195 | Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) 196 | )? 197 | .with_import_queue_and_fprb(|_config, client, _select_chain, transaction_pool| { 198 | #[allow(deprecated)] 199 | let fetch_checker = client.backend().blockchain().fetcher() 200 | .upgrade() 201 | .map(|fetcher| fetcher.checker().clone()) 202 | .ok_or_else(|| "Trying to start light import queue without active fetch checker")?; 203 | let block_import = grandpa::light_block_import::<_, _, _, RuntimeApi, _>( 204 | client.clone(), Arc::new(fetch_checker), client.clone() 205 | )?; 206 | 207 | let finality_proof_import = block_import.clone(); 208 | let finality_proof_request_builder = 209 | finality_proof_import.create_finality_proof_request_builder(); 210 | 211 | // FIXME: pruning task isn't started since light client doesn't do `AuthoritySetup`. 212 | let (import_queue, ..) = import_queue( 213 | Config::get_or_compute(&*client)?, 214 | block_import, 215 | None, 216 | Some(Box::new(finality_proof_import)), 217 | client.clone(), 218 | client, 219 | inherent_data_providers.clone(), 220 | Some(transaction_pool) 221 | )?; 222 | 223 | Ok((import_queue, finality_proof_request_builder)) 224 | })? 225 | .with_network_protocol(|_| Ok(NodeProtocol::new()))? 226 | .with_finality_proof_provider(|client| 227 | Ok(Arc::new(GrandpaFinalityProofProvider::new(client.clone(), client)) as _) 228 | )? 229 | .build() 230 | } 231 | -------------------------------------------------------------------------------- /substrate-node-rename.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | name=$1 4 | shift 5 | author=$1 6 | shift 7 | 8 | if [[ "$name" == "" || "$name" == "-"* ]] 9 | then 10 | echo "Usage: substrate-node-rename " 11 | exit 1 12 | fi 13 | if [[ "$author" == "" || "$author" == "-"* ]] 14 | then 15 | echo "Usage: substrate-node-rename " 16 | exit 1 17 | fi 18 | 19 | lname="$(echo $name | tr '[:upper:]' '[:lower:]')" 20 | dirname="${lname// /-}" 21 | 22 | bold=$(tput bold) 23 | normal=$(tput sgr0) 24 | 25 | if [ -d "$dirname" ]; then 26 | echo "Directory '$name' already exists!" 27 | exit 1 28 | fi 29 | 30 | echo "${bold}Moving project folder...${normal}" 31 | 32 | git mv substrate-node-template $dirname 33 | 34 | pushd $dirname >/dev/null 35 | 36 | echo "${bold}Customizing project...${normal}" 37 | function replace { 38 | find_this="$1" 39 | shift 40 | replace_with="$1" 41 | shift 42 | IFS=$'\n' 43 | TEMP=$(mktemp -d "${TMPDIR:-/tmp}/.XXXXXXXXXXXX") 44 | rmdir $TEMP 45 | for item in `find . -not -path '*/\.*' -type f \( -name "*.rs" -o -name "*.md" -o -name "build.sh" -o -name "Cargo.toml" -o -name "Cargo.lock" \)` 46 | do 47 | sed "s/$find_this/$replace_with/g" "$item" > $TEMP 48 | cat $TEMP > "$item" 49 | done 50 | rm -f $TEMP 51 | } 52 | 53 | replace "Template Node" "${name}" 54 | replace node-template "${lname//[_ ]/-}" 55 | replace node_template "${lname//[- ]/_}" 56 | replace Anonymous "$author" 57 | 58 | echo "Rename Complete" 59 | 60 | popd >/dev/null 61 | -------------------------------------------------------------------------------- /vector-commitment/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ['andrewtam'] 3 | edition = '2018' 4 | name = 'vector-commitment' 5 | version = '1.0.0' 6 | 7 | [dependencies.accumulator] 8 | path = "../accumulator" 9 | default-features=false 10 | 11 | [dependencies] 12 | bit-vec = { version = "0.6", default-features = false } 13 | clear_on_drop = { version="0.2.3", features=["nightly"] } 14 | 15 | [features] 16 | default = ['std'] 17 | std = [ 18 | 'serde', 19 | 'codec/std', 20 | 'support/std', 21 | 'system/std', 22 | 'sr-primitives/std', 23 | 'runtime-io/std', 24 | 'rstd/std', 25 | ] 26 | 27 | [dependencies.serde] 28 | features = ['derive'] 29 | optional = true 30 | version = '1.0' 31 | 32 | [dependencies.codec] 33 | default-features = false 34 | features = ['derive'] 35 | package = 'parity-scale-codec' 36 | version = '1.0.0' 37 | 38 | [dependencies.support] 39 | default_features = false 40 | git = 'https://github.com/paritytech/substrate.git' 41 | package = 'srml-support' 42 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 43 | 44 | [dependencies.system] 45 | default_features = false 46 | git = 'https://github.com/paritytech/substrate.git' 47 | package = 'srml-system' 48 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 49 | 50 | [dependencies.sr-primitives] 51 | default_features = false 52 | git = 'https://github.com/paritytech/substrate.git' 53 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 54 | 55 | [dependencies.runtime-io] 56 | default_features = false 57 | git = 'https://github.com/paritytech/substrate.git' 58 | package = 'sr-io' 59 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 60 | 61 | [dependencies.primitives] 62 | default_features = false 63 | git = 'https://github.com/paritytech/substrate.git' 64 | package = 'substrate-primitives' 65 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 66 | 67 | [dependencies.rstd] 68 | default_features = false 69 | git = 'https://github.com/paritytech/substrate.git' 70 | package = 'sr-std' 71 | rev = '3ba0f2a2dbd37c31851a0ff1c1c0c47aa940de90' 72 | -------------------------------------------------------------------------------- /vector-commitment/src/binary.rs: -------------------------------------------------------------------------------- 1 | /// Binary Vector Commitments 2 | /// Functions have been slightly modified from original paper specification. 3 | 4 | use accumulator::*; 5 | use accumulator::witnesses; 6 | use rstd::prelude::Vec; 7 | use runtime_io; 8 | 9 | /// Commit a vector of bits(represented as bool array) to an accumulator. The second value of 10 | /// the returned tuple is the product of the accumulated elements. 11 | /// NOTE: In the stateless blockchain model, after the validator commits the vector to the accumulator, 12 | /// users should immediately request membership witnesses for their committed bit using the returned "product" value. 13 | pub fn commit(accumulator: U2048, values: &[bool], indices: &[usize]) -> (U2048, U2048) { 14 | let elems: Vec = values 15 | .into_iter() 16 | .enumerate() 17 | .filter(|(_, val)| **val) 18 | .map(|(index, _)| subroutines::hash_to_prime(&indices[index].to_le_bytes())) 19 | .collect(); 20 | let (state, product, _) = batch_add(accumulator, &elems); 21 | return (state, product); 22 | } 23 | 24 | /// Create an opening for a bit commitment. The current state of the accumulator should equal 25 | /// "old_state" raised to the "agg" power(product of aggregated elements) where the committed bit 26 | /// is contained in "agg". 27 | pub fn open(old_state: U2048, bit: bool, index: usize, agg: U2048) -> Witness { 28 | let elem = subroutines::hash_to_prime(&index.to_le_bytes()); 29 | if bit { 30 | return Witness::MemWit(witnesses::mem_wit_create(old_state, agg, elem).unwrap()); 31 | } 32 | else { 33 | return Witness::NonMemWit(witnesses::non_mem_wit_create(old_state, agg, elem)); 34 | } 35 | } 36 | 37 | /// Verify a membership/non-membership proof (produced by an opening) for a given bit commitment. 38 | pub fn verify(old_state: U2048, accumulator: U2048, bit: bool, index: usize, proof: Witness) -> bool { 39 | let elem = subroutines::hash_to_prime(&index.to_le_bytes()); 40 | if bit { 41 | match proof { 42 | Witness::MemWit(witness) => { 43 | return witnesses::verify_mem_wit(accumulator, witness, elem); 44 | }, 45 | Witness::NonMemWit(_) => { 46 | return false; 47 | }, 48 | } 49 | } 50 | else { 51 | match proof { 52 | Witness::NonMemWit(witness) => { 53 | return witnesses::verify_non_mem_wit(old_state,accumulator, witness, elem); 54 | }, 55 | Witness::MemWit(_) => { 56 | return false; 57 | }, 58 | } 59 | } 60 | } 61 | 62 | /// Given a bit array and an array of corresponding indices, outputs the product of the "ones" 63 | /// elements and the product of the "zeros" elements. 64 | pub fn get_bit_elems(b: &[bool], i: &[usize]) -> (U2048, U2048) { 65 | let ones_indices: Vec = b 66 | .into_iter() 67 | .enumerate() 68 | .filter(|(_, bit)| **bit) 69 | .map(|(index, _)| index) 70 | .collect(); 71 | 72 | let zeros_indices: Vec = b 73 | .into_iter() 74 | .enumerate() 75 | .filter(|(_, bit)| !**bit) 76 | .map(|(index, _)| index) 77 | .collect(); 78 | 79 | let ones: Vec = ones_indices 80 | .into_iter() 81 | .enumerate() 82 | .map(|(_, index)| subroutines::hash_to_prime(&(i[index]).to_le_bytes())) 83 | .collect(); 84 | 85 | let zeros: Vec = zeros_indices 86 | .into_iter() 87 | .enumerate() 88 | .map(|(_, index)| subroutines::hash_to_prime(&(i[index]).to_le_bytes())) 89 | .collect(); 90 | 91 | let p_ones = subroutines::prime_product(&ones); 92 | let p_zeros = subroutines::prime_product(&zeros); 93 | return (p_ones, p_zeros) 94 | } 95 | 96 | /// Batch opens a set of bit commitments. The accumulated values of the commitments must be contained in 97 | /// the inputted aggregated value(agg) and the current state of the accumulator must equal old_state^agg. 98 | /// This function has been slightly modified from the original specification. See page 20 of the paper for more info. 99 | pub fn batch_open(old_state: U2048, agg: U2048, b: &[bool], i: &[usize]) -> (Witness, Witness) { 100 | let (p_ones, p_zeros) = get_bit_elems(b, i); 101 | 102 | let pi_inclusion = Witness::MemWit(witnesses::mem_wit_create(old_state, agg, p_ones).unwrap()); 103 | let pi_exclusion = Witness::NonMemWit(witnesses::non_mem_wit_create(old_state, agg, p_zeros)); 104 | 105 | return (pi_inclusion, pi_exclusion); 106 | } 107 | 108 | /// Verifies a set of membership and non-membership witnesses for a set of bit commitments. 109 | /// This function has been slightly modified from the original specification. See page 20 of the paper for more info. 110 | pub fn batch_verify(old_state: U2048, accumulator: U2048, b: &[bool], i: &[usize], pi_i: Witness, pi_e: Witness) -> bool { 111 | let (p_ones, p_zeros) = get_bit_elems(b, i); 112 | 113 | let ver_mem_result; 114 | match pi_i { 115 | Witness::MemWit(mem_wit) => { 116 | ver_mem_result = witnesses::verify_mem_wit(accumulator, mem_wit, p_ones); 117 | }, 118 | Witness::NonMemWit(_) => { 119 | return false; 120 | }, 121 | } 122 | 123 | let ver_non_mem_result; 124 | match pi_e { 125 | Witness::MemWit(_) => { 126 | return false; 127 | }, 128 | Witness::NonMemWit(non_mem_wit) => { 129 | ver_non_mem_result = witnesses::verify_non_mem_wit(old_state,accumulator,non_mem_wit, p_zeros); }, 130 | } 131 | 132 | return ver_mem_result && ver_non_mem_result; 133 | } 134 | 135 | /// Updates a segment of a vector commitment. Assumes that an honest party performs the update. 136 | /// Arguments: 137 | /// - accumulator: The current state of the accumulator. 138 | /// - old_state: A previous state. 139 | /// - agg: Product of some aggregated elements where old_state^agg = accumulator. The aggregated 140 | /// bits should be contained in this value. 141 | /// - b: New bit array. 142 | /// - i: Affected indices. 143 | pub fn update(accumulator: U2048, old_state: U2048, agg: U2048, b: &[bool], i: &[usize]) -> U2048 { 144 | let (p_ones, p_zeros) = get_bit_elems(b, i); 145 | 146 | // Delete p_zeros elements 147 | let mem_wit = witnesses::mem_wit_create(old_state, agg, p_zeros).unwrap(); 148 | let mut new_state = delete(accumulator, p_zeros, mem_wit).unwrap(); 149 | 150 | // Add p_ones elements 151 | new_state = add(new_state, p_ones); 152 | 153 | return new_state; 154 | } 155 | 156 | #[cfg(test)] 157 | mod tests { 158 | use super::*; 159 | 160 | #[test] 161 | fn test_open_and_verify() { 162 | // Commit vector 163 | let accumulator = U2048::from(2); 164 | let arr: [bool; 3] = [true, false, true]; 165 | let (state, product) = commit(accumulator, &arr, &[0, 1, 2]); 166 | 167 | // Check commit 168 | let h_0 = subroutines::hash_to_prime(&(0 as usize).to_le_bytes()); 169 | let h_2 = subroutines::hash_to_prime(&(2 as usize).to_le_bytes()); 170 | assert_eq!(subroutines::mod_exp(accumulator, h_0*h_2, U2048::from_dec_str(MODULUS).unwrap()), state); 171 | 172 | // Open at two indices 173 | let open_1 = open(U2048::from(2), false, 1, product); 174 | let open_2 = open(U2048::from(2), true, 2, product); 175 | 176 | // Verify 177 | assert_eq!(verify(accumulator, state, false, 1, open_1), true); 178 | assert_eq!(verify(accumulator, state, true, 1, open_1), false); 179 | assert_eq!(verify(accumulator, state, false, 1, open_2), false); 180 | 181 | assert_eq!(verify(accumulator, state, true, 2, open_2), true); 182 | assert_eq!(verify(accumulator, state, false, 2, open_2), false); 183 | assert_eq!(verify(accumulator, state, true, 2, open_1), false); 184 | } 185 | 186 | #[test] 187 | fn test_get_bit_elems() { 188 | let arr: [bool; 3] = [false, false, true]; 189 | let indices = [0, 1, 5]; 190 | 191 | let h_0 = subroutines::hash_to_prime(&(0 as usize).to_le_bytes()); 192 | let h_1 = subroutines::hash_to_prime(&(1 as usize).to_le_bytes()); 193 | let h_5 = subroutines::hash_to_prime(&(5 as usize).to_le_bytes()); 194 | 195 | let (p_ones, p_zeros) = get_bit_elems(&arr, &indices); 196 | assert_eq!(p_ones, h_5); 197 | assert_eq!(p_zeros, h_0 * h_1); 198 | } 199 | 200 | #[test] 201 | fn test_batch_open_and_verify() { 202 | let accumulator = U2048::from(2); 203 | let arr: [bool; 6] = [true, false, true, false, false, true]; 204 | let (state, product) = commit(accumulator, &arr, &[0, 1, 2, 3, 4, 5]); 205 | 206 | let (i, e) = batch_open(accumulator, product, &[true, false, false, true], &[0, 3, 4, 5]); 207 | 208 | let h_0 = subroutines::hash_to_prime(&(0 as usize).to_le_bytes()); 209 | let h_3 = subroutines::hash_to_prime(&(3 as usize).to_le_bytes()); 210 | let h_4 = subroutines::hash_to_prime(&(4 as usize).to_le_bytes()); 211 | let h_5 = subroutines::hash_to_prime(&(5 as usize).to_le_bytes()); 212 | 213 | // Manual check of openings 214 | let ones_product = subroutines::prime_product(&vec![h_0, h_5]); 215 | let zeros_product = subroutines::prime_product(&vec![h_3, h_4]); 216 | 217 | let mut mem_result = false; 218 | let mut non_mem_result = false; 219 | 220 | match i { 221 | Witness::MemWit(mem_wit) => { 222 | mem_result = witnesses::verify_mem_wit(state, mem_wit, ones_product); 223 | }, 224 | Witness::NonMemWit(_) => { }, 225 | } 226 | 227 | match e { 228 | Witness::MemWit(_) => { }, 229 | Witness::NonMemWit(non_mem_wit) => { 230 | non_mem_result = witnesses::verify_non_mem_wit(accumulator, state, non_mem_wit, zeros_product); 231 | }, 232 | } 233 | assert_eq!(mem_result && non_mem_result, true); 234 | 235 | assert_eq!(batch_verify(accumulator, state, &[true, false, false, true], &[0, 3, 4, 5], i, e), true); 236 | } 237 | 238 | #[test] 239 | fn test_update() { 240 | let accumulator = U2048::from(2); 241 | let arr: [bool; 6] = [true, false, true, false, false, true]; 242 | let (state, product) = commit(accumulator, &arr, &[0, 1, 2, 3, 4, 5]); 243 | 244 | let h_0 = subroutines::hash_to_prime(&(0 as usize).to_le_bytes()); 245 | let h_3 = subroutines::hash_to_prime(&(3 as usize).to_le_bytes()); 246 | let h_4 = subroutines::hash_to_prime(&(4 as usize).to_le_bytes()); 247 | 248 | // Missing: checking that inputs are valid 249 | let new_state = update(state, accumulator, product, &[false, true, true, false], &[2, 3, 4, 5]); 250 | assert_eq!(new_state, subroutines::mod_exp(accumulator, h_0 * h_3 * h_4, U2048::from_dec_str(MODULUS).unwrap())); 251 | } 252 | 253 | } -------------------------------------------------------------------------------- /vector-commitment/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | 3 | /// Account-Based Stateless Blockchain 4 | /// ***DISCLOSURE*** This module is incomplete, untested, and completely experimental. 5 | 6 | use support::{decl_module, decl_storage, decl_event, ensure, dispatch::Result, StorageValue, traits::Get}; 7 | use system::ensure_signed; 8 | use codec::{Encode, Decode}; 9 | use accumulator::*; 10 | pub mod binary; 11 | pub mod vc; 12 | 13 | #[cfg_attr(feature = "std", derive(Debug))] 14 | #[derive(Default, Clone, Encode, Decode, PartialEq, Eq)] 15 | pub struct Transaction { 16 | sender_key: u8, 17 | sender_balance: u8, 18 | sender_elem: U2048, 19 | sender_opening: (Witness, Witness), 20 | receiver_key: u8, 21 | receiver_balance: u8, 22 | receiver_elem: U2048, 23 | receiver_opening: (Witness, Witness), 24 | amount: u8, 25 | } 26 | 27 | /// The module's configuration trait. 28 | pub trait Trait: system::Trait { 29 | type Event: From + Into<::Event>; 30 | type KeySpace: Get; 31 | } 32 | 33 | // This module's storage items. 34 | decl_storage! { 35 | trait Store for Module as StatelessAccounts { 36 | State get(get_state): U2048 = U2048::from(2); // Use 2 as an arbitrary generator with "unknown" order. 37 | WitnessData get(get_witness_data): Vec<(U2048, U2048)>; 38 | NewKeyValuePairs: Vec<(u8, u8)>; 39 | } 40 | } 41 | 42 | decl_event!( 43 | pub enum Event { 44 | TokensMinted(U2048, U2048), 45 | Deletion(U2048, U2048, U2048), 46 | Addition(U2048, U2048, U2048), 47 | } 48 | ); 49 | 50 | decl_module! { 51 | /// The module declaration. 52 | pub struct Module for enum Call where origin: T::Origin { 53 | fn deposit_event() = default; 54 | const KeySpace: u8 = T::KeySpace::get(); 55 | 56 | /// Arbitrarily add a new key-value store to the accumulator. 57 | /// NOTE: The key must not exist initially. 58 | pub fn mint(origin, key: u8, amount: u8) -> Result { 59 | ensure_signed(origin)?; 60 | let (state, product) = vc::commit(State::get(), &[key as usize], &[amount]); 61 | State::put(state); 62 | Self::deposit_event(Event::TokensMinted(state, product)); 63 | Ok(()) 64 | } 65 | 66 | /// Submit a transaction to the chain. 67 | /// NOTE: All transactions must be referenced from the same previous "state". In practice, 68 | /// this might be the state of the previous block for example. This is a workaround to 69 | /// prevent having to pass in the product of all of the elements in the accumulator. 70 | pub fn add_transaction(origin, transaction: Transaction, old_state: U2048) -> Result { 71 | ensure_signed(origin)?; 72 | // Get the opening of the sender 73 | let (pi_i_sender, pi_e_sender) = transaction.sender_opening; 74 | 75 | // Verify that it is valid 76 | ensure!(vc::verify_at_key(old_state, State::get(), transaction.sender_key as usize, 77 | transaction.sender_balance, pi_i_sender, pi_e_sender), "Opening is invalid."); 78 | 79 | // Ensure that the sender isn't spending more than balance 80 | ensure!(transaction.sender_balance >= transaction.amount, "User is trying to spend more than balance."); 81 | 82 | // Verify receiver opening 83 | let (pi_i_receiver, pi_e_receiver) = transaction.receiver_opening; 84 | ensure!(vc::verify_at_key(old_state, State::get(), transaction.receiver_key as usize, 85 | transaction.receiver_balance, pi_i_receiver, pi_e_receiver), "Opening is invalid."); 86 | 87 | // Add membership proofs to temporary vector to be processed later 88 | if let Witness::MemWit(sender_witness) = pi_i_sender { 89 | WitnessData::append(&vec![(transaction.sender_elem, sender_witness)]); 90 | } 91 | 92 | if let Witness::MemWit(receiver_witness) = pi_i_receiver { 93 | WitnessData::append(&vec![(transaction.receiver_elem, receiver_witness)]); 94 | } 95 | 96 | // Currently omitting non-membership proofs for simplicity 97 | 98 | // Temporarily store the new key-value pairs 99 | NewKeyValuePairs::append(&vec![(transaction.sender_key, transaction.sender_balance-transaction.amount)]); 100 | NewKeyValuePairs::append(&vec![(transaction.receiver_key, transaction.receiver_balance+transaction.amount)]); 101 | Ok(()) 102 | } 103 | 104 | fn on_finalize() { 105 | // Remove previous key-value commitment. 106 | let (state, product, proof) = accumulator::batch_delete(State::get(), &WitnessData::get()); 107 | Self::deposit_event(Event::Deletion(state, product, proof)); 108 | 109 | // Get the integer representations of the new key-value pairs. 110 | let elems: Vec = NewKeyValuePairs::get() 111 | .into_iter() 112 | .enumerate() 113 | .map(|(_, (key, value))| -> U2048 { 114 | let (binary_vec, indices) = vc::convert_key_value(&[key as usize], &[value]); 115 | let (p_ones, _) = binary::get_bit_elems(&binary_vec, &indices); 116 | return p_ones; 117 | }) 118 | .collect(); 119 | 120 | // Add updated key-value pairs. 121 | let (state, product, proof) = accumulator::batch_add(state, &elems); 122 | Self::deposit_event(Event::Addition(state, product, proof)); 123 | 124 | // Update accumulator 125 | State::put(state); 126 | 127 | // Clear storage items 128 | WitnessData::kill(); 129 | NewKeyValuePairs::kill(); 130 | } 131 | } 132 | } 133 | 134 | /// Tests for this module 135 | #[cfg(test)] 136 | mod tests { 137 | use super::*; 138 | 139 | use runtime_io::with_externalities; 140 | use primitives::{H256, Blake2Hasher}; 141 | use support::{impl_outer_origin, assert_ok, parameter_types}; 142 | use sr_primitives::{ 143 | traits::{BlakeTwo256, IdentityLookup, OnFinalize}, testing::Header, weights::Weight, Perbill, 144 | }; 145 | 146 | impl_outer_origin! { 147 | pub enum Origin for Test {} 148 | } 149 | 150 | // For testing the module, we construct most of a mock runtime. This means 151 | // first constructing a configuration type (`Test`) which `impl`s each of the 152 | // configuration traits of modules we want to use. 153 | #[derive(Clone, Eq, PartialEq)] 154 | pub struct Test; 155 | parameter_types! { 156 | pub const BlockHashCount: u64 = 250; 157 | pub const MaximumBlockWeight: Weight = 1024; 158 | pub const MaximumBlockLength: u32 = 2 * 1024; 159 | pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75); 160 | pub const KeySpace: u8 = 255; 161 | } 162 | 163 | impl system::Trait for Test { 164 | type Origin = Origin; 165 | type Call = (); 166 | type Index = u64; 167 | type BlockNumber = u64; 168 | type Hash = H256; 169 | type Hashing = BlakeTwo256; 170 | type AccountId = u64; 171 | type Lookup = IdentityLookup; 172 | type Header = Header; 173 | type WeightMultiplierUpdate = (); 174 | type Event = (); 175 | type BlockHashCount = BlockHashCount; 176 | type MaximumBlockWeight = MaximumBlockWeight; 177 | type MaximumBlockLength = MaximumBlockLength; 178 | type AvailableBlockRatio = AvailableBlockRatio; 179 | type Version = (); 180 | } 181 | 182 | impl Trait for Test { 183 | type Event = (); 184 | type KeySpace = KeySpace; 185 | } 186 | 187 | type StatelessAccounts = Module; 188 | type System = system::Module; 189 | 190 | // This function basically just builds a genesis storage key/value store according to 191 | // our desired mockup. 192 | fn new_test_ext() -> runtime_io::TestExternalities { 193 | system::GenesisConfig::default().build_storage::().unwrap().into() 194 | } 195 | 196 | #[test] 197 | fn test_mint() { 198 | with_externalities(&mut new_test_ext(), || { 199 | let key: u8 = 1; 200 | let value: u8 = 10; 201 | StatelessAccounts::mint(Origin::signed(1), key, value); 202 | 203 | let (binary_vec, indices) = vc::convert_key_value(&[key as usize], &[value]); 204 | let (p_ones, _) = binary::get_bit_elems(&binary_vec, &indices); 205 | assert_eq!(StatelessAccounts::get_state(), subroutines::mod_exp(U2048::from(2), p_ones, U2048::from_dec_str(MODULUS).unwrap())); 206 | }); 207 | } 208 | 209 | #[test] 210 | fn test_transaction() { 211 | with_externalities(&mut new_test_ext(), || { 212 | let generator = StatelessAccounts::get_state(); 213 | 214 | // Define keys for alice and bob 215 | let alice_key: u8 = 12; 216 | let bob_key: u8 = 58; 217 | 218 | // Define balances for alice and bob 219 | let alice_balance: u8 = 10; 220 | let bob_balance: u8 = 5; 221 | 222 | // Mint tokens for each user 223 | StatelessAccounts::mint(Origin::signed(1), alice_key, alice_balance); 224 | StatelessAccounts::mint(Origin::signed(1), bob_key, bob_balance); 225 | 226 | // Derive integer representations for manual testing 227 | let alice_elem = vc::get_key_value_elem(alice_key as usize, alice_balance); // This value would be received from the emitted event. 228 | let bob_elem = vc::get_key_value_elem(bob_key as usize, bob_balance); // This value would be received from the emitted event. 229 | let product = alice_elem * bob_elem; 230 | 231 | // Get state after minting 232 | let state_after_mint = StatelessAccounts::get_state(); 233 | 234 | // Get openings for each user 235 | let (alice_pi_i, alice_pi_e) = vc::open_at_key(generator, product, alice_key as usize, alice_balance); 236 | let (bob_pi_i, bob_pi_e) = vc::open_at_key(generator, product, bob_key as usize, bob_balance); 237 | 238 | // Construct transaction 239 | let transaction = Transaction { 240 | sender_key: alice_key, 241 | sender_balance: alice_balance, 242 | sender_elem: alice_elem, 243 | sender_opening: (alice_pi_i, alice_pi_e), 244 | receiver_key: bob_key, 245 | receiver_balance: bob_balance, 246 | receiver_elem: bob_elem, 247 | receiver_opening: (bob_pi_i, bob_pi_e), 248 | amount: 3, 249 | }; 250 | 251 | // Submit transaction 252 | StatelessAccounts::add_transaction(Origin::signed(1), transaction, generator); 253 | 254 | // Manually get the state after deletion for manual testing 255 | let (state_after_del, _, _) = batch_delete(state_after_mint, &StatelessAccounts::get_witness_data()); 256 | 257 | // Finalize block 258 | StatelessAccounts::on_finalize(System::block_number()); 259 | 260 | // Get the new state 261 | let new_state = StatelessAccounts::get_state(); 262 | 263 | // Derive integer representations for alice and bob's new key-value stores 264 | let new_alice_elem = vc::get_key_value_elem(alice_key as usize, alice_balance-3); // This value would be received from the emitted event. 265 | let new_bob_elem = vc::get_key_value_elem(bob_key as usize, bob_balance+3); // This value would be received from the emitted event. 266 | 267 | // Create openings with the new balances 268 | let (alice_pi_i_new, alice_pi_e_new) = vc::open_at_key(state_after_del, new_alice_elem*new_bob_elem, alice_key as usize, alice_balance-3); 269 | let (bob_pi_i_new, bob_pi_e_new) = vc::open_at_key(state_after_del, new_alice_elem*new_bob_elem, bob_key as usize, bob_balance+3); 270 | 271 | // Verify that the openings are valid 272 | assert_eq!(vc::verify_at_key(state_after_del, new_state, alice_key as usize, alice_balance-3, alice_pi_i_new, alice_pi_e_new), true); 273 | assert_eq!(vc::verify_at_key(state_after_del, new_state, bob_key as usize, bob_balance+3, bob_pi_i_new, bob_pi_e_new), true); 274 | }); 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /vector-commitment/src/vc.rs: -------------------------------------------------------------------------------- 1 | /// Vector Commitments for Arbitrary Integer Values 2 | /// Acts as a wrapper for binary vector commitments. 3 | 4 | use accumulator::*; 5 | use rstd::prelude::Vec; 6 | use bit_vec::BitVec; 7 | use crate::binary; 8 | 9 | type ValueType = u8; 10 | 11 | /// Commit to a set of keys and corresponding values. 12 | pub fn commit(accumulator: U2048, keys: &[usize], values: &[ValueType]) -> (U2048, U2048) { 13 | let (binary_vec, indices) = convert_key_value(keys, values); 14 | return binary::commit(accumulator, &binary_vec, &indices); 15 | } 16 | 17 | /// Open a commitment for a value at a specific key. This function would be immediately called by a 18 | /// user following a relevant state commitment. 19 | pub fn open_at_key(old_state: U2048, product: U2048, key: usize, value: ValueType) -> (Witness, Witness) { 20 | let (binary_vec, indices) = convert_key_value(&[key], &[value]); 21 | return binary::batch_open(old_state, product, &binary_vec, &indices); 22 | } 23 | 24 | /// Verify a commitment for a value at a specific key. 25 | pub fn verify_at_key(old_state: U2048, accumulator: U2048, key: usize, value: ValueType, pi_i: Witness, pi_e: Witness) -> bool { 26 | let (binary_vec, indices) = convert_key_value(&[key], &[value]); 27 | return binary::batch_verify(old_state, accumulator, &binary_vec, &indices, pi_i, pi_e); 28 | } 29 | 30 | /// Update the values for a set of keys. Assumes key-value pairs are valid. 31 | pub fn update(accumulator: U2048, old_state: U2048, agg: U2048, keys: &[usize], values: &[ValueType]) -> U2048 { 32 | let (binary_vec, indices) = convert_key_value(keys, values); 33 | return binary::update(accumulator, old_state, agg, &binary_vec, &indices); 34 | } 35 | 36 | /// Converts key-value pairs into a binary representation of the values along with corresponding 37 | /// indices. 38 | pub fn convert_key_value(keys: &[usize], values: &[ValueType]) -> (Vec, Vec) { 39 | let mut binary_vec: Vec = [].to_vec(); 40 | let mut indices: Vec = [].to_vec(); 41 | for (i, &value) in values.iter().enumerate() { 42 | let mut value_vec = to_binary(value); 43 | let offset = core::mem::size_of::()*8; 44 | let mut index_vec = (keys[i]*offset..keys[i]*offset+offset).collect(); 45 | binary_vec.append(&mut value_vec); 46 | indices.append(&mut index_vec); 47 | } 48 | return (binary_vec, indices); 49 | } 50 | 51 | /// Converts an element to a binary representation. 52 | pub fn to_binary(elem: ValueType) -> Vec { 53 | let byte_vec = elem.to_le_bytes().to_vec(); 54 | let bv = BitVec::from_bytes(&byte_vec); 55 | return bv.iter().collect::>(); 56 | } 57 | 58 | /// Quick helper function that gets the product of the accumulated elements for a given 59 | /// key-value pair. 60 | pub fn get_key_value_elem(key: usize, value: ValueType) -> U2048 { 61 | let (binary_vec, indices) = convert_key_value(&[key], &[value]); 62 | let (elem, _) = binary::get_bit_elems(&binary_vec, &indices); 63 | return elem; 64 | } 65 | 66 | #[cfg(test)] 67 | mod tests { 68 | use super::*; 69 | 70 | #[test] 71 | fn test_to_binary() { 72 | let elem: ValueType = 6; 73 | let bv = to_binary(elem); 74 | assert_eq!(bv, vec![false, false, false, false, false, true, true, false]); 75 | } 76 | 77 | #[test] 78 | fn test_commit() { 79 | let accumulator: U2048 = U2048::from(2); 80 | let keys = [0, 1]; 81 | let values = vec![4, 7]; 82 | 83 | let (new_accumulator, _) = commit(accumulator, &keys, &values); 84 | 85 | // Manual check 86 | let check_product = subroutines::hash_to_prime(&(5 as usize).to_le_bytes()) 87 | * subroutines::hash_to_prime(&(13 as usize).to_le_bytes()) 88 | * subroutines::hash_to_prime(&(14 as usize).to_le_bytes()) 89 | * subroutines::hash_to_prime(&(15 as usize).to_le_bytes()); 90 | 91 | assert_eq!(new_accumulator, subroutines::mod_exp(U2048::from(2), U2048::from(check_product), U2048::from_dec_str(MODULUS).unwrap())); 92 | } 93 | 94 | #[test] 95 | fn test_convert() { 96 | let keys = vec![0, 1]; 97 | let values = vec![4, 7]; 98 | let (binary_vec, indices) = convert_key_value(&keys, &values); 99 | assert_eq!(binary_vec, vec![false, false, false, false, false, true, false, false, false, false, false, false, 100 | false, true, true, true]); 101 | assert_eq!(indices, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); 102 | } 103 | 104 | #[test] 105 | fn test_vc_open_and_verify() { 106 | let accumulator: U2048 = U2048::from(2); 107 | let keys = vec![0, 1]; 108 | let values = vec![4, 7]; 109 | let (new_accumulator, product) = commit(accumulator, &keys, &values); 110 | 111 | let (pi_i, pi_e) = open_at_key(accumulator, product, 1, 7); 112 | 113 | assert_eq!(verify_at_key(accumulator, new_accumulator, 1, 7, pi_i, pi_e), true); 114 | assert_eq!(verify_at_key(accumulator, new_accumulator, 0, 7, pi_i, pi_e), false); 115 | assert_eq!(verify_at_key(accumulator, new_accumulator, 1, 4, pi_i, pi_e), false); 116 | } 117 | 118 | #[test] 119 | fn test_get_key_value_elem() { 120 | let (key, value): (usize, u8) = (0, 5); 121 | let elem = get_key_value_elem(key, value); 122 | 123 | let bv = to_binary(value); 124 | let indices: Vec = (0..8).collect(); 125 | let (state, _) = binary::commit(U2048::from(2), &bv, &indices); 126 | 127 | assert_eq!(state, subroutines::mod_exp(U2048::from(2), elem, U2048::from_dec_str(MODULUS).unwrap())) 128 | } 129 | 130 | } --------------------------------------------------------------------------------