diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..278c27d
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "docs/src/alabaster"]
+ path = docs/src/alabaster
+ url = https://github.com/bitprophet/alabaster.git
diff --git a/README.md b/README.md
index 4f036d9..0addfc0 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,3 @@
-
+
## Python bitcoin library
diff --git a/change.log b/change.log
new file mode 100644
index 0000000..e69de29
diff --git a/docs/.DS_Store b/docs/.DS_Store
new file mode 100644
index 0000000..68fa5ca
Binary files /dev/null and b/docs/.DS_Store differ
diff --git a/docs/_build/.DS_Store b/docs/_build/.DS_Store
new file mode 100644
index 0000000..2c3e9ff
Binary files /dev/null and b/docs/_build/.DS_Store differ
diff --git a/docs/img/.DS_Store b/docs/img/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/docs/img/.DS_Store differ
diff --git a/docs/source/address.rst b/docs/source/address.rst
new file mode 100644
index 0000000..fc02023
--- /dev/null
+++ b/docs/source/address.rst
@@ -0,0 +1,30 @@
+=========
+Addresses
+=========
+
+Collection of base classes that implement the work with Bitcoin addresses and address keys.
+Supports addresses types PUBKEY, P2PKH, P2SH, P2SH-PWPKH, P2WPKH, P2WSH.
+
+|
+|
+
+.. autoclass:: pybtc.PrivateKey
+ :members:
+ :inherited-members:
+
+|
+|
+
+.. autoclass:: pybtc.PublicKey
+ :members:
+ :inherited-members:
+
+|
+|
+
+.. autoclass:: pybtc.Address
+ :members:
+ :inherited-members:
+
+
+
diff --git a/docs/source/classes.rst b/docs/source/classes.rst
new file mode 100644
index 0000000..4555cae
--- /dev/null
+++ b/docs/source/classes.rst
@@ -0,0 +1,13 @@
+=========
+Reference
+=========
+
+.. toctree::
+ :name: mastertoc
+ :maxdepth: 2
+
+ address.rst
+ transaction.rst
+ block.rst
+
+
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 5a89d26..c68f7fc 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -14,6 +14,9 @@
#
import os
import sys
+from sphinx.ext.autodoc import (
+ ClassLevelDocumenter, InstanceAttributeDocumenter)
+
sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('..'))
@@ -28,10 +31,14 @@ sys.path.insert(0, os.path.abspath('../..'))
# -- Project information -----------------------------------------------------
project = 'pybtc'
-copyright = '2018, Aleksey Karpov'
+copyright = '2015-2018, bitaps.com'
author = 'Aleksey Karpov'
+def iad_add_directive_header(self, sig):
+ ClassLevelDocumenter.add_directive_header(self, sig)
+
+InstanceAttributeDocumenter.add_directive_header = iad_add_directive_header
# The short X.Y version
version = ''
diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst
new file mode 100644
index 0000000..a7b801b
--- /dev/null
+++ b/docs/source/contributing.rst
@@ -0,0 +1,36 @@
+.. _pybtc-contributing:
+
+============
+Contributing
+============
+
+Instructions for contributors
+-----------------------------
+
+
+In order to make a clone of the GitHub repo: open the link and press the
+"Fork" button on the upper-right menu of the web page.
+
+
+Workflow is pretty straightforward:
+
+ 1. Clone the GitHub
+
+ 2. Make a change
+
+ 3. Make sure all tests passed
+
+ 4. Add a record intp file into ``change.log``.
+
+ 5. Commit changes to own aiohttp clone
+
+ 6. Make pull request from github page for your clone against master branch
+
+
+Tests coverage
+--------------
+
+We are trying hard to have good test coverage; please don't make it worse.
+All tests located in ``tests/`` folder.
+
+
diff --git a/docs/source/examples.rst b/docs/source/examples.rst
new file mode 100644
index 0000000..3dc5ae3
--- /dev/null
+++ b/docs/source/examples.rst
@@ -0,0 +1,118 @@
+========
+Examples
+========
+
+
+Create address
+--------------
+
+This is example of usage Address class. The address class implements the work with addresses controlled by a private key.
+Supports the ability to create P2WPKH, P2PKH, PUBKEY address types and P2SH_P2WPKH as exception for SEGWIT adoption.
+It is recommended to use native SEGWIT address type - P2WPKH, which reduces costs of miner fee and expand block capacity.
+To create an address, you need to create a class object. Buy default,
+will be created P2WPKH address for mainnet.
+
+
+
+.. code-block:: bash
+
+ >>> import pybtc
+ >>> a = pybtc.Address()
+ >>> a.address
+ 'bc1q6cxx5t8xkruz3s5khx7923xvsx5ry4c6p74m5s'
+ >>> a.private_key.wif
+ 'L5XKGA2xEHcinWEpmyiABS1bqQux8Av5dGVqcpRtVJC3ZCR5sXUe'
+ >>>
+ >>> # create P2PKH legacy format
+ >>> pybtc.Address(address_type="P2PKH").address
+ '1ChpKurzFhdCULKaNHCc3Ra9KfxM2LRguw'
+ >>>
+ >>> # create testnet address
+ >>> pybtc.Address(address_type="P2PKH", testnet=True).address
+ 'mpR4hDfu269yxgZtPVYSD21gtpvdxpTmH6'
+ >>>
+ >>> # create P2SH_P2WPKH SEGWIT adoption address
+ >>> pybtc.Address(address_type="P2SH_P2WPKH").address
+ '3Bqeq3XqL6azMK3BxNyr8vXgXUtoG63J4T'
+ >>>
+
+
+Get address from key
+--------------------
+
+In case you already have private or public key you can object from your key.
+
+.. code-block:: bash
+
+ >>> a = pybtc.Address('L5XKGA2xEHcinWEpmyiABS1bqQux8Av5dGVqcpRtVJC3ZCR5sXUe')
+ >>> a.address
+ 'bc1q6cxx5t8xkruz3s5khx7923xvsx5ry4c6p74m5s'
+ >>> a.public_key.hex
+ '03b8b44876e1f45be7e42953ea47026c39cc45341344d3ab32701b93de696107af'
+ >>>
+ >>> # get address from public key
+ >>> pub = pybtc.PublicKey('03b8b44876e1f45be7e42953ea47026c39cc45341344d3ab32701b93de696107af')
+ >>>
+ >>> pybtc.Address(pub).address
+ 'bc1q6cxx5t8xkruz3s5khx7923xvsx5ry4c6p74m5s'
+ >>>
+
+Pure functions for address
+--------------------------
+
+Create private key
+
+.. code-block:: bash
+
+ >>> import pybtc
+ >>> pybtc.create_private_key()
+ 'KyvZYvdzWD4JSPFt4wXwjG53as227zT2qiWbMTicZEUSjiwvbEqi'
+ >>>
+ >>> pybtc.create_private_key(compressed=False)
+ '5Jw8DY1uBrd35xup6eD6KLEFa4AJFbX381HWuHvPGirJto9ZTnr'
+ >>>
+ >>> pybtc.is_wif_valid('5Jw8DY1uBrd35xup6eD6KLEFa4AJFbX381HWuHvPGirJto9ZTnr')
+ True
+ >>> pybtc.is_wif_valid('5Jw8DY1uBrd35xup6eD6KLEFa4AJFbX381**********Jto9ZTnr')
+ False
+ >>>
+
+Get public key from private key
+
+.. code-block:: bash
+
+ >>> import pybtc
+ >>> pybtc.private_to_public_key('5Jw8DY1uBrd35xup6eD6KLEFa4AJFbX381HWuHvPGirJto9ZTnr')
+ '0479f17a94410afd4f27588a192bacada53add0741765092dc0f8e2a29ea1bcd276dbc1ef74c3e0172d9db8047f2a0a5dc2e8e51a13f7f0cc072de906b765e0f7f'
+ >>>
+ >>> pybtc.public_key_to_address('0479f17a94410afd4f27588a192bacada53add0741765092dc0f8e2a29ea1bcd276dbc1ef74c3e0172d9db8047f2a0a5dc2e8e51a13f7f0cc072de906b765e0f7f')
+ >>>
+ >>> # this is uncompressed public key, so we can't create witness address
+ >>> # we have to set witness_version to None to get non segwit address
+ >>> pub = pybtc.private_to_public_key('5Jw8DY1uBrd35xup6eD6KLEFa4AJFbX381HWuHvPGirJto9ZTnr')
+ >>> pybtc.public_key_to_address(pub, witness_version=None)
+ '17mXwxxZRmj1nJJzDszZbW9URSAradEuAt'
+ >>>
+
+Tools
+
+.. code-block:: bash
+
+ >>> pybtc.is_address_valid('17mXwxxZRmj1nJJzDszZbW9URSAradEuAt')
+ True
+ >>> pybtc.address_type('17mXwxxZRmj1nJJzDszZbW9URSAradEuAt')
+ 'P2PKH'
+ >>> pybtc.address_net_type('17mXwxxZRmj1nJJzDszZbW9URSAradEuAt')
+ 'mainnet'
+ >>>
+
+
+Create script address
+---------------------
+
+
+
+
+
+
+
diff --git a/docs/source/functional.rst b/docs/source/functional.rst
index 2446767..f0d3686 100644
--- a/docs/source/functional.rst
+++ b/docs/source/functional.rst
@@ -1,18 +1,14 @@
-==============
-Pure functions
-==============
+========================
+Pure functions reference
+========================
-Base function primitives implemeted in functional programming paradigm.
-
-Key management
-==============
-
-Tools for private and public key managment
+Base function primitives implemented in functional programming paradigm.
-Private key
------------
+
+Private keys
+============
.. autofunction:: pybtc.create_private_key
.. autofunction:: pybtc.private_key_to_wif
@@ -20,8 +16,8 @@ Private key
.. autofunction:: pybtc.is_wif_valid
-Public key
-----------
+Public keys
+===========
.. WARNING::
Using uncompressed public keys is
@@ -47,6 +43,66 @@ Addresses
.. autofunction:: pybtc.is_address_valid
+Script
+======
+
+.. autofunction:: pybtc.decode_script
+.. autofunction:: pybtc.parse_script
+.. autofunction:: pybtc.delete_from_script
+.. autofunction:: pybtc.script_to_hash
+
+
Signatures
==========
+.. autofunction:: pybtc.verify_signature
+.. autofunction:: pybtc.sign_message
+.. autofunction:: pybtc.is_valid_signature_encoding
+
+
+Hash encoding
+=============
+
+.. autofunction:: pybtc.rh2s
+.. autofunction:: pybtc.s2rh
+.. autofunction:: pybtc.reverse_hash
+
+
+Merkle root
+===========
+
+.. autofunction:: pybtc.merkle_root
+.. autofunction:: pybtc.merkle_branches
+.. autofunction:: pybtc.merkleroot_from_branches
+
+
+Difficulty
+==========
+
+.. autofunction:: pybtc.bits_to_target
+.. autofunction:: pybtc.target_to_difficulty
+.. autofunction:: pybtc.bits_to_difficulty
+.. autofunction:: pybtc.difficulty_to_target
+
+
+Tools
+=====
+
+.. autofunction:: pybtc.bytes_needed
+.. autofunction:: pybtc.int_to_bytes
+.. autofunction:: pybtc.bytes_to_int
+.. autofunction:: pybtc.int_to_var_int
+.. autofunction:: pybtc.var_int_to_int
+.. autofunction:: pybtc.var_int_len
+.. autofunction:: pybtc.get_var_int_len
+.. autofunction:: pybtc.read_var_int
+.. autofunction:: pybtc.read_var_list
+.. autofunction:: pybtc.int_to_c_int
+.. autofunction:: pybtc.c_int_to_int
+.. autofunction:: pybtc.c_int_len
+
+
+
+
+
+
diff --git a/docs/source/index.rst b/docs/source/index.rst
index ee67d62..8ac9e21 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -28,39 +28,36 @@ Key Features
.. _aiohttp-installation:
-Library Installation
-====================
+Quick library Installation
+==========================
.. code-block:: bash
- $ pip install secp256k1
$ pip install pybtc
Getting Started
===============
-Client example::
-
- import pybtc
- import asyncio
-
-Server example::
+Usage example::
import pybtc
+ a = pybtc.Address()
+ print(a.address)
+ print(a.private_key.wif())
-What's new in pybtc 2?
-========================
+What's new in pybtc 2.0 ?
+=========================
-
-Tutorial
-========
-
+- Mnemonic code generation (BIP39)
+- Hierarchical Deterministic Wallets (BIP32)
+- Wallet class implemented acording BIP44
+- Imporved transaction deserialization perfomance
@@ -81,21 +78,16 @@ Dependencies
- *secp256k1*
-Communication channels
-======================
-
-
-
-Contributing
-============
-
-
-
Authors and License
===================
-The ``pybtc`` package is written mostly by Aleksey Karpov.
+The ``pybtc`` package was initially written by `Aleksey Karpov `_ and development continues with contributors.
+
+Recent contributors:
+
+- `Aleksey Karpov `_
+- `Aleksey Karybkin `_
It's *GPL-3.0* licensed and freely available.
@@ -111,4 +103,11 @@ Table Of Contents
:name: mastertoc
:maxdepth: 2
- functional.rst
\ No newline at end of file
+ installation.rst
+ examples.rst
+ classes.rst
+ functional.rst
+ contributing.rst
+
+
+
diff --git a/docs/source/installation.rst b/docs/source/installation.rst
new file mode 100644
index 0000000..d5c3d9d
--- /dev/null
+++ b/docs/source/installation.rst
@@ -0,0 +1,39 @@
+============
+Installation
+============
+
+This part of the documentation covers the installation of pybtc library. The first step to using any software package is getting it properly installed.
+
+Get from pip package
+--------------------
+
+To install pybtc, simply run this simple command in your terminal of choice:
+
+.. code-block:: bash
+
+ $ pip install pybtc
+
+
+If you don’t have pip installed, this Python pip `installation guide `_ can guide you through the process.
+
+
+Get the Source Code
+-------------------
+
+You can clone the public repository:
+
+.. code-block:: bash
+
+ $ git clone git://github.com/bitaps-com/pybtc
+
+Once you have a copy of the source, you can embed it in your own Python package, or install it into your site-packages easily:
+
+.. code-block:: bash
+
+ $ cd pybtc
+ $ python3 setup.py install
+
+
+
+
+
diff --git a/docs/source/transaction.rst b/docs/source/transaction.rst
new file mode 100644
index 0000000..ac2360c
--- /dev/null
+++ b/docs/source/transaction.rst
@@ -0,0 +1,11 @@
+============
+Transactions
+============
+
+The class for creating transaction.
+
+
+.. autoclass:: pybtc.Transaction
+ :members:
+
+
diff --git a/docs/src/alabaster b/docs/src/alabaster
new file mode 160000
index 0000000..609374c
--- /dev/null
+++ b/docs/src/alabaster
@@ -0,0 +1 @@
+Subproject commit 609374c5434eb960eab1ae25ea50b007fbc6c2ae
diff --git a/docs/src/pip-delete-this-directory.txt b/docs/src/pip-delete-this-directory.txt
new file mode 100644
index 0000000..c8883ea
--- /dev/null
+++ b/docs/src/pip-delete-this-directory.txt
@@ -0,0 +1,5 @@
+This file is placed here by pip to indicate the source was put
+here by pip.
+
+Once this package is successfully installed this source code will be
+deleted (unless you remove this file).
diff --git a/pybtc/__init__.py b/pybtc/__init__.py
index feaa99a..bc2e576 100644
--- a/pybtc/__init__.py
+++ b/pybtc/__init__.py
@@ -2,6 +2,7 @@ from .tools import *
from .opcodes import *
from .consensus import *
from .transaction import *
+from .block import *
from .address import *
version = "2.0.1"
diff --git a/pybtc/address.py b/pybtc/address.py
index 132a82b..b67b2c1 100644
--- a/pybtc/address.py
+++ b/pybtc/address.py
@@ -2,11 +2,32 @@ from .tools import *
class PrivateKey():
+ """
+ The class for creating private key object.
+
+ :param key: (optional) private key in HEX, bytes string or WIF format. In case no key specified
+ new random private key will be created.
+ :param compressed: (optional) if set to True private key corresponding compressed public key,
+ by default set to True. Recommended use only compressed public key.
+ :param testnet: (optional) if set to True mean that this private key for testnet Bitcoin network.
+
+ """
def __init__(self, key=None, compressed=True, testnet=False):
+
if key is None:
+
+ #: flag for compressed type of corresponding public key (boolean)
self.compressed = compressed
+ #: flag for testnet network private key (boolean)
self.testnet = testnet
- self.raw_key = create_private_key(wif=False)
+
+ #: private key in bytes (bytes)
+ self.key = create_private_key(wif=False)
+ #: private key in HEX (string)
+ self.hex = hexlify(self.key).decode()
+ #: private key in WIF format (string)
+ self.wif = private_key_to_wif(self.key, compressed, testnet)
+
else:
if isinstance(key, str):
try:
@@ -15,13 +36,17 @@ class PrivateKey():
pass
if isinstance(key, bytes):
if len(key) != 32:
- raise TypeError("private key invalid")
- self.raw_key = key
+ raise TypeError("private key invalid length")
+ self.key = key
self.compressed = compressed
self.testnet = testnet
+ self.hex = hexlify(self.key).decode()
+ self.wif = private_key_to_wif(self.key, compressed, testnet)
return
assert isinstance(key, str)
- self.raw_key = wif_to_private_key(key, hex=False)
+ self.key = wif_to_private_key(key, hex=False)
+ self.hex = hexlify(self.key).decode()
+ self.wif = private_key_to_wif(self.key, compressed, testnet)
if key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX,
TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX):
self.compressed = False
@@ -33,57 +58,101 @@ class PrivateKey():
else:
self.testnet = False
- def hex(self):
- return hexlify(self.raw_key).decode()
-
- def wif(self, compressed=None, testnet=None):
- if compressed is None:
- compressed = self.compressed
- if testnet is None:
- testnet = self.testnet
- return private_key_to_wif(self.raw_key, compressed, testnet)
-
def __str__(self):
- return self.wif()
+ return self.wif
class PublicKey():
- def __init__(self, key=None):
+ """
+ The class for public key object.
+
+ :param key: one of this types allowed:
+
+ - private key is instance of ``PrivateKey`` class
+ - private key HEX encoded string
+ - private key 32 bytes string
+ - private key in WIF format
+ - public key in HEX encoded string
+ - public key [33/65] bytes string
+
+ In case no key specified with HEX or bytes string you have to provide flag for testnet
+ and compressed key. WIF format and ``PrivateKey`` instance already contain this flags.
+ For HEX or bytes public key only testnet flag has the meaning, comressed flag is determined
+ according to the length of key.
+
+ :param compressed: (optional) if set to True private key corresponding compressed public key,
+ by default set to True. Recommended use only compressed public key.
+ :param testnet: (optional) if set to True mean that this private key for testnet Bitcoin network.
+
+ """
+ def __init__(self, key, compressed=True, testnet=False):
if isinstance(key, str):
try:
key = unhexlify(key)
except:
- pass
+ if is_wif_valid(key):
+ key = PrivateKey(key)
+
+ if isinstance(key, bytes):
+ if len(key) == 32:
+ key = PrivateKey(key, compressed=compressed, testnet=testnet)
+ elif is_public_key_valid(key):
+ public_key = key
+ self.testnet = testnet
+ self.compressed = True if len(key) == 33 else False
+ else:
+ raise TypeError("key invalid")
+
if isinstance(key, PrivateKey):
- key = private_to_public_key(key.raw_key,
+ #: flag for testnet network private key (boolean)
+ self.testnet = key.testnet
+ #: flag for compressed type of corresponding public key (boolean)
+ self.compressed = key.compressed
+ public_key = private_to_public_key(key.key,
compressed=key.compressed,
hex=False)
- if not isinstance(key, bytes):
- raise TypeError("public key invalid")
- if len(key) != 33 and len(key) != 65:
- raise TypeError("public key invalid")
- if len(key) == 33:
- self.compressed = True
- else:
- self.compressed = False
- self.raw_key = key
-
- def hex(self):
- return hexlify(self.raw_key).decode()
+ #: public key in bytes (bytes)
+ self.key = public_key
+ #: public key in HEX (string)
+ self.hex = hexlify(self.key).decode()
def __str__(self):
- return self.hex()
+ return self.hex
class Address():
+ """
+ The class for Address object.
+
+ :param key: (optional) one of this types allowed:
+
+ - private key WIF format
+ - instance of ``PrivateKey``
+ - private key HEX encoded string
+ - instance of ``PublicKey``
+
+ In case no key specified new Address will be created with random keys.
+ :param address_type: (optional) P2PKH, PUBKEY, P2WPKH, P2SH_P2WPKH, by default P2WPKH.
+ :param compressed: (optional) if set to True private key corresponding compressed public key,
+ by default set to True. Recommended use only compressed public key.
+ :param testnet: (optional) if set to True mean that this private key for testnet Bitcoin network.
+
+ In case instanse is created from WIF private key, ``PrivateKey`` or ``PublicKey`` compressed and testnet flags
+ already contain in initial key parameter and will be ignored.
+ """
def __init__(self, key=None,
address_type="P2WPKH", testnet=False, compressed=True):
if key is None:
+ #: instance of ``PrivateKey`` class
self.private_key = PrivateKey(testnet=testnet,
compressed=compressed)
+ #: instance of ``PublicKey`` class
self.public_key = PublicKey(self.private_key)
+ #: flag for testnet network address (boolean)
self.testnet = testnet
- elif isinstance(key, PrivateKey):
+ if isinstance(key, str) or isinstance(key, bytes):
+ key = PrivateKey(key, testnet=testnet, compressed=compressed)
+ if isinstance(key, PrivateKey):
self.private_key = key
self.testnet = key.testnet
compressed = key.compressed
@@ -97,21 +166,31 @@ class Address():
if not compressed:
if address_type not in ("P2PKH", "PUBKEY", "P2SH"):
raise TypeError("compressed public key invalid")
+ #: flag for testnet network address (boolean)
self.type = address_type
if address_type in ("P2WPKH"):
+ #: version of witness program for SEGWIT address (string)
self.witness_version = 0
else:
self.witness_version = None
self.compressed = compressed
if address_type == "P2SH_P2WPKH":
+ #: flag for script hash address (boolean)
self.script_hash = True
- self.redeem_script_raw = public_key_to_p2sh_p2wpkh_script(self.public_key.raw_key)
- self.redeem_script = hexlify(self.redeem_script_raw).decode()
- self.hash = hash160(self.redeem_script_raw)
+ #: redeeem script, only for P2SH_P2WPKH (bytes)
+ self.redeem_script = public_key_to_p2sh_p2wpkh_script(self.public_key.key)
+ #: redeeem script HEX, only for P2SH_P2WPKH (string)
+ self.redeem_script_hex = hexlify(self.redeem_script).decode()
+ #: address hash
+ self.hash = hash160(self.redeem_script)
+ self.witness_version = None
else:
self.script_hash = False
- self.hash = hash160(self.public_key.raw_key)
+ self.hash = hash160(self.public_key.key)
+ #: address hash HEX (string)
+ self.hash_hex = hexlify(self.hash).decode()
+ #: address in base58 or bech32 encoding (string)
self.address = hash_to_address(self.hash,
script_hash=self.script_hash,
witness_version=self.witness_version,
@@ -128,12 +207,13 @@ class ScriptAddress():
self.testnet = testnet
if isinstance(script, str):
script = unhexlify(script)
- self.script_raw = script
- self.script = hexlify(self.script_raw).decode()
- self.hash = hash160(self.script_raw)
- self.script_opcodes = decode_script(self.script_raw)
- self.script_opcodes_asm = decode_script(self.script_raw, 1)
+ self.script = script
+ self.script_hex = hexlify(self.script).decode()
+ self.hash = hash160(self.script)
+ self.script_opcodes = decode_script(self.script)
+ self.script_opcodes_asm = decode_script(self.script, 1)
self.address = hash_to_address(self.hash,
script_hash=True,
witness_version=self.witness_version,
- testnet=self.testnet)
\ No newline at end of file
+ testnet=self.testnet)
+
diff --git a/pybtc/constants.py b/pybtc/constants.py
index 7001e04..7f47d23 100644
--- a/pybtc/constants.py
+++ b/pybtc/constants.py
@@ -2,11 +2,11 @@ from secp256k1 import lib as secp256k1
import random
MAX_AMOUNT = 2100000000000000
-SIGHASH_ALL = 0x00000001
-SIGHASH_NONE = 0x00000002
-SIGHASH_SINGLE = 0x00000003
-SIGHASH_ANYONECANPAY = 0x00000080
-MAX_INT_PRIVATE_KEY = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
+SIGHASH_ALL = 0x00000001
+SIGHASH_NONE = 0x00000002
+SIGHASH_SINGLE = 0x00000003
+SIGHASH_ANYONECANPAY = 0x00000080
+MAX_INT_PRIVATE_KEY = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
MAINNET_ADDRESS_BYTE_PREFIX = b'\x00'
TESTNET_ADDRESS_BYTE_PREFIX = b'\x6f'
@@ -62,16 +62,16 @@ ECDSA_CONTEXT_SIGN = secp256k1.secp256k1_context_create(FLAG_SIGN)
ECDSA_CONTEXT_VERIFY = secp256k1.secp256k1_context_create(FLAG_VERIFY)
ECDSA_CONTEXT_ALL = secp256k1.secp256k1_context_create(ALL_FLAGS)
secp256k1.secp256k1_context_randomize(ECDSA_CONTEXT_SIGN,
- random.SystemRandom().randint(0,MAX_INT_PRIVATE_KEY).to_bytes(32,byteorder="big"))
+ random.SystemRandom().randint(0, MAX_INT_PRIVATE_KEY).to_bytes(32,byteorder="big"))
-SCRIPT_TYPES = { "P2PKH": 0,
- "P2SH" : 1,
- "PUBKEY": 2,
- "NULL_DATA": 3,
- "MULTISIG": 4,
- "P2WPKH": 5,
- "P2WSH": 6,
- "NON_STANDART": 7
+SCRIPT_TYPES = {"P2PKH": 0,
+ "P2SH": 1,
+ "PUBKEY": 2,
+ "NULL_DATA": 3,
+ "MULTISIG": 4,
+ "P2WPKH": 5,
+ "P2WSH": 6,
+ "NON_STANDART": 7
}
diff --git a/pybtc/opcodes.py b/pybtc/opcodes.py
index ad416ee..93d9162 100644
--- a/pybtc/opcodes.py
+++ b/pybtc/opcodes.py
@@ -150,6 +150,150 @@ OPCODE["OP_PUBKEY"] = 0xfe
OPCODE["OP_INVALIDOPCODE"] = 0xff
-RAW_OPCODE = dict ( (OPCODE[i], i) for i in OPCODE )
-BYTE_OPCODE = dict ((i,bytes([OPCODE[i]])) for i in OPCODE )
-HEX_OPCODE = dict ((i,hexlify(bytes([OPCODE[i]])).decode()) for i in OPCODE )
+RAW_OPCODE = dict((OPCODE[i], i) for i in OPCODE)
+BYTE_OPCODE = dict((i, bytes([OPCODE[i]])) for i in OPCODE)
+HEX_OPCODE = dict((i, hexlify(bytes([OPCODE[i]])).decode()) for i in OPCODE)
+
+OP_FALSE = BYTE_OPCODE["OP_FALSE"]
+OP_0 = BYTE_OPCODE["OP_0"]
+OP_PUSHDATA1 = BYTE_OPCODE["OP_PUSHDATA1"]
+OP_PUSHDATA2 = BYTE_OPCODE["OP_PUSHDATA2"]
+OP_PUSHDATA4 = BYTE_OPCODE["OP_PUSHDATA4"]
+OP_1NEGATE = BYTE_OPCODE["OP_1NEGATE"]
+OP_RESERVED = BYTE_OPCODE["OP_RESERVED"]
+OP_1 = BYTE_OPCODE["OP_1"]
+OP_TRUE = BYTE_OPCODE["OP_TRUE"]
+OP_2 = BYTE_OPCODE["OP_2"]
+OP_3 = BYTE_OPCODE["OP_3"]
+OP_4 = BYTE_OPCODE["OP_4"]
+OP_5 = BYTE_OPCODE["OP_5"]
+OP_6 = BYTE_OPCODE["OP_6"]
+OP_7 = BYTE_OPCODE["OP_7"]
+OP_8 = BYTE_OPCODE["OP_8"]
+OP_9 = BYTE_OPCODE["OP_9"]
+OP_10 = BYTE_OPCODE["OP_10"]
+OP_11 = BYTE_OPCODE["OP_11"]
+OP_12 = BYTE_OPCODE["OP_12"]
+OP_13 = BYTE_OPCODE["OP_13"]
+OP_14 = BYTE_OPCODE["OP_14"]
+OP_15 = BYTE_OPCODE["OP_15"]
+OP_16 = BYTE_OPCODE["OP_16"]
+
+# control
+
+OP_NOP = BYTE_OPCODE["OP_NOP"]
+OP_VER = BYTE_OPCODE["OP_VER"]
+OP_IF = BYTE_OPCODE["OP_IF"]
+OP_NOTIF = BYTE_OPCODE["OP_NOTIF"]
+OP_VERIF = BYTE_OPCODE["OP_VERIF"]
+OP_ELSE = BYTE_OPCODE["OP_ELSE"]
+OP_ENDIF = BYTE_OPCODE["OP_ENDIF"]
+OP_VERIFY = BYTE_OPCODE["OP_VERIFY"]
+OP_RETURN = BYTE_OPCODE["OP_RETURN"]
+
+# stack
+
+OP_TOALTSTACK = BYTE_OPCODE["OP_TOALTSTACK"]
+OP_FROMALTSTACK = BYTE_OPCODE["OP_FROMALTSTACK"]
+OP_2DROP = BYTE_OPCODE["OP_2DROP"]
+OP_2DUP = BYTE_OPCODE["OP_2DUP"]
+OP_3DUP = BYTE_OPCODE["OP_3DUP"]
+OP_2OVER = BYTE_OPCODE["OP_2OVER"]
+OP_2ROT = BYTE_OPCODE["OP_2ROT"]
+OP_2SWAP = BYTE_OPCODE["OP_2SWAP"]
+OP_IFDUP = BYTE_OPCODE["OP_IFDUP"]
+OP_DEPTH = BYTE_OPCODE["OP_DEPTH"]
+OP_DROP = BYTE_OPCODE["OP_DROP"]
+OP_DUP = BYTE_OPCODE["OP_DUP"]
+OP_NIP = BYTE_OPCODE["OP_NIP"]
+OP_OVER = BYTE_OPCODE["OP_OVER"]
+OP_PICK = BYTE_OPCODE["OP_PICK"]
+OP_ROLL = BYTE_OPCODE["OP_ROLL"]
+OP_ROT = BYTE_OPCODE["OP_ROT"]
+OP_SWAP = BYTE_OPCODE["OP_SWAP"]
+OP_TUCK = BYTE_OPCODE["OP_TUCK"]
+
+# splice
+
+OP_CAT = BYTE_OPCODE["OP_CAT"]
+OP_SUBSTR = BYTE_OPCODE["OP_SUBSTR"]
+OP_LEFT = BYTE_OPCODE["OP_LEFT"]
+OP_RIGHT = BYTE_OPCODE["OP_RIGHT"]
+OP_SIZE = BYTE_OPCODE["OP_SIZE"]
+
+# bit operations
+
+OP_INVERT = BYTE_OPCODE["OP_INVERT"]
+OP_AND = BYTE_OPCODE["OP_AND"]
+OP_OR = BYTE_OPCODE["OP_OR"]
+OP_XOR = BYTE_OPCODE["OP_XOR"]
+OP_EQUAL = BYTE_OPCODE["OP_EQUAL"]
+OP_EQUALVERIFY = BYTE_OPCODE["OP_EQUALVERIFY"]
+OP_RESERVED1 = BYTE_OPCODE["OP_RESERVED1"]
+OP_RESERVED2 = BYTE_OPCODE["OP_RESERVED2"]
+
+# math
+
+OP_1ADD = BYTE_OPCODE["OP_1ADD"]
+OP_1SUB = BYTE_OPCODE["OP_1SUB"]
+OP_2MUL = BYTE_OPCODE["OP_2MUL"]
+OP_2DIV = BYTE_OPCODE["OP_2DIV"]
+OP_NEGATE = BYTE_OPCODE["OP_NEGATE"]
+OP_ABS = BYTE_OPCODE["OP_ABS"]
+OP_NOT = BYTE_OPCODE["OP_NOT"]
+OP_0NOTEQUAL = BYTE_OPCODE["OP_0NOTEQUAL"]
+
+OP_ADD = BYTE_OPCODE["OP_ADD"]
+OP_SUB = BYTE_OPCODE["OP_SUB"]
+OP_MUL = BYTE_OPCODE["OP_MUL"]
+OP_DIV = BYTE_OPCODE["OP_DIV"]
+OP_MOD = BYTE_OPCODE["OP_MOD"]
+OP_LSHIFT = BYTE_OPCODE["OP_LSHIFT"]
+OP_RSHIFT = BYTE_OPCODE["OP_RSHIFT"]
+
+OP_BOOLAND = BYTE_OPCODE["OP_BOOLAND"]
+OP_BOOLOR = BYTE_OPCODE["OP_BOOLOR"]
+OP_NUMEQUAL = BYTE_OPCODE["OP_NUMEQUAL"]
+OP_NUMEQUALVERIFY = BYTE_OPCODE["OP_NUMEQUALVERIFY"]
+OP_NUMNOTEQUAL = BYTE_OPCODE["OP_NUMNOTEQUAL"]
+OP_LESSTHAN = BYTE_OPCODE["OP_LESSTHAN"]
+OP_GREATERTHAN = BYTE_OPCODE["OP_GREATERTHAN"]
+OP_LESSTHANOREQUAL = BYTE_OPCODE["OP_LESSTHANOREQUAL"]
+OP_GREATERTHANOREQUAL = BYTE_OPCODE["OP_GREATERTHANOREQUAL"]
+OP_MIN = BYTE_OPCODE["OP_MIN"]
+OP_MAX = BYTE_OPCODE["OP_MAX"]
+OP_WITHIN = BYTE_OPCODE["OP_WITHIN"]
+
+# crypto
+
+OP_RIPEMD160 = BYTE_OPCODE["OP_RIPEMD160"]
+OP_SHA1 = BYTE_OPCODE["OP_SHA1"]
+OP_SHA256 = BYTE_OPCODE["OP_SHA256"]
+OP_HASH160 = BYTE_OPCODE["OP_HASH160"]
+OP_HASH256 = BYTE_OPCODE["OP_HASH256"]
+OP_CODESEPARATOR = BYTE_OPCODE["OP_CODESEPARATOR"]
+OP_CHECKSIG = BYTE_OPCODE["OP_CHECKSIG"]
+OP_CHECKSIGVERIFY = BYTE_OPCODE["OP_CHECKSIGVERIFY"]
+OP_CHECKMULTISIG = BYTE_OPCODE["OP_CHECKMULTISIG"]
+OP_CHECKMULTISIGVERIFY = BYTE_OPCODE["OP_CHECKMULTISIGVERIFY"]
+
+# expansion
+
+OP_NOP1 = BYTE_OPCODE["OP_NOP1"]
+OP_CHECKLOCKTIMEVERIFY = BYTE_OPCODE["OP_CHECKLOCKTIMEVERIFY"]
+OP_CHECKSEQUENCEVERIFY = BYTE_OPCODE["OP_CHECKSEQUENCEVERIFY"]
+OP_NOP4 = BYTE_OPCODE["OP_NOP4"]
+OP_NOP5 = BYTE_OPCODE["OP_NOP5"]
+OP_NOP6 = BYTE_OPCODE["OP_NOP6"]
+OP_NOP7 = BYTE_OPCODE["OP_NOP7"]
+OP_NOP8 = BYTE_OPCODE["OP_NOP8"]
+OP_NOP9 = BYTE_OPCODE["OP_NOP9"]
+OP_NOP10 = BYTE_OPCODE["OP_NOP10"]
+
+# template matching params
+
+OP_SMALLINTEGER = BYTE_OPCODE["OP_SMALLINTEGER"]
+OP_PUBKEYS = BYTE_OPCODE["OP_PUBKEYS"]
+OP_PUBKEYHASH = BYTE_OPCODE["OP_PUBKEYHASH"]
+OP_PUBKEY = BYTE_OPCODE["OP_PUBKEY"]
+OP_INVALIDOPCODE = BYTE_OPCODE["OP_INVALIDOPCODE"]
\ No newline at end of file
diff --git a/pybtc/tools.py b/pybtc/tools.py
index 918d209..a3808a3 100644
--- a/pybtc/tools.py
+++ b/pybtc/tools.py
@@ -35,7 +35,7 @@ def create_private_key(compressed=True, testnet=False, wif=True, hex=False):
if not i and int.from_bytes(h, byteorder="big") > MAX_INT_PRIVATE_KEY:
i += 1
if wif:
- return private_key_to_wif(h)
+ return private_key_to_wif(h, compressed=compressed, testnet=testnet)
elif hex:
return hexlify(h).decode()
return h
@@ -48,7 +48,7 @@ def private_key_to_wif(h, compressed=True, testnet=False):
:param h: private key 32 byte string or HEX encoded string.
:param compressed: (optional) flag of public key compressed format, by default set to True.
:param testnet: (optional) flag for testnet network, by default is False.
- :return: Private key in WIF format
+ :return: Private key in WIF format.
"""
# uncompressed: 0x80 + [32-byte secret] + [4 bytes of Hash() of previous 33 bytes], base58 encoded.
# compressed: 0x80 + [32-byte secret] + 0x01 + [4 bytes of Hash() previous 34 bytes], base58 encoded.
@@ -86,7 +86,7 @@ def is_wif_valid(wif):
Check is private key in WIF format string is valid.
:param wif: private key in WIF format string.
- :return: boolean
+ :return: boolean.
"""
if not isinstance(wif, str):
raise TypeError("invalid wif key")
@@ -117,7 +117,7 @@ def private_to_public_key(private_key, compressed=True, hex=True):
In case private_key in WIF format, this flag is set in accordance with
the key format specified in WIF string.
:param hex: (optional) if set to True return key in HEX format, by default is True.
- :return: 33/65 bytes public key in HEX or bytes string
+ :return: 33/65 bytes public key in HEX or bytes string.
"""
if not isinstance(private_key, bytes):
if isinstance(private_key, bytearray):
@@ -152,7 +152,7 @@ def is_public_key_valid(key):
Check public key is valid.
:param key: public key in HEX or bytes string format.
- :return: boolean
+ :return: boolean.
"""
if isinstance(key, str):
key = unhexlify(key)
@@ -260,7 +260,7 @@ def address_to_hash(address, hex=True):
:param address: address in base58 or bech32 format.
:param hex: (optional) If set to True return key in HEX format, by default is True.
- :return: script in HEX or bytes string
+ :return: script in HEX or bytes string.
"""
if address[0] in ADDRESS_PREFIX_LIST:
h = decode_base58(address)[1:-4]
@@ -328,7 +328,7 @@ def address_to_script(address, hex=False):
:param address: address in base58 or bech32 format.
:param hex: (optional) If set to True return key in HEX format, by default is True.
- :return: public key script in HEX or bytes string
+ :return: public key script in HEX or bytes string.
"""
if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX):
@@ -358,8 +358,9 @@ def address_to_script(address, hex=False):
def public_key_to_p2sh_p2wpkh_script(pubkey):
- assert len(pubkey) == 33
- return b'\x00\x14' + hash160(pubkey)
+ if len(pubkey) != 33:
+ raise TypeError("public key len invalid")
+ return b'\x00\x14%s' % hash160(pubkey)
def is_address_valid(address, testnet=False):
@@ -368,7 +369,7 @@ def is_address_valid(address, testnet=False):
:param address: address in base58 or bech32 format.
:param testnet: (optional) flag for testnet network, by default is False.
- :return: boolean
+ :return: boolean.
"""
if not address or type(address) != str:
return False
@@ -438,6 +439,20 @@ def get_witness_version(address):
# Script
def parse_script(script, segwit=True):
+ """
+ Parse script and return script type, script address and required signatures count.
+
+ :param script: script in bytes string or HEX encoded string format.
+ :param segwit: (optional) If set to True recognize P2WPKH and P2WSH sripts, by default set to True.
+
+ :return: dictionary:
+
+ - nType - numeric script type
+ - type - script type
+ - addressHash - address hash in case address recognized
+ - script - script if no address recognized
+ - reqSigs - required signatures count
+ """
if not script:
return {"nType": 7, "type": "NON_STANDARD", "reqSigs": 0, "script": b""}
if type(script) == str:
@@ -533,12 +548,20 @@ def parse_script(script, segwit=True):
def decode_script(script, asm=False):
- if type(script) == str:
+ """
+ Decode script to ASM format or to human readable OPCODES string.
+
+ :param script: script in bytes string or HEX encoded string format.
+ :param asm: (optional) If set to True decode to ASM fromat, by default set to False.
+ :return: script in ASM format string or OPCODES string.
+ """
+ if isinstance(script, str):
try:
script = unhexlify(script)
except:
pass
- assert type(script) == bytes
+ if not isinstance(script, bytes):
+ raise TypeError("script invalid")
l = len(script)
s = 0
result = []
@@ -562,22 +585,33 @@ def decode_script(script, asm=False):
def delete_from_script(script, sub_script):
+ """
+ Decode OPCODE or subscript from script.
+
+ :param script: traget script in bytes or HEX encoded string.
+ :param sub_script: sub_script which is necessary to remove from target script in bytes or HEX encoded string.
+ :return: script in bytes or HEX encoded string corresponding to the format of target script.
+ """
if not sub_script:
return script
s_hex = False
- if type(script) == str:
+ if isinstance(script, str):
try:
script = unhexlify(script)
s_hex = True
except:
pass
- assert type(script) == bytes
- if type(sub_script) == str:
+ if isinstance(sub_script, str):
try:
sub_script = unhexlify(sub_script)
except:
pass
- assert type(sub_script) == bytes
+
+ if not isinstance(script, bytes):
+ raise TypeError("script invalid")
+ if not isinstance(sub_script, bytes):
+ raise TypeError("sub_script invalid")
+
l = len(script)
ls = len(sub_script)
s = 0
@@ -621,41 +655,57 @@ def delete_from_script(script, sub_script):
return b''.join(result) if not s_hex else hexlify(b''.join(result)).decode()
-def script_to_hash(s, witness=False, hex=False):
- if type(s) == str:
- s = unhexlify(s)
+def script_to_hash(script, witness=False, hex=True):
+ """
+ Encode script to hash HASH160 or SHA256 in dependency of the witness.
+
+ :param script: script in bytes or HEX encoded string.
+ :param witness: (optional) If set to True return SHA256 hash for P2WSH, by default is False.
+ :param hex: (optional) If set to True return key in HEX format, by default is True.
+ :param sub_script: sub_script which is necessary to remove from target script in bytes or HEX encoded string.
+ :return: script in bytes or HEX encoded string corresponding to the format of target script.
+ """
+ if isinstance(script, str):
+ s = unhexlify(script)
if witness:
- return sha256(s, hex)
+ return sha256(script, hex)
else:
- return hash160(s, hex)
+ return hash160(script, hex)
-#
-# ECDSA
-#
+# Signatures
def verify_signature(sig, pub_key, msg):
- if type(sig) != bytes:
- if type(sig) == bytearray:
+ """
+ Verify signature for message and given public key
+
+ :param sig: signature in bytes or HEX encoded string.
+ :param pub_key: public key in bytes or HEX encoded string.
+ :param msg: message in bytes or HEX encoded string.
+ :return: boolean.
+ """
+ if not isinstance(sig, bytes):
+ if isinstance(sig, bytearray):
sig = bytes(sig)
- elif type(sig) == str:
+ elif isinstance(sig, str):
sig = unhexlify(sig)
else:
raise TypeError("signature must be a bytes or hex encoded string")
- if type(pub_key) != bytes:
- if type(pub_key) == bytearray:
+ if not isinstance(pub_key, bytes):
+ if isinstance(pub_key, bytearray):
pub_key = bytes(pub_key)
- elif type(pub_key) == str:
+ elif isinstance(pub_key, str):
pub_key = unhexlify(pub_key)
else:
raise TypeError("public key must be a bytes or hex encoded string")
- if type(msg) != bytes:
- if type(msg) == bytearray:
+ if not isinstance(msg, bytes):
+ if isinstance(msg, bytearray):
msg = bytes(msg)
- elif type(msg) == str:
+ elif isinstance(msg, str):
msg = unhexlify(msg)
else:
raise TypeError("message must be a bytes or hex encoded string")
+
raw_sig = ffi.new('secp256k1_ecdsa_signature *')
raw_pubkey = ffi.new('secp256k1_pubkey *')
if not secp256k1.secp256k1_ecdsa_signature_parse_der(ECDSA_CONTEXT_VERIFY, raw_sig, sig, len(sig)):
@@ -666,45 +716,59 @@ def verify_signature(sig, pub_key, msg):
return True if result else False
-def sign_message(msg, private_key, hex=False):
+def sign_message(msg, private_key, hex=True):
"""
Sign message
- :param msg: message to sign
- :param private_key: private key (bytes, hex encoded string)
- :param hex:
- :return: DER encoded sinature
+ :param msg: message to sign bytes or HEX encoded string.
+ :param private_key: private key (bytes, hex encoded string or WIF format)
+ :param hex: (optional) If set to True return key in HEX format, by default is True.
+ :return: DER encoded signature in bytes or HEX encoded string.
"""
- if type(msg) != bytes:
- if type(msg) == bytearray:
- msg = bytes(msg)
-
- elif type(msg) == str:
+ if isinstance(msg, bytearray):
+ msg = bytes(msg)
+ if isinstance(msg, str):
+ try:
msg = unhexlify(msg)
- else:
+ except:
+ pass
+ if not isinstance(msg, bytes):
raise TypeError("message must be a bytes or hex encoded string")
- if type(private_key) != bytes:
- if type(private_key) == bytearray:
- private_key = bytes(private_key)
- elif type(private_key) == str:
+
+ if isinstance(private_key, bytearray):
+ private_key = bytes(private_key)
+ if isinstance(private_key, str):
+ try:
private_key = unhexlify(private_key)
- else:
- raise TypeError("private key must be a bytes or hex encoded string")
+ except:
+ if is_wif_valid(private_key):
+ private_key = wif_to_private_key(private_key, hex=False)
+ if not isinstance(private_key, bytes):
+ raise TypeError("private key must be a bytes, hex encoded string or in WIF format")
+
raw_sig = ffi.new('secp256k1_ecdsa_signature *')
signed = secp256k1.secp256k1_ecdsa_sign(ECDSA_CONTEXT_SIGN, raw_sig, msg,
private_key, ffi.NULL, ffi.NULL)
- assert signed == 1
+ if not signed:
+ raise RuntimeError("secp256k1 error")
len_sig = 74
output = ffi.new('unsigned char[%d]' % len_sig)
outputlen = ffi.new('size_t *', len_sig)
res = secp256k1.secp256k1_ecdsa_signature_serialize_der(ECDSA_CONTEXT_SIGN,
output, outputlen, raw_sig)
- assert res == 1
+ if not res:
+ raise RuntimeError("secp256k1 error")
signature = bytes(ffi.buffer(output, outputlen[0]))
return hexlify(signature).decode() if hex else signature
def is_valid_signature_encoding(sig):
+ """
+ Check is valid signature encoded in DER format
+
+ :param sig: signature in bytes or HEX encoded string.
+ :return: boolean.
+ """
# Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash]
# * total-length: 1-byte length descriptor of everything that follows,
# excluding the sighash byte.
@@ -766,17 +830,25 @@ def is_valid_signature_encoding(sig):
return True
-#
-# Transaction encoding
-#
+# Hash encoding
-def rh2s(tthash):
- # raw hash to string
- return hexlify(tthash[::-1]).decode()
+def rh2s(raw_hash):
+ """
+ Encode raw transaction hash to HEX string with bytes order change
+
+ :param raw_hash: transaction hash in bytes string.
+ :return: HEX encoded string.
+ """
+ return hexlify(raw_hash[::-1]).decode()
def s2rh(hash_string):
- # string to raw hash
+ """
+ Decode HEX transaction hash to bytes with byte order change
+
+ :param raw_hash: transaction hash in bytes string.
+ :return: bytes string.
+ """
return unhexlify(hash_string)[::-1]
@@ -785,16 +857,27 @@ def s2rh_step4(hash_string):
return reverse_hash(h)
-def reverse_hash(h):
- return struct.pack('>IIIIIIII', *struct.unpack('>IIIIIIII', h)[::-1])[::-1]
+def reverse_hash(raw_hash):
+ """
+ Reverse hash order
-#
-#
-#
+ :param raw_hash: bytes string.
+ :return: bytes string.
+ """
+ return struct.pack('>IIIIIIII', *struct.unpack('>IIIIIIII', raw_hash)[::-1])[::-1]
-def merkle_root(tx_hash_list):
- tx_hash_list = list(tx_hash_list)
+# Merkle root
+
+def merkle_root(tx_hash_list, hex=True):
+ """
+ Calculate merkle root from transaction hash list
+
+ :param tx_hash_list: list of transaction hashes in bytes or HEX encoded string.
+ :param hex: (optional) If set to True return result in HEX format, by default is True.
+ :return: merkle root in bytes or HEX encoded string corresponding hex flag.
+ """
+ tx_hash_list = [h if isinstance(h, bytes) else s2rh(h) for h in tx_hash_list]
if len(tx_hash_list) == 1:
return tx_hash_list[0]
while True:
@@ -809,11 +892,18 @@ def merkle_root(tx_hash_list):
if len(new_hash_list) > 1:
tx_hash_list = new_hash_list
else:
- return new_hash_list[0]
+ return new_hash_list[0] if not hex else hexlify(new_hash_list[0]).decode()
-def merkle_branches(tx_hash_list):
- tx_hash_list = list(tx_hash_list)
+def merkle_branches(tx_hash_list, hex=True):
+ """
+ Calculate merkle branches for coinbase transacton
+
+ :param tx_hash_list: list of transaction hashes in bytes or HEX encoded string.
+ :param hex: (optional) If set to True return result in HEX format, by default is True.
+ :return: list of merkle branches in bytes or HEX encoded string corresponding hex flag.
+ """
+ tx_hash_list = [h if isinstance(h, bytes) else s2rh(h) for h in tx_hash_list]
branches = []
if len(tx_hash_list) == 1:
return []
@@ -833,19 +923,36 @@ def merkle_branches(tx_hash_list):
else:
if new_hash_list:
branches.append(new_hash_list.pop(0))
- return branches
+ return branches if not hex else [hexlify(h).decode() for h in branches]
-def merkleroot_from_branches(merkle_branches, coinbase_hash_bin):
- merkle_root = coinbase_hash_bin
+def merkleroot_from_branches(merkle_branches, coinbase_hash, hex=True):
+ """
+ Calculate merkle root from merkle branches and coinbase transacton hash
+
+ :param merkle_branches: list merkle branches in bytes or HEX encoded string.
+ :param coinbase_hash: list coinbase transaction hash in bytes or HEX encoded string.
+ :param hex: (optional) If set to True return result in HEX format, by default is True.
+ :return: merkle root in bytes or HEX encoded string corresponding hex flag.
+ """
+ merkle_root = coinbase_hash if not isinstance(coinbase_hash, str) else unhexlify(coinbase_hash)
for h in merkle_branches:
if type(h) == str:
h = unhexlify(h)
merkle_root = double_sha256(merkle_root + h)
- return merkle_root
+ return merkle_root if not hex else hexlify(merkle_root).decode()
+
+
+# Difficulty
def bits_to_target(bits):
+ """
+ Calculate target from bits
+
+ :param bits: HEX string, bytes string or integer representation of bits.
+ :return: integer.
+ """
if type(bits) == str:
bits = unhexlify(bits)
if type(bits) == bytes:
@@ -857,48 +964,97 @@ def bits_to_target(bits):
def target_to_difficulty(target):
+ """
+ Calculate difficulty from target
+
+ :param target: integer.
+ :return: float.
+ """
return 0x00000000FFFF0000000000000000000000000000000000000000000000000000 / target
def bits_to_difficulty(bits):
+ """
+ Calculate difficulty from bits
+
+ :param bits: HEX string, bytes string or integer representation of bits.
+ :return: integer.
+ """
return target_to_difficulty(bits_to_target(bits))
def difficulty_to_target(difficulty):
+ """
+ Calculate target from difficulty
+
+ :param target: integer.
+ :return: float.
+ """
return int(0x00000000FFFF0000000000000000000000000000000000000000000000000000 / difficulty)
-#
-#
-#
+# Tools
+
def bytes_needed(n):
+ """
+ Calculate bytes needed to convert integer to bytes.
+
+ :param n: integer.
+ :return: integer.
+ """
if n == 0:
return 1
return math.ceil(n.bit_length()/8)
def int_to_bytes(i, byteorder='big'):
+ """
+ Convert integer to bytes.
+
+ :param n: integer.
+ :param byteorder: (optional) byte order 'big' or 'little', by default 'big'.
+ :return: bytes.
+ """
return i.to_bytes(bytes_needed(i), byteorder=byteorder, signed=False)
def bytes_to_int(i, byteorder='big'):
+ """
+ Convert bytes to integer.
+
+ :param i: bytes.
+ :param byteorder: (optional) byte order 'big' or 'little', by default 'big'.
+ :return: integer.
+ """
return int.from_bytes(i, byteorder=byteorder, signed=False)
# variable integer
def int_to_var_int(i):
+ """
+ Convert integer to variable integer
+
+ :param i: integer.
+ :return: bytes.
+ """
if i < 0xfd:
return struct.pack('