Skip to content
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

equihash: Add Rust API for Tromp solver #1083

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open

Conversation

str4d
Copy link
Contributor

@str4d str4d commented Jan 4, 2024

No description provided.

components/equihash/src/lib.rs Outdated Show resolved Hide resolved
Copy link

codecov bot commented Jan 4, 2024

Codecov Report

Attention: Patch coverage is 92.80000% with 9 lines in your changes are missing coverage. Please review.

Project coverage is 66.82%. Comparing base (f7c29f0) to head (b201c10).
Report is 10 commits behind head on main.

❗ Current head b201c10 differs from pull request most recent head 76131db. Consider uploading reports for the commit 76131db to get more accurate results

Files Patch % Lines
components/equihash/src/blake2b.rs 70.83% 7 Missing ⚠️
components/equihash/src/tromp.rs 97.95% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1083      +/-   ##
==========================================
+ Coverage   63.23%   66.82%   +3.58%     
==========================================
  Files         124      134      +10     
  Lines       14507    19622    +5115     
==========================================
+ Hits         9174    13113    +3939     
- Misses       5333     6509    +1176     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@str4d
Copy link
Contributor Author

str4d commented Apr 18, 2024

Force-pushed to clean up the commit history; no changes to @teor2345's changes, just regrouping them (and squashing some of the fixes into the original commits that didn't make sense to keep in standalone commits).

@str4d
Copy link
Contributor Author

str4d commented Apr 18, 2024

Force-pushed to GitHub-linkify an imported source link in a commit message.

@str4d str4d closed this Apr 18, 2024
@str4d str4d deleted the equihash-solver-tromp branch April 18, 2024 20:14
@str4d str4d restored the equihash-solver-tromp branch April 18, 2024 20:14
@str4d str4d reopened this Apr 18, 2024
@str4d
Copy link
Contributor Author

str4d commented Apr 18, 2024

And somehow I managed to click two wrong buttons in a row 🤦

.collect()
.collect();

// Just in case the solver returns solutions that become the same when compressed.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think this should ever be the case. The compressed representation should not be lossy, it just packs the indices together to remove the effective padding bits that exist in the u32 representation.

Copy link

@arya2 arya2 May 4, 2024

Choose a reason for hiding this comment

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

It may not have been observed, or there could be a subtle difference between the C code and the Rust code in compress_array().

Update: Yeah, something must be wrong if identical solutions were observed, I'll check for changes to the logic in equi_miner.c, everything else looks equivalent to the zcashd logic.

Update: Duplicate solutions were observed prior to a bug fix, is there any chance that the solver could produce duplicate uncompressed solutions?

@@ -70,13 +71,21 @@ unsafe fn worker(eq: *mut CEqui, p: verify::Params, curr_state: &State) -> Vec<V
let nsols = equi_nsols(eq);
let sols = equi_sols(eq);
let solution_len = 1 << p.k;
//println!("{nsols} solutions of length {solution_len} at {sols:?}");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm inclined to remove this and the other introduced debugging print statements, unless anyone else disagrees.

@str4d
Copy link
Contributor Author

str4d commented Apr 18, 2024

Force-pushed to clean up the feature flagging in the build script, and add C #ifdefs to prevent some warnings.

@str4d
Copy link
Contributor Author

str4d commented Apr 18, 2024

Rebased on current main. I will rebase this PR again once #1357 merges.

@str4d
Copy link
Contributor Author

str4d commented Apr 19, 2024

Rebased on main to fix merge conflicts after #1357 was merged.

@str4d
Copy link
Contributor Author

str4d commented Apr 19, 2024

Force-pushed to move the new minimal-form compression methods into the new submodule from #1357.

@str4d
Copy link
Contributor Author

str4d commented Apr 19, 2024

Force-pushed to adjust the minimal-form compression method APIs and integrate them into the existing tests.

/// `next_nonce` function.
///
/// Returns zero or more unique solutions.
pub fn solve_200_9<const N: usize>(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think there is value in exposing the API for uncompressed solutions; block headers only use compressed solutions, and this crate's other existing API is only for verifying compressed solutions. So I'll probably just make this internal.

What I do want to think about before merging is how to handle configurable parameters. We will need at least two parameter sets (for mainnet and regtest), and depending on how that will be enabled in the C code, we might end up supporting any parameter set. But if we don't then we should probably instead have an enum or a generic parameter for selecting which Equihash parameters to solve with.

Copy link

@arya2 arya2 May 4, 2024

Choose a reason for hiding this comment

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

I don't think there is value in exposing the API for uncompressed solutions; block headers only use compressed solutions, and this crate's other existing API is only for verifying compressed solutions. So I'll probably just make this internal.

This is currently true for Zebra's use case. I see no harm in exposing the API for uncompressed solutions either, so solutions could be compressed outside this crate more easily (though it seems unlikely that anyone will actually do that).

What I do want to think about before merging is how to handle configurable parameters. We will need at least two parameter sets (for mainnet and regtest), and depending on how that will be enabled in the C code, we might end up supporting any parameter set. But if we don't then we should probably instead have an enum or a generic parameter for selecting which Equihash parameters to solve with.

It may be useful to allow for configurable parameters for custom testnets to allow for picking even easier parameters instead of disabling PoW checks altogether for ephemeral testnets, or harder ones than those used by regtest for testnets that are intended to be used for longer periods.

Let me know what you decide so we can make the parameters configurable in Zebra if they're configurable here.

Update: Configurable parameters don't seem easy to add, let's allow the Regtest parameters later but leave it there.

@str4d str4d marked this pull request as ready for review April 19, 2024 02:34
@arya2 arya2 self-requested a review June 20, 2024 15:05
Copy link

@arya2 arya2 left a comment

Choose a reason for hiding this comment

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

This looks like it should work so far, and none of my suggestions are blockers, feel free to resolve any/all of them without making changes.

I'm still reviewing equi_miner.c in detail, but everything else looks equivalent to the logic in zcashd, at least for the Mainnet parameters.

Update: Collision errors are still being logged here (and DuplicateIdxs errors).

@@ -0,0 +1,47 @@
// Equihash solver
// Copyright (c) 2016-2016 John Tromp, The Zcash developers
Copy link

Choose a reason for hiding this comment

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

Suggested change
// Copyright (c) 2016-2016 John Tromp, The Zcash developers
// Copyright (c) 2016 John Tromp, The Zcash developers

// example on how to get the endian conversion functions on different
// platforms.

// Downloaded from https://raw.githubusercontent.com/mikepb/endian.h/master/endian.h
Copy link

Choose a reason for hiding this comment

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

Suggested change
// Downloaded from https://raw.githubusercontent.com/mikepb/endian.h/master/endian.h
// Downloaded and modified from https://raw.githubusercontent.com/mikepb/endian.h/master/endian.h

// https://github.com/zcash/zcash/blob/6fdd9f1b81d3b228326c9826fa10696fc516444b/src/crypto/equihash.cpp#L39-L76
#[cfg(any(feature = "solver", test))]
fn compress_array(array: &[u8], bit_len: usize, byte_pad: usize) -> Vec<u8> {
let index_bytes = (u32::BITS / 8) as usize;
Copy link

Choose a reason for hiding this comment

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

For consistency:

Suggested change
let index_bytes = (u32::BITS / 8) as usize;
let index_bytes = size_of::<u32>();

Comment on lines +37 to +38
)
.wrapping_shl(8 * (in_width - x - 1) as u32); // Big-endian
Copy link

@arya2 arya2 Jul 5, 2024

Choose a reason for hiding this comment

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

This seems like an unnecessary change from the c++ logic, it should panic in minimal_from_indices() before it gets here when using parameters that would be affected by the change.

Suggested change
)
.wrapping_shl(8 * (in_width - x - 1) as u32); // Big-endian
) << (8 * (in_width - x - 1) as u32); // Big-endian

#[cfg(any(feature = "solver", test))]
pub(crate) fn minimal_from_indices(p: Params, indices: &[u32]) -> Vec<u8> {
let c_bit_len = p.collision_bit_length();
let index_bytes = (u32::BITS / 8) as usize;
Copy link

Choose a reason for hiding this comment

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

For consistency:

Suggested change
let index_bytes = (u32::BITS / 8) as usize;
let index_bytes = size_of::<u32>();

Comment on lines +12 to +13
_f: [u8; 0],
_m: PhantomData<(*mut u8, PhantomPinned)>,
Copy link

Choose a reason for hiding this comment

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

Could we add a short explanation here?

eq->blake2b_update(state, (uchar *)&leb, sizeof(u32));
eq->blake2b_finalize(state, hash, HASHOUT);
eq->blake2b_free(state);
// Avoid use-after-free and double-free
Copy link

Choose a reason for hiding this comment

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

It may be a good idea to add this warning to the blake2b_free() function docs too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants