Skip to content

Commit

Permalink
feat: add ledgers-from-now arg to invoke and auth
Browse files Browse the repository at this point in the history
This allows users to specify how long a signed auth entry is valid.
  • Loading branch information
willemneal committed Jul 11, 2024
1 parent 9df886f commit 7203fe9
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 11 deletions.
6 changes: 6 additions & 0 deletions FULL_HELP_DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,9 @@ stellar contract invoke ... -- --help
* `--instructions <INSTRUCTIONS>` — Number of instructions to simulate
* `--build-only` — Build the transaction and only write the base64 xdr to stdout
* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout
* `--ledgers-from-now <FROM_NOW>` — Number of ledgers from current ledger before the signed auth entry expires. Default 60 ~ 5 minutes

Default value: `60`



Expand Down Expand Up @@ -1256,6 +1259,9 @@ Sign a transaction
* `--global` — Use global config
* `--config-dir <CONFIG_DIR>` — Location of config directory, default is "."
* `--check` — Check with user before signature. Eventually this will be replaced with `--yes`, which does the opposite and will force a check without --yes
* `--ledgers-from-now <FROM_NOW>` — Number of ledgers from current ledger before the signed auth entry expires. Default 60 ~ 5 minutes

Default value: `60`
* `-a`, `--auth-only` — Only sign the Authorization Entries required by the provided source account


Expand Down
55 changes: 55 additions & 0 deletions cmd/crates/soroban-test/tests/it/integration/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,58 @@ async fn sign() {
.success()
.stdout(predicates::str::contains(r#""status": "SUCCESS""#));
}

#[tokio::test]
async fn expired_auth_entry() {
let sandbox = &TestEnv::new();
let id = &deploy_hello(sandbox).await;
// Create new test_other account
sandbox
.new_assert_cmd("keys")
.arg("generate")
.arg("test_other")
.assert();

// Get Xdr for transaction where auth is required for test_other
let xdr_base64 = sandbox
.new_assert_cmd("contract")
.arg("invoke")
.arg("--id")
.arg(id)
.arg("--sim-only")
.arg("--")
.arg("auth")
.arg("--world=world")
.arg("--addr=test_other")
.assert()
.success()
.stdout_as_str();
// Sign the transaction's auth entry with test_other
let xdr_base64 = sandbox
.new_assert_cmd("tx")
.arg("sign")
.arg("--auth")
.arg("--source=test_other")
.arg("--expiration-ledger=1")
.write_stdin(xdr_base64.as_bytes())
.assert()
.success()
.stdout_as_str();
tokio::sleep(tokio::time::Duration::from_secs(10)).await;
// Sign the transaction with test as source account
let xdr_base64 = sandbox
.new_assert_cmd("tx")
.arg("sign")
.write_stdin(xdr_base64.as_bytes())
.assert()
.success()
.stdout_as_str();
// Send transaction
sandbox
.new_assert_cmd("tx")
.arg("send")
.write_stdin(xdr_base64.as_bytes())
.assert()
.stdout(predicates::str::contains(r#""status": "FAILED""#))
.stdout(predicates::str::contains("signature has expired"));
}
10 changes: 8 additions & 2 deletions cmd/soroban-cli/src/commands/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,23 @@ impl Args {
pub async fn sign_soroban_authorizations(
&self,
tx: &Transaction,
ledgers_from_current: u32,
) -> Result<Option<Transaction>, Error> {
self.sign_soroban_authorizations_with_signer(&self.signer()?, tx)
self.sign_soroban_authorizations_with_signer(&self.signer()?, tx, ledgers_from_current)
.await
}
pub async fn sign_soroban_authorizations_with_signer(
&self,
signer: &(impl Stellar + std::marker::Sync),
tx: &Transaction,
ledgers_from_current: u32,
) -> Result<Option<Transaction>, Error> {
let network = self.get_network()?;
Ok(signer.sign_soroban_authorizations(tx, &network).await?)
let client = crate::rpc::Client::new(&network.rpc_url)?;
let expiration_ledger = client.get_latest_ledger().await?.sequence + ledgers_from_current;
Ok(signer
.sign_soroban_authorizations(tx, &network, expiration_ledger)
.await?)
}

pub fn get_network(&self) -> Result<Network, Error> {
Expand Down
4 changes: 3 additions & 1 deletion cmd/soroban-cli/src/commands/contract/invoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ pub struct Cmd {
pub config: config::Args,
#[command(flatten)]
pub fee: crate::fee::Args,
#[command(flatten)]
pub ledgers: crate::commands::tx::auth::Args,
}

impl FromStr for Cmd {
Expand Down Expand Up @@ -393,7 +395,7 @@ impl NetworkRunnable for Cmd {
// crate::log::auth(&[auth]);
for signer in &signers {
if let Some(tx) = config
.sign_soroban_authorizations_with_signer(signer, &txn)
.sign_soroban_authorizations_with_signer(signer, &txn, self.ledgers.from_now)
.await?
{
txn = tx;
Expand Down
19 changes: 19 additions & 0 deletions cmd/soroban-cli/src/commands/tx/auth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use clap::arg;

#[derive(Debug, clap::Args, Clone)]
#[group(skip)]
pub struct Args {
/// Number of ledgers from current ledger before the signed auth entry expires. Default 60 ~ 5 minutes.
#[arg(
long = "ledgers-from-now",
visible_alias = "ledgers",
default_value = "60"
)]
pub from_now: u32,
}

impl Default for Args {
fn default() -> Self {
Self { from_now: 60 }
}
}
1 change: 1 addition & 0 deletions cmd/soroban-cli/src/commands/tx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use clap::Parser;

use super::global;

pub mod auth;
pub mod send;
pub mod sign;
pub mod simulate;
Expand Down
4 changes: 3 additions & 1 deletion cmd/soroban-cli/src/commands/tx/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub enum Error {
pub struct Cmd {
#[clap(flatten)]
pub config: config::Args,
#[clap(flatten)]
pub ledgers: super::auth::Args,
/// Only sign the Authorization Entries required by the provided source account
#[arg(long, visible_alias = "auth", short = 'a')]
pub auth_only: bool,
Expand All @@ -35,7 +37,7 @@ impl Cmd {
if self.auth_only {
Ok(self
.config
.sign_soroban_authorizations(&tx)
.sign_soroban_authorizations(&tx, self.ledgers.from_now)
.await?
.unwrap_or(tx)
.into())
Expand Down
13 changes: 6 additions & 7 deletions cmd/soroban-cli/src/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ pub trait Stellar {
&self,
raw: &Transaction,
network: &Network,
expiration_ledger: u32,
) -> Result<Option<Transaction>, Error> {
let mut tx = raw.clone();
let Some(mut op) = requires_auth(&tx) else {
Expand All @@ -116,12 +117,10 @@ pub trait Stellar {
else {
return Ok(None);
};
let client = crate::rpc::Client::new(&network.rpc_url)?;
let mut auths = body.auth.to_vec();
let current_ledger = client.get_latest_ledger().await?.sequence;
for auth in &mut auths {
*auth = self
.maybe_sign_soroban_authorization_entry(auth, network, current_ledger)
.maybe_sign_soroban_authorization_entry(auth, network, expiration_ledger)
.await?;
}
body.auth = auths.try_into()?;
Expand All @@ -136,7 +135,7 @@ pub trait Stellar {
&self,
unsigned_entry: &SorobanAuthorizationEntry,
network: &Network,
current_ledger: u32,
expiration_ledger: u32,
) -> Result<SorobanAuthorizationEntry, Error> {
if let SorobanAuthorizationEntry {
credentials: SorobanCredentials::Address(SorobanAddressCredentials { address, .. }),
Expand All @@ -160,7 +159,7 @@ pub trait Stellar {
};
if key == self.get_public_key().await? {
return self
.sign_soroban_authorization_entry(unsigned_entry, network, current_ledger)
.sign_soroban_authorization_entry(unsigned_entry, network, expiration_ledger)
.await;
}
}
Expand All @@ -176,7 +175,7 @@ pub trait Stellar {
Network {
network_passphrase, ..
}: &Network,
current_ledger: u32,
expiration_ledger: u32,
) -> Result<SorobanAuthorizationEntry, Error> {
let address = self.get_public_key().await?;
let mut auth = unsigned_entry.clone();
Expand All @@ -194,7 +193,7 @@ pub trait Stellar {
..
} = credentials;

*signature_expiration_ledger = current_ledger + 60;
*signature_expiration_ledger = expiration_ledger;

let preimage = HashIdPreimage::SorobanAuthorization(HashIdPreimageSorobanAuthorization {
network_id: hash(network_passphrase),
Expand Down

0 comments on commit 7203fe9

Please sign in to comment.