Skip to content

Commit

Permalink
Delayed block penalization (#62)
Browse files Browse the repository at this point in the history
- Delayed block penalization
- Reset regtest
- Change protocol version
- Update blockdelay RPC test.
- Fix min chain work on regtest chain params.
- Add chain penalization field to getchaintips method.
- Fix log formating
  • Loading branch information
alejandromgk authored Feb 12, 2019
1 parent a88795c commit ab45a5f
Show file tree
Hide file tree
Showing 15 changed files with 276 additions and 22 deletions.
6 changes: 3 additions & 3 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N)
AC_PREREQ([2.60])
define(_CLIENT_VERSION_MAJOR, 0)
define(_CLIENT_VERSION_MINOR, 12)
define(_CLIENT_VERSION_REVISION, 5)
define(_CLIENT_VERSION_BUILD, 1)
define(_CLIENT_VERSION_REVISION, 6)
define(_CLIENT_VERSION_BUILD, 0)
define(_CLIENT_VERSION_IS_RELEASE, true)
define(_COPYRIGHT_YEAR, 2018)
define(_COPYRIGHT_YEAR, 2019)
AC_INIT([Paccoin Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[https://github.com/PACCommunity/PAC/issues],[paccoincore])
AC_CONFIG_SRCDIR([src/validation.cpp])
AC_CONFIG_HEADERS([src/config/paccoin-config.h])
Expand Down
1 change: 1 addition & 0 deletions qa/pull-tester/rpc-tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
'invalidtxrequest.py', # NOTE: needs paccoin_hash to pass
'abandonconflict.py',
'p2p-versionbits-warning.py',
'blockdelay.py',
]
if ENABLE_ZMQ:
testScripts.append('zmq_test.py')
Expand Down
169 changes: 169 additions & 0 deletions qa/rpc-tests/blockdelay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#!/usr/bin/env python2
# Copyright (c) 2014 The Bitcoin Core developers
# Copyright (c) 2018 The Zencash 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 BitcoinTestFramework
from test_framework.authproxy import JSONRPCException
from test_framework.util import assert_equal, initialize_chain_clean, \
start_nodes, start_node, connect_nodes, stop_node, stop_nodes, \
sync_blocks, sync_mempools, connect_nodes_bi, wait_bitcoinds, p2p_port, check_json_precision
import traceback
import os,sys
import shutil
from random import randint
from decimal import Decimal
import logging

import time
class blockdelay(BitcoinTestFramework):

alert_filename = None

def setup_chain(self, split=False):
print("Initializing test directory "+self.options.tmpdir)
initialize_chain_clean(self.options.tmpdir, 4)
self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
with open(self.alert_filename, 'w'):
pass # Just open then close to create zero-length file

def setup_network(self, split=False):
self.nodes = []
self.nodes = start_nodes(4, self.options.tmpdir)

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 disconnect_nodes(self, from_connection, node_num):
ip_port = "127.0.0.1:"+str(p2p_port(node_num))
from_connection.disconnectnode(ip_port)
# poll until version handshake complete to avoid race conditions
# with transaction relaying
while any(peer['version'] == 0 for peer in from_connection.getpeerinfo()):
time.sleep(0.1)

def split_network(self):
# Split the network of four nodes into nodes 0/1 and 2/3.
assert not self.is_network_split
self.disconnect_nodes(self.nodes[1], 2)
self.disconnect_nodes(self.nodes[2], 1)
self.is_network_split = True


def join_network(self):
#Join the (previously split) network halves together.
assert self.is_network_split
connect_nodes_bi(self.nodes, 0, 3)
connect_nodes_bi(self.nodes, 3, 0)
connect_nodes_bi(self.nodes, 1, 3)
connect_nodes_bi(self.nodes, 3, 1)
#sync_blocks(self.nodes[0:3],1,True)
#sync_mempools(self.nodes[1:3])
self.sync_all()
self.is_network_split = False


def run_test(self):
blocks = []

blocks.append(self.nodes[0].getblockhash(0))
print("\n\nGenesis block is: " + blocks[0])
# raw_input("press enter to start..")

print("\n\nGenerating initial blockchain of 4 blocks")
blocks.extend(self.nodes[0].generate(1)) # block height 1
self.sync_all()
blocks.extend(self.nodes[1].generate(1)) # block height 2
self.sync_all()
blocks.extend(self.nodes[2].generate(1)) # block height 3
self.sync_all()
blocks.extend(self.nodes[3].generate(1)) # block height 4
self.sync_all()
print("Four blocks generated.")


print("\n\nSplit network")
self.split_network()
print("The network is split")


# Main chain
print("\n\nGenerating 2 parallel chains with different length")

print("\nGenerating 12 honest blocks (blocks 5-16)")
blocks.extend(self.nodes[0].generate(6)) # block height 5 -6 -7 -8 - 9 - 10
self.sync_all()
blocks.extend(self.nodes[1].generate(6)) # block height 11-12-13-14-15-16
last_main_blockhash=blocks[len(blocks)-1]
self.sync_all()
print("Honest blocks generated.")

assert self.nodes[0].getbestblockhash() == last_main_blockhash

# Malicious nodes mining privately faster
print("\nGenerating 13 malicious blocks (blocks 5-17)")
self.nodes[2].generate(10) # block height 5 - 6 -7 -8 -9-10 -11 12 13 14
self.sync_all()
self.nodes[3].generate(3) # block height 15 - 16 - 17
self.sync_all()
print("Malicious blocks generated.")


print("\n\nJoin network")
# raw_input("press enter to join the netorks..")
self.join_network()
time.sleep(2)
print("\nNetwork joined")

print("\nTesting if the current chain is still the honest chain")
assert self.nodes[0].getbestblockhash() == last_main_blockhash
print("Confirmed: malicious chain is under penalty")

print("\nTesting chaintips")
current_chain_tips = self.nodes[0].getchaintips()
assert (current_chain_tips[0]['penalization'] == 65)
print("Malicious chain is correctly penalized with 65 blocks after generating 12 old blocks + 1 new block.")

print("\nGenerating 64 malicious blocks (New height = 81)")
self.nodes[3].generate(64)
print("Malicious blocks generated")

time.sleep(10)

print("\nTesting if the current chain is still the honest chain")
assert self.nodes[0].getbestblockhash() == last_main_blockhash
print("Confirmed: malicious chain is under penalty")

print("\nGenerating 65 more honest blocks (New height = 81)")
self.nodes[0].generate(65)
print("Honest blocks generated")

print("\nGenerating 1 more malicious block (New height = 82)")
last_malicious_blockhash=self.nodes[3].generate(1)[0]
print("Malicious blocks generated")

print("\nWaiting that all network nodes are synced with same chain length")
sync_blocks(self.nodes, 1, True)
print("Network nodes are synced")

print("\nTesting if all the nodes/chains have the same best tip")
assert (self.nodes[0].getbestblockhash() == self.nodes[1].getbestblockhash()
== self.nodes[2].getbestblockhash() == self.nodes[3].getbestblockhash())
print("Confirmed: all the nodes have the same best tip")

print("\nTesting if the current chain switched to the malicious chain")
assert self.nodes[0].getbestblockhash() == last_malicious_blockhash
print("Confirmed: malicious chain is the best chain")

time.sleep(2)
sync_mempools(self.nodes)

if __name__ == '__main__':
blockdelay().main()
2 changes: 1 addition & 1 deletion qa/rpc-tests/test_framework/authproxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@

USER_AGENT = "AuthServiceProxy/0.1"

HTTP_TIMEOUT = 30
HTTP_TIMEOUT = 600

log = logging.getLogger("BitcoinRPC")

Expand Down
6 changes: 3 additions & 3 deletions qa/rpc-tests/test_framework/test_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,12 @@ def split_network(self):

def sync_all(self):
if self.is_network_split:
sync_blocks(self.nodes[:2])
sync_blocks(self.nodes[2:])
sync_blocks(self.nodes[:2], p=False)
sync_blocks(self.nodes[2:], p=False)
sync_mempools(self.nodes[:2])
sync_mempools(self.nodes[2:])
else:
sync_blocks(self.nodes)
sync_blocks(self.nodes, p=False)
sync_mempools(self.nodes)

def join_network(self):
Expand Down
6 changes: 4 additions & 2 deletions qa/rpc-tests/test_framework/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,14 @@ def hex_str_to_bytes(hex_str):
def str_to_b64str(string):
return b64encode(string.encode('utf-8')).decode('ascii')

def sync_blocks(rpc_connections, wait=1):
def sync_blocks(rpc_connections, wait=1, p=False):
"""
Wait until everybody has the same block count
"""
while True:
counts = [ x.getblockcount() for x in rpc_connections ]
if p:
print counts
if counts == [ counts[0] ]*len(counts):
break
time.sleep(wait)
Expand Down Expand Up @@ -294,7 +296,7 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
url = rpc_url(i, rpchost)
wait_for_bitcoind_start(bitcoind_processes[i], url, i)
if os.getenv("PYTHON_DEBUG", ""):
print "start_node: RPC succesfully started"
print "start_node: RPC succesfully started at: " + url
proxy = get_rpc_proxy(url, i, timeout=timewait)

if COVERAGE_DIR:
Expand Down
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ BITCOIN_CORE_H = \
wallet/wallet.h \
wallet/wallet_ismine.h \
wallet/walletdb.h \
zen/delay.h \
zmq/zmqabstractnotifier.h \
zmq/zmqconfig.h\
zmq/zmqnotificationinterface.h \
Expand Down Expand Up @@ -266,6 +267,7 @@ libbitcoin_server_a_SOURCES = \
validation.cpp \
validationinterface.cpp \
versionbits.cpp \
zen/delay.cpp \
$(BITCOIN_CORE_H)

if ENABLE_ZMQ
Expand Down
3 changes: 3 additions & 0 deletions src/chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ class CBlockIndex
//! (memory only) Total amount of work (expected number of hashes) in the chain up to and including this block
arith_uint256 nChainWork;

int64_t nChainDelay;

//! Number of transactions in this block.
//! Note: in a potential headers-first mode, this number cannot be relied upon
unsigned int nTx;
Expand Down Expand Up @@ -210,6 +212,7 @@ class CBlockIndex
nDataPos = 0;
nUndoPos = 0;
nChainWork = arith_uint256();
nChainDelay = 0;
nTx = 0;
nChainTx = 0;
nStatus = 0;
Expand Down
12 changes: 6 additions & 6 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
#define TESTNET_GENESIS_NONCE 2279587UL
#define TESTNET_GENESIS_TIMESTAMP 1517541975UL

#define REGTEST_GENESIS_HASH "0x084c58211fe102add2fb5c3976f14f997e3c27a3a6fd5ab15a1929f7b5db9d95"
#define REGTEST_GENESIS_NONCE 1UL
#define REGTEST_GENESIS_TIMESTAMP 1517542062UL
#define REGTEST_GENESIS_HASH "0x03ddaf131760ea9614a00fd504b035061b6ad367dc91be7a8821bd787484cba0"
#define REGTEST_GENESIS_NONCE 2UL
#define REGTEST_GENESIS_TIMESTAMP 1548210160UL

static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward, const Consensus::Params& consensus)
{
Expand Down Expand Up @@ -402,10 +402,10 @@ class CRegTestParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nTimeout = 999999999999ULL;

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000000000000100010");
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000000000000000002");

// By default assume that the signatures in ancestors of this block are valid.
consensus.defaultAssumeValid = uint256S("0x084c58211fe102add2fb5c3976f14f997e3c27a3a6fd5ab15a1929f7b5db9d95");
consensus.defaultAssumeValid = uint256S("0x03ddaf131760ea9614a00fd504b035061b6ad367dc91be7a8821bd787484cba0");

pchMessageStart[0] = 0x96;
pchMessageStart[1] = 0xa6;
Expand Down Expand Up @@ -434,7 +434,7 @@ class CRegTestParams : public CChainParams {

checkpointData = (CCheckpointData){
boost::assign::map_list_of
( 0, uint256S("0x084c58211fe102add2fb5c3976f14f997e3c27a3a6fd5ab15a1929f7b5db9d95")),
( 0, uint256S("0x03ddaf131760ea9614a00fd504b035061b6ad367dc91be7a8821bd787484cba0")),
0,
0,
0
Expand Down
6 changes: 3 additions & 3 deletions src/clientversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
//! These need to be macros, as clientversion.cpp's and paccoin*-res.rc's voodoo requires it
#define CLIENT_VERSION_MAJOR 0
#define CLIENT_VERSION_MINOR 12
#define CLIENT_VERSION_REVISION 5
#define CLIENT_VERSION_BUILD 1
#define CLIENT_VERSION_REVISION 6
#define CLIENT_VERSION_BUILD 0

//! Set to true for release, false for prerelease or test build
#define CLIENT_VERSION_IS_RELEASE true
Expand All @@ -26,7 +26,7 @@
* Copyright year (2009-this)
* Todo: update this when changing our copyright comments in the source
*/
#define COPYRIGHT_YEAR 2018
#define COPYRIGHT_YEAR 2019

#endif //HAVE_CONFIG_H

Expand Down
4 changes: 3 additions & 1 deletion src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,7 @@ UniValue getchaintips(const UniValue& params, bool fHelp)
" \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n"
" \"branchlen\": 0 (numeric) zero for main chain\n"
" \"status\": \"active\" (string) \"active\" for the main chain\n"
" \"penalization\": 0 (numeric) zero for main chain\n"
" },\n"
" {\n"
" \"height\": xxxx,\n"
Expand All @@ -932,6 +933,7 @@ UniValue getchaintips(const UniValue& params, bool fHelp)
" \"chainwork\" : \"0000...1f3\"\n"
" \"branchlen\": 1 (numeric) length of branch connecting the tip to the main chain\n"
" \"status\": \"xxxx\" (string) status of the chain (active, valid-fork, valid-headers, headers-only, invalid)\n"
" \"penalization\": 0 (numeric) penalization of the chain, zero for the main chain.\n"
" }\n"
"]\n"
"Possible values for status:\n"
Expand Down Expand Up @@ -1009,7 +1011,7 @@ UniValue getchaintips(const UniValue& params, bool fHelp)
status = "unknown";
}
obj.push_back(Pair("status", status));

obj.push_back(Pair("penalization", block->nChainDelay));
res.push_back(obj);
}

Expand Down
Loading

0 comments on commit ab45a5f

Please sign in to comment.