diff --git a/.travis.yml b/.travis.yml
index 9439f01..2b22bc7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -31,11 +31,13 @@ install:
- pip install blake256
- pip install scrypt
- pip install x11_hash
+ - pip install git+https://github.com/bitcoinplusorg/x13-hash
- pip install xevan_hash
- pip install quark_hash
- pip install groestlcoin_hash
- pip install git+https://github.com/goacoincore/neoscrypt
- pip install git+https://github.com/motioncrypto/x16r_hash
+ - pip install pycryptodomex
# command to run tests
script:
- pytest --cov=electrumx
diff --git a/README.rst b/README.rst
index 03bdd8a..8dc06d4 100644
--- a/README.rst
+++ b/README.rst
@@ -22,3 +22,73 @@ See `readthedocs `_.
**Neil Booth** kyuupichan@gmail.com https://github.com/kyuupichan
bitcoincash:qzxpdlt8ehu9ehftw6rqsy2jgfq4nsltxvhrdmdfpn
+
+
+Supported Coins
+=============
+
+- Argentum - ARG
+- Bitbay - BAY
+- Bitcoin Cash - BCH
+- Bitcoin Gold - BTG
+- Bitcoin Segwit - BTC
+- Bitcoin SV - BSV
+- BitcoinAtom - BCA
+- BitcoinGreen - BITG
+- BitcoinPlus - XBC
+- BitcoinZ - BTCZ
+- Bitcore - BTX
+- Bitzeny - ZNY
+- Blackcoin - BLK
+- CanadaeCoin - CDN
+- Chips - CHIPS
+- CivX - CIVX
+- ColossusXT - COLX
+- Crown - CRW
+- Dash - DASH
+- Decred - DCR
+- Denarius - D
+- Digibyte - DGB
+- Dogecoin - DOGE
+- Einsteninium - EMC2
+- EmerCoin - EMC
+- Faircoin - FAIR
+- Feathercoin - FTC
+- Fujicoin - FJC
+- GameCredits - GAME
+- GoByte - GBX
+- Groestlcoin - GRS
+- HODLcoin - HODL
+- Hush - HUSH
+- Komodo - KMD
+- Koto - KOTO
+- Litecoin - LTC
+- Machinecoin - MAC
+- Minexcoin - MNX
+- Monacoin - MONA
+- Monaize - MNZ
+- Monoeci - XMCC
+- Motion - XMN
+- Myriadcoin - XMY
+- NIX - NIX
+- Namecoin - NMC
+- Neblio - NEBL
+- NewYorkcoin - NYC
+- Noir - NOR
+- Paccoin - PAC
+- Peercoin - PPC
+- Pivx - PIVX
+- Polis - POLIS
+- Reddcoin - RDD
+- Sibcoin - SIB
+- SmartCash - SMART
+- SnowGem - XSG
+- TokenPay - TPAY
+- Trezarcoin - TZC
+- Uniform Fiscal Object - UFO
+- Vertcoin - VTC
+- Viacoin - VIA
+- Xuez - XUEZ
+- Zcash - ZEC
+- Zclassic - ZCL
+- ZelCash - ZEL
diff --git a/docs/changelog.rst b/docs/changelog.rst
index ca0f9d8..23cc13e 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -7,6 +7,58 @@
and memory consumption whilst serving clients. Those problems
should not occur with Python 3.7.
+.. note:: Bitcoin ABC developers have hastily introduced controversial
+ changes that break ElectrumX's block processing by requiring it to
+ be non-sequential. Unlike others with unique requirements they
+ refused to make their code coin-specific. ElectrumX continues to
+ require blocks be naturally ordered, and is compatible with any
+ non-CToR daemon, such as Bitcoin SV, and Bitcoin Unlimited /
+ Bitcoin XT with CToR disabled.
+
+Version 1.8.12 (10 Nov 2018)
+============================
+
+* bug fix
+
+Version 1.8.11 (07 Nov 2018)
+============================
+
+* require aiorpcX 0.10.1
+
+Version 1.8.10 (05 Nov 2018)
+============================
+
+* require aiorpcX 0.10.0
+* fix `#632`_
+* coin additions / updates: ZelCash (TheTrunk)
+
+Version 1.8.9 (02 Nov 2018)
+===========================
+
+* fix `#630`_
+
+Version 1.8.8 (01 Nov 2018)
+===========================
+
+* require aiorpcX 0.9.0
+* coin additions / updates: decred (dajohi, bolapara), zcash (erasmospunk),
+ namecoin (JeremyRand),CivX (turcol), NewYorkCoin (erasmospunk)
+* fix `#603`_, `#608`_
+* other minor fixes and changes: FMCorz
+
+Version 1.8.7 (13 Sep 2018)
+===========================
+
+* require aiorpcX 0.8.1
+* fix reorg bug loading blocks from disk (erasmospunk)
+
+Version 1.8.6 (12 Sep 2018)
+===========================
+
+* require aiorpcX 0.8.0
+* suppress socket.send() errors
+* new coin TokenPay (samfiragabriel)
+* minor fix: wakiyamap
Version 1.8.7 (13 Sep 2018)
===========================
@@ -170,111 +222,14 @@ Version 1.5
* minor tweaks: romanz, you21979, SuBPaR42, sangaman, wakiyamap, DaShak
-Version 1.4.3
-=============
-
-* Fix `#442`_.
-
-Version 1.4.2
-=============
-
-* proxy remote IP reported properly if :envvar:`FORCE_PROXY` is set.
- Fixes `#301`_.
-* requires aiorpcx 0.5.5
-
-Version 1.4.1
-=============
-
-* minor bugfixes - cleaner shutdown; group handling
-* set PROTOCOL_MIN to 1.0; this will prevent 2.9.x clients from connecting
- and encourage upgrades to more recent clients without the security hole
-* requires aiorpcx 0.5.4
-
-Version 1.4
-===========
-
-* switch to `aiorpcX `_ for all
- networking, ``JSON RPC`` and proxy handling
-* proxy detection improvements
-* `documentation `_ rewrite
-* new environment variable :envvar:`LOG_FORMAT` to control logging format
-* new environment variable :envvar:`DROP_CLIENT` to cut off unsupported
- client software
-* coin updates: Litecoin (pooler), bitbayd (kongeo), BTG (wilsonmeier),
- danny91, wakiyamap, snowgem, Dash (theLazier), fujicoin
-* new coins: Decred (cipherzzz), axe (-k),
-* typo fixes (dax, romanz)
-
-.. note:: the Dash-specific undocumented ``masternode.subscribe()``
- RPC call was not following the JSON RPC spec; this was shown up by
- the switch to aiorpcX. I had to modify the code but it may break
- Dash clients.
-
- The Decred implementation doesn't work on mainnet; I will remove it
- if this remains unfixed.
-
-Version 1.3
-===========
-
-* Switch to :ref:`version 1.2` of the protocol.
- :func:`mempool.get_fee_histogram` implementation contributed by ecdsa,
- verbose mode of :func:`blockchain.transaction.get` by gdassori.
-* :func:`blockchain.scripthash.listunspent` now takes into account mempool
- spends and receipts.
-* Improved client notification handling.
-* Wait for mempool to fully sync before serving.
-* Documentation moved to `readthedocs.io
- `_. Rewritten and improved
- protocol documentation.
-* new/updated coins: Chips (cipig), Feathercoin (lclc), Zclassic(heyrhett),
- Dash (thelazier), NYC (xarakas), Koto (wo01), BitcoinZ (cipig), BitCore
- (cipig), Fujicoin (fujicoin), Bitcoin Atom (erasmospunk), Deanrius (carsenk),
- SNG (blackjok3rtt).
-* Minor fixes and improvements: duckartes, blin00, bauerj,
- erasmospunk, SomberNight, romanz.
-
-Version 1.2.1
-=============
-
-- remove IRC support. Most coins had empty IRC channels. Those that
- don't have peers populated.
-- use estimatesmartfee RPC call if available (SomberNight)
-- new/updated coins: Emercoin (Sergii Vakula), Bitcoin Gold (erasmospunk),
- Monacoin testnet (Wakiyama P), sibcoin (53r63rn4r), Komodo and Monaize
- (cipig), Hush (Duke Leto)
-- doc updates (fr3aker)
-- issues fixed: `#302`_
-
-Version 1.2
-===========
-
-.. note:: version 1.2 changes script hash indexing in the database, so
- you will need to rebuild your databases from scratch. Running this
- version will refuse to open the DB and not corrupt it, so you can
- revert to 1.1.x if you wish. The initial synchronisation process
- should be around 10-15% faster than 1.1, owing to this change and
- Justin Arthur's optimisations from 1.1.1.
-
-- separate P2PKH from P2PK entries in the history and UTXO databases.
- These were previously amalgamated by address as that is what
- electrum-server used to do. However Electrum didn't handle P2PK
- spends correctly and now the protocol admits subscriptions by script
- hash there is no need to have these merged any more.
-
-For Bitcoin (BitcoinSegwit/mainnet) you can download a leveldb database
-synced up to block 490153 using this bittorrent magnet
-`link (~24GB) `_.
-
**Neil Booth** kyuupichan@gmail.com https://github.com/kyuupichan
bitcoincash:qzxpdlt8ehu9ehftw6rqsy2jgfq4nsltxvhrdmdfpn
.. _#258: https://github.com/kyuupichan/electrumx/issues/258
-.. _#301: https://github.com/kyuupichan/electrumx/issues/301
.. _#315: https://github.com/kyuupichan/electrumx/issues/315
.. _#414: https://github.com/kyuupichan/electrumx/issues/414
-.. _#442: https://github.com/kyuupichan/electrumx/issues/442
.. _#443: https://github.com/kyuupichan/electrumx/issues/443
.. _#455: https://github.com/kyuupichan/electrumx/issues/455
.. _#479: https://github.com/kyuupichan/electrumx/issues/479
@@ -295,3 +250,7 @@ bitcoincash:qzxpdlt8ehu9ehftw6rqsy2jgfq4nsltxvhrdmdfpn
.. _#567: https://github.com/kyuupichan/electrumx/issues/567
.. _#570: https://github.com/kyuupichan/electrumx/issues/570
.. _#577: https://github.com/kyuupichan/electrumx/issues/577
+.. _#603: https://github.com/kyuupichan/electrumx/issues/603
+.. _#608: https://github.com/kyuupichan/electrumx/issues/608
+.. _#630: https://github.com/kyuupichan/electrumx/issues/630
+.. _#632: https://github.com/kyuupichan/electrumx/issues/630
diff --git a/docs/conf.py b/docs/conf.py
index 91e5f39..fc82fd3 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -15,7 +15,7 @@
import os
import sys
sys.path.insert(0, os.path.abspath('..'))
-VERSION="ElectrumX 1.8.7"
+VERSION="ElectrumX 1.8.12"
# -- Project information -----------------------------------------------------
diff --git a/docs/environment.rst b/docs/environment.rst
index 5c1de25..d95e578 100644
--- a/docs/environment.rst
+++ b/docs/environment.rst
@@ -418,6 +418,5 @@ your available physical RAM:
I do not recommend raising this above 2000.
-.. _lib/coins.py:
- https://github.com/kyuupichan/electrumx/blob/master/lib/coins.py
+.. _lib/coins.py: https://github.com/kyuupichan/electrumx/blob/master/electrumx/lib/coins.py
.. _uvloop: https://pypi.python.org/pypi/uvloop
diff --git a/electrumx/__init__.py b/electrumx/__init__.py
index 02d2af8..73c239f 100644
--- a/electrumx/__init__.py
+++ b/electrumx/__init__.py
@@ -1,4 +1,4 @@
-version = 'ElectrumX 1.8.7'
+version = 'ElectrumX 1.8.12'
version_short = version.split()[-1]
from electrumx.server.controller import Controller
diff --git a/electrumx/lib/coins.py b/electrumx/lib/coins.py
index 4064da3..96a8c08 100644
--- a/electrumx/lib/coins.py
+++ b/electrumx/lib/coins.py
@@ -40,12 +40,14 @@ import base64
import electrumx.lib.util as util
from electrumx.lib.hash import Base58, hash160, double_sha256, hash_to_hex_str
-from electrumx.lib.hash import HASHX_LEN
+from electrumx.lib.hash import HASHX_LEN, hex_str_to_hash
from electrumx.lib.script import ScriptPubKey, OpCodes
import electrumx.lib.tx as lib_tx
+import electrumx.lib.tx_dash as lib_tx_dash
import electrumx.server.block_processor as block_proc
import electrumx.server.daemon as daemon
-from electrumx.server.session import ElectrumX, DashElectrumX
+from electrumx.server.session import (ElectrumX, DashElectrumX,
+ SmartCashElectrumX)
Block = namedtuple("Block", "raw header transactions")
@@ -364,25 +366,34 @@ class HOdlcoin(Coin):
TX_PER_BLOCK = 5
-class BitcoinCash(BitcoinMixin, Coin):
- NAME = "BitcoinCash"
- SHORTNAME = "BCH"
- TX_COUNT = 246362688
- TX_COUNT_HEIGHT = 511484
+class BitcoinSV(BitcoinMixin, Coin):
+ NAME = "BitcoinSV"
+ SHORTNAME = "BSV"
+ TX_COUNT = 267318795
+ TX_COUNT_HEIGHT = 557037
TX_PER_BLOCK = 400
PEERS = [
- 'electroncash.cascharia.com s50002',
- 'bch.electrumx.cash s t',
- 'bccarihace4jdcnt.onion t52001 s52002',
- 'abc1.hsmiths.com t60001 s60002',
- 'electroncash.checksum0.com s t',
- 'electrumx-cash.1209k.com s t',
- 'electrum.leblancnet.us t50011 s50012',
- 'electroncash.dk s t',
- 'electrum.imaginary.cash s t',
+ 'sv.electrumx.cash s t',
+ 'sv1.hsmiths.com t60003 s60004',
+ 'satoshi.vision.cash s',
+ 'electroncash.cascharia.com s t',
]
+class BitcoinCash(BitcoinMixin, Coin):
+ NAME = "BitcoinCashABC" # Some releases later remove the ABC suffix
+ SHORTNAME = "BCH"
+ TX_COUNT = 265479628
+ TX_COUNT_HEIGHT = 556592
+ TX_PER_BLOCK = 400
+ PEERS = [
+ 'bch.imaginary.cash s t',
+ 'electroncash.dk s t',
+ 'wallet.satoshiscoffeehouse.com s t',
+ ]
+ BLOCK_PROCESSOR = block_proc.LTORBlockProcessor
+
+
class BitcoinSegwit(BitcoinMixin, Coin):
NAME = "BitcoinSegwit"
DESERIALIZER = lib_tx.DeserializerSegWit
@@ -395,7 +406,6 @@ class BitcoinSegwit(BitcoinMixin, Coin):
'E-X.not.fyi s t',
'elec.luggs.co s443',
'electrum.vom-stausee.de s t',
- 'electrum3.hachre.de s t',
'electrum.hsmiths.com s t',
'helicarrier.bauerj.eu s t',
'hsmiths4fyqlw5xw.onion s t',
@@ -538,18 +548,15 @@ class BitcoinTestnetMixin(object):
PEER_DEFAULT_PORTS = {'t': '51001', 's': '51002'}
-class BitcoinCashTestnet(BitcoinTestnetMixin, Coin):
- '''Bitcoin Testnet for Bitcoin Cash daemons.'''
- NAME = "BitcoinCash"
+class BitcoinSVTestnet(BitcoinTestnetMixin, Coin):
+ '''Bitcoin Testnet for Bitcoin SV daemons.'''
+ NAME = "BitcoinSV"
PEERS = [
- 'electrum-testnet-abc.criptolayer.net s50112',
- 'bchtestnet.arihanc.com t53001 s53002',
- 'ciiattqkgzebpp6jofjbrkhvhwmgnsfoayljdcrve2p3qmkbv3duaoyd.onion '
- 't53001 s53002',
+ 'electrontest.cascharia.com t51001 s51002',
]
-class BitcoinCashRegtest(BitcoinCashTestnet):
+class BitcoinSVRegtest(BitcoinSVTestnet):
NET = "regtest"
GENESIS_HASH = ('0f9188f13cb7b2c71f2a335e3a4fc328'
'bf5beb436012afca590b1a11466e2206')
@@ -647,6 +654,26 @@ class LitecoinTestnet(Litecoin):
]
+class LitecoinRegtest(LitecoinTestnet):
+ NET = "regtest"
+ GENESIS_HASH = ('530827f38f93b43ed12af0b3ad25a288'
+ 'dc02ed74d6d7857862df51fc56c416f9')
+ PEERS = []
+ TX_COUNT = 1
+ TX_COUNT_HEIGHT = 1
+
+
+class BitcoinCashRegtest(BitcoinTestnetMixin, Coin):
+ NAME = "BitcoinCashABC" # Some releases later remove the ABC suffix
+ NET = "regtest"
+ PEERS = []
+ GENESIS_HASH = ('0f9188f13cb7b2c71f2a335e3a4fc328'
+ 'bf5beb436012afca590b1a11466e2206')
+ TX_COUNT = 1
+ TX_COUNT_HEIGHT = 1
+ BLOCK_PROCESSOR = block_proc.LTORBlockProcessor
+
+
class Viacoin(AuxPowMixin, Coin):
NAME = "Viacoin"
SHORTNAME = "VIA"
@@ -709,6 +736,109 @@ class Namecoin(AuxPowMixin, Coin):
PEERS = [
'elec.luggs.co s446',
]
+ BLOCK_PROCESSOR = block_proc.NamecoinBlockProcessor
+
+ @classmethod
+ def split_name_script(cls, script):
+ from electrumx.lib.script import _match_ops, Script, ScriptError
+
+ try:
+ ops = Script.get_ops(script)
+ except ScriptError:
+ return None, script
+
+ match = _match_ops
+
+ # Name opcodes
+ OP_NAME_NEW = OpCodes.OP_1
+ OP_NAME_FIRSTUPDATE = OpCodes.OP_2
+ OP_NAME_UPDATE = OpCodes.OP_3
+
+ # Opcode sequences for name operations
+ NAME_NEW_OPS = [OP_NAME_NEW, -1, OpCodes.OP_2DROP]
+ NAME_FIRSTUPDATE_OPS = [OP_NAME_FIRSTUPDATE, -1, -1, -1,
+ OpCodes.OP_2DROP, OpCodes.OP_2DROP]
+ NAME_UPDATE_OPS = [OP_NAME_UPDATE, -1, -1, OpCodes.OP_2DROP,
+ OpCodes.OP_DROP]
+
+ name_script_op_count = None
+ name_pushdata = None
+
+ # Detect name operations; determine count of opcodes.
+ # Also extract the name field -- we might use that for something in a
+ # future version.
+ if match(ops[:len(NAME_NEW_OPS)], NAME_NEW_OPS):
+ name_script_op_count = len(NAME_NEW_OPS)
+ elif match(ops[:len(NAME_FIRSTUPDATE_OPS)], NAME_FIRSTUPDATE_OPS):
+ name_script_op_count = len(NAME_FIRSTUPDATE_OPS)
+ name_pushdata = ops[1]
+ elif match(ops[:len(NAME_UPDATE_OPS)], NAME_UPDATE_OPS):
+ name_script_op_count = len(NAME_UPDATE_OPS)
+ name_pushdata = ops[1]
+
+ if name_script_op_count is None:
+ return None, script
+
+ # Find the end position of the name data
+ n = 0
+ for i in range(name_script_op_count):
+ # Content of this loop is copied from Script.get_ops's loop
+ op = script[n]
+ n += 1
+
+ if op <= OpCodes.OP_PUSHDATA4:
+ # Raw bytes follow
+ if op < OpCodes.OP_PUSHDATA1:
+ dlen = op
+ elif op == OpCodes.OP_PUSHDATA1:
+ dlen = script[n]
+ n += 1
+ elif op == OpCodes.OP_PUSHDATA2:
+ dlen, = struct.unpack(' len(script):
+ raise IndexError
+ op = (op, script[n:n + dlen])
+ n += dlen
+ # Strip the name data to yield the address script
+ address_script = script[n:]
+
+ if name_pushdata is None:
+ return None, address_script
+
+ normalized_name_op_script = bytearray()
+ normalized_name_op_script.append(OP_NAME_UPDATE)
+ normalized_name_op_script.extend(Script.push_data(name_pushdata[1]))
+ normalized_name_op_script.extend(Script.push_data(bytes([])))
+ normalized_name_op_script.append(OpCodes.OP_2DROP)
+ normalized_name_op_script.append(OpCodes.OP_DROP)
+ normalized_name_op_script.append(OpCodes.OP_RETURN)
+
+ return bytes(normalized_name_op_script), address_script
+
+ @classmethod
+ def hashX_from_script(cls, script):
+ name_op_script, address_script = cls.split_name_script(script)
+
+ return super().hashX_from_script(address_script)
+
+ @classmethod
+ def address_from_script(cls, script):
+ name_op_script, address_script = cls.split_name_script(script)
+
+ return super().address_from_script(address_script)
+
+ @classmethod
+ def name_hashX_from_script(cls, script):
+ name_op_script, address_script = cls.split_name_script(script)
+
+ if name_op_script is None:
+ return None
+
+ return super().hashX_from_script(name_op_script)
class NamecoinTestnet(Namecoin):
@@ -802,6 +932,7 @@ class Dash(Coin):
]
SESSIONCLS = DashElectrumX
DAEMON = daemon.DashDaemon
+ DESERIALIZER = lib_tx_dash.DeserializerDash
@classmethod
def header_hash(cls, header):
@@ -1010,6 +1141,23 @@ class Hush(EquihashMixin, Coin):
REORG_LIMIT = 800
+class ZelCash(EquihashMixin, Coin):
+ NAME = "ZelCash"
+ SHORTNAME = "ZEL"
+ NET = "mainnet"
+ P2PKH_VERBYTE = bytes.fromhex("1CB8")
+ P2SH_VERBYTES = [bytes.fromhex("1CBD")]
+ WIF_BYTE = bytes.fromhex("80")
+ GENESIS_HASH = ('00052461a5006c2e3b74ce48992a0869'
+ '5607912d5604c3eb8da25749b0900444')
+ DESERIALIZER = lib_tx.DeserializerZcash
+ TX_COUNT = 450539
+ TX_COUNT_HEIGHT = 167114
+ TX_PER_BLOCK = 3
+ RPC_PORT = 16124
+ REORG_LIMIT = 800
+
+
class Zclassic(EquihashMixin, Coin):
NAME = "Zclassic"
SHORTNAME = "ZCL"
@@ -1158,6 +1306,50 @@ class Peercoin(Coin):
REORG_LIMIT = 5000
+class Trezarcoin(Coin):
+ NAME = "Trezarcoin"
+ SHORTNAME = "TZC"
+ NET = "mainnet"
+ VALUE_PER_COIN = 1000000
+ XPUB_VERBYTES = bytes.fromhex("0488B21E")
+ XPRV_VERBYTES = bytes.fromhex("0488ADE4")
+ P2PKH_VERBYTE = bytes.fromhex("42")
+ P2SH_VERBYTES = [bytes.fromhex("08")]
+ WIF_BYTE = bytes.fromhex("c2")
+ GENESIS_HASH = ('24502ba55d673d2ee9170d83dae2d1ad'
+ 'b3bfb4718e4f200db9951382cc4f6ee6')
+ DESERIALIZER = lib_tx.DeserializerTrezarcoin
+ HEADER_HASH = lib_tx.DeserializerTrezarcoin.blake2s
+ HEADER_HASH_GEN = lib_tx.DeserializerTrezarcoin.blake2s_gen
+ BASIC_HEADER_SIZE = 80
+ TX_COUNT = 742886
+ TX_COUNT_HEIGHT = 643128
+ TX_PER_BLOCK = 2
+ RPC_PORT = 17299
+ REORG_LIMIT = 2000
+ PEERS = [
+ 'electrumx1.trezarcoin.com s t',
+ ]
+
+ @classmethod
+ def genesis_block(cls, block):
+ '''Check the Genesis block is the right one for this coin.
+
+ Return the block less its unspendable coinbase.
+ '''
+ header = cls.block_header(block, 0)
+ header_hex_hash = cls.HEADER_HASH_GEN(header)
+ if header_hex_hash != cls.GENESIS_HASH:
+ raise CoinError('genesis block has hash {} expected {}'
+ .format(header_hex_hash, cls.GENESIS_HASH))
+ return header + bytes(1)
+
+ @classmethod
+ def header_hash(cls, header):
+ '''Given a header return the hash.'''
+ return cls.HEADER_HASH(header)
+
+
class Reddcoin(Coin):
NAME = "Reddcoin"
SHORTNAME = "RDD"
@@ -1524,7 +1716,6 @@ class Newyorkcoin(AuxPowMixin, Coin):
WIF_BYTE = bytes.fromhex("bc")
GENESIS_HASH = ('5597f25c062a3038c7fd815fe46c67de'
'dfcb3c839fbc8e01ed4044540d08fe48')
- DAEMON = daemon.LegacyRPCDaemon
TX_COUNT = 5161944
TX_COUNT_HEIGHT = 3948743
TX_PER_BLOCK = 2
@@ -1539,7 +1730,6 @@ class NewyorkcoinTestnet(Newyorkcoin):
WIF_BYTE = bytes.fromhex("f1")
GENESIS_HASH = ('24463e4d3c625b0a9059f309044c2cf0'
'd7e196cf2a6ecce901f24f681be33c8f')
- DAEMON = daemon.LegacyRPCDaemon
TX_COUNT = 5161944
TX_COUNT_HEIGHT = 3948743
TX_PER_BLOCK = 2
@@ -1722,7 +1912,9 @@ class Axe(Dash):
WIF_BYTE = bytes.fromhex("cc")
GENESIS_HASH = ('00000c33631ca6f2f61368991ce2dc03'
'306b5bb50bf7cede5cfbba6db38e52e6')
+ SESSIONCLS = DashElectrumX
DAEMON = daemon.DashDaemon
+ DESERIALIZER = lib_tx_dash.DeserializerDash
TX_COUNT = 18405
TX_COUNT_HEIGHT = 30237
TX_PER_BLOCK = 1
@@ -1965,6 +2157,7 @@ class Minexcoin(EquihashMixin, Coin):
RPC_PORT = 8022
CHUNK_SIZE = 960
PEERS = [
+ 'electrumx.xpresit.net s t',
'elex01-ams.turinex.eu s t',
'eu.minexpool.nl s t'
]
@@ -2174,3 +2367,189 @@ class FloTestnet(Flo):
PEER_DEFAULT_PORTS = {'t': '51001', 's': '51002'}
PEERS = [
]
+
+class CivX(Coin):
+ NAME = "CivX"
+ SHORTNAME = "CIVX"
+ NET = "mainnet"
+ XPUB_VERBYTES = bytes.fromhex("0488b21e")
+ XPRV_VERBYTES = bytes.fromhex("0488ade4")
+ GENESIS_HASH = ('00000036090a68c523471da7a4f0f958'
+ 'c1b4403fef74a003be7f71877699cab7')
+ P2PKH_VERBYTE = bytes.fromhex("1C")
+ P2SH_VERBYTE = [bytes.fromhex("57")]
+ WIF_BYTE = bytes.fromhex("9C")
+ RPC_PORT = 4561
+ TX_COUNT = 1000
+ TX_COUNT_HEIGHT = 10000
+ TX_PER_BLOCK = 4
+ DAEMON = daemon.PreLegacyRPCDaemon
+ DESERIALIZER = lib_tx.DeserializerTxTime
+
+ @classmethod
+ def header_hash(cls, header):
+ version, = util.unpack_le_uint32_from(header)
+
+ if version > 2:
+ return double_sha256(header)
+ else:
+ return hex_str_to_hash(CivX.GENESIS_HASH)
+
+
+class CivXTestnet(CivX):
+ SHORTNAME = "tCIVX"
+ NET = "testnet"
+ XPUB_VERBYTES = bytes.fromhex("043587cf")
+ XPRV_VERBYTES = bytes.fromhex("04358394")
+ GENESIS_HASH = ('0000059bb2c2048493efcb0f1a034972'
+ 'b3ce4089d54c93b69aaab212fb369887')
+ P2PKH_VERBYTE = bytes.fromhex("4B")
+ P2SH_VERBYTE = [bytes.fromhex("CE")]
+ WIF_BYTE = bytes.fromhex("CB")
+ RPC_PORT = 14561
+
+ @classmethod
+ def header_hash(cls, header):
+ version, = util.unpack_le_uint32_from(header)
+
+ if version > 2:
+ return double_sha256(header)
+ else:
+ return hex_str_to_hash(CivXTestnet.GENESIS_HASH)
+
+
+class SmartCash(Coin):
+ NAME = "SmartCash"
+ SHORTNAME = "SMART"
+ NET = "mainnet"
+ P2PKH_VERBYTE = bytes.fromhex("3f")
+ P2SH_VERBYTES = [bytes.fromhex("12")]
+ WIF_BYTE = bytes.fromhex("bf")
+ GENESIS_HASH = ('000007acc6970b812948d14ea5a0a13d'
+ 'b0fdd07d5047c7e69101fa8b361e05a4')
+ DESERIALIZER = lib_tx.DeserializerSmartCash
+ RPC_PORT = 9679
+ REORG_LIMIT = 5000
+ TX_COUNT = 1115016
+ TX_COUNT_HEIGHT = 541656
+ TX_PER_BLOCK = 1
+ ENCODE_CHECK = partial(Base58.encode_check,
+ hash_fn=lib_tx.DeserializerSmartCash.keccak)
+ DECODE_CHECK = partial(Base58.decode_check,
+ hash_fn=lib_tx.DeserializerSmartCash.keccak)
+ HEADER_HASH = lib_tx.DeserializerSmartCash.keccak
+ DAEMON = daemon.SmartCashDaemon
+ SESSIONCLS = SmartCashElectrumX
+
+ @classmethod
+ def header_hash(cls, header):
+ '''Given a header return the hash.'''
+ return cls.HEADER_HASH(header)
+
+
+class NIX(Coin):
+ NAME = "NIX"
+ SHORTNAME = "NIX"
+ NET = "mainnet"
+ XPUB_VERBYTES = bytes.fromhex("0488b21e")
+ XPRV_VERBYTES = bytes.fromhex("0488ade4")
+ P2PKH_VERBYTE = bytes.fromhex("26")
+ P2SH_VERBYTES = [bytes.fromhex("35")]
+ WIF_BYTE = bytes.fromhex("80")
+ GENESIS_HASH = ('dd28ad86def767c3cfc34267a950d871'
+ 'fc7462bc57ea4a929fc3596d9b598e41')
+ DESERIALIZER = lib_tx.DeserializerSegWit
+ TX_COUNT = 114240
+ TX_COUNT_HEIGHT = 87846
+ TX_PER_BLOCK = 3
+ RPC_PORT = 6215
+ REORG_LIMIT = 1000
+
+
+class NIXTestnet(NIX):
+ SHORTNAME = "tNIX"
+ NET = "testnet"
+ XPUB_VERBYTES = bytes.fromhex("0488b21e")
+ XPRV_VERBYTES = bytes.fromhex("0488ade4")
+ GENESIS_HASH = ('dd28ad86def767c3cfc34267a950d871'
+ 'fc7462bc57ea4a929fc3596d9b598e41')
+ P2PKH_VERBYTE = bytes.fromhex("01")
+ P2SH_VERBYTE = [bytes.fromhex("03")]
+ WIF_BYTE = bytes.fromhex("80")
+ RPC_PORT = 16215
+ DESERIALIZER = lib_tx.DeserializerSegWit
+
+
+class Noir(Coin):
+ NAME = "Noir"
+ SHORTNAME = "NOR"
+ NET = "mainnet"
+ XPUB_VERBYTES = bytes.fromhex("0488b21e")
+ XPRV_VERBYTES = bytes.fromhex("0488ade4")
+ P2PKH_VERBYTE = bytes.fromhex("80")
+ P2SH_VERBYTES = [bytes.fromhex("07")]
+ WIF_BYTE = bytes.fromhex("D0")
+ GENESIS_HASH = ('23911212a525e3d149fcad6c559c8b17'
+ 'f1e8326a272a75ff9bb315c8d96433ef')
+ RPC_PORT = 8825
+ TX_COUNT = 586369
+ TX_COUNT_HEIGHT = 379290
+ TX_PER_BLOCK = 5
+
+
+class BitcoinPlus(Coin):
+ NAME = "BitcoinPlus"
+ SHORTNAME = "XBC"
+ 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 = ('0000005f6a28e686f641c616e56182d1'
+ 'b43afbe08a223f23bda23cdf9d55b882')
+ DESERIALIZER = lib_tx.DeserializerTxTime
+ DAEMON = daemon.LegacyRPCDaemon
+ TX_COUNT = 1479247
+ TX_COUNT_HEIGHT = 749740
+ TX_PER_BLOCK = 2
+ RPC_PORT = 8885
+ REORG_LIMIT = 2000
+
+ @classmethod
+ def header_hash(cls, header):
+ '''Given a header return the hash.'''
+ import x13_hash
+ return x13_hash.getPoWHash(header)
+
+
+class Myriadcoin(AuxPowMixin, Coin):
+ NAME = "Myriadcoin"
+ SHORTNAME = "XMY"
+ NET = "mainnet"
+ XPUB_VERBYTES = bytes.fromhex("0488b21e")
+ XPRV_VERBYTES = bytes.fromhex("0488ade4")
+ P2PKH_VERBYTE = bytes.fromhex("32")
+ P2SH_VERBYTES = [bytes.fromhex("09")]
+ WIF_BYTE = bytes.fromhex("b2")
+ GENESIS_HASH = ('00000ffde4c020b5938441a0ea3d314b'
+ 'f619eff0b38f32f78f7583cffa1ea485')
+ DESERIALIZER = lib_tx.DeserializerAuxPowSegWit
+ TX_COUNT = 1976629
+ TX_COUNT_HEIGHT = 2580356
+ TX_PER_BLOCK = 20
+ REORG_LIMIT = 2000
+ RPC_PORT = 10889
+
+
+class MyriadcoinTestnet(Myriadcoin):
+ NAME = "Myriadcoin"
+ SHORTNAME = "XMT"
+ NET = "testnet"
+ XPUB_VERBYTES = bytes.fromhex("043587cf")
+ XPRV_VERBYTES = bytes.fromhex("04358394")
+ P2PKH_VERBYTE = bytes.fromhex("58")
+ P2SH_VERBYTES = [bytes.fromhex("bc")]
+ WIF_BYTE = bytes.fromhex("ef")
+ GENESIS_HASH = ('0000017ce2a79c8bddafbbe47c004aa9'
+ '2b20678c354b34085f62b762084b9788')
diff --git a/electrumx/lib/peer.py b/electrumx/lib/peer.py
index e35481e..4427e30 100644
--- a/electrumx/lib/peer.py
+++ b/electrumx/lib/peer.py
@@ -25,7 +25,8 @@
'''Representation of a peer server.'''
-from ipaddress import ip_address
+from ipaddress import ip_address, IPv4Address, IPv6Address
+from socket import AF_INET, AF_INET6
from electrumx.lib.util import cachedproperty
import electrumx.lib.util as util
@@ -112,15 +113,24 @@ class Peer(object):
for feature in self.FEATURES:
setattr(self, feature, getattr(peer, feature))
- def connection_port_pairs(self):
- '''Return a list of (kind, port) pairs to try when making a
- connection.'''
+ def connection_tuples(self):
+ '''Return a list of (kind, port, family) tuples to try when making a
+ connection.
+ '''
# Use a list not a set - it's important to try the registered
# ports first.
pairs = [('SSL', self.ssl_port), ('TCP', self.tcp_port)]
while self.other_port_pairs:
pairs.append(self.other_port_pairs.pop())
- return [pair for pair in pairs if pair[1]]
+ if isinstance(self.ip_address, IPv4Address):
+ families = [AF_INET]
+ elif isinstance(self.ip_address, IPv6Address):
+ families = [AF_INET6]
+ else:
+ families = [AF_INET, AF_INET6]
+ return [(kind, port, family)
+ for kind, port in pairs if port
+ for family in families]
def mark_bad(self):
'''Mark as bad to avoid reconnects but also to remember for a
diff --git a/electrumx/lib/tx.py b/electrumx/lib/tx.py
index bb516fd..b34e17a 100644
--- a/electrumx/lib/tx.py
+++ b/electrumx/lib/tx.py
@@ -28,6 +28,7 @@
'''Transaction-related classes and functions.'''
from collections import namedtuple
+from hashlib import blake2s
from electrumx.lib.hash import sha256, double_sha256, hash_to_hex_str
from electrumx.lib.script import OpCodes
@@ -320,26 +321,47 @@ class TxJoinSplit(namedtuple("Tx", "version inputs outputs locktime")):
class DeserializerZcash(DeserializerEquihash):
def read_tx(self):
header = self._read_le_uint32()
- overwinterd = ((header >> 31) == 1)
- if overwinterd:
+ overwintered = ((header >> 31) == 1)
+ if overwintered:
version = header & 0x7fffffff
- self._read_le_uint32() # versionGroupId
+ self.cursor += 4 # versionGroupId
else:
version = header
+
+ is_overwinter_v3 = version == 3
+ is_sapling_v4 = version == 4
+
base_tx = TxJoinSplit(
version,
self._read_inputs(), # inputs
self._read_outputs(), # outputs
self._read_le_uint32() # locktime
)
- if base_tx.version >= 3:
- self._read_le_uint32() # expiryHeight
+
+ if is_overwinter_v3 or is_sapling_v4:
+ self.cursor += 4 # expiryHeight
+
+ has_shielded = False
+ if is_sapling_v4:
+ self.cursor += 8 # valueBalance
+ shielded_spend_size = self._read_varint()
+ self.cursor += shielded_spend_size * 384 # vShieldedSpend
+ shielded_output_size = self._read_varint()
+ self.cursor += shielded_output_size * 948 # vShieldedOutput
+ has_shielded = shielded_spend_size > 0 or shielded_output_size > 0
+
if base_tx.version >= 2:
joinsplit_size = self._read_varint()
if joinsplit_size > 0:
- self.cursor += joinsplit_size * 1802 # JSDescription
+ joinsplit_desc_len = 1506 + (192 if is_sapling_v4 else 296)
+ # JSDescription
+ self.cursor += joinsplit_size * joinsplit_desc_len
self.cursor += 32 # joinSplitPubKey
self.cursor += 64 # joinSplitSig
+
+ if is_sapling_v4 and has_shielded:
+ self.cursor += 64 # bindingSig
+
return base_tx
@@ -358,6 +380,62 @@ class DeserializerTxTime(Deserializer):
)
+class TxTrezarcoin(
+ namedtuple("Tx", "version time inputs outputs locktime txcomment")):
+ '''Class representing transaction that has a time and txcomment field.'''
+
+
+class DeserializerTrezarcoin(Deserializer):
+
+ def read_tx(self):
+ version = self._read_le_int32()
+ time = self._read_le_uint32()
+ inputs = self._read_inputs()
+ outputs = self._read_outputs()
+ locktime = self._read_le_uint32()
+ if version >= 2:
+ txcomment = self._read_varbytes()
+ else:
+ txcomment = b''
+ return TxTrezarcoin(version, time, inputs, outputs, locktime,
+ txcomment)
+
+ @staticmethod
+ def blake2s_gen(data):
+ version = data[0:1]
+ keyOne = data[36:46]
+ keyTwo = data[58:68]
+ ntime = data[68:72]
+ _nBits = data[72:76]
+ _nonce = data[76:80]
+ _full_merkle = data[36:68]
+ _input112 = data + _full_merkle
+ _key = keyTwo + ntime + _nBits + _nonce + keyOne
+ '''Prepare 112Byte Header '''
+ blake2s_hash = blake2s(key=_key, digest_size=32)
+ blake2s_hash.update(_input112)
+ '''TrezarFlips - Only for Genesis'''
+ return ''.join(map(str.__add__, blake2s_hash.hexdigest()[-2::-2],
+ blake2s_hash.hexdigest()[-1::-2]))
+
+ @staticmethod
+ def blake2s(data):
+ version = data[0:1]
+ keyOne = data[36:46]
+ keyTwo = data[58:68]
+ ntime = data[68:72]
+ _nBits = data[72:76]
+ _nonce = data[76:80]
+ _full_merkle = data[36:68]
+ _input112 = data + _full_merkle
+ _key = keyTwo + ntime + _nBits + _nonce + keyOne
+ '''Prepare 112Byte Header '''
+ blake2s_hash = blake2s(key=_key, digest_size=32)
+ blake2s_hash.update(_input112)
+ '''TrezarFlips'''
+ return blake2s_hash.digest()
+
+
class DeserializerReddcoin(Deserializer):
def read_tx(self):
version = self._read_le_int32()
@@ -500,6 +578,10 @@ class TxInputDcr(namedtuple("TxInput", "prev_hash prev_idx tree sequence")):
return ("Input({}, {:d}, tree={}, sequence={:d})"
.format(prev_hash, self.prev_idx, self.tree, self.sequence))
+ def is_generation(self):
+ '''Test if an input is generation/coinbase like'''
+ return self.prev_idx == MINUS_1 and self.prev_hash == ZERO
+
class TxOutputDcr(namedtuple("TxOutput", "value version pk_script")):
'''Class representing a Decred transaction output.'''
@@ -599,6 +681,7 @@ class DeserializerDecred(Deserializer):
witness
), tx_hash, self.cursor - start
+
class TxFlo(namedtuple("Tx", "version inputs outputs locktime txcomment")):
'''Class representing a transaction.'''
@@ -671,3 +754,18 @@ class DeserializerFlo(DeserializerSegWit):
return TxFloSegWit(version, marker, flag, inputs, outputs, witness,
locktime, tx_comment), double_sha256(orig_ser), vsize
+
+
+class DeserializerSmartCash(Deserializer):
+
+ @staticmethod
+ def keccak(data):
+ from Cryptodome.Hash import keccak
+ keccak_hash = keccak.new(digest_bits=256)
+ keccak_hash.update(data)
+ return keccak_hash.digest()
+
+ def read_tx_and_hash(self):
+ from electrumx.lib.hash import sha256
+ start = self.cursor
+ return self.read_tx(), sha256(self.binary[start:self.cursor])
diff --git a/electrumx/lib/tx_dash.py b/electrumx/lib/tx_dash.py
new file mode 100644
index 0000000..31dee65
--- /dev/null
+++ b/electrumx/lib/tx_dash.py
@@ -0,0 +1,405 @@
+# Copyright (c) 2016-2018, Neil Booth
+# Copyright (c) 2018, the ElectrumX authors
+#
+# All rights reserved.
+#
+# The MIT License (MIT)
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+'''Deserializer for Dash DIP2 special transaction types'''
+
+from collections import namedtuple
+
+from electrumx.lib.tx import Deserializer
+from electrumx.lib.util import (pack_le_uint16, pack_le_int32, pack_le_uint32,
+ pack_le_int64, pack_varint, pack_varbytes)
+
+
+# https://github.com/dashpay/dips/blob/master/dip-0002.md
+class DashTx(namedtuple("DashTx",
+ "version inputs outputs locktime "
+ "tx_type extra_payload")):
+ '''Class representing a Dash transaction'''
+ def serialize(self):
+ nLocktime = pack_le_uint32(self.locktime)
+ txins = (pack_varint(len(self.inputs)) +
+ b''.join(tx_in.serialize() for tx_in in self.inputs))
+ txouts = (pack_varint(len(self.outputs)) +
+ b''.join(tx_out.serialize() for tx_out in self.outputs))
+
+ if self.tx_type:
+ uVersion = pack_le_uint16(self.version)
+ uTxType = pack_le_uint16(self.tx_type)
+ vExtra = self._serialize_extra_payload()
+ return uVersion + uTxType + txins + txouts + nLocktime + vExtra
+ else:
+ nVersion = pack_le_int32(self.version)
+ return nVersion + txins + txouts + nLocktime
+
+ def _serialize_extra_payload(self):
+ extra = self.extra_payload
+ spec_tx_class = DeserializerDash.SPEC_TX_HANDLERS.get(self.tx_type)
+ if not spec_tx_class:
+ assert isinstance(extra, (bytes, bytearray))
+ return pack_varbytes(extra)
+
+ if not isinstance(extra, spec_tx_class):
+ raise ValueError('Dash tx_type does not conform with extra'
+ ' payload class: %s, %s' % (self.tx_type, extra))
+ return pack_varbytes(extra.serialize())
+
+
+# https://github.com/dashpay/dips/blob/master/dip-0002-special-transactions.md
+class DashProRegTx(namedtuple("DashProRegTx",
+ "version type mode collateralOutpoint "
+ "ipAddress port KeyIdOwner PubKeyOperator "
+ "KeyIdVoting operatorReward scriptPayout "
+ "inputsHash payloadSig")):
+ '''Class representing DIP3 ProRegTx'''
+ def serialize(self):
+ assert (len(self.ipAddress) == 16
+ and len(self.KeyIdOwner) == 20
+ and len(self.PubKeyOperator) == 48
+ and len(self.KeyIdVoting) == 20
+ and len(self.inputsHash) == 32)
+ return (
+ pack_le_uint16(self.version) + # version
+ pack_le_uint16(self.type) + # type
+ pack_le_uint16(self.mode) + # mode
+ self.collateralOutpoint.serialize() + # collateralOutpoint
+ self.ipAddress + # ipAddress
+ pack_le_uint16(self.port) + # port
+ self.KeyIdOwner + # KeyIdOwner
+ self.PubKeyOperator + # PubKeyOperator
+ self.KeyIdVoting + # KeyIdVoting
+ pack_le_uint16(self.operatorReward) + # operatorReward
+ pack_varbytes(self.scriptPayout) + # scriptPayout
+ self.inputsHash + # inputsHash
+ pack_varbytes(self.payloadSig) # payloadSig
+ )
+
+ @classmethod
+ def read_tx_extra(cls, deser):
+ return DashProRegTx(
+ deser._read_le_uint16(), # version
+ deser._read_le_uint16(), # type
+ deser._read_le_uint16(), # mode
+ deser._read_outpoint(), # collateralOutpoint
+ deser._read_nbytes(16), # ipAddress
+ deser._read_le_uint16(), # port
+ deser._read_nbytes(20), # KeyIdOwner
+ deser._read_nbytes(48), # PubKeyOperator
+ deser._read_nbytes(20), # KeyIdVoting
+ deser._read_le_uint16(), # operatorReward
+ deser._read_varbytes(), # scriptPayout
+ deser._read_nbytes(32), # inputsHash
+ deser._read_varbytes() # payloadSig
+ )
+
+
+class DashProUpServTx(namedtuple("DashProUpServTx",
+ "version proTxHash ipAddress port "
+ "scriptOperatorPayout inputsHash "
+ "payloadSig")):
+ '''Class representing DIP3 ProUpServTx'''
+ def serialize(self):
+ assert (len(self.proTxHash) == 32
+ and len(self.ipAddress) == 16
+ and len(self.inputsHash) == 32
+ and len(self.payloadSig) == 96)
+ return (
+ pack_le_uint16(self.version) + # version
+ self.proTxHash + # proTxHash
+ self.ipAddress + # ipAddress
+ pack_le_uint16(self.port) + # port
+ pack_varbytes(self.scriptOperatorPayout) + # scriptOperatorPayout
+ self.inputsHash + # inputsHash
+ self.payloadSig # payloadSig
+ )
+
+ @classmethod
+ def read_tx_extra(cls, deser):
+ return DashProUpServTx(
+ deser._read_le_uint16(), # version
+ deser._read_nbytes(32), # proTxHash
+ deser._read_nbytes(16), # ipAddress
+ deser._read_le_uint16(), # port
+ deser._read_varbytes(), # scriptOperatorPayout
+ deser._read_nbytes(32), # inputsHash
+ deser._read_nbytes(96) # payloadSig
+ )
+
+
+class DashProUpRegTx(namedtuple("DashProUpRegTx",
+ "version proTxHash mode PubKeyOperator "
+ "KeyIdVoting scriptPayout inputsHash "
+ "payloadSig")):
+ '''Class representing DIP3 ProUpRegTx'''
+ def serialize(self):
+ assert (len(self.proTxHash) == 32
+ and len(self.PubKeyOperator) == 48
+ and len(self.KeyIdVoting) == 20
+ and len(self.inputsHash) == 32)
+ return (
+ pack_le_uint16(self.version) + # version
+ self.proTxHash + # proTxHash
+ pack_le_uint16(self.mode) + # mode
+ self.PubKeyOperator + # PubKeyOperator
+ self.KeyIdVoting + # KeyIdVoting
+ pack_varbytes(self.scriptPayout) + # scriptPayout
+ self.inputsHash + # inputsHash
+ pack_varbytes(self.payloadSig) # payloadSig
+ )
+
+ @classmethod
+ def read_tx_extra(cls, deser):
+ return DashProUpRegTx(
+ deser._read_le_uint16(), # version
+ deser._read_nbytes(32), # proTxHash
+ deser._read_le_uint16(), # mode
+ deser._read_nbytes(48), # PubKeyOperator
+ deser._read_nbytes(20), # KeyIdVoting
+ deser._read_varbytes(), # scriptPayout
+ deser._read_nbytes(32), # inputsHash
+ deser._read_varbytes() # payloadSig
+ )
+
+
+class DashProUpRevTx(namedtuple("DashProUpRevTx",
+ "version proTxHash reason "
+ "inputsHash payloadSig")):
+ '''Class representing DIP3 ProUpRevTx'''
+ def serialize(self):
+ assert (len(self.proTxHash) == 32
+ and len(self.inputsHash) == 32
+ and len(self.payloadSig) == 96)
+ return (
+ pack_le_uint16(self.version) + # version
+ self.proTxHash + # proTxHash
+ pack_le_uint16(self.reason) + # reason
+ self.inputsHash + # inputsHash
+ self.payloadSig # payloadSig
+ )
+
+ @classmethod
+ def read_tx_extra(cls, deser):
+ return DashProUpRevTx(
+ deser._read_le_uint16(), # version
+ deser._read_nbytes(32), # proTxHash
+ deser._read_le_uint16(), # reason
+ deser._read_nbytes(32), # inputsHash
+ deser._read_nbytes(96) # payloadSig
+ )
+
+
+class DashCbTx(namedtuple("DashCbTx", "version height merkleRootMNList")):
+ '''Class representing DIP4 coinbase special tx'''
+ def serialize(self):
+ assert len(self.merkleRootMNList) == 32
+ return (
+ pack_le_uint16(self.version) + # version
+ pack_le_uint32(self.height) + # height
+ self.merkleRootMNList # merkleRootMNList
+ )
+
+ @classmethod
+ def read_tx_extra(cls, deser):
+ return DashCbTx(
+ deser._read_le_uint16(), # version
+ deser._read_le_uint32(), # height
+ deser._read_nbytes(32) # merkleRootMNList
+ )
+
+
+class DashSubTxRegister(namedtuple("DashSubTxRegister",
+ "version userName pubKey payloadSig")):
+ '''Class representing DIP5 SubTxRegister'''
+ def serialize(self):
+ assert (len(self.pubKey) == 48
+ and len(self.payloadSig) == 96)
+ return (
+ pack_le_uint16(self.version) + # version
+ pack_varbytes(self.userName) + # userName
+ self.pubKey + # pubKey
+ self.payloadSig # payloadSig
+ )
+
+ @classmethod
+ def read_tx_extra(cls, deser):
+ return DashSubTxRegister(
+ deser._read_le_uint16(), # version
+ deser._read_varbytes(), # userName
+ deser._read_nbytes(48), # pubKey
+ deser._read_nbytes(96) # payloadSig
+ )
+
+
+class DashSubTxTopup(namedtuple("DashSubTxTopup",
+ "version regTxHash")):
+ '''Class representing DIP5 SubTxTopup'''
+ def serialize(self):
+ assert len(self.regTxHash) == 32
+ return (
+ pack_le_uint16(self.version) + # version
+ self.regTxHash # regTxHash
+ )
+
+ @classmethod
+ def read_tx_extra(cls, deser):
+ return DashSubTxTopup(
+ deser._read_le_uint16(), # version
+ deser._read_nbytes(32) # regTxHash
+ )
+
+
+class DashSubTxResetKey(namedtuple("DashSubTxResetKey",
+ "version regTxHash hashPrevSubTx "
+ "creditFee newPubKey payloadSig")):
+ '''Class representing DIP5 SubTxResetKey'''
+ def serialize(self):
+ assert (len(self.regTxHash) == 32
+ and len(self.hashPrevSubTx) == 32
+ and len(self.newPubKey) == 48
+ and len(self.payloadSig) == 96)
+ return (
+ pack_le_uint16(self.version) + # version
+ self.regTxHash + # regTxHash
+ self.hashPrevSubTx + # hashPrevSubTx
+ pack_le_int64(self.creditFee) + # creditFee
+ self.newPubKey + # newPubKey
+ self.payloadSig # payloadSig
+ )
+
+ @classmethod
+ def read_tx_extra(cls, deser):
+ return DashSubTxResetKey(
+ deser._read_le_uint16(), # version
+ deser._read_nbytes(32), # regTxHash
+ deser._read_nbytes(32), # hashPrevSubTx
+ deser._read_le_int64(), # creditFee
+ deser._read_nbytes(48), # newPubKey
+ deser._read_nbytes(96) # payloadSig
+ )
+
+
+class DashSubTxCloseAccount(namedtuple("DashSubTxCloseAccount",
+ "version regTxHash hashPrevSubTx "
+ "creditFee payloadSig")):
+ '''Class representing DIP5 SubTxCloseAccount'''
+ def serialize(self):
+ assert (len(self.regTxHash) == 32
+ and len(self.hashPrevSubTx) == 32
+ and len(self.payloadSig) == 96)
+ return (
+ pack_le_uint16(self.version) + # version
+ self.regTxHash + # regTxHash
+ self.hashPrevSubTx + # hashPrevSubTx
+ pack_le_int64(self.creditFee) + # creditFee
+ self.payloadSig # payloadSig
+ )
+
+ @classmethod
+ def read_tx_extra(cls, deser):
+ return DashSubTxCloseAccount(
+ deser._read_le_uint16(), # version
+ deser._read_nbytes(32), # regTxHash
+ deser._read_nbytes(32), # hashPrevSubTx
+ deser._read_le_int64(), # creditFee
+ deser._read_nbytes(96) # payloadSig
+ )
+
+
+# https://dash-docs.github.io/en/developer-reference#outpoint
+class TxOutPoint(namedtuple("TxOutPoint", "hash index")):
+ '''Class representing tx output outpoint'''
+ def serialize(self):
+ assert len(self.hash) == 32
+ return (
+ self.hash + # hash
+ pack_le_uint32(self.index) # index
+ )
+
+ @classmethod
+ def read_outpoint(cls, deser):
+ return TxOutPoint(
+ deser._read_nbytes(32), # hash
+ deser._read_le_uint32() # index
+ )
+
+
+class DeserializerDash(Deserializer):
+ '''Deserializer for Dash DIP2 special tx types'''
+ # Supported Spec Tx types and corresponding classes mapping
+ PRO_REG_TX = 1
+ PRO_UP_SERV_TX = 2
+ PRO_UP_REG_TX = 3
+ PRO_UP_REV_TX = 4
+ CB_TX = 5
+ SUB_TX_REGISTER = 8
+ SUB_TX_TOPUP = 9
+ SUB_TX_RESET_KEY = 10
+ SUB_TX_CLOSE_ACCOUNT = 11
+
+ SPEC_TX_HANDLERS = {
+ PRO_REG_TX: DashProRegTx,
+ PRO_UP_SERV_TX: DashProUpServTx,
+ PRO_UP_REG_TX: DashProUpRegTx,
+ PRO_UP_REV_TX: DashProUpRevTx,
+ CB_TX: DashCbTx,
+ SUB_TX_REGISTER: DashSubTxRegister,
+ SUB_TX_TOPUP: DashSubTxTopup,
+ SUB_TX_RESET_KEY: DashSubTxResetKey,
+ SUB_TX_CLOSE_ACCOUNT: DashSubTxCloseAccount,
+ }
+
+ def _read_outpoint(self):
+ return TxOutPoint.read_outpoint(self)
+
+ def read_tx(self):
+ header = self._read_le_uint32()
+ tx_type = header >> 16 # DIP2 tx type
+ if tx_type:
+ version = header & 0x0000ffff
+ else:
+ version = header
+
+ if tx_type and version < 3:
+ version = header
+ tx_type = 0
+
+ inputs = self._read_inputs()
+ outputs = self._read_outputs()
+ locktime = self._read_le_uint32()
+ if tx_type:
+ extra_payload_size = self._read_varint()
+ end = self.cursor + extra_payload_size
+ spec_tx_class = DeserializerDash.SPEC_TX_HANDLERS.get(tx_type)
+ if spec_tx_class:
+ read_method = getattr(spec_tx_class, 'read_tx_extra', None)
+ extra_payload = read_method(self)
+ assert isinstance(extra_payload, spec_tx_class)
+ else:
+ extra_payload = self._read_nbytes(extra_payload_size)
+ assert self.cursor == end
+ else:
+ extra_payload = b''
+ tx = DashTx(version, inputs, outputs, locktime, tx_type, extra_payload)
+ return tx
diff --git a/electrumx/server/block_processor.py b/electrumx/server/block_processor.py
index c37eec2..6061a15 100644
--- a/electrumx/server/block_processor.py
+++ b/electrumx/server/block_processor.py
@@ -44,6 +44,7 @@ class Prefetcher(object):
self.min_cache_size = 10 * 1024 * 1024
# This makes the first fetch be 10 blocks
self.ave_size = self.min_cache_size // 10
+ self.polling_delay = 5
async def main_loop(self, bp_height):
'''Loop forever polling for more blocks.'''
@@ -53,7 +54,7 @@ class Prefetcher(object):
# Sleep a while if there is nothing to prefetch
await self.refill_event.wait()
if not await self._prefetch_blocks():
- await asyncio.sleep(5)
+ await asyncio.sleep(self.polling_delay)
except DaemonError as e:
self.logger.info(f'ignoring daemon error: {e}')
@@ -98,11 +99,11 @@ class Prefetcher(object):
async with self.semaphore:
while self.cache_size < self.min_cache_size:
# Try and catch up all blocks but limit to room in cache.
- # Constrain fetch count to between 0 and 500 regardless;
- # testnet can be lumpy.
- cache_room = self.min_cache_size // self.ave_size
+ # Constrain fetch count to between 0 and 100 regardless;
+ # some chains can be lumpy.
+ cache_room = max(self.min_cache_size // self.ave_size, 1)
count = min(daemon_height - self.fetched_height, cache_room)
- count = min(500, max(count, 0))
+ count = min(100, max(count, 0))
if not count:
self.caught_up = True
return False
@@ -626,8 +627,6 @@ class BlockProcessor(object):
if first_sync:
self.logger.info(f'{electrumx.version} synced to '
f'height {self.height:,d}')
- # Initialise the notification framework
- await self.notifications.on_block(set(), self.height)
# Reopen for serving
await self.db.open_for_serving()
@@ -682,3 +681,129 @@ class DecredBlockProcessor(BlockProcessor):
start -= 1
count += 1
return start, count
+
+
+class NamecoinBlockProcessor(BlockProcessor):
+
+ def advance_txs(self, txs):
+ result = super().advance_txs(txs)
+
+ tx_num = self.tx_count - len(txs)
+ script_name_hashX = self.coin.name_hashX_from_script
+ update_touched = self.touched.update
+ hashXs_by_tx = []
+ append_hashXs = hashXs_by_tx.append
+
+ for tx, tx_hash in txs:
+ hashXs = []
+ append_hashX = hashXs.append
+
+ # Add the new UTXOs and associate them with the name script
+ for idx, txout in enumerate(tx.outputs):
+ # Get the hashX of the name script. Ignore non-name scripts.
+ hashX = script_name_hashX(txout.pk_script)
+ if hashX:
+ append_hashX(hashX)
+
+ append_hashXs(hashXs)
+ update_touched(hashXs)
+ tx_num += 1
+
+ self.db.history.add_unflushed(hashXs_by_tx, self.tx_count - len(txs))
+
+ return result
+
+
+class LTORBlockProcessor(BlockProcessor):
+
+ def advance_txs(self, txs):
+ self.tx_hashes.append(b''.join(tx_hash for tx, tx_hash in txs))
+
+ # Use local vars for speed in the loops
+ undo_info = []
+ tx_num = self.tx_count
+ script_hashX = self.coin.hashX_from_script
+ s_pack = pack
+ put_utxo = self.utxo_cache.__setitem__
+ spend_utxo = self.spend_utxo
+ undo_info_append = undo_info.append
+ update_touched = self.touched.update
+
+ hashXs_by_tx = [set() for _ in txs]
+
+ # Add the new UTXOs
+ for (tx, tx_hash), hashXs in zip(txs, hashXs_by_tx):
+ add_hashXs = hashXs.add
+ tx_numb = s_pack('= 1 required')
+ if not (0, 10, 1) <= aiorpcx_version < (0, 11):
+ raise RuntimeError('aiorpcX version 0.10.x, x >= 1, required')
env = self.env
min_str, max_str = env.coin.SESSIONCLS.protocol_min_max_strings()
@@ -99,31 +101,31 @@ class Controller(ServerBase):
db = DB(env)
bp = BlockProcessor(env, db, daemon, notifications)
- # Set ourselves up to implement the MemPoolAPI
- self.height = daemon.height
- self.cached_height = daemon.cached_height
- self.mempool_hashes = daemon.mempool_hashes
- self.raw_transactions = daemon.getrawtransactions
- self.lookup_utxos = db.lookup_utxos
- self.on_mempool = notifications.on_mempool
- MemPoolAPI.register(Controller)
- mempool = MemPool(env.coin, self)
+ # Set notifications up to implement the MemPoolAPI
+ notifications.height = daemon.height
+ notifications.cached_height = daemon.cached_height
+ notifications.mempool_hashes = daemon.mempool_hashes
+ notifications.raw_transactions = daemon.getrawtransactions
+ notifications.lookup_utxos = db.lookup_utxos
+ MemPoolAPI.register(Notifications)
+ mempool = MemPool(env.coin, notifications)
session_mgr = SessionManager(env, db, bp, daemon, mempool,
- notifications, shutdown_event)
+ shutdown_event)
# Test daemon authentication, and also ensure it has a cached
# height. Do this before entering the task group.
await daemon.height()
caught_up_event = Event()
- serve_externally_event = Event()
- synchronized_event = Event()
- async with TaskGroup() as group:
- await group.spawn(session_mgr.serve(serve_externally_event))
- await group.spawn(bp.fetch_and_process_blocks(caught_up_event))
+ mempool_event = Event()
+
+ async def wait_for_catchup():
await caught_up_event.wait()
await group.spawn(db.populate_header_merkle_cache())
- await group.spawn(mempool.keep_synchronized(synchronized_event))
- await synchronized_event.wait()
- serve_externally_event.set()
\ No newline at end of file
+ await group.spawn(mempool.keep_synchronized(mempool_event))
+
+ async with TaskGroup() as group:
+ await group.spawn(session_mgr.serve(notifications, mempool_event))
+ await group.spawn(bp.fetch_and_process_blocks(caught_up_event))
+ await group.spawn(wait_for_catchup())
diff --git a/electrumx/server/daemon.py b/electrumx/server/daemon.py
index 506459f..9a87099 100644
--- a/electrumx/server/daemon.py
+++ b/electrumx/server/daemon.py
@@ -116,7 +116,7 @@ class Daemon(object):
nonlocal last_error_log, retry
now = time.time()
if now - last_error_log > 60:
- last_error_time = now
+ last_error_log = now
self.logger.error(f'{error} Retrying occasionally...')
if retry == self.max_retry and self.failover():
retry = 0
@@ -445,3 +445,30 @@ class DecredDaemon(Daemon):
# FIXME allow self signed certificates
connector = aiohttp.TCPConnector(verify_ssl=False)
return aiohttp.ClientSession(connector=connector)
+
+
+class PreLegacyRPCDaemon(LegacyRPCDaemon):
+ '''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, and need the False parameter
+ for the getblock'''
+
+ async def deserialised_block(self, hex_hash):
+ '''Return the deserialised block with the given hex hash.'''
+ return await self._send_single('getblock', (hex_hash, False))
+
+
+class SmartCashDaemon(Daemon):
+
+ async def masternode_broadcast(self, params):
+ '''Broadcast a smartnode to the network.'''
+ return await self._send_single('smartnodebroadcast', params)
+
+ async def masternode_list(self, params):
+ '''Return the smartnode status.'''
+ return await self._send_single('smartnodelist', params)
+
+ async def smartrewards(self, params):
+ '''Return smartrewards data.'''
+ return await self._send_single('smartrewards', params)
diff --git a/electrumx/server/peers.py b/electrumx/server/peers.py
index 72b71e4..8e040b4 100644
--- a/electrumx/server/peers.py
+++ b/electrumx/server/peers.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2017, Neil Booth
+# Copyright (c) 2017-2018, Neil Booth
#
# All rights reserved.
#
@@ -14,7 +14,7 @@ import ssl
import time
from collections import defaultdict, Counter
-from aiorpcx import (ClientSession, SOCKSProxy,
+from aiorpcx import (Connector, RPCSession, SOCKSProxy,
Notification, handler_invocation,
SOCKSError, RPCError, TaskTimeout, TaskGroup, Event,
sleep, run_in_thread, ignore_after, timeout_after)
@@ -37,7 +37,7 @@ def assert_good(message, result, instance):
f'{type(result).__name__}')
-class PeerSession(ClientSession):
+class PeerSession(RPCSession):
'''An outgoing session to a peer.'''
async def handle_request(self, request):
@@ -197,14 +197,15 @@ class PeerManager(object):
pause = WAKEUP_SECS * 2 ** peer.try_count
async with ignore_after(pause):
await peer.retry_event.wait()
+ peer.retry_event.clear()
async def _should_drop_peer(self, peer):
peer.try_count += 1
is_good = False
- for kind, port in peer.connection_port_pairs():
+ for kind, port, family in peer.connection_tuples():
peer.last_try = time.time()
- kwargs = {}
+ kwargs = {'family': family}
if kind == 'SSL':
kwargs['ssl'] = ssl.SSLContext(ssl.PROTOCOL_TLS)
@@ -225,8 +226,8 @@ class PeerManager(object):
peer_text = f'[{peer}:{port} {kind}]'
try:
async with timeout_after(120 if peer.is_tor else 30):
- async with PeerSession(peer.host, port,
- **kwargs) as session:
+ async with Connector(PeerSession, peer.host, port,
+ **kwargs) as session:
await self._verify_peer(session, peer)
is_good = True
break
@@ -293,7 +294,17 @@ class PeerManager(object):
async with TaskGroup() as g:
await g.spawn(self._send_headers_subscribe(session, peer, ptuple))
await g.spawn(self._send_server_features(session, peer))
- await g.spawn(self._send_peers_subscribe(session, peer))
+ peers_task = await g.spawn(self._send_peers_subscribe
+ (session, peer))
+
+ # Process reported peers if remote peer is good
+ peers = peers_task.result()
+ await self._note_peers(peers)
+ features = self._features_to_register(peer, peers)
+ if features:
+ self.logger.info(f'registering ourself with {peer}')
+ # We only care to wait for the response
+ await session.send_request('server.add_peer', [features])
async def _send_headers_subscribe(self, session, peer, ptuple):
message = 'blockchain.headers.subscribe'
@@ -356,19 +367,11 @@ class PeerManager(object):
# Call add_peer if the remote doesn't appear to know about us.
try:
real_names = [' '.join([u[1]] + u[2]) for u in raw_peers]
- peers = [Peer.from_real_name(real_name, str(peer))
- for real_name in real_names]
+ return [Peer.from_real_name(real_name, str(peer))
+ for real_name in real_names]
except Exception:
raise BadPeerError('bad server.peers.subscribe response')
- await self._note_peers(peers)
- features = self._features_to_register(peer, peers)
- if not features:
- return
- self.logger.info(f'registering ourself with {peer}')
- # We only care to wait for the response
- await session.send_request('server.add_peer', [features])
-
#
# External interface
#
diff --git a/electrumx/server/session.py b/electrumx/server/session.py
index 0d114ee..7fcb396 100644
--- a/electrumx/server/session.py
+++ b/electrumx/server/session.py
@@ -20,7 +20,7 @@ from collections import defaultdict
from functools import partial
from aiorpcx import (
- ServerSession, JSONRPCAutoDetect, JSONRPCConnection,
+ RPCSession, JSONRPCAutoDetect, JSONRPCConnection,
TaskGroup, handler_invocation, RPCError, Request, ignore_after, sleep,
Event
)
@@ -108,8 +108,7 @@ class SessionGroup(object):
class SessionManager(object):
'''Holds global state about all sessions.'''
- def __init__(self, env, db, bp, daemon, mempool, notifications,
- shutdown_event):
+ def __init__(self, env, db, bp, daemon, mempool, shutdown_event):
env.max_send = max(350000, env.max_send)
self.env = env
self.db = db
@@ -136,8 +135,6 @@ class SessionManager(object):
# Event triggered when electrumx is listening for incoming requests.
self.server_listening = Event()
self.session_event = Event()
- # Tell sessions about subscription changes
- notifications.add_callback(self._notify_sessions)
# Set up the RPC request handlers
cmds = ('add_peer daemon_url disconnect getinfo groups log peers '
@@ -158,7 +155,7 @@ class SessionManager(object):
host, port = args[:2]
try:
self.servers[kind] = await server
- except Exception as e:
+ except OSError as e: # don't suppress CancelledError
self.logger.error(f'{kind} server failed to listen on {host}:'
f'{port:d} :{e!r}')
else:
@@ -205,6 +202,7 @@ class SessionManager(object):
# Start listening for incoming connections if paused and
# session count has fallen
if paused and len(self.sessions) <= low_watermark:
+ self.logger.info('resuming listening for incoming connections')
await self._start_external_servers()
paused = False
@@ -291,7 +289,7 @@ class SessionManager(object):
'errors': sum(s.errors for s in self.sessions),
'groups': len(group_map),
'logged': len([s for s in self.sessions if s.log_me]),
- 'paused': sum(s.paused for s in self.sessions),
+ 'paused': sum(not s._can_send.is_set() for s in self.sessions),
'pid': os.getpid(),
'peers': self.peer_mgr.info(),
'requests': sum(s.count_pending_items() for s in self.sessions),
@@ -346,6 +344,8 @@ class SessionManager(object):
'''Refresh the cached header subscription responses to be for height,
and record that as notified_height.
'''
+ # Paranoia: a reorg could race and leave db_height lower
+ height = min(height, self.db.db_height)
electrum, raw = await self._electrum_and_raw_headers(height)
self.hsub_results = (electrum, {'hex': raw.hex(), 'height': height})
self.notified_height = height
@@ -477,7 +477,7 @@ class SessionManager(object):
# --- External Interface
- async def serve(self, event):
+ async def serve(self, notifications, event):
'''Start the RPC server if enabled. When the event is triggered,
start TCP and SSL servers.'''
try:
@@ -499,6 +499,8 @@ class SessionManager(object):
if self.env.drop_client is not None:
self.logger.info('drop clients matching: {}'
.format(self.env.drop_client.pattern))
+ # Start notifications; initialize hsub_results
+ await notifications.start(self.db.db_height, self._notify_sessions)
await self._start_external_servers()
# Peer discovery should start after the external servers
# because we connect to ourself
@@ -511,7 +513,7 @@ class SessionManager(object):
# Close servers and sessions
await self._close_servers(list(self.servers.keys()))
async with TaskGroup() as group:
- for session in list(self.sessions):
+ for session in self.sessions:
await group.spawn(session.close(force_after=1))
def session_count(self):
@@ -559,16 +561,14 @@ class SessionManager(object):
'''Notify sessions about height changes and touched addresses.'''
height_changed = height != self.notified_height
if height_changed:
- # Paranoia: a reorg could race and leave db_height lower
- await self._refresh_hsub_results(min(height, self.db.db_height))
+ await self._refresh_hsub_results(height)
# Invalidate our history cache for touched hashXs
hc = self.history_cache
for hashX in set(hc).intersection(touched):
del hc[hashX]
- async with TaskGroup() as group:
- for session in self.sessions:
- await group.spawn(session.notify(touched, height_changed))
+ for session in self.sessions:
+ await session.spawn(session.notify, touched, height_changed)
def add_session(self, session):
self.sessions.add(session)
@@ -592,7 +592,7 @@ class SessionManager(object):
self.subs_room -= 1
-class SessionBase(ServerSession):
+class SessionBase(RPCSession):
'''Base class of ElectrumX JSON sessions.
Each session runs its tasks in asynchronous parallelism with other
@@ -648,7 +648,7 @@ class SessionBase(ServerSession):
status += 'C'
if self.log_me:
status += 'L'
- status += str(self.concurrency.max_concurrent)
+ status += str(self._concurrency.max_concurrent)
return status
def connection_made(self, transport):
@@ -663,12 +663,11 @@ class SessionBase(ServerSession):
def connection_lost(self, exc):
'''Handle client disconnection.'''
- super().connection_lost(exc)
self.session_mgr.remove_session(self)
msg = ''
- if self.paused:
- msg += ' whilst paused'
- if self.concurrency.max_concurrent != self.max_concurrent:
+ if not self._can_send.is_set():
+ msg += ' with full socket buffer'
+ if self._concurrency.max_concurrent != self.max_concurrent:
msg += ' whilst throttled'
if self.send_size >= 1024*1024:
msg += ('. Sent {:,d} bytes in {:,d} messages'
@@ -676,12 +675,13 @@ class SessionBase(ServerSession):
if msg:
msg = 'disconnected' + msg
self.logger.info(msg)
+ super().connection_lost(exc)
def count_pending_items(self):
return len(self.connection.pending_requests())
def semaphore(self):
- return Semaphores([self.concurrency.semaphore, self.group.semaphore])
+ return Semaphores([self._concurrency.semaphore, self.group.semaphore])
def sub_count(self):
return 0
@@ -701,7 +701,7 @@ class SessionBase(ServerSession):
class ElectrumX(SessionBase):
'''A TCP server that handles incoming Electrum connections.'''
- PROTOCOL_MIN = (1, 1)
+ PROTOCOL_MIN = (1, 2)
PROTOCOL_MAX = (1, 4)
def __init__(self, *args, **kwargs):
@@ -1061,7 +1061,7 @@ class ElectrumX(SessionBase):
async def banner(self):
'''Return the server banner text.'''
- banner = 'Welcome to Electrum!'
+ banner = f'You are connected to an {electrumx.version} server.'
if self.is_tor():
banner_file = self.env.tor_banner_file
@@ -1230,6 +1230,7 @@ class ElectrumX(SessionBase):
handlers = {
'blockchain.block.get_chunk': self.block_get_chunk,
'blockchain.block.get_header': self.block_get_header,
+ 'blockchain.block.headers': self.block_headers_12,
'blockchain.estimatefee': self.estimatefee,
'blockchain.relayfee': self.relayfee,
'blockchain.scripthash.get_balance': self.scripthash_get_balance,
@@ -1240,23 +1241,16 @@ class ElectrumX(SessionBase):
'blockchain.transaction.broadcast': self.transaction_broadcast,
'blockchain.transaction.get': self.transaction_get,
'blockchain.transaction.get_merkle': self.transaction_merkle,
+ 'mempool.get_fee_histogram': self.mempool.compact_fee_histogram,
'server.add_peer': self.add_peer,
'server.banner': self.banner,
'server.donation_address': self.donation_address,
'server.features': self.server_features_async,
'server.peers.subscribe': self.peers_subscribe,
+ 'server.ping': self.ping,
'server.version': self.server_version,
}
- if ptuple >= (1, 2):
- # New handler as of 1.2
- handlers.update({
- 'mempool.get_fee_histogram':
- self.mempool.compact_fee_histogram,
- 'blockchain.block.headers': self.block_headers_12,
- 'server.ping': self.ping,
- })
-
if ptuple >= (1, 4):
handlers.update({
'blockchain.block.header': self.block_header,
@@ -1440,3 +1434,32 @@ class DashElectrumX(ElectrumX):
return [mn for mn in cache if mn['payee'] in payees]
else:
return cache
+
+
+class SmartCashElectrumX(DashElectrumX):
+ '''A TCP server that handles incoming Electrum-SMART connections.'''
+
+ def set_request_handlers(self, ptuple):
+ super().set_request_handlers(ptuple)
+ self.request_handlers.update({
+ 'smartrewards.current': self.smartrewards_current,
+ 'smartrewards.check': self.smartrewards_check
+ })
+
+ async def smartrewards_current(self):
+ '''Returns the current smartrewards info.'''
+ result = await self.daemon_request('smartrewards', ['current'])
+ if result is not None:
+ return result
+ return None
+
+ async def smartrewards_check(self, addr):
+ '''
+ Returns the status of an address
+
+ addr: a single smartcash address
+ '''
+ result = await self.daemon_request('smartrewards', ['check', addr])
+ if result is not None:
+ return result
+ return None
diff --git a/electrumx_rpc b/electrumx_rpc
index b4aec74..69cc70b 100755
--- a/electrumx_rpc
+++ b/electrumx_rpc
@@ -10,7 +10,7 @@
'''Script to send RPC commands to a running ElectrumX server.'''
-from aiorpcx import timeout_after, ClientSession, TaskTimeout
+from aiorpcx import timeout_after, Connector, RPCSession, TaskTimeout
import argparse
import asyncio
import json
@@ -114,7 +114,7 @@ def main():
async def send_request():
try:
async with timeout_after(15):
- async with ClientSession('localhost', port) as session:
+ async with Connector(RPCSession, 'localhost', port) as session:
result = await session.send_request(method, args)
if method in ('query', ):
for line in result:
diff --git a/setup.py b/setup.py
index 4aafdf7..b6746c5 100644
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,5 @@
import setuptools
-version = '1.8.7'
+version = '1.8.12'
setuptools.setup(
name='electrumX',
@@ -8,11 +8,13 @@ setuptools.setup(
python_requires='>=3.6',
# via environment variables, in which case I've tested with 15.0.4
# "x11_hash" package (1.4) is required to sync DASH network.
+ # "x13_hash" package is required to sync BitcoinPlus network.
# "tribus_hash" package is required to sync Denarius network.
# "blake256" package is required to sync Decred network.
# "xevan_hash" package is required to sync Xuez network.
# "groestlcoin_hash" package is required to sync Groestlcoin network.
- install_requires=['aiorpcX>=0.8.1,<0.9', 'attrs',
+ # "pycryptodomex" package is required to sync SmartCash network.
+ install_requires=['aiorpcX>=0.10.1,<0.11', 'attrs',
'plyvel', 'pylru', 'aiohttp >= 2'],
packages=setuptools.find_packages(include=('electrumx*',)),
description='ElectrumX Server',
diff --git a/tests/blocks/bitcoinplus_mainnet_749740.json b/tests/blocks/bitcoinplus_mainnet_749740.json
new file mode 100644
index 0000000..308e55c
--- /dev/null
+++ b/tests/blocks/bitcoinplus_mainnet_749740.json
@@ -0,0 +1,15 @@
+{
+ "hash": "b51b081592349793bbf2d761828aabef40db8c7dd9451f0a0ea279faec1aecaf",
+ "size": 456,
+ "height": 749740,
+ "merkleroot": "cddd323b42891a0787389d40ea0971fbf4b0215ca40fc0229e94edf486eeb1dc",
+ "tx": [
+ "03079ad8d2c3e9db200582e7f3addec3f663c1388ea565482f6140ed58e83f1a",
+ "6805f87aa7027c84db875d7147696dadd998cf261237bac4a3229e0d7d35c104"
+ ],
+ "time": 1544168075,
+ "nonce": 0,
+ "bits": "1e0cd5ed",
+ "previousblockhash": "d86428d967a901e169de1313276f2d463664179e5a7f303923811d1c4a177102",
+ "block": "030000200271174a1c1d812339307f5a9e176436462d6f271313de69e101a967d92864d8dcb1ee86f4ed949e22c00fa45c21b0f4fb7109ea409d3887071a89423b32ddcd8b220a5cedd50c1e0000000002010000008b220a5c010000000000000000000000000000000000000000000000000000000000000000ffffffff0503ac700b00ffffffff0200000000000000000000000000000000000000000000010000008b220a5c015186024f3de419c8846da76a254930f9b5d68246eb21b1aceca5ba889a190ba30100000049483045022100d81ae320b3f1a296cae778b342937412cc4689f375d5dfc594c1df7fac6aa046022015c71b090e85579f041ae31c6d1881959414231882648b0878a886ba8b8b2a5a01ffffffff03000000000000000000408491a400000000232102eeb314d0ae50152e8f416521b0f3184325f57d8c7932d6a91d186fa2820d3da7ac56aca7a400000000232102eeb314d0ae50152e8f416521b0f3184325f57d8c7932d6a91d186fa2820d3da7ac00000000473045022100c4a62b764a0984e6e2f909ca41b21f61b17f230d60f6c2ba69cb504300931bc202200aa57659cf8c54861002ca924b018385aa3a44c4ff0bc9207ddecc3b8086f76e"
+}
diff --git a/tests/blocks/bitcoincash_mainnet_100000.json b/tests/blocks/bitcoinsv_mainnet_100000.json
similarity index 100%
rename from tests/blocks/bitcoincash_mainnet_100000.json
rename to tests/blocks/bitcoinsv_mainnet_100000.json
diff --git a/tests/blocks/civx_mainnet_50000.json b/tests/blocks/civx_mainnet_50000.json
new file mode 100644
index 0000000..aa8cdde
--- /dev/null
+++ b/tests/blocks/civx_mainnet_50000.json
@@ -0,0 +1,16 @@
+{
+ "hash": "596b9249ffd6c1dfb3cfa16d3ade2e0cb57342fed66b90e6444dc54cc99d0fc4",
+ "size": 445,
+ "height": 50000,
+ "merkleroot": "8db60d963dad433c8655021d291aafd731a6754aac9b69cf0dece61e5b3c1b75",
+ "tx": [
+ "364c947002496d3115379dd779e2ce54cf114fb6cca42bd3da4de1823e4f08cc",
+ "a09b658cbe21162bbeb1821e2caf1f0947f8cdd517b87f2232ba1150043790e1"
+ ],
+ "time": 1526710352,
+ "nonce": 0,
+ "bits": "1a7b61c6",
+ "previousblockhash": "44530f8cc7040528199dc3a41a8b660bf2feb8e0015d5c787086655ac03cbca3",
+ "signature": "30440220710162172b283cca7007c72cbb848a52388518b8cfb7a6c906744f473afe312b022056fd92b06351ee7049f0857d688134449566dcb82d0f6a3cb36208832988cba3",
+ "block": "07000000a3bc3cc05a658670785c5d01e0b8fef20b668b1aa4c39d19280504c78c0f5344751b3c5b1ee6ec0dcf699bac4a75a631d7af1a291d0255863c43ad3d960db68d50c0ff5ac6617b1a00000000020100000050c0ff5a010000000000000000000000000000000000000000000000000000000000000000ffffffff040350c300ffffffff01000000000000000000000000000100000050c0ff5a017879028fc93da2d5ce18feee9f074086790d34c97ccfafe15a42b2423f3e28490200000049483045022100e3a092a079346179213d9b7079c9795e291883cb715e3efee656b9a981632432022002cafb6d6d497705f455b5d6a60bca38c88c26910101faef433ecfe80515e1ef01ffffffff03000000000000000000c07975296d000000232103c0c30d173c8478ceaaba836e8cae3c8c4e43f88f6d555600be124781b533956bacc07975296d000000232103c0c30d173c8478ceaaba836e8cae3c8c4e43f88f6d555600be124781b533956bac000000004630440220710162172b283cca7007c72cbb848a52388518b8cfb7a6c906744f473afe312b022056fd92b06351ee7049f0857d688134449566dcb82d0f6a3cb36208832988cba3"
+}
diff --git a/tests/blocks/civx_testnet_60000.json b/tests/blocks/civx_testnet_60000.json
new file mode 100644
index 0000000..ac7b20c
--- /dev/null
+++ b/tests/blocks/civx_testnet_60000.json
@@ -0,0 +1,16 @@
+{
+ "hash": "1e4447195f4259b313b2c56072f7000237828e659254d5bf55f2b91e443f124b",
+ "size": 401,
+ "height": 60000,
+ "merkleroot": "9cf808c8f0e0d62864edee37a27fc44114a8896a6ace0c4ac6434c58e0d450ef",
+ "tx": [
+ "b3e734e183f1b4f10cc3de258d02efbd73fc64577e9c5fc54f7d750b95b29aff",
+ "859a989109ee967e941808a43224463e181b51af8acc27ad05e2e4f7fdf45f81"
+ ],
+ "time": 1537111488,
+ "nonce": 0,
+ "bits": "1b00ffff",
+ "previousblockhash": "4f57fffd01fabbf020ac9e2110b4de9e127c06ba19f83741a5cb26f3b0aa13fe",
+ "signature" : "304402201d89a82d54b81e3aa0de97875bb15a874fce3319c7baf1751d18620f905909dc02202fc3fe4c17ef43aaa62dab4a6e0c735751d75b8f66e2a81cbead52f744570769",
+ "block": "07000000fe13aab0f326cba54137f819ba067c129edeb410219eac20f0bbfa01fdff574fef50d4e0584c43c64a0cce6a6a89a81441c47fa237eeed6428d6e0f0c808f89cc0759e5bffff001b000000000201000000c0759e5b010000000000000000000000000000000000000000000000000000000000000000ffffffff040360ea00ffffffff010000000000000000000000000001000000c0759e5b0143723908791e72544c4796be9da581a40eae3ba54b71b596d167f6ab245b37d60100000049483045022100d11ebd7ac7d0dd94f22416a1b223cf91cf1a70de52b2cd502edc1121c4d5409302207e22f22add1dd96bd214ca0f8875e4f06d9688c27053db82ea0ae0e9676d575401ffffffff02000000000000000000c05469f60300000023210288e5256969a3a9fd4735e6b8c8f905b270564f2448658177faf4c990e5745c45ac0000000046304402201d89a82d54b81e3aa0de97875bb15a874fce3319c7baf1751d18620f905909dc02202fc3fe4c17ef43aaa62dab4a6e0c735751d75b8f66e2a81cbead52f744570769"
+}
diff --git a/tests/blocks/myriadcoin_mainnet_2587044.json b/tests/blocks/myriadcoin_mainnet_2587044.json
new file mode 100644
index 0000000..b87182a
--- /dev/null
+++ b/tests/blocks/myriadcoin_mainnet_2587044.json
@@ -0,0 +1,14 @@
+{
+ "hash": "09a2344ca39c422a473ab2ac0a93c0de5eef7bbc63c59ea36bf8a126ae2fbc26",
+ "size": 326,
+ "height": 2587044,
+ "merkleroot": "0373487200798a478a9b5330fddcc092cb08f9b517c62eacbfbe733b8e8d3680",
+ "tx": [
+ "0373487200798a478a9b5330fddcc092cb08f9b517c62eacbfbe733b8e8d3680"
+ ],
+ "time": 1540383063,
+ "nonce": 1758391936,
+ "bits": "1b013fb1",
+ "previousblockhash": "6bebe78892419acf8f47fa34ac08417036c244a244d209dbb5113edfda7ae480",
+ "block": "00065a2080e47adadf3e11b5db09d244a244c236704108ac34fa478fcf9a419288e7eb6b80368d8e3b73bebfac2ec617b5f908cb92c0dcfd30539b8a478a7900724873035761d05bb13f011b80eece6801010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff4d03a47927045761d05b08fabe6d6db5b8ddd39dc3e80a8930480db7f9dd41acc305fe5f41f5c953ae73e980a534b9020000000000000000005275250000000e2f6d696e696e672d64757463682f00000000020000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf900ba1dd2050000001976a9140c6de8cbb3e5fc90476c696881dc28bb9b4989e088ac0120000000000000000000000000000000000000000000000000000000000000000000000000"
+}
diff --git a/tests/blocks/nix_mainnet_50000.json b/tests/blocks/nix_mainnet_50000.json
new file mode 100644
index 0000000..4dde731
--- /dev/null
+++ b/tests/blocks/nix_mainnet_50000.json
@@ -0,0 +1,14 @@
+{
+ "hash": "b9fa17b5469c68aeddea48666f12c8ae5cdc3e8521b4b5637e10099cda8728fd",
+ "size": 377,
+ "height": 50000,
+ "merkleroot": "2ee565d865d766a9861d77f1d3c34b364261a105fd0b10c15e9cb07dc61b1493",
+ "tx": [
+ "2ee565d865d766a9861d77f1d3c34b364261a105fd0b10c15e9cb07dc61b1493"
+ ],
+ "time": 1536401916,
+ "nonce": 1610805540,
+ "bits": "1b0551f5",
+ "previousblockhash": "acec77ce8845f34d2a854dc7da66151338c2f42f1c7f51782bd7732017f13c31",
+ "block": "00000020313cf1172073d72b78517f1c2ff4c238131566dac74d852a4df34588ce77ecac93141bc67db09c5ec1100bfd05a16142364bc3d3f1771d86a966d765d865e52efca1935bf551051b24f1026001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff200350c30004fca1935b08180006a3510200000d2f6e6f64655374726174756d2f00000000050000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90090f4f70000000017a914e48c970e4a4faa6222688ec6333eb53c578e59f78700d012130000000017a9146a27966d76edfdfb3c53dc37dd5471bdeac7d46c870020a1070000000017a9147150055215791b779dddb1d112541a3fce71061b8700c0cf6a000000001976a91490f50a1fa5b280282de2a3ec15164a766fd48a0288ac0120000000000000000000000000000000000000000000000000000000000000000000000000"
+}
diff --git a/tests/blocks/nix_testnet_200.json b/tests/blocks/nix_testnet_200.json
new file mode 100644
index 0000000..e03db0e
--- /dev/null
+++ b/tests/blocks/nix_testnet_200.json
@@ -0,0 +1,14 @@
+{
+ "hash": "9d5d8d308484a654cfc18fc6290734dca584bf0c02409429682a8b46715a0811",
+ "size": 384,
+ "height": 200,
+ "merkleroot": "ee1377591fe82696a15f416476a28cf57a83af77e799bbfdff24909be06cc559",
+ "tx": [
+ "ee1377591fe82696a15f416476a28cf57a83af77e799bbfdff24909be06cc559"
+ ],
+ "time": 1542147760,
+ "nonce": 0,
+ "bits": "1a2157b9",
+ "previousblockhash": "1634ccd348860d24b5eabffc8782ae1aa1d6797477f0d4910cb25488a28cb372",
+ "block": "0000002072b38ca28854b20c91d4f0777479d6a11aae8287fcbfeab5240d8648d3cc341659c56ce09b9024fffdbb99e777af837af58ca27664415fa19626e81f597713eeb04eeb5bb957211a00000000010203000001daf694ffcf73e2710b6148d89fc10b95cb55e255d804014c5141712168b9524c01000000171600147c5aad68a58a92630e3feab030fcaeb2cfff37feffffffff0530ecadf66b01000032b863a914f0c3b8f1fb7c1887a70c0108f729620c4c0901228767a914bdb5bb31196596f760ddaa49f7a82956e30b58de876830ecadf66b01000032b863a914f0c3b8f1fb7c1887a70c0108f729620c4c0901228767a914bdb5bb31196596f760ddaa49f7a82956e30b58de87680090d0030000000017a9147667be5313f1e3fb921a096b4b9b59c82b157ebe870090d0030000000017a914bf41e0caa3649bd8b9f4008103fb2a2757f3b8f3870000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf900000000473045022100b517ff2a36aee842e4ff750723b1cea0e7cc0064677d69735e4775a01245224802206db5bf42c16d76e790e97f741a10f576628c38bcb5a7072d1df4be9c1cd874e4"
+}
diff --git a/tests/blocks/noir_mainnet_370000.json b/tests/blocks/noir_mainnet_370000.json
new file mode 100644
index 0000000..d464e9d
--- /dev/null
+++ b/tests/blocks/noir_mainnet_370000.json
@@ -0,0 +1,14 @@
+{
+ "hash": "572d8a5897cad3f3a75811258513d321e8971c27ed210bb24eba4cd59697dc6d",
+ "size": 230,
+ "height": 370000,
+ "merkleroot": "21000ff185207fc1b208ab1c03de33acb678d6ef542731426da5bfb08627d0b0",
+ "tx": [
+ "21000ff185207fc1b208ab1c03de33acb678d6ef542731426da5bfb08627d0b0"
+ ],
+ "time": 1542089008,
+ "nonce": 1713111040,
+ "bits": "1d05e718",
+ "previousblockhash": "cb9b3ff2a750f644e9389f50c508e9a4e217df2f07c5bc6a89a2e7fc82e2cdfe",
+ "block": "00000020fecde282fce7a2896abcc5072fdf17e2a4e908c5509f38e944f650a7f23f9bcbb0d02786b0bfa56d42312754efd678b6ac33de031cab08b2c17f2085f10f00213069ea5b18e7051d00001c660101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1e0350a505043069ea5b083fff6fd4000000000b2f47666c6172652e696f2f000000000260b8131a000000001976a914122466aaa347290e3b3b7858f2ba041af97d1b4388ac20c46d30000000001976a914a26f5f0cf1b72ae0a5ab533f1e5f420876b3bf6988ac00000000"
+}
\ No newline at end of file
diff --git a/tests/blocks/smartcash_mainnet_200000.json b/tests/blocks/smartcash_mainnet_200000.json
new file mode 100644
index 0000000..5056cbe
--- /dev/null
+++ b/tests/blocks/smartcash_mainnet_200000.json
@@ -0,0 +1,16 @@
+{
+ "hash": "000000000000d1772b7e3ffc661133c252faedcc52b3dd186b83da4b2f2f8cbe",
+ "size": 692,
+ "height": 200000,
+ "merkleroot": "f8f490671e499b66a73e3fe37f3b2288b82e3d359fc3c18938f32e9e830928d2",
+ "tx": [
+ "c19c6e1dd5ca9d06b046a2962782293cdd9be39bb006ce65add0ad945c9eaeec",
+ "0151e788a439caa317dce1cf5a754f0b5bb8639d4f1073e89d3baa450fda5306",
+ "a4217a9a787b66a597efae52b24e2c88d1700cf5661bac6ed58c718f90a98b97"
+ ],
+ "time": 1511678597,
+ "nonce": 3716569074,
+ "bits": "1b01c757",
+ "previousblockhash": "0000000000016e6bcdbe8af532509cf5aaf99e74b8b0346a59d360beb460e0bf",
+ "block": "02000000bfe060b4be60d3596a34b0b8749ef9aaf59c5032f58abecd6b6e010000000000d22809839e2ef33889c1c39f353d2eb888223b7fe33f3ea7669b491e6790f4f885621a5a57c7011bf25786dd0301000000010000000000000000000000000000000000000000000000000000000000000000ffffffff2703400d03062f503253482f0485621a5a087ffd6961cf0000000d2f6e6f64655374726174756d2f000000000240e6202d040000001976a9145dd99e7cfdee45d47edf9b2d92df27961241145e88ac40412d574f0000001976a9144a7018841fc6003d66b6a1b859e73b4cc3e621b488ac0000000001000000019521d2cde436c42b81158198b2cd8b9d52dc093d4f8465163ecc98a134dd12a6000000006b483045022100ddad79fd8a9fa0ca664ac8e91aa7c8dcc7cad8c4894e1a7d9143de4bdedeef9f022013dfd0605b50d22d2675e21a27255540fa48263ab44801d287a61883ee8074c3012103635fd7668414f4eb1d6088cac789e58709cae9c3c07a00791f415164b67cda4cffffffff0281c03b9e000000001976a9141dc91417504cb86ac5eea49d85a4d3fd51f72db988acbc91e5cb000000001976a914ecdccf8dec8482314050acd84f72d6559a19b30d88ac0000000001000000011a8b6c07eb61bb5e9910de4d31a410a5b69de2bde8a3cb9df599ddaccd373d95000000006c493046022100d1bf91d45a0d53bfad9aa0d226be625f667641fa53661e2cdc21526cf84ede21022100d0cbf38220d789c9b1a5c6288ebfe2e5ddd4ceb5714881d0f43a27d06a6e9649012103133678a3c4d9855f4a25ab318619fe68e0ed67d67c14fa9be678cb561a75f71bffffffff027fd500fc000000001976a914098cd3573bcca56674eb6eeb51fad42ff353945088ac58bda83d000000001976a91456feb1c72217b8f45aa4183da1c81b314a47f5f388ac00000000"
+}
\ No newline at end of file
diff --git a/tests/blocks/trezarcoin_mainnet_612581.json b/tests/blocks/trezarcoin_mainnet_612581.json
new file mode 100644
index 0000000..e1f3efa
--- /dev/null
+++ b/tests/blocks/trezarcoin_mainnet_612581.json
@@ -0,0 +1,16 @@
+{
+ "hash": "7fccc90aa5997b036d41ec079a2e8e2c52a0d1f64079b59aaa08c25e0e1e1a30",
+ "size": 682,
+ "height": 612581,
+ "merkleroot": "7d9f01b60ab6718e97018b47cd0cf61ca85afe77bcf8d9c90e9709bb2aab9539",
+ "tx": [
+ "758012ea68d57bdc6e413a6d4195638b410df2ed8a3b2387343a854bb3a1c4b6",
+ "c9f6d252f90f42884774d66d8ca17629333cda2871eeb3612307abeebb7e888d",
+ "1354ba387c5f42f807889741a63704fc0ea87dc04f29834111c5f8d80ac0a70b"
+ ],
+ "time": 1541864019,
+ "nonce": 0,
+ "bits": "1c092c62",
+ "previousblockhash": "ac69b095cc53f7a0e9197a243fab2dfa49e7f0934fa79ec68f47993be813b33e",
+ "block": "030000003eb313e83b99478fc69ea74f93f0e749fa2dab3f247a19e9a0f753cc95b069ac3995ab2abb09970ec9d9f8bc77fe5aa81cf60ccd478b01978e71b60ab6019f7d53fae65b622c091c00000000030200000053fae65b010000000000000000000000000000000000000000000000000000000000000000ffffffff0d03e558090130062f503253482fffffffff0100000000000000000000000000000200000053fae65b014f5dccf6592b950c707a5ae62c11644bf0cd1bd5777412a26c25e60015ce4f2001000000494830450220360d685960bd8fa30617beac5c952073e2d89be2528eeb11d475e43b894abdfd0221009b7cd6f2b5d8e2f8ad1919ea81b058ba1703e8c07180c09bacc974ec418a612b01ffffffff020000000000000000002059a4cd000000002321026b1af42e2ea1f61ca8bdb563a61787f8d69d7578de77229ef7bb2d2120b7f8f3ac000000000002000000d8f9e65b01b170ad1b81ac0164a637b6bd970c9897cc9be7d4f05b358ccd868a5d2adb2779010000006b483045022100bfffc1364ad5dfc2da6aedc8d8cdb31905d4e8fb7e4c7c5efafada7cbb18559502205a9cd74bba48fc60e142214917a6d66347aa5ca8f0d9664546e37955ca09c401012103765dbe24359bee468d2fde0ad714906bc0c15ce02e7170dbe98d42e663adb3a2feffffff0290aa0830000000001976a9149d864f487a38c872013901e463ea47a56967251888ac404b4c00000000001976a914690acc951705cd7668f6df688ba33c62cf15a3c288ace358090026746578743a536d696c6579476e6f6d6520697320612062616461737320676e6f6d65205e2e5e473045022100ef1ee96a60ce2d0368b371eccf92aea0d532ff5d447ff70b403130f0212b10ba022061c2c9d7b061035fa416303699768c8e99f3f27a470d3b63abc055181ea91350"
+}
\ No newline at end of file
diff --git a/tests/blocks/zcash_testnet_283046.json b/tests/blocks/zcash_testnet_283046.json
new file mode 100644
index 0000000..5fb169b
--- /dev/null
+++ b/tests/blocks/zcash_testnet_283046.json
@@ -0,0 +1,16 @@
+{
+ "hash": "00036319ae4cfea536d543ea410b89277826340af6134b5ae7972a4f7a3de0a6",
+ "size": 5276,
+ "height": 283046,
+ "merkleroot": "888873d6cc24794d835db193072079d07818dd94dc7401faba955b718965c821",
+ "tx": [
+ "d89a3b4ff8c333875a4a6b6cbe7de9e651f3c6007598c5ef108174ac1b626336",
+ "ba4d2c82b6700d57c0614fe5bd1fb90fe73da10e61dde2f3b3c87209388c419d",
+ "5be150e493a89448530a3b0cb43a7bf1c134be937bdc6ea8bb4cbaa5c89c8914"
+ ],
+ "time": 1535721514,
+ "nonce": "00008bfa4bc3c42aaf193cbbb47bca9024391021403e1d45f39a01159c82009a",
+ "bits": "1f101e83",
+ "previousblockhash": "00028bdd7797c57413d00165953ff6313e9c5f2d194338312e78b8a55ca6aa24",
+ "block": "0400000024aaa65ca5b8782e313843192d5f9c3e31f63f956501d01374c59777dd8b020021c86589715b95bafa0174dc94dd1878d079200793b15d834d7924ccd67388888e237da776e7b2209e994340db5cb6124351b503e52943ea095df5f2b93385322a40895b831e101f9a00829c15019af3451d3e402110392490ca7bb4bb3c19af2ac4c34bfa8b0000fd4005001e3c11c2a45123afb3e96715d03aa6e3127b0bb225499523d710dc893d3bf2c1f334794cc3fe8d80fe0881e1d7b9d4a386dd47b0d051d5f8e6158755b52d3af9dad1ae974c2b978156a687bd21c62f6ad49ee9040c295406602a6d1cf482a03e7924f12544134135153c3779202b31cbeacfb2c943b7b98b0d795a622f15b14a82948a2db4f2bb0a3d937427af7c2f5d3ff231bf1da5469c3dafc8c8489be3f5803fb923bf27760e7fe52dddafa581af7494c46d72870ab68ff5e0654d312fc37ce17ded3fd9360dccb3b139a4e09e59990f0cb707640849496eaf856acd424107a6377dfea63967abb4425289139395c54e1330d9625075d96c221911e4b2629f62afad84048d3a55a91deea95322b41b4fb165eee79b75a240522496128d0cd6905e93f42dba2efe3b6cf6276be533b068f7f379533a9d16ce622f048a1b1f1feb8c16b83bd9dd52f76d78fe7f7d016740e2bef9c135fe83e1b889f269fdb77433ab4f15c4a9010517ac156246150c923f641e0b3c9702f10784dde19a43e5211992765c5841ab51adaed4177e0f8291eeba12d600d3d803d7cec09e2947cf5c188805a6147490c19c81368291fcb1d971c66cb8d5a612494b7e9d4895e6f9563b7a52a07a22035a037ba03f186a7cac5ba44b699bbcb1fc067c0841a571bda4b623fb24e4fa2db4737304e4caf17bdf6ec0013af28a03ce1c75b98cc6f5acf242b355a41187218b3d98781026c8be15eb0069a5e642554cea1a590ccdfa425a0c62d9ca05596fbf92c511135f32e4e9b018b3f0f624de86dd5d7bfcbdf053072bddda2e45d498b286780f79fa3890b33a7bb120e77e2feaf376c85a7802642db0a6670dd735dd8f1602f5ad37eaa0cdfa780ef91b26598f122a5f17ca8b763a636e622a24c5bc44b04891cfcd5d9228ab5635266a1a3be239e70a1784d905673bee5555c8bd7e08b452d05792f9b328fce2cd198fce244b89e03987bd12a41bd5ec0aeb3f3bd5342765372b49dbe00fb1e3347fea7abb2dde3691b3a32d4afb4ec9528f94d9628797cc3d0e7dfffb33af2d0d219f3188b713b5e4dad11edd407e28f8b8ec4904281fb40840a22e7b9ec0868ade467cd6890cdef721499a63a4401f6e4be95a251644f31d75a9da11ed954ac2840b0b59792db113e63dd7d09179b5a437dac5097bf7b5418ed871448c51f74c186d662a8e993afb0ba909025cb011698bb831035b3fc66ef86bfa947b09dabeb1a3ac63fdb3891524e86630361bceb58c3a2e725a4ae49d6f4d7c1344993d6644c6970bdca50e099e65ae0e89d3389113528bdb1b42d8bcd2923d803fd9ba493452319761c3f42031ccd7c1655055d90d142061b0758a34d6ebaed959c0fa6dafac86be20ed1fe20f26e3312833af5c75479ebe4a2d556b12b0e709ef7351ebcad944952c760a57d76619d5a72f0af94ccd7ed05d1b5b974067b8409cceb7da871e99a9550d793cabf0e03f0bc54a40e1d45fb71fb40eff2e6d0fa7c994c086a5c1afda32c3fcc62854bff62e19871679922ea6cfa2dfffc9d8764d43e162ce3d7ede0e1c56cd625dc92aab124955bdc4c56d15a26f9da23225f5a2e22f4c5c773afcb2dcf4e0dceee183fdab88291b16bea9a952837df075e439759f55cc05b80d440b637a3bfcccf2194daf02581278594676efdbadac0f098ac53299756b6b89357e6f7d0419ad55f6527b0e1763c0c3e3c2737be3e319dade3525aeedefc806683064bafc69c21b545837472abea915f41ab3d7d826bb22e6eedc128736b6928c7b9ffa3366633fbde03546fa3c36f2e9fdc81e0500f1c8a6cdee3b121d11332769bd7db2dae1b19e24e856cfb8fd99ccbcc6953f9d44e1c1eab607f600c42ce171b92f2cc3799fcb030400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0603a651040113ffffffff0220189b3b000000001976a9148a6c756f01e86875e6b6213d536dbd263266062888ac80b2e60e0000000017a9142a71f51b268f74eb3ee5631090589bbf6239bc0c87000000000000000000000000000000000000000400008085202f89000000000000b8510400000000000000000000000100000000000000001027000000000000b68ee5cc6a137376d3c0ee85f6b79a83e432866f99c703db6217064078cc5f4d5ce93244554705daea62988d0f0da3be49ddcc476e471a2bbc2accd465c0baf402e6d35f8b303e3f8b1231fa399781419d28ef75e02391ba22bfee327c51d66e09489e9658689c1d4620010867d5127d04868580c555a91628aecae28061b2c9add8145b186169af5473b7b3bcad895f697b6ddfe8ae866cd6e4de64bd95d20e54f53097353de0fb55e95cd4e711218cb8371b7dce40610318e4f1741369fd390b3028ba8997c9c26de87586cdc9660446e275b936e8338b8151df4bae86fc6e12d382fbb070f93da7e754c06120662c1cae5c220f42c0844fab2aabd7217efb124c049dd377ff53d462133bf83d391566fea5739069d2e1fdf546b915b4f835b51be43a6e82d4ed73490912e3e12a75e7cee1c193f9f8683b7f61ee0813fe949039d4ab7ab220e88b62a5f8fdaf12b588a63c4007410ca49876e5c27a1b2f9c7470509f02a1c50561f39b5bb82e4ab7af2ccfc59254996f107e9f6357b86c49073cc1f772cf33f72deeb09d66d55a97a757b20b3344d3f3b2951c4f3b79db614111f11f6c73fba9d6c0a3fb6740785197d15b900e95795bc48459f3c068022bcb4d15bc4a37f3a92c42c66035cb3fa9fa467c9cafe0efb4fd4d9a9313f0f0889a16c0287e1cd7f0f1952104de00faf616b5981010bfeba2f89fcb2a4e86d496cd579281e6e0bc3fe395fb2a695c38574a0d755c43fe8a5bb802c9d7524c2f353bf4f885d12b986ab608cca547264f9e00d1f611f9d9d0068e7d78e4ce44e6fb83ae7b25448d40c7949bb07331fe030ff81490346d5896b686a760c6139384dfcbdf2dbe43bff776a228c2418178a8f20b9fb483d60788e3b5126f1fca8f311c4af651efa3595957530f8376c60e945e10af3295d3bc6377d93056ba9135dfe43af5b355bbb9c80f10007a5b77ad6d2db49debd7f4f6733d58c79aa0e41d57fe6b37e65a612f5a6087db37c26819691c44f54a208fc210129b51ecb04aec3c6ffd36037efa69986df75d27d0ff01436999f39e4eef379d2b82ca1a8ea5330b383eb6132a23f1a53ff3e02bc2376509a402d5fb4a19fc5685ab5f829a91bef032573e68df896187baae4f24d88db8ea132237fa90b91c1b1cfd42f09952d2f18cfc9c152c532021a06b9dddc1d757a1fe9cf954081a1ef7fd5df0d9a2567a0e6e523f1bc9820dc05c63dd66d2d0dd12d4aaff4ad8a1fd9aca06dc88f5e4820f03078a0aaa1d09c66ff7e57ee4008202175c78905321fcf6beba47d733dc8b527d07bc37bf16ccbe48a79001df1ff104ec90022ed0f6ea7fcd2868a1c5f1f2f3ded4136eeb7db04a59d61e3ec467d8309754b935ab93270222eabce4a66fd656adaa4fb5b461a7a9bc7b3585d36b44d454b64f357043c6f3493eccb06dab906b5cd04bb7e528f72152ef9c5780a78c3a4595a0b075d93c335fc520c53864da9b651109096c121b21b59eceee1c77b64bcccbf257ce119cb087f4aad5ca370550ba2d36810812c40c5c089a84133ae44edb7b42ab45e24cb1fc7b596de06633150e5bf788bed5425846bd5a675b2058235a548483c3b10b0bd695e332aff865596e42ad4ca4a2237ac2bc2373d2d57516b81c81f4349115bcea3d0c72db84a3118ad37836b0ef60a4b3e3cf73348c549678f252531a28fcb90dc6f33b9dac715850cbfca1b16665b13e6212665e72560210dd6e31a92b0186fb698d44a21f496d7b81c9ca9f27ad3d74e7c13169de2e05b69792f18bd19d4dc4eabf75651dddeffe07d924cbcdf284cc586632b6740deec554cb4b46f1ee9a68c8c480e531233b3f2711063353405b4da84c0059b33c1f052a4672ab39cd95902158b5b0401126faa0fc79fd484714569761e8c374ba144480a16abba522acd20b6c629b07c7e8fc70bb5a5e760bf8dc7917a3cec7be5a95a1e28db54032eea849bb018b48bda820848e531131629e59cd1cf4fac78c074be3b22fcc316b0791bcc481841cc1bdcfb489f92eefc24343a34944bf71aaa9a324978a183551b4c179691794cad00cf96181deed56444e307a64f288deb49646c0cc5dddcd691824644c36845f1bb823874393e4e9d6b3fcb35e2f05f45a9ffd60e36d1937751de7eca464613cf3fc7e18a0906f5ca134392125d7001a5d659065893c22d3cd0128b1b971b311df2c6a339fd97fa27ac8fcd5f1852c31c5dd56bf44f736c822bb970820c5770ec86ac879ebc9d8469d4edca1430591c37264a2a5b669c579a73132c7881e98f5f94082b250e7151251728543bd31a54a6cb36c0506c3758d6447c241e22b79519a77aab153c860a2ab4d783f994779930a1f4d111d0473ab5de7fba0de94e0f585f34c55354de5b00408e86c26002887eb94cc653475079e079d1d064f3b359017b346881c16a76150ca763065df511374af9a358dc059ee8dd7a18581f71d12bde6a8c0e7a249c5bec16534b735d23cf49cc0fa020400008085202f89000000000000ba510400000000000000000000000100000000000000001027000000000000b68ee5cc6a137376d3c0ee85f6b79a83e432866f99c703db6217064078cc5f4d3606859d04a0e71b1ee043cd99c1b35967ed9f853e8764dab4de155943f6b6ba79856451b7efa0f4094806394f70edc46d4db04053b48e21ad34f509b0e5386eea55fb6517af0685a00dfcd503bfafd1506001b1b13b8a99674ca16218a3934d6a47ae0bdf70715d4c367df445b48b5cf11ef449cb616e44aeab19cbfdf1b758ae81632f41503cfeaec680e4a2d066b80558f1184c9c8f37f66d5a8a3576226473d4acc5c021853da749f14ad1be45849e012c96c66103e638e06a081287fa8fa3f065e677bd72f767e5cf83fe2d98bfbe2edc21d5f64b1411ba690536722a0fb7cdd8ede09d7b6878bdd9d41c28152d829c3fdeb77be1e2f3d60787023d8f59a0c1742f52c6e894242fa62e0f20806c8b101e281c0a791eb9b0901f3e3d4c6181d0d35e17f5a0a3afa59739e5a872c8afa34af5656459a463bcecb7ab5f6b312e4d8c76491c35c9db5ab020cf038fb6ec721e08e813bc7f3c5d12a11967d91c13a37b8113ad9844ca15375b7cc7227a1a875eb1db927219c0d5748cae57b5769a4306636846b91236ffd02bba6005b28ca2ee3b7e655c157fa67b8667abe2896865ab8ee6dcdb15d146adfe6d731b7822025c8e4b8a526c9a78c97e441c1a055682c142cf070c971008c9f75114fc4c474a2b4ff3b525a8c427ab105acbcede3d23e6338993d4ea21a446e03fc0e2dc37ceefb5859a9044c6e95d3088a897ce615c498a9f4adbae30843338b241f9774349bd52f234332d2b201683a0f132744e1208fc8adb26a3c65a0f13573de20b71d433c960a23840f1edee60a992c4365100534807c8b40e1797464683b25abfcf69322e3ad0eee9d03b8ec33c24f8d8128f5bb93774f54104cc951451d8a7a40306da1e136b8f0521fcc2c4f61bc7bdd9132560b4ecd612752eea3bcf97e7b4f1ac0a55d6c43fc993e7b370a08d2b2b6f64ecb57ce52ef904dec716b28a6c2349c624f53537b8b56b4a86577edb64b3955081de856ba910e2edf5daaff1fd15abd78bf8c527280feca2d2c821ff06c6b2e15799f9c0f2f51c79cd3435d36f9653ab10f184211d986409a79fd6620cdfa5b918c12c0fe841c972924e1699a17eb990909b47793088928880ec30ce5ecfef74462f8c187af913fa3873f1410e16a455d8fb9c64fa671b6c5a0166ec60d752d7f4a42c5b6fefbb530e0eccc7400272687c389d4b543b7681d3c11411515d70407b0261954f401eb73e9567a36c003d257ec5f11c41a87a9510e7d0ca34629153c3b7f286fb06d311df026ccca6bc432879b1cb0ef0bcec1fbfcd68d09347eeeddb49ff37b1d86ceb1e84c2eafea5173a960763ddfeb90c8c1f8155bc74281bc48ca6ea61335df04406851baa3ac473140627773b6c1fc0f5d7cba570b88e74123bd7001e4c12a34c73fefaa48fc852f826bfafd45a036394b200939e849bfc637ec7161e3383694c3cf5ae1d47911899d3e914337de0c979edd5f3cb15e8f34a95ec5bda063555edc061fe1046f9419769bdf54452d50b02e0c911aba993541d33e3e3e71b2387182fe3d8bacd38f0e51c2e38b5367f59475156bd4f4ff62cd4f7e7cae7508d24bb0a9f9f6a8fa7d7349a71ffc37e28322efe7f8637035e24529fadd47d8b9a4354519ed9e936c1c18f24e180735f8da535ce35666651d336b139b0226457e43b6d61991ab9a8db3a72b2889d6d0f58d4b96e29c6b67a410afb384c8b7279697811d8f0d7b555eaee0807b084ab3584164108f435e40c3ca3c7399157a19c6ea7f22031154dc71ee134daf71b8e2911430c01d6d7e92577b5c97cc552e302a429a443d8ff508ed2b77102b9eac113e031816a2f57662a780b79f606727d61ef330e3c872cadbb10987396dd99baa7216e04914520a148d141108bd03e8babec344294ecf9f1068213206bfdb8ec2d31586c13d2b74b7a188e6389fb06ed83f5b9eb72f37d3c1184dad4e2e9c84eba2bcd910ca4a34ec175eb8de2e9fb7eb52a0ccf91248a0ddfcbf266ca8117dd49acf7c24a0d708bce93cfd2ff2da2e3dae465859841a3777c8a26e469a5aadfeef788cd98588e4cf464bae81b80621e5c967e4184a51b7f2c36f6ed519d763e3462823cd67c78c0888912ab49e5c9ea415b57eb7e60355de6847c7efa283c31a68e42d0df833f8299adbba9997de51cfdba9fb9f07c7eeffdbbd29b5dc6ae406eb0540a20bdbafc5ec04a3e373cd72ee7c2625c601375d4b1762127ed53c6d2ceb79663d5a804c1822427fa659f3e1e47ae25c50d12f34626baab6155efd8bcf29ef5f4585b5ea7c3d19c8dda13da4fb573b9f08850a1b1a641b4c0dc91f44ae7923b65fccc8e45c58738c59a5448f6e5a608628595afe1f93c093327979473271adc5d5817ee5ab8853d200e1a7aabf864dc396a2a92a995659d5eb68e155767e81d0f04beecff6e08901a7141e084022517a1d55511f6a2f00606"
+}
\ No newline at end of file
diff --git a/tests/blocks/zcash_testnet_304290.json b/tests/blocks/zcash_testnet_304290.json
new file mode 100644
index 0000000..4a84b07
--- /dev/null
+++ b/tests/blocks/zcash_testnet_304290.json
@@ -0,0 +1,15 @@
+{
+ "hash": "001156b74e55b2de497686814210359dccacc4fd062378eb9f2589192331f92b",
+ "size": 4003,
+ "height": 304290,
+ "merkleroot": "a5a00e24671c56e83c4c3ab3dce220190ba050eb337b0e9c65845b84c5809276",
+ "tx": [
+ "0dc31cacd7fa3d79148e7182650efdaefac811f616b2b5ee3b8637d54fac98d5",
+ "bc3710f869faf00d8453b2960dd73d2c59d4e56def31ef24287af3c2527acba0"
+ ],
+ "time": 1539292375,
+ "nonce": "0000f309899c840746395e25bd7fd6f992ea9bd4de5426e657dcd76552aa0093",
+ "bits": "1f1ebb93",
+ "previousblockhash": "001dcc6713694e27d28354a105154156b1fc3e5b82ffe0f49b964828f1b37100",
+ "block": "040000000071b3f12848969bf4e0ff825b3efcb156411505a15483d2274e691367cc1d00769280c5845b84659c0e7b33eb50a00b1920e2dcb33a4c3ce8561c67240ea0a5b1ecc5e604c6f589b1fc11175cdaa14fd0299b6d13b1c0079e2ffd89b9a39647d7bcbf5b93bb1e1f9300aa5265d7dc57e62654ded49bea92f9d67fbd255e394607849c8909f30000fd4005005cd7cacf4d66f4aa19a4359d2ff896fd383a48152a991db271e3e771eec2d504f369c449bc2a9a0484105b224efa8a4ba12d5dc2ce0da840fa2a96531dbd356f8bdd29a581218f9445917368b0bed3023c23660460d8b6e790e77f220dd06f2666ef5992b332419824f83c60cc666e6b65df7679e43b8185b1080e901f0c1dedfcf55740fee61f147715cef392ed967e3ddd49c00f882062fd616a63a60cad4d0c69aff9766dc303b4a0a7171df1a593dd97b47e43e18ea3551a53ae05d4e1efbf4e8710c122808f970fc56cf28ff4840c18336ccebca601add1320942a2d7c43ac06edb16491fc07f73d54c2992b4a213e815736b0629f299254b166568de3659afb3745ad3b3b8b7e571865631ae5f1db385e5ffac4923bf0f596470cfbf6eb163f906da187215d244d3c8674d1c74267a469cb154eadca5163d0917113df1b7579d7b79141b7c397addedd8a2c2052be6e33e17bb05ab6041710fa90ec0d296af88850c98245b451254cfad9fa187ccdabf4dbbdd4f4b4616fc937f6c1be86d993e117ce0925104e2734a8e3137f45e16efe25907b841b3d57ad7cb78f6829d002307e64f7d50f542cbe38d91e81859233282a37d85e41e51e9dd61666bf1b74a4318bb2426db2fbdbc27dc097ab97d37829d6708f690e9596e1bc473b5d945ad244a7ef1f7122c8ac425b513a73fe5d95f891bbb3209becb92dde3d0ad39f6a8aab3486fcaa7bf199ff90ccaeb6cf40b9dbaae6ca82ef846308290761b1e4f0d8fef88c3d6f25d086a2671afe5ed169f97bf935c2da90ac0ec5f64ade38624899b7dd5d9d67c1d50b7107ddc89eddcd1ede23e9146011158315ed05782691d140dee182c77d381592b98c37fd7df11b03ca0c210814dd869045bb42dbd318629a153b4777e74d5201be544542a5a7aed3b4b436b61dc872142cf6eef25014992f0104f51f1645d008e462319fa440598607c195a315eb7a41e9d86e7a46c4cd6391db4b71b16df04303489024b530b8423e1af015ccb3ae3ff7da9a1243f079e341d0dbee9587395c3fd4a7949ecf83144090b823b28596eaf60ca84df00d5a0da0acd7b8b01613f6415f56eec7dd7258728724487a63f667e41612e008baee19652474a22ade42477255bc9265f05963e9d84f9b09c4266f97685b250e523adafa78efc5f03057282ff257f3d4851b4f5327b0926c3093c0adc0e7ad8bd175567dce4a3a50f11d0e05980c17371730ee27fe6a6d16234d488c52ea4dad729ae85f4d6dd1704fb5b79112520a8dac7c5075531d69205dbfe60193ec3841451c0c8f1f512fdb6fd4609a25d9828871ae58700a360c7c9d64897c20b6bb16210907988b21e2c3cecbe52b30caec0823a3e2d3872503b7d5c1820fb13ff8c963387319bb6689d743cf2c4e1be352f018826ff0d8d1abeb87ae431732c372d74de710df41ec574e363ec4645c4d95384b2fac96779a99c56e90adf412f0204f081cc30f20ad4f5efe338f77d7c7d10fc98f3b5bb678dde567135daa2e03d4bcf0e7a450adc12d4be5c79374e3654104aaac461835aecfdd82bdec4a8ceec593d62f7e6548abf849a55999c7e880db8787b6d7a5a33d849c4580c64a91937dcae6f6c1c373aee3ce2610f6341823e5d19f7a9a2d2ba252f019bbca2e812c0478e5de47b7b4c8f99edffffb1209ef6feba8371522b9c19aa4b5a5f0b1768bd5c611814610aedd3c620035cb505cb9bd07fbf0930bd6d3c37b7339aaa650237906737e348d5d5ce18a7bb8ebb03117b48dff61683f1532081e0a4d8be03f1f1ddc5249eedd3b4551a6d115e62848153020dd7d03a36a60e4b99198966b6ed753476bc2fd54e25e6e0df41472018fe9dc289c0ce689bf5f3614895f1f527b5d9c6020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0603a2a4040103ffffffff0210f19a3b000000001976a914a4f60943699e0939f0d5a6029c4e5d398eb5fdf888ac80b2e60e0000000017a91489169b066f07b60f31ca11628b7964313113687e87000000000000000000000000000000000000000400008085202f89000000000000b6a404001027000000000000012783c73a247606f857b41cc1bdaf4e74669c362b1e7c8fb0068f54ab54bf156cdcc0cb05ce91faf3412053254d13322e3d2065c39ba2ae6eb64896791561035418ba0523eef4bc2c04520f170ae5e6a875ccc389fa471393537f19ed13f531ff4306a367086cd90c67d0640ccaa8b4a32ca7743118105fa03f292d1f01e0ea8882e5cf6d139a6cbcac9ca888ef357bca8503d33199423b6f57a88cbc5dc3a226ba535fde1fa74615dbda11a26b36b724997d6922129926665754b846e187329624138e70d6c9c3a8001430194a6ae895c7443db9c3ab0e122db2c41ef27e1cae1165352f5a665e0e8cd08cd4d4eb18bdd39a848baf6d500eb65d4105cec2ea8e34d16b511d5066daf5d394622644a70a8202a62096f749c8884a7b7ddeda1c1c25b7f49ac187961cb6182f91aaedeab011fcc54e645652c88aa0e11963f2afc823c4851026d5410fa253025f2948e44e439ed4af3c34279b8e0fd1f86913f2d15347ba47d52e279a043f32e188c2242d7779d4ce7d072ef0e5da9a53d9ad44030268bdeb98a85e1a094aded6cfcf4e7d3dc2b9fd5acb278eb5459a506205a831cd5847eaaae3937699a2a9bb76e5abbbde0dbf404ffc1c30023271748b37d91c2d527e00667c591a8e7c362d563f6d3f27681d278b6ceb84eae510ca894daa0517f13638cc0cd1aff20c13ce8ee11b7add11319cccc63791cd9515c6499c32e466db89c7faed6e323688b5eac22d81c6033a4b972428546f6742f45ed5ff8d311c5c6a4c0608394140e048868ef9884f35a8bed165370b52d655340c10742ad00d951532ff8811e45a562cfbd81266322e37c497110ede4ad5716c832970dd2e56c84b2709573b7a8531bdf56b24c3a3be02c92696ae9382a94536c60767bd16574f57a1fb1c5ae7891d951dabab81e49f4b48719d925a4fd9f7c86ea42d02ff8ccbcfa7dadaf0d3973bd3f34aa3c701fd5240970c57a4cdf828d242be24cf2a66e7b1035973b8e960c6f47c1fb12a1c3b6d528791b242762c4490e9c1f10c31c676e4ed90d1c3cf33a0b3f25a62a2be13a80abe6d8e0f3a5461103e6978776d7d762e4e43205a0b10ab50793cbac5773fe95de575ae502d7633c33a424094a281c9b7a5c0362af2fefe61e56246f40270a748755379d7c3aa4de19b298bc58e8e4783ded7e242d78397e65b617e72089a13ca6bd7eb9817760dc6122974f4fe5f4862ced23b63e8d1f928253992f0a8c0ea2763ffd94c072a338af1ac7c4d1871fc01c9c43a6d730c2474525b2bc9ef2c680d6842a13bb13b46c4580a12739ce6fef4a93735efbeb0002cbd5c5bdf4bf0ecb9a5dbe82137a718408769a00c457286f601f6c6391b7e7ec0642fe59617e5ff344d8faf53833c2417beb2dfbdbf7c122ff7148358a060256ba349d2dcb6be6ca62e98d1ceefda45af5afdd8c7adf4e62d94aa1999593accf55374ef4fb0142a29b94fd3303e962466be26b2d6a8502faae9280706f85c07991b71543981c2097f1a3cefaac0fce519d88d8d974be0da1188f41db7d639ce167317cc42db7d031e5bf3211eefb5ef888594d3528676e0a156d61d228ae98dc165a2c15edbbbcde02ccd862eff9c24895a81f31f019f9df9b14a8030d877664f7566f90d96b7daccef283fe66d871ce2c8af9960ae6efbb933c499bcab1fd18b5dd1f0432493bf770459c6b8c7e0e0897ed35085cf438c96afd8b9544f8165f5e2b2f8aa785ba33de6160e4bb2c5b9caf50652147e037db45a8ecd623ab67cfd10c5390e69666747a9b285d8eaa87b3441abcc94fa5c9244389e8b26e20f283b7a9b8e3865704b689443d94eefe522fb6072e6031d8428cb13e11fe7432bc14ba446ba6954ada17a15c9c699a71c8a18c8eb8279601b2910994778de631680c3a883d947f3b0cce68d8e500d7a787b8d04dbcca231069c9db5600adb4b4c3960e2b98aeffbe666b3595038a4585d38701ff9d0737cf1eb91f2e90683de2f41d8235c042fb7b5438b6f6eed1218b8eb57237e10593d330d49d5fe693b17a87d7b01eda59dcdb6dd5a714f56528574405894193f3585690c4ec2124213cfdc37c70d92f3f58a250ae3309f7d3ba137902f6c99ac049b9ee641979656c27f5ce57357a5d30f17963207cda6b83ded6d1d3de4e228a6ac2ed5714900b8c4b797abe72cdf24e35ff85c0aa7b487e12e14850efc401dc5897e2ebe49c898ddd360678ce883ebbb155a7253bd0a7c57f5acce35610d7c929ea1d41ca8958b5e548451ff51468007112b1c65f72bd3933d4e1528090b0e842b677965f665db02511a8c7bf7f93f9853dd456c89661f945bc65b93b8752a546798d3b09040a93555d8a5c5cb7a63dc79e9f4e8479fea07ace9d0dc717874514b14a884906a6440513f7e26fe29f7143f1bfc2637ecde22d9fbc795febdb2a654325f625d595e4e0b629b1b640721181f1bfe1bf19be2513f4f8f29d3e943ff97c6b3c9bcdef16d37dfa188243baddc585d4f7490611b66665ec03f6de2e5e9ea62918722bd20902bd0a6a7da8bbe976be89ec925de50268ca22d9659eb4edb790b1529b90abfe77ff81e601652fa27a3ab6157ed26ec235faa5ad4ef349231af61faaf1fbde3dd19ff219b46f4ebbaa09eaddbe7eef170704a523495d93491f9694f14d50f417c403811a6d8ff8cc3a5716b5c4342c26bae4565de958e8d062dc62a753fe52d4ac3cf7b192b2a10353e04297a3e311b3b7e3e5ce3101481cc656d114c628bbb53f4a665537ba1e81df87845176f63c9726cc6239ce6950875927eb41529fbd9e0426dc702874d41674466e8ca1409df471d6de22553f169f80ad7e86770f86b5ce6a128f133227f58e714eedffcd18c890418539270594da17242d6df728d9759ece3a277e0f674c3299ca1556411e5cf143ce2c2bc2a665c8559db1b2631e0b178b693c777755dc150c09891da7109aa188364295f23f6fcafca60986658e17c675b642b5338851cc5947313c6911e44c5db87c46eb6e520e542b6b1c4d47ed7a41195dacecdaca3f6e0580621dc7874b0309dc70a34db1c61d37d5a0fb5cd68fbb009ed8b1b81f39ba366750a5e2a4438a9008166c541de4f8e27792533aeb2a7a2c4db86216c7d53c4c520eeb18e8673e1b0461bd169355dfef59eb28f883c86f982be501730f732399c4148d2aee30c217a5f75087265f64e8e5d098b64291b4c00395384ca762b1c63e3509b180efc2ae837c83feb213d72103bd80ea928270719f572d75173b18b9810e62ad76585e47181911dcd4f33b2cc37213de985d13401"
+}
\ No newline at end of file
diff --git a/tests/blocks/zelcash_mainnet_100000.json b/tests/blocks/zelcash_mainnet_100000.json
new file mode 100644
index 0000000..c2ca509
--- /dev/null
+++ b/tests/blocks/zelcash_mainnet_100000.json
@@ -0,0 +1,15 @@
+{
+ "hash": "00000006521f5900d13e126636749f49c1c8f0b3d6df985ccdfea90f45610a06",
+ "size": 3598,
+ "height": 100000,
+ "merkleroot": "689c4a80dd0a3812e770f5a7bb21adcbb0069c742a480494ce8850eeccfb4986",
+ "tx": [
+ "2a90896956c1a479a8f9eff304e236411fa0b5380469c3907802dbdff44a6272",
+ "719cc32c99c27e536104169810c2c245334353f98776e9c8c358a783d0b5a73f"
+ ],
+ "time": 1529272800,
+ "nonce": "0000000000000000000000008a36a23b54ffdb9e1482518181d39ca2d08a2da2",
+ "bits": "1d113a2e",
+ "previousblockhash": "0000000378c42a7e9adfecc053d34c2591ddd57489fa27712b429ff2a938d419",
+ "block": "0400000019d438a9f29f422b7127fa8974d5dd91254cd353c0ecdf9a7e2ac478030000008649fbccee5088ce9404482a749c06b0cbad21bba7f570e712380add804a9c680000000000000000000000000000000000000000000000000000000000000000e0d9265b2e3a111da22d8ad0a29cd381815182149edbff543ba2368a000000000000000000000000fd400500a5c56751961c076d71c2d7b5a4c15e3d55b7b45d1316ac0e9d0c8469debd15dc7858037e05ae1926f506c8031fe4919ac7baa25071b31246d43a323652e41cb96172a49ba716fd8d522baf39d5df083fdfe677106099cb21db2e994a2c84cb3c7d4f99c72118bd941b836ca6c98d04d9071fd2e2aad8b442458ebbb8da16346aead2206b1bd849f297906a54421d55f28a123a7d4da58a5f2da725c2140b5a24a6d967fe8f5d6a0799f73e525ee759b2a5a284ec5a9229b29d7bd16418b17c884a98c74f9d3634431d6597b9df351182f316a11bdb3d1eabaf908a4a41b178a0438f7f9f48c71f1efd26860a5d03029d53284dcb4555d077b5d56111c965dd1e186b8760b938d04469849761ec5c21804f16359b0bdc66ed80cfd890127e8d9aaaa0fc786f12ee7d90a797be25e02323e1d2feea72861e7c110d18217bd00df44e51e270122b6250dbd6eaa95db52900ca4588c2069155eaa6031b35530d8d0a035a14370b0575deb950b3f49693c198316eff998f6a4d21c511d3faabc2ecd5137edcaa8cf6e4fa16ae89d6b9d21a1feec7fec9a4d96f0ab3c5f773edeb3130bb12bc07d2407eec6ca783c8b731f6ce66850649323cd47423ef0bd7201d86d0ef15345cc76c34ededb9f696e5088d6c362c96e0df14d090efe37690b2a9fdfc3b9814312370778aa94368cc5209e99e78164e4c34b2b602bacc65a39ac4a1e52103a83fbc90131089d8fc4317913c47d021dd75537d92c2082265ce25a37639e806df12ac00d4d485c1a2e0ad6789cd02bc01367c943e4bbeae0f2268cd360774d2f17c473de7aa9adad0072fb1814647d2a3bfd0455e2a5449d5711dced199584a9aff6719e1b2dd8e0d109d7ae82bcfb8fec8444da6f36def595bc9be25a5d67f74a5fb4a283eac567837b4dac66c7019f18cea6a8ee1cc82d3f67cc02801a496af1343519b4a5ba36ea0ed292a08da37e04627069a4707134713d837736c4fa33bc5b702bb5a8402692106e806cdca84b8463311b75517ac7c5e93211aefa5a33fa2a2698828e94e5079ea66ae01bdab2a049ec9dbfa65194970825225645f3e1cd04b1ca34a215a9ea78511145e928be2e53cf5f7993d061d8b3106f12abd05cb00f3a6b3497f3ddc91f6c3c617df7a0fe8ac3d8ed28a4719998b5770f7332f812b1f17640776319a09dca815c7a3b2f074d50831785c98fe9310e7d92c26092624923a654f48fd4b7faf951dc38d0f2faaaa89afde3fcac9653279f1ef0d68cdf749372bfbc9f0bc100865e31c88813ff9e90a72fb37bbfc0ad94f4a0e7139fb9484f0e440da1ed92bd0d177e1191753dbf270a8c9ef0e04fe72bcaa42d3795f4da9101aa0b8df63494fa68ee19518a0277cdd753e4d7c1fddbe57a3655d372b2774a1d6cef2ce5a093caf6a02342570f9549d96cd78527ad7f90b1a24ced46d271c0a060b7c6404b99dff8314b570c02e7e62b6cf8131e375f897b0981fb8c7834e6651bb0aa367f7d6c146da37115f5c8ad8f80f04bc882dc02e0c91dbb808082b6c81c9ce52030f7fe5c8bf55f4a32d75fc36a00f118a3679e86c17a0c927db2ee71962e5625b4bff0cbc0e753c9e33754a905464afe2f32a665356448814709408ea921a07b28574968e2a4a3acfabdfb83e0507bf67cb0e500f2e1f04291e358b45b9f79602e11c1bbeb21589632c59bab3635dd66bea649b7530700592134b580be816c6e44d3cd6f9520fb3413eba911ba0244d961bd87de55cf237609a3789f35231b2600e92196a7cd22ed3d0737279ce4fb8a54c1157f0e13aaa9726b46897db589f954778c2216dd1ef37efb114e9814e6fed82cdcd8d8686bd4e5ed6e9cfd820574e1e3b8617d5bf932022d51efdf32899852ed7e6ec0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff3003a0860100636f696e626c6f636b6572732e636f6d202d2068747470733a2f2f636f696e626c6f636b6572732e636f6dffffffff0280042175030000001976a91494366c530bdec75b6b4a6c647e902069f06cf6c188ac80d1f008000000001976a9142a1efd9d0d4c050df4c88822ed349bfe13fcaace88ac0000000002000000000160b62075030000001976a914c17d10001ecf9fcb20327d96869383217d2f226088ac0000000001000000000000000070dd20750300000078d5e6b10dc8f65fe4627954325df8e2f7af95845e40c4ff34c6d18a20e9db85c33a12a613c78aab75851c0deba2046d3ca0b0980ce4acb546e9c1bbb2c77f3024c853f1689045d3f989f72fda77a91e0c597dc8ad93222a91ffb27e9302b043904b367b917ba27956c819af4d7938e4cc9cdf0949f4705822ff6509ea31a69e676ab31c97c8383f35795a5f40944bc632d8fcb60f0d39f6436cba0463a9824cac60777f94ea3c096411e913c12c576afabd6bf5e9e988808c3347e6def20d1cb6c870ecf0eac01330f5f0430608437ea0b86f3b74d40b7ff8d98ef9ac0ba030dbeebf478de58c2f9805e5643f0fc095fd6578774ea655d1a49bf492aaba7d981441ad1ac73febac08bcab5c3183497afb2d3533c1ea0acde97491d118da0fff021e4a7399f4a95a2b12a3566de942935f6f3979e9dbbe7f6bdfa47aeb3a1db10902198cf90087a388c7757d4d5006040239e1cc3ea90e9d16d15563aef13d60a6700a07e801ca9b20ca83643cd4ec66f150757b76ae22a6016a09298a3d6975655cf9ddcd48650fb9e4d7a49679c57c076af409b5207a34a6f0a60304cd08e7e7eaff031ddb0e357c99439da7915db192bc43207e1dce037c1ed9f0756a0c52de5241060230333e3a4421495fef12a074c02db43393828a25579427dcf307b768faf09733022775b6cc390a84e06bf6a096d2d0055b76264059aca223b84a959da033470ce1030c863b7fca1edcf83d5ac8cf769903027991cd181fdfb2c2649534c37663e9ca030798fc266d3b9099657c49dcfcf7f23ea4661c3d44956a469c78a927a1bbebc24c3adad68d7c11b997acc57ed89eedc2666799282d0e7ac8e97ed1c57f20c28b19c9306ba78e202a860a37ef56ffca54898c73c2cd08ed6f289729a10844f1e250ac7b470cecac85ae778009461fbce9e384fdea5f0518f759947a3f60a24f725039b5bf3335c8bf82a22975e30686cf7a67eacb48957cbeca17883800c597e877f6995a91a944d4578c31813efcdb54c710cc2313717bcde01edd9d671e68591f4d827729ee9f3bc127ed8329a5f71c032b3644d68823078b26c8ecc1c034286dc4278402b45d791685663effa7bd1975d3b4da3c0fd31589c8340c0b7fb1d397cae2c1d33a7c973d2db585aa98b25c6c023a626462a85d5dd0d48f446471a8c3d2d0574c30b31ff38c647b2f34e62476aff26a543cac7ed37e3cc5a9e384a47ca2d75447d0483b91e34cca6d0bc9a8fdcb05b464a4def7069a989b4327b503d26c7c59f83584b726940b4210c5815c85c3de85051905b7c1cfc96e550b5dcaba68e27879b4b2bcef391d8e4decc6dbcf13b8d348c96502cd4d74778f2065b7341ae196d024a2075ea12c18769b0758acb47c87c24c463e27638e0ae146e624ee13eb0a9a292a05742242f3c5eebb4cc62b66aa2773ff1b87e4975052ffa51a6b4090ec71f38df2f0826fe9c303233a51961995ecd7ec45bbc120c65cbe5bb7a5cb6344f7d82d0f3568794b5b263a63b77eacf59bae8335acb3de563c65bed2645b6a0fada48d9aeb2945f25a6f4d757440f09af182dc583e3ef5e7e1048aafb270a776de19398e3ac9e0054965381e1da2b1dccdb53f8d61ac2a99f604a939e5961141d05fd9dfdb16213a2ba6c5b860d7348c67de0fecf842a980de3dbed5efa31146cf038fc7ad274c7093840ce5193eb8865b850caf6d564ffa6160f9e5ce188bb5e957e772da25a278c22b5680c9fb5bc52de0f425e2ea83bd397bf11475464e3acadcbc1b8504a94a4cbc731b6f3d10eaf9568c81c85749ad6fa2dbbd91a6016c52457ea2b8f6e5f7cb661b58e9faf3bd4240a094702db63c4c98e277524869fa31f91147aab17eaef8a16e63f1a78989917903c9cc54419d42f5f111e7abe55a1ea5ab3866216f68de55f4943ad01dd8911edbf1995f82f0b7c068dbc3d48a2bcccf5b19d45e8d7fbfaf4084f638a094bbd97d03a224577eb904396943f6f0e29f2db356de735e06b62e27c121a46c80280eca47d41a89e1aa1043856f99b9c41c7aee4707b12c573d8b690cdb7b3be2f1219fcbfa75f3d458bd51ac3107b681d16b91c08f2a19c917563d708b1c32ba65aa6c66e91fd7d659ba8e62ea877d334d7a4be2c6873c093e53974a05ff3b12608caa570e2c48b7dc97817cfa18492749fc2855c7ba75b20e3e20baef72424b83128c2902633848b5ad076f84aab5b4273844673b6ef48f8e6e340efd549cd46a33b3f2315d9f21fe80100fec6a24c61233692fd053412422e402a4c2254f056f5cb447a558fc06ea64a890df6384dd46723bd91bdd38ef3a3ab1de09e5482ca73020ea075e7bf97e5fe0dda9eb13953dea103289715bfb9d820497838accec2d53c0e225c4fce5f3c987e8e4c7333e4f5c882c5fe6dff3c4d7b7799f50a3da8472d9274e0ecce43049a8d0938e79410d790e84278858271407b3c9556087b3bf131d0cf3c78ab3570dc85e85ddbe7d51e976ffccd7d94928b3252c29b8fc14154b0042ec623861b7e3b373059742540945f74984cc425698e1e1b248080a49a4275816f6c9de5c31f6035135ca86ef69df53cd7b91a843eb891c3a8ae3f5535686e5144499d1b798b847cf14cafcc1fbaaf14951af986eac84f32ca10e"
+}
diff --git a/tests/lib/__init__.py b/tests/lib/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/lib/test_addresses.py b/tests/lib/test_addresses.py
index 173f7f2..b1d6bea 100644
--- a/tests/lib/test_addresses.py
+++ b/tests/lib/test_addresses.py
@@ -67,6 +67,8 @@ addresses = [
"ab72728952c06dfc0f6cf21449dd645422731ec4", "eb3a3155215538d51de7cc"),
(coins.TokenPay, "TDE2X28FGtckatxuP3d8s3V726G4TLNHpT",
"23b5dd9b7b402388c7a40bc88c261f3178acf30d", "7c7bdf0e0713f3752f4b88"),
+ (coins.SmartCash, "SQFDM9NtRRmpHebq3H5RA3qpGJfGqp8Xgw",
+ "206168f5322583ff37f8e55665a4789ae8963532", "b8cb80b26e8932f5b12a7e"),
]
diff --git a/tests/lib/test_tx_dash.py b/tests/lib/test_tx_dash.py
new file mode 100644
index 0000000..0cf21dc
--- /dev/null
+++ b/tests/lib/test_tx_dash.py
@@ -0,0 +1,464 @@
+import pytest
+
+import electrumx.lib.tx_dash as lib_tx_dash
+
+
+bfh = bytes.fromhex
+
+
+V2_TX = (
+ '020000000192809f0b234cb850d71d020e678e93f074648ed0df5affd0c46d3bcb177f'
+ '9ccf020000008b483045022100c5403bcf86c3ae7b8fd4ca0d1e4df6729cc1af05ff95'
+ 'd9726b43a64b41dd5d9902207fab615f41871885aa3062fc7d8f8d9d3dcbc2e4867c5d'
+ '96dd7a176b99e927924141040baa4271a82c5f1a09a5ea63d763697ca0545b6049c4dd'
+ '8e8d099dd91f2da10eb11e829000a82047ac56969fb582433067a21c3171e569d1832c'
+ '34fdd793cfc8ffffffff030000000000000000226a20195ce612d20e5284eb78bb28c9'
+ 'c50d6139b10b77b2d5b2f94711b13162700472bfc53000000000001976a9144a519c63'
+ 'f985ba5ab8b71bb42f1ecb82a0a0d80788acf6984315000000001976a9148b80536aa3'
+ 'c460258cda834b86a46787c9a2b0bf88ac00000000')
+
+
+CB_TX = (
+ '0300050001000000000000000000000000000000000000000000000000000000000000'
+ '0000ffffffff1303c407040e2f5032506f6f6c2d74444153482fffffffff0448d6a73d'
+ '000000001976a914293859173a34194d445c2962b97383e2a93d7cb288ac22fc433e00'
+ '0000001976a914bf09c602c6b8f1db246aba5c37ad1cfdcb16b15e88ace9259c000000'
+ '00004341047559d13c3f81b1fadbd8dd03e4b5a1c73b05e2b980e00d467aa9440b29c7'
+ 'de23664dde6428d75cafed22ae4f0d302e26c5c5a5dd4d3e1b796d7281bdc9430f35ac'
+ '00000000000000002a6a28be61411c3c79b7fd45923118ba74d340afb248ae2edafe78'
+ 'c15e2d1aa337c942000000000000000000000000260100c407040076629a6e42fb5191'
+ '88f65889fd3ac0201be87aa227462b5643e8bb2ec1d7a82a')
+
+
+PRO_REG_TX = (
+ '030001000335f1c2ca44a1eb72e59f589df2852caacba39b7c0a5e61967f6b71d7a763'
+ '3153000000006b483045022100b2d457bbe855abc365a7db9c8014ea106fdb6dae6327'
+ '927fe81dfbdecf032b260220262e7e6c28899cd741db55c2e2ec35ed849cf99e78e36a'
+ '70c2ec3dac3c2ef60a012102500859b69a4cad6cfe4cf6b606be25b367c562b3be9a24'
+ 'b06d60c7047ee18fa2feffffff473ac70b52b2260aa0e4bec818c5a8c71d37a1b17430'
+ '75823c8e572ad71938b0000000006b483045022100fa4d57cdeb61f8ff1298fdc40256'
+ 'c68dfce320d44f584494c0a53233ddbe30a702206a50aaa245a6097d06c790fb1d7a37'
+ 'ced1622299c0aa93ebc018f1590d0eb15c012103f273126b24f755ab7e41311d03d545'
+ '590c162ea179421c5e18271c57de1a1635feffffff4de1afa0a321bc88c34978d4eeba'
+ '739256b86f8d8cdf47651b6f60e451f0a3de000000006a47304402202c4c5c48ac1d37'
+ '9f6da8143664072d6545d64691ce4738e026adf80c9afab24f022053804b4166a342da'
+ '38c538757680bebdc7785ce8c18a817fb3014fdaeec6d3bb0121028e99f6bc86489a43'
+ 'f953b2f0b046666efd7f7ad44da30f62ed5d32921556f8c5feffffff01c7430f000000'
+ '00001976a914c1de5f0587dc39112a28644904b0f3ed3298a6ed88ac00000000fd1201'
+ '0100000000004de1afa0a321bc88c34978d4eeba739256b86f8d8cdf47651b6f60e451'
+ 'f0a3de0100000000000000000000000000ffff12ca34aa752f2b3edeed6842db1f59cf'
+ '35de1ab5721094f049d000ab986c589053b3f3bd720724e75e18581afdca54bce80d14'
+ '750b1bcf9202158fe6c596ce8391815265747bd4a2009e2b3edeed6842db1f59cf35de'
+ '1ab5721094f049d000001976a9149bf5948b901a1e3e54e42c6e10496a17cd4067e088'
+ 'ac54d046585434668b4ee664c597864248b8a6aac33a7b2f4fcd1cc1b5da474a8a411f'
+ 'c1617ae83406c92a9132f14f9fff1487f2890f401e776fdddd639bc5055c456268cf74'
+ '97400d3196109c8cd31b94732caf6937d63de81d9a5be4db5beb83f9aa')
+
+
+PRO_UP_SERV_TX = (
+ '03000200010931c6b0ad7ce07f3c8aefeeb78e246a4fe6872bbf08ab6e4eb6a7b69acd'
+ '64a6010000006b483045022100a2feb698c43c752738fabea281b7e9e5a3aa648a4c54'
+ '1171e06d7c372db92c65022061c1ec3c92f2e76bb7fb1b548d854f19a41e6421267231'
+ '74150412caf3e98e9601210293360bf2a2e810673412bc6e8e0e358f3fb7bdbe9a667b'
+ '3d0103f761cc69a211feffffff0189fa433e000000001976a914551ab8ca96a9142217'
+ '4d22769c3a4f90b2dcd0de88ac00000000ce01003c6dca244f49f19d3f09889753ffff'
+ '1fec5bb8f9f5bd5bc09dabd999da21198f00000000000000000000ffff5fb735802711'
+ '1976a91421851058431a7d722e8e8dd9509e7f2b8e7042ec88acefcfe3d578914bb48c'
+ '6bd71b3459d384e4237446d521c9e2c6b6fcf019b5aafc99443fe14f644cfa47086e88'
+ '97cf7b546a67723d4a8ec5353a82f962a96ec3cea328343b647aace2897d6eddd0b8c8'
+ 'ee0f2e56f6733aed2e9f0006caafa6fc21c18a013c619d6e37af8d2f0985e3b769abc3'
+ '8ffa60e46c365a38d9fa0d44fd62')
+
+
+PRO_UP_REG_TX = (
+ '0300030001f8f9a27ca1c727fb971d45983c9a08a0bbd76753f8eb7913130c72d94218'
+ '8d32000000006a47304402205d530dc4e9e34b44fdf58f06fff0c225d80490be2861ad'
+ '7fe5fed7e62b48053b022052a78b5beaccc468b7fdb80e47090cb54c351aa9aa82fa7e'
+ '9b15b82d53b5f15a0121028106cde1660d2bfcc11231dfb1a05b60ded262d59e5e021a'
+ 'a3a814234013f4e9feffffff01c60c0000000000001976a91452a23d803da188cca952'
+ 'f9b7bc94c47c6fd1468a88ac00000000e40100aeb817f94b8e699b58130a53d2fbe98d'
+ '5519c2abe3b15e6f36c9abeb32e4dcce00001061eb559a64427ad239830742ef59591c'
+ 'dbbdffda7d3f5e7a2d95b9607ad80e389191e44c59ea5987b85e6d0e3eb527b9e198fa'
+ '7a745913c9278ec993d4472a95dac4251976a914eebbacffff3a55437803e0efb68a7d'
+ '591e0409d188ac0eb0067e6ccdd2acb96e7279113702218f3f0ab6f2287e14c11c5be6'
+ 'f2051d5a4120cb00124d838b02207097048cb668244cd79df825eb2d4d211fd2c4604c'
+ '18b30e1ae9bb654787144d16856676efff180889f05b5c9121a483b4ae3f0ea0ff3faf')
+
+
+PRO_UP_REV_TX = (
+ '030004000100366cd80169116da28e387413e8e3660a7aedd65002b320d0bd165eea8e'
+ 'ba52000000006a4730440220043a639f4554842f38253c75d066e70098ef02b141d5ff'
+ 'dea9fc408d307fce1202205d5d779f416fbc431847d19d83ae90c4036cf9925d3c4852'
+ 'cdd5df25d5843a48012102688d37c6d08a236d7952cdbc310dcb344ddae8b02e028720'
+ '1e79fd774509e8abfeffffff01570b0000000000001976a91490c5ce9d8bfefe3526d8'
+ '538cd0ed5e5d472c992a88ac00000000a40100b67ffbbd095de31ea38446754b6bf251'
+ '287936d2881d58b7c4efae0b54c75e9f0000eb073521b60306717f1d4feb3e9022f886'
+ 'b97bf981137684716a7d3d7e45b7fe83f4bb5530f7c5954e8b1ad50a74a9e1d65dcdcb'
+ 'e4acb8cbe3671abc7911e8c3954856c4da7e5fd242f2e4f5546f08d90849245bc593d1'
+ '605654e1a99cd0a79e9729799742c48d4920044666ad25a85fd093559c43e4900e634c'
+ '371b9b8d89ba')
+
+
+SUB_TX_REGISTER = (
+ '03000800010931c6b0ad7ce07f3c8aefeeb78e246a4fe6872bbf08ab6e4eb6a7b69acd'
+ '64a6010000006b483045022100a2feb698c43c752738fabea281b7e9e5a3aa648a4c54'
+ '1171e06d7c372db92c65022061c1ec3c92f2e76bb7fb1b548d854f19a41e6421267231'
+ '74150412caf3e98e9601210293360bf2a2e810673412bc6e8e0e358f3fb7bdbe9a667b'
+ '3d0103f761cc69a211feffffff0189fa433e000000001976a914551ab8ca96a9142217'
+ '4d22769c3a4f90b2dcd0de88ac00000000960100036162638e7042ec88acefcfe3d578'
+ '914bb48c6bd71b3459d384e42374e8abfeffffff01570b0000000000001976a91490c5'
+ 'ce9d8bc992a88ac00000000a40100b67ffbbd095de31ea38446754e8abfeffffff0157'
+ '0b0000000000001976a91490c5ce9d8bc992a88ac00000000a40100b67ffbbd095de31'
+ 'ea38446754e8abfeffffff01570b0000000000001976a91490c5ce9d')
+
+
+SUB_TX_TOPUP = (
+ '03000900010931c6b0ad7ce07f3c8aefeeb78e246a4fe6872bbf08ab6e4eb6a7b69acd'
+ '64a6010000006b483045022100a2feb698c43c752738fabea281b7e9e5a3aa648a4c54'
+ '1171e06d7c372db92c65022061c1ec3c92f2e76bb7fb1b548d854f19a41e6421267231'
+ '74150412caf3e98e9601210293360bf2a2e810673412bc6e8e0e358f3fb7bdbe9a667b'
+ '3d0103f761cc69a211feffffff0189fa433e000000001976a914551ab8ca96a9142217'
+ '4d22769c3a4f90b2dcd0de88ac00000000220100d384e42374e8abfeffffff01570b00'
+ '0000a40100b67ffbbd095de31ea3844675')
+
+
+SUB_TX_RESET_KEY = (
+ '03000a00010931c6b0ad7ce07f3c8aefeeb78e246a4fe6872bbf08ab6e4eb6a7b69acd'
+ '64a6010000006b483045022100a2feb698c43c752738fabea281b7e9e5a3aa648a4c54'
+ '1171e06d7c372db92c65022061c1ec3c92f2e76bb7fb1b548d854f19a41e6421267231'
+ '74150412caf3e98e9601210293360bf2a2e810673412bc6e8e0e358f3fb7bdbe9a667b'
+ '3d0103f761cc69a211feffffff0189fa433e000000001976a914551ab8ca96a9142217'
+ '4d22769c3a4f90b2dcd0de88ac00000000da0100d384e42374e8abfeffffff01570b00'
+ '0000a40100b67ffbbd095de31ea3844675af3e98e9601210293360bf2a2e810673412b'
+ 'c6e8e0e358f3fb7bdbe9a667b3d0e803000000000000601210293360bf2a2e81067341'
+ '2bc6e8e0e358f3fb7bdbe9a667b3d0103f761caf3e98e9601210293360bf2a2e810673'
+ '412bc6e8e0e358f3fb7bdbe9a667b3d0103f761caf3e98e9601210293360bf2a2e8106'
+ '73412bc6e8e0e358f3fb7bdbe9a667b3d0103f761caf3e98e9601210293360bf2a2e81'
+ '0673412bc6e8e0e358f3fb7bdbe9a667b3d0103f761cabcdefab')
+
+
+SUB_TX_CLOSE_ACCOUNT = (
+ '03000b00010931c6b0ad7ce07f3c8aefeeb78e246a4fe6872bbf08ab6e4eb6a7b69acd'
+ '64a6010000006b483045022100a2feb698c43c752738fabea281b7e9e5a3aa648a4c54'
+ '1171e06d7c372db92c65022061c1ec3c92f2e76bb7fb1b548d854f19a41e6421267231'
+ '74150412caf3e98e9601210293360bf2a2e810673412bc6e8e0e358f3fb7bdbe9a667b'
+ '3d0103f761cc69a211feffffff0189fa433e000000001976a914551ab8ca96a9142217'
+ '4d22769c3a4f90b2dcd0de88ac00000000aa0100d384e42374e8abfeffffff01570b00'
+ '0000a40100b67ffbbd095de31ea3844675af3e98e9601210293360bf2a2e810673412b'
+ 'c6e8e0e358f3fb7bdbe9a12bc6e8e803000000000000a62bc6e8e0e358f3fb7bdbe9a6'
+ '67b3d0103f761caf3e98e9601210293360bf2a2e810673412bc6e8e0e358f3fb7bdbe9'
+ 'a667b3d0103f761caf3e98e9601210293360bf2a2e810673412bc6e8e0e358f3fb7bdb'
+ 'e9a667b3d0103f761cabcdefab')
+
+
+UNKNOWN_SPEC_TX = (
+ '0300bb00010931c6b0ad7ce07f3c8aefeeb78e246a4fe6872bbf08ab6e4eb6a7b69acd'
+ '64a6010000006b483045022100a2feb698c43c752738fabea281b7e9e5a3aa648a4c54'
+ '1171e06d7c372db92c65022061c1ec3c92f2e76bb7fb1b548d854f19a41e6421267231'
+ '74150412caf3e98e9601210293360bf2a2e810673412bc6e8e0e358f3fb7bdbe9a667b'
+ '3d0103f761cc69a211feffffff0189fa433e000000001976a914551ab8ca96a9142217'
+ '4d22769c3a4f90b2dcd0de88ac00000000aa0100d384e42374e8abfeffffff01570b00'
+ '0000a40100b67ffbbd095de31ea3844675af3e98e9601210293360bf2a2e810673412b'
+ 'c6e8e0e358f3fb7bdbe9a12bc6e8e0e358f3fb7bdbe9a62bc6e8e0e358f3fb7bdbe9a6'
+ '67b3d0103f761caf3e98e9601210293360bf2a2e810673412bc6e8e0e358f3fb7bdbe9'
+ 'a667b3d0103f761caf3e98e9601210293360bf2a2e810673412bc6e8e0e358f3fb7bdb'
+ 'e9a667b3d0103f761cabcdefab')
+
+
+WRONG_SPEC_TX = ( # Tx version < 3
+ '0200bb00010931c6b0ad7ce07f3c8aefeeb78e246a4fe6872bbf08ab6e4eb6a7b69acd'
+ '64a6010000006b483045022100a2feb698c43c752738fabea281b7e9e5a3aa648a4c54'
+ '1171e06d7c372db92c65022061c1ec3c92f2e76bb7fb1b548d854f19a41e6421267231'
+ '74150412caf3e98e9601210293360bf2a2e810673412bc6e8e0e358f3fb7bdbe9a667b'
+ '3d0103f761cc69a211feffffff0189fa433e000000001976a914551ab8ca96a9142217'
+ '4d22769c3a4f90b2dcd0de88ac00000000')
+
+
+def test_dash_v2_tx():
+ test = bfh(V2_TX)
+ deser = lib_tx_dash.DeserializerDash(test)
+ tx = deser.read_tx()
+ assert tx.version == 2
+ assert tx.tx_type == 0
+ assert tx.extra_payload == b''
+ ser = tx.serialize()
+ assert ser == test
+
+
+def test_dash_tx_cb_tx():
+ test = bfh(CB_TX)
+ deser = lib_tx_dash.DeserializerDash(test)
+ tx = deser.read_tx()
+ assert tx.version == 3
+ assert tx.tx_type == 5
+ extra = tx.extra_payload
+ assert extra.version == 1
+ assert extra.height == 264132
+ assert len(extra.merkleRootMNList) == 32
+ assert extra.merkleRootMNList == bfh(
+ '76629a6e42fb519188f65889fd3ac0201be87aa227462b5643e8bb2ec1d7a82a')
+ ser = tx.serialize()
+ assert ser == test
+
+
+def test_dash_tx_pro_reg_tx():
+ test = bfh(PRO_REG_TX)
+ deser = lib_tx_dash.DeserializerDash(test)
+ tx = deser.read_tx()
+ assert tx.version == 3
+ assert tx.tx_type == 1
+ extra = tx.extra_payload
+ assert extra.version == 1
+ assert extra.type == 0
+ assert extra.mode == 0
+ assert len(extra.collateralOutpoint.hash) == 32
+ assert extra.collateralOutpoint.hash == bfh(
+ '4de1afa0a321bc88c34978d4eeba739256b86f8d8cdf47651b6f60e451f0a3de')
+ assert extra.collateralOutpoint.index == 1
+ assert len(extra.ipAddress) == 16
+ assert extra.ipAddress == bfh('00000000000000000000ffff12ca34aa')
+ assert extra.port == 12149
+ assert len(extra.KeyIdOwner) == 20
+ assert extra.KeyIdOwner == bfh(
+ '2b3edeed6842db1f59cf35de1ab5721094f049d0')
+ assert len(extra.PubKeyOperator) == 48
+ assert extra.PubKeyOperator == bfh(
+ '00ab986c589053b3f3bd720724e75e18581afdca54bce80d14750b1bcf920215'
+ '8fe6c596ce8391815265747bd4a2009e')
+ assert len(extra.KeyIdVoting) == 20
+ assert extra.KeyIdVoting == bfh(
+ '2b3edeed6842db1f59cf35de1ab5721094f049d0')
+ assert extra.operatorReward == 0
+ assert extra.scriptPayout == bfh(
+ '76a9149bf5948b901a1e3e54e42c6e10496a17cd4067e088ac')
+ assert len(extra.inputsHash) == 32
+ assert extra.inputsHash == bfh(
+ '54d046585434668b4ee664c597864248b8a6aac33a7b2f4fcd1cc1b5da474a8a')
+ assert extra.payloadSig == bfh(
+ '1fc1617ae83406c92a9132f14f9fff1487f2890f401e776fdddd639bc505'
+ '5c456268cf7497400d3196109c8cd31b94732caf6937d63de81d9a5be4db'
+ '5beb83f9aa')
+ ser = tx.serialize()
+ assert ser == test
+
+
+def test_dash_tx_pro_up_serv_tx():
+ test = bfh(PRO_UP_SERV_TX)
+ deser = lib_tx_dash.DeserializerDash(test)
+ tx = deser.read_tx()
+ assert tx.version == 3
+ assert tx.tx_type == 2
+ extra = tx.extra_payload
+ assert extra.version == 1
+ assert len(extra.proTxHash) == 32
+ assert extra.proTxHash == bfh(
+ '3c6dca244f49f19d3f09889753ffff1fec5bb8f9f5bd5bc09dabd999da21198f')
+ assert len(extra.ipAddress) == 16
+ assert extra.ipAddress == bfh('00000000000000000000ffff5fb73580')
+ assert extra.port == 4391
+ assert extra.scriptOperatorPayout == bfh(
+ '76a91421851058431a7d722e8e8dd9509e7f2b8e7042ec88ac')
+ assert len(extra.inputsHash) == 32
+ assert extra.inputsHash == bfh(
+ 'efcfe3d578914bb48c6bd71b3459d384e4237446d521c9e2c6'
+ 'b6fcf019b5aafc')
+ assert len(extra.payloadSig) == 96
+ assert extra.payloadSig == bfh(
+ '99443fe14f644cfa47086e8897cf7b546a67723d4a8ec5353a82f962a96e'
+ 'c3cea328343b647aace2897d6eddd0b8c8ee0f2e56f6733aed2e9f0006ca'
+ 'afa6fc21c18a013c619d6e37af8d2f0985e3b769abc38ffa60e46c365a38'
+ 'd9fa0d44fd62')
+ ser = tx.serialize()
+ assert ser == test
+
+
+def test_dash_tx_pro_up_reg_tx():
+ test = bfh(PRO_UP_REG_TX)
+ deser = lib_tx_dash.DeserializerDash(test)
+ tx = deser.read_tx()
+ assert tx.version == 3
+ assert tx.tx_type == 3
+ extra = tx.extra_payload
+ assert extra.version == 1
+ assert len(extra.proTxHash) == 32
+ assert extra.proTxHash == bfh(
+ 'aeb817f94b8e699b58130a53d2fbe98d5519c2abe3b15e6f36c9abeb32e4dcce')
+ assert extra.mode == 0
+ assert len(extra.PubKeyOperator) == 48
+ assert extra.PubKeyOperator == bfh(
+ '1061eb559a64427ad239830742ef59591cdbbdffda7d3f5e7a2d95b9607a'
+ 'd80e389191e44c59ea5987b85e6d0e3eb527')
+ assert len(extra.KeyIdVoting) == 20
+ assert extra.KeyIdVoting == bfh(
+ 'b9e198fa7a745913c9278ec993d4472a95dac425')
+ assert extra.scriptPayout == bfh(
+ '76a914eebbacffff3a55437803e0efb68a7d591e0409d188ac')
+ assert len(extra.inputsHash) == 32
+ assert extra.inputsHash == bfh(
+ '0eb0067e6ccdd2acb96e7279113702218f3f0ab6f2287e14c11c5be6f2051d5a')
+ assert extra.payloadSig == bfh(
+ '20cb00124d838b02207097048cb668244cd79df825eb2d4d211fd2c4604c1'
+ '8b30e1ae9bb654787144d16856676efff180889f05b5c9121a483b4ae3f0e'
+ 'a0ff3faf')
+ ser = tx.serialize()
+ assert ser == test
+
+
+def test_dash_tx_pro_up_rev_tx():
+ test = bfh(PRO_UP_REV_TX)
+ deser = lib_tx_dash.DeserializerDash(test)
+ tx = deser.read_tx()
+ assert tx.version == 3
+ assert tx.tx_type == 4
+ extra = tx.extra_payload
+ assert extra.version == 1
+ assert len(extra.proTxHash) == 32
+ assert extra.proTxHash == bfh(
+ 'b67ffbbd095de31ea38446754b6bf251287936d2881d58b7c4efae0b54c75e9f')
+ assert extra.reason == 0
+ assert len(extra.inputsHash) == 32
+ assert extra.inputsHash == bfh(
+ 'eb073521b60306717f1d4feb3e9022f886b97bf981137684716a7d3d7e45b7fe')
+ assert len(extra.payloadSig) == 96
+ assert extra.payloadSig == bfh(
+ '83f4bb5530f7c5954e8b1ad50a74a9e1d65dcdcbe4acb8cbe3671abc7911'
+ 'e8c3954856c4da7e5fd242f2e4f5546f08d90849245bc593d1605654e1a9'
+ '9cd0a79e9729799742c48d4920044666ad25a85fd093559c43e4900e634c'
+ '371b9b8d89ba')
+ ser = tx.serialize()
+ assert ser == test
+
+
+def test_dash_tx_sub_tx_register_tx():
+ test = bfh(SUB_TX_REGISTER)
+ deser = lib_tx_dash.DeserializerDash(test)
+ tx = deser.read_tx()
+ assert tx.version == 3
+ assert tx.tx_type == 8
+ extra = tx.extra_payload
+ assert extra.version == 1
+ assert extra.userName == b'abc'
+ assert len(extra.pubKey) == 48
+ assert extra.pubKey == bfh(
+ '8e7042ec88acefcfe3d578914bb48c6bd71b3459d384e42374e8abfeffff'
+ 'ff01570b0000000000001976a91490c5ce9d')
+ assert len(extra.payloadSig) == 96
+ assert extra.payloadSig == bfh(
+ '8bc992a88ac00000000a40100b67ffbbd095de31ea38446754e8abfeffff'
+ 'ff01570b0000000000001976a91490c5ce9d8bc992a88ac00000000a4010'
+ '0b67ffbbd095de31ea38446754e8abfeffffff01570b0000000000001976'
+ 'a91490c5ce9d')
+ ser = tx.serialize()
+ assert ser == test
+
+
+def test_dash_tx_sub_tx_topup_tx():
+ test = bfh(SUB_TX_TOPUP)
+ deser = lib_tx_dash.DeserializerDash(test)
+ tx = deser.read_tx()
+ assert tx.version == 3
+ assert tx.tx_type == 9
+ extra = tx.extra_payload
+ assert extra.version == 1
+ assert len(extra.regTxHash) == 32
+ assert extra.regTxHash == bfh(
+ 'd384e42374e8abfeffffff01570b000000a40100b67ffbbd095de31ea3844675')
+ ser = tx.serialize()
+ assert ser == test
+
+
+def test_dash_tx_sub_tx_reset_key_tx():
+ test = bfh(SUB_TX_RESET_KEY)
+ deser = lib_tx_dash.DeserializerDash(test)
+ tx = deser.read_tx()
+ assert tx.version == 3
+ assert tx.tx_type == 10
+ extra = tx.extra_payload
+ assert extra.version == 1
+ assert len(extra.regTxHash) == 32
+ assert extra.regTxHash == bfh(
+ 'd384e42374e8abfeffffff01570b000000a40100b67ffbbd095de31ea3844675')
+ assert len(extra.hashPrevSubTx) == 32
+ assert extra.hashPrevSubTx == bfh(
+ 'af3e98e9601210293360bf2a2e810673412bc6e8e0e358f3fb7bdbe9a667b3d0')
+ assert extra.creditFee == 1000
+ assert len(extra.newPubKey) == 48
+ assert extra.newPubKey == bfh(
+ '601210293360bf2a2e810673412bc6e8e0e358f3fb7bdbe9a667b3d0103f7'
+ '61caf3e98e9601210293360bf2a2e810673')
+ assert len(extra.payloadSig) == 96
+ assert extra.payloadSig == bfh(
+ '412bc6e8e0e358f3fb7bdbe9a667b3d0103f761caf3e98e9601210293360b'
+ 'f2a2e810673412bc6e8e0e358f3fb7bdbe9a667b3d0103f761caf3e98e960'
+ '1210293360bf2a2e810673412bc6e8e0e358f3fb7bdbe9a667b3d0103f761'
+ 'cabcdefab')
+ ser = tx.serialize()
+ assert ser == test
+
+
+def test_dash_tx_sub_tx_close_account_tx():
+ test = bfh(SUB_TX_CLOSE_ACCOUNT)
+ deser = lib_tx_dash.DeserializerDash(test)
+ tx = deser.read_tx()
+ assert tx.version == 3
+ assert tx.tx_type == 11
+ extra = tx.extra_payload
+ assert extra.version == 1
+ assert len(extra.regTxHash) == 32
+ assert extra.regTxHash == bfh(
+ 'd384e42374e8abfeffffff01570b000000a40100b67ffbbd095de31ea3844675')
+ assert len(extra.hashPrevSubTx) == 32
+ assert extra.hashPrevSubTx == bfh(
+ 'af3e98e9601210293360bf2a2e810673412bc6e8e0e358f3fb7bdbe9a12bc6e8')
+ assert extra.creditFee == 1000
+ assert len(extra.payloadSig) == 96
+ assert extra.payloadSig == bfh(
+ 'a62bc6e8e0e358f3fb7bdbe9a667b3d0103f761caf3e98e9601210293360b'
+ 'f2a2e810673412bc6e8e0e358f3fb7bdbe9a667b3d0103f761caf3e98e960'
+ '1210293360bf2a2e810673412bc6e8e0e358f3fb7bdbe9a667b3d0103f761'
+ 'cabcdefab')
+ ser = tx.serialize()
+ assert ser == test
+
+
+def test_dash_tx_unknown_spec_tx():
+ test = bfh(UNKNOWN_SPEC_TX)
+ deser = lib_tx_dash.DeserializerDash(test)
+ tx = deser.read_tx()
+ assert tx.version == 3
+ assert tx.tx_type == 187
+ extra = tx.extra_payload
+ assert extra == bfh(
+ '0100d384e42374e8abfeffffff01570b000000a40100b67ffbbd095de31e'
+ 'a3844675af3e98e9601210293360bf2a2e810673412bc6e8e0e358f3fb7b'
+ 'dbe9a12bc6e8e0e358f3fb7bdbe9a62bc6e8e0e358f3fb7bdbe9a667b3d0'
+ '103f761caf3e98e9601210293360bf2a2e810673412bc6e8e0e358f3fb7b'
+ 'dbe9a667b3d0103f761caf3e98e9601210293360bf2a2e810673412bc6e8'
+ 'e0e358f3fb7bdbe9a667b3d0103f761cabcdefab')
+ ser = tx.serialize()
+ assert ser == test
+
+
+def test_dash_tx_wrong_spec_tx():
+ test = bfh(WRONG_SPEC_TX)
+ deser = lib_tx_dash.DeserializerDash(test)
+ tx = deser.read_tx()
+ assert tx.version == 12255234
+ assert tx.tx_type == 0
+ extra = tx.extra_payload
+ assert extra == b''
+ ser = tx.serialize()
+ assert ser == test
+
+
+def test_dash_tx_serialize_wrong_tx_type():
+ test = bfh(CB_TX)
+ deser = lib_tx_dash.DeserializerDash(test)
+ tx = deser.read_tx()
+ assert tx.tx_type == 5
+ tx = tx._replace(tx_type=4)
+ assert tx.tx_type == 4
+ with pytest.raises(ValueError) as excinfo:
+ ser = tx.serialize()
+ assert ('Dash tx_type does not conform'
+ ' with extra payload class' in str(excinfo.value))
diff --git a/tests/server/test_compaction.py b/tests/server/test_compaction.py
index 44017f1..a831226 100644
--- a/tests/server/test_compaction.py
+++ b/tests/server/test_compaction.py
@@ -113,7 +113,7 @@ async def run_test(db_dir):
environ.clear()
environ['DB_DIRECTORY'] = db_dir
environ['DAEMON_URL'] = ''
- environ['COIN'] = 'BitcoinCash'
+ environ['COIN'] = 'BitcoinSV'
db = DB(Env())
await db.open_for_serving()
history = db.history
diff --git a/tests/server/test_daemon.py b/tests/server/test_daemon.py
index 712e02a..20b5d89 100644
--- a/tests/server/test_daemon.py
+++ b/tests/server/test_daemon.py
@@ -487,6 +487,6 @@ async def test_failover(daemon, caplog):
with ClientSessionFailover(('getblockcount', [], height)):
await daemon.height() == height
- assert in_caplog(caplog, "disconnected", 3)
+ assert in_caplog(caplog, "disconnected", 1)
assert in_caplog(caplog, "failing over")
assert in_caplog(caplog, "connection restored")
diff --git a/tests/server/test_env.py b/tests/server/test_env.py
index 3dcec3c..fbc8f45 100644
--- a/tests/server/test_env.py
+++ b/tests/server/test_env.py
@@ -16,7 +16,7 @@ BASE_DB_DIR = '/some/dir'
base_environ = {
'DB_DIRECTORY': BASE_DB_DIR,
'DAEMON_URL': BASE_DAEMON_URL,
- 'COIN': 'BitcoinCash',
+ 'COIN': 'BitcoinSV',
}
@@ -88,13 +88,13 @@ def test_COIN_NET():
'''Test COIN and NET defaults and redirection.'''
setup_base_env()
e = Env()
- assert e.coin == lib_coins.BitcoinCash
+ assert e.coin == lib_coins.BitcoinSV
os.environ['NET'] = 'testnet'
e = Env()
- assert e.coin == lib_coins.BitcoinCashTestnet
+ assert e.coin == lib_coins.BitcoinSVTestnet
os.environ['NET'] = ' testnet '
e = Env()
- assert e.coin == lib_coins.BitcoinCashTestnet
+ assert e.coin == lib_coins.BitcoinSVTestnet
os.environ.pop('NET')
os.environ['COIN'] = ' Litecoin '
e = Env()
@@ -169,7 +169,7 @@ def test_RPC_HOST():
def test_REORG_LIMIT():
assert_integer('REORG_LIMIT', 'reorg_limit',
- lib_coins.BitcoinCash.REORG_LIMIT)
+ lib_coins.BitcoinSV.REORG_LIMIT)
def test_TCP_PORT():
@@ -416,5 +416,5 @@ def test_ban_versions():
def test_coin_class_provided():
- e = Env(lib_coins.BitcoinCash)
- assert e.coin == lib_coins.BitcoinCash
+ e = Env(lib_coins.BitcoinSV)
+ assert e.coin == lib_coins.BitcoinSV
diff --git a/tests/server/test_notifications.py b/tests/server/test_notifications.py
new file mode 100644
index 0000000..af559a4
--- /dev/null
+++ b/tests/server/test_notifications.py
@@ -0,0 +1,47 @@
+import asyncio
+
+import pytest
+
+from electrumx.server.controller import Notifications
+
+
+@pytest.mark.asyncio
+async def test_simple_mempool():
+ n = Notifications()
+ notified = []
+ async def notify(height, touched):
+ notified.append((height, touched))
+ await n.start(5, notify)
+
+ mtouched = {'a', 'b'}
+ btouched = {'b', 'c'}
+ await n.on_mempool(mtouched, 6)
+ assert notified == [(5, set())]
+ await n.on_block(btouched, 6)
+ assert notified == [(5, set()), (6, set.union(mtouched, btouched))]
+
+
+@pytest.mark.asyncio
+async def test_enter_mempool_quick_blocks_2():
+ n = Notifications()
+ notified = []
+ async def notify(height, touched):
+ notified.append((height, touched))
+ await n.start(5, notify)
+
+ # Suppose a gets in block 6 and blocks 7,8 found right after and
+ # the block processer processes them together.
+ await n.on_mempool({'a'}, 5)
+ assert notified == [(5, set()), (5, {'a'})]
+ # Mempool refreshes with daemon on block 6
+ await n.on_mempool({'a'}, 6)
+ assert notified == [(5, set()), (5, {'a'})]
+ # Blocks 6, 7 processed together
+ await n.on_block({'a', 'b'}, 7)
+ assert notified == [(5, set()), (5, {'a'})]
+ # Then block 8 processed
+ await n.on_block({'c'}, 8)
+ assert notified == [(5, set()), (5, {'a'})]
+ # Now mempool refreshes
+ await n.on_mempool(set(), 8)
+ assert notified == [(5, set()), (5, {'a'}), (8, {'a', 'b', 'c'})]
diff --git a/tests/test_transactions.py b/tests/test_transactions.py
index d91a65c..731012d 100644
--- a/tests/test_transactions.py
+++ b/tests/test_transactions.py
@@ -11,8 +11,9 @@ from binascii import unhexlify
import pytest
-from electrumx.lib.coins import Coin
+from electrumx.lib.coins import Coin, Namecoin
from electrumx.lib.hash import hash_to_hex_str
+from electrumx.lib.script import OpCodes, Script
TRANSACTION_DIR = os.path.join(
os.path.dirname(os.path.realpath(__file__)), 'transactions')
@@ -57,3 +58,16 @@ def test_transaction(transaction_details):
assert spk['hex'] == tx_pks.hex()
assert coin.address_to_hashX(spk['address']) == \
coin.hashX_from_script(tx_pks)
+ if issubclass(coin, Namecoin):
+ if "nameOp" not in spk or "name" not in spk["nameOp"]:
+ assert coin.name_hashX_from_script(tx_pks) is None
+ else:
+ OP_NAME_UPDATE = OpCodes.OP_3
+ normalized_name_op_script = bytearray()
+ normalized_name_op_script.append(OP_NAME_UPDATE)
+ normalized_name_op_script.extend(Script.push_data(spk["nameOp"]["name"].encode("ascii")))
+ normalized_name_op_script.extend(Script.push_data(bytes([])))
+ normalized_name_op_script.append(OpCodes.OP_2DROP)
+ normalized_name_op_script.append(OpCodes.OP_DROP)
+ normalized_name_op_script.append(OpCodes.OP_RETURN)
+ assert coin.name_hashX_from_script(tx_pks) == Coin.hashX_from_script(normalized_name_op_script)
diff --git a/tests/transactions/namecoin_mainnet_037339.json b/tests/transactions/namecoin_mainnet_037339.json
new file mode 100644
index 0000000..467039e
--- /dev/null
+++ b/tests/transactions/namecoin_mainnet_037339.json
@@ -0,0 +1,62 @@
+{
+ "txid": "037339b97cf38d0fd8d68046c0ce7661a3d0117fac1da802c1d4c13056a74096",
+ "hash": "037339b97cf38d0fd8d68046c0ce7661a3d0117fac1da802c1d4c13056a74096",
+ "version": 28928,
+ "size": 568,
+ "vsize": 568,
+ "locktime": 411806,
+ "vin": [
+ {
+ "txid": "4e6adc779e9d6d359c131a6c162fda9ab6822c829ed85e6461eca09a7f2da860",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "30450221009934d4cef1c24141e5d74d825f1d50cc199bd9daf76e15024ff6119bc86fb8da02200d648a0c0dff5b4d091fdd8562a2f4a4cde243d460708565c1c96b2a3c1db786[ALL] 02beb88ae836bc254ef277e0911c8affe248d1e6a893bba6ce7da6cd9aee6079e4",
+ "hex": "4830450221009934d4cef1c24141e5d74d825f1d50cc199bd9daf76e15024ff6119bc86fb8da02200d648a0c0dff5b4d091fdd8562a2f4a4cde243d460708565c1c96b2a3c1db786012102beb88ae836bc254ef277e0911c8affe248d1e6a893bba6ce7da6cd9aee6079e4"
+ },
+ "sequence": 4294967294
+ },
+ {
+ "txid": "d269af00988e45ed9a5fe085884b8f729afac4a5ccaaf8e1ee601439e7bcc125",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "3045022100d2c96c88ceff7653e741c40e011d0253a7426f29af68097f6c0af45878f83bc8022008908f2a63ab0230caec18443c034478ce32367fcdcf810998293f6bb6808225[ALL] 02d514d8b84fe28a4f5b998fb15d931456dd98365df9576dbcc47f787412eec1a6",
+ "hex": "483045022100d2c96c88ceff7653e741c40e011d0253a7426f29af68097f6c0af45878f83bc8022008908f2a63ab0230caec18443c034478ce32367fcdcf810998293f6bb6808225012102d514d8b84fe28a4f5b998fb15d931456dd98365df9576dbcc47f787412eec1a6"
+ },
+ "sequence": 4294967294
+ }
+ ],
+ "vout": [
+ {
+ "value": 1477675940,
+ "n": 0,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 224538b431f8deb3fddccc433ebc1327017634e7 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a914224538b431f8deb3fddccc433ebc1327017634e788ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "address": "Myha6mFMZV7u9Yvj6X43cidZocas5Xhq3E"
+ }
+ },
+ {
+ "value": 1000000,
+ "n": 1,
+ "scriptPubKey": {
+ "nameOp": {
+ "op": "name_update",
+ "name": "dd/domob",
+ "value": "{\"ip\":\"176.31.184.255\",\"map\":{\"*\":{\"ip\":\"176.31.184.255\",\"tor\":\"wivfwn64tm3uaeig.onion\"}},\"fingerprint\":[\"6969C8037D2318BEDBC8111474B0A6E66E73BDB9\"],\"tor\":\"wivfwn64tm3uaeig.onion\"}"
+ },
+ "asm": "OP_NAME_UPDATE 64642f646f6d6f62 7b226970223a223137362e33312e3138342e323535222c226d6170223a7b222a223a7b226970223a223137362e33312e3138342e323535222c22746f72223a2277697666776e3634746d3375616569672e6f6e696f6e227d7d2c2266696e6765727072696e74223a5b2236393639433830333744323331384245444243383131313437344230413645363645373342444239225d2c22746f72223a2277697666776e3634746d3375616569672e6f6e696f6e227d OP_2DROP OP_DROP OP_DUP OP_HASH160 57fd3c4a6a417c17f8d902e90e2bfb97b1a12e11 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "530864642f646f6d6f624cb47b226970223a223137362e33312e3138342e323535222c226d6170223a7b222a223a7b226970223a223137362e33312e3138342e323535222c22746f72223a2277697666776e3634746d3375616569672e6f6e696f6e227d7d2c2266696e6765727072696e74223a5b2236393639433830333744323331384245444243383131313437344230413645363645373342444239225d2c22746f72223a2277697666776e3634746d3375616569672e6f6e696f6e227d6d7576a91457fd3c4a6a417c17f8d902e90e2bfb97b1a12e1188ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "address": "N4bcMSxUL7jNDAJa34DpkdZPp9faHRJeaP"
+ }
+ }
+ ],
+ "hex": "007100000260a82d7f9aa0ec61645ed89e822c82b69ada2f166c1a139c356d9d9e77dc6a4e000000006b4830450221009934d4cef1c24141e5d74d825f1d50cc199bd9daf76e15024ff6119bc86fb8da02200d648a0c0dff5b4d091fdd8562a2f4a4cde243d460708565c1c96b2a3c1db786012102beb88ae836bc254ef277e0911c8affe248d1e6a893bba6ce7da6cd9aee6079e4feffffff25c1bce7391460eee1f8aacca5c4fa9a728f4b8885e05f9aed458e9800af69d2010000006b483045022100d2c96c88ceff7653e741c40e011d0253a7426f29af68097f6c0af45878f83bc8022008908f2a63ab0230caec18443c034478ce32367fcdcf810998293f6bb6808225012102d514d8b84fe28a4f5b998fb15d931456dd98365df9576dbcc47f787412eec1a6feffffff02a48b1358000000001976a914224538b431f8deb3fddccc433ebc1327017634e788ac40420f0000000000db530864642f646f6d6f624cb47b226970223a223137362e33312e3138342e323535222c226d6170223a7b222a223a7b226970223a223137362e33312e3138342e323535222c22746f72223a2277697666776e3634746d3375616569672e6f6e696f6e227d7d2c2266696e6765727072696e74223a5b2236393639433830333744323331384245444243383131313437344230413645363645373342444239225d2c22746f72223a2277697666776e3634746d3375616569672e6f6e696f6e227d6d7576a91457fd3c4a6a417c17f8d902e90e2bfb97b1a12e1188ac9e480600",
+ "blockhash": "1453f1744573abc329a116d071f9041bea5639f51c6e9bb2463fcb21f0638d2c",
+ "confirmations": 10342,
+ "time": 1533902501,
+ "blocktime": 1533902501
+}
diff --git a/tests/transactions/namecoin_mainnet_0c6867.json b/tests/transactions/namecoin_mainnet_0c6867.json
new file mode 100644
index 0000000..6c7b912
--- /dev/null
+++ b/tests/transactions/namecoin_mainnet_0c6867.json
@@ -0,0 +1,52 @@
+{
+ "txid": "0c686779a1dcc867039a3c71934d6cde487c4eabd9cd2efd0bdcf15262ed9886",
+ "hash": "0c686779a1dcc867039a3c71934d6cde487c4eabd9cd2efd0bdcf15262ed9886",
+ "version": 28928,
+ "size": 280,
+ "vsize": 280,
+ "locktime": 421976,
+ "vin": [
+ {
+ "txid": "0b9468e87947debbf999ac9290fcd25af7eff5f4d6df4773be2a89fd31009078",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "304402203b3d10f8ba91fb477fdcbeb0354016bce97fcdf7d050191c7958a7d174ae4e76022064bd96fb14fcfc535320dd9eafd9a6c6dd9410937afa4fdb1064e2d8cc1110fa[ALL] 0421c34686d12da7331dcf876a61b58cdfa9bb892120fa8e699daac1333ad7325ed09a37432a414ccc2bed6506b3badd0413cd35469dcfda23ebadca86929f59c1",
+ "hex": "47304402203b3d10f8ba91fb477fdcbeb0354016bce97fcdf7d050191c7958a7d174ae4e76022064bd96fb14fcfc535320dd9eafd9a6c6dd9410937afa4fdb1064e2d8cc1110fa01410421c34686d12da7331dcf876a61b58cdfa9bb892120fa8e699daac1333ad7325ed09a37432a414ccc2bed6506b3badd0413cd35469dcfda23ebadca86929f59c1"
+ },
+ "sequence": 4294967294
+ }
+ ],
+ "vout": [
+ {
+ "value": 280480622,
+ "n": 0,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 b7db2908b47c32f4b8864caf2522d2eb1ed3d50b OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a914b7db2908b47c32f4b8864caf2522d2eb1ed3d50b88ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "address": "NDLWQGyHyhneB7ySsL6na2QBPK1T2i1WSQ"
+ }
+ },
+ {
+ "value": 1000000,
+ "n": 1,
+ "scriptPubKey": {
+ "nameOp": {
+ "op": "name_new",
+ "hash": "2dbe200657c5c3cce22f0637480865d5db3aaaac"
+ },
+ "asm": "OP_NAME_NEW 2dbe200657c5c3cce22f0637480865d5db3aaaac OP_2DROP OP_DUP OP_HASH160 6985a7f580409d4fdaf223f34848212955d371eb OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "51142dbe200657c5c3cce22f0637480865d5db3aaaac6d76a9146985a7f580409d4fdaf223f34848212955d371eb88ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "address": "N6CKGNr6iBDriS6iwZBAFswM2auAQnjd5K"
+ }
+ }
+ ],
+ "hex": "007100000178900031fd892abe7347dfd6f4f5eff75ad2fc9092ac99f9bbde4779e868940b010000008a47304402203b3d10f8ba91fb477fdcbeb0354016bce97fcdf7d050191c7958a7d174ae4e76022064bd96fb14fcfc535320dd9eafd9a6c6dd9410937afa4fdb1064e2d8cc1110fa01410421c34686d12da7331dcf876a61b58cdfa9bb892120fa8e699daac1333ad7325ed09a37432a414ccc2bed6506b3badd0413cd35469dcfda23ebadca86929f59c1feffffff026ecbb710000000001976a914b7db2908b47c32f4b8864caf2522d2eb1ed3d50b88ac40420f00000000003051142dbe200657c5c3cce22f0637480865d5db3aaaac6d76a9146985a7f580409d4fdaf223f34848212955d371eb88ac58700600",
+ "blockhash": "15b3979258287d26becc021b265bcccc964b9863d44a8a5802b9f6ce53d9509d",
+ "confirmations": 170,
+ "time": 1539845549,
+ "blocktime": 1539845549
+}
diff --git a/tests/transactions/namecoin_mainnet_460f2b.json b/tests/transactions/namecoin_mainnet_460f2b.json
new file mode 100644
index 0000000..e22d578
--- /dev/null
+++ b/tests/transactions/namecoin_mainnet_460f2b.json
@@ -0,0 +1,58 @@
+{
+ "txid": "460f2b0a6f480b0d16b99fd4ea55afbf7ee628365a2a32d54b56422163e7660b",
+ "hash": "460f2b0a6f480b0d16b99fd4ea55afbf7ee628365a2a32d54b56422163e7660b",
+ "version": 28928,
+ "size": 688,
+ "vsize": 688,
+ "locktime": 422150,
+ "vin": [
+ {
+ "txid": "33991d6cc33c2f9d85484086d0e9dcb93717be11e89bb315c5f0233d0fc0fc01",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "3045022100c26ad0e826c1759a02a4bb3a8f816841b6330b44d3bba5ace371af7aeb736cae02205e6e9927d5e8279d32e741fbcd567c08f6dda8bdd0e3597c85b85831c3a73090[ALL] 03a7c03fa10f4ffa393a2e90ba098bf1f9b5d0cf84f83bc006fc9902884f22a5b5",
+ "hex": "483045022100c26ad0e826c1759a02a4bb3a8f816841b6330b44d3bba5ace371af7aeb736cae02205e6e9927d5e8279d32e741fbcd567c08f6dda8bdd0e3597c85b85831c3a73090012103a7c03fa10f4ffa393a2e90ba098bf1f9b5d0cf84f83bc006fc9902884f22a5b5"
+ },
+ "sequence": 4294967293
+ },
+ {
+ "txid": "4632320203dddc75cac6e0078a040b6601de73b95d0069c8ef37f34e085f883c",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "304402205bf9d8fe538d18d2f589f818acfc66efb3d7c563a6d0ce94379761e3ad5de4bb02204841c4f51cdb4433991f73fcff41fc82afd0a4411a89b09de30e6fb603ddf5a1[ALL] 0284741f8ca6ab581a46ae0ba5edfbfd134378c41e576c9267ca99054ecdf121cb",
+ "hex": "47304402205bf9d8fe538d18d2f589f818acfc66efb3d7c563a6d0ce94379761e3ad5de4bb02204841c4f51cdb4433991f73fcff41fc82afd0a4411a89b09de30e6fb603ddf5a101210284741f8ca6ab581a46ae0ba5edfbfd134378c41e576c9267ca99054ecdf121cb"
+ },
+ "sequence": 4294967293
+ }
+ ],
+ "vout": [
+ {
+ "value": 1000000,
+ "n": 0,
+ "scriptPubKey": {
+ "nameOp": {
+ "op": "name_update",
+ "name": "test/6",
+ "value": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ },
+ "asm": "OP_NAME_UPDATE 746573742f36 787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878 OP_2DROP OP_DROP OP_DUP OP_HASH160 734b00d1b31f3c3a89bcd6d302b820d4c0459fc1 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "5306746573742f364d2c017878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878786d7576a914734b00d1b31f3c3a89bcd6d302b820d4c0459fc188ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "address": "N75ym2CibThuKTtR4eRRZAMvkcLybL77fS"
+ }
+ },
+ {
+ "value": 354398,
+ "n": 1,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 3b9cde2101e9da0fbf47d98ed859b0ad8fa886b5 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a9143b9cde2101e9da0fbf47d98ed859b0ad8fa886b588ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "address": "N21ZzKiuLLQ5pZnoJrqaSNZSbipQUtAaGt"
+ }
+ }
+ ],
+ "hex": "007100000201fcc00f3d23f0c515b39be811be1737b9dce9d0864048859d2f3cc36c1d9933010000006b483045022100c26ad0e826c1759a02a4bb3a8f816841b6330b44d3bba5ace371af7aeb736cae02205e6e9927d5e8279d32e741fbcd567c08f6dda8bdd0e3597c85b85831c3a73090012103a7c03fa10f4ffa393a2e90ba098bf1f9b5d0cf84f83bc006fc9902884f22a5b5fdffffff3c885f084ef337efc869005db973de01660b048a07e0c6ca75dcdd0302323246000000006a47304402205bf9d8fe538d18d2f589f818acfc66efb3d7c563a6d0ce94379761e3ad5de4bb02204841c4f51cdb4433991f73fcff41fc82afd0a4411a89b09de30e6fb603ddf5a101210284741f8ca6ab581a46ae0ba5edfbfd134378c41e576c9267ca99054ecdf121cbfdffffff0240420f0000000000fd52015306746573742f364d2c017878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878786d7576a914734b00d1b31f3c3a89bcd6d302b820d4c0459fc188ac5e680500000000001976a9143b9cde2101e9da0fbf47d98ed859b0ad8fa886b588ac06710600"
+}
diff --git a/tests/transactions/namecoin_mainnet_5e3495.json b/tests/transactions/namecoin_mainnet_5e3495.json
new file mode 100644
index 0000000..209f857
--- /dev/null
+++ b/tests/transactions/namecoin_mainnet_5e3495.json
@@ -0,0 +1,63 @@
+{
+ "txid": "5e349540789309a088f9ede78c2e42e233e670aabbbd79886adc541389cfa4fa",
+ "hash": "5e349540789309a088f9ede78c2e42e233e670aabbbd79886adc541389cfa4fa",
+ "version": 28928,
+ "size": 478,
+ "vsize": 478,
+ "locktime": 421921,
+ "vin": [
+ {
+ "txid": "4748ec1d3acad8bdb357b5499530ce1c3ada97ae9952475e3901e8f75dd55506",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "30450221008eff8db772c8018181ccb1355d29e32436982f13d032d6ad72191139f7d9790402201a98c67c368b2c389156cb20b88aef251ec4e22535d830cea7986bcf62815059[ALL] 04e1c4b9e1cb0090fb6f55b98da47ed030a9e6383f6dd7d17089237abf10e5d939147dae8da00fc0840cf452c0e1a1001a56691abaa2cf07dc03c5477a586be6e2",
+ "hex": "4830450221008eff8db772c8018181ccb1355d29e32436982f13d032d6ad72191139f7d9790402201a98c67c368b2c389156cb20b88aef251ec4e22535d830cea7986bcf62815059014104e1c4b9e1cb0090fb6f55b98da47ed030a9e6383f6dd7d17089237abf10e5d939147dae8da00fc0840cf452c0e1a1001a56691abaa2cf07dc03c5477a586be6e2"
+ },
+ "sequence": 4294967294
+ },
+ {
+ "txid": "0c686779a1dcc867039a3c71934d6cde487c4eabd9cd2efd0bdcf15262ed9886",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "3045022100d242038864fc1b09ccdf4707b4ba828289c946c1c8681cff6347708332b9f6630220425d2dc3e4bd608efa82c6392e9732502ccffeba8c037ebf22ba035fc959cda6[ALL] 046cd0bf29656995ca841923f30f4b7ac9ae7cb4265da65ca8bd38363387b9317449cb1a0d6a11bc71d70a571ca7ee5b6a5dfe37cac10ccb4ed5cdb82751ceaa4b",
+ "hex": "483045022100d242038864fc1b09ccdf4707b4ba828289c946c1c8681cff6347708332b9f6630220425d2dc3e4bd608efa82c6392e9732502ccffeba8c037ebf22ba035fc959cda60141046cd0bf29656995ca841923f30f4b7ac9ae7cb4265da65ca8bd38363387b9317449cb1a0d6a11bc71d70a571ca7ee5b6a5dfe37cac10ccb4ed5cdb82751ceaa4b"
+ },
+ "sequence": 4294967294
+ }
+ ],
+ "vout": [
+ {
+ "value": 280384718,
+ "n": 0,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 9a52d8b8f425031b6ef40a847e771f6134af9804 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a9149a52d8b8f425031b6ef40a847e771f6134af980488ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "address": "NAeMWUDKZAjbDXM7BryV3Wa9Lp3C3aG7oq"
+ }
+ },
+ {
+ "value": 1000000,
+ "n": 1,
+ "scriptPubKey": {
+ "nameOp": {
+ "op": "name_firstupdate",
+ "name": "d/heretostay",
+ "value": "{}",
+ "rand": "ca7d07e0aa9d23c2dc7efe5f2902f0e2ec22fe7c"
+ },
+ "asm": "OP_NAME_FIRSTUPDATE 642f68657265746f73746179 ca7d07e0aa9d23c2dc7efe5f2902f0e2ec22fe7c 32123 OP_2DROP OP_2DROP OP_DUP OP_HASH160 ce26e759dbb966466446aab6e48cf39a23badd75 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "520c642f68657265746f7374617914ca7d07e0aa9d23c2dc7efe5f2902f0e2ec22fe7c027b7d6d6d76a914ce26e759dbb966466446aab6e48cf39a23badd7588ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "address": "NFNQ1Ez6ZYVQpvsWcePBQMSiA3LB4BT9o1"
+ }
+ }
+ ],
+ "hex": "00710000020655d55df7e801395e475299ae97da3a1cce309549b557b3bdd8ca3a1dec4847010000008b4830450221008eff8db772c8018181ccb1355d29e32436982f13d032d6ad72191139f7d9790402201a98c67c368b2c389156cb20b88aef251ec4e22535d830cea7986bcf62815059014104e1c4b9e1cb0090fb6f55b98da47ed030a9e6383f6dd7d17089237abf10e5d939147dae8da00fc0840cf452c0e1a1001a56691abaa2cf07dc03c5477a586be6e2feffffff8698ed6252f1dc0bfd2ecdd9ab4e7c48de6c4d93713c9a0367c8dca17967680c010000008b483045022100d242038864fc1b09ccdf4707b4ba828289c946c1c8681cff6347708332b9f6630220425d2dc3e4bd608efa82c6392e9732502ccffeba8c037ebf22ba035fc959cda60141046cd0bf29656995ca841923f30f4b7ac9ae7cb4265da65ca8bd38363387b9317449cb1a0d6a11bc71d70a571ca7ee5b6a5dfe37cac10ccb4ed5cdb82751ceaa4bfeffffff02ce54b610000000001976a9149a52d8b8f425031b6ef40a847e771f6134af980488ac40420f000000000041520c642f68657265746f7374617914ca7d07e0aa9d23c2dc7efe5f2902f0e2ec22fe7c027b7d6d6d76a914ce26e759dbb966466446aab6e48cf39a23badd7588ac21700600",
+ "blockhash": "5b8c0254e59018d9cba170efccec0e1909898b8552ab581ba73a482d5693d25b",
+ "confirmations": 145,
+ "time": 1539853690,
+ "blocktime": 1539853690
+}
diff --git a/tests/transactions/namecoin_mainnet_cfc5d7.json b/tests/transactions/namecoin_mainnet_cfc5d7.json
new file mode 100644
index 0000000..eca550d
--- /dev/null
+++ b/tests/transactions/namecoin_mainnet_cfc5d7.json
@@ -0,0 +1,62 @@
+{
+ "txid": "cfc5d7e6280af21ce96f016826080490106dbf53f12ed1e38737595564b125c1",
+ "hash": "cfc5d7e6280af21ce96f016826080490106dbf53f12ed1e38737595564b125c1",
+ "version": 28928,
+ "size": 513,
+ "vsize": 513,
+ "locktime": 422067,
+ "vin": [
+ {
+ "txid": "29c4c6c3443395a675111f7d809f192874d574425c014bb30ba8b6d4958fce2a",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "30440220545b4eb23ddd3053580b58ef445092135a39a102cd858c3f46a8e3bbd68c0bb302200eb29decfaf4d189ca6dcae6cf4d86ddee5a2e2122fc504179e84c119873b014[ALL] 04d9fb2666d199134a53392189011a1aac0942d21299d338c274ae46f35cbeb631f4a413810d43fc1f968d7abc07b4181e56a5042e9f134538f8208b648d3bc5c7",
+ "hex": "4730440220545b4eb23ddd3053580b58ef445092135a39a102cd858c3f46a8e3bbd68c0bb302200eb29decfaf4d189ca6dcae6cf4d86ddee5a2e2122fc504179e84c119873b014014104d9fb2666d199134a53392189011a1aac0942d21299d338c274ae46f35cbeb631f4a413810d43fc1f968d7abc07b4181e56a5042e9f134538f8208b648d3bc5c7"
+ },
+ "sequence": 4294967294
+ },
+ {
+ "txid": "5e349540789309a088f9ede78c2e42e233e670aabbbd79886adc541389cfa4fa",
+ "vout": 1,
+ "scriptSig": {
+ "asm": "3045022100f446e902ad3218d9d14ea2328f3ee5432b8f3a20224c2fc5fd9919b1c3d4362f022072fa9b4f840f53795f96336fadb79af2fb8c1e750a6cd84a5189fc381b5d8d56[ALL] 040df31f5db8614a8178e6d0785a56118c4f7d8fed10e80156e47934bd486f71ad1e81da5fbca360507eadc12e6c6b082bc6bee9f30fe64e235ea1127e28dfdf9c",
+ "hex": "483045022100f446e902ad3218d9d14ea2328f3ee5432b8f3a20224c2fc5fd9919b1c3d4362f022072fa9b4f840f53795f96336fadb79af2fb8c1e750a6cd84a5189fc381b5d8d560141040df31f5db8614a8178e6d0785a56118c4f7d8fed10e80156e47934bd486f71ad1e81da5fbca360507eadc12e6c6b082bc6bee9f30fe64e235ea1127e28dfdf9c"
+ },
+ "sequence": 4294967294
+ }
+ ],
+ "vout": [
+ {
+ "value": 1000000,
+ "n": 0,
+ "scriptPubKey": {
+ "nameOp": {
+ "op": "name_update",
+ "name": "d/heretostay",
+ "value": "{\"ip\":\"185.234.217.36\",\"map\":{\"*\":{\"ip\":\"185.234.217.36\"}}}"
+ },
+ "asm": "OP_NAME_UPDATE 642f68657265746f73746179 7b226970223a223138352e3233342e3231372e3336222c226d6170223a7b222a223a7b226970223a223138352e3233342e3231372e3336227d7d7d OP_2DROP OP_DROP OP_DUP OP_HASH160 584e13c024ce2eac261902a3c9508731a3dfa143 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "530c642f68657265746f737461793b7b226970223a223138352e3233342e3231372e3336222c226d6170223a7b222a223a7b226970223a223138352e3233342e3231372e3336227d7d7d6d7576a914584e13c024ce2eac261902a3c9508731a3dfa14388ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "address": "N4dHCRYVP8oQa6ZXLC1DPnzbvuvq29hy6v"
+ }
+ },
+ {
+ "value": 279093004,
+ "n": 1,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 09804e98bc777f101343fcd3c67044742e785bf6 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a91409804e98bc777f101343fcd3c67044742e785bf688ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "address": "MwSbzJTK1Dc2dBcE4u6nLJ95ZmEnzPUtSE"
+ }
+ }
+ ],
+ "hex": "00710000022ace8f95d4b6a80bb34b015c4274d57428199f807d1f1175a6953344c3c6c429010000008a4730440220545b4eb23ddd3053580b58ef445092135a39a102cd858c3f46a8e3bbd68c0bb302200eb29decfaf4d189ca6dcae6cf4d86ddee5a2e2122fc504179e84c119873b014014104d9fb2666d199134a53392189011a1aac0942d21299d338c274ae46f35cbeb631f4a413810d43fc1f968d7abc07b4181e56a5042e9f134538f8208b648d3bc5c7fefffffffaa4cf891354dc6a8879bdbbaa70e633e2422e8ce7edf988a00993784095345e010000008b483045022100f446e902ad3218d9d14ea2328f3ee5432b8f3a20224c2fc5fd9919b1c3d4362f022072fa9b4f840f53795f96336fadb79af2fb8c1e750a6cd84a5189fc381b5d8d560141040df31f5db8614a8178e6d0785a56118c4f7d8fed10e80156e47934bd486f71ad1e81da5fbca360507eadc12e6c6b082bc6bee9f30fe64e235ea1127e28dfdf9cfeffffff0240420f000000000065530c642f68657265746f737461793b7b226970223a223138352e3233342e3231372e3336222c226d6170223a7b222a223a7b226970223a223138352e3233342e3231372e3336227d7d7d6d7576a914584e13c024ce2eac261902a3c9508731a3dfa14388ac0c9fa210000000001976a91409804e98bc777f101343fcd3c67044742e785bf688acb3700600",
+ "blockhash": "182f4fbdac2309b8f3333802ff81008178661662bf2d7bd385a2e93d3ac493a4",
+ "confirmations": 3,
+ "time": 1539945678,
+ "blocktime": 1539945678
+}