Skip to content

Commit

Permalink
feat: make Ledger support option
Browse files Browse the repository at this point in the history
Also improves wasm compatibility.

The part disabling Ledger is rather preliminary and should be improved
in the future. Specifically, even when the feature is disabled, relevant
command options are still available that lead to runtime errors, which
is not ideal.
  • Loading branch information
xJonathanLEI committed Dec 2, 2024
1 parent 5b4f853 commit 669a6f0
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 28 deletions.
23 changes: 12 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 15 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ cairo-starknet-2-8-2 = { package = "cairo-lang-starknet-classes", git = "https:/
chrono = "0.4.26"
clap = { version = "4.3.8", features = ["derive", "env", "string"] }
clap_complete = "4.3.1"
coins-bip32 = "0.11.1"
colored = "2.0.0"
colored_json = "3.2.0"
env_logger = "0.10.0"
etcetera = "0.8.0"
flate2 = "1.0.28"
hex = "0.4.3"
hex-literal = "0.4.1"
Expand All @@ -39,17 +39,28 @@ serde_json_pythonic = { version = "0.1.2", default-features = false, features =
serde_with = "3.11.0"
sha2 = "0.10.8"
shellexpand = "3.1.0"
starknet = { git = "https://github.com/xJonathanLEI/starknet-rs", rev = "b4d90ac9c839cb7f48e9a88032a4ee50be8c85b6", features = ["ledger"] }
starknet-crypto = { git = "https://github.com/xJonathanLEI/starknet-rs", rev = "b4d90ac9c839cb7f48e9a88032a4ee50be8c85b6" }
starknet = { git = "https://github.com/xJonathanLEI/starknet-rs", rev = "093d5ecbbd0929720db38a1ca34d516ccb807398" }
starknet-crypto = { git = "https://github.com/xJonathanLEI/starknet-rs", rev = "093d5ecbbd0929720db38a1ca34d516ccb807398" }
tempfile = "3.8.0"
thiserror = "1.0.40"
tokio = { version = "1.28.2", default-features = false, features = ["macros", "rt-multi-thread"] }
tokio = { version = "1.28.2", default-features = false, features = ["macros"] }
toml = "0.8.8"
url = "2.4.0"

[target.'cfg(target_arch = "wasm32")'.dependencies]
tokio = { version = "1.28.2", default-features = false, features = ["rt", "time", "sync"] }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
etcetera = "0.8.0"
tokio = { version = "1.28.2", default-features = false, features = ["rt-multi-thread"] }

[build-dependencies]
vergen = { version = "8.2.1", features = ["build", "git", "git2"] }

[features]
default = ["ledger"]
ledger = ["starknet/ledger"]

# We need https://github.com/roynalnaruto/eth-keystore-rs/pull/15 for Android.
#
# Ideally, we should only patch this for Android. However, Cargo does not support target-specific
Expand Down
2 changes: 1 addition & 1 deletion src/hd_path.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::str::FromStr;

use clap::{builder::TypedValueParser, error::ErrorKind, Arg, Command, Error};
use coins_bip32::path::DerivationPath;
use colored::Colorize;
use sha2::{Digest, Sha256};
use starknet::signers::DerivationPath;

const EIP2645_LENGTH: usize = 6;

Expand Down
5 changes: 4 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ enum Subcommands {
//
#[clap(about = "Signer management commands")]
Signer(Signer),
#[cfg(feature = "ledger")]
#[clap(about = "Shortcut for `starkli signer ledger`")]
Ledger(crate::subcommands::signer::ledger::Ledger),
#[clap(aliases = ["erc2645"], about = "EIP-2645 helper commands")]
Expand Down Expand Up @@ -155,7 +156,8 @@ enum Subcommands {
Lab(Lab),
}

#[tokio::main]
#[cfg_attr(target_arch = "wasm32", tokio::main(flavor = "current_thread"))]
#[cfg_attr(not(target_arch = "wasm32"), tokio::main)]
async fn main() {
if let Err(err) = run_command(Cli::parse()).await {
eprintln!("{}", format!("Error: {err}").red());
Expand Down Expand Up @@ -206,6 +208,7 @@ async fn run_command(cli: Cli) -> Result<()> {
Subcommands::Syncing(cmd) => cmd.run().await,
Subcommands::SpecVersion(cmd) => cmd.run().await,
Subcommands::Signer(cmd) => cmd.run().await,
#[cfg(feature = "ledger")]
Subcommands::Ledger(cmd) => cmd.run().await,
Subcommands::Eip2645(cmd) => cmd.run(),
Subcommands::Account(cmd) => cmd.run().await,
Expand Down
20 changes: 14 additions & 6 deletions src/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use std::{
};

use anyhow::Result;
use etcetera::{choose_base_strategy, BaseStrategy};
use indexmap::IndexMap;
use serde::{de::Visitor, Deserialize, Serialize};
use starknet::core::{
Expand Down Expand Up @@ -114,11 +113,20 @@ impl Profiles {
}

fn get_config_folder() -> Result<PathBuf> {
let strategy = choose_base_strategy()
.map_err(|_| anyhow::anyhow!("unable to find the config directory"))?;
let mut path = strategy.config_dir();
path.push("starkli");
Ok(path)
#[cfg(target_arch = "wasm32")]
{
Ok(PathBuf::from("/home/dev/.config/starkli"))
}

#[cfg(not(target_arch = "wasm32"))]
{
use etcetera::{choose_base_strategy, BaseStrategy};
let strategy = choose_base_strategy()
.map_err(|_| anyhow::anyhow!("unable to find the config directory"))?;
let mut path = strategy.config_dir();
path.push("starkli");
Ok(path)
}
}

fn get_profiles_path() -> Result<PathBuf> {
Expand Down
27 changes: 22 additions & 5 deletions src/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,25 @@ use std::{path::PathBuf, str::FromStr};
use anyhow::Result;
use async_trait::async_trait;
use clap::Parser;
use coins_bip32::path::DerivationPath;
use colored::Colorize;
use starknet::{
core::{crypto::Signature, types::Felt},
signers::{
DerivationPath, LedgerSigner, LocalWallet, Signer, SignerInteractivityContext, SigningKey,
VerifyingKey,
},
signers::{LocalWallet, Signer, SignerInteractivityContext, SigningKey, VerifyingKey},
};

use crate::hd_path::{DerivationPathParser, Eip2645Path};

#[cfg(feature = "ledger")]
type LedgerSigner = starknet::signers::LedgerSigner;

// This is not ideal. All command line options related to Ledger should removed instead.
// TODO: properly disable Ledger
#[cfg(not(feature = "ledger"))]
mod void_signer;
#[cfg(not(feature = "ledger"))]
type LedgerSigner = void_signer::VoidSigner;

#[derive(Debug)]
pub enum AnySigner {
LocalWallet(LocalWallet),
Expand Down Expand Up @@ -80,6 +88,7 @@ pub struct PrivateKeyTaskContent {

#[derive(Debug)]
pub struct LedgerTaskContent {
#[cfg_attr(not(feature = "ledger"), allow(unused))]
path: DerivationPath,
}

Expand Down Expand Up @@ -398,7 +407,15 @@ impl PrivateKeyTaskContent {

impl LedgerTaskContent {
pub async fn resolve(self) -> Result<AnySigner> {
Ok(AnySigner::Ledger(LedgerSigner::new(self.path).await?))
#[cfg(feature = "ledger")]
{
Ok(AnySigner::Ledger(LedgerSigner::new(self.path).await?))
}

#[cfg(not(feature = "ledger"))]
{
Ok(AnySigner::Ledger(LedgerSigner::default()))
}
}
}

Expand Down
29 changes: 29 additions & 0 deletions src/signer/void_signer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use std::convert::Infallible;

use async_trait::async_trait;
use starknet::{
core::types::Felt,
signers::{Signer, SignerInteractivityContext, VerifyingKey},
};
use starknet_crypto::Signature;

#[derive(Debug, Default)]
pub struct VoidSigner;

#[async_trait(?Send)]
impl Signer for VoidSigner {
type GetPublicKeyError = Infallible;
type SignError = Infallible;

async fn get_public_key(&self) -> Result<VerifyingKey, Self::GetPublicKeyError> {
panic!("This signer is not supported on this platform")
}

async fn sign_hash(&self, _hash: &Felt) -> Result<Signature, Self::SignError> {
panic!("This signer is not supported on this platform")
}

fn is_interactive(&self, _context: SignerInteractivityContext<'_>) -> bool {
panic!("This signer is not supported on this platform")
}
}
4 changes: 4 additions & 0 deletions src/subcommands/signer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use clap::{Parser, Subcommand};
mod keystore;
use keystore::Keystore;

#[cfg(feature = "ledger")]
pub mod ledger;
#[cfg(feature = "ledger")]
use ledger::Ledger;

mod gen_keypair;
Expand All @@ -20,6 +22,7 @@ pub struct Signer {
enum Subcommands {
#[clap(about = "Keystore management commands")]
Keystore(Keystore),
#[cfg(feature = "ledger")]
#[clap(about = "Ledger hardware wallet management commands")]
Ledger(Ledger),
#[clap(about = "Randomly generate a new key pair")]
Expand All @@ -30,6 +33,7 @@ impl Signer {
pub async fn run(self) -> Result<()> {
match self.command {
Subcommands::Keystore(cmd) => cmd.run(),
#[cfg(feature = "ledger")]
Subcommands::Ledger(cmd) => cmd.run().await,
Subcommands::GenKeypair(cmd) => cmd.run(),
}
Expand Down

0 comments on commit 669a6f0

Please sign in to comment.