Skip to content

Commit

Permalink
account show command (0xPolygonMiden#21)
Browse files Browse the repository at this point in the history
* account show

* remove unnecessary use

* store and retrieve keys

* add accounts show

* change comment

* Change back miden-base rev to main

* Add table formatting
  • Loading branch information
igamigo authored Dec 4, 2023
1 parent cb5926c commit 0d8d6cd
Show file tree
Hide file tree
Showing 5 changed files with 369 additions and 23 deletions.
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ clap = { version = "4.3" , features = ["derive"] }
crypto = { package = "miden-crypto", git = "https://github.com/0xPolygonMiden/crypto", branch = "next", default-features = false }
lazy_static = "1.4.0"
objects = { package = "miden-objects", git = "https://github.com/0xPolygonMiden/miden-base", branch = "main", features = ["serde"] }
miden_lib = { package = "miden-lib", git = "https://github.com/0xPolygonMiden/miden-base", branch = "main", default-features = false }
mock = { package = "miden-mock", git = "https://github.com/0xPolygonMiden/miden-base", branch = "main", default-features = false, optional = true }
miden_lib = { package = "miden-lib", git = "https://github.com/0xPolygonMiden/miden-base", branch = "main" }
mock = { package = "miden-mock", git = "https://github.com/0xPolygonMiden/miden-base", branch = "main", optional = true }
rusqlite = { version = "0.29.0", features = ["bundled"] }
rusqlite_migration = { version = "1.0" }
rand = { version="0.8.5" }
serde = {version="1.0", features = ["derive"]}
rand = { version = "0.8.5" }
serde = {version = "1.0", features = ["derive"]}
serde_json = { version = "1.0", features = ["raw_value"] }
comfy-table = "7.1.0"

Expand Down
131 changes: 125 additions & 6 deletions src/cli/account.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
use clap::Parser;
use comfy_table::{presets, Attribute, Cell, ContentArrangement, Table};
use crypto::{dsa::rpo_falcon512::KeyPair, Felt};
use crypto::{
dsa::rpo_falcon512::KeyPair,
utils::{bytes_to_hex_string, Serializable},
Felt,
};
use miden_client::Client;
use miden_lib::{faucets, AuthScheme};
use objects::{accounts::AccountType, assets::TokenSymbol};
use objects::{
accounts::{AccountId, AccountType},
assets::TokenSymbol,
};
use rand::Rng;

// ACCOUNT COMMAND
Expand All @@ -16,11 +23,20 @@ pub enum AccountCmd {
#[clap(short_flag = 'l')]
List,

/// View details of the account for the specified ID
#[clap(short_flag = 'v')]
View {
/// Show details of the account for the specified ID
#[clap(short_flag = 's')]
Show {
// TODO: We should create a value parser for catching input parsing errors earlier (ie AccountID) once complexity grows
#[clap()]
id: Option<String>,
#[clap(short, long, default_value_t = false)]
keys: bool,
#[clap(short, long, default_value_t = false)]
vault: bool,
#[clap(short, long, default_value_t = false)]
storage: bool,
#[clap(short, long, default_value_t = false)]
code: bool,
},

/// Create new account and store it locally
Expand Down Expand Up @@ -64,7 +80,21 @@ impl AccountCmd {
AccountCmd::New { template, deploy } => {
new_account(client, template, *deploy)?;
}
AccountCmd::View { id: _ } => todo!(),
AccountCmd::Show { id: None, .. } => {
todo!("Default accounts are not supported yet")
}
AccountCmd::Show {
id: Some(v),
keys,
vault,
storage,
code,
} => {
let account_id: AccountId = AccountId::from_hex(v)
.map_err(|_| "Input number was not a valid Account Id")?;

show_account(client, account_id, *keys, *vault, *storage, *code)?;
}
}
Ok(())
}
Expand Down Expand Up @@ -164,6 +194,7 @@ fn new_account(
.and_then(|_| client.store().insert_account_storage(account.storage()))
.and_then(|_| client.store().insert_account_vault(account.vault()))
.and_then(|_| client.store().insert_account(&account))
.and_then(|_| client.store().insert_account_keys(account.id(), &key_pair))
.map(|_| {
println!(
"Succesfully created and stored Account ID: {}",
Expand All @@ -174,3 +205,91 @@ fn new_account(

Ok(())
}

pub fn show_account(
client: Client,
account_id: AccountId,
show_keys: bool,
show_vault: bool,
show_storage: bool,
show_code: bool,
) -> Result<(), String> {
let account = client
.get_account_by_id(account_id)
.map_err(|err| err.to_string())?;

let mut table = Table::new();
table
.load_preset(presets::UTF8_FULL)
.set_content_arrangement(ContentArrangement::DynamicFullWidth)
.set_header(vec![
Cell::new("account id").add_attribute(Attribute::Bold),
Cell::new("code root").add_attribute(Attribute::Bold),
Cell::new("vault root").add_attribute(Attribute::Bold),
Cell::new("storage root").add_attribute(Attribute::Bold),
Cell::new("nonce").add_attribute(Attribute::Bold),
]);

table.add_row(vec![
account.id().to_string(),
account.code_root().to_string(),
account.vault_root().to_string(),
account.storage_root().to_string(),
account.nonce().to_string(),
]);

println!("{table}\n");

if show_keys {
let auth_info = client
.get_account_keys(account_id)
.map_err(|err| err.to_string())?;

// TODO: Decide how we want to output and import auth info

const KEY_PAIR_SIZE: usize = std::mem::size_of::<KeyPair>();
let auth_info: [u8; KEY_PAIR_SIZE] = auth_info
.to_bytes()
.try_into()
.expect("Array size is const and should always exactly fit KeyPair");
println!("Key pair:\n0x{}", bytes_to_hex_string(auth_info));
}

if show_vault {
let assets = client
.get_vault_assets(account.vault_root())
.map_err(|err| err.to_string())?;

println!(
"Vault assets: {}\n",
serde_json::to_string(&assets).map_err(|_| "Error serializing account assets")?
);
}

if show_storage {
let account_storage = client
.get_account_storage(account.storage_root())
.map_err(|err| err.to_string())?;

println!(
"Storage: {}\n",
serde_json::to_string(&account_storage)
.map_err(|_| "Error serializing account storage")?
);
}

if show_code {
let (procedure_digests, module) = client
.get_account_code(account.code_root())
.map_err(|err| err.to_string())?;

println!(
"Procedure digests:\n{}\n",
serde_json::to_string(&procedure_digests)
.map_err(|_| "Error serializing account storage for display")?
);
println!("Module AST:\n{}\n", module);
}

Ok(())
}
26 changes: 24 additions & 2 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use core::fmt;
use objects::{AccountError, Digest};
use crypto::utils::DeserializationError;
use objects::{accounts::AccountId, AccountError, Digest};

// CLIENT ERROR
// ================================================================================================
Expand Down Expand Up @@ -38,7 +39,12 @@ pub enum StoreError {
ColumnParsingError(rusqlite::Error),
QueryError(rusqlite::Error),
InputSerializationError(serde_json::Error),
DataDeserializationError(serde_json::Error),
JsonDataDeserializationError(serde_json::Error),
DataDeserializationError(DeserializationError),
AccountDataNotFound(AccountId),
AccountStorageNotFound(Digest),
VaultDataNotFound(Digest),
AccountCodeDataNotFound(Digest),
InputNoteNotFound(Digest),
}

Expand All @@ -55,10 +61,26 @@ impl fmt::Display for StoreError {
InputSerializationError(err) => {
write!(f, "error trying to serialize inputs for the store: {err}")
}
JsonDataDeserializationError(err) => {
write!(
f,
"error deserializing data from JSON from the store: {err}"
)
}
DataDeserializationError(err) => {
write!(f, "error deserializing data from the store: {err}")
}
AccountDataNotFound(account_id) => {
write!(f, "Account data was not found for Account Id {account_id}")
}
InputNoteNotFound(hash) => write!(f, "input note with hash {} not found", hash),
AccountStorageNotFound(root) => {
write!(f, "account storage data with root {} not found", root)
}
VaultDataNotFound(root) => write!(f, "account vault data for root {} not found", root),
AccountCodeDataNotFound(root) => {
write!(f, "account code data with root {} not found", root)
}
}
}
}
Expand Down
47 changes: 46 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use objects::{
accounts::{Account, AccountId, AccountStub},
assembly::ModuleAst,
assets::Asset,
crypto::dsa::rpo_falcon512::KeyPair,
notes::RecordedNote,
Digest,
utils::collections::BTreeMap,
Digest, Word,
};
use std::path::PathBuf;

Expand Down Expand Up @@ -61,6 +65,47 @@ impl Client {
self.store.get_accounts().map_err(|err| err.into())
}

/// Returns summary info about the specified account.
pub fn get_account_by_id(&self, account_id: AccountId) -> Result<AccountStub, ClientError> {
self.store
.get_account_by_id(account_id)
.map_err(|err| err.into())
}

/// Returns key pair structure for an Account Id.
pub fn get_account_keys(&self, account_id: AccountId) -> Result<KeyPair, ClientError> {
self.store
.get_account_keys(account_id)
.map_err(|err| err.into())
}

/// Returns vault assets from a vault root.
pub fn get_vault_assets(&self, vault_root: Digest) -> Result<Vec<Asset>, ClientError> {
self.store
.get_vault_assets(vault_root)
.map_err(|err| err.into())
}

/// Returns account code data from a root.
pub fn get_account_code(
&self,
code_root: Digest,
) -> Result<(Vec<Digest>, ModuleAst), ClientError> {
self.store
.get_account_code(code_root)
.map_err(|err| err.into())
}

/// Returns account storage data from a storage root.
pub fn get_account_storage(
&self,
storage_root: Digest,
) -> Result<BTreeMap<u64, Word>, ClientError> {
self.store
.get_account_storage(storage_root)
.map_err(|err| err.into())
}

/// Returns historical states for the account with the specified ID.
///
/// TODO: wrap `Account` in a type with additional info.
Expand Down
Loading

0 comments on commit 0d8d6cd

Please sign in to comment.