Extend copyright notice; improve comments
This commit is contained in:
parent
58a5e69fca
commit
d2ebb80fac
@ -1,8 +1,15 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
# See the file "LICENSE" for information about the copyright
|
# Copyright (c) 2016, Neil Booth
|
||||||
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# See the file "LICENCE" for information about the copyright
|
||||||
# and warranty status of this software.
|
# and warranty status of this software.
|
||||||
|
|
||||||
|
'''Script to send RPC commands to a running ElectrumX server.'''
|
||||||
|
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
|||||||
48
lib/coins.py
48
lib/coins.py
@ -1,6 +1,15 @@
|
|||||||
# See the file "LICENSE" for information about the copyright
|
# Copyright (c) 2016, Neil Booth
|
||||||
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# See the file "LICENCE" for information about the copyright
|
||||||
# and warranty status of this software.
|
# and warranty status of this software.
|
||||||
|
|
||||||
|
'''Module providing coin abstraction.
|
||||||
|
|
||||||
|
Anything coin-specific should go in this file and be subclassed where
|
||||||
|
necessary for appropriate handling.
|
||||||
|
'''
|
||||||
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import inspect
|
import inspect
|
||||||
@ -12,7 +21,7 @@ from lib.tx import Deserializer
|
|||||||
|
|
||||||
|
|
||||||
class CoinError(Exception):
|
class CoinError(Exception):
|
||||||
pass
|
'''Exception raised for coin-related errors.'''
|
||||||
|
|
||||||
|
|
||||||
class Coin(object):
|
class Coin(object):
|
||||||
@ -24,17 +33,20 @@ class Coin(object):
|
|||||||
VALUE_PER_COIN = 100000000
|
VALUE_PER_COIN = 100000000
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def coins():
|
def coin_classes():
|
||||||
|
'''Return a list of coin classes in declaration order.'''
|
||||||
is_coin = lambda obj: (inspect.isclass(obj)
|
is_coin = lambda obj: (inspect.isclass(obj)
|
||||||
and issubclass(obj, Coin)
|
and issubclass(obj, Coin)
|
||||||
and obj != Coin)
|
and obj != Coin)
|
||||||
pairs = inspect.getmembers(sys.modules[__name__], is_coin)
|
pairs = inspect.getmembers(sys.modules[__name__], is_coin)
|
||||||
# Returned in the order they appear in this file
|
|
||||||
return [pair[1] for pair in pairs]
|
return [pair[1] for pair in pairs]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def lookup_coin_class(cls, name, net):
|
def lookup_coin_class(cls, name, net):
|
||||||
for coin in cls.coins():
|
'''Return a coin class given name and network.
|
||||||
|
|
||||||
|
Raise an exception if unrecognised.'''
|
||||||
|
for coin in cls.coin_classes():
|
||||||
if (coin.NAME.lower() == name.lower()
|
if (coin.NAME.lower() == name.lower()
|
||||||
and coin.NET.lower() == net.lower()):
|
and coin.NET.lower() == net.lower()):
|
||||||
return coin
|
return coin
|
||||||
@ -43,13 +55,14 @@ class Coin(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def lookup_xverbytes(verbytes):
|
def lookup_xverbytes(verbytes):
|
||||||
|
'''Return a (is_xpub, coin_class) pair given xpub/xprv verbytes.'''
|
||||||
# Order means BTC testnet will override NMC testnet
|
# Order means BTC testnet will override NMC testnet
|
||||||
for coin in Coin.coins():
|
for coin in Coin.coin_classes():
|
||||||
if verbytes == coin.XPUB_VERBYTES:
|
if verbytes == coin.XPUB_VERBYTES:
|
||||||
return True, coin
|
return True, coin
|
||||||
if verbytes == coin.XPRV_VERBYTES:
|
if verbytes == coin.XPRV_VERBYTES:
|
||||||
return False, coin
|
return False, coin
|
||||||
raise CoinError("version bytes unrecognised")
|
raise CoinError('version bytes unrecognised')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def address_to_hash168(cls, addr):
|
def address_to_hash168(cls, addr):
|
||||||
@ -129,7 +142,7 @@ class Coin(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def prvkey_WIF(privkey_bytes, compressed):
|
def prvkey_WIF(privkey_bytes, compressed):
|
||||||
"Return the private key encoded in Wallet Import Format."
|
'''Return the private key encoded in Wallet Import Format.'''
|
||||||
payload = bytearray([cls.WIF_BYTE]) + privkey_bytes
|
payload = bytearray([cls.WIF_BYTE]) + privkey_bytes
|
||||||
if compressed:
|
if compressed:
|
||||||
payload.append(0x01)
|
payload.append(0x01)
|
||||||
@ -137,18 +150,22 @@ class Coin(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def header_hashes(cls, header):
|
def header_hashes(cls, header):
|
||||||
'''Given a header return the previous block hash and the current block
|
'''Given a header return the previous and current block hashes.'''
|
||||||
hash.'''
|
|
||||||
return header[4:36], double_sha256(header)
|
return header[4:36], double_sha256(header)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def read_block(cls, block):
|
def read_block(cls, block):
|
||||||
'''Read a block and return (header, tx_hashes, txs)'''
|
'''Return a tuple (header, tx_hashes, txs) given a raw block.'''
|
||||||
header, rest = block[:cls.HEADER_LEN], block[cls.HEADER_LEN:]
|
header, rest = block[:cls.HEADER_LEN], block[cls.HEADER_LEN:]
|
||||||
return (header, ) + Deserializer(rest).read_block()
|
return (header, ) + Deserializer(rest).read_block()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def decimal_value(cls, value):
|
def decimal_value(cls, value):
|
||||||
|
'''Return the number of standard coin units as a Decimal given a
|
||||||
|
quantity of smallest units.
|
||||||
|
|
||||||
|
For example 1 BTC is returned for 100 million satoshis.
|
||||||
|
'''
|
||||||
return Decimal(value) / cls.VALUE_PER_COIN
|
return Decimal(value) / cls.VALUE_PER_COIN
|
||||||
|
|
||||||
|
|
||||||
@ -167,6 +184,7 @@ class Bitcoin(Coin):
|
|||||||
TX_COUNT_HEIGHT = 420976
|
TX_COUNT_HEIGHT = 420976
|
||||||
TX_PER_BLOCK = 1600
|
TX_PER_BLOCK = 1600
|
||||||
|
|
||||||
|
|
||||||
class BitcoinTestnet(Coin):
|
class BitcoinTestnet(Coin):
|
||||||
NAME = "Bitcoin"
|
NAME = "Bitcoin"
|
||||||
SHORTNAME = "XTN"
|
SHORTNAME = "XTN"
|
||||||
@ -177,6 +195,7 @@ class BitcoinTestnet(Coin):
|
|||||||
P2SH_VERBYTE = 0xc4
|
P2SH_VERBYTE = 0xc4
|
||||||
WIF_BYTE = 0xef
|
WIF_BYTE = 0xef
|
||||||
|
|
||||||
|
|
||||||
# Source: pycoin and others
|
# Source: pycoin and others
|
||||||
class Litecoin(Coin):
|
class Litecoin(Coin):
|
||||||
NAME = "Litecoin"
|
NAME = "Litecoin"
|
||||||
@ -188,6 +207,7 @@ class Litecoin(Coin):
|
|||||||
P2SH_VERBYTE = 0x05
|
P2SH_VERBYTE = 0x05
|
||||||
WIF_BYTE = 0xb0
|
WIF_BYTE = 0xb0
|
||||||
|
|
||||||
|
|
||||||
class LitecoinTestnet(Coin):
|
class LitecoinTestnet(Coin):
|
||||||
NAME = "Litecoin"
|
NAME = "Litecoin"
|
||||||
SHORTNAME = "XLT"
|
SHORTNAME = "XLT"
|
||||||
@ -198,6 +218,7 @@ class LitecoinTestnet(Coin):
|
|||||||
P2SH_VERBYTE = 0xc4
|
P2SH_VERBYTE = 0xc4
|
||||||
WIF_BYTE = 0xef
|
WIF_BYTE = 0xef
|
||||||
|
|
||||||
|
|
||||||
# Source: namecoin.org
|
# Source: namecoin.org
|
||||||
class Namecoin(Coin):
|
class Namecoin(Coin):
|
||||||
NAME = "Namecoin"
|
NAME = "Namecoin"
|
||||||
@ -209,6 +230,7 @@ class Namecoin(Coin):
|
|||||||
P2SH_VERBYTE = 0x0d
|
P2SH_VERBYTE = 0x0d
|
||||||
WIF_BYTE = 0xe4
|
WIF_BYTE = 0xe4
|
||||||
|
|
||||||
|
|
||||||
class NamecoinTestnet(Coin):
|
class NamecoinTestnet(Coin):
|
||||||
NAME = "Namecoin"
|
NAME = "Namecoin"
|
||||||
SHORTNAME = "XNM"
|
SHORTNAME = "XNM"
|
||||||
@ -219,6 +241,7 @@ class NamecoinTestnet(Coin):
|
|||||||
P2SH_VERBYTE = 0xc4
|
P2SH_VERBYTE = 0xc4
|
||||||
WIF_BYTE = 0xef
|
WIF_BYTE = 0xef
|
||||||
|
|
||||||
|
|
||||||
# For DOGE there is disagreement across sites like bip32.org and
|
# For DOGE there is disagreement across sites like bip32.org and
|
||||||
# pycoin. Taken from bip32.org and bitmerchant on github
|
# pycoin. Taken from bip32.org and bitmerchant on github
|
||||||
class Dogecoin(Coin):
|
class Dogecoin(Coin):
|
||||||
@ -231,6 +254,7 @@ class Dogecoin(Coin):
|
|||||||
P2SH_VERBYTE = 0x16
|
P2SH_VERBYTE = 0x16
|
||||||
WIF_BYTE = 0x9e
|
WIF_BYTE = 0x9e
|
||||||
|
|
||||||
|
|
||||||
class DogecoinTestnet(Coin):
|
class DogecoinTestnet(Coin):
|
||||||
NAME = "Dogecoin"
|
NAME = "Dogecoin"
|
||||||
SHORTNAME = "XDT"
|
SHORTNAME = "XDT"
|
||||||
@ -241,6 +265,7 @@ class DogecoinTestnet(Coin):
|
|||||||
P2SH_VERBYTE = 0xc4
|
P2SH_VERBYTE = 0xc4
|
||||||
WIF_BYTE = 0xf1
|
WIF_BYTE = 0xf1
|
||||||
|
|
||||||
|
|
||||||
# Source: pycoin
|
# Source: pycoin
|
||||||
class Dash(Coin):
|
class Dash(Coin):
|
||||||
NAME = "Dash"
|
NAME = "Dash"
|
||||||
@ -252,6 +277,7 @@ class Dash(Coin):
|
|||||||
P2SH_VERBYTE = 0x10
|
P2SH_VERBYTE = 0x10
|
||||||
WIF_BYTE = 0xcc
|
WIF_BYTE = 0xcc
|
||||||
|
|
||||||
|
|
||||||
class DashTestnet(Coin):
|
class DashTestnet(Coin):
|
||||||
NAME = "Dogecoin"
|
NAME = "Dogecoin"
|
||||||
SHORTNAME = "tDASH"
|
SHORTNAME = "tDASH"
|
||||||
|
|||||||
23
lib/enum.py
23
lib/enum.py
@ -1,8 +1,17 @@
|
|||||||
# enum-like type
|
# Copyright (c) 2016, Neil Booth
|
||||||
# From the Python Cookbook from http://code.activestate.com/recipes/67107/
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# See the file "LICENCE" for information about the copyright
|
||||||
|
# and warranty status of this software.
|
||||||
|
|
||||||
|
'''An enum-like type with reverse lookup.
|
||||||
|
|
||||||
|
Source: Python Cookbook, http://code.activestate.com/recipes/67107/
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
class EnumException(Exception):
|
class EnumError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -20,13 +29,13 @@ class Enumeration:
|
|||||||
if isinstance(x, tuple):
|
if isinstance(x, tuple):
|
||||||
x, i = x
|
x, i = x
|
||||||
if not isinstance(x, str):
|
if not isinstance(x, str):
|
||||||
raise EnumException("enum name {} not a string".format(x))
|
raise EnumError("enum name {} not a string".format(x))
|
||||||
if not isinstance(i, int):
|
if not isinstance(i, int):
|
||||||
raise EnumException("enum value {} not an integer".format(i))
|
raise EnumError("enum value {} not an integer".format(i))
|
||||||
if x in uniqueNames:
|
if x in uniqueNames:
|
||||||
raise EnumException("enum name {} not unique".format(x))
|
raise EnumError("enum name {} not unique".format(x))
|
||||||
if i in uniqueValues:
|
if i in uniqueValues:
|
||||||
raise EnumException("enum value {} not unique".format(x))
|
raise EnumError("enum value {} not unique".format(x))
|
||||||
uniqueNames.add(x)
|
uniqueNames.add(x)
|
||||||
uniqueValues.add(i)
|
uniqueValues.add(i)
|
||||||
lookup[x] = i
|
lookup[x] = i
|
||||||
|
|||||||
44
lib/hash.py
44
lib/hash.py
@ -1,6 +1,13 @@
|
|||||||
# See the file "LICENSE" for information about the copyright
|
# Copyright (c) 2016, Neil Booth
|
||||||
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# See the file "LICENCE" for information about the copyright
|
||||||
# and warranty status of this software.
|
# and warranty status of this software.
|
||||||
|
|
||||||
|
'''Cryptograph hash functions and related classes.'''
|
||||||
|
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
|
|
||||||
@ -8,11 +15,13 @@ from lib.util import bytes_to_int, int_to_bytes
|
|||||||
|
|
||||||
|
|
||||||
def sha256(x):
|
def sha256(x):
|
||||||
|
'''Simple wrapper of hashlib sha256.'''
|
||||||
assert isinstance(x, (bytes, bytearray, memoryview))
|
assert isinstance(x, (bytes, bytearray, memoryview))
|
||||||
return hashlib.sha256(x).digest()
|
return hashlib.sha256(x).digest()
|
||||||
|
|
||||||
|
|
||||||
def ripemd160(x):
|
def ripemd160(x):
|
||||||
|
'''Simple wrapper of hashlib ripemd160.'''
|
||||||
assert isinstance(x, (bytes, bytearray, memoryview))
|
assert isinstance(x, (bytes, bytearray, memoryview))
|
||||||
h = hashlib.new('ripemd160')
|
h = hashlib.new('ripemd160')
|
||||||
h.update(x)
|
h.update(x)
|
||||||
@ -20,36 +29,41 @@ def ripemd160(x):
|
|||||||
|
|
||||||
|
|
||||||
def double_sha256(x):
|
def double_sha256(x):
|
||||||
|
'''SHA-256 of SHA-256, as used extensively in bitcoin.'''
|
||||||
return sha256(sha256(x))
|
return sha256(sha256(x))
|
||||||
|
|
||||||
|
|
||||||
def hmac_sha512(key, msg):
|
def hmac_sha512(key, msg):
|
||||||
|
'''Use SHA-512 to provide an HMAC.'''
|
||||||
return hmac.new(key, msg, hashlib.sha512).digest()
|
return hmac.new(key, msg, hashlib.sha512).digest()
|
||||||
|
|
||||||
|
|
||||||
def hash160(x):
|
def hash160(x):
|
||||||
|
'''RIPEMD-160 of SHA-256.
|
||||||
|
|
||||||
|
Used to make bitcoin addresses from pubkeys.'''
|
||||||
return ripemd160(sha256(x))
|
return ripemd160(sha256(x))
|
||||||
|
|
||||||
|
|
||||||
def hash_to_str(x):
|
def hash_to_str(x):
|
||||||
'''Converts a big-endian binary hash to a little-endian hex string, as
|
'''Convert a big-endian binary hash to displayed hex string.
|
||||||
shown in block explorers, etc.
|
|
||||||
|
Display form of a binary hash is reversed and converted to hex.
|
||||||
'''
|
'''
|
||||||
return bytes(reversed(x)).hex()
|
return bytes(reversed(x)).hex()
|
||||||
|
|
||||||
|
|
||||||
def hex_str_to_hash(x):
|
def hex_str_to_hash(x):
|
||||||
'''Converts a little-endian hex string as shown to a big-endian binary
|
'''Convert a displayed hex string to a binary hash.'''
|
||||||
hash.'''
|
|
||||||
return bytes(reversed(bytes.fromhex(x)))
|
return bytes(reversed(bytes.fromhex(x)))
|
||||||
|
|
||||||
class InvalidBase58String(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
class Base58Error(Exception):
|
||||||
class InvalidBase58CheckSum(Exception):
|
'''Exception used for Base58 errors.'''
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Base58(object):
|
class Base58(object):
|
||||||
|
'''Class providing base 58 functionality.'''
|
||||||
|
|
||||||
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||||
assert len(chars) == 58
|
assert len(chars) == 58
|
||||||
@ -59,17 +73,17 @@ class Base58(object):
|
|||||||
def char_value(c):
|
def char_value(c):
|
||||||
val = Base58.cmap.get(c)
|
val = Base58.cmap.get(c)
|
||||||
if val is None:
|
if val is None:
|
||||||
raise InvalidBase58String
|
raise Base58Error('invalid base 58 character "{}"'.format(c))
|
||||||
return val
|
return val
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def decode(txt):
|
def decode(txt):
|
||||||
"""Decodes txt into a big-endian bytearray."""
|
"""Decodes txt into a big-endian bytearray."""
|
||||||
if not isinstance(txt, str):
|
if not isinstance(txt, str):
|
||||||
raise InvalidBase58String("a string is required")
|
raise Base58Error('a string is required')
|
||||||
|
|
||||||
if not txt:
|
if not txt:
|
||||||
raise InvalidBase58String("string cannot be empty")
|
raise Base58Error('string cannot be empty')
|
||||||
|
|
||||||
value = 0
|
value = 0
|
||||||
for c in txt:
|
for c in txt:
|
||||||
@ -112,14 +126,14 @@ class Base58(object):
|
|||||||
be_bytes = Base58.decode(txt)
|
be_bytes = Base58.decode(txt)
|
||||||
result, check = be_bytes[:-4], be_bytes[-4:]
|
result, check = be_bytes[:-4], be_bytes[-4:]
|
||||||
if check != double_sha256(result)[:4]:
|
if check != double_sha256(result)[:4]:
|
||||||
raise InvalidBase58CheckSum
|
raise Base58Error('invalid base 58 checksum for {}'.format(txt))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def encode_check(payload):
|
def encode_check(payload):
|
||||||
"""Encodes a payload bytearray (which includes the version byte(s))
|
"""Encodes a payload bytearray (which includes the version byte(s))
|
||||||
into a Base58Check string."""
|
into a Base58Check string."""
|
||||||
assert isinstance(payload, (bytes, bytearray))
|
assert isinstance(payload, (bytes, bytearray, memoryview))
|
||||||
|
|
||||||
be_bytes = payload + double_sha256(payload)[:4]
|
be_bytes = payload + double_sha256(payload)[:4]
|
||||||
return Base58.encode(be_bytes)
|
return Base58.encode(be_bytes)
|
||||||
|
|||||||
@ -1,6 +1,13 @@
|
|||||||
# See the file "LICENSE" for information about the copyright
|
# Copyright (c) 2016, Neil Booth
|
||||||
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# See the file "LICENCE" for information about the copyright
|
||||||
# and warranty status of this software.
|
# and warranty status of this software.
|
||||||
|
|
||||||
|
'''Script-related classes and functions.'''
|
||||||
|
|
||||||
|
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
@ -10,7 +17,7 @@ from lib.util import cachedproperty
|
|||||||
|
|
||||||
|
|
||||||
class ScriptError(Exception):
|
class ScriptError(Exception):
|
||||||
pass
|
'''Exception used for script errors.'''
|
||||||
|
|
||||||
|
|
||||||
OpCodes = Enumeration("Opcodes", [
|
OpCodes = Enumeration("Opcodes", [
|
||||||
@ -52,7 +59,9 @@ assert OpCodes.OP_CHECKMULTISIG == 0xae
|
|||||||
|
|
||||||
|
|
||||||
class ScriptSig(object):
|
class ScriptSig(object):
|
||||||
'''A script from a tx input, typically provides one or more signatures.'''
|
'''A script from a tx input.
|
||||||
|
|
||||||
|
Typically provides one or more signatures.'''
|
||||||
|
|
||||||
SIG_ADDRESS, SIG_MULTI, SIG_PUBKEY, SIG_UNKNOWN = range(4)
|
SIG_ADDRESS, SIG_MULTI, SIG_PUBKEY, SIG_UNKNOWN = range(4)
|
||||||
|
|
||||||
@ -73,8 +82,9 @@ class ScriptSig(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_script(cls, script, coin):
|
def from_script(cls, script, coin):
|
||||||
'''Returns an instance of this class. Uncrecognised scripts return
|
'''Return an instance of this class.
|
||||||
an object of kind SIG_UNKNOWN.'''
|
|
||||||
|
Return an object with kind SIG_UNKNOWN for unrecognised scripts.'''
|
||||||
try:
|
try:
|
||||||
return cls.parse_script(script, coin)
|
return cls.parse_script(script, coin)
|
||||||
except ScriptError:
|
except ScriptError:
|
||||||
@ -82,8 +92,9 @@ class ScriptSig(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse_script(cls, script, coin):
|
def parse_script(cls, script, coin):
|
||||||
'''Returns an instance of this class. Raises on unrecognised
|
'''Return an instance of this class.
|
||||||
scripts.'''
|
|
||||||
|
Raises on unrecognised scripts.'''
|
||||||
ops, datas = Script.get_ops(script)
|
ops, datas = Script.get_ops(script)
|
||||||
|
|
||||||
# Address, PubKey and P2SH redeems only push data
|
# Address, PubKey and P2SH redeems only push data
|
||||||
|
|||||||
15
lib/tx.py
15
lib/tx.py
@ -1,6 +1,13 @@
|
|||||||
# See the file "LICENSE" for information about the copyright
|
# Copyright (c) 2016, Neil Booth
|
||||||
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# See the file "LICENCE" for information about the copyright
|
||||||
# and warranty status of this software.
|
# and warranty status of this software.
|
||||||
|
|
||||||
|
'''Transaction-related classes and functions.'''
|
||||||
|
|
||||||
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
@ -9,6 +16,7 @@ from lib.hash import double_sha256, hash_to_str
|
|||||||
|
|
||||||
|
|
||||||
class Tx(namedtuple("Tx", "version inputs outputs locktime")):
|
class Tx(namedtuple("Tx", "version inputs outputs locktime")):
|
||||||
|
'''Class representing a transaction.'''
|
||||||
|
|
||||||
@cachedproperty
|
@cachedproperty
|
||||||
def is_coinbase(self):
|
def is_coinbase(self):
|
||||||
@ -17,6 +25,7 @@ class Tx(namedtuple("Tx", "version inputs outputs locktime")):
|
|||||||
# FIXME: add hash as a cached property?
|
# FIXME: add hash as a cached property?
|
||||||
|
|
||||||
class TxInput(namedtuple("TxInput", "prev_hash prev_idx script sequence")):
|
class TxInput(namedtuple("TxInput", "prev_hash prev_idx script sequence")):
|
||||||
|
'''Class representing a transaction input.'''
|
||||||
|
|
||||||
ZERO = bytes(32)
|
ZERO = bytes(32)
|
||||||
MINUS_1 = 4294967295
|
MINUS_1 = 4294967295
|
||||||
@ -41,6 +50,7 @@ class TxInput(namedtuple("TxInput", "prev_hash prev_idx script sequence")):
|
|||||||
|
|
||||||
|
|
||||||
class TxOutput(namedtuple("TxOutput", "value pk_script")):
|
class TxOutput(namedtuple("TxOutput", "value pk_script")):
|
||||||
|
'''Class representing a transaction output.'''
|
||||||
|
|
||||||
@cachedproperty
|
@cachedproperty
|
||||||
def pay_to(self):
|
def pay_to(self):
|
||||||
@ -48,9 +58,10 @@ class TxOutput(namedtuple("TxOutput", "value pk_script")):
|
|||||||
|
|
||||||
|
|
||||||
class Deserializer(object):
|
class Deserializer(object):
|
||||||
|
'''Deserializes blocks into transactions.'''
|
||||||
|
|
||||||
def __init__(self, binary):
|
def __init__(self, binary):
|
||||||
assert isinstance(binary, (bytes, memoryview))
|
assert isinstance(binary, bytes)
|
||||||
self.binary = binary
|
self.binary = binary
|
||||||
self.cursor = 0
|
self.cursor = 0
|
||||||
|
|
||||||
|
|||||||
10
lib/util.py
10
lib/util.py
@ -1,6 +1,13 @@
|
|||||||
# See the file "LICENSE" for information about the copyright
|
# Copyright (c) 2016, Neil Booth
|
||||||
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# See the file "LICENCE" for information about the copyright
|
||||||
# and warranty status of this software.
|
# and warranty status of this software.
|
||||||
|
|
||||||
|
'''Miscellaneous utility classes and functions.'''
|
||||||
|
|
||||||
|
|
||||||
import array
|
import array
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
@ -72,6 +79,7 @@ def deep_getsizeof(obj):
|
|||||||
|
|
||||||
|
|
||||||
def chunks(items, size):
|
def chunks(items, size):
|
||||||
|
'''Break up items, an iterable, into chunks of length size.'''
|
||||||
for i in range(0, len(items), size):
|
for i in range(0, len(items), size):
|
||||||
yield items[i: i + size]
|
yield items[i: i + size]
|
||||||
|
|
||||||
|
|||||||
14
query.py
14
query.py
@ -1,8 +1,18 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
# See the file "LICENSE" for information about the copyright
|
# Copyright (c) 2016, Neil Booth
|
||||||
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# See the file "LICENCE" for information about the copyright
|
||||||
# and warranty status of this software.
|
# and warranty status of this software.
|
||||||
|
|
||||||
|
'''Script to query the database for debugging purposes.
|
||||||
|
|
||||||
|
Not currently documented; might become easier to use in future.
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,13 @@
|
|||||||
# See the file "LICENSE" for information about the copyright
|
# Copyright (c) 2016, Neil Booth
|
||||||
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# See the file "LICENCE" for information about the copyright
|
||||||
# and warranty status of this software.
|
# and warranty status of this software.
|
||||||
|
|
||||||
|
'''Block prefetcher and chain processor.'''
|
||||||
|
|
||||||
|
|
||||||
import array
|
import array
|
||||||
import ast
|
import ast
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|||||||
@ -1,6 +1,17 @@
|
|||||||
# See the file "LICENSE" for information about the copyright
|
# Copyright (c) 2016, Neil Booth
|
||||||
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# See the file "LICENCE" for information about the copyright
|
||||||
# and warranty status of this software.
|
# and warranty status of this software.
|
||||||
|
|
||||||
|
'''UTXO and file cache.
|
||||||
|
|
||||||
|
During initial sync these cache data and only flush occasionally.
|
||||||
|
Once synced flushes are performed after processing each block.
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
import array
|
import array
|
||||||
import itertools
|
import itertools
|
||||||
import os
|
import os
|
||||||
|
|||||||
@ -1,6 +1,16 @@
|
|||||||
# See the file "LICENSE" for information about the copyright
|
# Copyright (c) 2016, Neil Booth
|
||||||
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# See the file "LICENCE" for information about the copyright
|
||||||
# and warranty status of this software.
|
# and warranty status of this software.
|
||||||
|
|
||||||
|
'''Server controller.
|
||||||
|
|
||||||
|
Coordinates the parts of the server. Serves as a cache for
|
||||||
|
client-serving data such as histories.
|
||||||
|
'''
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import signal
|
import signal
|
||||||
import traceback
|
import traceback
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
# See the file "LICENSE" for information about the copyright
|
# Copyright (c) 2016, Neil Booth
|
||||||
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# See the file "LICENCE" for information about the copyright
|
||||||
# and warranty status of this software.
|
# and warranty status of this software.
|
||||||
|
|
||||||
'''Classes for handling asynchronous connections to a blockchain
|
'''Class for handling asynchronous connections to a blockchain
|
||||||
daemon.'''
|
daemon.'''
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|||||||
@ -1,6 +1,13 @@
|
|||||||
# See the file "LICENSE" for information about the copyright
|
# Copyright (c) 2016, Neil Booth
|
||||||
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# See the file "LICENCE" for information about the copyright
|
||||||
# and warranty status of this software.
|
# and warranty status of this software.
|
||||||
|
|
||||||
|
'''Class for handling environment configuration and defaults.'''
|
||||||
|
|
||||||
|
|
||||||
from os import environ
|
from os import environ
|
||||||
|
|
||||||
from lib.coins import Coin
|
from lib.coins import Coin
|
||||||
|
|||||||
@ -1,6 +1,13 @@
|
|||||||
# See the file "LICENSE" for information about the copyright
|
# Copyright (c) 2016, Neil Booth
|
||||||
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# See the file "LICENCE" for information about the copyright
|
||||||
# and warranty status of this software.
|
# and warranty status of this software.
|
||||||
|
|
||||||
|
'''Classes for local RPC server and remote client TCP/SSL servers.'''
|
||||||
|
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import codecs
|
import codecs
|
||||||
import json
|
import json
|
||||||
|
|||||||
@ -1,8 +1,15 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
# See the file "LICENSE" for information about the copyright
|
# Copyright (c) 2016, Neil Booth
|
||||||
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# See the file "LICENCE" for information about the copyright
|
||||||
# and warranty status of this software.
|
# and warranty status of this software.
|
||||||
|
|
||||||
|
'''Script to kick off the server.'''
|
||||||
|
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user