fix
This commit is contained in:
parent
ac6ab0ae4c
commit
2eed4aa1ef
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "docs/src/alabaster"]
|
||||||
|
path = docs/src/alabaster
|
||||||
|
url = https://github.com/bitprophet/alabaster.git
|
||||||
@ -1,3 +1,3 @@
|
|||||||
<img src="doc/img/pybtc.png" width="100">
|
<img src="docs/img/pybtc.png" width="100">
|
||||||
|
|
||||||
## Python bitcoin library
|
## Python bitcoin library
|
||||||
|
|||||||
@ -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:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
=========
|
||||||
|
Reference
|
||||||
|
=========
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:name: mastertoc
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
address.rst
|
||||||
|
transaction.rst
|
||||||
|
block.rst
|
||||||
|
|
||||||
|
|
||||||
@ -14,6 +14,9 @@
|
|||||||
#
|
#
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from sphinx.ext.autodoc import (
|
||||||
|
ClassLevelDocumenter, InstanceAttributeDocumenter)
|
||||||
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath('.'))
|
sys.path.insert(0, os.path.abspath('.'))
|
||||||
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 information -----------------------------------------------------
|
||||||
|
|
||||||
project = 'pybtc'
|
project = 'pybtc'
|
||||||
copyright = '2018, Aleksey Karpov'
|
copyright = '2015-2018, bitaps.com'
|
||||||
author = 'Aleksey Karpov'
|
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
|
# The short X.Y version
|
||||||
version = ''
|
version = ''
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|
||||||
@ -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
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,18 +1,14 @@
|
|||||||
|
|
||||||
==============
|
========================
|
||||||
Pure functions
|
Pure functions reference
|
||||||
==============
|
========================
|
||||||
|
|
||||||
Base function primitives implemeted in functional programming paradigm.
|
Base function primitives implemented in functional programming paradigm.
|
||||||
|
|
||||||
Key management
|
|
||||||
==============
|
|
||||||
|
|
||||||
Tools for private and public key managment
|
|
||||||
|
|
||||||
|
|
||||||
Private key
|
|
||||||
-----------
|
Private keys
|
||||||
|
============
|
||||||
|
|
||||||
.. autofunction:: pybtc.create_private_key
|
.. autofunction:: pybtc.create_private_key
|
||||||
.. autofunction:: pybtc.private_key_to_wif
|
.. autofunction:: pybtc.private_key_to_wif
|
||||||
@ -20,8 +16,8 @@ Private key
|
|||||||
.. autofunction:: pybtc.is_wif_valid
|
.. autofunction:: pybtc.is_wif_valid
|
||||||
|
|
||||||
|
|
||||||
Public key
|
Public keys
|
||||||
----------
|
===========
|
||||||
|
|
||||||
.. WARNING::
|
.. WARNING::
|
||||||
Using uncompressed public keys is
|
Using uncompressed public keys is
|
||||||
@ -47,6 +43,66 @@ Addresses
|
|||||||
.. autofunction:: pybtc.is_address_valid
|
.. 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
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -28,39 +28,36 @@ Key Features
|
|||||||
|
|
||||||
.. _aiohttp-installation:
|
.. _aiohttp-installation:
|
||||||
|
|
||||||
Library Installation
|
Quick library Installation
|
||||||
====================
|
==========================
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ pip install secp256k1
|
|
||||||
$ pip install pybtc
|
$ pip install pybtc
|
||||||
|
|
||||||
|
|
||||||
Getting Started
|
Getting Started
|
||||||
===============
|
===============
|
||||||
|
|
||||||
Client example::
|
Usage example::
|
||||||
|
|
||||||
import pybtc
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
Server example::
|
|
||||||
|
|
||||||
import pybtc
|
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 ?
|
||||||
|
=========================
|
||||||
|
|
||||||
|
- Mnemonic code generation (BIP39)
|
||||||
Tutorial
|
- Hierarchical Deterministic Wallets (BIP32)
|
||||||
========
|
- Wallet class implemented acording BIP44
|
||||||
|
- Imporved transaction deserialization perfomance
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -81,21 +78,16 @@ Dependencies
|
|||||||
- *secp256k1*
|
- *secp256k1*
|
||||||
|
|
||||||
|
|
||||||
Communication channels
|
|
||||||
======================
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Contributing
|
|
||||||
============
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Authors and License
|
Authors and License
|
||||||
===================
|
===================
|
||||||
|
|
||||||
The ``pybtc`` package is written mostly by Aleksey Karpov.
|
The ``pybtc`` package was initially written by `Aleksey Karpov <https://github.com/4tochka>`_ and development continues with contributors.
|
||||||
|
|
||||||
|
Recent contributors:
|
||||||
|
|
||||||
|
- `Aleksey Karpov <https://github.com/4tochka>`_
|
||||||
|
- `Aleksey Karybkin <https://github.com/mboxk3team>`_
|
||||||
|
|
||||||
It's *GPL-3.0* licensed and freely available.
|
It's *GPL-3.0* licensed and freely available.
|
||||||
|
|
||||||
@ -111,4 +103,11 @@ Table Of Contents
|
|||||||
:name: mastertoc
|
:name: mastertoc
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
|
installation.rst
|
||||||
|
examples.rst
|
||||||
|
classes.rst
|
||||||
functional.rst
|
functional.rst
|
||||||
|
contributing.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 <https://pip.pypa.io/en/stable/installing/>`_ 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
============
|
||||||
|
Transactions
|
||||||
|
============
|
||||||
|
|
||||||
|
The class for creating transaction.
|
||||||
|
|
||||||
|
|
||||||
|
.. autoclass:: pybtc.Transaction
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|
||||||
1
docs/src/alabaster
Submodule
1
docs/src/alabaster
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 609374c5434eb960eab1ae25ea50b007fbc6c2ae
|
||||||
5
docs/src/pip-delete-this-directory.txt
Normal file
5
docs/src/pip-delete-this-directory.txt
Normal file
@ -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).
|
||||||
@ -2,6 +2,7 @@ from .tools import *
|
|||||||
from .opcodes import *
|
from .opcodes import *
|
||||||
from .consensus import *
|
from .consensus import *
|
||||||
from .transaction import *
|
from .transaction import *
|
||||||
|
from .block import *
|
||||||
from .address import *
|
from .address import *
|
||||||
|
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
|
|||||||
162
pybtc/address.py
162
pybtc/address.py
@ -2,11 +2,32 @@ from .tools import *
|
|||||||
|
|
||||||
|
|
||||||
class PrivateKey():
|
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):
|
def __init__(self, key=None, compressed=True, testnet=False):
|
||||||
|
|
||||||
if key is None:
|
if key is None:
|
||||||
|
|
||||||
|
#: flag for compressed type of corresponding public key (boolean)
|
||||||
self.compressed = compressed
|
self.compressed = compressed
|
||||||
|
#: flag for testnet network private key (boolean)
|
||||||
self.testnet = testnet
|
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:
|
else:
|
||||||
if isinstance(key, str):
|
if isinstance(key, str):
|
||||||
try:
|
try:
|
||||||
@ -15,13 +36,17 @@ class PrivateKey():
|
|||||||
pass
|
pass
|
||||||
if isinstance(key, bytes):
|
if isinstance(key, bytes):
|
||||||
if len(key) != 32:
|
if len(key) != 32:
|
||||||
raise TypeError("private key invalid")
|
raise TypeError("private key invalid length")
|
||||||
self.raw_key = key
|
self.key = key
|
||||||
self.compressed = compressed
|
self.compressed = compressed
|
||||||
self.testnet = testnet
|
self.testnet = testnet
|
||||||
|
self.hex = hexlify(self.key).decode()
|
||||||
|
self.wif = private_key_to_wif(self.key, compressed, testnet)
|
||||||
return
|
return
|
||||||
assert isinstance(key, str)
|
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,
|
if key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX,
|
||||||
TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX):
|
TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX):
|
||||||
self.compressed = False
|
self.compressed = False
|
||||||
@ -33,57 +58,101 @@ class PrivateKey():
|
|||||||
else:
|
else:
|
||||||
self.testnet = False
|
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):
|
def __str__(self):
|
||||||
return self.wif()
|
return self.wif
|
||||||
|
|
||||||
|
|
||||||
class PublicKey():
|
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):
|
if isinstance(key, str):
|
||||||
try:
|
try:
|
||||||
key = unhexlify(key)
|
key = unhexlify(key)
|
||||||
except:
|
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):
|
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,
|
compressed=key.compressed,
|
||||||
hex=False)
|
hex=False)
|
||||||
if not isinstance(key, bytes):
|
#: public key in bytes (bytes)
|
||||||
raise TypeError("public key invalid")
|
self.key = public_key
|
||||||
if len(key) != 33 and len(key) != 65:
|
#: public key in HEX (string)
|
||||||
raise TypeError("public key invalid")
|
self.hex = hexlify(self.key).decode()
|
||||||
if len(key) == 33:
|
|
||||||
self.compressed = True
|
|
||||||
else:
|
|
||||||
self.compressed = False
|
|
||||||
self.raw_key = key
|
|
||||||
|
|
||||||
def hex(self):
|
|
||||||
return hexlify(self.raw_key).decode()
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.hex()
|
return self.hex
|
||||||
|
|
||||||
|
|
||||||
class Address():
|
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,
|
def __init__(self, key=None,
|
||||||
address_type="P2WPKH", testnet=False, compressed=True):
|
address_type="P2WPKH", testnet=False, compressed=True):
|
||||||
if key is None:
|
if key is None:
|
||||||
|
#: instance of ``PrivateKey`` class
|
||||||
self.private_key = PrivateKey(testnet=testnet,
|
self.private_key = PrivateKey(testnet=testnet,
|
||||||
compressed=compressed)
|
compressed=compressed)
|
||||||
|
#: instance of ``PublicKey`` class
|
||||||
self.public_key = PublicKey(self.private_key)
|
self.public_key = PublicKey(self.private_key)
|
||||||
|
#: flag for testnet network address (boolean)
|
||||||
self.testnet = testnet
|
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.private_key = key
|
||||||
self.testnet = key.testnet
|
self.testnet = key.testnet
|
||||||
compressed = key.compressed
|
compressed = key.compressed
|
||||||
@ -97,21 +166,31 @@ class Address():
|
|||||||
if not compressed:
|
if not compressed:
|
||||||
if address_type not in ("P2PKH", "PUBKEY", "P2SH"):
|
if address_type not in ("P2PKH", "PUBKEY", "P2SH"):
|
||||||
raise TypeError("compressed public key invalid")
|
raise TypeError("compressed public key invalid")
|
||||||
|
#: flag for testnet network address (boolean)
|
||||||
self.type = address_type
|
self.type = address_type
|
||||||
|
|
||||||
if address_type in ("P2WPKH"):
|
if address_type in ("P2WPKH"):
|
||||||
|
#: version of witness program for SEGWIT address (string)
|
||||||
self.witness_version = 0
|
self.witness_version = 0
|
||||||
else:
|
else:
|
||||||
self.witness_version = None
|
self.witness_version = None
|
||||||
self.compressed = compressed
|
self.compressed = compressed
|
||||||
if address_type == "P2SH_P2WPKH":
|
if address_type == "P2SH_P2WPKH":
|
||||||
|
#: flag for script hash address (boolean)
|
||||||
self.script_hash = True
|
self.script_hash = True
|
||||||
self.redeem_script_raw = public_key_to_p2sh_p2wpkh_script(self.public_key.raw_key)
|
#: redeeem script, only for P2SH_P2WPKH (bytes)
|
||||||
self.redeem_script = hexlify(self.redeem_script_raw).decode()
|
self.redeem_script = public_key_to_p2sh_p2wpkh_script(self.public_key.key)
|
||||||
self.hash = hash160(self.redeem_script_raw)
|
#: 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:
|
else:
|
||||||
self.script_hash = False
|
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,
|
self.address = hash_to_address(self.hash,
|
||||||
script_hash=self.script_hash,
|
script_hash=self.script_hash,
|
||||||
witness_version=self.witness_version,
|
witness_version=self.witness_version,
|
||||||
@ -128,12 +207,13 @@ class ScriptAddress():
|
|||||||
self.testnet = testnet
|
self.testnet = testnet
|
||||||
if isinstance(script, str):
|
if isinstance(script, str):
|
||||||
script = unhexlify(script)
|
script = unhexlify(script)
|
||||||
self.script_raw = script
|
self.script = script
|
||||||
self.script = hexlify(self.script_raw).decode()
|
self.script_hex = hexlify(self.script).decode()
|
||||||
self.hash = hash160(self.script_raw)
|
self.hash = hash160(self.script)
|
||||||
self.script_opcodes = decode_script(self.script_raw)
|
self.script_opcodes = decode_script(self.script)
|
||||||
self.script_opcodes_asm = decode_script(self.script_raw, 1)
|
self.script_opcodes_asm = decode_script(self.script, 1)
|
||||||
self.address = hash_to_address(self.hash,
|
self.address = hash_to_address(self.hash,
|
||||||
script_hash=True,
|
script_hash=True,
|
||||||
witness_version=self.witness_version,
|
witness_version=self.witness_version,
|
||||||
testnet=self.testnet)
|
testnet=self.testnet)
|
||||||
|
|
||||||
|
|||||||
@ -2,11 +2,11 @@ from secp256k1 import lib as secp256k1
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
MAX_AMOUNT = 2100000000000000
|
MAX_AMOUNT = 2100000000000000
|
||||||
SIGHASH_ALL = 0x00000001
|
SIGHASH_ALL = 0x00000001
|
||||||
SIGHASH_NONE = 0x00000002
|
SIGHASH_NONE = 0x00000002
|
||||||
SIGHASH_SINGLE = 0x00000003
|
SIGHASH_SINGLE = 0x00000003
|
||||||
SIGHASH_ANYONECANPAY = 0x00000080
|
SIGHASH_ANYONECANPAY = 0x00000080
|
||||||
MAX_INT_PRIVATE_KEY = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
|
MAX_INT_PRIVATE_KEY = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
|
||||||
|
|
||||||
MAINNET_ADDRESS_BYTE_PREFIX = b'\x00'
|
MAINNET_ADDRESS_BYTE_PREFIX = b'\x00'
|
||||||
TESTNET_ADDRESS_BYTE_PREFIX = b'\x6f'
|
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_VERIFY = secp256k1.secp256k1_context_create(FLAG_VERIFY)
|
||||||
ECDSA_CONTEXT_ALL = secp256k1.secp256k1_context_create(ALL_FLAGS)
|
ECDSA_CONTEXT_ALL = secp256k1.secp256k1_context_create(ALL_FLAGS)
|
||||||
secp256k1.secp256k1_context_randomize(ECDSA_CONTEXT_SIGN,
|
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,
|
SCRIPT_TYPES = {"P2PKH": 0,
|
||||||
"P2SH" : 1,
|
"P2SH": 1,
|
||||||
"PUBKEY": 2,
|
"PUBKEY": 2,
|
||||||
"NULL_DATA": 3,
|
"NULL_DATA": 3,
|
||||||
"MULTISIG": 4,
|
"MULTISIG": 4,
|
||||||
"P2WPKH": 5,
|
"P2WPKH": 5,
|
||||||
"P2WSH": 6,
|
"P2WSH": 6,
|
||||||
"NON_STANDART": 7
|
"NON_STANDART": 7
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
150
pybtc/opcodes.py
150
pybtc/opcodes.py
@ -150,6 +150,150 @@ OPCODE["OP_PUBKEY"] = 0xfe
|
|||||||
OPCODE["OP_INVALIDOPCODE"] = 0xff
|
OPCODE["OP_INVALIDOPCODE"] = 0xff
|
||||||
|
|
||||||
|
|
||||||
RAW_OPCODE = dict ( (OPCODE[i], i) 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 )
|
BYTE_OPCODE = dict((i, bytes([OPCODE[i]])) for i in OPCODE)
|
||||||
HEX_OPCODE = dict ((i,hexlify(bytes([OPCODE[i]])).decode()) 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"]
|
||||||
364
pybtc/tools.py
364
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:
|
if not i and int.from_bytes(h, byteorder="big") > MAX_INT_PRIVATE_KEY:
|
||||||
i += 1
|
i += 1
|
||||||
if wif:
|
if wif:
|
||||||
return private_key_to_wif(h)
|
return private_key_to_wif(h, compressed=compressed, testnet=testnet)
|
||||||
elif hex:
|
elif hex:
|
||||||
return hexlify(h).decode()
|
return hexlify(h).decode()
|
||||||
return h
|
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 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 compressed: (optional) flag of public key compressed format, by default set to True.
|
||||||
:param testnet: (optional) flag for testnet network, by default is False.
|
: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.
|
# 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.
|
# 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.
|
Check is private key in WIF format string is valid.
|
||||||
|
|
||||||
:param wif: private key in WIF format string.
|
:param wif: private key in WIF format string.
|
||||||
:return: boolean
|
:return: boolean.
|
||||||
"""
|
"""
|
||||||
if not isinstance(wif, str):
|
if not isinstance(wif, str):
|
||||||
raise TypeError("invalid wif key")
|
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
|
In case private_key in WIF format, this flag is set in accordance with
|
||||||
the key format specified in WIF string.
|
the key format specified in WIF string.
|
||||||
:param hex: (optional) if set to True return key in HEX format, by default is True.
|
: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 not isinstance(private_key, bytes):
|
||||||
if isinstance(private_key, bytearray):
|
if isinstance(private_key, bytearray):
|
||||||
@ -152,7 +152,7 @@ def is_public_key_valid(key):
|
|||||||
Check public key is valid.
|
Check public key is valid.
|
||||||
|
|
||||||
:param key: public key in HEX or bytes string format.
|
:param key: public key in HEX or bytes string format.
|
||||||
:return: boolean
|
:return: boolean.
|
||||||
"""
|
"""
|
||||||
if isinstance(key, str):
|
if isinstance(key, str):
|
||||||
key = unhexlify(key)
|
key = unhexlify(key)
|
||||||
@ -260,7 +260,7 @@ def address_to_hash(address, hex=True):
|
|||||||
|
|
||||||
:param address: address in base58 or bech32 format.
|
:param address: address in base58 or bech32 format.
|
||||||
:param hex: (optional) If set to True return key in HEX format, by default is True.
|
: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:
|
if address[0] in ADDRESS_PREFIX_LIST:
|
||||||
h = decode_base58(address)[1:-4]
|
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 address: address in base58 or bech32 format.
|
||||||
:param hex: (optional) If set to True return key in HEX format, by default is True.
|
: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,
|
if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX,
|
||||||
MAINNET_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):
|
def public_key_to_p2sh_p2wpkh_script(pubkey):
|
||||||
assert len(pubkey) == 33
|
if len(pubkey) != 33:
|
||||||
return b'\x00\x14' + hash160(pubkey)
|
raise TypeError("public key len invalid")
|
||||||
|
return b'\x00\x14%s' % hash160(pubkey)
|
||||||
|
|
||||||
|
|
||||||
def is_address_valid(address, testnet=False):
|
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 address: address in base58 or bech32 format.
|
||||||
:param testnet: (optional) flag for testnet network, by default is False.
|
:param testnet: (optional) flag for testnet network, by default is False.
|
||||||
:return: boolean
|
:return: boolean.
|
||||||
"""
|
"""
|
||||||
if not address or type(address) != str:
|
if not address or type(address) != str:
|
||||||
return False
|
return False
|
||||||
@ -438,6 +439,20 @@ def get_witness_version(address):
|
|||||||
# Script
|
# Script
|
||||||
|
|
||||||
def parse_script(script, segwit=True):
|
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:
|
if not script:
|
||||||
return {"nType": 7, "type": "NON_STANDARD", "reqSigs": 0, "script": b""}
|
return {"nType": 7, "type": "NON_STANDARD", "reqSigs": 0, "script": b""}
|
||||||
if type(script) == str:
|
if type(script) == str:
|
||||||
@ -533,12 +548,20 @@ def parse_script(script, segwit=True):
|
|||||||
|
|
||||||
|
|
||||||
def decode_script(script, asm=False):
|
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:
|
try:
|
||||||
script = unhexlify(script)
|
script = unhexlify(script)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
assert type(script) == bytes
|
if not isinstance(script, bytes):
|
||||||
|
raise TypeError("script invalid")
|
||||||
l = len(script)
|
l = len(script)
|
||||||
s = 0
|
s = 0
|
||||||
result = []
|
result = []
|
||||||
@ -562,22 +585,33 @@ def decode_script(script, asm=False):
|
|||||||
|
|
||||||
|
|
||||||
def delete_from_script(script, sub_script):
|
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:
|
if not sub_script:
|
||||||
return script
|
return script
|
||||||
s_hex = False
|
s_hex = False
|
||||||
if type(script) == str:
|
if isinstance(script, str):
|
||||||
try:
|
try:
|
||||||
script = unhexlify(script)
|
script = unhexlify(script)
|
||||||
s_hex = True
|
s_hex = True
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
assert type(script) == bytes
|
if isinstance(sub_script, str):
|
||||||
if type(sub_script) == str:
|
|
||||||
try:
|
try:
|
||||||
sub_script = unhexlify(sub_script)
|
sub_script = unhexlify(sub_script)
|
||||||
except:
|
except:
|
||||||
pass
|
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)
|
l = len(script)
|
||||||
ls = len(sub_script)
|
ls = len(sub_script)
|
||||||
s = 0
|
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()
|
return b''.join(result) if not s_hex else hexlify(b''.join(result)).decode()
|
||||||
|
|
||||||
|
|
||||||
def script_to_hash(s, witness=False, hex=False):
|
def script_to_hash(script, witness=False, hex=True):
|
||||||
if type(s) == str:
|
"""
|
||||||
s = unhexlify(s)
|
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:
|
if witness:
|
||||||
return sha256(s, hex)
|
return sha256(script, hex)
|
||||||
else:
|
else:
|
||||||
return hash160(s, hex)
|
return hash160(script, hex)
|
||||||
|
|
||||||
|
|
||||||
#
|
# Signatures
|
||||||
# ECDSA
|
|
||||||
#
|
|
||||||
|
|
||||||
def verify_signature(sig, pub_key, msg):
|
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)
|
sig = bytes(sig)
|
||||||
elif type(sig) == str:
|
elif isinstance(sig, str):
|
||||||
sig = unhexlify(sig)
|
sig = unhexlify(sig)
|
||||||
else:
|
else:
|
||||||
raise TypeError("signature must be a bytes or hex encoded string")
|
raise TypeError("signature must be a bytes or hex encoded string")
|
||||||
if type(pub_key) != bytes:
|
if not isinstance(pub_key, bytes):
|
||||||
if type(pub_key) == bytearray:
|
if isinstance(pub_key, bytearray):
|
||||||
pub_key = bytes(pub_key)
|
pub_key = bytes(pub_key)
|
||||||
elif type(pub_key) == str:
|
elif isinstance(pub_key, str):
|
||||||
pub_key = unhexlify(pub_key)
|
pub_key = unhexlify(pub_key)
|
||||||
else:
|
else:
|
||||||
raise TypeError("public key must be a bytes or hex encoded string")
|
raise TypeError("public key must be a bytes or hex encoded string")
|
||||||
if type(msg) != bytes:
|
if not isinstance(msg, bytes):
|
||||||
if type(msg) == bytearray:
|
if isinstance(msg, bytearray):
|
||||||
msg = bytes(msg)
|
msg = bytes(msg)
|
||||||
elif type(msg) == str:
|
elif isinstance(msg, str):
|
||||||
msg = unhexlify(msg)
|
msg = unhexlify(msg)
|
||||||
else:
|
else:
|
||||||
raise TypeError("message must be a bytes or hex encoded string")
|
raise TypeError("message must be a bytes or hex encoded string")
|
||||||
|
|
||||||
raw_sig = ffi.new('secp256k1_ecdsa_signature *')
|
raw_sig = ffi.new('secp256k1_ecdsa_signature *')
|
||||||
raw_pubkey = ffi.new('secp256k1_pubkey *')
|
raw_pubkey = ffi.new('secp256k1_pubkey *')
|
||||||
if not secp256k1.secp256k1_ecdsa_signature_parse_der(ECDSA_CONTEXT_VERIFY, raw_sig, sig, len(sig)):
|
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
|
return True if result else False
|
||||||
|
|
||||||
|
|
||||||
def sign_message(msg, private_key, hex=False):
|
def sign_message(msg, private_key, hex=True):
|
||||||
"""
|
"""
|
||||||
Sign message
|
Sign message
|
||||||
|
|
||||||
:param msg: message to sign
|
:param msg: message to sign bytes or HEX encoded string.
|
||||||
:param private_key: private key (bytes, hex encoded string)
|
:param private_key: private key (bytes, hex encoded string or WIF format)
|
||||||
:param hex:
|
:param hex: (optional) If set to True return key in HEX format, by default is True.
|
||||||
:return: DER encoded sinature
|
:return: DER encoded signature in bytes or HEX encoded string.
|
||||||
"""
|
"""
|
||||||
if type(msg) != bytes:
|
if isinstance(msg, bytearray):
|
||||||
if type(msg) == bytearray:
|
msg = bytes(msg)
|
||||||
msg = bytes(msg)
|
if isinstance(msg, str):
|
||||||
|
try:
|
||||||
elif type(msg) == str:
|
|
||||||
msg = unhexlify(msg)
|
msg = unhexlify(msg)
|
||||||
else:
|
except:
|
||||||
|
pass
|
||||||
|
if not isinstance(msg, bytes):
|
||||||
raise TypeError("message must be a bytes or hex encoded string")
|
raise TypeError("message must be a bytes or hex encoded string")
|
||||||
if type(private_key) != bytes:
|
|
||||||
if type(private_key) == bytearray:
|
if isinstance(private_key, bytearray):
|
||||||
private_key = bytes(private_key)
|
private_key = bytes(private_key)
|
||||||
elif type(private_key) == str:
|
if isinstance(private_key, str):
|
||||||
|
try:
|
||||||
private_key = unhexlify(private_key)
|
private_key = unhexlify(private_key)
|
||||||
else:
|
except:
|
||||||
raise TypeError("private key must be a bytes or hex encoded string")
|
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 *')
|
raw_sig = ffi.new('secp256k1_ecdsa_signature *')
|
||||||
signed = secp256k1.secp256k1_ecdsa_sign(ECDSA_CONTEXT_SIGN, raw_sig, msg,
|
signed = secp256k1.secp256k1_ecdsa_sign(ECDSA_CONTEXT_SIGN, raw_sig, msg,
|
||||||
private_key, ffi.NULL, ffi.NULL)
|
private_key, ffi.NULL, ffi.NULL)
|
||||||
assert signed == 1
|
if not signed:
|
||||||
|
raise RuntimeError("secp256k1 error")
|
||||||
len_sig = 74
|
len_sig = 74
|
||||||
output = ffi.new('unsigned char[%d]' % len_sig)
|
output = ffi.new('unsigned char[%d]' % len_sig)
|
||||||
outputlen = ffi.new('size_t *', len_sig)
|
outputlen = ffi.new('size_t *', len_sig)
|
||||||
res = secp256k1.secp256k1_ecdsa_signature_serialize_der(ECDSA_CONTEXT_SIGN,
|
res = secp256k1.secp256k1_ecdsa_signature_serialize_der(ECDSA_CONTEXT_SIGN,
|
||||||
output, outputlen, raw_sig)
|
output, outputlen, raw_sig)
|
||||||
assert res == 1
|
if not res:
|
||||||
|
raise RuntimeError("secp256k1 error")
|
||||||
signature = bytes(ffi.buffer(output, outputlen[0]))
|
signature = bytes(ffi.buffer(output, outputlen[0]))
|
||||||
return hexlify(signature).decode() if hex else signature
|
return hexlify(signature).decode() if hex else signature
|
||||||
|
|
||||||
|
|
||||||
def is_valid_signature_encoding(sig):
|
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]
|
# Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash]
|
||||||
# * total-length: 1-byte length descriptor of everything that follows,
|
# * total-length: 1-byte length descriptor of everything that follows,
|
||||||
# excluding the sighash byte.
|
# excluding the sighash byte.
|
||||||
@ -766,17 +830,25 @@ def is_valid_signature_encoding(sig):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
#
|
# Hash encoding
|
||||||
# Transaction encoding
|
|
||||||
#
|
|
||||||
|
|
||||||
def rh2s(tthash):
|
def rh2s(raw_hash):
|
||||||
# raw hash to string
|
"""
|
||||||
return hexlify(tthash[::-1]).decode()
|
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):
|
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]
|
return unhexlify(hash_string)[::-1]
|
||||||
|
|
||||||
|
|
||||||
@ -785,16 +857,27 @@ def s2rh_step4(hash_string):
|
|||||||
return reverse_hash(h)
|
return reverse_hash(h)
|
||||||
|
|
||||||
|
|
||||||
def reverse_hash(h):
|
def reverse_hash(raw_hash):
|
||||||
return struct.pack('>IIIIIIII', *struct.unpack('>IIIIIIII', h)[::-1])[::-1]
|
"""
|
||||||
|
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):
|
# Merkle root
|
||||||
tx_hash_list = list(tx_hash_list)
|
|
||||||
|
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:
|
if len(tx_hash_list) == 1:
|
||||||
return tx_hash_list[0]
|
return tx_hash_list[0]
|
||||||
while True:
|
while True:
|
||||||
@ -809,11 +892,18 @@ def merkle_root(tx_hash_list):
|
|||||||
if len(new_hash_list) > 1:
|
if len(new_hash_list) > 1:
|
||||||
tx_hash_list = new_hash_list
|
tx_hash_list = new_hash_list
|
||||||
else:
|
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):
|
def merkle_branches(tx_hash_list, hex=True):
|
||||||
tx_hash_list = list(tx_hash_list)
|
"""
|
||||||
|
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 = []
|
branches = []
|
||||||
if len(tx_hash_list) == 1:
|
if len(tx_hash_list) == 1:
|
||||||
return []
|
return []
|
||||||
@ -833,19 +923,36 @@ def merkle_branches(tx_hash_list):
|
|||||||
else:
|
else:
|
||||||
if new_hash_list:
|
if new_hash_list:
|
||||||
branches.append(new_hash_list.pop(0))
|
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):
|
def merkleroot_from_branches(merkle_branches, coinbase_hash, hex=True):
|
||||||
merkle_root = coinbase_hash_bin
|
"""
|
||||||
|
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:
|
for h in merkle_branches:
|
||||||
if type(h) == str:
|
if type(h) == str:
|
||||||
h = unhexlify(h)
|
h = unhexlify(h)
|
||||||
merkle_root = double_sha256(merkle_root + 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):
|
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:
|
if type(bits) == str:
|
||||||
bits = unhexlify(bits)
|
bits = unhexlify(bits)
|
||||||
if type(bits) == bytes:
|
if type(bits) == bytes:
|
||||||
@ -857,48 +964,97 @@ def bits_to_target(bits):
|
|||||||
|
|
||||||
|
|
||||||
def target_to_difficulty(target):
|
def target_to_difficulty(target):
|
||||||
|
"""
|
||||||
|
Calculate difficulty from target
|
||||||
|
|
||||||
|
:param target: integer.
|
||||||
|
:return: float.
|
||||||
|
"""
|
||||||
return 0x00000000FFFF0000000000000000000000000000000000000000000000000000 / target
|
return 0x00000000FFFF0000000000000000000000000000000000000000000000000000 / target
|
||||||
|
|
||||||
|
|
||||||
def bits_to_difficulty(bits):
|
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))
|
return target_to_difficulty(bits_to_target(bits))
|
||||||
|
|
||||||
|
|
||||||
def difficulty_to_target(difficulty):
|
def difficulty_to_target(difficulty):
|
||||||
|
"""
|
||||||
|
Calculate target from difficulty
|
||||||
|
|
||||||
|
:param target: integer.
|
||||||
|
:return: float.
|
||||||
|
"""
|
||||||
return int(0x00000000FFFF0000000000000000000000000000000000000000000000000000 / difficulty)
|
return int(0x00000000FFFF0000000000000000000000000000000000000000000000000000 / difficulty)
|
||||||
|
|
||||||
|
|
||||||
#
|
# Tools
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
def bytes_needed(n):
|
def bytes_needed(n):
|
||||||
|
"""
|
||||||
|
Calculate bytes needed to convert integer to bytes.
|
||||||
|
|
||||||
|
:param n: integer.
|
||||||
|
:return: integer.
|
||||||
|
"""
|
||||||
if n == 0:
|
if n == 0:
|
||||||
return 1
|
return 1
|
||||||
return math.ceil(n.bit_length()/8)
|
return math.ceil(n.bit_length()/8)
|
||||||
|
|
||||||
|
|
||||||
def int_to_bytes(i, byteorder='big'):
|
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)
|
return i.to_bytes(bytes_needed(i), byteorder=byteorder, signed=False)
|
||||||
|
|
||||||
|
|
||||||
def bytes_to_int(i, byteorder='big'):
|
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)
|
return int.from_bytes(i, byteorder=byteorder, signed=False)
|
||||||
|
|
||||||
|
|
||||||
# variable integer
|
# variable integer
|
||||||
|
|
||||||
def int_to_var_int(i):
|
def int_to_var_int(i):
|
||||||
|
"""
|
||||||
|
Convert integer to variable integer
|
||||||
|
|
||||||
|
:param i: integer.
|
||||||
|
:return: bytes.
|
||||||
|
"""
|
||||||
if i < 0xfd:
|
if i < 0xfd:
|
||||||
return struct.pack('<B', i)
|
return struct.pack('<B', i)
|
||||||
if i <= 0xffff:
|
if i <= 0xffff:
|
||||||
return b'\xfd' + struct.pack('<H', i)
|
return b'\xfd' + struct.pack('<H', i)
|
||||||
if i <= 0xffffffff:
|
if i <= 0xffffffff:
|
||||||
return b'\xfe' + struct.pack('<L', i)
|
return b'\xfe' + struct.pack('<L', i)
|
||||||
return b'\xff' + struct.pack('<Q', i)
|
return b'\xff' + struct.pack('<Q', i)
|
||||||
|
|
||||||
|
|
||||||
def var_int_to_int(data):
|
def var_int_to_int(data):
|
||||||
|
"""
|
||||||
|
Convert variable integer to integer
|
||||||
|
|
||||||
|
:param data: bytes vriable integer.
|
||||||
|
:return: integer.
|
||||||
|
"""
|
||||||
if data[0] == 0xfd:
|
if data[0] == 0xfd:
|
||||||
return struct.unpack('<H', data[1:3])[0]
|
return struct.unpack('<H', data[1:3])[0]
|
||||||
elif data[0] == 0xfe:
|
elif data[0] == 0xfe:
|
||||||
@ -909,6 +1065,12 @@ def var_int_to_int(data):
|
|||||||
|
|
||||||
|
|
||||||
def var_int_len(n):
|
def var_int_len(n):
|
||||||
|
"""
|
||||||
|
Get variable integer length in bytes from integer value
|
||||||
|
|
||||||
|
:param n: integer.
|
||||||
|
:return: integer.
|
||||||
|
"""
|
||||||
if n <= 0xfc:
|
if n <= 0xfc:
|
||||||
return 1
|
return 1
|
||||||
if n <= 0xffff:
|
if n <= 0xffff:
|
||||||
@ -918,23 +1080,42 @@ def var_int_len(n):
|
|||||||
return 9
|
return 9
|
||||||
|
|
||||||
|
|
||||||
def get_var_int_len(byte):
|
def get_var_int_len(bytes):
|
||||||
if byte[0] == 253:
|
"""
|
||||||
|
Get variable integer length in bytes from bytes
|
||||||
|
|
||||||
|
:param bytes: bytes.
|
||||||
|
:return: integer.
|
||||||
|
"""
|
||||||
|
if bytes[0] == 253:
|
||||||
return 3
|
return 3
|
||||||
elif byte[0] == 254:
|
elif bytes[0] == 254:
|
||||||
return 5
|
return 5
|
||||||
elif byte[0] == 255:
|
elif bytes[0] == 255:
|
||||||
return 9
|
return 9
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
def read_var_int(stream):
|
def read_var_int(stream):
|
||||||
|
"""
|
||||||
|
Read variable integer from io.BytesIO stream to bytes
|
||||||
|
|
||||||
|
:param stream: io.BytesIO stream.
|
||||||
|
:return: bytes.
|
||||||
|
"""
|
||||||
l = stream.read(1)
|
l = stream.read(1)
|
||||||
bytes_length = get_var_int_len(l)
|
bytes_length = get_var_int_len(l)
|
||||||
return l + stream.read(bytes_length - 1)
|
return l + stream.read(bytes_length - 1)
|
||||||
|
|
||||||
|
|
||||||
def read_var_list(stream, data_type):
|
def read_var_list(stream, data_type):
|
||||||
|
"""
|
||||||
|
Read variable integer list from io.BytesIO stream to bytes
|
||||||
|
|
||||||
|
:param stream: io.BytesIO stream.
|
||||||
|
:param data_type: list data type.
|
||||||
|
:return: list of data_type.
|
||||||
|
"""
|
||||||
count = var_int_to_int(read_var_int(stream))
|
count = var_int_to_int(read_var_int(stream))
|
||||||
return [data_type.deserialize(stream) for i in range(count)]
|
return [data_type.deserialize(stream) for i in range(count)]
|
||||||
|
|
||||||
@ -942,6 +1123,13 @@ def read_var_list(stream, data_type):
|
|||||||
|
|
||||||
|
|
||||||
def int_to_c_int(n, base_bytes=1):
|
def int_to_c_int(n, base_bytes=1):
|
||||||
|
"""
|
||||||
|
Convert integer to compresed integer
|
||||||
|
|
||||||
|
:param n: integer.
|
||||||
|
:param base_bytes: len of bytes base from which start compression.
|
||||||
|
:return: bytes.
|
||||||
|
"""
|
||||||
if n == 0:
|
if n == 0:
|
||||||
return b'\x00'
|
return b'\x00'
|
||||||
else:
|
else:
|
||||||
@ -964,6 +1152,13 @@ def int_to_c_int(n, base_bytes=1):
|
|||||||
|
|
||||||
|
|
||||||
def c_int_to_int(b, base_bytes=1):
|
def c_int_to_int(b, base_bytes=1):
|
||||||
|
"""
|
||||||
|
Convert compressed integer bytes to integer
|
||||||
|
|
||||||
|
:param b: compressed integer bytes.
|
||||||
|
:param base_bytes: len of bytes base from which start compression.
|
||||||
|
:return: integer.
|
||||||
|
"""
|
||||||
byte_length = 0
|
byte_length = 0
|
||||||
f = 0
|
f = 0
|
||||||
while True:
|
while True:
|
||||||
@ -983,6 +1178,13 @@ def c_int_to_int(b, base_bytes=1):
|
|||||||
|
|
||||||
|
|
||||||
def c_int_len(n, base_bytes=1):
|
def c_int_len(n, base_bytes=1):
|
||||||
|
"""
|
||||||
|
Get length of compressed integer from integer value
|
||||||
|
|
||||||
|
:param n: bytes.
|
||||||
|
:param base_bytes: len of bytes base from which start compression.
|
||||||
|
:return: integer.
|
||||||
|
"""
|
||||||
if n == 0:
|
if n == 0:
|
||||||
return 1
|
return 1
|
||||||
l = n.bit_length() + 1
|
l = n.bit_length() + 1
|
||||||
|
|||||||
@ -473,13 +473,13 @@ class Transaction(dict):
|
|||||||
if private_key:
|
if private_key:
|
||||||
if type(private_key) != PrivateKey:
|
if type(private_key) != PrivateKey:
|
||||||
private_key_obj = PrivateKey(private_key)
|
private_key_obj = PrivateKey(private_key)
|
||||||
public_key = PublicKey(private_key_obj).raw_key
|
public_key = PublicKey(private_key_obj).key
|
||||||
private_key = private_key_obj.raw_key
|
private_key = private_key_obj.key
|
||||||
else:
|
else:
|
||||||
if "privateKey" not in self["vIn"][n]:
|
if "privateKey" not in self["vIn"][n]:
|
||||||
return self
|
return self
|
||||||
private_key = self["vIn"][n].private_key.raw_key
|
private_key = self["vIn"][n].private_key.key
|
||||||
public_key = PublicKey(self["vIn"][n].private_key).raw_key
|
public_key = PublicKey(self["vIn"][n].private_key).key
|
||||||
|
|
||||||
if redeem_script:
|
if redeem_script:
|
||||||
if type(redeem_script) == str:
|
if type(redeem_script) == str:
|
||||||
@ -492,7 +492,7 @@ class Transaction(dict):
|
|||||||
sighash = self.sig_hash_input(n, script_pub_key=script, sighash_type=sighash_type)
|
sighash = self.sig_hash_input(n, script_pub_key=script, sighash_type=sighash_type)
|
||||||
if type(sighash) == str:
|
if type(sighash) == str:
|
||||||
sighash = s2rh(sighash)
|
sighash = s2rh(sighash)
|
||||||
signature = sign_message(sighash, private_key) + bytes([sighash_type])
|
signature = sign_message(sighash, private_key, 0) + bytes([sighash_type])
|
||||||
if redeem_script:
|
if redeem_script:
|
||||||
if self["vIn"][n]["scriptSig"]:
|
if self["vIn"][n]["scriptSig"]:
|
||||||
sig_script = self["vIn"][n]["scriptSig"]
|
sig_script = self["vIn"][n]["scriptSig"]
|
||||||
|
|||||||
17
setup.py
17
setup.py
@ -5,7 +5,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
|
|
||||||
setup(name='pybtc',
|
setup(name='pybtc',
|
||||||
version='0.1',
|
version='2.0',
|
||||||
description='Python Bitcoin library',
|
description='Python Bitcoin library',
|
||||||
keywords='bitcoin',
|
keywords='bitcoin',
|
||||||
url='https://github.com/bitaps-com/pybtc',
|
url='https://github.com/bitaps-com/pybtc',
|
||||||
@ -13,19 +13,6 @@ setup(name='pybtc',
|
|||||||
author_email='admin@bitaps.com',
|
author_email='admin@bitaps.com',
|
||||||
license='GPL-3.0',
|
license='GPL-3.0',
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
install_requires=[ 'secp256k1', ],
|
install_requires=[ 'secp256k1'],
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
zip_safe=False)
|
zip_safe=False)
|
||||||
|
|
||||||
#
|
|
||||||
# from distutils.core import setup
|
|
||||||
#
|
|
||||||
# setup(name='pybtc',
|
|
||||||
# version='1.0.1',
|
|
||||||
# description='Bitcoin library',
|
|
||||||
# author='Alexsei Karpov',
|
|
||||||
# author_email='admin@bitaps.com',
|
|
||||||
# url='https://github.com/bitaps-com/pybtc',
|
|
||||||
# packages=['pybtc'],
|
|
||||||
#
|
|
||||||
# )
|
|
||||||
|
|||||||
@ -15,7 +15,6 @@ class AddressClassTests(unittest.TestCase):
|
|||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
print("\nTesting address class:\n")
|
print("\nTesting address class:\n")
|
||||||
|
|
||||||
|
|
||||||
def test_is_WIF_valid(self):
|
def test_is_WIF_valid(self):
|
||||||
p = address.PrivateKey("L1MU1jUjUwZ6Fd1L2HDZ8qH4oSWxct5boCQ4C87YvoSZbTW41hg4")
|
p = address.PrivateKey("L1MU1jUjUwZ6Fd1L2HDZ8qH4oSWxct5boCQ4C87YvoSZbTW41hg4")
|
||||||
pub = address.PublicKey(p)
|
pub = address.PublicKey(p)
|
||||||
@ -27,8 +26,8 @@ class AddressClassTests(unittest.TestCase):
|
|||||||
self.assertEqual(a.address, '15m65JmFohJiioQbzMWhqFeCS3ZL1KVaNh')
|
self.assertEqual(a.address, '15m65JmFohJiioQbzMWhqFeCS3ZL1KVaNh')
|
||||||
a = address.Address(p, address_type = "P2SH_P2WPKH")
|
a = address.Address(p, address_type = "P2SH_P2WPKH")
|
||||||
self.assertEqual(a.address, '37WJdFAoHDbxUQioDgtvPZuyJPyrrNQ7aL')
|
self.assertEqual(a.address, '37WJdFAoHDbxUQioDgtvPZuyJPyrrNQ7aL')
|
||||||
self.assertEqual(a.redeem_script, '001434370a8d74e9965d7aade2ba2f30110b321bf236')
|
self.assertEqual(a.redeem_script_hex, '001434370a8d74e9965d7aade2ba2f30110b321bf236')
|
||||||
self.assertEqual(a.public_key.hex(), '02a8fb85e98c99b79150df12fde488639d8445c57babef83d53c66c1e5c818eeb4')
|
self.assertEqual(a.public_key.hex, '02a8fb85e98c99b79150df12fde488639d8445c57babef83d53c66c1e5c818eeb4')
|
||||||
|
|
||||||
# compressed public key
|
# compressed public key
|
||||||
p = address.PrivateKey("7b56e2b7bd189f4491d43a1d209e6268046df1741f61b6397349d7aa54978e76", compressed=False)
|
p = address.PrivateKey("7b56e2b7bd189f4491d43a1d209e6268046df1741f61b6397349d7aa54978e76", compressed=False)
|
||||||
@ -48,8 +47,8 @@ class AddressClassTests(unittest.TestCase):
|
|||||||
self.assertEqual(a.address, '15m65JmFohJiioQbzMWhqFeCS3ZL1KVaNh')
|
self.assertEqual(a.address, '15m65JmFohJiioQbzMWhqFeCS3ZL1KVaNh')
|
||||||
a = address.Address(p, address_type="P2SH_P2WPKH")
|
a = address.Address(p, address_type="P2SH_P2WPKH")
|
||||||
self.assertEqual(a.address, '37WJdFAoHDbxUQioDgtvPZuyJPyrrNQ7aL')
|
self.assertEqual(a.address, '37WJdFAoHDbxUQioDgtvPZuyJPyrrNQ7aL')
|
||||||
self.assertEqual(a.redeem_script, '001434370a8d74e9965d7aade2ba2f30110b321bf236')
|
self.assertEqual(a.redeem_script_hex, '001434370a8d74e9965d7aade2ba2f30110b321bf236')
|
||||||
self.assertEqual(a.public_key.hex(), '02a8fb85e98c99b79150df12fde488639d8445c57babef83d53c66c1e5c818eeb4')
|
self.assertEqual(a.public_key.hex, '02a8fb85e98c99b79150df12fde488639d8445c57babef83d53c66c1e5c818eeb4')
|
||||||
|
|
||||||
# from uncompressed pubkey
|
# from uncompressed pubkey
|
||||||
p = address.PublicKey('04a8fb85e98c99b79150df12fde488639d8445c57babef83d53c66c1e5c818eeb43bbd96a641808e5f34eb568e804fe679de82de419e2512736ea09013a82324a6')
|
p = address.PublicKey('04a8fb85e98c99b79150df12fde488639d8445c57babef83d53c66c1e5c818eeb43bbd96a641808e5f34eb568e804fe679de82de419e2512736ea09013a82324a6')
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user