Merge branch 'sync-upstream'
This commit is contained in:
commit
03d219db10
@ -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
|
||||
|
||||
70
README.rst
70
README.rst
@ -22,3 +22,73 @@ See `readthedocs <https://electrumx.readthedocs.io/>`_.
|
||||
**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
|
||||
|
||||
@ -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 <https://github.com/kyuupichan/aiorpcX>`_ for all
|
||||
networking, ``JSON RPC`` and proxy handling
|
||||
* proxy detection improvements
|
||||
* `documentation <https://electrumx.readthedocs.io/>`_ 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
|
||||
<https://electrumx.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) <magnet:?xt=urn:btih:caa804f48a319b061be3884ac011656c27121a6f&dn=electrumx_1.2_btc_leveldb_490153>`_.
|
||||
|
||||
|
||||
**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
|
||||
|
||||
@ -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 -----------------------------------------------------
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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('<H', script[n: n + 2])
|
||||
n += 2
|
||||
else:
|
||||
dlen, = struct.unpack('<I', script[n: n + 4])
|
||||
n += 4
|
||||
if n + dlen > 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')
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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])
|
||||
|
||||
405
electrumx/lib/tx_dash.py
Normal file
405
electrumx/lib/tx_dash.py
Normal file
@ -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
|
||||
@ -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('<I', tx_num)
|
||||
|
||||
for idx, txout in enumerate(tx.outputs):
|
||||
# Get the hashX. Ignore unspendable outputs.
|
||||
hashX = script_hashX(txout.pk_script)
|
||||
if hashX:
|
||||
add_hashXs(hashX)
|
||||
put_utxo(tx_hash + s_pack('<H', idx),
|
||||
hashX + tx_numb + s_pack('<Q', txout.value))
|
||||
tx_num += 1
|
||||
|
||||
# Spend the inputs
|
||||
# A separate for-loop here allows any tx ordering in block.
|
||||
for (tx, tx_hash), hashXs in zip(txs, hashXs_by_tx):
|
||||
add_hashXs = hashXs.add
|
||||
for txin in tx.inputs:
|
||||
if txin.is_generation():
|
||||
continue
|
||||
cache_value = spend_utxo(txin.prev_hash, txin.prev_idx)
|
||||
undo_info_append(cache_value)
|
||||
add_hashXs(cache_value[:-12])
|
||||
|
||||
# Update touched set for notifications
|
||||
for hashXs in hashXs_by_tx:
|
||||
update_touched(hashXs)
|
||||
|
||||
self.db.history.add_unflushed(hashXs_by_tx, self.tx_count)
|
||||
|
||||
self.tx_count = tx_num
|
||||
self.db.tx_counts.append(tx_num)
|
||||
|
||||
return undo_info
|
||||
|
||||
def backup_txs(self, txs):
|
||||
undo_info = self.db.read_undo_info(self.height)
|
||||
if undo_info is None:
|
||||
raise ChainError('no undo information found for height {:,d}'
|
||||
.format(self.height))
|
||||
|
||||
# Use local vars for speed in the loops
|
||||
s_pack = pack
|
||||
put_utxo = self.utxo_cache.__setitem__
|
||||
spend_utxo = self.spend_utxo
|
||||
script_hashX = self.coin.hashX_from_script
|
||||
add_touched = self.touched.add
|
||||
undo_entry_len = 12 + HASHX_LEN
|
||||
|
||||
# Restore coins that had been spent
|
||||
# (may include coins made then spent in this block)
|
||||
n = 0
|
||||
for tx, tx_hash in txs:
|
||||
for txin in tx.inputs:
|
||||
if txin.is_generation():
|
||||
continue
|
||||
undo_item = undo_info[n:n + undo_entry_len]
|
||||
put_utxo(txin.prev_hash + s_pack('<H', txin.prev_idx),
|
||||
undo_item)
|
||||
add_touched(undo_item[:-12])
|
||||
n += undo_entry_len
|
||||
|
||||
assert n == len(undo_info)
|
||||
|
||||
# Remove tx outputs made in this block, by spending them.
|
||||
for tx, tx_hash in txs:
|
||||
for idx, txout in enumerate(tx.outputs):
|
||||
hashX = script_hashX(txout.pk_script)
|
||||
if hashX:
|
||||
# Be careful with unspendable outputs- we didn't save those
|
||||
# in the first place.
|
||||
cache_value = spend_utxo(tx_hash, idx)
|
||||
add_touched(cache_value[:-12])
|
||||
|
||||
self.tx_count -= len(txs)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2016-2017, Neil Booth
|
||||
# Copyright (c) 2016-2018, Neil Booth
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
@ -33,8 +33,7 @@ class Notifications(object):
|
||||
def __init__(self):
|
||||
self._touched_mp = {}
|
||||
self._touched_bp = {}
|
||||
self._highest_block = 0
|
||||
self._notify_funcs = []
|
||||
self._highest_block = -1
|
||||
|
||||
async def _maybe_notify(self):
|
||||
tmp, tbp = self._touched_mp, self._touched_bp
|
||||
@ -49,16 +48,19 @@ class Notifications(object):
|
||||
# new block height
|
||||
return
|
||||
touched = tmp.pop(height)
|
||||
touched.update(tbp.pop(height, set()))
|
||||
for old in [h for h in tmp if h <= height]:
|
||||
del tmp[old]
|
||||
for old in [h for h in tbp if h <= height]:
|
||||
del tbp[old]
|
||||
for notify_func in self._notify_funcs:
|
||||
await notify_func(height, touched)
|
||||
touched.update(tbp.pop(old))
|
||||
await self.notify(height, touched)
|
||||
|
||||
def add_callback(self, notify_func):
|
||||
self._notify_funcs.append(notify_func)
|
||||
async def notify(self, height, touched):
|
||||
pass
|
||||
|
||||
async def start(self, height, notify_func):
|
||||
self._highest_block = height
|
||||
self.notify = notify_func
|
||||
await self.notify(height, set())
|
||||
|
||||
async def on_mempool(self, touched, height):
|
||||
self._touched_mp[height] = touched
|
||||
@ -80,8 +82,8 @@ class Controller(ServerBase):
|
||||
'''Start the RPC server and wait for the mempool to synchronize. Then
|
||||
start serving external clients.
|
||||
'''
|
||||
if not (0, 8, 1) <= aiorpcx_version < (0, 9):
|
||||
raise RuntimeError('aiorpcX version 0.8.x with x >= 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()
|
||||
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())
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
#
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
6
setup.py
6
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',
|
||||
|
||||
15
tests/blocks/bitcoinplus_mainnet_749740.json
Normal file
15
tests/blocks/bitcoinplus_mainnet_749740.json
Normal file
@ -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"
|
||||
}
|
||||
16
tests/blocks/civx_mainnet_50000.json
Normal file
16
tests/blocks/civx_mainnet_50000.json
Normal file
@ -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"
|
||||
}
|
||||
16
tests/blocks/civx_testnet_60000.json
Normal file
16
tests/blocks/civx_testnet_60000.json
Normal file
@ -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"
|
||||
}
|
||||
14
tests/blocks/myriadcoin_mainnet_2587044.json
Normal file
14
tests/blocks/myriadcoin_mainnet_2587044.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"hash": "09a2344ca39c422a473ab2ac0a93c0de5eef7bbc63c59ea36bf8a126ae2fbc26",
|
||||
"size": 326,
|
||||
"height": 2587044,
|
||||
"merkleroot": "0373487200798a478a9b5330fddcc092cb08f9b517c62eacbfbe733b8e8d3680",
|
||||
"tx": [
|
||||
"0373487200798a478a9b5330fddcc092cb08f9b517c62eacbfbe733b8e8d3680"
|
||||
],
|
||||
"time": 1540383063,
|
||||
"nonce": 1758391936,
|
||||
"bits": "1b013fb1",
|
||||
"previousblockhash": "6bebe78892419acf8f47fa34ac08417036c244a244d209dbb5113edfda7ae480",
|
||||
"block": "00065a2080e47adadf3e11b5db09d244a244c236704108ac34fa478fcf9a419288e7eb6b80368d8e3b73bebfac2ec617b5f908cb92c0dcfd30539b8a478a7900724873035761d05bb13f011b80eece6801010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff4d03a47927045761d05b08fabe6d6db5b8ddd39dc3e80a8930480db7f9dd41acc305fe5f41f5c953ae73e980a534b9020000000000000000005275250000000e2f6d696e696e672d64757463682f00000000020000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf900ba1dd2050000001976a9140c6de8cbb3e5fc90476c696881dc28bb9b4989e088ac0120000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
14
tests/blocks/nix_mainnet_50000.json
Normal file
14
tests/blocks/nix_mainnet_50000.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"hash": "b9fa17b5469c68aeddea48666f12c8ae5cdc3e8521b4b5637e10099cda8728fd",
|
||||
"size": 377,
|
||||
"height": 50000,
|
||||
"merkleroot": "2ee565d865d766a9861d77f1d3c34b364261a105fd0b10c15e9cb07dc61b1493",
|
||||
"tx": [
|
||||
"2ee565d865d766a9861d77f1d3c34b364261a105fd0b10c15e9cb07dc61b1493"
|
||||
],
|
||||
"time": 1536401916,
|
||||
"nonce": 1610805540,
|
||||
"bits": "1b0551f5",
|
||||
"previousblockhash": "acec77ce8845f34d2a854dc7da66151338c2f42f1c7f51782bd7732017f13c31",
|
||||
"block": "00000020313cf1172073d72b78517f1c2ff4c238131566dac74d852a4df34588ce77ecac93141bc67db09c5ec1100bfd05a16142364bc3d3f1771d86a966d765d865e52efca1935bf551051b24f1026001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff200350c30004fca1935b08180006a3510200000d2f6e6f64655374726174756d2f00000000050000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90090f4f70000000017a914e48c970e4a4faa6222688ec6333eb53c578e59f78700d012130000000017a9146a27966d76edfdfb3c53dc37dd5471bdeac7d46c870020a1070000000017a9147150055215791b779dddb1d112541a3fce71061b8700c0cf6a000000001976a91490f50a1fa5b280282de2a3ec15164a766fd48a0288ac0120000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
14
tests/blocks/nix_testnet_200.json
Normal file
14
tests/blocks/nix_testnet_200.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"hash": "9d5d8d308484a654cfc18fc6290734dca584bf0c02409429682a8b46715a0811",
|
||||
"size": 384,
|
||||
"height": 200,
|
||||
"merkleroot": "ee1377591fe82696a15f416476a28cf57a83af77e799bbfdff24909be06cc559",
|
||||
"tx": [
|
||||
"ee1377591fe82696a15f416476a28cf57a83af77e799bbfdff24909be06cc559"
|
||||
],
|
||||
"time": 1542147760,
|
||||
"nonce": 0,
|
||||
"bits": "1a2157b9",
|
||||
"previousblockhash": "1634ccd348860d24b5eabffc8782ae1aa1d6797477f0d4910cb25488a28cb372",
|
||||
"block": "0000002072b38ca28854b20c91d4f0777479d6a11aae8287fcbfeab5240d8648d3cc341659c56ce09b9024fffdbb99e777af837af58ca27664415fa19626e81f597713eeb04eeb5bb957211a00000000010203000001daf694ffcf73e2710b6148d89fc10b95cb55e255d804014c5141712168b9524c01000000171600147c5aad68a58a92630e3feab030fcaeb2cfff37feffffffff0530ecadf66b01000032b863a914f0c3b8f1fb7c1887a70c0108f729620c4c0901228767a914bdb5bb31196596f760ddaa49f7a82956e30b58de876830ecadf66b01000032b863a914f0c3b8f1fb7c1887a70c0108f729620c4c0901228767a914bdb5bb31196596f760ddaa49f7a82956e30b58de87680090d0030000000017a9147667be5313f1e3fb921a096b4b9b59c82b157ebe870090d0030000000017a914bf41e0caa3649bd8b9f4008103fb2a2757f3b8f3870000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf900000000473045022100b517ff2a36aee842e4ff750723b1cea0e7cc0064677d69735e4775a01245224802206db5bf42c16d76e790e97f741a10f576628c38bcb5a7072d1df4be9c1cd874e4"
|
||||
}
|
||||
14
tests/blocks/noir_mainnet_370000.json
Normal file
14
tests/blocks/noir_mainnet_370000.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"hash": "572d8a5897cad3f3a75811258513d321e8971c27ed210bb24eba4cd59697dc6d",
|
||||
"size": 230,
|
||||
"height": 370000,
|
||||
"merkleroot": "21000ff185207fc1b208ab1c03de33acb678d6ef542731426da5bfb08627d0b0",
|
||||
"tx": [
|
||||
"21000ff185207fc1b208ab1c03de33acb678d6ef542731426da5bfb08627d0b0"
|
||||
],
|
||||
"time": 1542089008,
|
||||
"nonce": 1713111040,
|
||||
"bits": "1d05e718",
|
||||
"previousblockhash": "cb9b3ff2a750f644e9389f50c508e9a4e217df2f07c5bc6a89a2e7fc82e2cdfe",
|
||||
"block": "00000020fecde282fce7a2896abcc5072fdf17e2a4e908c5509f38e944f650a7f23f9bcbb0d02786b0bfa56d42312754efd678b6ac33de031cab08b2c17f2085f10f00213069ea5b18e7051d00001c660101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1e0350a505043069ea5b083fff6fd4000000000b2f47666c6172652e696f2f000000000260b8131a000000001976a914122466aaa347290e3b3b7858f2ba041af97d1b4388ac20c46d30000000001976a914a26f5f0cf1b72ae0a5ab533f1e5f420876b3bf6988ac00000000"
|
||||
}
|
||||
16
tests/blocks/smartcash_mainnet_200000.json
Normal file
16
tests/blocks/smartcash_mainnet_200000.json
Normal file
@ -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"
|
||||
}
|
||||
16
tests/blocks/trezarcoin_mainnet_612581.json
Normal file
16
tests/blocks/trezarcoin_mainnet_612581.json
Normal file
@ -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"
|
||||
}
|
||||
16
tests/blocks/zcash_testnet_283046.json
Normal file
16
tests/blocks/zcash_testnet_283046.json
Normal file
File diff suppressed because one or more lines are too long
15
tests/blocks/zcash_testnet_304290.json
Normal file
15
tests/blocks/zcash_testnet_304290.json
Normal file
File diff suppressed because one or more lines are too long
15
tests/blocks/zelcash_mainnet_100000.json
Normal file
15
tests/blocks/zelcash_mainnet_100000.json
Normal file
File diff suppressed because one or more lines are too long
0
tests/lib/__init__.py
Normal file
0
tests/lib/__init__.py
Normal file
@ -67,6 +67,8 @@ addresses = [
|
||||
"ab72728952c06dfc0f6cf21449dd645422731ec4", "eb3a3155215538d51de7cc"),
|
||||
(coins.TokenPay, "TDE2X28FGtckatxuP3d8s3V726G4TLNHpT",
|
||||
"23b5dd9b7b402388c7a40bc88c261f3178acf30d", "7c7bdf0e0713f3752f4b88"),
|
||||
(coins.SmartCash, "SQFDM9NtRRmpHebq3H5RA3qpGJfGqp8Xgw",
|
||||
"206168f5322583ff37f8e55665a4789ae8963532", "b8cb80b26e8932f5b12a7e"),
|
||||
]
|
||||
|
||||
|
||||
|
||||
464
tests/lib/test_tx_dash.py
Normal file
464
tests/lib/test_tx_dash.py
Normal file
@ -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))
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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
|
||||
|
||||
47
tests/server/test_notifications.py
Normal file
47
tests/server/test_notifications.py
Normal file
@ -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'})]
|
||||
@ -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)
|
||||
|
||||
62
tests/transactions/namecoin_mainnet_037339.json
Normal file
62
tests/transactions/namecoin_mainnet_037339.json
Normal file
@ -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
|
||||
}
|
||||
52
tests/transactions/namecoin_mainnet_0c6867.json
Normal file
52
tests/transactions/namecoin_mainnet_0c6867.json
Normal file
@ -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
|
||||
}
|
||||
58
tests/transactions/namecoin_mainnet_460f2b.json
Normal file
58
tests/transactions/namecoin_mainnet_460f2b.json
Normal file
@ -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"
|
||||
}
|
||||
63
tests/transactions/namecoin_mainnet_5e3495.json
Normal file
63
tests/transactions/namecoin_mainnet_5e3495.json
Normal file
@ -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
|
||||
}
|
||||
62
tests/transactions/namecoin_mainnet_cfc5d7.json
Normal file
62
tests/transactions/namecoin_mainnet_cfc5d7.json
Normal file
@ -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
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user