1use near_api_types::{
2 crypto::KeyType,
3 transaction::{
4 delegate_action::{DelegateAction, NonDelegateAction, SignedDelegateAction},
5 PrepopulateTransaction, SignedTransaction, Transaction, TransactionV0,
6 },
7 AccountId, BlockHeight, CryptoHash, Nonce, PublicKey, SecretKey, Signature,
8};
9use slipped10::BIP32Path;
10use tokio::sync::OnceCell;
11use tracing::{debug, info, instrument, warn};
12
13use crate::errors::{LedgerError, MetaSignError, SignerError};
14
15use super::{NEP413Payload, SignerTrait};
16
17const LEDGER_SIGNER_TARGET: &str = "near_api::signer::ledger";
18
19#[derive(Debug, Clone)]
20pub struct LedgerSigner {
21 hd_path: BIP32Path,
22 public_key: OnceCell<PublicKey>,
23}
24
25impl LedgerSigner {
26 pub const fn new(hd_path: BIP32Path) -> Self {
27 Self {
28 hd_path,
29 public_key: OnceCell::const_new(),
30 }
31 }
32}
33
34#[async_trait::async_trait]
35impl SignerTrait for LedgerSigner {
36 #[instrument(skip(self, tr), fields(signer_id = %tr.signer_id, receiver_id = %tr.receiver_id))]
37 async fn sign(
38 &self,
39 tr: PrepopulateTransaction,
40 public_key: PublicKey,
41 nonce: Nonce,
42 block_hash: CryptoHash,
43 ) -> Result<SignedTransaction, SignerError> {
44 debug!(target: LEDGER_SIGNER_TARGET, "Preparing unsigned transaction");
45 let unsigned_tx = Transaction::V0(TransactionV0 {
46 signer_id: tr.signer_id.clone(),
47 public_key,
48 receiver_id: tr.receiver_id,
49 nonce,
50 block_hash,
51 actions: tr.actions,
52 });
53 let unsigned_tx_bytes = borsh::to_vec(&unsigned_tx).map_err(LedgerError::from)?;
54 let hd_path = self.hd_path.clone();
55
56 info!(target: LEDGER_SIGNER_TARGET, "Signing transaction with Ledger");
57 let signature = tokio::task::spawn_blocking(move || {
58 let unsigned_tx_bytes = unsigned_tx_bytes;
59 let signature = near_ledger::sign_transaction(&unsigned_tx_bytes, hd_path)
60 .map_err(LedgerError::from)?;
61
62 Ok::<_, LedgerError>(signature)
63 })
64 .await
65 .map_err(LedgerError::from)?;
66
67 let signature = signature?;
68
69 debug!(target: LEDGER_SIGNER_TARGET, "Creating Signature object");
70 let signature = Signature::from_parts(KeyType::ED25519, signature.as_ref())
71 .map_err(|e| LedgerError::SignatureDeserializationError(e.to_string()))?;
72
73 info!(target: LEDGER_SIGNER_TARGET, "Transaction signed successfully");
74 Ok(SignedTransaction::new(signature, unsigned_tx))
75 }
76
77 #[instrument(skip(self, tr), fields(signer_id = %tr.signer_id, receiver_id = %tr.receiver_id))]
78 async fn sign_meta(
79 &self,
80 tr: PrepopulateTransaction,
81 public_key: PublicKey,
82 nonce: Nonce,
83 _block_hash: CryptoHash,
84 max_block_height: BlockHeight,
85 ) -> Result<SignedDelegateAction, MetaSignError> {
86 debug!(target: LEDGER_SIGNER_TARGET, "Preparing delegate action");
87 let actions = tr
88 .actions
89 .into_iter()
90 .map(NonDelegateAction::try_from)
91 .collect::<Result<_, _>>()
92 .map_err(|_| MetaSignError::DelegateActionIsNotSupported)?;
93 let delegate_action = DelegateAction {
94 sender_id: tr.signer_id,
95 receiver_id: tr.receiver_id,
96 actions,
97 nonce,
98 max_block_height,
99 public_key,
100 };
101
102 let delegate_action_bytes = borsh::to_vec(&delegate_action)
103 .map_err(LedgerError::from)
104 .map_err(SignerError::from)?;
105 let hd_path = self.hd_path.clone();
106
107 info!(target: LEDGER_SIGNER_TARGET, "Signing delegate action with Ledger");
108 let signature = tokio::task::spawn_blocking(move || {
109 let delegate_action_bytes = delegate_action_bytes;
110 let signature =
111 near_ledger::sign_message_nep366_delegate_action(&delegate_action_bytes, hd_path)
112 .map_err(LedgerError::from)?;
113
114 Ok::<_, LedgerError>(signature)
115 })
116 .await
117 .map_err(LedgerError::from)
118 .map_err(SignerError::from)?;
119
120 let signature = signature.map_err(SignerError::from)?;
121
122 debug!(target: LEDGER_SIGNER_TARGET, "Creating Signature object for delegate action");
123 let signature =
124 Signature::from_parts(KeyType::ED25519, signature.as_ref()).map_err(|e| {
125 SignerError::LedgerError(LedgerError::SignatureDeserializationError(e.to_string()))
126 })?;
127
128 info!(target: LEDGER_SIGNER_TARGET, "Delegate action signed successfully");
129 Ok(SignedDelegateAction {
130 delegate_action,
131 signature,
132 })
133 }
134
135 #[instrument(skip(self), fields(signer_id = %_signer_id, receiver_id = %payload.recipient, message = %payload.message))]
136 async fn sign_message_nep413(
137 &self,
138 _signer_id: AccountId,
139 _public_key: PublicKey,
140 payload: NEP413Payload,
141 ) -> Result<Signature, SignerError> {
142 info!(target: LEDGER_SIGNER_TARGET, "Signing NEP413 message with Ledger");
143 let hd_path = self.hd_path.clone();
144 let payload = payload.into();
145
146 let signature: Vec<u8> = tokio::task::spawn_blocking(move || {
147 let signature =
148 near_ledger::sign_message_nep413(&payload, hd_path).map_err(LedgerError::from)?;
149
150 Ok::<_, LedgerError>(signature)
151 })
152 .await
153 .unwrap_or_else(|tokio_join_error| Err(LedgerError::from(tokio_join_error)))?;
154
155 debug!(target: LEDGER_SIGNER_TARGET, "Creating Signature object for NEP413");
156 let signature =
157 Signature::from_parts(KeyType::ED25519, signature.as_ref()).map_err(|e| {
158 SignerError::LedgerError(LedgerError::SignatureDeserializationError(e.to_string()))
159 })?;
160
161 Ok(signature)
162 }
163
164 async fn get_secret_key(
165 &self,
166 _signer_id: &AccountId,
167 _public_key: &PublicKey,
168 ) -> Result<SecretKey, SignerError> {
169 warn!(target: LEDGER_SIGNER_TARGET, "Attempted to access secret key, which is not available for Ledger signer");
170 Err(SignerError::SecretKeyIsNotAvailable)
171 }
172
173 #[instrument(skip(self))]
174 fn get_public_key(&self) -> Result<PublicKey, SignerError> {
175 if let Some(public_key) = self.public_key.get() {
176 Ok(public_key.clone())
177 } else {
178 let public_key = near_ledger::get_wallet_id(self.hd_path.clone())
179 .map_err(|_| SignerError::PublicKeyIsNotAvailable)?;
180 let public_key = PublicKey::ED25519(
181 near_api_types::crypto::public_key::ED25519PublicKey(*public_key.as_bytes()),
182 );
183 self.public_key
184 .set(public_key.clone())
185 .map_err(LedgerError::from)?;
186 Ok(public_key)
187 }
188 }
189}