Skip to content

Commit 70ca1dd

Browse files
authored
basic dockerfiles and docker compose (#50)
* basic docker compose for dev and prod * clean up dockerfiles * Implement feature to use pre-built docker ELF * update control ID for g16 verifier * Add docs for docker builds and add sanity check * rebuild contract * remove whitespace * fmt
1 parent a1bdc61 commit 70ca1dd

File tree

15 files changed

+220
-13
lines changed

15 files changed

+220
-13
lines changed

.dockerignore

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.DS_Store
2+
target/
3+
.env
4+
5+
contracts/cache
6+
dockerfiles
7+
8+
# Docker build ignored, to allow overriding the dockerized service with the deterministic build.
9+
!target/riscv-guest/riscv32im-risc0-zkvm-elf/docker/light_client_guest/light-client-guest

.env.local

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ RISC0_DEV_MODE=true
22
RUST_LOG=blobstream0=debug,info
33

44
ETH_RPC=http://127.0.0.1:8545
5-
ETH_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
5+
ETH_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
66
PRIVATE_KEY_HEX=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
77

88
BATCH_SIZE=64

cli/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,6 @@ reqwest = "0.12.4"
2424
serde = { workspace = true }
2525
serde_json = "1.0"
2626
serde_with = { version = "3.8", features = ["base64"] }
27+
28+
[features]
29+
prebuilt-docker = ["blobstream0-core/prebuilt-docker"]

cli/src/main.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ sol!(
5050

5151
// Pulled from https://github.com/risc0/risc0-ethereum/blob/ebec385cc526adb9279c1af55d699c645ca6d694/contracts/src/groth16/ControlID.sol
5252
const CONTROL_ID: [u8; 32] =
53-
hex!("a516a057c9fbf5629106300934d48e0e775d4230e41e503347cad96fcbde7e2e");
53+
hex!("8b6dcf11d463ac455361b41fb3ed053febb817491bdea00fdb340e45013b852e");
5454
const BN254_CONTROL_ID: [u8; 32] =
55-
hex!("0eb6febcf06c5df079111be116f79bd8c7e85dc9448776ef9a59aaf2624ab551");
55+
hex!("05a022e1db38457fb510bc347b30eb8f8cf3eda95587653d0eac19e1f10d164e");
5656

5757
#[derive(Parser, Debug)]
5858
#[command(name = "blobstream0-cli")]

compose.yml

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
services:
2+
blobstream0:
3+
container_name: blobstream0
4+
build:
5+
context: .
6+
dockerfile: ./dockerfiles/blobstream0.Dockerfile
7+
target: final
8+
args:
9+
- BONSAI_API_URL
10+
- BONSAI_API_KEY
11+
env_file:
12+
- .env
13+
platform: linux/amd64
14+
entrypoint: ["blobstream0", "service"]
15+
16+
blobstream0-dev:
17+
container_name: blobstream0-dev
18+
build:
19+
context: .
20+
dockerfile: ./dockerfiles/blobstream0-dev.Dockerfile
21+
env_file:
22+
- .env
23+
platform: linux/amd64
24+
entrypoint: ["/bin/sh", "-c"]
25+
command:
26+
- |
27+
echo "Waiting for Anvil to be ready..."
28+
until curl --silent --fail http://anvil:8545 -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' > /dev/null 2>&1; do
29+
sleep 1
30+
done
31+
echo "Anvil is ready"
32+
if [ ! -f .deployed ]; then
33+
echo "Deploying contracts..."
34+
blobstream0 deploy --dev && touch .deployed
35+
fi
36+
exec blobstream0 service
37+
depends_on:
38+
anvil:
39+
condition: service_healthy
40+
environment:
41+
- ETH_RPC=http://anvil:8545
42+
43+
anvil:
44+
container_name: anvil
45+
image: ghcr.io/foundry-rs/foundry:latest
46+
ports:
47+
- "8545:8545"
48+
platform: linux/amd64
49+
entrypoint: ["anvil", "--host", "0.0.0.0", "--port", "8545"]
50+
healthcheck:
51+
test: ["CMD", "nc", "-z", "localhost", "8545"]
52+
interval: 5s
53+
timeout: 5s
54+
retries: 5
55+
start_period: 5s

contracts/artifacts/Blobstream0.json

+7-7
Large diffs are not rendered by default.

contracts/src/ImageID.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ pragma solidity ^0.8.20;
2020

2121
library ImageID {
2222
bytes32 public constant LIGHT_CLIENT_GUEST_ID =
23-
bytes32(0x6dce022c2aea568a4484a24c36aa59bad7b10186205272b4e4f11157c9ad1421);
23+
bytes32(0x3229c3708db83fd93c3f28275361870e40d75740c5c09498196474da8fc65f8e);
2424
}

core/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,6 @@ tendermint-proto = { workspace = true }
1818
tendermint-rpc = { workspace = true, features = ["http-client"] }
1919
tokio = { version = "1.38.0", features = ["rt", "macros", "fs"] }
2020
tracing = "0.1.40"
21+
22+
[features]
23+
prebuilt-docker = ["light-client-guest/prebuilt-docker"]

core/src/lib.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ use blobstream0_primitives::{
2020
IBlobstream::IBlobstreamInstance,
2121
LightBlockProveData, RangeCommitment,
2222
};
23-
use light_client_guest::LIGHT_CLIENT_GUEST_ELF;
2423
use risc0_ethereum_contracts::groth16;
2524
use risc0_zkvm::{default_prover, is_dev_mode, sha::Digestible, ExecutorEnv, ProverOpts, Receipt};
2625
use std::{ops::Range, sync::Arc, time::Duration};
@@ -34,6 +33,15 @@ use tracing::{instrument, Level};
3433
mod range_iterator;
3534
use range_iterator::LightBlockRangeIterator;
3635

36+
// This is configured to use the default docker build path. The reason for the feature flag is
37+
// because we want a consistent docker image to build the program, which should not be run within
38+
// the dockerized service container.
39+
#[cfg(feature = "prebuilt-docker")]
40+
const LIGHT_CLIENT_GUEST_ELF: &[u8] =
41+
include_bytes!("../../target/riscv-guest/riscv32im-risc0-zkvm-elf/docker/light_client_guest/light-client-guest");
42+
#[cfg(not(feature = "prebuilt-docker"))]
43+
use light_client_guest::LIGHT_CLIENT_GUEST_ELF;
44+
3745
/// Currently set to the max allowed by tendermint RPC
3846
const HEADER_REQ_COUNT: u64 = 20;
3947

@@ -144,6 +152,7 @@ pub async fn prove_block(input: LightBlockProveData) -> anyhow::Result<Receipt>
144152
);
145153
let expected_next_hash = input.untrusted_block.signed_header.header().hash();
146154
let expected_next_height = input.untrusted_height();
155+
let expected_trusted_hash = input.trusted_block.signed_header.header().hash();
147156

148157
TrustedLightBlock {
149158
signed_header: input.trusted_block.signed_header,
@@ -183,6 +192,10 @@ pub async fn prove_block(input: LightBlockProveData) -> anyhow::Result<Receipt>
183192
// Assert that what is proven is expected based on the inputs.
184193
assert_eq!(expected_next_hash.as_bytes(), commitment.newHeaderHash);
185194
assert_eq!(expected_next_height, commitment.newHeight);
195+
assert_eq!(
196+
expected_trusted_hash.as_bytes(),
197+
commitment.trustedHeaderHash.as_slice()
198+
);
186199

187200
Ok(receipt)
188201
}
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Use the official Rust image as the base image
2+
FROM rust:1.81-bullseye as builder
3+
4+
# Install system dependencies
5+
RUN apt-get update && apt-get install -y \
6+
build-essential \
7+
curl \
8+
git \
9+
libssl-dev \
10+
pkg-config
11+
12+
# Install Foundry
13+
RUN curl -L https://foundry.paradigm.xyz | bash
14+
ENV PATH="/root/.foundry/bin:${PATH}"
15+
RUN foundryup
16+
17+
RUN cargo install cargo-binstall --version '=1.6.9' --locked
18+
RUN cargo binstall [email protected] --no-confirm --force
19+
RUN cargo risczero install
20+
21+
# Create and set permissions for the /app directory
22+
# RUN mkdir -p /app && chown -R nobody:nobody /app
23+
24+
# Set the working directory to /app
25+
WORKDIR /app
26+
27+
# Copy the entire project
28+
COPY . .
29+
30+
RUN cargo c -p light-client-guest
31+
32+
# Build the project
33+
RUN cargo build -p blobstream0
34+
35+
run cp target/debug/blobstream0 /usr/local/bin/blobstream0
36+
37+
# Set the entrypoint to the blobstream0 CLI
38+
ENTRYPOINT ["blobstream0"]

dockerfiles/blobstream0.Dockerfile

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Use the official Rust image as the base image
2+
FROM rust:1.81-bullseye as builder
3+
4+
# Install system dependencies
5+
RUN apt-get update && apt-get install -y \
6+
build-essential \
7+
curl \
8+
git \
9+
libssl-dev \
10+
pkg-config
11+
12+
# Install Foundry
13+
RUN curl -L https://foundry.paradigm.xyz | bash
14+
ENV PATH="/root/.foundry/bin:${PATH}"
15+
RUN foundryup
16+
17+
RUN cargo install cargo-binstall --version '=1.6.9' --locked
18+
RUN cargo binstall [email protected] --no-confirm --force
19+
RUN cargo risczero install
20+
21+
# Create and set permissions for the /app directory
22+
# RUN mkdir -p /app && chown -R nobody:nobody /app
23+
24+
# Set the working directory to /app
25+
WORKDIR /app
26+
27+
# Copy the entire project
28+
COPY . .
29+
30+
# Build the project
31+
RUN cargo build -p blobstream0 --release --features prebuilt-docker
32+
33+
# Create a new stage for a smaller final image
34+
FROM debian:bullseye-slim as final
35+
36+
# Install necessary runtime dependencies
37+
RUN apt-get update && apt-get install -y \
38+
ca-certificates \
39+
libssl-dev \
40+
&& rm -rf /var/lib/apt/lists/*
41+
42+
# Copy the built binary from the builder stage
43+
COPY --from=builder /app/target/release/blobstream0 /usr/local/bin/blobstream0
44+
45+
# Set the entrypoint to the blobstream0 CLI
46+
ENTRYPOINT ["blobstream0"]

light-client-guest/Cargo.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,8 @@ risc0-build-ethereum = { git = "https://github.com/risc0/risc0-ethereum", tag =
1010
# Currently just used to format built artifact
1111
serde_json = "1.0"
1212

13+
[features]
14+
prebuilt-docker = []
15+
1316
[package.metadata.risc0]
14-
methods = ["guest"]
17+
methods = ["guest"]

light-client-guest/build.rs

+6
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ const SOLIDITY_IMAGE_ID_PATH: &str = "../contracts/src/ImageID.sol";
2929
const SOLIDITY_ELF_PATH: &str = "../contracts/test/Elf.sol";
3030

3131
fn main() {
32+
// If this feature is enabled, skip build and just use pre-built guest and EVM artifacts.
33+
if cfg!(feature = "prebuilt-docker") {
34+
println!("cargo:rerun-if-env-changed=CARGO_FEATURE_PREBUILT_DOCKER");
35+
return;
36+
}
37+
3238
// Builds can be made deterministic, and thereby reproducible, by using Docker to build the
3339
// guest. Check the RISC0_USE_DOCKER variable and use Docker to build the guest if set.
3440
let use_docker = env::var("RISC0_USE_DOCKER").ok().map(|_| DockerOptions {

light-client-guest/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#[cfg(not(feature = "prebuilt-docker"))]
1516
include!(concat!(env!("OUT_DIR"), "/methods.rs"));

usage-guide.md

+30
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,36 @@ RUST_LOG=blobstream0=debug,info cargo run -p blobstream0 --release -- service \
9191
--batch-size 16
9292
```
9393

94+
### Dockerized service
95+
96+
```console
97+
# Build the guest ELF in docker
98+
RISC0_USE_DOCKER=true cargo c
99+
100+
# Build the docker image, which will use the docker built guest ELF
101+
docker build -f dockerfiles/blobstream0.Dockerfile --platform linux/amd64 -t blobstream0 .
102+
```
103+
104+
And can use the `blobstream0` binary as:
105+
106+
```console
107+
docker run blobstream0 deploy \
108+
--eth-rpc http://host.docker.internal:8545 \
109+
--private-key-hex 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
110+
--tm-height 9 \
111+
--tm-block-hash 5C5451567973D8658A607D58F035BA9078291E33D880A0E6E67145C717E6B11B \
112+
--min-batch-size 7
113+
```
114+
115+
```console
116+
docker run -e RUST_LOG=blobstream0=debug,info -e BONSAI_API_KEY=<API KEY> -e BONSAI_API_URL=https://api.bonsai.xyz blobstream0 service \
117+
--tendermint-rpc https://celestia-testnet.brightlystake.com \
118+
--eth-rpc http://host.docker.internal:8545/ \
119+
--eth-address 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 \
120+
--private-key-hex 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
121+
--batch-size 64
122+
123+
94124
### Admin transaction examples:
95125

96126

0 commit comments

Comments
 (0)