Skip to content

Commit

Permalink
WIP on feat/signer_with_send
Browse files Browse the repository at this point in the history
  • Loading branch information
willemneal committed Jun 19, 2024
1 parent 027a3f4 commit 139b1a7
Show file tree
Hide file tree
Showing 16 changed files with 253 additions and 185 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ version = "=21.0.0"
path = "cmd/soroban-cli"

[workspace.dependencies.stellar-ledger]
version = "=21.0.0-rc.1"
version = "=21.0.0"
path = "cmd/crates/stellar-ledger"

[workspace.dependencies.soroban-rpc]
Expand Down
4 changes: 3 additions & 1 deletion cmd/crates/soroban-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ impl TestEnv {
config_dir,
},
hd_path: None,
yes: true,
}
}

Expand Down Expand Up @@ -263,9 +264,10 @@ impl TestEnv {
}

/// Returns the public key corresponding to the test keys's `hd_path`
pub fn test_address(&self, hd_path: usize) -> String {
pub async fn test_address(&self, hd_path: usize) -> String {
self.cmd::<keys::address::Cmd>(&format!("--hd-path={hd_path}"))
.public_key()
.await
.unwrap()
.to_string()
}
Expand Down
6 changes: 3 additions & 3 deletions cmd/crates/soroban-test/tests/it/util.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::path::Path;

use soroban_cli::commands::{
config::{locator::KeyType, secret::Secret},
config::{locator::KeyType, secret::Signer},
contract,
};
use soroban_test::{TestEnv, Wasm, TEST_ACCOUNT};
Expand All @@ -17,10 +17,10 @@ pub enum SecretKind {
#[allow(clippy::needless_pass_by_value)]
pub fn add_key(dir: &Path, name: &str, kind: SecretKind, data: &str) {
let secret = match kind {
SecretKind::Seed => Secret::SeedPhrase {
SecretKind::Seed => Signer::SeedPhrase {
seed_phrase: data.to_string(),
},
SecretKind::Key => Secret::SecretKey {
SecretKind::Key => Signer::SecretKey {
secret_key: data.to_string(),
},
};
Expand Down
21 changes: 8 additions & 13 deletions cmd/crates/stellar-ledger/src/hd_path.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use crate::Error;

#[derive(Clone, Copy)]
pub struct HdPath(pub u32);

impl HdPath {
#[must_use]
pub fn depth(&self) -> u8 {
let path: slip10::BIP32Path = self.into();
path.depth()
Expand All @@ -23,7 +22,8 @@ impl From<&u32> for HdPath {
}

impl HdPath {
pub fn to_vec(&self) -> Result<Vec<u8>, Error> {
#[must_use]
pub fn to_vec(&self) -> Vec<u8> {
hd_path_to_bytes(&self.into())
}
}
Expand All @@ -35,16 +35,11 @@ impl From<&HdPath> for slip10::BIP32Path {
}
}

fn hd_path_to_bytes(hd_path: &slip10::BIP32Path) -> Result<Vec<u8>, Error> {
fn hd_path_to_bytes(hd_path: &slip10::BIP32Path) -> Vec<u8> {
let hd_path_indices = 0..hd_path.depth();
let result = hd_path_indices
// Unsafe unwrap is safe because the depth is the length of interneal vector
hd_path_indices
.into_iter()
.map(|index| {
Ok(hd_path
.index(index)
.ok_or_else(|| Error::Bip32PathError(format!("{hd_path}")))?
.to_be_bytes())
})
.collect::<Result<Vec<_>, Error>>()?;
Ok(result.into_iter().flatten().collect())
.flat_map(|index| unsafe { hd_path.index(index).unwrap_unchecked().to_be_bytes() })
.collect()
}
16 changes: 12 additions & 4 deletions cmd/crates/stellar-ledger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ pub use crate::signer::Blob;
pub mod hd_path;
pub use ledger_transport::Exchange;

mod emulator_http_transport;
mod signer;

// this is from https://github.com/LedgerHQ/ledger-live/blob/36cfbf3fa3300fd99bcee2ab72e1fd8f280e6280/libs/ledgerjs/packages/hw-app-str/src/Str.ts#L181
Expand Down Expand Up @@ -84,6 +83,10 @@ pub struct LedgerSigner<T: Exchange> {
unsafe impl<T> Send for LedgerSigner<T> where T: Exchange {}
unsafe impl<T> Sync for LedgerSigner<T> where T: Exchange {}

/// Returns a new `LedgerSigner` with a native HID transport, e.i. the transport is connected to the Ledger device
///
/// # Errors
/// Returns an error if there is an issue with connecting with the device
pub fn native() -> Result<LedgerSigner<TransportNativeHID>, Error> {
Ok(LedgerSigner {
transport: get_transport()?,
Expand Down Expand Up @@ -145,7 +148,7 @@ where
};
let mut signature_payload_as_bytes = signature_payload.to_xdr(Limits::none())?;

let mut hd_path_to_bytes = hd_path.into().to_vec()?;
let mut hd_path_to_bytes = hd_path.into().to_vec();

let capacity = 1 + hd_path_to_bytes.len() + signature_payload_as_bytes.len();
let mut data: Vec<u8> = Vec::with_capacity(capacity);
Expand Down Expand Up @@ -186,6 +189,8 @@ where
}

/// The `display_and_confirm` bool determines if the Ledger will display the public key on its screen and requires user approval to share
/// # Errors
/// Returns an error if there is an issue with connecting with the device or getting the public key from the device
pub async fn get_public_key_with_display_flag(
&self,
hd_path: impl Into<HdPath>,
Expand All @@ -195,7 +200,7 @@ where
// the first element of the data should be the number of elements in the path
let hd_path = hd_path.into();
let hd_path_elements_count = hd_path.depth();
let mut hd_path_to_bytes = hd_path.to_vec()?;
let mut hd_path_to_bytes = hd_path.to_vec();
hd_path_to_bytes.insert(0, hd_path_elements_count);

let p2 = if display_and_confirm {
Expand Down Expand Up @@ -246,8 +251,11 @@ where
}
}

/// Sign a blob of data with the account on the Ledger device
/// # Errors
/// Returns an error if there is an issue with connecting with the device or signing the given tx on the device
pub async fn sign_data(&self, index: &HdPath, blob: &[u8]) -> Result<Vec<u8>, Error> {
let mut hd_path_to_bytes = index.to_vec()?;
let mut hd_path_to_bytes = index.to_vec();

let capacity = 1 + hd_path_to_bytes.len() + blob.len();
let mut data: Vec<u8> = Vec::with_capacity(capacity);
Expand Down
15 changes: 12 additions & 3 deletions cmd/soroban-cli/src/commands/config/locator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::{

use crate::{utils::find_config_dir, Pwd};

use super::{network::Network, secret::Secret};
use super::{network::Network, secret::Signer};

#[derive(thiserror::Error, Debug)]
pub enum Error {
Expand Down Expand Up @@ -139,7 +139,7 @@ impl Args {
)
}

pub fn write_identity(&self, name: &str, secret: &Secret) -> Result<(), Error> {
pub fn write_identity(&self, name: &str, secret: &Signer) -> Result<(), Error> {
KeyType::Identity.write(name, secret, &self.config_dir()?)
}

Expand Down Expand Up @@ -192,10 +192,19 @@ impl Args {
})
.collect::<Vec<_>>())
}
pub fn read_identity(&self, name: &str) -> Result<Secret, Error> {

pub fn read_identity(&self, name: &str) -> Result<Signer, Error> {
KeyType::Identity.read_with_global(name, &self.local_config()?)
}

pub fn account(&self, account_str: &str) -> Result<Signer, Error> {
if let Ok(secret) = self.read_identity(account_str) {
Ok(secret)
} else {
Ok(account_str.parse::<Signer>()?)
}
}

pub fn read_network(&self, name: &str) -> Result<Network, Error> {
let res = KeyType::Network.read_with_global(name, &self.local_config()?);
if let Err(Error::ConfigMissing(_, _)) = &res {
Expand Down
71 changes: 39 additions & 32 deletions cmd/soroban-cli/src/commands/config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
use std::path::PathBuf;

use clap::{arg, command};
use secret::StellarSigner;
use serde::{Deserialize, Serialize};
use stellar_strkey::ed25519::PublicKey;

use soroban_rpc::Client;
use stellar_strkey::Strkey;
use crate::signer;
use crate::xdr::{Transaction, TransactionEnvelope};
use crate::{signer::Stellar, Pwd};

use crate::xdr::{MuxedAccount, SequenceNumber, Transaction, TransactionEnvelope, Uint256};
use crate::{
signer::{LocalKey, Stellar},
Pwd,
};

use self::{network::Network, secret::Secret};
use self::network::Network;

use super::{keys, network};

Expand All @@ -33,6 +30,8 @@ pub enum Error {
Config(#[from] locator::Error),
#[error(transparent)]
Rpc(#[from] soroban_rpc::Error),
#[error(transparent)]
Signer(#[from] signer::Error),
}

#[derive(Debug, clap::Args, Clone, Default)]
Expand All @@ -51,46 +50,54 @@ pub struct Args {

#[command(flatten)]
pub locator: locator::Args,

/// Confirm that a signature can be signed by the given keypair automatically.
#[arg(long, short = 'y')]
pub yes: bool,
}

impl Args {
pub fn signer(&self) -> Result<StellarSigner, Error> {
Ok(self
.locator
.account(&self.source_account)?
.signer(self.hd_path, !self.yes)?)
}

pub fn key_pair(&self) -> Result<ed25519_dalek::SigningKey, Error> {
let key = self.account(&self.source_account)?;
let key = self.locator.account(&self.source_account)?;
Ok(key.key_pair(self.hd_path)?)
}

pub async fn public_key(&self) -> Result<PublicKey, Error> {
Ok(self.signer()?.get_public_key().await?)
}

pub async fn sign_with_local_key(
&self,
tx: Transaction,
) -> Result<TransactionEnvelope, Error> {
let signer = LocalKey::new(self.key_pair()?, false);
pub async fn sign_with_local_key(&self, tx: Transaction) -> Result<TransactionEnvelope, Error> {
let signer = self.signer()?;
self.sign(&signer, tx).await
}

pub async fn sign(
&self,
signer: &impl Stellar,
mut tx: Transaction,
tx: Transaction,
) -> Result<TransactionEnvelope, Error> {
let key = signer.get_public_key().await.unwrap();
let account = Strkey::PublicKeyEd25519(key);
let network = self.get_network()?;
let client = Client::new(&network.rpc_url)?;
tx.seq_num = SequenceNumber(client.get_account(&account.to_string()).await?.seq_num.0 + 1);
tx.source_account = MuxedAccount::Ed25519(Uint256(key.0));
Ok(signer
.sign_txn(tx, &network.network_passphrase)
.await
.unwrap())
let Network {
network_passphrase, ..
} = &self.get_network()?;
Ok(signer.sign_txn(tx, network_passphrase).await?)
}

pub fn account(&self, account_str: &str) -> Result<Secret, Error> {
if let Ok(secret) = self.locator.read_identity(account_str) {
Ok(secret)
} else {
Ok(account_str.parse::<Secret>()?)
}
pub async fn sign_soroban_authorizations(
&self,
signer: &impl Stellar,
tx: &Transaction,
) -> Result<Option<Transaction>, Error> {
let network = self.get_network()?;
Ok(signer
.sign_soroban_authorizations(tx, &network.network_passphrase)
.await?)
}

pub fn get_network(&self) -> Result<Network, Error> {
Expand Down
Loading

0 comments on commit 139b1a7

Please sign in to comment.