Skip to main content

nym_validator_client/signing/
signer.rs

1// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::signing::AccountData;
5pub use cosmrs::crypto::secp256k1::Signature;
6use cosmrs::tx::SignDoc;
7use cosmrs::{tx, AccountId};
8use thiserror::Error;
9
10#[derive(Debug, Error)]
11pub enum SigningError {
12    #[error("the requested signing type: {typ:?} is not supported by this signer.")]
13    UnsupportedSigningType { typ: SignerType },
14
15    #[error("account {account} was not found within this signer")]
16    AccountNotFound { account: AccountId },
17
18    #[error("failed to sign the requested message: {source}")]
19    SigningFailure { source: eyre::Report },
20
21    #[error("failed to construct the sign doc: {source}")]
22    SignDocFailure { source: eyre::Report },
23}
24
25#[derive(Copy, Clone, Eq, PartialEq, Debug)]
26pub enum SignerType {
27    Amino,
28    Direct,
29}
30
31// TODO: check if this trait needs to be async
32// (I guess it depends on future ledger requirements)
33pub trait OfflineSigner {
34    type Error: From<SigningError>;
35
36    fn signer_addresses(&self) -> Vec<AccountId> {
37        self.get_accounts()
38            .iter()
39            .map(|account| account.address.clone())
40            .collect()
41    }
42
43    fn get_accounts(&self) -> &[AccountData];
44
45    fn find_account(&self, signer_address: &AccountId) -> Result<&AccountData, Self::Error> {
46        self.get_accounts()
47            .iter()
48            .find(|account| &account.address == signer_address)
49            .ok_or_else(|| {
50                SigningError::AccountNotFound {
51                    account: signer_address.clone(),
52                }
53                .into()
54            })
55    }
56
57    fn sign_raw_with_account<M: AsRef<[u8]>>(
58        &self,
59        signer: &AccountData,
60        message: M,
61    ) -> Result<Signature, Self::Error> {
62        signer
63            .private_key
64            .sign(message.as_ref())
65            .map_err(|source| SigningError::SigningFailure { source }.into())
66    }
67
68    fn sign_raw<M: AsRef<[u8]>>(
69        &self,
70        signer_address: &AccountId,
71        message: M,
72    ) -> Result<Signature, Self::Error> {
73        let signer = self.find_account(signer_address)?;
74        self.sign_raw_with_account(signer, message)
75    }
76
77    fn sign_direct(
78        &self,
79        signer_address: &AccountId,
80        sign_doc: SignDoc,
81    ) -> Result<tx::Raw, Self::Error> {
82        let signer = self.find_account(signer_address)?;
83        self.sign_direct_with_account(signer, sign_doc)
84    }
85
86    // unless explicitly defined, each signing method is unsupported
87    fn sign_direct_with_account(
88        &self,
89        _signer: &AccountData,
90        _sign_doc: SignDoc,
91    ) -> Result<tx::Raw, Self::Error> {
92        Err(SigningError::UnsupportedSigningType {
93            typ: SignerType::Direct,
94        }
95        .into())
96    }
97
98    // fn sign_amino(&self, signer_address: &AccountId, sign_doc: AminoSignDoc) -> Result<tx::Raw, Self::Error>;
99
100    // fn sign_amino_with_account(&self, signer: &AccountData, sign_doc: AminoSignDoc) -> Result<tx::Raw, Self::Error>;
101}
102
103#[derive(Debug, Default, Copy, Clone)]
104pub struct NoSigner;
105
106// #[derive(Debug, Copy, Clone, Error)]
107// #[error("no signer is available")]
108// struct SignerUnavailable;
109//
110// // trait bound requirements
111// impl From<SigningError> for SignerUnavailable {
112//     fn from(_: SigningError) -> Self {
113//         SignerUnavailable
114//     }
115// }
116//
117// impl OfflineSigner for NoSigner {
118//     type Error = SignerUnavailable;
119//
120//     fn get_accounts(&self) -> &[AccountData] {
121//         return Err(SignerUnavailable);
122//     }
123// }