Merge branch 'master' into devel
This commit is contained in:
commit
a108817dd4
@ -2100,3 +2100,42 @@ class GroestlcoinTestnet(Groestlcoin):
|
|||||||
'7frvhgofuf522b5i.onion t',
|
'7frvhgofuf522b5i.onion t',
|
||||||
'aocojvqcybdoxekv.onion t',
|
'aocojvqcybdoxekv.onion t',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Pivx(Coin):
|
||||||
|
NAME = "Pivx"
|
||||||
|
SHORTNAME = "PIVX"
|
||||||
|
NET = "mainnet"
|
||||||
|
XPUB_VERBYTES = bytes.fromhex("022D2533")
|
||||||
|
XPRV_VERBYTES = bytes.fromhex("0221312B")
|
||||||
|
P2PKH_VERBYTE = bytes.fromhex("1e")
|
||||||
|
P2SH_VERBYTES = [bytes.fromhex("0d")]
|
||||||
|
WIF_BYTE = bytes.fromhex("d4")
|
||||||
|
GENESIS_HASH = ('0000041e482b9b9691d98eefb4847340'
|
||||||
|
'5c0b8ec31b76df3797c74a78680ef818')
|
||||||
|
BASIC_HEADER_SIZE = 80
|
||||||
|
HDR_V4_SIZE = 112
|
||||||
|
HDR_V4_HEIGHT = 863787
|
||||||
|
HDR_V4_START_OFFSET = HDR_V4_HEIGHT * BASIC_HEADER_SIZE
|
||||||
|
TX_COUNT = 2930206
|
||||||
|
TX_COUNT_HEIGHT = 1299212
|
||||||
|
TX_PER_BLOCK = 2
|
||||||
|
RPC_PORT = 51473
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def static_header_offset(cls, height):
|
||||||
|
assert cls.STATIC_BLOCK_HEADERS
|
||||||
|
if height >= cls.HDR_V4_HEIGHT:
|
||||||
|
relative_v4_offset = (height - cls.HDR_V4_HEIGHT) * cls.HDR_V4_SIZE
|
||||||
|
return cls.HDR_V4_START_OFFSET + relative_v4_offset
|
||||||
|
else:
|
||||||
|
return height * cls.BASIC_HEADER_SIZE
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def header_hash(cls, header):
|
||||||
|
version, = struct.unpack('<I', header[:4])
|
||||||
|
if version >= 4:
|
||||||
|
return super().header_hash(header)
|
||||||
|
else:
|
||||||
|
import quark_hash
|
||||||
|
return quark_hash.getPoWHash(header)
|
||||||
|
|||||||
@ -42,8 +42,8 @@ class Tx(namedtuple("Tx", "version inputs outputs locktime")):
|
|||||||
'''Class representing a transaction.'''
|
'''Class representing a transaction.'''
|
||||||
|
|
||||||
@cachedproperty
|
@cachedproperty
|
||||||
def is_coinbase(self):
|
def is_generation(self):
|
||||||
return self.inputs[0].is_coinbase
|
return self.inputs[0].is_generation
|
||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
return b''.join((
|
return b''.join((
|
||||||
@ -63,9 +63,9 @@ class TxInput(namedtuple("TxInput", "prev_hash prev_idx script sequence")):
|
|||||||
MINUS_1 = 4294967295
|
MINUS_1 = 4294967295
|
||||||
|
|
||||||
@cachedproperty
|
@cachedproperty
|
||||||
def is_coinbase(self):
|
def is_generation(self):
|
||||||
return (self.prev_hash == TxInput.ZERO and
|
return (self.prev_idx == TxInput.MINUS_1 and
|
||||||
self.prev_idx == TxInput.MINUS_1)
|
self.prev_hash == TxInput.ZERO)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
script = self.script.hex()
|
script = self.script.hex()
|
||||||
@ -215,8 +215,8 @@ class TxSegWit(namedtuple("Tx", "version marker flag inputs outputs "
|
|||||||
'''Class representing a SegWit transaction.'''
|
'''Class representing a SegWit transaction.'''
|
||||||
|
|
||||||
@cachedproperty
|
@cachedproperty
|
||||||
def is_coinbase(self):
|
def is_generation(self):
|
||||||
return self.inputs[0].is_coinbase
|
return self.inputs[0].is_generation
|
||||||
|
|
||||||
|
|
||||||
class DeserializerSegWit(Deserializer):
|
class DeserializerSegWit(Deserializer):
|
||||||
@ -327,8 +327,8 @@ class TxJoinSplit(namedtuple("Tx", "version inputs outputs locktime")):
|
|||||||
'''Class representing a JoinSplit transaction.'''
|
'''Class representing a JoinSplit transaction.'''
|
||||||
|
|
||||||
@cachedproperty
|
@cachedproperty
|
||||||
def is_coinbase(self):
|
def is_generation(self):
|
||||||
return self.inputs[0].is_coinbase if len(self.inputs) > 0 else False
|
return self.inputs[0].is_generation if len(self.inputs) > 0 else False
|
||||||
|
|
||||||
|
|
||||||
class DeserializerZcash(DeserializerEquihash):
|
class DeserializerZcash(DeserializerEquihash):
|
||||||
@ -361,8 +361,8 @@ class TxTime(namedtuple("Tx", "version time inputs outputs locktime")):
|
|||||||
'''Class representing transaction that has a time field.'''
|
'''Class representing transaction that has a time field.'''
|
||||||
|
|
||||||
@cachedproperty
|
@cachedproperty
|
||||||
def is_coinbase(self):
|
def is_generation(self):
|
||||||
return self.inputs[0].is_coinbase
|
return self.inputs[0].is_generation
|
||||||
|
|
||||||
|
|
||||||
class DeserializerTxTime(Deserializer):
|
class DeserializerTxTime(Deserializer):
|
||||||
@ -445,13 +445,10 @@ class DeserializerGroestlcoin(DeserializerSegWit):
|
|||||||
class TxInputDcr(namedtuple("TxInput", "prev_hash prev_idx tree sequence")):
|
class TxInputDcr(namedtuple("TxInput", "prev_hash prev_idx tree sequence")):
|
||||||
'''Class representing a Decred transaction input.'''
|
'''Class representing a Decred transaction input.'''
|
||||||
|
|
||||||
ZERO = bytes(32)
|
|
||||||
MINUS_1 = 4294967295
|
|
||||||
|
|
||||||
@cachedproperty
|
@cachedproperty
|
||||||
def is_coinbase(self):
|
def is_generation(self):
|
||||||
return (self.prev_hash == TxInputDcr.ZERO and
|
return (self.prev_idx == TxInput.MINUS_1 and
|
||||||
self.prev_idx == TxInputDcr.MINUS_1)
|
self.prev_hash == TxInput.ZERO)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
prev_hash = hash_to_hex_str(self.prev_hash)
|
prev_hash = hash_to_hex_str(self.prev_hash)
|
||||||
@ -469,8 +466,8 @@ class TxDcr(namedtuple("Tx", "version inputs outputs locktime expiry "
|
|||||||
'''Class representing a Decred transaction.'''
|
'''Class representing a Decred transaction.'''
|
||||||
|
|
||||||
@cachedproperty
|
@cachedproperty
|
||||||
def is_coinbase(self):
|
def is_generation(self):
|
||||||
return self.inputs[0].is_coinbase
|
return self.inputs[0].is_generation
|
||||||
|
|
||||||
|
|
||||||
class DeserializerDecred(Deserializer):
|
class DeserializerDecred(Deserializer):
|
||||||
@ -546,7 +543,7 @@ class DeserializerDecred(Deserializer):
|
|||||||
|
|
||||||
# Drop the coinbase-like input from a vote tx as it creates problems
|
# Drop the coinbase-like input from a vote tx as it creates problems
|
||||||
# with UTXOs lookups and mempool management
|
# with UTXOs lookups and mempool management
|
||||||
if inputs[0].is_coinbase and len(inputs) > 1:
|
if inputs[0].is_generation and len(inputs) > 1:
|
||||||
inputs = inputs[1:]
|
inputs = inputs[1:]
|
||||||
|
|
||||||
if produce_hash:
|
if produce_hash:
|
||||||
|
|||||||
@ -411,7 +411,7 @@ class BlockProcessor(object):
|
|||||||
tx_numb = s_pack('<I', tx_num)
|
tx_numb = s_pack('<I', tx_num)
|
||||||
|
|
||||||
# Spend the inputs
|
# Spend the inputs
|
||||||
if not tx.is_coinbase:
|
if not tx.is_generation:
|
||||||
for txin in tx.inputs:
|
for txin in tx.inputs:
|
||||||
cache_value = spend_utxo(txin.prev_hash, txin.prev_idx)
|
cache_value = spend_utxo(txin.prev_hash, txin.prev_idx)
|
||||||
undo_info_append(cache_value)
|
undo_info_append(cache_value)
|
||||||
@ -490,7 +490,7 @@ class BlockProcessor(object):
|
|||||||
touched.add(cache_value[:-12])
|
touched.add(cache_value[:-12])
|
||||||
|
|
||||||
# Restore the inputs
|
# Restore the inputs
|
||||||
if not tx.is_coinbase:
|
if not tx.is_generation:
|
||||||
for txin in reversed(tx.inputs):
|
for txin in reversed(tx.inputs):
|
||||||
n -= undo_entry_len
|
n -= undo_entry_len
|
||||||
undo_item = undo_info[n:n + undo_entry_len]
|
undo_item = undo_info[n:n + undo_entry_len]
|
||||||
|
|||||||
@ -586,9 +586,7 @@ class DB(object):
|
|||||||
self.write_utxo_state(batch)
|
self.write_utxo_state(batch)
|
||||||
|
|
||||||
async def all_utxos(self, hashX):
|
async def all_utxos(self, hashX):
|
||||||
'''Return all UTXOs for an address sorted in no particular order. By
|
'''Return all UTXOs for an address sorted in no particular order.'''
|
||||||
default yields at most 1000 entries.
|
|
||||||
'''
|
|
||||||
def read_utxos():
|
def read_utxos():
|
||||||
utxos = []
|
utxos = []
|
||||||
utxos_append = utxos.append
|
utxos_append = utxos.append
|
||||||
|
|||||||
@ -985,7 +985,7 @@ class ElectrumX(SessionBase):
|
|||||||
'''Return a raw block header as a hexadecimal string.
|
'''Return a raw block header as a hexadecimal string.
|
||||||
|
|
||||||
height: the header's height'''
|
height: the header's height'''
|
||||||
return self.block_header(height)
|
return await self.block_header(height)
|
||||||
|
|
||||||
async def block_headers(self, start_height, count, cp_height=0):
|
async def block_headers(self, start_height, count, cp_height=0):
|
||||||
'''Return count concatenated block headers as hex for the main chain;
|
'''Return count concatenated block headers as hex for the main chain;
|
||||||
|
|||||||
14
tests/blocks/pivx_mainnet_10000.json
Normal file
14
tests/blocks/pivx_mainnet_10000.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"hash": "00000000003ce77135d15257a57f7508b8c5021372de2eae554f3142b60944cc",
|
||||||
|
"size": 231,
|
||||||
|
"height": 10000,
|
||||||
|
"merkleroot": "bd27e18a8abbd501b775d32fc6402a1a474a0517c1c8b04b9d487b0203f6b0f0",
|
||||||
|
"tx": [
|
||||||
|
"bd27e18a8abbd501b775d32fc6402a1a474a0517c1c8b04b9d487b0203f6b0f0"
|
||||||
|
],
|
||||||
|
"time": 1454814715,
|
||||||
|
"nonce": 14876727,
|
||||||
|
"bits": "1c06b486",
|
||||||
|
"previousblockhash": "00000000023fecac81bc148c7ab26a0190dbabeac2e8e260f62dff5205596f02",
|
||||||
|
"block": "03000000026f590552ff2df660e2e8c2eaabdb90016ab27a8c14bc81acec3f0200000000f0b0f603027b489d4bb0c8c117054a471a2a40c62fd375b701d5bb8a8ae127bdfbb5b65686b4061c3700e3000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1f02102704fbb5b65608380001169c6108000d2f6e6f64655374726174756d2f000000000200c817a8040000001976a91493d5f8ec14e203ef275ebfe942bc6a1e2784f95088ac00f2052a010000001976a914d6716d39693e034596a1e8546b4d65e4a06159cc88ac00000000"
|
||||||
|
}
|
||||||
15
tests/blocks/pivx_mainnet_1000000.json
Normal file
15
tests/blocks/pivx_mainnet_1000000.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"hash": "bb6127b99bf96b150c86ac38fde17c47d6c95b460c462ef45fd5043a44479068",
|
||||||
|
"size": 461,
|
||||||
|
"height": 1000000,
|
||||||
|
"merkleroot": "ecafe8267cbc5e4b6f4aec0978b02f0ba47a19828b0f8780e25685184d61c9e2",
|
||||||
|
"tx": [
|
||||||
|
"290871d1dc1673069a4c3efef424278b377becbff0c776e20972d212daa4a3d4",
|
||||||
|
"e217611a8db9ec5994632e1f66727c53e0ba09c406f6c993c8d52f5474efe620"
|
||||||
|
],
|
||||||
|
"time": 1516510311,
|
||||||
|
"nonce": 0,
|
||||||
|
"bits": "1a7128a9",
|
||||||
|
"previousblockhash": "795ab132be197a0a173cbe3590660235d906e0011052a10206244e0eae4b5152",
|
||||||
|
"block": "0400000052514bae0e4e240602a1521001e006d93502669035be3c170a7a19be32b15a79e2c9614d188556e280870f8b82197aa40b2fb07809ec4a6f4b5ebc7c26e8afec671c645aa928711a000000004ff5c33f2832ca5fa5ecbefb33c068e32ad0ae6e47c71be916fc34615c299b8c0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff060340420f0101ffffffff01000000000000000000000000000100000001eb79b426b80013c330e13e49764deeba311f8be69d0ec117d5b4f11d72215f3e0100000049483045022100f495580e30a64b4945582fe3e9a9118a60b53ca22c44232c170ff99a9fa7e8210220712921e4ddda1afe47b2481f2d1512759aaa0ab584f16ffe4b28771437c716be01ffffffff03000000000000000000500ae8470c0000002321037e0b96219461080ce2f837825fd3c52b08199c404a0d6d59f026a6a890dc4b15ac808ef20d000000001976a914f0364dfac5fca72abd79085c6b00f9e8f35bbff688ac0000000046304402203b270304b2fa14362d9718c16bf20e5c6ede49abfa7fe5fc5326b9f598d592d402203b32fda981ead1df35aebc1eca9b4766555dd2f200538f6b3d62d6ccc5180aa7"
|
||||||
|
}
|
||||||
24
tests/lib/test_coins.py
Normal file
24
tests/lib/test_coins.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import electrumx.lib.coins as coins
|
||||||
|
|
||||||
|
|
||||||
|
def test_bitcoin_cash():
|
||||||
|
raw_header = bytes.fromhex(
|
||||||
|
"00000020df975c121dcbc18bbb7ddfd0419fc368b45db86b48c87e0"
|
||||||
|
"1000000000000000036ae3dd40a10a40d3050de13ca546a2f81589d"
|
||||||
|
"e2d2f317925a43a115437e2381f5bf535b94da0118ac8df8c5"
|
||||||
|
)
|
||||||
|
height = 540000
|
||||||
|
electrum_header = {
|
||||||
|
'block_height': 540000,
|
||||||
|
'version': 536870912,
|
||||||
|
'prev_block_hash':
|
||||||
|
'0000000000000000017ec8486bb85db468c39f41d0df7dbb8bc1cb1d125c97df',
|
||||||
|
'merkle_root':
|
||||||
|
'81237e4315a1435a9217f3d2e29d58812f6a54ca13de50300da4100ad43dae36',
|
||||||
|
'timestamp': 1532215285,
|
||||||
|
'bits': 402774676,
|
||||||
|
'nonce': 3321400748
|
||||||
|
}
|
||||||
|
|
||||||
|
assert coins.BitcoinCash.electrum_header(
|
||||||
|
raw_header, height) == electrum_header
|
||||||
Loading…
Reference in New Issue
Block a user