Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/mypy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Type checking with mypy

on:
push:
branches:
- '**'
pull_request:
branches:
- main

jobs:
mypy:
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
matrix:
python-version: [3.8, "3.10", "3.11"]

steps:
- name: Checkout Code
uses: actions/checkout@v2
with:
fetch-depth: 0

- name: Setup Python environment
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
pip install --upgrade pip
pip install mypy>=1.0

- name: Run mypy
run: |
mypy pycoin
43 changes: 0 additions & 43 deletions .github/workflows/test-py27.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
fail-fast: false
max-parallel: 4
matrix:
python-version: [3.7, 3.8, 3.9, "3.10", "3.11", "3.12", "3.13"]
python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "3.13"]

steps:
- name: Checkout Code
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ files.txt
*egg-info
.DS_STORE
.tox
.coverage
.coverage.*
htmlcov/
.pytest_cache/
.mypy_cache/
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
include CHANGES CREDITS LICENSE *.md *.txt
include pycoin/py.typed
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pycoin -- Python Cryptocoin Utilities
=====================================

The pycoin library implements many utilities useful when dealing with bitcoin and some bitcoin-like
alt-coins. It has been tested with Python 2.7, 3.7-3.13.
alt-coins. It requires Python 3.8 or higher.

See also [pycoinnet](http://github.com/richardkiss/pycoinnet/) for a library that speaks the bitcoin protocol.

Expand Down
12 changes: 12 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[mypy]
python_version = 3.8
warn_return_any = False
warn_unused_configs = True
disallow_untyped_defs = False
ignore_missing_imports = True
strict_optional = False
files = pycoin

# Specific module ignores can be added as needed
# [mypy-pycoin.contrib.*]
# ignore_errors = True
13 changes: 7 additions & 6 deletions pycoin/bloomfilter.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import math
import struct
from typing import Tuple

from pycoin.encoding.b58 import a2b_hashed_base58
from pycoin.intbytes import indexbytes

LOG_2 = math.log(2)


def filter_size_required(element_count, false_positive_probability):
def filter_size_required(element_count: int, false_positive_probability: float) -> int:
# The size S of the filter in bytes is given by
# (-1 / pow(log(2), 2) * N * log(P)) / 8
# Of course you must ensure it does not go over the maximum size
Expand All @@ -17,23 +18,23 @@ def filter_size_required(element_count, false_positive_probability):
return min(36000, int(((-1 / pow(LOG_2, 2) * element_count * lfpp)+7) // 8))


def hash_function_count_required(filter_size, element_count):
def hash_function_count_required(filter_size: int, element_count: int) -> int:
# The number of hash functions required is given by S * 8 / N * log(2).
return int(filter_size * 8.0 / element_count * LOG_2 + 0.5)


class BloomFilter(object):
MASK_ARRAY = [1 << _ for _ in range(8)]

def __init__(self, size_in_bytes, hash_function_count, tweak):
def __init__(self, size_in_bytes: int, hash_function_count: int, tweak: int) -> None:
if size_in_bytes > 36000:
raise ValueError("too large")
self.filter_bytes = bytearray(size_in_bytes)
self.bit_count = 8 * size_in_bytes
self.hash_function_count = hash_function_count
self.tweak = tweak

def add_item(self, item_bytes):
def add_item(self, item_bytes: bytes) -> None:
for hash_index in range(self.hash_function_count):
seed = hash_index * 0xFBA4C795 + self.tweak
self.set_bit(murmur3(item_bytes, seed=seed) % self.bit_count)
Expand Down Expand Up @@ -63,13 +64,13 @@ def check_bit(self, v):
byte_index, mask = self._index_for_bit(v)
return (self.filter_bytes[byte_index] & mask) == mask

def filter_load_params(self):
def filter_load_params(self) -> Tuple[bytearray, int, int]:
return self.filter_bytes, self.hash_function_count, self.tweak


# http://stackoverflow.com/questions/13305290/is-there-a-pure-python-implementation-of-murmurhash

def murmur3(data, seed=0):
def murmur3(data: bytes, seed: int = 0) -> int:
c1 = 0xcc9e2d51
c2 = 0x1b873593

Expand Down
2 changes: 0 additions & 2 deletions pycoin/cmds/b58.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#!/usr/bin/env python

from __future__ import print_function

import argparse

from pycoin.encoding.b58 import a2b_base58, b2a_base58, a2b_hashed_base58, b2a_hashed_base58
Expand Down
1 change: 0 additions & 1 deletion pycoin/cmds/dump.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from __future__ import print_function

import datetime

Expand Down
2 changes: 0 additions & 2 deletions pycoin/cmds/keychain.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#!/usr/bin/env python

from __future__ import print_function

import argparse
import sqlite3
import sys
Expand Down
2 changes: 0 additions & 2 deletions pycoin/cmds/ku.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#!/usr/bin/env python

from __future__ import print_function

import argparse
import json
import re
Expand Down
2 changes: 0 additions & 2 deletions pycoin/cmds/msg.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#!/usr/bin/env python

from __future__ import print_function

import argparse
import sys

Expand Down
2 changes: 0 additions & 2 deletions pycoin/cmds/tx.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#!/usr/bin/env python

from __future__ import print_function

import argparse
import calendar
import codecs
Expand Down
2 changes: 1 addition & 1 deletion pycoin/coins/SolutionChecker.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ def check_solution(self, tx_context, traceback_f=None, *args, **kwargs):
tx_context: information about the transaction that the VM may need
traceback_f: a function invoked on occasion to check intermediate state
"""
raise NotImplemented()
raise NotImplementedError()
14 changes: 7 additions & 7 deletions pycoin/coins/Tx.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Tx(object):
@classmethod
def parse(class_, f):
"""Parse a transaction Tx from the file-like object f."""
raise NotImplemented()
raise NotImplementedError()

@classmethod
def from_bin(class_, blob):
Expand Down Expand Up @@ -53,11 +53,11 @@ def from_hex(class_, hex_string):
return class_.from_bin(h2b(hex_string))

def __init__(self, *args, **kwargs):
raise NotImplemented()
raise NotImplementedError()

def stream(self, f, *args, **kwargs):
"""Stream a transaction Tx to the file-like object f."""
raise NotImplemented()
raise NotImplementedError()

def as_bin(self, *args, **kwargs):
"""Returns a binary blob containing the streamed transaction.
Expand All @@ -81,7 +81,7 @@ def as_hex(self, *args, **kwargs):

def hash(self, hash_type=None):
"""Return the hash for this Tx object."""
raise NotImplemented()
raise NotImplementedError()

def id(self):
"""Return the human-readable hash for this Tx object."""
Expand All @@ -97,16 +97,16 @@ def tx_outs_as_spendable(self, block_index_available=0):
for tx_out_index, tx_out in enumerate(self.txs_out)]

def __str__(self):
raise NotImplemented()
raise NotImplementedError()

def __repr__(self):
raise NotImplemented()
raise NotImplementedError()

def check(self):
"""
Basic checks that don't depend on network or block context.
"""
raise NotImplemented()
raise NotImplementedError()

"""
The functions below here deal with an optional additional parameter: "unspents".
Expand Down
8 changes: 4 additions & 4 deletions pycoin/coins/TxIn.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ class TxIn(object):

@classmethod
def parse(class_, f):
raise NotImplemented()
raise NotImplementedError()

def __init__(self, *args, **kwargs):
raise NotImplemented()
raise NotImplementedError()

def stream(self, f):
raise NotImplemented()
raise NotImplementedError()

def __str__(self):
raise NotImplemented()
raise NotImplementedError()


"""
Expand Down
8 changes: 4 additions & 4 deletions pycoin/coins/TxOut.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ class TxOut(object):

@classmethod
def parse(class_, f):
raise NotImplemented()
raise NotImplementedError()

def __init__(self, *args, **kwargs):
raise NotImplemented()
raise NotImplementedError()

def stream(self, f):
raise NotImplemented()
raise NotImplementedError()

def __str__(self):
raise NotImplemented()
raise NotImplementedError()


"""
Expand Down
37 changes: 4 additions & 33 deletions pycoin/ecdsa/intstream.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,11 @@
from typing import Literal

from pycoin.intbytes import iterbytes, byte2int


def _to_bytes(v, length, byteorder="big"):
def to_bytes(v: int, length: int, byteorder: Literal["big", "little"] = "big") -> bytes:
"""This is the same functionality as ``int.to_bytes`` in python 3"""
return v.to_bytes(length, byteorder=byteorder)


def _from_bytes(bytes, byteorder="big", signed=False):
def from_bytes(data: bytes, byteorder: Literal["big", "little"] = "big", signed: bool = False) -> int:
"""This is the same functionality as ``int.from_bytes`` in python 3"""
return int.from_bytes(bytes, byteorder=byteorder, signed=signed)


if hasattr(int, "to_bytes"):
to_bytes = _to_bytes
from_bytes = _from_bytes
else:
def to_bytes(v, length, byteorder="big"):
"""This is the same functionality as ``int.to_bytes`` in python 3"""
ba = bytearray()
for i in range(length):
mod = v & 0xff
v >>= 8
ba.append(mod)
if byteorder == "big":
ba.reverse()
return bytes(ba)

def from_bytes(bytes, byteorder="big", signed=False):
"""This is the same functionality as ``int.from_bytes`` in python 3"""
if byteorder != "big":
bytes = reversed(bytes)
v = 0
for c in iterbytes(bytes):
v <<= 8
v += c
if signed and byte2int(bytes) & 0x80:
v = v - (1 << (8*len(bytes)))
return v
return int.from_bytes(data, byteorder=byteorder, signed=signed)
Loading