├── doc ├── .gitignore ├── _static │ └── .placeholder ├── _templates │ └── .placeholder ├── bitcointx.core.rst ├── index.rst ├── bitcointx.rst ├── make.bat ├── Makefile └── conf.py ├── MANIFEST.in ├── .gitignore ├── runtests.sh ├── bitcointx ├── tests │ ├── data │ │ ├── base58_encode_decode.json │ │ ├── bech32_encode_decode.json │ │ ├── bech32_invalid.json │ │ ├── checkblock_valid.json │ │ ├── checkblock_invalid.json │ │ └── tx_invalid.json │ ├── __init__.py │ ├── test_rpc.py │ ├── test_key.py │ ├── test_base58.py │ ├── test_checkblock.py │ ├── test_signmessage.py │ ├── test_bech32.py │ ├── test_scripteval.py │ ├── test_serialize.py │ ├── test_transactions.py │ ├── test_segwit.py │ ├── test_core.py │ └── test_script.py ├── signature.py ├── signmessage.py ├── __init__.py ├── bech32.py ├── core │ ├── _bignum.py │ └── serialize.py ├── base58.py ├── segwit_addr.py └── wallet.py ├── tox.ini ├── setup.py ├── examples ├── timestamp-op-ret.py ├── sign-message.py ├── bip-0070-payment-protocol.py ├── spend-p2pkh-txout.py ├── spend-p2sh-txout.py └── publish-text.py ├── README.md ├── release-notes.md └── LICENSE /doc/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | -------------------------------------------------------------------------------- /doc/_static/.placeholder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/_templates/.placeholder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE README.md 2 | graft bitcointx/tests/data/ 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw? 2 | *.pyc 3 | 4 | local*.cfg 5 | 6 | .coverage 7 | .tox/ 8 | build/ 9 | htmlcov/ 10 | python_bitcoinlib.egg-info/ 11 | dist/ 12 | -------------------------------------------------------------------------------- /runtests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | #-*-mode: sh; encoding: utf-8-*- 3 | 4 | _MY_DIR="$( cd "$( dirname "${0}" )" && pwd )" 5 | set -ex 6 | [ -d "${_MY_DIR}" ] 7 | [ "${_MY_DIR}/runtests.sh" -ef "${0}" ] 8 | cd "${_MY_DIR}" 9 | exec tox ${1+"${@}"} 10 | -------------------------------------------------------------------------------- /bitcointx/tests/data/base58_encode_decode.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["", ""], 3 | ["61", "2g"], 4 | ["626262", "a3gV"], 5 | ["636363", "aPEr"], 6 | ["73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2"], 7 | ["00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"], 8 | ["516b6fcd0f", "ABnLTmg"], 9 | ["bf4f89001e670274dd", "3SEo3LWLoPntC"], 10 | ["572e4794", "3EFU7m"], 11 | ["ecac89cad93923c02321", "EJDM8drfXA6uyA"], 12 | ["10c8511e", "Rt5zm"], 13 | ["00000000000000000000", "1111111111"] 14 | ] 15 | -------------------------------------------------------------------------------- /bitcointx/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013-2014 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | from __future__ import absolute_import, division, print_function, unicode_literals 13 | -------------------------------------------------------------------------------- /doc/bitcointx.core.rst: -------------------------------------------------------------------------------- 1 | bitcointx.core 2 | ============ 3 | 4 | Everything consensus critical is found in the core subpackage. 5 | 6 | :mod:`core` 7 | ----------- 8 | 9 | .. automodule:: bitcointx.core 10 | 11 | :mod:`key` 12 | ---------- 13 | 14 | .. automodule:: bitcointx.core.key 15 | 16 | :mod:`script` 17 | ------------- 18 | 19 | .. automodule:: bitcointx.core.script 20 | 21 | :mod:`scripteval` 22 | ----------------- 23 | 24 | .. automodule:: bitcointx.core.scripteval 25 | 26 | :mod:`serialize` 27 | ---------------- 28 | 29 | .. automodule:: bitcointx.core.serialize 30 | 31 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | .. bitcoin documentation master file, created by 2 | sphinx-quickstart on Thu May 28 20:40:55 2015. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | python-bitcointx Documentation 7 | =============================== 8 | 9 | API Reference 10 | ------------- 11 | 12 | This section contains auto-generated API documentation. 13 | 14 | Contents: 15 | 16 | .. toctree:: 17 | :maxdepth: 2 18 | 19 | bitcointx 20 | bitcointx.core 21 | 22 | 23 | Indices and tables 24 | ================== 25 | 26 | * :ref:`genindex` 27 | * :ref:`modindex` 28 | * :ref:`search` 29 | 30 | -------------------------------------------------------------------------------- /bitcointx/tests/data/bech32_encode_decode.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["0014751e76e8199196d454941c45d1b3a323f1433bd6", "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4"], 3 | ["00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7"], 4 | ["5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6", "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx"], 5 | ["6002751e", "BC1SW50QA3JX3S"], 6 | ["5210751e76e8199196d454941c45d1b3a323", "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj"], 7 | ["0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy"] 8 | ] 9 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | #-*-mode: ini; encoding: utf-8-*- 2 | 3 | [tox] #------------------------------------------------------------------- 4 | 5 | envlist = reset,py27,py33,py34,py35,pypy,pypy3,stats 6 | skip_missing_interpreters = True 7 | 8 | [testenv] #--------------------------------------------------------------- 9 | 10 | commands = 11 | coverage run --append --omit='tests/*,*/site-packages/*,*/distutils/*,*/lib_pypy/*' setup.py test -q 12 | 13 | deps = 14 | coverage 15 | 16 | setenv = 17 | PYTHONWARNINGS = all 18 | 19 | [testenv:reset] #--------------------------------------------------------- 20 | 21 | commands = 22 | coverage erase 23 | 24 | [testenv:stats] #--------------------------------------------------------- 25 | 26 | commands = 27 | coverage report 28 | coverage html 29 | -------------------------------------------------------------------------------- /doc/bitcointx.rst: -------------------------------------------------------------------------------- 1 | bitcointx 2 | ======= 3 | 4 | :mod:`bitcointx` 5 | -------------- 6 | 7 | .. automodule:: bitcointx 8 | 9 | :mod:`base58` 10 | ------------- 11 | 12 | .. automodule:: bitcointx.base58 13 | 14 | :mod:`bloom` 15 | ------------ 16 | 17 | .. automodule:: bitcointx.bloom 18 | 19 | :mod:`messages` 20 | --------------- 21 | 22 | .. automodule:: bitcointx.messages 23 | 24 | :mod:`net` 25 | ---------- 26 | 27 | .. automodule:: bitcointx.net 28 | 29 | :mod:`rpc` 30 | ---------- 31 | 32 | .. automodule:: bitcointx.rpc 33 | 34 | :mod:`signature` 35 | ---------------- 36 | 37 | .. automodule:: bitcointx.signature 38 | 39 | :mod:`signmessage` 40 | ------------------ 41 | 42 | .. automodule:: bitcointx.signmessage 43 | 44 | :mod:`wallet` 45 | ------------- 46 | 47 | .. automodule:: bitcointx.wallet 48 | -------------------------------------------------------------------------------- /bitcointx/tests/data/bech32_invalid.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty", "Invalid human-readable part"], 3 | ["bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", "Invalid checksum"], 4 | ["BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2", "Invalid witness version"], 5 | ["bc1rw5uspcuh", "Invalid program length"], 6 | ["bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90", "Invalid program length"], 7 | ["BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P", "Invalid program length for witness version 0 (per BIP141)"], 8 | ["tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7", "Mixed case"], 9 | ["bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du", "zero padding of more than 4 bits"], 10 | ["tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv", "Non-zero padding in 8-to-5 conversion"], 11 | ["bc1gmk9yu", "Empty data section"] 12 | ] -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup, find_packages 4 | import os 5 | 6 | from bitcointx import __version__ 7 | 8 | here = os.path.abspath(os.path.dirname(__file__)) 9 | with open(os.path.join(here, 'README.md')) as f: 10 | README = f.read() 11 | 12 | requires = [] 13 | 14 | setup(name='python-bitcointx', 15 | version=__version__, 16 | description='The Swiss Army Knife of the Bitcoin protocol.', 17 | long_description=README, 18 | long_description_content_type='text/markdown', 19 | classifiers=[ 20 | "Programming Language :: Python", 21 | "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)", 22 | ], 23 | url='https://github.com/Simplexum/python-bitcointx', 24 | keywords='bitcoin', 25 | packages=find_packages(), 26 | zip_safe=False, 27 | install_requires=requires, 28 | test_suite="bitcointx.tests" 29 | ) 30 | -------------------------------------------------------------------------------- /bitcointx/tests/test_rpc.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013-2014 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | from __future__ import absolute_import, division, print_function, unicode_literals 13 | 14 | import unittest 15 | 16 | from bitcointx.rpc import Proxy 17 | 18 | class Test_RPC(unittest.TestCase): 19 | # Tests disabled, see discussion below. 20 | # "Looks like your unit tests won't work if Bitcoin Core isn't running; 21 | # maybe they in turn need to check that and disable the test if core isn't available?" 22 | # https://github.com/petertodd/python-bitcoinlib/pull/10 23 | pass 24 | 25 | # def test_can_validate(self): 26 | # working_address = '1CB2fxLGAZEzgaY4pjr4ndeDWJiz3D3AT7' 27 | # p = Proxy() 28 | # r = p.validateAddress(working_address) 29 | # self.assertEqual(r['address'], working_address) 30 | # self.assertEqual(r['isvalid'], True) 31 | # 32 | # def test_cannot_validate(self): 33 | # non_working_address = 'LTatMHrYyHcxhxrY27AqFN53bT4TauR86h' 34 | # p = Proxy() 35 | # r = p.validateAddress(non_working_address) 36 | # self.assertEqual(r['isvalid'], False) 37 | -------------------------------------------------------------------------------- /bitcointx/tests/test_key.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013-2014 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | from __future__ import absolute_import, division, print_function, unicode_literals 13 | 14 | import unittest 15 | 16 | from bitcointx.core.key import * 17 | from bitcointx.core import x 18 | 19 | class Test_CPubKey(unittest.TestCase): 20 | def test(self): 21 | def T(hex_pubkey, is_valid, is_fullyvalid, is_compressed): 22 | key = CPubKey(x(hex_pubkey)) 23 | self.assertEqual(key.is_valid, is_valid) 24 | self.assertEqual(key.is_fullyvalid, is_fullyvalid) 25 | self.assertEqual(key.is_compressed, is_compressed) 26 | 27 | T('', False, False, False) 28 | T('00', True, True, False) # why is this valid? 29 | T('01', True, False, False) 30 | T('02', True, False, False) 31 | 32 | T('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71', 33 | True, True, True) 34 | T('0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71', 35 | True, False, True) 36 | 37 | T('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71', 38 | True, True, True) 39 | 40 | T('0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455', 41 | True, True, False) 42 | -------------------------------------------------------------------------------- /bitcointx/signature.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2012-2014 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | from __future__ import absolute_import, division, print_function, unicode_literals 13 | 14 | from bitcointx.core.serialize import * 15 | 16 | # Py3 compatibility 17 | import sys 18 | 19 | if sys.version > '3': 20 | from io import BytesIO as _BytesIO 21 | else: 22 | from cStringIO import StringIO as _BytesIO 23 | 24 | 25 | class DERSignature(ImmutableSerializable): 26 | __slots__ = ['length', 'r', 's'] 27 | 28 | def __init__(self, r, s, length): 29 | object.__setattr__(self, 'r', r) 30 | object.__setattr__(self, 's', s) 31 | object.__setattr__(self, 'length', length) 32 | 33 | @classmethod 34 | def stream_deserialize(cls, f): 35 | assert ser_read(f, 1) == b"\x30" 36 | rs = BytesSerializer.stream_deserialize(f) 37 | f = _BytesIO(rs) 38 | assert ser_read(f, 1) == b"\x02" 39 | r = BytesSerializer.stream_deserialize(f) 40 | assert ser_read(f, 1) == b"\x02" 41 | s = BytesSerializer.stream_deserialize(f) 42 | return cls(r, s, len(r + s)) 43 | 44 | def stream_serialize(self, f): 45 | f.write(b"\x30") 46 | f.write(b"\x02") 47 | BytesSerializer.stream_serialize(self.r, f) 48 | f.write(b"\x30") 49 | BytesSerializer.stream_serialize(self.s, f) 50 | 51 | def __repr__(self): 52 | return 'DERSignature(%s, %s)' % (self.r, self.s) 53 | 54 | 55 | __all__ = ( 56 | 'DERSignature', 57 | ) 58 | -------------------------------------------------------------------------------- /bitcointx/tests/test_base58.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013-2014 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | from __future__ import absolute_import, division, print_function, unicode_literals 13 | 14 | import json 15 | import os 16 | import unittest 17 | 18 | from binascii import unhexlify 19 | 20 | from bitcointx.base58 import * 21 | 22 | 23 | def load_test_vectors(name): 24 | with open(os.path.dirname(__file__) + '/data/' + name, 'r') as fd: 25 | for testcase in json.load(fd): 26 | yield testcase 27 | 28 | class Test_base58(unittest.TestCase): 29 | def test_encode_decode(self): 30 | for exp_bin, exp_base58 in load_test_vectors('base58_encode_decode.json'): 31 | exp_bin = unhexlify(exp_bin.encode('utf8')) 32 | 33 | act_base58 = encode(exp_bin) 34 | act_bin = decode(exp_base58) 35 | 36 | self.assertEqual(act_base58, exp_base58) 37 | self.assertEqual(act_bin, exp_bin) 38 | 39 | class Test_CBase58Data(unittest.TestCase): 40 | def test_from_data(self): 41 | b = CBase58Data.from_bytes(b"b\xe9\x07\xb1\\\xbf'\xd5BS\x99\xeb\xf6\xf0\xfbP\xeb\xb8\x8f\x18", 0) 42 | self.assertEqual(b.nVersion, 0) 43 | self.assertEqual(str(b), '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa') 44 | 45 | b = CBase58Data.from_bytes(b'Bf\xfco,(a\xd7\xfe"\x9b\'\x9ay\x80:\xfc\xa7\xba4', 196) 46 | self.assertEqual(b.nVersion, 196) 47 | self.assertEqual(str(b), '2MyJKxYR2zNZZsZ39SgkCXWCfQtXKhnWSWq') 48 | 49 | def test_invalid_base58_exception(self): 50 | invalids = ('', # missing everything 51 | '#', # invalid character 52 | '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNb', # invalid checksum 53 | ) 54 | 55 | for invalid in invalids: 56 | msg = '%r should have raised InvalidBase58Error but did not' % invalid 57 | with self.assertRaises(Base58Error, msg=msg): 58 | CBase58Data(invalid) 59 | -------------------------------------------------------------------------------- /bitcointx/signmessage.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013-2015 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | from __future__ import absolute_import, division, print_function, unicode_literals 13 | 14 | from bitcointx.core.key import CPubKey 15 | from bitcointx.core.serialize import ImmutableSerializable 16 | from bitcointx.wallet import P2PKHBitcoinAddress 17 | import bitcointx 18 | import base64 19 | import sys 20 | 21 | _bchr = chr 22 | _bord = ord 23 | if sys.version > '3': 24 | long = int 25 | _bchr = lambda x: bytes([x]) 26 | _bord = lambda x: x 27 | 28 | 29 | def VerifyMessage(address, message, sig): 30 | sig = base64.b64decode(sig) 31 | hash = message.GetHash() 32 | 33 | pubkey = CPubKey.recover_compact(hash, sig) 34 | 35 | return str(P2PKHBitcoinAddress.from_pubkey(pubkey)) == str(address) 36 | 37 | 38 | def SignMessage(key, message): 39 | sig, i = key.sign_compact(message.GetHash()) 40 | 41 | meta = 27 + i 42 | if key.is_compressed: 43 | meta += 4 44 | 45 | return base64.b64encode(_bchr(meta) + sig) 46 | 47 | 48 | class BitcoinMessage(ImmutableSerializable): 49 | __slots__ = ['magic', 'message'] 50 | 51 | def __init__(self, message="", magic="Bitcoin Signed Message:\n"): 52 | object.__setattr__(self, 'message', message.encode("utf-8")) 53 | object.__setattr__(self, 'magic', magic.encode("utf-8")) 54 | 55 | @classmethod 56 | def stream_deserialize(cls, f): 57 | magic = bitcointx.core.serialize.BytesSerializer.stream_deserialize(f) 58 | message = bitcointx.core.serialize.BytesSerializer.stream_deserialize(f) 59 | return cls(message, magic) 60 | 61 | def stream_serialize(self, f): 62 | bitcointx.core.serialize.BytesSerializer.stream_serialize(self.magic, f) 63 | bitcointx.core.serialize.BytesSerializer.stream_serialize(self.message, f) 64 | 65 | def __str__(self): 66 | return self.message.decode('ascii') 67 | 68 | def __repr__(self): 69 | return 'BitcoinMessage(%s, %s)' % (self.magic, self.message) 70 | -------------------------------------------------------------------------------- /bitcointx/tests/test_checkblock.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013-2014 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | from __future__ import absolute_import, division, print_function, unicode_literals 13 | 14 | import json 15 | import unittest 16 | import os 17 | 18 | from bitcointx.core import * 19 | 20 | def load_test_vectors(name): 21 | with open(os.path.dirname(__file__) + '/data/' + name, 'r') as fd: 22 | for test_case in json.load(fd): 23 | # Comments designated by single length strings 24 | if len(test_case) == 1: 25 | continue 26 | assert len(test_case) == 5 27 | 28 | (comment, fHeader, fCheckPoW, cur_time, serialized_blk) = test_case 29 | 30 | blk = None 31 | if fHeader: 32 | blk = CBlockHeader.deserialize(x(serialized_blk)) 33 | else: 34 | blk = CBlock.deserialize(x(serialized_blk)) 35 | 36 | yield (comment, fHeader, fCheckPoW, cur_time, blk) 37 | 38 | 39 | class Test_CheckBlock(unittest.TestCase): 40 | def test_checkblock_valid(self): 41 | for comment, fHeader, fCheckPoW, cur_time, blk in load_test_vectors('checkblock_valid.json'): 42 | try: 43 | if fHeader: 44 | CheckBlockHeader(blk, fCheckPoW=fCheckPoW, cur_time=cur_time) 45 | else: 46 | CheckBlock(blk, fCheckPoW=fCheckPoW, cur_time=cur_time) 47 | except ValidationError as err: 48 | self.fail('Failed "%s" with error %r' % (comment, err)) 49 | 50 | def test_checkblock_invalid(self): 51 | for comment, fHeader, fCheckPoW, cur_time, blk in load_test_vectors('checkblock_invalid.json'): 52 | try: 53 | if fHeader: 54 | CheckBlockHeader(blk, fCheckPoW=fCheckPoW, cur_time=cur_time) 55 | else: 56 | CheckBlock(blk, fCheckPoW=fCheckPoW, cur_time=cur_time) 57 | except ValidationError as err: 58 | continue 59 | 60 | self.fail('Invalid block "%s" passed checks' % comment) 61 | -------------------------------------------------------------------------------- /bitcointx/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2012-2018 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | from __future__ import absolute_import, division, print_function, unicode_literals 13 | 14 | import bitcointx.core 15 | 16 | # Note that setup.py can break if __init__.py imports any external 17 | # dependencies, as these might not be installed when setup.py runs. In this 18 | # case __version__ could be moved to a separate version.py and imported here. 19 | __version__ = '0.10.2dev' 20 | 21 | class MainParams(bitcointx.core.CoreMainParams): 22 | RPC_PORT = 8332 23 | BASE58_PREFIXES = {'PUBKEY_ADDR':0, 24 | 'SCRIPT_ADDR':5, 25 | 'SECRET_KEY' :128} 26 | BECH32_HRP = 'bc' 27 | 28 | class TestNetParams(bitcointx.core.CoreTestNetParams): 29 | RPC_PORT = 18332 30 | BASE58_PREFIXES = {'PUBKEY_ADDR':111, 31 | 'SCRIPT_ADDR':196, 32 | 'SECRET_KEY' :239} 33 | BECH32_HRP = 'tb' 34 | 35 | class RegTestParams(bitcointx.core.CoreRegTestParams): 36 | RPC_PORT = 18443 37 | BASE58_PREFIXES = {'PUBKEY_ADDR':111, 38 | 'SCRIPT_ADDR':196, 39 | 'SECRET_KEY' :239} 40 | BECH32_HRP = 'bcrt' 41 | 42 | """Master global setting for what chain params we're using. 43 | 44 | However, don't set this directly, use SelectParams() instead so as to set the 45 | bitcointx.core.params correctly too. 46 | """ 47 | #params = bitcointx.core.coreparams = MainParams() 48 | params = MainParams() 49 | 50 | def SelectParams(name): 51 | """Select the chain parameters to use 52 | 53 | name is one of 'mainnet', 'testnet', or 'regtest' 54 | 55 | Default chain is 'mainnet' 56 | """ 57 | global params 58 | bitcointx.core._SelectCoreParams(name) 59 | if name == 'mainnet': 60 | params = bitcointx.core.coreparams = MainParams() 61 | elif name == 'testnet': 62 | params = bitcointx.core.coreparams = TestNetParams() 63 | elif name == 'regtest': 64 | params = bitcointx.core.coreparams = RegTestParams() 65 | else: 66 | raise ValueError('Unknown chain %r' % name) 67 | -------------------------------------------------------------------------------- /examples/timestamp-op-ret.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (C) 2014 The python-bitcoinlib developers 4 | # 5 | # This file is part of python-bitcoinlib. 6 | # 7 | # It is subject to the license terms in the LICENSE file found in the top-level 8 | # directory of this distribution. 9 | # 10 | # No part of python-bitcoinlib, including this file, may be copied, modified, 11 | # propagated, or distributed except according to the terms contained in the 12 | # LICENSE file. 13 | 14 | """Example of timestamping a file via OP_RETURN""" 15 | 16 | import sys 17 | if sys.version_info.major < 3: 18 | sys.stderr.write('Sorry, Python 3.x required by this example.\n') 19 | sys.exit(1) 20 | 21 | import hashlib 22 | import bitcointx.rpc 23 | import sys 24 | 25 | from bitcointx import params 26 | from bitcointx.core import * 27 | from bitcointx.core.script import * 28 | 29 | proxy = bitcointx.rpc.Proxy() 30 | 31 | assert len(sys.argv) > 1 32 | 33 | digests = [] 34 | for f in sys.argv[1:]: 35 | try: 36 | with open(f, 'rb') as fd: 37 | digests.append(Hash(fd.read())) 38 | except FileNotFoundError as exp: 39 | if len(f)/2 in (20, 32): 40 | digests.append(x(f)) 41 | else: 42 | raise exp 43 | except IOError as exp: 44 | print(exp, file=sys.stderr) 45 | continue 46 | 47 | for digest in digests: 48 | txouts = [] 49 | 50 | unspent = sorted(proxy.listunspent(0), key=lambda x: hash(x['amount'])) 51 | 52 | txins = [CTxIn(unspent[-1]['outpoint'])] 53 | value_in = unspent[-1]['amount'] 54 | 55 | change_addr = proxy.getnewaddress() 56 | change_pubkey = proxy.validateaddress(change_addr)['pubkey'] 57 | change_out = CMutableTxOut(params.MAX_MONEY, CScript([change_pubkey, OP_CHECKSIG])) 58 | 59 | digest_outs = [CMutableTxOut(0, CScript([OP_RETURN, digest]))] 60 | 61 | txouts = [change_out] + digest_outs 62 | 63 | tx = CMutableTransaction(txins, txouts) 64 | 65 | 66 | FEE_PER_BYTE = 0.00025*COIN/1000 67 | while True: 68 | tx.vout[0].nValue = int(value_in - max(len(tx.serialize()) * FEE_PER_BYTE, 0.00011*COIN)) 69 | 70 | r = proxy.signrawtransaction(tx) 71 | assert r['complete'] 72 | tx = r['tx'] 73 | 74 | if value_in - tx.vout[0].nValue >= len(tx.serialize()) * FEE_PER_BYTE: 75 | print(b2x(tx.serialize())) 76 | print(len(tx.serialize()), 'bytes', file=sys.stderr) 77 | print(b2lx(proxy.sendrawtransaction(tx))) 78 | break 79 | -------------------------------------------------------------------------------- /bitcointx/bech32.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2017 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | """Bech32 encoding and decoding""" 13 | 14 | import sys 15 | _bchr = chr 16 | _bord = ord 17 | if sys.version > '3': 18 | long = int 19 | _bchr = lambda x: bytes([x]) 20 | _bord = lambda x: x 21 | 22 | from bitcointx.segwit_addr import encode, decode 23 | import bitcointx 24 | 25 | class Bech32Error(Exception): 26 | pass 27 | 28 | class Bech32ChecksumError(Bech32Error): 29 | pass 30 | 31 | class CBech32Data(bytes): 32 | """Bech32-encoded data 33 | 34 | Includes a witver and checksum. 35 | """ 36 | def __new__(cls, s): 37 | """from bech32 addr to """ 38 | witver, data = decode(bitcointx.params.BECH32_HRP, s) 39 | if witver is None and data is None: 40 | raise Bech32Error('Bech32 decoding error') 41 | 42 | return cls.from_bytes(witver, data) 43 | 44 | def __init__(self, s): 45 | """Initialize from bech32-encoded string 46 | 47 | Note: subclasses put your initialization routines here, but ignore the 48 | argument - that's handled by __new__(), and .from_bytes() will call 49 | __init__() with None in place of the string. 50 | """ 51 | 52 | @classmethod 53 | def from_bytes(cls, witver, witprog): 54 | """Instantiate from witver and data""" 55 | if not (0 <= witver <= 16): 56 | raise ValueError('witver must be in range 0 to 16 inclusive; got %d' % witver) 57 | self = bytes.__new__(cls, witprog) 58 | self.witver = witver 59 | 60 | return self 61 | 62 | def to_bytes(self): 63 | """Convert to bytes instance 64 | 65 | Note that it's the data represented that is converted; the checkum and 66 | witver is not included. 67 | """ 68 | return b'' + self 69 | 70 | def __str__(self): 71 | """Convert to string""" 72 | return encode(bitcointx.params.BECH32_HRP, self.witver, self) 73 | 74 | def __repr__(self): 75 | return '%s(%r)' % (self.__class__.__name__, str(self)) 76 | 77 | __all__ = ( 78 | 'Bech32Error', 79 | 'Bech32ChecksumError', 80 | 'CBech32Data', 81 | ) 82 | -------------------------------------------------------------------------------- /examples/sign-message.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Copyright (C) 2013-2015 The python-bitcoinlib developers 4 | # 5 | # This file is part of python-bitcoinlib. 6 | # 7 | # It is subject to the license terms in the LICENSE file found in the top-level 8 | # directory of this distribution. 9 | # 10 | # No part of python-bitcoinlib, including this file, may be copied, modified, 11 | # propagated, or distributed except according to the terms contained in the 12 | # LICENSE file. 13 | 14 | from __future__ import absolute_import, division, print_function, unicode_literals 15 | 16 | from bitcointx.wallet import CBitcoinSecret, P2PKHBitcoinAddress 17 | from bitcointx.signmessage import BitcoinMessage, VerifyMessage, SignMessage 18 | 19 | def sign_message(key, msg): 20 | secret = CBitcoinSecret(key) 21 | message = BitcoinMessage(msg) 22 | return SignMessage(secret, message) 23 | 24 | def print_default(signature, key=None, msg=None): 25 | print(signature.decode('ascii')) 26 | 27 | def print_verbose(signature, key, msg): 28 | secret = CBitcoinSecret(key) 29 | address = P2PKHBitcoinAddress.from_pubkey(secret.pub) 30 | message = BitcoinMessage(msg) 31 | print('Address: %s' % address) 32 | print('Message: %s' % msg) 33 | print('Signature: %s' % signature) 34 | print('Verified: %s' % VerifyMessage(address, message, signature)) 35 | print('\nTo verify using bitcoin core:') 36 | print('\n`bitcoin-cli verifymessage %s \'%s\' \'%s\'`\n' % (address, signature.decode('ascii'), msg)) 37 | 38 | def parser(): 39 | import argparse 40 | parser = argparse.ArgumentParser( 41 | description='Sign a message with a private key.', 42 | epilog='Security warning: arguments may be visible to other users on the same host.') 43 | parser.add_argument( 44 | '-v', '--verbose', dest='print_result', 45 | action='store_const', const=print_verbose, default=print_default, 46 | help='verbose output') 47 | parser.add_argument( 48 | '-k', '--key', 49 | required=True, 50 | help='private key in base58 encoding') 51 | parser.add_argument( 52 | '-m', '--msg', 53 | required=True, 54 | help='message to sign') 55 | return parser 56 | 57 | if __name__ == '__main__': 58 | args = parser().parse_args() 59 | try: 60 | signature = sign_message(args.key, args.msg) 61 | except Exception as error: 62 | print('%s: %s' % (error.__class__.__name__, str(error))) 63 | exit(1) 64 | else: 65 | args.print_result(signature, args.key, args.msg) 66 | -------------------------------------------------------------------------------- /bitcointx/tests/test_signmessage.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013-2015 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | from __future__ import absolute_import, division, print_function, unicode_literals 13 | 14 | import unittest 15 | 16 | from bitcointx.wallet import CBitcoinSecret 17 | from bitcointx.signmessage import BitcoinMessage, VerifyMessage, SignMessage 18 | import sys 19 | import os 20 | import json 21 | 22 | _bchr = chr 23 | _bord = ord 24 | if sys.version > '3': 25 | long = int 26 | _bchr = lambda x: bytes([x]) 27 | _bord = lambda x: x 28 | 29 | def load_test_vectors(name): 30 | with open(os.path.dirname(__file__) + '/data/' + name, 'r') as fd: 31 | return json.load(fd) 32 | 33 | 34 | class Test_SignVerifyMessage(unittest.TestCase): 35 | def test_verify_message_simple(self): 36 | address = "1F26pNMrywyZJdr22jErtKcjF8R3Ttt55G" 37 | message = address 38 | signature = "H85WKpqtNZDrajOnYDgUY+abh0KCAcOsAIOQwx2PftAbLEPRA7mzXA/CjXRxzz0MC225pR/hx02Vf2Ag2x33kU4=" 39 | 40 | message = BitcoinMessage(message) 41 | 42 | self.assertTrue(VerifyMessage(address, message, signature)) 43 | 44 | def test_verify_message_vectors(self): 45 | for vector in load_test_vectors('signmessage.json'): 46 | message = BitcoinMessage(vector['address']) 47 | self.assertTrue(VerifyMessage(vector['address'], message, vector['signature'])) 48 | 49 | def test_sign_message_simple(self): 50 | key = CBitcoinSecret("L4vB5fomsK8L95wQ7GFzvErYGht49JsCPJyJMHpB4xGM6xgi2jvG") 51 | address = "1F26pNMrywyZJdr22jErtKcjF8R3Ttt55G" 52 | message = address 53 | 54 | message = BitcoinMessage(message) 55 | signature = SignMessage(key, message) 56 | 57 | self.assertTrue(signature) 58 | self.assertTrue(VerifyMessage(address, message, signature)) 59 | 60 | def test_sign_message_vectors(self): 61 | for vector in load_test_vectors('signmessage.json'): 62 | key = CBitcoinSecret(vector['wif']) 63 | message = BitcoinMessage(vector['address']) 64 | 65 | signature = SignMessage(key, message) 66 | 67 | self.assertTrue(signature, "Failed to sign for [%s]" % vector['address']) 68 | self.assertTrue(VerifyMessage(vector['address'], message, vector['signature']), "Failed to verify signature for [%s]" % vector['address']) 69 | 70 | 71 | if __name__ == "__main__": 72 | unittest.main() 73 | -------------------------------------------------------------------------------- /bitcointx/core/_bignum.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2012-2014 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | # Bignum routines 13 | # 14 | # Internally used for script evaluation; not to be used externally. 15 | 16 | from __future__ import absolute_import, division, print_function, unicode_literals 17 | 18 | import struct 19 | 20 | 21 | # generic big endian MPI format 22 | 23 | def bn_bytes(v, have_ext=False): 24 | ext = 0 25 | if have_ext: 26 | ext = 1 27 | return ((v.bit_length() + 7) // 8) + ext 28 | 29 | def bn2bin(v): 30 | s = bytearray() 31 | i = bn_bytes(v) 32 | while i > 0: 33 | s.append((v >> ((i - 1) * 8)) & 0xff) 34 | i -= 1 35 | return s 36 | 37 | def bin2bn(s): 38 | l = 0 39 | for ch in s: 40 | l = (l << 8) | ch 41 | return l 42 | 43 | def bn2mpi(v): 44 | have_ext = False 45 | if v.bit_length() > 0: 46 | have_ext = (v.bit_length() & 0x07) == 0 47 | 48 | neg = False 49 | if v < 0: 50 | neg = True 51 | v = -v 52 | 53 | s = struct.pack(b">I", bn_bytes(v, have_ext)) 54 | ext = bytearray() 55 | if have_ext: 56 | ext.append(0) 57 | v_bin = bn2bin(v) 58 | if neg: 59 | if have_ext: 60 | ext[0] |= 0x80 61 | else: 62 | v_bin[0] |= 0x80 63 | return s + ext + v_bin 64 | 65 | def mpi2bn(s): 66 | if len(s) < 4: 67 | return None 68 | s_size = bytes(s[:4]) 69 | v_len = struct.unpack(b">I", s_size)[0] 70 | if len(s) != (v_len + 4): 71 | return None 72 | if v_len == 0: 73 | return 0 74 | 75 | v_str = bytearray(s[4:]) 76 | neg = False 77 | i = v_str[0] 78 | if i & 0x80: 79 | neg = True 80 | i &= ~0x80 81 | v_str[0] = i 82 | 83 | v = bin2bn(v_str) 84 | 85 | if neg: 86 | return -v 87 | return v 88 | 89 | # bitcoin-specific little endian format, with implicit size 90 | def mpi2vch(s): 91 | r = s[4:] # strip size 92 | r = r[::-1] # reverse string, converting BE->LE 93 | return r 94 | 95 | def bn2vch(v): 96 | return bytes(mpi2vch(bn2mpi(v))) 97 | 98 | def vch2mpi(s): 99 | r = struct.pack(b">I", len(s)) # size 100 | r += s[::-1] # reverse string, converting LE->BE 101 | return r 102 | 103 | def vch2bn(s): 104 | return mpi2bn(vch2mpi(s)) 105 | 106 | -------------------------------------------------------------------------------- /bitcointx/tests/test_bech32.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013-2014 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | from __future__ import absolute_import, division, print_function, unicode_literals 13 | 14 | import json 15 | import os 16 | import unittest 17 | import array 18 | import sys 19 | _bchr = chr 20 | _bord = ord 21 | _tobytes = lambda x: array.array('B', x).tostring() 22 | if sys.version > '3': 23 | long = int 24 | _bchr = lambda x: bytes([x]) 25 | _bord = lambda x: x 26 | _tobytes = bytes 27 | 28 | from binascii import unhexlify 29 | 30 | from bitcointx.core.script import CScript, OP_0, OP_1, OP_16 31 | from bitcointx.bech32 import * 32 | from bitcointx.segwit_addr import encode, decode 33 | 34 | 35 | def load_test_vectors(name): 36 | with open(os.path.dirname(__file__) + '/data/' + name, 'r') as fd: 37 | for testcase in json.load(fd): 38 | yield testcase 39 | 40 | def to_scriptPubKey(witver, witprog): 41 | """Decoded bech32 address to script""" 42 | return CScript([witver]) + CScript(_tobytes(witprog)) 43 | 44 | class Test_bech32(unittest.TestCase): 45 | 46 | def op_decode(self, witver): 47 | """OP encoding to int""" 48 | if witver == OP_0: 49 | return 0 50 | if OP_1 <= witver <= OP_16: 51 | return witver - OP_1 + 1 52 | self.fail('Wrong witver: %d' % witver) 53 | 54 | def test_encode_decode(self): 55 | for exp_bin, exp_bech32 in load_test_vectors('bech32_encode_decode.json'): 56 | exp_bin = [_bord(y) for y in unhexlify(exp_bin.encode('utf8'))] 57 | witver = self.op_decode(exp_bin[0]) 58 | hrp = exp_bech32[:exp_bech32.rindex('1')].lower() 59 | self.assertEqual(exp_bin[1], len(exp_bin[2:])) 60 | act_bech32 = encode(hrp, witver, exp_bin[2:]) 61 | act_bin = decode(hrp, exp_bech32) 62 | 63 | self.assertEqual(act_bech32.lower(), exp_bech32.lower()) 64 | self.assertEqual(to_scriptPubKey(*act_bin), _tobytes(exp_bin)) 65 | 66 | class Test_CBech32Data(unittest.TestCase): 67 | def test_from_data(self): 68 | b = CBech32Data.from_bytes(0, unhexlify('751e76e8199196d454941c45d1b3a323f1433bd6')) 69 | self.assertEqual(b.witver, 0) 70 | self.assertEqual(str(b).upper(), 'BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4') 71 | 72 | def test_invalid_bech32_exception(self): 73 | 74 | for invalid, _ in load_test_vectors("bech32_invalid.json"): 75 | msg = '%r should have raised Bech32Error but did not' % invalid 76 | with self.assertRaises(Bech32Error, msg=msg): 77 | CBech32Data(invalid) 78 | -------------------------------------------------------------------------------- /examples/bip-0070-payment-protocol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (C) 2013-2014 The python-bitcoinlib developers 4 | # 5 | # This file is part of python-bitcoinlib. 6 | # 7 | # It is subject to the license terms in the LICENSE file found in the top-level 8 | # directory of this distribution. 9 | # 10 | # No part of python-bitcoinlib, including this file, may be copied, modified, 11 | # propagated, or distributed except according to the terms contained in the 12 | # LICENSE file. 13 | 14 | """Bip-0070-related functionality 15 | 16 | Creates http response objects suitable for use with 17 | bitcoin bip 70 using googles protocol buffers. 18 | """ 19 | 20 | import urllib2 21 | 22 | # https://github.com/bitcoin/bips/blob/master/bip-0070/paymentrequest.proto 23 | import payments_pb2 24 | o = payments_pb2 25 | 26 | import bitcointx 27 | #bitcointx.SelectParams('testnet') 28 | from bitcointx.wallet import CBitcoinAddress 29 | from bitcointx.core.script import CScript 30 | from bitcointx.rpc import Proxy 31 | 32 | from time import time 33 | 34 | def payment_request(): 35 | """Generates a http PaymentRequest object""" 36 | 37 | bc = Proxy() 38 | btc = bc.getnewaddress() 39 | 40 | # Setting the 'amount' field to 0 (zero) should prompt the user to enter 41 | # the amount for us but a bug in bitcoin core qt version 0.9.1 (at time of 42 | # writing) wrongly informs us that the value is too small and aborts. 43 | # https://github.com/bitcoin/bitcoin/issues/3095 44 | # Also there can be no leading 0's (zeros). 45 | btc_amount = 100000 46 | serialized_pubkey = btc.to_scriptPubKey() 47 | 48 | pdo = o.PaymentDetails() 49 | #pdo.network = 'test' 50 | pdo.outputs.add(amount = btc_amount,script = serialized_pubkey) 51 | pdo.time = int(time()) 52 | pdo.memo = 'String shown to user before confirming payment' 53 | pdo.payment_url = 'http://payment_ack.url' 54 | 55 | pro = o.PaymentRequest() 56 | pro.serialized_payment_details = pdo.SerializeToString() 57 | 58 | sds_pr = pro.SerializeToString() 59 | 60 | open('sds_pr_blob', 'wb').write(sds_pr) 61 | headers = {'Content-Type': 'application/bitcoin-payment', 62 | 'Accept': 'application/bitcoin-paymentrequest'} 63 | http_response_object = urllib2.Request('file:sds_pr_blob', None, headers) 64 | 65 | return http_response_object 66 | 67 | 68 | def payment_ack(serialized_Payment_message): 69 | """Generates a PaymentACK object, captures client refund address and returns a message""" 70 | 71 | pao = o.PaymentACK() 72 | pao.payment.ParseFromString(serialized_Payment_message) 73 | pao.memo = 'String shown to user after payment confirmation' 74 | 75 | refund_address = CBitcoinAddress.from_scriptPubKey(CScript(pao.payment.refund_to[0].script)) 76 | 77 | sds_pa = pao.SerializeToString() 78 | 79 | open('sds_pa_blob', 'wb').write(sds_pa) 80 | headers = {'Content-Type' : 'application/bitcoin-payment', 'Accept' : 'application/bitcoin-paymentack'} 81 | http_response_object = urllib2.Request('file:sds_pa_blob', None, headers) 82 | 83 | return http_response_object 84 | -------------------------------------------------------------------------------- /examples/spend-p2pkh-txout.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (C) 2014 The python-bitcoinlib developers 4 | # 5 | # This file is part of python-bitcoinlib. 6 | # 7 | # It is subject to the license terms in the LICENSE file found in the top-level 8 | # directory of this distribution. 9 | # 10 | # No part of python-bitcoinlib, including this file, may be copied, modified, 11 | # propagated, or distributed except according to the terms contained in the 12 | # LICENSE file. 13 | 14 | """Low-level example of how to spend a standard pay-to-pubkey-hash (P2PKH) txout""" 15 | 16 | import sys 17 | if sys.version_info.major < 3: 18 | sys.stderr.write('Sorry, Python 3.x required by this example.\n') 19 | sys.exit(1) 20 | 21 | import hashlib 22 | 23 | from bitcointx import SelectParams 24 | from bitcointx.core import b2x, lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160 25 | from bitcointx.core.script import CScript, OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL 26 | from bitcointx.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH 27 | from bitcointx.wallet import CBitcoinAddress, CBitcoinSecret 28 | 29 | SelectParams('mainnet') 30 | 31 | # Create the (in)famous correct brainwallet secret key. 32 | h = hashlib.sha256(b'correct horse battery staple').digest() 33 | seckey = CBitcoinSecret.from_secret_bytes(h) 34 | 35 | # Same as the txid:vout the createrawtransaction RPC call requires 36 | # 37 | # lx() takes *little-endian* hex and converts it to bytes; in Bitcoin 38 | # transaction hashes are shown little-endian rather than the usual big-endian. 39 | # There's also a corresponding x() convenience function that takes big-endian 40 | # hex and converts it to bytes. 41 | txid = lx('7e195aa3de827814f172c362fcf838d92ba10e3f9fdd9c3ecaf79522b311b22d') 42 | vout = 0 43 | 44 | # Create the txin structure, which includes the outpoint. The scriptSig 45 | # defaults to being empty. 46 | txin = CMutableTxIn(COutPoint(txid, vout)) 47 | 48 | # We also need the scriptPubKey of the output we're spending because 49 | # SignatureHash() replaces the transaction scriptSig's with it. 50 | # 51 | # Here we'll create that scriptPubKey from scratch using the pubkey that 52 | # corresponds to the secret key we generated above. 53 | txin_scriptPubKey = CScript([OP_DUP, OP_HASH160, Hash160(seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG]) 54 | 55 | # Create the txout. This time we create the scriptPubKey from a Bitcoin 56 | # address. 57 | txout = CMutableTxOut(0.001*COIN, CBitcoinAddress('1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8').to_scriptPubKey()) 58 | 59 | # Create the unsigned transaction. 60 | tx = CMutableTransaction([txin], [txout]) 61 | 62 | # Calculate the signature hash for that transaction. 63 | sighash = SignatureHash(txin_scriptPubKey, tx, 0, SIGHASH_ALL) 64 | 65 | # Now sign it. We have to append the type of signature we want to the end, in 66 | # this case the usual SIGHASH_ALL. 67 | sig = seckey.sign(sighash) + bytes([SIGHASH_ALL]) 68 | 69 | # Set the scriptSig of our transaction input appropriately. 70 | txin.scriptSig = CScript([sig, seckey.pub]) 71 | 72 | # Verify the signature worked. This calls EvalScript() and actually executes 73 | # the opcodes in the scripts to see if everything worked out. If it doesn't an 74 | # exception will be raised. 75 | VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) 76 | 77 | # Done! Print the transaction to standard output with the bytes-to-hex 78 | # function. 79 | print(b2x(tx.serialize())) 80 | -------------------------------------------------------------------------------- /examples/spend-p2sh-txout.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (C) 2014 The python-bitcoinlib developers 4 | # 5 | # This file is part of python-bitcoinlib. 6 | # 7 | # It is subject to the license terms in the LICENSE file found in the top-level 8 | # directory of this distribution. 9 | # 10 | # No part of python-bitcoinlib, including this file, may be copied, modified, 11 | # propagated, or distributed except according to the terms contained in the 12 | # LICENSE file. 13 | 14 | """Low-level example of how to spend a P2SH/BIP16 txout""" 15 | 16 | import sys 17 | if sys.version_info.major < 3: 18 | sys.stderr.write('Sorry, Python 3.x required by this example.\n') 19 | sys.exit(1) 20 | 21 | import hashlib 22 | 23 | from bitcointx import SelectParams 24 | from bitcointx.core import b2x, lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160 25 | from bitcointx.core.script import CScript, OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL 26 | from bitcointx.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH 27 | from bitcointx.wallet import CBitcoinAddress, CBitcoinSecret 28 | 29 | SelectParams('mainnet') 30 | 31 | # Create the (in)famous correct brainwallet secret key. 32 | h = hashlib.sha256(b'correct horse battery staple').digest() 33 | seckey = CBitcoinSecret.from_secret_bytes(h) 34 | 35 | # Create a redeemScript. Similar to a scriptPubKey the redeemScript must be 36 | # satisfied for the funds to be spent. 37 | txin_redeemScript = CScript([seckey.pub, OP_CHECKSIG]) 38 | print(b2x(txin_redeemScript)) 39 | 40 | # Create the magic P2SH scriptPubKey format from that redeemScript. You should 41 | # look at the CScript.to_p2sh_scriptPubKey() function in bitcointx.core.script to 42 | # understand what's happening, as well as read BIP16: 43 | # https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki 44 | txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey() 45 | 46 | # Convert the P2SH scriptPubKey to a base58 Bitcoin address and print it. 47 | # You'll need to send some funds to it to create a txout to spend. 48 | txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey) 49 | print('Pay to:',str(txin_p2sh_address)) 50 | 51 | # Same as the txid:vout the createrawtransaction RPC call requires 52 | # 53 | # lx() takes *little-endian* hex and converts it to bytes; in Bitcoin 54 | # transaction hashes are shown little-endian rather than the usual big-endian. 55 | # There's also a corresponding x() convenience function that takes big-endian 56 | # hex and converts it to bytes. 57 | txid = lx('bff785da9f8169f49be92fa95e31f0890c385bfb1bd24d6b94d7900057c617ae') 58 | vout = 0 59 | 60 | # Create the txin structure, which includes the outpoint. The scriptSig 61 | # defaults to being empty. 62 | txin = CMutableTxIn(COutPoint(txid, vout)) 63 | 64 | # Create the txout. This time we create the scriptPubKey from a Bitcoin 65 | # address. 66 | txout = CMutableTxOut(0.0005*COIN, CBitcoinAddress('323uf9MgLaSn9T7vDaK1cGAZ2qpvYUuqSp').to_scriptPubKey()) 67 | 68 | # Create the unsigned transaction. 69 | tx = CMutableTransaction([txin], [txout]) 70 | 71 | # Calculate the signature hash for that transaction. Note how the script we use 72 | # is the redeemScript, not the scriptPubKey. That's because when the CHECKSIG 73 | # operation happens EvalScript() will be evaluating the redeemScript, so the 74 | # corresponding SignatureHash() function will use that same script when it 75 | # replaces the scriptSig in the transaction being hashed with the script being 76 | # executed. 77 | sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL) 78 | 79 | # Now sign it. We have to append the type of signature we want to the end, in 80 | # this case the usual SIGHASH_ALL. 81 | sig = seckey.sign(sighash) + bytes([SIGHASH_ALL]) 82 | 83 | # Set the scriptSig of our transaction input appropriately. 84 | txin.scriptSig = CScript([sig, txin_redeemScript]) 85 | 86 | # Verify the signature worked. This calls EvalScript() and actually executes 87 | # the opcodes in the scripts to see if everything worked out. If it doesn't an 88 | # exception will be raised. 89 | VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) 90 | 91 | # Done! Print the transaction to standard output with the bytes-to-hex 92 | # function. 93 | print(b2x(tx.serialize())) 94 | -------------------------------------------------------------------------------- /bitcointx/tests/test_scripteval.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013-2017 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | from __future__ import absolute_import, division, print_function, unicode_literals 13 | 14 | import json 15 | import os 16 | import unittest 17 | 18 | import sys 19 | if sys.version > '3': 20 | long = int 21 | 22 | from binascii import unhexlify 23 | 24 | from bitcointx.core import * 25 | from bitcointx.core.script import * 26 | from bitcointx.core.scripteval import * 27 | 28 | def parse_script(s): 29 | def ishex(s): 30 | return set(s).issubset(set('0123456789abcdefABCDEF')) 31 | 32 | r = [] 33 | 34 | # Create an opcodes_by_name table with both OP_ prefixed names and 35 | # shortened ones with the OP_ dropped. 36 | opcodes_by_name = {} 37 | for name, code in OPCODES_BY_NAME.items(): 38 | opcodes_by_name[name] = code 39 | opcodes_by_name[name[3:]] = code 40 | 41 | for word in s.split(): 42 | if word.isdigit() or (word[0] == '-' and word[1:].isdigit()): 43 | r.append(CScript([long(word)])) 44 | elif word.startswith('0x') and ishex(word[2:]): 45 | # Raw ex data, inserted NOT pushed onto stack: 46 | r.append(unhexlify(word[2:].encode('utf8'))) 47 | elif len(word) >= 2 and word[0] == "'" and word[-1] == "'": 48 | r.append(CScript([bytes(word[1:-1].encode('utf8'))])) 49 | elif word in opcodes_by_name: 50 | r.append(CScript([opcodes_by_name[word]])) 51 | else: 52 | raise ValueError("Error parsing script: %r" % s) 53 | 54 | return CScript(b''.join(r)) 55 | 56 | 57 | def load_test_vectors(name): 58 | with open(os.path.dirname(__file__) + '/data/' + name, 'r') as fd: 59 | for test_case in json.load(fd): 60 | if len(test_case) == 1: 61 | continue # comment 62 | 63 | if len(test_case) == 3: 64 | test_case.append('') # add missing comment 65 | 66 | scriptSig, scriptPubKey, flags, comment = test_case 67 | 68 | scriptSig = parse_script(scriptSig) 69 | scriptPubKey = parse_script(scriptPubKey) 70 | 71 | flag_set = set() 72 | for flag in flags.split(','): 73 | if flag == '' or flag == 'NONE': 74 | pass 75 | 76 | else: 77 | try: 78 | flag = SCRIPT_VERIFY_FLAGS_BY_NAME[flag] 79 | except IndexError: 80 | raise Exception('Unknown script verify flag %r' % flag) 81 | 82 | flag_set.add(flag) 83 | 84 | yield (scriptSig, scriptPubKey, flag_set, comment, test_case) 85 | 86 | 87 | class Test_EvalScript(unittest.TestCase): 88 | def create_test_txs(self, scriptSig, scriptPubKey): 89 | txCredit = CTransaction([CTxIn(COutPoint(), CScript([OP_0, OP_0]), nSequence=0xFFFFFFFF)], 90 | [CTxOut(0, scriptPubKey)], 91 | nLockTime=0) 92 | txSpend = CTransaction([CTxIn(COutPoint(txCredit.GetTxid(), 0), scriptSig, nSequence=0xFFFFFFFF)], 93 | [CTxOut(0, CScript())], 94 | nLockTime=0) 95 | return (txCredit, txSpend) 96 | 97 | def test_script_valid(self): 98 | for scriptSig, scriptPubKey, flags, comment, test_case in load_test_vectors('script_valid.json'): 99 | (txCredit, txSpend) = self.create_test_txs(scriptSig, scriptPubKey) 100 | 101 | try: 102 | VerifyScript(scriptSig, scriptPubKey, txSpend, 0, flags) 103 | except ValidationError as err: 104 | self.fail('Script FAILED: %r %r %r with exception %r' % (scriptSig, scriptPubKey, comment, err)) 105 | 106 | def test_script_invalid(self): 107 | for scriptSig, scriptPubKey, flags, comment, test_case in load_test_vectors('script_invalid.json'): 108 | (txCredit, txSpend) = self.create_test_txs(scriptSig, scriptPubKey) 109 | 110 | try: 111 | VerifyScript(scriptSig, scriptPubKey, txSpend, 0, flags) 112 | except ValidationError: 113 | continue 114 | 115 | self.fail('Expected %r to fail' % test_case) 116 | -------------------------------------------------------------------------------- /bitcointx/base58.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Sam Rushing 2 | # Copyright (C) 2013-2014 The python-bitcoinlib developers 3 | # 4 | # This file is part of python-bitcoinlib. 5 | # 6 | # It is subject to the license terms in the LICENSE file found in the top-level 7 | # directory of this distribution. 8 | # 9 | # No part of python-bitcoinlib, including this file, may be copied, modified, 10 | # propagated, or distributed except according to the terms contained in the 11 | # LICENSE file. 12 | 13 | """Base58 encoding and decoding""" 14 | 15 | from __future__ import absolute_import, division, print_function, unicode_literals 16 | 17 | import sys 18 | _bchr = chr 19 | _bord = ord 20 | if sys.version > '3': 21 | long = int 22 | _bchr = lambda x: bytes([x]) 23 | _bord = lambda x: x 24 | 25 | import binascii 26 | 27 | import bitcointx.core 28 | 29 | B58_DIGITS = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 30 | 31 | class Base58Error(Exception): 32 | pass 33 | 34 | class InvalidBase58Error(Base58Error): 35 | """Raised on generic invalid base58 data, such as bad characters. 36 | 37 | Checksum failures raise Base58ChecksumError specifically. 38 | """ 39 | pass 40 | 41 | def encode(b): 42 | """Encode bytes to a base58-encoded string""" 43 | 44 | # Convert big-endian bytes to integer 45 | n = int('0x0' + binascii.hexlify(b).decode('utf8'), 16) 46 | 47 | # Divide that integer into bas58 48 | res = [] 49 | while n > 0: 50 | n, r = divmod(n, 58) 51 | res.append(B58_DIGITS[r]) 52 | res = ''.join(res[::-1]) 53 | 54 | # Encode leading zeros as base58 zeros 55 | czero = b'\x00' 56 | if sys.version > '3': 57 | # In Python3 indexing a bytes returns numbers, not characters. 58 | czero = 0 59 | pad = 0 60 | for c in b: 61 | if c == czero: 62 | pad += 1 63 | else: 64 | break 65 | return B58_DIGITS[0] * pad + res 66 | 67 | def decode(s): 68 | """Decode a base58-encoding string, returning bytes""" 69 | if not s: 70 | return b'' 71 | 72 | # Convert the string to an integer 73 | n = 0 74 | for c in s: 75 | n *= 58 76 | if c not in B58_DIGITS: 77 | raise InvalidBase58Error('Character %r is not a valid base58 character' % c) 78 | digit = B58_DIGITS.index(c) 79 | n += digit 80 | 81 | # Convert the integer to bytes 82 | h = '%x' % n 83 | if len(h) % 2: 84 | h = '0' + h 85 | res = binascii.unhexlify(h.encode('utf8')) 86 | 87 | # Add padding back. 88 | pad = 0 89 | for c in s[:-1]: 90 | if c == B58_DIGITS[0]: pad += 1 91 | else: break 92 | return b'\x00' * pad + res 93 | 94 | 95 | class Base58ChecksumError(Base58Error): 96 | """Raised on Base58 checksum errors""" 97 | pass 98 | 99 | class CBase58Data(bytes): 100 | """Base58-encoded data 101 | 102 | Includes a version and checksum. 103 | """ 104 | def __new__(cls, s): 105 | k = decode(s) 106 | verbyte, data, check0 = k[0:1], k[1:-4], k[-4:] 107 | check1 = bitcointx.core.Hash(verbyte + data)[:4] 108 | if check0 != check1: 109 | raise Base58ChecksumError('Checksum mismatch: expected %r, calculated %r' % (check0, check1)) 110 | 111 | return cls.from_bytes(data, _bord(verbyte[0])) 112 | 113 | def __init__(self, s): 114 | """Initialize from base58-encoded string 115 | 116 | Note: subclasses put your initialization routines here, but ignore the 117 | argument - that's handled by __new__(), and .from_bytes() will call 118 | __init__() with None in place of the string. 119 | """ 120 | 121 | @classmethod 122 | def from_bytes(cls, data, nVersion): 123 | """Instantiate from data and nVersion""" 124 | if not (0 <= nVersion <= 255): 125 | raise ValueError('nVersion must be in range 0 to 255 inclusive; got %d' % nVersion) 126 | self = bytes.__new__(cls, data) 127 | self.nVersion = nVersion 128 | 129 | return self 130 | 131 | def to_bytes(self): 132 | """Convert to bytes instance 133 | 134 | Note that it's the data represented that is converted; the checkum and 135 | nVersion is not included. 136 | """ 137 | return b'' + self 138 | 139 | def __str__(self): 140 | """Convert to string""" 141 | vs = _bchr(self.nVersion) + self 142 | check = bitcointx.core.Hash(vs)[0:4] 143 | return encode(vs + check) 144 | 145 | def __repr__(self): 146 | return '%s(%r)' % (self.__class__.__name__, str(self)) 147 | 148 | __all__ = ( 149 | 'B58_DIGITS', 150 | 'Base58Error', 151 | 'InvalidBase58Error', 152 | 'encode', 153 | 'decode', 154 | 'Base58ChecksumError', 155 | 'CBase58Data', 156 | ) 157 | -------------------------------------------------------------------------------- /bitcointx/segwit_addr.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017 Pieter Wuille 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | # THE SOFTWARE. 20 | 21 | """Reference implementation for Bech32 and segwit addresses.""" 22 | 23 | import sys 24 | _bchr = chr 25 | _bord = lambda x: ord(x) if isinstance(x, str) else x 26 | if sys.version > '3': 27 | long = int 28 | _bchr = lambda x: bytes([x]) 29 | _bord = lambda x: x 30 | 31 | CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" 32 | 33 | 34 | def bech32_polymod(values): 35 | """Internal function that computes the Bech32 checksum.""" 36 | generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] 37 | chk = 1 38 | for value in values: 39 | top = chk >> 25 40 | chk = (chk & 0x1ffffff) << 5 ^ value 41 | for i in range(5): 42 | chk ^= generator[i] if ((top >> i) & 1) else 0 43 | return chk 44 | 45 | 46 | def bech32_hrp_expand(hrp): 47 | """Expand the HRP into values for checksum computation.""" 48 | return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp] 49 | 50 | 51 | def bech32_verify_checksum(hrp, data): 52 | """Verify a checksum given HRP and converted data characters.""" 53 | return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1 54 | 55 | 56 | def bech32_create_checksum(hrp, data): 57 | """Compute the checksum values given HRP and data.""" 58 | values = bech32_hrp_expand(hrp) + data 59 | polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1 60 | return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)] 61 | 62 | 63 | def bech32_encode(hrp, data): 64 | """Compute a Bech32 string given HRP and data values.""" 65 | combined = data + bech32_create_checksum(hrp, data) 66 | return hrp + '1' + ''.join([CHARSET[d] for d in combined]) 67 | 68 | 69 | def bech32_decode(bech): 70 | """Validate a Bech32 string, and determine HRP and data.""" 71 | if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or 72 | (bech.lower() != bech and bech.upper() != bech)): 73 | return (None, None) 74 | bech = bech.lower() 75 | pos = bech.rfind('1') 76 | if pos < 1 or pos + 7 > len(bech) or len(bech) > 90: 77 | return (None, None) 78 | if not all(x in CHARSET for x in bech[pos+1:]): 79 | return (None, None) 80 | hrp = bech[:pos] 81 | data = [CHARSET.find(x) for x in bech[pos+1:]] 82 | if not bech32_verify_checksum(hrp, data): 83 | return (None, None) 84 | return (hrp, data[:-6]) 85 | 86 | 87 | def convertbits(data, frombits, tobits, pad=True): 88 | """General power-of-2 base conversion.""" 89 | acc = 0 90 | bits = 0 91 | ret = [] 92 | maxv = (1 << tobits) - 1 93 | max_acc = (1 << (frombits + tobits - 1)) - 1 94 | for value in data: 95 | value = _bord(value) 96 | if value < 0 or (value >> frombits): 97 | return None 98 | acc = ((acc << frombits) | value) & max_acc 99 | bits += frombits 100 | while bits >= tobits: 101 | bits -= tobits 102 | ret.append((acc >> bits) & maxv) 103 | if pad: 104 | if bits: 105 | ret.append((acc << (tobits - bits)) & maxv) 106 | elif bits >= frombits or ((acc << (tobits - bits)) & maxv): 107 | return None 108 | return ret 109 | 110 | 111 | def decode(hrp, addr): 112 | """Decode a segwit address.""" 113 | hrpgot, data = bech32_decode(addr) 114 | if hrpgot != hrp: 115 | return (None, None) 116 | decoded = convertbits(data[1:], 5, 8, False) 117 | if decoded is None or len(decoded) < 2 or len(decoded) > 40: 118 | return (None, None) 119 | if data[0] > 16: 120 | return (None, None) 121 | if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32: 122 | return (None, None) 123 | return (data[0], decoded) 124 | 125 | 126 | def encode(hrp, witver, witprog): 127 | """Encode a segwit address.""" 128 | ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5)) 129 | if decode(hrp, ret) == (None, None): 130 | return None 131 | return ret 132 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python-bitcointx 2 | 3 | This Python3 library provides an easy interface to the bitcoin data 4 | structures. This is based on https://github.com/petertodd/python-bitcoinlib, 5 | but is focused only on providing the tools to build, manipulate and sign 6 | bitcoin transactions, and related data structures. Network-related code 7 | that deals with sending and receiving data from and to bitcoin nodes is removed. 8 | Bech32 segwit address support and RFC6979 signing with libsecp256k1 are added. 9 | 10 | "The Swiss Army Knife of the Bitcoin protocol." - Wladimir J. van der Laan 11 | 12 | 13 | ## Requirements 14 | 15 | sudo apt-get install libssl-dev 16 | 17 | The RPC interface, `bitcointx.rpc`, is designed to work with Bitcoin Core v0.16.0. 18 | Older versions may work but there do exist some incompatibilities. 19 | 20 | 21 | ## Structure 22 | 23 | Everything consensus critical is found in the modules under bitcointx.core. This 24 | rule is followed pretty strictly, for instance chain parameters are split into 25 | consensus critical and non-consensus-critical. 26 | 27 | bitcointx.core - Basic core definitions, datastructures, and 28 | (context-independent) validation 29 | bitcointx.core.key - ECC pubkeys 30 | bitcointx.core.script - Scripts and opcodes 31 | bitcointx.core.scripteval - Script evaluation/verification 32 | bitcointx.core.serialize - Serialization 33 | 34 | In the future the bitcointx.core may use the Satoshi sourcecode directly as a 35 | library. Non-consensus critical modules include the following: 36 | 37 | bitcointx - Chain selection 38 | bitcointx.base58 - Base58 encoding 39 | bitcointx.rpc - Bitcoin Core RPC interface support (may also be stripped later) 40 | bitcointx.wallet - Wallet-related code, currently Bitcoin address and 41 | private key support 42 | 43 | Effort has been made to follow the Satoshi source relatively closely, for 44 | instance Python code and classes that duplicate the functionality of 45 | corresponding Satoshi C++ code uses the same naming conventions: CTransaction, 46 | CBlockHeader, nValue etc. Otherwise Python naming conventions are followed. 47 | 48 | 49 | ## Mutable vs. Immutable objects 50 | 51 | Like the Bitcoin Core codebase CTransaction is immutable and 52 | CMutableTransaction is mutable; unlike the Bitcoin Core codebase this 53 | distinction also applies to COutPoint, CTxIn, CTxOut, and CBlock. 54 | 55 | 56 | ## Endianness Gotchas 57 | 58 | Rather confusingly Bitcoin Core shows transaction and block hashes as 59 | little-endian hex rather than the big-endian the rest of the world uses for 60 | SHA256. python-bitcointx provides the convenience functions x() and lx() in 61 | bitcointx.core to convert from big-endian and little-endian hex to raw bytes to 62 | accomodate this. In addition see b2x() and b2lx() for conversion from bytes to 63 | big/little-endian hex. 64 | 65 | 66 | ## Module import style 67 | 68 | While not always good style, it's often convenient for quick scripts if 69 | `import *` can be used. To support that all the modules have `__all__` defined 70 | appropriately. 71 | 72 | 73 | # Example Code 74 | 75 | See `examples/` directory. For instance this example creates a transaction 76 | spending a pay-to-script-hash transaction output: 77 | 78 | $ PYTHONPATH=. examples/spend-pay-to-script-hash-txout.py 79 | 80 | 81 | 82 | ## Selecting the chain to use 83 | 84 | Do the following: 85 | 86 | import bitcointx 87 | bitcointx.SelectParams(NAME) 88 | 89 | Where NAME is one of 'testnet', 'mainnet', or 'regtest'. The chain currently 90 | selected is a global variable that changes behavior everywhere, just like in 91 | the Satoshi codebase. 92 | 93 | ## Using libsecp256k1 for signing 94 | 95 | It is possible to use libsecp256k1 for signing, but it have to be enabled manually, at this time. 96 | 97 | The relevant functions are is_libsec256k1_available() and use_libsecp256k1_for_signing(do_use) 98 | 99 | NOTE: libsecp256k1 will likely become default and required for signing, 100 | and this functions will be removed then. 101 | 102 | refer to Test_RFC6979() in bitcointx/tests/test_wallet.py for example of usage. 103 | 104 | 105 | ## Unit tests 106 | 107 | Under bitcointx/tests using test data from Bitcoin Core. To run them: 108 | 109 | python -m unittest discover && python3 -m unittest discover 110 | 111 | Please run the tests on both Python2 and Python3 for your pull-reqs! 112 | 113 | Alternately, if Tox (see https://tox.readthedocs.org/) is available on your 114 | system, you can run unit tests for multiple Python versions: 115 | 116 | ./runtests.sh 117 | 118 | Currently, the following implementations are tried (any not installed are 119 | skipped): 120 | 121 | * CPython 3.3 122 | * CPython 3.4 123 | * CPython 3.5 124 | * PyPy 125 | * PyPy3 126 | 127 | HTML coverage reports can then be found in the htmlcov/ subdirectory. 128 | 129 | ## Documentation 130 | 131 | Sphinx documentation is in the "doc" subdirectory. Run "make help" from there 132 | to see how to build. You will need the Python "sphinx" package installed. 133 | 134 | Currently this is just API documentation generated from the code and 135 | docstrings. Higher level written docs would be useful, perhaps starting with 136 | much of this README. Pages are written in reStructuredText and linked from 137 | index.rst. 138 | -------------------------------------------------------------------------------- /bitcointx/tests/data/checkblock_valid.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["CheckBlock and CheckBlockHeader tests, valid blocks."], 3 | ["They are in the form"], 4 | ["[comment, fHeader, fCheckPow, cur_time, serialized_block/block_header],"], 5 | ["Objects that are only a single string (like this one) are ignored"], 6 | 7 | ["Genesis block", 8 | false, true, 1231006505, 9 | "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"], 10 | 11 | ["Genesis block with time set to two hours - 1 second behind", 12 | false, true, 1230999305, 13 | "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"], 14 | 15 | ["Block 99960, three transactions", 16 | false, true, 1293603080, 17 | "01000000e78b20013e6e9a21b6366ead5d866b2f9dc00664508b90f24da8000000000000f94b61259c7e9af3455b277275800d0d6a58b929eedf9e0153a6ef2278a5d53408d11a4d4c86041b0fbf10b00301000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0119ffffffff0100f2052a0100000043410427e729f9cb5564abf2a1ccda596c636b77bd4d9d91f657d4738f3c70fce8ac4e12b1c782905554d9ff2c2e050fdfe3ff93c91c5817e617877d51f450b528c9e4ac000000000100000001e853c9e0c133547fd9e162b1d3860dd0f27d5b9b8a7430d28896c00fbb3f1bc7000000008c49304602210095bcd54ebd0caa7cee75f0f89de472a765e6ef4b98c5fd4b32c7f9d4905db9ae022100ebd3f668e3a1a36d56e30184c27531dbb9fc136c84b1282be562064d86997d1e014104727eb4fdcc90658cd26abe7dcb0ae7297810b15b9e27c32bcf8e3edd934901968806dc18b1276d7273cc4c223feee0070361ed947888a3cef422bebfede96e08ffffffff020065cd1d000000001976a91468c6c2b3c0bc4a8eeb10d16a300d627a31a3b58588ac0008af2f000000001976a9141d87f0a54a1d704ffc70eae83b025698bc0fdcfc88ac00000000010000000125f582f1d37b6713b14b85665a2daea4f464f5ed1c3ab3d4dcf152fb61414b9e000000008a473044022066ec12ced31659e1bf961b542b58bba76ba8f2a1e8f36d5f60be0601598eac21022047ce33685a63283a4c3ebc390261191f215999b2f7d8e1504b8af39aae4a2881014104c5e1d713d10fe59cc48f60701a3efcac418969c22e9c6cf57440f71e44dc82837af5351bf3e1d898f06aa5c792bf0251a39902311d1d27c16847b1b414494f35ffffffff02404b4c00000000001976a91466a3b2e43cfa5c6d9b2f0095f7be5a5cb608478c88ac80b8dc3c030000001976a9146df5ed8cee34df5c05c90406761a11ed143c202d88ac00000000"], 18 | 19 | ["Block 99993, four transactions", 20 | false, true, 1293622397, 21 | "01000000acda3db591d5c2c63e8c09e7523a5b0581707ef3e3520d6ca180000000000000701179cb9a9e0fe709cc96261b6b943b31362b61dacba94b03f9b71a06cc2eff7d1c1b4d4c86041b75962f880401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0152ffffffff014034152a01000000434104216220ab283b5e2871c332de670d163fb1b7e509fd67db77997c5568e7c25afd988f19cd5cc5aec6430866ec64b5214826b28e0f7a86458073ff933994b47a5cac0000000001000000042a40ae58b06c3a61ae55dbee05cab546e80c508f71f24ef0cdc9749dac91ea5f000000004a49304602210089c685b37903c4aa62d984929afeaca554d1641f9a668398cd228fb54588f06b0221008a5cfbc5b0a38ba78c4f4341e53272b9cd0e377b2fb740106009b8d7fa693f0b01ffffffff7b999491e30af112b11105cb053bc3633a8a87f44740eb158849a76891ff228b00000000494830450221009a4aa8663ff4017063d2020519f2eade5b4e3e30be69bf9a62b4e6472d1747b2022021ee3b3090b8ce439dbf08a5df31e2dc23d68073ebda45dc573e8a4f74f5cdfc01ffffffffdea82ec2f9e88e0241faa676c13d093030b17c479770c6cc83239436a4327d49000000004a493046022100c29d9de71a34707c52578e355fa0fdc2bb69ce0a957e6b591658a02b1e039d69022100f82c8af79c166a822d305f0832fb800786d831aea419069b3aed97a6edf8f02101fffffffff3e7987da9981c2ae099f97a551783e1b21669ba0bf3aca8fe12896add91a11a0000000049483045022100e332c81781b281a3b35cf75a5a204a2be451746dad8147831255291ebac2604d02205f889a2935270d1bf1ef47db773d68c4d5c6a51bb51f082d3e1c491de63c345601ffffffff0100c817a8040000001976a91420420e56079150b50fb0617dce4c374bd61eccea88ac00000000010000000265a7293b2d69ba51d554cd32ac7586f7fbeaeea06835f26e03a2feab6aec375f000000004a493046022100922361eaafe316003087d355dd3c0ef3d9f44edae661c212a28a91e020408008022100c9b9c84d53d82c0ba9208f695c79eb42a453faea4d19706a8440e1d05e6cff7501fffffffff6971f00725d17c1c531088144b45ed795a307a22d51ca377c6f7f93675bb03a000000008b483045022100d060f2b2f4122edac61a25ea06396fe9135affdabc66d350b5ae1813bc6bf3f302205d8363deef2101fc9f3d528a8b3907e9d29c40772e587dcea12838c574cb80f801410449fce4a25c972a43a6bc67456407a0d4ced782d4cf8c0a35a130d5f65f0561e9f35198349a7c0b4ec79a15fead66bd7642f17cc8c40c5df95f15ac7190c76442ffffffff0200f2052a010000001976a914c3f537bc307c7eda43d86b55695e46047b770ea388ac00cf7b05000000001976a91407bef290008c089a60321b21b1df2d7f2202f40388ac0000000001000000014ab7418ecda2b2531eef0145d4644a4c82a7da1edd285d1aab1ec0595ac06b69000000008c493046022100a796490f89e0ef0326e8460edebff9161da19c36e00c7408608135f72ef0e03e0221009e01ef7bc17cddce8dfda1f1a6d3805c51f9ab2f8f2145793d8e85e0dd6e55300141043e6d26812f24a5a9485c9d40b8712215f0c3a37b0334d76b2c24fcafa587ae5258853b6f49ceeb29cd13ebb76aa79099fad84f516bbba47bd170576b121052f1ffffffff0200a24a04000000001976a9143542e17b6229a25d5b76909f9d28dd6ed9295b2088ac003fab01000000001976a9149cea2b6e3e64ad982c99ebba56a882b9e8a816fe88ac00000000"], 22 | 23 | ["FIXME: need test cases for PoW"], 24 | 25 | ["Make diffs cleaner by leaving a comment here without comma at the end"] 26 | ] 27 | -------------------------------------------------------------------------------- /doc/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. linkcheck to check all external links for integrity 37 | echo. doctest to run all doctests embedded in the documentation if enabled 38 | goto end 39 | ) 40 | 41 | if "%1" == "clean" ( 42 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 43 | del /q /s %BUILDDIR%\* 44 | goto end 45 | ) 46 | 47 | if "%1" == "html" ( 48 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 49 | if errorlevel 1 exit /b 1 50 | echo. 51 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 52 | goto end 53 | ) 54 | 55 | if "%1" == "dirhtml" ( 56 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 57 | if errorlevel 1 exit /b 1 58 | echo. 59 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 60 | goto end 61 | ) 62 | 63 | if "%1" == "singlehtml" ( 64 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 68 | goto end 69 | ) 70 | 71 | if "%1" == "pickle" ( 72 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished; now you can process the pickle files. 76 | goto end 77 | ) 78 | 79 | if "%1" == "json" ( 80 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished; now you can process the JSON files. 84 | goto end 85 | ) 86 | 87 | if "%1" == "htmlhelp" ( 88 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can run HTML Help Workshop with the ^ 92 | .hhp project file in %BUILDDIR%/htmlhelp. 93 | goto end 94 | ) 95 | 96 | if "%1" == "qthelp" ( 97 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 98 | if errorlevel 1 exit /b 1 99 | echo. 100 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 101 | .qhcp project file in %BUILDDIR%/qthelp, like this: 102 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\bitcoin.qhcp 103 | echo.To view the help file: 104 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\bitcoin.ghc 105 | goto end 106 | ) 107 | 108 | if "%1" == "devhelp" ( 109 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 110 | if errorlevel 1 exit /b 1 111 | echo. 112 | echo.Build finished. 113 | goto end 114 | ) 115 | 116 | if "%1" == "epub" ( 117 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 118 | if errorlevel 1 exit /b 1 119 | echo. 120 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 121 | goto end 122 | ) 123 | 124 | if "%1" == "latex" ( 125 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 129 | goto end 130 | ) 131 | 132 | if "%1" == "text" ( 133 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The text files are in %BUILDDIR%/text. 137 | goto end 138 | ) 139 | 140 | if "%1" == "man" ( 141 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 145 | goto end 146 | ) 147 | 148 | if "%1" == "texinfo" ( 149 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 150 | if errorlevel 1 exit /b 1 151 | echo. 152 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 153 | goto end 154 | ) 155 | 156 | if "%1" == "gettext" ( 157 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 158 | if errorlevel 1 exit /b 1 159 | echo. 160 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 161 | goto end 162 | ) 163 | 164 | if "%1" == "changes" ( 165 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 166 | if errorlevel 1 exit /b 1 167 | echo. 168 | echo.The overview file is in %BUILDDIR%/changes. 169 | goto end 170 | ) 171 | 172 | if "%1" == "linkcheck" ( 173 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 174 | if errorlevel 1 exit /b 1 175 | echo. 176 | echo.Link check complete; look for any errors in the above output ^ 177 | or in %BUILDDIR%/linkcheck/output.txt. 178 | goto end 179 | ) 180 | 181 | if "%1" == "doctest" ( 182 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 183 | if errorlevel 1 exit /b 1 184 | echo. 185 | echo.Testing of doctests in the sources finished, look at the ^ 186 | results in %BUILDDIR%/doctest/output.txt. 187 | goto end 188 | ) 189 | 190 | :end 191 | -------------------------------------------------------------------------------- /bitcointx/tests/test_serialize.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013-2014 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | from __future__ import absolute_import, division, print_function, unicode_literals 13 | 14 | import unittest, random 15 | 16 | from binascii import unhexlify 17 | 18 | from bitcointx.core.serialize import * 19 | 20 | class Test_Serializable(unittest.TestCase): 21 | def test_extra_data(self): 22 | """Serializable.deserialize() fails if extra data is present""" 23 | 24 | class FooSerializable(Serializable): 25 | @classmethod 26 | def stream_deserialize(cls, f): 27 | return cls() 28 | 29 | def stream_serialize(self, f): 30 | pass 31 | 32 | try: 33 | FooSerializable.deserialize(b'\x00') 34 | except DeserializationExtraDataError as err: 35 | self.assertEqual(err.obj, FooSerializable()) 36 | self.assertEqual(err.padding, b'\x00') 37 | 38 | else: 39 | self.fail("DeserializationExtraDataError not raised") 40 | 41 | FooSerializable.deserialize(b'\x00', allow_padding=True) 42 | 43 | class Test_VarIntSerializer(unittest.TestCase): 44 | def test(self): 45 | def T(value, expected): 46 | expected = unhexlify(expected) 47 | actual = VarIntSerializer.serialize(value) 48 | self.assertEqual(actual, expected) 49 | roundtrip = VarIntSerializer.deserialize(actual) 50 | self.assertEqual(value, roundtrip) 51 | T(0x0, b'00') 52 | T(0xfc, b'fc') 53 | T(0xfd, b'fdfd00') 54 | T(0xffff, b'fdffff') 55 | T(0x10000, b'fe00000100') 56 | T(0xffffffff, b'feffffffff') 57 | T(0x100000000, b'ff0000000001000000') 58 | T(0xffffffffffffffff, b'ffffffffffffffffff') 59 | 60 | def test_non_optimal(self): 61 | def T(serialized, expected_value): 62 | serialized = unhexlify(serialized) 63 | actual_value = VarIntSerializer.deserialize(serialized) 64 | self.assertEqual(actual_value, expected_value) 65 | T(b'fd0000', 0) 66 | T(b'fd3412', 0x1234) 67 | T(b'fe00000000', 0) 68 | T(b'fe67452301', 0x1234567) 69 | T(b'ff0000000000000000', 0) 70 | T(b'ffefcdab8967452301', 0x123456789abcdef) 71 | 72 | def test_truncated(self): 73 | def T(serialized): 74 | serialized = unhexlify(serialized) 75 | with self.assertRaises(SerializationTruncationError): 76 | VarIntSerializer.deserialize(serialized) 77 | T(b'') 78 | T(b'fd') 79 | T(b'fd00') 80 | T(b'fe') 81 | T(b'fe00') 82 | T(b'fe0000') 83 | T(b'fe000000') 84 | T(b'ff') 85 | T(b'ff00000000000000') 86 | 87 | class Test_BytesSerializer(unittest.TestCase): 88 | def test(self): 89 | def T(value, expected): 90 | value = unhexlify(value) 91 | expected = unhexlify(expected) 92 | actual = BytesSerializer.serialize(value) 93 | self.assertEqual(actual, expected) 94 | roundtrip = BytesSerializer.deserialize(actual) 95 | self.assertEqual(value, roundtrip) 96 | T(b'', b'00') 97 | T(b'00', b'0100') 98 | T(b'00'*0xffff, b'fdffff' + b'00'*0xffff) 99 | 100 | def test_truncated(self): 101 | def T(serialized, ex_cls=SerializationTruncationError): 102 | serialized = unhexlify(serialized) 103 | with self.assertRaises(ex_cls): 104 | BytesSerializer.deserialize(serialized) 105 | T(b'') 106 | T(b'01') 107 | T(b'0200') 108 | T(b'ff00000000000000ff11223344', SerializationError) # > max_size 109 | 110 | class Test_Compact(unittest.TestCase): 111 | def test_from_compact_zero(self): 112 | self.assertEqual(uint256_from_compact(0x00123456), 0) 113 | self.assertEqual(uint256_from_compact(0x01003456), 0) 114 | self.assertEqual(uint256_from_compact(0x02000056), 0) 115 | self.assertEqual(uint256_from_compact(0x03000000), 0) 116 | self.assertEqual(uint256_from_compact(0x04000000), 0) 117 | self.assertEqual(uint256_from_compact(0x00923456), 0) 118 | def test_from_compact_negative_zero(self): 119 | # Negative bit isn't supported yet 120 | # self.assertEqual(uint256_from_compact(0x01803456), 0) 121 | # self.assertEqual(uint256_from_compact(0x02800056), 0) 122 | # self.assertEqual(uint256_from_compact(0x03800000), 0) 123 | # self.assertEqual(uint256_from_compact(0x04800000), 0) 124 | return 125 | 126 | def test_twelve(self): 127 | self.assertEqual(uint256_from_compact(0x01123456), 0x0012) 128 | self.assertEqual(compact_from_uint256(0x0012), 0x01120000) 129 | 130 | def test_from_uint256(self): 131 | self.assertEqual(compact_from_uint256(0x1234), 0x02123400) 132 | self.assertEqual(compact_from_uint256(0x123456), 0x03123456) 133 | self.assertEqual(compact_from_uint256(0x12345600), 0x04123456) 134 | self.assertEqual(compact_from_uint256(0x92340000), 0x05009234) 135 | self.assertEqual(compact_from_uint256(0x1234560000000000000000000000000000000000000000000000000000000000), 0x20123456) 136 | 137 | class Test_Uint256_Serialize(unittest.TestCase): 138 | def test_fixed(self): 139 | values = [] 140 | values.append(0) 141 | values.append(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) 142 | for x in range(100): 143 | values.append(random.getrandbits(256)) 144 | for n in values: 145 | assert(uint256_from_str(uint256_to_str(n)) == n) 146 | 147 | -------------------------------------------------------------------------------- /bitcointx/tests/test_transactions.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013-2014 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | from __future__ import absolute_import, division, print_function, unicode_literals 13 | 14 | import json 15 | import unittest 16 | import os 17 | 18 | from bitcointx.core import * 19 | from bitcointx.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH 20 | 21 | from bitcointx.tests.test_scripteval import parse_script 22 | 23 | def load_test_vectors(name): 24 | with open(os.path.dirname(__file__) + '/data/' + name, 'r') as fd: 25 | for test_case in json.load(fd): 26 | # Comments designated by single length strings 27 | if len(test_case) == 1: 28 | continue 29 | assert len(test_case) == 3 30 | 31 | prevouts = {} 32 | for json_prevout in test_case[0]: 33 | assert len(json_prevout) == 3 34 | n = json_prevout[1] 35 | if n == -1: 36 | n = 0xffffffff 37 | prevout = COutPoint(lx(json_prevout[0]), n) 38 | prevouts[prevout] = parse_script(json_prevout[2]) 39 | 40 | tx = CTransaction.deserialize(x(test_case[1])) 41 | enforceP2SH = test_case[2] 42 | 43 | yield (prevouts, tx, enforceP2SH) 44 | 45 | class Test_COutPoint(unittest.TestCase): 46 | def test_is_null(self): 47 | self.assertTrue(COutPoint().is_null()) 48 | self.assertTrue(COutPoint(hash=b'\x00'*32,n=0xffffffff).is_null()) 49 | self.assertFalse(COutPoint(hash=b'\x00'*31 + b'\x01').is_null()) 50 | self.assertFalse(COutPoint(n=1).is_null()) 51 | 52 | def test_repr(self): 53 | def T(outpoint, expected): 54 | actual = repr(outpoint) 55 | self.assertEqual(actual, expected) 56 | T( COutPoint(), 57 | 'COutPoint()') 58 | T( COutPoint(lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'), 0), 59 | "COutPoint(lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'), 0)") 60 | 61 | def test_str(self): 62 | def T(outpoint, expected): 63 | actual = str(outpoint) 64 | self.assertEqual(actual, expected) 65 | T(COutPoint(), 66 | '0000000000000000000000000000000000000000000000000000000000000000:4294967295') 67 | T(COutPoint(lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'), 0), 68 | '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0') 69 | T(COutPoint(lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'), 10), 70 | '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:10') 71 | 72 | class Test_CMutableOutPoint(unittest.TestCase): 73 | def test_GetHash(self): 74 | """CMutableOutPoint.GetHash() is not cached""" 75 | outpoint = CMutableOutPoint() 76 | 77 | h1 = outpoint.GetHash() 78 | outpoint.n = 1 79 | 80 | self.assertNotEqual(h1, outpoint.GetHash()) 81 | 82 | 83 | class Test_CTxIn(unittest.TestCase): 84 | def test_is_final(self): 85 | self.assertTrue(CTxIn().is_final()) 86 | self.assertTrue(CTxIn(nSequence=0xffffffff).is_final()) 87 | self.assertFalse(CTxIn(nSequence=0).is_final()) 88 | 89 | def test_repr(self): 90 | def T(txin, expected): 91 | actual = repr(txin) 92 | self.assertEqual(actual, expected) 93 | T( CTxIn(), 94 | 'CTxIn(COutPoint(), CScript([]), 0xffffffff)') 95 | 96 | class Test_CMutableTxIn(unittest.TestCase): 97 | def test_GetHash(self): 98 | """CMutableTxIn.GetHash() is not cached""" 99 | txin = CMutableTxIn() 100 | 101 | h1 = txin.GetHash() 102 | txin.prevout.n = 1 103 | 104 | self.assertNotEqual(h1, txin.GetHash()) 105 | 106 | class Test_CTransaction(unittest.TestCase): 107 | def test_is_coinbase(self): 108 | tx = CMutableTransaction() 109 | self.assertFalse(tx.is_coinbase()) 110 | 111 | tx.vin.append(CMutableTxIn()) 112 | 113 | # IsCoinBase() in reference client doesn't check if vout is empty 114 | self.assertTrue(tx.is_coinbase()) 115 | 116 | tx.vin[0].prevout.n = 0 117 | self.assertFalse(tx.is_coinbase()) 118 | 119 | tx.vin[0] = CTxIn() 120 | tx.vin.append(CTxIn()) 121 | self.assertFalse(tx.is_coinbase()) 122 | 123 | def test_tx_valid(self): 124 | for prevouts, tx, enforceP2SH in load_test_vectors('tx_valid.json'): 125 | try: 126 | CheckTransaction(tx) 127 | except CheckTransactionError: 128 | self.fail('tx failed CheckTransaction(): ' \ 129 | + str((prevouts, b2x(tx.serialize()), enforceP2SH))) 130 | continue 131 | 132 | for i in range(len(tx.vin)): 133 | flags = set() 134 | if enforceP2SH: 135 | flags.add(SCRIPT_VERIFY_P2SH) 136 | 137 | VerifyScript(tx.vin[i].scriptSig, prevouts[tx.vin[i].prevout], tx, i, flags=flags) 138 | 139 | 140 | def test_tx_invalid(self): 141 | for prevouts, tx, enforceP2SH in load_test_vectors('tx_invalid.json'): 142 | try: 143 | CheckTransaction(tx) 144 | except CheckTransactionError: 145 | continue 146 | 147 | with self.assertRaises(ValidationError): 148 | for i in range(len(tx.vin)): 149 | flags = set() 150 | if enforceP2SH: 151 | flags.add(SCRIPT_VERIFY_P2SH) 152 | 153 | VerifyScript(tx.vin[i].scriptSig, prevouts[tx.vin[i].prevout], tx, i, flags=flags) 154 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 16 | 17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 18 | 19 | help: 20 | @echo "Please use \`make ' where is one of" 21 | @echo " html to make standalone HTML files" 22 | @echo " dirhtml to make HTML files named index.html in directories" 23 | @echo " singlehtml to make a single large HTML file" 24 | @echo " pickle to make pickle files" 25 | @echo " json to make JSON files" 26 | @echo " htmlhelp to make HTML files and a HTML help project" 27 | @echo " qthelp to make HTML files and a qthelp project" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 31 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 32 | @echo " text to make text files" 33 | @echo " man to make manual pages" 34 | @echo " texinfo to make Texinfo files" 35 | @echo " info to make Texinfo files and run them through makeinfo" 36 | @echo " gettext to make PO message catalogs" 37 | @echo " changes to make an overview of all changed/added/deprecated items" 38 | @echo " linkcheck to check all external links for integrity" 39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 40 | 41 | clean: 42 | -rm -rf $(BUILDDIR)/* 43 | 44 | html: 45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 48 | 49 | dirhtml: 50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 53 | 54 | singlehtml: 55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 56 | @echo 57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 58 | 59 | pickle: 60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 61 | @echo 62 | @echo "Build finished; now you can process the pickle files." 63 | 64 | json: 65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 66 | @echo 67 | @echo "Build finished; now you can process the JSON files." 68 | 69 | htmlhelp: 70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 71 | @echo 72 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 73 | ".hhp project file in $(BUILDDIR)/htmlhelp." 74 | 75 | qthelp: 76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 77 | @echo 78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/bitcoin.qhcp" 81 | @echo "To view the help file:" 82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/bitcoin.qhc" 83 | 84 | devhelp: 85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 86 | @echo 87 | @echo "Build finished." 88 | @echo "To view the help file:" 89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/bitcoin" 90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/bitcoin" 91 | @echo "# devhelp" 92 | 93 | epub: 94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 95 | @echo 96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 97 | 98 | latex: 99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 100 | @echo 101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 103 | "(use \`make latexpdf' here to do that automatically)." 104 | 105 | latexpdf: 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | text: 112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 113 | @echo 114 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 115 | 116 | man: 117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 118 | @echo 119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 120 | 121 | texinfo: 122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 123 | @echo 124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 125 | @echo "Run \`make' in that directory to run these through makeinfo" \ 126 | "(use \`make info' here to do that automatically)." 127 | 128 | info: 129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 130 | @echo "Running Texinfo files through makeinfo..." 131 | make -C $(BUILDDIR)/texinfo info 132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 133 | 134 | gettext: 135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 136 | @echo 137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 138 | 139 | changes: 140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 141 | @echo 142 | @echo "The overview file is in $(BUILDDIR)/changes." 143 | 144 | linkcheck: 145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 146 | @echo 147 | @echo "Link check complete; look for any errors in the above output " \ 148 | "or in $(BUILDDIR)/linkcheck/output.txt." 149 | 150 | doctest: 151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 152 | @echo "Testing of doctests in the sources finished, look at the " \ 153 | "results in $(BUILDDIR)/doctest/output.txt." 154 | -------------------------------------------------------------------------------- /bitcointx/tests/data/checkblock_invalid.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["CheckBlock and CheckBlockHeader tests, valid blocks."], 3 | ["They are in the form"], 4 | ["[comment, fHeader, fCheckPow, cur_time, serialized_block/block_header],"], 5 | ["Objects that are only a single string (like this one) are ignored"], 6 | 7 | ["Genesis block with time set to two hours + 1 second behind", 8 | false, true, 1230999304, 9 | "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"], 10 | 11 | ["Genesis with one byte changed", 12 | false, true, 1231006505, 13 | "1100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"], 14 | 15 | ["Empty vtx", 16 | false, false, 0, 17 | "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a00000000ffff001d1dac2b7c00"], 18 | 19 | ["One tx, but not a coinbase", 20 | false, false, 1231731025, 21 | "0100000055bd840a78798ad0da853f68974f3d183e2bd1db6a842c1feecf222a00000000169e1e83e930853391bc6f35f605c6754cfead57cf8387639d3b4096c54f18f451b96a49ffff001d283e9e70010100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704000000004847304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000"], 22 | 23 | ["More than one coinbase (different coinbases)", 24 | false, false, 1231731025, 25 | "0100000055bd840a78798ad0da853f68974f3d183e2bd1db6a842c1feecf222a00000000622d1f9da7a372968847d7ecfc3892c51ccc08297762c4a767b51cdd6628d2cf51b96a49ffff001d283e9e700201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0102ffffffff0100f2052a01000000434104d46c4968bde02899d2aa0963367c7a6ce34eec332b32e42e5f3407e052d64ac625da6f0718e7b302140434bd725706957c092db53805b821a85b23a7ac61725bac0000000001000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0102ffffffff0100f2052a01000000434104d46c4968bde02899d2aa0963367c7a6ce34eec332b32e42e5f3407e052d64ac625da6f0718e7b302140434bd725706957c092db53805b821a85b23a7ac61725bac01000000"], 26 | 27 | ["Duplicate transaction", 28 | false, false, 1231731025, 29 | "0100000055bd840a78798ad0da853f68974f3d183e2bd1db6a842c1feecf222a00000000ff104ccb05421ab93e63f8c3ce5c2c2e9dbb37de2764b3a3175c8166562cac7d51b96a49ffff001d283e9e700301000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0102ffffffff0100f2052a01000000434104d46c4968bde02899d2aa0963367c7a6ce34eec332b32e42e5f3407e052d64ac625da6f0718e7b302140434bd725706957c092db53805b821a85b23a7ac61725bac000000000100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704000000004847304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac000000000100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704000000004847304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000"], 30 | 31 | ["Merkle root mismatch", 32 | false, false, 1293603080, 33 | "01000000e78b20013e6e9a21b6366ead5d866b2f9dc00664508b90f24da8000000000000f94b61259c7e9af3455b277275800d0d6a58b929eedf9e0153a6ef2278a5d53408d11a4d4c86041b0fbf10b00301000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0119ffffffff0100f2052a0100000043410427e729f9cb5564abf2a1ccda596c636b77bd4d9d91f657d4738f3c70fce8ac4e12b1c782905554d9ff2c2e050fdfe3ff93c91c5817e617877d51f450b528c9e4ac000000000100000001e853c9e0c133547fd9e162b1d3860dd0f27d5b9b8a7430d28896c00fbb3f1bc7000000008c49304602210095bcd54ebd0caa7cee75f0f89de472a765e6ef4b98c5fd4b32c7f9d4905db9ae022100ebd3f668e3a1a36d56e30184c27531dbb9fc136c84b1282be562064d86997d1e014104727eb4fdcc90658cd26abe7dcb0ae7297810b15b9e27c32bcf8e3edd934901968806dc18b1276d7273cc4c223feee0070361ed947888a3cef422bebfede96e08ffffffff020065cd1d000000001976a91468c6c2b3c0bc4a8eeb10d16a300d627a31a3b58588ac0008af2f000000001976a9141d87f0a54a1d704ffc70eae83b025698bc0fdcfc88ac00000000010000000125f582f1d37b6713b14b85665a2daea4f464f5ed1c3ab3d4dcf152fb61414b9e000000008a473044022066ec12ced31659e1bf961b542b58bba76ba8f2a1e8f36d5f60be0601598eac21022047ce33685a63283a4c3ebc390261191f215999b2f7d8e1504b8af39aae4a2881014104c5e1d713d10fe59cc48f60701a3efcac418969c22e9c6cf57440f71e44dc82837af5351bf3e1d898f06aa5c792bf0251a39902311d1d27c16847b1b414494f35ffffffff02404b4c00000000001976a91466a3b2e43cfa5c6d9b2f0095f7be5a5cb608478c88ac80b8dc3c030000001976a9146df5ed8cee34df5c05c90406761a11ed143c202d88ac01000000"], 34 | 35 | ["FIXME: need to test sigops count, among other things"], 36 | 37 | ["Make diffs cleaner by leaving a comment here without comma at the end"] 38 | ] 39 | -------------------------------------------------------------------------------- /release-notes.md: -------------------------------------------------------------------------------- 1 | # python-bitcointx release notes 2 | # To be added at release 3 | 4 | # python-bitcointx is based on python-bitcoinlib 5 | # as of commit 05cbb3c9560b36cfe71bac06085a231a6244e13a 6 | # 2018-04-26 06:46:09 7 | # therefore we include 8 | # python-bitcoinlib release notes 9 | 10 | ## v0.10.1 11 | 12 | Identical in every way to v0.10.0, but re-uploaded under a new version to fix a 13 | PyPi issue. 14 | 15 | 16 | ## v0.10.0 17 | 18 | Minor breaking change: RPC port for regtest updated to the new v0.16.0 default. 19 | 20 | Other changes: 21 | 22 | * Now looks for `.cookie` files in the datadir, if specified. 23 | * Authentication in a RPC `service_url` is now parsed. 24 | * Implemented bip-0037 version message. 25 | * `contrib/verify-commits/` removed for now due to breakage. 26 | 27 | 28 | ## v0.9.0 29 | 30 | Now supports segwit, which breaks the API in minor ways from v0.8.0. This 31 | version introduces lots of new API functionality related to this, such as the 32 | new `CScriptWitness`, `CTxInWitness`, `CTxWitness`, new sigwit-specific logic 33 | in `SignatureHash()` etc. 34 | 35 | 36 | ## v0.8.0 37 | 38 | Major breaking API change! 39 | 40 | While this interim release doesn't by itself include segwit support, it does 41 | change the name of the `CTransaction/CMutableTransaction` method `GetHash()` to 42 | `GetTxid()` to prepare for a future segwit-enabled release. Incorrect calls to 43 | `GetHash()` will now raise a `AttributeError` exception with an explanation. 44 | 45 | Since this release doesn't yet include segwit support, you will need to set the 46 | Bitcoin Core `-rpcserialversion=0` option, either as a command line argument, 47 | or in your `bitcoin.conf` file. Otherwise the RPC interface will return 48 | segwit-serialized transactions that this release's RPC support doesn't 49 | understand. 50 | 51 | Other changes: 52 | 53 | * Cookie file RPC authentication is now supported. 54 | * `msg_header` now correctly uses `CBlockHeader` rather than `CBlock`. 55 | * RPC `getbalance` now supports `include_watchonly` 56 | * RPC `unlockwallet` is now supported 57 | 58 | 59 | ## v0.7.0 60 | 61 | Breaking API changes: 62 | 63 | * The 'cooked' CScript iterator now returns `OP_0` for the empty binary string 64 | rather than `b''` 65 | 66 | * The alias `JSONRPCException = JSONRPCError` has been removed. This alias was 67 | added for compatibility with v0.4.0 of python-bitcoinlib. 68 | 69 | * Where appropriate, `RPC_INVALID_ADDRESS_OR_KEY` errors are now caught 70 | properly, which means that rather than raising `IndexError`, RPC commands 71 | such as `getblock` may raise `JSONRPCError` instead. For instance during 72 | initial startup previously python-bitcoinlib would incorrectly raise 73 | `IndexError` rather than letting the callee know that RPC was unusable. Along 74 | those lines, `JSONRPCError` subclasses have been added for some (but not 75 | all!) of the types of RPC errors Bitcoin Core returns. 76 | 77 | Bugfixes: 78 | 79 | * Fixed a spurious `AttributeError` when `bitcoin.rpc.Proxy()` fails. 80 | 81 | 82 | ## v0.6.1 83 | 84 | New features: 85 | 86 | * getblockheader RPC call now supports the verbose option; there's no other way 87 | to get the block height, among other things, from the RPC interface. 88 | * subtoaddress and sendmany RPC calls now support comment and 89 | subtractfeefromamount arguments. 90 | 91 | 92 | ## v0.6.0 93 | 94 | Breaking API changes: 95 | 96 | * RPC over SSL support removed to match Bitcoin Core's removal of RPC SSL 97 | support in v0.12.0 If you need this, use an alternative such as a stunnel or 98 | a SSH tunnel. 99 | 100 | * Removed SCRIPT_VERIFY constants ``bitcoin.core.script``, leaving just the 101 | constants in ``bitcoin.core.scripteval``; being singletons the redundant 102 | constants were broken anyway. 103 | 104 | * SCRIPT_VERIFY_EVEN_S renamed to SCRIPT_VERIFY_LOW_S to match Bitcoin Core's naming 105 | 106 | * SCRIPT_VERIFY_NOCACHE removed as Bitcoin Core no longer has it (and we never 107 | did anything with it anyway) 108 | 109 | 110 | ## v0.5.1 111 | 112 | Various small bugfixes; see git history. 113 | 114 | New features: 115 | 116 | * New RPC calls: fundrawtransaction, generate, getblockheader 117 | * OP_CHECKLOCKTIMEVERIFY opcode constant 118 | 119 | 120 | ## v0.5.0 121 | 122 | Major fix: Fixed OpenSSL related crashes on OSX and Arch Linux. Big thanks to 123 | everyone who helped fix this! 124 | 125 | Breaking API changes: 126 | 127 | * Proxy no longer has ``__getattr__`` to support arbitrary methods. Use 128 | RawProxy or Proxy.call instead. This allows new wrappers to be added safely. 129 | See docstrings for details. 130 | 131 | New features: 132 | 133 | * New RPC calls: getbestblockhash, getblockcount, getmininginfo 134 | * Signing and verification of Bitcoin Core compatible messages. (w/ pubkey recovery) 135 | * Tox tests 136 | * Sphinx docs 137 | 138 | Notable bugfixes: 139 | 140 | * getinfo() now works where disablewallet=1 141 | 142 | 143 | ## v0.4.0 144 | 145 | Major fix: OpenSSL 1.0.1k rejects non-canonical DER signatures, which Bitcoin 146 | Core does not, so we now canonicalize signatures prior to passing them to 147 | OpenSSL. Secondly we now only generate low-S DER signatures as per BIP62. 148 | 149 | API changes that might break compatibility with existing code: 150 | 151 | * MAX_MONEY is now a core chain parameter 152 | * MainParams now inherits from CoreMainParams rather than CoreChainParams 153 | * str() now returns hash:n format; previously was same as repr() 154 | * RawProxy() no longer has _connection parameter 155 | 156 | Notable bugfixes: 157 | 158 | * MsgSerializable.to_bytes() no longer clobbers testnet params 159 | * HTTPS RPC connections now use port 443 as default 160 | * No longer assumes bitcoin.conf specifes rpcuser 161 | 162 | New features: 163 | 164 | * New RPC calls: dumpprivkey, importaddress 165 | * Added P2P support for msg_notfound and msg_reject 166 | * Added support for IPv6 addr messages 167 | 168 | 169 | ## v0.3.0 170 | 171 | Major change: cleaned up what symbols are exported by modules. \_\_all\_\_ is now 172 | used extensively, which may break some applications that were not importing the 173 | right modules. Along those lines some implementation details like the ssl 174 | attribute of the bitcoin.core.key module, and the entire bitcoin.core.bignum 175 | module, are no longer part of the public API. This should not affect too many 176 | users, but it will break some code. 177 | 178 | Other notable changes: 179 | 180 | * New getreceivedbyaddress RPC call. 181 | * Fixed getbalance RPC call when wallet is configured off. 182 | * Various code cleanups and minor bug fixes. 183 | 184 | 185 | ## v0.2.1 186 | 187 | * Improve bitcoin address handling. P2SH and P2PKH addresses now get their own 188 | classes - P2SHBitcoinAddress and P2PKHBitcoinAddress respectively - and P2PKH 189 | can now convert scriptPubKeys containing non-canonical pushes as well as bare 190 | checksig to addresses. 191 | * .deserialize() methods now fail if there is extra data left over. 192 | * Various other small bugfixes. 193 | * License is now LGPL v3 or later. 194 | 195 | 196 | ## v0.2.0 197 | 198 | Major change: CTransaction, CBlock, etc. now come in immutable (default) and 199 | mutable forms. In most cases mutable and immutable can be used interchangeably; 200 | when that is not possible methods are provided to create new (im)mutable 201 | objects from (im)mutable ones efficiently. 202 | 203 | Other changes: 204 | 205 | * New BIP70 payment protocol example. (Derren Desouza) 206 | * Rework of message serialization. Note that this may not represent the final 207 | form of P2P support, which is still in flux. (Florian Schmaus) 208 | * Various bugfixes 209 | 210 | Finally starting this release, git tags will be of the form 211 | 'python-bitcoinlib-(version)', replacing the less specific '(version)' form 212 | previously used. 213 | 214 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | python-bitcoinlib is free software: you can redistribute it and/or modify it 2 | under the terms of the GNU Lesser General Public License as published by the 3 | Free Software Foundation, either version 3 of the License, or (at your option) 4 | any later version. 5 | 6 | python-bitcoinlib is distributed in the hope that it will be useful, but 7 | WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 8 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 9 | below for more details. 10 | 11 | 12 | 13 | GNU LESSER GENERAL PUBLIC LICENSE 14 | Version 3, 29 June 2007 15 | 16 | Copyright (C) 2007 Free Software Foundation, Inc. 17 | Everyone is permitted to copy and distribute verbatim copies 18 | of this license document, but changing it is not allowed. 19 | 20 | 21 | This version of the GNU Lesser General Public License incorporates 22 | the terms and conditions of version 3 of the GNU General Public 23 | License, supplemented by the additional permissions listed below. 24 | 25 | 0. Additional Definitions. 26 | 27 | As used herein, "this License" refers to version 3 of the GNU Lesser 28 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 29 | General Public License. 30 | 31 | "The Library" refers to a covered work governed by this License, 32 | other than an Application or a Combined Work as defined below. 33 | 34 | An "Application" is any work that makes use of an interface provided 35 | by the Library, but which is not otherwise based on the Library. 36 | Defining a subclass of a class defined by the Library is deemed a mode 37 | of using an interface provided by the Library. 38 | 39 | A "Combined Work" is a work produced by combining or linking an 40 | Application with the Library. The particular version of the Library 41 | with which the Combined Work was made is also called the "Linked 42 | Version". 43 | 44 | The "Minimal Corresponding Source" for a Combined Work means the 45 | Corresponding Source for the Combined Work, excluding any source code 46 | for portions of the Combined Work that, considered in isolation, are 47 | based on the Application, and not on the Linked Version. 48 | 49 | The "Corresponding Application Code" for a Combined Work means the 50 | object code and/or source code for the Application, including any data 51 | and utility programs needed for reproducing the Combined Work from the 52 | Application, but excluding the System Libraries of the Combined Work. 53 | 54 | 1. Exception to Section 3 of the GNU GPL. 55 | 56 | You may convey a covered work under sections 3 and 4 of this License 57 | without being bound by section 3 of the GNU GPL. 58 | 59 | 2. Conveying Modified Versions. 60 | 61 | If you modify a copy of the Library, and, in your modifications, a 62 | facility refers to a function or data to be supplied by an Application 63 | that uses the facility (other than as an argument passed when the 64 | facility is invoked), then you may convey a copy of the modified 65 | version: 66 | 67 | a) under this License, provided that you make a good faith effort to 68 | ensure that, in the event an Application does not supply the 69 | function or data, the facility still operates, and performs 70 | whatever part of its purpose remains meaningful, or 71 | 72 | b) under the GNU GPL, with none of the additional permissions of 73 | this License applicable to that copy. 74 | 75 | 3. Object Code Incorporating Material from Library Header Files. 76 | 77 | The object code form of an Application may incorporate material from 78 | a header file that is part of the Library. You may convey such object 79 | code under terms of your choice, provided that, if the incorporated 80 | material is not limited to numerical parameters, data structure 81 | layouts and accessors, or small macros, inline functions and templates 82 | (ten or fewer lines in length), you do both of the following: 83 | 84 | a) Give prominent notice with each copy of the object code that the 85 | Library is used in it and that the Library and its use are 86 | covered by this License. 87 | 88 | b) Accompany the object code with a copy of the GNU GPL and this license 89 | document. 90 | 91 | 4. Combined Works. 92 | 93 | You may convey a Combined Work under terms of your choice that, 94 | taken together, effectively do not restrict modification of the 95 | portions of the Library contained in the Combined Work and reverse 96 | engineering for debugging such modifications, if you also do each of 97 | the following: 98 | 99 | a) Give prominent notice with each copy of the Combined Work that 100 | the Library is used in it and that the Library and its use are 101 | covered by this License. 102 | 103 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 104 | document. 105 | 106 | c) For a Combined Work that displays copyright notices during 107 | execution, include the copyright notice for the Library among 108 | these notices, as well as a reference directing the user to the 109 | copies of the GNU GPL and this license document. 110 | 111 | d) Do one of the following: 112 | 113 | 0) Convey the Minimal Corresponding Source under the terms of this 114 | License, and the Corresponding Application Code in a form 115 | suitable for, and under terms that permit, the user to 116 | recombine or relink the Application with a modified version of 117 | the Linked Version to produce a modified Combined Work, in the 118 | manner specified by section 6 of the GNU GPL for conveying 119 | Corresponding Source. 120 | 121 | 1) Use a suitable shared library mechanism for linking with the 122 | Library. A suitable mechanism is one that (a) uses at run time 123 | a copy of the Library already present on the user's computer 124 | system, and (b) will operate properly with a modified version 125 | of the Library that is interface-compatible with the Linked 126 | Version. 127 | 128 | e) Provide Installation Information, but only if you would otherwise 129 | be required to provide such information under section 6 of the 130 | GNU GPL, and only to the extent that such information is 131 | necessary to install and execute a modified version of the 132 | Combined Work produced by recombining or relinking the 133 | Application with a modified version of the Linked Version. (If 134 | you use option 4d0, the Installation Information must accompany 135 | the Minimal Corresponding Source and Corresponding Application 136 | Code. If you use option 4d1, you must provide the Installation 137 | Information in the manner specified by section 6 of the GNU GPL 138 | for conveying Corresponding Source.) 139 | 140 | 5. Combined Libraries. 141 | 142 | You may place library facilities that are a work based on the 143 | Library side by side in a single library together with other library 144 | facilities that are not Applications and are not covered by this 145 | License, and convey such a combined library under terms of your 146 | choice, if you do both of the following: 147 | 148 | a) Accompany the combined library with a copy of the same work based 149 | on the Library, uncombined with any other library facilities, 150 | conveyed under the terms of this License. 151 | 152 | b) Give prominent notice with the combined library that part of it 153 | is a work based on the Library, and explaining where to find the 154 | accompanying uncombined form of the same work. 155 | 156 | 6. Revised Versions of the GNU Lesser General Public License. 157 | 158 | The Free Software Foundation may publish revised and/or new versions 159 | of the GNU Lesser General Public License from time to time. Such new 160 | versions will be similar in spirit to the present version, but may 161 | differ in detail to address new problems or concerns. 162 | 163 | Each version is given a distinguishing version number. If the 164 | Library as you received it specifies that a certain numbered version 165 | of the GNU Lesser General Public License "or any later version" 166 | applies to it, you have the option of following the terms and 167 | conditions either of that published version or of any later version 168 | published by the Free Software Foundation. If the Library as you 169 | received it does not specify a version number of the GNU Lesser 170 | General Public License, you may choose any version of the GNU Lesser 171 | General Public License ever published by the Free Software Foundation. 172 | 173 | If the Library as you received it specifies that a proxy can decide 174 | whether future versions of the GNU Lesser General Public License shall 175 | apply, that proxy's public statement of acceptance of any version is 176 | permanent authorization for you to choose that version for the 177 | Library. 178 | -------------------------------------------------------------------------------- /examples/publish-text.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Copyright (C) 2015 Peter Todd 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | # WARNING: Do not run this on a wallet with a non-trivial amount of BTC. This 24 | # utility has had very little testing and is being published as a 25 | # proof-of-concept only. 26 | 27 | # Requires python-bitcoinlib w/ sendmany support: 28 | # 29 | # https://github.com/petertodd/python-bitcoinlib/commit/6a0a2b9429edea318bea7b65a68a950cae536790 30 | 31 | import sys 32 | if sys.version_info.major < 3: 33 | sys.stderr.write('Sorry, Python 3.x required by this example.\n') 34 | sys.exit(1) 35 | 36 | import argparse 37 | import hashlib 38 | import logging 39 | import sys 40 | import os 41 | 42 | import bitcointx.rpc 43 | from bitcointx.core import * 44 | from bitcointx.core.script import * 45 | from bitcointx.wallet import * 46 | 47 | parser = argparse.ArgumentParser( 48 | description="Publish text in the blockchain, suitably padded for easy recovery with strings", 49 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 50 | 51 | parser.add_argument('-n', action='store_true', 52 | dest='dryrun', 53 | help="Dry-run; don't actually send the transactions") 54 | parser.add_argument("-q","--quiet",action="count",default=0, 55 | help="Be more quiet.") 56 | parser.add_argument("-v","--verbose",action="count",default=0, 57 | help="Be more verbose. Both -v and -q may be used multiple times.") 58 | parser.add_argument("--min-len",action="store",type=int,default=20, 59 | help="Minimum text length; shorter text is padded to this length") 60 | parser.add_argument("-f","--fee-per-kb",action="store",type=float,default=0.0002, 61 | help="Fee-per-KB") 62 | parser.add_argument("-k","--privkey",action="store",type=str,default=None, 63 | help="Specify private key") 64 | 65 | net_parser = parser.add_mutually_exclusive_group() 66 | net_parser.add_argument('-t','--testnet', action='store_true', 67 | dest='testnet', 68 | help='Use testnet') 69 | net_parser.add_argument('-r','--regtest', action='store_true', 70 | dest='regtest', 71 | help='Use regtest') 72 | 73 | parser.add_argument('fd', type=argparse.FileType('rb'), metavar='FILE', 74 | help='Text file') 75 | 76 | args = parser.parse_args() 77 | 78 | # Setup logging 79 | args.verbosity = args.verbose - args.quiet 80 | if args.verbosity == 0: 81 | logging.root.setLevel(logging.INFO) 82 | elif args.verbosity >= 1: 83 | logging.root.setLevel(logging.DEBUG) 84 | elif args.verbosity == -1: 85 | logging.root.setLevel(logging.WARNING) 86 | elif args.verbosity <= -2: 87 | logging.root.setLevel(logging.ERROR) 88 | 89 | if args.testnet: 90 | bitcointx.SelectParams('testnet') 91 | elif args.regtest: 92 | bitcointx.SelectParams('regtest') 93 | 94 | proxy = bitcointx.rpc.Proxy() 95 | 96 | if args.privkey is None: 97 | args.privkey = CBitcoinSecret.from_secret_bytes(os.urandom(32)) 98 | 99 | else: 100 | args.privkey = CBitcoinSecret(args.privkey) 101 | 102 | logging.info('Using keypair %s %s' % (b2x(args.privkey.pub), args.privkey)) 103 | 104 | # Turn the text file into padded lines 105 | if args.fd is sys.stdin: 106 | # work around a bug where even though we specified binary encoding we get 107 | # the sys.stdin instead. 108 | args.fd = sys.stdin.buffer 109 | raw_padded_lines = [b'\x00' + line.rstrip().ljust(args.min_len) + b'\x00' for line in args.fd.readlines()] 110 | 111 | # combine lines if < MAX_SCRIPT_ELEMENT_SIZE 112 | padded_lines = [] 113 | prev_line = b'\x00' 114 | for line in raw_padded_lines: 115 | if len(prev_line) + len(line) <= MAX_SCRIPT_ELEMENT_SIZE: 116 | prev_line = prev_line + line[1:] 117 | 118 | else: 119 | padded_lines.append(prev_line) 120 | prev_line = line 121 | 122 | if prev_line: 123 | padded_lines.append(prev_line) 124 | 125 | scripts = [] 126 | while padded_lines: 127 | def make_scripts(lines, n): 128 | # The n makes sure every p2sh addr is unique; the pubkey lets us 129 | # control the order the vin order vs. just using hashlocks. 130 | redeemScript = [] 131 | for chunk in reversed(lines): 132 | if len(chunk) > MAX_SCRIPT_ELEMENT_SIZE: 133 | parser.exit('Lines must be less than %d characters; got %d characters' %\ 134 | (MAX_SCRIPT_ELEMENT_SIZE, len(chunk))) 135 | redeemScript.extend([OP_HASH160, Hash160(chunk), OP_EQUALVERIFY]) 136 | redeemScript = CScript(redeemScript + 137 | [args.privkey.pub, OP_CHECKSIGVERIFY, 138 | n, OP_DROP, # deduplicate push dropped to meet BIP62 rules 139 | OP_DEPTH, 0, OP_EQUAL]) # prevent scriptSig malleability 140 | 141 | return CScript(lines) + redeemScript, redeemScript 142 | 143 | scriptSig = redeemScript = None 144 | for i in range(len(padded_lines)): 145 | next_scriptSig, next_redeemScript = make_scripts(padded_lines[0:i+1], len(scripts)) 146 | 147 | # FIXME: magic numbers! 148 | if len(next_redeemScript) > 520 or len(next_scriptSig) > 1600-100: 149 | padded_lines = padded_lines[i:] 150 | break 151 | 152 | else: 153 | scriptSig = next_scriptSig 154 | redeemScript = next_redeemScript 155 | 156 | else: 157 | padded_lines = [] 158 | 159 | scripts.append((scriptSig, redeemScript)) 160 | 161 | # pay to the redeemScripts to make them spendable 162 | 163 | # the 41 accounts for the size of the CTxIn itself 164 | payments = {P2SHBitcoinAddress.from_redeemScript(redeemScript):int(((len(scriptSig)+41)/1000 * args.fee_per_kb)*COIN) 165 | for scriptSig, redeemScript in scripts} 166 | 167 | prevouts_by_scriptPubKey = None 168 | if not args.dryrun: 169 | txid = proxy.sendmany('', payments, 0) 170 | 171 | logging.info('Sent pre-pub tx: %s' % b2lx(txid)) 172 | 173 | tx = proxy.getrawtransaction(txid) 174 | 175 | prevouts_by_scriptPubKey = {txout.scriptPubKey:COutPoint(txid, i) for i, txout in enumerate(tx.vout)} 176 | 177 | else: 178 | prevouts_by_scriptPubKey = {redeemScript.to_p2sh_scriptPubKey():COutPoint(b'\x00'*32, i) 179 | for i, (scriptSig, redeemScript) in enumerate(scripts)} 180 | logging.debug('Payments: %r' % payments) 181 | logging.info('Total cost: %s BTC' % str_money_value(sum(amount for addr, amount in payments.items()))) 182 | 183 | # Create unsigned tx for SignatureHash 184 | vout = [CTxOut(0, CScript([OP_RETURN]))] 185 | 186 | unsigned_vin = [] 187 | for scriptSig, redeemScript in scripts: 188 | scriptPubKey = redeemScript.to_p2sh_scriptPubKey() 189 | 190 | txin = CTxIn(prevouts_by_scriptPubKey[scriptPubKey]) 191 | unsigned_vin.append(txin) 192 | unsigned_tx = CTransaction(unsigned_vin, vout) 193 | 194 | # Sign! 195 | signed_vin = [] 196 | for i, (scriptSig, redeemScript) in enumerate(scripts): 197 | sighash = SignatureHash(redeemScript, unsigned_tx, i, SIGHASH_NONE) 198 | sig = args.privkey.sign(sighash) + bytes([SIGHASH_NONE]) 199 | 200 | signed_scriptSig = CScript([sig] + list(scriptSig)) 201 | 202 | txin = CTxIn(unsigned_vin[i].prevout, signed_scriptSig) 203 | 204 | signed_vin.append(txin) 205 | 206 | signed_tx = CTransaction(signed_vin, vout) 207 | 208 | if args.dryrun: 209 | serialized_tx = signed_tx.serialize() 210 | logging.info('tx size: %d bytes' % len(serialized_tx)) 211 | logging.debug('hex: %s' % b2x(serialized_tx)) 212 | 213 | else: 214 | # FIXME: the tx could be too long here, but there's no way to get sendmany 215 | # to *not* broadcast the transaction first. This is a proof-of-concept, so 216 | # punting. 217 | 218 | logging.debug('Sending publish tx, hex: %s' % b2x(signed_tx.serialize())) 219 | txid = proxy.sendrawtransaction(signed_tx) 220 | logging.info('Sent publish tx: %s' % b2lx(txid)) 221 | 222 | -------------------------------------------------------------------------------- /bitcointx/tests/data/tx_invalid.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["The following are deserialized transactions which are invalid."], 3 | ["They are in the form"], 4 | ["[[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], 5 | ["serializedTransaction, enforceP2SH]"], 6 | ["Objects that are only a single string (like this one) are ignored"], 7 | 8 | ["0e1b5688cf179cd9f7cbda1fac0090f6e684bbf8cd946660120197c3f3681809 but with extra junk appended to the end of the scriptPubKey"], 9 | [[["6ca7ec7b1847f6bdbd737176050e6a08d66ccd55bb94ad24f4018024107a5827", 0, "0x41 0x043b640e983c9690a14c039a2037ecc3467b27a0dcd58f19d76c7bc118d09fec45adc5370a1c5bf8067ca9f5557a4cf885fdb0fe0dcc9c3a7137226106fbc779a5 CHECKSIG VERIFY 1"]], 10 | "010000000127587a10248001f424ad94bb55cd6cd6086a0e05767173bdbdf647187beca76c000000004948304502201b822ad10d6adc1a341ae8835be3f70a25201bbff31f59cbb9c5353a5f0eca18022100ea7b2f7074e9aa9cf70aa8d0ffee13e6b45dddabf1ab961bda378bcdb778fa4701ffffffff0100f2052a010000001976a914fc50c5907d86fed474ba5ce8b12a66e0a4c139d888ac00000000", true], 11 | 12 | ["This is the nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG from tx_valid.json"], 13 | ["but with the signature duplicated in the scriptPubKey with a non-standard pushdata prefix"], 14 | ["See FindAndDelete, which will only remove if it uses the same pushdata prefix as is standard"], 15 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], 16 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", true], 17 | 18 | ["Same as above, but with the sig in the scriptSig also pushed with the same non-standard OP_PUSHDATA"], 19 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], 20 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000006b4c473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", true], 21 | 22 | ["An invalid P2SH Transaction"], 23 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], 24 | "010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", true], 25 | 26 | ["No outputs"], 27 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x05ab9e14d983742513f0f451e105ffb4198d1dd4 EQUAL"]], 28 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022100f16703104aab4e4088317c862daec83440242411b039d14280e03dd33b487ab802201318a7be236672c5c56083eb7a5a195bc57a40af7923ff8545016cd3b571e2a601232103c40e5d339df3f30bf753e7e04450ae4ef76c9e45587d1d993bdc4cd06f0651c7acffffffff0000000000", true], 29 | 30 | ["Negative output"], 31 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xae609aca8061d77c5e111f6bb62501a6bbe2bfdb EQUAL"]], 32 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000006d4830450220063222cbb128731fc09de0d7323746539166544d6c1df84d867ccea84bcc8903022100bf568e8552844de664cd41648a031554327aa8844af34b4f27397c65b92c04de0123210243ec37dee0e2e053a9c976f43147e79bc7d9dc606ea51010af1ac80db6b069e1acffffffff01ffffffffffffffff015100000000", true], 33 | 34 | ["MAX_MONEY + 1 output"], 35 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x32afac281462b822adbec5094b8d4d337dd5bd6a EQUAL"]], 36 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010140075af0750700015100000000", true], 37 | 38 | ["MAX_MONEY output + 1 output"], 39 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xb558cbf4930954aa6a344363a15668d7477ae716 EQUAL"]], 40 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510001000000000000015100000000", true], 41 | 42 | ["Duplicate inputs"], 43 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x236d0639db62b0773fd8ac34dc85ae19e9aba80a EQUAL"]], 44 | "01000000020001000000000000000000000000000000000000000000000000000000000000000000006c47304402204bb1197053d0d7799bf1b30cd503c44b58d6240cccbdc85b6fe76d087980208f02204beeed78200178ffc6c74237bb74b3f276bbb4098b5605d814304fe128bf1431012321039e8815e15952a7c3fada1905f8cf55419837133bd7756c0ef14fc8dfe50c0deaacffffffff0001000000000000000000000000000000000000000000000000000000000000000000006c47304402202306489afef52a6f62e90bf750bbcdf40c06f5c6b138286e6b6b86176bb9341802200dba98486ea68380f47ebb19a7df173b99e6bc9c681d6ccf3bde31465d1f16b3012321039e8815e15952a7c3fada1905f8cf55419837133bd7756c0ef14fc8dfe50c0deaacffffffff010000000000000000015100000000", true], 45 | 46 | ["Coinbase of size 1"], 47 | ["Note the input is just required to make the tester happy"], 48 | [[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], 49 | "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0151ffffffff010000000000000000015100000000", true], 50 | 51 | ["Coinbase of size 101"], 52 | ["Note the input is just required to make the tester happy"], 53 | [[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], 54 | "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff655151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", true], 55 | 56 | ["Null txin"], 57 | [[["0000000000000000000000000000000000000000000000000000000000000000", -1, "HASH160 0x14 0x02dae7dbbda56097959cba59b1989dd3e47937bf EQUAL"]], 58 | "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6e49304602210086f39e028e46dafa8e1e3be63906465f4cf038fbe5ed6403dc3e74ae876e6431022100c4625c675cfc5c7e3a0e0d7eaec92ac24da20c73a88eb40d09253e51ac6def5201232103a183ddc41e84753aca47723c965d1b5c8b0e2b537963518355e6dd6cf8415e50acffffffff010000000000000000015100000000", true], 59 | 60 | ["Same as the transactions in valid with one input SIGHASH_ALL and one SIGHASH_ANYONECANPAY, but we set the _ANYONECANPAY sequence number, invalidating the SIGHASH_ALL signature"], 61 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"], 62 | ["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]], 63 | "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df10101000000000200000000000000000000000000000000000000000000000000000000000000000000484730440220201dc2d030e380e8f9cfb41b442d930fa5a685bb2c8db5906671f865507d0670022018d9e7a8d4c8d86a73c2a724ee38ef983ec249827e0e464841735955c707ece98101000000010100000000000000015100000000", true], 64 | 65 | ["Incorrect signature order"], 66 | ["Note the input is just required to make the tester happy"], 67 | [[["b3da01dd4aae683c7aee4d5d8b52a540a508e1115f77cd7fa9a291243f501223", 0, "HASH160 0x14 0xb1ce99298d5f07364b57b1e5c9cc00be0b04a954 EQUAL"]], 68 | "01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe000048304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f401483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", true], 69 | 70 | ["Empty stack when we try to run CHECKSIG"], 71 | [[["ad503f72c18df5801ee64d76090afe4c607fb2b822e9b7b63c5826c50e22fc3b", 0, "0x21 0x027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5 CHECKSIG NOT"]], 72 | "01000000013bfc220ec526583cb6b7e922b8b27f604cfe0a09764de61e80f58dc1723f50ad0000000000ffffffff0101000000000000002321027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5ac00000000", true], 73 | 74 | ["Make diffs cleaner by leaving a comment here without comma at the end"] 75 | ] 76 | -------------------------------------------------------------------------------- /bitcointx/tests/test_segwit.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | from __future__ import absolute_import, division, print_function, unicode_literals 13 | 14 | import unittest 15 | import random 16 | import sys 17 | 18 | import bitcointx 19 | from bitcointx.core import * 20 | from bitcointx.core.script import * 21 | from bitcointx.wallet import * 22 | 23 | if sys.version > '3': 24 | _bytes = bytes 25 | else: 26 | _bytes = lambda x: bytes(bytearray(x)) 27 | 28 | # Test serialization 29 | 30 | 31 | class Test_Segwit(unittest.TestCase): 32 | 33 | # Test BIP 143 vectors 34 | def test_p2wpkh_signaturehash(self): 35 | unsigned_tx = x('0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000') 36 | scriptpubkey = CScript(x('00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1')) 37 | value = int(6*COIN) 38 | 39 | address = CBech32BitcoinAddress.from_scriptPubKey(scriptpubkey) 40 | self.assertEqual(SignatureHash(address.to_redeemScript(), CTransaction.deserialize(unsigned_tx), 41 | 1, SIGHASH_ALL, value, SIGVERSION_WITNESS_V0), 42 | x('c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670')) 43 | 44 | def test_p2sh_p2wpkh_signaturehash(self): 45 | unsigned_tx = x('0100000001db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a54770100000000feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac92040000') 46 | scriptpubkey = CScript(x('a9144733f37cf4db86fbc2efed2500b4f4e49f31202387')) 47 | redeemscript = CScript(x('001479091972186c449eb1ded22b78e40d009bdf0089')) 48 | value = int(10*COIN) 49 | 50 | address = CBase58BitcoinAddress.from_scriptPubKey(redeemscript) 51 | self.assertEqual(SignatureHash(address.to_redeemScript(), CTransaction.deserialize(unsigned_tx), 52 | 0, SIGHASH_ALL, value, SIGVERSION_WITNESS_V0), 53 | x('64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6')) 54 | 55 | def test_p2wsh_signaturehash1(self): 56 | unsigned_tx = x('0100000002fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e0000000000ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac00000000') 57 | scriptpubkey1 = CScript(x('21036d5c20fa14fb2f635474c1dc4ef5909d4568e5569b79fc94d3448486e14685f8ac')) 58 | value1 = int(1.5625*COIN) 59 | scriptpubkey2 = CScript(x('00205d1b56b63d714eebe542309525f484b7e9d6f686b3781b6f61ef925d66d6f6a0')) 60 | value2 = int(49*COIN) 61 | scriptcode1 = CScript(x('21026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac')) 62 | # This is the same script with everything up to the last executed OP_CODESEPARATOR, including that 63 | # OP_CODESEPARATOR removed 64 | scriptcode2 = CScript(x('210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac')) 65 | 66 | self.assertEqual(SignatureHash(scriptcode1, CTransaction.deserialize(unsigned_tx), 67 | 1, SIGHASH_SINGLE, value2, SIGVERSION_WITNESS_V0), 68 | x('82dde6e4f1e94d02c2b7ad03d2115d691f48d064e9d52f58194a6637e4194391')) 69 | self.assertEqual(SignatureHash(scriptcode2, CTransaction.deserialize(unsigned_tx), 70 | 1, SIGHASH_SINGLE, value2, SIGVERSION_WITNESS_V0), 71 | x('fef7bd749cce710c5c052bd796df1af0d935e59cea63736268bcbe2d2134fc47')) 72 | 73 | def test_p2wsh_signaturehash2(self): 74 | unsigned_tx = x('0100000002e9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff80e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffff0280969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac80969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac00000000') 75 | scriptpubkey1 = CScript(x('0020ba468eea561b26301e4cf69fa34bde4ad60c81e70f059f045ca9a79931004a4d')) 76 | value1 = int(0.16777215*COIN) 77 | witnessscript1= CScript(x('0063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac')) 78 | scriptcode1 = CScript(x('0063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac')) 79 | scriptpubkey2 = CScript(x('0020d9bbfbe56af7c4b7f960a70d7ea107156913d9e5a26b0a71429df5e097ca6537')) 80 | value2 = int(0.16777215*COIN) 81 | witnessscript2= CScript(x('5163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac')) 82 | scriptcode2 = CScript(x('68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac')) 83 | 84 | self.assertEqual(SignatureHash(scriptcode1, CTransaction.deserialize(unsigned_tx), 85 | 0, SIGHASH_SINGLE|SIGHASH_ANYONECANPAY, value1, SIGVERSION_WITNESS_V0), 86 | x('e9071e75e25b8a1e298a72f0d2e9f4f95a0f5cdf86a533cda597eb402ed13b3a')) 87 | self.assertEqual(SignatureHash(scriptcode2, CTransaction.deserialize(unsigned_tx), 88 | 1, SIGHASH_SINGLE|SIGHASH_ANYONECANPAY, value2, SIGVERSION_WITNESS_V0), 89 | x('cd72f1f1a433ee9df816857fad88d8ebd97e09a75cd481583eb841c330275e54')) 90 | 91 | def test_p2sh_p2wsh_signaturehash(self): 92 | unsigned_tx = x('010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000') 93 | 94 | scriptPubKey = CScript(x('a9149993a429037b5d912407a71c252019287b8d27a587')) 95 | value = int(9.87654321*COIN) 96 | redeemScript = CScript(x('0020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54')) 97 | witnessscript= CScript(x('56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae')) 98 | 99 | self.assertEqual(SignatureHash(witnessscript, CTransaction.deserialize(unsigned_tx), 100 | 0, SIGHASH_ALL, value, SIGVERSION_WITNESS_V0), 101 | x('185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c')) 102 | self.assertEqual(SignatureHash(witnessscript, CTransaction.deserialize(unsigned_tx), 103 | 0, SIGHASH_NONE, value, SIGVERSION_WITNESS_V0), 104 | x('e9733bc60ea13c95c6527066bb975a2ff29a925e80aa14c213f686cbae5d2f36')) 105 | self.assertEqual(SignatureHash(witnessscript, CTransaction.deserialize(unsigned_tx), 106 | 0, SIGHASH_SINGLE, value, SIGVERSION_WITNESS_V0), 107 | x('1e1f1c303dc025bd664acb72e583e933fae4cff9148bf78c157d1e8f78530aea')) 108 | self.assertEqual(SignatureHash(witnessscript, CTransaction.deserialize(unsigned_tx), 109 | 0, SIGHASH_ALL|SIGHASH_ANYONECANPAY, value, SIGVERSION_WITNESS_V0), 110 | x('2a67f03e63a6a422125878b40b82da593be8d4efaafe88ee528af6e5a9955c6e')) 111 | self.assertEqual(SignatureHash(witnessscript, CTransaction.deserialize(unsigned_tx), 112 | 0, SIGHASH_NONE|SIGHASH_ANYONECANPAY, value, SIGVERSION_WITNESS_V0), 113 | x('781ba15f3779d5542ce8ecb5c18716733a5ee42a6f51488ec96154934e2c890a')) 114 | self.assertEqual(SignatureHash(witnessscript, CTransaction.deserialize(unsigned_tx), 115 | 0, SIGHASH_SINGLE|SIGHASH_ANYONECANPAY, value, SIGVERSION_WITNESS_V0), 116 | x('511e8e52ed574121fc1b654970395502128263f62662e076dc6baf05c2e6a99b')) 117 | 118 | def test_checkblock(self): 119 | # (No witness) coinbase generated by Bitcoin Core 120 | str_coinbase = '01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03520101ffffffff0100f2052a01000000232102960c90bc04a631cb17922e4f5d80ac75fd590a88b8baaa5a3d5086ac85e4d788ac00000000' 121 | # No witness transaction 122 | str_tx = '0100000001e3d0c1d051a3abe79d5951dcab86f71d8926aff5caed5ff9bd72cb593e4ebaf5010000006b483045022100a28e1c57e160296953e1af85c5034bb1b907c12c50367da75ba547874a8a8c52022040682e888ddce7fd5c72519a9f28f22f5164c8af864d33332bbc7f65596ad4ae012102db30394fd5cc8288bed607b9382338240c014a98c9c0febbfb380db74ceb30a3ffffffff020d920000000000001976a914ad781c8ffcc18b2155433cba2da4601180a66eef88aca3170000000000001976a914bacb1c4b0725e61e385c7093b4260533953c8e1688ac00000000' 123 | # SegWit transaction 124 | str_w_tx = '0100000000010115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f8560100000000ffffffff0100b4f505000000001976a9141d7cd6c75c2e86f4cbf98eaed221b30bd9a0b92888ac02483045022100df7b7e5cda14ddf91290e02ea10786e03eb11ee36ec02dd862fe9a326bbcb7fd02203f5b4496b667e6e281cc654a2da9e4f08660c620a1051337fa8965f727eb19190121038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990ac00000000' 125 | witness_nonce = _bytes(random.getrandbits(8) for _ in range(32)) 126 | 127 | coinbase = CMutableTransaction.deserialize(x(str_coinbase)) 128 | coinbase.wit = CTxWitness([CTxInWitness(CScriptWitness([witness_nonce]))]) 129 | 130 | tx_legacy = CTransaction.deserialize(x(str_tx)) 131 | tx_segwit = CTransaction.deserialize(x(str_w_tx)) 132 | 133 | witness_merkle_root = CBlock.build_witness_merkle_tree_from_txs((coinbase, tx_legacy, tx_segwit))[-1] 134 | 135 | commitment = Hash(witness_merkle_root + witness_nonce) 136 | commitment_script = bitcointx.core.WITNESS_COINBASE_SCRIPTPUBKEY_MAGIC + commitment 137 | coinbase.vout.append(CTxOut(0, CScript(commitment_script))) 138 | 139 | block = CBlock(2, b'\x00'*32, b'\x00'*32, 0, 0, 0, (coinbase, tx_legacy, tx_segwit)) 140 | 141 | CheckBlock(block, False, True) 142 | -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # bitcointx documentation build configuration file, created by 4 | # sphinx-quickstart on Thu May 28 20:40:55 2015. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | PROJECT = 'python-bitcointx' 15 | DESCRIPTION = 'The Swiss Army Knife of the Bitcoin protocol.' 16 | AUTHORS = 'The python-bitcoinlib and python-bitcointx developers' 17 | 18 | import sphinx 19 | import sys 20 | import os 21 | 22 | # If extensions (or modules to document with autodoc) are in another directory, 23 | # add these directories to sys.path here. If the directory is relative to the 24 | # documentation root, use os.path.abspath to make it absolute, like shown here. 25 | sys.path.insert(0, os.path.abspath('..')) 26 | 27 | from bitcointx import __version__ 28 | 29 | # Prevent loading openssl when generating API docs. Either the whole library or 30 | # the necessary elliptic curve might not be available, causing import to fail. 31 | try: 32 | from unittest.mock import MagicMock 33 | except ImportError: 34 | from mock import MagicMock 35 | sys.modules['ctypes'] = MagicMock() 36 | 37 | # -- General configuration ----------------------------------------------------- 38 | 39 | # If your documentation needs a minimal Sphinx version, state it here. 40 | #needs_sphinx = '1.0' 41 | 42 | # Add any Sphinx extension module names here, as strings. They can be extensions 43 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 44 | extensions = [ 45 | 'sphinx.ext.autodoc', 46 | 'sphinx.ext.viewcode', 47 | ] 48 | 49 | autodoc_default_flags = [ 50 | 'members', 51 | 'undoc-members', 52 | 'show-inheritance', 53 | ] 54 | 55 | # Include __init__ docstring in class level docs 56 | autoclass_content = 'both' 57 | 58 | # Add any paths that contain templates here, relative to this directory. 59 | templates_path = ['_templates'] 60 | 61 | # The suffix of source filenames. 62 | source_suffix = '.rst' 63 | 64 | # The encoding of source files. 65 | #source_encoding = 'utf-8-sig' 66 | 67 | # The master toctree document. 68 | master_doc = 'index' 69 | 70 | # General information about the project. 71 | project = PROJECT 72 | copyright = '2012-2015, ' + AUTHORS 73 | 74 | # The version info for the project you're documenting, acts as replacement for 75 | # |version| and |release|, also used in various other places throughout the 76 | # built documents. 77 | # 78 | # The short X.Y version. 79 | version = __version__ 80 | # The full version, including alpha/beta/rc tags. 81 | release = __version__ 82 | 83 | # The language for content autogenerated by Sphinx. Refer to documentation 84 | # for a list of supported languages. 85 | #language = None 86 | 87 | # There are two options for replacing |today|: either, you set today to some 88 | # non-false value, then it is used: 89 | #today = '' 90 | # Else, today_fmt is used as the format for a strftime call. 91 | #today_fmt = '%B %d, %Y' 92 | 93 | # List of patterns, relative to source directory, that match files and 94 | # directories to ignore when looking for source files. 95 | exclude_patterns = ['_build'] 96 | 97 | # The reST default role (used for this markup: `text`) to use for all documents. 98 | #default_role = None 99 | 100 | # If true, '()' will be appended to :func: etc. cross-reference text. 101 | #add_function_parentheses = True 102 | 103 | # If true, the current module name will be prepended to all description 104 | # unit titles (such as .. function::). 105 | #add_module_names = True 106 | 107 | # If true, sectionauthor and moduleauthor directives will be shown in the 108 | # output. They are ignored by default. 109 | #show_authors = False 110 | 111 | # The name of the Pygments (syntax highlighting) style to use. 112 | pygments_style = 'sphinx' 113 | 114 | # A list of ignored prefixes for module index sorting. 115 | #modindex_common_prefix = [] 116 | 117 | 118 | # -- Options for HTML output --------------------------------------------------- 119 | 120 | # The theme to use for HTML and HTML Help pages. See the documentation for 121 | # a list of builtin themes. 122 | if getattr(sphinx, 'version_info', (0, 0)) >= (1, 3): 123 | html_theme = 'classic' 124 | else: 125 | html_theme = 'default' 126 | 127 | # Theme options are theme-specific and customize the look and feel of a theme 128 | # further. For a list of options available for each theme, see the 129 | # documentation. 130 | #html_theme_options = {} 131 | 132 | # Add any paths that contain custom themes here, relative to this directory. 133 | #html_theme_path = [] 134 | 135 | # The name for this set of Sphinx documents. If None, it defaults to 136 | # " v documentation". 137 | #html_title = None 138 | 139 | # A shorter title for the navigation bar. Default is the same as html_title. 140 | #html_short_title = None 141 | 142 | # The name of an image file (relative to this directory) to place at the top 143 | # of the sidebar. 144 | #html_logo = None 145 | 146 | # The name of an image file (within the static path) to use as favicon of the 147 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 148 | # pixels large. 149 | #html_favicon = None 150 | 151 | # Add any paths that contain custom static files (such as style sheets) here, 152 | # relative to this directory. They are copied after the builtin static files, 153 | # so a file named "default.css" will overwrite the builtin "default.css". 154 | html_static_path = ['_static'] 155 | 156 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 157 | # using the given strftime format. 158 | #html_last_updated_fmt = '%b %d, %Y' 159 | 160 | # If true, SmartyPants will be used to convert quotes and dashes to 161 | # typographically correct entities. 162 | #html_use_smartypants = True 163 | 164 | # Custom sidebar templates, maps document names to template names. 165 | #html_sidebars = {} 166 | 167 | # Additional templates that should be rendered to pages, maps page names to 168 | # template names. 169 | #html_additional_pages = {} 170 | 171 | # If false, no module index is generated. 172 | #html_domain_indices = True 173 | 174 | # If false, no index is generated. 175 | #html_use_index = True 176 | 177 | # If true, the index is split into individual pages for each letter. 178 | #html_split_index = False 179 | 180 | # If true, links to the reST sources are added to the pages. 181 | #html_show_sourcelink = True 182 | 183 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 184 | #html_show_sphinx = True 185 | 186 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 187 | #html_show_copyright = True 188 | 189 | # If true, an OpenSearch description file will be output, and all pages will 190 | # contain a tag referring to it. The value of this option must be the 191 | # base URL from which the finished HTML is served. 192 | #html_use_opensearch = '' 193 | 194 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 195 | #html_file_suffix = None 196 | 197 | # Output file base name for HTML help builder. 198 | htmlhelp_basename = PROJECT + 'doc' 199 | 200 | 201 | # -- Options for LaTeX output -------------------------------------------------- 202 | 203 | latex_elements = { 204 | # The paper size ('letterpaper' or 'a4paper'). 205 | #'papersize': 'letterpaper', 206 | 207 | # The font size ('10pt', '11pt' or '12pt'). 208 | #'pointsize': '10pt', 209 | 210 | # Additional stuff for the LaTeX preamble. 211 | #'preamble': '', 212 | } 213 | 214 | # Grouping the document tree into LaTeX files. List of tuples 215 | # (source start file, target name, title, author, documentclass [howto/manual]). 216 | latex_documents = [ 217 | ('index', PROJECT + '.tex', PROJECT + ' Documentation', 218 | AUTHORS, 'manual'), 219 | ] 220 | 221 | # The name of an image file (relative to this directory) to place at the top of 222 | # the title page. 223 | #latex_logo = None 224 | 225 | # For "manual" documents, if this is true, then toplevel headings are parts, 226 | # not chapters. 227 | #latex_use_parts = False 228 | 229 | # If true, show page references after internal links. 230 | #latex_show_pagerefs = False 231 | 232 | # If true, show URL addresses after external links. 233 | #latex_show_urls = False 234 | 235 | # Documents to append as an appendix to all manuals. 236 | #latex_appendices = [] 237 | 238 | # If false, no module index is generated. 239 | #latex_domain_indices = True 240 | 241 | 242 | # -- Options for manual page output -------------------------------------------- 243 | 244 | # One entry per manual page. List of tuples 245 | # (source start file, name, description, authors, manual section). 246 | man_pages = [ 247 | ('index', PROJECT, PROJECT + ' Documentation', 248 | [AUTHORS], 1) 249 | ] 250 | 251 | # If true, show URL addresses after external links. 252 | #man_show_urls = False 253 | 254 | 255 | # -- Options for Texinfo output ------------------------------------------------ 256 | 257 | # Grouping the document tree into Texinfo files. List of tuples 258 | # (source start file, target name, title, author, 259 | # dir menu entry, description, category) 260 | texinfo_documents = [ 261 | ('index', PROJECT, PROJECT + ' Documentation', 262 | AUTHORS, PROJECT, DESCRIPTION, 263 | 'Miscellaneous'), 264 | ] 265 | 266 | # Documents to append as an appendix to all manuals. 267 | #texinfo_appendices = [] 268 | 269 | # If false, no module index is generated. 270 | #texinfo_domain_indices = True 271 | 272 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 273 | #texinfo_show_urls = 'footnote' 274 | 275 | 276 | # -- Options for Epub output --------------------------------------------------- 277 | 278 | # Bibliographic Dublin Core info. 279 | epub_title = PROJECT 280 | epub_author = AUTHORS 281 | epub_publisher = AUTHORS 282 | epub_copyright = copyright 283 | 284 | # The language of the text. It defaults to the language option 285 | # or en if the language is not set. 286 | #epub_language = '' 287 | 288 | # The scheme of the identifier. Typical schemes are ISBN or URL. 289 | #epub_scheme = '' 290 | 291 | # The unique identifier of the text. This can be a ISBN number 292 | # or the project homepage. 293 | #epub_identifier = '' 294 | 295 | # A unique identification for the text. 296 | #epub_uid = '' 297 | 298 | # A tuple containing the cover image and cover page html template filenames. 299 | #epub_cover = () 300 | 301 | # HTML files that should be inserted before the pages created by sphinx. 302 | # The format is a list of tuples containing the path and title. 303 | #epub_pre_files = [] 304 | 305 | # HTML files shat should be inserted after the pages created by sphinx. 306 | # The format is a list of tuples containing the path and title. 307 | #epub_post_files = [] 308 | 309 | # A list of files that should not be packed into the epub file. 310 | #epub_exclude_files = [] 311 | 312 | # The depth of the table of contents in toc.ncx. 313 | #epub_tocdepth = 3 314 | 315 | # Allow duplicate toc entries. 316 | #epub_tocdup = True 317 | -------------------------------------------------------------------------------- /bitcointx/tests/test_core.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013-2015 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | from __future__ import absolute_import, division, print_function, unicode_literals 13 | 14 | import unittest 15 | 16 | from bitcointx.core import * 17 | 18 | class Test_str_value(unittest.TestCase): 19 | def test(self): 20 | def T(value, expected): 21 | actual = str_money_value(value) 22 | self.assertEqual(actual, expected) 23 | 24 | T( 0, '0.0') 25 | T( 1, '0.00000001') 26 | T( 10, '0.0000001') 27 | T( 12345678, '0.12345678') 28 | T( 10000000, '0.1') 29 | T( 100000000, '1.0') 30 | T(1000000000, '10.0') 31 | T(1010000000, '10.1') 32 | T(1001000000, '10.01') 33 | T(1012345678, '10.12345678') 34 | 35 | class Test_Money(unittest.TestCase): 36 | def test_MoneyRange(self): 37 | self.assertFalse(MoneyRange(-1)) 38 | self.assertTrue(MoneyRange(0)) 39 | self.assertTrue(MoneyRange(100000)) 40 | self.assertTrue(MoneyRange(21000000 * COIN)) # Maximum money on Bitcoin network 41 | self.assertFalse(MoneyRange(21000001 * COIN)) 42 | 43 | def test_MoneyRangeCustomParams(self): 44 | highMaxParamsType = type(str('CoreHighMainParams'), (CoreMainParams,object), {'MAX_MONEY': 22000000 * COIN }) 45 | highMaxParams = highMaxParamsType() 46 | self.assertTrue(MoneyRange(21000001 * COIN, highMaxParams)) 47 | self.assertTrue(MoneyRange(22000000 * COIN, highMaxParams)) 48 | self.assertFalse(MoneyRange(22000001 * COIN, highMaxParams)) 49 | 50 | class Test_CBlockHeader(unittest.TestCase): 51 | def test_serialization(self): 52 | genesis = CBlockHeader(nVersion=1, 53 | hashPrevBlock=lx('0000000000000000000000000000000000000000000000000000000000000000'), 54 | hashMerkleRoot=lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'), 55 | nTime=1231006505, 56 | nBits=486604799, 57 | nNonce=2083236893) 58 | serialized = genesis.serialize() 59 | self.assertEqual(Hash(serialized), 60 | lx('000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f')) 61 | 62 | genesis2 = CBlockHeader.deserialize(serialized) 63 | self.assertEqual(genesis, genesis2) 64 | 65 | def test_GetHash(self): 66 | genesis = CBlockHeader(nVersion=1, 67 | hashPrevBlock=lx('0000000000000000000000000000000000000000000000000000000000000000'), 68 | hashMerkleRoot=lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'), 69 | nTime=1231006505, 70 | nBits=486604799, 71 | nNonce=2083236893) 72 | self.assertEqual(genesis.GetHash(), lx('000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f')) 73 | 74 | def test_calc_difficulty(self): 75 | def T(nbits, expected): 76 | actual = CBlockHeader.calc_difficulty(nbits) 77 | actual = round(actual, 3) 78 | self.assertEqual(actual, expected) 79 | 80 | T(486604799, 1.000) # block 0 81 | T(486594666, 1.183) # block 33333 82 | T(469809688, 352.161) # block 74000 83 | T(453179945, 22012.381) # block 105000 84 | T(436527338, 3438908.960) # block 210000 85 | T(426957810, 37392766.136) # block 250000 86 | 87 | class Test_CBlock(unittest.TestCase): 88 | def test_serialization(self): 89 | initial_serialized = x('0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000') 90 | genesis = CBlock.deserialize(initial_serialized) 91 | serialized = genesis.serialize() 92 | self.assertEqual(Hash(serialized[0:80]), 93 | lx('000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f')) 94 | self.assertEqual(serialized, initial_serialized) 95 | 96 | def test_GetHash(self): 97 | genesis = CBlock.deserialize(x('0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000')) 98 | self.assertEqual(genesis.GetHash(), lx('000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f')) 99 | 100 | def test_calc_merkle_root_of_empty_block(self): 101 | """CBlock.calc_merkle_root() fails if vtx empty""" 102 | block = CBlock() 103 | with self.assertRaises(ValueError): 104 | block.calc_merkle_root() 105 | 106 | def test_calc_merkle_root(self): 107 | # genesis 108 | block = CBlock.deserialize(x('0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000')) 109 | self.assertEqual(block.calc_merkle_root(), lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b')) 110 | self.assertEqual(block.vMerkleTree, (lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'),)) 111 | 112 | # 170 two transactions 113 | block = CBlock.deserialize(x('0100000055bd840a78798ad0da853f68974f3d183e2bd1db6a842c1feecf222a00000000ff104ccb05421ab93e63f8c3ce5c2c2e9dbb37de2764b3a3175c8166562cac7d51b96a49ffff001d283e9e700201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0102ffffffff0100f2052a01000000434104d46c4968bde02899d2aa0963367c7a6ce34eec332b32e42e5f3407e052d64ac625da6f0718e7b302140434bd725706957c092db53805b821a85b23a7ac61725bac000000000100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704000000004847304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000')) 114 | self.assertEqual(block.calc_merkle_root(), lx('7dac2c5666815c17a3b36427de37bb9d2e2c5ccec3f8633eb91a4205cb4c10ff')) 115 | 116 | # 99960 three transactions 117 | block = CBlock.deserialize(x('01000000e78b20013e6e9a21b6366ead5d866b2f9dc00664508b90f24da8000000000000f94b61259c7e9af3455b277275800d0d6a58b929eedf9e0153a6ef2278a5d53408d11a4d4c86041b0fbf10b00301000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0119ffffffff0100f2052a0100000043410427e729f9cb5564abf2a1ccda596c636b77bd4d9d91f657d4738f3c70fce8ac4e12b1c782905554d9ff2c2e050fdfe3ff93c91c5817e617877d51f450b528c9e4ac000000000100000001e853c9e0c133547fd9e162b1d3860dd0f27d5b9b8a7430d28896c00fbb3f1bc7000000008c49304602210095bcd54ebd0caa7cee75f0f89de472a765e6ef4b98c5fd4b32c7f9d4905db9ae022100ebd3f668e3a1a36d56e30184c27531dbb9fc136c84b1282be562064d86997d1e014104727eb4fdcc90658cd26abe7dcb0ae7297810b15b9e27c32bcf8e3edd934901968806dc18b1276d7273cc4c223feee0070361ed947888a3cef422bebfede96e08ffffffff020065cd1d000000001976a91468c6c2b3c0bc4a8eeb10d16a300d627a31a3b58588ac0008af2f000000001976a9141d87f0a54a1d704ffc70eae83b025698bc0fdcfc88ac00000000010000000125f582f1d37b6713b14b85665a2daea4f464f5ed1c3ab3d4dcf152fb61414b9e000000008a473044022066ec12ced31659e1bf961b542b58bba76ba8f2a1e8f36d5f60be0601598eac21022047ce33685a63283a4c3ebc390261191f215999b2f7d8e1504b8af39aae4a2881014104c5e1d713d10fe59cc48f60701a3efcac418969c22e9c6cf57440f71e44dc82837af5351bf3e1d898f06aa5c792bf0251a39902311d1d27c16847b1b414494f35ffffffff02404b4c00000000001976a91466a3b2e43cfa5c6d9b2f0095f7be5a5cb608478c88ac80b8dc3c030000001976a9146df5ed8cee34df5c05c90406761a11ed143c202d88ac00000000')) 118 | self.assertEqual(block.calc_merkle_root(), lx('34d5a57822efa653019edfee29b9586a0d0d807572275b45f39a7e9c25614bf9')) 119 | 120 | # 99993 four transactions 121 | block = CBlock.deserialize(x('01000000acda3db591d5c2c63e8c09e7523a5b0581707ef3e3520d6ca180000000000000701179cb9a9e0fe709cc96261b6b943b31362b61dacba94b03f9b71a06cc2eff7d1c1b4d4c86041b75962f880401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0152ffffffff014034152a01000000434104216220ab283b5e2871c332de670d163fb1b7e509fd67db77997c5568e7c25afd988f19cd5cc5aec6430866ec64b5214826b28e0f7a86458073ff933994b47a5cac0000000001000000042a40ae58b06c3a61ae55dbee05cab546e80c508f71f24ef0cdc9749dac91ea5f000000004a49304602210089c685b37903c4aa62d984929afeaca554d1641f9a668398cd228fb54588f06b0221008a5cfbc5b0a38ba78c4f4341e53272b9cd0e377b2fb740106009b8d7fa693f0b01ffffffff7b999491e30af112b11105cb053bc3633a8a87f44740eb158849a76891ff228b00000000494830450221009a4aa8663ff4017063d2020519f2eade5b4e3e30be69bf9a62b4e6472d1747b2022021ee3b3090b8ce439dbf08a5df31e2dc23d68073ebda45dc573e8a4f74f5cdfc01ffffffffdea82ec2f9e88e0241faa676c13d093030b17c479770c6cc83239436a4327d49000000004a493046022100c29d9de71a34707c52578e355fa0fdc2bb69ce0a957e6b591658a02b1e039d69022100f82c8af79c166a822d305f0832fb800786d831aea419069b3aed97a6edf8f02101fffffffff3e7987da9981c2ae099f97a551783e1b21669ba0bf3aca8fe12896add91a11a0000000049483045022100e332c81781b281a3b35cf75a5a204a2be451746dad8147831255291ebac2604d02205f889a2935270d1bf1ef47db773d68c4d5c6a51bb51f082d3e1c491de63c345601ffffffff0100c817a8040000001976a91420420e56079150b50fb0617dce4c374bd61eccea88ac00000000010000000265a7293b2d69ba51d554cd32ac7586f7fbeaeea06835f26e03a2feab6aec375f000000004a493046022100922361eaafe316003087d355dd3c0ef3d9f44edae661c212a28a91e020408008022100c9b9c84d53d82c0ba9208f695c79eb42a453faea4d19706a8440e1d05e6cff7501fffffffff6971f00725d17c1c531088144b45ed795a307a22d51ca377c6f7f93675bb03a000000008b483045022100d060f2b2f4122edac61a25ea06396fe9135affdabc66d350b5ae1813bc6bf3f302205d8363deef2101fc9f3d528a8b3907e9d29c40772e587dcea12838c574cb80f801410449fce4a25c972a43a6bc67456407a0d4ced782d4cf8c0a35a130d5f65f0561e9f35198349a7c0b4ec79a15fead66bd7642f17cc8c40c5df95f15ac7190c76442ffffffff0200f2052a010000001976a914c3f537bc307c7eda43d86b55695e46047b770ea388ac00cf7b05000000001976a91407bef290008c089a60321b21b1df2d7f2202f40388ac0000000001000000014ab7418ecda2b2531eef0145d4644a4c82a7da1edd285d1aab1ec0595ac06b69000000008c493046022100a796490f89e0ef0326e8460edebff9161da19c36e00c7408608135f72ef0e03e0221009e01ef7bc17cddce8dfda1f1a6d3805c51f9ab2f8f2145793d8e85e0dd6e55300141043e6d26812f24a5a9485c9d40b8712215f0c3a37b0334d76b2c24fcafa587ae5258853b6f49ceeb29cd13ebb76aa79099fad84f516bbba47bd170576b121052f1ffffffff0200a24a04000000001976a9143542e17b6229a25d5b76909f9d28dd6ed9295b2088ac003fab01000000001976a9149cea2b6e3e64ad982c99ebba56a882b9e8a816fe88ac00000000')) 122 | self.assertEqual(block.calc_merkle_root(), lx('ff2ecc061ab7f9034ba9cbda612b36313b946b1b2696cc09e70f9e9acb791170')) 123 | -------------------------------------------------------------------------------- /bitcointx/core/serialize.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2012-2018 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | """Serialization routines 13 | 14 | You probabably don't need to use these directly. 15 | """ 16 | 17 | from __future__ import absolute_import, division, print_function, unicode_literals 18 | 19 | import hashlib 20 | import struct 21 | 22 | # Py3 compatibility 23 | import sys 24 | 25 | if sys.version > '3': 26 | _bchr = lambda x: bytes([x]) 27 | _bord = lambda x: x[0] 28 | from io import BytesIO as _BytesIO 29 | else: 30 | _bchr = chr 31 | _bord = ord 32 | from cStringIO import StringIO as _BytesIO 33 | 34 | MAX_SIZE = 0x02000000 35 | 36 | 37 | def Hash(msg): 38 | """SHA256^2)(msg) -> bytes""" 39 | return hashlib.sha256(hashlib.sha256(msg).digest()).digest() 40 | 41 | def Hash160(msg): 42 | """RIPEME160(SHA256(msg)) -> bytes""" 43 | h = hashlib.new('ripemd160') 44 | h.update(hashlib.sha256(msg).digest()) 45 | return h.digest() 46 | 47 | 48 | class SerializationError(Exception): 49 | """Base class for serialization errors""" 50 | 51 | 52 | class SerializationTruncationError(SerializationError): 53 | """Serialized data was truncated 54 | 55 | Thrown by deserialize() and stream_deserialize() 56 | """ 57 | 58 | class DeserializationExtraDataError(SerializationError): 59 | """Deserialized data had extra data at the end 60 | 61 | Thrown by deserialize() when not all data is consumed during 62 | deserialization. The deserialized object and extra padding not consumed are 63 | saved. 64 | """ 65 | def __init__(self, msg, obj, padding): 66 | super(DeserializationExtraDataError, self).__init__(msg) 67 | self.obj = obj 68 | self.padding = padding 69 | 70 | def ser_read(f, n): 71 | """Read from a stream safely 72 | 73 | Raises SerializationError and SerializationTruncationError appropriately. 74 | Use this instead of f.read() in your classes stream_(de)serialization() 75 | functions. 76 | """ 77 | if n > MAX_SIZE: 78 | raise SerializationError('Asked to read 0x%x bytes; MAX_SIZE exceeded' % n) 79 | r = f.read(n) 80 | if len(r) < n: 81 | raise SerializationTruncationError('Asked to read %i bytes, but only got %i' % (n, len(r))) 82 | return r 83 | 84 | 85 | class Serializable(object): 86 | """Base class for serializable objects""" 87 | 88 | __slots__ = [] 89 | 90 | def stream_serialize(self, f, **kwargs): 91 | """Serialize to a stream""" 92 | raise NotImplementedError 93 | 94 | @classmethod 95 | def stream_deserialize(cls, f, **kwargs): 96 | """Deserialize from a stream""" 97 | raise NotImplementedError 98 | 99 | def serialize(self, params={}): 100 | """Serialize, returning bytes""" 101 | f = _BytesIO() 102 | self.stream_serialize(f, **params) 103 | return f.getvalue() 104 | 105 | @classmethod 106 | def deserialize(cls, buf, allow_padding=False, params={}): 107 | """Deserialize bytes, returning an instance 108 | 109 | allow_padding - Allow buf to include extra padding. (default False) 110 | 111 | If allow_padding is False and not all bytes are consumed during 112 | deserialization DeserializationExtraDataError will be raised. 113 | """ 114 | fd = _BytesIO(buf) 115 | r = cls.stream_deserialize(fd, **params) 116 | if not allow_padding: 117 | padding = fd.read() 118 | if len(padding) != 0: 119 | raise DeserializationExtraDataError('Not all bytes consumed during deserialization', 120 | r, padding) 121 | return r 122 | 123 | def GetHash(self): 124 | """Return the hash of the serialized object""" 125 | return Hash(self.serialize()) 126 | 127 | def __eq__(self, other): 128 | if (not isinstance(other, self.__class__) and 129 | not isinstance(self, other.__class__)): 130 | return NotImplemented 131 | return self.serialize() == other.serialize() 132 | 133 | def __ne__(self, other): 134 | return not (self == other) 135 | 136 | def __hash__(self): 137 | return hash(self.serialize()) 138 | 139 | class ImmutableSerializable(Serializable): 140 | """Immutable serializable object""" 141 | 142 | __slots__ = ['_cached_GetHash', '_cached__hash__'] 143 | 144 | def __setattr__(self, name, value): 145 | raise AttributeError('Object is immutable') 146 | 147 | def __delattr__(self, name): 148 | raise AttributeError('Object is immutable') 149 | 150 | def GetHash(self): 151 | """Return the hash of the serialized object""" 152 | try: 153 | return self._cached_GetHash 154 | except AttributeError: 155 | _cached_GetHash = super(ImmutableSerializable, self).GetHash() 156 | object.__setattr__(self, '_cached_GetHash', _cached_GetHash) 157 | return _cached_GetHash 158 | 159 | def __hash__(self): 160 | try: 161 | return self._cached__hash__ 162 | except AttributeError: 163 | _cached__hash__ = hash(self.serialize()) 164 | object.__setattr__(self, '_cached__hash__', _cached__hash__) 165 | return _cached__hash__ 166 | 167 | class Serializer(object): 168 | """Base class for object serializers""" 169 | def __new__(cls): 170 | raise NotImplementedError 171 | 172 | @classmethod 173 | def stream_serialize(cls, obj, f): 174 | raise NotImplementedError 175 | 176 | @classmethod 177 | def stream_deserialize(cls, f): 178 | raise NotImplementedError 179 | 180 | @classmethod 181 | def serialize(cls, obj): 182 | f = _BytesIO() 183 | cls.stream_serialize(obj, f) 184 | return f.getvalue() 185 | 186 | @classmethod 187 | def deserialize(cls, buf): 188 | if isinstance(buf, str) or isinstance(buf, bytes): 189 | buf = _BytesIO(buf) 190 | return cls.stream_deserialize(buf) 191 | 192 | 193 | class VarIntSerializer(Serializer): 194 | """Serialization of variable length ints""" 195 | @classmethod 196 | def stream_serialize(cls, i, f): 197 | if i < 0: 198 | raise ValueError('varint must be non-negative integer') 199 | elif i < 0xfd: 200 | f.write(_bchr(i)) 201 | elif i <= 0xffff: 202 | f.write(_bchr(0xfd)) 203 | f.write(struct.pack(b'> 24) & 0xFF 324 | if nbytes <= 3: 325 | v = (c & 0xFFFFFF) >> 8 * (3 - nbytes) 326 | else: 327 | v = (c & 0xFFFFFF) << (8 * (nbytes - 3)) 328 | return v 329 | 330 | def compact_from_uint256(v): 331 | """Convert uint256 to compact encoding 332 | """ 333 | nbytes = (v.bit_length() + 7) >> 3 334 | compact = 0 335 | if nbytes <= 3: 336 | compact = (v & 0xFFFFFF) << 8 * (3 - nbytes) 337 | else: 338 | compact = v >> 8 * (nbytes - 3) 339 | compact = compact & 0xFFFFFF 340 | 341 | # If the sign bit (0x00800000) is set, divide the mantissa by 256 and 342 | # increase the exponent to get an encoding without it set. 343 | if compact & 0x00800000: 344 | compact >>= 8 345 | nbytes += 1 346 | 347 | return compact | nbytes << 24 348 | 349 | def uint256_to_str(u): 350 | r = b"" 351 | for i in range(8): 352 | r += struct.pack('> (i * 32) & 0xffffffff) 353 | return r 354 | 355 | def uint256_to_shortstr(u): 356 | s = "%064x" % (u,) 357 | return s[:16] 358 | 359 | __all__ = ( 360 | 'MAX_SIZE', 361 | 'Hash', 362 | 'Hash160', 363 | 'SerializationError', 364 | 'SerializationTruncationError', 365 | 'DeserializationExtraDataError', 366 | 'ser_read', 367 | 'Serializable', 368 | 'ImmutableSerializable', 369 | 'Serializer', 370 | 'VarIntSerializer', 371 | 'BytesSerializer', 372 | 'VectorSerializer', 373 | 'uint256VectorSerializer', 374 | 'intVectorSerializer', 375 | 'VarStringSerializer', 376 | 'uint256_from_str', 377 | 'uint256_from_compact', 378 | 'compact_from_uint256', 379 | 'uint256_to_str', 380 | 'uint256_to_shortstr', 381 | ) 382 | -------------------------------------------------------------------------------- /bitcointx/tests/test_script.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013-2015 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | from __future__ import absolute_import, division, print_function, unicode_literals 13 | 14 | import unittest 15 | import os 16 | 17 | from bitcointx.core import b2x,x 18 | from bitcointx.core.script import * 19 | 20 | class Test_CScriptOp(unittest.TestCase): 21 | def test_pushdata(self): 22 | def T(data, expected): 23 | data = x(data) 24 | expected = x(expected) 25 | serialized_data = CScriptOp.encode_op_pushdata(data) 26 | self.assertEqual(serialized_data, expected) 27 | 28 | T('', '00') 29 | T('00', '0100') 30 | T('0011223344556677', '080011223344556677') 31 | T('ff'*0x4b, '4b' + 'ff'*0x4b) 32 | T('ff'*0x4c, '4c4c' + 'ff'*0x4c) 33 | T('ff'*0x4c, '4c4c' + 'ff'*0x4c) 34 | T('ff'*0xff, '4cff' + 'ff'*0xff) 35 | T('ff'*0x100, '4d0001' + 'ff'*0x100) 36 | T('ff'*0xffff, '4dffff' + 'ff'*0xffff) 37 | T('ff'*0x10000, '4e00000100' + 'ff'*0x10000) 38 | 39 | def test_is_singleton(self): 40 | self.assertTrue(OP_0 is CScriptOp(0x00)) 41 | self.assertTrue(OP_1 is CScriptOp(0x51)) 42 | self.assertTrue(OP_16 is CScriptOp(0x60)) 43 | self.assertTrue(OP_CHECKSIG is CScriptOp(0xac)) 44 | 45 | for i in range(0x0, 0x100): 46 | self.assertTrue(CScriptOp(i) is CScriptOp(i)) 47 | 48 | def test_encode_decode_op_n(self): 49 | def t(n, op): 50 | actual = CScriptOp.encode_op_n(n) 51 | self.assertEqual(actual, op) 52 | self.assertTrue(isinstance(actual, CScriptOp)) 53 | 54 | actual = op.decode_op_n() 55 | self.assertEqual(actual, n) 56 | self.assertTrue(isinstance(actual, int)) 57 | 58 | t(0, OP_0) 59 | t(1, OP_1) 60 | t(2, OP_2) 61 | t(3, OP_3) 62 | t(4, OP_4) 63 | t(5, OP_5) 64 | t(6, OP_6) 65 | t(7, OP_7) 66 | t(8, OP_8) 67 | t(9, OP_9) 68 | t(9, OP_9) 69 | t(10, OP_10) 70 | t(11, OP_11) 71 | t(12, OP_12) 72 | t(13, OP_13) 73 | t(14, OP_14) 74 | t(15, OP_15) 75 | t(16, OP_16) 76 | 77 | with self.assertRaises(ValueError): 78 | OP_CHECKSIG.decode_op_n() 79 | 80 | with self.assertRaises(ValueError): 81 | CScriptOp(1).decode_op_n() 82 | 83 | class Test_CScript(unittest.TestCase): 84 | def test_tokenize_roundtrip(self): 85 | def T(serialized_script, expected_tokens, test_roundtrip=True): 86 | serialized_script = x(serialized_script) 87 | script_obj = CScript(serialized_script) 88 | actual_tokens = list(script_obj) 89 | self.assertEqual(actual_tokens, expected_tokens) 90 | 91 | if test_roundtrip: 92 | recreated_script = CScript(actual_tokens) 93 | self.assertEqual(recreated_script, serialized_script) 94 | 95 | T('', []) 96 | 97 | # standard pushdata 98 | T('00', [OP_0]) 99 | T('0100', [b'\x00']) 100 | T('4b' + 'ff'*0x4b, [b'\xff'*0x4b]) 101 | 102 | # non-optimal pushdata 103 | T('4c00', [b''], False) 104 | T('4c04deadbeef', [x('deadbeef')], False) 105 | T('4d0000', [b''], False) 106 | T('4d0400deadbeef', [x('deadbeef')], False) 107 | T('4e00000000', [b''], False) 108 | T('4e04000000deadbeef', [x('deadbeef')], False) 109 | 110 | # numbers 111 | T('00', [0x0]) 112 | T('4f', [OP_1NEGATE]) 113 | T('51', [0x1]) 114 | T('52', [0x2]) 115 | T('53', [0x3]) 116 | T('54', [0x4]) 117 | T('55', [0x5]) 118 | T('56', [0x6]) 119 | T('57', [0x7]) 120 | T('58', [0x8]) 121 | T('59', [0x9]) 122 | T('5a', [0xa]) 123 | T('5b', [0xb]) 124 | T('5c', [0xc]) 125 | T('5d', [0xd]) 126 | T('5e', [0xe]) 127 | T('5f', [0xf]) 128 | 129 | # some opcodes 130 | T('9b', [OP_BOOLOR]) 131 | T('9a9b', [OP_BOOLAND, OP_BOOLOR]) 132 | T('ff', [OP_INVALIDOPCODE]) 133 | T('fafbfcfd', [CScriptOp(0xfa), CScriptOp(0xfb), CScriptOp(0xfc), CScriptOp(0xfd)]) 134 | 135 | # all three types 136 | T('512103e2a0e6a91fa985ce4dda7f048fca5ec8264292aed9290594321aa53d37fdea32410478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc345552ae', 137 | [1, 138 | x('03e2a0e6a91fa985ce4dda7f048fca5ec8264292aed9290594321aa53d37fdea32'), 139 | x('0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455'), 140 | 2, 141 | OP_CHECKMULTISIG]) 142 | 143 | def test_invalid_scripts(self): 144 | def T(serialized): 145 | with self.assertRaises(CScriptInvalidError): 146 | list(CScript(x(serialized))) 147 | 148 | T('01') 149 | T('02') 150 | T('0201') 151 | T('4b') 152 | T('4b' + 'ff'*0x4a) 153 | T('4c') 154 | T('4cff' + 'ff'*0xfe) 155 | T('4d') 156 | T('4dff') 157 | T('4dffff' + 'ff'*0xfffe) 158 | T('4e') 159 | T('4effffff') 160 | T('4effffffff' + 'ff'*0xfffe) # not going to test with 4GiB-1... 161 | 162 | def test_equality(self): 163 | # Equality is on the serialized script, not the logical meaning. 164 | # This is important for P2SH. 165 | def T(serialized1, serialized2, are_equal): 166 | script1 = CScript(x(serialized1)) 167 | script2 = CScript(x(serialized2)) 168 | if are_equal: 169 | self.assertEqual(script1, script2) 170 | else: 171 | self.assertNotEqual(script1, script2) 172 | 173 | T('', '', True) 174 | T('', '00', False) 175 | T('00', '00', True) 176 | T('00', '01', False) 177 | T('01ff', '01ff', True) 178 | T('fc01ff', '01ff', False) 179 | 180 | # testing equality on an invalid script is legal, and evaluates based 181 | # on the serialization 182 | T('4e', '4e', True) 183 | T('4e', '4e00', False) 184 | 185 | def test_add(self): 186 | script = CScript() 187 | script2 = script + 1 188 | 189 | # + operator must create a new instance 190 | self.assertIsNot(script, script2) 191 | 192 | script = script2 193 | self.assertEqual(script, b'\x51') 194 | 195 | script += 2 196 | # += should not be done in place 197 | self.assertIsNot(script, script2) 198 | self.assertEqual(script, b'\x51\x52') 199 | 200 | script += OP_CHECKSIG 201 | self.assertEqual(script, b'\x51\x52\xac') 202 | 203 | script += b'deadbeef' 204 | self.assertEqual(script, b'\x51\x52\xac\x08deadbeef') 205 | 206 | script = CScript() + 1 + 2 + OP_CHECKSIG + b'deadbeef' 207 | self.assertEqual(script, b'\x51\x52\xac\x08deadbeef') 208 | 209 | # big number 210 | script = CScript() + 2**64 211 | self.assertEqual(script, b'\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01') 212 | 213 | # some stuff we can't add 214 | with self.assertRaises(TypeError): 215 | script += None 216 | self.assertEqual(script, b'\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01') 217 | 218 | with self.assertRaises(TypeError): 219 | script += [1, 2, 3] 220 | self.assertEqual(script, b'\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01') 221 | 222 | with self.assertRaises(TypeError): 223 | script = script + None 224 | self.assertEqual(script, b'\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01') 225 | 226 | def test_repr(self): 227 | def T(script, expected_repr): 228 | actual_repr = repr(script) 229 | self.assertEqual(actual_repr, expected_repr) 230 | 231 | T( CScript([]), 232 | 'CScript([])') 233 | 234 | T( CScript([1]), 235 | 'CScript([1])') 236 | 237 | T( CScript([1, 2, 3]), 238 | 'CScript([1, 2, 3])') 239 | 240 | T( CScript([1, x('7ac977d8373df875eceda362298e5d09d4b72b53'), OP_DROP]), 241 | "CScript([1, x('7ac977d8373df875eceda362298e5d09d4b72b53'), OP_DROP])") 242 | 243 | T(CScript(x('0001ff515261ff')), 244 | "CScript([0, x('ff'), 1, 2, OP_NOP, OP_INVALIDOPCODE])") 245 | 246 | # truncated scripts 247 | T(CScript(x('6101')), 248 | "CScript([OP_NOP, x('')...])") 249 | 250 | T(CScript(x('614bff')), 251 | "CScript([OP_NOP, x('ff')...])") 252 | 253 | T(CScript(x('614c')), 254 | "CScript([OP_NOP, ])") 255 | 256 | T(CScript(x('614c0200')), 257 | "CScript([OP_NOP, x('00')...])") 258 | 259 | def test_is_p2sh(self): 260 | def T(serialized, b): 261 | script = CScript(x(serialized)) 262 | self.assertEqual(script.is_p2sh(), b) 263 | 264 | # standard P2SH 265 | T('a9146567e91196c49e1dffd09d5759f6bbc0c6d4c2e587', True) 266 | 267 | # NOT a P2SH txout due to the non-optimal PUSHDATA encoding 268 | T('a94c146567e91196c49e1dffd09d5759f6bbc0c6d4c2e587', False) 269 | 270 | def test_is_push_only(self): 271 | def T(serialized, b): 272 | script = CScript(x(serialized)) 273 | self.assertEqual(script.is_push_only(), b) 274 | 275 | T('', True) 276 | T('00', True) 277 | T('0101', True) 278 | T('4c00', True) 279 | T('4d0000', True) 280 | T('4e00000000', True) 281 | T('4f', True) 282 | 283 | # OP_RESERVED *is* considered to be a pushdata op by is_push_only! 284 | # Or specifically, the IsPushOnly() used in P2SH validation. 285 | T('50', True) 286 | 287 | T('51', True) 288 | T('52', True) 289 | T('53', True) 290 | T('54', True) 291 | T('55', True) 292 | T('56', True) 293 | T('57', True) 294 | T('58', True) 295 | T('59', True) 296 | T('5a', True) 297 | T('5b', True) 298 | T('5c', True) 299 | T('5d', True) 300 | T('5e', True) 301 | T('5f', True) 302 | T('60', True) 303 | 304 | T('61', False) 305 | 306 | def test_is_push_only_on_invalid_pushdata(self): 307 | def T(hex_script): 308 | invalid_script = CScript(x(hex_script)) 309 | self.assertFalse(invalid_script.is_push_only()) 310 | 311 | T('01') 312 | T('02ff') 313 | T('4b') 314 | T('4c01') 315 | T('4c02ff') 316 | T('4d') 317 | T('4d0100') 318 | T('4d0200ff') 319 | T('4e') 320 | T('4e01000000') 321 | T('4e02000000ff') 322 | 323 | def test_has_canonical_pushes(self): 324 | def T(hex_script, expected_result): 325 | script = CScript(x(hex_script)) 326 | self.assertEqual(script.has_canonical_pushes(), expected_result) 327 | 328 | T('', True) 329 | T('00', True) 330 | T('FF', True) 331 | 332 | # could have used an OP_n code, rather than a 1-byte push 333 | T('0100', False) 334 | T('0101', False) 335 | T('0102', False) 336 | T('0103', False) 337 | T('0104', False) 338 | T('0105', False) 339 | T('0106', False) 340 | T('0107', False) 341 | T('0108', False) 342 | T('0109', False) 343 | T('010A', False) 344 | T('010B', False) 345 | T('010C', False) 346 | T('010D', False) 347 | T('010E', False) 348 | T('010F', False) 349 | T('0110', False) 350 | T('0111', True) 351 | 352 | # Could have used a normal n-byte push, rather than OP_PUSHDATA1 353 | T('4c00', False) 354 | T('4c0100', False) 355 | T('4c01FF', False) 356 | T('4b' + '00'*75, True) 357 | T('4c4b' + '00'*75, False) 358 | T('4c4c' + '00'*76, True) 359 | 360 | # Could have used a OP_PUSHDATA1. 361 | T('4d0000', False) 362 | T('4d0100FF', False) 363 | T('4dFF00' + 'FF'*0xFF, False) 364 | T('4d0001' + 'FF'*0x100, True) 365 | 366 | # Could have used a OP_PUSHDATA2. 367 | T('4e00000000', False) 368 | T('4e01000000FF', False) 369 | T('4eFFFF0000' + 'FF'*0xFFFF, False) 370 | T('4e00000100' + 'FF'*0x10000, True) 371 | 372 | def test_has_canonical_pushes_with_invalid_truncated_script(self): 373 | def T(hex_script): 374 | script = CScript(x(hex_script)) 375 | self.assertEqual(script.has_canonical_pushes(), False) 376 | 377 | T('01') 378 | T('02ff') 379 | T('4b') 380 | T('4c01') 381 | T('4c02ff') 382 | T('4d') 383 | T('4d0100') 384 | T('4d0200ff') 385 | T('4e') 386 | T('4e01000000') 387 | T('4e02000000ff') 388 | 389 | def test_is_unspendable(self): 390 | def T(serialized, b): 391 | script = CScript(x(serialized)) 392 | self.assertEqual(script.is_unspendable(), b) 393 | 394 | T('', False) 395 | T('00', False) 396 | T('006a', False) 397 | T('6a', True) 398 | T('6a6a', True) 399 | T('6a51', True) 400 | 401 | def test_is_valid(self): 402 | def T(serialized, b): 403 | script = CScript(x(serialized)) 404 | self.assertEqual(script.is_valid(), b) 405 | 406 | T('', True) 407 | T('00', True) 408 | T('01', False) 409 | 410 | # invalid opcodes do not by themselves make a script invalid 411 | T('ff', True) 412 | 413 | def test_to_p2sh_scriptPubKey(self): 414 | def T(redeemScript, expected_hex_bytes): 415 | redeemScript = CScript(redeemScript) 416 | actual_script = redeemScript.to_p2sh_scriptPubKey() 417 | self.assertEqual(b2x(actual_script), expected_hex_bytes) 418 | 419 | T([], 420 | 'a914b472a266d0bd89c13706a4132ccfb16f7c3b9fcb87') 421 | 422 | T([1,x('029b6d2c97b8b7c718c325d7be3ac30f7c9d67651bce0c929f55ee77ce58efcf84'),1,OP_CHECKMULTISIG], 423 | 'a91419a7d869032368fd1f1e26e5e73a4ad0e474960e87') 424 | 425 | T([b'\xff'*517], 426 | 'a9140da7fa40ebf248dfbca363c79921bdd665fed5ba87') 427 | 428 | with self.assertRaises(ValueError): 429 | CScript([b'a' * 518]).to_p2sh_scriptPubKey() 430 | 431 | class Test_IsLowDERSignature(unittest.TestCase): 432 | def test_high_s_value(self): 433 | sig = x('3046022100820121109528efda8bb20ca28788639e5ba5b365e0a84f8bd85744321e7312c6022100a7c86a21446daa405306fe10d0a9906e37d1a2c6b6fdfaaf6700053058029bbe') 434 | self.assertFalse(IsLowDERSignature(sig)) 435 | def test_low_s_value(self): 436 | sig = x('3045022100b135074e08cc93904a1712b2600d3cb01899a5b1cc7498caa4b8585bcf5f27e7022074ab544045285baef0a63f0fb4c95e577dcbf5c969c0bf47c7da8e478909d669') 437 | self.assertTrue(IsLowDERSignature(sig)) 438 | -------------------------------------------------------------------------------- /bitcointx/wallet.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2012-2014 The python-bitcoinlib developers 2 | # 3 | # This file is part of python-bitcoinlib. 4 | # 5 | # It is subject to the license terms in the LICENSE file found in the top-level 6 | # directory of this distribution. 7 | # 8 | # No part of python-bitcoinlib, including this file, may be copied, modified, 9 | # propagated, or distributed except according to the terms contained in the 10 | # LICENSE file. 11 | 12 | """Wallet-related functionality 13 | 14 | Includes things like representing addresses and converting them to/from 15 | scriptPubKeys; currently there is no actual wallet support implemented. 16 | """ 17 | 18 | from __future__ import absolute_import, division, print_function, unicode_literals 19 | 20 | import array 21 | import sys 22 | 23 | _bord = ord 24 | _tobytes = lambda x: array.array('B', x).tostring() 25 | if sys.version > '3': 26 | _bord = lambda x: x 27 | _tobytes = bytes 28 | 29 | import bitcointx 30 | import bitcointx.base58 31 | import bitcointx.bech32 32 | import bitcointx.core 33 | import bitcointx.core.key 34 | import bitcointx.core.script as script 35 | 36 | 37 | class CBitcoinAddress(object): 38 | 39 | def __new__(cls, s): 40 | try: 41 | return CBech32BitcoinAddress(s) 42 | except bitcointx.bech32.Bech32Error: 43 | pass 44 | 45 | try: 46 | return CBase58BitcoinAddress(s) 47 | except bitcointx.base58.Base58Error: 48 | pass 49 | 50 | raise CBitcoinAddressError('Unrecognized encoding for bitcoin address') 51 | 52 | @classmethod 53 | def from_scriptPubKey(cls, scriptPubKey): 54 | """Convert a scriptPubKey to a subclass of CBitcoinAddress""" 55 | try: 56 | return CBech32BitcoinAddress.from_scriptPubKey(scriptPubKey) 57 | except CBitcoinAddressError: 58 | pass 59 | 60 | try: 61 | return CBase58BitcoinAddress.from_scriptPubKey(scriptPubKey) 62 | except CBitcoinAddressError: 63 | pass 64 | 65 | raise CBitcoinAddressError('scriptPubKey is not in a recognized address format') 66 | 67 | 68 | class CBitcoinAddressError(Exception): 69 | """Raised when an invalid Bitcoin address is encountered""" 70 | 71 | 72 | class CBech32BitcoinAddress(bitcointx.bech32.CBech32Data, CBitcoinAddress): 73 | """A Bech32-encoded Bitcoin address""" 74 | 75 | @classmethod 76 | def from_bytes(cls, witver, witprog): 77 | 78 | assert witver == 0 79 | self = super(CBech32BitcoinAddress, cls).from_bytes( 80 | witver, 81 | _tobytes(witprog) 82 | ) 83 | 84 | if len(self) == 32: 85 | self.__class__ = P2WSHBitcoinAddress 86 | elif len(self) == 20: 87 | self.__class__ = P2WPKHBitcoinAddress 88 | else: 89 | raise CBitcoinAddressError('witness program does not match any known segwit address format') 90 | 91 | return self 92 | 93 | @classmethod 94 | def from_scriptPubKey(cls, scriptPubKey): 95 | """Convert a scriptPubKey to a CBech32BitcoinAddress 96 | 97 | Returns a CBech32BitcoinAddress subclass, either P2WSHBitcoinAddress or 98 | P2WPKHBitcoinAddress. If the scriptPubKey is not recognized 99 | CBitcoinAddressError will be raised. 100 | """ 101 | try: 102 | return P2WSHBitcoinAddress.from_scriptPubKey(scriptPubKey) 103 | except CBitcoinAddressError: 104 | pass 105 | 106 | try: 107 | return P2WPKHBitcoinAddress.from_scriptPubKey(scriptPubKey) 108 | except CBitcoinAddressError: 109 | pass 110 | 111 | raise CBitcoinAddressError('scriptPubKey not a valid bech32-encoded address') 112 | 113 | 114 | class CBase58BitcoinAddress(bitcointx.base58.CBase58Data, CBitcoinAddress): 115 | """A Base58-encoded Bitcoin address""" 116 | 117 | @classmethod 118 | def from_bytes(cls, data, nVersion): 119 | self = super(CBase58BitcoinAddress, cls).from_bytes(data, nVersion) 120 | 121 | if nVersion == bitcointx.params.BASE58_PREFIXES['SCRIPT_ADDR']: 122 | self.__class__ = P2SHBitcoinAddress 123 | 124 | elif nVersion == bitcointx.params.BASE58_PREFIXES['PUBKEY_ADDR']: 125 | self.__class__ = P2PKHBitcoinAddress 126 | 127 | else: 128 | raise CBitcoinAddressError('Version %d not a recognized Bitcoin Address' % nVersion) 129 | 130 | return self 131 | 132 | @classmethod 133 | def from_scriptPubKey(cls, scriptPubKey): 134 | """Convert a scriptPubKey to a CBitcoinAddress 135 | 136 | Returns a CBitcoinAddress subclass, either P2SHBitcoinAddress or 137 | P2PKHBitcoinAddress. If the scriptPubKey is not recognized 138 | CBitcoinAddressError will be raised. 139 | """ 140 | try: 141 | return P2SHBitcoinAddress.from_scriptPubKey(scriptPubKey) 142 | except CBitcoinAddressError: 143 | pass 144 | 145 | try: 146 | return P2PKHBitcoinAddress.from_scriptPubKey(scriptPubKey) 147 | except CBitcoinAddressError: 148 | pass 149 | 150 | raise CBitcoinAddressError('scriptPubKey not a valid base58-encoded address') 151 | 152 | 153 | class P2SHBitcoinAddress(CBase58BitcoinAddress): 154 | @classmethod 155 | def from_bytes(cls, data, nVersion=None): 156 | if nVersion is None: 157 | nVersion = bitcointx.params.BASE58_PREFIXES['SCRIPT_ADDR'] 158 | 159 | elif nVersion != bitcointx.params.BASE58_PREFIXES['SCRIPT_ADDR']: 160 | raise ValueError('nVersion incorrect for P2SH address: got %d; expected %d' % \ 161 | (nVersion, bitcointx.params.BASE58_PREFIXES['SCRIPT_ADDR'])) 162 | 163 | return super(P2SHBitcoinAddress, cls).from_bytes(data, nVersion) 164 | 165 | @classmethod 166 | def from_redeemScript(cls, redeemScript): 167 | """Convert a redeemScript to a P2SH address 168 | 169 | Convenience function: equivalent to P2SHBitcoinAddress.from_scriptPubKey(redeemScript.to_p2sh_scriptPubKey()) 170 | """ 171 | return cls.from_scriptPubKey(redeemScript.to_p2sh_scriptPubKey()) 172 | 173 | @classmethod 174 | def from_scriptPubKey(cls, scriptPubKey): 175 | """Convert a scriptPubKey to a P2SH address 176 | 177 | Raises CBitcoinAddressError if the scriptPubKey isn't of the correct 178 | form. 179 | """ 180 | if scriptPubKey.is_p2sh(): 181 | return cls.from_bytes(scriptPubKey[2:22], bitcointx.params.BASE58_PREFIXES['SCRIPT_ADDR']) 182 | 183 | else: 184 | raise CBitcoinAddressError('not a P2SH scriptPubKey') 185 | 186 | def to_scriptPubKey(self): 187 | """Convert an address to a scriptPubKey""" 188 | assert self.nVersion == bitcointx.params.BASE58_PREFIXES['SCRIPT_ADDR'] 189 | return script.CScript([script.OP_HASH160, self, script.OP_EQUAL]) 190 | 191 | def to_redeemScript(self): 192 | return self.to_scriptPubKey() 193 | 194 | 195 | class P2PKHBitcoinAddress(CBase58BitcoinAddress): 196 | @classmethod 197 | def from_bytes(cls, data, nVersion=None): 198 | if nVersion is None: 199 | nVersion = bitcointx.params.BASE58_PREFIXES['PUBKEY_ADDR'] 200 | 201 | elif nVersion != bitcointx.params.BASE58_PREFIXES['PUBKEY_ADDR']: 202 | raise ValueError('nVersion incorrect for P2PKH address: got %d; expected %d' % \ 203 | (nVersion, bitcointx.params.BASE58_PREFIXES['PUBKEY_ADDR'])) 204 | 205 | return super(P2PKHBitcoinAddress, cls).from_bytes(data, nVersion) 206 | 207 | @classmethod 208 | def from_pubkey(cls, pubkey, accept_invalid=False): 209 | """Create a P2PKH bitcoin address from a pubkey 210 | 211 | Raises CBitcoinAddressError if pubkey is invalid, unless accept_invalid 212 | is True. 213 | 214 | The pubkey must be a bytes instance; CECKey instances are not accepted. 215 | """ 216 | if not isinstance(pubkey, bytes): 217 | raise TypeError('pubkey must be bytes instance; got %r' % pubkey.__class__) 218 | 219 | if not accept_invalid: 220 | if not isinstance(pubkey, bitcointx.core.key.CPubKey): 221 | pubkey = bitcointx.core.key.CPubKey(pubkey) 222 | if not pubkey.is_fullyvalid: 223 | raise CBitcoinAddressError('invalid pubkey') 224 | 225 | pubkey_hash = bitcointx.core.Hash160(pubkey) 226 | return P2PKHBitcoinAddress.from_bytes(pubkey_hash) 227 | 228 | @classmethod 229 | def from_scriptPubKey(cls, scriptPubKey, accept_non_canonical_pushdata=True, accept_bare_checksig=True): 230 | """Convert a scriptPubKey to a P2PKH address 231 | 232 | Raises CBitcoinAddressError if the scriptPubKey isn't of the correct 233 | form. 234 | 235 | accept_non_canonical_pushdata - Allow non-canonical pushes (default True) 236 | 237 | accept_bare_checksig - Treat bare-checksig as P2PKH scriptPubKeys (default True) 238 | """ 239 | if accept_non_canonical_pushdata: 240 | # Canonicalize script pushes 241 | scriptPubKey = script.CScript(scriptPubKey) # in case it's not a CScript instance yet 242 | 243 | try: 244 | scriptPubKey = script.CScript(tuple(scriptPubKey)) # canonicalize 245 | except bitcointx.core.script.CScriptInvalidError: 246 | raise CBitcoinAddressError('not a P2PKH scriptPubKey: script is invalid') 247 | 248 | if scriptPubKey.is_witness_v0_keyhash(): 249 | return cls.from_bytes(scriptPubKey[2:22], bitcointx.params.BASE58_PREFIXES['PUBKEY_ADDR']) 250 | elif scriptPubKey.is_witness_v0_nested_keyhash(): 251 | return cls.from_bytes(scriptPubKey[3:23], bitcointx.params.BASE58_PREFIXES['PUBKEY_ADDR']) 252 | elif (len(scriptPubKey) == 25 253 | and _bord(scriptPubKey[0]) == script.OP_DUP 254 | and _bord(scriptPubKey[1]) == script.OP_HASH160 255 | and _bord(scriptPubKey[2]) == 0x14 256 | and _bord(scriptPubKey[23]) == script.OP_EQUALVERIFY 257 | and _bord(scriptPubKey[24]) == script.OP_CHECKSIG): 258 | return cls.from_bytes(scriptPubKey[3:23], bitcointx.params.BASE58_PREFIXES['PUBKEY_ADDR']) 259 | 260 | elif accept_bare_checksig: 261 | pubkey = None 262 | 263 | # We can operate on the raw bytes directly because we've 264 | # canonicalized everything above. 265 | if (len(scriptPubKey) == 35 # compressed 266 | and _bord(scriptPubKey[0]) == 0x21 267 | and _bord(scriptPubKey[34]) == script.OP_CHECKSIG): 268 | 269 | pubkey = scriptPubKey[1:34] 270 | 271 | elif (len(scriptPubKey) == 67 # uncompressed 272 | and _bord(scriptPubKey[0]) == 0x41 273 | and _bord(scriptPubKey[66]) == script.OP_CHECKSIG): 274 | 275 | pubkey = scriptPubKey[1:65] 276 | 277 | if pubkey is not None: 278 | return cls.from_pubkey(pubkey, accept_invalid=True) 279 | 280 | raise CBitcoinAddressError('not a P2PKH scriptPubKey') 281 | 282 | def to_scriptPubKey(self, nested=False): 283 | """Convert an address to a scriptPubKey""" 284 | assert self.nVersion == bitcointx.params.BASE58_PREFIXES['PUBKEY_ADDR'] 285 | return script.CScript([script.OP_DUP, script.OP_HASH160, self, script.OP_EQUALVERIFY, script.OP_CHECKSIG]) 286 | 287 | def to_redeemScript(self): 288 | return self.to_scriptPubKey() 289 | 290 | 291 | class P2WSHBitcoinAddress(CBech32BitcoinAddress): 292 | 293 | @classmethod 294 | def from_scriptPubKey(cls, scriptPubKey): 295 | """Convert a scriptPubKey to a P2WSH address 296 | 297 | Raises CBitcoinAddressError if the scriptPubKey isn't of the correct 298 | form. 299 | """ 300 | if scriptPubKey.is_witness_v0_scripthash(): 301 | return cls.from_bytes(0, scriptPubKey[2:34]) 302 | else: 303 | raise CBitcoinAddressError('not a P2WSH scriptPubKey') 304 | 305 | def to_scriptPubKey(self): 306 | """Convert an address to a scriptPubKey""" 307 | assert self.witver == 0 308 | return script.CScript([0, self]) 309 | 310 | def to_redeemScript(self): 311 | return NotImplementedError("not enough data in p2wsh address to reconstruct redeem script") 312 | 313 | 314 | class P2WPKHBitcoinAddress(CBech32BitcoinAddress): 315 | 316 | @classmethod 317 | def from_scriptPubKey(cls, scriptPubKey): 318 | """Convert a scriptPubKey to a P2WSH address 319 | 320 | Raises CBitcoinAddressError if the scriptPubKey isn't of the correct 321 | form. 322 | """ 323 | if scriptPubKey.is_witness_v0_keyhash(): 324 | return cls.from_bytes(0, scriptPubKey[2:22]) 325 | else: 326 | raise CBitcoinAddressError('not a P2WSH scriptPubKey') 327 | 328 | def to_scriptPubKey(self): 329 | """Convert an address to a scriptPubKey""" 330 | assert self.witver == 0 331 | return script.CScript([0, self]) 332 | 333 | def to_redeemScript(self): 334 | return script.CScript([script.OP_DUP, script.OP_HASH160, self, script.OP_EQUALVERIFY, script.OP_CHECKSIG]) 335 | 336 | class CKey(object): 337 | """An encapsulated private key 338 | 339 | Attributes: 340 | 341 | pub - The corresponding CPubKey for this private key 342 | 343 | is_compressed - True if compressed 344 | 345 | """ 346 | def __init__(self, secret, compressed=True): 347 | self._cec_key = bitcointx.core.key.CECKey() 348 | self._cec_key.set_secretbytes(secret) 349 | self._cec_key.set_compressed(compressed) 350 | 351 | self.pub = bitcointx.core.key.CPubKey(self._cec_key.get_pubkey(), self._cec_key) 352 | 353 | @property 354 | def is_compressed(self): 355 | return self.pub.is_compressed 356 | 357 | def sign(self, hash): 358 | return self._cec_key.sign(hash) 359 | 360 | def sign_compact(self, hash): 361 | return self._cec_key.sign_compact(hash) 362 | 363 | class CBitcoinSecretError(bitcointx.base58.Base58Error): 364 | pass 365 | 366 | class CBitcoinSecret(bitcointx.base58.CBase58Data, CKey): 367 | """A base58-encoded secret key""" 368 | 369 | @classmethod 370 | def from_secret_bytes(cls, secret, compressed=True): 371 | """Create a secret key from a 32-byte secret""" 372 | self = cls.from_bytes(secret + (b'\x01' if compressed else b''), 373 | bitcointx.params.BASE58_PREFIXES['SECRET_KEY']) 374 | self.__init__(None) 375 | return self 376 | 377 | def __init__(self, s): 378 | if self.nVersion != bitcointx.params.BASE58_PREFIXES['SECRET_KEY']: 379 | raise CBitcoinSecretError('Not a base58-encoded secret key: got nVersion=%d; expected nVersion=%d' % \ 380 | (self.nVersion, bitcointx.params.BASE58_PREFIXES['SECRET_KEY'])) 381 | 382 | CKey.__init__(self, self[0:32], len(self) > 32 and _bord(self[32]) == 1) 383 | 384 | 385 | __all__ = ( 386 | 'CBitcoinAddressError', 387 | 'CBitcoinAddress', 388 | 'CBase58BitcoinAddress', 389 | 'CBech32BitcoinAddress', 390 | 'P2SHBitcoinAddress', 391 | 'P2PKHBitcoinAddress', 392 | 'P2WSHBitcoinAddress', 393 | 'P2WPKHBitcoinAddress', 394 | 'CKey', 395 | 'CBitcoinSecretError', 396 | 'CBitcoinSecret', 397 | ) 398 | --------------------------------------------------------------------------------