1#![cfg_attr(docsrs, feature(doc_cfg))]
3#![no_std]
4extern crate alloc;
5#[cfg(feature = "std")]
6extern crate std;
7
8#[cfg(feature = "std")]
9use {
10 alloc::{boxed::Box, string::ToString},
11 std::{
12 error,
13 fs::{self, File, OpenOptions},
14 io::{Read, Write},
15 path::Path,
16 },
17};
18use {
19 alloc::{collections::BTreeSet, string::String, vec::Vec},
20 core::{fmt, ops::Deref},
21 solana_pubkey::Pubkey,
22 solana_signature::Signature,
23 solana_transaction_error::TransactionError,
24};
25
26pub mod null_signer;
27pub mod signers;
28
29#[derive(Debug, PartialEq, Eq)]
30pub enum PresignerError {
31 VerificationFailure,
32}
33
34impl core::error::Error for PresignerError {}
35
36impl fmt::Display for PresignerError {
37 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38 match self {
39 Self::VerificationFailure => f.write_str("pre-generated signature cannot verify data"),
40 }
41 }
42}
43
44#[derive(Debug, PartialEq, Eq)]
45pub enum SignerError {
46 KeypairPubkeyMismatch,
47 NotEnoughSigners,
48 TransactionError(TransactionError),
49 Custom(String),
50 PresignerError(PresignerError),
52 Connection(String),
54 InvalidInput(String),
55 NoDeviceFound,
56 Protocol(String),
57 UserCancel(String),
58 TooManySigners,
59}
60
61impl core::error::Error for SignerError {
62 fn source(&self) -> ::core::option::Option<&(dyn core::error::Error + 'static)> {
63 match self {
64 Self::KeypairPubkeyMismatch => None,
65 Self::NotEnoughSigners => None,
66 Self::TransactionError(e) => Some(e),
67 Self::Custom(_) => None,
68 Self::PresignerError(e) => Some(e),
69 Self::Connection(_) => None,
70 Self::InvalidInput(_) => None,
71 Self::NoDeviceFound => None,
72 Self::Protocol(_) => None,
73 Self::UserCancel(_) => None,
74 Self::TooManySigners => None,
75 }
76 }
77}
78impl fmt::Display for SignerError {
79 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80 match self {
81 SignerError::KeypairPubkeyMismatch => f.write_str("keypair-pubkey mismatch"),
82 SignerError::NotEnoughSigners => f.write_str("not enough signers"),
83 SignerError::TransactionError(_) => f.write_str("transaction error"),
84 SignerError::Custom(e) => write!(f, "custom error: {e}",),
85 SignerError::PresignerError(_) => f.write_str("presigner error"),
86 SignerError::Connection(e) => write!(f, "connection error: {e}",),
87 SignerError::InvalidInput(s) => write!(f, "invalid input: {s}",),
88 SignerError::NoDeviceFound => f.write_str("no device found"),
89 SignerError::Protocol(s) => {
90 write!(f, "{s}")
91 }
92 SignerError::UserCancel(s) => {
93 write!(f, "{s}")
94 }
95 SignerError::TooManySigners => f.write_str("too many signers"),
96 }
97 }
98}
99
100impl From<TransactionError> for SignerError {
101 fn from(source: TransactionError) -> Self {
102 SignerError::TransactionError(source)
103 }
104}
105
106impl From<PresignerError> for SignerError {
107 fn from(source: PresignerError) -> Self {
108 SignerError::PresignerError(source)
109 }
110}
111
112pub trait Signer {
116 fn pubkey(&self) -> Pubkey {
119 self.try_pubkey().unwrap_or_default()
120 }
121 fn try_pubkey(&self) -> Result<Pubkey, SignerError>;
123 fn sign_message(&self, message: &[u8]) -> Signature {
126 self.try_sign_message(message).unwrap_or_default()
127 }
128 fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError>;
130 fn is_interactive(&self) -> bool;
132}
133
134impl<Container: Deref<Target = impl Signer + ?Sized>> Signer for Container {
136 #[inline]
137 fn pubkey(&self) -> Pubkey {
138 self.deref().pubkey()
139 }
140
141 fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
142 self.deref().try_pubkey()
143 }
144
145 fn sign_message(&self, message: &[u8]) -> Signature {
146 self.deref().sign_message(message)
147 }
148
149 fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError> {
150 self.deref().try_sign_message(message)
151 }
152
153 fn is_interactive(&self) -> bool {
154 self.deref().is_interactive()
155 }
156}
157
158impl PartialEq for dyn Signer {
159 fn eq(&self, other: &dyn Signer) -> bool {
160 self.pubkey() == other.pubkey()
161 }
162}
163
164impl Eq for dyn Signer {}
165
166impl fmt::Debug for dyn Signer {
167 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
168 write!(fmt, "Signer: {:?}", self.pubkey())
169 }
170}
171
172pub fn unique_signers(signers: Vec<&dyn Signer>) -> Vec<&dyn Signer> {
174 let capacity = signers.len();
175 let mut out = Vec::with_capacity(capacity);
176 let mut seen = BTreeSet::new();
177 for signer in signers {
178 let pubkey = signer.pubkey();
179 let is_unique = seen.insert(pubkey);
180 if is_unique {
181 out.push(signer);
182 }
183 }
184 out
185}
186
187#[cfg(feature = "std")]
190pub trait EncodableKey: Sized {
191 fn read<R: Read>(reader: &mut R) -> Result<Self, Box<dyn error::Error>>;
192 fn read_from_file<F: AsRef<Path>>(path: F) -> Result<Self, Box<dyn error::Error>> {
193 let mut file = File::open(path.as_ref())?;
194 Self::read(&mut file)
195 }
196 fn write<W: Write>(&self, writer: &mut W) -> Result<String, Box<dyn error::Error>>;
197 fn write_to_file<F: AsRef<Path>>(&self, outfile: F) -> Result<String, Box<dyn error::Error>> {
198 let outfile = outfile.as_ref();
199
200 if let Some(outdir) = outfile.parent() {
201 fs::create_dir_all(outdir)?;
202 }
203
204 let mut f = {
205 #[cfg(not(unix))]
206 {
207 OpenOptions::new()
208 }
209 #[cfg(unix)]
210 {
211 use std::os::unix::fs::OpenOptionsExt;
212 OpenOptions::new().mode(0o600)
213 }
214 }
215 .write(true)
216 .truncate(true)
217 .create(true)
218 .open(outfile)?;
219
220 self.write(&mut f)
221 }
222}
223
224#[cfg(feature = "std")]
227pub trait EncodableKeypair: EncodableKey {
228 type Pubkey: ToString;
229
230 fn encodable_pubkey(&self) -> Self::Pubkey;
232}