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

Forest tool migration of forest-cli subcommands #3336

Merged
merged 60 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
b429561
Move DB subcommands to forest-tool
elmattic Aug 7, 2023
f1e07e4
Add hidden commands to help users migrating
elmattic Aug 8, 2023
71b4230
Merge branch 'main' into elmattic/forest-tool-migration
elmattic Aug 8, 2023
175d282
Fix lints
elmattic Aug 8, 2023
f97f070
Migrate config subcommand
elmattic Aug 8, 2023
2368ca7
Migrate fetch-params subcommand
elmattic Aug 8, 2023
2f46a5f
Merge branch 'main' into elmattic/forest-tool-migration
elmattic Aug 8, 2023
ae61c2f
Move Config to commands that require it
elmattic Aug 8, 2023
d13a849
Migrate snapshot fetch and validate subcommands
elmattic Aug 8, 2023
c6df6ae
Remove string manipulations
elmattic Aug 8, 2023
355aa7e
Migrate archive info subcommand
elmattic Aug 8, 2023
11c0442
Remove all duplicated strings
elmattic Aug 8, 2023
cf05e60
Use dedicated flag
elmattic Aug 8, 2023
f4dbb70
Rename DB clean subcommand
elmattic Aug 9, 2023
2405b4e
Add optional config file
elmattic Aug 9, 2023
55ad715
Merge branch 'main' into elmattic/forest-tool-migration
elmattic Aug 9, 2023
5bacfc8
Update harness script
elmattic Aug 9, 2023
a986ee4
Update other scripts
elmattic Aug 9, 2023
7d87792
Use toml file instead
elmattic Aug 9, 2023
ce05ce0
Merge branch 'main' into elmattic/forest-tool-migration
elmattic Aug 9, 2023
45c9d95
Add back db clean and db stats subcommands
elmattic Aug 9, 2023
2b513be
Update calls to snapshot fetch
elmattic Aug 9, 2023
fa26642
Merge branch 'main' into elmattic/forest-tool-migration
elmattic Aug 9, 2023
5910f10
Remove forest-tool db subcommands (will be in a dedicated PR)
elmattic Aug 9, 2023
035f5d4
Remove second pattern
elmattic Aug 9, 2023
7cfb7b0
Update CHANGELOG
elmattic Aug 9, 2023
6ff4c34
Fix shell script
elmattic Aug 9, 2023
fb3b711
Fix: chain flag is not needed for snapshot validate
elmattic Aug 9, 2023
9eed518
Fix fetch params tests
elmattic Aug 9, 2023
7a8f494
Revert config dump subcommand (will be in its own PR)
elmattic Aug 9, 2023
048312b
Update CHANGELOG
elmattic Aug 9, 2023
4da33d3
Update src/cli/subcommands/archive_cmd.rs
elmattic Aug 10, 2023
71108f7
Update src/cli/subcommands/db_cmd.rs
elmattic Aug 10, 2023
76b23f3
Update src/cli/subcommands/mod.rs
elmattic Aug 10, 2023
23de902
Update src/cli/subcommands/mod.rs
elmattic Aug 10, 2023
2ec85e2
Revert useless change
elmattic Aug 10, 2023
27eb1be
Merge branch 'main' into elmattic/forest-tool-migration
elmattic Aug 10, 2023
bd3e904
Update src/tool/subcommands/mod.rs
elmattic Aug 10, 2023
db6e18c
Update src/cli/subcommands/snapshot_cmd.rs
elmattic Aug 10, 2023
eb91628
Maintain order
elmattic Aug 10, 2023
42aee09
Fix last commit
elmattic Aug 10, 2023
ae2202a
Merge branch 'main' into elmattic/forest-tool-migration
elmattic Aug 10, 2023
1ac60a2
Change ordering
elmattic Aug 10, 2023
9e1d992
Merge branch 'main' into elmattic/forest-tool-migration
elmattic Aug 10, 2023
2fce1a1
Formatting
elmattic Aug 10, 2023
3b9fd28
Update README
elmattic Aug 10, 2023
26153f0
Fix formatting
elmattic Aug 10, 2023
16b7c53
Update the release git workflow
elmattic Aug 10, 2023
ef95096
Update Dockerfile
elmattic Aug 10, 2023
ef5f8b1
Merge branch 'main' into elmattic/forest-tool-migration
elmattic Aug 11, 2023
7ebc868
`forest-cli db` subcommands migration (#3355)
elmattic Aug 11, 2023
13774ab
Merge branch 'main' into elmattic/forest-tool-migration
elmattic Aug 11, 2023
2e9df3e
Merge branch 'main' into elmattic/forest-tool-migration
elmattic Aug 11, 2023
16765af
Merge branch 'elmattic/forest-tool-migration' of https://github.com/C…
elmattic Aug 11, 2023
602f0de
Replace macro by a function
elmattic Aug 11, 2023
1ecce48
Add check to alpine Dockerfile
elmattic Aug 11, 2023
86076c1
Add tracking issue
elmattic Aug 11, 2023
bcf4861
Merge branch 'main' into elmattic/forest-tool-migration
elmattic Aug 14, 2023
92a7e5f
Merge branch 'main' into elmattic/forest-tool-migration
elmattic Aug 17, 2023
f73094b
Merge branch 'main' into elmattic/forest-tool-migration
elmattic Aug 18, 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
2 changes: 1 addition & 1 deletion .github/CHECKPOINT_ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Checkpoints have to be regularly updated, though, and this issue is automaticall
How to compute a new checkpoint for calibnet:

1. Install `forest-cli`
2. Download calibnet snapshot: `forest-cli --chain calibnet snapshot fetch`
2. Download calibnet snapshot: `forest-tool snapshot fetch --chain calibnet`
3. Decompress snapshot: `zstd -d forest_snapshot_calibnet_*.car.zst`
4. Extract checkpoints: `forest-cli archive checkpoints forest_snapshot_calibnet_*.car`
5. Put checkpoints in `build/known_blocks.yaml`
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,15 @@ jobs:
timeout-minutes: 5
continue-on-error: true
- name: Cargo Build
run: cargo build --release --bin forest --bin forest-cli
run: cargo build --release --bin forest --bin forest-cli --bin forest-tool
- name: Compress Binary
run: |
mkdir -p forest-${{ github.ref_name }}
cp -v target/release/forest target/release/forest-cli forest-${{ github.ref_name }}
cp -v target/release/forest target/release/forest-cli target/release/forest-tool forest-${{ github.ref_name }}
cp -rv CHANGELOG.md LICENSE-APACHE LICENSE-MIT README.md documentation forest-${{ github.ref_name }}
sha256sum forest-${{ github.ref_name }}/forest > forest-${{ github.ref_name }}/forest.sha256
sha256sum forest-${{ github.ref_name }}/forest-cli > forest-${{ github.ref_name }}/forest-cli.sha256
sha256sum forest-${{ github.ref_name }}/forest-tool > forest-${{ github.ref_name }}/forest-tool.sha256
zip -r ${{ matrix.file }} forest-${{ github.ref_name }}
- name: Upload Binary
uses: svenstaro/upload-release-action@v2
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@
internal settings from files to the database.
- [#3333](https://github.com/ChainSafe/forest/pull/3333) Changed default rpc
port from 1234 to 2345.
- [#3336](https://github.com/ChainSafe/forest/pull/3336) Moved following
`forest-cli` subcommands to `forest-tool`
- `archive info`
- `fetch-params`
- `snapshot fetch`
- `snapshot validate`

### Added

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,6 @@ USER ${SERVICE_USER}
WORKDIR /home/${SERVICE_USER}

# Basic verification of dynamically linked dependencies
RUN forest -V && forest-cli -V
RUN forest -V && forest-cli -V && forest-tool -V
elmattic marked this conversation as resolved.
Show resolved Hide resolved

ENTRYPOINT ["forest"]
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,17 @@ The admin token can also be set using `--token` flag.
forest-cli --token <ADMIN_TOKEN>
```

### Forest executable organization

The binaries in the Forest repository are organized into the following
categories:

| Binary | Role | Command example |
| ------------- | -------------------------------------------------------- | -------------------------------------------------- |
| `forest` | Forest daemon, used to connect to the Filecoin network | `forest --chain calibnet --encrypt-keystore false` |
| `forest-cli` | Human-friendly wrappers around the Filecoin JSON-RPC API | `forest-cli info show` |
| `forest-tool` | Handle tasks not involving the Forest daemon | `forest-tool snapshot fetch` |

### Detaching Forest process

You can detach Forest process via the `--detach` flag so that it runs in the
Expand Down
4 changes: 2 additions & 2 deletions scripts/gen_coverage_report.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ function cov {
cargo llvm-cov --workspace clean
cargo llvm-cov --workspace --no-report
cov forest-cli --chain calibnet db clean --force
cov forest-cli --chain calibnet snapshot fetch --aria2 --provider filecoin -s "$TMP_DIR"
cov forest-tool snapshot fetch --chain calibnet --vendor filops -s "$TMP_DIR"
SNAPSHOT_PATH=$(find "$TMP_DIR" -name \*.zst | head -n 1)
cov forest --chain calibnet --encrypt-keystore false --import-snapshot "$SNAPSHOT_PATH" --halt-after-import --height=-200 --track-peak-rss
cov forest-cli --chain calibnet db clean --force
cov forest-cli --chain calibnet snapshot fetch --aria2 -s "$TMP_DIR"
cov forest-tool snapshot fetch --chain calibnet -s "$TMP_DIR"
SNAPSHOT_PATH=$(find "$TMP_DIR" -name \*.car | head -n 1)
cov forest --chain calibnet --encrypt-keystore false --import-snapshot "$SNAPSHOT_PATH" --height=-200 --detach --track-peak-rss --save-token "$TOKEN_PATH"
cov forest-cli sync wait
Expand Down
2 changes: 1 addition & 1 deletion scripts/tests/calibnet_export_check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ echo "Validating CAR files"
zstd --decompress ./*.car.zst
for f in *.car; do
echo "Validating CAR file $f"
$FOREST_CLI_PATH --chain calibnet snapshot validate "$f"
$FOREST_TOOL_PATH snapshot validate "$f"
done
14 changes: 7 additions & 7 deletions scripts/tests/forest_cli_check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function num-files-here() {
| wc --lines
}

"$FOREST_CLI_PATH" fetch-params --keys
"$FOREST_TOOL_PATH" fetch-params --keys

: "cleaning an empty database doesn't fail (see #2811)"
"$FOREST_CLI_PATH" --chain calibnet db clean --force
Expand All @@ -28,8 +28,8 @@ function num-files-here() {

: fetch snapshot
pushd "$(mktemp --directory)"
"$FOREST_CLI_PATH" --chain calibnet snapshot fetch --vendor forest
"$FOREST_CLI_PATH" --chain calibnet snapshot fetch --vendor filops
"$FOREST_TOOL_PATH" snapshot fetch --chain calibnet --vendor forest
"$FOREST_TOOL_PATH" snapshot fetch --chain calibnet --vendor filops
# this will fail if they happen to have the same height - we should change the format of our filenames
test "$(num-files-here)" -eq 2

Expand Down Expand Up @@ -59,7 +59,7 @@ pushd "$(mktemp --directory)"
#assert_eq "$DIFF_STATE_ROOTS" 1100

: Validate the union of a snapshot and a diff
"$FOREST_CLI_PATH" snapshot validate --check-network calibnet base_snapshot.forest.car.zst diff_snapshot.forest.car.zst
"$FOREST_TOOL_PATH" snapshot validate --check-network calibnet base_snapshot.forest.car.zst diff_snapshot.forest.car.zst
rm -- *
popd

Expand All @@ -68,7 +68,7 @@ popd
: validate latest calibnet snapshot
pushd "$(mktemp --directory)"
: : fetch a compressed calibnet snapshot
"$FOREST_CLI_PATH" --chain calibnet snapshot fetch
"$FOREST_TOOL_PATH" snapshot fetch --chain calibnet
test "$(num-files-here)" -eq 1
uncompress_me=$(find . -type f | head -1)

Expand All @@ -77,10 +77,10 @@ pushd "$(mktemp --directory)"

validate_me=$(find . -type f | head -1)
: : validating under calibnet chain should succeed
"$FOREST_CLI_PATH" snapshot validate --check-network calibnet "$validate_me"
"$FOREST_TOOL_PATH" snapshot validate --check-network calibnet "$validate_me"

: : validating under mainnet chain should fail
if "$FOREST_CLI_PATH" snapshot validate --check-network mainnet "$validate_me"; then
if "$FOREST_TOOL_PATH" snapshot validate --check-network mainnet "$validate_me"; then
exit 1
fi

Expand Down
7 changes: 4 additions & 3 deletions scripts/tests/harness.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

FOREST_PATH="forest"
FOREST_CLI_PATH="forest-cli"
FOREST_TOOL_PATH="forest-tool"

TMP_DIR=$(mktemp --directory)
LOG_DIRECTORY=$TMP_DIR/logs
Expand All @@ -23,15 +24,15 @@ function forest_check_db_stats {
}

function forest_query_epoch {
$FOREST_CLI_PATH archive info "$1" | grep Epoch | awk '{print $2}'
$FOREST_TOOL_PATH archive info "$1" | grep Epoch | awk '{print $2}'
}

function forest_query_state_roots {
$FOREST_CLI_PATH archive info "$1" | grep State-roots | awk '{print $2}'
$FOREST_TOOL_PATH archive info "$1" | grep State-roots | awk '{print $2}'
}

function forest_query_format {
$FOREST_CLI_PATH archive info "$1" | grep "CAR format" | awk '{print $3}'
$FOREST_TOOL_PATH archive info "$1" | grep "CAR format" | awk '{print $3}'
}

function forest_run_node_detached {
Expand Down
12 changes: 11 additions & 1 deletion src/cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ use clap::Parser;

use super::subcommands::Subcommand;

#[macro_export]
elmattic marked this conversation as resolved.
Show resolved Hide resolved
macro_rules! bail_moved_cmd {
($src:literal) => {
anyhow::bail!(
"Invalid subcommand: {}. It has been moved to forest-tool binary.",
$src
)
};
}

pub fn main<ArgT>(args: impl IntoIterator<Item = ArgT>) -> anyhow::Result<()>
where
ArgT: Into<OsString> + Clone,
Expand Down Expand Up @@ -50,7 +60,7 @@ where
}
// Run command
match cmd {
elmattic marked this conversation as resolved.
Show resolved Hide resolved
Subcommand::Fetch(cmd) => cmd.run(config).await,
Subcommand::Fetch(_cmd) => bail_moved_cmd!("fetch-params"),
Subcommand::Chain(cmd) => cmd.run(config).await,
Subcommand::Auth(cmd) => cmd.run(config).await,
Subcommand::Net(cmd) => cmd.run(config).await,
Expand Down
146 changes: 6 additions & 140 deletions src/cli/subcommands/archive_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use crate::chain::{
ChainEpochDelta,
};
use crate::cli_shared::{snapshot, snapshot::TrustedVendor};
use crate::db::car::{AnyCar, ManyCar, RandomAccessFileReader};
use crate::db::car::ManyCar;
use crate::ipld::{stream_graph, CidHashSet};
use crate::networks::{calibnet, mainnet, ChainConfig, NetworkChain};
use crate::shim::clock::{ChainEpoch, EPOCHS_IN_DAY, EPOCH_DURATION_SECONDS};
Expand All @@ -41,20 +41,16 @@ use chrono::NaiveDateTime;
use clap::Subcommand;
use futures::TryStreamExt;
use fvm_ipld_blockstore::Blockstore;
use indicatif::ProgressIterator;
use itertools::Itertools;
use sha2::Sha256;
use std::path::PathBuf;
use std::sync::Arc;
use tracing::info;

#[derive(Debug, Subcommand)]
pub enum ArchiveCommands {
/// Show basic information about an archive.
Info {
/// Path to an uncompressed archive (CAR)
snapshot: PathBuf,
},
// This subcommand is hidden and only here to help users migrating to forest-tool
#[command(hide = true)]
Info { snapshot: PathBuf },
/// Trim a snapshot of the chain and write it to `<output_path>`
Export {
/// Snapshot input path. Currently supports only `.car` file format.
Expand Down Expand Up @@ -90,10 +86,7 @@ pub enum ArchiveCommands {
impl ArchiveCommands {
pub async fn run(self) -> anyhow::Result<()> {
match self {
Self::Info { snapshot } => {
elmattic marked this conversation as resolved.
Show resolved Hide resolved
println!("{}", ArchiveInfo::from_store(AnyCar::try_from(snapshot)?)?);
Ok(())
}
Self::Info { .. } => crate::bail_moved_cmd!("archive info"),
Self::Export {
snapshot_files,
output_path,
Expand Down Expand Up @@ -233,112 +226,6 @@ async fn do_export(
Ok(())
}

#[derive(Debug)]
struct ArchiveInfo {
variant: String,
network: String,
epoch: ChainEpoch,
tipsets: ChainEpoch,
messages: ChainEpoch,
}

impl std::fmt::Display for ArchiveInfo {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(f, "CAR format: {}", self.variant)?;
writeln!(f, "Network: {}", self.network)?;
writeln!(f, "Epoch: {}", self.epoch)?;
writeln!(f, "State-roots: {}", self.epoch - self.tipsets + 1)?;
write!(f, "Messages sets: {}", self.epoch - self.messages + 1)?;
Ok(())
}
}

impl ArchiveInfo {
// Scan a CAR archive to identify which network it belongs to and how many
// tipsets/messages are available. Progress is rendered to stdout.
fn from_store(store: AnyCar<impl RandomAccessFileReader>) -> anyhow::Result<Self> {
Self::from_store_with(store, true)
}

// Scan a CAR archive to identify which network it belongs to and how many
// tipsets/messages are available. Progress is optionally rendered to
// stdout.
fn from_store_with(
store: AnyCar<impl RandomAccessFileReader>,
progress: bool,
) -> anyhow::Result<Self> {
let root = store.heaviest_tipset()?;
let root_epoch = root.epoch();

let tipsets = root.clone().chain(&store);

let windowed = (std::iter::once(root).chain(tipsets)).tuple_windows();

let mut network: String = "unknown".into();
let mut lowest_stateroot_epoch = root_epoch;
let mut lowest_message_epoch = root_epoch;

let iter = if progress {
itertools::Either::Left(windowed.progress_count(root_epoch as u64))
} else {
itertools::Either::Right(windowed)
};

for (parent, tipset) in iter {
if tipset.epoch() >= parent.epoch() && parent.epoch() != root_epoch {
bail!("Broken invariant: non-sequential epochs");
}

if tipset.epoch() < 0 {
bail!("Broken invariant: tipset with negative epoch");
}

// Update the lowest-stateroot-epoch only if our parent also has a
// state-root. The genesis state-root is usually available but we're
// not interested in that.
if lowest_stateroot_epoch == parent.epoch() && store.has(tipset.parent_state())? {
lowest_stateroot_epoch = tipset.epoch();
}
if lowest_message_epoch == parent.epoch()
&& store.has(tipset.min_ticket_block().messages())?
{
lowest_message_epoch = tipset.epoch();
}

if tipset.epoch() == 0 {
if tipset.min_ticket_block().cid() == &*calibnet::GENESIS_CID {
network = "calibnet".into();
} else if tipset.min_ticket_block().cid() == &*mainnet::GENESIS_CID {
network = "mainnet".into();
}
}

// If we've already found the lowest-stateroot-epoch and
// lowest-message-epoch then we can skip scanning the rest of the
// archive when we find a checkpoint.
let may_skip =
lowest_stateroot_epoch != tipset.epoch() && lowest_message_epoch != tipset.epoch();
if may_skip {
let genesis_block = tipset.genesis(&store)?;
if genesis_block.cid() == &*calibnet::GENESIS_CID {
network = "calibnet".into();
} else if genesis_block.cid() == &*mainnet::GENESIS_CID {
network = "mainnet".into();
}
break;
}
}

Ok(ArchiveInfo {
variant: store.variant().to_string(),
network,
epoch: root_epoch,
tipsets: lowest_stateroot_epoch,
messages: lowest_message_epoch,
})
}
}

// Print a mapping of epochs to block headers in yaml format. This mapping can
// be used by Forest to quickly identify tipsets.
fn print_checkpoints(snapshot_files: Vec<PathBuf>) -> anyhow::Result<()> {
Expand Down Expand Up @@ -380,34 +267,13 @@ fn list_checkpoints(
#[cfg(test)]
mod tests {
use super::*;
use crate::db::car::AnyCar;
use async_compression::tokio::bufread::ZstdDecoder;
use fvm_ipld_car::CarReader;
use tempfile::TempDir;
use tokio::io::BufReader;
use tokio_util::compat::TokioAsyncReadCompatExt;

#[test]
fn archive_info_calibnet() {
let info = ArchiveInfo::from_store_with(
AnyCar::try_from(calibnet::DEFAULT_GENESIS).unwrap(),
false,
)
.unwrap();
assert_eq!(info.network, "calibnet");
assert_eq!(info.epoch, 0);
}

#[test]
fn archive_info_mainnet() {
let info = ArchiveInfo::from_store_with(
AnyCar::try_from(mainnet::DEFAULT_GENESIS).unwrap(),
false,
)
.unwrap();
assert_eq!(info.network, "mainnet");
assert_eq!(info.epoch, 0);
}

fn genesis_timestamp(genesis_car: &'static [u8]) -> u64 {
let db = crate::db::car::PlainCar::try_from(genesis_car).unwrap();
let ts = db.heaviest_tipset().unwrap();
Expand Down
Loading