Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

beautify TuxedoGenesisConfig #128

Merged
merged 5 commits into from
Nov 12, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
50 changes: 46 additions & 4 deletions node/src/chain_spec.rs
Original file line number Diff line number Diff line change
@@ -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,
RuntimeGenesisConfig, 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<TuxedoGenesisConfig>;
pub type ChainSpec = sc_service::GenericChainSpec<RuntimeGenesisConfig>;

// /// Generate a crypto pair from seed.
// pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
Expand All @@ -29,14 +40,44 @@ pub type ChainSpec = sc_service::GenericChainSpec<TuxedoGenesisConfig>;
// (get_from_seed::<AuraId>(s), get_from_seed::<GrandpaId>(s))
// }

muraca marked this conversation as resolved.
Show resolved Hide resolved
const SHAWN_PUB_KEY_BYTES: [u8; 32] =
hex!("d2bf4b844dfefd6772a8843e669f943408966a977e3ae2af1dd78e0f55f4df67");
const ANDREW_PUB_KEY_BYTES: [u8; 32] =
hex!("baa81e58b1b4d053c2e86d93045765036f9d265c7dfe8b9693bbc2c0f048d93a");

fn development_genesis_config() -> TuxedoGenesisConfig<OuterVerifier, OuterConstraintChecker> {
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<ChainSpec, String> {
Ok(ChainSpec::from_genesis(
// Name
"Development",
// ID
"dev",
ChainType::Development,
TuxedoGenesisConfig::default,
// TuxedoGenesisConfig
development_genesis_config,
// Bootnodes
vec![],
// Telemetry
Expand All @@ -58,7 +99,8 @@ pub fn local_testnet_config() -> Result<ChainSpec, String> {
// ID
"local_testnet",
ChainType::Local,
TuxedoGenesisConfig::default,
// TuxedoGenesisConfig
development_genesis_config,
// Bootnodes
vec![],
// Telemetry
Expand Down
103 changes: 77 additions & 26 deletions tuxedo-core/src/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -97,31 +97,82 @@ impl<'a, Block: BlockT, B: Backend<Block>, 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<V: Encode + TypeInfo, C: Encode + TypeInfo>(
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<V, C> {
wasm_binary: Vec<u8>,
genesis_transactions: Vec<Transaction<V, C>>,
) -> 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<V, C> TuxedoGenesisConfig<V, C> {
/// 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<u8>, genesis_transactions: Vec<Transaction<V, C>>) -> Self {
Self {
wasm_binary,
genesis_transactions,
}
}

Ok(())
pub fn get_transaction(&self, i: usize) -> Option<&Transaction<V, C>> {
self.genesis_transactions.get(i)
}
}

impl<V, C> BuildStorage for TuxedoGenesisConfig<V, C>
where
V: Verifier,
C: ConstraintChecker<V>,
Transaction<V, C>: Encode,
Output<V>: 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(),
);

// 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(())
}
}
Loading
Loading