Skip to content

Commit

Permalink
feat!: remove direct near dependencies from near-ledger (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
akorchyn authored Aug 21, 2024
1 parent a9bcf8c commit 8a7310a
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 118 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@ ledger-apdu = "0.11.0"
slipped10 = { version = "0.4.6" }
log = "0.4.20"
hex = "0.4.3"
near-primitives-core = ">0.22,<0.26"
near-primitives = ">0.22,<0.26"
borsh = "1.5"

[dev-dependencies]
env_logger = "0.11.0"
near-crypto = ">0.22,<0.26"
near-primitives = ">0.22,<0.26"
near-primitives-core = ">0.22,<0.26"
near-account-id = { version = "1.0.0", features = ["internal_unstable"] }
2 changes: 1 addition & 1 deletion examples/common/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ where
let unsigned_transaction = f_transaction(ledger_pub_key);

let bytes = serialize_and_display_tx(unsigned_transaction);
let signature_bytes = near_ledger::sign_transaction(bytes.clone(), hd_path)?;
let signature_bytes = near_ledger::sign_transaction(&bytes, hd_path)?;

display_and_verify_signature(bytes, signature_bytes, ledger_pub_key);

Expand Down
6 changes: 4 additions & 2 deletions examples/sign_nep_366_delegate_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ fn main() -> Result<(), NEARLedgerError> {
public_key: ledger_pub_key,
};

let signature_bytes =
near_ledger::sign_message_nep366_delegate_action(&delegate_action, hd_path)?;
let bytes = borsh::to_vec(&delegate_action)
.expect("Delegate action is not expected to fail on serialization");

let signature_bytes = near_ledger::sign_message_nep366_delegate_action(&bytes, hd_path)?;

let signature = Signature::from_parts(near_crypto::KeyType::ED25519, &signature_bytes).unwrap();

Expand Down
139 changes: 26 additions & 113 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
//! Provides a set of commands that can be executed to communicate with NEAR App installed on Ledger device:
//! - Read PublicKey from Ledger device by HD Path
//! - Sign a Transaction
use borsh::BorshSerialize;
use ed25519_dalek::PUBLIC_KEY_LENGTH;
use ledger_apdu::APDUAnswer;
use ledger_transport::APDUCommand;
use ledger_transport_hid::{
hidapi::{HidApi, HidError},
LedgerHIDError, TransportNativeHID,
};
use near_primitives::action::delegate::DelegateAction;
use near_primitives_core::borsh::{self, BorshSerialize};

const CLA: u8 = 0x80; // Instruction class
const INS_GET_PUBLIC_KEY: u8 = 4; // Instruction code to get public key
Expand All @@ -25,7 +24,9 @@ const RETURN_CODE_OK: u16 = 36864; // APDUAnswer.retcode which means success fro
const CHUNK_SIZE: usize = 250; // Chunk size to be sent to Ledger

/// Alias of `Vec<u8>`. The goal is naming to help understand what the bytes to deal with
pub type BorshSerializedUnsignedTransaction = Vec<u8>;
pub type BorshSerializedUnsignedTransaction<'a> = &'a [u8];
/// Alias of `Vec<u8>`. The goal is naming to help understand what the bytes to deal with
pub type BorshSerializedDelegateAction<'a> = &'a [u8];

const P1_GET_PUB_DISPLAY: u8 = 0;
const P1_GET_PUB_SILENT: u8 = 1;
Expand Down Expand Up @@ -289,7 +290,7 @@ fn get_transport() -> Result<TransportNativeHID, NEARLedgerError> {
/// # let near_unsigned_transaction = [10; 250];
/// let hd_path = BIP32Path::from_str("44'/397'/0'/0'/1'").unwrap();
/// let borsh_transaction = borsh::to_vec(&near_unsigned_transaction).unwrap();
/// let signature = sign_transaction(borsh_transaction, hd_path).unwrap();
/// let signature = sign_transaction(&borsh_transaction, hd_path).unwrap();
/// println!("{:#?}", signature);
/// # }
/// ```
Expand All @@ -307,62 +308,10 @@ pub fn sign_transaction(
unsigned_tx: BorshSerializedUnsignedTransaction,
seed_phrase_hd_path: slipped10::BIP32Path,
) -> Result<SignatureBytes, NEARLedgerError> {
let transport = get_transport()?;
// seed_phrase_hd_path must be converted into bytes to be sent as `data` to the Ledger
let hd_path_bytes = hd_path_to_bytes(&seed_phrase_hd_path);

let mut data: Vec<u8> = vec![];
data.extend(hd_path_bytes);
data.extend(&unsigned_tx);

let chunks = data.chunks(CHUNK_SIZE);
let chunks_count = chunks.len();

for (i, chunk) in chunks.enumerate() {
let is_last_chunk = chunks_count == i + 1;
let command = APDUCommand {
cla: CLA,
ins: INS_SIGN_TRANSACTION,
p1: if is_last_chunk {
P1_SIGN_NORMAL_LAST_CHUNK
} else {
P1_SIGN_NORMAL
}, // Instruction parameter 1 (offset)
p2: NETWORK_ID,
data: chunk.to_vec(),
};
log_command(i, is_last_chunk, &command);
match transport.exchange(&command) {
Ok(response) => {
log::info!(
"APDU out: {}\nAPDU ret code: {:x}",
hex::encode(response.apdu_data()),
response.retcode(),
);
// Ok means we successfully exchanged with the Ledger
// but doesn't mean our request succeeded
// we need to check it based on `response.retcode`
if response.retcode() == RETURN_CODE_OK {
if is_last_chunk {
return Ok(response.data().to_vec());
}
} else {
let retcode = response.retcode();

let error_string = format!("Ledger APDU retcode: 0x{:X}", retcode);
return Err(NEARLedgerError::APDUExchangeError(error_string));
}
}
Err(err) => return Err(NEARLedgerError::LedgerHIDError(err)),
};
}
Err(NEARLedgerError::APDUExchangeError(
"Unable to process request".to_owned(),
))
sign_internal(unsigned_tx, seed_phrase_hd_path, INS_SIGN_TRANSACTION)
}

#[derive(Debug, BorshSerialize)]
#[borsh(crate = "near_primitives_core::borsh")]
pub struct NEP413Payload {
pub messsage: String,
pub nonce: [u8; 32],
Expand All @@ -374,80 +323,44 @@ pub fn sign_message_nep413(
payload: &NEP413Payload,
seed_phrase_hd_path: slipped10::BIP32Path,
) -> Result<SignatureBytes, NEARLedgerError> {
let transport = get_transport()?;
// seed_phrase_hd_path must be converted into bytes to be sent as `data` to the Ledger
let hd_path_bytes = hd_path_to_bytes(&seed_phrase_hd_path);

let mut data: Vec<u8> = vec![];
data.extend(hd_path_bytes);
data.extend_from_slice(&borsh::to_vec(payload).unwrap());

let chunks = data.chunks(CHUNK_SIZE);
let chunks_count = chunks.len();

for (i, chunk) in chunks.enumerate() {
let is_last_chunk = chunks_count == i + 1;
let command = APDUCommand {
cla: CLA,
ins: INS_SIGN_NEP413_MESSAGE,
p1: if is_last_chunk {
P1_SIGN_NORMAL_LAST_CHUNK
} else {
P1_SIGN_NORMAL
}, // Instruction parameter 1 (offset)
p2: NETWORK_ID,
data: chunk.to_vec(),
};
log_command(i, is_last_chunk, &command);
match transport.exchange(&command) {
Ok(response) => {
log::info!(
"APDU out: {}\nAPDU ret code: {:x}",
hex::encode(response.apdu_data()),
response.retcode(),
);
// Ok means we successfully exchanged with the Ledger
// but doesn't mean our request succeeded
// we need to check it based on `response.retcode`
if response.retcode() == RETURN_CODE_OK {
if is_last_chunk {
return Ok(response.data().to_vec());
}
} else {
let retcode = response.retcode();

let error_string = format!("Ledger APDU retcode: 0x{:X}", retcode);
return Err(NEARLedgerError::APDUExchangeError(error_string));
}
}
Err(err) => return Err(NEARLedgerError::LedgerHIDError(err)),
};
}
Err(NEARLedgerError::APDUExchangeError(
"Unable to process request".to_owned(),
))
sign_internal(
&borsh::to_vec(payload).unwrap(),
seed_phrase_hd_path,
INS_SIGN_NEP413_MESSAGE,
)
}

pub fn sign_message_nep366_delegate_action(
payload: &DelegateAction,
payload: BorshSerializedDelegateAction,
seed_phrase_hd_path: slipped10::BIP32Path,
) -> Result<SignatureBytes, NEARLedgerError> {
sign_internal(
payload,
seed_phrase_hd_path,
INS_SIGN_NEP366_DELEGATE_ACTION,
)
}

fn sign_internal(
payload: &[u8],
seed_phrase_hd_path: slipped10::BIP32Path,
ins: u8,
) -> Result<SignatureBytes, NEARLedgerError> {
let transport = get_transport()?;
// seed_phrase_hd_path must be converted into bytes to be sent as `data` to the Ledger
let hd_path_bytes = hd_path_to_bytes(&seed_phrase_hd_path);

let mut data: Vec<u8> = vec![];
data.extend(hd_path_bytes);
data.extend_from_slice(&borsh::to_vec(payload).unwrap());

data.extend_from_slice(payload);
let chunks = data.chunks(CHUNK_SIZE);
let chunks_count = chunks.len();

for (i, chunk) in chunks.enumerate() {
let is_last_chunk = chunks_count == i + 1;
let command = APDUCommand {
cla: CLA,
ins: INS_SIGN_NEP366_DELEGATE_ACTION,
ins,
p1: if is_last_chunk {
P1_SIGN_NORMAL_LAST_CHUNK
} else {
Expand Down

0 comments on commit 8a7310a

Please sign in to comment.