Skip to content

Commit

Permalink
feat(state-viewer): list actions invoked by each contract (near#8483)
Browse files Browse the repository at this point in the history
List account names with contracts deployed and additional 
information about the contracts.

See the state-viewer README for docs and examples.

This has been used to produce reports in near#8427
  • Loading branch information
jakmeier authored and nikurt committed Feb 13, 2023
1 parent 5231148 commit 2c030cb
Show file tree
Hide file tree
Showing 8 changed files with 736 additions and 64 deletions.
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

0 comments on commit 2c030cb

Please sign in to comment.