-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[pallet-transaction-storage] Improved check_proof check + tests + docs
#10153
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
Changes from 10 commits
ee90a9b
d5506de
1453f12
4c3d67f
cd2d2d3
3085804
98ef883
d0bc14c
92923d7
f0eed0b
bc743b3
b306af7
98fcd56
29ffae2
f670f90
513fa70
f96557b
04cb7d7
36aea9d
23eee5b
7d1b78a
15ad008
4556d1f
ba6a7a1
d988c36
03def22
c6057b0
59e7642
fae8ba9
f0c12a8
596b27b
cd1fb01
390ee76
fca0b92
97cf2fa
b7d6d08
084a681
4e35eab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| title: '[pallet-transaction-storage] Improved `check_proof` check + tests + docs' | ||
| doc: | ||
| - audience: Runtime Dev | ||
| description: |- | ||
| **This PR:** | ||
|
|
||
| * Fixes `check_proof` and its `binary_search_by_key` chunk | ||
| * Adds the `ensure_chunk_proof_works` test, which covers all possible chunk index build/verify proof roundtrips (to catch all corner cases) | ||
| * Improves docs around `pallet-transaction-storage` | ||
| crates: | ||
| - name: pallet-transaction-storage | ||
| bump: patch | ||
| - name: sp-transaction-storage-proof | ||
| bump: patch | ||
bkontur marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - name: sp-io | ||
| bump: patch | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,8 @@ | |
|
|
||
| //! Storage proof primitives. Contains types and basic code to extract storage | ||
| //! proofs for indexed transactions. | ||
| //! | ||
| //! Note: We use `u32` for both `total_chunks` and the chunk index. | ||
bkontur marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| #![cfg_attr(not(feature = "std"), no_std)] | ||
|
|
||
|
|
@@ -104,7 +106,7 @@ impl sp_inherents::InherentDataProvider for InherentDataProvider { | |
| mut error: &[u8], | ||
| ) -> Option<Result<(), Error>> { | ||
| if *identifier != INHERENT_IDENTIFIER { | ||
| return None | ||
| return None; | ||
| } | ||
|
|
||
| let error = InherentError::decode(&mut error).ok()?; | ||
|
|
@@ -114,14 +116,25 @@ impl sp_inherents::InherentDataProvider for InherentDataProvider { | |
| } | ||
|
|
||
| /// A utility function to extract a chunk index from the source of randomness. | ||
| /// | ||
| /// Note: The caller must ensure that `total_chunks` is not 0. | ||
bkontur marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| pub fn random_chunk(random_hash: &[u8], total_chunks: u32) -> u32 { | ||
| let mut buf = [0u8; 8]; | ||
| buf.copy_from_slice(&random_hash[0..8]); | ||
| let random_u64 = u64::from_be_bytes(buf); | ||
| (random_u64 % total_chunks as u64) as u32 | ||
| } | ||
|
|
||
| /// A utility function to encode transaction index as trie key. | ||
| /// A utility function to calculate the number of chunks. | ||
| /// | ||
| /// * `bytes` - number of bytes | ||
| pub fn num_chunks(bytes: u32) -> u32 { | ||
| (bytes as u64).div_ceil(CHUNK_SIZE as u64) as u32 | ||
| } | ||
|
|
||
| /// A utility function to encode the transaction index as a trie key. | ||
| /// | ||
| /// * `input` - chunk index. | ||
| pub fn encode_index(input: u32) -> Vec<u8> { | ||
| codec::Encode::encode(&codec::Compact(input)) | ||
| } | ||
|
|
@@ -163,13 +176,12 @@ pub mod registration { | |
| .saturating_sub(DEFAULT_STORAGE_PERIOD.into()); | ||
| if number.is_zero() { | ||
| // Too early to collect proofs. | ||
| return Ok(InherentDataProvider::new(None)) | ||
| return Ok(InherentDataProvider::new(None)); | ||
| } | ||
|
|
||
| let proof = match client.block_indexed_body(number)? { | ||
| Some(transactions) if !transactions.is_empty() => | ||
| Some(build_proof(parent.as_ref(), transactions)?), | ||
| Some(_) | None => { | ||
| Some(transactions) => Some(build_proof(parent.as_ref(), transactions)?), | ||
| None => { | ||
| // Nothing was indexed in that block. | ||
| None | ||
| }, | ||
|
|
@@ -182,19 +194,20 @@ pub mod registration { | |
| random_hash: &[u8], | ||
| transactions: Vec<Vec<u8>>, | ||
| ) -> Result<TransactionStorageProof, Error> { | ||
| let mut db = sp_trie::MemoryDB::<Hasher>::default(); | ||
| // Get total chunks, we will need it to generate a random chunk index. | ||
| let total_chunks: u32 = transactions.iter().map(|t| num_chunks(t.len() as u32)).sum(); | ||
| if total_chunks.is_zero() { | ||
| const MSG: &str = "No chunks to build proof for."; | ||
| return Err(Error::Application(Box::from(MSG))); | ||
| } | ||
|
Comment on lines
203
to
205
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this now an error? This will make the block production fail. Before it was just not providing any proof, which is the sensible thing to do here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
oh, good point, thank you, you're right, my bad, please check here: 04cb7d7 I also tried to remove that @bkchr What would you suggest here? I also talked to @arkpar and added an early return when the chunk is found, since we don’t need to process the subsequent transactions. |
||
| let target_chunk_index = random_chunk(random_hash.as_ref(), total_chunks); | ||
bkontur marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| let mut db = sp_trie::MemoryDB::<Hasher>::default(); | ||
| let mut target_chunk = None; | ||
| let mut target_root = Default::default(); | ||
| let mut target_chunk_key = Default::default(); | ||
| let mut chunk_proof = Default::default(); | ||
|
|
||
| let total_chunks: u64 = | ||
| transactions.iter().map(|t| t.len().div_ceil(CHUNK_SIZE) as u64).sum(); | ||
| let mut buf = [0u8; 8]; | ||
| buf.copy_from_slice(&random_hash[0..8]); | ||
| let random_u64 = u64::from_be_bytes(buf); | ||
| let target_chunk_index = random_u64 % total_chunks; | ||
| // Generate tries for each transaction. | ||
| let mut chunk_index = 0; | ||
| for transaction in transactions { | ||
|
|
@@ -244,5 +257,9 @@ pub mod registration { | |
| &[(encode_index(0), Some(proof.chunk))], | ||
| ) | ||
| .unwrap(); | ||
|
|
||
| // Fail for empty transactions/chunks. | ||
| assert!(build_proof(&random, vec![]).is_err()); | ||
| assert!(build_proof(&random, vec![vec![]]).is_err()); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.