diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index e2272ad43..3a6412d08 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -51,6 +51,7 @@ testScripts=( 'getblocktemplate.py' 'bip65-cltv-p2p.py' 'bipdersig-p2p.py' + 'overwinter_peer_management.py' ); testScriptsExt=( 'getblocktemplate_longpoll.py' diff --git a/qa/rpc-tests/overwinter_peer_management.py b/qa/rpc-tests/overwinter_peer_management.py new file mode 100755 index 000000000..a7a71e52d --- /dev/null +++ b/qa/rpc-tests/overwinter_peer_management.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 The Zcash 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 NodeConn, NodeConnCB, NetworkThread, \ + EarlyDisconnectError, msg_inv, mininode_lock, msg_ping, \ + MY_VERSION, OVERWINTER_PROTO_VERSION +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import initialize_chain_clean, start_nodes, \ + p2p_port, assert_equal + +import time + +# +# In this test we connect Sprout and Overwinter mininodes to a Zcashd node +# which will activate Overwinter at block 10. +# +# We test: +# 1. the mininodes stay connected to Zcash with Sprout consensus rules +# 2. when Overwinter activates, the Sprout mininodes are dropped +# 3. new Overwinter nodes can connect to Zcash +# 4. new Sprout nodes cannot connect to Zcash +# +# This test *does not* verify that prior to Overwinter activation, the Zcashd +# node will prefer connections with Overwinter nodes, with an eviction process +# that prioritizes Sprout connections. +# + + +class TestManager(NodeConnCB): + def __init__(self): + NodeConnCB.__init__(self) + self.create_callback_map() + + def on_close(self, conn): + pass + + def on_reject(self, conn, message): + conn.rejectMessage = message + + +class OverwinterPeerManagementTest(BitcoinTestFramework): + + def setup_chain(self): + print "Initializing test directory "+self.options.tmpdir + initialize_chain_clean(self.options.tmpdir, 1) + + def setup_network(self): + self.nodes = start_nodes(1, self.options.tmpdir, + extra_args=[['-nuparams=5ba81b19:10', '-debug', '-whitelist=127.0.0.1']]) + + def run_test(self): + test = TestManager() + + # Launch 10 Sprout and 10 Overwinter mininodes + nodes = [] + for x in xrange(10): + nodes.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test, "regtest", False)) + nodes.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test, "regtest", True)) + + # Start up network handling in another thread + NetworkThread().start() + + # Sprout consensus rules apply at block height 9 + self.nodes[0].generate(9) + assert_equal(9, self.nodes[0].getblockcount()) + + # Verify mininodes are still connected to zcashd node + peerinfo = self.nodes[0].getpeerinfo() + versions = [x["version"] for x in peerinfo] + assert_equal(10, versions.count(MY_VERSION)) + assert_equal(10, versions.count(OVERWINTER_PROTO_VERSION)) + + # Overwinter consensus rules activate at block height 10 + self.nodes[0].generate(1) + assert_equal(10, self.nodes[0].getblockcount()) + + # Mininodes send ping message to zcashd node. + pingCounter = 1 + for node in nodes: + node.send_message(msg_ping(pingCounter)) + pingCounter = pingCounter + 1 + + time.sleep(3) + + # Verify Sprout mininodes have been dropped and Overwinter mininodes are still connected. + peerinfo = self.nodes[0].getpeerinfo() + versions = [x["version"] for x in peerinfo] + assert_equal(0, versions.count(MY_VERSION)) + assert_equal(10, versions.count(OVERWINTER_PROTO_VERSION)) + + # Extend the Overwinter chain with another block. + self.nodes[0].generate(1) + + # Connect a new Overwinter mininode to the zcashd node, which is accepted. + nodes.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test, "regtest", True)) + time.sleep(3) + assert_equal(11, len(self.nodes[0].getpeerinfo())) + + # Try to connect a new Sprout mininode to the zcashd node, which is rejected. + sprout = NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test, "regtest", False) + nodes.append(sprout) + time.sleep(3) + assert("Version must be 170003 or greater" in str(sprout.rejectMessage)) + + # Verify that only Overwinter mininodes are connected. + peerinfo = self.nodes[0].getpeerinfo() + versions = [x["version"] for x in peerinfo] + assert_equal(0, versions.count(MY_VERSION)) + assert_equal(11, versions.count(OVERWINTER_PROTO_VERSION)) + + for node in nodes: + node.disconnect_node() + +if __name__ == '__main__': + OverwinterPeerManagementTest().main() diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index c74d3f931..3c9821259 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -39,6 +39,7 @@ from .equihash import ( zcash_person, ) +OVERWINTER_PROTO_VERSION = 170003 BIP0031_VERSION = 60000 MY_VERSION = 170002 # past bip-31 for ping/pong MY_SUBVERSION = "/python-mininode-tester:0.0.1/" @@ -868,8 +869,12 @@ class CAlert(object): class msg_version(object): command = "version" - def __init__(self): - self.nVersion = MY_VERSION + def __init__(self, overwintered=False): + if overwintered: + self.nVersion = OVERWINTER_PROTO_VERSION + else: + self.nVersion = MY_VERSION + self.nServices = 1 self.nTime = time.time() self.addrTo = CAddress() @@ -1326,7 +1331,7 @@ class NodeConn(asyncore.dispatcher): "regtest": "\xaa\xe8\x3f\x5f" # regtest } - def __init__(self, dstaddr, dstport, rpc, callback, net="regtest"): + def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", overwintered=False): asyncore.dispatcher.__init__(self, map=mininode_socket_map) self.log = logging.getLogger("NodeConn(%s:%d)" % (dstaddr, dstport)) self.dstaddr = dstaddr @@ -1343,7 +1348,7 @@ class NodeConn(asyncore.dispatcher): self.disconnect = False # stuff version msg into sendbuf - vt = msg_version() + vt = msg_version(overwintered) vt.addrTo.ip = self.dstaddr vt.addrTo.port = self.dstport vt.addrFrom.ip = "0.0.0.0"