Skip to content

Commit 890c630

Browse files
committed
tests(sighash): add sighash bitmask tests
1 parent 922055e commit 890c630

File tree

7 files changed

+749
-1
lines changed

7 files changed

+749
-1
lines changed

tests/tx/scripts/__init__.py

Whitespace-only changes.

tests/tx/scripts/test_p2pkh.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Copyright 2023 Hathor Labs
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from hathor.transaction.scripts import P2PKH, Opcode
16+
from hathor.transaction.scripts.sighash import InputsOutputsLimit, SighashBitmask
17+
18+
19+
def test_create_input_data_simple() -> None:
20+
pub_key = b'my_pub_key'
21+
signature = b'my_signature'
22+
data = P2PKH.create_input_data(public_key_bytes=pub_key, signature=signature)
23+
24+
assert data == bytes([
25+
len(signature),
26+
*signature,
27+
len(pub_key),
28+
*pub_key
29+
])
30+
31+
32+
def test_create_input_data_with_sighash_bitmask() -> None:
33+
pub_key = b'my_pub_key'
34+
signature = b'my_signature'
35+
inputs_bitmask = 0b111
36+
outputs_bitmask = 0b101
37+
sighash = SighashBitmask(inputs=inputs_bitmask, outputs=outputs_bitmask)
38+
data = P2PKH.create_input_data(public_key_bytes=pub_key, signature=signature, sighash=sighash)
39+
40+
assert data == bytes([
41+
1,
42+
inputs_bitmask,
43+
1,
44+
outputs_bitmask,
45+
Opcode.OP_SIGHASH_BITMASK,
46+
len(signature),
47+
*signature,
48+
len(pub_key),
49+
*pub_key
50+
])
51+
52+
53+
def test_create_input_data_with_inputs_outputs_limit() -> None:
54+
pub_key = b'my_pub_key'
55+
signature = b'my_signature'
56+
max_inputs = 2
57+
max_outputs = 3
58+
limit = InputsOutputsLimit(max_inputs=max_inputs, max_outputs=max_outputs)
59+
data = P2PKH.create_input_data(public_key_bytes=pub_key, signature=signature, inputs_outputs_limit=limit)
60+
61+
assert data == bytes([
62+
1,
63+
max_inputs,
64+
1,
65+
max_outputs,
66+
Opcode.OP_MAX_INPUTS_OUTPUTS,
67+
len(signature),
68+
*signature,
69+
len(pub_key),
70+
*pub_key
71+
])
72+
73+
74+
def test_create_input_data_with_sighash_bitmask_and_inputs_outputs_limit() -> None:
75+
pub_key = b'my_pub_key'
76+
signature = b'my_signature'
77+
inputs_bitmask = 0b111
78+
outputs_bitmask = 0b101
79+
max_inputs = 2
80+
max_outputs = 3
81+
sighash = SighashBitmask(inputs=inputs_bitmask, outputs=outputs_bitmask)
82+
limit = InputsOutputsLimit(max_inputs=max_inputs, max_outputs=max_outputs)
83+
data = P2PKH.create_input_data(
84+
public_key_bytes=pub_key,
85+
signature=signature,
86+
sighash=sighash,
87+
inputs_outputs_limit=limit
88+
)
89+
90+
assert data == bytes([
91+
1,
92+
inputs_bitmask,
93+
1,
94+
outputs_bitmask,
95+
Opcode.OP_SIGHASH_BITMASK,
96+
1,
97+
max_inputs,
98+
1,
99+
max_outputs,
100+
Opcode.OP_MAX_INPUTS_OUTPUTS,
101+
len(signature),
102+
*signature,
103+
len(pub_key),
104+
*pub_key
105+
])
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Copyright 2023 Hathor Labs
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import hashlib
16+
from unittest.mock import Mock
17+
18+
import pytest
19+
20+
from hathor.conf.settings import HathorSettings
21+
from hathor.transaction import Transaction, TxInput, TxOutput
22+
from hathor.transaction.exceptions import ScriptError
23+
from hathor.transaction.scripts.script_context import ScriptContext
24+
from hathor.transaction.scripts.sighash import SighashAll, SighashBitmask
25+
26+
27+
@pytest.mark.parametrize(['max_num_outputs'], [(99,), (255,)])
28+
def test_defaults(max_num_outputs: int) -> None:
29+
settings = Mock(spec_set=HathorSettings)
30+
settings.MAX_NUM_OUTPUTS = max_num_outputs
31+
32+
context = ScriptContext(settings=settings, stack=Mock(), logs=[], extras=Mock())
33+
34+
tx = Transaction(
35+
inputs=[
36+
TxInput(tx_id=b'tx1', index=0, data=b''),
37+
TxInput(tx_id=b'tx2', index=1, data=b''),
38+
],
39+
outputs=[
40+
TxOutput(value=11, script=b''),
41+
TxOutput(value=22, script=b''),
42+
]
43+
)
44+
45+
assert context._sighash == SighashAll()
46+
assert context.get_tx_sighash_data(tx) == tx.get_sighash_all_data()
47+
assert context.get_selected_outputs() == set(range(max_num_outputs))
48+
49+
50+
def test_set_sighash() -> None:
51+
context = ScriptContext(settings=Mock(), stack=Mock(), logs=[], extras=Mock())
52+
53+
sighash = SighashBitmask(inputs=0b111, outputs=0b101)
54+
context.set_sighash(sighash)
55+
assert context._sighash == sighash
56+
57+
with pytest.raises(ScriptError):
58+
context.set_sighash(sighash)
59+
60+
61+
@pytest.mark.parametrize(
62+
['outputs_bitmask', 'selected_outputs'],
63+
[
64+
(0b00, set()),
65+
(0b01, {0}),
66+
(0b10, {1}),
67+
(0b11, {0, 1}),
68+
]
69+
)
70+
def test_sighash_bitmask(outputs_bitmask: int, selected_outputs: set[int]) -> None:
71+
settings = Mock()
72+
settings.MAX_NUM_INPUTS = 88
73+
settings.MAX_NUM_OUTPUTS = 99
74+
75+
context = ScriptContext(settings=settings, stack=Mock(), logs=[], extras=Mock())
76+
tx = Transaction(
77+
inputs=[
78+
TxInput(tx_id=b'tx1', index=0, data=b''),
79+
TxInput(tx_id=b'tx2', index=1, data=b''),
80+
],
81+
outputs=[
82+
TxOutput(value=11, script=b''),
83+
TxOutput(value=22, script=b''),
84+
]
85+
)
86+
87+
sighash_bitmask = SighashBitmask(inputs=0b11, outputs=outputs_bitmask)
88+
context.set_sighash(sighash_bitmask)
89+
90+
data = tx.get_custom_sighash_data(sighash_bitmask)
91+
assert context.get_tx_sighash_data(tx) == hashlib.sha256(data).digest()
92+
93+
with pytest.raises(ScriptError) as e:
94+
context.set_sighash(Mock())
95+
96+
assert str(e.value) == 'Cannot modify sighash after it is already set.'
97+
assert context.get_selected_outputs() == selected_outputs

0 commit comments

Comments
 (0)