Skip to content

Commit

Permalink
feat: subnet IDs in StateMachine tests derived from the subnets' publ…
Browse files Browse the repository at this point in the history
…ic keys
  • Loading branch information
mraszyk committed Feb 15, 2024
1 parent 4d213e2 commit b740b01
Show file tree
Hide file tree
Showing 9 changed files with 260 additions and 272 deletions.
4 changes: 1 addition & 3 deletions Cargo.lock

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

29 changes: 28 additions & 1 deletion rs/crypto/test_utils/ni-dkg/src/initial_config/mod.rs
@@ -1,6 +1,6 @@
//! Utilities for non-interactive Distributed Key Generation (NI-DKG), and
//! for testing distributed key generation and threshold signing.
use crate::dummy_transcript_for_tests_with_params;
use crate::{dummy_transcript_for_tests, dummy_transcript_for_tests_with_params};
use ic_crypto_internal_bls12_381_type::{G1Affine, G2Affine, Scalar};
use ic_crypto_internal_types::sign::threshold_sig::ni_dkg::CspNiDkgTranscript::Groth20_Bls12_381;
use ic_crypto_internal_types::sign::threshold_sig::public_key::bls12_381::PublicKeyBytes;
Expand Down Expand Up @@ -165,6 +165,33 @@ pub fn initial_dkg_transcript_and_master_key<R: rand::Rng + rand::CryptoRng>(
(transcript, master_secret_bytes)
}

/// Return a fake transcript and the master secret associated with it
///
/// The transcript is not valid and cannot be used by NIDKG
pub fn dummy_initial_dkg_transcript_with_master_key<R: rand::Rng + rand::CryptoRng>(
rng: &mut R,
) -> (NiDkgTranscript, SecretKeyBytes) {
let mut transcript = dummy_transcript_for_tests();

let master_secret = Scalar::random(rng);

let public_key_bytes = G2Affine::from(G2Affine::generator() * &master_secret).serialize();

let master_secret_bytes = SecretKeyBytes {
val: master_secret.serialize(),
};

transcript.internal_csp_transcript = match transcript.internal_csp_transcript {
Groth20_Bls12_381(transcript) => {
let mut mod_transcript = transcript.clone();
mod_transcript.public_coefficients.coefficients[0] = PublicKeyBytes(public_key_bytes);
Groth20_Bls12_381(mod_transcript)
}
};

(transcript, master_secret_bytes)
}

#[derive(Debug, Clone, Copy)]
pub struct CombinedSignatureBytes {
val: [u8; 48],
Expand Down
5 changes: 3 additions & 2 deletions rs/crypto/test_utils/ni-dkg/src/lib.rs
Expand Up @@ -33,8 +33,9 @@ use std::sync::Arc;
mod initial_config;

pub use initial_config::{
dummy_initial_dkg_transcript, initial_dkg_transcript, initial_dkg_transcript_and_master_key,
sign_message, InitialNiDkgConfig,
dummy_initial_dkg_transcript, dummy_initial_dkg_transcript_with_master_key,
initial_dkg_transcript, initial_dkg_transcript_and_master_key, sign_message,
InitialNiDkgConfig, SecretKeyBytes,
};

pub fn create_transcript<R: CryptoComponentRng>(
Expand Down
11 changes: 8 additions & 3 deletions rs/pocket_ic_server/CHANGELOG.md
Expand Up @@ -11,12 +11,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Fixed

- Subnet IDs are derived from the subnets' public keys by default.


## 3.0.1 - 2024-02-14

### Fixed
- traps in tECDSA calls
- server rejects jsons containing unimplemented variants of SubnetSpec
- inspect_message no longer panics when call is rejected
- Traps in tECDSA calls due to malformed tECDSA public key.
- Server rejects jsons containing unimplemented variants of `SubnetSpec`.
- The `inspect_message` method no longer panics when call is rejected.

## 3.0.0 - 2024-02-06

Expand Down
109 changes: 48 additions & 61 deletions rs/pocket_ic_server/src/pocket_ic.rs
Expand Up @@ -5,16 +5,14 @@ use crate::{copy_dir, BlobStore};
use ic_config::execution_environment;
use ic_config::subnet_config::SubnetConfig;
use ic_crypto_sha2::Sha256;
use ic_crypto_utils_threshold_sig_der::threshold_sig_public_key_to_der;
use ic_management_canister_types::CanisterInstallMode;
use ic_registry_proto_data_provider::ProtoRegistryDataProvider;
use ic_registry_routing_table::{CanisterIdRange, RoutingTable, CANISTER_IDS_PER_SUBNET};
use ic_registry_subnet_type::SubnetType;
use ic_state_machine_tests::{
EcdsaCurve, EcdsaKeyId, IngressState, IngressStatus, StateMachine, StateMachineBuilder,
finalize_registry, IngressState, IngressStatus, StateMachine, StateMachineBuilder,
StateMachineConfig, SubmitIngressError, Time,
};
use ic_test_utilities::types::ids::subnet_test_id;
use ic_types::messages::CertificateDelegation;
use ic_types::{CanisterId, PrincipalId, SubnetId};
use itertools::Itertools;
Expand Down Expand Up @@ -68,7 +66,6 @@ impl PocketIc {

let mut range_gen = RangeGen::new();
let mut subnet_config_info: Vec<SubnetConfigInfo> = vec![];
let mut subnet_ids = vec![];
let mut routing_table = RoutingTable::new();

let mut nns_subnet_id = subnet_configs.nns.and_then(|x| {
Expand All @@ -78,47 +75,14 @@ impl PocketIc {

let ii_subnet_split = subnet_configs.ii.is_some();

let mut subnet_counter = 0_u64;
let mut apply_subnet_counter = move || -> u64 {
let current_subnet_counter = subnet_counter;
subnet_counter += 1;
current_subnet_counter
};

for (subnet_kind, subnet_state_dir) in
fixed_range_subnets.into_iter().chain(flexible_subnets)
{
let subnet_id = match (subnet_kind, nns_subnet_id) {
(SubnetKind::NNS, Some(nns_subnet_id)) => nns_subnet_id,
(SubnetKind::NNS, None) => {
let subnet_id = subnet_test_id(apply_subnet_counter());
nns_subnet_id = Some(subnet_id);
subnet_id
}
(_, None) => subnet_test_id(apply_subnet_counter()),
// Ensure that a generated `subnet_id` does not collide with `nns_subnet_id`.
(_, Some(nns_subnet_id)) => loop {
let subnet_id = subnet_test_id(apply_subnet_counter());
if subnet_id != nns_subnet_id {
break subnet_id;
}
},
};
subnet_ids.push(subnet_id);

let RangeConfig {
canister_id_ranges: ranges,
canister_allocation_range: alloc_range,
} = get_range_config(subnet_kind, &mut range_gen, ii_subnet_split);

// Insert ranges and allocation range into routing table
for range in &ranges {
routing_table.insert(*range, subnet_id).unwrap();
}
if let Some(alloc_range) = alloc_range {
routing_table.insert(alloc_range, subnet_id).unwrap();
}

let state_dir = if let Some(subnet_state_dir) = subnet_state_dir {
let tmp_dir = TempDir::new().expect("Failed to create temporary directory");
copy_dir(subnet_state_dir, tmp_dir.path()).expect("Failed to copy state directory");
Expand All @@ -128,8 +92,8 @@ impl PocketIc {
};

subnet_config_info.push(SubnetConfigInfo {
subnet_id,
ranges,
alloc_range,
subnet_kind,
state_dir,
});
Expand All @@ -141,12 +105,15 @@ impl PocketIc {
let mut topology = Topology(HashMap::new());

// Create all StateMachines and the topology from the subnet config infos.
for SubnetConfigInfo {
subnet_id,
ranges,
subnet_kind,
state_dir,
} in subnet_config_info
for (
subnet_seq_no,
SubnetConfigInfo {
ranges,
alloc_range,
subnet_kind,
state_dir,
},
) in subnet_config_info.into_iter().enumerate()
{
let subnet_config = SubnetConfig::new(conv_type(subnet_kind));
let hypervisor_config = execution_environment::Config::default();
Expand All @@ -155,23 +122,38 @@ impl PocketIc {
let mut builder = StateMachineBuilder::new()
.with_runtime(runtime.clone())
.with_config(Some(sm_config))
.with_subnet_id(subnet_id)
.with_nns_subnet_id(nns_subnet_id.unwrap_or(subnet_ids[0]))
.with_subnet_list(subnet_ids.clone())
.with_subnet_seq_no(subnet_seq_no as u8)
.with_subnet_size(subnet_size.try_into().unwrap())
.with_routing_table(routing_table.clone())
.with_registry_data_provider(registry_data_provider.clone())
.with_ecdsa_keys(vec![EcdsaKeyId {
curve: EcdsaCurve::Secp256k1,
name: format!("master_ecdsa_public_key_{}", subnet_id),
}])
.with_multisubnet_ecdsa_key()
.with_use_cost_scaling_flag(true);

if subnet_kind == SubnetKind::NNS {
builder = builder.with_root_subnet_config();
if let Some(nns_subnet_id) = nns_subnet_id {
builder = builder.with_subnet_id(nns_subnet_id);
}
}

if let Some(state_dir) = state_dir {
builder = builder.with_state_dir(state_dir);
}

builder.build_with_subnets(subnets.clone());
let sm = builder.build_with_subnets(subnets.clone());
let subnet_id = sm.get_subnet_id();

// Store the actual NNS subnet ID if none was provided by the client.
if let (SubnetKind::NNS, None) = (subnet_kind, nns_subnet_id) {
nns_subnet_id = Some(subnet_id);
};

// Insert ranges and allocation range into routing table
for range in &ranges {
routing_table.insert(*range, subnet_id).unwrap();
}
if let Some(alloc_range) = alloc_range {
routing_table.insert(alloc_range, subnet_id).unwrap();
}

// What will be returned to the client:
let subnet_config = pocket_ic::common::rest::SubnetConfig {
Expand All @@ -182,11 +164,19 @@ impl PocketIc {
topology.0.insert(subnet_id.get().0, subnet_config);
}

// Finalize registry with subnet IDs that are only available now that we created
// all the StateMachines.
let subnet_list = topology.0.keys().map(|p| PrincipalId(*p).into()).collect();
finalize_registry(
nns_subnet_id.unwrap_or(PrincipalId(*topology.0.keys().next().unwrap()).into()),
routing_table.clone(),
subnet_list,
registry_data_provider,
);

for subnet in subnets.read().unwrap().values() {
// Reload registry on the state machines to make sure
// the registry contains all subnet records
// added incrementally to the registry data provider
// when creating the individual state machines.
// all the state machines have a consistent view of the registry.
subnet.reload_registry();
}

Expand Down Expand Up @@ -445,8 +435,8 @@ struct RangeConfig {

/// Internal struct used during initialization.
struct SubnetConfigInfo {
pub subnet_id: SubnetId,
pub ranges: Vec<CanisterIdRange>,
pub alloc_range: Option<CanisterIdRange>,
pub subnet_kind: SubnetKind,
pub state_dir: Option<TempDir>,
}
Expand Down Expand Up @@ -509,10 +499,7 @@ impl Operation for PubKey {
fn compute(self, pic: &mut PocketIc) -> OpOut {
let subnet = pic.get_subnet_with_id(self.subnet_id);
match subnet {
Some(subnet) => {
let bytes = threshold_sig_public_key_to_der(subnet.root_key()).unwrap();
OpOut::Bytes(bytes)
}
Some(subnet) => OpOut::Bytes(subnet.root_key_der()),
None => OpOut::Error(PocketIcError::SubnetNotFound(self.subnet_id.get().0)),
}
}
Expand Down
5 changes: 2 additions & 3 deletions rs/state_machine_tests/BUILD.bazel
Expand Up @@ -10,11 +10,10 @@ DEPENDENCIES = [
"//rs/crypto/ecdsa_secp256k1",
"//rs/crypto/extended_bip32",
"//rs/crypto/interfaces/sig_verification",
"//rs/crypto/internal/crypto_lib/seed",
"//rs/crypto/internal/crypto_lib/threshold_sig/bls12_381",
"//rs/crypto/internal/crypto_lib/types",
"//rs/crypto/test_utils/keys",
"//rs/crypto/test_utils/ni-dkg",
"//rs/crypto/tree_hash",
"//rs/crypto/utils/threshold_sig_der",
"//rs/cycles_account_manager",
"//rs/execution_environment",
"//rs/ingress_manager",
Expand Down
4 changes: 1 addition & 3 deletions rs/state_machine_tests/Cargo.toml
Expand Up @@ -21,10 +21,8 @@ ic-crypto-ecdsa-secp256k1 = { path = "../crypto/ecdsa_secp256k1" }
ic-crypto-extended-bip32 = { path = "../crypto/extended_bip32" }
ic-crypto-iccsa = { path = "../crypto/iccsa" }
ic-crypto-interfaces-sig-verification = { path = "../crypto/interfaces/sig_verification" }
ic-crypto-internal-seed = { path = "../crypto/internal/crypto_lib/seed" }
ic-crypto-internal-threshold-sig-bls12381 = { path = "../crypto/internal/crypto_lib/threshold_sig/bls12_381" }
ic-crypto-internal-types = { path = "../crypto/internal/crypto_lib/types" }
ic-crypto-test-utils-keys = { path = "../crypto/test_utils/keys" }
ic-crypto-test-utils-ni-dkg = { path = "../crypto/test_utils/ni-dkg" }
ic-crypto-tree-hash = { path = "../crypto/tree_hash" }
ic-crypto-utils-threshold-sig-der = { path = "../crypto/utils/threshold_sig_der" }
ic-cycles-account-manager = { path = "../cycles_account_manager" }
Expand Down

0 comments on commit b740b01

Please sign in to comment.