-
Notifications
You must be signed in to change notification settings - Fork 1.6k
backing-availability-audit: Move ErasureChunk Proof to BoundedVec #3626
backing-availability-audit: Move ErasureChunk Proof to BoundedVec #3626
Conversation
e660af4
to
2541526
Compare
node/core/av-store/src/lib.rs
Outdated
@@ -1177,7 +1178,7 @@ fn store_available_data( | |||
let erasure_chunks = chunks.iter().zip(branches.map(|(proof, _)| proof)).enumerate().map( | |||
|(index, (chunk, proof))| ErasureChunk { | |||
chunk: chunk.clone(), | |||
proof, | |||
proof: Proof::try_from(proof).unwrap(), |
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.
Note: I am not sure what the best approach is here.
Since the obtain_chunks
function (c.f. here) and branches
function (c.f. here returns an AsRef<[u8]> and Vec<Vec> respectively, it may be prudent to modify those functions to return the actual Proof object. However, this is a failable conversion and would need proper error-handling, e.g. modifying the .map
above to .filter_map
One option would be to have the erasure-coding return the Proof as oppose to the AsRef<[u8]>
and the Vec<Vec<u8>>
. But before I go ahead and do that it maybe be best to sync up regarding the proper approach.
@@ -16,11 +16,14 @@ | |||
|
|||
use super::*; | |||
|
|||
use std::convert::TryFrom; |
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 there's no use of this trait anywhere (it seems), I believe it may be incorrect to implement the TryFrom
trait as oppose to just making a Method on 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.
How would it be incorrect?
node/primitives/src/lib.rs
Outdated
let mut out = Vec::new(); | ||
for element in self.0.iter() { | ||
let temp = element.as_vec(); | ||
let mut element_vec = [0u8; MERKLE_NODE_MAX_SIZE]; |
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.
This is not the tersest way to store Merkle proofs, but it works. I wanted to have something working quickly in order to get some of the higher-level questions out of the way.
Strangely, the Merkle proofs generated in the polkadot tests seem to always an outer length 2, which I currently don't fully understand. The inner length varies from 3 to 347 and I also don't fully understand why.
Based on how we store these proofs as a Vec<Vec>, we can choose the best way to bound the inner and out vectors.
node/primitives/src/lib.rs
Outdated
@@ -51,6 +52,9 @@ pub use disputes::{ | |||
SignedDisputeStatement, UncheckedDisputeMessage, ValidDisputeVote, | |||
}; | |||
|
|||
const MERKLE_NODE_MAX_SIZE: usize = 347; | |||
const MERKLE_PROOF_MAX_DEPTH: usize = 3; |
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.
Note that these constants would not work in practice. I've just set them to what I observed being crated in the tests. It is my belief that the depth is no more than 3 (in fact it's not more than 2) throughout all tests because we don't ever create a Merkle Tree with that many elements, i.e. intermediate nodes.
The correct values to use here are:
MERKLE_NODE_MAX_SIZE = 512;
MERKLE_PROOF_MAX_DEPTH = 8;
I just found it interesting that the node max size was uneven. I may be mistaken, but I'm quite certain the max proof node created is 347 bytes.
2541526
to
576c48f
Compare
With the limits in place, how big can a proof become now? |
4096 (= 512 * 8) bytes is the maximum size of the Proof with this change, as oppose to the 500KB protocol-level limit. |
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 would prefer the error handling to not be static strings, with the exception of CodecError
unless there is a particular requirement to do so.
Besides that, 👍
node/primitives/src/lib.rs
Outdated
const MERKLE_NODE_MAX_SIZE: usize = 512; | ||
const MERKLE_PROOF_MAX_DEPTH: usize = 8; |
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.
Adding comments as to why these were chosen as they are would be appreciated.
node/primitives/src/lib.rs
Outdated
} | ||
|
||
impl TryFrom<Vec<Vec<u8>>> for Proof { | ||
type Error = &'static str; |
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 would argue for a
#[derive(thiserror::Error)]
enum Error {
#[error("erkle max proof depth exceeded {0} > {} .", MERKLE_PROOF_MAX_DEPTH)]
MerkleProofDepthExceeded(depth),
// ..
}
node/primitives/src/lib.rs
Outdated
let mut out = Vec::new(); | ||
for element in input.into_iter() { | ||
let data: BoundedVec<u8, 1, MERKLE_NODE_MAX_SIZE> = | ||
BoundedVec::from_vec(element).map_err(|_| "Merkle node max size exceeded.")?; |
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.
CC
node/primitives/src/lib.rs
Outdated
BoundedVec::from_vec(element).map_err(|_| "Merkle node max size exceeded.")?; | ||
out.push(data); | ||
} | ||
Ok(Proof(BoundedVec::from_vec(out).expect("Buffer size is deterined above. QED"))) |
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.
Ok(Proof(BoundedVec::from_vec(out).expect("Buffer size is deterined above. QED"))) | |
Ok(Proof(BoundedVec::from_vec(out).expect("Buffer size is already checked; qed"))) |
|
||
impl Proof { | ||
/// This function allows to convert back to the standard nested Vec format | ||
pub fn as_vec(&self) -> 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.
nit/future: Do we truely require a Vec<Vec<_>>
? Or could we avoid all those allocations by employing a single BoundedVec<_>
and providing a Vec<&[]>
or even &[&[]]
? Again, not for this PR, a potential future optimization.
5eb17d3
to
bf72823
Compare
bf72823
to
f442c43
Compare
self.current_pos += 1; | ||
Some((nodes, chunk.as_ref())) | ||
Proof::try_from(nodes).ok().map(|proof| (proof, chunk.as_ref())) |
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.
Not sure it is the right behavior to just let the iterator end if the Proof
could not be constructed. Can this even happen, without the code having a logic error?
@@ -363,7 +363,7 @@ impl RunningTask { | |||
|
|||
fn validate_chunk(&self, validator: &AuthorityDiscoveryId, chunk: &ErasureChunk) -> bool { | |||
let anticipated_hash = | |||
match branch_hash(&self.erasure_root, &chunk.proof, chunk.index.0 as usize) { | |||
match branch_hash(&self.erasure_root, &chunk.proof_as_vec(), chunk.index.0 as usize) { |
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't we fix branch_hash
in order to get rid of that conversion?
} | ||
} | ||
|
||
impl Decode for 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.
I am missing tests, to make sure Encode/Decode works and is compatible to what we had before.
8ed41b6
to
5a4ad8e
Compare
* master: backing-availability-audit: Move ErasureChunk Proof to BoundedVec (#3626) Substrate Companion #9575 (#3695) Fill up requests slots via `launch_parallel_requests` (#3681) Bump serde_json from 1.0.64 to 1.0.66 (#3669) substrate #9202 companion: Multiple vesting schedules (#3407) XCM: Introduce versioning to dispatchables' params (#3693) remove dead_code from chain selection test (#3685) Improve MultiLocation conversion functions in xcm-procedural (#3690)
Addresses: https://github.com/paritytech/srlabs_findings/issues/100
This PR moved the
proof
field inErasureChunk
to a nested set ofBoundedVec
s.