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: offline server devnet #4638

Merged
merged 1 commit into from
Aug 14, 2024
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
snapshots instead of moving or copying them. This can be invoked with
`--import-snapshot <path> --import-mode=symlink`.

- [#4628](https://github.com/ChainSafe/forest/issues/4628) Added support for
devnets (2k networks) in the offline Forest.

### Changed

- [#4583](https://github.com/ChainSafe/forest/pull/4583) Removed the expiration
Expand Down
1 change: 1 addition & 0 deletions documentation/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- [Troubleshooting](./trouble_shooting.md)
- [Glossary](./glossary.md)
- [Bootstrap node](./bootstrap_node.md)
- [Offline Forest](./offline-forest.md)

# Developer documentation

Expand Down
121 changes: 121 additions & 0 deletions documentation/src/offline-forest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Offline Forest

Forest offers an offline mode, allowing using the snapshot as the source of
chain data and not connecting to any peers. This is useful for querying the
chain's archive state without syncing, and various testing scenarios.

## Usage

```bash
❯ forest-tool api serve --help
Usage: forest-tool api serve [OPTIONS] [SNAPSHOT_FILES]...

Arguments:
[SNAPSHOT_FILES]... Snapshot input paths. Supports `.car`, `.car.zst`, and `.forest.car.zst`

Options:
--chain <CHAIN> Filecoin network chain [default: mainnet]
--port <PORT> [default: 2345]
--auto-download-snapshot
--height <HEIGHT> Validate snapshot at given EPOCH, use a negative value -N to validate the last N EPOCH(s) starting at HEAD [default: -50]
--genesis <GENESIS> Genesis file path, only applicable for devnet
-h, --help Print help
```

## Example: serving a calibnet snapshot

The following command will start an offline server using the latest available
snapshot, which will be downloaded automatically. The server will listen on the
default port and act as a calibnet node _stuck_ at the latest snapshot's height.

```bash
forest-tool api serve --chain calibnet --auto-download-snapshot
```

## Example: serving a custom snapshot on calibnet

The following command will start an offline server using a custom snapshot,
which will be loaded from the provided path. The server will listen on the
default port and act as a calibnet node _stuck_ at the snapshot's
height: 1859736.

```bash
❯ forest-tool api serve --chain calibnet ~/Downloads/forest_snapshot_calibnet_2024-08-08_height_1859736.forest.car.zst
2024-08-12T12:29:16.624698Z INFO forest_filecoin::tool::offline_server::server: Configuring Offline RPC Server
2024-08-12T12:29:16.640402Z INFO forest_filecoin::tool::offline_server::server: Using chain config for calibnet
2024-08-12T12:29:16.641654Z INFO forest_filecoin::genesis: Initialized genesis: bafy2bzacecyaggy24wol5ruvs6qm73gjibs2l2iyhcqmvi7r7a4ph7zx3yqd4
2024-08-12T12:29:16.643263Z INFO forest_filecoin::daemon::db_util: Populating column EthMappings from range: [322354, 1859736]
...
2024-08-12T12:29:44.218675Z INFO forest_filecoin::tool::offline_server::server: Starting offline RPC Server
2024-08-12T12:29:44.218804Z INFO forest_filecoin::rpc: Ready for RPC connections
```

The server can then be queried using `forest-cli` or raw requests.

```bash
❯ curl --silent -X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","id":2,"method":"Filecoin.ChainHead","param":"null"}' \
"http://127.0.0.1:2345/rpc/v0" | jq
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"Cids": [
{
"/": "bafy2bzaceafill2bfzjwfq7o5x5idqe2odrriz5n4pup5xfrbsdrzjsa6mspk"
}
],
"Blocks": [
{
...
}
],
"Height": 1859736
}
}
```

## Example: usage on a devnet

The devnet case is a bit more complex, as the genesis file and the network name
need to be provided. If no snapshots are provided, the server will start at the
genesis block.

```bash
forest-tool api serve --chain localnet-55c7758d-c91a-41eb-94a2-718cb4601bc5 --genesis /lotus_data/devgen.car
```

The server can be later queried:

```bash
❯ curl --silent -X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","id":2,"method":"Filecoin.StateGetNetworkParams","param":"null"}' \
"http://127.0.0.1:2345/rpc/v0" | jq
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"NetworkName": "localnet-55c7758d-c91a-41eb-94a2-718cb4601bc5",
"BlockDelaySecs": 4,
"ConsensusMinerMinPower": "2040",
"SupportedProofTypes": [
0,
1
],
"PreCommitChallengeDelay": 10,
"ForkUpgradeParams": {
"UpgradeSmokeHeight": -2,
...
"UpgradeWaffleHeight": 18
},
"Eip155ChainID": 31415926
}
}
```

Note that the network name will vary depending on the genesis file used.

⚠️ The offline server is unable to append blocks to the chain at the moment of
writing. See [#4598](https://github.com/ChainSafe/forest/issues/4598) for
details and updates. This means that starting the server only with a genesis
file won't be very useful, as the chain will be stuck at the genesis block.
1 change: 1 addition & 0 deletions scripts/devnet/.env
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ LOTUS_RPC_PORT=1234
LOTUS_P2P_PORT=1235
MINER_RPC_PORT=2345
FOREST_RPC_PORT=3456
FOREST_OFFLINE_RPC_PORT=3457
GENESIS_NETWORK_VERSION=18
SHARK_HEIGHT=-10
HYGGE_HEIGHT=-9
Expand Down
12 changes: 10 additions & 2 deletions scripts/devnet/check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ source .env
# Forest check - assert that we sync past the genesis block.
# Allow for 300 seconds of sync time.
function get_sync_height {
local port=$1
curl --silent -X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","id":2,"method":"Filecoin.ChainHead","param":"null"}' \
"http://127.0.0.1:${FOREST_RPC_PORT}/rpc/v0" | jq '.result.Height'
"http://127.0.0.1:${port}/rpc/v0" | jq '.result.Height'
}

start_time=$(date +%s)
Expand All @@ -24,7 +25,7 @@ timeout=$((start_time + 300)) # Set timeout to 10 minutes
target_height=$TARGET_HEIGHT

while true; do
height=$(get_sync_height)
height=$(get_sync_height ${FOREST_RPC_PORT})
if [ "$height" -gt "$target_height" ]; then
echo "Height is larger than $target_height: $height"
break
Expand All @@ -38,3 +39,10 @@ while true; do

sleep 1
done

# Check the offline RPC, which should be initialized at that point. It should be at the genesis height, so 0.
height=$(get_sync_height ${FOREST_OFFLINE_RPC_PORT})
LesnyRumcajs marked this conversation as resolved.
Show resolved Hide resolved
if [ "$height" -ne 0 ]; then
echo "Offline RPC height is not zero: $height"
exit 1
fi
48 changes: 48 additions & 0 deletions scripts/devnet/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# - Lotus node (2k build),
# - Lotus miner (2k build),
# - Forest node.
# - Forest offline node.

services:
# Basic devnet initialisation. This will populate Lotus data volume with necessary artifacts
Expand Down Expand Up @@ -205,6 +206,53 @@ services:
fi
forest --genesis ${LOTUS_DATA_DIR}/devgen.car --config ${FOREST_DATA_DIR}/forest_config.toml --save-token ${FOREST_DATA_DIR}/token.jwt --rpc-address 0.0.0.0:${FOREST_RPC_PORT}

forest_offline:
user: root
depends_on:
lotus_config:
condition: service_completed_successfully
build:
context: ../../.
dockerfile: ${FOREST_DOCKERFILE_OVERRIDE:-Dockerfile}
container_name: forest-offline
healthcheck:
test: |
export FULLNODE_API_INFO=/ip4/127.0.0.1/tcp/${FOREST_OFFLINE_RPC_PORT}/http
forest-cli sync status || exit 1
interval: 10s
retries: 10
timeout: 5s
start_period: 15s
volumes:
- lotus-data:${LOTUS_DATA_DIR}
- filecoin-proofs:${FIL_PROOFS_PARAMETER_CACHE}
- forest-data:${FOREST_DATA_DIR}
environment:
- FIL_PROOFS_PARAMETER_CACHE=${FIL_PROOFS_PARAMETER_CACHE}
- RUST_LOG=info,forest_filecoin::blocks::header=trace
- FOREST_GENESIS_NETWORK_VERSION=${GENESIS_NETWORK_VERSION}
- FOREST_SHARK_HEIGHT=${SHARK_HEIGHT}
- FOREST_HYGGE_HEIGHT=${HYGGE_HEIGHT}
- FOREST_LIGHTNING_HEIGHT=${LIGHTNING_HEIGHT}
- FOREST_THUNDER_HEIGHT=${THUNDER_HEIGHT}
- FOREST_WATERMELON_HEIGHT=${WATERMELON_HEIGHT}
- FOREST_DRAGON_HEIGHT=${DRAGON_HEIGHT}
- FOREST_DRAND_QUICKNET_HEIGHT=${DRAND_QUICKNET_HEIGHT}
- FOREST_WAFFLE_HEIGHT=${WAFFLE_HEIGHT}
networks:
- devnet
ports:
- ${FOREST_OFFLINE_RPC_PORT}:${FOREST_OFFLINE_RPC_PORT}
entrypoint: ["/bin/bash", "-c" ]
command:
- |
set -euxo pipefail
if [ ! -f ${FOREST_DATA_DIR}/network_name ]; then
grep -o \"localnet.*\" ${LOTUS_DATA_DIR}/localnet.json | tr -d '"' | tee ${FOREST_DATA_DIR}/network_name
fi
NETWORK_NAME=$$(cat ${FOREST_DATA_DIR}/network_name)
forest-tool api serve --chain $$NETWORK_NAME --genesis ${LOTUS_DATA_DIR}/devgen.car --port ${FOREST_OFFLINE_RPC_PORT}

# At the moment of writing, Forest was not able to connect to a devnet node using its config.
# This is a workaround to force the connection.
forest_connecter:
Expand Down
2 changes: 1 addition & 1 deletion src/cli_shared/cli/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl Default for BufferSize {
#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
pub struct Client {
pub data_dir: PathBuf,
pub genesis_file: Option<String>,
pub genesis_file: Option<PathBuf>,
pub enable_rpc: bool,
pub enable_metrics_endpoint: bool,
pub enable_health_check: bool,
Expand Down
2 changes: 1 addition & 1 deletion src/cli_shared/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub struct CliOpts {
pub config: Option<PathBuf>,
/// The genesis CAR file
#[arg(long)]
pub genesis: Option<String>,
pub genesis: Option<PathBuf>,
/// Allow RPC to be active or not (default: true)
#[arg(long)]
pub rpc: Option<bool>,
Expand Down
2 changes: 1 addition & 1 deletion src/daemon/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ pub(super) async fn start(
// * When snapshot command implemented, this genesis does not need to be
// initialized
let genesis_header = read_genesis_header(
config.client.genesis_file.as_ref(),
config.client.genesis_file.as_deref(),
chain_config.genesis_bytes(&db).await?.as_deref(),
&db,
)
Expand Down
2 changes: 1 addition & 1 deletion src/db/migration/v0_19_0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ async fn create_state_manager_and_populate(config: Config, db_name: String) -> a
let chain_config = Arc::new(ChainConfig::from_chain(&config.chain));

let genesis_header = read_genesis_header(
config.client.genesis_file.as_ref(),
config.client.genesis_file.as_deref(),
chain_config.genesis_bytes(&db).await?.as_deref(),
&db,
)
Expand Down
4 changes: 3 additions & 1 deletion src/genesis/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright 2019-2024 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use std::path::Path;

use crate::blocks::CachingBlockHeader;
use crate::state_manager::StateManager;
use crate::utils::db::car_util::load_car;
Expand All @@ -15,7 +17,7 @@ pub const EXPORT_SR_40: &[u8] = std::include_bytes!("export40.car");
/// Uses an optional file path or the default genesis to parse the genesis and
/// determine if chain store has existing data for the given genesis.
pub async fn read_genesis_header<DB>(
genesis_fp: Option<&String>,
genesis_fp: Option<&Path>,
genesis_bytes: Option<&[u8]>,
db: &DB,
) -> Result<CachingBlockHeader, anyhow::Error>
Expand Down
Loading
Loading