Merge pull request #192 from erasmospunk/legacy-rpc
Legacy rpc and coin additions
This commit is contained in:
commit
721685ba4b
83
lib/coins.py
83
lib/coins.py
@ -39,9 +39,10 @@ from hashlib import sha256
|
||||
import lib.util as util
|
||||
from lib.hash import Base58, hash160, double_sha256, hash_to_str
|
||||
from lib.script import ScriptPubKey
|
||||
from lib.tx import Deserializer, DeserializerSegWit, DeserializerAuxPow, DeserializerZcash
|
||||
from lib.tx import Deserializer, DeserializerSegWit, DeserializerAuxPow, \
|
||||
DeserializerZcash, DeserializerTxTime, DeserializerReddcoin
|
||||
from server.block_processor import BlockProcessor
|
||||
from server.daemon import Daemon
|
||||
from server.daemon import Daemon, LegacyRPCDaemon
|
||||
from server.session import ElectrumX
|
||||
|
||||
Block = namedtuple("Block", "header transactions")
|
||||
@ -667,6 +668,7 @@ class DigiByte(Coin):
|
||||
WIF_BYTE = bytes.fromhex("80")
|
||||
GENESIS_HASH = ('7497ea1b465eb39f1c8f507bc877078f'
|
||||
'e016d6fcb6dfad3a64c98dcc6e1e8496')
|
||||
DESERIALIZER = DeserializerSegWit
|
||||
TX_COUNT = 1046018
|
||||
TX_COUNT_HEIGHT = 1435000
|
||||
TX_PER_BLOCK = 1000
|
||||
@ -801,3 +803,80 @@ class Einsteinium(Coin):
|
||||
IRC_PREFIX = "E_"
|
||||
IRC_CHANNEL = "#electrum-emc2"
|
||||
RPC_PORT = 41879
|
||||
REORG_LIMIT = 2000
|
||||
|
||||
|
||||
class Blackcoin(Coin):
|
||||
NAME = "Blackcoin"
|
||||
SHORTNAME = "BLK"
|
||||
NET = "mainnet"
|
||||
XPUB_VERBYTES = bytes.fromhex("0488B21E")
|
||||
XPRV_VERBYTES = bytes.fromhex("0488ADE4")
|
||||
P2PKH_VERBYTE = bytes.fromhex("19")
|
||||
P2SH_VERBYTES = [bytes.fromhex("55")]
|
||||
WIF_BYTE = bytes.fromhex("99")
|
||||
GENESIS_HASH = ('000001faef25dec4fbcf906e6242621d'
|
||||
'f2c183bf232f263d0ba5b101911e4563')
|
||||
DESERIALIZER = DeserializerTxTime
|
||||
DAEMON = LegacyRPCDaemon
|
||||
TX_COUNT = 4594999
|
||||
TX_COUNT_HEIGHT = 1667070
|
||||
TX_PER_BLOCK = 3
|
||||
IRC_PREFIX = "E_"
|
||||
IRC_CHANNEL = "#electrum-blk"
|
||||
RPC_PORT = 15715
|
||||
REORG_LIMIT = 5000
|
||||
HEADER_HASH = None
|
||||
|
||||
@classmethod
|
||||
def header_hash(cls, header):
|
||||
'''Given a header return the hash.'''
|
||||
if cls.HEADER_HASH is None:
|
||||
import scrypt
|
||||
cls.HEADER_HASH = lambda x: scrypt.hash(x, x, 1024, 1, 1, 32)
|
||||
|
||||
version, = struct.unpack('<I', header[:4])
|
||||
if version > 6:
|
||||
return super().header_hash(header)
|
||||
else:
|
||||
return cls.HEADER_HASH(header);
|
||||
|
||||
|
||||
class Peercoin(Coin):
|
||||
NAME = "Peercoin"
|
||||
SHORTNAME = "PPC"
|
||||
NET = "mainnet"
|
||||
P2PKH_VERBYTE = bytes.fromhex("37")
|
||||
P2SH_VERBYTES = [bytes.fromhex("75")]
|
||||
WIF_BYTE = bytes.fromhex("b7")
|
||||
GENESIS_HASH = ('0000000032fe677166d54963b62a4677'
|
||||
'd8957e87c508eaa4fd7eb1c880cd27e3')
|
||||
DESERIALIZER = DeserializerTxTime
|
||||
DAEMON = LegacyRPCDaemon
|
||||
TX_COUNT = 1207356
|
||||
TX_COUNT_HEIGHT = 306425
|
||||
TX_PER_BLOCK = 4
|
||||
IRC_PREFIX = "E_"
|
||||
IRC_CHANNEL = "#electrum-ppc"
|
||||
RPC_PORT = 9902
|
||||
REORG_LIMIT = 5000
|
||||
|
||||
|
||||
class Reddcoin(Coin):
|
||||
NAME = "Reddcoin"
|
||||
SHORTNAME = "RDD"
|
||||
NET = "mainnet"
|
||||
XPUB_VERBYTES = bytes.fromhex("0488B21E")
|
||||
XPRV_VERBYTES = bytes.fromhex("0488ADE4")
|
||||
P2PKH_VERBYTE = bytes.fromhex("3d")
|
||||
P2SH_VERBYTES = [bytes.fromhex("05")]
|
||||
WIF_BYTE = bytes.fromhex("bd")
|
||||
GENESIS_HASH = ('b868e0d95a3c3c0e0dadc67ee587aaf9'
|
||||
'dc8acbf99e3b4b3110fad4eb74c1decc')
|
||||
DESERIALIZER = DeserializerReddcoin
|
||||
TX_COUNT = 5413508
|
||||
TX_COUNT_HEIGHT = 1717382
|
||||
TX_PER_BLOCK = 3
|
||||
IRC_PREFIX = "E_"
|
||||
IRC_CHANNEL = "#electrum-rdd"
|
||||
RPC_PORT = 45443
|
||||
|
||||
53
lib/tx.py
53
lib/tx.py
@ -123,6 +123,11 @@ class Deserializer(object):
|
||||
self._read_varbytes(), # pk_script
|
||||
)
|
||||
|
||||
def _read_byte(self):
|
||||
cursor = self.cursor
|
||||
self.cursor += 1
|
||||
return self.binary[cursor]
|
||||
|
||||
def _read_nbytes(self, n):
|
||||
cursor = self.cursor
|
||||
self.cursor = end = cursor + n
|
||||
@ -182,11 +187,6 @@ class DeserializerSegWit(Deserializer):
|
||||
|
||||
# https://bitcoincore.org/en/segwit_wallet_dev/#transaction-serialization
|
||||
|
||||
def _read_byte(self):
|
||||
cursor = self.cursor
|
||||
self.cursor += 1
|
||||
return self.binary[cursor]
|
||||
|
||||
def _read_witness(self, fields):
|
||||
read_witness_field = self._read_witness_field
|
||||
return [read_witness_field() for i in range(fields)]
|
||||
@ -290,3 +290,46 @@ class DeserializerZcash(Deserializer):
|
||||
self.cursor += 32 # joinSplitPubKey
|
||||
self.cursor += 64 # joinSplitSig
|
||||
return base_tx, double_sha256(self.binary[start:self.cursor])
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
class DeserializerTxTime(Deserializer):
|
||||
def read_tx(self):
|
||||
start = self.cursor
|
||||
|
||||
return TxTime(
|
||||
self._read_le_int32(), # version
|
||||
self._read_le_uint32(), # time
|
||||
self._read_inputs(), # inputs
|
||||
self._read_outputs(), # outputs
|
||||
self._read_le_uint32(), # locktime
|
||||
), double_sha256(self.binary[start:self.cursor])
|
||||
|
||||
|
||||
class DeserializerReddcoin(Deserializer):
|
||||
def read_tx(self):
|
||||
start = self.cursor
|
||||
|
||||
version = self._read_le_int32()
|
||||
inputs = self._read_inputs()
|
||||
outputs = self._read_outputs()
|
||||
locktime = self._read_le_uint32()
|
||||
if version > 1:
|
||||
time = self._read_le_uint32()
|
||||
else:
|
||||
time = 0
|
||||
|
||||
return TxTime(
|
||||
version,
|
||||
time,
|
||||
inputs,
|
||||
outputs,
|
||||
locktime,
|
||||
), double_sha256(self.binary[start:self.cursor])
|
||||
|
||||
15
lib/util.py
15
lib/util.py
@ -34,6 +34,7 @@ import logging
|
||||
import re
|
||||
import sys
|
||||
from collections import Container, Mapping
|
||||
from struct import pack
|
||||
|
||||
|
||||
class LoggedClass(object):
|
||||
@ -156,6 +157,20 @@ def int_to_bytes(value):
|
||||
return value.to_bytes((value.bit_length() + 7) // 8, 'big')
|
||||
|
||||
|
||||
def int_to_varint(value):
|
||||
'''Converts an integer to a Bitcoin-like varint bytes'''
|
||||
if value < 0:
|
||||
raise Exception("attempt to write size < 0")
|
||||
elif value < 253:
|
||||
return pack('<B', value)
|
||||
elif value < 2**16:
|
||||
return b'\xfd' + pack('<H', value)
|
||||
elif value < 2**32:
|
||||
return b'\xfe' + pack('<I', value)
|
||||
elif value < 2**64:
|
||||
return b'\xff' + pack('<Q', value)
|
||||
|
||||
|
||||
def increment_byte_string(bs):
|
||||
'''Return the lexicographically next byte string of the same length.
|
||||
|
||||
|
||||
@ -12,10 +12,14 @@ import asyncio
|
||||
import json
|
||||
import time
|
||||
import traceback
|
||||
from calendar import timegm
|
||||
from struct import pack
|
||||
from time import strptime
|
||||
|
||||
import aiohttp
|
||||
|
||||
import lib.util as util
|
||||
from lib.hash import hex_str_to_hash
|
||||
|
||||
|
||||
class DaemonError(Exception):
|
||||
@ -256,3 +260,61 @@ class DashDaemon(Daemon):
|
||||
async def masternode_list(self, params ):
|
||||
'''Return the masternode status.'''
|
||||
return await self._send_single('masternodelist', params)
|
||||
|
||||
|
||||
class LegacyRPCDaemon(Daemon):
|
||||
'''Handles connections to a daemon at the given URL.
|
||||
|
||||
This class is useful for daemons that don't have the new 'getblock'
|
||||
RPC call that returns the block in hex, the workaround is to manually
|
||||
recreate the block bytes. The recreated block bytes may not be the exact
|
||||
as in the underlying blockchain but it is good enough for our indexing
|
||||
purposes.'''
|
||||
|
||||
|
||||
async def raw_blocks(self, hex_hashes):
|
||||
'''Return the raw binary blocks with the given hex hashes.'''
|
||||
params_iterable = ((h, False) for h in hex_hashes)
|
||||
block_info = await self._send_vector('getblock', params_iterable)
|
||||
|
||||
blocks = []
|
||||
for i in block_info:
|
||||
raw_block = await self.make_raw_block(i)
|
||||
blocks.append(raw_block)
|
||||
|
||||
# Convert hex string to bytes
|
||||
return blocks
|
||||
|
||||
async def make_raw_header(self, b):
|
||||
pbh = b.get('previousblockhash')
|
||||
if pbh is None:
|
||||
pbh = '0' * 64
|
||||
header = pack('<L', b.get('version')) \
|
||||
+ hex_str_to_hash(pbh) \
|
||||
+ hex_str_to_hash(b.get('merkleroot')) \
|
||||
+ pack('<L', self.timestamp_safe(b['time'])) \
|
||||
+ pack('<L', int(b.get('bits'), 16)) \
|
||||
+ pack('<L', int(b.get('nonce')))
|
||||
return header
|
||||
|
||||
async def make_raw_block(self, b):
|
||||
'''Construct a raw block'''
|
||||
|
||||
header = await self.make_raw_header(b)
|
||||
|
||||
transactions = []
|
||||
if b.get('height') > 0:
|
||||
transactions = await self.getrawtransactions(b.get('tx'), False)
|
||||
|
||||
raw_block = header
|
||||
num_txs = len(transactions)
|
||||
if num_txs > 0:
|
||||
raw_block += util.int_to_varint(num_txs)
|
||||
raw_block += b''.join(transactions)
|
||||
else:
|
||||
raw_block += b'\x00'
|
||||
|
||||
return raw_block
|
||||
|
||||
def timestamp_safe(self, t):
|
||||
return t if isinstance(t, int) else timegm(strptime(t, "%Y-%m-%d %H:%M:%S %Z"))
|
||||
|
||||
19
tests/blocks/digibyte_mainnet_4394891.json
Normal file
19
tests/blocks/digibyte_mainnet_4394891.json
Normal file
File diff suppressed because one or more lines are too long
15
tests/blocks/reddcoin_mainnet_1200000.json
Normal file
15
tests/blocks/reddcoin_mainnet_1200000.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"hash": "bea68724bfcdc5d35bf9bbf9cb6680e196e5661afe95b2a205e74a2fe175ac79",
|
||||
"size": 443,
|
||||
"height": 1200000,
|
||||
"merkleroot": "504b073c16d872d24f2c3de8a4c2c76d08df5056f3a4a8d0e32ff4220215a250",
|
||||
"tx": [
|
||||
"6aaad9725ae7beb40d80dac9c5300a8e1cf8783adb0ea41da4988b3476bda9b8",
|
||||
"4a949402995c11b3306c0c91fd85edf0d3eb8dee4bf6bd07a241fa170156cd3c"
|
||||
],
|
||||
"time": 1463612841,
|
||||
"nonce": 0,
|
||||
"bits": "1c0a4691",
|
||||
"previousblockhash": "438b564171da6fbbe6fd9d52c16ea2b1aa8c169951822225cf097d5da7cdba76",
|
||||
"block": "0300000076bacda75d7d09cf2522825199168caab1a26ec1529dfde6bb6fda7141568b4350a2150222f42fe3d0a8a4f35650df086dc7c2a4e83d2c4fd272d8163c074b50a9f53c5791460a1c000000000202000000010000000000000000000000000000000000000000000000000000000000000000ffffffff020000ffffffff0100000000000000000000000000a9f53c570200000001a40cad8a9afe2888f746d762cb36649b5afd4e8ce4468fd8d08fc296d26dc4840100000048473044022036392ee6eb58c5a9a2a681692cabdc2b00166c374cfb711055bc2c4d6c61a1d40220475728eed260bf972ef44909f0d6fa282f17e92b5e57ee383c7171e8a3baee1f01ffffffff030000000000000000000056b12a38720000232102bee8ce24a99260fbb6c10f0b904498fa71ec08e51b531878d3f6568ef09acb91ac0ad6b22a38720000232102bee8ce24a99260fbb6c10f0b904498fa71ec08e51b531878d3f6568ef09acb91ac00000000a9f53c57473045022100fe801bae06c9db3076fad2f72930f76dbe1cae29a162447b13d0df749e5913df02203621013f87da4dbca08702d8c7975f702bad9df40902038b93e622a0dd9c0896"
|
||||
}
|
||||
20
tests/blocks/reddcoin_mainnet_8000.json
Normal file
20
tests/blocks/reddcoin_mainnet_8000.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"hash": "4889bb7d1ba24cc66c2d903f6643b0ade243aca5101a8aff87ab4c2ab2a15ec5",
|
||||
"size": 1560,
|
||||
"height": 80000,
|
||||
"merkleroot": "193313cfa4d8a4bc15fb5526b69a87c922e0f6520295f66165358f0af6b5d637",
|
||||
"tx": [
|
||||
"ad01e368a301b855d5f4499bc787b161428d6994c4847c0b2813950630a73950",
|
||||
"1799481d7fed61c029159d314f75f3d6f69a7f8c237443470394085307802782",
|
||||
"8db4b2c62fca987462c482d24ce0b78d2a3dd3928d5d99112ccad75deb6ff7de",
|
||||
"ab0a1e66e54c737be6ea2be2c61cd55879d33c0fc5d35aa6389487e06c809cfc",
|
||||
"1bb3854ed7fe9905b5637d405cd0715e5cb6f5fe233304a1588c53bdcf60f593",
|
||||
"08d3ccf77f30e62d8773669adea730698806516239933ac7c4285bcacdb37989",
|
||||
"19cbdc4acfb07dc29c73f039d8f5db967ce30c0667fda60babc700a7c53c0b5f"
|
||||
],
|
||||
"time": 1396181239,
|
||||
"nonce": 1368399360,
|
||||
"bits": "1c0cc111",
|
||||
"previousblockhash": "e34cfbf84095c64276ee098de50c3a1f863df9336968e9fb8a973bdd52e3ed04",
|
||||
"block": "0200000004ede352dd3b978afbe9686933f93d861f3a0ce58d09ee7642c69540f8fb4ce337d6b5f60a8f356561f6950252f6e022c9879ab62655fb15bca4d8a4cf133319f708385311c10c1c001e90510701000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2703803801062f503253482f04f608385308f800f159010000000d2f7374726174756d506f6f6c2f000000000100e63572180900001976a9148d41bc27ab2cc999338750edd4b0012bdb36f70288ac0000000001000000014b1a8085e29ca8ef2bf1de5f0637c6ef597b67223a087458e51e21168a0e44a3000000006b48304502200b781c255481e90f0e1d2fedbc1ffb42562434c324566444da8718a8a2c5182d022100f50faa7a9f7b90b4b805050c9731a79fb9c599ddfb3d84449d0cff7ee216bf59012103d7ab8ea88d09589410bdb93cd466d92f56985a3cff6d74dce3f033500135f0c5ffffffff02d72ea96581330e001976a91422758e8f790ea0e4ab87ad3990e8af86c77375c088ac1c1cab70190000001976a91434e880ed4cb32ebb1e0842b4f05efe562724f08788ac000000000100000001616c5b1a7ee823fa2d5347011b34e1ea027f9494823d37fb175eece8f852f987000000006a473044022000be9cf6677d879d170c597b8a465137577119ebc7d01773dc13df7af7e0bf1102202acfce90f478c0d179ab98d708f1e24f6dab4fe60c75893f8bad12991b30f41301210355dad820f63f1c315dc16c5afd9782e4d0b225ea29320a85576bc2c82fde6e7effffffff02ceb618fa97ac10001976a914e14548bfd2e14e0cabaf535c7c80a227238b35e188ac1c1cab70190000001976a914d2046a1ad1dbc32e69dae4da0a8730379105936e88ac000000000100000001a6b3081431b43c3247df88b3b6d123d2f2d7ba2095c6ef4f6532feb2c45f9210010000006b4830450221008fb902cc4130bae26439c47c13467a7d8a8c52ac2d88a200548f1e8f8b100b910220125b45cee0765389a59d4cca65482bdf79d3bc8fdaa5a0142e7829e4a2568124012103cdece1576249c8e05fb0aa2cbe61aa959330ff2f9e3c5cd2e5152e90650d9386ffffffff02bbba56d0d88606001976a91407499b20688a0b61b4a526681647de739dab818e88ac1c1cab70190000001976a9147085556af12556138277188e3958a869eeced02088ac000000000100000001fc9c806ce0879438a65ad3c50f3cd37958d51cc6e22beae67b734ce5661e0aab000000006c493046022100dca959b02a4dde588b3e5c3e71877797b97d7094a82cdd6b6b52c3d04a8c17c3022100938b2f70eed007d20ef9d7d055fc9b8785e71e3f0981558503fb3635b08aa6d40121039d216b71bad34246ceff262afe6df520761fc696fd9862c3f2f7e337ad93d881ffffffff0202386cc4f57e06001976a914ee343e816e6782262c3f6b1b9ec8f8c17d47a88c88acb9a1f405e30700001976a914ba81e33df7ba3d18728c6c206f8ad0b30b83b71988ac00000000010000000193f560cfbd538c58a1043323fef5b65c5e71d05c407d63b50599fed74e85b31b000000006a4730440220153f0a0a16e13943c4869e8f768c64e9f1844d14823f80878a6e44752a041c49022036ec13a307bafee74387048c3772cfb5ebdc138d70d6b4c256788a86db93ab5801210281232e155b37ebd64759ee4983962e9f8ccfd95e302d828de1406549e7c327a4ffffffff029014fad0166506001976a914b05959ea5dd831fd082488298466c9307a46f55b88ac72427cedde1900001976a914c2e3e90990f452c19ccef5df1cc3711c2e5d448288ac0000000001000000018979b3cdca5b28c4c73a9339625106886930a7de9a6673872de6307ff7ccd308000000006b483045022100ec50258bfec642e6c986192f338b7a1eec84c872d9b51ccc6f1c7329da20af77022047a6836d7c5f416c2eef6ef59fae9cc627ff80882897fe3eabd775e2a4a08533012102240bb70ae679cb25d60e2e0f90f98017eac7b6abbf1e00797ef930f02f0b98eeffffffff029e75ab66cd6306001976a9144c9ef3b178febefc62a0067e67e8434afe864a6788acf2bd5864490100001976a9143cde6d950e730b199c5857564afe7f222e139ead88ac00000000"
|
||||
}
|
||||
@ -47,7 +47,7 @@ for name in os.listdir(BLOCKS_DIR):
|
||||
with open(os.path.join(BLOCKS_DIR, name)) as f:
|
||||
blocks.append((coin, json.load(f)))
|
||||
except Exception as e:
|
||||
blocks.append(pytest.mark.skip(name))
|
||||
blocks.append(pytest.fail(name))
|
||||
|
||||
|
||||
@pytest.fixture(params=blocks)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user