Auto merge of #2919 - bitcartel:2904_branch_id_protocol_handshake, r=str4d
Overwinter peer management Implements ZIP 201. Part of #2904.
This commit is contained in:
@@ -51,6 +51,7 @@ testScripts=(
|
|||||||
'getblocktemplate.py'
|
'getblocktemplate.py'
|
||||||
'bip65-cltv-p2p.py'
|
'bip65-cltv-p2p.py'
|
||||||
'bipdersig-p2p.py'
|
'bipdersig-p2p.py'
|
||||||
|
'overwinter_peer_management.py'
|
||||||
);
|
);
|
||||||
testScriptsExt=(
|
testScriptsExt=(
|
||||||
'getblocktemplate_longpoll.py'
|
'getblocktemplate_longpoll.py'
|
||||||
|
|||||||
117
qa/rpc-tests/overwinter_peer_management.py
Executable file
117
qa/rpc-tests/overwinter_peer_management.py
Executable file
@@ -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()
|
||||||
@@ -39,6 +39,7 @@ from .equihash import (
|
|||||||
zcash_person,
|
zcash_person,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
OVERWINTER_PROTO_VERSION = 170003
|
||||||
BIP0031_VERSION = 60000
|
BIP0031_VERSION = 60000
|
||||||
MY_VERSION = 170002 # past bip-31 for ping/pong
|
MY_VERSION = 170002 # past bip-31 for ping/pong
|
||||||
MY_SUBVERSION = "/python-mininode-tester:0.0.1/"
|
MY_SUBVERSION = "/python-mininode-tester:0.0.1/"
|
||||||
@@ -868,8 +869,12 @@ class CAlert(object):
|
|||||||
class msg_version(object):
|
class msg_version(object):
|
||||||
command = "version"
|
command = "version"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, overwintered=False):
|
||||||
self.nVersion = MY_VERSION
|
if overwintered:
|
||||||
|
self.nVersion = OVERWINTER_PROTO_VERSION
|
||||||
|
else:
|
||||||
|
self.nVersion = MY_VERSION
|
||||||
|
|
||||||
self.nServices = 1
|
self.nServices = 1
|
||||||
self.nTime = time.time()
|
self.nTime = time.time()
|
||||||
self.addrTo = CAddress()
|
self.addrTo = CAddress()
|
||||||
@@ -1326,7 +1331,7 @@ class NodeConn(asyncore.dispatcher):
|
|||||||
"regtest": "\xaa\xe8\x3f\x5f" # regtest
|
"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)
|
asyncore.dispatcher.__init__(self, map=mininode_socket_map)
|
||||||
self.log = logging.getLogger("NodeConn(%s:%d)" % (dstaddr, dstport))
|
self.log = logging.getLogger("NodeConn(%s:%d)" % (dstaddr, dstport))
|
||||||
self.dstaddr = dstaddr
|
self.dstaddr = dstaddr
|
||||||
@@ -1343,7 +1348,7 @@ class NodeConn(asyncore.dispatcher):
|
|||||||
self.disconnect = False
|
self.disconnect = False
|
||||||
|
|
||||||
# stuff version msg into sendbuf
|
# stuff version msg into sendbuf
|
||||||
vt = msg_version()
|
vt = msg_version(overwintered)
|
||||||
vt.addrTo.ip = self.dstaddr
|
vt.addrTo.ip = self.dstaddr
|
||||||
vt.addrTo.port = self.dstport
|
vt.addrTo.port = self.dstport
|
||||||
vt.addrFrom.ip = "0.0.0.0"
|
vt.addrFrom.ip = "0.0.0.0"
|
||||||
|
|||||||
@@ -95,10 +95,13 @@ public:
|
|||||||
consensus.nPowMaxAdjustDown = 32; // 32% adjustment down
|
consensus.nPowMaxAdjustDown = 32; // 32% adjustment down
|
||||||
consensus.nPowMaxAdjustUp = 16; // 16% adjustment up
|
consensus.nPowMaxAdjustUp = 16; // 16% adjustment up
|
||||||
consensus.nPowTargetSpacing = 2.5 * 60;
|
consensus.nPowTargetSpacing = 2.5 * 60;
|
||||||
|
consensus.vUpgrades[Consensus::BASE_SPROUT].nProtocolVersion = 170002;
|
||||||
consensus.vUpgrades[Consensus::BASE_SPROUT].nActivationHeight =
|
consensus.vUpgrades[Consensus::BASE_SPROUT].nActivationHeight =
|
||||||
Consensus::NetworkUpgrade::ALWAYS_ACTIVE;
|
Consensus::NetworkUpgrade::ALWAYS_ACTIVE;
|
||||||
|
consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nProtocolVersion = 170002;
|
||||||
consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight =
|
consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight =
|
||||||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
||||||
|
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion = 170004;
|
||||||
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight =
|
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight =
|
||||||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
||||||
|
|
||||||
@@ -253,10 +256,13 @@ public:
|
|||||||
consensus.nPowMaxAdjustDown = 32; // 32% adjustment down
|
consensus.nPowMaxAdjustDown = 32; // 32% adjustment down
|
||||||
consensus.nPowMaxAdjustUp = 16; // 16% adjustment up
|
consensus.nPowMaxAdjustUp = 16; // 16% adjustment up
|
||||||
consensus.nPowTargetSpacing = 2.5 * 60;
|
consensus.nPowTargetSpacing = 2.5 * 60;
|
||||||
|
consensus.vUpgrades[Consensus::BASE_SPROUT].nProtocolVersion = 170002;
|
||||||
consensus.vUpgrades[Consensus::BASE_SPROUT].nActivationHeight =
|
consensus.vUpgrades[Consensus::BASE_SPROUT].nActivationHeight =
|
||||||
Consensus::NetworkUpgrade::ALWAYS_ACTIVE;
|
Consensus::NetworkUpgrade::ALWAYS_ACTIVE;
|
||||||
|
consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nProtocolVersion = 170002;
|
||||||
consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight =
|
consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight =
|
||||||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
||||||
|
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion = 170003;
|
||||||
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight =
|
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight =
|
||||||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
||||||
|
|
||||||
@@ -360,10 +366,13 @@ public:
|
|||||||
consensus.nPowMaxAdjustDown = 0; // Turn off adjustment down
|
consensus.nPowMaxAdjustDown = 0; // Turn off adjustment down
|
||||||
consensus.nPowMaxAdjustUp = 0; // Turn off adjustment up
|
consensus.nPowMaxAdjustUp = 0; // Turn off adjustment up
|
||||||
consensus.nPowTargetSpacing = 2.5 * 60;
|
consensus.nPowTargetSpacing = 2.5 * 60;
|
||||||
|
consensus.vUpgrades[Consensus::BASE_SPROUT].nProtocolVersion = 170002;
|
||||||
consensus.vUpgrades[Consensus::BASE_SPROUT].nActivationHeight =
|
consensus.vUpgrades[Consensus::BASE_SPROUT].nActivationHeight =
|
||||||
Consensus::NetworkUpgrade::ALWAYS_ACTIVE;
|
Consensus::NetworkUpgrade::ALWAYS_ACTIVE;
|
||||||
|
consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nProtocolVersion = 170002;
|
||||||
consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight =
|
consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight =
|
||||||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
||||||
|
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion = 170003;
|
||||||
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight =
|
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight =
|
||||||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT;
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ enum UpgradeIndex {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct NetworkUpgrade {
|
struct NetworkUpgrade {
|
||||||
|
/**
|
||||||
|
* The first protocol version which will understand the new consensus rules
|
||||||
|
*/
|
||||||
|
int nProtocolVersion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Height of the first block for which the new consensus rules will be active
|
* Height of the first block for which the new consensus rules will be active
|
||||||
*/
|
*/
|
||||||
|
|||||||
29
src/main.cpp
29
src/main.cpp
@@ -4721,6 +4721,19 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When Overwinter is active, reject incoming connections from non-Overwinter nodes
|
||||||
|
const Consensus::Params& params = Params().GetConsensus();
|
||||||
|
if (NetworkUpgradeActive(GetHeight(), params, Consensus::UPGRADE_OVERWINTER)
|
||||||
|
&& pfrom->nVersion < params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)
|
||||||
|
{
|
||||||
|
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
|
||||||
|
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
|
||||||
|
strprintf("Version must be %d or greater",
|
||||||
|
params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion));
|
||||||
|
pfrom->fDisconnect = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (pfrom->nVersion == 10300)
|
if (pfrom->nVersion == 10300)
|
||||||
pfrom->nVersion = 300;
|
pfrom->nVersion = 300;
|
||||||
if (!vRecv.empty())
|
if (!vRecv.empty())
|
||||||
@@ -4839,6 +4852,22 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Disconnect existing peer connection when:
|
||||||
|
// 1. The version message has been received
|
||||||
|
// 2. Overwinter is active
|
||||||
|
// 3. Peer version is pre-Overwinter
|
||||||
|
else if (NetworkUpgradeActive(GetHeight(), chainparams.GetConsensus(), Consensus::UPGRADE_OVERWINTER)
|
||||||
|
&& (pfrom->nVersion < chainparams.GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion))
|
||||||
|
{
|
||||||
|
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
|
||||||
|
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
|
||||||
|
strprintf("Version must be %d or greater",
|
||||||
|
chainparams.GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion));
|
||||||
|
pfrom->fDisconnect = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
else if (strCommand == "addr")
|
else if (strCommand == "addr")
|
||||||
{
|
{
|
||||||
vector<CAddress> vAddr;
|
vector<CAddress> vAddr;
|
||||||
|
|||||||
29
src/net.cpp
29
src/net.cpp
@@ -7,6 +7,7 @@
|
|||||||
#include "config/bitcoin-config.h"
|
#include "config/bitcoin-config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
||||||
#include "addrman.h"
|
#include "addrman.h"
|
||||||
@@ -812,6 +813,34 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
|
|||||||
|
|
||||||
// Protect connections with certain characteristics
|
// Protect connections with certain characteristics
|
||||||
|
|
||||||
|
// Check version of eviction candidates and prioritize nodes which do not support network upgrade.
|
||||||
|
std::vector<CNodeRef> vTmpEvictionCandidates;
|
||||||
|
int height;
|
||||||
|
{
|
||||||
|
LOCK(cs_main);
|
||||||
|
height = chainActive.Height();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Consensus::Params& params = Params().GetConsensus();
|
||||||
|
int nActivationHeight = params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight;
|
||||||
|
|
||||||
|
if (nActivationHeight > 0 &&
|
||||||
|
height < nActivationHeight &&
|
||||||
|
height >= nActivationHeight - NETWORK_UPGRADE_PEER_PREFERENCE_BLOCK_PERIOD)
|
||||||
|
{
|
||||||
|
// Find any nodes which don't support Overwinter protocol version
|
||||||
|
BOOST_FOREACH(const CNodeRef &node, vEvictionCandidates) {
|
||||||
|
if (node->nVersion < params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion) {
|
||||||
|
vTmpEvictionCandidates.push_back(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prioritize these nodes by replacing eviction set with them
|
||||||
|
if (vTmpEvictionCandidates.size() > 0) {
|
||||||
|
vEvictionCandidates = vTmpEvictionCandidates;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Deterministically select 4 peers to protect by netgroup.
|
// Deterministically select 4 peers to protect by netgroup.
|
||||||
// An attacker cannot predict which netgroups will be protected.
|
// An attacker cannot predict which netgroups will be protected.
|
||||||
static CompareNetGroupKeyed comparerNetGroupKeyed;
|
static CompareNetGroupKeyed comparerNetGroupKeyed;
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ static const size_t MAPASKFOR_MAX_SZ = MAX_INV_SZ;
|
|||||||
static const size_t SETASKFOR_MAX_SZ = 2 * MAX_INV_SZ;
|
static const size_t SETASKFOR_MAX_SZ = 2 * MAX_INV_SZ;
|
||||||
/** The maximum number of peer connections to maintain. */
|
/** The maximum number of peer connections to maintain. */
|
||||||
static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125;
|
static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125;
|
||||||
|
/** The period before a network upgrade activates, where connections to upgrading peers are preferred (in blocks). */
|
||||||
|
static const int NETWORK_UPGRADE_PEER_PREFERENCE_BLOCK_PERIOD = 24 * 24 * 3;
|
||||||
|
|
||||||
unsigned int ReceiveFloodSize();
|
unsigned int ReceiveFloodSize();
|
||||||
unsigned int SendBufferSize();
|
unsigned int SendBufferSize();
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
* network protocol versioning
|
* network protocol versioning
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const int PROTOCOL_VERSION = 170002;
|
static const int PROTOCOL_VERSION = 170003;
|
||||||
|
|
||||||
//! initial proto version, to be increased after version/verack negotiation
|
//! initial proto version, to be increased after version/verack negotiation
|
||||||
static const int INIT_PROTO_VERSION = 209;
|
static const int INIT_PROTO_VERSION = 209;
|
||||||
|
|||||||
Reference in New Issue
Block a user