The integration suite has never run against DragonX. Six independent
defects stacked up, each only visible once the previous was fixed:
1. test_framework used python2 implicit relative imports
("from authproxy import ..."), removed in python3, so every test died
at import. Made explicit relative imports.
2. It wrote ZZZ.conf -- a Komodo assetchain convention -- while DragonX
reads DRAGONX.conf. The daemon therefore never saw the generated
config, fell back to mainnet defaults and tried to bind RPC 21769,
which on a seed node is already held by the real node.
3. start_node() was hard-wired for the -ac_name=ZZZ assetchain tests: it
took the RPC port from extra_args[3], passed extra_args[0] as argv[0]
of the CLI, and only wrote a config when extra_args[0] matched. Any
test that passes no extra args crashed on len(None). The generic path
now takes the port from rpc_port(i) -- the same helper
initialize_datadir() already used -- and drives the CLI with -datadir.
4. dragonxd refuses to start without an asmap file, which no test datadir
had. initialize_datadir() now provisions one.
5. -asmap relative paths resolve against the NET-SPECIFIC datadir, so a
copy in <datadir> is never found. Pass an absolute path.
6. Worst: -regtest was only ever set as "regtest=1" in the conf file,
which DragonX ignores. Every "regtest" node therefore ran on MAINNET:
real genesis, real seeds, real peers. An observed run synced 196,180
live blocks and 679MB into /tmp before the test timed out. -regtest is
now passed as a command-line flag, with -connect=0 so an isolated
regtest node stays off the public network.
With these, nodes start, RPC answers, and tests run to a real result.
They do not all pass yet -- getblocktemplate.py reaches an assertion --
but that is now a test outcome rather than a harness failure.
135 lines
4.3 KiB
Python
135 lines
4.3 KiB
Python
#!/usr/bin/env python2
|
|
# Copyright (c) 2016-2024 The Hush developers
|
|
# Distributed under the GPLv3 software license, see the accompanying
|
|
# file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|
# BlockStore: a helper class that keeps a map of blocks and implements
|
|
# helper functions for responding to getheaders and getdata,
|
|
# and for constructing a getheaders message
|
|
#
|
|
|
|
from .mininode import CBlock, CBlockHeader, CBlockLocator, CTransaction, msg_block, msg_headers, msg_tx
|
|
|
|
import sys
|
|
import cStringIO
|
|
import dbm
|
|
|
|
class BlockStore(object):
|
|
def __init__(self, datadir):
|
|
self.blockDB = dbm.open(datadir + "/blocks", 'c')
|
|
self.currentBlock = 0L
|
|
|
|
def close(self):
|
|
self.blockDB.close()
|
|
|
|
def get(self, blockhash):
|
|
serialized_block = None
|
|
try:
|
|
serialized_block = self.blockDB[repr(blockhash)]
|
|
except KeyError:
|
|
return None
|
|
f = cStringIO.StringIO(serialized_block)
|
|
ret = CBlock()
|
|
ret.deserialize(f)
|
|
ret.calc_sha256()
|
|
return ret
|
|
|
|
# Note: this pulls full blocks out of the database just to retrieve
|
|
# the headers -- perhaps we could keep a separate data structure
|
|
# to avoid this overhead.
|
|
def headers_for(self, locator, hash_stop, current_tip=None):
|
|
if current_tip is None:
|
|
current_tip = self.currentBlock
|
|
current_block = self.get(current_tip)
|
|
if current_block is None:
|
|
return None
|
|
|
|
response = msg_headers()
|
|
headersList = [ CBlockHeader(current_block) ]
|
|
maxheaders = 2000
|
|
while (headersList[0].sha256 not in locator.vHave):
|
|
prevBlockHash = headersList[0].hashPrevBlock
|
|
prevBlock = self.get(prevBlockHash)
|
|
if prevBlock is not None:
|
|
headersList.insert(0, CBlockHeader(prevBlock))
|
|
else:
|
|
break
|
|
headersList = headersList[:maxheaders] # truncate if we have too many
|
|
hashList = [x.sha256 for x in headersList]
|
|
index = len(headersList)
|
|
if (hash_stop in hashList):
|
|
index = hashList.index(hash_stop)+1
|
|
response.headers = headersList[:index]
|
|
return response
|
|
|
|
def add_block(self, block):
|
|
block.calc_sha256()
|
|
try:
|
|
self.blockDB[repr(block.sha256)] = bytes(block.serialize())
|
|
except TypeError as e:
|
|
print "Unexpected error: ", sys.exc_info()[0], e.args
|
|
self.currentBlock = block.sha256
|
|
|
|
def get_blocks(self, inv):
|
|
responses = []
|
|
for i in inv:
|
|
if (i.type == 2): # MSG_BLOCK
|
|
block = self.get(i.hash)
|
|
if block is not None:
|
|
responses.append(msg_block(block))
|
|
return responses
|
|
|
|
def get_locator(self, current_tip=None):
|
|
if current_tip is None:
|
|
current_tip = self.currentBlock
|
|
r = []
|
|
counter = 0
|
|
step = 1
|
|
lastBlock = self.get(current_tip)
|
|
while lastBlock is not None:
|
|
r.append(lastBlock.hashPrevBlock)
|
|
for i in range(step):
|
|
lastBlock = self.get(lastBlock.hashPrevBlock)
|
|
if lastBlock is None:
|
|
break
|
|
counter += 1
|
|
if counter > 10:
|
|
step *= 2
|
|
locator = CBlockLocator()
|
|
locator.vHave = r
|
|
return locator
|
|
|
|
class TxStore(object):
|
|
def __init__(self, datadir):
|
|
self.txDB = dbm.open(datadir + "/transactions", 'c')
|
|
|
|
def close(self):
|
|
self.txDB.close()
|
|
|
|
def get(self, txhash):
|
|
serialized_tx = None
|
|
try:
|
|
serialized_tx = self.txDB[repr(txhash)]
|
|
except KeyError:
|
|
return None
|
|
f = cStringIO.StringIO(serialized_tx)
|
|
ret = CTransaction()
|
|
ret.deserialize(f)
|
|
ret.calc_sha256()
|
|
return ret
|
|
|
|
def add_transaction(self, tx):
|
|
tx.calc_sha256()
|
|
try:
|
|
self.txDB[repr(tx.sha256)] = bytes(tx.serialize())
|
|
except TypeError as e:
|
|
print "Unexpected error: ", sys.exc_info()[0], e.args
|
|
|
|
def get_transactions(self, inv):
|
|
responses = []
|
|
for i in inv:
|
|
if (i.type == 1): # MSG_TX
|
|
tx = self.get(i.hash)
|
|
if tx is not None:
|
|
responses.append(msg_tx(tx))
|
|
return responses
|