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

Provide a Rustier wrapper for zcash_script #171

Merged
merged 7 commits into from
Sep 17, 2024

Conversation

sellout
Copy link
Collaborator

@sellout sellout commented Aug 8, 2024

This adds a Script trait that exposes slightly Rustier types in order to have a common interface for the existing C++ implementation as well as the upcoming Rust implementation (and a third instance that runs both and checks that the Rust result matches the C++ one).

@sellout
Copy link
Collaborator Author

sellout commented Aug 8, 2024

ZcashFoundation/zebra#8751 integrates this with zebrad, and shouldn’t be merged until we know that that relationship is working (which it is currently not).

@sellout sellout force-pushed the wrap-bindgen branch 3 times, most recently from 707fc0b to 3c353bd Compare August 9, 2024 05:59
Have lib.rs re-export them, but make room for the upcoming Rust implementation.
@sellout sellout force-pushed the wrap-bindgen branch 2 times, most recently from b9e0ea0 to 92d9ceb Compare August 12, 2024 19:34
This adds a `Script` trait that exposes slightly Rustier types in order
to have a common interface for the existing C++ implementation as well
as the upcoming Rust implementation (and a third instance that runs both
and checks that the Rust result matches the C++ one).

The module structure (interpreter.rs, zcash_script.rs) and locations of
definitions are intended to mirror the structure of the C++ code, especially as
we get the Rust implementation in place, for easier comparison. That
organization is very likely to change once everything has been checked.
Cargo.toml Outdated Show resolved Hide resolved
src/interpreter.rs Outdated Show resolved Hide resolved
Copy link
Collaborator

@nuttycom nuttycom left a comment

Choose a reason for hiding this comment

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

Flushing a few initial comments.

Cargo.toml Outdated Show resolved Hide resolved
src/interpreter.rs Show resolved Hide resolved
src/zcash_script.rs Show resolved Hide resolved
src/zcash_script.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
src/zcash_script.rs Outdated Show resolved Hide resolved
@sellout sellout marked this pull request as ready for review August 30, 2024 20:09
@sellout
Copy link
Collaborator Author

sellout commented Aug 30, 2024

The changes in ZcashFoundation/zebra#8751 now work correctly with this, so we’re good for final review.

src/lib.rs Show resolved Hide resolved
Copy link
Contributor

@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 is looking great, thank you for making these changes.

I still need to review this in-depth, but, at a glance, I don't see any blockers.

src/interpreter.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@conradoplg conradoplg left a comment

Choose a reason for hiding this comment

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

It looks good, thanks! I think we just need to look a bit more into whether passing a Rust function pointer across FFI is safe.

src/zcash_script.rs Outdated Show resolved Hide resolved
src/lib.rs Show resolved Hide resolved
src/cxx.rs Outdated Show resolved Hide resolved
This should fix the Windows build.
@conradoplg conradoplg merged commit 9d16e79 into ZcashFoundation:master Sep 17, 2024
16 checks passed
Copy link
Contributor

@str4d str4d left a comment

Choose a reason for hiding this comment

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

Post-hoc review.

Comment on lines +1 to +17
bitflags::bitflags! {
/// The different SigHash types, as defined in <https://zips.z.cash/zip-0143>
///
/// TODO: This is currently defined as `i32` to match the `c_int` constants in this package, but
/// should use librustzcash’s `u8` constants once we’ve removed the C++.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct HashType: i32 {
/// Sign all the outputs
const All = 1;
/// Sign none of the outputs - anyone can spend
const None = 2;
/// Sign one of the outputs - anyone can spend the rest
const Single = 3;
/// Anyone can add inputs to this transaction
const AnyoneCanPay = 0x80;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It's incorrect to represent HashType as purely bitflags, because the lower bits are not valid to interpret as flags. That is, HashType::All | HashType::None should not be considered equal to HashType::Single, but that is what this type enables.

It's not immediately obvious to me where the boundary between "upper bitflags" and "lower type number" should be, but that should be figured out at some point.

///
/// __NB__: Linux uses `u32` for the underlying C++ enum while Windows uses `i32`, so `i64` can
/// hold either.
Unknown(i64),
Copy link
Contributor

Choose a reason for hiding this comment

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

The repr(u32) combined with the explicit enum discriminants and the documentation for this field, indicates that the expectation is that this field contains the C++ enum discriminant if it doesn't match an existing one. But that's not how repr(u32) works. This enum will instead have the following layout:

#[repr(C)]
union ErrorRepr {
    Ok: ErrorVariantOk,
    VerifyScript: ErrorVariantVerifyScript,
    InvalidScriptSize: ErrorVariantInvalidScriptSize,
    Unknown: ErrorVariantUnknown,
}

#[repr(u32)]
enum ErrorTag {
    Ok = 0,
    VerifyScript = 7,
    InvalidScriptSize, // Implicit = 8
    Unknown, // Implicit = 9
}

#[repr(C)]
struct ErrorVariantOk(ErrorTag);

#[repr(C)]
struct ErrorVariantVerifyScript(ErrorTag);

#[repr(C)]
struct ErrorVariantInvalidScriptSize(ErrorTag, TryFromIntError);

#[repr(C)]
struct ErrorVariantUnknown(ErrorTag, i64);

It is therefore unsuitable for passing across an FFI, which is the only reason we'd want to add #[repr(u32)] and the explicit discriminants. In particular, two things are misleading:

  • Error::Unknown will have an implicit discriminant that potentially collides with a real discriminant on the C++ side.
  • Error::Unknown's field is disjoint from the discriminant values.

Remove the #[repr(u32)] and the explicit discriminants.

Unknown(i64),
}

/// All signature hashes are 32 bits, since they are necessarily produced by SHA256.
Copy link
Contributor

Choose a reason for hiding this comment

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

We've not used SHA-256 for sighashes since Overwinter. See ZIPs 143, 243, and 244.

Suggested change
/// All signature hashes are 32 bits, since they are necessarily produced by SHA256.
/// All signature hashes are 32 bytes, since they are either:
/// - a SHA-256 output (for v1 or v2 transactions).
/// - a BLAKE2b-256 output (for v3 and above transactions).

/// the transaction itself. In particular, the sighash for the spend
/// is obtained using a callback function.
///
/// - sighash_callback: a callback function which is called to obtain the sighash.
Copy link
Contributor

Choose a reason for hiding this comment

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

This field doesn't appear anywhere in the trait method. Is it meant to refer to sighash: SighashCalculator?

// function.
let script_code_vec =
unsafe { std::slice::from_raw_parts(script_code, checked_script_code_len) };
let ctx = ctx as *const SighashCalculator;
Copy link
Contributor

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 fine for now, but we definitely will want to improve how the callbacks are handled. I don't have a good sense of how the lifetimes are being passed around.

@sellout sellout deleted the wrap-bindgen branch September 26, 2024 21:56
sellout added a commit to sellout/zcash_script that referenced this pull request Sep 27, 2024
Notably, `HashType` has changed incompatibly, so
ZcashFoundation/zebra#8751 will need to be updated.
sellout added a commit to sellout/zcash_script that referenced this pull request Sep 27, 2024
Notably, `HashType` has changed incompatibly, so
ZcashFoundation/zebra#8751 will need to be updated.
@sellout
Copy link
Collaborator Author

sellout commented Sep 29, 2024

Post-hoc review.

@str4d’s review has been addressed in #175.

conradoplg pushed a commit that referenced this pull request Oct 24, 2024
* Address Str4d’s comments on #171

Notably, `HashType` has changed incompatibly, so
ZcashFoundation/zebra#8751 will need to be updated.

* Apply suggestions from code review

Co-authored-by: Jack Grigg <[email protected]>

* Restrict bitflags used for `HashType` in v5 tx

---------

Co-authored-by: Jack Grigg <[email protected]>
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.

5 participants