Skip to content

Commit 78b027b

Browse files
committed
tests(sighash): add sighash bitmask tests
1 parent 9ebcf3e commit 78b027b

File tree

7 files changed

+747
-1
lines changed

7 files changed

+747
-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: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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.transaction import Transaction, TxInput, TxOutput
21+
from hathor.transaction.exceptions import ScriptError
22+
from hathor.transaction.scripts.script_context import ScriptContext
23+
from hathor.transaction.scripts.sighash import SighashAll, SighashBitmask
24+
25+
26+
def test_defaults() -> None:
27+
settings = Mock()
28+
settings.MAX_NUM_OUTPUTS = 99
29+
30+
context = ScriptContext(settings=settings, stack=Mock(), logs=[], extras=Mock())
31+
32+
tx = Transaction(
33+
inputs=[
34+
TxInput(tx_id=b'tx1', index=0, data=b''),
35+
TxInput(tx_id=b'tx2', index=1, data=b''),
36+
],
37+
outputs=[
38+
TxOutput(value=11, script=b''),
39+
TxOutput(value=22, script=b''),
40+
]
41+
)
42+
43+
assert context._sighash == SighashAll()
44+
assert context.get_tx_sighash_data(tx) == tx.get_sighash_all_data()
45+
assert context.get_selected_outputs() == set(range(99))
46+
47+
48+
def test_set_sighash() -> None:
49+
context = ScriptContext(settings=Mock(), stack=Mock(), logs=[], extras=Mock())
50+
51+
sighash = SighashBitmask(inputs=0b111, outputs=0b101)
52+
context.set_sighash(sighash)
53+
assert context._sighash == sighash
54+
55+
with pytest.raises(ScriptError):
56+
context.set_sighash(sighash)
57+
58+
59+
@pytest.mark.parametrize(
60+
['outputs_bitmask', 'selected_outputs'],
61+
[
62+
(0b00, set()),
63+
(0b01, {0}),
64+
(0b10, {1}),
65+
(0b11, {0, 1}),
66+
]
67+
)
68+
def test_sighash_bitmask(outputs_bitmask: int, selected_outputs: set[int]) -> None:
69+
settings = Mock()
70+
settings.MAX_NUM_INPUTS = 88
71+
settings.MAX_NUM_OUTPUTS = 99
72+
73+
context = ScriptContext(settings=settings, stack=Mock(), logs=[], extras=Mock())
74+
tx = Transaction(
75+
inputs=[
76+
TxInput(tx_id=b'tx1', index=0, data=b''),
77+
TxInput(tx_id=b'tx2', index=1, data=b''),
78+
],
79+
outputs=[
80+
TxOutput(value=11, script=b''),
81+
TxOutput(value=22, script=b''),
82+
]
83+
)
84+
85+
sighash_bitmask = SighashBitmask(inputs=0b11, outputs=outputs_bitmask)
86+
context.set_sighash(sighash_bitmask)
87+
88+
data = tx.get_custom_sighash_data(sighash_bitmask)
89+
assert context.get_tx_sighash_data(tx) == hashlib.sha256(data).digest()
90+
91+
with pytest.raises(ScriptError) as e:
92+
context.set_sighash(Mock())
93+
94+
assert str(e.value) == 'Cannot modify sighash after it is already set.'
95+
assert context.get_selected_outputs() == selected_outputs

0 commit comments

Comments
 (0)