neo3/neo_wallets/
wallet_signer.rs

1use std::fmt;
2
3use p256::{ecdsa::Signature, NistP256};
4use primitive_types::{H160, H256};
5use serde_derive::{Deserialize, Serialize};
6use signature::hazmat::{PrehashSigner, PrehashVerifier};
7
8use crate::{
9	neo_builder::{AccountSigner, Transaction, TransactionError},
10	neo_clients::JsonRpcProvider,
11	neo_crypto::HashableForVec,
12	neo_types::Address,
13	neo_wallets::WalletError,
14};
15
16/// A Neo private-public key pair which can be used for signing messages.
17///
18/// # Examples
19///
20/// ## Signing and Verifying a message
21///
22/// The wallet can be used to produce ECDSA [`p256::NistP256`] objects, which can be
23/// then verified.
24///
25/// ```ignore
26/// // WalletSigner is used for transaction signing
27/// // Example usage is demonstrated in integration tests
28/// ```
29///
30/// [`p256::NistP256`]: p256::NistP256
31#[derive(Clone, Serialize, Deserialize)]
32pub struct WalletSigner<D: PrehashSigner<Signature>> {
33	/// The WalletSigner's private Key
34	pub(crate) signer: D,
35	/// The wallet's address
36	pub(crate) address: Address,
37	pub(crate) network: Option<u64>,
38}
39
40impl<D: PrehashSigner<Signature>> WalletSigner<D> {
41	/// Creates a new `WalletSigner` instance using an external `Signer` and associated Ethereum address.
42	///
43	/// # Arguments
44	///
45	/// * `signer` - An implementation of the `PrehashSigner` trait capable of signing messages.
46	/// * `address` - The Ethereum address associated with the signer's public key.
47	///
48	/// # Returns
49	///
50	/// A new `WalletSigner` instance.
51	pub fn new_with_signer(signer: D, address: Address) -> Self {
52		WalletSigner { signer, address, network: None }
53	}
54}
55
56impl<D: Sync + Send + PrehashSigner<Signature>> WalletSigner<D> {
57	/// Signs a given `Transaction`, using the wallet's private key.
58	///
59	/// # Arguments
60	///
61	/// * `tx` - A reference to the transaction to be signed.
62	///
63	/// # Returns
64	///
65	/// A `Result` containing the `p256::NistP256` of the transaction, or a `WalletError` on failure.
66	pub(crate) async fn sign_transaction<'a, P: JsonRpcProvider + 'static>(
67		&self,
68		tx: &Transaction<'a, P>,
69	) -> Result<Signature, WalletError> {
70		let mut tx_with_network = tx.clone();
71		if tx_with_network.network().is_none() {
72			// in the case we don't have a network, let's use the signer chain id instead
73			// tx_with_network.set_network(self.network.map(|n| n as u32));
74		}
75		let hash_data = tx_with_network.get_hash_data().await.map_err(|e| {
76			WalletError::TransactionError(TransactionError::TransactionConfiguration(format!(
77				"Failed to get transaction hash data: {}",
78				e
79			)))
80		})?;
81
82		self.signer.sign_prehash(&hash_data).map_err(|_| WalletError::SignHashError)
83	}
84
85	/// Signs a given hash directly, without performing any additional hashing.
86	///
87	/// # Arguments
88	///
89	/// * `hash` - A `H256` hash to be signed.
90	///
91	/// # Returns
92	///
93	/// A `Result` containing the `p256::NistP256` of the hash, or a `WalletError` on failure.
94	pub fn sign_hash(&self, hash: H256) -> Result<Signature, WalletError> {
95		self.signer.sign_prehash(hash.as_ref()).map_err(|_| WalletError::SignHashError)
96	}
97
98	/// Signs a given message, using the wallet's private key.
99	/// The message will be hashed using the `Sha256` algorithm before being signed.
100	///
101	/// # Arguments
102	///
103	/// * `message` - The message to be signed.
104	///
105	/// # Returns
106	///
107	/// A `Result` containing the `p256::NistP256` of the message, or a `WalletError` on failure.
108	pub async fn sign_message(&self, message: &[u8]) -> Result<Signature, WalletError> {
109		let hash = message.hash256();
110		self.sign_hash(H256::from_slice(hash.as_slice()))
111	}
112
113	/// Returns a reference to the wallet's signer.
114	///
115	/// # Returns
116	///
117	/// A reference to the `D`, the signer.
118	pub fn signer(&self) -> &D {
119		&self.signer
120	}
121
122	/// Returns the wallet's associated address.
123	///
124	/// # Returns
125	///
126	/// The `Address` of the wallet.
127	pub(crate) fn address(&self) -> Address {
128		self.address.clone()
129	}
130
131	/// Gets the wallet's chain id
132	fn network(&self) -> Option<u64> {
133		self.network
134	}
135
136	/// Associates the wallet with a specific network ID.
137	///
138	/// # Arguments
139	///
140	/// * `network` - The network ID to associate with the wallet.
141	///
142	/// # Returns
143	///
144	/// The `WalletSigner` instance with the network ID set.
145	fn with_network<T: Into<u64>>(mut self, network: T) -> Self {
146		self.network = Some(network.into());
147		self
148	}
149}
150
151// do not log the signer
152impl<D: PrehashSigner<Signature> + PrehashVerifier<Signature>> fmt::Debug for WalletSigner<D> {
153	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154		f.debug_struct("WalletSigner")
155			.field("address", &self.address)
156			.field("chain_Id", &self.network)
157			.finish()
158	}
159}