代码分析
proposer/succinct/bin/server.rs
let app = Router::new()
.route("/request_span_proof", post(request_span_proof)) // 请求对一系列区块的证明。
.route("/request_agg_proof", post(request_agg_proof)) // 请求一组子证明的聚合证明。
.route("/request_mock_span_proof", post(request_mock_span_proof)) // 请求对一系列区块的Mock证明。
.route("/request_mock_agg_proof", post(request_mock_agg_proof))// 请求一组子证明的Mock聚合证明。
.route("/status/:proof_id", get(get_proof_status)) // 获取证明的状态。
.route("/validate_config", post(validate_config)) // 验证 L2 输出 Oracle 的配置
.layer(DefaultBodyLimit::disable())
.layer(RequestBodyLimitLayer::new(102400 * 1024 * 1024))
.with_state(global_hashes);
let port = env::var("PORT").unwrap_or_else(|_| "3000".to_string());
let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", port))
.await
.unwrap();
info!("Server listening on {}", listener.local_addr().unwrap());
request_span_proof
async fn request_span_proof(
State(state): State<ContractConfig>,
Json(payload): Json<SpanProofRequest>,
) -> Result<(StatusCode, Json<ProofResponse>), AppError> {
info!("Received span proof request: {:?}", payload);
let fetcher = match OPSuccinctDataFetcher::new_with_rollup_config(RunContext::Docker).await { // get_rpcs() 通过环境变量 L1_RPC L1_BEACON_RPC L2_RPC L2_NODE_RPC 获取相应配置,并通过fetch_and_save_rollup_config 获取L2的rollup_config
Ok(f) => f,
Err(e) => {
error!("Failed to create data fetcher: {}", e);
return Err(AppError(e));
}
};
let host_cli = match fetcher
.get_host_cli_args( // 获取给定块号的 L2 输出数据,并将启动信息保存到数据目录中带有 block_number 的文件中。返回要传递给 datagen 的本机主机的参数。
payload.start,
payload.end,
ProgramType::Multi,
CacheMode::DeleteCache,
)
.await
{
Ok(cli) => cli,
Err(e) => {
error!("Failed to get host CLI args: {}", e);
return Err(AppError(anyhow::anyhow!(
"Failed to get host CLI args: {}",
e
)));
}
};
// 启动服务器和本机客户端并设置超时时间。
// 注意:理想情况下,服务器应调用执行本机的单独进程
// 主机,并返回客户端可以轮询以检查证明是否已提交的 ID。
let mut witnessgen_executor = WitnessGenExecutor::new(WITNESSGEN_TIMEOUT, RunContext::Docker);
if let Err(e) = witnessgen_executor.spawn_witnessgen(&host_cli).await { // 为给定的主机 CLI 生成见证生成进程,并将其添加到正在进行的进程列表中。
error!("Failed to spawn witness generation: {}", e);
return Err(AppError(anyhow::anyhow!(
"Failed to spawn witness generation: {}",
e
)));
}
// 记录运行见证生成过程时出现的任何错误。
if let Err(e) = witnessgen_executor.flush().await {
error!("Failed to generate witness: {}", e);
return Err(AppError(anyhow::anyhow!(
"Failed to generate witness: {}",
e
)));
}
let sp1_stdin = match get_proof_stdin(&host_cli) { // 获取标准输入来为给定的 L2 声明生成证明。
Ok(stdin) => stdin,
Err(e) => {
error!("Failed to get proof stdin: {}", e);
return Err(AppError(anyhow::anyhow!(
"Failed to get proof stdin: {}",
e
)));
}
};
let private_key = match env::var("SP1_PRIVATE_KEY") {
Ok(private_key) => private_key,
Err(e) => {
error!("Failed to get SP1 private key: {}", e);
return Err(AppError(anyhow::anyhow!(
"Failed to get SP1 private key: {}",
e
)));
}
};
let rpc_url = match env::var("PROVER_NETWORK_RPC") {
Ok(rpc_url) => rpc_url,
Err(e) => {
error!("Failed to get PROVER_NETWORK_RPC: {}", e);
return Err(AppError(anyhow::anyhow!(
"Failed to get PROVER_NETWORK_RPC: {}",
e
)));
}
};
let mut prover = NetworkProverV2::new(&private_key, Some(rpc_url.to_string()), false); // 根据private key和rpc 创建prover
// 使用预留策略路由到特定集群。
prover.with_strategy(FulfillmentStrategy::Reserved);
// 由于范围证明很大,因此将模拟设置为 false。
env::set_var("SKIP_SIMULATION", "true");
let vk_hash = match prover.register_program(&state.range_vk, RANGE_ELF).await { // 注册elf/range-elf
Ok(vk_hash) => vk_hash,
Err(e) => {
error!("Failed to register program: {}", e);
return Err(AppError(anyhow::anyhow!(
"Failed to register program: {}",
e
)));
}
};
let proof_id = match prover
.request_proof( // 向证明者网络请求证明,并返回请求 ID。
&vk_hash,
&sp1_stdin,
ProofMode::Compressed, // 压缩证明模式。还支持 Core,Plonk,Groth16
1_000_000_000_000,
None,
)
.await
{
Ok(proof_id) => proof_id,
Err(e) => {
error!("Failed to request proof: {}", e);
return Err(AppError(anyhow::anyhow!("Failed to request proof: {}", e)));
}
};
env::set_var("SKIP_SIMULATION", "false");
Ok((StatusCode::OK, Json(ProofResponse { proof_id })))
}
pub async fn request_proof(
&self,
vk_hash: &[u8],
stdin: &SP1Stdin,
mode: ProofMode,
cycle_limit: u64,
timeout: Option<Duration>,
) -> Result<Vec<u8>> {
// Get the timeout.
let timeout_secs = timeout.map(|dur| dur.as_secs()).unwrap_or(TIMEOUT_SECS);
log::info!("Requesting proof with cycle limit: {}", cycle_limit);
// Request the proof with retries.
let response = with_retry(
|| async {
self.client
.request_proof(
vk_hash,
stdin,
mode,
SP1_CIRCUIT_VERSION,
self.strategy,
timeout_secs,
cycle_limit,
)
.await
},
timeout,
"requesting proof",
)
.await?;
// 记录请求 ID 和交易哈希。
let tx_hash_hex = "0x".to_string() + &hex::encode(response.tx_hash);
let request_id = response.body.unwrap().request_id;
let request_id_hex = "0x".to_string() + &hex::encode(request_id.clone());
log::info!("Created request {} in transaction {}", request_id_hex, tx_hash_hex);
if self.client.rpc_url() == DEFAULT_PROVER_NETWORK_RPC { // "https://rpc.production.succinct.tools/"
log::info!("View in explorer: https://network.succinct.xyz/request/{}", request_id_hex);
}
Ok(request_id)
}
// 使用给定的验证密钥哈希和标准输入创建证明请求。
pub async fn request_proof(
&self,
vk_hash: &[u8],
stdin: &SP1Stdin,
mode: ProofMode,
version: &str,
strategy: FulfillmentStrategy,
timeout_secs: u64,
cycle_limit: u64,
) -> Result<RequestProofResponse> {
// 计算截止期限。
let start = SystemTime::now();
let since_the_epoch = start.duration_since(UNIX_EPOCH).expect("Invalid start time");
let deadline = since_the_epoch.as_secs() + timeout_secs;
// 创建 stdin artifact.
let mut store = self.get_store().await?;
let stdin_uri = self.create_artifact_with_content(&mut store, &stdin).await?;
// 发送请求
let mut rpc = self.get_rpc().await?; // 获取 ProverNetwork rpc
let nonce = self.get_nonce().await?; // 从ProverNetwork中获取nonce
let request_body = RequestProofRequestBody { // 组装请求体
nonce,
version: format!("sp1-{}", version),
vk_hash: vk_hash.to_vec(),
mode: mode.into(),
strategy: strategy.into(),
stdin_uri,
deadline,
cycle_limit,
};
let request_response = rpc
.request_proof(RequestProofRequest { // 调用的为sp1客户端服务 /network.ProverNetwork/RequestProof
format: MessageFormat::Binary.into(),
signature: request_body.sign(&self.signer).into(), // 使用私钥进行签名
body: Some(request_body),
})
.await?
.into_inner();
Ok(request_response)
}
// This file is @generated by prost-build.
#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq, ::prost::Message)]
pub struct RequestProofRequest {
/// The message format of the body.
#[prost(enumeration = "MessageFormat", tag = "1")]
pub format: i32,
/// The signature of the sender.
#[prost(bytes = "vec", tag = "2")]
pub signature: ::prost::alloc::vec::Vec<u8>,
/// The body of the request.
#[prost(message, optional, tag = "3")]
pub body: ::core::option::Option<RequestProofRequestBody>,
}
具体SP1服务相关逻辑,后续文章单独分析
request_agg_proof
// 请求一组子证明的聚合证明。
async fn request_agg_proof(
State(state): State<ContractConfig>,
Json(payload): Json<AggProofRequest>,
) -> Result<(StatusCode, Json<ProofResponse>), AppError> {
info!("Received agg proof request");
let mut proofs_with_pv: Vec<SP1ProofWithPublicValues> = payload
.subproofs
.iter()
.map(|sp| bincode::deserialize(sp).unwrap())
.collect();
let boot_infos: Vec<BootInfoStruct> = proofs_with_pv
.iter_mut()
.map(|proof| proof.public_values.read())
.collect();
let proofs: Vec<SP1Proof> = proofs_with_pv // 从payload中获取多个证明
.iter_mut()
.map(|proof| proof.proof.clone())
.collect();
let l1_head_bytes = hex::decode(
payload
.head
.strip_prefix("0x")
.expect("Invalid L1 head, no 0x prefix."),
)?;
let l1_head: [u8; 32] = l1_head_bytes.try_into().unwrap();
let fetcher = match OPSuccinctDataFetcher::new_with_rollup_config(RunContext::Docker).await {
Ok(f) => f,
Err(e) => return Err(AppError(anyhow::anyhow!("Failed to create fetcher: {}", e))),
};
let headers = match fetcher
.get_header_preimages(&boot_infos, l1_head.into()) // 获取与启动信息对应的标头的原映像。具体来说,获取与启动信息和最新的 L1 头对应的标头。 通过get_earliest_l1_head_in_batch从boot_infos中获取最早的L1 Head 作为start, get_l1_header 获取最新的 L1 Head(在链上验证)的完整标头, 通过fetch_headers_in_range获取start到end多个区块的headers
.await
{
Ok(h) => h,
Err(e) => {
error!("Failed to get header preimages: {}", e);
return Err(AppError(anyhow::anyhow!(
"Failed to get header preimages: {}",
e
)));
}
};
let private_key = env::var("SP1_PRIVATE_KEY")?;
let rpc_url = env::var("PROVER_NETWORK_RPC")?;
let mut prover = NetworkProverV2::new(&private_key, Some(rpc_url.to_string()), false);
// 使用预留策略路由到特定集群。
prover.with_strategy(FulfillmentStrategy::Reserved);
let stdin =
match get_agg_proof_stdin(proofs, boot_infos, headers, &state.range_vk, l1_head.into()) { // 获取聚合证明的标准输入。
Ok(s) => s,
Err(e) => {
error!("Failed to get agg proof stdin: {}", e);
return Err(AppError(anyhow::anyhow!(
"Failed to get agg proof stdin: {}",
e
)));
}
};
let vk_hash = match prover.register_program(&state.agg_vk, AGG_ELF).await { // 注册elf/aggregation-elf
Ok(vk_hash) => vk_hash,
Err(e) => {
error!("Failed to register program: {}", e);
return Err(AppError(anyhow::anyhow!(
"Failed to register program: {}",
e
)));
}
};
let proof_id = match prover
.request_proof( // 使用给定的验证密钥哈希和标准输入创建证明请求。具体看上面的request_span_proof->request_proof
&vk_hash,
&stdin,
ProofMode::Groth16, // 和request_span_proof有区别,ProofMode::Compressed,
1_000_000_000_000,
None,
)
.await
{
Ok(id) => id,
Err(e) => {
error!("Failed to request proof: {}", e);
return Err(AppError(anyhow::anyhow!("Failed to request proof: {}", e)));
}
};
Ok((StatusCode::OK, Json(ProofResponse { proof_id })))
}
pub fn get_agg_proof_stdin(
proofs: Vec<SP1Proof>,
boot_infos: Vec<BootInfoStruct>,
headers: Vec<Header>,
multi_block_vkey: &sp1_sdk::SP1VerifyingKey,
latest_checkpoint_head: B256,
) -> Result<SP1Stdin> {
let mut stdin = SP1Stdin::new();
for proof in proofs {
let SP1Proof::Compressed(compressed_proof) = proof else {
panic!();
};
stdin.write_proof(*compressed_proof, multi_block_vkey.vk.clone());
}
// 将聚合输入写入标准输入。
stdin.write(&AggregationInputs {
boot_infos,
latest_l1_checkpoint_head: latest_checkpoint_head,
multi_block_vkey: multi_block_vkey.hash_u32(),
});
// Head在使用 bincode 序列化时存在问题,因此请改用 serde_json。
let headers_bytes = serde_cbor::to_vec(&headers).unwrap();
stdin.write_vec(headers_bytes);
Ok(stdin)
}
总结
- request_span_proof:通过get_proof_stdin获取证明所需参数,使用ProofMode::Compressed压缩证明模式,通过request_proof向sp1服务申请生成proof
- request_agg_proof: 将获取到的多个proof,使用ProofMode::Groth16证明模式,通过request_proof向sp1服务申请生成聚合proof
版权属于:区块链中文技术社区 / 转载原创者
本文链接:https://bcskill.com/index.php/archives/2119.html
相关技术文章仅限于相关区块链底层技术研究,禁止用于非法用途,后果自负!本站严格遵守一切相关法律政策!