Skip to content

Commit 0022640

Browse files
committed
Add 'ipld-resolver/' from commit '4888d016b639ad6519c17cd3d8ff3ea8cda94122'
git-subtree-dir: ipld-resolver git-subtree-mainline: e1c5fa2 git-subtree-split: 4888d01
2 parents e1c5fa2 + 4888d01 commit 0022640

36 files changed

+4436
-0
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- '**'
10+
11+
jobs:
12+
# Check code formatting; anything that doesn't require compilation.
13+
pre-compile-checks:
14+
name: Pre-compile checks
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Check out the project
18+
uses: actions/checkout@v3
19+
- name: Install Rust
20+
uses: actions-rs/toolchain@v1
21+
with:
22+
profile: minimal
23+
toolchain: nightly
24+
components: rustfmt
25+
- name: Check code formatting
26+
run: make check-fmt
27+
- name: Check license headers
28+
run: make license
29+
# - name: Check diagrams
30+
# run: make check-diagrams
31+
32+
# Test matrix, running tasks from the Makefile.
33+
tests:
34+
needs: [pre-compile-checks]
35+
name: ${{ matrix.make.name }} (${{ matrix.os }}, ${{ matrix.rust }})
36+
runs-on: ${{ matrix.os }}
37+
strategy:
38+
fail-fast: false
39+
matrix:
40+
os: [ubuntu-latest]
41+
rust: [nightly]
42+
make:
43+
- name: Lint
44+
task: lint
45+
- name: Test
46+
task: test
47+
exclude:
48+
- rust: stable
49+
make:
50+
name: Lint
51+
52+
env:
53+
RUST_BACKTRACE: full
54+
RUSTFLAGS: -Dwarnings
55+
CARGO_INCREMENTAL: '0'
56+
SCCACHE_CACHE_SIZE: 10G
57+
CC: "sccache clang"
58+
CXX: "sccache clang++"
59+
60+
steps:
61+
- name: Check out the project
62+
uses: actions/checkout@v3
63+
64+
- name: Install Rust
65+
uses: dtolnay/rust-toolchain@master
66+
with:
67+
targets: wasm32-unknown-unknown
68+
toolchain: ${{ matrix.rust }}
69+
components: rustfmt,clippy
70+
71+
# Protobuf compiler required by libp2p-core
72+
- name: Install Protoc
73+
uses: arduino/setup-protoc@v1
74+
with:
75+
repo-token: ${{ secrets.GITHUB_TOKEN }}
76+
77+
- name: Setup sccache
78+
uses: hanabi1224/[email protected] # https://github.com/hanabi1224/sccache-action used by Forest.
79+
timeout-minutes: 5
80+
continue-on-error: true
81+
with:
82+
release-name: v0.3.1
83+
# Caching everything separately, in case they don't ask for the same things to be compiled.
84+
cache-key: ${{ matrix.make.name }}-${{ matrix.os }}-${{matrix.rust}}-${{ hashFiles('Cargo.lock', 'rust-toolchain', 'rust-toolchain.toml') }}
85+
# Not sure why we should ever update a cache that has the hash of the lock file in it.
86+
# In Forest it only contains the rust-toolchain, so it makes sense to update because dependencies could have changed.
87+
cache-update: false
88+
89+
- name: ${{ matrix.make.name }}
90+
run: make ${{ matrix.make.task }}

ipld-resolver/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.idea/
2+
*.iml
3+
/target
4+
docs/diagrams/plantuml.jar
5+
Cargo.lock

ipld-resolver/Cargo.toml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
[package]
2+
name = "ipc_ipld_resolver"
3+
version = "0.1.0"
4+
description = "P2P library to resolve IPLD content across IPC subnets."
5+
authors = ["Protocol Labs"]
6+
edition = "2021"
7+
license-file = "LICENSE"
8+
9+
[dependencies]
10+
anyhow = "1.0"
11+
base64 = "0.21.0"
12+
blake2b_simd = "1.0"
13+
bloom = "0.3"
14+
gcra = "0.4"
15+
lazy_static = "1.4"
16+
libipld = { version = "0.14", default-features = false, features = ["dag-cbor"] }
17+
libp2p = { version = "0.50", default-features = false, features = [
18+
"gossipsub",
19+
"kad",
20+
"identify",
21+
"ping",
22+
"noise",
23+
"yamux",
24+
"tcp",
25+
"dns",
26+
"mplex",
27+
"request-response",
28+
"metrics",
29+
"tokio",
30+
"macros",
31+
"serde",
32+
"secp256k1",
33+
"plaintext",
34+
] }
35+
libp2p-bitswap = "0.25.1"
36+
log = "0.4"
37+
prometheus = "0.13"
38+
quickcheck = { version = "1", optional = true }
39+
rand = "0.8"
40+
serde = { version = "1.0", features = ["derive"] }
41+
serde_json = { version = "1.0.91", features = ["raw_value"] }
42+
thiserror = "1.0.38"
43+
tokio = { version = "1.16", features = ["full"] }
44+
45+
fvm_ipld_encoding = "0.3"
46+
fvm_shared = { version = "~3.2", default-features = false, features = ["crypto"], optional = true }
47+
fvm_ipld_blockstore = { version = "0.1", optional = true }
48+
49+
# Using the IPC SDK without the `fil-actor` feature so as not to depend on the actor `Runtime`.
50+
# Using the `main` branch instead of the highest available tag `v0.3.0` because the latter doesn't have a feature flag for the `Runtime`.
51+
ipc-sdk = { git = "https://github.com/consensus-shipyard/ipc.git", default-features = false, branch = "dev" }
52+
53+
[dev-dependencies]
54+
quickcheck_macros = "1"
55+
env_logger = "0.10"
56+
fvm_ipld_hamt = "0.6"
57+
58+
ipc_ipld_resolver = { path = ".", features = ["arb"] }
59+
60+
[features]
61+
default = ["arb", "missing_blocks"]
62+
arb = ["quickcheck", "fvm_shared/arb"]
63+
missing_blocks = ["fvm_ipld_blockstore"]
64+
65+
[patch.crates-io]
66+
# Use stable-only features.
67+
gcra = { git = "https://github.com/consensus-shipyard/gcra-rs.git", branch = "main" }

ipld-resolver/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 ConsensusLab
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

ipld-resolver/Makefile

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
.PHONY: all build test lint license check-fmt check-clippy diagrams
2+
3+
all: test build
4+
5+
build:
6+
cargo build -Z unstable-options --release
7+
8+
test:
9+
cargo test --release --workspace
10+
11+
clean:
12+
cargo clean
13+
14+
lint: \
15+
license \
16+
check-fmt \
17+
check-clippy
18+
19+
license:
20+
./scripts/add_license.sh
21+
22+
check-fmt:
23+
cargo fmt --all --check
24+
25+
check-clippy:
26+
cargo clippy --all --tests -- -D clippy::all
27+
28+
diagrams:
29+
$(MAKE) -C docs/diagrams
30+
31+
check-diagrams: diagrams
32+
if git diff --name-only docs/diagrams | grep .png; then \
33+
echo "There are uncommitted changes to the diagrams"; \
34+
exit 1; \
35+
fi

ipld-resolver/README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# IPLD Resolver
2+
3+
The IPLD Resolver is a Peer-to-Peer library which can be used to resolve arbitrary CIDs from subnets in InterPlanetary Consensus.
4+
5+
See the [docs](./docs/) for a conceptual overview.
6+
7+
## Usage
8+
9+
Please have a look at the [smoke test](./tests/smoke.rs) for an example of using the library.
10+
11+
The following snippet demonstrates how one would create a resolver instance and use it:
12+
13+
```rust
14+
async fn main() {
15+
let config = Config {
16+
connection: ConnectionConfig {
17+
listen_addr: "/ip4/127.0.0.1/tcp/0".parse().unwrap(),
18+
expected_peer_count: 1000,
19+
max_incoming: 25,
20+
max_peers_per_query: 10,
21+
event_buffer_capacity: 100,
22+
},
23+
network: NetworkConfig {
24+
local_key: Keypair::generate_secp256k1(),
25+
network_name: "example".to_owned(),
26+
},
27+
discovery: DiscoveryConfig {
28+
static_addresses: vec!["/ip4/95.217.194.97/tcp/8008/p2p/12D3KooWC1EaEEpghwnPdd89LaPTKEweD1PRLz4aRBkJEA9UiUuS".parse().unwrap()]
29+
target_connections: 50,
30+
enable_kademlia: true,
31+
},
32+
membership: MembershipConfig {
33+
static_subnets: vec![],
34+
max_subnets: 10,
35+
publish_interval: Duration::from_secs(300),
36+
min_time_between_publish: Duration::from_secs(5),
37+
max_provider_age: Duration::from_secs(60),
38+
},
39+
};
40+
41+
let store = todo!("implement BitswapStore and a Blockstore");
42+
43+
let service = Service::new(config, store.clone());
44+
let client = service.client();
45+
46+
tokio::task::spawn(async move { service.run().await });
47+
48+
let cid: Cid = todo!("the CID we want to resolve");
49+
let subnet_id: SubnetID = todo!("the SubnetID from where the CID can be resolved");
50+
51+
match client.resolve(cid, subnet_id).await.unwrap() {
52+
Ok(()) => {
53+
let _content: MyContent = store.get(cid).unwrap();
54+
}
55+
Err(e) => {
56+
println!("{cid} could not be resolved from {subnet_id}: {e}")
57+
}
58+
}
59+
}
60+
```

ipld-resolver/docs/README.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# IPLD Resolver
2+
3+
The IPLD Resolver is a library that [IPC Agents](https://github.com/consensus-shipyard/ipc/) can use to exchange data between subnets in IPLD format.
4+
5+
## Checkpointing
6+
7+
The most typical use case would be the propagation of checkpoints from child subnets to the parent subnet.
8+
9+
### Checkpoint Schema
10+
11+
One possible conceptual model of checkpointing is depicted by the following Entity Relationship diagram:
12+
13+
![Checkpoint Schema](diagrams/checkpoint_schema.png)
14+
15+
It shows that the Subnet Actor in the parent subnet governs the power of validators in the child subnet by proposing _Configurations_, which the child subnet is free to adopt in its _Epochs_ when the time is right, communicating back the next adopted config via _Checkpoints_.
16+
17+
At the end of an epoch, the validators in the child subnet produce a checkpoint over some contents, notably the cross-messages they want to propagate towards the parent subnet. Through the cross-messages, the checkpoint indirectly points to individual messages that users or actors wanted to send.
18+
19+
Once enough signatures are collected to form a Quorum Certificate over the checkpoint (the specific rules are in the jurisdiction of the Subnet Actor), the checkpoint is submitted to the parent ledger.
20+
21+
However, the submitted checkpoint does not contain the raw messages, only the meta-data. The content needs to be resolved using the IPC Resolver, as indicated by the dotted line.
22+
23+
### Checkpoint Submission and Resolution
24+
25+
The following sequence diagram shows one possible way how checkpoints can be submitted from the child to the parent subnet.
26+
27+
It depicts two validators: one only participating on the parent subnet, and the other on the child subnet; the latter has to also run at least a full node on the parent subnet. Both validators run one IPC Agent each.
28+
29+
The diagram shows that at the end of the epoch the child subnet validators produce a Quorum Certificate over the checkpoint, which some of their agents submit to the parent subnet.
30+
31+
After that, the parent subnet nodes reach out to their associated IPC Agent to resolve the messages referenced by the checkpoint, which the Agent does by communicating with some of its child-subnet peers.
32+
33+
![Checkpoint Submission](diagrams/checkpoint_submission.png)
34+
35+
This is just a high level view of what happens during message resolution. In the next section we will delve deeper into the internals of the IPLD Resolver.
36+
37+
38+
## IPLD Resolver Sub-components
39+
40+
The IPLD Resolver uses libp2p to form a Peer-to-Peer network, using the following protocols:
41+
* [Ping](https://github.com/libp2p/rust-libp2p/tree/v0.50.1/protocols/ping)
42+
* [Identify](https://github.com/libp2p/rust-libp2p/tree/v0.50.1/protocols/ping) is used to learn the listening address of the remote peers
43+
* [Kademlia](https://github.com/libp2p/rust-libp2p/tree/v0.50.1/protocols/kad) is used for peer discovery
44+
* [Gossipsub](https://github.com/libp2p/rust-libp2p/tree/v0.50.1/protocols/gossipsub) is used to announce information about subnets the peers provide data for
45+
* [Bitswap](https://github.com/ipfs-rust/libp2p-bitswap) is used to resolve CIDs to content
46+
47+
See the libp2p [specs](https://github.com/libp2p/specs) and [docs](https://docs.libp2p.io/concepts/fundamentals/protocols/) for details on each protocol, and look [here](https://docs.ipfs.tech/concepts/bitswap/) for Bitswap.
48+
49+
The Resolver is completely agnostic over what content it can resolve, as long as it's based on CIDs; it's not aware of the checkpointing use case above.
50+
51+
The interface with the host system is through a host-provided implementation of the [BitswapStore](https://github.com/ipfs-rust/libp2p-bitswap/blob/7dd9cececda3e4a8f6e14c200a4b457159d8db33/src/behaviour.rs#L55) which the library uses to retrieve and store content. Implementors can make use of the [missing_blocks](../src/missing_blocks.rs) helper method which recursively collects all CIDs from an IPLD `Blockstore`, starting from the root CID we are looking for.
52+
53+
Internally the protocols are wrapped into behaviours that interpret their events and manage their associated state:
54+
* `Discovery` wraps `Kademlia`
55+
* `Membership` wraps `Gossipsub`
56+
* `Content` wraps `Bitswap`
57+
58+
The following diagram shows a typical sequence of events within the IPLD Resolver. For brevity, only one peer is shown in detail; it's counterpart is represented as a single boundary.
59+
60+
![IPLD Resolver](diagrams/ipld_resolver.png)
61+
62+
# Diagram Automation
63+
64+
The diagrams in this directory can be rendered with `make diagrams`.
65+
66+
Adding the following script to `.git/hooks/pre-commit` automatically renders and checks in the images when we commit changes to the them. CI should also check that there are no uncommitted changes.
67+
68+
```bash
69+
#!/usr/bin/env bash
70+
71+
# If any command fails, exit immediately with that command's exit status
72+
set -eo pipefail
73+
74+
# Redirect output to stderr.
75+
exec 1>&2
76+
77+
if git diff --cached --name-only --diff-filter=d | grep .puml
78+
then
79+
make diagrams
80+
git add docs/diagrams/*.png
81+
fi
82+
```
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
PUMLS = $(shell find . -type f -name "*.puml")
2+
PNGS = $(PUMLS:.puml=.png)
3+
PUML_VER=1.2023.2
4+
5+
.PHONY: all
6+
all: diagrams
7+
8+
.PHONY: diagrams
9+
diagrams: $(PNGS)
10+
11+
plantuml.jar:
12+
wget -O $@ https://github.com/plantuml/plantuml/releases/download/v$(PUML_VER)/plantuml-$(PUML_VER).jar --no-check-certificate --quiet
13+
14+
%.png: plantuml.jar %.puml
15+
@# Using pipelining to preserve file names.
16+
cat $*.puml | java -jar plantuml.jar -pipe > $*.png
104 KB
Loading

0 commit comments

Comments
 (0)