Skip to content

Commit a17f07d

Browse files
authored
add better .env.examples file, add better docs for dev setup, fix orchestrator + validator s3 parsing (#592)
1 parent 570e0a6 commit a17f07d

File tree

8 files changed

+148
-111
lines changed

8 files changed

+148
-111
lines changed

.env.example

Lines changed: 60 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,65 @@
1-
RPC_URL=
2-
NETWORK_ID=
3-
MIN_STAKE_AMOUNT=
4-
WORKER_COMPUTE_POOL_ID=
5-
WORKER_EXTERNAL_IP=
1+
# Prime Network Configuration
2+
# ===========================
63

7-
# Discovery service URLs (comma-separated for multiple services)
4+
# Network
5+
# -------
6+
RPC_URL=http://localhost:8545
7+
NETWORK_ID=31337
8+
MIN_STAKE_AMOUNT=10
9+
WORKER_COMPUTE_POOL_ID=0
10+
# Address that we get when running deploy_work_validation.sh in smart contracts
11+
# This automatically runs when starting the worker
12+
WORK_VALIDATION_CONTRACT=0x0B306BF915C4d645ff596e518fAf3F9669b97016
13+
14+
# Discovery
15+
# ---------
816
DISCOVERY_URLS=http://localhost:8089
917

10-
# Legacy support for single discovery URL (use DISCOVERY_URLS instead)
11-
# DISCOVERY_URL=http://localhost:8089
12-
13-
# Private keys of privileged accounts
14-
# See deploy.sh files in smart-contracts
15-
PRIVATE_KEY_FEDERATOR=
16-
FEDERATOR_ADDRESS=
17-
PRIVATE_KEY_VALIDATOR=
18-
VALIDATOR_ADDRESS=
19-
20-
# Provider with their node
21-
# This can simple be a provider that is funded as part of the anvil start cmd.
22-
# You can also generate wallets using prime-worker generate-wallets
23-
PRIVATE_KEY_PROVIDER=
24-
PROVIDER_ADDRESS=
25-
PRIVATE_KEY_NODE=
26-
NODE_ADDRESS=
27-
28-
# Pool Owner
29-
POOL_OWNER_PRIVATE_KEY=
30-
POOL_OWNER_ADDRESS=
31-
32-
# Contracts - you get these addresses after deploying the contracts
33-
PRIME_NETWORK_ADDRESS=
34-
AI_TOKEN_ADDRESS=
35-
COMPUTE_REGISTRY_ADDRESS=
36-
DOMAIN_REGISTRY_ADDRESS=
37-
STAKE_MANAGER_ADDRESS=
38-
COMPUTE_POOL_ADDRESS=
39-
40-
# Optional
41-
WORK_VALIDATION_CONTRACT=
42-
# Toploc configurations as JSON array string
43-
# Example: [{"server_url": "toploc url", "auth_token": "toploc auth token", "file_prefix_filter": "optional prefix filter"}]
44-
TOPLOC_CONFIGS=
18+
# Accounts (Anvil Test Accounts - DO NOT USE IN PRODUCTION)
19+
# ---------------------------------------------------------
20+
# Federator (Account #0)
21+
PRIVATE_KEY_FEDERATOR=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
22+
FEDERATOR_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
23+
24+
# Validator (Account #1)
25+
PRIVATE_KEY_VALIDATOR=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
26+
VALIDATOR_ADDRESS=0x70997970C51812dc3A010C7d01b50e0d17dc79C8
27+
28+
# Provider (Account #2)
29+
PRIVATE_KEY_PROVIDER=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
30+
PROVIDER_ADDRESS=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC
31+
32+
# Node (Account #3)
33+
PRIVATE_KEY_NODE=0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
34+
NODE_ADDRESS=0x90F79bf6EB2c4f870365E785982E1f101E93b906
35+
36+
# Pool Owner (Account #5)
37+
POOL_OWNER_PRIVATE_KEY=0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba
38+
POOL_OWNER_ADDRESS=0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc
39+
40+
41+
# Storage (Optional)
42+
# ------------------
4543
S3_CREDENTIALS=
46-
BUCKET_NAME=
44+
HOURLY_S3_LIMIT=12
45+
BUCKET_NAME=sample-bucket
46+
47+
# Location Service (Optional)
48+
# ---------------------------
49+
LOCATION_SERVICE_URL=https://ipapi.co
50+
LOCATION_SERVICE_API_KEY=
51+
52+
# Orchestrator
53+
# ------------
54+
NODE_GROUP_CONFIGS='[{"name":"test-config","min_group_size":1,"max_group_size":1,"compute_requirements":null}]'
55+
56+
# Validator
57+
# ---------
58+
TOPLOC_CONFIGS='[]'
59+
TOPLOC_SERVER_URL=
60+
TOPLOC_AUTH_TOKEN=
61+
VALIDATOR_API_KEY=prime
4762

48-
# Webhook configurations as JSON array string
49-
# Example: [{"url": "webhook url", "bearer_token": "webhook bearer token"}]
50-
WEBHOOK_CONFIGS=
63+
# Webhooks (Optional)
64+
# -------------------
65+
WEBHOOK_CONFIGS='[]'

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,11 @@ watch-check:
9090

9191
watch-validator:
9292
set -a; source ${ENV_FILE}; set +a; \
93-
cargo watch -w crates/validator/src -x "run --bin validator -- --validator-key $${PRIVATE_KEY_VALIDATOR} --rpc-url $${RPC_URL} --discovery-urls $${DISCOVERY_URLS:-$${DISCOVERY_URL:-http://localhost:8089}} --pool-id $${WORKER_COMPUTE_POOL_ID} --bucket-name $${BUCKET_NAME} -l $${LOG_LEVEL:-info} --toploc-grace-interval $${TOPLOC_GRACE_INTERVAL:-30} --incomplete-group-grace-period-minutes $${INCOMPLETE_GROUP_GRACE_PERIOD_MINUTES:-1} --use-grouping"
93+
cargo watch -w crates/validator/src -x "run --bin validator -- --validator-key $${PRIVATE_KEY_VALIDATOR} --rpc-url $${RPC_URL} --discovery-urls $${DISCOVERY_URLS:-$${DISCOVERY_URL:-http://localhost:8089}} --pool-id $${WORKER_COMPUTE_POOL_ID} $${BUCKET_NAME:+--bucket-name $${BUCKET_NAME}} -l $${LOG_LEVEL:-info} --toploc-grace-interval $${TOPLOC_GRACE_INTERVAL:-30} --incomplete-group-grace-period-minutes $${INCOMPLETE_GROUP_GRACE_PERIOD_MINUTES:-1} --use-grouping"
9494

9595
watch-orchestrator:
9696
set -a; source ${ENV_FILE}; set +a; \
97-
cargo watch -w crates/orchestrator/src -x "run --bin orchestrator -- -r $$RPC_URL -k $$POOL_OWNER_PRIVATE_KEY -d 0 -p 8090 -i 10 -u http://localhost:8090 --discovery-urls $${DISCOVERY_URLS:-$${DISCOVERY_URL:-http://localhost:8089}} --compute-pool-id $$WORKER_COMPUTE_POOL_ID --bucket-name $$BUCKET_NAME -l $${LOG_LEVEL:-info} --hourly-s3-upload-limit $${HOURLY_S3_LIMIT:-3} --max-healthy-nodes-with-same-endpoint $${MAX_HEALTHY_NODES_WITH_SAME_ENDPOINT:-2}"
97+
cargo watch -w crates/orchestrator/src -x "run --bin orchestrator -- -r $$RPC_URL -k $$POOL_OWNER_PRIVATE_KEY -d 0 -p 8090 -i 10 -u http://localhost:8090 --discovery-urls $${DISCOVERY_URLS:-$${DISCOVERY_URL:-http://localhost:8089}} --compute-pool-id $$WORKER_COMPUTE_POOL_ID $${BUCKET_NAME:+--bucket-name $$BUCKET_NAME} -l $${LOG_LEVEL:-info} --hourly-s3-upload-limit $${HOURLY_S3_LIMIT:-3} --max-healthy-nodes-with-same-endpoint $${MAX_HEALTHY_NODES_WITH_SAME_ENDPOINT:-2}"
9898

9999
build-worker:
100100
cargo build --release --bin worker

crates/orchestrator/src/api/routes/storage.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ async fn request_upload(
2626
request_upload: web::Json<RequestUploadRequest>,
2727
app_state: Data<AppState>,
2828
) -> HttpResponse {
29+
let Some(storage_provider) = &app_state.storage_provider else {
30+
return HttpResponse::InternalServerError().json(serde_json::json!({
31+
"success": false,
32+
"error": "Storage provider not found"
33+
}));
34+
};
35+
2936
// Check file size limit first
3037
if request_upload.file_size > MAX_FILE_SIZE {
3138
return HttpResponse::PayloadTooLarge().json(serde_json::json!({
@@ -217,8 +224,7 @@ async fn request_upload(
217224

218225
log::info!("Generating mapping file for sha256: {sha256} to file: {file_name}",);
219226

220-
if let Err(e) = app_state
221-
.storage_provider
227+
if let Err(e) = storage_provider
222228
.generate_mapping_file(sha256, &file_name)
223229
.await
224230
{
@@ -234,8 +240,7 @@ async fn request_upload(
234240
);
235241

236242
// Generate signed upload URL
237-
match app_state
238-
.storage_provider
243+
match storage_provider
239244
.generate_upload_signed_url(
240245
&file_name,
241246
Some(file_type.to_string()),

crates/orchestrator/src/api/server.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ async fn health_check(data: web::Data<AppState>) -> HttpResponse {
117117

118118
pub struct AppState {
119119
pub store_context: Arc<StoreContext>,
120-
pub storage_provider: Arc<dyn StorageProvider>,
120+
pub storage_provider: Option<Arc<dyn StorageProvider>>,
121121
pub heartbeats: Arc<LoopHeartbeats>,
122122
pub redis_store: Arc<RedisStore>,
123123
pub hourly_upload_limit: i64,
@@ -135,7 +135,7 @@ pub async fn start_server(
135135
port: u16,
136136
store_context: Arc<StoreContext>,
137137
admin_api_key: String,
138-
storage_provider: Arc<dyn StorageProvider>,
138+
storage_provider: Option<Arc<dyn StorageProvider>>,
139139
heartbeats: Arc<LoopHeartbeats>,
140140
redis_store: Arc<RedisStore>,
141141
hourly_upload_limit: i64,

crates/orchestrator/src/api/tests/helper.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ pub async fn create_test_app_state() -> Data<AppState> {
5757
store_context: store_context.clone(),
5858
contracts: None,
5959
pool_id: 1,
60-
storage_provider,
60+
storage_provider: Some(storage_provider),
6161
heartbeats: Arc::new(LoopHeartbeats::new(&mode)),
6262
hourly_upload_limit: 12,
6363
redis_store: store.clone(),
@@ -127,7 +127,7 @@ pub async fn create_test_app_state_with_nodegroups() -> Data<AppState> {
127127
store_context: store_context.clone(),
128128
contracts: None,
129129
pool_id: 1,
130-
storage_provider,
130+
storage_provider: Some(storage_provider),
131131
heartbeats: Arc::new(LoopHeartbeats::new(&mode)),
132132
hourly_upload_limit: 12,
133133
redis_store: store.clone(),
@@ -193,7 +193,7 @@ pub async fn create_test_app_state_with_metrics() -> Data<AppState> {
193193
store_context: store_context.clone(),
194194
contracts: None,
195195
pool_id: 1,
196-
storage_provider,
196+
storage_provider: Some(storage_provider),
197197
heartbeats: Arc::new(LoopHeartbeats::new(&mode)),
198198
hourly_upload_limit: 12,
199199
redis_store: store.clone(),

crates/orchestrator/src/main.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -400,13 +400,22 @@ async fn main() -> Result<()> {
400400

401401
let port = args.port;
402402
let server_store_context = store_context.clone();
403-
404403
let s3_credentials = std::env::var("S3_CREDENTIALS").ok();
405-
406-
let gcs_storage = GcsStorageProvider::new(&args.bucket_name.unwrap(), &s3_credentials.unwrap())
407-
.await
408-
.unwrap();
409-
let storage_provider = Arc::new(gcs_storage);
404+
let storage_provider: Option<Arc<dyn shared::utils::StorageProvider>> =
405+
match (args.bucket_name.as_ref(), s3_credentials) {
406+
(Some(bucket_name), Some(s3_credentials))
407+
if !bucket_name.is_empty() && !s3_credentials.is_empty() =>
408+
{
409+
let gcs_storage = GcsStorageProvider::new(bucket_name, &s3_credentials)
410+
.await
411+
.unwrap_or_else(|_| panic!("Failed to create GCS storage provider"));
412+
Some(Arc::new(gcs_storage) as Arc<dyn shared::utils::StorageProvider>)
413+
}
414+
_ => {
415+
info!("Bucket name or S3 credentials not provided, storage provider disabled");
416+
None
417+
}
418+
};
410419

411420
// Always start server regardless of mode
412421
tokio::select! {

crates/validator/src/main.rs

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -341,34 +341,43 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
341341
std::process::exit(1);
342342
}
343343
};
344-
345344
let s3_credentials = std::env::var("S3_CREDENTIALS").ok();
346-
let gcs_storage =
347-
GcsStorageProvider::new(&args.bucket_name.unwrap(), &s3_credentials.unwrap())
348-
.await
349-
.unwrap();
350-
let storage_provider = Arc::new(gcs_storage);
351-
352-
Some(SyntheticDataValidator::new(
353-
pool_id,
354-
validator,
355-
contracts.prime_network.clone(),
356-
configs,
357-
penalty,
358-
storage_provider,
359-
redis_store,
360-
cancellation_token,
361-
args.toploc_work_validation_interval,
362-
args.toploc_work_validation_unknown_status_expiry_seconds,
363-
args.toploc_grace_interval,
364-
args.batch_trigger_size,
365-
args.use_grouping,
366-
args.disable_toploc_invalidation,
367-
args.incomplete_group_grace_period_minutes,
368-
args.toploc_invalidation_type,
369-
args.work_unit_invalidation_type,
370-
Some(metrics_ctx.clone()),
371-
))
345+
346+
match (args.bucket_name.as_ref(), s3_credentials) {
347+
(Some(bucket_name), Some(s3_credentials))
348+
if !bucket_name.is_empty() && !s3_credentials.is_empty() =>
349+
{
350+
let gcs_storage = GcsStorageProvider::new(bucket_name, &s3_credentials)
351+
.await
352+
.unwrap_or_else(|_| panic!("Failed to create GCS storage provider"));
353+
let storage_provider = Arc::new(gcs_storage);
354+
355+
Some(SyntheticDataValidator::new(
356+
pool_id,
357+
validator,
358+
contracts.prime_network.clone(),
359+
configs,
360+
penalty,
361+
storage_provider,
362+
redis_store,
363+
cancellation_token,
364+
args.toploc_work_validation_interval,
365+
args.toploc_work_validation_unknown_status_expiry_seconds,
366+
args.toploc_grace_interval,
367+
args.batch_trigger_size,
368+
args.use_grouping,
369+
args.disable_toploc_invalidation,
370+
args.incomplete_group_grace_period_minutes,
371+
args.toploc_invalidation_type,
372+
args.work_unit_invalidation_type,
373+
Some(metrics_ctx.clone()),
374+
))
375+
}
376+
_ => {
377+
info!("Bucket name or S3 credentials not provided, skipping synthetic data validation");
378+
None
379+
}
380+
}
372381
}
373382
None => {
374383
error!("Synthetic data validator not found");

docs/development-setup.md

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -100,25 +100,6 @@ This will start:
100100
- Redis instance
101101
- Supporting infrastructure
102102

103-
## Docker Compose Setup
104-
105-
You can run all supporting services (chain, validator, discovery, orchestrator) in docker compose.
106-
107-
1. Start docker compose:
108-
```bash
109-
docker compose up
110-
```
111-
112-
2. Run Setup:
113-
```bash
114-
make setup
115-
```
116-
117-
3. Launch a worker:
118-
- Adjust the .env var `WORKER_EXTERNAL_IP` to: `WORKER_EXTERNAL_IP=host.docker.internal`
119-
- Launch the worker using `make watch-worker`
120-
- Whitelist the worker once you see the whitelist alert using: `make whitelist-provider`
121-
122103
## Running a Worker Node
123104

124105
Once the core services are running, you can start a worker node in a new terminal:
@@ -127,9 +108,9 @@ make watch-worker
127108
```
128109

129110
The worker will automatically connect to the discovery service and begin processing tasks.
111+
Your worker will show an error that it is not whitelisted yet. You'll have to run `make whitelist-provider` here.
130112
It takes a couple of seconds until the worker is whitelisted. This is done using a simple loop on the second page of tmux.
131-
132-
You can find more details on the APIs in the orchestrator and discovery service directory.
113+
You should see your worker eventually on the orchestrator. Checkout the orchestrator doc also: `http://localhost:8090/docs`
133114

134115
## Remote GPU Development
135116
> ⚠️ **IMPORTANT**: The video shows the whitelist process happening automatically. Currently, this must be done manually using the command `make whitelist-provider`.
@@ -155,4 +136,22 @@ Set up your remote GPU worker:
155136
To gracefully shutdown all services:
156137
```bash
157138
make down
158-
```
139+
```
140+
141+
## Docker Compose Setup
142+
You can run all supporting services (chain, validator, discovery, orchestrator) (if you only want to work on the worker sw) in docker compose.
143+
144+
1. Start docker compose:
145+
```bash
146+
docker compose up
147+
```
148+
149+
2. Run Setup:
150+
```bash
151+
make setup
152+
```
153+
154+
3. Launch a worker:
155+
- Adjust the .env var `WORKER_EXTERNAL_IP` to: `WORKER_EXTERNAL_IP=host.docker.internal`
156+
- Launch the worker using `make watch-worker`
157+
- Whitelist the worker once you see the whitelist alert using: `make whitelist-provider`

0 commit comments

Comments
 (0)