feat(rpc): add verifyutxochaintipinclusionproof RPC method#819
feat(rpc): add verifyutxochaintipinclusionproof RPC method#819bc1cindy wants to merge 3 commits intogetfloresta:masterfrom
Conversation
moisesPompilio
left a comment
There was a problem hiding this comment.
Can the name of this RPC be shorter, like verifyutxoproof?
This is the RPC's name on |
6c51864 to
04fbc6e
Compare
|
thanks for the review! @moisesPompilio done! |
04fbc6e to
f977dbc
Compare
|
thanks for the review! @csgui initially, I chose to remain faithful to the utreexod RPC implementation, but the validation of the proof size is important and it doesn't exist in utreexod, as well as explicit error handling, but all done now. Also added a test for the proof size. furthermore, I realized that
|
04435ee to
882aec3
Compare
8e354d2 to
93274ab
Compare
|
thanks guys! addressed all feedback : added ready for re-review! @moisesPompilio @csgui |
93274ab to
8e2deea
Compare
moisesPompilio
left a comment
There was a problem hiding this comment.
For now we don't use simple assert statements in the integration tests; we have to use the asserts from FlorestaTestFramework. I marked some asserts and suggested how they should use the FlorestaTestFramework asserts, but check FlorestaTestFramework for which ones you need to use instead of the simple assert calls you added.
73ca04f to
79589d2
Compare
|
hey @moisesPompilio I improved the test as a full integration using the existing frameworks. Utreexod is instantiated via add_node() (no external dependency), coinbase txids are obtained via getblock/getblockhash (no bdkwallet), and shutil.rmtree cleans up data between runs. It runs on CI without manual setup. |
79589d2 to
1a23b57
Compare
|
applied CI lint fix |
1a23b57 to
f573885
Compare
|
thanks! @moisesPompilio sugestions applied |
ff1081d to
0fdd2e0
Compare
|
updated commit descriptions |
8d469cd to
7fc11ea
Compare
|
extracted updated integration test |
139aaa5 to
e7f3f40
Compare
|
verbosity added thanks! @jaoleal |
jaoleal
left a comment
There was a problem hiding this comment.
Just a few more suggestions but everything looks fine already
e7f3f40 to
e1ab794
Compare
|
I moved the struct to also created this matches how |
I chose regarding JSON object parsing, to accept the proof as a JSON object, I'd need to add not sure it's worth it since happy to add, if you think it's the right direction though |
|
IMHO its important to keep efforts to maintain the idiomaticity and strong structure of the project because of some rust 101 principles but I agree to skip this to not over complicate the PR. WYT about opening a follow-up issue: "Strong typing for verifyutxoproof method" ?
I consider it to be worth some time exactly because its far more convenient and intuitive to understand the meaning of data when working on it, specially considering that |
rust-bitcoin has a
Since that's what utreexod does, I think it's fine to do that. Accepting both would be too much IMO |
|
Also,
|
csgui
left a comment
There was a problem hiding this comment.
Left some comments about proper error handling on unwrap
e1ab794 to
15da2e1
Compare
Implements utreexo accumulator proof UTXO verification for chain tip, based on the utreexod's reference implementation. This allows clients to cryptographically verify UTXO inclusion without maintaining the full UTXO set. - Parses hex-encoded proofs from utreexod's proveutxochaintipinclusion - Validates proof was generated at current chain tip (rejects stale proofs) - Verifies cryptographic proof against the accumulator state - Makes MAX_INPUTS_PER_BLOCK and MAX_PROOF_HASHES public for DoS bounds - Adds InvalidProof error variant with appropriate HTTP/JSON-RPC codes Returns true for valid proofs, false for well-formed but invalid proofs. Malformed or stale proofs return specific error types.
15da2e1 to
9d33279
Compare
nice, it's the same, I didn't know about it, thanks! changes applied |
Adds CLI command to verify utreexo proofs, making the RPC accessible via floresta-cli. Includes comprehensive documentation with usage examples showing both valid and invalid proof scenarios to help users understand expected behavior.
End-to-end test using florestad and utreexod that validates: - Input rejection: invalid hex, oversized proofs, empty input, truncated data - Valid proofs: generates and verifies proofs for all mined coinbase UTXOs - Invalid proofs: tampered data returns false, trailing bytes and wrong block hash return appropriate errors Also adds RPC bindings for both floresta (verifyutxochaintipinclusionproof) and utreexo (proveutxochaintipinclusion) in the test framework.
9d33279 to
8ccc35f
Compare
| /// Max hex-encoded proof size (DoS protection). | ||
| /// 4MB based on MAX_INPUTS_PER_BLOCK (24,386) with safety margin. | ||
| const MAX_PROOF_SIZE_BYTES: usize = 4 * 1024 * 1024; |
There was a problem hiding this comment.
Nit: is this the best place for this ?
| let valid = match stump.verify(&tip_proof.proof, &tip_proof.hashes_proven) { | ||
| Ok(valid) => valid, | ||
| Err(e) => { | ||
| return Err(JsonRpcError::InvalidProof(format!( | ||
| "Proof verification failed: {}", | ||
| e | ||
| ))) | ||
| } | ||
| }; |
There was a problem hiding this comment.
This can be better written with a map_err
| @@ -0,0 +1,126 @@ | |||
| //! Chain-tip inclusion proofs for the Utreexo accumulator. | |||
There was a problem hiding this comment.
| //! Chain-tip inclusion proofs for the Utreexo accumulator. | |
| // SPDX-License-Identifier: MIT OR Apache-2.0 | |
| //! Chain-tip inclusion proofs for the Utreexo accumulator. |
| /// The block hash at which this proof was generated. | ||
| pub proved_at_hash: BlockHash, | ||
| /// The Utreexo accumulator proof (targets + sibling hashes). | ||
| pub proof: Proof, | ||
| /// The raw leaf hashes that were proven. | ||
| pub hashes_proven: Vec<BitcoinNodeHash>, | ||
| } |
There was a problem hiding this comment.
| /// The block hash at which this proof was generated. | |
| pub proved_at_hash: BlockHash, | |
| /// The Utreexo accumulator proof (targets + sibling hashes). | |
| pub proof: Proof, | |
| /// The raw leaf hashes that were proven. | |
| pub hashes_proven: Vec<BitcoinNodeHash>, | |
| } | |
| /// The block hash at which this proof was generated. | |
| pub proved_at_hash: BlockHash, | |
| /// The Utreexo accumulator proof (targets + sibling hashes). | |
| pub proof: Proof, | |
| /// The raw leaf hashes that were proven. | |
| pub hashes_proven: Vec<BitcoinNodeHash>, | |
| } |
| #[derive(Debug, Serialize)] | ||
| pub struct VerifyChainTipProofVerbose { | ||
| pub valid: bool, | ||
|
|
||
| pub proved_at_hash: String, | ||
|
|
||
| pub targets: Vec<u64>, | ||
|
|
||
| pub num_proof_hashes: usize, | ||
|
|
||
| pub proof_hashes: Vec<String>, | ||
|
|
||
| pub hashes_proven: Vec<String>, | ||
| } | ||
|
|
| params = [proof] | ||
| if verbosity is not None: | ||
| params.append(verbosity) | ||
| return self.perform_request("verifyutxochaintipinclusionproof", params) |
There was a problem hiding this comment.
| params = [proof] | |
| if verbosity is not None: | |
| params.append(verbosity) | |
| return self.perform_request("verifyutxochaintipinclusionproof", params) | |
| return self.perform_request("verifyutxochaintipinclusionproof", [proof, verbosity]) |
| return self.perform_request("getmemoryinfo", params=[mode]) | ||
|
|
||
| def verifyutxochaintipinclusionproof( | ||
| self, proof: str, verbosity: int = None |
There was a problem hiding this comment.
| self, proof: str, verbosity: int = None | |
| self, proof: str, verbosity: int = 0 |
| match verbosity { | ||
| Some(v) => self.call( | ||
| "verifyutxochaintipinclusionproof", | ||
| &[Value::String(proof), Value::Number(Number::from(v))], | ||
| ), | ||
| None => self.call("verifyutxochaintipinclusionproof", &[Value::String(proof)]), | ||
| } |
There was a problem hiding this comment.
| match verbosity { | |
| Some(v) => self.call( | |
| "verifyutxochaintipinclusionproof", | |
| &[Value::String(proof), Value::Number(Number::from(v))], | |
| ), | |
| None => self.call("verifyutxochaintipinclusionproof", &[Value::String(proof)]), | |
| } | |
| self.call( | |
| "verifyutxochaintipinclusionproof", | |
| &[Value::String(proof), Value::Number(Number::from(verbosity.unwrap_or(0)))], | |
| ) |
| pub enum VerifyChainTipProofRes { | ||
| /// Verbosity 0: true/false | ||
| Bool(bool), | ||
|
|
||
| /// Verbosity 1: detailed proof info | ||
| Verbose(VerifyChainTipProofVerbose), | ||
| } |
There was a problem hiding this comment.
| pub enum VerifyChainTipProofRes { | |
| /// Verbosity 0: true/false | |
| Bool(bool), | |
| /// Verbosity 1: detailed proof info | |
| Verbose(VerifyChainTipProofVerbose), | |
| } | |
| /// Holds structured response for the verifychaintipproof rpc command by its verbosity level. | |
| pub enum VerifyChainTipProofRes { | |
| /// Verbosity 0: true/false | |
| Zero(bool), | |
| /// Verbosity 1: detailed proof info | |
| One(VerifyChainTipProofVerbose), | |
| } |




Hey guys, I talked to Davidson and we agreed I'd implement this RPC.
This PR implements the
verifyutxochaintipinclusionproofRPC method to verify utreexo accumulator proofs for chain tip UTXOs. It's based on the utreexod's reference implementation and allows clients to cryptographically verify UTXO inclusion without maintaining the full UTXO set.proveutxochaintipinclusionRPCPollard::from_roots()pollard.verify()to cryptographically verify inclusion by reconstructing the merkle path from proven hashes through the provided proof hashes up to the known rootstruefor valid proofs,falsefor invalid proofs and errors for malformed input.Running:
RPC:
./target/release/floresta-cli --network regtest verifyutxochaintipinclusionproof <proof>Test:
uv run tests/test_runner.py -k verifyutxochaintipinclusionproofTested with proofs generated from utreexod
proveutxochaintipinclusionRPC in regtest mode.