-
Notifications
You must be signed in to change notification settings - Fork 119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add flag --all to cargo contract info #1319
Changes from 9 commits
941c652
74b43b7
efef0b1
01e768c
0fdece8
be59195
a4d93df
a1d059b
d59e201
01d8d5d
3b056d5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -16,13 +16,15 @@ | |||||
|
||||||
use super::{ | ||||||
basic_display_format_contract_info, | ||||||
display_all_contracts, | ||||||
DefaultConfig, | ||||||
}; | ||||||
use anyhow::{ | ||||||
anyhow, | ||||||
Result, | ||||||
}; | ||||||
use contract_extrinsics::{ | ||||||
fetch_all_contracts, | ||||||
fetch_contract_info, | ||||||
fetch_wasm_code, | ||||||
ErrorVariant, | ||||||
|
@@ -35,14 +37,18 @@ use subxt::{ | |||||
Config, | ||||||
OnlineClient, | ||||||
}; | ||||||
use tokio::runtime::Runtime; | ||||||
|
||||||
#[derive(Debug, clap::Args)] | ||||||
#[clap(name = "info", about = "Get infos from a contract")] | ||||||
pub struct InfoCommand { | ||||||
/// The address of the contract to display info of. | ||||||
#[clap(name = "contract", long, env = "CONTRACT")] | ||||||
contract: <DefaultConfig as Config>::AccountId, | ||||||
#[clap( | ||||||
name = "contract", | ||||||
long, | ||||||
env = "CONTRACT", | ||||||
required_unless_present = "all" | ||||||
)] | ||||||
contract: Option<<DefaultConfig as Config>::AccountId>, | ||||||
/// Websockets url of a substrate node. | ||||||
#[clap( | ||||||
name = "url", | ||||||
|
@@ -55,76 +61,83 @@ pub struct InfoCommand { | |||||
#[clap(name = "output-json", long)] | ||||||
output_json: bool, | ||||||
/// Display the contract's Wasm bytecode. | ||||||
#[clap(name = "binary", long)] | ||||||
#[clap(name = "binary", long, conflicts_with = "all")] | ||||||
binary: bool, | ||||||
/// Display all contracts addresses | ||||||
#[clap(name = "all", long)] | ||||||
all: bool, | ||||||
} | ||||||
|
||||||
impl InfoCommand { | ||||||
pub fn run(&self) -> Result<(), ErrorVariant> { | ||||||
tracing::debug!( | ||||||
"Getting contract information for AccountId {:?}", | ||||||
self.contract | ||||||
); | ||||||
pub async fn run(&self) -> Result<(), ErrorVariant> { | ||||||
let url = self.url.clone(); | ||||||
let client = OnlineClient::<DefaultConfig>::from_url(url).await?; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||
|
||||||
Runtime::new()?.block_on(async { | ||||||
let url = self.url.clone(); | ||||||
let client = OnlineClient::<DefaultConfig>::from_url(url).await?; | ||||||
// All flag applied | ||||||
if self.all { | ||||||
// 1000 is max allowed value | ||||||
const COUNT: u32 = 1000; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||
let mut from = None; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||
let mut contracts = Vec::new(); | ||||||
loop { | ||||||
let len = contracts.len(); | ||||||
contracts.append(&mut fetch_all_contracts(&client, COUNT, from).await?); | ||||||
if contracts.len() < len + COUNT as usize { | ||||||
break | ||||||
} | ||||||
from = contracts.last(); | ||||||
} | ||||||
|
||||||
let info_result = fetch_contract_info(&self.contract, &client).await?; | ||||||
if self.output_json { | ||||||
let contracts_json = serde_json::json!({ | ||||||
"contracts": contracts | ||||||
}); | ||||||
println!("{}", serde_json::to_string_pretty(&contracts_json)?); | ||||||
} else { | ||||||
display_all_contracts(&contracts) | ||||||
} | ||||||
Ok(()) | ||||||
} else { | ||||||
// Contract arg shall be always present in this case, it is enforced by | ||||||
// clap configuration | ||||||
let contract = self | ||||||
.contract | ||||||
.as_ref() | ||||||
.expect("Contract argument was not provided"); | ||||||
|
||||||
match info_result { | ||||||
Some(info_to_json) => { | ||||||
match (self.output_json, self.binary) { | ||||||
(true, false) => println!("{}", info_to_json.to_json()?), | ||||||
(false, false) => { | ||||||
basic_display_format_contract_info(&info_to_json) | ||||||
} | ||||||
// Binary flag applied | ||||||
(_, true) => { | ||||||
let wasm_code = | ||||||
fetch_wasm_code(*info_to_json.code_hash(), &client) | ||||||
.await?; | ||||||
match (wasm_code, self.output_json) { | ||||||
(Some(code), false) => { | ||||||
std::io::stdout() | ||||||
.write_all(&code) | ||||||
.expect("Writing to stdout failed") | ||||||
} | ||||||
(Some(code), true) => { | ||||||
let wasm = serde_json::json!({ | ||||||
"wasm": format!("0x{}", hex::encode(code)) | ||||||
}); | ||||||
println!( | ||||||
"{}", | ||||||
serde_json::to_string_pretty(&wasm).map_err( | ||||||
|err| { | ||||||
anyhow!( | ||||||
"JSON serialization failed: {}", | ||||||
err | ||||||
) | ||||||
} | ||||||
)? | ||||||
); | ||||||
} | ||||||
(None, _) => { | ||||||
return Err(anyhow!( | ||||||
"Contract wasm code was not found" | ||||||
) | ||||||
.into()) | ||||||
} | ||||||
} | ||||||
} | ||||||
} | ||||||
Ok(()) | ||||||
} | ||||||
None => { | ||||||
Err(anyhow!( | ||||||
let info_to_json = | ||||||
fetch_contract_info(contract, &client) | ||||||
.await? | ||||||
.ok_or(anyhow!( | ||||||
"No contract information was found for account id {}", | ||||||
self.contract | ||||||
) | ||||||
.into()) | ||||||
contract | ||||||
))?; | ||||||
|
||||||
// Binary flag applied | ||||||
if self.binary { | ||||||
let wasm_code = fetch_wasm_code(&client, info_to_json.code_hash()) | ||||||
.await? | ||||||
.ok_or(anyhow!( | ||||||
"Contract wasm code was not found for account id {}", | ||||||
contract | ||||||
))?; | ||||||
|
||||||
if self.output_json { | ||||||
let wasm = serde_json::json!({ | ||||||
"wasm": format!("0x{}", hex::encode(wasm_code)) | ||||||
}); | ||||||
println!("{}", serde_json::to_string_pretty(&wasm)?); | ||||||
} else { | ||||||
std::io::stdout() | ||||||
.write_all(&wasm_code) | ||||||
.expect("Writing to stdout failed") | ||||||
} | ||||||
} else if self.output_json { | ||||||
println!("{}", info_to_json.to_json()?) | ||||||
} else { | ||||||
basic_display_format_contract_info(&info_to_json) | ||||||
} | ||||||
}) | ||||||
Ok(()) | ||||||
} | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,7 +65,10 @@ use std::io::{ | |
self, | ||
Write, | ||
}; | ||
pub use subxt::PolkadotConfig as DefaultConfig; | ||
pub use subxt::{ | ||
Config, | ||
PolkadotConfig as DefaultConfig, | ||
}; | ||
|
||
/// Arguments required for creating and sending an extrinsic to a substrate node. | ||
#[derive(Clone, Debug, clap::Args)] | ||
|
@@ -230,3 +233,12 @@ pub fn basic_display_format_contract_info(info: &ContractInfo) { | |
MAX_KEY_COL_WIDTH | ||
); | ||
} | ||
|
||
/// Display all contracts addresses in a formatted way | ||
pub fn display_all_contracts(contracts: &[<DefaultConfig as Config>::AccountId]) { | ||
contracts | ||
.iter() | ||
.for_each(|e: &<DefaultConfig as Config>::AccountId| { | ||
name_value_println!("Contract", format!("{}", e), MAX_KEY_COL_WIDTH); | ||
}) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would rewrite it to display the result something like: Found contracts:
* <Address>
* <Address>
* <Address> There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that in case when we have more than 1000 contracts, it will be hard to see this line There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then I would enumerate them at least. Useful when you want to pipe the output into another bash command: Found contracts:
1. <Address>
2. <Address>
...
1000. <Address> It is up to you if you want to keep There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In this case it is best then to have only the list of contract addresses so instead of
have
I like it this way, unix command compatible. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -52,7 +52,10 @@ use scale::{ | |||||
Decode, | ||||||
Encode, | ||||||
}; | ||||||
use sp_core::Bytes; | ||||||
use sp_core::{ | ||||||
hashing, | ||||||
Bytes, | ||||||
}; | ||||||
use subxt::{ | ||||||
blocks, | ||||||
config, | ||||||
|
@@ -364,7 +367,10 @@ impl ContractInfo { | |||||
} | ||||||
|
||||||
/// Fetch the contract wasm code from the storage using the provided client and code hash. | ||||||
pub async fn fetch_wasm_code(hash: CodeHash, client: &Client) -> Result<Option<Vec<u8>>> { | ||||||
pub async fn fetch_wasm_code( | ||||||
client: &Client, | ||||||
hash: &CodeHash, | ||||||
) -> Result<Option<Vec<u8>>> { | ||||||
let pristine_code_address = api::storage().contracts().pristine_code(hash); | ||||||
|
||||||
let pristine_bytes = client | ||||||
|
@@ -378,6 +384,42 @@ pub async fn fetch_wasm_code(hash: CodeHash, client: &Client) -> Result<Option<V | |||||
Ok(pristine_bytes) | ||||||
} | ||||||
|
||||||
/// Fetch all contracts addresses from the storage using the provided client and count of | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||
/// requested elements starting from an optional address | ||||||
pub async fn fetch_all_contracts( | ||||||
client: &Client, | ||||||
count: u32, | ||||||
from: Option<&AccountId32>, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||
) -> Result<Vec<AccountId32>> { | ||||||
let key = api::storage() | ||||||
.contracts() | ||||||
.contract_info_of_root() | ||||||
.to_root_bytes(); | ||||||
let start_key = from | ||||||
.map(|e| [key.clone(), hashing::twox_64(&e.0).to_vec(), e.0.to_vec()].concat()); | ||||||
let keys = client | ||||||
.storage() | ||||||
.at_latest() | ||||||
.await? | ||||||
.fetch_keys(key.as_ref(), count, start_key.as_deref()) | ||||||
.await?; | ||||||
|
||||||
// StorageKey is a concatention of contract_info_of root key and | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||||||
// Twox64Concat(AccountId) | ||||||
let contracts = keys | ||||||
.into_iter() | ||||||
.map(|e| { | ||||||
let mut account = | ||||||
e.0.get(key.len() + 8..) | ||||||
.ok_or(anyhow!("Unexpected storage key size"))?; | ||||||
AccountId32::decode(&mut account) | ||||||
.map_err(|err| anyhow!("AccountId deserialization error: {}", err)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would extract it into a separate function for better readability There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||
}) | ||||||
.collect::<Result<_, _>>()?; | ||||||
|
||||||
Ok(contracts) | ||||||
} | ||||||
|
||||||
/// Copy of `pallet_contracts_primitives::StorageDeposit` which implements `Serialize`, | ||||||
/// required for json output. | ||||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, serde::Serialize)] | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done