sp1_prover/worker/controller/
deferred.rs1use std::borrow::Borrow;
2
3use serde::{Deserialize, Serialize};
4use slop_algebra::{AbstractField, PrimeField32};
5
6use sp1_hypercube::{
7 air::{ShardRange, POSEIDON_NUM_WORDS},
8 MerkleProof, SP1PcsProofInner, SP1RecursionProof,
9};
10use sp1_primitives::{hash_deferred_proof, SP1Field, SP1GlobalContext};
11use sp1_prover_types::{Artifact, ArtifactClient, TaskType};
12use sp1_recursion_circuit::machine::SP1ShapedWitnessValues;
13use sp1_recursion_executor::{RecursionPublicValues, DIGEST_SIZE};
14
15use crate::{
16 utils::words_to_bytes,
17 worker::{
18 MessageSender, ProofData, RecursionDeferredTaskRequest, TaskContext, TaskError,
19 WorkerClient,
20 },
21};
22
23#[derive(Clone, Serialize, Deserialize)]
24pub struct SP1DeferredData {
25 pub input: SP1ShapedWitnessValues<SP1GlobalContext, SP1PcsProofInner>,
26 pub vk_merkle_proofs: Vec<MerkleProof<SP1GlobalContext>>,
27 pub start_reconstruct_deferred_digest: [SP1Field; POSEIDON_NUM_WORDS],
28 pub deferred_proof_index: SP1Field,
29}
30
31pub struct DeferredInputs {
32 inputs: Vec<SP1DeferredData>,
33 deferred_digest: [SP1Field; DIGEST_SIZE],
34}
35
36impl DeferredInputs {
37 pub fn new(
38 deferred_proofs: impl IntoIterator<Item = SP1RecursionProof<SP1GlobalContext, SP1PcsProofInner>>,
39 ) -> Self {
40 let initial_deferred_digest = Self::initial_deferred_digest();
41 let mut deferred_digest = initial_deferred_digest;
43 let mut deferred_inputs = Vec::new();
44
45 for (index, proof) in deferred_proofs.into_iter().enumerate() {
46 let vks_and_proofs = vec![(proof.vk.clone(), proof.proof.clone())];
47 let merkle_proofs = vec![proof.vk_merkle_proof.clone()];
48
49 let input = SP1ShapedWitnessValues { vks_and_proofs, is_complete: true };
50
51 deferred_inputs.push(SP1DeferredData {
52 input,
53 start_reconstruct_deferred_digest: deferred_digest,
54 vk_merkle_proofs: merkle_proofs,
55 deferred_proof_index: SP1Field::from_canonical_usize(index),
56 });
57
58 deferred_digest = hash_deferred_proofs(deferred_digest, &[proof]);
59 }
60 DeferredInputs { inputs: deferred_inputs, deferred_digest }
61 }
62
63 pub fn initial_deferred_digest() -> [SP1Field; DIGEST_SIZE] {
64 [SP1Field::zero(); DIGEST_SIZE]
65 }
66
67 pub fn num_deferred_proofs(&self) -> usize {
68 self.inputs.len()
69 }
70
71 pub fn deferred_digest(&self) -> [SP1Field; DIGEST_SIZE] {
72 self.deferred_digest
73 }
74
75 pub async fn emit_deferred_tasks<W: WorkerClient>(
76 self,
77 common_input: Artifact,
78 context: TaskContext,
79 core_proofs_tx: MessageSender<W, ProofData>,
80 artifact_client: impl ArtifactClient,
81 worker_client: W,
82 ) -> Result<(), TaskError> {
83 for input in self.inputs {
84 let prev_deferred_proof = input.deferred_proof_index.as_canonical_u32() as u64;
86 let deferred_proof = prev_deferred_proof + input.input.vks_and_proofs.len() as u64;
87 let range = ShardRange::deferred(prev_deferred_proof, deferred_proof);
88 let deferred_data = artifact_client.create_artifact()?;
90 artifact_client.upload(&deferred_data, input).await?;
91 let output = artifact_client.create_artifact()?;
93
94 let task_request = RecursionDeferredTaskRequest {
95 common_input: common_input.clone(),
96 deferred_data,
97 output: output.clone(),
98 context: context.clone(),
99 };
100 let task_request = task_request.into_raw()?;
101 let task_id =
102 worker_client.submit_task(TaskType::RecursionDeferred, task_request).await?;
103 let proof_data = ProofData { task_id, range, proof: output };
105 core_proofs_tx
106 .send(proof_data)
107 .await
108 .map_err(|_| TaskError::Fatal(anyhow::anyhow!("Controller panicked, failed to send deferred proof data to core proofs channel")))?;
109 }
110 Ok(())
111 }
112}
113
114pub fn hash_deferred_proofs(
115 prev_digest: [SP1Field; DIGEST_SIZE],
116 deferred_proofs: &[SP1RecursionProof<SP1GlobalContext, SP1PcsProofInner>],
117) -> [SP1Field; 8] {
118 let mut digest = prev_digest;
119 for proof in deferred_proofs.iter() {
120 let pv: &RecursionPublicValues<SP1Field> = proof.proof.public_values.as_slice().borrow();
121 let committed_values_digest = words_to_bytes(&pv.committed_value_digest);
122
123 digest = hash_deferred_proof(
124 &digest,
125 &pv.sp1_vk_digest,
126 &committed_values_digest.try_into().unwrap(),
127 );
128 }
129 digest
130}