11// Copyright (C) 2025, Ava Labs, Inc. All rights reserved.
22// See the file LICENSE.md for licensing terms.
33
4+ use std:: num:: NonZeroUsize ;
5+
46#[ cfg( feature = "ethhash" ) ]
57use firewood:: ProofError ;
68#[ cfg( feature = "ethhash" ) ]
79use firewood_storage:: TrieHash ;
810#[ cfg( feature = "ethhash" ) ]
911use rlp:: Rlp ;
1012
11- use firewood:: v2:: api;
13+ use firewood:: v2:: api:: { self , FrozenChangeProof } ;
1214
1315use crate :: {
1416 BorrowedBytes , CResult , ChangeProofResult , DatabaseHandle , HashKey , HashResult , Maybe ,
@@ -28,11 +30,11 @@ const EMPTY_CODE_HASH: [u8; 32] = [
2830pub struct CreateChangeProofArgs < ' a > {
2931 /// The root hash of the starting revision. This must be provided.
3032 /// If the root is not found in the database, the function will return
31- /// [`ChangeProofResult::RevisionNotFound `].
33+ /// [`ChangeProofResult::StartRevisionNotFound `].
3234 pub start_root : HashKey ,
3335 /// The root hash of the ending revision. This must be provided.
3436 /// If the root is not found in the database, the function will return
35- /// [`ChangeProofResult::RevisionNotFound `].
37+ /// [`ChangeProofResult::EndRevisionNotFound `].
3638 pub end_root : HashKey ,
3739 /// The start key of the range to create the proof for. If `None`, the range
3840 /// starts from the beginning of the keyspace.
@@ -54,7 +56,7 @@ pub struct VerifyChangeProofArgs<'a> {
5456 /// The change proof to verify. If null, the function will return
5557 /// [`VoidResult::NullHandlePointer`]. We need a mutable reference to
5658 /// update the validation context.
57- pub proof : Option < & ' a mut ChangeProofContext > ,
59+ pub proof : Option < & ' a mut ChangeProofContext < ' a > > ,
5860 /// The root hash of the starting revision. This must match the starting
5961 /// root of the proof.
6062 pub start_root : HashKey ,
@@ -73,12 +75,44 @@ pub struct VerifyChangeProofArgs<'a> {
7375 pub max_length : u32 ,
7476}
7577
78+ /// Tracks the state of a proposal created from a change proof. A proposal is
79+ /// created after calling `fwd_db_verify_change_proof` and is committed after
80+ /// calling `fwd_db_verify_and_commit_change_proof`.
81+ #[ derive( Debug ) ]
82+ #[ expect( dead_code) ]
83+ enum ProposalState < ' db > {
84+ Immutable ( crate :: ProposalHandle < ' db > ) ,
85+ Committed ( Option < HashKey > ) ,
86+ }
87+
7688/// FFI context for a parsed or generated change proof.
7789#[ derive( Debug ) ]
78- pub struct ChangeProofContext {
79- _proof : ( ) , // currently not implemented
80- _validation_context : ( ) , // placeholder for future use
81- _commit_context : ( ) , // placeholder for future use
90+ #[ expect( dead_code) ]
91+ pub struct ChangeProofContext < ' db > {
92+ proof : FrozenChangeProof ,
93+ verification : Option < VerificationContext > ,
94+ proposal_state : Option < ProposalState < ' db > > ,
95+ }
96+
97+ impl From < FrozenChangeProof > for ChangeProofContext < ' _ > {
98+ fn from ( proof : FrozenChangeProof ) -> Self {
99+ Self {
100+ proof,
101+ verification : None ,
102+ proposal_state : None ,
103+ }
104+ }
105+ }
106+
107+ /// FFI context for verifying a change proof
108+ #[ derive( Debug ) ]
109+ #[ expect( dead_code) ]
110+ struct VerificationContext {
111+ start_root : HashKey ,
112+ end_root : HashKey ,
113+ start_key : Option < Box < [ u8 ] > > ,
114+ end_key : Option < Box < [ u8 ] > > ,
115+ max_length : Option < NonZeroUsize > ,
82116}
83117
84118/// A key range that should be fetched to continue iterating through a range
@@ -183,19 +217,37 @@ impl<'a> CodeIteratorHandle<'a> {
183217/// # Returns
184218///
185219/// - [`ChangeProofResult::NullHandlePointer`] if the caller provided a null pointer.
186- /// - [`ChangeProofResult::RevisionNotFound `] if the caller provided a start or end root
220+ /// - [`ChangeProofResult::StartRevisionNotFound `] if the caller provided a start root
187221/// that was not found in the database. The missing root hash is included in the result.
188- /// The start root is checked first, and if both are missing, only the start root is
222+ /// If both the start root and end root are missing, then only the end root is
223+ /// reported.
224+ /// - [`ChangeProofResult::EndRevisionNotFound`] if the caller provided an end root
225+ /// that was not found in the database. The missing root hash is included in the result.
226+ /// If both the start root and end root are missing, then only the end root is
189227/// reported.
190228/// - [`ChangeProofResult::Ok`] containing a pointer to the `ChangeProofContext` if the proof
191229/// was successfully created.
192230/// - [`ChangeProofResult::Err`] containing an error message if the proof could not be created.
193231#[ unsafe( no_mangle) ]
194- pub extern "C" fn fwd_db_change_proof (
195- _db : Option < & DatabaseHandle > ,
196- _args : CreateChangeProofArgs ,
197- ) -> ChangeProofResult {
198- CResult :: from_err ( "not yet implemented" )
232+ pub extern "C" fn fwd_db_change_proof < ' db > (
233+ db : Option < & ' db DatabaseHandle > ,
234+ args : CreateChangeProofArgs < ' db > ,
235+ ) -> ChangeProofResult < ' db > {
236+ crate :: invoke_with_handle ( db, |db| {
237+ db. change_proof (
238+ args. start_root . into ( ) ,
239+ args. end_root . into ( ) ,
240+ args. start_key
241+ . as_ref ( )
242+ . map ( BorrowedBytes :: as_slice)
243+ . into_option ( ) ,
244+ args. end_key
245+ . as_ref ( )
246+ . map ( BorrowedBytes :: as_slice)
247+ . into_option ( ) ,
248+ NonZeroUsize :: new ( args. max_length as usize ) ,
249+ )
250+ } )
199251}
200252
201253/// Verify a change proof and prepare a proposal to later commit or drop.
@@ -351,7 +403,7 @@ pub extern "C" fn fwd_free_change_proof(proof: Option<Box<ChangeProofContext>>)
351403 crate :: invoke_with_handle ( proof, drop)
352404}
353405
354- impl crate :: MetricsContextExt for ChangeProofContext {
406+ impl crate :: MetricsContextExt for ChangeProofContext < ' _ > {
355407 fn metrics_context ( & self ) -> Option < firewood_metrics:: MetricsContext > {
356408 None
357409 }
0 commit comments