Skip to content

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

glevco
Copy link
Contributor

@glevco glevco commented Jan 8, 2024

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

  • New models SighashAll, SighashBitmask, SighashRange, and SighashLimit.
  • Implement evaluate_final_context() and change execute_eval() to call it.
  • Change execute_eval() and script_eval() to return the selected outputs from the context.
  • Create new opcodes and implement op_sighash_bitmask() and op_max_inputs_outputs().
  • Update op_checksig() to use the ScriptContext for signature verification.
  • Implement is_opcode_valid() and use it to gate new opcodes in execute_op_code().
  • Update P2PKH.create_input_data() to support SighashBitmask and SighashLimit.
  • Update ScriptContext with methods for interacting with sighash.
  • Implement Transaction.get_custom_sighash_data() and change get_sighash_all() to unify implementations.
  • Update TransactionVerifier.verify_inputs() to check for unsigned outputs.
  • Update existing tests.

@glevco glevco self-assigned this Jan 8, 2024
@glevco glevco marked this pull request as ready for review January 8, 2024 02:10
Copy link

codecov bot commented Jan 8, 2024

Codecov Report

Attention: Patch coverage is 63.33333% with 66 lines in your changes missing coverage. Please review.

Project coverage is 84.12%. Comparing base (dbcff55) to head (922055e).

Files with missing lines Patch % Lines
hathor/transaction/scripts/opcode.py 29.78% 32 Missing and 1 partial ⚠️
hathor/transaction/scripts/script_context.py 54.83% 12 Missing and 2 partials ⚠️
hathor/transaction/scripts/p2pkh.py 20.00% 6 Missing and 2 partials ⚠️
hathor/transaction/transaction.py 66.66% 6 Missing ⚠️
hathor/transaction/scripts/sighash.py 90.00% 3 Missing ⚠️
hathor/verification/transaction_verifier.py 84.61% 1 Missing and 1 partial ⚠️
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.
📢 Have feedback on the report? Share it here.

@glevco glevco force-pushed the feat/sighash/bitmask branch from 9ebcf3e to 9042fd7 Compare January 8, 2024 20:10
@glevco glevco force-pushed the feat/sighash/bitmask branch 3 times, most recently from 5df23a6 to 9f778fd Compare January 19, 2024 22:13
outputs=bytes_to_int(outputs)
)
except Exception as e:
raise CustomSighashModelInvalid('Could not construct sighash bitmask.') from e
Copy link
Member

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.

Copy link
Contributor Author

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.

max_inputs=bytes_to_int(max_inputs),
max_outputs=bytes_to_int(max_outputs)
)
except Exception as e:
Copy link
Member

Choose a reason for hiding this comment

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

Same here.

Copy link
Contributor Author

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)

@glevco glevco force-pushed the feat/sighash/bitmask branch from b611297 to 783f397 Compare March 5, 2024 16:11
jansegre
jansegre previously approved these changes Mar 25, 2024
@glevco glevco added this to the Test Gabriel milestone Aug 8, 2024
@glevco glevco force-pushed the feat/sighash/bitmask branch from 783f397 to ebebb1f Compare August 12, 2024 23:40
Copy link

github-actions bot commented Aug 13, 2024

🐰 Bencher Report

Branchfeat/sighash/bitmask
Testbedubuntu-22.04
Click to view all benchmark results
BenchmarkLatencyBenchmark 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%)
🐰 View full continuous benchmarking report in Bencher

@glevco glevco force-pushed the feat/sighash/bitmask branch from ebebb1f to 3c1f9a7 Compare August 20, 2024 21:18
@glevco glevco force-pushed the feat/sighash/bitmask branch from 3c1f9a7 to a457f0a Compare December 9, 2024 23:14
@@ -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':
Copy link
Member

@msbrogli msbrogli Dec 10, 2024

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in 2f8579f

msbrogli
msbrogli previously approved these changes Dec 20, 2024
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)
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@glevco glevco force-pushed the feat/sighash/bitmask branch from 2f8579f to 6bbc55a Compare February 25, 2025 20:59
@glevco glevco moved this from In Review (WIP) to Todo in Hathor Network Mar 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Todo
Development

Successfully merging this pull request may close these issues.

3 participants