This commit is contained in:
4tochka 2018-06-20 15:22:24 +04:00
parent ac6ab0ae4c
commit 2eed4aa1ef
21 changed files with 939 additions and 208 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "docs/src/alabaster"]
path = docs/src/alabaster
url = https://github.com/bitprophet/alabaster.git

View File

@ -1,3 +1,3 @@
<img src="doc/img/pybtc.png" width="100">
<img src="docs/img/pybtc.png" width="100">
## Python bitcoin library

View File

@ -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:

View File

@ -0,0 +1,13 @@
=========
Reference
=========
.. toctree::
:name: mastertoc
:maxdepth: 2
address.rst
transaction.rst
block.rst

View File

@ -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 = ''

View File

@ -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.

View File

@ -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
---------------------

View File

@ -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

View File

@ -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

View File

@ -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 dont 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

View File

@ -0,0 +1,11 @@
============
Transactions
============
The class for creating transaction.
.. autoclass:: pybtc.Transaction
:members:

1
docs/src/alabaster Submodule

@ -0,0 +1 @@
Subproject commit 609374c5434eb960eab1ae25ea50b007fbc6c2ae

View 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).

View 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"

View File

@ -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)

View File

@ -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
}

View File

@ -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"]

View File

@ -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

View File

@ -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"]

View File

@ -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)

View File

@ -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')