diff --git a/.changelog/5623.bugfix.md b/.changelog/5623.bugfix.md new file mode 100644 index 00000000000..6623dc88c98 --- /dev/null +++ b/.changelog/5623.bugfix.md @@ -0,0 +1 @@ +runtime: Add missing support for per-role admission policy decoding diff --git a/go/consensus/cometbft/apps/registry/state/interop/interop.go b/go/consensus/cometbft/apps/registry/state/interop/interop.go index fd9f6661032..6d37de4bfe5 100644 --- a/go/consensus/cometbft/apps/registry/state/interop/interop.go +++ b/go/consensus/cometbft/apps/registry/state/interop/interop.go @@ -34,6 +34,10 @@ func InitializeTestRegistryState(ctx context.Context, mkvs mkvs.Tree) error { if err := runtimeID2.UnmarshalHex("8000000000000000000000000000000000000000000000000000000000000011"); err != nil { return err } + var runtimeID3 common.Namespace + if err := runtimeID3.UnmarshalHex("8000000000000000000000000000000000000000000000000000000000000012"); err != nil { + return err + } // Populate nodes. for _, fix := range []struct { nodeSigner signature.Signer @@ -152,6 +156,33 @@ func InitializeTestRegistryState(ctx context.Context, mkvs mkvs.Tree) error { }, true, }, + { + ®istry.Runtime{ + Versioned: cbor.NewVersioned(registry.LatestRuntimeDescriptorVersion), + ID: runtimeID3, + EntityID: entitySigner.Public(), + Kind: registry.KindCompute, + TEEHardware: node.TEEHardwareIntelSGX, + AdmissionPolicy: registry.RuntimeAdmissionPolicy{ + PerRole: map[node.RolesMask]registry.PerRoleAdmissionPolicy{ + node.RoleObserver: { + EntityWhitelist: ®istry.EntityWhitelistRoleAdmissionPolicy{ + Entities: map[signature.PublicKey]registry.EntityWhitelistRoleConfig{}, + }, + }, + }, + }, + Deployments: []*registry.VersionInfo{ + { + Version: version.FromU64(123), + ValidFrom: 42, + TEE: []byte{1, 2, 3, 4, 5}, + BundleChecksum: bytes.Repeat([]byte{0x05}, 32), + }, + }, + }, + false, + }, } { if err := state.SetRuntime(ctx, fix.rt, fix.suspended); err != nil { return fmt.Errorf("setting runtime: %w", err) diff --git a/runtime/src/consensus/registry.rs b/runtime/src/consensus/registry.rs index 90de6bb3d01..455e8164b0b 100644 --- a/runtime/src/consensus/registry.rs +++ b/runtime/src/consensus/registry.rs @@ -458,22 +458,45 @@ pub struct EntityWhitelistConfig { pub max_nodes: BTreeMap, } +/// A per-entity whitelist configuration for a given role. +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, cbor::Encode, cbor::Decode)] +pub struct EntityWhitelistRoleConfig { + #[cbor(optional)] + pub max_nodes: u16, +} + +/// A per-role entity whitelist policy. +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, cbor::Encode, cbor::Decode)] +pub struct EntityWhitelistRoleAdmissionPolicy { + pub entities: BTreeMap, +} + +/// A per-role admission policy. +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, cbor::Encode, cbor::Decode)] +pub struct PerRoleAdmissionPolicy { + #[cbor(optional)] + pub entity_whitelist: Option, +} + +/// Admission policy that allows any node to register. +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, cbor::Encode, cbor::Decode)] +pub struct AnyNodeRuntimeAdmissionPolicy {} + /// Specification of which nodes are allowed to register for a runtime. -#[derive(Clone, Debug, PartialEq, Eq, Hash, cbor::Encode, cbor::Decode)] -pub enum RuntimeAdmissionPolicy { +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, cbor::Encode, cbor::Decode)] +pub struct RuntimeAdmissionPolicy { /// Allow any node to register. - #[cbor(rename = "any_node")] - AnyNode {}, + #[cbor(optional)] + pub any_node: Option, /// Allow only the whitelisted entities' nodes to register. - #[cbor(rename = "entity_whitelist")] - EntityWhitelist(EntityWhitelistRuntimeAdmissionPolicy), -} + #[cbor(optional)] + pub entity_whitelist: Option, -impl Default for RuntimeAdmissionPolicy { - fn default() -> Self { - RuntimeAdmissionPolicy::AnyNode {} - } + /// A per-role admission policy that must be satisfied in addition to the global admission + /// policy for a specific role. + #[cbor(optional)] + pub per_role: BTreeMap, } /// Runtime governance model. @@ -753,11 +776,21 @@ mod tests { // NOTE: These tests MUST be synced with go/registry/api/runtime.go. let tcs = vec![ // FIXME: Change to "qmF2AGJpZFggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABka2luZABnZ2VuZXNpc6Jlcm91bmQAanN0YXRlX3Jvb3RYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ3N0b3JhZ2Wjc2NoZWNrcG9pbnRfaW50ZXJ2YWwAc2NoZWNrcG9pbnRfbnVtX2tlcHQAdWNoZWNrcG9pbnRfY2h1bmtfc2l6ZQBoZXhlY3V0b3Klamdyb3VwX3NpemUAbG1heF9tZXNzYWdlcwBtcm91bmRfdGltZW91dABxZ3JvdXBfYmFja3VwX3NpemUAcmFsbG93ZWRfc3RyYWdnbGVycwBpZW50aXR5X2lkWCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGx0ZWVfaGFyZHdhcmUAcGFkbWlzc2lvbl9wb2xpY3mhaGFueV9ub2RloHBnb3Zlcm5hbmNlX21vZGVsAA==" once cbor is fixed. - ("q2F2AGJpZFggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABka2luZABnZ2VuZXNpc6Jlcm91bmQAanN0YXRlX3Jvb3RYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ3N0b3JhZ2Wjc2NoZWNrcG9pbnRfaW50ZXJ2YWwAc2NoZWNrcG9pbnRfbnVtX2tlcHQAdWNoZWNrcG9pbnRfY2h1bmtfc2l6ZQBoZXhlY3V0b3Klamdyb3VwX3NpemUAbG1heF9tZXNzYWdlcwBtcm91bmRfdGltZW91dABxZ3JvdXBfYmFja3VwX3NpemUAcmFsbG93ZWRfc3RyYWdnbGVycwBpZW50aXR5X2lkWCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGx0ZWVfaGFyZHdhcmUAbXR4bl9zY2hlZHVsZXKgcGFkbWlzc2lvbl9wb2xpY3mhaGFueV9ub2RloHBnb3Zlcm5hbmNlX21vZGVsAA==", Runtime::default()), + ("q2F2AGJpZFggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABka2luZABnZ2VuZXNpc6Jlcm91bmQAanN0YXRlX3Jvb3RYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ3N0b3JhZ2Wjc2NoZWNrcG9pbnRfaW50ZXJ2YWwAc2NoZWNrcG9pbnRfbnVtX2tlcHQAdWNoZWNrcG9pbnRfY2h1bmtfc2l6ZQBoZXhlY3V0b3Klamdyb3VwX3NpemUAbG1heF9tZXNzYWdlcwBtcm91bmRfdGltZW91dABxZ3JvdXBfYmFja3VwX3NpemUAcmFsbG93ZWRfc3RyYWdnbGVycwBpZW50aXR5X2lkWCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGx0ZWVfaGFyZHdhcmUAbXR4bl9zY2hlZHVsZXKgcGFkbWlzc2lvbl9wb2xpY3mhaGFueV9ub2RloHBnb3Zlcm5hbmNlX21vZGVsAA==", Runtime { + admission_policy: RuntimeAdmissionPolicy { + any_node: Some(AnyNodeRuntimeAdmissionPolicy {}), + ..Default::default() + }, + ..Default::default() + }), // FIXME: Change to "qmF2AGJpZFggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABka2luZABnZ2VuZXNpc6Jlcm91bmQAanN0YXRlX3Jvb3RYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ3N0b3JhZ2Wjc2NoZWNrcG9pbnRfaW50ZXJ2YWwAc2NoZWNrcG9pbnRfbnVtX2tlcHQAdWNoZWNrcG9pbnRfY2h1bmtfc2l6ZQBoZXhlY3V0b3Klamdyb3VwX3NpemUAbG1heF9tZXNzYWdlcwBtcm91bmRfdGltZW91dABxZ3JvdXBfYmFja3VwX3NpemUAcmFsbG93ZWRfc3RyYWdnbGVycwBpZW50aXR5X2lkWCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGx0ZWVfaGFyZHdhcmUAcGFkbWlzc2lvbl9wb2xpY3mhaGFueV9ub2RloHBnb3Zlcm5hbmNlX21vZGVsAA==" once cbor is fixed. ( "q2F2AGJpZFggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABka2luZABnZ2VuZXNpc6Jlcm91bmQAanN0YXRlX3Jvb3RYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ3N0b3JhZ2Wjc2NoZWNrcG9pbnRfaW50ZXJ2YWwAc2NoZWNrcG9pbnRfbnVtX2tlcHQAdWNoZWNrcG9pbnRfY2h1bmtfc2l6ZQBoZXhlY3V0b3Klamdyb3VwX3NpemUAbG1heF9tZXNzYWdlcwBtcm91bmRfdGltZW91dABxZ3JvdXBfYmFja3VwX3NpemUAcmFsbG93ZWRfc3RyYWdnbGVycwBpZW50aXR5X2lkWCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGx0ZWVfaGFyZHdhcmUAbXR4bl9zY2hlZHVsZXKgcGFkbWlzc2lvbl9wb2xpY3mhaGFueV9ub2RloHBnb3Zlcm5hbmNlX21vZGVsAA==", Runtime { + admission_policy: RuntimeAdmissionPolicy { + any_node: Some(AnyNodeRuntimeAdmissionPolicy {}), + ..Default::default() + }, staking: RuntimeStakingParameters { thresholds: BTreeMap::new(), slashing: BTreeMap::new(), @@ -772,6 +805,10 @@ mod tests { ( "rGF2AGJpZFggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABka2luZABnZ2VuZXNpc6Jlcm91bmQAanN0YXRlX3Jvb3RYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ3N0YWtpbmehcnJld2FyZF9iYWRfcmVzdWx0cwpnc3RvcmFnZaNzY2hlY2twb2ludF9pbnRlcnZhbABzY2hlY2twb2ludF9udW1fa2VwdAB1Y2hlY2twb2ludF9jaHVua19zaXplAGhleGVjdXRvcqVqZ3JvdXBfc2l6ZQBsbWF4X21lc3NhZ2VzAG1yb3VuZF90aW1lb3V0AHFncm91cF9iYWNrdXBfc2l6ZQByYWxsb3dlZF9zdHJhZ2dsZXJzAGllbnRpdHlfaWRYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbHRlZV9oYXJkd2FyZQBtdHhuX3NjaGVkdWxlcqBwYWRtaXNzaW9uX3BvbGljeaFoYW55X25vZGWgcGdvdmVybmFuY2VfbW9kZWwA", Runtime { + admission_policy: RuntimeAdmissionPolicy { + any_node: Some(AnyNodeRuntimeAdmissionPolicy {}), + ..Default::default() + }, staking: RuntimeStakingParameters { thresholds: BTreeMap::new(), slashing: BTreeMap::new(), @@ -834,8 +871,8 @@ mod tests { checkpoint_num_kept: 6, checkpoint_chunk_size: 101, }, - admission_policy: RuntimeAdmissionPolicy::EntityWhitelist( - EntityWhitelistRuntimeAdmissionPolicy { + admission_policy: RuntimeAdmissionPolicy { + entity_whitelist: Some(EntityWhitelistRuntimeAdmissionPolicy { entities: btreemap! { signature::PublicKey::from("1234567890000000000000000000000000000000000000000000000000000000") => EntityWhitelistConfig { max_nodes: btreemap! { @@ -844,8 +881,9 @@ mod tests { } } }, - }, - ), + }), + ..Default::default() + }, constraints: btreemap! { scheduler::CommitteeKind::ComputeExecutor => btreemap! { scheduler::Role::Worker => SchedulingConstraints{ diff --git a/runtime/src/consensus/roothash/message.rs b/runtime/src/consensus/roothash/message.rs index 391fdee0457..51568a535f1 100644 --- a/runtime/src/consensus/roothash/message.rs +++ b/runtime/src/consensus/roothash/message.rs @@ -228,9 +228,12 @@ mod tests { checkpoint_num_kept: 0, checkpoint_chunk_size: 0, }, - admission_policy: registry::RuntimeAdmissionPolicy::EntityWhitelist( - registry::EntityWhitelistRuntimeAdmissionPolicy { entities: wl }, - ), + admission_policy: registry::RuntimeAdmissionPolicy { + entity_whitelist: Some(registry::EntityWhitelistRuntimeAdmissionPolicy { + entities: wl, + }), + ..Default::default() + }, constraints: { let mut cs = BTreeMap::new(); cs.insert(scheduler::CommitteeKind::ComputeExecutor, { @@ -299,7 +302,13 @@ mod tests { ( vec![Message::Registry(Versioned::new( 0, - RegistryMessage::UpdateRuntime(registry::Runtime::default()), + RegistryMessage::UpdateRuntime(registry::Runtime { + admission_policy: registry::RuntimeAdmissionPolicy { + any_node: Some(registry::AnyNodeRuntimeAdmissionPolicy {}), + ..Default::default() + }, + ..Default::default() + }), ))], // FIXME: Change to e6e170fb771583147255e0c96dc88615d4fd2fd28488ae489df01da201affe72 once cbor is fixed. "baf9eeaa4860e363a9c27d99555839afc535f0cd32d23dc640f0f020677460e0", diff --git a/runtime/src/consensus/state/beacon.rs b/runtime/src/consensus/state/beacon.rs index 02a431f549e..9a99fd26224 100644 --- a/runtime/src/consensus/state/beacon.rs +++ b/runtime/src/consensus/state/beacon.rs @@ -154,7 +154,7 @@ mod test { let mock_consensus_root = Root { version: 1, root_type: RootType::State, - hash: Hash::from("280a0d815030421f16366da7cc57efbfc7fc87d9a7d16964216c79873ebd240a"), + hash: Hash::from("1c2ce324ac7d7b3a5f47cc73fed9455b96d562c2488250f28af8101fe2e32cb3"), ..Default::default() }; let mkvs = Tree::builder() diff --git a/runtime/src/consensus/state/keymanager.rs b/runtime/src/consensus/state/keymanager.rs index 977783e21f5..b203e6a6fb1 100644 --- a/runtime/src/consensus/state/keymanager.rs +++ b/runtime/src/consensus/state/keymanager.rs @@ -166,7 +166,7 @@ mod test { let mock_consensus_root = Root { version: 1, root_type: RootType::State, - hash: Hash::from("280a0d815030421f16366da7cc57efbfc7fc87d9a7d16964216c79873ebd240a"), + hash: Hash::from("1c2ce324ac7d7b3a5f47cc73fed9455b96d562c2488250f28af8101fe2e32cb3"), ..Default::default() }; let mkvs = Tree::builder() diff --git a/runtime/src/consensus/state/keymanager/churp.rs b/runtime/src/consensus/state/keymanager/churp.rs index 12b9c5cf8d8..cc96da12be3 100644 --- a/runtime/src/consensus/state/keymanager/churp.rs +++ b/runtime/src/consensus/state/keymanager/churp.rs @@ -76,7 +76,7 @@ mod test { let mock_consensus_root = Root { version: 1, root_type: RootType::State, - hash: Hash::from("280a0d815030421f16366da7cc57efbfc7fc87d9a7d16964216c79873ebd240a"), + hash: Hash::from("1c2ce324ac7d7b3a5f47cc73fed9455b96d562c2488250f28af8101fe2e32cb3"), ..Default::default() }; let mkvs = Tree::builder() diff --git a/runtime/src/consensus/state/registry.rs b/runtime/src/consensus/state/registry.rs index 0db56ecd2ff..eab1e4e5fa7 100644 --- a/runtime/src/consensus/state/registry.rs +++ b/runtime/src/consensus/state/registry.rs @@ -100,11 +100,15 @@ impl<'a, T: ImmutableMKVS> ImmutableState<'a, T> { #[cfg(test)] mod test { + use std::collections::BTreeMap; + use crate::{ common::crypto::signature, consensus::registry::{ - Capabilities, CapabilityTEE, ConsensusInfo, NodeRuntime, P2PInfo, RuntimeKind, - TEEHardware, TLSInfo, VRFInfo, VersionInfo, + AnyNodeRuntimeAdmissionPolicy, Capabilities, CapabilityTEE, ConsensusInfo, + EntityWhitelistRoleAdmissionPolicy, NodeRuntime, P2PInfo, PerRoleAdmissionPolicy, + RolesMask, RuntimeAdmissionPolicy, RuntimeKind, TEEHardware, TLSInfo, VRFInfo, + VersionInfo, }, storage::mkvs::{ interop::{Fixture, ProtocolServer}, @@ -128,7 +132,7 @@ mod test { let mock_consensus_root = Root { version: 1, root_type: RootType::State, - hash: Hash::from("280a0d815030421f16366da7cc57efbfc7fc87d9a7d16964216c79873ebd240a"), + hash: Hash::from("1c2ce324ac7d7b3a5f47cc73fed9455b96d562c2488250f28af8101fe2e32cb3"), ..Default::default() }; let mkvs = Tree::builder() @@ -221,6 +225,10 @@ mod test { ), kind: RuntimeKind::KindCompute, tee_hardware: TEEHardware::TEEHardwareInvalid, + admission_policy: RuntimeAdmissionPolicy { + any_node: Some(AnyNodeRuntimeAdmissionPolicy {}), + ..Default::default() + }, deployments: vec![ VersionInfo { version: Version::from(321), @@ -245,6 +253,10 @@ mod test { ), kind: RuntimeKind::KindCompute, tee_hardware: TEEHardware::TEEHardwareIntelSGX, + admission_policy: RuntimeAdmissionPolicy { + any_node: Some(AnyNodeRuntimeAdmissionPolicy {}), + ..Default::default() + }, deployments: vec![ VersionInfo { version: Version::from(123), @@ -261,6 +273,35 @@ mod test { ], ..Default::default() }, + Runtime { + v: 3, + id: Namespace::from( + "8000000000000000000000000000000000000000000000000000000000000012", + ), + entity_id: signature::PublicKey::from( + "761950dfe65936f6e9d06a0124bc930f7d5b1812ceefdfb2cae0ef5841291531", + ), + kind: RuntimeKind::KindCompute, + tee_hardware: TEEHardware::TEEHardwareIntelSGX, + admission_policy: RuntimeAdmissionPolicy { + per_role: BTreeMap::from([( + RolesMask::ROLE_OBSERVER, + PerRoleAdmissionPolicy { + entity_whitelist: Some(EntityWhitelistRoleAdmissionPolicy { + entities: BTreeMap::new(), + }), + }, + )]), + ..Default::default() + }, + deployments: vec![VersionInfo { + version: Version::from(123), + valid_from: 42, + tee: vec![1, 2, 3, 4, 5], + bundle_checksum: vec![0x5; 32], + }], + ..Default::default() + }, ]; for rt in expected_runtimes { diff --git a/runtime/src/consensus/state/roothash.rs b/runtime/src/consensus/state/roothash.rs index 9ce7dc73248..c386e9cecd6 100644 --- a/runtime/src/consensus/state/roothash.rs +++ b/runtime/src/consensus/state/roothash.rs @@ -120,7 +120,7 @@ mod test { let mock_consensus_root = Root { version: 1, root_type: RootType::State, - hash: Hash::from("280a0d815030421f16366da7cc57efbfc7fc87d9a7d16964216c79873ebd240a"), + hash: Hash::from("1c2ce324ac7d7b3a5f47cc73fed9455b96d562c2488250f28af8101fe2e32cb3"), ..Default::default() }; let mkvs = Tree::builder() diff --git a/runtime/src/consensus/state/staking.rs b/runtime/src/consensus/state/staking.rs index 45b9abb26f9..5262aa60f74 100644 --- a/runtime/src/consensus/state/staking.rs +++ b/runtime/src/consensus/state/staking.rs @@ -221,7 +221,7 @@ mod test { let mock_consensus_root = Root { version: 1, root_type: RootType::State, - hash: Hash::from("280a0d815030421f16366da7cc57efbfc7fc87d9a7d16964216c79873ebd240a"), + hash: Hash::from("1c2ce324ac7d7b3a5f47cc73fed9455b96d562c2488250f28af8101fe2e32cb3"), ..Default::default() }; let mkvs = Tree::builder()