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