Equihash validator#1
Conversation
There was a problem hiding this comment.
You need to leak st (std::mem::forget) or the Vec will deallocate and this function will always return a dangling pointer.
If I were you, I would encapsulate all of this so that we can be sure there are no memory safety problems. Create a structure containing *mut crypto_generichash_blake2b_state. It'll be a transmuted pointer from a Box::into_raw, which you can get with Vec::into_boxed_slice if you want to keep crypto_generichash_statebytes dynamic. You can Box::from_raw to get the box back in your Drop impl.
Also, use std::mem::align_of to test that u8 and crypto_generichash_blake2b_state have the same alignment.
There was a problem hiding this comment.
Replaced with blake2b crate.
There was a problem hiding this comment.
Perhaps use byteorder crate to break n and k up instead.
There was a problem hiding this comment.
IIRC the reason I did it this way is because n and k are different types to bare chars, and I couldn't get the type checker to be satisfied. But this was my first Rust code, so I should definitely try that 😃
There was a problem hiding this comment.
self.hash.iter().take(len).all(|v| v == 0)
There was a problem hiding this comment.
It looks like these lifetimes can be elided.
There was a problem hiding this comment.
let hash: Vec<_> = a.hash.iter()
.zip(b.hash.iter())
.skip(trim)
.map(|(a, b)| a ^ b)
.collect();or something like that would be more idomatic.
There was a problem hiding this comment.
a.hash.iter().zip(b.hash.iter()).take(len).all(|(a, b)| a == b)
There was a problem hiding this comment.
#![derive(Clone)] for Node instead of manually doing it.
There was a problem hiding this comment.
collapse these uses together like
use libc::{
uint32_t,
uint64_t,
c_uchar,
size_t
};There was a problem hiding this comment.
Since you always test against both, make a function that returns the result and panics if the result is different between them.
|
Consider rather than using libsodium, use https://crates.io/crates/blake2b since it offers personalization. (Not sure if we can cache states and things like that with it though.) |
|
@ebfull comments addressed. I also tried adding benchmarks locally, but couldn't get |
There was a problem hiding this comment.
a.indicies.iter().zip(b.indicies.iter()).all(|(a, b)| a != b)
There was a problem hiding this comment.
AIUI all() runs the closure on every item in the iterator, which in this case is pairs of indices (a.indices[i], b.indices[i]) due to the zip(). What we actually want is to run the closure on every possible pair of indices (a.indices[i], b.indices[j]).
There was a problem hiding this comment.
Ah. itertools::cartesian_product would be useful here if only I felt good about bringing that into the dependency tree. :)
There was a problem hiding this comment.
Is this section performance critical or are these lists big? This could be n log(n) if we first sorted the lists. I'm surprised there isn't a trivial way to do this in Rust, but I couldn't find anything immediately.
There was a problem hiding this comment.
Why not do .extend()? Then a and b don't need to be mutable references, and in other parts of your code mutable temporaries don't need to be created.
There was a problem hiding this comment.
Switched to .extend(), and also realised that in the recursive case, I can take ownership of the underlying child Nodes, and extend one's indices with the other to eliminate half of the copying cost.
There was a problem hiding this comment.
I can't do that in the iterative case because I'm using chunks() which is a representation, which I can't take ownership of (unless there's a clever way to do so).
|
@ebfull addressed your comments, and also fixed a bug with certain Equihash parameters (including our mainnet ones) by porting over |
|
Rebased on master, and switched to using @gtank's |
| .skip(trim) | ||
| .map(|(a, b)| a ^ b) | ||
| .collect(); | ||
| let mut indices = Vec::new(); |
There was a problem hiding this comment.
Use with_capacity here if you can.
There was a problem hiding this comment.
Ah. itertools::cartesian_product would be useful here if only I felt good about bringing that into the dependency tree. :)
| return rows[0].is_zero(hash_len); | ||
| } | ||
|
|
||
| fn tree_validator<'a>(p: &Params, state: &Blake2b, indices: &[u32]) -> Option<Node> { |
There was a problem hiding this comment.
The 'a lifetime isn't used.
|
re-ACK |
|
Rebased on master to fix merge conflicts. |
| .map(|(a, b)| a ^ b) | ||
| .collect(); | ||
| let indices = if a.indices_before(&b) { | ||
| let mut indices = a.indices; |
There was a problem hiding this comment.
I think that on average this will end up performing a re-allocation. I wouldn't be surprised if it's just as efficient to always use from_children_ref.
There was a problem hiding this comment.
If not, would it be modifying a's indices? In either event, I am in favor of removing this method and just having from_children_ref which we could then remove the '_ref' from
| fn indices_before(&self, other: &Node) -> bool { | ||
| // Indices are serialized in big-endian so that integer | ||
| // comparison is equivalent to array comparison | ||
| self.indices[0] < other.indices[0] |
There was a problem hiding this comment.
I don't know enough about Equihash to determine if this is a sufficient check.
There was a problem hiding this comment.
The name of this method could be changed to is_first_index_before or something like that. I think saying indices is misleading.
| } | ||
|
|
||
| fn generate_hash(base_state: &Blake2b, i: u32) -> Blake2bResult { | ||
| let mut lei = vec![]; |
There was a problem hiding this comment.
It would be better to create a fixed-width stack-allocated array here.
let mut lei = [0u8; 4];
(&mut lei[..]).write_u32::<LittleEndian>(i).unwrap();|
Rebased on master to fix merge conflicts, and addressed @ebfull's comment. |
| 512 / self.n | ||
| } | ||
| fn hash_output(&self) -> u8 { | ||
| (self.indices_per_hash_output() * self.n / 8) as u8 |
There was a problem hiding this comment.
self.indices_per_hash_output() * self.n / 8
=(512 / self.n) * self.n / 8
=(512 * self.n) / (self.n * 8)
=512/8
=64
There was a problem hiding this comment.
(512 / self.n) is floor division, so this isn't commutative.
| // error!("Invalid solution: duplicate indices"); | ||
| return None; | ||
| } | ||
| Some(Node::from_children(a, b, p.collision_byte_length())) |
There was a problem hiding this comment.
I think we should extract this into a method (from the line starting "if !has_collision") and then reuse it in is_valid_solution_iterative. It's a short block of code, but it will have the added benefit of making this look much nicer.
This would lend itself to consolidating the from_children functions.
| fn indices_before(&self, other: &Node) -> bool { | ||
| // Indices are serialized in big-endian so that integer | ||
| // comparison is equivalent to array comparison | ||
| self.indices[0] < other.indices[0] |
There was a problem hiding this comment.
The name of this method could be changed to is_first_index_before or something like that. I think saying indices is misleading.
| .map(|(a, b)| a ^ b) | ||
| .collect(); | ||
| let indices = if a.indices_before(&b) { | ||
| let mut indices = a.indices; |
There was a problem hiding this comment.
If not, would it be modifying a's indices? In either event, I am in favor of removing this method and just having from_children_ref which we could then remove the '_ref' from
There was a problem hiding this comment.
Is this section performance critical or are these lists big? This could be n log(n) if we first sorted the lists. I'm surprised there isn't a trivial way to do this in Rust, but I couldn't find anything immediately.
| Some(Node { | ||
| hash: expand_array(&hash.as_bytes()[start..end], p.collision_bit_length(), 0), | ||
| indices: vec![i], | ||
| }) |
There was a problem hiding this comment.
It would be nice to extract this else block in to a function. This would make more clear the similarities and differences between this function and the iterative version.
| } | ||
|
|
||
| assert!(rows.len() == 1); | ||
| return rows[0].is_zero(hash_len); |
There was a problem hiding this comment.
Why do we use hash_len here rather than p.collision_byte_length()?
There was a problem hiding this comment.
The final round collides two chunks instead of one, and since we've been subtracting from hash_len along the way, it's the correct length, so it's fine to say "just check the entire remaining hash is zero".
Follows Zcash implementation as closely as possible.
|
@Eirik0 and I paired to refactor some of the code, and also rebased this on master to fix merge conflicts. |
|
ACK. |
Implement Sapling components of ZIP 32
Migrate curve traits and tests, and WNAF, from pairing
fix: use rust2018 idioms
ZIP-225/244 #1: Minor refactoring and preparatory updates.
Minor cleanups
* add stubbed version of getting balances for wallet summary * adds helper methods at top level and partially implements filter for unspent notes * add map to store block end heights for tree shards * add orchard as well * accounts table * implement block_height_extrema * add impl of update_chain_tip * remove some unwraps from get_summary * add impls for get_max_checkpointed_height * add impl of get_target_and_anchor_heights * fix get_transaction * add impl for get_min_unspent_height * add get_memo impl * adds comment for truncate_to_height. Will implement this in future * tracing debug * yep * update last_Scanned_height outside tx loop * add updating orchard balance * clippy * add impls for inputSource trait * add sorting when selecting notes * Update Cargo.toml * serde block * allow constructing nullifiers * serde notes * serde transactions * top level in progress * scan queue * export error type * start account serde * serde account birthday * move serialization to separate module * clippy fix * skip * add unimplemented warnings for account import from seed phrase * remove unused imports * Remove Uivk * serde ufvk * Keep track of current diversifier idx * serde account * serde accounts * only serde left is on shard tree * almost done shardstore * use new trait * remote visit_seq from orchard note wrapper * remove unused array module * more nullifier to use trait * serialize shard store. need to deser now * serde memoryshardstore * serd shardtree * bring remaing code across for scanning * add orchard tree handling in put_blocks * only import if orchard enabled * Network Wrapper * done! cleanup time * clippy * break into modules * clippy fix * fmt * fmt * clean up frontierwrapper structs * add implementation of DataStoreFactor for memwallet * add back loading accounts from seeds only for testing * switch to use mutable ref in testing trait * partial implementation of scan_complete * fix bug from attepting to iterate over a reverse range in Rust * type safe tree and frontier serde * fmt * rename * more rename * fix issue with non-inclusive range * more cleanup * forgot to import * more clean * add proper recording of spend notes and memos * add partially implemented get_tx_history * ByteArray Trait * more refactor * clippy\ fix * Rename * cippy cix * proptest for shardtree serde * add fix for orchard support * more clean * donezo * clippy * add wasm-bindgen feature * move to upstream imt and shardtree patch * migrate 3 more tests across * adds new trait method to implement change_note_spends_succeed * Serde SendNoteTable * clippy fix * fmt * remove comment * Fix tree serialization when wallet has just been initialized * remove the raw field on get_tx_history as it broke tests * fix issue querying notes * add error handling * migrate send_multi_step_proposed_transfer * fix wallet deser * add data_api methods to allow fetching historical checkpoints * global fmt * fix clippy lints * fix test * add logic for detecting pending notes in wallet summary * fix serialization * fmt * change_note_spends_succeed passing for sapling! * add scan progress * add more comprehensive scan progress calculatioon * add unscanned ranges functionality * feature gate orchard import * add updating scan ranges on wallet import * fix bug in shard_end for updating ranges * passing birthday range scan check * cargo lock * screen out locked transactions when computing balance * fix missing nullifiers for orchard * retrievel all inputs in get_spendable_notes rather than filtering * rewrite note selection strategy to be split per pool * fix checkpoint getter for tests * fix clippy lints * switch from maybe_rayon to rayon for memwallet * remove proptest genereated file * move MemBlockCache to own module outside of testing * implement BlockCache and BlockSource * works! * change back batch size of batchrunners until further experimentation * TODO on for_each_checkpoint on sql store * switch from parking_lot to wasm_sync * add new records for transaction and add_partial method * mostly complete adding transparent output logic * remove debug * re add async trait * remove comment * test passing for shielding a transparent output * add start of implementation of data retrieval queue * add the extra features to wallet_read we need for tests * migrate tests to librustzcash and make generic over wallet impl * add test module for transparent tests * implement get_transparent_balance and get first transparent test passing * fix sqllite test * add truncate_to_height and gets transparent tests passing * global fmt * remove all IntoIterator impls * handle unwrap in get_spendable_transparent_outputs * serde on transparent related tables * fmt sqlite changes * remove incorrect todo * proto * make all methods in sync public * add from impl * adds serialization attributes to librustzcash types * format * add serialization to enable proposals to be serialized * put client memory back * add derives for noteId * add derives for FeeRule * add NoteSummary type to serialize notes * proto * fix transparent_balance_across_shielding * fix account delta * remove prints * handle ephemeral addrs * Add todo * default legacy transparent * filling in store_decrypted_tx * funding_accounts * multi_step passes * fmt * remove some prints * fmt * fmt * fmt * fix suggestions * ensure tests build with all feature flag combinations * removes serde skip from addresses field for an account and adds intermediate types to enable serialization * merge upstream and fix missing implementations. tests not passing * return a zero recovery ratio for now * fixing is_change error * fix nullifier spend detection logic * clippy lint * add tx account detection for utxo sending only. All tests passing * Willem's refactor * re-add .cargo * re-add editor config * keep willems branch with main * fix proto * remove whitespace --------- Co-authored-by: Willem Olding <willemolding@gmail.com>
…14868de..d978256a2 d978256a2 Merge pull request #1 from zcash/compact_tx_transparent 7eeb82e7c Merge pull request #4 from zcash/add_changelog a95359dc9 Apply suggestions from code review 592b637a8 Add transparent data to the `CompactBlock` format. 9d1fb2c41 Add a CHANGELOG.md that documents the evolution of the light client protocol. 180717dfa Merge pull request #3 from zcash/merge_librustzcash_history 450bd4181 Merge the history of the .proto files from `librustzcash` for complete history preservation. a4859d11d Move protobuf files into place for use in `zcash/lightwallet-protocol` 2e66cdd9e Update zcash_client_backend/proto/service.proto eda012519 fix comment f838d10ad Add gRPC LightdInfo Donation Address db12c0415 Merge pull request #1473 from nuttycom/wallet/enrichment_queue 698feba96 Apply suggestions from code review 20ce57ab3 zcash_client_backend: Add `block_height` argument to `decrypt_and_store_transaction` a6dea1da8 Merge pull request #1482 from zancas/doc_tweak 4d2d45fc9 fix incorrect doc-comment e826f4740 update CompactBlock doc-comment, to cover non-Sapling shielded notes, and addresses e9a6c00bf Various documentation improvements 988bc7214 Merge pull request #872 from nuttycom/feature/pre_dag_sync-suggest_scan_ranges 58d07d469 Implement `suggest_scan_ranges` and `update_chain_tip` a9222b338 Address comments from code review. e20310857 Rename proto::compact::{BlockMetadata => ChainMetadata} ac63418c5 Reorganize Sapling and Orchard note commitment tree sizes in CompactBlock. 0fdca14f1 zcash_client_backend: Add note commitment tree sizes to `CompactBlock` serialization. 2a0c2b8b7 zcash_client_backend: Add gRPC bindings behind feature flag 1342f0480 zcash_client_backend: Address compact_formats.proto comments 68aa4e01b zcash_client_backend: Bring in latest `compact_formats.proto` e712eb1bc Add prevHash field to CompactBlock 440384c3e Build protobufs for compact formats git-subtree-dir: zcash_client_backend/lightwallet-protocol git-subtree-split: d978256a2bf33bb5f83846d8994e7377512f5f05
…14868de..23f0768ea 23f0768ea Release lightwallet-protocol v0.4.0 41156c767 Merge pull request #11 from zcash/feature/get_mempool_tx_pools 7c130e883 Add `lightwalletProtocolVersion` field to `LightdInfo` struct. edbb726d7 Apply suggestion from code review 38fddd73b Apply suggestions from code review 0250f2720 Add pool type filtering to `GetMempoolTx` argument. 54ccaadd5 Change semantics of pool-based pruning of compact transactions from "may prune" to "must prune". b0667ec99 Merge pull request #9 from zcash/2025-11-doc-TransparentAddressBlockFilter f3fea7bd4 doc: TransparentAddressBlockFilter doesn't include mempool a67dd323a Merge pull request #8 from zcash/2025-11-lightdinfo-upgrade-info 11da4b7e3 add next upgrade info to LightdInfo structure (GetLightdInfo) 42cd8f720 Transparent data docs update (#7) c0cf957ac Merge pull request #5 from zcash/2025-11-comments 912fc3609 Minor clarification in GetBlockRange documentation. 6b03f2cce Documentation (comments) only d978256a2 Merge pull request #1 from zcash/compact_tx_transparent 7eeb82e7c Merge pull request #4 from zcash/add_changelog a95359dc9 Apply suggestions from code review 592b637a8 Add transparent data to the `CompactBlock` format. 9d1fb2c41 Add a CHANGELOG.md that documents the evolution of the light client protocol. 180717dfa Merge pull request #3 from zcash/merge_librustzcash_history 450bd4181 Merge the history of the .proto files from `librustzcash` for complete history preservation. a4859d11d Move protobuf files into place for use in `zcash/lightwallet-protocol` 2e66cdd9e Update zcash_client_backend/proto/service.proto eda012519 fix comment f838d10ad Add gRPC LightdInfo Donation Address db12c0415 Merge pull request #1473 from nuttycom/wallet/enrichment_queue 698feba96 Apply suggestions from code review 20ce57ab3 zcash_client_backend: Add `block_height` argument to `decrypt_and_store_transaction` a6dea1da8 Merge pull request #1482 from zancas/doc_tweak 4d2d45fc9 fix incorrect doc-comment e826f4740 update CompactBlock doc-comment, to cover non-Sapling shielded notes, and addresses e9a6c00bf Various documentation improvements 988bc7214 Merge pull request #872 from nuttycom/feature/pre_dag_sync-suggest_scan_ranges 58d07d469 Implement `suggest_scan_ranges` and `update_chain_tip` a9222b338 Address comments from code review. e20310857 Rename proto::compact::{BlockMetadata => ChainMetadata} ac63418c5 Reorganize Sapling and Orchard note commitment tree sizes in CompactBlock. 0fdca14f1 zcash_client_backend: Add note commitment tree sizes to `CompactBlock` serialization. 2a0c2b8b7 zcash_client_backend: Add gRPC bindings behind feature flag 1342f0480 zcash_client_backend: Address compact_formats.proto comments 68aa4e01b zcash_client_backend: Bring in latest `compact_formats.proto` e712eb1bc Add prevHash field to CompactBlock 440384c3e Build protobufs for compact formats git-subtree-dir: zcash_client_backend/lightwallet-protocol git-subtree-split: 23f0768ea4471b63285f3c0e9b6fbb361674aa2b
No description provided.