11use std:: { env, fmt, sync:: Arc } ;
22
33use async_trait:: async_trait;
4+ use k256:: schnorr:: {
5+ signature:: { Signer , Verifier } ,
6+ Signature , SigningKey ,
7+ } ;
8+ use rand_core:: OsRng ;
49use zkaleido:: {
510 ExecutionSummary , Proof , ProofMetadata , ProofReceipt , ProofReceiptWithMetadata , ProofType ,
611 PublicValues , VerifyingKey , VerifyingKeyCommitment , ZkVm , ZkVmError , ZkVmExecutor , ZkVmHost ,
@@ -12,11 +17,12 @@ use crate::{env::NativeMachine, input::NativeMachineInputBuilder, proof::NativeP
1217
1318type ProcessProofFn = dyn Fn ( & NativeMachine ) -> ZkVmResult < ( ) > + Send + Sync ;
1419
15- /// A native host that holds a reference to a proof-processing function (`process_proof`) .
20+ /// A native host that holds a reference to a proof-processing function and a Schnorr signing key .
1621///
1722/// This struct can be cloned cheaply (due to the internal [`Arc`]), and used by various
18- /// parts of the application to execute native proofs or validations without
19- /// requiring a real cryptographic backend.
23+ /// parts of the application to execute native proofs with Schnorr signature-based verification.
24+ /// The signing key is used to sign public values during proof generation and verify them during
25+ /// verification.
2026#[ derive( Clone ) ]
2127pub struct NativeHost {
2228 /// A function wrapped in [`Arc`] and [`Box`] that processes proofs for a
@@ -25,15 +31,57 @@ pub struct NativeHost {
2531 /// By storing the function in a dynamic pointer (`Box<dyn ...>`) inside an
2632 /// [`Arc`], multiple host instances or threads can share the same proof
2733 /// logic without needing to replicate code or data.
28- pub process_proof : Arc < Box < ProcessProofFn > > ,
34+ process_fn : Arc < Box < ProcessProofFn > > ,
35+
36+ /// The Schnorr signing key used for signing public values during proof generation
37+ /// and verifying signatures during proof verification.
38+ schnorr_key : SigningKey ,
39+ }
40+
41+ impl NativeHost {
42+ /// Creates a new [`NativeHost`] with the given proof processing function.
43+ ///
44+ /// Generates a fresh Schnorr signing key pair to sign and verify proof outputs,
45+ /// providing authenticity guarantees for native execution.
46+ ///
47+ /// This method accepts infallible functions that return `()`. For functions that
48+ /// may fail and return `ZkVmResult<()>`, use [`new_fallible`](Self::new_fallible) instead.
49+ pub fn new < F > ( process_fn : F ) -> Self
50+ where
51+ F : Fn ( & NativeMachine ) + Send + Sync + ' static ,
52+ {
53+ let schnorr_key = SigningKey :: random ( & mut OsRng ) ;
54+ Self {
55+ process_fn : Arc :: new ( Box :: new ( move |zkvm : & NativeMachine | -> ZkVmResult < ( ) > {
56+ process_fn ( zkvm) ;
57+ Ok ( ( ) )
58+ } ) ) ,
59+ schnorr_key,
60+ }
61+ }
62+
63+ /// Creates a new [`NativeHost`] with a fallible proof processing function.
64+ ///
65+ /// Use this method when your processing function may fail and returns `ZkVmResult<()>`.
66+ /// For infallible functions that return `()`, use [`new`](Self::new) instead.
67+ pub fn new_fallible < F > ( process_fn : F ) -> Self
68+ where
69+ F : Fn ( & NativeMachine ) -> ZkVmResult < ( ) > + Send + Sync + ' static ,
70+ {
71+ let schnorr_key = SigningKey :: random ( & mut OsRng ) ;
72+ Self {
73+ process_fn : Arc :: new ( Box :: new ( process_fn) ) ,
74+ schnorr_key,
75+ }
76+ }
2977}
3078
3179impl ZkVmHost for NativeHost { }
3280
3381impl ZkVmExecutor for NativeHost {
3482 type Input < ' a > = NativeMachineInputBuilder ;
3583 fn execute < ' a > ( & self , native_machine : NativeMachine ) -> ZkVmResult < ExecutionSummary > {
36- ( self . process_proof ) ( & native_machine) ?;
84+ ( self . process_fn ) ( & native_machine) ?;
3785 let output = native_machine. state . borrow ( ) . output . clone ( ) ;
3886 let public_values = PublicValues :: new ( output) ;
3987 // There is no straightforward equivalent of cycles and gas for native execution
@@ -58,7 +106,9 @@ impl ZkVmProver for NativeHost {
58106 ) -> ZkVmResult < NativeProofReceipt > {
59107 let execution_result = self . execute ( native_machine) ?;
60108 let public_values = execution_result. into_public_values ( ) ;
61- let proof = Proof :: default ( ) ;
109+ // Sign the public values using the Schnorr signing key
110+ let signature = self . schnorr_key . sign ( public_values. as_bytes ( ) ) ;
111+ let proof = Proof :: new ( signature. to_bytes ( ) . to_vec ( ) ) ;
62112 let receipt = ProofReceipt :: new ( proof, public_values) ;
63113
64114 let version: & str = env ! ( "CARGO_PKG_VERSION" ) ;
@@ -72,14 +122,30 @@ impl ZkVmProver for NativeHost {
72122impl ZkVmTypedVerifier for NativeHost {
73123 type ZkVmProofReceipt = NativeProofReceipt ;
74124
75- fn verify_inner ( & self , _proof : & NativeProofReceipt ) -> ZkVmResult < ( ) > {
125+ fn verify_inner ( & self , proof : & NativeProofReceipt ) -> ZkVmResult < ( ) > {
126+ let receipt: ProofReceiptWithMetadata = proof
127+ . clone ( )
128+ . try_into ( )
129+ . map_err ( ZkVmError :: InvalidProofReceipt ) ?;
130+ let signature = Signature :: try_from ( receipt. receipt ( ) . proof ( ) . as_bytes ( ) )
131+ . map_err ( |e| ZkVmError :: ProofVerificationError ( format ! ( "invalid signature: {e}" ) ) ) ?;
132+ // Verify the Schnorr signature over the public values
133+ self . schnorr_key
134+ . verifying_key ( )
135+ . verify ( receipt. receipt ( ) . public_values ( ) . as_bytes ( ) , & signature)
136+ . map_err ( |e| {
137+ ZkVmError :: ProofVerificationError ( format ! ( "signature verification failed: {e}" ) )
138+ } ) ?;
139+
76140 Ok ( ( ) )
77141 }
78142}
79143
80144impl ZkVmVkProvider for NativeHost {
81145 fn vk ( & self ) -> VerifyingKey {
82- VerifyingKey :: default ( )
146+ // Return the Schnorr public key (verifying key) as the verifying key
147+ let schnorr_public_key = self . schnorr_key . verifying_key ( ) . to_bytes ( ) . to_vec ( ) ;
148+ VerifyingKey :: new ( schnorr_public_key)
83149 }
84150
85151 fn vk_commitment ( & self ) -> VerifyingKeyCommitment {
0 commit comments