-
Notifications
You must be signed in to change notification settings - Fork 11
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
Conversation
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). |
707fc0b
to
3c353bd
Compare
Have lib.rs re-export them, but make room for the upcoming Rust implementation.
b9e0ea0
to
92d9ceb
Compare
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.
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.
Flushing a few initial comments.
The changes in ZcashFoundation/zebra#8751 now work correctly with this, so we’re good for final review. |
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 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.
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.
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.
b4ad098
to
eca9469
Compare
This should fix the Windows build.
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.
Post-hoc review.
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; | ||
} | ||
} |
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.
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), |
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.
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. |
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.
We've not used SHA-256 for sighashes since Overwinter. See ZIPs 143, 243, and 244.
/// 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. |
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 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; |
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 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.
Notably, `HashType` has changed incompatibly, so ZcashFoundation/zebra#8751 will need to be updated.
Notably, `HashType` has changed incompatibly, so ZcashFoundation/zebra#8751 will need to be updated.
* 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]>
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).