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
|
||||
|
||||
@ -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 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 = ''
|
||||
|
||||
@ -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.
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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 <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.
|
||||
|
||||
@ -111,4 +103,11 @@ Table Of Contents
|
||||
:name: mastertoc
|
||||
:maxdepth: 2
|
||||
|
||||
functional.rst
|
||||
installation.rst
|
||||
examples.rst
|
||||
classes.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 .consensus import *
|
||||
from .transaction import *
|
||||
from .block import *
|
||||
from .address import *
|
||||
|
||||
version = "2.0.1"
|
||||
|
||||
164
pybtc/address.py
164
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)
|
||||
testnet=self.testnet)
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
150
pybtc/opcodes.py
150
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"]
|
||||
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:
|
||||
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('<B', i)
|
||||
if i <= 0xffff:
|
||||
return b'\xfd' + struct.pack('<H', i)
|
||||
if i <= 0xffffffff:
|
||||
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):
|
||||
"""
|
||||
Convert variable integer to integer
|
||||
|
||||
:param data: bytes vriable integer.
|
||||
:return: integer.
|
||||
"""
|
||||
if data[0] == 0xfd:
|
||||
return struct.unpack('<H', data[1:3])[0]
|
||||
elif data[0] == 0xfe:
|
||||
@ -909,6 +1065,12 @@ def var_int_to_int(data):
|
||||
|
||||
|
||||
def var_int_len(n):
|
||||
"""
|
||||
Get variable integer length in bytes from integer value
|
||||
|
||||
:param n: integer.
|
||||
:return: integer.
|
||||
"""
|
||||
if n <= 0xfc:
|
||||
return 1
|
||||
if n <= 0xffff:
|
||||
@ -918,23 +1080,42 @@ def var_int_len(n):
|
||||
return 9
|
||||
|
||||
|
||||
def get_var_int_len(byte):
|
||||
if byte[0] == 253:
|
||||
def get_var_int_len(bytes):
|
||||
"""
|
||||
Get variable integer length in bytes from bytes
|
||||
|
||||
:param bytes: bytes.
|
||||
:return: integer.
|
||||
"""
|
||||
if bytes[0] == 253:
|
||||
return 3
|
||||
elif byte[0] == 254:
|
||||
elif bytes[0] == 254:
|
||||
return 5
|
||||
elif byte[0] == 255:
|
||||
elif bytes[0] == 255:
|
||||
return 9
|
||||
return 1
|
||||
|
||||
|
||||
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)
|
||||
bytes_length = get_var_int_len(l)
|
||||
return l + stream.read(bytes_length - 1)
|
||||
|
||||
|
||||
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))
|
||||
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):
|
||||
"""
|
||||
Convert integer to compresed integer
|
||||
|
||||
:param n: integer.
|
||||
:param base_bytes: len of bytes base from which start compression.
|
||||
:return: bytes.
|
||||
"""
|
||||
if n == 0:
|
||||
return b'\x00'
|
||||
else:
|
||||
@ -964,6 +1152,13 @@ def int_to_c_int(n, 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
|
||||
f = 0
|
||||
while True:
|
||||
@ -983,6 +1178,13 @@ def c_int_to_int(b, 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:
|
||||
return 1
|
||||
l = n.bit_length() + 1
|
||||
|
||||
@ -473,13 +473,13 @@ class Transaction(dict):
|
||||
if private_key:
|
||||
if type(private_key) != PrivateKey:
|
||||
private_key_obj = PrivateKey(private_key)
|
||||
public_key = PublicKey(private_key_obj).raw_key
|
||||
private_key = private_key_obj.raw_key
|
||||
public_key = PublicKey(private_key_obj).key
|
||||
private_key = private_key_obj.key
|
||||
else:
|
||||
if "privateKey" not in self["vIn"][n]:
|
||||
return self
|
||||
private_key = self["vIn"][n].private_key.raw_key
|
||||
public_key = PublicKey(self["vIn"][n].private_key).raw_key
|
||||
private_key = self["vIn"][n].private_key.key
|
||||
public_key = PublicKey(self["vIn"][n].private_key).key
|
||||
|
||||
if redeem_script:
|
||||
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)
|
||||
if type(sighash) == str:
|
||||
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 self["vIn"][n]["scriptSig"]:
|
||||
sig_script = self["vIn"][n]["scriptSig"]
|
||||
|
||||
19
setup.py
19
setup.py
@ -5,7 +5,7 @@ from setuptools import setup, find_packages
|
||||
|
||||
|
||||
setup(name='pybtc',
|
||||
version='0.1',
|
||||
version='2.0',
|
||||
description='Python Bitcoin library',
|
||||
keywords='bitcoin',
|
||||
url='https://github.com/bitaps-com/pybtc',
|
||||
@ -13,19 +13,6 @@ setup(name='pybtc',
|
||||
author_email='admin@bitaps.com',
|
||||
license='GPL-3.0',
|
||||
packages=find_packages(),
|
||||
install_requires=[ 'secp256k1', ],
|
||||
install_requires=[ 'secp256k1'],
|
||||
include_package_data=True,
|
||||
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'],
|
||||
#
|
||||
# )
|
||||
zip_safe=False)
|
||||
@ -15,7 +15,6 @@ class AddressClassTests(unittest.TestCase):
|
||||
def setUpClass(cls):
|
||||
print("\nTesting address class:\n")
|
||||
|
||||
|
||||
def test_is_WIF_valid(self):
|
||||
p = address.PrivateKey("L1MU1jUjUwZ6Fd1L2HDZ8qH4oSWxct5boCQ4C87YvoSZbTW41hg4")
|
||||
pub = address.PublicKey(p)
|
||||
@ -27,8 +26,8 @@ class AddressClassTests(unittest.TestCase):
|
||||
self.assertEqual(a.address, '15m65JmFohJiioQbzMWhqFeCS3ZL1KVaNh')
|
||||
a = address.Address(p, address_type = "P2SH_P2WPKH")
|
||||
self.assertEqual(a.address, '37WJdFAoHDbxUQioDgtvPZuyJPyrrNQ7aL')
|
||||
self.assertEqual(a.redeem_script, '001434370a8d74e9965d7aade2ba2f30110b321bf236')
|
||||
self.assertEqual(a.public_key.hex(), '02a8fb85e98c99b79150df12fde488639d8445c57babef83d53c66c1e5c818eeb4')
|
||||
self.assertEqual(a.redeem_script_hex, '001434370a8d74e9965d7aade2ba2f30110b321bf236')
|
||||
self.assertEqual(a.public_key.hex, '02a8fb85e98c99b79150df12fde488639d8445c57babef83d53c66c1e5c818eeb4')
|
||||
|
||||
# compressed public key
|
||||
p = address.PrivateKey("7b56e2b7bd189f4491d43a1d209e6268046df1741f61b6397349d7aa54978e76", compressed=False)
|
||||
@ -48,8 +47,8 @@ class AddressClassTests(unittest.TestCase):
|
||||
self.assertEqual(a.address, '15m65JmFohJiioQbzMWhqFeCS3ZL1KVaNh')
|
||||
a = address.Address(p, address_type="P2SH_P2WPKH")
|
||||
self.assertEqual(a.address, '37WJdFAoHDbxUQioDgtvPZuyJPyrrNQ7aL')
|
||||
self.assertEqual(a.redeem_script, '001434370a8d74e9965d7aade2ba2f30110b321bf236')
|
||||
self.assertEqual(a.public_key.hex(), '02a8fb85e98c99b79150df12fde488639d8445c57babef83d53c66c1e5c818eeb4')
|
||||
self.assertEqual(a.redeem_script_hex, '001434370a8d74e9965d7aade2ba2f30110b321bf236')
|
||||
self.assertEqual(a.public_key.hex, '02a8fb85e98c99b79150df12fde488639d8445c57babef83d53c66c1e5c818eeb4')
|
||||
|
||||
# from uncompressed pubkey
|
||||
p = address.PublicKey('04a8fb85e98c99b79150df12fde488639d8445c57babef83d53c66c1e5c818eeb43bbd96a641808e5f34eb568e804fe679de82de419e2512736ea09013a82324a6')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user