Skip to content

Commit fc3e9df

Browse files
Feat/seq bump (#1909)
* Add tx edit subcommand * Add tx edit seq-num bump command * Add tx edit seq-num bump it test * Factor out an it helper * Apply cargo fmt changes * Clippy * Check in generated docs * Add bump amount arg * Fmt * Bump -> increment * Generated docs * Implement tx edit seq-num next * Move seq-num next to update subcommand * Update generated docs * Cleanup * Address pr feedback: use tx source acct for seq num * Address feedback: update xdr error
1 parent 4a1393e commit fc3e9df

9 files changed

Lines changed: 235 additions & 17 deletions

File tree

FULL_HELP_DOCS.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,6 +1575,7 @@ Sign, Simulate, and Send transactions
15751575

15761576
###### **Subcommands:**
15771577

1578+
* `update` — Update the transaction
15781579
* `edit` — Edit a transaction envelope from stdin. This command respects the environment variables `STELLAR_EDITOR`, `EDITOR` and `VISUAL`, in that order
15791580
* `hash` — Calculate the hash of a transaction envelope
15801581
* `new` — Create a new transaction
@@ -1585,6 +1586,47 @@ Sign, Simulate, and Send transactions
15851586

15861587

15871588

1589+
## `stellar tx update`
1590+
1591+
Update the transaction
1592+
1593+
**Usage:** `stellar tx update <COMMAND>`
1594+
1595+
###### **Subcommands:**
1596+
1597+
* `sequence-number` — Edit the sequence number on a transaction
1598+
1599+
1600+
1601+
## `stellar tx update sequence-number`
1602+
1603+
Edit the sequence number on a transaction
1604+
1605+
**Usage:** `stellar tx update sequence-number <COMMAND>`
1606+
1607+
###### **Subcommands:**
1608+
1609+
* `next` — Fetch the source account's seq-num and increment for the given tx
1610+
1611+
1612+
1613+
## `stellar tx update sequence-number next`
1614+
1615+
Fetch the source account's seq-num and increment for the given tx
1616+
1617+
**Usage:** `stellar tx update sequence-number next [OPTIONS]`
1618+
1619+
###### **Options:**
1620+
1621+
* `--rpc-url <RPC_URL>` — RPC server endpoint
1622+
* `--rpc-header <RPC_HEADERS>` — RPC Header(s) to include in requests to the RPC provider
1623+
* `--network-passphrase <NETWORK_PASSPHRASE>` — Network passphrase to sign the transaction sent to the rpc server
1624+
* `-n`, `--network <NETWORK>` — Name of network to use from config
1625+
* `--global` — Use global config
1626+
* `--config-dir <CONFIG_DIR>` — Location of config directory, default is "."
1627+
1628+
1629+
15881630
## `stellar tx edit`
15891631

15901632
Edit a transaction envelope from stdin. This command respects the environment variables `STELLAR_EDITOR`, `EDITOR` and `VISUAL`, in that order.

cmd/crates/soroban-test/tests/it/integration/custom_types.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ use serde_json::json;
33
use soroban_cli::commands;
44
use soroban_test::TestEnv;
55

6-
use crate::integration::util::{deploy_custom, extend_contract};
7-
8-
use super::util::{invoke, invoke_with_roundtrip};
6+
use crate::integration::util::{
7+
deploy_custom, extend_contract, invoke, invoke_with_roundtrip, test_address,
8+
};
99

1010
fn invoke_custom(e: &TestEnv, id: &str, func: &str) -> assert_cmd::Command {
1111
let mut s = e.new_assert_cmd("contract");
@@ -241,7 +241,7 @@ async fn account_address(sandbox: &TestEnv, id: &str) {
241241

242242
async fn account_address_with_alias(sandbox: &TestEnv, id: &str) {
243243
let res = invoke(sandbox, id, "addresse", &json!("test").to_string()).await;
244-
let test = format!("\"{}\"", super::tx::operations::test_address(sandbox));
244+
let test = format!("\"{}\"", test_address(sandbox));
245245
assert_eq!(test, res);
246246
}
247247

cmd/crates/soroban-test/tests/it/integration/tx.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use soroban_cli::assembled::simulate_and_assemble_transaction;
22
use soroban_cli::xdr::{Limits, ReadXdr, TransactionEnvelope, WriteXdr};
33
use soroban_test::{AssertExt, TestEnv};
44

5-
use crate::integration::util::{deploy_contract, DeployKind, DeployOptions, HELLO_WORLD};
5+
use crate::integration::util::{
6+
deploy_contract, test_address, DeployKind, DeployOptions, HELLO_WORLD,
7+
};
68

79
pub mod operations;
810

@@ -60,6 +62,47 @@ async fn simulate() {
6062
);
6163
}
6264

65+
fn test_tx_string(sandbox: &TestEnv) -> String {
66+
sandbox
67+
.new_assert_cmd("contract")
68+
.arg("install")
69+
.args([
70+
"--wasm",
71+
HELLO_WORLD.path().as_os_str().to_str().unwrap(),
72+
"--build-only",
73+
])
74+
.assert()
75+
.success()
76+
.stdout_as_str()
77+
}
78+
79+
#[tokio::test]
80+
async fn sequence_number_next() {
81+
let sandbox = &TestEnv::new();
82+
let tx_base64 = test_tx_string(sandbox);
83+
let test = test_address(sandbox);
84+
let client = sandbox.network.rpc_client().unwrap();
85+
let test_account = client.get_account(&test).await.unwrap();
86+
let test_account_seq_num = test_account.seq_num.as_ref();
87+
88+
let updated_tx = sandbox
89+
.new_assert_cmd("tx")
90+
.arg("update")
91+
.arg("seq-num")
92+
.arg("next")
93+
.write_stdin(tx_base64.as_bytes())
94+
.assert()
95+
.success()
96+
.stdout_as_str();
97+
98+
let updated_tx_env = TransactionEnvelope::from_xdr_base64(&updated_tx, Limits::none()).unwrap();
99+
let tx = soroban_cli::commands::tx::xdr::unwrap_envelope_v1(updated_tx_env).unwrap();
100+
assert_eq!(
101+
tx.seq_num,
102+
soroban_cli::xdr::SequenceNumber(test_account_seq_num + 1)
103+
);
104+
}
105+
63106
#[tokio::test]
64107
async fn txn_hash() {
65108
let sandbox = &TestEnv::new();

cmd/crates/soroban-test/tests/it/integration/tx/operations.rs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,15 @@ use soroban_cli::{
44
utils::contract_id_hash_from_asset,
55
xdr::{self, ReadXdr, SequenceNumber},
66
};
7+
78
use soroban_rpc::LedgerEntryResult;
89
use soroban_test::{AssertExt, TestEnv};
910

1011
use crate::integration::{
1112
hello_world::invoke_hello_world,
12-
util::{deploy_contract, DeployOptions, HELLO_WORLD},
13+
util::{deploy_contract, test_address, DeployOptions, HELLO_WORLD},
1314
};
1415

15-
pub fn test_address(sandbox: &TestEnv) -> String {
16-
sandbox
17-
.new_assert_cmd("keys")
18-
.arg("address")
19-
.arg("test")
20-
.assert()
21-
.success()
22-
.stdout_as_str()
23-
}
24-
2516
fn new_account(sandbox: &TestEnv, name: &str) -> String {
2617
sandbox.generate_account(name, None).assert().success();
2718
sandbox

cmd/crates/soroban-test/tests/it/integration/util.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use soroban_cli::{
22
commands,
33
xdr::{Limits, WriteXdr},
44
};
5-
use soroban_test::{TestEnv, Wasm};
5+
use soroban_test::{AssertExt, TestEnv, Wasm};
66
use std::fmt::Display;
77

88
pub const HELLO_WORLD: &Wasm = &Wasm::Custom("test-wasms", "test_hello_world");
@@ -125,3 +125,13 @@ pub async fn extend(sandbox: &TestEnv, id: &str, value: Option<&str>) {
125125
.assert()
126126
.success();
127127
}
128+
129+
pub fn test_address(sandbox: &TestEnv) -> String {
130+
sandbox
131+
.new_assert_cmd("keys")
132+
.arg("address")
133+
.arg("test")
134+
.assert()
135+
.success()
136+
.stdout_as_str()
137+
}

cmd/soroban-cli/src/commands/tx/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,16 @@ pub mod op;
99
pub mod send;
1010
pub mod sign;
1111
pub mod simulate;
12+
pub mod update;
1213
pub mod xdr;
1314

1415
pub use args::Args;
1516

1617
#[derive(Debug, clap::Subcommand)]
1718
pub enum Cmd {
19+
/// Update the transaction
20+
#[command(subcommand)]
21+
Update(update::Cmd),
1822
/// Edit a transaction envelope from stdin. This command respects the environment variables
1923
/// `STELLAR_EDITOR`, `EDITOR` and `VISUAL`, in that order.
2024
///
@@ -61,6 +65,8 @@ pub enum Error {
6165
Args(#[from] args::Error),
6266
#[error(transparent)]
6367
Simulate(#[from] simulate::Error),
68+
#[error(transparent)]
69+
Update(#[from] update::Error),
6470
}
6571

6672
impl Cmd {
@@ -73,6 +79,7 @@ impl Cmd {
7379
Cmd::Send(cmd) => cmd.run(global_args).await?,
7480
Cmd::Sign(cmd) => cmd.run(global_args).await?,
7581
Cmd::Simulate(cmd) => cmd.run(global_args).await?,
82+
Cmd::Update(cmd) => cmd.run(global_args).await?,
7683
};
7784
Ok(())
7885
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use super::global;
2+
3+
pub mod sequence_number;
4+
5+
#[derive(Debug, clap::Subcommand)]
6+
pub enum Cmd {
7+
/// Edit the sequence number on a transaction
8+
#[command(subcommand, visible_alias = "seq-num")]
9+
SequenceNumber(sequence_number::Cmd),
10+
}
11+
12+
#[derive(thiserror::Error, Debug)]
13+
pub enum Error {
14+
#[error(transparent)]
15+
SequenceNumber(#[from] sequence_number::Error),
16+
}
17+
18+
impl Cmd {
19+
pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> {
20+
match self {
21+
Cmd::SequenceNumber(cmd) => cmd.run(global_args).await?,
22+
};
23+
Ok(())
24+
}
25+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use super::global;
2+
3+
mod next;
4+
5+
#[derive(Debug, clap::Subcommand)]
6+
pub enum Cmd {
7+
/// Fetch the source account's seq-num and increment for the given tx
8+
#[command()]
9+
Next(next::Cmd),
10+
}
11+
12+
#[derive(thiserror::Error, Debug)]
13+
pub enum Error {
14+
#[error(transparent)]
15+
Next(#[from] next::Error),
16+
}
17+
18+
impl Cmd {
19+
pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> {
20+
match self {
21+
Cmd::Next(cmd) => cmd.run(global_args).await?,
22+
};
23+
Ok(())
24+
}
25+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use stellar_xdr::curr::MuxedAccount;
2+
3+
use crate::{
4+
commands::{
5+
global,
6+
tx::xdr::{tx_envelope_from_input, Error as XdrParsingError},
7+
},
8+
config::{self, locator, network},
9+
xdr::{self, SequenceNumber, TransactionEnvelope, WriteXdr},
10+
};
11+
12+
#[derive(clap::Parser, Debug, Clone)]
13+
pub struct Cmd {
14+
#[command(flatten)]
15+
pub network: network::Args,
16+
#[command(flatten)]
17+
pub locator: locator::Args,
18+
}
19+
20+
#[derive(thiserror::Error, Debug)]
21+
pub enum Error {
22+
#[error(transparent)]
23+
XdrStdin(#[from] XdrParsingError),
24+
#[error(transparent)]
25+
Xdr(#[from] xdr::Error),
26+
#[error("V0 and fee bump transactions are not supported")]
27+
Unsupported,
28+
#[error(transparent)]
29+
RpcClient(#[from] crate::rpc::Error),
30+
#[error(transparent)]
31+
Config(#[from] config::Error),
32+
#[error(transparent)]
33+
Network(#[from] config::network::Error),
34+
}
35+
36+
impl Cmd {
37+
pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> {
38+
let mut tx = tx_envelope_from_input(&None)?;
39+
self.update_tx_env(&mut tx, global_args).await?;
40+
println!("{}", tx.to_xdr_base64(xdr::Limits::none())?);
41+
Ok(())
42+
}
43+
44+
pub async fn update_tx_env(
45+
&self,
46+
tx_env: &mut TransactionEnvelope,
47+
_global: &global::Args,
48+
) -> Result<(), Error> {
49+
match tx_env {
50+
TransactionEnvelope::Tx(transaction_v1_envelope) => {
51+
let tx_source_acct = &transaction_v1_envelope.tx.source_account;
52+
let current_seq_num = self.current_seq_num(tx_source_acct).await?;
53+
let next_seq_num = current_seq_num + 1;
54+
transaction_v1_envelope.tx.seq_num = SequenceNumber(next_seq_num);
55+
}
56+
TransactionEnvelope::TxV0(_) | TransactionEnvelope::TxFeeBump(_) => {
57+
return Err(Error::Unsupported);
58+
}
59+
};
60+
Ok(())
61+
}
62+
63+
async fn current_seq_num(&self, tx_source_acct: &MuxedAccount) -> Result<i64, Error> {
64+
let network = &self.network.get(&self.locator)?;
65+
let client = network.rpc_client()?;
66+
client
67+
.verify_network_passphrase(Some(&network.network_passphrase))
68+
.await?;
69+
70+
let address = tx_source_acct.to_string();
71+
72+
let account = client.get_account(&address).await?;
73+
Ok(*account.seq_num.as_ref())
74+
}
75+
}

0 commit comments

Comments
 (0)