Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add new Key type; allowing public keys to be named keys #1700

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
9 changes: 5 additions & 4 deletions Cargo.lock

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

5 changes: 3 additions & 2 deletions FULL_HELP_DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,7 @@ Add a new identity (keypair, ledger, macOS keychain)

* `--secret-key` — Add using `secret_key` Can provide with `SOROBAN_SECRET_KEY`
* `--seed-phrase` — Add using 12 word seed phrase to generate `secret_key`
* `--public-key <PUBLIC_KEY>` — Add a public key, ed25519, or muxed account, e.g. G1.., M2..
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the name used here, public key, because this command is concerned with public keys. But the command to read the public key out is unfortunately named stellar keys address. Doesn't have to happen as part of this PR, but it would be helpful for symmetry and consistency if we renamed (in a non-breaking way) the address subcommand to public-key.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we need to also make install an alias of upload. So we could do both in one PR.

* `--global` — Use global config
* `--config-dir <CONFIG_DIR>` — Location of config directory, default is "."

Expand Down Expand Up @@ -1576,7 +1577,7 @@ Transfers the XLM balance of an account to another account and removes the sourc
* `--hd-path <HD_PATH>` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0`
* `--global` — Use global config
* `--config-dir <CONFIG_DIR>` — Location of config directory, default is "."
* `--account <ACCOUNT>` — Muxed Account to merge with, e.g. `GBX...`, 'MBX...'
* `--account <ACCOUNT>` — Muxed Account to merge with, e.g. `GBX...`, 'MBX...' or alias



Expand Down Expand Up @@ -1791,7 +1792,7 @@ Allows issuing account to configure authorization and trustline flags to an asse
* `--hd-path <HD_PATH>` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0`
* `--global` — Use global config
* `--config-dir <CONFIG_DIR>` — Location of config directory, default is "."
* `--trustor <TRUSTOR>` — Account to set trustline flags for
* `--trustor <TRUSTOR>` — Account to set trustline flags for, e.g. `GBX...`, or alias, or muxed account, `M123...``
* `--asset <ASSET>` — Asset to set trustline flags for
* `--set-authorize` — Signifies complete authorization allowing an account to transact freely with the asset to make and receive payments and place orders
* `--set-authorize-to-maintain-liabilities` — Denotes limited authorization that allows an account to maintain current orders but not to otherwise transact with the asset
Expand Down
92 changes: 92 additions & 0 deletions cmd/crates/soroban-test/tests/it/integration/tx/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,71 @@ async fn create_account() {
invoke_hello_world(sandbox, &id);
}

#[tokio::test]
async fn create_account_with_alias() {
let sandbox = &TestEnv::new();
sandbox
.new_assert_cmd("keys")
.args(["generate", "--no-fund", "new"])
.assert()
.success();
let test = test_address(sandbox);
let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap();
let test_account = client.get_account(&test).await.unwrap();
println!("test account has a balance of {}", test_account.balance);
let starting_balance = ONE_XLM * 100;
sandbox
.new_assert_cmd("tx")
.args([
"new",
"create-account",
"--destination",
"new",
"--starting-balance",
starting_balance.to_string().as_str(),
])
.assert()
.success();
let test_account_after = client.get_account(&test).await.unwrap();
assert!(test_account_after.balance < test_account.balance);
let id = deploy_contract(sandbox, HELLO_WORLD, DeployKind::Normal, Some("new")).await;
println!("{id}");
invoke_hello_world(sandbox, &id);
}

#[tokio::test]
async fn payment_with_alias() {
let sandbox = &TestEnv::new();
let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap();
let (test, test1) = setup_accounts(sandbox);
let test_account = client.get_account(&test).await.unwrap();
println!("test account has a balance of {}", test_account.balance);

let before = client.get_account(&test).await.unwrap();
let test1_account_entry_before = client.get_account(&test1).await.unwrap();

sandbox
.new_assert_cmd("tx")
.args([
"new",
"payment",
"--destination",
"test1",
"--amount",
ONE_XLM.to_string().as_str(),
])
.assert()
.success();
let test1_account_entry = client.get_account(&test1).await.unwrap();
assert_eq!(
ONE_XLM,
test1_account_entry.balance - test1_account_entry_before.balance,
"Should have One XLM more"
);
let after = client.get_account(&test).await.unwrap();
assert_eq!(before.balance - 10_000_100, after.balance);
}

#[tokio::test]
async fn payment() {
let sandbox = &TestEnv::new();
Expand Down Expand Up @@ -157,6 +222,33 @@ async fn account_merge() {
assert_eq!(before.balance + before1.balance - fee, after.balance);
}

#[tokio::test]
async fn account_merge_with_alias() {
let sandbox = &TestEnv::new();
let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap();
let (test, test1) = setup_accounts(sandbox);
let before = client.get_account(&test).await.unwrap();
let before1 = client.get_account(&test1).await.unwrap();
let fee = 100;
sandbox
.new_assert_cmd("tx")
.args([
"new",
"account-merge",
"--source",
"test1",
"--account",
"test",
"--fee",
fee.to_string().as_str(),
])
.assert()
.success();
let after = client.get_account(&test).await.unwrap();
assert!(client.get_account(&test1).await.is_err());
assert_eq!(before.balance + before1.balance - fee, after.balance);
}

#[tokio::test]
async fn set_trustline_flags() {
let sandbox = &TestEnv::new();
Expand Down
1 change: 1 addition & 0 deletions cmd/soroban-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ fqdn = "0.3.12"
open = "5.3.0"
url = "2.5.2"
wasm-gen = "0.1.4"
serde_with = "3.11.0"

[build-dependencies]
crate-git-revision = "0.0.6"
Expand Down
6 changes: 3 additions & 3 deletions cmd/soroban-cli/src/commands/keys/add.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use clap::command;

use crate::config::{locator, secret};
use crate::config::{key, locator, secret};

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
Secret(#[from] secret::Error),
Key(#[from] key::Error),

#[error(transparent)]
Config(#[from] locator::Error),
Expand All @@ -28,6 +28,6 @@ impl Cmd {
pub fn run(&self) -> Result<(), Error> {
Ok(self
.config_locator
.write_identity(&self.name, &self.secrets.read_secret()?)?)
.write_key(&self.name, &self.secrets.read_key()?)?)
}
}
18 changes: 10 additions & 8 deletions cmd/soroban-cli/src/commands/keys/address.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use crate::commands::config::secret;

use super::super::config::locator;
use clap::arg;

use crate::commands::config::{key, locator};

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
Config(#[from] locator::Error),

#[error(transparent)]
Secret(#[from] secret::Error),
Key(#[from] key::Error),

#[error(transparent)]
StrKey(#[from] stellar_strkey::DecodeError),
Expand All @@ -36,10 +35,13 @@ impl Cmd {
}

pub fn private_key(&self) -> Result<ed25519_dalek::SigningKey, Error> {
Ok(self
.locator
.read_identity(&self.name)?
.key_pair(self.hd_path)?)
Ok(ed25519_dalek::SigningKey::from_bytes(
&self
.locator
.read_identity(&self.name)?
.private_key(self.hd_path)?
.0,
))
}

pub fn public_key(&self) -> Result<stellar_strkey::ed25519::PublicKey, Error> {
Expand Down
7 changes: 2 additions & 5 deletions cmd/soroban-cli/src/commands/keys/show.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
use clap::arg;

use crate::config::{locator, secret};
use crate::config::{key, locator};

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
Config(#[from] locator::Error),

#[error(transparent)]
Secret(#[from] secret::Error),

#[error(transparent)]
StrKey(#[from] stellar_strkey::DecodeError),
Key(#[from] key::Error),
}

#[derive(Debug, clap::Parser, Clone)]
Expand Down
20 changes: 18 additions & 2 deletions cmd/soroban-cli/src/commands/tx/args.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use crate::{
commands::{global, txn_result::TxnEnvelopeResult},
config::{self, data, network, secret},
config::{
self,
address::{self, Address},
data, network, secret,
},
fee,
rpc::{self, Client, GetTransactionResponse},
tx::builder::{self, TxExt},
Expand Down Expand Up @@ -32,6 +36,8 @@ pub enum Error {
Data(#[from] data::Error),
#[error(transparent)]
Xdr(#[from] xdr::Error),
#[error(transparent)]
Address(#[from] address::Error),
}

impl Args {
Expand Down Expand Up @@ -64,7 +70,7 @@ impl Args {
op: impl Into<xdr::OperationBody>,
global_args: &global::Args,
) -> Result<TxnEnvelopeResult<GetTransactionResponse>, Error> {
let tx = self.tx(op.into()).await?;
let tx = self.tx(op).await?;
self.handle_tx(tx, global_args).await
}
pub async fn handle_and_print(
Expand Down Expand Up @@ -104,4 +110,14 @@ impl Args {
pub fn source_account(&self) -> Result<xdr::MuxedAccount, Error> {
Ok(self.config.source_account()?)
}

pub fn reslove_muxed_address(&self, address: &Address) -> Result<xdr::MuxedAccount, Error> {
Ok(address.resolve_muxed_account(&self.config.locator, self.config.hd_path)?)
}

pub fn reslove_account_id(&self, address: &Address) -> Result<xdr::AccountId, Error> {
Ok(address
.resolve_muxed_account(&self.config.locator, self.config.hd_path)?
.account_id())
}
}
2 changes: 2 additions & 0 deletions cmd/soroban-cli/src/commands/tx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub enum Error {
Sign(#[from] sign::Error),
#[error(transparent)]
Send(#[from] send::Error),
#[error(transparent)]
Args(#[from] args::Error),
}

impl Cmd {
Expand Down
15 changes: 9 additions & 6 deletions cmd/soroban-cli/src/commands/tx/new/account_merge.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
use clap::{command, Parser};

use crate::{commands::tx, xdr};
use crate::{commands::tx, config::address, xdr};

#[derive(Parser, Debug, Clone)]
#[group(skip)]
pub struct Cmd {
#[command(flatten)]
pub tx: tx::Args,
/// Muxed Account to merge with, e.g. `GBX...`, 'MBX...'
/// Muxed Account to merge with, e.g. `GBX...`, 'MBX...' or alias
#[arg(long)]
pub account: xdr::MuxedAccount,
pub account: address::Address,
}

impl From<&Cmd> for xdr::OperationBody {
fn from(cmd: &Cmd) -> Self {
xdr::OperationBody::AccountMerge(cmd.account.clone())
impl TryFrom<&Cmd> for xdr::OperationBody {
type Error = tx::args::Error;
fn try_from(cmd: &Cmd) -> Result<Self, Self::Error> {
Ok(xdr::OperationBody::AccountMerge(
cmd.tx.reslove_muxed_address(&cmd.account)?,
))
}
}
15 changes: 8 additions & 7 deletions cmd/soroban-cli/src/commands/tx/new/create_account.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use clap::{command, Parser};

use crate::{commands::tx, tx::builder, xdr};
use crate::{commands::tx, config::address, tx::builder, xdr};

#[derive(Parser, Debug, Clone)]
#[group(skip)]
Expand All @@ -9,17 +9,18 @@ pub struct Cmd {
pub tx: tx::Args,
/// Account Id to create, e.g. `GBX...`
#[arg(long)]
pub destination: xdr::AccountId,
pub destination: address::Address,
/// Initial balance in stroops of the account, default 1 XLM
#[arg(long, default_value = "10_000_000")]
pub starting_balance: builder::Amount,
}

impl From<&Cmd> for xdr::OperationBody {
fn from(cmd: &Cmd) -> Self {
xdr::OperationBody::CreateAccount(xdr::CreateAccountOp {
destination: cmd.destination.clone(),
impl TryFrom<&Cmd> for xdr::OperationBody {
type Error = tx::args::Error;
fn try_from(cmd: &Cmd) -> Result<Self, Self::Error> {
Ok(xdr::OperationBody::CreateAccount(xdr::CreateAccountOp {
destination: cmd.tx.reslove_account_id(&cmd.destination)?,
starting_balance: cmd.starting_balance.into(),
})
}))
}
}
Loading