Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Create opaque struct for StorageProof. #3834

Merged
merged 1 commit into from
Oct 31, 2019

Conversation

jimpo
Copy link
Contributor

@jimpo jimpo commented Oct 16, 2019

Passing around Vec<Vec<u8>> everywhere is gross and confusing and breaks encapsulation.

@jimpo jimpo requested a review from cheme October 16, 2019 13:12
Copy link
Contributor

@cheme cheme left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems pretty straight forward, and actually a very good change.
Still a few comments to address (the one on cht may be counter productive to address).

Ok(())
}, ())?;

Ok(proof.into_iter().collect())
Ok(merge_storage_proofs(proofs))
Copy link
Contributor

@cheme cheme Oct 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the old version where we did not cat vecs: here we are eating memory. Maybe an iterator version of merge_storage_proofs will get the best of both world.
Actually merge_storage_proofs takes an iterator as input, so there is maybe something todo, does not seems super easy though since it neends to transform for_each_cht_group to an iterator.

Edit: may not be worth addressing.

.collect::<HashSet<_>>()
.into_iter()
.collect();
let total_proof = merge_storage_proofs(vec![init_proof, exec_proof]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vec allocation can be avoided here, especially since merge_storage_proofs takes an iterator as input.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How exactly do you recommend creating this iterator? I think something like iter::once(init_proof).chain(iter::once(exec_proof)) is a bit clunky.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not have better solution than using 'chain', I know some people prefer using Some(init_proof) rather than iter::once, I prefer once.
Clunkier solution (I tend to like fn_iter but I never try to bench it but it should have no advantage over chain): https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8c36252915e606e2fbd845ac2618ffdd

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This really doesn't seem like a good reason to sacrifice readability to me. merge_storage_proofs internally has to create a new HashSet and much larger vector of trie nodes anyway.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course it is just an alloc among lot of others, I think crazy iter expression are rather common in the code base, but not abusing them is probably a good thing.

Regarding the other comment where we grow memory, you also can use a different approach, I know some poeple like this kind of api, I never really use it but it may be good here.

struct ProofMerger(Hashset<StorageProof>);

impl ProofMerger {
  fn add(self, proof: StorageProof) {
    self.0.extends(proof);
    self
  }
  fn merge(self) -> StorageProof {
    StorageProof { trie_nodes: self.0.into_iter().collect() }
  }

}

in this case would look like

let total_proof = ProofMerger(init_proof)
  .add(exec_proof)
  .merge();

Copy link
Contributor

@cheme cheme Oct 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that it is really only nitpicking, should not prevent merging this PR for me.

@jimpo jimpo added the A3-in_progress Pull request is in progress. No review needed at this stage. label Oct 17, 2019
Copy link
Contributor

@Demi-Marie Demi-Marie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, and definitely cleans things up.

Copy link
Contributor

@gui1117 gui1117 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems in all cases you can do merge_storage_proof(Hashet<Proof>) maybe change the signature to get an Hashset directly.

/// the keys covered by the proof. Verifying the proof requires constructing the partial trie from
/// the serialized nodes and performing the key lookups.
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
pub struct StorageProof {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be moved to substrate-primitives. State-machine just compiles on native, but the proof stuff is required on no-std as well e.g. in Cumulus.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I added a TrieProofWithValues struct in substrate-trie which has a no_std option. StorageProof in substrate-state-machine now wraps that.

The reason these need to be separate is because a TrieBackend is an abstration over a single trie which is defined in substrate-state-machine. In particular, it allows storage accesses to child tries which traverse multiple tries, a concept foreign to substrate-trie.

Currently, these structs are both very simple and are basically just a vec of encoded nodes. In the future, though, it would be nice to create trie proofs that omit redundant internal hashes and storage proofs that internally contain multiple separate trie proofs, one for the root trie and one for each child trie.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we please get a method into_inner() or similar?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, yes, I removed the whole TrieProof thing and there's a StorageProof::iter_nodes(self) method which iterates over all trie nodes.

Copy link
Member

@bkchr bkchr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some last nitpicks.

/// the keys covered by the proof. Verifying the proof requires constructing the partial trie from
/// the serialized nodes and performing the key lookups.
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
pub struct StorageProof {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we please get a method into_inner() or similar?

@jimpo jimpo force-pushed the storage-proof-struct branch from 78f0495 to 030605a Compare October 30, 2019 13:26
Passing around Vec<Vec<u8>> everywhere is gross and confusing and
breaks encapsulation.
@jimpo jimpo force-pushed the storage-proof-struct branch from 030605a to 5a623ad Compare October 30, 2019 13:32
@jimpo jimpo added A0-please_review Pull request needs code review. and removed A3-in_progress Pull request is in progress. No review needed at this stage. labels Oct 30, 2019
@bkchr bkchr merged commit 8a22faa into paritytech:master Oct 31, 2019
HCastano added a commit to HCastano/substrate that referenced this pull request Nov 11, 2019
HCastano added a commit that referenced this pull request Dec 12, 2019
HCastano added a commit that referenced this pull request Jan 10, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A0-please_review Pull request needs code review.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants