Initial Vexta Core v0.1.0 release
This commit is contained in:
commit
25c454329d
2481 changed files with 689289 additions and 0 deletions
97
qa/README.md
Normal file
97
qa/README.md
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
# Testing
|
||||
|
||||
The [pull-tester](/qa/pull-tester/) folder contains a script to call
|
||||
multiple tests from the [rpc-tests](/qa/rpc-tests/) folder.
|
||||
|
||||
Every pull request to the digibyte repository is built and run through
|
||||
the regression test suite. You can also run all or only individual
|
||||
tests locally.
|
||||
|
||||
## Test dependencies
|
||||
|
||||
Before running the tests, the following must be installed.
|
||||
|
||||
## Unix
|
||||
|
||||
The python3-zmq library is required. On Ubuntu or Debian it can be installed via:
|
||||
|
||||
```bash
|
||||
sudo apt-get install python3-zmq
|
||||
```
|
||||
|
||||
## OS X
|
||||
|
||||
```bash
|
||||
pip3 install pyzmq
|
||||
```
|
||||
|
||||
## Running tests
|
||||
|
||||
You can run any single test by calling
|
||||
|
||||
```bash
|
||||
qa/pull-tester/rpc-tests.py <testname>
|
||||
```
|
||||
|
||||
Or you can run any combination of tests by calling
|
||||
|
||||
```bash
|
||||
qa/pull-tester/rpc-tests.py <testname1> <testname2> <testname3> ...
|
||||
```
|
||||
|
||||
Run the regression test suite with
|
||||
|
||||
```bash
|
||||
qa/pull-tester/rpc-tests.py
|
||||
```
|
||||
|
||||
Run all possible tests with
|
||||
|
||||
```bash
|
||||
qa/pull-tester/rpc-tests.py -extended
|
||||
```
|
||||
|
||||
By default, tests will be run in parallel. To specify how many jobs to run,
|
||||
append `-parallel=n` (default n=4).
|
||||
|
||||
If you want to create a basic coverage report for the rpc test suite, append `--coverage`.
|
||||
|
||||
Possible options, which apply to each individual test run:
|
||||
|
||||
```bash
|
||||
-h, --help show this help message and exit
|
||||
--nocleanup Leave digibyteds and test.* datadir on exit or error
|
||||
--noshutdown Don't stop digibyteds after the test execution
|
||||
--srcdir=SRCDIR Source directory containing digibyted/digibyte-cli
|
||||
(default: ../../src)
|
||||
--tmpdir=TMPDIR Root directory for datadirs
|
||||
--tracerpc Print out all RPC calls as they are made
|
||||
--coveragedir=COVERAGEDIR
|
||||
Write tested RPC commands into this directory
|
||||
```
|
||||
|
||||
If you set the environment variable `PYTHON_DEBUG=1` you will get some debug
|
||||
output (example: `PYTHON_DEBUG=1 qa/pull-tester/rpc-tests.py wallet`).
|
||||
|
||||
A 200-block -regtest blockchain and wallets for four nodes
|
||||
is created the first time a regression test is run and
|
||||
is stored in the cache/ directory. Each node has 25 mature
|
||||
blocks (25*50=1250 DGB) in its wallet.
|
||||
|
||||
After the first run, the cache/ blockchain and wallets are
|
||||
copied into a temporary directory and used as the initial
|
||||
test state.
|
||||
|
||||
If you get into a bad state, you should be able
|
||||
to recover with:
|
||||
|
||||
```bash
|
||||
rm -rf cache
|
||||
killall digibyted
|
||||
```
|
||||
|
||||
## Writing tests
|
||||
|
||||
You are encouraged to write tests for new or existing features.
|
||||
Further information about the test framework and individual rpc
|
||||
tests is found in [qa/rpc-tests](/qa/rpc-tests).
|
||||
372
qa/pull-tester/rpc-tests.py
Executable file
372
qa/pull-tester/rpc-tests.py
Executable file
|
|
@ -0,0 +1,372 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
"""
|
||||
Run Regression Test Suite
|
||||
|
||||
This module calls down into individual test cases via subprocess. It will
|
||||
forward all unrecognized arguments onto the individual test scripts, other
|
||||
than:
|
||||
|
||||
- `-extended`: run the "extended" test suite in addition to the basic one.
|
||||
- `-win`: signal that this is running in a Windows environment, and we
|
||||
should run the tests.
|
||||
- `--coverage`: this generates a basic coverage report for the RPC
|
||||
interface.
|
||||
|
||||
For a description of arguments recognized by test scripts, see
|
||||
`qa/pull-tester/test_framework/test_framework.py:DigiByteTestFramework.main`.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
import shutil
|
||||
import sys
|
||||
import subprocess
|
||||
import tempfile
|
||||
import re
|
||||
|
||||
sys.path.append("qa/pull-tester/")
|
||||
from tests_config import *
|
||||
|
||||
BOLD = ("","")
|
||||
if os.name == 'posix':
|
||||
# primitive formatting on supported
|
||||
# terminal via ANSI escape sequences:
|
||||
BOLD = ('\033[0m', '\033[1m')
|
||||
|
||||
RPC_TESTS_DIR = SRCDIR + '/qa/rpc-tests/'
|
||||
|
||||
#If imported values are not defined then set to zero (or disabled)
|
||||
if 'ENABLE_WALLET' not in vars():
|
||||
ENABLE_WALLET=0
|
||||
if 'ENABLE_DIGIBYTED' not in vars():
|
||||
ENABLE_DIGIBYTED=0
|
||||
if 'ENABLE_UTILS' not in vars():
|
||||
ENABLE_UTILS=0
|
||||
if 'ENABLE_ZMQ' not in vars():
|
||||
ENABLE_ZMQ=0
|
||||
|
||||
ENABLE_COVERAGE=0
|
||||
|
||||
#Create a set to store arguments and create the passon string
|
||||
opts = set()
|
||||
passon_args = []
|
||||
PASSON_REGEX = re.compile("^--")
|
||||
PARALLEL_REGEX = re.compile('^-parallel=')
|
||||
|
||||
print_help = False
|
||||
run_parallel = 4
|
||||
|
||||
for arg in sys.argv[1:]:
|
||||
if arg == "--help" or arg == "-h" or arg == "-?":
|
||||
print_help = True
|
||||
break
|
||||
if arg == '--coverage':
|
||||
ENABLE_COVERAGE = 1
|
||||
elif PASSON_REGEX.match(arg):
|
||||
passon_args.append(arg)
|
||||
elif PARALLEL_REGEX.match(arg):
|
||||
run_parallel = int(arg.split(sep='=', maxsplit=1)[1])
|
||||
else:
|
||||
opts.add(arg)
|
||||
|
||||
#Set env vars
|
||||
if "DIGIBYTED" not in os.environ:
|
||||
os.environ["DIGIBYTED"] = BUILDDIR + '/src/digibyted' + EXEEXT
|
||||
|
||||
if EXEEXT == ".exe" and "-win" not in opts:
|
||||
# https://github.com/digibyte/digibyte/commit/d52802551752140cf41f0d9a225a43e84404d3e9
|
||||
# https://github.com/digibyte/digibyte/pull/5677#issuecomment-136646964
|
||||
print("Win tests currently disabled by default. Use -win option to enable")
|
||||
sys.exit(0)
|
||||
|
||||
if not (ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_DIGIBYTED == 1):
|
||||
print("No rpc tests to run. Wallet, utils, and digibyted must all be enabled")
|
||||
sys.exit(0)
|
||||
|
||||
# python3-zmq may not be installed. Handle this gracefully and with some helpful info
|
||||
if ENABLE_ZMQ:
|
||||
try:
|
||||
import zmq
|
||||
except ImportError:
|
||||
print("ERROR: \"import zmq\" failed. Set ENABLE_ZMQ=0 or "
|
||||
"to run zmq tests, see dependency info in /qa/README.md.")
|
||||
# ENABLE_ZMQ=0
|
||||
raise
|
||||
|
||||
testScripts = [
|
||||
# longest test should go first, to favor running tests in parallel
|
||||
'wallet-hd.py',
|
||||
'walletbackup.py',
|
||||
# vv Tests less than 5m vv
|
||||
'p2p-fullblocktest.py',
|
||||
'fundrawtransaction.py',
|
||||
'p2p-compactblocks.py',
|
||||
'segwit.py',
|
||||
# vv Tests less than 2m vv
|
||||
'wallet.py',
|
||||
'wallet-accounts.py',
|
||||
'p2p-segwit.py',
|
||||
'wallet-dump.py',
|
||||
'listtransactions.py',
|
||||
# vv Tests less than 60s vv
|
||||
'sendheaders.py',
|
||||
'zapwallettxes.py',
|
||||
'importmulti.py',
|
||||
'mempool_limit.py',
|
||||
'merkle_blocks.py',
|
||||
'receivedby.py',
|
||||
'abandonconflict.py',
|
||||
'bip68-112-113-p2p.py',
|
||||
'rawtransactions.py',
|
||||
'reindex.py',
|
||||
# vv Tests less than 30s vv
|
||||
'mempool_resurrect_test.py',
|
||||
'txn_doublespend.py --mineblock',
|
||||
'txn_clone.py',
|
||||
'getchaintips.py',
|
||||
'rest.py',
|
||||
'mempool_spendcoinbase.py',
|
||||
'mempool_reorg.py',
|
||||
'httpbasics.py',
|
||||
'multi_rpc.py',
|
||||
'proxy_test.py',
|
||||
'signrawtransactions.py',
|
||||
'nodehandling.py',
|
||||
'decodescript.py',
|
||||
'blockchain.py',
|
||||
'disablewallet.py',
|
||||
'keypool.py',
|
||||
'p2p-mempool.py',
|
||||
'prioritise_transaction.py',
|
||||
'invalidblockrequest.py',
|
||||
'invalidtxrequest.py',
|
||||
'p2p-versionbits-warning.py',
|
||||
'preciousblock.py',
|
||||
'importprunedfunds.py',
|
||||
'signmessages.py',
|
||||
'nulldummy.py',
|
||||
'import-rescan.py',
|
||||
'bumpfee.py',
|
||||
'rpcnamedargs.py',
|
||||
'listsinceblock.py',
|
||||
'p2p-leaktests.py',
|
||||
]
|
||||
if ENABLE_ZMQ:
|
||||
testScripts.append('zmq_test.py')
|
||||
|
||||
testScriptsExt = [
|
||||
'pruning.py',
|
||||
# vv Tests less than 20m vv
|
||||
'smartfees.py',
|
||||
# vv Tests less than 5m vv
|
||||
'maxuploadtarget.py',
|
||||
'mempool_packages.py',
|
||||
# vv Tests less than 2m vv
|
||||
'bip68-sequence.py',
|
||||
'getblocktemplate_longpoll.py',
|
||||
'p2p-timeouts.py',
|
||||
# vv Tests less than 60s vv
|
||||
'bip9-softforks.py',
|
||||
'p2p-feefilter.py',
|
||||
'rpcbind_test.py',
|
||||
# vv Tests less than 30s vv
|
||||
'bip65-cltv.py',
|
||||
'bip65-cltv-p2p.py',
|
||||
'bipdersig-p2p.py',
|
||||
'bipdersig.py',
|
||||
'getblocktemplate_proposals.py',
|
||||
'txn_doublespend.py',
|
||||
'txn_clone.py --mineblock',
|
||||
'forknotify.py',
|
||||
'invalidateblock.py',
|
||||
'maxblocksinflight.py',
|
||||
'p2p-acceptblock.py',
|
||||
'replace-by-fee.py',
|
||||
]
|
||||
|
||||
|
||||
def runtests():
|
||||
test_list = []
|
||||
if '-extended' in opts:
|
||||
test_list = testScripts + testScriptsExt
|
||||
elif len(opts) == 0 or (len(opts) == 1 and "-win" in opts):
|
||||
test_list = testScripts
|
||||
else:
|
||||
for t in testScripts + testScriptsExt:
|
||||
if t in opts or re.sub(".py$", "", t) in opts:
|
||||
test_list.append(t)
|
||||
|
||||
if print_help:
|
||||
# Only print help of the first script and exit
|
||||
subprocess.check_call((RPC_TESTS_DIR + test_list[0]).split() + ['-h'])
|
||||
sys.exit(0)
|
||||
|
||||
coverage = None
|
||||
|
||||
if ENABLE_COVERAGE:
|
||||
coverage = RPCCoverage()
|
||||
print("Initializing coverage directory at %s\n" % coverage.dir)
|
||||
flags = ["--srcdir=%s/src" % BUILDDIR] + passon_args
|
||||
flags.append("--cachedir=%s/qa/cache" % BUILDDIR)
|
||||
if coverage:
|
||||
flags.append(coverage.flag)
|
||||
|
||||
if len(test_list) > 1 and run_parallel > 1:
|
||||
# Populate cache
|
||||
subprocess.check_output([RPC_TESTS_DIR + 'create_cache.py'] + flags)
|
||||
|
||||
#Run Tests
|
||||
max_len_name = len(max(test_list, key=len))
|
||||
time_sum = 0
|
||||
time0 = time.time()
|
||||
job_queue = RPCTestHandler(run_parallel, test_list, flags)
|
||||
results = BOLD[1] + "%s | %s | %s\n\n" % ("TEST".ljust(max_len_name), "PASSED", "DURATION") + BOLD[0]
|
||||
all_passed = True
|
||||
for _ in range(len(test_list)):
|
||||
(name, stdout, stderr, passed, duration) = job_queue.get_next()
|
||||
all_passed = all_passed and passed
|
||||
time_sum += duration
|
||||
|
||||
print('\n' + BOLD[1] + name + BOLD[0] + ":")
|
||||
print('' if passed else stdout + '\n', end='')
|
||||
print('' if stderr == '' else 'stderr:\n' + stderr + '\n', end='')
|
||||
results += "%s | %s | %s s\n" % (name.ljust(max_len_name), str(passed).ljust(6), duration)
|
||||
print("Pass: %s%s%s, Duration: %s s\n" % (BOLD[1], passed, BOLD[0], duration))
|
||||
results += BOLD[1] + "\n%s | %s | %s s (accumulated)" % ("ALL".ljust(max_len_name), str(all_passed).ljust(6), time_sum) + BOLD[0]
|
||||
print(results)
|
||||
print("\nRuntime: %s s" % (int(time.time() - time0)))
|
||||
|
||||
if coverage:
|
||||
coverage.report_rpc_coverage()
|
||||
|
||||
print("Cleaning up coverage data")
|
||||
coverage.cleanup()
|
||||
|
||||
sys.exit(not all_passed)
|
||||
|
||||
|
||||
class RPCTestHandler:
|
||||
"""
|
||||
Trigger the testscrips passed in via the list.
|
||||
"""
|
||||
|
||||
def __init__(self, num_tests_parallel, test_list=None, flags=None):
|
||||
assert(num_tests_parallel >= 1)
|
||||
self.num_jobs = num_tests_parallel
|
||||
self.test_list = test_list
|
||||
self.flags = flags
|
||||
self.num_running = 0
|
||||
# In case there is a graveyard of zombie digibyteds, we can apply a
|
||||
# pseudorandom offset to hopefully jump over them.
|
||||
# (625 is PORT_RANGE/MAX_NODES)
|
||||
self.portseed_offset = int(time.time() * 1000) % 625
|
||||
self.jobs = []
|
||||
|
||||
def get_next(self):
|
||||
while self.num_running < self.num_jobs and self.test_list:
|
||||
# Add tests
|
||||
self.num_running += 1
|
||||
t = self.test_list.pop(0)
|
||||
port_seed = ["--portseed={}".format(len(self.test_list) + self.portseed_offset)]
|
||||
log_stdout = tempfile.SpooledTemporaryFile(max_size=2**16)
|
||||
log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16)
|
||||
self.jobs.append((t,
|
||||
time.time(),
|
||||
subprocess.Popen((RPC_TESTS_DIR + t).split() + self.flags + port_seed,
|
||||
universal_newlines=True,
|
||||
stdout=log_stdout,
|
||||
stderr=log_stderr),
|
||||
log_stdout,
|
||||
log_stderr))
|
||||
if not self.jobs:
|
||||
raise IndexError('pop from empty list')
|
||||
while True:
|
||||
# Return first proc that finishes
|
||||
time.sleep(.5)
|
||||
for j in self.jobs:
|
||||
(name, time0, proc, log_out, log_err) = j
|
||||
if proc.poll() is not None:
|
||||
log_out.seek(0), log_err.seek(0)
|
||||
[stdout, stderr] = [l.read().decode('utf-8') for l in (log_out, log_err)]
|
||||
log_out.close(), log_err.close()
|
||||
passed = stderr == "" and proc.returncode == 0
|
||||
self.num_running -= 1
|
||||
self.jobs.remove(j)
|
||||
return name, stdout, stderr, passed, int(time.time() - time0)
|
||||
print('.', end='', flush=True)
|
||||
|
||||
|
||||
class RPCCoverage(object):
|
||||
"""
|
||||
Coverage reporting utilities for pull-tester.
|
||||
|
||||
Coverage calculation works by having each test script subprocess write
|
||||
coverage files into a particular directory. These files contain the RPC
|
||||
commands invoked during testing, as well as a complete listing of RPC
|
||||
commands per `digibyte-cli help` (`rpc_interface.txt`).
|
||||
|
||||
After all tests complete, the commands run are combined and diff'd against
|
||||
the complete list to calculate uncovered RPC commands.
|
||||
|
||||
See also: qa/rpc-tests/test_framework/coverage.py
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
self.dir = tempfile.mkdtemp(prefix="coverage")
|
||||
self.flag = '--coveragedir=%s' % self.dir
|
||||
|
||||
def report_rpc_coverage(self):
|
||||
"""
|
||||
Print out RPC commands that were unexercised by tests.
|
||||
|
||||
"""
|
||||
uncovered = self._get_uncovered_rpc_commands()
|
||||
|
||||
if uncovered:
|
||||
print("Uncovered RPC commands:")
|
||||
print("".join((" - %s\n" % i) for i in sorted(uncovered)))
|
||||
else:
|
||||
print("All RPC commands covered.")
|
||||
|
||||
def cleanup(self):
|
||||
return shutil.rmtree(self.dir)
|
||||
|
||||
def _get_uncovered_rpc_commands(self):
|
||||
"""
|
||||
Return a set of currently untested RPC commands.
|
||||
|
||||
"""
|
||||
# This is shared from `qa/rpc-tests/test-framework/coverage.py`
|
||||
REFERENCE_FILENAME = 'rpc_interface.txt'
|
||||
COVERAGE_FILE_PREFIX = 'coverage.'
|
||||
|
||||
coverage_ref_filename = os.path.join(self.dir, REFERENCE_FILENAME)
|
||||
coverage_filenames = set()
|
||||
all_cmds = set()
|
||||
covered_cmds = set()
|
||||
|
||||
if not os.path.isfile(coverage_ref_filename):
|
||||
raise RuntimeError("No coverage reference found")
|
||||
|
||||
with open(coverage_ref_filename, 'r') as f:
|
||||
all_cmds.update([i.strip() for i in f.readlines()])
|
||||
|
||||
for root, dirs, files in os.walk(self.dir):
|
||||
for filename in files:
|
||||
if filename.startswith(COVERAGE_FILE_PREFIX):
|
||||
coverage_filenames.add(os.path.join(root, filename))
|
||||
|
||||
for filename in coverage_filenames:
|
||||
with open(filename, 'r') as f:
|
||||
covered_cmds.update([i.strip() for i in f.readlines()])
|
||||
|
||||
return all_cmds - covered_cmds
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
runtests()
|
||||
14
qa/pull-tester/tests_config.py
Executable file
14
qa/pull-tester/tests_config.py
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2013-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
SRCDIR="/Users/jaredtate/Code/digibyte"
|
||||
BUILDDIR="/Users/jaredtate/Code/digibyte"
|
||||
EXEEXT=""
|
||||
|
||||
# These will turn into comments if they were disabled when configuring.
|
||||
ENABLE_WALLET=1
|
||||
ENABLE_UTILS=1
|
||||
ENABLE_DIGIBYTED=1
|
||||
#ENABLE_ZMQ=1
|
||||
14
qa/pull-tester/tests_config.py.in
Normal file
14
qa/pull-tester/tests_config.py.in
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2013-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
SRCDIR="@abs_top_srcdir@"
|
||||
BUILDDIR="@abs_top_builddir@"
|
||||
EXEEXT="@EXEEXT@"
|
||||
|
||||
# These will turn into comments if they were disabled when configuring.
|
||||
@ENABLE_WALLET_TRUE@ENABLE_WALLET=1
|
||||
@BUILD_DIGIBYTE_UTILS_TRUE@ENABLE_UTILS=1
|
||||
@BUILD_DIGIBYTED_TRUE@ENABLE_DIGIBYTED=1
|
||||
@ENABLE_ZMQ_TRUE@ENABLE_ZMQ=1
|
||||
108
qa/rpc-tests/README.md
Normal file
108
qa/rpc-tests/README.md
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
Regression tests
|
||||
================
|
||||
|
||||
### [test_framework/authproxy.py](test_framework/authproxy.py)
|
||||
Taken from the [python-digibyterpc repository](https://github.com/jgarzik/python-digibyterpc).
|
||||
|
||||
### [test_framework/test_framework.py](test_framework/test_framework.py)
|
||||
Base class for new regression tests.
|
||||
|
||||
### [test_framework/util.py](test_framework/util.py)
|
||||
Generally useful functions.
|
||||
|
||||
### [test_framework/mininode.py](test_framework/mininode.py)
|
||||
Basic code to support p2p connectivity to a digibyted.
|
||||
|
||||
### [test_framework/comptool.py](test_framework/comptool.py)
|
||||
Framework for comparison-tool style, p2p tests.
|
||||
|
||||
### [test_framework/script.py](test_framework/script.py)
|
||||
Utilities for manipulating transaction scripts (originally from python-digibytelib)
|
||||
|
||||
### [test_framework/blockstore.py](test_framework/blockstore.py)
|
||||
Implements disk-backed block and tx storage.
|
||||
|
||||
### [test_framework/key.py](test_framework/key.py)
|
||||
Wrapper around OpenSSL EC_Key (originally from python-digibytelib)
|
||||
|
||||
### [test_framework/bignum.py](test_framework/bignum.py)
|
||||
Helpers for script.py
|
||||
|
||||
### [test_framework/blocktools.py](test_framework/blocktools.py)
|
||||
Helper functions for creating blocks and transactions.
|
||||
|
||||
P2P test design notes
|
||||
---------------------
|
||||
|
||||
## Mininode
|
||||
|
||||
* ```mininode.py``` contains all the definitions for objects that pass
|
||||
over the network (```CBlock```, ```CTransaction```, etc, along with the network-level
|
||||
wrappers for them, ```msg_block```, ```msg_tx```, etc).
|
||||
|
||||
* P2P tests have two threads. One thread handles all network communication
|
||||
with the digibyted(s) being tested (using python's asyncore package); the other
|
||||
implements the test logic.
|
||||
|
||||
* ```NodeConn``` is the class used to connect to a digibyted. If you implement
|
||||
a callback class that derives from ```NodeConnCB``` and pass that to the
|
||||
```NodeConn``` object, your code will receive the appropriate callbacks when
|
||||
events of interest arrive.
|
||||
|
||||
* You can pass the same handler to multiple ```NodeConn```'s if you like, or pass
|
||||
different ones to each -- whatever makes the most sense for your test.
|
||||
|
||||
* Call ```NetworkThread.start()``` after all ```NodeConn``` objects are created to
|
||||
start the networking thread. (Continue with the test logic in your existing
|
||||
thread.)
|
||||
|
||||
* RPC calls are available in p2p tests.
|
||||
|
||||
* Can be used to write free-form tests, where specific p2p-protocol behavior
|
||||
is tested. Examples: ```p2p-accept-block.py```, ```maxblocksinflight.py```.
|
||||
|
||||
## Comptool
|
||||
|
||||
* Testing framework for writing tests that compare the block/tx acceptance
|
||||
behavior of a digibyted against 1 or more other digibyted instances, or against
|
||||
known outcomes, or both.
|
||||
|
||||
* Set the ```num_nodes``` variable (defined in ```ComparisonTestFramework```) to start up
|
||||
1 or more nodes. If using 1 node, then ```--testbinary``` can be used as a command line
|
||||
option to change the digibyted binary used by the test. If using 2 or more nodes,
|
||||
then ```--refbinary``` can be optionally used to change the digibyted that will be used
|
||||
on nodes 2 and up.
|
||||
|
||||
* Implement a (generator) function called ```get_tests()``` which yields ```TestInstance```s.
|
||||
Each ```TestInstance``` consists of:
|
||||
- a list of ```[object, outcome, hash]``` entries
|
||||
* ```object``` is a ```CBlock```, ```CTransaction```, or
|
||||
```CBlockHeader```. ```CBlock```'s and ```CTransaction```'s are tested for
|
||||
acceptance. ```CBlockHeader```s can be used so that the test runner can deliver
|
||||
complete headers-chains when requested from the digibyted, to allow writing
|
||||
tests where blocks can be delivered out of order but still processed by
|
||||
headers-first digibyted's.
|
||||
* ```outcome``` is ```True```, ```False```, or ```None```. If ```True```
|
||||
or ```False```, the tip is compared with the expected tip -- either the
|
||||
block passed in, or the hash specified as the optional 3rd entry. If
|
||||
```None``` is specified, then the test will compare all the digibyted's
|
||||
being tested to see if they all agree on what the best tip is.
|
||||
* ```hash``` is the block hash of the tip to compare against. Optional to
|
||||
specify; if left out then the hash of the block passed in will be used as
|
||||
the expected tip. This allows for specifying an expected tip while testing
|
||||
the handling of either invalid blocks or blocks delivered out of order,
|
||||
which complete a longer chain.
|
||||
- ```sync_every_block```: ```True/False```. If ```False```, then all blocks
|
||||
are inv'ed together, and the test runner waits until the node receives the
|
||||
last one, and tests only the last block for tip acceptance using the
|
||||
outcome and specified tip. If ```True```, then each block is tested in
|
||||
sequence and synced (this is slower when processing many blocks).
|
||||
- ```sync_every_transaction```: ```True/False```. Analogous to
|
||||
```sync_every_block```, except if the outcome on the last tx is "None",
|
||||
then the contents of the entire mempool are compared across all digibyted
|
||||
connections. If ```True``` or ```False```, then only the last tx's
|
||||
acceptance is tested against the given outcome.
|
||||
|
||||
* For examples of tests written in this framework, see
|
||||
```invalidblockrequest.py``` and ```p2p-fullblocktest.py```.
|
||||
|
||||
181
qa/rpc-tests/bip65-cltv-p2p.py
Executable file
181
qa/rpc-tests/bip65-cltv-p2p.py
Executable file
|
|
@ -0,0 +1,181 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from test_framework.test_framework import ComparisonTestFramework
|
||||
from test_framework.util import *
|
||||
from test_framework.mininode import CTransaction, NetworkThread
|
||||
from test_framework.blocktools import create_coinbase, create_block
|
||||
from test_framework.comptool import TestInstance, TestManager
|
||||
from test_framework.script import CScript, OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP
|
||||
from io import BytesIO
|
||||
import time
|
||||
|
||||
def cltv_invalidate(tx):
|
||||
'''Modify the signature in vin 0 of the tx to fail CLTV
|
||||
|
||||
Prepends -1 CLTV DROP in the scriptSig itself.
|
||||
'''
|
||||
tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP] +
|
||||
list(CScript(tx.vin[0].scriptSig)))
|
||||
|
||||
'''
|
||||
This test is meant to exercise BIP65 (CHECKLOCKTIMEVERIFY)
|
||||
Connect to a single node.
|
||||
Mine 2 (version 3) blocks (save the coinbases for later).
|
||||
Generate 98 more version 3 blocks, verify the node accepts.
|
||||
Mine 749 version 4 blocks, verify the node accepts.
|
||||
Check that the new CLTV rules are not enforced on the 750th version 4 block.
|
||||
Check that the new CLTV rules are enforced on the 751st version 4 block.
|
||||
Mine 199 new version blocks.
|
||||
Mine 1 old-version block.
|
||||
Mine 1 new version block.
|
||||
Mine 1 old version block, see that the node rejects.
|
||||
'''
|
||||
|
||||
class BIP65Test(ComparisonTestFramework):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.num_nodes = 1
|
||||
|
||||
def setup_network(self):
|
||||
# Must set the blockversion for this test
|
||||
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir,
|
||||
extra_args=[['-debug', '-whitelist=127.0.0.1', '-blockversion=3']],
|
||||
binary=[self.options.testbinary])
|
||||
|
||||
def run_test(self):
|
||||
test = TestManager(self, self.options.tmpdir)
|
||||
test.add_all_connections(self.nodes)
|
||||
NetworkThread().start() # Start up network handling in another thread
|
||||
test.run()
|
||||
|
||||
def create_transaction(self, node, coinbase, to_address, amount):
|
||||
from_txid = node.getblock(coinbase)['tx'][0]
|
||||
inputs = [{ "txid" : from_txid, "vout" : 0}]
|
||||
outputs = { to_address : amount }
|
||||
rawtx = node.createrawtransaction(inputs, outputs)
|
||||
signresult = node.signrawtransaction(rawtx)
|
||||
tx = CTransaction()
|
||||
f = BytesIO(hex_str_to_bytes(signresult['hex']))
|
||||
tx.deserialize(f)
|
||||
return tx
|
||||
|
||||
def get_tests(self):
|
||||
|
||||
self.coinbase_blocks = self.nodes[0].generate(2)
|
||||
height = 3 # height of the next block to build
|
||||
self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0)
|
||||
self.nodeaddress = self.nodes[0].getnewaddress()
|
||||
self.last_block_time = int(time.time())
|
||||
|
||||
''' 398 more version 3 blocks '''
|
||||
test_blocks = []
|
||||
for i in range(398):
|
||||
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
|
||||
block.nVersion = 3
|
||||
block.rehash()
|
||||
block.solve()
|
||||
test_blocks.append([block, True])
|
||||
self.last_block_time += 1
|
||||
self.tip = block.sha256
|
||||
height += 1
|
||||
yield TestInstance(test_blocks, sync_every_block=False)
|
||||
|
||||
''' Mine 749 version 4 blocks '''
|
||||
test_blocks = []
|
||||
for i in range(749):
|
||||
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
|
||||
block.nVersion = 4
|
||||
block.rehash()
|
||||
block.solve()
|
||||
test_blocks.append([block, True])
|
||||
self.last_block_time += 1
|
||||
self.tip = block.sha256
|
||||
height += 1
|
||||
yield TestInstance(test_blocks, sync_every_block=False)
|
||||
|
||||
'''
|
||||
Check that the new CLTV rules are not enforced in the 750th
|
||||
version 3 block.
|
||||
'''
|
||||
spendtx = self.create_transaction(self.nodes[0],
|
||||
self.coinbase_blocks[0], self.nodeaddress, 1.0)
|
||||
cltv_invalidate(spendtx)
|
||||
spendtx.rehash()
|
||||
|
||||
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
|
||||
block.nVersion = 4
|
||||
block.vtx.append(spendtx)
|
||||
block.hashMerkleRoot = block.calc_merkle_root()
|
||||
block.rehash()
|
||||
block.solve()
|
||||
|
||||
self.last_block_time += 1
|
||||
self.tip = block.sha256
|
||||
height += 1
|
||||
yield TestInstance([[block, True]])
|
||||
|
||||
''' Mine 199 new version blocks on last valid tip '''
|
||||
test_blocks = []
|
||||
for i in range(199):
|
||||
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
|
||||
block.nVersion = 4
|
||||
block.rehash()
|
||||
block.solve()
|
||||
test_blocks.append([block, True])
|
||||
self.last_block_time += 1
|
||||
self.tip = block.sha256
|
||||
height += 1
|
||||
yield TestInstance(test_blocks, sync_every_block=False)
|
||||
|
||||
''' Mine 1 old version block '''
|
||||
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
|
||||
block.nVersion = 3
|
||||
block.rehash()
|
||||
block.solve()
|
||||
self.last_block_time += 1
|
||||
self.tip = block.sha256
|
||||
height += 1
|
||||
yield TestInstance([[block, True]])
|
||||
|
||||
''' Mine 1 new version block '''
|
||||
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
|
||||
block.nVersion = 4
|
||||
block.rehash()
|
||||
block.solve()
|
||||
self.last_block_time += 1
|
||||
self.tip = block.sha256
|
||||
height += 1
|
||||
yield TestInstance([[block, True]])
|
||||
|
||||
'''
|
||||
Check that the new CLTV rules are enforced in the 951st version 4
|
||||
block.
|
||||
'''
|
||||
spendtx = self.create_transaction(self.nodes[0],
|
||||
self.coinbase_blocks[1], self.nodeaddress, 1.0)
|
||||
cltv_invalidate(spendtx)
|
||||
spendtx.rehash()
|
||||
|
||||
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
|
||||
block.nVersion = 4
|
||||
block.vtx.append(spendtx)
|
||||
block.hashMerkleRoot = block.calc_merkle_root()
|
||||
block.rehash()
|
||||
block.solve()
|
||||
self.last_block_time += 1
|
||||
yield TestInstance([[block, False]])
|
||||
|
||||
''' Mine 1 old version block, should be invalid '''
|
||||
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
|
||||
block.nVersion = 3
|
||||
block.rehash()
|
||||
block.solve()
|
||||
self.last_block_time += 1
|
||||
yield TestInstance([[block, False]])
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIP65Test().main()
|
||||
92
qa/rpc-tests/bip65-cltv.py
Executable file
92
qa/rpc-tests/bip65-cltv.py
Executable file
|
|
@ -0,0 +1,92 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# Test the CHECKLOCKTIMEVERIFY (BIP65) soft-fork logic
|
||||
#
|
||||
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import *
|
||||
|
||||
class BIP65Test(DigiByteTestFramework):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.num_nodes = 3
|
||||
self.setup_clean_chain = False
|
||||
|
||||
def setup_network(self):
|
||||
self.nodes = []
|
||||
self.nodes.append(start_node(0, self.options.tmpdir, []))
|
||||
self.nodes.append(start_node(1, self.options.tmpdir, ["-blockversion=3"]))
|
||||
self.nodes.append(start_node(2, self.options.tmpdir, ["-blockversion=4"]))
|
||||
connect_nodes(self.nodes[1], 0)
|
||||
connect_nodes(self.nodes[2], 0)
|
||||
self.is_network_split = False
|
||||
self.sync_all()
|
||||
|
||||
def run_test(self):
|
||||
cnt = self.nodes[0].getblockcount()
|
||||
|
||||
# Mine some old-version blocks
|
||||
self.nodes[1].generate(200)
|
||||
cnt += 100
|
||||
self.sync_all()
|
||||
if (self.nodes[0].getblockcount() != cnt + 100):
|
||||
raise AssertionError("Failed to mine 100 version=3 blocks")
|
||||
|
||||
# Mine 750 new-version blocks
|
||||
for i in range(15):
|
||||
self.nodes[2].generate(50)
|
||||
self.sync_all()
|
||||
if (self.nodes[0].getblockcount() != cnt + 850):
|
||||
raise AssertionError("Failed to mine 750 version=4 blocks")
|
||||
|
||||
# TODO: check that new CHECKLOCKTIMEVERIFY rules are not enforced
|
||||
|
||||
# Mine 1 new-version block
|
||||
self.nodes[2].generate(1)
|
||||
self.sync_all()
|
||||
if (self.nodes[0].getblockcount() != cnt + 851):
|
||||
raise AssertionError("Failed to mine a version=4 blocks")
|
||||
|
||||
# TODO: check that new CHECKLOCKTIMEVERIFY rules are enforced
|
||||
|
||||
# Mine 198 new-version blocks
|
||||
for i in range(2):
|
||||
self.nodes[2].generate(99)
|
||||
self.sync_all()
|
||||
if (self.nodes[0].getblockcount() != cnt + 1049):
|
||||
raise AssertionError("Failed to mine 198 version=4 blocks")
|
||||
|
||||
# Mine 1 old-version block
|
||||
self.nodes[1].generate(1)
|
||||
self.sync_all()
|
||||
if (self.nodes[0].getblockcount() != cnt + 1050):
|
||||
raise AssertionError("Failed to mine a version=3 block after 949 version=4 blocks")
|
||||
|
||||
# Mine 1 new-version blocks
|
||||
self.nodes[2].generate(1)
|
||||
self.sync_all()
|
||||
if (self.nodes[0].getblockcount() != cnt + 1051):
|
||||
raise AssertionError("Failed to mine a version=4 block")
|
||||
|
||||
# Mine 1 old-version blocks
|
||||
try:
|
||||
self.nodes[1].generate(1)
|
||||
raise AssertionError("Succeeded to mine a version=3 block after 950 version=4 blocks")
|
||||
except JSONRPCException:
|
||||
pass
|
||||
self.sync_all()
|
||||
if (self.nodes[0].getblockcount() != cnt + 1051):
|
||||
raise AssertionError("Accepted a version=3 block after 950 version=4 blocks")
|
||||
|
||||
# Mine 1 new-version blocks
|
||||
self.nodes[2].generate(1)
|
||||
self.sync_all()
|
||||
if (self.nodes[0].getblockcount() != cnt + 1052):
|
||||
raise AssertionError("Failed to mine a version=4 block")
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIP65Test().main()
|
||||
189
qa/rpc-tests/bipdersig-p2p.py
Executable file
189
qa/rpc-tests/bipdersig-p2p.py
Executable file
|
|
@ -0,0 +1,189 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from test_framework.test_framework import ComparisonTestFramework
|
||||
from test_framework.util import *
|
||||
from test_framework.mininode import CTransaction, NetworkThread
|
||||
from test_framework.blocktools import create_coinbase, create_block
|
||||
from test_framework.comptool import TestInstance, TestManager
|
||||
from test_framework.script import CScript
|
||||
from io import BytesIO
|
||||
import time
|
||||
|
||||
# A canonical signature consists of:
|
||||
# <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
|
||||
def unDERify(tx):
|
||||
'''
|
||||
Make the signature in vin 0 of a tx non-DER-compliant,
|
||||
by adding padding after the S-value.
|
||||
'''
|
||||
scriptSig = CScript(tx.vin[0].scriptSig)
|
||||
newscript = []
|
||||
for i in scriptSig:
|
||||
if (len(newscript) == 0):
|
||||
newscript.append(i[0:-1] + b'\0' + i[-1:])
|
||||
else:
|
||||
newscript.append(i)
|
||||
tx.vin[0].scriptSig = CScript(newscript)
|
||||
|
||||
'''
|
||||
This test is meant to exercise BIP66 (DER SIG).
|
||||
Connect to a single node.
|
||||
Mine 2 (version 2) blocks (save the coinbases for later).
|
||||
Generate 98 more version 2 blocks, verify the node accepts.
|
||||
Mine 749 version 3 blocks, verify the node accepts.
|
||||
Check that the new DERSIG rules are not enforced on the 750th version 3 block.
|
||||
Check that the new DERSIG rules are enforced on the 751st version 3 block.
|
||||
Mine 199 new version blocks.
|
||||
Mine 1 old-version block.
|
||||
Mine 1 new version block.
|
||||
Mine 1 old version block, see that the node rejects.
|
||||
'''
|
||||
|
||||
class BIP66Test(ComparisonTestFramework):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.num_nodes = 1
|
||||
|
||||
def setup_network(self):
|
||||
# Must set the blockversion for this test
|
||||
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir,
|
||||
extra_args=[['-debug', '-whitelist=127.0.0.1', '-blockversion=2']],
|
||||
binary=[self.options.testbinary])
|
||||
|
||||
def run_test(self):
|
||||
test = TestManager(self, self.options.tmpdir)
|
||||
test.add_all_connections(self.nodes)
|
||||
NetworkThread().start() # Start up network handling in another thread
|
||||
test.run()
|
||||
|
||||
def create_transaction(self, node, coinbase, to_address, amount):
|
||||
from_txid = node.getblock(coinbase)['tx'][0]
|
||||
inputs = [{ "txid" : from_txid, "vout" : 0}]
|
||||
outputs = { to_address : amount }
|
||||
rawtx = node.createrawtransaction(inputs, outputs)
|
||||
signresult = node.signrawtransaction(rawtx)
|
||||
tx = CTransaction()
|
||||
f = BytesIO(hex_str_to_bytes(signresult['hex']))
|
||||
tx.deserialize(f)
|
||||
return tx
|
||||
|
||||
def get_tests(self):
|
||||
|
||||
self.coinbase_blocks = self.nodes[0].generate(2)
|
||||
height = 3 # height of the next block to build
|
||||
self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0)
|
||||
self.nodeaddress = self.nodes[0].getnewaddress()
|
||||
self.last_block_time = int(time.time())
|
||||
|
||||
''' 298 more version 2 blocks '''
|
||||
test_blocks = []
|
||||
for i in range(298):
|
||||
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
|
||||
block.nVersion = 2
|
||||
block.rehash()
|
||||
block.solve()
|
||||
test_blocks.append([block, True])
|
||||
self.last_block_time += 1
|
||||
self.tip = block.sha256
|
||||
height += 1
|
||||
yield TestInstance(test_blocks, sync_every_block=False)
|
||||
|
||||
''' Mine 749 version 3 blocks '''
|
||||
test_blocks = []
|
||||
for i in range(749):
|
||||
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
|
||||
block.nVersion = 3
|
||||
block.rehash()
|
||||
block.solve()
|
||||
test_blocks.append([block, True])
|
||||
self.last_block_time += 1
|
||||
self.tip = block.sha256
|
||||
height += 1
|
||||
yield TestInstance(test_blocks, sync_every_block=False)
|
||||
|
||||
'''
|
||||
Check that the new DERSIG rules are not enforced in the 750th
|
||||
version 3 block.
|
||||
'''
|
||||
spendtx = self.create_transaction(self.nodes[0],
|
||||
self.coinbase_blocks[0], self.nodeaddress, 1.0)
|
||||
unDERify(spendtx)
|
||||
spendtx.rehash()
|
||||
|
||||
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
|
||||
block.nVersion = 3
|
||||
block.vtx.append(spendtx)
|
||||
block.hashMerkleRoot = block.calc_merkle_root()
|
||||
block.rehash()
|
||||
block.solve()
|
||||
|
||||
self.last_block_time += 1
|
||||
self.tip = block.sha256
|
||||
height += 1
|
||||
yield TestInstance([[block, True]])
|
||||
|
||||
''' Mine 199 new version blocks on last valid tip '''
|
||||
test_blocks = []
|
||||
for i in range(199):
|
||||
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
|
||||
block.nVersion = 3
|
||||
block.rehash()
|
||||
block.solve()
|
||||
test_blocks.append([block, True])
|
||||
self.last_block_time += 1
|
||||
self.tip = block.sha256
|
||||
height += 1
|
||||
yield TestInstance(test_blocks, sync_every_block=False)
|
||||
|
||||
''' Mine 1 old version block '''
|
||||
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
|
||||
block.nVersion = 2
|
||||
block.rehash()
|
||||
block.solve()
|
||||
self.last_block_time += 1
|
||||
self.tip = block.sha256
|
||||
height += 1
|
||||
yield TestInstance([[block, True]])
|
||||
|
||||
''' Mine 1 new version block '''
|
||||
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
|
||||
block.nVersion = 3
|
||||
block.rehash()
|
||||
block.solve()
|
||||
self.last_block_time += 1
|
||||
self.tip = block.sha256
|
||||
height += 1
|
||||
yield TestInstance([[block, True]])
|
||||
|
||||
'''
|
||||
Check that the new DERSIG rules are enforced in the 951st version 3
|
||||
block.
|
||||
'''
|
||||
spendtx = self.create_transaction(self.nodes[0],
|
||||
self.coinbase_blocks[1], self.nodeaddress, 1.0)
|
||||
unDERify(spendtx)
|
||||
spendtx.rehash()
|
||||
|
||||
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
|
||||
block.nVersion = 3
|
||||
block.vtx.append(spendtx)
|
||||
block.hashMerkleRoot = block.calc_merkle_root()
|
||||
block.rehash()
|
||||
block.solve()
|
||||
self.last_block_time += 1
|
||||
yield TestInstance([[block, False]])
|
||||
|
||||
''' Mine 1 old version block, should be invalid '''
|
||||
block = create_block(self.tip, create_coinbase(height), self.last_block_time + 1)
|
||||
block.nVersion = 2
|
||||
block.rehash()
|
||||
block.solve()
|
||||
self.last_block_time += 1
|
||||
yield TestInstance([[block, False]])
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIP66Test().main()
|
||||
91
qa/rpc-tests/bipdersig.py
Executable file
91
qa/rpc-tests/bipdersig.py
Executable file
|
|
@ -0,0 +1,91 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# Test the BIP66 changeover logic
|
||||
#
|
||||
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import *
|
||||
|
||||
class BIP66Test(DigiByteTestFramework):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.num_nodes = 3
|
||||
self.setup_clean_chain = False
|
||||
|
||||
def setup_network(self):
|
||||
self.nodes = []
|
||||
self.nodes.append(start_node(0, self.options.tmpdir, []))
|
||||
self.nodes.append(start_node(1, self.options.tmpdir, ["-blockversion=2"]))
|
||||
self.nodes.append(start_node(2, self.options.tmpdir, ["-blockversion=3"]))
|
||||
connect_nodes(self.nodes[1], 0)
|
||||
connect_nodes(self.nodes[2], 0)
|
||||
self.is_network_split = False
|
||||
self.sync_all()
|
||||
|
||||
def run_test(self):
|
||||
cnt = self.nodes[0].getblockcount()
|
||||
|
||||
# Mine some old-version blocks
|
||||
self.nodes[1].generate(100)
|
||||
self.sync_all()
|
||||
if (self.nodes[0].getblockcount() != cnt + 100):
|
||||
raise AssertionError("Failed to mine 100 version=2 blocks")
|
||||
|
||||
# Mine 750 new-version blocks
|
||||
for i in range(15):
|
||||
self.nodes[2].generate(50)
|
||||
self.sync_all()
|
||||
if (self.nodes[0].getblockcount() != cnt + 850):
|
||||
raise AssertionError("Failed to mine 750 version=3 blocks")
|
||||
|
||||
# TODO: check that new DERSIG rules are not enforced
|
||||
|
||||
# Mine 1 new-version block
|
||||
self.nodes[2].generate(1)
|
||||
self.sync_all()
|
||||
if (self.nodes[0].getblockcount() != cnt + 851):
|
||||
raise AssertionError("Failed to mine a version=3 blocks")
|
||||
|
||||
# TODO: check that new DERSIG rules are enforced
|
||||
|
||||
# Mine 198 new-version blocks
|
||||
for i in range(2):
|
||||
self.nodes[2].generate(99)
|
||||
self.sync_all()
|
||||
if (self.nodes[0].getblockcount() != cnt + 1049):
|
||||
raise AssertionError("Failed to mine 198 version=3 blocks")
|
||||
|
||||
# Mine 1 old-version block
|
||||
self.nodes[1].generate(1)
|
||||
self.sync_all()
|
||||
if (self.nodes[0].getblockcount() != cnt + 1050):
|
||||
raise AssertionError("Failed to mine a version=2 block after 949 version=3 blocks")
|
||||
|
||||
# Mine 1 new-version blocks
|
||||
self.nodes[2].generate(1)
|
||||
self.sync_all()
|
||||
if (self.nodes[0].getblockcount() != cnt + 1051):
|
||||
raise AssertionError("Failed to mine a version=3 block")
|
||||
|
||||
# Mine 1 old-version blocks
|
||||
try:
|
||||
self.nodes[1].generate(1)
|
||||
raise AssertionError("Succeeded to mine a version=2 block after 950 version=3 blocks")
|
||||
except JSONRPCException:
|
||||
pass
|
||||
self.sync_all()
|
||||
if (self.nodes[0].getblockcount() != cnt + 1051):
|
||||
raise AssertionError("Accepted a version=2 block after 950 version=3 blocks")
|
||||
|
||||
# Mine 1 new-version blocks
|
||||
self.nodes[2].generate(1)
|
||||
self.sync_all()
|
||||
if (self.nodes[0].getblockcount() != cnt + 1052):
|
||||
raise AssertionError("Failed to mine a version=3 block")
|
||||
|
||||
if __name__ == '__main__':
|
||||
BIP66Test().main()
|
||||
89
qa/rpc-tests/blockchain.py
Executable file
89
qa/rpc-tests/blockchain.py
Executable file
|
|
@ -0,0 +1,89 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# Test RPC calls related to blockchain state. Tests correspond to code in
|
||||
# rpc/blockchain.cpp.
|
||||
#
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.authproxy import JSONRPCException
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
assert_raises,
|
||||
assert_is_hex_string,
|
||||
assert_is_hash_string,
|
||||
start_nodes,
|
||||
connect_nodes_bi,
|
||||
)
|
||||
|
||||
|
||||
class BlockchainTest(DigiByteTestFramework):
|
||||
"""
|
||||
Test blockchain-related RPC calls:
|
||||
|
||||
- gettxoutsetinfo
|
||||
- verifychain
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setup_clean_chain = False
|
||||
self.num_nodes = 2
|
||||
|
||||
def setup_network(self, split=False):
|
||||
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
|
||||
connect_nodes_bi(self.nodes, 0, 1)
|
||||
self.is_network_split = False
|
||||
self.sync_all()
|
||||
|
||||
def run_test(self):
|
||||
self._test_gettxoutsetinfo()
|
||||
self._test_getblockheader()
|
||||
self.nodes[0].verifychain(4, 0)
|
||||
|
||||
def _test_gettxoutsetinfo(self):
|
||||
node = self.nodes[0]
|
||||
res = node.gettxoutsetinfo()
|
||||
|
||||
assert_equal(res['total_amount'], Decimal('8725.00000000'))
|
||||
assert_equal(res['transactions'], 200)
|
||||
assert_equal(res['height'], 200)
|
||||
assert_equal(res['txouts'], 200)
|
||||
assert_equal(res['bytes_serialized'], 13924),
|
||||
assert_equal(len(res['bestblock']), 64)
|
||||
assert_equal(len(res['hash_serialized']), 64)
|
||||
|
||||
def _test_getblockheader(self):
|
||||
node = self.nodes[0]
|
||||
|
||||
assert_raises(
|
||||
JSONRPCException, lambda: node.getblockheader('nonsense'))
|
||||
|
||||
besthash = node.getbestblockhash()
|
||||
secondbesthash = node.getblockhash(199)
|
||||
header = node.getblockheader(besthash)
|
||||
|
||||
assert_equal(header['hash'], besthash)
|
||||
assert_equal(header['height'], 200)
|
||||
assert_equal(header['confirmations'], 1)
|
||||
assert_equal(header['previousblockhash'], secondbesthash)
|
||||
assert_is_hex_string(header['chainwork'])
|
||||
assert_is_hash_string(header['hash'])
|
||||
assert_is_hash_string(header['previousblockhash'])
|
||||
assert_is_hash_string(header['merkleroot'])
|
||||
assert_is_hash_string(header['bits'], length=None)
|
||||
assert isinstance(header['time'], int)
|
||||
assert isinstance(header['mediantime'], int)
|
||||
assert isinstance(header['nonce'], int)
|
||||
assert isinstance(header['version'], int)
|
||||
assert isinstance(int(header['versionHex'], 16), int)
|
||||
assert isinstance(header['difficulty'], Decimal)
|
||||
|
||||
if __name__ == '__main__':
|
||||
BlockchainTest().main()
|
||||
29
qa/rpc-tests/create_cache.py
Executable file
29
qa/rpc-tests/create_cache.py
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# Helper script to create the cache
|
||||
# (see DigiByteTestFramework.setup_chain)
|
||||
#
|
||||
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
|
||||
class CreateCache(DigiByteTestFramework):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# Test network and test nodes are not required:
|
||||
self.num_nodes = 0
|
||||
self.nodes = []
|
||||
|
||||
def setup_network(self):
|
||||
pass
|
||||
|
||||
def run_test(self):
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
CreateCache().main()
|
||||
48
qa/rpc-tests/disablewallet.py
Executable file
48
qa/rpc-tests/disablewallet.py
Executable file
|
|
@ -0,0 +1,48 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# Exercise API with -disablewallet.
|
||||
#
|
||||
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import *
|
||||
|
||||
|
||||
class DisableWalletTest (DigiByteTestFramework):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
|
||||
def setup_network(self, split=False):
|
||||
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [['-disablewallet']])
|
||||
self.is_network_split = False
|
||||
self.sync_all()
|
||||
|
||||
def run_test (self):
|
||||
# Check regression: https://github.com/digibyte/digibyte/issues/6963#issuecomment-154548880
|
||||
x = self.nodes[0].validateaddress('3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy')
|
||||
assert(x['isvalid'] == False)
|
||||
x = self.nodes[0].validateaddress('mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ')
|
||||
assert(x['isvalid'] == True)
|
||||
|
||||
# Checking mining to an address without a wallet
|
||||
try:
|
||||
self.nodes[0].generatetoaddress(1, 'mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ')
|
||||
except JSONRPCException as e:
|
||||
assert("Invalid address" not in e.error['message'])
|
||||
assert("ProcessNewBlock, block not accepted" not in e.error['message'])
|
||||
assert("Couldn't create new block" not in e.error['message'])
|
||||
|
||||
try:
|
||||
self.nodes[0].generatetoaddress(1, '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy')
|
||||
raise AssertionError("Must not mine to invalid address!")
|
||||
except JSONRPCException as e:
|
||||
assert("Invalid address" in e.error['message'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
DisableWalletTest ().main ()
|
||||
163
qa/rpc-tests/getblocktemplate_proposals.py
Executable file
163
qa/rpc-tests/getblocktemplate_proposals.py
Executable file
|
|
@ -0,0 +1,163 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import *
|
||||
|
||||
from binascii import a2b_hex, b2a_hex
|
||||
from hashlib import sha256
|
||||
from struct import pack
|
||||
|
||||
def b2x(b):
|
||||
return b2a_hex(b).decode('ascii')
|
||||
|
||||
# NOTE: This does not work for signed numbers (set the high bit) or zero (use b'\0')
|
||||
def encodeUNum(n):
|
||||
s = bytearray(b'\1')
|
||||
while n > 127:
|
||||
s[0] += 1
|
||||
s.append(n % 256)
|
||||
n //= 256
|
||||
s.append(n)
|
||||
return bytes(s)
|
||||
|
||||
def varlenEncode(n):
|
||||
if n < 0xfd:
|
||||
return pack('<B', n)
|
||||
if n <= 0xffff:
|
||||
return b'\xfd' + pack('<H', n)
|
||||
if n <= 0xffffffff:
|
||||
return b'\xfe' + pack('<L', n)
|
||||
return b'\xff' + pack('<Q', n)
|
||||
|
||||
def dblsha(b):
|
||||
return sha256(sha256(b).digest()).digest()
|
||||
|
||||
def genmrklroot(leaflist):
|
||||
cur = leaflist
|
||||
while len(cur) > 1:
|
||||
n = []
|
||||
if len(cur) & 1:
|
||||
cur.append(cur[-1])
|
||||
for i in range(0, len(cur), 2):
|
||||
n.append(dblsha(cur[i] + cur[i+1]))
|
||||
cur = n
|
||||
return cur[0]
|
||||
|
||||
def template_to_bytearray(tmpl, txlist):
|
||||
blkver = pack('<L', tmpl['version'])
|
||||
mrklroot = genmrklroot(list(dblsha(a) for a in txlist))
|
||||
timestamp = pack('<L', tmpl['curtime'])
|
||||
nonce = b'\0\0\0\0'
|
||||
blk = blkver + a2b_hex(tmpl['previousblockhash'])[::-1] + mrklroot + timestamp + a2b_hex(tmpl['bits'])[::-1] + nonce
|
||||
blk += varlenEncode(len(txlist))
|
||||
for tx in txlist:
|
||||
blk += tx
|
||||
return bytearray(blk)
|
||||
|
||||
def template_to_hex(tmpl, txlist):
|
||||
return b2x(template_to_bytearray(tmpl, txlist))
|
||||
|
||||
def assert_template(node, tmpl, txlist, expect):
|
||||
rsp = node.getblocktemplate({'data':template_to_hex(tmpl, txlist),'mode':'proposal'})
|
||||
if rsp != expect:
|
||||
raise AssertionError('unexpected: %s' % (rsp,))
|
||||
|
||||
class GetBlockTemplateProposalTest(DigiByteTestFramework):
|
||||
'''
|
||||
Test block proposals with getblocktemplate.
|
||||
'''
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.num_nodes = 2
|
||||
self.setup_clean_chain = False
|
||||
|
||||
def setup_network(self):
|
||||
self.nodes = self.setup_nodes()
|
||||
connect_nodes_bi(self.nodes, 0, 1)
|
||||
|
||||
def run_test(self):
|
||||
node = self.nodes[0]
|
||||
node.generate(1) # Mine a block to leave initial block download
|
||||
tmpl = node.getblocktemplate()
|
||||
if 'coinbasetxn' not in tmpl:
|
||||
rawcoinbase = encodeUNum(tmpl['height'])
|
||||
rawcoinbase += b'\x01-'
|
||||
hexcoinbase = b2x(rawcoinbase)
|
||||
hexoutval = b2x(pack('<Q', tmpl['coinbasevalue']))
|
||||
tmpl['coinbasetxn'] = {'data': '01000000' + '01' + '0000000000000000000000000000000000000000000000000000000000000000ffffffff' + ('%02x' % (len(rawcoinbase),)) + hexcoinbase + 'fffffffe' + '01' + hexoutval + '00' + '00000000'}
|
||||
txlist = list(bytearray(a2b_hex(a['data'])) for a in (tmpl['coinbasetxn'],) + tuple(tmpl['transactions']))
|
||||
|
||||
# Test 0: Capability advertised
|
||||
assert('proposal' in tmpl['capabilities'])
|
||||
|
||||
# NOTE: This test currently FAILS (regtest mode doesn't enforce block height in coinbase)
|
||||
## Test 1: Bad height in coinbase
|
||||
#txlist[0][4+1+36+1+1] += 1
|
||||
#assert_template(node, tmpl, txlist, 'FIXME')
|
||||
#txlist[0][4+1+36+1+1] -= 1
|
||||
|
||||
# Test 2: Bad input hash for gen tx
|
||||
txlist[0][4+1] += 1
|
||||
assert_template(node, tmpl, txlist, 'bad-cb-missing')
|
||||
txlist[0][4+1] -= 1
|
||||
|
||||
# Test 3: Truncated final tx
|
||||
lastbyte = txlist[-1].pop()
|
||||
assert_raises(JSONRPCException, assert_template, node, tmpl, txlist, 'n/a')
|
||||
txlist[-1].append(lastbyte)
|
||||
|
||||
# Test 4: Add an invalid tx to the end (duplicate of gen tx)
|
||||
txlist.append(txlist[0])
|
||||
assert_template(node, tmpl, txlist, 'bad-txns-duplicate')
|
||||
txlist.pop()
|
||||
|
||||
# Test 5: Add an invalid tx to the end (non-duplicate)
|
||||
txlist.append(bytearray(txlist[0]))
|
||||
txlist[-1][4+1] = 0xff
|
||||
assert_template(node, tmpl, txlist, 'bad-txns-inputs-missingorspent')
|
||||
txlist.pop()
|
||||
|
||||
# Test 6: Future tx lock time
|
||||
txlist[0][-4:] = b'\xff\xff\xff\xff'
|
||||
assert_template(node, tmpl, txlist, 'bad-txns-nonfinal')
|
||||
txlist[0][-4:] = b'\0\0\0\0'
|
||||
|
||||
# Test 7: Bad tx count
|
||||
txlist.append(b'')
|
||||
assert_raises(JSONRPCException, assert_template, node, tmpl, txlist, 'n/a')
|
||||
txlist.pop()
|
||||
|
||||
# Test 8: Bad bits
|
||||
realbits = tmpl['bits']
|
||||
tmpl['bits'] = '1c0000ff' # impossible in the real world
|
||||
assert_template(node, tmpl, txlist, 'bad-diffbits')
|
||||
tmpl['bits'] = realbits
|
||||
|
||||
# Test 9: Bad merkle root
|
||||
rawtmpl = template_to_bytearray(tmpl, txlist)
|
||||
rawtmpl[4+32] = (rawtmpl[4+32] + 1) % 0x100
|
||||
rsp = node.getblocktemplate({'data':b2x(rawtmpl),'mode':'proposal'})
|
||||
if rsp != 'bad-txnmrklroot':
|
||||
raise AssertionError('unexpected: %s' % (rsp,))
|
||||
|
||||
# Test 10: Bad timestamps
|
||||
realtime = tmpl['curtime']
|
||||
tmpl['curtime'] = 0x7fffffff
|
||||
assert_template(node, tmpl, txlist, 'time-too-new')
|
||||
tmpl['curtime'] = 0
|
||||
assert_template(node, tmpl, txlist, 'time-too-old')
|
||||
tmpl['curtime'] = realtime
|
||||
|
||||
# Test 11: Valid block
|
||||
assert_template(node, tmpl, txlist, None)
|
||||
|
||||
# Test 12: Orphan block
|
||||
tmpl['previousblockhash'] = 'ff00' * 16
|
||||
assert_template(node, tmpl, txlist, 'inconclusive-not-best-prevblk')
|
||||
|
||||
if __name__ == '__main__':
|
||||
GetBlockTemplateProposalTest().main()
|
||||
191
qa/rpc-tests/import-rescan.py
Executable file
191
qa/rpc-tests/import-rescan.py
Executable file
|
|
@ -0,0 +1,191 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test rescan behavior of importaddress, importpubkey, importprivkey, and
|
||||
importmulti RPCs with different types of keys and rescan options.
|
||||
|
||||
In the first part of the test, node 1 creates an address for each type of
|
||||
import RPC call and node 0 sends DGB to it. Then other nodes import the
|
||||
addresses, and the test makes listtransactions and getbalance calls to confirm
|
||||
that the importing node either did or did not execute rescans picking up the
|
||||
send transactions.
|
||||
|
||||
In the second part of the test, node 0 sends more DGB to each address, and the
|
||||
test makes more listtransactions and getbalance calls to confirm that the
|
||||
importing nodes pick up the new transactions regardless of whether rescans
|
||||
happened previously.
|
||||
"""
|
||||
|
||||
from test_framework.authproxy import JSONRPCException
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import (start_nodes, connect_nodes, sync_blocks, assert_equal, set_node_times)
|
||||
from decimal import Decimal
|
||||
|
||||
import collections
|
||||
import enum
|
||||
import itertools
|
||||
|
||||
Call = enum.Enum("Call", "single multi")
|
||||
Data = enum.Enum("Data", "address pub priv")
|
||||
Rescan = enum.Enum("Rescan", "no yes late_timestamp")
|
||||
|
||||
|
||||
class Variant(collections.namedtuple("Variant", "call data rescan prune")):
|
||||
"""Helper for importing one key and verifying scanned transactions."""
|
||||
|
||||
def do_import(self, timestamp):
|
||||
"""Call one key import RPC."""
|
||||
|
||||
if self.call == Call.single:
|
||||
if self.data == Data.address:
|
||||
response, error = try_rpc(self.node.importaddress, self.address["address"], self.label,
|
||||
self.rescan == Rescan.yes)
|
||||
elif self.data == Data.pub:
|
||||
response, error = try_rpc(self.node.importpubkey, self.address["pubkey"], self.label,
|
||||
self.rescan == Rescan.yes)
|
||||
elif self.data == Data.priv:
|
||||
response, error = try_rpc(self.node.importprivkey, self.key, self.label, self.rescan == Rescan.yes)
|
||||
assert_equal(response, None)
|
||||
assert_equal(error, {'message': 'Rescan is disabled in pruned mode',
|
||||
'code': -4} if self.expect_disabled else None)
|
||||
elif self.call == Call.multi:
|
||||
response = self.node.importmulti([{
|
||||
"scriptPubKey": {
|
||||
"address": self.address["address"]
|
||||
},
|
||||
"timestamp": timestamp + RESCAN_WINDOW + (1 if self.rescan == Rescan.late_timestamp else 0),
|
||||
"pubkeys": [self.address["pubkey"]] if self.data == Data.pub else [],
|
||||
"keys": [self.key] if self.data == Data.priv else [],
|
||||
"label": self.label,
|
||||
"watchonly": self.data != Data.priv
|
||||
}], {"rescan": self.rescan in (Rescan.yes, Rescan.late_timestamp)})
|
||||
assert_equal(response, [{"success": True}])
|
||||
|
||||
def check(self, txid=None, amount=None, confirmations=None):
|
||||
"""Verify that getbalance/listtransactions return expected values."""
|
||||
|
||||
balance = self.node.getbalance(self.label, 0, True)
|
||||
assert_equal(balance, self.expected_balance)
|
||||
|
||||
txs = self.node.listtransactions(self.label, 10000, 0, True)
|
||||
assert_equal(len(txs), self.expected_txs)
|
||||
|
||||
if txid is not None:
|
||||
tx, = [tx for tx in txs if tx["txid"] == txid]
|
||||
assert_equal(tx["account"], self.label)
|
||||
assert_equal(tx["address"], self.address["address"])
|
||||
assert_equal(tx["amount"], amount)
|
||||
assert_equal(tx["category"], "receive")
|
||||
assert_equal(tx["label"], self.label)
|
||||
assert_equal(tx["txid"], txid)
|
||||
assert_equal(tx["confirmations"], confirmations)
|
||||
assert_equal("trusted" not in tx, True)
|
||||
# Verify the transaction is correctly marked watchonly depending on
|
||||
# whether the transaction pays to an imported public key or
|
||||
# imported private key. The test setup ensures that transaction
|
||||
# inputs will not be from watchonly keys (important because
|
||||
# involvesWatchonly will be true if either the transaction output
|
||||
# or inputs are watchonly).
|
||||
if self.data != Data.priv:
|
||||
assert_equal(tx["involvesWatchonly"], True)
|
||||
else:
|
||||
assert_equal("involvesWatchonly" not in tx, True)
|
||||
|
||||
|
||||
# List of Variants for each way a key or address could be imported.
|
||||
IMPORT_VARIANTS = [Variant(*variants) for variants in itertools.product(Call, Data, Rescan, (False, True))]
|
||||
|
||||
# List of nodes to import keys to. Half the nodes will have pruning disabled,
|
||||
# half will have it enabled. Different nodes will be used for imports that are
|
||||
# expected to cause rescans, and imports that are not expected to cause
|
||||
# rescans, in order to prevent rescans during later imports picking up
|
||||
# transactions associated with earlier imports. This makes it easier to keep
|
||||
# track of expected balances and transactions.
|
||||
ImportNode = collections.namedtuple("ImportNode", "prune rescan")
|
||||
IMPORT_NODES = [ImportNode(*fields) for fields in itertools.product((False, True), repeat=2)]
|
||||
|
||||
# Rescans start at the earliest block up to 2 hours before the key timestamp.
|
||||
RESCAN_WINDOW = 2 * 60 * 60
|
||||
|
||||
|
||||
class ImportRescanTest(DigiByteTestFramework):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.num_nodes = 2 + len(IMPORT_NODES)
|
||||
|
||||
def setup_network(self):
|
||||
extra_args = [["-debug=1"] for _ in range(self.num_nodes)]
|
||||
for i, import_node in enumerate(IMPORT_NODES, 2):
|
||||
if import_node.prune:
|
||||
extra_args[i] += ["-prune=1"]
|
||||
|
||||
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
|
||||
for i in range(1, self.num_nodes):
|
||||
connect_nodes(self.nodes[i], 0)
|
||||
|
||||
def run_test(self):
|
||||
# Create one transaction on node 0 with a unique amount and label for
|
||||
# each possible type of wallet import RPC.
|
||||
for i, variant in enumerate(IMPORT_VARIANTS):
|
||||
variant.label = "label {} {}".format(i, variant)
|
||||
variant.address = self.nodes[1].validateaddress(self.nodes[1].getnewaddress(variant.label))
|
||||
variant.key = self.nodes[1].dumpprivkey(variant.address["address"])
|
||||
variant.initial_amount = 10 - (i + 1) / 4.0
|
||||
variant.initial_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.initial_amount)
|
||||
|
||||
# Generate a block containing the initial transactions, then another
|
||||
# block further in the future (past the rescan window).
|
||||
self.nodes[0].generate(1)
|
||||
assert_equal(self.nodes[0].getrawmempool(), [])
|
||||
timestamp = self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"]
|
||||
set_node_times(self.nodes, timestamp + RESCAN_WINDOW + 1)
|
||||
self.nodes[0].generate(1)
|
||||
sync_blocks(self.nodes)
|
||||
|
||||
# For each variation of wallet key import, invoke the import RPC and
|
||||
# check the results from getbalance and listtransactions.
|
||||
for variant in IMPORT_VARIANTS:
|
||||
variant.expect_disabled = variant.rescan == Rescan.yes and variant.prune and variant.call == Call.single
|
||||
expect_rescan = variant.rescan == Rescan.yes and not variant.expect_disabled
|
||||
variant.node = self.nodes[2 + IMPORT_NODES.index(ImportNode(variant.prune, expect_rescan))]
|
||||
variant.do_import(timestamp)
|
||||
if expect_rescan:
|
||||
variant.expected_balance = variant.initial_amount
|
||||
variant.expected_txs = 1
|
||||
variant.check(variant.initial_txid, variant.initial_amount, 2)
|
||||
else:
|
||||
variant.expected_balance = 0
|
||||
variant.expected_txs = 0
|
||||
variant.check()
|
||||
|
||||
# Create new transactions sending to each address.
|
||||
fee = self.nodes[0].getnetworkinfo()["relayfee"]
|
||||
for i, variant in enumerate(IMPORT_VARIANTS):
|
||||
variant.sent_amount = 10 - (2 * i + 1) / 8.0
|
||||
variant.sent_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.sent_amount)
|
||||
|
||||
# Generate a block containing the new transactions.
|
||||
self.nodes[0].generate(1)
|
||||
assert_equal(self.nodes[0].getrawmempool(), [])
|
||||
sync_blocks(self.nodes)
|
||||
|
||||
# Check the latest results from getbalance and listtransactions.
|
||||
for variant in IMPORT_VARIANTS:
|
||||
if not variant.expect_disabled:
|
||||
variant.expected_balance += variant.sent_amount
|
||||
variant.expected_txs += 1
|
||||
variant.check(variant.sent_txid, variant.sent_amount, 1)
|
||||
else:
|
||||
variant.check()
|
||||
|
||||
|
||||
def try_rpc(func, *args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs), None
|
||||
except JSONRPCException as e:
|
||||
return None, e.error
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
ImportRescanTest().main()
|
||||
85
qa/rpc-tests/keypool.py
Executable file
85
qa/rpc-tests/keypool.py
Executable file
|
|
@ -0,0 +1,85 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
# Exercise the wallet keypool, and interaction with wallet encryption/locking
|
||||
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import *
|
||||
|
||||
class KeyPoolTest(DigiByteTestFramework):
|
||||
|
||||
def run_test(self):
|
||||
nodes = self.nodes
|
||||
addr_before_encrypting = nodes[0].getnewaddress()
|
||||
addr_before_encrypting_data = nodes[0].validateaddress(addr_before_encrypting)
|
||||
wallet_info_old = nodes[0].getwalletinfo()
|
||||
assert(addr_before_encrypting_data['hdmasterkeyid'] == wallet_info_old['hdmasterkeyid'])
|
||||
|
||||
# Encrypt wallet and wait to terminate
|
||||
nodes[0].encryptwallet('test')
|
||||
digibyted_processes[0].wait()
|
||||
# Restart node 0
|
||||
nodes[0] = start_node(0, self.options.tmpdir)
|
||||
# Keep creating keys
|
||||
addr = nodes[0].getnewaddress()
|
||||
addr_data = nodes[0].validateaddress(addr)
|
||||
wallet_info = nodes[0].getwalletinfo()
|
||||
assert(addr_before_encrypting_data['hdmasterkeyid'] != wallet_info['hdmasterkeyid'])
|
||||
assert(addr_data['hdmasterkeyid'] == wallet_info['hdmasterkeyid'])
|
||||
|
||||
try:
|
||||
addr = nodes[0].getnewaddress()
|
||||
raise AssertionError('Keypool should be exhausted after one address')
|
||||
except JSONRPCException as e:
|
||||
assert(e.error['code']==-12)
|
||||
|
||||
# put three new keys in the keypool
|
||||
nodes[0].walletpassphrase('test', 12000)
|
||||
nodes[0].keypoolrefill(3)
|
||||
nodes[0].walletlock()
|
||||
|
||||
# drain the keys
|
||||
addr = set()
|
||||
addr.add(nodes[0].getrawchangeaddress())
|
||||
addr.add(nodes[0].getrawchangeaddress())
|
||||
addr.add(nodes[0].getrawchangeaddress())
|
||||
addr.add(nodes[0].getrawchangeaddress())
|
||||
# assert that four unique addresses were returned
|
||||
assert(len(addr) == 4)
|
||||
# the next one should fail
|
||||
try:
|
||||
addr = nodes[0].getrawchangeaddress()
|
||||
raise AssertionError('Keypool should be exhausted after three addresses')
|
||||
except JSONRPCException as e:
|
||||
assert(e.error['code']==-12)
|
||||
|
||||
# refill keypool with three new addresses
|
||||
nodes[0].walletpassphrase('test', 1)
|
||||
nodes[0].keypoolrefill(3)
|
||||
# test walletpassphrase timeout
|
||||
time.sleep(1.1)
|
||||
assert_equal(nodes[0].getwalletinfo()["unlocked_until"], 0)
|
||||
|
||||
# drain them by mining
|
||||
nodes[0].generate(1)
|
||||
nodes[0].generate(1)
|
||||
nodes[0].generate(1)
|
||||
nodes[0].generate(1)
|
||||
try:
|
||||
nodes[0].generate(1)
|
||||
raise AssertionError('Keypool should be exhausted after three addesses')
|
||||
except JSONRPCException as e:
|
||||
assert(e.error['code']==-12)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setup_clean_chain = False
|
||||
self.num_nodes = 1
|
||||
|
||||
def setup_network(self):
|
||||
self.nodes = self.setup_nodes()
|
||||
|
||||
if __name__ == '__main__':
|
||||
KeyPoolTest().main()
|
||||
80
qa/rpc-tests/listsinceblock.py
Executable file
80
qa/rpc-tests/listsinceblock.py
Executable file
|
|
@ -0,0 +1,80 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import assert_equal
|
||||
|
||||
class ListSinceBlockTest (DigiByteTestFramework):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 4
|
||||
|
||||
def run_test (self):
|
||||
'''
|
||||
`listsinceblock` did not behave correctly when handed a block that was
|
||||
no longer in the main chain:
|
||||
|
||||
ab0
|
||||
/ \
|
||||
aa1 [tx0] bb1
|
||||
| |
|
||||
aa2 bb2
|
||||
| |
|
||||
aa3 bb3
|
||||
|
|
||||
bb4
|
||||
|
||||
Consider a client that has only seen block `aa3` above. It asks the node
|
||||
to `listsinceblock aa3`. But at some point prior the main chain switched
|
||||
to the bb chain.
|
||||
|
||||
Previously: listsinceblock would find height=4 for block aa3 and compare
|
||||
this to height=5 for the tip of the chain (bb4). It would then return
|
||||
results restricted to bb3-bb4.
|
||||
|
||||
Now: listsinceblock finds the fork at ab0 and returns results in the
|
||||
range bb1-bb4.
|
||||
|
||||
This test only checks that [tx0] is present.
|
||||
'''
|
||||
|
||||
assert_equal(self.is_network_split, False)
|
||||
self.nodes[2].generate(101)
|
||||
self.sync_all()
|
||||
|
||||
assert_equal(self.nodes[0].getbalance(), 0)
|
||||
assert_equal(self.nodes[1].getbalance(), 0)
|
||||
assert_equal(self.nodes[2].getbalance(), 50)
|
||||
assert_equal(self.nodes[3].getbalance(), 0)
|
||||
|
||||
# Split network into two
|
||||
self.split_network()
|
||||
assert_equal(self.is_network_split, True)
|
||||
|
||||
# send to nodes[0] from nodes[2]
|
||||
senttx = self.nodes[2].sendtoaddress(self.nodes[0].getnewaddress(), 1)
|
||||
|
||||
# generate on both sides
|
||||
lastblockhash = self.nodes[1].generate(6)[5]
|
||||
self.nodes[2].generate(7)
|
||||
print('lastblockhash=%s' % (lastblockhash))
|
||||
|
||||
self.sync_all()
|
||||
|
||||
self.join_network()
|
||||
|
||||
# listsinceblock(lastblockhash) should now include tx, as seen from nodes[0]
|
||||
lsbres = self.nodes[0].listsinceblock(lastblockhash)
|
||||
found = False
|
||||
for tx in lsbres['transactions']:
|
||||
if tx['txid'] == senttx:
|
||||
found = True
|
||||
break
|
||||
assert_equal(found, True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
ListSinceBlockTest().main()
|
||||
96
qa/rpc-tests/maxblocksinflight.py
Executable file
96
qa/rpc-tests/maxblocksinflight.py
Executable file
|
|
@ -0,0 +1,96 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from test_framework.mininode import *
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import *
|
||||
import logging
|
||||
|
||||
'''
|
||||
In this test we connect to one node over p2p, send it numerous inv's, and
|
||||
compare the resulting number of getdata requests to a max allowed value. We
|
||||
test for exceeding 128 blocks in flight, which was the limit an 0.9 client will
|
||||
reach. [0.10 clients shouldn't request more than 16 from a single peer.]
|
||||
'''
|
||||
MAX_REQUESTS = 128
|
||||
|
||||
class TestManager(NodeConnCB):
|
||||
# set up NodeConnCB callbacks, overriding base class
|
||||
def on_getdata(self, conn, message):
|
||||
self.log.debug("got getdata %s" % repr(message))
|
||||
# Log the requests
|
||||
for inv in message.inv:
|
||||
if inv.hash not in self.blockReqCounts:
|
||||
self.blockReqCounts[inv.hash] = 0
|
||||
self.blockReqCounts[inv.hash] += 1
|
||||
|
||||
def on_close(self, conn):
|
||||
if not self.disconnectOkay:
|
||||
raise EarlyDisconnectError(0)
|
||||
|
||||
def __init__(self):
|
||||
NodeConnCB.__init__(self)
|
||||
self.log = logging.getLogger("BlockRelayTest")
|
||||
|
||||
def add_new_connection(self, connection):
|
||||
self.connection = connection
|
||||
self.blockReqCounts = {}
|
||||
self.disconnectOkay = False
|
||||
|
||||
def run(self):
|
||||
self.connection.rpc.generate(1) # Leave IBD
|
||||
|
||||
numBlocksToGenerate = [8, 16, 128, 1024]
|
||||
for count in range(len(numBlocksToGenerate)):
|
||||
current_invs = []
|
||||
for i in range(numBlocksToGenerate[count]):
|
||||
current_invs.append(CInv(2, random.randrange(0, 1 << 256)))
|
||||
if len(current_invs) >= 50000:
|
||||
self.connection.send_message(msg_inv(current_invs))
|
||||
current_invs = []
|
||||
if len(current_invs) > 0:
|
||||
self.connection.send_message(msg_inv(current_invs))
|
||||
|
||||
# Wait and see how many blocks were requested
|
||||
time.sleep(2)
|
||||
|
||||
total_requests = 0
|
||||
with mininode_lock:
|
||||
for key in self.blockReqCounts:
|
||||
total_requests += self.blockReqCounts[key]
|
||||
if self.blockReqCounts[key] > 1:
|
||||
raise AssertionError("Error, test failed: block %064x requested more than once" % key)
|
||||
if total_requests > MAX_REQUESTS:
|
||||
raise AssertionError("Error, too many blocks (%d) requested" % total_requests)
|
||||
print("Round %d: success (total requests: %d)" % (count, total_requests))
|
||||
|
||||
self.disconnectOkay = True
|
||||
self.connection.disconnect_node()
|
||||
|
||||
|
||||
class MaxBlocksInFlightTest(DigiByteTestFramework):
|
||||
def add_options(self, parser):
|
||||
parser.add_option("--testbinary", dest="testbinary",
|
||||
default=os.getenv("DIGIBYTED", "digibyted"),
|
||||
help="Binary to test max block requests behavior")
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
|
||||
def setup_network(self):
|
||||
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir,
|
||||
extra_args=[['-debug', '-whitelist=127.0.0.1']],
|
||||
binary=[self.options.testbinary])
|
||||
|
||||
def run_test(self):
|
||||
test = TestManager()
|
||||
test.add_new_connection(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test))
|
||||
NetworkThread().start() # Start up network handling in another thread
|
||||
test.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
MaxBlocksInFlightTest().main()
|
||||
85
qa/rpc-tests/nodehandling.py
Executable file
85
qa/rpc-tests/nodehandling.py
Executable file
|
|
@ -0,0 +1,85 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# Test node handling
|
||||
#
|
||||
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import *
|
||||
|
||||
import urllib.parse
|
||||
|
||||
class NodeHandlingTest (DigiByteTestFramework):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.num_nodes = 4
|
||||
self.setup_clean_chain = False
|
||||
|
||||
def run_test(self):
|
||||
###########################
|
||||
# setban/listbanned tests #
|
||||
###########################
|
||||
assert_equal(len(self.nodes[2].getpeerinfo()), 4) #we should have 4 nodes at this point
|
||||
self.nodes[2].setban("127.0.0.1", "add")
|
||||
time.sleep(3) #wait till the nodes are disconected
|
||||
assert_equal(len(self.nodes[2].getpeerinfo()), 0) #all nodes must be disconnected at this point
|
||||
assert_equal(len(self.nodes[2].listbanned()), 1)
|
||||
self.nodes[2].clearbanned()
|
||||
assert_equal(len(self.nodes[2].listbanned()), 0)
|
||||
self.nodes[2].setban("127.0.0.0/24", "add")
|
||||
assert_equal(len(self.nodes[2].listbanned()), 1)
|
||||
try:
|
||||
self.nodes[2].setban("127.0.0.1", "add") #throws exception because 127.0.0.1 is within range 127.0.0.0/24
|
||||
except:
|
||||
pass
|
||||
assert_equal(len(self.nodes[2].listbanned()), 1) #still only one banned ip because 127.0.0.1 is within the range of 127.0.0.0/24
|
||||
try:
|
||||
self.nodes[2].setban("127.0.0.1", "remove")
|
||||
except:
|
||||
pass
|
||||
assert_equal(len(self.nodes[2].listbanned()), 1)
|
||||
self.nodes[2].setban("127.0.0.0/24", "remove")
|
||||
assert_equal(len(self.nodes[2].listbanned()), 0)
|
||||
self.nodes[2].clearbanned()
|
||||
assert_equal(len(self.nodes[2].listbanned()), 0)
|
||||
|
||||
##test persisted banlist
|
||||
self.nodes[2].setban("127.0.0.0/32", "add")
|
||||
self.nodes[2].setban("127.0.0.0/24", "add")
|
||||
self.nodes[2].setban("192.168.0.1", "add", 1) #ban for 1 seconds
|
||||
self.nodes[2].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19", "add", 1000) #ban for 1000 seconds
|
||||
listBeforeShutdown = self.nodes[2].listbanned()
|
||||
assert_equal("192.168.0.1/32", listBeforeShutdown[2]['address']) #must be here
|
||||
time.sleep(2) #make 100% sure we expired 192.168.0.1 node time
|
||||
|
||||
#stop node
|
||||
stop_node(self.nodes[2], 2)
|
||||
|
||||
self.nodes[2] = start_node(2, self.options.tmpdir)
|
||||
listAfterShutdown = self.nodes[2].listbanned()
|
||||
assert_equal("127.0.0.0/24", listAfterShutdown[0]['address'])
|
||||
assert_equal("127.0.0.0/32", listAfterShutdown[1]['address'])
|
||||
assert_equal("/19" in listAfterShutdown[2]['address'], True)
|
||||
|
||||
###########################
|
||||
# RPC disconnectnode test #
|
||||
###########################
|
||||
url = urllib.parse.urlparse(self.nodes[1].url)
|
||||
self.nodes[0].disconnectnode(url.hostname+":"+str(p2p_port(1)))
|
||||
time.sleep(2) #disconnecting a node needs a little bit of time
|
||||
for node in self.nodes[0].getpeerinfo():
|
||||
assert(node['addr'] != url.hostname+":"+str(p2p_port(1)))
|
||||
|
||||
connect_nodes_bi(self.nodes,0,1) #reconnect the node
|
||||
found = False
|
||||
for node in self.nodes[0].getpeerinfo():
|
||||
if node['addr'] == url.hostname+":"+str(p2p_port(1)):
|
||||
found = True
|
||||
assert(found)
|
||||
|
||||
if __name__ == '__main__':
|
||||
NodeHandlingTest ().main ()
|
||||
291
qa/rpc-tests/p2p-acceptblock.py
Executable file
291
qa/rpc-tests/p2p-acceptblock.py
Executable file
|
|
@ -0,0 +1,291 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from test_framework.mininode import *
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import *
|
||||
import time
|
||||
from test_framework.blocktools import create_block, create_coinbase
|
||||
|
||||
'''
|
||||
AcceptBlockTest -- test processing of unrequested blocks.
|
||||
|
||||
Since behavior differs when receiving unrequested blocks from whitelisted peers
|
||||
versus non-whitelisted peers, this tests the behavior of both (effectively two
|
||||
separate tests running in parallel).
|
||||
|
||||
Setup: two nodes, node0 and node1, not connected to each other. Node0 does not
|
||||
whitelist localhost, but node1 does. They will each be on their own chain for
|
||||
this test.
|
||||
|
||||
We have one NodeConn connection to each, test_node and white_node respectively.
|
||||
|
||||
The test:
|
||||
1. Generate one block on each node, to leave IBD.
|
||||
|
||||
2. Mine a new block on each tip, and deliver to each node from node's peer.
|
||||
The tip should advance.
|
||||
|
||||
3. Mine a block that forks the previous block, and deliver to each node from
|
||||
corresponding peer.
|
||||
Node0 should not process this block (just accept the header), because it is
|
||||
unrequested and doesn't have more work than the tip.
|
||||
Node1 should process because this is coming from a whitelisted peer.
|
||||
|
||||
4. Send another block that builds on the forking block.
|
||||
Node0 should process this block but be stuck on the shorter chain, because
|
||||
it's missing an intermediate block.
|
||||
Node1 should reorg to this longer chain.
|
||||
|
||||
4b.Send 288 more blocks on the longer chain.
|
||||
Node0 should process all but the last block (too far ahead in height).
|
||||
Send all headers to Node1, and then send the last block in that chain.
|
||||
Node1 should accept the block because it's coming from a whitelisted peer.
|
||||
|
||||
5. Send a duplicate of the block in #3 to Node0.
|
||||
Node0 should not process the block because it is unrequested, and stay on
|
||||
the shorter chain.
|
||||
|
||||
6. Send Node0 an inv for the height 3 block produced in #4 above.
|
||||
Node0 should figure out that Node0 has the missing height 2 block and send a
|
||||
getdata.
|
||||
|
||||
7. Send Node0 the missing block again.
|
||||
Node0 should process and the tip should advance.
|
||||
'''
|
||||
|
||||
# TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending
|
||||
# p2p messages to a node, generating the messages in the main testing logic.
|
||||
class TestNode(NodeConnCB):
|
||||
def __init__(self):
|
||||
NodeConnCB.__init__(self)
|
||||
self.connection = None
|
||||
self.ping_counter = 1
|
||||
self.last_pong = msg_pong()
|
||||
|
||||
def add_connection(self, conn):
|
||||
self.connection = conn
|
||||
|
||||
# Track the last getdata message we receive (used in the test)
|
||||
def on_getdata(self, conn, message):
|
||||
self.last_getdata = message
|
||||
|
||||
# Spin until verack message is received from the node.
|
||||
# We use this to signal that our test can begin. This
|
||||
# is called from the testing thread, so it needs to acquire
|
||||
# the global lock.
|
||||
def wait_for_verack(self):
|
||||
while True:
|
||||
with mininode_lock:
|
||||
if self.verack_received:
|
||||
return
|
||||
time.sleep(0.05)
|
||||
|
||||
# Wrapper for the NodeConn's send_message function
|
||||
def send_message(self, message):
|
||||
self.connection.send_message(message)
|
||||
|
||||
def on_pong(self, conn, message):
|
||||
self.last_pong = message
|
||||
|
||||
# Sync up with the node after delivery of a block
|
||||
def sync_with_ping(self, timeout=30):
|
||||
self.connection.send_message(msg_ping(nonce=self.ping_counter))
|
||||
received_pong = False
|
||||
sleep_time = 0.05
|
||||
while not received_pong and timeout > 0:
|
||||
time.sleep(sleep_time)
|
||||
timeout -= sleep_time
|
||||
with mininode_lock:
|
||||
if self.last_pong.nonce == self.ping_counter:
|
||||
received_pong = True
|
||||
self.ping_counter += 1
|
||||
return received_pong
|
||||
|
||||
|
||||
class AcceptBlockTest(DigiByteTestFramework):
|
||||
def add_options(self, parser):
|
||||
parser.add_option("--testbinary", dest="testbinary",
|
||||
default=os.getenv("DIGIBYTED", "digibyted"),
|
||||
help="digibyted binary to test")
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 2
|
||||
|
||||
def setup_network(self):
|
||||
# Node0 will be used to test behavior of processing unrequested blocks
|
||||
# from peers which are not whitelisted, while Node1 will be used for
|
||||
# the whitelisted case.
|
||||
self.nodes = []
|
||||
self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"],
|
||||
binary=self.options.testbinary))
|
||||
self.nodes.append(start_node(1, self.options.tmpdir,
|
||||
["-debug", "-whitelist=127.0.0.1"],
|
||||
binary=self.options.testbinary))
|
||||
|
||||
def run_test(self):
|
||||
# Setup the p2p connections and start up the network thread.
|
||||
test_node = TestNode() # connects to node0 (not whitelisted)
|
||||
white_node = TestNode() # connects to node1 (whitelisted)
|
||||
|
||||
connections = []
|
||||
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node))
|
||||
connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], white_node))
|
||||
test_node.add_connection(connections[0])
|
||||
white_node.add_connection(connections[1])
|
||||
|
||||
NetworkThread().start() # Start up network handling in another thread
|
||||
|
||||
# Test logic begins here
|
||||
test_node.wait_for_verack()
|
||||
white_node.wait_for_verack()
|
||||
|
||||
# 1. Have both nodes mine a block (leave IBD)
|
||||
[ n.generate(1) for n in self.nodes ]
|
||||
tips = [ int("0x" + n.getbestblockhash(), 0) for n in self.nodes ]
|
||||
|
||||
# 2. Send one block that builds on each tip.
|
||||
# This should be accepted.
|
||||
blocks_h2 = [] # the height 2 blocks on each node's chain
|
||||
block_time = int(time.time()) + 1
|
||||
for i in range(2):
|
||||
blocks_h2.append(create_block(tips[i], create_coinbase(2), block_time))
|
||||
blocks_h2[i].solve()
|
||||
block_time += 1
|
||||
test_node.send_message(msg_block(blocks_h2[0]))
|
||||
white_node.send_message(msg_block(blocks_h2[1]))
|
||||
|
||||
[ x.sync_with_ping() for x in [test_node, white_node] ]
|
||||
assert_equal(self.nodes[0].getblockcount(), 2)
|
||||
assert_equal(self.nodes[1].getblockcount(), 2)
|
||||
print("First height 2 block accepted by both nodes")
|
||||
|
||||
# 3. Send another block that builds on the original tip.
|
||||
blocks_h2f = [] # Blocks at height 2 that fork off the main chain
|
||||
for i in range(2):
|
||||
blocks_h2f.append(create_block(tips[i], create_coinbase(2), blocks_h2[i].nTime+1))
|
||||
blocks_h2f[i].solve()
|
||||
test_node.send_message(msg_block(blocks_h2f[0]))
|
||||
white_node.send_message(msg_block(blocks_h2f[1]))
|
||||
|
||||
[ x.sync_with_ping() for x in [test_node, white_node] ]
|
||||
for x in self.nodes[0].getchaintips():
|
||||
if x['hash'] == blocks_h2f[0].hash:
|
||||
assert_equal(x['status'], "headers-only")
|
||||
|
||||
for x in self.nodes[1].getchaintips():
|
||||
if x['hash'] == blocks_h2f[1].hash:
|
||||
assert_equal(x['status'], "valid-headers")
|
||||
|
||||
print("Second height 2 block accepted only from whitelisted peer")
|
||||
|
||||
# 4. Now send another block that builds on the forking chain.
|
||||
blocks_h3 = []
|
||||
for i in range(2):
|
||||
blocks_h3.append(create_block(blocks_h2f[i].sha256, create_coinbase(3), blocks_h2f[i].nTime+1))
|
||||
blocks_h3[i].solve()
|
||||
test_node.send_message(msg_block(blocks_h3[0]))
|
||||
white_node.send_message(msg_block(blocks_h3[1]))
|
||||
|
||||
[ x.sync_with_ping() for x in [test_node, white_node] ]
|
||||
# Since the earlier block was not processed by node0, the new block
|
||||
# can't be fully validated.
|
||||
for x in self.nodes[0].getchaintips():
|
||||
if x['hash'] == blocks_h3[0].hash:
|
||||
assert_equal(x['status'], "headers-only")
|
||||
|
||||
# But this block should be accepted by node0 since it has more work.
|
||||
try:
|
||||
self.nodes[0].getblock(blocks_h3[0].hash)
|
||||
print("Unrequested more-work block accepted from non-whitelisted peer")
|
||||
except:
|
||||
raise AssertionError("Unrequested more work block was not processed")
|
||||
|
||||
# Node1 should have accepted and reorged.
|
||||
assert_equal(self.nodes[1].getblockcount(), 3)
|
||||
print("Successfully reorged to length 3 chain from whitelisted peer")
|
||||
|
||||
# 4b. Now mine 288 more blocks and deliver; all should be processed but
|
||||
# the last (height-too-high) on node0. Node1 should process the tip if
|
||||
# we give it the headers chain leading to the tip.
|
||||
tips = blocks_h3
|
||||
headers_message = msg_headers()
|
||||
all_blocks = [] # node0's blocks
|
||||
for j in range(2):
|
||||
for i in range(288):
|
||||
next_block = create_block(tips[j].sha256, create_coinbase(i + 4), tips[j].nTime+1)
|
||||
next_block.solve()
|
||||
if j==0:
|
||||
test_node.send_message(msg_block(next_block))
|
||||
all_blocks.append(next_block)
|
||||
else:
|
||||
headers_message.headers.append(CBlockHeader(next_block))
|
||||
tips[j] = next_block
|
||||
|
||||
time.sleep(2)
|
||||
for x in all_blocks:
|
||||
try:
|
||||
self.nodes[0].getblock(x.hash)
|
||||
if x == all_blocks[287]:
|
||||
raise AssertionError("Unrequested block too far-ahead should have been ignored")
|
||||
except:
|
||||
if x == all_blocks[287]:
|
||||
print("Unrequested block too far-ahead not processed")
|
||||
else:
|
||||
raise AssertionError("Unrequested block with more work should have been accepted")
|
||||
|
||||
headers_message.headers.pop() # Ensure the last block is unrequested
|
||||
white_node.send_message(headers_message) # Send headers leading to tip
|
||||
white_node.send_message(msg_block(tips[1])) # Now deliver the tip
|
||||
try:
|
||||
white_node.sync_with_ping()
|
||||
self.nodes[1].getblock(tips[1].hash)
|
||||
print("Unrequested block far ahead of tip accepted from whitelisted peer")
|
||||
except:
|
||||
raise AssertionError("Unrequested block from whitelisted peer not accepted")
|
||||
|
||||
# 5. Test handling of unrequested block on the node that didn't process
|
||||
# Should still not be processed (even though it has a child that has more
|
||||
# work).
|
||||
test_node.send_message(msg_block(blocks_h2f[0]))
|
||||
|
||||
# Here, if the sleep is too short, the test could falsely succeed (if the
|
||||
# node hasn't processed the block by the time the sleep returns, and then
|
||||
# the node processes it and incorrectly advances the tip).
|
||||
# But this would be caught later on, when we verify that an inv triggers
|
||||
# a getdata request for this block.
|
||||
test_node.sync_with_ping()
|
||||
assert_equal(self.nodes[0].getblockcount(), 2)
|
||||
print("Unrequested block that would complete more-work chain was ignored")
|
||||
|
||||
# 6. Try to get node to request the missing block.
|
||||
# Poke the node with an inv for block at height 3 and see if that
|
||||
# triggers a getdata on block 2 (it should if block 2 is missing).
|
||||
with mininode_lock:
|
||||
# Clear state so we can check the getdata request
|
||||
test_node.last_getdata = None
|
||||
test_node.send_message(msg_inv([CInv(2, blocks_h3[0].sha256)]))
|
||||
|
||||
test_node.sync_with_ping()
|
||||
with mininode_lock:
|
||||
getdata = test_node.last_getdata
|
||||
|
||||
# Check that the getdata includes the right block
|
||||
assert_equal(getdata.inv[0].hash, blocks_h2f[0].sha256)
|
||||
print("Inv at tip triggered getdata for unprocessed block")
|
||||
|
||||
# 7. Send the missing block for the third time (now it is requested)
|
||||
test_node.send_message(msg_block(blocks_h2f[0]))
|
||||
|
||||
test_node.sync_with_ping()
|
||||
assert_equal(self.nodes[0].getblockcount(), 290)
|
||||
print("Successfully reorged to longer chain from non-whitelisted peer")
|
||||
|
||||
[ c.disconnect_node() for c in connections ]
|
||||
|
||||
if __name__ == '__main__':
|
||||
AcceptBlockTest().main()
|
||||
145
qa/rpc-tests/p2p-leaktests.py
Executable file
145
qa/rpc-tests/p2p-leaktests.py
Executable file
|
|
@ -0,0 +1,145 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from test_framework.mininode import *
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import *
|
||||
|
||||
'''
|
||||
Test for message sending before handshake completion
|
||||
|
||||
A node should never send anything other than VERSION/VERACK/REJECT until it's
|
||||
received a VERACK.
|
||||
|
||||
This test connects to a node and sends it a few messages, trying to intice it
|
||||
into sending us something it shouldn't.
|
||||
'''
|
||||
|
||||
banscore = 10
|
||||
|
||||
class CLazyNode(NodeConnCB):
|
||||
def __init__(self):
|
||||
self.connection = None
|
||||
self.unexpected_msg = False
|
||||
self.connected = False
|
||||
super().__init__()
|
||||
|
||||
def add_connection(self, conn):
|
||||
self.connection = conn
|
||||
|
||||
def send_message(self, message):
|
||||
self.connection.send_message(message)
|
||||
|
||||
def bad_message(self, message):
|
||||
self.unexpected_msg = True
|
||||
print("should not have received message: %s" % message.command)
|
||||
|
||||
def on_open(self, conn):
|
||||
self.connected = True
|
||||
|
||||
def on_version(self, conn, message): self.bad_message(message)
|
||||
def on_verack(self, conn, message): self.bad_message(message)
|
||||
def on_reject(self, conn, message): self.bad_message(message)
|
||||
def on_inv(self, conn, message): self.bad_message(message)
|
||||
def on_addr(self, conn, message): self.bad_message(message)
|
||||
def on_alert(self, conn, message): self.bad_message(message)
|
||||
def on_getdata(self, conn, message): self.bad_message(message)
|
||||
def on_getblocks(self, conn, message): self.bad_message(message)
|
||||
def on_tx(self, conn, message): self.bad_message(message)
|
||||
def on_block(self, conn, message): self.bad_message(message)
|
||||
def on_getaddr(self, conn, message): self.bad_message(message)
|
||||
def on_headers(self, conn, message): self.bad_message(message)
|
||||
def on_getheaders(self, conn, message): self.bad_message(message)
|
||||
def on_ping(self, conn, message): self.bad_message(message)
|
||||
def on_mempool(self, conn): self.bad_message(message)
|
||||
def on_pong(self, conn, message): self.bad_message(message)
|
||||
def on_feefilter(self, conn, message): self.bad_message(message)
|
||||
def on_sendheaders(self, conn, message): self.bad_message(message)
|
||||
def on_sendcmpct(self, conn, message): self.bad_message(message)
|
||||
def on_cmpctblock(self, conn, message): self.bad_message(message)
|
||||
def on_getblocktxn(self, conn, message): self.bad_message(message)
|
||||
def on_blocktxn(self, conn, message): self.bad_message(message)
|
||||
|
||||
# Node that never sends a version. We'll use this to send a bunch of messages
|
||||
# anyway, and eventually get disconnected.
|
||||
class CNodeNoVersionBan(CLazyNode):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# send a bunch of veracks without sending a message. This should get us disconnected.
|
||||
# NOTE: implementation-specific check here. Remove if digibyted ban behavior changes
|
||||
def on_open(self, conn):
|
||||
super().on_open(conn)
|
||||
for i in range(banscore):
|
||||
self.send_message(msg_verack())
|
||||
|
||||
def on_reject(self, conn, message): pass
|
||||
|
||||
# Node that never sends a version. This one just sits idle and hopes to receive
|
||||
# any message (it shouldn't!)
|
||||
class CNodeNoVersionIdle(CLazyNode):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# Node that sends a version but not a verack.
|
||||
class CNodeNoVerackIdle(CLazyNode):
|
||||
def __init__(self):
|
||||
self.version_received = False
|
||||
super().__init__()
|
||||
|
||||
def on_reject(self, conn, message): pass
|
||||
def on_verack(self, conn, message): pass
|
||||
# When version is received, don't reply with a verack. Instead, see if the
|
||||
# node will give us a message that it shouldn't. This is not an exhaustive
|
||||
# list!
|
||||
def on_version(self, conn, message):
|
||||
self.version_received = True
|
||||
conn.send_message(msg_ping())
|
||||
conn.send_message(msg_getaddr())
|
||||
|
||||
class P2PLeakTest(DigiByteTestFramework):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.num_nodes = 1
|
||||
def setup_network(self):
|
||||
extra_args = [['-debug', '-banscore='+str(banscore)]
|
||||
for i in range(self.num_nodes)]
|
||||
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
|
||||
|
||||
def run_test(self):
|
||||
no_version_bannode = CNodeNoVersionBan()
|
||||
no_version_idlenode = CNodeNoVersionIdle()
|
||||
no_verack_idlenode = CNodeNoVerackIdle()
|
||||
|
||||
connections = []
|
||||
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_version_bannode, send_version=False))
|
||||
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_version_idlenode, send_version=False))
|
||||
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_verack_idlenode))
|
||||
no_version_bannode.add_connection(connections[0])
|
||||
no_version_idlenode.add_connection(connections[1])
|
||||
no_verack_idlenode.add_connection(connections[2])
|
||||
|
||||
NetworkThread().start() # Start up network handling in another thread
|
||||
|
||||
assert(wait_until(lambda: no_version_bannode.connected and no_version_idlenode.connected and no_verack_idlenode.version_received, timeout=10))
|
||||
|
||||
# Mine a block and make sure that it's not sent to the connected nodes
|
||||
self.nodes[0].generate(1)
|
||||
|
||||
#Give the node enough time to possibly leak out a message
|
||||
time.sleep(5)
|
||||
|
||||
#This node should have been banned
|
||||
assert(no_version_bannode.connection.state == "closed")
|
||||
|
||||
[conn.disconnect_node() for conn in connections]
|
||||
|
||||
# Make sure no unexpected messages came in
|
||||
assert(no_version_bannode.unexpected_msg == False)
|
||||
assert(no_version_idlenode.unexpected_msg == False)
|
||||
assert(no_verack_idlenode.unexpected_msg == False)
|
||||
|
||||
if __name__ == '__main__':
|
||||
P2PLeakTest().main()
|
||||
101
qa/rpc-tests/p2p-mempool.py
Executable file
101
qa/rpc-tests/p2p-mempool.py
Executable file
|
|
@ -0,0 +1,101 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from test_framework.mininode import *
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import *
|
||||
|
||||
class TestNode(NodeConnCB):
|
||||
def __init__(self):
|
||||
NodeConnCB.__init__(self)
|
||||
self.connection = None
|
||||
self.ping_counter = 1
|
||||
self.last_pong = msg_pong()
|
||||
self.block_receive_map = {}
|
||||
|
||||
def add_connection(self, conn):
|
||||
self.connection = conn
|
||||
self.peer_disconnected = False
|
||||
|
||||
def on_inv(self, conn, message):
|
||||
pass
|
||||
|
||||
# Track the last getdata message we receive (used in the test)
|
||||
def on_getdata(self, conn, message):
|
||||
self.last_getdata = message
|
||||
|
||||
def on_block(self, conn, message):
|
||||
message.block.calc_sha256()
|
||||
try:
|
||||
self.block_receive_map[message.block.sha256] += 1
|
||||
except KeyError as e:
|
||||
self.block_receive_map[message.block.sha256] = 1
|
||||
|
||||
# Spin until verack message is received from the node.
|
||||
# We use this to signal that our test can begin. This
|
||||
# is called from the testing thread, so it needs to acquire
|
||||
# the global lock.
|
||||
def wait_for_verack(self):
|
||||
def veracked():
|
||||
return self.verack_received
|
||||
return wait_until(veracked, timeout=10)
|
||||
|
||||
def wait_for_disconnect(self):
|
||||
def disconnected():
|
||||
return self.peer_disconnected
|
||||
return wait_until(disconnected, timeout=10)
|
||||
|
||||
# Wrapper for the NodeConn's send_message function
|
||||
def send_message(self, message):
|
||||
self.connection.send_message(message)
|
||||
|
||||
def on_pong(self, conn, message):
|
||||
self.last_pong = message
|
||||
|
||||
def on_close(self, conn):
|
||||
self.peer_disconnected = True
|
||||
|
||||
# Sync up with the node after delivery of a block
|
||||
def sync_with_ping(self, timeout=30):
|
||||
def received_pong():
|
||||
return (self.last_pong.nonce == self.ping_counter)
|
||||
self.connection.send_message(msg_ping(nonce=self.ping_counter))
|
||||
success = wait_until(received_pong, timeout=timeout)
|
||||
self.ping_counter += 1
|
||||
return success
|
||||
|
||||
def send_mempool(self):
|
||||
self.lastInv = []
|
||||
self.send_message(msg_mempool())
|
||||
|
||||
class P2PMempoolTests(DigiByteTestFramework):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 2
|
||||
|
||||
def setup_network(self):
|
||||
# Start a node with maxuploadtarget of 200 MB (/24h)
|
||||
self.nodes = []
|
||||
self.nodes.append(start_node(0, self.options.tmpdir, ["-debug", "-peerbloomfilters=0"]))
|
||||
|
||||
def run_test(self):
|
||||
#connect a mininode
|
||||
aTestNode = TestNode()
|
||||
node = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], aTestNode)
|
||||
aTestNode.add_connection(node)
|
||||
NetworkThread().start()
|
||||
aTestNode.wait_for_verack()
|
||||
|
||||
#request mempool
|
||||
aTestNode.send_mempool()
|
||||
aTestNode.wait_for_disconnect()
|
||||
|
||||
#mininode must be disconnected at this point
|
||||
assert_equal(len(self.nodes[0].getpeerinfo()), 0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
P2PMempoolTests().main()
|
||||
103
qa/rpc-tests/p2p-timeouts.py
Executable file
103
qa/rpc-tests/p2p-timeouts.py
Executable file
|
|
@ -0,0 +1,103 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
""" TimeoutsTest -- test various net timeouts (only in extended tests)
|
||||
|
||||
- Create three digibyted nodes:
|
||||
|
||||
no_verack_node - we never send a verack in response to their version
|
||||
no_version_node - we never send a version (only a ping)
|
||||
no_send_node - we never send any P2P message.
|
||||
|
||||
- Start all three nodes
|
||||
- Wait 1 second
|
||||
- Assert that we're connected
|
||||
- Send a ping to no_verack_node and no_version_node
|
||||
- Wait 30 seconds
|
||||
- Assert that we're still connected
|
||||
- Send a ping to no_verack_node and no_version_node
|
||||
- Wait 31 seconds
|
||||
- Assert that we're no longer connected (timeout to receive version/verack is 60 seconds)
|
||||
"""
|
||||
|
||||
from time import sleep
|
||||
|
||||
from test_framework.mininode import *
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import *
|
||||
|
||||
class TestNode(SingleNodeConnCB):
|
||||
def __init__(self):
|
||||
SingleNodeConnCB.__init__(self)
|
||||
self.connected = False
|
||||
self.received_version = False
|
||||
|
||||
def on_open(self, conn):
|
||||
self.connected = True
|
||||
|
||||
def on_close(self, conn):
|
||||
self.connected = False
|
||||
|
||||
def on_version(self, conn, message):
|
||||
# Don't send a verack in response
|
||||
self.received_version = True
|
||||
|
||||
class TimeoutsTest(DigiByteTestFramework):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
|
||||
def setup_network(self):
|
||||
self.nodes = []
|
||||
|
||||
# Start up node0 to be a version 1, pre-segwit node.
|
||||
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir,
|
||||
[["-debug", "-logtimemicros=1"]])
|
||||
|
||||
def run_test(self):
|
||||
# Setup the p2p connections and start up the network thread.
|
||||
self.no_verack_node = TestNode() # never send verack
|
||||
self.no_version_node = TestNode() # never send version (just ping)
|
||||
self.no_send_node = TestNode() # never send anything
|
||||
|
||||
connections = []
|
||||
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.no_verack_node))
|
||||
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.no_version_node, send_version=False))
|
||||
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.no_send_node, send_version=False))
|
||||
self.no_verack_node.add_connection(connections[0])
|
||||
self.no_version_node.add_connection(connections[1])
|
||||
self.no_send_node.add_connection(connections[2])
|
||||
|
||||
NetworkThread().start() # Start up network handling in another thread
|
||||
|
||||
sleep(1)
|
||||
|
||||
assert(self.no_verack_node.connected)
|
||||
assert(self.no_version_node.connected)
|
||||
assert(self.no_send_node.connected)
|
||||
|
||||
ping_msg = msg_ping()
|
||||
connections[0].send_message(ping_msg)
|
||||
connections[1].send_message(ping_msg)
|
||||
|
||||
sleep(30)
|
||||
|
||||
assert(self.no_verack_node.received_version)
|
||||
|
||||
assert(self.no_verack_node.connected)
|
||||
assert(self.no_version_node.connected)
|
||||
assert(self.no_send_node.connected)
|
||||
|
||||
connections[0].send_message(ping_msg)
|
||||
connections[1].send_message(ping_msg)
|
||||
|
||||
sleep(31)
|
||||
|
||||
assert(not self.no_verack_node.connected)
|
||||
assert(not self.no_version_node.connected)
|
||||
assert(not self.no_send_node.connected)
|
||||
|
||||
if __name__ == '__main__':
|
||||
TimeoutsTest().main()
|
||||
45
qa/rpc-tests/reindex.py
Executable file
45
qa/rpc-tests/reindex.py
Executable file
|
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# Test -reindex and -reindex-chainstate with CheckBlockIndex
|
||||
#
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import (
|
||||
start_nodes,
|
||||
stop_nodes,
|
||||
assert_equal,
|
||||
)
|
||||
import time
|
||||
|
||||
class ReindexTest(DigiByteTestFramework):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
|
||||
def setup_network(self):
|
||||
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
|
||||
|
||||
def reindex(self, justchainstate=False):
|
||||
self.nodes[0].generate(3)
|
||||
blockcount = self.nodes[0].getblockcount()
|
||||
stop_nodes(self.nodes)
|
||||
extra_args = [["-debug", "-reindex-chainstate" if justchainstate else "-reindex", "-checkblockindex=1"]]
|
||||
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
|
||||
while self.nodes[0].getblockcount() < blockcount:
|
||||
time.sleep(0.1)
|
||||
assert_equal(self.nodes[0].getblockcount(), blockcount)
|
||||
print("Success")
|
||||
|
||||
def run_test(self):
|
||||
self.reindex(False)
|
||||
self.reindex(True)
|
||||
self.reindex(False)
|
||||
self.reindex(True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
ReindexTest().main()
|
||||
41
qa/rpc-tests/signmessages.py
Executable file
41
qa/rpc-tests/signmessages.py
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import *
|
||||
|
||||
|
||||
class SignMessagesTest(DigiByteTestFramework):
|
||||
"""Tests RPC commands for signing and verifying messages."""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
|
||||
def setup_network(self, split=False):
|
||||
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
|
||||
self.is_network_split = False
|
||||
|
||||
def run_test(self):
|
||||
message = 'This is just a test message'
|
||||
|
||||
# Test the signing with a privkey
|
||||
privKey = 'cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N'
|
||||
address = 'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB'
|
||||
signature = self.nodes[0].signmessagewithprivkey(privKey, message)
|
||||
|
||||
# Verify the message
|
||||
assert(self.nodes[0].verifymessage(address, signature, message))
|
||||
|
||||
# Test the signing with an address with wallet
|
||||
address = self.nodes[0].getnewaddress()
|
||||
signature = self.nodes[0].signmessage(address, message)
|
||||
|
||||
# Verify the message
|
||||
assert(self.nodes[0].verifymessage(address, signature, message))
|
||||
|
||||
if __name__ == '__main__':
|
||||
SignMessagesTest().main()
|
||||
219
qa/rpc-tests/test_framework/test_framework.py
Executable file
219
qa/rpc-tests/test_framework/test_framework.py
Executable file
|
|
@ -0,0 +1,219 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
# Base class for RPC testing
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import tempfile
|
||||
import traceback
|
||||
|
||||
from .util import (
|
||||
initialize_chain,
|
||||
start_nodes,
|
||||
connect_nodes_bi,
|
||||
sync_blocks,
|
||||
sync_mempools,
|
||||
stop_nodes,
|
||||
stop_node,
|
||||
enable_coverage,
|
||||
check_json_precision,
|
||||
initialize_chain_clean,
|
||||
PortSeed,
|
||||
)
|
||||
from .authproxy import JSONRPCException
|
||||
|
||||
|
||||
class DigiByteTestFramework(object):
|
||||
|
||||
def __init__(self):
|
||||
self.num_nodes = 4
|
||||
self.setup_clean_chain = False
|
||||
self.nodes = None
|
||||
|
||||
def run_test(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def add_options(self, parser):
|
||||
pass
|
||||
|
||||
def setup_chain(self):
|
||||
print("Initializing test directory "+self.options.tmpdir)
|
||||
if self.setup_clean_chain:
|
||||
initialize_chain_clean(self.options.tmpdir, self.num_nodes)
|
||||
else:
|
||||
initialize_chain(self.options.tmpdir, self.num_nodes, self.options.cachedir)
|
||||
|
||||
def stop_node(self, num_node):
|
||||
stop_node(self.nodes[num_node], num_node)
|
||||
|
||||
def setup_nodes(self):
|
||||
return start_nodes(self.num_nodes, self.options.tmpdir)
|
||||
|
||||
def setup_network(self, split = False):
|
||||
self.nodes = self.setup_nodes()
|
||||
|
||||
# Connect the nodes as a "chain". This allows us
|
||||
# to split the network between nodes 1 and 2 to get
|
||||
# two halves that can work on competing chains.
|
||||
|
||||
# If we joined network halves, connect the nodes from the joint
|
||||
# on outward. This ensures that chains are properly reorganised.
|
||||
if not split:
|
||||
connect_nodes_bi(self.nodes, 1, 2)
|
||||
sync_blocks(self.nodes[1:3])
|
||||
sync_mempools(self.nodes[1:3])
|
||||
|
||||
connect_nodes_bi(self.nodes, 0, 1)
|
||||
connect_nodes_bi(self.nodes, 2, 3)
|
||||
self.is_network_split = split
|
||||
self.sync_all()
|
||||
|
||||
def split_network(self):
|
||||
"""
|
||||
Split the network of four nodes into nodes 0/1 and 2/3.
|
||||
"""
|
||||
assert not self.is_network_split
|
||||
stop_nodes(self.nodes)
|
||||
self.setup_network(True)
|
||||
|
||||
def sync_all(self):
|
||||
if self.is_network_split:
|
||||
sync_blocks(self.nodes[:2])
|
||||
sync_blocks(self.nodes[2:])
|
||||
sync_mempools(self.nodes[:2])
|
||||
sync_mempools(self.nodes[2:])
|
||||
else:
|
||||
sync_blocks(self.nodes)
|
||||
sync_mempools(self.nodes)
|
||||
|
||||
def join_network(self):
|
||||
"""
|
||||
Join the (previously split) network halves together.
|
||||
"""
|
||||
assert self.is_network_split
|
||||
stop_nodes(self.nodes)
|
||||
self.setup_network(False)
|
||||
|
||||
def main(self):
|
||||
|
||||
parser = optparse.OptionParser(usage="%prog [options]")
|
||||
parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true",
|
||||
help="Leave digibyteds and test.* datadir on exit or error")
|
||||
parser.add_option("--noshutdown", dest="noshutdown", default=False, action="store_true",
|
||||
help="Don't stop digibyteds after the test execution")
|
||||
parser.add_option("--srcdir", dest="srcdir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__))+"/../../../src"),
|
||||
help="Source directory containing digibyted/digibyte-cli (default: %default)")
|
||||
parser.add_option("--cachedir", dest="cachedir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__))+"/../../cache"),
|
||||
help="Directory for caching pregenerated datadirs")
|
||||
parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"),
|
||||
help="Root directory for datadirs")
|
||||
parser.add_option("--tracerpc", dest="trace_rpc", default=False, action="store_true",
|
||||
help="Print out all RPC calls as they are made")
|
||||
parser.add_option("--portseed", dest="port_seed", default=os.getpid(), type='int',
|
||||
help="The seed to use for assigning port numbers (default: current process id)")
|
||||
parser.add_option("--coveragedir", dest="coveragedir",
|
||||
help="Write tested RPC commands into this directory")
|
||||
self.add_options(parser)
|
||||
(self.options, self.args) = parser.parse_args()
|
||||
|
||||
# backup dir variable for removal at cleanup
|
||||
self.options.root, self.options.tmpdir = self.options.tmpdir, self.options.tmpdir + '/' + str(self.options.port_seed)
|
||||
|
||||
if self.options.trace_rpc:
|
||||
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
|
||||
|
||||
if self.options.coveragedir:
|
||||
enable_coverage(self.options.coveragedir)
|
||||
|
||||
PortSeed.n = self.options.port_seed
|
||||
|
||||
os.environ['PATH'] = self.options.srcdir+":"+self.options.srcdir+"/qt:"+os.environ['PATH']
|
||||
|
||||
check_json_precision()
|
||||
|
||||
success = False
|
||||
try:
|
||||
os.makedirs(self.options.tmpdir, exist_ok=False)
|
||||
self.setup_chain()
|
||||
self.setup_network()
|
||||
self.run_test()
|
||||
success = True
|
||||
except JSONRPCException as e:
|
||||
print("JSONRPC error: "+e.error['message'])
|
||||
traceback.print_tb(sys.exc_info()[2])
|
||||
except AssertionError as e:
|
||||
print("Assertion failed: " + str(e))
|
||||
traceback.print_tb(sys.exc_info()[2])
|
||||
except KeyError as e:
|
||||
print("key not found: "+ str(e))
|
||||
traceback.print_tb(sys.exc_info()[2])
|
||||
except Exception as e:
|
||||
print("Unexpected exception caught during testing: " + repr(e))
|
||||
traceback.print_tb(sys.exc_info()[2])
|
||||
except KeyboardInterrupt as e:
|
||||
print("Exiting after " + repr(e))
|
||||
|
||||
if not self.options.noshutdown:
|
||||
print("Stopping nodes")
|
||||
stop_nodes(self.nodes)
|
||||
else:
|
||||
print("Note: digibyteds were not stopped and may still be running")
|
||||
|
||||
if not self.options.nocleanup and not self.options.noshutdown and success:
|
||||
print("Cleaning up")
|
||||
shutil.rmtree(self.options.tmpdir)
|
||||
if not os.listdir(self.options.root):
|
||||
os.rmdir(self.options.root)
|
||||
else:
|
||||
print("Not cleaning up dir %s" % self.options.tmpdir)
|
||||
if os.getenv("PYTHON_DEBUG", ""):
|
||||
# Dump the end of the debug logs, to aid in debugging rare
|
||||
# travis failures.
|
||||
import glob
|
||||
filenames = glob.glob(self.options.tmpdir + "/node*/regtest/debug.log")
|
||||
MAX_LINES_TO_PRINT = 1000
|
||||
for f in filenames:
|
||||
print("From" , f, ":")
|
||||
from collections import deque
|
||||
print("".join(deque(open(f), MAX_LINES_TO_PRINT)))
|
||||
if success:
|
||||
print("Tests successful")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("Failed")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# Test framework for doing p2p comparison testing, which sets up some digibyted
|
||||
# binaries:
|
||||
# 1 binary: test binary
|
||||
# 2 binaries: 1 test binary, 1 ref binary
|
||||
# n>2 binaries: 1 test binary, n-1 ref binaries
|
||||
|
||||
class ComparisonTestFramework(DigiByteTestFramework):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.num_nodes = 2
|
||||
self.setup_clean_chain = True
|
||||
|
||||
def add_options(self, parser):
|
||||
parser.add_option("--testbinary", dest="testbinary",
|
||||
default=os.getenv("DIGIBYTED", "digibyted"),
|
||||
help="digibyted binary to test")
|
||||
parser.add_option("--refbinary", dest="refbinary",
|
||||
default=os.getenv("DIGIBYTED", "digibyted"),
|
||||
help="digibyted binary to use for reference nodes (if any)")
|
||||
|
||||
def setup_network(self):
|
||||
self.nodes = start_nodes(
|
||||
self.num_nodes, self.options.tmpdir,
|
||||
extra_args=[['-debug', '-whitelist=127.0.0.1']] * self.num_nodes,
|
||||
binary=[self.options.testbinary] +
|
||||
[self.options.refbinary]*(self.num_nodes-1))
|
||||
94
qa/rpc-tests/wallet-accounts.py
Executable file
94
qa/rpc-tests/wallet-accounts.py
Executable file
|
|
@ -0,0 +1,94 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import (
|
||||
start_nodes,
|
||||
start_node,
|
||||
assert_equal,
|
||||
connect_nodes_bi,
|
||||
)
|
||||
|
||||
|
||||
class WalletAccountsTest(DigiByteTestFramework):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
self.node_args = [[]]
|
||||
|
||||
def setup_network(self):
|
||||
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.node_args)
|
||||
self.is_network_split = False
|
||||
|
||||
def run_test (self):
|
||||
node = self.nodes[0]
|
||||
# Check that there's no UTXO on any of the nodes
|
||||
assert_equal(len(node.listunspent()), 0)
|
||||
|
||||
node.generate(101)
|
||||
|
||||
assert_equal(node.getbalance(), 50)
|
||||
|
||||
accounts = ["a","b","c","d","e"]
|
||||
amount_to_send = 1.0
|
||||
account_addresses = dict()
|
||||
for account in accounts:
|
||||
address = node.getaccountaddress(account)
|
||||
account_addresses[account] = address
|
||||
|
||||
node.getnewaddress(account)
|
||||
assert_equal(node.getaccount(address), account)
|
||||
assert(address in node.getaddressesbyaccount(account))
|
||||
|
||||
node.sendfrom("", address, amount_to_send)
|
||||
|
||||
node.generate(1)
|
||||
|
||||
for i in range(len(accounts)):
|
||||
from_account = accounts[i]
|
||||
to_account = accounts[(i+1)%len(accounts)]
|
||||
to_address = account_addresses[to_account]
|
||||
node.sendfrom(from_account, to_address, amount_to_send)
|
||||
|
||||
node.generate(1)
|
||||
|
||||
for account in accounts:
|
||||
address = node.getaccountaddress(account)
|
||||
assert(address != account_addresses[account])
|
||||
assert_equal(node.getreceivedbyaccount(account), 2)
|
||||
node.move(account, "", node.getbalance(account))
|
||||
|
||||
node.generate(101)
|
||||
|
||||
expected_account_balances = {"": 5200}
|
||||
for account in accounts:
|
||||
expected_account_balances[account] = 0
|
||||
|
||||
assert_equal(node.listaccounts(), expected_account_balances)
|
||||
|
||||
assert_equal(node.getbalance(""), 5200)
|
||||
|
||||
for account in accounts:
|
||||
address = node.getaccountaddress("")
|
||||
node.setaccount(address, account)
|
||||
assert(address in node.getaddressesbyaccount(account))
|
||||
assert(address not in node.getaddressesbyaccount(""))
|
||||
|
||||
for account in accounts:
|
||||
addresses = []
|
||||
for x in range(10):
|
||||
addresses.append(node.getnewaddress())
|
||||
multisig_address = node.addmultisigaddress(5, addresses, account)
|
||||
node.sendfrom("", multisig_address, 50)
|
||||
|
||||
node.generate(101)
|
||||
|
||||
for account in accounts:
|
||||
assert_equal(node.getbalance(account), 50)
|
||||
|
||||
if __name__ == '__main__':
|
||||
WalletAccountsTest().main ()
|
||||
77
qa/rpc-tests/zapwallettxes.py
Executable file
77
qa/rpc-tests/zapwallettxes.py
Executable file
|
|
@ -0,0 +1,77 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import *
|
||||
|
||||
|
||||
class ZapWalletTXesTest (DigiByteTestFramework):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 3
|
||||
|
||||
def setup_network(self, split=False):
|
||||
self.nodes = start_nodes(self.num_nodes, self.options.tmpdir)
|
||||
connect_nodes_bi(self.nodes,0,1)
|
||||
connect_nodes_bi(self.nodes,1,2)
|
||||
connect_nodes_bi(self.nodes,0,2)
|
||||
self.is_network_split=False
|
||||
self.sync_all()
|
||||
|
||||
def run_test (self):
|
||||
print("Mining blocks...")
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
self.nodes[1].generate(101)
|
||||
self.sync_all()
|
||||
|
||||
assert_equal(self.nodes[0].getbalance(), 50)
|
||||
|
||||
txid0 = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11)
|
||||
txid1 = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 10)
|
||||
self.sync_all()
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
txid2 = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11)
|
||||
txid3 = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 10)
|
||||
|
||||
tx0 = self.nodes[0].gettransaction(txid0)
|
||||
assert_equal(tx0['txid'], txid0) #tx0 must be available (confirmed)
|
||||
|
||||
tx1 = self.nodes[0].gettransaction(txid1)
|
||||
assert_equal(tx1['txid'], txid1) #tx1 must be available (confirmed)
|
||||
|
||||
tx2 = self.nodes[0].gettransaction(txid2)
|
||||
assert_equal(tx2['txid'], txid2) #tx2 must be available (unconfirmed)
|
||||
|
||||
tx3 = self.nodes[0].gettransaction(txid3)
|
||||
assert_equal(tx3['txid'], txid3) #tx3 must be available (unconfirmed)
|
||||
|
||||
#restart digibyted
|
||||
self.nodes[0].stop()
|
||||
digibyted_processes[0].wait()
|
||||
self.nodes[0] = start_node(0,self.options.tmpdir)
|
||||
|
||||
tx3 = self.nodes[0].gettransaction(txid3)
|
||||
assert_equal(tx3['txid'], txid3) #tx must be available (unconfirmed)
|
||||
|
||||
self.nodes[0].stop()
|
||||
digibyted_processes[0].wait()
|
||||
|
||||
#restart digibyted with zapwallettxes
|
||||
self.nodes[0] = start_node(0,self.options.tmpdir, ["-zapwallettxes=1"])
|
||||
|
||||
assert_raises(JSONRPCException, self.nodes[0].gettransaction, [txid3])
|
||||
#there must be a expection because the unconfirmed wallettx0 must be gone by now
|
||||
|
||||
tx0 = self.nodes[0].gettransaction(txid0)
|
||||
assert_equal(tx0['txid'], txid0) #tx0 (confirmed) must still be available because it was confirmed
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ZapWalletTXesTest ().main ()
|
||||
97
qa/rpc-tests/zmq_test.py
Executable file
97
qa/rpc-tests/zmq_test.py
Executable file
|
|
@ -0,0 +1,97 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2017 The DigiByte Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# Test ZMQ interface
|
||||
#
|
||||
|
||||
from test_framework.test_framework import DigiByteTestFramework
|
||||
from test_framework.util import *
|
||||
import zmq
|
||||
import struct
|
||||
|
||||
class ZMQTest (DigiByteTestFramework):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.num_nodes = 4
|
||||
|
||||
port = 28332
|
||||
|
||||
def setup_nodes(self):
|
||||
self.zmqContext = zmq.Context()
|
||||
self.zmqSubSocket = self.zmqContext.socket(zmq.SUB)
|
||||
self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashblock")
|
||||
self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtx")
|
||||
self.zmqSubSocket.connect("tcp://127.0.0.1:%i" % self.port)
|
||||
return start_nodes(self.num_nodes, self.options.tmpdir, extra_args=[
|
||||
['-zmqpubhashtx=tcp://127.0.0.1:'+str(self.port), '-zmqpubhashblock=tcp://127.0.0.1:'+str(self.port)],
|
||||
[],
|
||||
[],
|
||||
[]
|
||||
])
|
||||
|
||||
def run_test(self):
|
||||
self.sync_all()
|
||||
|
||||
genhashes = self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
|
||||
print("listen...")
|
||||
msg = self.zmqSubSocket.recv_multipart()
|
||||
topic = msg[0]
|
||||
assert_equal(topic, b"hashtx")
|
||||
body = msg[1]
|
||||
nseq = msg[2]
|
||||
msgSequence = struct.unpack('<I', msg[-1])[-1]
|
||||
assert_equal(msgSequence, 0) #must be sequence 0 on hashtx
|
||||
|
||||
msg = self.zmqSubSocket.recv_multipart()
|
||||
topic = msg[0]
|
||||
body = msg[1]
|
||||
msgSequence = struct.unpack('<I', msg[-1])[-1]
|
||||
assert_equal(msgSequence, 0) #must be sequence 0 on hashblock
|
||||
blkhash = bytes_to_hex_str(body)
|
||||
|
||||
assert_equal(genhashes[0], blkhash) #blockhash from generate must be equal to the hash received over zmq
|
||||
|
||||
n = 10
|
||||
genhashes = self.nodes[1].generate(n)
|
||||
self.sync_all()
|
||||
|
||||
zmqHashes = []
|
||||
blockcount = 0
|
||||
for x in range(0,n*2):
|
||||
msg = self.zmqSubSocket.recv_multipart()
|
||||
topic = msg[0]
|
||||
body = msg[1]
|
||||
if topic == b"hashblock":
|
||||
zmqHashes.append(bytes_to_hex_str(body))
|
||||
msgSequence = struct.unpack('<I', msg[-1])[-1]
|
||||
assert_equal(msgSequence, blockcount+1)
|
||||
blockcount += 1
|
||||
|
||||
for x in range(0,n):
|
||||
assert_equal(genhashes[x], zmqHashes[x]) #blockhash from generate must be equal to the hash received over zmq
|
||||
|
||||
#test tx from a second node
|
||||
hashRPC = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0)
|
||||
self.sync_all()
|
||||
|
||||
# now we should receive a zmq msg because the tx was broadcast
|
||||
msg = self.zmqSubSocket.recv_multipart()
|
||||
topic = msg[0]
|
||||
body = msg[1]
|
||||
hashZMQ = ""
|
||||
if topic == b"hashtx":
|
||||
hashZMQ = bytes_to_hex_str(body)
|
||||
msgSequence = struct.unpack('<I', msg[-1])[-1]
|
||||
assert_equal(msgSequence, blockcount+1)
|
||||
|
||||
assert_equal(hashRPC, hashZMQ) #blockhash from generate must be equal to the hash received over zmq
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ZMQTest ().main ()
|
||||
Loading…
Reference in a new issue