Merge branch 'master' into devel
This commit is contained in:
commit
a108817dd4
@ -2100,3 +2100,42 @@ class GroestlcoinTestnet(Groestlcoin):
|
||||
'7frvhgofuf522b5i.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.'''
|
||||
|
||||
@cachedproperty
|
||||
def is_coinbase(self):
|
||||
return self.inputs[0].is_coinbase
|
||||
def is_generation(self):
|
||||
return self.inputs[0].is_generation
|
||||
|
||||
def serialize(self):
|
||||
return b''.join((
|
||||
@ -63,9 +63,9 @@ class TxInput(namedtuple("TxInput", "prev_hash prev_idx script sequence")):
|
||||
MINUS_1 = 4294967295
|
||||
|
||||
@cachedproperty
|
||||
def is_coinbase(self):
|
||||
return (self.prev_hash == TxInput.ZERO and
|
||||
self.prev_idx == TxInput.MINUS_1)
|
||||
def is_generation(self):
|
||||
return (self.prev_idx == TxInput.MINUS_1 and
|
||||
self.prev_hash == TxInput.ZERO)
|
||||
|
||||
def __str__(self):
|
||||
script = self.script.hex()
|
||||
@ -215,8 +215,8 @@ class TxSegWit(namedtuple("Tx", "version marker flag inputs outputs "
|
||||
'''Class representing a SegWit transaction.'''
|
||||
|
||||
@cachedproperty
|
||||
def is_coinbase(self):
|
||||
return self.inputs[0].is_coinbase
|
||||
def is_generation(self):
|
||||
return self.inputs[0].is_generation
|
||||
|
||||
|
||||
class DeserializerSegWit(Deserializer):
|
||||
@ -327,8 +327,8 @@ class TxJoinSplit(namedtuple("Tx", "version inputs outputs locktime")):
|
||||
'''Class representing a JoinSplit transaction.'''
|
||||
|
||||
@cachedproperty
|
||||
def is_coinbase(self):
|
||||
return self.inputs[0].is_coinbase if len(self.inputs) > 0 else False
|
||||
def is_generation(self):
|
||||
return self.inputs[0].is_generation if len(self.inputs) > 0 else False
|
||||
|
||||
|
||||
class DeserializerZcash(DeserializerEquihash):
|
||||
@ -361,8 +361,8 @@ class TxTime(namedtuple("Tx", "version time inputs outputs locktime")):
|
||||
'''Class representing transaction that has a time field.'''
|
||||
|
||||
@cachedproperty
|
||||
def is_coinbase(self):
|
||||
return self.inputs[0].is_coinbase
|
||||
def is_generation(self):
|
||||
return self.inputs[0].is_generation
|
||||
|
||||
|
||||
class DeserializerTxTime(Deserializer):
|
||||
@ -445,13 +445,10 @@ class DeserializerGroestlcoin(DeserializerSegWit):
|
||||
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):
|
||||
return (self.prev_hash == TxInputDcr.ZERO and
|
||||
self.prev_idx == TxInputDcr.MINUS_1)
|
||||
def is_generation(self):
|
||||
return (self.prev_idx == TxInput.MINUS_1 and
|
||||
self.prev_hash == TxInput.ZERO)
|
||||
|
||||
def __str__(self):
|
||||
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.'''
|
||||
|
||||
@cachedproperty
|
||||
def is_coinbase(self):
|
||||
return self.inputs[0].is_coinbase
|
||||
def is_generation(self):
|
||||
return self.inputs[0].is_generation
|
||||
|
||||
|
||||
class DeserializerDecred(Deserializer):
|
||||
@ -546,7 +543,7 @@ class DeserializerDecred(Deserializer):
|
||||
|
||||
# Drop the coinbase-like input from a vote tx as it creates problems
|
||||
# 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:]
|
||||
|
||||
if produce_hash:
|
||||
|
||||
@ -411,7 +411,7 @@ class BlockProcessor(object):
|
||||
tx_numb = s_pack('<I', tx_num)
|
||||
|
||||
# Spend the inputs
|
||||
if not tx.is_coinbase:
|
||||
if not tx.is_generation:
|
||||
for txin in tx.inputs:
|
||||
cache_value = spend_utxo(txin.prev_hash, txin.prev_idx)
|
||||
undo_info_append(cache_value)
|
||||
@ -490,7 +490,7 @@ class BlockProcessor(object):
|
||||
touched.add(cache_value[:-12])
|
||||
|
||||
# Restore the inputs
|
||||
if not tx.is_coinbase:
|
||||
if not tx.is_generation:
|
||||
for txin in reversed(tx.inputs):
|
||||
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)
|
||||
|
||||
async def all_utxos(self, hashX):
|
||||
'''Return all UTXOs for an address sorted in no particular order. By
|
||||
default yields at most 1000 entries.
|
||||
'''
|
||||
'''Return all UTXOs for an address sorted in no particular order.'''
|
||||
def read_utxos():
|
||||
utxos = []
|
||||
utxos_append = utxos.append
|
||||
|
||||
@ -985,7 +985,7 @@ class ElectrumX(SessionBase):
|
||||
'''Return a raw block header as a hexadecimal string.
|
||||
|
||||
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):
|
||||
'''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