Skip to content

Commit

Permalink
Fix import (#105)
Browse files Browse the repository at this point in the history
* Fix circular import with script

* Tx with no inputs or outputs is not valid

* Small refactor
  • Loading branch information
giacomocaironi authored Feb 6, 2023
1 parent a7ad275 commit 57c2516
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 29 deletions.
8 changes: 8 additions & 0 deletions btclib/alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
# Hash digest constructor: it may be any name suitable to hashlib.new()
HashF = Callable[[], Any]
# HashF = Callable[[Any], Any]
H160_Net = Tuple[bytes, str]

# Elliptic curve point in affine coordinates.
# Warning: to make Point a NamedTuple would slow down the code
Expand All @@ -97,3 +98,10 @@
# of the INF Point
# QJ = Q[0], Q[1], 1 if Q[1] else 0
INFJ = 7, 0, 0

# TODO add type hinting to script_tree
# unfortunately recursive type hinting is not supported
# https://github.com/python/mypy/issues/731
# TaprootLeaf = Tuple[int, Script]
# TaprootScriptTree = List[Union[Any, TaprootLeaf]]
TaprootScriptTree = Any
4 changes: 2 additions & 2 deletions btclib/b32.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@

from typing import Iterable

from btclib.alias import Octets, String
from btclib.alias import Octets, String, TaprootScriptTree
from btclib.bech32 import decode, encode
from btclib.exceptions import BTClibValueError
from btclib.hashes import hash160, sha256
from btclib.network import NETWORKS, network_from_key_value
from btclib.script import TaprootScriptTree, output_pubkey
from btclib.script import output_pubkey
from btclib.to_pub_key import Key, pub_keyinfo_from_key
from btclib.utils import bytes_from_octets

Expand Down
4 changes: 1 addition & 3 deletions btclib/hashes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@
from __future__ import annotations

import hashlib
from typing import Callable, Sequence, Tuple
from typing import Callable, Sequence

from btclib.alias import HashF, Octets
from btclib.utils import bytes_from_octets

H160_Net = Tuple[bytes, str]

# see https://bugs.python.org/issue47101
# With OpenSSL 3.x, hashlib still includes ripemd160
# but it is not usable unless the legacy provider is loaded.
Expand Down
4 changes: 2 additions & 2 deletions btclib/psbt/psbt.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,12 @@ def __init__(

def assert_valid(self) -> None:
"""Assert logical self-consistency."""
self.tx.assert_valid()

# ensure a non-null tx has been included
if not (self.tx.vin and self.tx.vout):
raise BTClibValueError("null transaction")

self.tx.assert_valid()

# ensure the tx is unsigned
if any(tx_in.script_sig or tx_in.script_witness for tx_in in self.tx.vin):
raise BTClibValueError("non empty script_sig or witness")
Expand Down
21 changes: 12 additions & 9 deletions btclib/script/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,17 @@
"""Module btclib.script."""

from btclib.script.script import Command, Script, op_int, parse, serialize
from btclib.script.script_pub_key import (
from btclib.script.taproot import (
TaprootScriptTree,
check_output_pubkey,
input_script_sig,
output_prvkey,
output_pubkey,
)
from btclib.script.witness import Witness

# hack to prevent circular import
from btclib.script.script_pub_key import ( # isort:skip
ScriptPubKey,
address,
assert_nulldata,
Expand All @@ -31,14 +41,7 @@
is_p2wsh,
type_and_payload,
)
from btclib.script.taproot import (
TaprootScriptTree,
check_output_pubkey,
input_script_sig,
output_prvkey,
output_pubkey,
)
from btclib.script.witness import Witness


__all__ = [
"Command",
Expand Down
9 changes: 1 addition & 8 deletions btclib/script/taproot.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from typing import Any

from btclib import var_bytes
from btclib.alias import Octets
from btclib.alias import Octets, TaprootScriptTree
from btclib.ec import Curve, mult, secp256k1
from btclib.exceptions import BTClibValueError
from btclib.hashes import tagged_hash
Expand All @@ -25,13 +25,6 @@
from btclib.to_pub_key import Key, pub_keyinfo_from_key
from btclib.utils import bytes_from_octets

# TODO add type hinting to script_tree
# unfortunately recursive type hinting is not supported
# https://github.com/python/mypy/issues/731
# TaprootLeaf = Tuple[int, Script]
# TaprootScriptTree = List[Union[Any, TaprootLeaf]]
TaprootScriptTree = Any


def tree_helper(script_tree: TaprootScriptTree) -> tuple[Any, bytes]:
if len(script_tree) == 1:
Expand Down
6 changes: 5 additions & 1 deletion btclib/tx/tx.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from btclib.alias import BinaryData
from btclib.exceptions import BTClibValueError
from btclib.hashes import hash256
from btclib.script import Witness
from btclib.script.witness import Witness
from btclib.tx.tx_in import TX_IN_COMPARES_WITNESS, TxIn
from btclib.tx.tx_out import TxOut
from btclib.utils import bytesio_from_binarydata
Expand Down Expand Up @@ -193,9 +193,13 @@ def assert_valid(self) -> None:
if not 0 <= self.lock_time <= 0xFFFFFFFF:
raise BTClibValueError(f"invalid lock time: {self.lock_time}")

if not self.vin:
raise BTClibValueError("Missing inputs")
for tx_in in self.vin:
tx_in.assert_valid()

if not self.vout:
raise BTClibValueError("Missing outputs")
for tx_out in self.vout:
tx_out.assert_valid()

Expand Down
19 changes: 15 additions & 4 deletions tests/tx/test_tx.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

def test_tx() -> None:
# default constructor
tx = Tx()
tx = Tx(check_validity=False)
assert not tx.is_segwit()
assert not any(bool(w) for w in tx.vwitness)
assert not any(bool(tx_in.script_witness) for tx_in in tx.vin)
Expand All @@ -40,15 +40,22 @@ def test_tx() -> None:
assert tx.vsize == tx.size
assert tx.weight == tx.size * 4

tx_2 = Tx.from_dict(tx.to_dict())
with pytest.raises(BTClibValueError, match="Missing inputs"):
tx.assert_valid()

tx_2 = Tx.from_dict(tx.to_dict(check_validity=False), check_validity=False)
assert tx_2.is_segwit() == tx.is_segwit()
assert tx_2 == tx

tx_2 = Tx.parse(tx.serialize(include_witness=True))
tx_2 = Tx.parse(
tx.serialize(include_witness=True, check_validity=False), check_validity=False
)
assert tx_2.is_segwit() == tx.is_segwit()
assert tx_2 == tx

tx_2 = Tx.parse(tx.serialize(include_witness=False))
tx_2 = Tx.parse(
tx.serialize(include_witness=False, check_validity=False), check_validity=False
)
assert not tx_2.is_segwit()
assert tx_2 == tx

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

tx.vin = [tx_in]
with pytest.raises(BTClibValueError, match="Missing outputs"):
tx.assert_valid()

tx_out1 = TxOut(2500000, "a914f987c321394968be164053d352fc49763b2be55c87")
tx_out2 = TxOut(
6381891, "0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d"
Expand Down

0 comments on commit 57c2516

Please sign in to comment.