Skip to content
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

Basic storage inspection command #1395

Merged
merged 39 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
c93e31f
Add storage command
ascjones Oct 27, 2023
eb314b6
Merge branch 'master' into aj/storage-basic
ascjones Nov 8, 2023
345441e
WIP separate contract_info.rs
ascjones Nov 9, 2023
c2e6a60
Extract contract_info queries to separate mod
ascjones Nov 9, 2023
6064a38
Extract contract_artifacts to separate mod
ascjones Nov 9, 2023
ef2e4e6
Delete contract_info.rs
ascjones Nov 9, 2023
df4d100
Merge branch 'aj/refactor-extrinsics-lib' into aj/storage-basic
ascjones Nov 9, 2023
dcfe419
Fetch contract root storage and wire up cmd
ascjones Nov 9, 2023
9755bcc
Merge branch 'master' into aj/storage-basic
ascjones Nov 10, 2023
4d79680
Fetch contract storage for root
ascjones Nov 10, 2023
f682131
Comment
ascjones Nov 10, 2023
c4b5972
add todo
ascjones Nov 10, 2023
6e63ffa
Merge branch 'master' into aj/storage-basic
ascjones Nov 20, 2023
8b38c19
Revert info files to `master`
ascjones Nov 20, 2023
e9492b2
Move to separate contract_storage.rs mod.
ascjones Nov 20, 2023
4eeb308
WIP getting keys from contract metadata storage layout
ascjones Nov 20, 2023
45c45d4
Get root storage key from metadata
ascjones Nov 21, 2023
5a5dbfd
Make it generic
ascjones Nov 21, 2023
0d254a0
Recurse
ascjones Nov 21, 2023
6e78b36
WIP fetching all keys for contract
ascjones Nov 21, 2023
245d008
Use prefix
ascjones Nov 22, 2023
636379f
Fetch all storage entries raw
ascjones Nov 22, 2023
961e52b
BTreeMap
ascjones Nov 22, 2023
70d8458
Display enriched contract storage
ascjones Nov 22, 2023
df7b093
Remove unused stuff
ascjones Nov 22, 2023
cc0d961
WIP query root keys
ascjones Nov 22, 2023
95be3bf
Query root keys
ascjones Nov 22, 2023
fa2c000
Warnings and don't serialize if none
ascjones Nov 22, 2023
ce0fd3a
Clippy
ascjones Nov 22, 2023
d5a2159
Merge branch 'master' into aj/storage-basic
ascjones Nov 23, 2023
f3d0a74
Use Display for TrieId
ascjones Nov 23, 2023
de9fd7d
Display raw storage
ascjones Nov 23, 2023
b588e38
Flatten TrieId properly
ascjones Nov 23, 2023
d496831
Clippy
ascjones Nov 23, 2023
1f88566
CHANGELOG.md
ascjones Nov 24, 2023
a74d787
README.md
ascjones Nov 24, 2023
a7870f7
Make `Client` generic
ascjones Nov 27, 2023
5055425
Don't need `required_unless_present = "all"`
ascjones Nov 27, 2023
4558255
Fmt
ascjones Nov 27, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add workspace support -[#1358](https://github.com/paritytech/cargo-contract/pull/1358)
- Add `Storage Total Deposit` to `info` command output - [#1347](https://github.com/paritytech/cargo-contract/pull/1347)
- Add dynamic types support - [#1399](https://github.com/paritytech/cargo-contract/pull/1399)
- Basic storage inspection command - [#1395](https://github.com/paritytech/cargo-contract/pull/1395)

### Fixed
- Do not allow to execute calls on immutable contract messages - [#1397](https://github.com/paritytech/cargo-contract/pull/1397)
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ Remove a contract from a `pallet-contracts` enabled chain. See [extrinsics](crat

Fetch and display contract information of a contract on chain. See [info](docs/info.md).

##### `cargo contract storage`

Fetch and display the storage of a contract on chain.

## Publishing

In order to publish a new version of `cargo-contract`:
Expand Down
5 changes: 3 additions & 2 deletions crates/cargo-contract/src/cmd/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use contract_extrinsics::{
CodeHash,
ContractInfo,
ErrorVariant,
TrieId,
};
use std::{
fmt::Debug,
Expand Down Expand Up @@ -139,7 +140,7 @@ impl InfoCommand {

#[derive(serde::Serialize)]
pub struct ExtendedContractInfo {
pub trie_id: String,
pub trie_id: TrieId,
pub code_hash: CodeHash,
pub storage_items: u32,
pub storage_items_deposit: Balance,
Expand All @@ -154,7 +155,7 @@ impl ExtendedContractInfo {
None => "Unknown".to_string(),
};
ExtendedContractInfo {
trie_id: contract_info.trie_id().to_string(),
trie_id: contract_info.trie_id().clone(),
code_hash: *contract_info.code_hash(),
storage_items: contract_info.storage_items(),
storage_items_deposit: contract_info.storage_items_deposit(),
Expand Down
4 changes: 3 additions & 1 deletion crates/cargo-contract/src/cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub mod info;
pub mod instantiate;
pub mod remove;
pub mod schema;
pub mod storage;
pub mod upload;
pub mod verify;

Expand All @@ -42,6 +43,7 @@ pub(crate) use self::{
GenerateSchemaCommand,
VerifySchemaCommand,
},
storage::StorageCommand,
upload::UploadCommand,
verify::VerifyCommand,
};
Expand Down Expand Up @@ -223,7 +225,7 @@ pub fn print_gas_required_success(gas: Weight) {

/// Display contract information in a formatted way
pub fn basic_display_format_extended_contract_info(info: &ExtendedContractInfo) {
name_value_println!("TrieId", format!("{}", info.trie_id), MAX_KEY_COL_WIDTH);
name_value_println!("TrieId", info.trie_id, MAX_KEY_COL_WIDTH);
name_value_println!(
"Code Hash",
format!("{:?}", info.code_hash),
Expand Down
113 changes: 113 additions & 0 deletions crates/cargo-contract/src/cmd/storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2018-2023 Parity Technologies (UK) Ltd.
// This file is part of cargo-contract.
//
// cargo-contract is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// cargo-contract is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with cargo-contract. If not, see <http://www.gnu.org/licenses/>.

use super::DefaultConfig;
use anyhow::Result;
use colored::Colorize;
use contract_extrinsics::{
ContractArtifacts,
ContractStorage,
ContractStorageRpc,
ErrorVariant,
};
use std::{
fmt::Debug,
path::PathBuf,
};
use subxt::Config;

#[derive(Debug, clap::Args)]
#[clap(name = "storage", about = "Inspect contract storage")]
pub struct StorageCommand {
/// The address of the contract to inspect storage of.
#[clap(
name = "contract",
long,
env = "CONTRACT",
required_unless_present = "all"
smiasojed marked this conversation as resolved.
Show resolved Hide resolved
)]
contract: <DefaultConfig as Config>::AccountId,
/// Fetch the "raw" storage keys and values for the contract.
#[clap(long)]
raw: bool,
/// Path to a contract build artifact file: a raw `.wasm` file, a `.contract` bundle,
/// or a `.json` metadata file.
#[clap(value_parser, conflicts_with = "manifest_path")]
file: Option<PathBuf>,
/// Path to the `Cargo.toml` of the contract.
#[clap(long, value_parser)]
manifest_path: Option<PathBuf>,
/// Websockets url of a substrate node.
#[clap(
name = "url",
long,
value_parser,
default_value = "ws://localhost:9944"
)]
url: url::Url,
}

impl StorageCommand {
pub async fn run(&self) -> Result<(), ErrorVariant> {
let rpc = ContractStorageRpc::<DefaultConfig>::new(&self.url).await?;
let storage_layout = ContractStorage::<DefaultConfig>::new(rpc);

if self.raw {
let storage_data = storage_layout
.load_contract_storage_data(&self.contract)
.await?;
println!(
"{json}",
json = serde_json::to_string_pretty(&storage_data)?
);
return Ok(())
}

let contract_artifacts = ContractArtifacts::from_manifest_or_file(
self.manifest_path.as_ref(),
self.file.as_ref(),
);

match contract_artifacts {
Ok(contract_artifacts) => {
let ink_metadata = contract_artifacts.ink_project_metadata()?;
let contract_storage = storage_layout
.load_contract_storage_with_layout(&ink_metadata, &self.contract)
.await?;
println!(
"{json}",
json = serde_json::to_string_pretty(&contract_storage)?
);
}
Err(_) => {
eprintln!(
"{} Displaying raw storage: no valid contract metadata artifacts found",
"Info:".cyan().bold(),
);
let storage_data = storage_layout
.load_contract_storage_data(&self.contract)
.await?;
println!(
"{json}",
json = serde_json::to_string_pretty(&storage_data)?
);
return Ok(())
}
}

Ok(())
}
}
7 changes: 7 additions & 0 deletions crates/cargo-contract/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use self::cmd::{
InfoCommand,
InstantiateCommand,
RemoveCommand,
StorageCommand,
UploadCommand,
VerifyCommand,
VerifySchemaCommand,
Expand Down Expand Up @@ -140,6 +141,9 @@ enum Command {
/// Display information about a contract
#[clap(name = "info")]
Info(InfoCommand),
/// Inspect the on-chain storage of a contract.
#[clap(name = "storage")]
Storage(StorageCommand),
/// Verifies that a given contract binary matches the build result of the specified
/// workspace.
#[clap(name = "verify")]
Expand Down Expand Up @@ -228,6 +232,9 @@ fn exec(cmd: Command) -> Result<()> {
Command::Info(info) => {
runtime.block_on(async { info.run().await.map_err(format_err) })
}
Command::Storage(storage) => {
runtime.block_on(async { storage.run().await.map_err(format_err) })
}
Command::Verify(verify) => {
let result = verify.run().map_err(format_err)?;

Expand Down
1 change: 1 addition & 0 deletions crates/extrinsics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ contract-metadata = { version = "4.0.0-alpha", path = "../metadata" }
contract-transcode = { version = "4.0.0-alpha", path = "../transcode" }

anyhow = "1.0.75"
blake2 = { version = "0.10.6", default-features = false }
clap = { version = "4.4.8", features = ["derive", "env"] }
futures = { version = "0.3.29", default-features = false, features = ["std"] }
tracing = "0.1.40"
Expand Down
15 changes: 15 additions & 0 deletions crates/extrinsics/src/contract_artifacts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use anyhow::{
Result,
};
use colored::Colorize;
use ink_metadata::InkProject;
use std::path::{
Path,
PathBuf,
Expand Down Expand Up @@ -142,6 +143,20 @@ impl ContractArtifacts {
})
}

/// Get the deserialized [`InkProject`] metadata.
///
/// ## Errors
/// - No contract metadata could be found.
/// - Invalid contract metadata.
pub fn ink_project_metadata(&self) -> Result<InkProject> {
smiasojed marked this conversation as resolved.
Show resolved Hide resolved
let metadata = self.metadata()?;
let ink_project = serde_json::from_value(serde_json::Value::Object(metadata.abi))
.context(
"Failed to deserialize ink project metadata from contract metadata",
)?;
Ok(ink_project)
}

/// Get the code hash from the contract metadata.
pub fn code_hash(&self) -> Result<[u8; 32]> {
let metadata = self.metadata()?;
Expand Down
Loading
Loading