Mixnet integration (#1346)

See #1345, <https://github.com/paritytech/substrate/pull/14207>.

This adds all the necessary mixnet components, and puts them together in
the "kitchen-sink" node/runtime. The components added are:

- A pallet (`frame/mixnet`). This is responsible for determining the
current mixnet session and phase, and the mixnodes to use in each
session. It provides a function that validators can call to register a
mixnode for the next session. The logic of this pallet is very similar
to that of the `im-online` pallet.
- A service (`client/mixnet`). This implements the core mixnet logic,
building on the `mixnet` crate. The service communicates with other
nodes using notifications sent over the "mixnet" protocol.
- An RPC interface. This currently only supports sending transactions
over the mixnet.

---------

Co-authored-by: David Emett <dave@sp4m.net>
Co-authored-by: Javier Viola <javier@parity.io>
This commit is contained in:
David Emett
2023-10-09 15:56:30 +02:00
committed by GitHub
parent 1dc935c715
commit a808a3a091
52 changed files with 3010 additions and 109 deletions
+28 -4
View File
@@ -33,6 +33,7 @@ use serde::{Deserialize, Serialize};
use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
use sp_consensus_babe::AuthorityId as BabeId;
use sp_core::{crypto::UncheckedInto, sr25519, Pair, Public};
use sp_mixnet::types::AuthorityId as MixnetId;
use sp_runtime::{
traits::{IdentifyAccount, Verify},
Perbill,
@@ -72,8 +73,9 @@ fn session_keys(
babe: BabeId,
im_online: ImOnlineId,
authority_discovery: AuthorityDiscoveryId,
mixnet: MixnetId,
) -> SessionKeys {
SessionKeys { grandpa, babe, im_online, authority_discovery }
SessionKeys { grandpa, babe, im_online, authority_discovery, mixnet }
}
fn staging_testnet_config_genesis() -> RuntimeGenesisConfig {
@@ -93,6 +95,7 @@ fn staging_testnet_config_genesis() -> RuntimeGenesisConfig {
BabeId,
ImOnlineId,
AuthorityDiscoveryId,
MixnetId,
)> = vec![
(
// 5Fbsd6WXDGiLTxunqeK5BATNiocfCqu9bS1yArVjCgeBLkVy
@@ -111,6 +114,9 @@ fn staging_testnet_config_genesis() -> RuntimeGenesisConfig {
// 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8
array_bytes::hex2array_unchecked("6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106")
.unchecked_into(),
// 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8
array_bytes::hex2array_unchecked("6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106")
.unchecked_into(),
),
(
// 5ERawXCzCWkjVq3xz1W5KGNtVx2VdefvZ62Bw1FEuZW4Vny2
@@ -129,6 +135,9 @@ fn staging_testnet_config_genesis() -> RuntimeGenesisConfig {
// 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ
array_bytes::hex2array_unchecked("482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e")
.unchecked_into(),
// 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ
array_bytes::hex2array_unchecked("482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e")
.unchecked_into(),
),
(
// 5DyVtKWPidondEu8iHZgi6Ffv9yrJJ1NDNLom3X9cTDi98qp
@@ -147,6 +156,9 @@ fn staging_testnet_config_genesis() -> RuntimeGenesisConfig {
// 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH
array_bytes::hex2array_unchecked("482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a")
.unchecked_into(),
// 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH
array_bytes::hex2array_unchecked("482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a")
.unchecked_into(),
),
(
// 5HYZnKWe5FVZQ33ZRJK1rG3WaLMztxWrrNDb1JRwaHHVWyP9
@@ -165,6 +177,9 @@ fn staging_testnet_config_genesis() -> RuntimeGenesisConfig {
// 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x
array_bytes::hex2array_unchecked("00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378")
.unchecked_into(),
// 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x
array_bytes::hex2array_unchecked("00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378")
.unchecked_into(),
),
];
@@ -217,7 +232,7 @@ where
/// Helper function to generate stash, controller and session key from seed.
pub fn authority_keys_from_seed(
seed: &str,
) -> (AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId) {
) -> (AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId, MixnetId) {
(
get_account_id_from_seed::<sr25519::Public>(&format!("{}//stash", seed)),
get_account_id_from_seed::<sr25519::Public>(seed),
@@ -225,6 +240,7 @@ pub fn authority_keys_from_seed(
get_from_seed::<BabeId>(seed),
get_from_seed::<ImOnlineId>(seed),
get_from_seed::<AuthorityDiscoveryId>(seed),
get_from_seed::<MixnetId>(seed),
)
}
@@ -237,6 +253,7 @@ pub fn testnet_genesis(
BabeId,
ImOnlineId,
AuthorityDiscoveryId,
MixnetId,
)>,
initial_nominators: Vec<AccountId>,
root_key: AccountId,
@@ -306,7 +323,13 @@ pub fn testnet_genesis(
(
x.0.clone(),
x.0.clone(),
session_keys(x.2.clone(), x.3.clone(), x.4.clone(), x.5.clone()),
session_keys(
x.2.clone(),
x.3.clone(),
x.4.clone(),
x.5.clone(),
x.6.clone(),
),
)
})
.collect::<Vec<_>>(),
@@ -367,6 +390,7 @@ pub fn testnet_genesis(
..Default::default()
},
glutton: Default::default(),
mixnet: Default::default(),
}
}
@@ -475,7 +499,7 @@ pub(crate) mod tests {
sc_service_test::connectivity(integration_test_config_with_two_authorities(), |config| {
let NewFullBase { task_manager, client, network, sync, transaction_pool, .. } =
new_full_base(config, false, |_, _| ())?;
new_full_base(config, None, false, |_, _| ())?;
Ok(sc_service_test::TestNetComponents::new(
task_manager,
client,
+4
View File
@@ -27,6 +27,10 @@ pub struct Cli {
#[clap(flatten)]
pub run: sc_cli::RunCmd,
#[allow(missing_docs)]
#[clap(flatten)]
pub mixnet_params: sc_cli::MixnetParams,
/// Disable automatic hardware benchmarks.
///
/// By default these benchmarks are automatically ran at startup and measure
+10 -9
View File
@@ -111,7 +111,7 @@ pub fn run() -> Result<()> {
},
BenchmarkCmd::Block(cmd) => {
// ensure that we keep the task manager alive
let partial = new_partial(&config)?;
let partial = new_partial(&config, None)?;
cmd.run(partial.client)
},
#[cfg(not(feature = "runtime-benchmarks"))]
@@ -122,7 +122,7 @@ pub fn run() -> Result<()> {
#[cfg(feature = "runtime-benchmarks")]
BenchmarkCmd::Storage(cmd) => {
// ensure that we keep the task manager alive
let partial = new_partial(&config)?;
let partial = new_partial(&config, None)?;
let db = partial.backend.expose_db();
let storage = partial.backend.expose_storage();
@@ -130,7 +130,7 @@ pub fn run() -> Result<()> {
},
BenchmarkCmd::Overhead(cmd) => {
// ensure that we keep the task manager alive
let partial = new_partial(&config)?;
let partial = new_partial(&config, None)?;
let ext_builder = RemarkBuilder::new(partial.client.clone());
cmd.run(
@@ -143,7 +143,7 @@ pub fn run() -> Result<()> {
},
BenchmarkCmd::Extrinsic(cmd) => {
// ensure that we keep the task manager alive
let partial = service::new_partial(&config)?;
let partial = service::new_partial(&config, None)?;
// Register the *Remark* and *TKA* builders.
let ext_factory = ExtrinsicFactory(vec![
Box::new(RemarkBuilder::new(partial.client.clone())),
@@ -178,21 +178,21 @@ pub fn run() -> Result<()> {
let runner = cli.create_runner(cmd)?;
runner.async_run(|config| {
let PartialComponents { client, task_manager, import_queue, .. } =
new_partial(&config)?;
new_partial(&config, None)?;
Ok((cmd.run(client, import_queue), task_manager))
})
},
Some(Subcommand::ExportBlocks(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.async_run(|config| {
let PartialComponents { client, task_manager, .. } = new_partial(&config)?;
let PartialComponents { client, task_manager, .. } = new_partial(&config, None)?;
Ok((cmd.run(client, config.database), task_manager))
})
},
Some(Subcommand::ExportState(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.async_run(|config| {
let PartialComponents { client, task_manager, .. } = new_partial(&config)?;
let PartialComponents { client, task_manager, .. } = new_partial(&config, None)?;
Ok((cmd.run(client, config.chain_spec), task_manager))
})
},
@@ -200,7 +200,7 @@ pub fn run() -> Result<()> {
let runner = cli.create_runner(cmd)?;
runner.async_run(|config| {
let PartialComponents { client, task_manager, import_queue, .. } =
new_partial(&config)?;
new_partial(&config, None)?;
Ok((cmd.run(client, import_queue), task_manager))
})
},
@@ -211,7 +211,8 @@ pub fn run() -> Result<()> {
Some(Subcommand::Revert(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.async_run(|config| {
let PartialComponents { client, task_manager, backend, .. } = new_partial(&config)?;
let PartialComponents { client, task_manager, backend, .. } =
new_partial(&config, None)?;
let aux_revert = Box::new(|client: Arc<FullClient>, backend, blocks| {
sc_consensus_babe::revert(client.clone(), backend, blocks)?;
grandpa::revert(client, blocks)?;
+50 -16
View File
@@ -134,6 +134,7 @@ pub fn create_extrinsic(
/// Creates a new partial node.
pub fn new_partial(
config: &Configuration,
mixnet_config: Option<&sc_mixnet::Config>,
) -> Result<
sc_service::PartialComponents<
FullClient,
@@ -154,6 +155,7 @@ pub fn new_partial(
grandpa::SharedVoterState,
Option<Telemetry>,
Arc<StatementStore>,
Option<sc_mixnet::ApiBackend>,
),
>,
ServiceError,
@@ -246,6 +248,8 @@ pub fn new_partial(
)
.map_err(|e| ServiceError::Other(format!("Statement store error: {:?}", e)))?;
let (mixnet_api, mixnet_api_backend) = mixnet_config.map(sc_mixnet::Api::new).unzip();
let (rpc_extensions_builder, rpc_setup) = {
let (_, grandpa_link, _) = &import_setup;
@@ -287,6 +291,7 @@ pub fn new_partial(
},
statement_store: rpc_statement_store.clone(),
backend: rpc_backend.clone(),
mixnet_api: mixnet_api.as_ref().cloned(),
};
node_rpc::create_full(deps).map_err(Into::into)
@@ -303,7 +308,14 @@ pub fn new_partial(
select_chain,
import_queue,
transaction_pool,
other: (rpc_extensions_builder, import_setup, rpc_setup, telemetry, statement_store),
other: (
rpc_extensions_builder,
import_setup,
rpc_setup,
telemetry,
statement_store,
mixnet_api_backend,
),
})
}
@@ -326,6 +338,7 @@ pub struct NewFullBase {
/// Creates a full service from the configuration.
pub fn new_full_base(
config: Configuration,
mixnet_config: Option<sc_mixnet::Config>,
disable_hardware_benchmarks: bool,
with_startup_data: impl FnOnce(
&sc_consensus_babe::BabeBlockImport<Block, FullClient, FullGrandpaBlockImport>,
@@ -347,31 +360,36 @@ pub fn new_full_base(
keystore_container,
select_chain,
transaction_pool,
other: (rpc_builder, import_setup, rpc_setup, mut telemetry, statement_store),
} = new_partial(&config)?;
other:
(rpc_builder, import_setup, rpc_setup, mut telemetry, statement_store, mixnet_api_backend),
} = new_partial(&config, mixnet_config.as_ref())?;
let shared_voter_state = rpc_setup;
let auth_disc_publish_non_global_ips = config.network.allow_non_globals_in_dht;
let mut net_config = sc_network::config::FullNetworkConfiguration::new(&config.network);
let grandpa_protocol_name = grandpa::protocol_standard_name(
&client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"),
&config.chain_spec,
);
let genesis_hash = client.block_hash(0).ok().flatten().expect("Genesis block exists; qed");
let grandpa_protocol_name = grandpa::protocol_standard_name(&genesis_hash, &config.chain_spec);
net_config.add_notification_protocol(grandpa::grandpa_peers_set_config(
grandpa_protocol_name.clone(),
));
let statement_handler_proto = sc_network_statement::StatementHandlerPrototype::new(
client
.block_hash(0u32.into())
.ok()
.flatten()
.expect("Genesis block exists; qed"),
genesis_hash,
config.chain_spec.fork_id(),
);
net_config.add_notification_protocol(statement_handler_proto.set_config());
let mixnet_protocol_name =
sc_mixnet::protocol_name(genesis_hash.as_ref(), config.chain_spec.fork_id());
if let Some(mixnet_config) = &mixnet_config {
net_config.add_notification_protocol(sc_mixnet::peers_set_config(
mixnet_protocol_name.clone(),
mixnet_config,
));
}
let warp_sync = Arc::new(grandpa::warp_proof::NetworkProvider::new(
backend.clone(),
import_setup.1.shared_authority_set().clone(),
@@ -391,6 +409,20 @@ pub fn new_full_base(
block_relay: None,
})?;
if let Some(mixnet_config) = mixnet_config {
let mixnet = sc_mixnet::run(
mixnet_config,
mixnet_api_backend.expect("Mixnet API backend created if mixnet enabled"),
client.clone(),
sync_service.clone(),
network.clone(),
mixnet_protocol_name,
transaction_pool.clone(),
Some(keystore_container.keystore()),
);
task_manager.spawn_handle().spawn("mixnet", None, mixnet);
}
let role = config.role.clone();
let force_authoring = config.force_authoring;
let backoff_authoring_blocks =
@@ -546,7 +578,7 @@ pub fn new_full_base(
// and vote data availability than the observer. The observer has not
// been tested extensively yet and having most nodes in a network run it
// could lead to finality stalls.
let grandpa_config = grandpa::GrandpaParams {
let grandpa_params = grandpa::GrandpaParams {
config: grandpa_config,
link: grandpa_link,
network: network.clone(),
@@ -563,7 +595,7 @@ pub fn new_full_base(
task_manager.spawn_essential_handle().spawn_blocking(
"grandpa-voter",
None,
grandpa::run_grandpa_voter(grandpa_config)?,
grandpa::run_grandpa_voter(grandpa_params)?,
);
}
@@ -623,8 +655,9 @@ pub fn new_full_base(
/// Builds a new service for a full client.
pub fn new_full(config: Configuration, cli: Cli) -> Result<TaskManager, ServiceError> {
let mixnet_config = cli.mixnet_params.config(config.role.is_authority());
let database_source = config.database.clone();
let task_manager = new_full_base(config, cli.no_hardware_benchmarks, |_, _| ())
let task_manager = new_full_base(config, mixnet_config, cli.no_hardware_benchmarks, |_, _| ())
.map(|NewFullBase { task_manager, .. }| task_manager)?;
sc_storage_monitor::StorageMonitorService::try_spawn(
@@ -702,6 +735,7 @@ mod tests {
let NewFullBase { task_manager, client, network, sync, transaction_pool, .. } =
new_full_base(
config,
None,
false,
|block_import: &sc_consensus_babe::BabeBlockImport<Block, _, _>,
babe_link: &sc_consensus_babe::BabeLink<Block>| {
@@ -876,7 +910,7 @@ mod tests {
crate::chain_spec::tests::integration_test_config_with_two_authorities(),
|config| {
let NewFullBase { task_manager, client, network, sync, transaction_pool, .. } =
new_full_base(config, false, |_, _| ())?;
new_full_base(config, None, false, |_, _| ())?;
Ok(sc_service_test::TestNetComponents::new(
task_manager,
client,