-
Notifications
You must be signed in to change notification settings - Fork 766
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create a Basic Proving Trie for the Runtime #3881
Create a Basic Proving Trie for the Runtime #3881
Conversation
This reverts commit 1cfb29f.
For a full end to end usage in the runtime, I would like to create a test which uses this object to verify some data in a child trie. However, I don't think the child trie currently supports creating a proof, and not sure how to get that working. |
I'd kinda expect merkle proofs should always be batched, so you prove for many keys simultaniously. I've no worked out how much more efficent this makes them of course, but we so some dumb things like compressing, when the proofs should've never contained duplicates in the first palce. In principle, one could make this work in the runtme by using unsafe code which writes the queries into a buffer, and then some function in on_finalize checks them all. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For airdrops? Yea i guess its useful. Maybe AAS (airdrop as a service) would also help.
let mut root = Default::default(); | ||
|
||
{ | ||
let mut trie = TrieDBMutBuilderV0::new(&mut db, &mut root).build(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cheme why do we use a specific version here? Should this not be V1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general the version does not matter much for proof as V0 can be use on a V1 trie and V1 can be use on a V0, and since it is a proof we mainly care for access node (both version should have same access patterns).
In this case though, because we return the root, we need to choose the right version (proof content will be the same but root will differ).
In the case of historical, I think the value where all 32 bytes so V0 and V1 would work the same, but I kept V0 to be conservative.
For a generic toolling, the version should be pass in parameter (even if in most case there is no reason to use V0).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So you want me to make this a parameter we pass in? or make it V1, or leave it untouched?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
either V1 only (would not be compatible in some case) or pass it as a parameter.
in this case all insert are done at the same time and trie keep all change in memory before calculating the root. If considering batching between different extrinsic it is not doable currently (as the mem is not shared between two extrinsic (vm design I think)), I hope it will be doable with polkavm. |
Thinking of it, the best would be to use this inline root update calculation over ordered key value payload and store the stack of pending change (max size the max depth of the trie time max branch size and a few temp variables). This way it could be processed in multiple blocks or extrinsic. Edit : actually the first case is also unbounded in size since we need to also store store the proof content (between blocks/extrinsics). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code existing is fine, and I think extracting it can be a good move, I am just thinking it is pretty limited in its usages (small trie size, single query at this time, suboptimal merkle trie) so I would prefer if it was not part of sp-runtime but in its own crate.
Would make sense to also replace the existing code to use this one.
@@ -85,6 +85,7 @@ pub mod generic; | |||
pub mod legacy; | |||
mod multiaddress; | |||
pub mod offchain; | |||
pub mod proving_trie; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not too sure if it should be part of sp-runtime crate.
Thing is the choice of using this trie should not be a default for any runtime (I think it is not the best tree for most use case, and I would encourage more binary trie, anyway it is very use case specific).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have a binary trie available in Substrate?
Does this trie work along side child tries?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the proof is to be run on child trie, there is no choice but to use this trie indeed.
(if using child trie root to prove, please be careful the chain will not be switching from trie V0 to trie V1 (should not matter for crowdloan as iirc the values are <= 32 byte), as a migration may change child trie root.
About this trie version, the simpliest would probably be to just pass the version in parameter.
When I read the PR I did not have this use case in mind but it is true that if what we are proving is from substrate state, then it could make sense to have this in runtime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can I name this ProvingTrieBase16
, and that would be better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not too sure anymore what was my point 🤦
Probably at first reading I was thinking it should be an external crate, then realizing it is being tightly linked with substrate state could actually be fine.
ProvingTrieBase16
may not be a good idea as long as substrate does have a single trie backend.
Maybe it could simple be a module in sp-trie, but guess I am ok with things being this way.
.and_then(|raw| Value::decode(&mut &*raw).ok()) | ||
} | ||
|
||
fn create_proof(&self, key: Key) -> Option<Vec<Vec<u8>>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe better passing Vec and allow call multiple query here.
For now we can simply rename to create_single_value_access_proof I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated name and also added the multi-value API
Co-authored-by: Ankan <[email protected]>
I hope the outcome is similar and reusable enough that we push this PR to completion and use that. Do you think this is the case? |
This pull request has been mentioned on Polkadot Forum. There might be relevant details there: https://forum.polkadot.network/t/disposable-parachains-for-airdrops-and-other-ideas/5769/20 |
|
||
/// Create the full verification data needed to prove all `keys` and their values in the trie. | ||
/// Returns `None` if the nodes within the current `MemoryDB` are insufficient to create a | ||
/// proof. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we do not pass the trie version in parameter and use v1 as default, it seems important to me to state it in the doc. Also true for create_single_proof
Proof are created with latest substrate trie format, runtime using non latest should not use it (eg trie_version = 0).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Co-authored-by: Oliver Tale-Yazdi <[email protected]>
The CI pipeline was cancelled due to failure one of the required jobs. |
1cff666
This is a refactor and improvement from: #3881 - `sp_runtime::proving_trie` now exposes a `BasicProvingTrie` for both `base2` and `base16`. - APIs for `base16` are more focused on single value proofs, also aligning their APIs with the `base2` trie - A `ProvingTrie` trait is included which wraps both the `base2` and `base16` trie, and exposes all APIs needed for an end to end scenario. - A `ProofToHashes` trait is exposed which can allow us to write proper benchmarks for the merkle trie. --------- Co-authored-by: Ankan <[email protected]> Co-authored-by: Adrian Catangiu <[email protected]>
This is a refactor and improvement from: #3881 - `sp_runtime::proving_trie` now exposes a `BasicProvingTrie` for both `base2` and `base16`. - APIs for `base16` are more focused on single value proofs, also aligning their APIs with the `base2` trie - A `ProvingTrie` trait is included which wraps both the `base2` and `base16` trie, and exposes all APIs needed for an end to end scenario. - A `ProofToHashes` trait is exposed which can allow us to write proper benchmarks for the merkle trie. --------- Co-authored-by: Ankan <[email protected]> Co-authored-by: Adrian Catangiu <[email protected]>
This PR will introduce a
BasicProvingTrie
type, which makes it easy to construct and prove data in a base-16 merkle trie within the runtime.Data into the merkle trie only require that they implement
Encode
/Decode
.A FRAME compatible
TrieError
was created and added toDispatchError
.Expected usage is to construct the merkle trie with all data offline, and then place only the merkle root of that trie on-chain.
Also offchain, a user is given a compact merkle proof of some data they want to prove exists on the blockchain.
Then in the runtime, you can call
verify_single_value_proof
orverify_proof
with the root, proof, and the keys and values you want to verify exists in the merkle trie.Closes #3880
Contributes to #5400