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}