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

Genesis Transactions #127

Merged
merged 20 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from 18 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
4 changes: 4 additions & 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ substrate-wasm-builder = { git = "https://github.com/paritytech/polkadot-sdk", t

# Substrate primitives and client
sc-basic-authorship = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0" }
sc-chain-spec = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0" }
sc-cli = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0" }
sc-client-api = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0" }
sc-consensus = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-v1.1.0" }
Expand Down
8 changes: 4 additions & 4 deletions node/src/chain_spec.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use node_template_runtime::GenesisConfig;
use node_template_runtime::TuxedoGenesisConfig;
use sc_service::ChainType;

// 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<GenesisConfig>;
pub type ChainSpec = sc_service::GenericChainSpec<TuxedoGenesisConfig>;

// /// Generate a crypto pair from seed.
// pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
Expand Down Expand Up @@ -36,7 +36,7 @@ pub fn development_config() -> Result<ChainSpec, String> {
// ID
"dev",
ChainType::Development,
GenesisConfig::default,
TuxedoGenesisConfig::default,
// Bootnodes
vec![],
// Telemetry
Expand All @@ -58,7 +58,7 @@ pub fn local_testnet_config() -> Result<ChainSpec, String> {
// ID
"local_testnet",
ChainType::Local,
GenesisConfig::default,
TuxedoGenesisConfig::default,
// Bootnodes
vec![],
// Telemetry
Expand Down
13 changes: 12 additions & 1 deletion node/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use sc_telemetry::{Telemetry, TelemetryWorker};
use sc_transaction_pool_api::OffchainTransactionPoolFactory;
use sp_consensus_aura::sr25519::AuthorityPair as AuraPair;
use std::{sync::Arc, time::Duration};
use tuxedo_core::genesis::TuxedoGenesisBlockBuilder;

// Our native executor instance.
pub struct ExecutorDispatch;
Expand Down Expand Up @@ -72,11 +73,21 @@ pub fn new_partial(

let executor = sc_service::new_native_or_wasm_executor(config);

let backend = sc_service::new_db_backend(config.db_config())?;
let genesis_block_builder = TuxedoGenesisBlockBuilder::new(
config.chain_spec.as_storage_builder(),
!config.no_genesis(),
backend.clone(),
executor.clone(),
)?;

let (client, backend, keystore_container, task_manager) =
sc_service::new_full_parts::<Block, RuntimeApi, _>(
sc_service::new_full_parts_with_genesis_builder::<Block, RuntimeApi, _, _>(
config,
telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
executor,
backend,
genesis_block_builder,
)?;
let client = Arc::new(client);

Expand Down
10 changes: 10 additions & 0 deletions tuxedo-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ sp-runtime = { default_features = false, workspace = true }
sp-std = { default_features = false, workspace = true }
sp-storage = { default_features = false, workspace = true }

# Genesis Builder dependencies
sc-chain-spec = { optional = true, workspace = true }
sc-client-api = { optional = true, workspace = true }
sc-executor = { optional = true, workspace = true }
sp-blockchain = { optional = true, workspace = true }

[dev-dependencies]
array-bytes = { workspace = true }

Expand All @@ -44,4 +50,8 @@ std = [
"sp-runtime/std",
"parity-util-mem",
"sp-storage/std",
"sc-client-api",
"sc-chain-spec",
"sc-executor",
"sp-blockchain",
]
21 changes: 20 additions & 1 deletion tuxedo-core/aggregator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ pub fn tuxedo_constraint_checker(attrs: TokenStream, body: TokenStream) -> Token
let inner_types3 = inner_types.clone();
let inner_types4 = inner_types.clone();
let inner_types6 = inner_types.clone();
let inner_types7 = inner_types.clone();
let variants2 = variants.clone();
let variants3 = variants.clone();
let variants4 = variants.clone();
Expand Down Expand Up @@ -240,6 +241,24 @@ pub fn tuxedo_constraint_checker(attrs: TokenStream, body: TokenStream) -> Token
)*
}

#[cfg(feature = "std")]
fn genesis_transactions() -> Vec<tuxedo_core::types::Transaction<#verifier, #outer_type>> {
let mut all_transactions: Vec<tuxedo_core::types::Transaction<#verifier, #outer_type>> = Vec::new();

#(
let transactions =
<<#inner_types6 as tuxedo_core::ConstraintChecker<#verifier>>::InherentHooks as tuxedo_core::inherents::InherentInternal<#verifier, #inner_types6>>::genesis_transactions();
all_transactions.extend(
transactions
.into_iter()
.map(|tx| tx.transform::<#outer_type>())
.collect::<Vec<_>>()
);
)*

all_transactions
}

}

impl tuxedo_core::ConstraintChecker<#verifier> for #outer_type {
Expand All @@ -263,7 +282,7 @@ pub fn tuxedo_constraint_checker(attrs: TokenStream, body: TokenStream) -> Token
fn is_inherent(&self) -> bool {
match self {
#(
Self::#variants6(inner) => <#inner_types6 as tuxedo_core::ConstraintChecker<#verifier>>::is_inherent(inner),
Self::#variants6(inner) => <#inner_types7 as tuxedo_core::ConstraintChecker<#verifier>>::is_inherent(inner),
)*
}

Expand Down
4 changes: 2 additions & 2 deletions tuxedo-core/src/executive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1050,8 +1050,8 @@ mod tests {
assert_eq!(returned_header, expected_header);

// Make sure the transient storage has been removed
assert!(!sp_io::storage::exists(&HEADER_KEY));
assert!(!sp_io::storage::exists(&EXTRINSIC_KEY));
assert!(!sp_io::storage::exists(HEADER_KEY));
assert!(!sp_io::storage::exists(EXTRINSIC_KEY));
});
}

Expand Down
127 changes: 127 additions & 0 deletions tuxedo-core/src/genesis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
//! Custom GenesisBlockBuilder for Tuxedo, to allow extrinsics to be added to the genesis block.

use crate::{
ensure,
types::{OutputRef, Transaction},
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 sp_core::{storage::Storage, traits::CodeExecutor};
use sp_runtime::{
traits::{BlakeTwo256, Block as BlockT, Hash as HashT, Header as HeaderT, Zero},
BuildStorage,
};
use std::sync::Arc;

pub struct TuxedoGenesisBlockBuilder<
'a,
Block: BlockT,
B: Backend<Block>,
E: RuntimeVersionOf + CodeExecutor,
> {
build_genesis_storage: &'a dyn BuildStorage,
commit_genesis_state: bool,
backend: Arc<B>,
executor: E,
_phantom: std::marker::PhantomData<Block>,
}

impl<'a, Block: BlockT, B: Backend<Block>, E: RuntimeVersionOf + CodeExecutor>
TuxedoGenesisBlockBuilder<'a, Block, B, E>
{
pub fn new(
build_genesis_storage: &'a dyn BuildStorage,
commit_genesis_state: bool,
backend: Arc<B>,
executor: E,
) -> sp_blockchain::Result<Self> {
Ok(Self {
build_genesis_storage,
commit_genesis_state,
backend,
executor,
_phantom: Default::default(),
})
}
}

impl<'a, Block: BlockT, B: Backend<Block>, E: RuntimeVersionOf + CodeExecutor>
BuildGenesisBlock<Block> for TuxedoGenesisBlockBuilder<'a, Block, B, E>
{
type BlockImportOperation = <B as Backend<Block>>::BlockImportOperation;

/// Build the genesis block, including the extrinsics found in storage at EXTRINSIC_KEY.
/// The extrinsics are not checked for validity, nor executed, so the values in storage must be placed manually.
/// This can be done by using the `assimilate_storage` function.
fn build_genesis_block(self) -> sp_blockchain::Result<(Block, Self::BlockImportOperation)> {
// We build it here to gain mutable access to the storage.
let mut genesis_storage = self
.build_genesis_storage
.build_storage()
.map_err(sp_blockchain::Error::Storage)?;

let state_version =
sc_chain_spec::resolve_state_version_from_wasm(&genesis_storage, &self.executor)?;

let extrinsics = match genesis_storage.top.remove(crate::EXTRINSIC_KEY) {
Some(v) => <Vec<<Block as BlockT>::Extrinsic>>::decode(&mut &v[..]).unwrap_or_default(),
None => Vec::new(),
};

let extrinsics_root =
<<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::ordered_trie_root(
extrinsics.iter().map(Encode::encode).collect(),
state_version,
);

let mut op = self.backend.begin_operation()?;
let state_root =
op.set_genesis_state(genesis_storage, self.commit_genesis_state, state_version)?;

let block = Block::new(
HeaderT::new(
Zero::zero(),
extrinsics_root,
state_root,
Default::default(),
Default::default(),
),
extrinsics,
);

Ok((block, op))
}
}

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

Ok(())
}
32 changes: 23 additions & 9 deletions tuxedo-core/src/inherents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,7 @@ pub trait TuxedoInherent<V, C: ConstraintChecker<V>>: Sized {
/// The inherent data is supplied by the authoring node.
fn create_inherent(
authoring_inherent_data: &InherentData,
// The option represents the so-called "first block hack".
// We need a way to initialize the chain with a first inherent on block one
// where there is no previous inherent. Once we introduce genesis extrinsics, this can be removed.
previous_inherent: Option<(Transaction<V, C>, H256)>,
previous_inherent: (Transaction<V, C>, H256),
) -> Transaction<V, C>;

/// Perform off-chain pre-execution checks on the inherent.
Expand All @@ -117,6 +114,12 @@ pub trait TuxedoInherent<V, C: ConstraintChecker<V>>: Sized {
inherent: Transaction<V, C>,
results: &mut CheckInherentsResult,
);

/// Return the genesis transactions that are required for this inherent.
#[cfg(feature = "std")]
fn genesis_transactions() -> Vec<Transaction<V, C>> {
Vec::new()
}
}

/// Almost identical to TuxedoInherent, but allows returning multiple extrinsics
Expand Down Expand Up @@ -144,6 +147,10 @@ pub trait InherentInternal<V, C: ConstraintChecker<V>>: Sized {
inherents: Vec<Transaction<V, C>>,
results: &mut CheckInherentsResult,
);

/// Return the genesis transactions that are required for the inherents.
#[cfg(feature = "std")]
fn genesis_transactions() -> Vec<Transaction<V, C>>;
}

/// An adapter to transform structured Tuxedo inherents into the more general and powerful
Expand All @@ -166,7 +173,7 @@ impl<V: Verifier, C: ConstraintChecker<V>, T: TuxedoInherent<V, C> + 'static> In

vec![<T as TuxedoInherent<V, C>>::create_inherent(
authoring_inherent_data,
previous_inherent,
previous_inherent.expect("Previous inherent exists."),
)]
}

Expand All @@ -191,12 +198,14 @@ impl<V: Verifier, C: ConstraintChecker<V>, T: TuxedoInherent<V, C> + 'static> In
.expect("Should be able to put an error.");
return;
}
let inherent = inherents
.get(0)
.expect("We already checked the bounds.")
.clone();
let inherent = inherents.get(0).expect("Previous inherent exists.").clone();
<T as TuxedoInherent<V, C>>::check_inherent(importing_inherent_data, inherent, results)
}

#[cfg(feature = "std")]
fn genesis_transactions() -> Vec<Transaction<V, C>> {
<T as TuxedoInherent<V, C>>::genesis_transactions()
}
}

impl<V, C: ConstraintChecker<V>> InherentInternal<V, C> for () {
Expand All @@ -219,4 +228,9 @@ impl<V, C: ConstraintChecker<V>> InherentInternal<V, C> for () {
"inherent extrinsic was passed to check inherents stub implementation."
)
}

#[cfg(feature = "std")]
fn genesis_transactions() -> Vec<Transaction<V, C>> {
Vec::new()
}
}
3 changes: 3 additions & 0 deletions tuxedo-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ pub mod types;
pub mod utxo_set;
pub mod verifier;

#[cfg(feature = "std")]
pub mod genesis;

pub use aggregator::{aggregate, tuxedo_constraint_checker, tuxedo_verifier};
pub use constraint_checker::{ConstraintChecker, SimpleConstraintChecker};
pub use executive::Executive;
Expand Down
9 changes: 9 additions & 0 deletions tuxedo-core/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ impl<V: Default> From<DynamicallyTypedData> for Output<V> {
}
}

impl<V, V1: Into<V>, P: Into<DynamicallyTypedData>> From<(P, V1)> for Output<V> {
fn from(values: (P, V1)) -> Self {
Self {
payload: values.0.into(),
verifier: values.1.into(),
}
}
}

#[cfg(test)]
pub mod tests {

Expand Down
Loading
Loading