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: versioned execution #88

Merged
merged 3 commits into from
Nov 5, 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ images/**/manifest.json
stark
MyLogFile.log
*_generated.rs
.pnpm-store
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ risc0-circuit-recursion = { git = "https://github.com/anagrambuild/risc0", branc
risc0-sys = { git = "https:m//github.com/anagrambuild/risc0", branch = "v1.0.1-bonsai-fix" }
tokio-test = "0.4.3"

bonsol-interface = { path = "./onchain/interface" }
bonsol-schema = { path = "./schemas-rust" }
bonsol-cli = { path = "./cli" }
bonsol-sdk = { path = "./sdk" }

[workspace.lints.clippy]
clone_on_ref_ptr = "deny"
missing_const_for_fn = "deny"
Expand Down
2 changes: 2 additions & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,5 @@ solana-rpc-client = { workspace = true }
solana-sdk = { workspace = true }
tera = "1.17.1"
tokio = { version = "1.38.0", features = ["full"] }

bonsol-interface.workspace = true
2 changes: 1 addition & 1 deletion cli/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ pub async fn execute(
println!("Execution expiry {}", expiry);
println!("current block {}", current_block);
indicator.set_message("Building transaction");

let ixs = sdk
.execute_v1(
&signer,
Expand All @@ -190,6 +189,7 @@ pub async fn execute(
expiry,
execution_config,
callback_config.map(|c| c.into()),
None, // A future cli change can implement prover version selection
jac18281828 marked this conversation as resolved.
Show resolved Hide resolved
)
.await?;
indicator.finish_with_message("Sending transaction");
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/contributing/contributing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ cargo run -p bonsol-cli build -z images/simple
```
5. Use the bonsol cli to deploy a zkprogram (here is a example already uploaded for you)
```bash
cargo run -p bonsol-cli deploy -m images/simple/manifest.json -t url --url https://bonsol-public-images.s3.amazonaws.com/simple-68f4b0c5f9ce034aa60ceb264a18d6c410a3af68fafd931bcfd9ebe7c1e42960
cargo run -p bonsol-cli deploy -m images/simple/manifest.json -t url --url https://bonsol-public-images.s3.us-east-1.amazonaws.com/simple-68f4b0c5f9ce034aa60ceb264a18d6c410a3af68fafd931bcfd9ebe7c1e42960
```
6. Use the bonsol cli to execute a zkprogram
```bash
Expand Down
3 changes: 2 additions & 1 deletion node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ ark-relations = { version = "0.4.0" }
ark-serialize = "0.4.0"
ark-std = { version = "0.4.0" }
async-trait = "0.1.80"
bonsol-interface = { path = "../onchain/interface" }
bonsol-prover = { path = "../prover" }
bytemuck = "1.15.0"
byteorder = "1.5.0"
Expand Down Expand Up @@ -81,6 +80,8 @@ tracing-subscriber = { version = "0.3.18", features = [
yellowstone-grpc-client = { workspace = true }
yellowstone-grpc-proto = { workspace = true }

bonsol-interface.workspace = true

[dev-dependencies]
expect-test = "1.5.0"

Expand Down
2 changes: 1 addition & 1 deletion node/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use {
rlimit::Resource,
solana_rpc_client::nonblocking::rpc_client::RpcClient,
solana_sdk::{pubkey::Pubkey, signature::read_keypair_file, signer::Signer},
std::{path::Path, str::FromStr, sync::Arc},
std::{str::FromStr, sync::Arc},
thiserror::Error,
tokio::{select, signal},
tracing::{error, info},
Expand Down
1 change: 1 addition & 0 deletions node/src/observe/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub enum MetricEvents {
ProofSegments,
BonsolStartup,
SignaturesInFlight,
IncompatibleProverVersion,
}

macro_rules! emit_event {
Expand Down
37 changes: 35 additions & 2 deletions node/src/prover/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
mod utils;
pub mod verify_prover_version;

use {solana_sdk::instruction::AccountMeta, utils::check_stark_compression_tools_path};

use {
Expand All @@ -9,7 +11,10 @@ use {
prover::utils::async_to_json,
MissingImageStrategy,
},
bonsol_interface::bonsol_schema::{ClaimV1, DeployV1, ExecutionRequestV1},
bonsol_interface::{
bonsol_schema::{ClaimV1, DeployV1, ExecutionRequestV1},
prover_version::{ProverVersion, VERSION_V1_0_1},
},
dashmap::DashMap,
risc0_binfmt::MemoryImage,
risc0_zkvm::{ExitCode, Journal, SuccinctReceipt},
Expand Down Expand Up @@ -46,9 +51,13 @@ use {
tokio::{
fs::File, io::AsyncReadExt, process::Command, sync::mpsc::UnboundedSender, task::JoinHandle,
},
tracing::{error, info},
tracing::{error, info, warn},
};

use verify_prover_version::verify_prover_version;

const REQUIRED_PROVER: ProverVersion = VERSION_V1_0_1;

#[derive(Debug, Error)]
pub enum Risc0RunnerError {
#[error("Empty instruction")]
Expand Down Expand Up @@ -143,6 +152,8 @@ impl Risc0Runner {
// Break into Image handling, Input handling, Execution Request
// Inputs and Image should be service used by this prover.
pub fn start(&mut self) -> Result<UnboundedSender<BonsolInstruction>> {
verify_prover_version(REQUIRED_PROVER)
.expect("Bonsol build conflict: prover version is not supported");
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<BonsolInstruction>();
let loaded_images = self.loaded_images.clone();
// TODO: move image handling out of prover
Expand Down Expand Up @@ -402,6 +413,15 @@ async fn handle_execution_request<'a>(
exec: ExecutionRequestV1<'a>,
accounts: &[Pubkey],
) -> Result<()> {
if !can_execute(exec) {
warn!(
"Execution request for incompatible prover version: {:?}",
exec.prover_version()
);
emit_event!(MetricEvents::IncompatibleProverVersion, execution_id => exec.execution_id().unwrap_or_default());
return Ok(());
}

// current naive implementation is to accept everything we have pending capacity for on this node, but this needs work
let inflight = in_flight_proofs.len();
emit_event!(MetricEvents::ExecutionRequest, execution_id => exec.execution_id().unwrap_or_default());
Expand Down Expand Up @@ -685,3 +705,16 @@ async fn risc0_compress_proof(
Err(Risc0RunnerError::ProofCompressionError.into())
}
}

fn can_execute(exec: ExecutionRequestV1) -> bool {
let version = exec.prover_version().try_into();
if version.is_ok() {
let is_matching = match version.unwrap() {
REQUIRED_PROVER => true,
_ => false,
};
is_matching
} else {
false
}
}
54 changes: 54 additions & 0 deletions node/src/prover/verify_prover_version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use {anyhow::Result, tracing::info};

use risc0_zkvm::{sha::Digestible, Groth16ReceiptVerifierParameters};

use bonsol_interface::prover_version::ProverVersion;

pub fn verify_prover_version(required: ProverVersion) -> Result<()> {
let actual_digest = Groth16ReceiptVerifierParameters::default().digest();
let prover_digest = actual_digest.to_string();

match required {
ProverVersion::V1_0_1 {
verifier_digest, ..
} => {
if verifier_digest != prover_digest {
return Err(anyhow::anyhow!(
"Prover version mismatch, expected: {}, got: {}",
verifier_digest,
prover_digest
));
}
info!("Risc0 Prover with digest {}", verifier_digest);
}
_ => {
return Err(anyhow::anyhow!("Unsupported prover version"));
}
}
Ok(())
}

#[cfg(test)]
mod tests {
use {super::*, bonsol_interface::prover_version::VERSION_V1_0_1};

#[test]
fn test_verify_prover_version() {
assert!(verify_prover_version(VERSION_V1_0_1).is_ok());
}

#[test]
fn test_verify_prover_version_fail() {
let version_malade = ProverVersion::V1_0_1 {
verifier_digest: "malade",
};
let result = verify_prover_version(version_malade);
assert!(result.is_err());
}

#[test]
fn test_verify_default_prover_version_is_supported() {
let result = verify_prover_version(ProverVersion::default());
assert!(result.is_ok());
}
}
1 change: 1 addition & 0 deletions onchain/example-program-on-bonsol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ fn main<'a>(
AccountMeta::new_readonly(EA3, false),
],
}),
None,
)
.map_err(|_| ProgramError::InvalidInstructionData)?;
invoke_signed(&ix, accounts, &[&[execution_id.as_bytes(), &[bump]]])?;
Expand Down
4 changes: 3 additions & 1 deletion onchain/interface/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ idl-build = ["anchor-lang/idl-build"]

[dependencies]
arrayref = "0.3.6"
bonsol-schema = { path = "../../schemas-rust", version = "0.2.1" }
base64 = "0.21.2"
bytemuck = { version = "1.15.0", features = ["derive"] }
flatbuffers = { workspace = true }
hex = "0.4.3"
Expand All @@ -23,6 +23,8 @@ solana-program = { workspace = true, optional = true }
solana-sdk = { workspace = true, optional = true }
thiserror = { workspace = true }

bonsol-schema.workspace = true

[dependencies.anchor-lang]
optional = true
version = ">=0.28"
Expand Down
10 changes: 9 additions & 1 deletion onchain/interface/src/instructions.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use bonsol_schema::{
Account, ChannelInstruction, ChannelInstructionArgs, ChannelInstructionIxType, DeployV1,
DeployV1Args, ExecutionRequestV1, ExecutionRequestV1Args, InputBuilder, InputT, InputType,
ProgramInputType,
ProgramInputType, ProverVersion,
};
use flatbuffers::{FlatBufferBuilder, WIPOffset};

Expand Down Expand Up @@ -220,6 +220,7 @@ pub fn execute_v1<'a>(
expiration: u64,
config: ExecutionConfig<'a>,
callback: Option<CallbackConfig>,
prover_version: Option<ProverVersion>,
) -> Result<Instruction, ClientError> {
let (execution_account, _) = execution_address(requester, execution_id.as_bytes());
let (deployment_account, _) = deployment_address(image_id);
Expand All @@ -235,6 +236,7 @@ pub fn execute_v1<'a>(
expiration,
config,
callback,
prover_version,
)
}
/// Executes a bonsol program with the provided accounts
Expand All @@ -252,6 +254,7 @@ pub fn execute_v1_with_accounts<'a>(
expiration: u64,
config: ExecutionConfig,
callback: Option<CallbackConfig>,
prover_version: Option<ProverVersion>,
) -> Result<Instruction, ClientError> {
config.validate()?;
let mut fbb = FlatBufferBuilder::new();
Expand Down Expand Up @@ -318,6 +321,10 @@ pub fn execute_v1_with_accounts<'a>(
} else {
None
};

// typically cli will pass None for the optional prover_version indicating bonsol should handle
// the default case here
let prover_version = prover_version.unwrap_or(ProverVersion::default());
let fbb_execute = ExecutionRequestV1::create(
&mut fbb,
&ExecutionRequestV1Args {
Expand All @@ -332,6 +339,7 @@ pub fn execute_v1_with_accounts<'a>(
max_block_height: expiration,
input_digest,
callback_extra_accounts: extra_accounts,
prover_version: prover_version,
},
);
fbb.finish(fbb_execute, None);
Expand Down
1 change: 1 addition & 0 deletions onchain/interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod callback;
pub mod claim_state;
pub mod error;
pub mod instructions;
pub mod prover_version;
pub mod util;

pub use bonsol_schema;
Expand Down
Loading
Loading