-
Notifications
You must be signed in to change notification settings - Fork 29
feat(sighash): implement sighash bitmask [part 1/7] #911
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
base: master
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #911 +/- ##
==========================================
- Coverage 84.26% 84.12% -0.15%
==========================================
Files 320 321 +1
Lines 24455 24601 +146
Branches 3744 3773 +29
==========================================
+ Hits 20608 20696 +88
- Misses 3113 3168 +55
- Partials 734 737 +3 ☔ View full report in Codecov by Sentry. |
9ebcf3e
to
9042fd7
Compare
5df23a6
to
9f778fd
Compare
outputs=bytes_to_int(outputs) | ||
) | ||
except Exception as e: | ||
raise CustomSighashModelInvalid('Could not construct sighash bitmask.') from e |
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.
How can we differentiate a code bug from a bad input? I feel that we should capture only the exceptions raised by the bytes_to_int()
and let all others blow up.
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.
Changed in b611297 to catch only pydantic.ValidationError
. The bytes_to_int()
function does not raise any exceptions explicitly, so the only expected exception to be raised here is the model validation from pydantic.
However, I believe this is a shortcoming caused by the try-except model itself. There's no way to formally differentiate between exceptions caused by bugs or bad inputs, because there's no formally typed list of expected exceptions. In my opinion we should improve this by using result monads throughout the code.
hathor/transaction/scripts/opcode.py
Outdated
max_inputs=bytes_to_int(max_inputs), | ||
max_outputs=bytes_to_int(max_outputs) | ||
) | ||
except Exception as e: |
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.
Same here.
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.
Done in b611297, see #911 (comment)
b611297
to
783f397
Compare
783f397
to
ebebb1f
Compare
|
Branch | feat/sighash/bitmask |
Testbed | ubuntu-22.04 |
Click to view all benchmark results
Benchmark | Latency | Benchmark Result minutes (m) (Result Δ%) | Lower Boundary minutes (m) (Limit %) | Upper Boundary minutes (m) (Limit %) |
---|---|---|---|---|
sync-v2 (up to 20000 blocks) | 📈 view plot 🚷 view threshold | 1.50 m(-7.99%)Baseline: 1.63 m | 1.47 m (97.82%) | 1.79 m (83.65%) |
ebebb1f
to
3c1f9a7
Compare
3c1f9a7
to
a457f0a
Compare
@@ -88,7 +107,7 @@ def evaluate_final_stack(stack: Stack, log: list[str]) -> None: | |||
raise FinalStackInvalid('\n'.join(log)) | |||
|
|||
|
|||
def script_eval(tx: Transaction, txin: TxInput, spent_tx: BaseTransaction) -> None: | |||
def script_eval(tx: Transaction, spent_tx: BaseTransaction, *, input_index: int) -> 'ScriptContext': |
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.
As you're already changing the method signature, you can put it all as kwargs only, right? The docstring is also outdated with the new signature.
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.
Done in 2f8579f
self.verify_script(tx=tx, input_tx=input_tx, spent_tx=spent_tx) | ||
script_context = self.verify_script(tx=tx, spent_tx=spent_tx, input_index=input_index) | ||
selected_outputs = script_context.get_selected_outputs() | ||
all_selected_outputs = all_selected_outputs.union(selected_outputs) |
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.
You can use all_selected_outputs.update(selected_outputs)
to prevent creating a new set.
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.
Done
2f8579f
to
6bbc55a
Compare
6bbc55a
to
922055e
Compare
Depends on #851
Motivation
Implement the new SIGHASH structure. This PR implements everything necessary for creating P2PKH inputs/outputs using
OP_SIGHASH_BITMASK
. That is, creating atomic swap txs is already fully functional, but gated to prevent a fork (this will later be toggled by Feature Activation).Adding support for
OP_SIGHASH_RANGE
and MultiSig is straightforward and will be done in separate PRs. Tests for new functionality will also be implemented in a separate PR.Acceptance Criteria
SighashAll
,SighashBitmask
,SighashRange
, andSighashLimit
.evaluate_final_context()
and changeexecute_eval()
to call it.execute_eval()
andscript_eval()
to return the selected outputs from the context.op_sighash_bitmask()
andop_max_inputs_outputs()
.op_checksig()
to use theScriptContext
for signature verification.is_opcode_valid()
and use it to gate new opcodes inexecute_op_code()
.P2PKH.create_input_data()
to supportSighashBitmask
andSighashLimit
.ScriptContext
with methods for interacting with sighash.Transaction.get_custom_sighash_data()
and changeget_sighash_all()
to unify implementations.TransactionVerifier.verify_inputs()
to check for unsigned outputs.