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

pmonitor: create client activity monitor #4844

Merged
merged 32 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
48a1ea8
pmonitor: add account activity tool
redshiftzero Aug 29, 2024
ba5a26a
pmonitor: add command-line args and parse FVKs
redshiftzero Aug 29, 2024
9a3916b
pmonitor: set up directory structures per FVK, add reset cmd
redshiftzero Aug 29, 2024
95901ee
pmonitor: sync all wallets to latest block height
redshiftzero Aug 29, 2024
a4a9738
pmonitor: add one-off syncing of genesis block w/a vec of FVKs
redshiftzero Sep 4, 2024
e12167a
pmonitor: compute the UM-equivalent balance per FVK in genesis block
redshiftzero Sep 6, 2024
f0c8ffa
pmonitor: take allocations from genesis data
redshiftzero Sep 11, 2024
e6236e5
pmonitor: get current UM-equivalent balances
redshiftzero Sep 13, 2024
43300d1
pmonitor: print friendly message to users
redshiftzero Sep 13, 2024
49a1695
pmonitor: steal sync logic from pcli
redshiftzero Sep 13, 2024
28d69d6
pmonitor: do trial decryption in genesis block
redshiftzero Sep 15, 2024
a9133e1
pmonitor: get current `RateData`
redshiftzero Sep 16, 2024
2d69612
view: store decryption memo text
redshiftzero Sep 16, 2024
db5bcb7
pmonitor: check if account has been migrated
redshiftzero Sep 16, 2024
e4200b9
pmonitor: add `PmonitorConfig` so we can track migrations
redshiftzero Sep 19, 2024
967bccb
pmonitor: refactor audit command to use `PmonitorConfig`
redshiftzero Sep 19, 2024
80c53ec
pmonitor: refactor wallet creation into method
redshiftzero Sep 19, 2024
99146de
pmonitor: move genesis balance computation into init
redshiftzero Sep 20, 2024
5476994
pmonitor: support balance tracking for migrated accounts
redshiftzero Sep 20, 2024
8b4abae
pmonitor: allow FVKs to be included that do not have genesis allocs
redshiftzero Sep 24, 2024
e507f14
pmonitor: more friendly messages during init
redshiftzero Sep 24, 2024
49e7b27
feat: add tracing log messages for pmonitor
conorsch Sep 27, 2024
b9ae61f
fix: sql memo query
conorsch Sep 27, 2024
a4564fc
fix: use VIEW_FILE_NAME const in pmonitor
conorsch Sep 27, 2024
2f773c2
fix: don't shell out to pcli for wallet init
conorsch Oct 4, 2024
cc53601
fix: allow small deviation from genesis, for fees
conorsch Oct 4, 2024
953e481
fix: reuse view client in pmonitor
conorsch Oct 8, 2024
b456565
feat: summary message for pmonitor
conorsch Oct 8, 2024
a2dd5c4
feat: better error handling on pmonitor config dir
conorsch Oct 10, 2024
181bc27
ci: add integration tests for pmonitor
conorsch Sep 25, 2024
eec9d25
docs: add module documentation for pmonitor
conorsch Oct 11, 2024
276a48e
fix: convert remaining println statements to logging
conorsch Oct 17, 2024
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
34 changes: 31 additions & 3 deletions .github/workflows/smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ on:
paths-ignore:
- 'docs/**'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
smoke_test:
runs-on: buildjet-16vcpu-ubuntu-2204
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
environment: smoke-test
steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -39,3 +40,30 @@ jobs:
- name: Display smoke-test logs
if: always()
run: cat deployments/logs/smoke-*.log

pmonitor-integration:
runs-on: buildjet-16vcpu-ubuntu-2204
steps:
- uses: actions/checkout@v4
with:
lfs: true

- name: install nix
uses: nixbuild/nix-quick-install-action@v28

- name: setup nix cache
uses: nix-community/cache-nix-action@v5
with:
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix') }}
restore-prefixes-first-match: nix-${{ runner.os }}-
backend: buildjet

- name: Load rust cache
uses: astriaorg/[email protected]

# Confirm that the nix devshell is buildable and runs at all.
- name: validate nix env
run: nix develop --command echo hello

- name: run the pmonitor integration tests
run: nix develop --command just test-pmonitor
38 changes: 38 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ members = [
"crates/bin/pclientd",
"crates/bin/pd",
"crates/bin/pindexer",
"crates/bin/pmonitor",
"crates/cnidarium",
"crates/cnidarium-component",
"crates/core/app",
Expand Down
2 changes: 1 addition & 1 deletion crates/bin/pcli/src/opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ impl Opt {
tracing::info!(%path, "using local view service");

let registry_path = self.home.join("registry.json");
// Check if the path exists or set it to nojne
// Check if the path exists or set it to none
let registry_path = if registry_path.exists() {
Some(registry_path)
} else {
Expand Down
46 changes: 46 additions & 0 deletions crates/bin/pmonitor/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
[package]
name = "pmonitor"
version = { workspace = true }
authors = { workspace = true }
edition = { workspace = true }
repository = { workspace = true }
homepage = { workspace = true }
license = { workspace = true }
publish = false

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = {workspace = true}
camino = {workspace = true}
clap = {workspace = true, features = ["derive", "env"]}
colored = "2.1.0"
directories = {workspace = true}
futures = {workspace = true}
indicatif = {workspace = true}
pcli = {path = "../pcli", default-features = true}
penumbra-app = {workspace = true}
penumbra-asset = {workspace = true, default-features = false}
penumbra-compact-block = {workspace = true, default-features = false}
penumbra-keys = {workspace = true, default-features = false}
penumbra-num = {workspace = true, default-features = false}
penumbra-proto = {workspace = true}
penumbra-shielded-pool = {workspace = true, default-features = false}
penumbra-stake = {workspace = true, default-features = false}
penumbra-tct = {workspace = true, default-features = false}
penumbra-view = {workspace = true}
regex = {workspace = true}
serde = {workspace = true, features = ["derive"]}
serde_json = {workspace = true}
tokio = {workspace = true, features = ["full"]}
toml = {workspace = true}
tonic = {workspace = true, features = ["tls-webpki-roots", "tls"]}
tracing = {workspace = true}
tracing-subscriber = { workspace = true, features = ["env-filter", "ansi"] }
url = {workspace = true, features = ["serde"]}
uuid = { version = "1.3", features = ["v4", "serde"] }

[dev-dependencies]
assert_cmd = {workspace = true}
once_cell = {workspace = true}
tempfile = {workspace = true}
118 changes: 118 additions & 0 deletions crates/bin/pmonitor/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//! Logic for reading and writing config files for `pmonitor`, in the TOML format.
use anyhow::Result;
use regex::Regex;
use serde::{Deserialize, Serialize};
use url::Url;
use uuid::Uuid;

use penumbra_keys::FullViewingKey;
use penumbra_num::Amount;

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FvkEntry {
pub fvk: FullViewingKey,
pub wallet_id: Uuid,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
/// Representation of a single Penumbra wallet to track.
pub struct AccountConfig {
/// The initial [FullViewingKey] has specified during `pmonitor init`.
///
/// Distinct because the tool understands account migrations.
original: FvkEntry,
/// The amount held by the account at the time of genesis.
genesis_balance: Amount,
/// List of account migrations, performed via `pcli migrate balance`, if any.
migrations: Vec<FvkEntry>,
}

impl AccountConfig {
pub fn new(original: FvkEntry, genesis_balance: Amount) -> Self {
Self {
original,
genesis_balance,
migrations: vec![],
}
}

/// Get original/genesis FVK.
pub fn original_fvk(&self) -> FullViewingKey {
self.original.fvk.clone()
}

/// Get genesis balance.
pub fn genesis_balance(&self) -> Amount {
self.genesis_balance
}

/// Add migration to the account config.
pub fn add_migration(&mut self, fvk_entry: FvkEntry) {
self.migrations.push(fvk_entry);
}

/// Get the active wallet, which is the last migration or the original FVK if no migrations have occurred.
pub fn active_wallet(&self) -> FvkEntry {
if self.migrations.is_empty() {
self.original.clone()
} else {
self.migrations
.last()
.expect("migrations must not be empty")
.clone()
}
}

pub fn active_fvk(&self) -> FullViewingKey {
self.active_wallet().fvk
}

pub fn active_uuid(&self) -> Uuid {
self.active_wallet().wallet_id
}
}

#[derive(Clone, Debug, Serialize, Deserialize)]
/// The primary TOML file for configuring `pmonitor`, containing all its account info.
///
/// During `pmonitor audit` runs, the config will be automatically updated
/// if tracked FVKs were detected to migrate, via `pcli migrate balance`, to save time
/// on future syncs.
pub struct PmonitorConfig {
/// The gRPC URL for a Penumbra node's `pd` endpoint, used for retrieving account activity.
grpc_url: Url,
/// The list of Penumbra wallets to track.
accounts: Vec<AccountConfig>,
}

impl PmonitorConfig {
pub fn new(grpc_url: Url, accounts: Vec<AccountConfig>) -> Self {
Self { grpc_url, accounts }
}

pub fn grpc_url(&self) -> Url {
self.grpc_url.clone()
}

pub fn accounts(&self) -> &Vec<AccountConfig> {
&self.accounts
}

pub fn set_account(&mut self, index: usize, account: AccountConfig) {
self.accounts[index] = account;
}
}

/// Get the destination FVK from a migration memo.
pub fn parse_dest_fvk_from_memo(memo: &str) -> Result<FullViewingKey> {
let re = Regex::new(r"Migrating balance from .+ to (.+)")?;
if let Some(captures) = re.captures(memo) {
if let Some(dest_fvk_str) = captures.get(1) {
return dest_fvk_str
.as_str()
.parse::<FullViewingKey>()
.map_err(|_| anyhow::anyhow!("Invalid destination FVK in memo"));
}
}
Err(anyhow::anyhow!("Could not parse destination FVK from memo"))
}
Loading
Loading