diff --git a/Cargo.lock b/Cargo.lock index cb0016809..ca36691bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4189,6 +4189,7 @@ name = "node-template" version = "4.0.0-dev" dependencies = [ "clap", + "hex-literal", "jsonrpsee", "sc-basic-authorship", "sc-cli", diff --git a/node/Cargo.toml b/node/Cargo.toml index 1d3b22412..2dd0eb514 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -12,6 +12,7 @@ targets = [ "x86_64-unknown-linux-gnu" ] [dependencies] clap = { features = [ "derive" ], workspace = true } +hex-literal = { workspace = true } sc-cli = { workspace = true } sc-client-api = { workspace = true } diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs index b2bdcd115..a738fb514 100644 --- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -1,11 +1,22 @@ -use node_template_runtime::TuxedoGenesisConfig; +use hex_literal::hex; +use node_template_runtime::{ + kitties::{KittyData, Parent}, + money::Coin, + OuterConstraintChecker, OuterConstraintCheckerInherentHooks, OuterVerifier, WASM_BINARY, +}; use sc_service::ChainType; +use tuxedo_core::{ + genesis::TuxedoGenesisConfig, + inherents::InherentInternal, + verifier::{SigCheck, ThresholdMultiSignature, UpForGrabs}, +}; // The URL for the telemetry server. // const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. -pub type ChainSpec = sc_service::GenericChainSpec; +pub type ChainSpec = + sc_service::GenericChainSpec>; // /// Generate a crypto pair from seed. // pub fn get_from_seed(seed: &str) -> ::Public { @@ -29,6 +40,35 @@ pub type ChainSpec = sc_service::GenericChainSpec; // (get_from_seed::(s), get_from_seed::(s)) // } +const SHAWN_PUB_KEY_BYTES: [u8; 32] = + hex!("d2bf4b844dfefd6772a8843e669f943408966a977e3ae2af1dd78e0f55f4df67"); +const ANDREW_PUB_KEY_BYTES: [u8; 32] = + hex!("baa81e58b1b4d053c2e86d93045765036f9d265c7dfe8b9693bbc2c0f048d93a"); + +fn development_genesis_config() -> TuxedoGenesisConfig { + let signatories = vec![SHAWN_PUB_KEY_BYTES.into(), ANDREW_PUB_KEY_BYTES.into()]; + + // The inherents are computed using the appropriate method, and placed before the extrinsics. + let mut genesis_transactions = OuterConstraintCheckerInherentHooks::genesis_transactions(); + + genesis_transactions.extend([ + // Money Transactions + Coin::<0>::mint(100, SigCheck::new(SHAWN_PUB_KEY_BYTES)), + Coin::<0>::mint(100, ThresholdMultiSignature::new(1, signatories)), + // Kitty Transactions + KittyData::mint(Parent::mom(), b"mother", UpForGrabs), + KittyData::mint(Parent::dad(), b"father", UpForGrabs), + // TODO: Initial Transactions for Existence + ]); + + TuxedoGenesisConfig::new( + WASM_BINARY + .expect("Runtime WASM binary must exist.") + .to_vec(), + genesis_transactions, + ) +} + pub fn development_config() -> Result { Ok(ChainSpec::from_genesis( // Name @@ -36,7 +76,8 @@ pub fn development_config() -> Result { // ID "dev", ChainType::Development, - TuxedoGenesisConfig::default, + // TuxedoGenesisConfig + development_genesis_config, // Bootnodes vec![], // Telemetry @@ -58,7 +99,8 @@ pub fn local_testnet_config() -> Result { // ID "local_testnet", ChainType::Local, - TuxedoGenesisConfig::default, + // TuxedoGenesisConfig + development_genesis_config, // Bootnodes vec![], // Telemetry diff --git a/tuxedo-core/src/genesis.rs b/tuxedo-core/src/genesis.rs index e4117397f..9a8c0d372 100644 --- a/tuxedo-core/src/genesis.rs +++ b/tuxedo-core/src/genesis.rs @@ -2,14 +2,14 @@ use crate::{ ensure, - types::{OutputRef, Transaction}, - EXTRINSIC_KEY, + types::{Output, OutputRef, Transaction}, + ConstraintChecker, Verifier, EXTRINSIC_KEY, }; use parity_scale_codec::{Decode, Encode}; use sc_chain_spec::BuildGenesisBlock; use sc_client_api::backend::{Backend, BlockImportOperation}; use sc_executor::RuntimeVersionOf; -use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; use sp_core::{storage::Storage, traits::CodeExecutor}; use sp_runtime::{ traits::{BlakeTwo256, Block as BlockT, Hash as HashT, Header as HeaderT, Zero}, @@ -97,31 +97,78 @@ impl<'a, Block: BlockT, B: Backend, E: RuntimeVersionOf + CodeExecutor> } } -/// Assimilate the storage into the genesis block. -/// This is done by inserting the genesis extrinsics into the genesis block, along with their outputs. -/// Make sure to pass the transactions in order: the inherents should be first, then the extrinsics. -pub fn assimilate_storage( - storage: &mut Storage, +#[derive(Serialize, Deserialize)] +/// The `TuxedoGenesisConfig` struct is used to configure the genesis state of the runtime. +/// It expects the wasm binary and a list of transactions to be included in the genesis block, and stored along with their outputs. +/// They must not contain any inputs or peeks. These transactions will not be validated by the corresponding ConstraintChecker or Verifier. +/// Make sure to pass the inherents before the extrinsics. +pub struct TuxedoGenesisConfig { + wasm_binary: Vec, genesis_transactions: Vec>, -) -> Result<(), String> { - storage - .top - .insert(EXTRINSIC_KEY.to_vec(), genesis_transactions.encode()); - - for tx in genesis_transactions { - ensure!( - tx.inputs.is_empty() && tx.peeks.is_empty(), - "Genesis transactions must not have any inputs or peeks." - ); - let tx_hash = BlakeTwo256::hash_of(&tx.encode()); - for (index, utxo) in tx.outputs.iter().enumerate() { - let output_ref = OutputRef { - tx_hash, - index: index as u32, - }; - storage.top.insert(output_ref.encode(), utxo.encode()); +} + +impl TuxedoGenesisConfig { + /// Create a new `TuxedoGenesisConfig` from a WASM binary and a list of transactions. + /// Make sure to pass the transactions in order: the inherents should be first, then the extrinsics. + pub fn new(wasm_binary: Vec, genesis_transactions: Vec>) -> Self { + Self { + wasm_binary, + genesis_transactions, } } +} + +impl BuildStorage for TuxedoGenesisConfig +where + V: Verifier, + C: ConstraintChecker, + Transaction: Encode, + Output: Encode, +{ + /// Assimilate the storage into the genesis block. + /// This is done by inserting the genesis extrinsics into the genesis block, along with their outputs. + fn assimilate_storage(&self, storage: &mut Storage) -> Result<(), String> { + // The wasm binary is stored under a special key. + storage.top.insert( + sp_storage::well_known_keys::CODE.into(), + self.wasm_binary.clone(), + ); - Ok(()) + // The transactions are stored under a special key. + storage + .top + .insert(EXTRINSIC_KEY.to_vec(), self.genesis_transactions.encode()); + + let mut finished_with_opening_inherents = false; + + for tx in self.genesis_transactions.iter() { + // Enforce that inherents are in the right place + let current_tx_is_inherent = tx.checker.is_inherent(); + if current_tx_is_inherent && finished_with_opening_inherents { + return Err( + "Tried to execute opening inherent after switching to non-inherents.".into(), + ); + } + if !current_tx_is_inherent && !finished_with_opening_inherents { + // This is the first non-inherent, so we update our flag and continue. + finished_with_opening_inherents = true; + } + // Enforce that transactions do not have any inputs or peeks. + ensure!( + tx.inputs.is_empty() && tx.peeks.is_empty(), + "Genesis transactions must not have any inputs or peeks." + ); + // Insert the outputs into the storage. + let tx_hash = BlakeTwo256::hash_of(&tx.encode()); + for (index, utxo) in tx.outputs.iter().enumerate() { + let output_ref = OutputRef { + tx_hash, + index: index as u32, + }; + storage.top.insert(output_ref.encode(), utxo.encode()); + } + } + + Ok(()) + } } diff --git a/tuxedo-template-runtime/src/lib.rs b/tuxedo-template-runtime/src/lib.rs index a7c92e8a1..622ea7eca 100644 --- a/tuxedo-template-runtime/src/lib.rs +++ b/tuxedo-template-runtime/src/lib.rs @@ -11,10 +11,12 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_consensus_grandpa::AuthorityId as GrandpaId; use sp_api::impl_runtime_apis; +use sp_core::OpaqueMetadata; use sp_inherents::InherentData; use sp_runtime::{ create_runtime_str, impl_opaque_keys, @@ -24,16 +26,10 @@ use sp_runtime::{ }; use sp_std::prelude::*; -use sp_core::OpaqueMetadata; -#[cfg(any(feature = "std", test))] -use sp_runtime::{BuildStorage, Storage}; - #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use serde::{Deserialize, Serialize}; - use tuxedo_core::{ tuxedo_constraint_checker, tuxedo_verifier, types::Transaction as TuxedoTransaction, @@ -100,58 +96,6 @@ pub fn native_version() -> NativeVersion { } } -#[derive(Serialize, Deserialize)] -/// The `TuxedoGenesisConfig` struct is used to configure the genesis state of the runtime. -/// The only parameter is a list of transactions to be included in the genesis block, and stored along with their outputs. -/// They must not contain any inputs or peeks. These transactions will not be validated by the corresponding ConstraintChecker or Verifier. -pub struct TuxedoGenesisConfig(pub Vec); - -impl Default for TuxedoGenesisConfig { - fn default() -> Self { - use hex_literal::hex; - use kitties::{KittyData, Parent}; - use money::Coin; - - const SHAWN_PUB_KEY_BYTES: [u8; 32] = - hex!("d2bf4b844dfefd6772a8843e669f943408966a977e3ae2af1dd78e0f55f4df67"); - const ANDREW_PUB_KEY_BYTES: [u8; 32] = - hex!("baa81e58b1b4d053c2e86d93045765036f9d265c7dfe8b9693bbc2c0f048d93a"); - let signatories = vec![SHAWN_PUB_KEY_BYTES.into(), ANDREW_PUB_KEY_BYTES.into()]; - - let genesis_transactions = vec![ - // Money Transactions - Coin::<0>::mint(100, SigCheck::new(SHAWN_PUB_KEY_BYTES)), - Coin::<0>::mint(100, ThresholdMultiSignature::new(1, signatories)), - // Kitty Transactions - KittyData::mint(Parent::mom(), b"mother", UpForGrabs), - KittyData::mint(Parent::dad(), b"father", UpForGrabs), - ]; - // TODO: Initial Transactions for Existence - - TuxedoGenesisConfig(genesis_transactions) - } -} - -#[cfg(feature = "std")] -impl BuildStorage for TuxedoGenesisConfig { - fn assimilate_storage(&self, storage: &mut Storage) -> Result<(), String> { - use tuxedo_core::inherents::InherentInternal; - - // The wasm binary is stored under a special key. - storage.top.insert( - sp_storage::well_known_keys::CODE.into(), - WASM_BINARY.unwrap().to_vec(), - ); - - // The inherents transactions are computed using the appropriate method, - // and placed in the block before the normal transactions. - let mut genesis_transactions = OuterConstraintCheckerInherentHooks::genesis_transactions(); - genesis_transactions.extend(self.0.clone()); - - tuxedo_core::genesis::assimilate_storage(storage, genesis_transactions) - } -} - pub type Transaction = TuxedoTransaction; pub type BlockNumber = u32; pub type Header = sp_runtime::generic::Header;