-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## Summary > [!NOTE] > Most of the code is copied from Sam (#1695) and Jordan's (#1694) PRs. Add two new subcommands `sign` and `submit` to the `sequencer` subcommand. ### `sign` 1. Reads a pbjson formatted `astria.protocol.transaction.v1.TransactionBody` from a file or `STDIN` (`file` is a positional argument; `STDIN` is read when providing `-` as the trailing argument). 2. Signs it with a given private key (`--private-key`). 3. Writes the pbjson formatted `astria.protocol.transaction.v1.Transaction` to `--output`/`-o`, if provided, or `STDOUT`. ### `submit` 1. 1. Reads a pbjson formatted `astria.protocol.transaction.v1.Transaction` from a file or `STDIN` (`file` is a positional argument; `STDIN` is read when providing `-` as the trailing argument). 2. Submits it to a sequencer's CometBFT url (`--sequencer-url`) ## Background We want to be able to test the submitting txs signed via FROST threshold signing (see #1654) but do not have a CLI command to submit already signed transactions. The `submit` command resolves this. To test the `submit` command it is desirable to have a corresponding `sign` command which creates a signed `Transaction` from a single private key. ## Changes - List changes which were made. ## Testing 1. Run a local sequencer network using `astria-cli-go` ``` just run dev purge all just run dev init just run dev run --network local ``` 2. Sign a `TransactionBody`: ``` cargo run -p astria-cli -- sequencer sign --private-key 2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db186d6e90 - <<EOF { "params": { "nonce": 0, "chainId": "sequencer-test-chain-0" }, "actions": [ { "ibcRelayerChange": { "removal": { "bech32m": "astria13r24h8mj42sdfqflqyg2fycqf9mdqqzmm2xllj" } } } ] } EOF ``` 3. Submit the signed `Transaction` ``` cargo run -p astria-cli -- sequencer submit --sequencer-url http://127.0.0.1:26657 - <<EOF { "signature": "+hb4bd8kEM8/AQ3wJ2znXcF3Ds1iLZu6OieNOnxY7n1SZsiDr5NQP3lMK4s5134O629XjXhae/FsL+qtbXnBDw==", "publicKey": "1b9KP8znF7A4i8wnSevBSK2ZabI/Re4bYF/Vh3hXasQ=", "body": { "typeUrl": "/astria.protocol.transaction.v1.TransactionBody", "value": "ChgSFnNlcXVlbmNlci10ZXN0LWNoYWluLTASNKIDMRIvEi1hc3RyaWExM3IyNGg4bWo0MnNkZnFmbHF5ZzJmeWNxZjltZHFxem1tMnhsbGo=" } } EOF ``` > [!NOTE] > You can also do this is in a single command using `xargs -0` ``` cargo run -p astria-cli -- sequencer sign --private-key 2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db186d6e90 - <<EOF { "params": { "nonce": 3, "chainId": "sequencer-test-chain-0" }, "actions": [ { "ibcRelayerChange": { "removal": { "bech32m": "astria13r24h8mj42sdfqflqyg2fycqf9mdqqzmm2xllj" } } } ] } EOF | xargs -0 cargo run -p astria-cli -- sequencer submit --sequencer-url http://127.0.0.1:26657 ``` ## Metrics - List out metrics added by PR, delete section if none. ## Breaking Changelist - Bulleted list of breaking changes, any notes on migration. Delete section if none. ## Related Issues Link any issues that are related, prefer full github links. closes <!-- list any issues closed here --> --------- Co-authored-by: Sam Bukowski <[email protected]> Co-authored-by: Richard Janis Goldschmidt <[email protected]> Co-authored-by: Fraser Hutchison <[email protected]> Co-authored-by: Jordan Oroshiba <[email protected]>
- Loading branch information
1 parent
3e16986
commit 40e86f0
Showing
18 changed files
with
382 additions
and
92 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
use std::{ | ||
io::Write, | ||
path::{ | ||
Path, | ||
PathBuf, | ||
}, | ||
}; | ||
|
||
use astria_core::{ | ||
protocol::transaction::v1::TransactionBody, | ||
Protobuf, | ||
}; | ||
use clap_stdin::FileOrStdin; | ||
use color_eyre::eyre::{ | ||
self, | ||
WrapErr as _, | ||
}; | ||
|
||
use crate::utils::signing_key_from_private_key; | ||
|
||
#[derive(clap::Args, Debug)] | ||
pub(super) struct Command { | ||
/// The private key of account being sent from | ||
#[arg(long, env = "SEQUENCER_PRIVATE_KEY")] | ||
// TODO: https://github.com/astriaorg/astria/issues/594 | ||
// Don't use a plain text private, prefer wrapper like from | ||
// the secrecy crate with specialized `Debug` and `Drop` implementations | ||
// that overwrite the key on drop and don't reveal it when printing. | ||
private_key: String, | ||
/// Target to write the signed transaction in pbjson format (omit to write to STDOUT). | ||
#[arg(long, short)] | ||
output: Option<PathBuf>, | ||
/// Forces an overwrite of `--output` if a file at that location exists. | ||
#[arg(long, short)] | ||
force: bool, | ||
/// The source to read the pbjson formatted astra.protocol.transaction.v1.Transaction (use `-` | ||
/// to pass via STDIN). | ||
input: FileOrStdin, | ||
} | ||
|
||
// The goal of the `sign` CLI command is to take in a `TransactionBody` and to sign with a private | ||
// key to create a `Transaction`. This signed `Transaction` should be printed to the console in | ||
// pbjson format. | ||
impl Command { | ||
pub(super) fn run(self) -> eyre::Result<()> { | ||
let key = signing_key_from_private_key(self.private_key.as_str())?; | ||
|
||
let filename = self.input.filename().to_string(); | ||
let transaction_body = read_transaction_body(self.input) | ||
.wrap_err_with(|| format!("failed to read transaction body from `{filename}`"))?; | ||
let transaction = transaction_body.sign(&key); | ||
|
||
serde_json::to_writer( | ||
stdout_or_file(self.output.as_ref(), self.force) | ||
.wrap_err("failed to determine output target")?, | ||
&transaction.to_raw(), | ||
) | ||
.wrap_err("failed to write signed transaction")?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
fn read_transaction_body(input: FileOrStdin) -> eyre::Result<TransactionBody> { | ||
let wire_body: <TransactionBody as Protobuf>::Raw = serde_json::from_reader( | ||
std::io::BufReader::new(input.into_reader()?), | ||
) | ||
.wrap_err_with(|| { | ||
format!( | ||
"failed to parse input as json `{}`", | ||
TransactionBody::full_name() | ||
) | ||
})?; | ||
TransactionBody::try_from_raw(wire_body).wrap_err("failed to validate transaction body") | ||
} | ||
|
||
fn stdout_or_file<P: AsRef<Path>>( | ||
output: Option<P>, | ||
force_overwrite: bool, | ||
) -> eyre::Result<Box<dyn Write>> { | ||
let writer = match output { | ||
Some(path) => { | ||
let file = if force_overwrite { | ||
std::fs::File::options() | ||
.write(true) | ||
.truncate(true) | ||
.open(path) | ||
} else { | ||
std::fs::File::options() | ||
.create_new(true) | ||
.write(true) | ||
.open(path) | ||
} | ||
.wrap_err("failed to open file for writing")?; | ||
Box::new(file) as Box<dyn Write> | ||
} | ||
None => Box::new(std::io::stdout()), | ||
}; | ||
Ok(writer) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
use astria_core::{ | ||
self, | ||
protocol::transaction::v1::Transaction, | ||
Protobuf, | ||
}; | ||
use astria_sequencer_client::{ | ||
HttpClient, | ||
SequencerClientExt as _, | ||
}; | ||
use clap_stdin::FileOrStdin; | ||
use color_eyre::eyre::{ | ||
self, | ||
ensure, | ||
WrapErr as _, | ||
}; | ||
|
||
#[derive(clap::Args, Debug)] | ||
pub(super) struct Command { | ||
/// The URL at which the Sequencer node is listening for ABCI commands. | ||
#[arg( | ||
long, | ||
env = "SEQUENCER_URL", | ||
default_value = crate::DEFAULT_SEQUENCER_RPC | ||
)] | ||
sequencer_url: String, | ||
/// The source to read the pbjson formatted astra.protocol.transaction.v1.Transaction (use `-` | ||
/// to pass via STDIN). | ||
input: FileOrStdin, | ||
} | ||
|
||
// The 'submit' command takes a 'Transaction' in pbjson form and submits it to the sequencer | ||
impl Command { | ||
pub(super) async fn run(self) -> eyre::Result<()> { | ||
let sequencer_client = HttpClient::new(self.sequencer_url.as_str()) | ||
.wrap_err("failed constructing http sequencer client")?; | ||
|
||
let filename = self.input.filename().to_string(); | ||
let transaction = read_transaction(self.input) | ||
.wrap_err_with(|| format!("to signed transaction from `{filename}`"))?; | ||
|
||
let res = sequencer_client | ||
.submit_transaction_sync(transaction) | ||
.await | ||
.wrap_err("failed to submit transaction")?; | ||
|
||
ensure!(res.code.is_ok(), "failed to check tx: {}", res.log); | ||
|
||
let tx_response = sequencer_client.wait_for_tx_inclusion(res.hash).await; | ||
|
||
ensure!( | ||
tx_response.tx_result.code.is_ok(), | ||
"failed to execute tx: {}", | ||
tx_response.tx_result.log | ||
); | ||
|
||
println!("Submission completed!"); | ||
println!("Included in block: {}", tx_response.height); | ||
Ok(()) | ||
} | ||
} | ||
|
||
fn read_transaction(input: FileOrStdin) -> eyre::Result<Transaction> { | ||
let wire_body: <Transaction as Protobuf>::Raw = serde_json::from_reader( | ||
std::io::BufReader::new(input.into_reader()?), | ||
) | ||
.wrap_err_with(|| { | ||
format!( | ||
"failed to parse input as json `{}`", | ||
Transaction::full_name() | ||
) | ||
})?; | ||
Transaction::try_from_raw(wire_body).wrap_err("failed to validate transaction body") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,7 @@ use astria_core::{ | |
Transaction, | ||
}, | ||
}, | ||
Protobuf as _, | ||
}; | ||
use astria_eyre::eyre::{ | ||
self, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ use crate::{ | |
block::Deposit, | ||
SequencerBlock, | ||
}, | ||
Protobuf as _, | ||
}; | ||
|
||
#[derive(Default)] | ||
|
Oops, something went wrong.