Browse Source

Merge pull request #192 from erasmospunk/legacy-rpc

Legacy rpc and coin additions
master
Neil 8 years ago
committed by GitHub
parent
commit
721685ba4b
  1. 83
      lib/coins.py
  2. 53
      lib/tx.py
  3. 15
      lib/util.py
  4. 62
      server/daemon.py
  5. 19
      tests/blocks/digibyte_mainnet_4394891.json
  6. 15
      tests/blocks/reddcoin_mainnet_1200000.json
  7. 20
      tests/blocks/reddcoin_mainnet_8000.json
  8. 2
      tests/test_blocks.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

@ -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

@ -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.

62
server/daemon.py

@ -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

File diff suppressed because one or more lines are too long

15
tests/blocks/reddcoin_mainnet_1200000.json

@ -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

@ -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"
}

2
tests/test_blocks.py

@ -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…
Cancel
Save