Skip to content

Commit 57c2516

Browse files
Fix import (#105)
* Fix circular import with script * Tx with no inputs or outputs is not valid * Small refactor
1 parent a7ad275 commit 57c2516

File tree

8 files changed

+46
-29
lines changed

8 files changed

+46
-29
lines changed

btclib/alias.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
# Hash digest constructor: it may be any name suitable to hashlib.new()
7474
HashF = Callable[[], Any]
7575
# HashF = Callable[[Any], Any]
76+
H160_Net = Tuple[bytes, str]
7677

7778
# Elliptic curve point in affine coordinates.
7879
# Warning: to make Point a NamedTuple would slow down the code
@@ -97,3 +98,10 @@
9798
# of the INF Point
9899
# QJ = Q[0], Q[1], 1 if Q[1] else 0
99100
INFJ = 7, 0, 0
101+
102+
# TODO add type hinting to script_tree
103+
# unfortunately recursive type hinting is not supported
104+
# https://github.com/python/mypy/issues/731
105+
# TaprootLeaf = Tuple[int, Script]
106+
# TaprootScriptTree = List[Union[Any, TaprootLeaf]]
107+
TaprootScriptTree = Any

btclib/b32.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@
4646

4747
from typing import Iterable
4848

49-
from btclib.alias import Octets, String
49+
from btclib.alias import Octets, String, TaprootScriptTree
5050
from btclib.bech32 import decode, encode
5151
from btclib.exceptions import BTClibValueError
5252
from btclib.hashes import hash160, sha256
5353
from btclib.network import NETWORKS, network_from_key_value
54-
from btclib.script import TaprootScriptTree, output_pubkey
54+
from btclib.script import output_pubkey
5555
from btclib.to_pub_key import Key, pub_keyinfo_from_key
5656
from btclib.utils import bytes_from_octets
5757

btclib/hashes.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,11 @@
1212
from __future__ import annotations
1313

1414
import hashlib
15-
from typing import Callable, Sequence, Tuple
15+
from typing import Callable, Sequence
1616

1717
from btclib.alias import HashF, Octets
1818
from btclib.utils import bytes_from_octets
1919

20-
H160_Net = Tuple[bytes, str]
21-
2220
# see https://bugs.python.org/issue47101
2321
# With OpenSSL 3.x, hashlib still includes ripemd160
2422
# but it is not usable unless the legacy provider is loaded.

btclib/psbt/psbt.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,12 @@ def __init__(
101101

102102
def assert_valid(self) -> None:
103103
"""Assert logical self-consistency."""
104-
self.tx.assert_valid()
105-
106104
# ensure a non-null tx has been included
107105
if not (self.tx.vin and self.tx.vout):
108106
raise BTClibValueError("null transaction")
109107

108+
self.tx.assert_valid()
109+
110110
# ensure the tx is unsigned
111111
if any(tx_in.script_sig or tx_in.script_witness for tx_in in self.tx.vin):
112112
raise BTClibValueError("non empty script_sig or witness")

btclib/script/__init__.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,17 @@
1111
"""Module btclib.script."""
1212

1313
from btclib.script.script import Command, Script, op_int, parse, serialize
14-
from btclib.script.script_pub_key import (
14+
from btclib.script.taproot import (
15+
TaprootScriptTree,
16+
check_output_pubkey,
17+
input_script_sig,
18+
output_prvkey,
19+
output_pubkey,
20+
)
21+
from btclib.script.witness import Witness
22+
23+
# hack to prevent circular import
24+
from btclib.script.script_pub_key import ( # isort:skip
1525
ScriptPubKey,
1626
address,
1727
assert_nulldata,
@@ -31,14 +41,7 @@
3141
is_p2wsh,
3242
type_and_payload,
3343
)
34-
from btclib.script.taproot import (
35-
TaprootScriptTree,
36-
check_output_pubkey,
37-
input_script_sig,
38-
output_prvkey,
39-
output_pubkey,
40-
)
41-
from btclib.script.witness import Witness
44+
4245

4346
__all__ = [
4447
"Command",

btclib/script/taproot.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from typing import Any
1717

1818
from btclib import var_bytes
19-
from btclib.alias import Octets
19+
from btclib.alias import Octets, TaprootScriptTree
2020
from btclib.ec import Curve, mult, secp256k1
2121
from btclib.exceptions import BTClibValueError
2222
from btclib.hashes import tagged_hash
@@ -25,13 +25,6 @@
2525
from btclib.to_pub_key import Key, pub_keyinfo_from_key
2626
from btclib.utils import bytes_from_octets
2727

28-
# TODO add type hinting to script_tree
29-
# unfortunately recursive type hinting is not supported
30-
# https://github.com/python/mypy/issues/731
31-
# TaprootLeaf = Tuple[int, Script]
32-
# TaprootScriptTree = List[Union[Any, TaprootLeaf]]
33-
TaprootScriptTree = Any
34-
3528

3629
def tree_helper(script_tree: TaprootScriptTree) -> tuple[Any, bytes]:
3730
if len(script_tree) == 1:

btclib/tx/tx.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
from btclib.alias import BinaryData
3737
from btclib.exceptions import BTClibValueError
3838
from btclib.hashes import hash256
39-
from btclib.script import Witness
39+
from btclib.script.witness import Witness
4040
from btclib.tx.tx_in import TX_IN_COMPARES_WITNESS, TxIn
4141
from btclib.tx.tx_out import TxOut
4242
from btclib.utils import bytesio_from_binarydata
@@ -193,9 +193,13 @@ def assert_valid(self) -> None:
193193
if not 0 <= self.lock_time <= 0xFFFFFFFF:
194194
raise BTClibValueError(f"invalid lock time: {self.lock_time}")
195195

196+
if not self.vin:
197+
raise BTClibValueError("Missing inputs")
196198
for tx_in in self.vin:
197199
tx_in.assert_valid()
198200

201+
if not self.vout:
202+
raise BTClibValueError("Missing outputs")
199203
for tx_out in self.vout:
200204
tx_out.assert_valid()
201205

tests/tx/test_tx.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
def test_tx() -> None:
2424
# default constructor
25-
tx = Tx()
25+
tx = Tx(check_validity=False)
2626
assert not tx.is_segwit()
2727
assert not any(bool(w) for w in tx.vwitness)
2828
assert not any(bool(tx_in.script_witness) for tx_in in tx.vin)
@@ -40,15 +40,22 @@ def test_tx() -> None:
4040
assert tx.vsize == tx.size
4141
assert tx.weight == tx.size * 4
4242

43-
tx_2 = Tx.from_dict(tx.to_dict())
43+
with pytest.raises(BTClibValueError, match="Missing inputs"):
44+
tx.assert_valid()
45+
46+
tx_2 = Tx.from_dict(tx.to_dict(check_validity=False), check_validity=False)
4447
assert tx_2.is_segwit() == tx.is_segwit()
4548
assert tx_2 == tx
4649

47-
tx_2 = Tx.parse(tx.serialize(include_witness=True))
50+
tx_2 = Tx.parse(
51+
tx.serialize(include_witness=True, check_validity=False), check_validity=False
52+
)
4853
assert tx_2.is_segwit() == tx.is_segwit()
4954
assert tx_2 == tx
5055

51-
tx_2 = Tx.parse(tx.serialize(include_witness=False))
56+
tx_2 = Tx.parse(
57+
tx.serialize(include_witness=False, check_validity=False), check_validity=False
58+
)
5259
assert not tx_2.is_segwit()
5360
assert tx_2 == tx
5461

@@ -60,6 +67,10 @@ def test_tx() -> None:
6067
sequence = 0xFFFFFFFF
6168
tx_in = TxIn(prev_out, script_sig, sequence)
6269

70+
tx.vin = [tx_in]
71+
with pytest.raises(BTClibValueError, match="Missing outputs"):
72+
tx.assert_valid()
73+
6374
tx_out1 = TxOut(2500000, "a914f987c321394968be164053d352fc49763b2be55c87")
6475
tx_out2 = TxOut(
6576
6381891, "0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d"

0 commit comments

Comments
 (0)