Skip to content

Commit 1f4a598

Browse files
pierugo-dfinityIDX GitHub Automation
andauthored
refactor(orchestrator): [CON-1457] Remove dependency to canister_client in orchestrator (#5104)
The orchestrator still depends on the unmaintained `canister_client` crate to send update messages to the registry during node registration. We would like to remove this dependency and instead rely on the [`ic_agent` crate](https://docs.rs/ic-agent/latest/ic_agent/), which is better maintained. This PR replaces the two occurrences of an update call using the mentioned crate, mostly refactoring the `orchestrator::signer` module to provide the API that `ic_agent` expects to sign messages. Concretely, it consists of implementing `ic_agent::Identity` traits functionally equivalently to `canister_client::Sender`, taking inspiration from the [trait’s implementation](https://docs.rs/ic-agent/latest/src/ic_agent/identity/secp256k1.rs.html#75) for `ic_agent::identity::Secp256k1Identity` compared to the one found in [`canister_client`](https://github.com/dfinity/ic/blob/528c6848d1afd1377958e6a71e8618e6d44c4665/rs/canister_client/sender/src/lib.rs#L126). --------- Co-authored-by: IDX GitHub Automation <infra+github-automation@dfinity.org>
1 parent 7c4fe9e commit 1f4a598

File tree

6 files changed

+135
-57
lines changed

6 files changed

+135
-57
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rs/canister_client/sender/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ pub enum Sender {
116116
PrincipalId(PrincipalId),
117117
/// Signed from the node itself, with its key.
118118
Node {
119-
/// DER encoded public key
119+
/// Ed25519 public key
120120
pub_key: Vec<u8>,
121121
/// Function that signs the message id
122122
sign: SignMessageId,

rs/orchestrator/BUILD.bazel

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ rust_library(
1818
version = "0.9.0",
1919
deps = [
2020
# Keep sorted.
21-
"//rs/canister_client",
22-
"//rs/canister_client/sender",
21+
"//packages/ic-ed25519",
2322
"//rs/config",
2423
"//rs/consensus",
2524
"//rs/consensus/dkg",
@@ -56,6 +55,7 @@ rust_library(
5655
"@crate_index//:hyper",
5756
"@crate_index//:hyper-rustls",
5857
"@crate_index//:hyper-util",
58+
"@crate_index//:ic-agent",
5959
"@crate_index//:idna",
6060
"@crate_index//:nix",
6161
"@crate_index//:prometheus",

rs/orchestrator/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ http-body-util = { workspace = true }
1919
hyper = { workspace = true }
2020
hyper-util = { workspace = true }
2121
hyper-rustls = { workspace = true }
22-
ic-http-endpoints-async-utils = { path = "../http_endpoints/async_utils" }
23-
ic-canister-client = { path = "../canister_client" }
24-
ic-canister-client-sender = { path = "../canister_client/sender" }
22+
ic-agent = { workspace = true }
2523
ic-config = { path = "../config" }
2624
ic-consensus = { path = "../consensus" }
2725
ic-consensus-dkg = { path = "../consensus/dkg" }
2826
ic-crypto = { path = "../crypto" }
2927
ic-crypto-node-key-generation = { path = "../crypto/node_key_generation" }
3028
ic-crypto-tls-interfaces = { path = "../crypto/tls_interfaces" }
3129
ic-dashboard = { path = "./dashboard" }
30+
ic-ed25519 = { path = "../../packages/ic-ed25519" }
31+
ic-http-endpoints-async-utils = { path = "../http_endpoints/async_utils" }
3232
ic-http-endpoints-metrics = { path = "../http_endpoints/metrics" }
3333
ic-http-utils = { path = "../http_utils" }
3434
ic-image-upgrader = { path = "./image_upgrader" }

rs/orchestrator/src/registration.rs

Lines changed: 25 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
use crate::{
33
error::{OrchestratorError, OrchestratorResult},
44
metrics::{KeyRotationStatus, OrchestratorMetrics},
5-
signer::{Hsm, NodeProviderSigner, Signer},
5+
signer::{Hsm, NodeProviderSigner, NodeSender, Signer},
66
utils::http_endpoint_to_url,
77
};
88
use candid::Encode;
9-
use ic_canister_client::{Agent, Sender};
9+
use ic_agent::{export::Principal, Agent};
1010
use ic_config::{
1111
http_handler::Config as HttpConfig,
1212
initial_ipv4_config::IPv4Config as InitialIPv4Config,
@@ -149,16 +149,18 @@ impl NodeRegistration {
149149
let nns_url = self
150150
.get_random_nns_url_from_config()
151151
.expect("no NNS urls available");
152-
let agent = Agent::new(nns_url, signer);
152+
let agent = Agent::builder()
153+
.with_url(nns_url)
154+
.with_identity(signer)
155+
.build()
156+
.expect("Failed to create IC agent");
157+
let add_node_encoded = Encode!(&add_node_payload)
158+
.expect("Could not encode payload for the registration request");
159+
153160
if let Err(e) = agent
154-
.execute_update(
155-
&REGISTRY_CANISTER_ID,
156-
&REGISTRY_CANISTER_ID,
157-
"add_node",
158-
Encode!(&add_node_payload)
159-
.expect("Could not encode payload for the registration request"),
160-
generate_nonce(),
161-
)
161+
.update(&Principal::from(REGISTRY_CANISTER_ID), "add_node")
162+
.with_arg(add_node_encoded)
163+
.call_and_wait()
162164
.await
163165
{
164166
warn!(self.log, "Registration request failed: {}", e);
@@ -440,26 +442,25 @@ impl NodeRegistration {
440442
})
441443
};
442444

443-
let sender = Sender::Node {
444-
pub_key: node_pub_key.key_value,
445-
sign: Arc::new(sign_cmd),
446-
};
447-
448-
let agent = Agent::new(nns_url.clone(), sender);
445+
let signer = NodeSender::new(node_pub_key, Arc::new(sign_cmd))?;
446+
let agent = Agent::builder()
447+
.with_url(nns_url)
448+
.with_identity(signer)
449+
.build()
450+
.map_err(|e| format!("Failed to create IC agent: {e}"))?;
449451
let update_node_payload = UpdateNodeDirectlyPayload {
450452
idkg_dealing_encryption_pk: Some(protobuf_to_vec(idkg_pk)),
451453
};
454+
let update_node_encoded = Encode!(&update_node_payload)
455+
.map_err(|e| format!("Could not encode payload for update_node-call: {e}"))?;
452456

453-
let arguments =
454-
Encode!(&update_node_payload).expect("Could not encode payload for update_node-call.");
455457
agent
456-
.execute_update(
457-
&REGISTRY_CANISTER_ID,
458-
&REGISTRY_CANISTER_ID,
458+
.update(
459+
&Principal::from(REGISTRY_CANISTER_ID),
459460
"update_node_directly",
460-
arguments,
461-
generate_nonce(),
462461
)
462+
.with_arg(update_node_encoded)
463+
.call_and_wait()
463464
.await
464465
.map_err(|e| format!("Error when sending register additional key request: {e}"))?;
465466

@@ -687,17 +688,6 @@ fn process_domain_name(log: &ReplicaLogger, domain: &str) -> OrchestratorResult<
687688
Ok(Some(domain.to_string()))
688689
}
689690

690-
/// Create a nonce to be included with the ingress message sent to the node
691-
/// handler.
692-
fn generate_nonce() -> Vec<u8> {
693-
SystemTime::now()
694-
.duration_since(SystemTime::UNIX_EPOCH)
695-
.unwrap()
696-
.as_nanos()
697-
.to_le_bytes()
698-
.to_vec()
699-
}
700-
701691
fn protobuf_to_vec<M: Message>(entry: M) -> Vec<u8> {
702692
let mut buf: Vec<u8> = Vec::new();
703693
entry.encode(&mut buf).expect("This must not fail");

rs/orchestrator/src/signer.rs

Lines changed: 102 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
1-
use ic_canister_client::Sender;
2-
use ic_canister_client_sender::{Secp256k1KeyPair, SigKeys};
1+
use ic_agent::{
2+
agent::EnvelopeContent, export::Principal, identity::Secp256k1Identity, Identity, Signature,
3+
};
4+
use ic_protobuf::registry::crypto::v1::{AlgorithmId, PublicKey};
35
use ic_sys::utility_command::{UtilityCommand, UtilityCommandResult};
6+
use ic_types::messages::MessageId;
47
use std::{path::Path, sync::Arc};
58

69
/// An abstract message signer interface.
710
pub trait Signer: Send + Sync {
811
/// Returns the message signer bundle containing the public key and a signing command. This
912
/// object is intended to be used with an agent to send messages to IC canisters.
10-
fn get(&self) -> UtilityCommandResult<Sender>;
13+
fn get(&self) -> UtilityCommandResult<Box<dyn Identity>>;
1114
}
1215

16+
type SignBytes = Arc<dyn Fn(&[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> + Send + Sync>;
17+
type SignMessageId =
18+
Arc<dyn Fn(&MessageId) -> Result<Vec<u8>, Box<dyn std::error::Error>> + Send + Sync>;
19+
1320
pub struct Hsm;
1421

1522
impl Signer for Hsm {
16-
fn get(&self) -> UtilityCommandResult<Sender> {
23+
fn get(&self) -> UtilityCommandResult<Box<dyn Identity>> {
1724
UtilityCommand::notify_host("Starting node registration.", 1);
1825
UtilityCommand::try_to_attach_hsm();
1926
let pub_key = UtilityCommand::read_public_key(None, None).execute()?;
@@ -27,29 +34,110 @@ impl Signer for Hsm {
2734
UtilityCommand::try_to_detach_hsm();
2835
res
2936
}
30-
Ok(Sender::ExternalHsm {
31-
pub_key,
37+
Ok(Box::new(ExternalHsmSender {
38+
der_encoded_pub_key: pub_key,
3239
sign: Arc::new(get_sign_command),
40+
}))
41+
}
42+
}
43+
44+
/// The sender is authenticated via an external HSM device and the signature mechanism is specified
45+
/// through the provided function reference.
46+
struct ExternalHsmSender {
47+
/// DER encoded public key
48+
der_encoded_pub_key: Vec<u8>,
49+
/// Function that abstracts the external HSM.
50+
sign: SignBytes,
51+
}
52+
53+
impl Identity for ExternalHsmSender {
54+
fn sender(&self) -> Result<Principal, String> {
55+
Ok(Principal::self_authenticating(
56+
self.der_encoded_pub_key.as_slice(),
57+
))
58+
}
59+
60+
fn public_key(&self) -> Option<Vec<u8>> {
61+
Some(self.der_encoded_pub_key.clone())
62+
}
63+
64+
fn sign(&self, content: &EnvelopeContent) -> Result<Signature, String> {
65+
let msg = content.to_request_id().signable();
66+
let signature =
67+
Some((self.sign)(&msg).map_err(|err| format!("Cannot create hsm signature: {err}"))?);
68+
let public_key = self.public_key();
69+
Ok(Signature {
70+
public_key,
71+
signature,
72+
delegations: None,
3373
})
3474
}
3575
}
3676

3777
pub struct NodeProviderSigner {
38-
keypair: Secp256k1KeyPair,
78+
identity: Secp256k1Identity,
3979
}
4080

4181
impl NodeProviderSigner {
4282
pub fn new(path: &Path) -> Option<Self> {
43-
let contents = std::fs::read_to_string(path).ok()?;
44-
let keypair = Secp256k1KeyPair::from_pem(&contents).ok()?;
45-
Some(Self { keypair })
83+
let identity = Secp256k1Identity::from_pem_file(path).ok()?;
84+
Some(Self { identity })
4685
}
4786
}
4887

4988
impl Signer for NodeProviderSigner {
50-
fn get(&self) -> UtilityCommandResult<Sender> {
51-
Ok(Sender::SigKeys(SigKeys::EcdsaSecp256k1(
52-
self.keypair.clone(),
53-
)))
89+
fn get(&self) -> UtilityCommandResult<Box<dyn Identity>> {
90+
Ok(Box::new(self.identity.clone()))
91+
}
92+
}
93+
94+
/// Signed from the node itself, with its key.
95+
pub struct NodeSender {
96+
/// DER encoded public key
97+
der_encoded_pub_key: Vec<u8>,
98+
/// Function that signs the message id
99+
sign: SignMessageId,
100+
}
101+
102+
impl NodeSender {
103+
pub fn new(pub_key: PublicKey, sign: SignMessageId) -> Result<Self, String> {
104+
if pub_key.algorithm() != AlgorithmId::Ed25519 {
105+
return Err(format!(
106+
"Unsupported algorithm: {}",
107+
pub_key.algorithm().as_str_name()
108+
));
109+
}
110+
111+
let der_encoded_pub_key = ic_ed25519::PublicKey::convert_raw_to_der(&pub_key.key_value)
112+
.map_err(|err| err.to_string())?;
113+
114+
Ok(Self {
115+
der_encoded_pub_key,
116+
sign,
117+
})
118+
}
119+
}
120+
121+
impl Identity for NodeSender {
122+
fn sender(&self) -> Result<Principal, String> {
123+
Ok(Principal::self_authenticating(
124+
self.der_encoded_pub_key.as_slice(),
125+
))
126+
}
127+
128+
fn public_key(&self) -> Option<Vec<u8>> {
129+
Some(self.der_encoded_pub_key.clone())
130+
}
131+
132+
fn sign(&self, content: &EnvelopeContent) -> Result<Signature, String> {
133+
let msg = MessageId::from(*content.to_request_id());
134+
let signature =
135+
Some((self.sign)(&msg).map_err(|err| format!("Cannot create node signature: {err}"))?);
136+
let public_key = self.public_key();
137+
Ok(Signature {
138+
public_key,
139+
signature,
140+
delegations: None,
141+
})
54142
}
55143
}

0 commit comments

Comments
 (0)