Add Decred(DCR) (#402)
This commit is contained in:
parent
1e6b30fd76
commit
c26227413e
@ -20,6 +20,7 @@ install:
|
||||
- pip install tribus-hash
|
||||
- pip install pytest-cov
|
||||
- pip install pylru
|
||||
- pip install blake256
|
||||
# command to run tests
|
||||
script: pytest --cov=server --cov=lib --cov=wallet
|
||||
# Dont report coverage from nightly
|
||||
|
||||
52
lib/coins.py
52
lib/coins.py
@ -35,6 +35,7 @@ import re
|
||||
import struct
|
||||
from decimal import Decimal
|
||||
from hashlib import sha256
|
||||
from functools import partial
|
||||
|
||||
import lib.util as util
|
||||
from lib.hash import Base58, hash160, double_sha256, hash_to_str
|
||||
@ -1420,3 +1421,54 @@ class BitcoinAtom(Coin):
|
||||
'''Return the block header bytes'''
|
||||
deserializer = cls.DESERIALIZER(block)
|
||||
return deserializer.read_header(height, cls.BASIC_HEADER_SIZE)
|
||||
|
||||
|
||||
class Decred(Coin):
|
||||
NAME = "Decred"
|
||||
SHORTNAME = "DCR"
|
||||
NET = "mainnet"
|
||||
XPUB_VERBYTES = bytes('dpub', 'utf-8')
|
||||
XPRV_VERBYTES = bytes('dprv', 'utf-8')
|
||||
P2PKH_VERBYTE = bytes('Ds', 'utf-8')
|
||||
P2SH_VERBYTES = [bytes('Dc', 'utf-8')]
|
||||
WIF_BYTE = bytes('Pm', 'utf-8')
|
||||
GENESIS_HASH = ('298e5cc3d985bfe7f81dc135f360abe089edd4396b86d2de66b0cef42b21d980')
|
||||
DESERIALIZER = lib_tx.DeserializerDecred
|
||||
ENCODE_CHECK = partial(Base58.encode_check, hash_fn=lib_tx.DeserializerDecred.blake256)
|
||||
DECODE_CHECK = partial(Base58.decode_check, hash_fn=lib_tx.DeserializerDecred.blake256)
|
||||
HEADER_HASH = lib_tx.DeserializerDecred.blake256
|
||||
BASIC_HEADER_SIZE = 180
|
||||
ALLOW_ADVANCING_ERRORS = True
|
||||
TX_COUNT = 217380620
|
||||
TX_COUNT_HEIGHT = 218875
|
||||
TX_PER_BLOCK = 1000
|
||||
RPC_PORT = 9109
|
||||
|
||||
@classmethod
|
||||
def header_hash(cls, header):
|
||||
'''Given a header return the hash.'''
|
||||
return cls.HEADER_HASH(header)
|
||||
|
||||
@classmethod
|
||||
def block(cls, raw_block, height):
|
||||
'''Return a Block namedtuple given a raw block and its height.'''
|
||||
if height > 0:
|
||||
return super().block(raw_block, height)
|
||||
else:
|
||||
return Block(raw_block, cls.block_header(raw_block, height), [])
|
||||
|
||||
|
||||
class DecredTestnet(Decred):
|
||||
NAME = "Decred"
|
||||
NET = "testnet"
|
||||
XPUB_VERBYTES = bytes('tpub', 'utf-8')
|
||||
XPRV_VERBYTES = bytes('tprv', 'utf-8')
|
||||
P2PKH_VERBYTE = bytes('Ts', 'utf-8')
|
||||
P2SH_VERBYTES = [bytes('Tc', 'utf-8')]
|
||||
WIF_BYTE = bytes('Pt', 'utf-8')
|
||||
GENESIS_HASH = ('4261602a9d07d80ad47621a64ba6a07754902e496777edc4ff581946bd7bc29c')
|
||||
TX_COUNT = 3176305
|
||||
TX_COUNT_HEIGHT = 254198
|
||||
TX_PER_BLOCK = 1000
|
||||
RPC_PORT = 19119
|
||||
|
||||
|
||||
103
lib/tx.py
103
lib/tx.py
@ -401,3 +401,106 @@ class DeserializerBitcoinAtom(DeserializerSegWit):
|
||||
if height >= self.FORK_BLOCK_HEIGHT:
|
||||
header_len += 4 # flags
|
||||
return self._read_nbytes(header_len)
|
||||
|
||||
|
||||
# Decred
|
||||
class TxInputDcr(namedtuple("TxInput", "prev_hash prev_idx tree sequence")):
|
||||
'''Class representing a Decred transaction input.'''
|
||||
|
||||
ZERO = bytes(32)
|
||||
MINUS_1 = 4294967295
|
||||
|
||||
@cachedproperty
|
||||
def is_coinbase(self):
|
||||
# The previous output of a coin base must have a max value index and a
|
||||
# zero hash.
|
||||
return (self.prev_hash == TxInputDcr.ZERO and
|
||||
self.prev_idx == TxInputDcr.MINUS_1)
|
||||
|
||||
def __str__(self):
|
||||
prev_hash = hash_to_str(self.prev_hash)
|
||||
return ("Input({}, {:d}, tree={}, sequence={:d})"
|
||||
.format(prev_hash, self.prev_idx, self.tree, self.sequence))
|
||||
|
||||
|
||||
class TxOutputDcr(namedtuple("TxOutput", "value version pk_script")):
|
||||
'''Class representing a transaction output.'''
|
||||
pass
|
||||
|
||||
|
||||
class TxDcr(namedtuple("Tx", "version inputs outputs locktime expiry "
|
||||
"witness")):
|
||||
'''Class representing transaction that has a time field.'''
|
||||
|
||||
@cachedproperty
|
||||
def is_coinbase(self):
|
||||
return self.inputs[0].is_coinbase
|
||||
|
||||
|
||||
class DeserializerDecred(Deserializer):
|
||||
|
||||
@staticmethod
|
||||
def blake256(data):
|
||||
from blake256.blake256 import blake_hash
|
||||
return blake_hash(data)
|
||||
|
||||
def read_tx_block(self):
|
||||
'''Returns a list of (deserialized_tx, tx_hash) pairs.'''
|
||||
read_tx = self.read_tx
|
||||
txs = [read_tx() for _ in range(self._read_varint())]
|
||||
stxs = [read_tx() for _ in range(self._read_varint())]
|
||||
return txs + stxs
|
||||
|
||||
def _read_inputs(self):
|
||||
read_input = self._read_input
|
||||
return [read_input() for i in range(self._read_varint())]
|
||||
|
||||
def _read_input(self):
|
||||
return TxInputDcr(
|
||||
self._read_nbytes(32), # prev_hash
|
||||
self._read_le_uint32(), # prev_idx
|
||||
self._read_byte(), # tree
|
||||
self._read_le_uint32(), # sequence
|
||||
)
|
||||
|
||||
def _read_outputs(self):
|
||||
read_output = self._read_output
|
||||
return [read_output() for _ in range(self._read_varint())]
|
||||
|
||||
def _read_output(self):
|
||||
return TxOutputDcr(
|
||||
self._read_le_int64(), # value
|
||||
self._read_le_uint16(), # version
|
||||
self._read_varbytes(), # pk_script
|
||||
)
|
||||
|
||||
def _read_witness(self, fields):
|
||||
read_witness_field = self._read_witness_field
|
||||
assert fields == self._read_varint()
|
||||
return [read_witness_field() for _ in range(fields)]
|
||||
|
||||
def _read_witness_field(self):
|
||||
value_in = self._read_le_int64()
|
||||
block_height = self._read_le_uint32()
|
||||
block_index = self._read_le_uint32()
|
||||
script = self._read_varbytes()
|
||||
return value_in, block_height, block_index, script
|
||||
|
||||
def read_tx(self):
|
||||
start = self.cursor
|
||||
version = self._read_le_int32()
|
||||
assert version == 1 # TODO check other versions for segwit
|
||||
inputs = self._read_inputs()
|
||||
outputs = self._read_outputs()
|
||||
locktime = self._read_le_uint32()
|
||||
expiry = self._read_le_uint32()
|
||||
no_witness_tx = b'\x01\x00\x01\x00' + self.binary[start+4:self.cursor]
|
||||
witness = self._read_witness(len(inputs))
|
||||
return TxDcr(
|
||||
version,
|
||||
inputs,
|
||||
outputs,
|
||||
locktime,
|
||||
expiry,
|
||||
witness
|
||||
), DeserializerDecred.blake256(no_witness_tx)
|
||||
|
||||
1
setup.py
1
setup.py
@ -10,6 +10,7 @@ setuptools.setup(
|
||||
# via environment variables, in which case I've tested with 15.0.4
|
||||
# "x11_hash" package (1.4) is required to sync DASH network.
|
||||
# "tribus_hash" package is required to sync Denarius network.
|
||||
# "blake256" package is required to sync Decred network.
|
||||
install_requires=['plyvel', 'pylru', 'aiohttp >= 1'],
|
||||
packages=setuptools.find_packages(exclude=['tests']),
|
||||
description='ElectrumX Server',
|
||||
|
||||
30
tests/blocks/decred_mainnet_200000.json
Normal file
30
tests/blocks/decred_mainnet_200000.json
Normal file
File diff suppressed because one or more lines are too long
17
tests/blocks/decred_testnet_200000.json
Normal file
17
tests/blocks/decred_testnet_200000.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"hash": "0000000003416ee936e24e3d8ebb0ae12cc9cd397df61346f2805642676291ae",
|
||||
"size": 1637,
|
||||
"height": 200000,
|
||||
"merkleroot": "560d636172de028d728723b41428eb51acb4e969caf54f50f0de19f14a66fb1e",
|
||||
"tx": [
|
||||
"42fb258edd91d3b4aeea43f2451311b1045e3d0f935491301e03377eb86b1c46",
|
||||
"9b1e326156e1426d1b2c32d711e660c936f53fcdf90de3ea4f8ae18c695751e0",
|
||||
"017ac016d59cbc1b381ae71f61173bf9b5eaf85e338f2b81df73f88574bc7fa6",
|
||||
"a15ddc52b33346ddb22005eaf622336d0f56856f598e1068d1fba280d9cf0408"
|
||||
],
|
||||
"time": 1513807068,
|
||||
"nonce": 4094753344,
|
||||
"bits": "1c038167",
|
||||
"previousblockhash": "0000000001c195bb55127867a4dbeb9385fc14113db92a787ccd8c64d4bab58f",
|
||||
"block": "060000008fb5bad4648ccd7c782ab93d1114fc8593ebdba467781255bb95c101000000001efb664af119def0504ff5ca69e9b4ac51eb2814b42387728d02de7261630d56778f843a51d3aa5d44dd17c5f35a0b163d925b9ce2dc7dfb25cb1534184cce4e01005768179ecf0803000000e41200006781031cbcf9645901000000400d030065060000dcdc3a5a40fa10f43ffde600cc7b6b00000000000000000000000000000000000000000000000000060000000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff03b0d76703000000000000144fa6cbd0dbe5ec407fe4c8ad374e667771fa0d4400000000000000000000266a24400d03000000000000000000000000000000000000000000000000008ca3582a0607db7b260e6f140000000000001976a9149d8e8bdc618035be32a14ab752af2e331f9abf3688ac000000000000000001d5e5d6170000000000000000ffffffff0800002f646372642f0301000000020000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffffab941af0b55cb4fd9a936b4b02cd052e9821b307c11389d3a3e68b40911b9c360000000001ffffffff0400000000000000000000266a248fb5bad4648ccd7c782ab93d1114fc8593ebdba467781255bb95c101000000003f0d030000000000000000000000086a060500060000003dc040000000000000001abb76a9147f686bc0e548bbb92f487db6da070e43a341172888ac3cdc96d60000000000001abb76a9149d8e8bdc618035be32a14ab752af2e331f9abf3688ac000000000000000002b0d767030000000000000000ffffffff020000cac46fd300000000260b03000d0000009047304402203a4a29b406b8eac0ac9adbbe818a28795cada504e83a46aa3e9c41cbfa014f700220310017fb5c5b5cc27d4b22f0867c324badf7a0b3953a274d95d1295e447f136d0147512102fe82f22f2e5bc1be0b67d85afef87329fc1c4512f30a47ce459c78bd7502ba9821022cf2f038dbb85f0a35fed9ac147e58d9ee85a80f8827085f51ef4129a02d458652ae01000000020000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff171c0027204501f9572b19306e8fe6813b00391aaec26d295fabbb449fa245e00000000001ffffffff0400000000000000000000266a248fb5bad4648ccd7c782ab93d1114fc8593ebdba467781255bb95c101000000003f0d030000000000000000000000086a060500060000005f6341000000000000001abb76a9147f686bc0e548bbb92f487db6da070e43a341172888ac38a085010100000000001abb76a9149d8e8bdc618035be32a14ab752af2e331f9abf3688ac000000000000000002b0d767030000000000000000ffffffff020000e82b5ffe00000000e40603000500000090473044022060f6d935b0385d55a12899c48fa18e7e938d819874c3fe72924d665122fa95c30220566fcc424cf25f772007cff3b0a0f6f99dbf5b197ac26c1b67347a2dccb152bb0147512102fe82f22f2e5bc1be0b67d85afef87329fc1c4512f30a47ce459c78bd7502ba9821022cf2f038dbb85f0a35fed9ac147e58d9ee85a80f8827085f51ef4129a02d458652ae01000000020000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff21cd5bfc8082569d5b317be8cf67d6b36dbb4711a5f69dc25aea0d6c7e319e4b0000000001ffffffff0400000000000000000000266a248fb5bad4648ccd7c782ab93d1114fc8593ebdba467781255bb95c101000000003f0d030000000000000000000000086a060500060000005f6341000000000000001abb76a9147f686bc0e548bbb92f487db6da070e43a341172888ac711c961c0100000000001abb76a9149d8e8bdc618035be32a14ab752af2e331f9abf3688ac000000000000000002b0d767030000000000000000ffffffff02000021a86f190100000029040300060000009148304502210098a4987ba360e1aea04de1ff77c1f5dd646b7dd1b00136ca58db005248e4451b022001b87cf0bb13bef9e09a48cfa88d5b9f6fc07af95cdc75b4e9f67c040541737a0147512102fe82f22f2e5bc1be0b67d85afef87329fc1c4512f30a47ce459c78bd7502ba9821022cf2f038dbb85f0a35fed9ac147e58d9ee85a80f8827085f51ef4129a02d458652ae"
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user