-
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
Initial Rust implementation #174
base: master
Are you sure you want to change the base?
Conversation
The files are intended to be compared side-by-side with the C++ implementation. E.g., script.rs first contains definitions from script.h, followed by definitions from script.cpp. |
629eed7
to
e95972b
Compare
The only changes here other than moving chunks of code around are - moving `evaluate` out of `impl Script`, which required changing `&self` to `script: &Script`; and - unifying `ExecutionOptions` with `VerificationFlags`.
For easier side-by-side comparison.
848ebaa
to
09ac96b
Compare
The C++ impl uses exceptions for `ScriptNum`, but catches them.
These tests run both the C++ and Rust impls. One uses completely arbitrary inputs, and the other limits script_sig to data pushes.
For now, the underlying errors are discarded when comparing against the C++ results, but there are corresponding changes on the C++ side in a separate branch.
09ac96b
to
4c34b3e
Compare
I don’t know why the test suite is failing on the C++ side now.
My only guess is that it is somehow caused by the secp256k1(_sys) dependency maybe forcing a different version of the underlying Since there’s a lot of noise in the logs, here’s the actual error:
|
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 comments; reviewed up to 336d0fe
src/interpreter.rs
Outdated
// Data is being pushed to the stack here; we may need to check | ||
// that the minimal script size was used to do so if our caller | ||
// requires it. | ||
if options.contains(VerificationFlags::MinimalData) |
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.
nonblocking: This is not a pure move; ideally this change would be split out into a separate commit.
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.
Do you mean because it’s not a const
with bitwise ops?
I can push this to a later PR and swing VerificationFlags
back to a bunch of const
s. I think I just wrote this bit before I decided to stick as close to the C++ as possible.
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.
Ah, I see. This is because of the commit it’s in, which is intended to just rearrange @ebfull’s implementation a bit.
src/interpreter.rs
Outdated
// of a standard tx rule, for example) they can | ||
// enable `discourage_upgradable_nops` to turn | ||
// these opcodes into errors. | ||
if options.contains(VerificationFlags::DiscourageUpgradableNOPs) { |
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.
ditto
src/interpreter.rs
Outdated
if !options.contains(VerificationFlags::CHECKLOCKTIMEVERIFY) { | ||
if options.contains(VerificationFlags::DiscourageUpgradableNOPs) { |
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.
ditto
src/script_error.rs
Outdated
pub enum ScriptError { | ||
Ok = 0, |
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'm not sure how big of a deal this is, but it might be better to be explicit about the exact binding to the C++ enum reprs for all of the cases; a reordering could accidentally cause these to get out of sync.
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 only reason for having the repr
is for the C++/Rust comparisons, where the C++ errors get mapped to the Rust ones. If they get out of sync, the tests should fail.
src/script_error.rs
Outdated
|
||
// Failed verify operations | ||
VERIFY, |
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.
Why are these all-caps?
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 ones that are all-caps map to an opcode, which are also all-caps. A better name might be something like FailedOP_VERIFY
(boy, that doesn’t look good either) or something, but this name just matches the C++ one.
It works on Rust 1.81 but it doesn't on 1.82. Which is super weird, but should give you a direction where to investigate |
This provides two more implementations of the
ZcashScript
trait. One is a pure Rust implementation and the other runs both the C++ and Rust implementations, returning the C++ result and logging a warning if they differ.This follows the C++ implementation extremely closely for ease of initial review. Future changes will move from this to a Rustier approach.
E.g.,
if
instead ofmatch
It differs from the C++ in some places, as well. Numeric types and casting/From are changed in some places to avoid partiality. Also, the Opcodes are partitioned into multiple types because we eventually want to be able to enforce
push_only
at the type level, andPushValue
operations carry data (although not yet in the structure), which makes them distinct from operations (PushdataBytelength
, in particular).Footnotes
accesses generally return in
Result
, so?
ensures they’re safe, but we keep the bounds checking so that the stack state matches the C++ version (e.g., without the bounds checking, an op that pops two elements off the stack could fail after popping one, which would leave the stack in a different state than the C++ impl which checks the length of the stack before popping either). ↩