1use crate::{
21 error, params::MessageParams, utils, with_crypto_scheme, CryptoSchemeFlag, KeystoreParams,
22};
23use array_bytes::bytes2hex;
24use clap::Parser;
25use sp_core::crypto::SecretString;
26use std::io::{BufRead, Write};
27
28#[derive(Debug, Clone, Parser)]
30#[command(name = "sign", about = "Sign a message, with a given (secret) key")]
31pub struct SignCmd {
32 #[arg(long)]
36 suri: Option<String>,
37
38 #[allow(missing_docs)]
39 #[clap(flatten)]
40 pub message_params: MessageParams,
41
42 #[allow(missing_docs)]
43 #[clap(flatten)]
44 pub keystore_params: KeystoreParams,
45
46 #[allow(missing_docs)]
47 #[clap(flatten)]
48 pub crypto_scheme: CryptoSchemeFlag,
49}
50
51impl SignCmd {
52 pub fn run(&self) -> error::Result<()> {
54 let sig = self.sign(|| std::io::stdin().lock())?;
55 std::io::stdout().lock().write_all(sig.as_bytes())?;
56 Ok(())
57 }
58
59 pub(crate) fn sign<F, R>(&self, create_reader: F) -> error::Result<String>
65 where
66 R: BufRead,
67 F: FnOnce() -> R,
68 {
69 let message = self.message_params.message_from(create_reader)?;
70 let suri = utils::read_uri(self.suri.as_ref())?;
71 let password = self.keystore_params.read_password()?;
72
73 with_crypto_scheme!(self.crypto_scheme.scheme, sign(&suri, password, message))
74 }
75}
76
77fn sign<P: sp_core::Pair>(
78 suri: &str,
79 password: Option<SecretString>,
80 message: Vec<u8>,
81) -> error::Result<String> {
82 let pair = utils::pair_from_suri::<P>(suri, password)?;
83 Ok(bytes2hex("0x", pair.sign(&message).as_ref()))
84}
85
86#[cfg(test)]
87mod test {
88 use super::*;
89
90 const SEED: &str = "0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a";
91
92 #[test]
93 fn sign_arg() {
94 let cmd = SignCmd::parse_from(&[
95 "sign",
96 "--suri",
97 &SEED,
98 "--message",
99 &SEED,
100 "--password",
101 "12345",
102 "--hex",
103 ]);
104 let sig = cmd.sign(|| std::io::stdin().lock()).expect("Must sign");
105
106 assert!(sig.starts_with("0x"), "Signature must start with 0x");
107 assert!(array_bytes::hex2bytes(&sig).is_ok(), "Signature is valid hex");
108 }
109
110 #[test]
111 fn sign_stdin() {
112 let cmd = SignCmd::parse_from(&[
113 "sign",
114 "--suri",
115 SEED,
116 "--message",
117 &SEED,
118 "--password",
119 "12345",
120 ]);
121 let sig = cmd.sign(|| SEED.as_bytes()).expect("Must sign");
122
123 assert!(sig.starts_with("0x"), "Signature must start with 0x");
124 assert!(array_bytes::hex2bytes(&sig).is_ok(), "Signature is valid hex");
125 }
126}