partner_chains_cli/register/
register2.rs1use super::RegisterValidatorMessage;
2use super::{PlainPublicKeyParam, SidechainPublicKeyParam, StakePoolSigningKeyParam};
3use crate::CmdRun;
4use crate::cardano_key::get_mc_staking_signing_key_from_file;
5use crate::io::IOContext;
6use clap::Parser;
7use sidechain_domain::UtxoId;
8use sidechain_domain::crypto::cardano_spo_public_key_and_signature;
9
10#[derive(Clone, Debug, Parser)]
11#[command(author, version, about, long_about = None)]
12pub struct Register2Cmd {
13 #[arg(long)]
14 pub genesis_utxo: UtxoId,
15 #[arg(long)]
16 pub registration_utxo: UtxoId,
17 #[arg(long)]
18 pub sidechain_pub_key: SidechainPublicKeyParam,
19 #[arg(long)]
20 pub aura_pub_key: PlainPublicKeyParam,
21 #[arg(long)]
22 pub grandpa_pub_key: PlainPublicKeyParam,
23 #[arg(long)]
24 pub sidechain_signature: String,
25}
26
27impl CmdRun for Register2Cmd {
28 fn run<C: IOContext>(&self, context: &C) -> anyhow::Result<()> {
29 context.print("⚙️ Register as a committee candidate (step 2/3)");
30 context.print(
31 " This command will use SPO cold signing key for signing the registration message.",
32 );
33
34 let stake_pool_signing_key_path =
35 context.prompt("Path to Stake Pool signing key file", Some("cold.skey"));
36 let mainchain_signing_key = get_stake_pool_cold_skey(context, &stake_pool_signing_key_path)
37 .inspect_err(|_| context.eprint("Unable to read Stake Pool signing key file"))?;
38
39 let registration_message = RegisterValidatorMessage {
40 genesis_utxo: self.genesis_utxo,
41 sidechain_pub_key: self.sidechain_pub_key.0.clone(),
42 registration_utxo: self.registration_utxo,
43 };
44
45 let (verification_key, spo_signature) =
46 cardano_spo_public_key_and_signature(mainchain_signing_key.0, registration_message);
47
48 let spo_public_key = verification_key.to_hex_string();
49 let spo_signature = spo_signature.to_hex_string();
50 let executable = context.current_executable()?;
51 context.print("To finish the registration process, run the following command on the machine with the partner chain dependencies running:\n");
52 context.print(&format!(
53 "{executable} wizards register3 \\\n--genesis-utxo {} \\\n--registration-utxo {} \\\n--aura-pub-key {} \\\n--grandpa-pub-key {} \\\n--partner-chain-pub-key {} \\\n--partner-chain-signature {} \\\n--spo-public-key {} \\\n--spo-signature {}",
54 self.genesis_utxo, self.registration_utxo, self.aura_pub_key, self.grandpa_pub_key, self.sidechain_pub_key, self.sidechain_signature, spo_public_key, spo_signature));
55 Ok(())
56 }
57}
58
59fn get_stake_pool_cold_skey<C: IOContext>(
60 context: &C,
61 keys_path: &str,
62) -> Result<StakePoolSigningKeyParam, anyhow::Error> {
63 Ok(StakePoolSigningKeyParam::from(get_mc_staking_signing_key_from_file(keys_path, context)?))
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use crate::tests::{MockIO, MockIOContext};
70
71 #[test]
72 fn happy_path() {
73 let mock_context = MockIOContext::new()
74 .with_json_file("/path/to/cold.skey", coldkey_content())
75 .with_expected_io(
76 vec![intro_msg_io(), prompt_mc_cold_key_path_io(), output_result_io()]
77 .into_iter()
78 .flatten()
79 .collect::<Vec<MockIO>>(),
80 );
81
82 let result = mock_register2_cmd().run(&mock_context);
83 result.expect("should succeed");
84 }
85
86 #[test]
87 fn invalid_stake_pool_signing_key() {
88 let mock_context = MockIOContext::new().with_expected_io(vec![
89 MockIO::Group(intro_msg_io()),
90 MockIO::prompt(
91 "Path to Stake Pool signing key file",
92 Some("cold.skey"),
93 "/invalid/cold.skey",
94 ),
95 MockIO::eprint("Unable to read Stake Pool signing key file"),
96 ]);
97
98 let result = mock_register2_cmd().run(&mock_context);
99 result.expect_err("should return error");
100 }
101
102 fn intro_msg_io() -> Vec<MockIO> {
103 vec![
104 MockIO::print("⚙️ Register as a committee candidate (step 2/3)"),
105 MockIO::print(
106 " This command will use SPO cold signing key for signing the registration message.",
107 ),
108 ]
109 }
110
111 fn prompt_mc_cold_key_path_io() -> Vec<MockIO> {
112 vec![MockIO::prompt(
113 "Path to Stake Pool signing key file",
114 Some("cold.skey"),
115 "/path/to/cold.skey",
116 )]
117 }
118
119 fn output_result_io() -> Vec<MockIO> {
120 vec![
121 MockIO::print(
122 "To finish the registration process, run the following command on the machine with the partner chain dependencies running:\n",
123 ),
124 MockIO::print(
125 "<mock executable> wizards register3 \\\n--genesis-utxo 0000000000000000000000000000000000000000000000000000000000000001#0 \\\n--registration-utxo 7e9ebd0950ae1bec5606f0cd7ac88b3c60b1103d7feb6ffa36402edae4d1b617#0 \\\n--aura-pub-key 0xdf883ee0648f33b6103017b61be702017742d501b8fe73b1d69ca0157460b777 \\\n--grandpa-pub-key 0x5a091a06abd64f245db11d2987b03218c6bd83d64c262fe10e3a2a1230e90327 \\\n--partner-chain-pub-key 0x031e75acbf45ef8df98bbe24b19b28fff807be32bf88838c30c0564d7bec5301f6 \\\n--partner-chain-signature 7a7e3e585a5dc248d4a2772814e1b58c90313443dd99369f994e960ecc4931442a08305743db7ab42ab9b8672e00250e1cc7c08bc018b0630a8197c4f95528a301 \\\n--spo-public-key 0xcef2d1630c034d3b9034eb7903d61f419a3074a1ad01d4550cc72f2b733de6e7 \\\n--spo-signature 0xaaa39fbf163ed77c69820536f5dc22854e7e13f964f1e077efde0844a09bde64c1aab4d2b401e0fe39b43c91aa931cad26fa55c8766378462c06d86c85134801",
126 ),
127 ]
128 }
129 fn mock_register2_cmd() -> Register2Cmd {
131 Register2Cmd {
132 genesis_utxo: "0000000000000000000000000000000000000000000000000000000000000001#0".parse().unwrap(),
133 registration_utxo: "7e9ebd0950ae1bec5606f0cd7ac88b3c60b1103d7feb6ffa36402edae4d1b617#0".parse().unwrap(),
134 sidechain_pub_key: "0x031e75acbf45ef8df98bbe24b19b28fff807be32bf88838c30c0564d7bec5301f6".parse().unwrap(),
135 aura_pub_key: "0xdf883ee0648f33b6103017b61be702017742d501b8fe73b1d69ca0157460b777".parse().unwrap(),
136 grandpa_pub_key: "0x5a091a06abd64f245db11d2987b03218c6bd83d64c262fe10e3a2a1230e90327".parse().unwrap(),
137 sidechain_signature: "7a7e3e585a5dc248d4a2772814e1b58c90313443dd99369f994e960ecc4931442a08305743db7ab42ab9b8672e00250e1cc7c08bc018b0630a8197c4f95528a301".parse().unwrap()
138 }
139 }
140
141 fn coldkey_content() -> serde_json::Value {
142 serde_json::json!({
143 "type": "StakePoolSigningKey_ed25519",
144 "description": "Stake Pool Operator Signing Key",
145 "cborHex": "58200c049bb92212b779ee8ba9550536d8103cc1892634f0d21dcaa8944f5e4bf718"
146 })
147 }
148}