cli_commands/
address_association_signatures.rs1use crate::key_params::StakeSigningKeyParam;
2use anyhow::Ok;
3use byte_string::ByteString;
4use clap::Parser;
5use pallet_address_associations::AddressAssociationSignedMessage;
6use parity_scale_codec::Encode;
7use serde::Serialize;
8use serde_json::json;
9use sidechain_domain::*;
10use std::str::FromStr;
11
12#[derive(Clone, Debug, Parser)]
13#[command(author, version, about, long_about = None)]
14pub struct AddressAssociationSignaturesCmd<
15 PartnerchainAddress: Clone + Sync + Send + FromStr + 'static,
16> {
17 #[arg(long)]
19 pub genesis_utxo: UtxoId,
20 #[arg(long, value_parser=parse_pc_address::<PartnerchainAddress>)]
22 pub partnerchain_address: PartnerchainAddress,
23 #[arg(long)]
25 pub signing_key: StakeSigningKeyParam,
26}
27
28fn parse_pc_address<T: FromStr>(s: &str) -> Result<T, String> {
29 T::from_str(s).map_err(|_| "Failed to parse Partner Chain address".to_owned())
30}
31
32impl<PartnerchainAddress> AddressAssociationSignaturesCmd<PartnerchainAddress>
33where
34 PartnerchainAddress: Serialize + Clone + Sync + Send + FromStr + Encode + 'static,
35{
36 pub fn execute(&self) -> anyhow::Result<()> {
37 let signature = self.sign();
38 let output = json!({
39 "partnerchain_address": self.partnerchain_address,
40 "signature": signature,
41 "stake_public_key": self.signing_key.vkey()
42
43 });
44 println!("{}", serde_json::to_string_pretty(&output)?);
45 Ok(())
46 }
47
48 fn sign(&self) -> ByteString {
49 let msg = AddressAssociationSignedMessage {
50 stake_public_key: self.signing_key.vkey(),
51 partnerchain_address: self.partnerchain_address.clone(),
52 genesis_utxo: self.genesis_utxo,
53 };
54 let encoded = msg.encode();
55 self.signing_key.0.sign(&encoded).to_bytes().to_vec().into()
56 }
57}
58
59#[cfg(test)]
60mod test {
61 use super::*;
62 use hex::FromHexError;
63 use hex_literal::hex;
64 use sidechain_domain::byte_string::ByteString;
65
66 #[derive(Clone, Encode, Serialize)]
67 struct AccountId32(pub [u8; 32]);
68
69 impl FromStr for AccountId32 {
70 type Err = anyhow::Error;
71
72 fn from_str(s: &str) -> Result<Self, Self::Err> {
73 let bytes =
74 hex::decode(s)?.try_into().map_err(|_| FromHexError::InvalidStringLength)?;
75
76 Ok(Self(bytes))
77 }
78 }
79
80 #[test]
82 fn signature_test() {
83 let cmd = AddressAssociationSignaturesCmd {
84 genesis_utxo: UtxoId::new(
85 hex!("59104061ffa0d66f9ba0135d6fc6a884a395b10f8ae9cb276fc2c3bfdfedc260"),
86 1,
87 ),
88 partnerchain_address:
89 AccountId32(hex!("d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d")),
91 signing_key: StakeSigningKeyParam::from_str(
92 "d75c630516c33a66b11b3444a70b65083aeb21353bd919cc5e3daa02c9732a84"
94 ).unwrap(),
95 };
96
97 assert_eq!(
98 cmd.sign(),
99 ByteString(hex!("1aa8c1b363a207ddadf0c6242a0632f5a557690a327d0245f9d473b983b3d8e1c95a3dd804cab41123c36ddbcb7137b8261c35d5c8ef04ce9d0f8d5c4b3ca607").into())
100 );
101 }
102}