arnaucube
arnaucube/pod2-blob-examplePublic Self-hosted repository
pod2-blob-example/src/main.rs
src/main.rsRaw
  1use alloy::{
  2    consensus::{SidecarBuilder, SimpleCoder},
  3    eips::eip4844::DATA_GAS_PER_BLOB,
  4    network::{TransactionBuilder, TransactionBuilder4844},
  5    primitives::Address,
  6    providers::{Provider, ProviderBuilder},
  7    rpc::types::TransactionRequest,
  8    signers::local::PrivateKeySigner,
  9};
 10use anyhow::Result;
 11use std::{
 12    fs,
 13    fs::File,
 14    io::{BufReader, Read, Write},
 15    time::Instant,
 16};
 17
 18use pod2::{
 19    backends::plonky2::{basetypes::DEFAULT_VD_SET, mainpod::Prover},
 20    frontend::{MainPodBuilder, Operation},
 21    middleware::{containers::Set, Params},
 22};
 23
 24// ethereum private key to use for the tx
 25const PRIV_KEY: &str = "PLACE THE ETHEREUM PRIV KEY HERE";
 26// ethereum node rpc url
 27const RPC_URL: &str = "PLACE THE RPC URL HERE";
 28
 29
 30// returns a MainPod, example adapted from pod2/examples/main_pod_points.rs
 31pub fn compute_pod_proof() -> Result<pod2::frontend::MainPod> {
 32    let params = Params {
 33        max_input_pods: 0,
 34        ..Default::default()
 35    };
 36
 37    let mut builder = MainPodBuilder::new(&params, &DEFAULT_VD_SET);
 38    let set_entries = [1, 2, 3].into_iter().map(|n| n.into()).collect();
 39    let set = Set::new(10, set_entries)?;
 40
 41    builder.pub_op(Operation::set_contains(set, 1))?;
 42
 43    let prover = Prover {};
 44    let pod = builder.prove(&prover).unwrap();
 45    Ok(pod)
 46}
 47
 48#[tokio::main]
 49async fn main() -> Result<()> {
 50    // PART 1: generate a pod2 proof, and wrap it into one level recursive proof
 51    // in order to shrink its size
 52
 53    // use the compute_pod_proof to obtain an example pod2 proof
 54    println!("start to generate a pod2 proof");
 55    let start = Instant::now();
 56    let pod = compute_pod_proof()?;
 57    println!(
 58        "[TIME] generate pod & compute pod proof took: {:?}",
 59        start.elapsed()
 60    );
 61    // generate new plonky2 proof from POD's proof. This is 1 extra recursion in
 62    // order to shrink the proof size, together with removing extra custom gates
 63    let start = Instant::now();
 64    let (verifier_data, common_circuit_data, proof_with_pis) = pod2_onchain::prove_pod(pod)?;
 65    println!("[TIME] plonky2 (wrapper) proof took: {:?}", start.elapsed());
 66
 67    // get the compressed proof, which we will send inside a blob
 68    let compressed_proof = proof_with_pis.compress(
 69        &verifier_data.verifier_only.circuit_digest,
 70        &common_circuit_data.common,
 71    )?;
 72    let compressed_proof_bytes = compressed_proof.to_bytes();
 73    // store it in a file just in case we want to check it later
 74    let mut file = fs::File::create("proof_with_public_inputs.bin")?;
 75    file.write_all(&compressed_proof_bytes)?;
 76    dbg!(&compressed_proof_bytes.len());
 77
 78    // alternatively, instead of generating the pod2 proof, we can load a
 79    // previously stored proof with public inputs from the file
 80    /*
 81    let file = File::open("./proof_with_public_inputs.bin")?;
 82    let mut reader = BufReader::new(file);
 83    let mut compressed_proof_bytes = Vec::new();
 84    reader.read_to_end(&mut compressed_proof_bytes)?;
 85    */
 86    println!("size of proof_with_pis: {}", compressed_proof_bytes.len());
 87
 88    // PART 2: send the pod2 proof into a tx blob
 89    let signer: PrivateKeySigner = PRIV_KEY.parse()?;
 90    let provider = ProviderBuilder::new()
 91        .wallet(signer.clone())
 92        .connect(RPC_URL)
 93        .await?;
 94
 95    let latest_block = provider.get_block_number().await?;
 96    println!("Latest block number: {latest_block}");
 97
 98    let alice = signer.address();
 99    let bob = Address::from([0x42; 20]);
100    dbg!(&alice);
101    dbg!(&bob);
102
103    let sidecar: SidecarBuilder<SimpleCoder> = SidecarBuilder::from_slice(&compressed_proof_bytes);
104    let sidecar = sidecar.build()?;
105
106    let tx = TransactionRequest::default()
107        // 'from' field is filled by signer's first address (Alice in our case)
108        .with_to(bob)
109        .with_blob_sidecar(sidecar);
110
111    let pending_tx = provider.send_transaction(tx).await?;
112
113    println!("Pending transaction... tx hash: {}", pending_tx.tx_hash());
114
115    let receipt = pending_tx.get_receipt().await?;
116
117    println!(
118        "Transaction included in block {}",
119        receipt.block_number.expect("Failed to get block number")
120    );
121
122    assert_eq!(receipt.from, alice);
123    assert_eq!(receipt.to, Some(bob));
124    assert_eq!(
125        receipt
126            .blob_gas_used
127            .expect("Expected to be EIP-4844 transaction"),
128        DATA_GAS_PER_BLOB
129    );
130
131    Ok(())
132}