feature: add db state dump command to `optics-cli` (#874)

* refactor: pull out prove into subcommand

* refactor: structopt not clap

* fix: leaf or leaf_index args

* prog: draft for dbstate command without printing

* prog: prints unordered list of roots, block nums, and leaves

* fix: decomposes run function and tidies up printing

* nit: rename committed_root to update_root at print stage

* fix: dockerfiles specifies optics-cli instead of prove-cli

* feature: allow saving to json file

* fix: use retrieve_metadata instead after history rearragement

* doc: fix info message and kv comment
buddies-main-deployment
Luke Tchang 3 years ago committed by GitHub
parent 5a9f4d5d77
commit ab8913df12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 107
      rust/Cargo.lock
  2. 2
      rust/Cargo.toml
  3. 2
      rust/Dockerfile
  4. 3
      rust/optics-core/src/db/home_db.rs
  5. 4
      rust/tools/optics-cli/Cargo.toml
  6. 0
      rust/tools/optics-cli/README.md
  7. 11
      rust/tools/optics-cli/src/commands.rs
  8. 19
      rust/tools/optics-cli/src/main.rs
  9. 0
      rust/tools/optics-cli/src/replicas.rs
  10. 0
      rust/tools/optics-cli/src/rpc.rs
  11. 153
      rust/tools/optics-cli/src/subcommands/db_state.rs
  12. 5
      rust/tools/optics-cli/src/subcommands/mod.rs
  13. 85
      rust/tools/optics-cli/src/subcommands/prove.rs

107
rust/Cargo.lock generated

@ -48,6 +48,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
@ -461,6 +470,21 @@ dependencies = [
"libloading",
]
[[package]]
name = "clap"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"ansi_term 0.11.0",
"atty",
"bitflags",
"strsim 0.8.0",
"textwrap 0.11.0",
"unicode-width",
"vec_map",
]
[[package]]
name = "clap"
version = "3.0.0-beta.4"
@ -473,9 +497,9 @@ dependencies = [
"indexmap",
"lazy_static",
"os_str_bytes",
"strsim",
"strsim 0.10.0",
"termcolor",
"textwrap",
"textwrap 0.14.2",
"vec_map",
]
@ -1660,7 +1684,7 @@ checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"
name = "kms-cli"
version = "0.1.0"
dependencies = [
"clap",
"clap 3.0.0-beta.4",
"color-eyre",
"ethers",
"ethers-signers",
@ -2140,6 +2164,24 @@ dependencies = [
"warp",
]
[[package]]
name = "optics-cli"
version = "0.1.0"
dependencies = [
"color-eyre",
"ethers",
"ethers-signers",
"hex",
"once_cell",
"optics-core",
"optics-ethereum",
"rusoto_core",
"rusoto_kms",
"serde_json",
"structopt",
"tokio",
]
[[package]]
name = "optics-core"
version = "0.1.0"
@ -2534,24 +2576,6 @@ version = "2.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23129d50f2c9355ced935fce8a08bd706ee2e7ce2b3b33bf61dace0e379ac63a"
[[package]]
name = "prove-cli"
version = "0.1.0"
dependencies = [
"clap",
"color-eyre",
"ethers",
"ethers-signers",
"hex",
"once_cell",
"optics-core",
"optics-ethereum",
"rusoto_core",
"rusoto_kms",
"serde_json",
"tokio",
]
[[package]]
name = "quick-error"
version = "1.2.3"
@ -3408,12 +3432,42 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "structopt"
version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf9d950ef167e25e0bdb073cf1d68e9ad2795ac826f2f3f59647817cf23c0bfa"
dependencies = [
"clap 2.33.3",
"lazy_static",
"structopt-derive",
]
[[package]]
name = "structopt-derive"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134d838a2c9943ac3125cf6df165eda53493451b719f3255b2a26b85f772d0ba"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "subtle"
version = "2.4.1"
@ -3460,6 +3514,15 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "textwrap"
version = "0.14.2"
@ -3819,7 +3882,7 @@ version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9cbe87a2fa7e35900ce5de20220a582a9483a7063811defce79d7cbd59d4cfe"
dependencies = [
"ansi_term",
"ansi_term 0.12.1",
"chrono",
"lazy_static",
"matchers",

@ -11,5 +11,5 @@ members = [
# "agents/watcher",
"agents/processor",
"tools/kms-cli",
"tools/prove-cli",
"tools/optics-cli",
]

@ -35,7 +35,7 @@ RUN --mount=id=cargo,type=cache,target=/usr/src/target cp /usr/src/target/relea
RUN --mount=id=cargo,type=cache,target=/usr/src/target cp /usr/src/target/release/processor .
RUN --mount=id=cargo,type=cache,target=/usr/src/target cp /usr/src/target/release/kathy .
RUN --mount=id=cargo,type=cache,target=/usr/src/target cp /usr/src/target/release/kms-cli .
RUN --mount=id=cargo,type=cache,target=/usr/src/target cp /usr/src/target/release/prove-cli .
RUN --mount=id=cargo,type=cache,target=/usr/src/target cp /usr/src/target/release/optics-cli .
# 2: Copy the binaries to release image
FROM ubuntu:20.04

@ -202,6 +202,9 @@ impl HomeDB {
}
/// Store update metadata (by update's new root)
///
/// Keys --> Values:
/// - `update_new_root` --> `update_metadata`
pub fn store_update_metadata(
&self,
new_root: H256,

@ -1,10 +1,9 @@
[package]
name = "prove-cli"
name = "optics-cli"
version = "0.1.0"
edition = "2018"
[dependencies]
clap = "3.0.0-beta.4"
color-eyre = "0.5.11"
ethers = { git = "https://github.com/gakonst/ethers-rs", branch = "master" }
ethers-signers = { git = "https://github.com/gakonst/ethers-rs", branch = "master", features = ["aws"] }
@ -14,6 +13,7 @@ rusoto_core = "0.47.0"
rusoto_kms = "0.47.0"
tokio = "1.9.0"
serde_json = "1.0.66"
structopt = "0.3.23"
optics-ethereum = { path = "../../chains/optics-ethereum" }
optics-core = { path = "../../optics-core" }

@ -0,0 +1,11 @@
use structopt::StructOpt;
use crate::subcommands::{db_state::DbStateCommand, prove::ProveCommand};
#[derive(StructOpt)]
pub enum Commands {
/// Prove a message on a home for a given leaf index
Prove(ProveCommand),
/// Print the processor's db state
DbState(DbStateCommand),
}

@ -0,0 +1,19 @@
use color_eyre::Result;
use structopt::StructOpt;
mod commands;
mod replicas;
mod rpc;
mod subcommands;
use commands::Commands;
#[tokio::main]
async fn main() -> Result<()> {
let command = Commands::from_args();
match command {
Commands::Prove(prove) => prove.run().await,
Commands::DbState(db_state) => db_state.run().await,
}
}

@ -0,0 +1,153 @@
use color_eyre::Result;
use serde_json::{json, Value};
use std::{collections::HashMap, convert::TryInto, fs::OpenOptions, io::Write};
use structopt::StructOpt;
use optics_core::{
db::{HomeDB, DB},
traits::CommittedMessage,
};
use ethers::types::H256;
#[derive(StructOpt, Debug)]
pub struct DbStateCommand {
/// Path to processor db
#[structopt(long)]
db_path: String,
/// Name of associated home
#[structopt(long)]
home_name: String,
/// Save output to json file
#[structopt(long)]
json: bool,
}
type OutputVec = Vec<((H256, u64), Vec<CommittedMessage>)>;
impl DbStateCommand {
pub async fn run(&self) -> Result<()> {
let db = HomeDB::new(DB::from_path(&self.db_path)?, self.home_name.clone());
let messages_by_committed_roots = DbStateCommand::create_comitted_root_to_message_map(&db)?;
let output_vec = DbStateCommand::create_output_vec(&db, messages_by_committed_roots)?;
if self.json {
DbStateCommand::save_to_json(output_vec)?;
} else {
DbStateCommand::print_output(output_vec);
}
Ok(())
}
fn create_comitted_root_to_message_map(
db: &HomeDB,
) -> Result<HashMap<H256, Vec<CommittedMessage>>> {
let mut messages_by_committed_roots: HashMap<H256, Vec<CommittedMessage>> = HashMap::new();
for index in 0.. {
match db.message_by_leaf_index(index)? {
Some(message) => {
let committed_root = message.committed_root;
let bucket_opt = messages_by_committed_roots.get_mut(&committed_root);
// Get reference to bucket for committed root
let bucket = match bucket_opt {
Some(bucket) => bucket,
None => {
messages_by_committed_roots
.insert(committed_root, Vec::<CommittedMessage>::new());
messages_by_committed_roots
.get_mut(&committed_root)
.unwrap()
}
};
// Add message to bucket for committed root
bucket.push(message.try_into()?);
}
None => break,
}
}
Ok(messages_by_committed_roots)
}
fn create_output_vec(
db: &HomeDB,
messages_by_committed_roots: HashMap<H256, Vec<CommittedMessage>>,
) -> Result<OutputVec> {
// Create mapping of (update root, block_number) to [messages]
let mut output_map: HashMap<(H256, u64), Vec<CommittedMessage>> = HashMap::new();
for (committed_root, bucket) in messages_by_committed_roots {
let containing_update_opt = db.update_by_previous_root(committed_root)?;
match containing_update_opt {
Some(containing_update) => {
let new_root = containing_update.update.new_root;
let update_metadata =
db.retrieve_update_metadata(new_root)?.unwrap_or_else(|| {
panic!("Couldn't find metadata for update {:?}", containing_update)
});
output_map.insert((new_root, update_metadata.block_number), bucket);
}
// No more updates left
None => break,
}
}
// Convert hashmap into vector of k,v pairs and sort the entries by
// update block number
let mut output_vec: Vec<_> = output_map.into_iter().collect();
output_vec.sort_by(|x, y| x.0 .1.cmp(&y.0 .1));
Ok(output_vec)
}
fn print_output(output_vec: OutputVec) {
for ((update_root, block_number), mut bucket) in output_vec {
println!("Update root: {:?}", update_root);
println!("Block number: {}", block_number);
bucket.sort_by(|x, y| x.leaf_index.cmp(&y.leaf_index));
print!("Leaves:");
for message in bucket {
print!(" {} ", message.leaf_index);
}
println!("\n");
}
}
fn save_to_json(output_vec: OutputVec) -> Result<()> {
let mut json_entries: Vec<Value> = Vec::new();
for ((update_root, block_number), mut bucket) in output_vec {
bucket.sort_by(|x, y| x.leaf_index.cmp(&y.leaf_index));
let leaf_indexes: Vec<_> = bucket.iter().map(|leaf| leaf.leaf_index).collect();
json_entries.push(json!({
"updateRoot": update_root,
"blockNumber": block_number,
"leaves": leaf_indexes,
}));
}
let json = json!(json_entries).to_string();
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open("dbState.json")
.expect("Failed to open/create file");
file.write_all(json.as_bytes())
.expect("Failed to write to file");
Ok(())
}
}

@ -0,0 +1,5 @@
pub mod db_state;
pub mod prove;
pub use db_state::*;
pub use prove::*;

@ -1,4 +1,7 @@
use std::{convert::TryFrom, sync::Arc};
use structopt::StructOpt;
use crate::{replicas, rpc};
use optics_core::{
accumulator::merkle::Proof,
@ -8,7 +11,6 @@ use optics_core::{
};
use optics_ethereum::EthereumReplica;
use clap::Clap;
use ethers::{
prelude::{Http, Middleware, Provider, SignerMiddleware, H160},
types::H256,
@ -21,53 +23,68 @@ use once_cell::sync::OnceCell;
use rusoto_core::{credential::EnvironmentProvider, HttpClient};
use rusoto_kms::KmsClient;
mod replicas;
mod rpc;
static KMS_CLIENT: OnceCell<KmsClient> = OnceCell::new();
type ConcreteReplica = EthereumReplica<SignerMiddleware<Provider<Http>, Signers>>;
#[derive(Clap)]
struct Opts {
/// Leaf index to prove
#[clap(long)]
leaf_index: Option<u32>,
#[derive(StructOpt, Debug)]
pub struct ProveCommand {
/// Leaf to prove
#[structopt(long, required_unless = "leaf_index")]
leaf: Option<H256>,
/// Leaf index to prove
#[clap(long)]
leaf_hash: Option<H256>,
#[structopt(long, required_unless = "leaf")]
leaf_index: Option<u32>,
/// The name of the home chain, used to lookup keys in the db
#[clap(long)]
home: String,
#[structopt(long)]
home_name: String,
/// Path to db containing proof
#[clap(long)]
db: String,
#[structopt(long)]
db_path: String,
/// HexKey to use (please be careful)
#[clap(long)]
#[structopt(long)]
key: Option<String>,
/// If using AWS signer, the key ID
#[clap(long)]
#[structopt(long)]
key_id: Option<String>,
/// If using AWS signer, the region
#[clap(long)]
#[structopt(long)]
aws_region: Option<String>,
/// replica contract address
#[clap(long)]
#[structopt(long)]
address: Option<String>,
/// RPC connection details
#[clap(long)]
#[structopt(long)]
rpc: Option<String>,
}
impl Opts {
impl ProveCommand {
pub async fn run(&self) -> Result<()> {
let (message, proof) = self.fetch_proof()?;
let replica = self.replica(message.origin, message.destination).await?;
let status = replica.message_status(message.to_leaf()).await?;
let outcome = match status {
MessageStatus::None => replica.prove_and_process(&message, &proof).await?,
MessageStatus::Proven => replica.process(&message).await?,
_ => {
println!("Message already processed.");
return Ok(());
}
};
println!("{:?}", outcome);
Ok(())
}
// mostly copied from optics-base settings
async fn signer(&self) -> Result<Signers> {
if let Some(key) = &self.key {
@ -94,9 +111,9 @@ impl Opts {
}
fn fetch_proof(&self) -> Result<(OpticsMessage, Proof)> {
let db = HomeDB::new(DB::from_path(&self.db)?, self.home.clone());
let db = HomeDB::new(DB::from_path(&self.db_path)?, self.home_name.clone());
let idx = match (self.leaf_index, self.leaf_hash) {
let idx = match (self.leaf_index, self.leaf) {
(Some(idx), _) => idx,
(None, Some(digest)) => match db.message_by_leaf(digest)? {
Some(leaf) => leaf.leaf_index,
@ -138,25 +155,3 @@ impl Opts {
Ok(EthereumReplica::new("", 0, address, Arc::new(middleware)))
}
}
#[tokio::main]
async fn main() -> Result<()> {
let opts = Opts::parse();
let (message, proof) = opts.fetch_proof()?;
let replica = opts.replica(message.origin, message.destination).await?;
let status = replica.message_status(message.to_leaf()).await?;
let outcome = match status {
MessageStatus::None => replica.prove_and_process(&message, &proof).await?,
MessageStatus::Proven => replica.process(&message).await?,
_ => {
println!("Message already processed.");
return Ok(());
}
};
println!("{:?}", outcome);
Ok(())
}
Loading…
Cancel
Save