Skip to content

Commit dc72732

Browse files
Alex6323gdanezissadhansoodmskd12Thoralf-M
authored
feat(iota-light-client)!: extend light client to use checkpoint archive (#6135)
# Description of change Porting: ~~MystenLabs/sui#19028 has already been merged to `develop` separatedly, but this PR started earlier and also included it. MystenLabs/sui#19604 MystenLabs/sui#20786 This adds the following features: [x] better crate structure [x] syncing from an archive store [x] syncing from a checkpoint store The following changes and features were added on top: [x] syncing from full node only (useful for local networks and non-pruning full nodes and as a last resort fallback) [x] automatic setup including downloading the genesis.blob [x] GraphQL query through `iota-graphql-rpc-client` instead of bare metal `reqwest` as in upstream [x] The old testdata has been removed and replaced and stored in a `fixtures` directory [x] the light client CLI resides in `main.rs` again, because it's more consistent with other binaries [x] improved error handling: user-facing panics have been replaced by error messages [x] improved configuration of the light-client { automatic sync, automatic genesis.blob download } [x] switched to new historical checkpoint endpoint for devnet `checkpoints.devnet.iota.cafe` [x] fixed a lot minor upstream issues [x] no upfront setup necessary to use the light client, which is different upstream ## Links to any relevant issues Partly fixes #5368 . In particular adds support for syncing the light client from a checkpoint archive, e.g. S3 or local directory, a remote checkpoint summary object store, or by directly querying a full node using its RPC and GraphQL endpoints. Also fixes #6090 ## Type of change Choose a type of change, and delete any options that are not relevant. - Bug fix (a non-breaking change which fixes an issue) - Enhancement (a non-breaking change which adds functionality) - Breaking change (fix or feature that would cause existing functionality to not work as expected) ## How the change has been tested - Unit tests - devnet: full support of { Sync, Check-* } commands - ``` cargo run --bin iota-light-client -- --config devnet.yaml sync cargo run --bin iota-light-client -- --config devnet.yaml check-transaction HAEPr4LGQXbUQzANTpKcK6eBFaGtztc1noh8jw1mPCKG cargo run --bin iota-light-client -- --config devnet.yaml check-object 0x80eb8f6e548d558e8d71d85688cf3a82186c6763cb8a499c387694323a60abdf ``` - testnet: full support of { Sync } command; `Check-` commands can currently only be tested on `devnet` as the corresponding `testnet` endpoint for historical checkpoints is not available yet - localnet full support of { Sync, Check-* } commands with this config: ```yaml rpc_url: "http://localhost:9000" graphql_url: "http://localhost:9125" checkpoints_dir: "checkpoints_localnet" genesis_blob_download_url: "file:///home/me/vps/.iota/iota_config/genesis.blob" sync_before_check: true object_store_url: ~ archive_store_config: ~ ``` ## Change checklist Tick the boxes that are relevant to your changes, and delete any items that are not. - [x] I have followed the contribution guidelines for this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [ ] I have added tests that prove my fix is effective or that my feature works - [x] I have checked that new and existing unit tests pass locally with my changes ### Release Notes <!-- Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. --> - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [ ] CLI: - [ ] Rust SDK: - [ ] REST API: --------- Co-authored-by: George Danezis <[email protected]> Co-authored-by: Sadhan Sood <[email protected]> Co-authored-by: Deepak Maram <[email protected]> Co-authored-by: Thoralf-M <[email protected]> Co-authored-by: DaughterOfMars <[email protected]> Co-authored-by: Thibault Martinez <[email protected]>
1 parent 1128809 commit dc72732

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1882
-19273
lines changed

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ fdlimit = "0.2.1"
265265
flate2 = "1.0.28"
266266
fs_extra = "1.3.0"
267267
futures = "0.3.28"
268+
getset = "0.1"
268269
git-version = "0.3.5"
269270
governor = "0.6.0"
270271
hdrhistogram = "7.5.1"

crates/iota-archival/src/lib.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,35 @@ impl Manifest {
210210
}
211211
}
212212
}
213+
214+
pub fn get_all_end_of_epoch_checkpoint_seq_numbers(&self) -> Result<Vec<u64>> {
215+
match self {
216+
Manifest::V1(manifest) => {
217+
let mut summary_files: Vec<_> = manifest
218+
.file_metadata
219+
.clone()
220+
.into_iter()
221+
.filter(|f| f.file_type == FileType::CheckpointSummary)
222+
.collect();
223+
summary_files.sort_by_key(|f| f.checkpoint_seq_range.start);
224+
assert_eq!(summary_files.first().unwrap().checkpoint_seq_range.start, 0);
225+
// get last checkpoint seq num per epoch
226+
let res = summary_files.windows(2).filter_map(|w| {
227+
assert_eq!(
228+
w[1].checkpoint_seq_range.start,
229+
w[0].checkpoint_seq_range.end
230+
);
231+
if w[1].epoch_num == w[0].epoch_num + 1 {
232+
Some(w[0].checkpoint_seq_range.end - 1)
233+
} else {
234+
None
235+
}
236+
});
237+
Ok(res.collect())
238+
}
239+
}
240+
}
241+
213242
pub fn update(
214243
&mut self,
215244
epoch_num: u64,

crates/iota-archival/src/reader.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -336,13 +336,13 @@ impl ArchiveReader {
336336
pub async fn read_summaries_for_list_no_verify<S>(
337337
&self,
338338
store: S,
339-
skiplist: Vec<CheckpointSequenceNumber>,
339+
checkpoints: Vec<CheckpointSequenceNumber>,
340340
checkpoint_counter: Arc<AtomicU64>,
341341
) -> Result<()>
342342
where
343343
S: WriteStore + Clone,
344344
{
345-
let summary_files = self.get_summary_files_for_list(skiplist.clone()).await?;
345+
let summary_files = self.get_summary_files_for_list(checkpoints.clone()).await?;
346346
let remote_object_store = self.remote_object_store.clone();
347347
let stream = futures::stream::iter(summary_files.iter())
348348
.map(|summary_metadata| {
@@ -365,7 +365,7 @@ impl ArchiveReader {
365365
)
366366
.and_then(|summary_iter| {
367367
summary_iter
368-
.filter(|s| skiplist.contains(&s.sequence_number))
368+
.filter(|s| checkpoints.contains(&s.sequence_number))
369369
.try_for_each(|summary| {
370370
Self::insert_certified_checkpoint(&store, summary)?;
371371
checkpoint_counter.fetch_add(1, Ordering::Relaxed);

crates/iota-light-client/.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
checkpoints_dir/*
1+
checkpoints_mainnet/
2+
checkpoints_testnet/
3+
checkpoints_devnet/
4+
checkpoints_localnet/

crates/iota-light-client/Cargo.toml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,18 @@ authors = ["IOTA Foundation <[email protected]>"]
55
edition = "2021"
66
license = "Apache-2.0"
77
publish = false
8+
description = "A light client for the IOTA blockchain"
89

910
[lib]
1011
path = "src/lib.rs"
1112

1213
[[bin]]
1314
name = "iota-light-client"
14-
path = "src/bin/light_client.rs"
15+
path = "src/main.rs"
16+
17+
[[bin]]
18+
name = "generate-checkpoint-snapshots"
19+
path = "src/bin/generate_checkpoint_snapshots.rs"
1520

1621
[dependencies]
1722
# external dependencies
@@ -20,20 +25,33 @@ async-trait.workspace = true
2025
bcs.workspace = true
2126
clap.workspace = true
2227
env_logger = "0.11.5"
28+
futures.workspace = true
29+
getset.workspace = true
2330
log = "0.4.22"
2431
object_store.workspace = true
32+
prometheus.workspace = true
2533
reqwest.workspace = true
34+
roaring.workspace = true
2635
serde.workspace = true
2736
serde_json.workspace = true
2837
serde_yaml.workspace = true
2938
tokio = { workspace = true, features = ["full"] }
39+
tracing.workspace = true
3040
url.workspace = true
3141

3242
# internal dependencies
43+
bin-version.workspace = true
44+
iota-archival.workspace = true
3345
iota-config.workspace = true
46+
iota-data-ingestion-core.workspace = true
47+
iota-graphql-rpc-client.workspace = true
3448
iota-json-rpc-types.workspace = true
3549
iota-package-resolver.workspace = true
3650
iota-rest-api.workspace = true
3751
iota-sdk.workspace = true
3852
iota-types.workspace = true
3953
move-core-types.workspace = true
54+
telemetry-subscribers.workspace = true
55+
56+
[dev-dependencies]
57+
tempfile.workspace = true

crates/iota-light-client/README.md

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,45 +20,77 @@ The light client requires a config file and a directory to cache checkpoints, an
2020

2121
## Setup
2222

23-
The config file for the light client takes a URL for a full node, a directory (that must exist) and within the directory to name of the genesis blob for the IOTA network.
23+
The config file for the light client takes a URL for a full node, a directory to store checkpoint summaries (that must exist) and within the directory the name of the genesis blob for the IOTA network.
2424

2525
```
26-
full_node_url: "https://api.testnet.iota.cafe:443"
27-
checkpoint_summary_dir: "checkpoints_dir"
28-
genesis_filename: "genesis.blob"
26+
# A full node JSON RPC endpoint to query the latest network state (mandatory)
27+
rpc_url: "https://api.mainnet.iota.cafe"
28+
29+
# A full node GraphQL RPC endpoint to query end-of-epoch checkpoints (optional if archive store config is provided)
30+
graphql_url: "https://graphql.mainnet.iota.cafe"
31+
32+
# Local directory to store checkpoint summaries and other synchronization data (mandatory)
33+
checkpoints_dir: "checkpoints_mainnet"
34+
35+
# A URL to download or copy the genesis blob file from (optional if genesis blob is already present in checkpoints_dir)
36+
genesis_blob_download_url: "https://dbfiles.mainnet.iota.cafe/genesis.blob"
37+
38+
# A flag to set whether the light client should always sync before checking an object or a transaction for inclusion (mandatory)
39+
sync_before_check: true
40+
41+
# A config for an object store that gets populated by a historical checkpoint writer (optional)
42+
checkpoint_store_config:
43+
object-store: "S3"
44+
aws-endpoint: "https://checkpoints.mainnet.iota.cafe"
45+
aws-virtual-hosted-style-request: true
46+
no-sign-request: true
47+
aws-region: "weur"
48+
object-store-connection-limit: 20
49+
50+
# A config for an object store that gets populated by an archiver (optional)
51+
archive_store_config:
52+
object-store: "S3"
53+
aws-endpoint: "https://archive.mainnet.iota.cafe"
54+
aws-virtual-hosted-style-request: true
55+
no-sign-request: true
56+
aws-region: "weur"
57+
object-store-connection-limit: 20
2958
```
3059

31-
The genesis blob for the IOTA mainnet can be found here: https://github.com/iotaledger/iota-genesis/blob/main/mainnet/genesis.blob
32-
3360
## Sync
3461

3562
Every day there is a need to download new checkpoints through sync by doing:
3663

3764
```
38-
$ iota-light-client --config light_client.yaml sync
65+
$ iota-light-client --config testnet.yaml sync
3966
```
4067

41-
Where `light_client.yaml` is the config file above.
68+
Where `testnet.yaml` is the config file above.
4269

4370
This command will download all end-of-epoch checkpoints, and check them for validity. They will be cached within the checkpoint summary directory for use by future invocations.
4471

72+
Internally, sync works in two steps. It first downloads the end-of-epoch checkpoint numbers into the `checkpoints.yaml` file (which needs to be present in the checkpoint summaries directory). Next, it downloads the corresponding checkpoint summaries.
73+
4574
## Check Transaction
4675

47-
To check a transaction was executed, as well as the events it emitted do:
76+
To check whether a transaction was executed in the testnet and its effects and events exist, run:
4877

4978
```
50-
$ iota-light-client --config light_client.yaml transaction -t 8RiKBwuAbtu8zNCtz8SrcfHyEUzto6zi6cMVA9t4WhWk
79+
$ iota-light-client --config testnet.yaml check-transaction <transaction-digest>
5180
```
5281

53-
Where the base58 encoding of the transaction ID is specified. If the transaction has been executed the transaction ID the effects digest are displayed and all the events are printed in JSON. If not an error is printed.
82+
where `transaction-digest` is a base58 encoded string. If the transaction has been executed in the past, its digest, the effects, and all events are displayed. Events are printed in JSON. Otherwise an error is shown.
83+
84+
If you set `sync_before_check: true` in the config, the light client will first sync itself to the latest network state before checking the transaction.
5485

5586
## Check Object
5687

57-
To check an object provide its ID in the following way:
88+
To check whether an object exists in the testnet, run:
5889

5990
```
60-
$ iota-light-client --config light_client.yaml object -o 0xc646887891adfc0540ec271fd0203603fb4c841a119ec1e00c469441
61-
abfc7078
91+
$ iota-light-client --config testnet.yaml check-object <object-id>
6292
```
6393

64-
The object ID is represented in Hex as displayed in explorers. If the object exists in the latest state it is printed out in JSON, otherwise an error is printed.
94+
where `object-id` is a hex encoded string with a 0x prefix. If the object exists, it is printed in JSON. Otherwise an error is shown.
95+
96+
If you set `sync_before_check: true` in the config, the light client will first sync itself to the latest network state before checking the object.

crates/iota-light-client/devnet.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
rpc_url: "https://api.devnet.iota.cafe"
2+
graphql_url: "https://graphql.devnet.iota.cafe"
3+
checkpoints_dir: "checkpoints_devnet"
4+
genesis_blob_download_url: "https://dbfiles.devnet.iota.cafe/genesis.blob"
5+
sync_before_check: true
6+
checkpoint_store_config:
7+
object-store: "S3"
8+
aws-endpoint: "https://checkpoints.devnet.iota.cafe"
9+
aws-virtual-hosted-style-request: true
10+
no-sign-request: true
11+
aws-region: "weur"
12+
object-store-connection-limit: 20
13+
archive_store_config:
14+
object-store: "S3"
15+
aws-endpoint: "https://archive.devnet.iota.cafe"
16+
aws-virtual-hosted-style-request: true
17+
no-sign-request: true
18+
aws-region: "weur"
19+
object-store-connection-limit: 20
-14.2 KB
Binary file not shown.

crates/iota-light-client/example_config/528.json

Lines changed: 0 additions & 79 deletions
This file was deleted.

0 commit comments

Comments
 (0)