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

feat(state-viewer): list actions invoked by each contract #8483

Merged
merged 15 commits into from
Feb 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 Cargo.lock

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

20 changes: 19 additions & 1 deletion core/store/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rand::Rng;

use crate::db::TestDB;
use crate::metadata::{DbKind, DbVersion, DB_VERSION};
use crate::{NodeStorage, ShardTries, Store, Temperature};
use crate::{DBCol, NodeStorage, ShardTries, Store, Temperature};
use near_primitives::account::id::AccountId;
use near_primitives::hash::CryptoHash;
use near_primitives::receipt::{DataReceipt, Receipt, ReceiptEnum};
Expand Down Expand Up @@ -84,6 +84,24 @@ pub fn test_populate_trie(
root
}

/// Insert values to non-reference-counted columns in the store.
pub fn test_populate_store(store: &Store, data: &[(DBCol, Vec<u8>, Vec<u8>)]) {
let mut update = store.store_update();
for (column, key, value) in data {
update.insert(*column, key, value);
}
update.commit().expect("db commit failed");
}

/// Insert values to reference-counted columns in the store.
pub fn test_populate_store_rc(store: &Store, data: &[(DBCol, Vec<u8>, Vec<u8>)]) {
let mut update = store.store_update();
for (column, key, value) in data {
update.increment_refcount(*column, key, value);
}
update.commit().expect("db commit failed");
}

fn gen_accounts_from_alphabet(
rng: &mut impl Rng,
max_size: usize,
Expand Down
8 changes: 6 additions & 2 deletions neard/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,15 @@ rosetta_rpc = ["nearcore/rosetta_rpc"]
json_rpc = ["nearcore/json_rpc"]
protocol_feature_fix_staking_threshold = ["nearcore/protocol_feature_fix_staking_threshold"]
protocol_feature_flat_state = ["nearcore/protocol_feature_flat_state"]
protocol_feature_nep366_delegate_action = ["nearcore/protocol_feature_nep366_delegate_action"]
protocol_feature_nep366_delegate_action = [
"nearcore/protocol_feature_nep366_delegate_action",
"near-state-viewer/protocol_feature_nep366_delegate_action",
]

nightly = [
"nightly_protocol",
"nearcore/nightly"
"nearcore/nightly",
"near-state-viewer/nightly",
]
nightly_protocol = ["nearcore/nightly_protocol"]

Expand Down
4 changes: 3 additions & 1 deletion tools/state-viewer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ node-runtime = { path = "../../runtime/runtime" }
[dev-dependencies]
near-client = { path = "../../chain/client" }
testlib = { path = "../../test-utils/testlib" }
insta.workspace = true

[features]
sandbox = [
Expand All @@ -48,5 +49,6 @@ nightly = [
"nightly_protocol",
"nearcore/nightly"
]
nightly_protocol = ["nearcore/nightly_protocol"]
nightly_protocol = ["nearcore/nightly_protocol", "protocol_feature_nep366_delegate_action"]
protocol_feature_flat_state = ["nearcore/protocol_feature_flat_state"]
protocol_feature_nep366_delegate_action = ["nearcore/protocol_feature_nep366_delegate_action"]
55 changes: 55 additions & 0 deletions tools/state-viewer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,58 @@ gcloud beta compute ssh --zone "europe-west4-a" "<machine>" --project "rpc-prod

Check running instances at <https://console.cloud.google.com/compute/instances?project=rpc-prod> to see the machine
name and datacenter.

### contract-accounts

List account names with contracts deployed and additional information about the
contracts.

By default, the command only displays the names of all accounts that have a
contract deployed right now. This should be fairly quick. Using flags, you can
display more information but it will also slow down the process.

To see a list of flags, run
```ignore
cargo run -p neard -- view-state contract-accounts --help
```

#### Example

The following command lists all (but the skipped) accounts with contracts
deployed with the additional information of how many times the account has been
the receiver of a receipt and how many times a receipt was sent by the contract.
(Note: outgoing counts only sent by the contract, not anything where the signed
was the same account id.)

Additionally, the output contains a list of actions that were in the outgoing
receipts. This is particularly useful to find contracts that call certain
actions on-chain.

```ignore
cargo run -p neard -- view-state contract-accounts \
--skip-accounts "aurora,relay.aurora,token.sweat,oracle.sweat,tge-lockup.near,sweat_welcome.near" \
--receipts-in \
--receipts-out \
--actions \
> output.log
```

And the output may look something like thi:
```ignore
ACCOUNT_ID RCPTS_IN RCPTS_OUT ACTIONS
0-0.near 37 14 Transfer
0-1.near 797 117 Transfer
0.app.hipodev.near 8 9 Transfer
0.app2.hipodev.near 4 5 Transfer
0.near 56 9 Transfer
00.near 29 5 Transfer
000.near 190 17 Transfer
0000.mintbase1.near 49 68 FunctionCall,Transfer
...
And 18858 errors:
failed loading outgoing receipt DpoPSrAHECYrpntTdYXrp2W2Ad3yEPyMyCav4mXi8kyh
failed loading outgoing receipt BoSv67f3CYsWu9LfLxPNkKGSEnwiH7jwPzEmYwL5c7rm
...
failed loading outgoing receipt D4AEcD6umuJKGjSNA2JEZ4EMxn3GK4Z8Ew1iAQpWYtPS
failed loading outgoing receipt AAht3HUDJeGRJ1N776ZKJ2vRiRBAD9GtsLabgbrdioAC
```
7 changes: 4 additions & 3 deletions tools/state-viewer/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::commands::*;
use crate::contract_accounts::ContractAccountFilter;
use crate::rocksdb_stats::get_rocksdb_stats;
use crate::state_parts::{apply_state_parts, dump_state_parts};
use crate::{epoch_info, state_parts};
Expand Down Expand Up @@ -302,13 +303,13 @@ impl ChunksCmd {

#[derive(Parser)]
pub struct ContractAccountsCmd {
// TODO: add filter options, e.g. only contracts that execute certain
// actions
#[clap(flatten)]
filter: ContractAccountFilter,
}

impl ContractAccountsCmd {
pub fn run(self, home_dir: &Path, near_config: NearConfig, store: Store) {
contract_accounts(home_dir, store, near_config).unwrap();
contract_accounts(home_dir, store, near_config, self.filter).unwrap();
}
}

Expand Down
36 changes: 28 additions & 8 deletions tools/state-viewer/src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::apply_chain_range::apply_chain_range;
use crate::contract_accounts::ContractAccount;
use crate::contract_accounts::ContractAccountFilter;
use crate::contract_accounts::Summary;
use crate::state_dump::state_dump;
use crate::state_dump::state_dump_redis;
use crate::tx_dump::dump_tx_from_block;
Expand Down Expand Up @@ -852,11 +854,11 @@ pub(crate) fn contract_accounts(
home_dir: &Path,
store: Store,
near_config: NearConfig,
filter: ContractAccountFilter,
) -> anyhow::Result<()> {
let (_runtime, state_roots, _header) = load_trie(store.clone(), home_dir, &near_config);

for (shard_id, &state_root) in state_roots.iter().enumerate() {
eprintln!("Starting shard {shard_id}");
let tries = state_roots.iter().enumerate().map(|(shard_id, &state_root)| {
// TODO: This assumes simple nightshade layout, it will need an update when we reshard.
let shard_uid = ShardUId::from_shard_id_and_layout(
shard_id as u64,
Expand All @@ -866,14 +868,32 @@ pub(crate) fn contract_accounts(
let storage = TrieDBStorage::new(store.clone(), shard_uid);
// We don't need flat state to traverse all accounts.
let flat_state = None;
let trie = Trie::new(Box::new(storage), state_root, flat_state);

for contract in ContractAccount::in_trie(&trie)? {
match contract {
Ok(contract) => println!("{contract}"),
Err(err) => eprintln!("{err}"),
Trie::new(Box::new(storage), state_root, flat_state)
});

filter.write_header(&mut std::io::stdout().lock())?;
// Prefer streaming the results, to use less memory and provide
// a feedback more quickly.
if filter.can_stream() {
// Process account after account and display results immediately.
for (i, trie) in tries.enumerate() {
eprintln!("Starting shard {i}");
let trie_iter = ContractAccount::in_trie(trie, filter.clone())?;
for contract in trie_iter {
match contract {
Ok(contract) => println!("{contract}"),
Err(err) => eprintln!("{err}"),
}
}
}
} else {
// Load all results into memory, which allows advanced lookups but also
// means we have to wait for everything to complete before output can be
// shown.
let tries_iterator = ContractAccount::in_tries(tries.collect(), &filter)?;
let result = tries_iterator.summary(&store, &filter);
println!("{result}");
}

Ok(())
}
Loading