Skip to content

Commit f765ce6

Browse files
committed
Restructure to separate out consensus critical code
All consensus critical is now under bitcoin.core Also improved the README
1 parent 3ed76f0 commit f765ce6

21 files changed

+377
-282
lines changed

README

+36-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,44 @@
1+
python-bitcoinlib
2+
-----------------
13

2-
This python library provides an easy interface to the bitcoin
3-
data structures and protocol.
4+
This Python2/3 library provides an easy interface to the bitcoin data
5+
structures and protocol.
6+
7+
8+
Structure
9+
---------
10+
11+
Everything consensus critical is found in the modules under bitcoin.core:
12+
13+
bitcoin.core - Basic datastructures, CTransaction, CBlock etc.
14+
bitcoin.core.bignum - Bignum handling
15+
bitcoin.core.coredef - Core definitions, MAX_MONEY, etc.
16+
bitcoin.core.key - ECC pubkeys
17+
bitcoin.core.script - Scripts and opcodes
18+
bitcoin.core.scripteval - Script evaluation/verification
19+
bitcoin.core.serialize - Serialization
20+
21+
In the future the bitcoin.core may use the Satoshi sourcecode directly as a
22+
libary. Non-consensus critical modules include the following:
23+
24+
bitcoin.base58 - Base58 encoding
25+
bitcoin.bloom - Bloom filters (incomplete)
26+
bitcoin.hash - Non-cryptographic hashing
27+
bitcoin.net - Network communication (broken currently)
28+
bitcoin.messages - Network messages (broken currently)
29+
bitcoin.rpc - Bitcoin Core RPC interface support
30+
bitcoin.wallet - Wallet code, currently just CBitcoinAddress
31+
32+
Effort has been made to follow the Satoshi source relatively closely, for
33+
instance Python code and classes that duplicate the functionality of
34+
corresponding Satoshi C++ code uses the same naming conventions: CTransaction,
35+
CBlockHeader, nValue etc. Otherwise Python naming conventions are followed.
436

537

638
Unit tests
739
----------
840

41+
Under bitcoin/tests using test data from Bitcoin Core. To run them:
42+
943
python -m unittest discover
1044
python3 -m unittest discover

bitcoin/base58.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
bchr = lambda x: bytes([x])
1919
bord = lambda x: x
2020

21-
from bitcoin.serialize import Hash
21+
import binascii
2222

23-
b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
23+
import bitcoin.core
2424

25-
from binascii import hexlify, unhexlify
25+
b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
2626

2727
class Base58Error(Exception):
2828
pass
@@ -34,7 +34,7 @@ def encode(b):
3434
"""Encode bytes to a base58-encoded string"""
3535

3636
# Convert big-endian bytes to integer
37-
n = int('0x0' + hexlify(b).decode('utf8'), 16)
37+
n = int('0x0' + binascii.hexlify(b).decode('utf8'), 16)
3838

3939
# Divide that integer into bas58
4040
res = []
@@ -73,7 +73,7 @@ def decode(s):
7373
h = '%x' % n
7474
if len(h) % 2:
7575
h = '0' + h
76-
res = unhexlify(h.encode('utf8'))
76+
res = binascii.unhexlify(h.encode('utf8'))
7777

7878
# Add padding back.
7979
pad = 0
@@ -90,7 +90,7 @@ class CBase58Data(bytes):
9090
def __new__(cls, s):
9191
k = decode(s)
9292
addrbyte, data, check0 = k[0:1], k[1:-4], k[-4:]
93-
check1 = Hash(addrbyte + data)[:4]
93+
check1 = bitcoin.core.Hash(addrbyte + data)[:4]
9494
if check0 != check1:
9595
raise Base58ChecksumError('Checksum mismatch: expected %r, calculated %r' % (check0, check1))
9696
return cls.from_bytes(data, ord(addrbyte))
@@ -106,7 +106,7 @@ def to_bytes(self):
106106

107107
def __str__(self):
108108
vs = bchr(self.nVersion) + self
109-
check = Hash(vs)[0:4]
109+
check = bitcoin.core.Hash(vs)[0:4]
110110
return encode(vs + check)
111111

112112
def __repr__(self):

bitcoin/bloom.py

+11-11
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
import struct
1212
import sys
1313
import math
14-
from bitcoin.serialize import *
15-
from bitcoin.coredefs import *
16-
from bitcoin.core import *
17-
from bitcoin.hash import MurmurHash3
1814

19-
class CBloomFilter(Serializable):
15+
import bitcoin.core
16+
import bitcoin.core.serialize
17+
import bitcoin.hash
18+
19+
class CBloomFilter(bitcoin.core.serialize.Serializable):
2020
# 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001%
2121
MAX_BLOOM_FILTER_SIZE = 36000
2222
MAX_HASH_FUNCS = 50
@@ -51,15 +51,15 @@ def __init__(self, nElements, nFPRate, nTweak, nFlags):
5151
self.nFlags = nFlags
5252

5353
def bloom_hash(self, nHashNum, vDataToHash):
54-
return MurmurHash3(((nHashNum * 0xFBA4C795) + self.nTweak) & 0xFFFFFFFF, vDataToHash) % (len(self.vData) * 8)
54+
return bitcoin.hash.MurmurHash3(((nHashNum * 0xFBA4C795) + self.nTweak) & 0xFFFFFFFF, vDataToHash) % (len(self.vData) * 8)
5555

5656
__bit_mask = bytearray([0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80])
5757
def insert(self, elem):
5858
"""Insert an element in the filter.
5959
6060
elem may be a COutPoint or bytes
6161
"""
62-
if isinstance(elem, COutPoint):
62+
if isinstance(elem, bitcoin.core.COutPoint):
6363
elem = elem.serialize()
6464

6565
if len(self.vData) == 1 and self.vData[0] == 0xff:
@@ -75,7 +75,7 @@ def contains(self, elem):
7575
7676
elem may be a COutPoint or bytes
7777
"""
78-
if isinstance(elem, COutPoint):
78+
if isinstance(elem, bitcoin.core.COutPoint):
7979
elem = elem.serialize()
8080

8181
if len(self.vData) == 1 and self.vData[0] == 0xff:
@@ -97,7 +97,7 @@ def IsRelevantAndUpdate(tx, tx_hash):
9797
__struct = struct.Struct(b'<IIB')
9898
@classmethod
9999
def stream_deserialize(cls, f):
100-
vData = BytesSerializer.stream_deserialize(f)
100+
vData = bitcoin.core.serialize.BytesSerializer.stream_deserialize(f)
101101
(nHashFuncs,
102102
nTweak,
103103
nFlags) = self.__struct.unpack(_ser_read(f, self.__struct.size))
@@ -110,8 +110,8 @@ def stream_deserialize(cls, f):
110110

111111
def stream_serialize(self, f):
112112
if sys.version > '3':
113-
BytesSerializer.stream_serialize(self.vData, f)
113+
bitcoin.core.serialize.BytesSerializer.stream_serialize(self.vData, f)
114114
else:
115115
# 2.7 has problems with f.write(bytearray())
116-
BytesSerializer.stream_serialize(bytes(self.vData), f)
116+
bitcoin.core.serialize.BytesSerializer.stream_serialize(bytes(self.vData), f)
117117
f.write(self.__struct.pack(self.nHashFuncs, self.nTweak, self.nFlags))

bitcoin/core.py bitcoin/core/__init__.py

+5-147
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
import socket
1313
import binascii
1414
import hashlib
15-
import bitcoin.base58 as base58
16-
import bitcoin.script as script
1715

18-
from bitcoin.serialize import *
19-
from bitcoin.coredefs import *
16+
from .script import CScript
17+
18+
from .serialize import *
19+
from .coredefs import *
2020

2121
def x(h):
2222
"""Convert a hex string to bytes"""
@@ -64,85 +64,6 @@ def str_money_value(value):
6464
r += '0'
6565
return r
6666

67-
class CBitcoinAddress(base58.CBase58Data):
68-
"""A Bitcoin address"""
69-
PUBKEY_ADDRESS = 0
70-
SCRIPT_ADDRESS = 5
71-
PUBKEY_ADDRESS_TEST = 111
72-
SCRIPT_ADDRESS_TEST = 196
73-
74-
def to_scriptPubKey(self):
75-
"""Convert an address to a scriptPubKey"""
76-
if self.nVersion in (self.PUBKEY_ADDRESS, self.PUBKEY_ADDRESS_TEST):
77-
return script.CScript([script.OP_DUP, script.OP_HASH160, self, script.OP_EQUALVERIFY, script.OP_CHECKSIG])
78-
79-
elif self.nVersion in (self.SCRIPT_ADDRESS, self.SCRIPT_ADDRESS_TEST):
80-
return script.CScript([script.OP_HASH160, self, script.OP_EQUAL])
81-
82-
else:
83-
raise ValueError("CBitcoinAddress: Don't know how to convert version %d to a scriptPubKey" % self.nVersion)
84-
85-
86-
class CAddress(object):
87-
def __init__(self, protover=PROTO_VERSION):
88-
self.protover = protover
89-
self.nTime = 0
90-
self.nServices = 1
91-
self.pchReserved = b"\x00" * 10 + b"\xff" * 2
92-
self.ip = "0.0.0.0"
93-
self.port = 0
94-
def deserialize(self, f):
95-
if self.protover >= CADDR_TIME_VERSION:
96-
self.nTime = struct.unpack(b"<I", ser_read(f,4))[0]
97-
self.nServices = struct.unpack(b"<Q", ser_read(f,8))[0]
98-
self.pchReserved = ser_read(f,12)
99-
self.ip = socket.inet_ntoa(ser_read(f,4))
100-
self.port = struct.unpack(b">H", ser_read(f,2))[0]
101-
def serialize(self):
102-
r = b""
103-
if self.protover >= CADDR_TIME_VERSION:
104-
r += struct.pack(b"<I", self.nTime)
105-
r += struct.pack(b"<Q", self.nServices)
106-
r += self.pchReserved
107-
r += socket.inet_aton(self.ip)
108-
r += struct.pack(b">H", self.port)
109-
return r
110-
def __repr__(self):
111-
return "CAddress(nTime=%d nServices=%i ip=%s port=%i)" % (self.nTime, self.nServices, self.ip, self.port)
112-
113-
class CInv(object):
114-
typemap = {
115-
0: "Error",
116-
1: "TX",
117-
2: "Block"}
118-
def __init__(self):
119-
self.type = 0
120-
self.hash = 0
121-
def deserialize(self, f):
122-
self.type = struct.unpack(b"<i", ser_read(f,4))[0]
123-
self.hash = ser_read(f,32)
124-
def serialize(self):
125-
r = b""
126-
r += struct.pack(b"<i", self.type)
127-
r += self.hash
128-
return r
129-
def __repr__(self):
130-
return "CInv(type=%s hash=%064x)" % (self.typemap[self.type], self.hash)
131-
132-
class CBlockLocator(object):
133-
def __init__(self):
134-
self.nVersion = PROTO_VERSION
135-
self.vHave = []
136-
def deserialize(self, f):
137-
self.nVersion = struct.unpack(b"<i", ser_read(f,4))[0]
138-
self.vHave = deser_uint256_vector(f)
139-
def serialize(self):
140-
r = b""
141-
r += struct.pack(b"<i", self.nVersion)
142-
r += ser_uint256_vector(self.vHave)
143-
return r
144-
def __repr__(self):
145-
return "CBlockLocator(nVersion=%i vHave=%s)" % (self.nVersion, repr(self.vHave))
14667

14768
class COutPoint(Serializable):
14869
"""The combination of a transaction hash and an index n into its vout"""
@@ -184,7 +105,7 @@ class CTxIn(Serializable):
184105
"""
185106
__slots__ = ['prevout', 'scriptSig', 'nSequence']
186107

187-
def __init__(self, prevout=None, scriptSig=script.CScript(), nSequence = 0xffffffff):
108+
def __init__(self, prevout=None, scriptSig=CScript(), nSequence = 0xffffffff):
188109
if prevout is None:
189110
prevout = COutPoint()
190111
self.prevout = prevout
@@ -377,69 +298,6 @@ def calc_merkle_root(self):
377298
hashes.append(Hash(tx.serialize()))
378299
return CBlock.calc_merkle_root_from_hashes(hashes)
379300

380-
class CUnsignedAlert(object):
381-
def __init__(self):
382-
self.nVersion = 1
383-
self.nRelayUntil = 0
384-
self.nExpiration = 0
385-
self.nID = 0
386-
self.nCancel = 0
387-
self.setCancel = []
388-
self.nMinVer = 0
389-
self.nMaxVer = 0
390-
self.setSubVer = []
391-
self.nPriority = 0
392-
self.strComment = b""
393-
self.strStatusBar = b""
394-
self.strReserved = b""
395-
def deserialize(self, f):
396-
self.nVersion = struct.unpack(b"<i", ser_read(f,4))[0]
397-
self.nRelayUntil = struct.unpack(b"<q", ser_read(f,8))[0]
398-
self.nExpiration = struct.unpack(b"<q", ser_read(f,8))[0]
399-
self.nID = struct.unpack(b"<i", ser_read(f,4))[0]
400-
self.nCancel = struct.unpack(b"<i", ser_read(f,4))[0]
401-
self.setCancel = deser_int_vector(f)
402-
self.nMinVer = struct.unpack(b"<i", ser_read(f,4))[0]
403-
self.nMaxVer = struct.unpack(b"<i", ser_read(f,4))[0]
404-
self.setSubVer = deser_string_vector(f)
405-
self.nPriority = struct.unpack(b"<i", ser_read(f,4))[0]
406-
self.strComment = deser_string(f)
407-
self.strStatusBar = deser_string(f)
408-
self.strReserved = deser_string(f)
409-
def serialize(self):
410-
r = b""
411-
r += struct.pack(b"<i", self.nVersion)
412-
r += struct.pack(b"<q", self.nRelayUntil)
413-
r += struct.pack(b"<q", self.nExpiration)
414-
r += struct.pack(b"<i", self.nID)
415-
r += struct.pack(b"<i", self.nCancel)
416-
r += ser_int_vector(self.setCancel)
417-
r += struct.pack(b"<i", self.nMinVer)
418-
r += struct.pack(b"<i", self.nMaxVer)
419-
r += ser_string_vector(self.setSubVer)
420-
r += struct.pack(b"<i", self.nPriority)
421-
r += ser_string(self.strComment)
422-
r += ser_string(self.strStatusBar)
423-
r += ser_string(self.strReserved)
424-
return r
425-
def __repr__(self):
426-
return "CUnsignedAlert(nVersion %d, nRelayUntil %d, nExpiration %d, nID %d, nCancel %d, nMinVer %d, nMaxVer %d, nPriority %d, strComment %s, strStatusBar %s, strReserved %s)" % (self.nVersion, self.nRelayUntil, self.nExpiration, self.nID, self.nCancel, self.nMinVer, self.nMaxVer, self.nPriority, self.strComment, self.strStatusBar, self.strReserved)
427-
428-
class CAlert(object):
429-
def __init__(self):
430-
self.vchMsg = b""
431-
self.vchSig = b""
432-
def deserialize(self, f):
433-
self.vchMsg = deser_string(f)
434-
self.vchSig = deser_string(f)
435-
def serialize(self):
436-
r = b""
437-
r += ser_string(self.vchMsg)
438-
r += ser_string(self.vchSig)
439-
return r
440-
def __repr__(self):
441-
return "CAlert(vchMsg.sz %d, vchSig.sz %d)" % (len(self.vchMsg), len(self.vchSig))
442-
443301

444302
class CheckTransactionError(ValueError):
445303
pass

bitcoin/bignum.py bitcoin/core/bignum.py

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
77
#
88

9+
"""Bignum routines"""
10+
911
from __future__ import absolute_import, division, print_function, unicode_literals
1012

1113
import struct
File renamed without changes.

bitcoin/key.py bitcoin/core/key.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
NID_secp256k1 = 714 # from openssl/obj_mac.h
1616

1717
# Thx to Sam Devlin for the ctypes magic 64-bit fix.
18-
def check_result (val, func, args):
18+
def _check_result (val, func, args):
1919
if val == 0:
2020
raise ValueError
2121
else:
2222
return ctypes.c_void_p (val)
2323

2424
ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p
25-
ssl.EC_KEY_new_by_curve_name.errcheck = check_result
25+
ssl.EC_KEY_new_by_curve_name.errcheck = _check_result
2626

2727
class CKey:
2828
POINT_CONVERSION_COMPRESSED = 2

0 commit comments

Comments
 (0)