Skip to main content

sp1_prover/worker/controller/
deferred.rs

1use 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        // Prepare the inputs for the deferred proofs recursive verification.
42        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            // Calculate the range of the deferred proof.
85            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            // Upload the input
89            let deferred_data = artifact_client.create_artifact()?;
90            artifact_client.upload(&deferred_data, input).await?;
91            // Create the output artifact
92            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            // Send the id and output to the channel.
104            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}