Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 179 additions & 0 deletions divi/qa/rpc-tests/custom_address_update.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
#!/usr/bin/env python3
# Copyright (c) 2020-2021 The DIVI developers
# Distributed under the MIT/X11 software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

# Tests that nodes with support for custom reward addresses are compatible
# to nodes without it before the change actually activates. For this,
# we run two masternodes (one with and one without support) and check
# that they can both see each other's masternode.
#
# We use five nodes:
# - node 0 is a masternode with default protocol (i.e. "new")
# - node 1 is a masternode with previous protocol (i.e. "old")
# - nodes 2-4 are just used to get above the "three full nodes" threshold

from test_framework import BitcoinTestFramework
from util import *
from masternode import *

import collections
import time

OLD_PROTOCOL = 70915
NEW_PROTOCOL = 71000


def args_for (n):
base = ["-debug", "-nolistenonion", "-activeversion=%d" % OLD_PROTOCOL]
if n == 1:
base.extend (["-protocolversion=%d" % OLD_PROTOCOL])
return base


class CustomAddressUpdateTest (BitcoinTestFramework):

def setup_chain (self):
for i in range (5):
initialize_datadir (self.options.tmpdir, i)

def setup_network (self, config_line=None, extra_args=[]):
self.nodes = [
start_node (i, self.options.tmpdir, extra_args=args_for (i))
for i in range (5)
]

# We want to work with mock times that are beyond the genesis
# block timestamp but before current time (so that nodes being
# started up and before they get on mocktime aren't rejecting
# the on-disk blockchain).
self.time = 1580000000
assert self.time < time.time ()
set_node_times (self.nodes, self.time)

connect_nodes (self.nodes[0], 2)
connect_nodes (self.nodes[1], 2)
connect_nodes (self.nodes[2], 3)
connect_nodes (self.nodes[2], 4)
connect_nodes (self.nodes[3], 4)

self.is_network_split = False

def start_node (self, n):
"""Starts node n (0 or 1) with the proper arguments
and masternode config for it."""

assert n in [0, 1]
configs = [[c.getLine ()] for c in self.cfg]

args = args_for (n)
args.append ("-masternode")
args.append ("-masternodeprivkey=%s" % self.cfg[n].privkey)
args.append ("-masternodeaddr=127.0.0.1:%d" % p2p_port (n))

self.nodes[n] = start_node (n, self.options.tmpdir,
extra_args=args, mn_config_lines=configs[n])
self.nodes[n].setmocktime (self.time)

for i in [2, 3, 4]:
connect_nodes (self.nodes[n], i)
sync_blocks (self.nodes)

def stop_node (self, n):
"""Stops node n."""

stop_node (self.nodes[n], n)
self.nodes[n] = None

def advance_time (self, dt=1):
"""Advances mocktime by the given number of seconds."""

self.time += dt
set_node_times (self.nodes, self.time)

def mine_blocks (self, n):
"""Mines blocks with node 2."""

self.nodes[2].setgenerate(True, n)
sync_blocks (self.nodes)

def run_test (self):
assert_equal (self.nodes[0].getnetworkinfo ()["protocolversion"], NEW_PROTOCOL)
assert_equal (self.nodes[1].getnetworkinfo ()["protocolversion"], OLD_PROTOCOL)

self.fund_masternodes ()
self.start_masternodes ()

def fund_masternodes (self):
print ("Funding masternodes...")

# The collateral needs 15 confirmations, and the masternode broadcast
# signature must be later than that block's timestamp. Thus we start
# with a very early timestamp.
genesis = self.nodes[0].getblockhash (0)
genesisTime = self.nodes[0].getblockheader (genesis)["time"]
assert genesisTime < self.time
set_node_times (self.nodes, genesisTime)

self.nodes[0].setgenerate (True, 1)
sync_blocks (self.nodes)
self.nodes[1].setgenerate (True, 1)
sync_blocks (self.nodes)
self.mine_blocks (25)
assert_equal (self.nodes[0].getbalance (), 1250)
assert_equal (self.nodes[1].getbalance (), 1250)

id1 = self.nodes[0].allocatefunds ("masternode", "mn1", "copper")["txhash"]
id2 = self.nodes[1].allocatefunds ("masternode", "mn2", "copper")["txhash"]
sync_mempools (self.nodes)
self.mine_blocks (15)
set_node_times (self.nodes, self.time)
self.mine_blocks (1)

self.cfg = [
fund_masternode (self.nodes[0], "mn1", "copper", id1, "localhost:%d" % p2p_port (0)),
fund_masternode (self.nodes[1], "mn2", "copper", id2, "localhost:%d" % p2p_port (1)),
]

def start_masternodes (self):
print ("Starting masternodes...")

for i in range (2):
self.stop_node (i)
self.start_node (i)

# Start the masternodes after the nodes are back up and connected
# (so they will receive each other's broadcast).
for i in range (2):
res = self.nodes[i].startmasternode ("mn%d" % (i + 1))
assert_equal (res, {"status": "success"})

# Check status of the masternodes themselves.
for i in [0, 1]:
data = self.nodes[i].getmasternodestatus ()
assert_equal (data["status"], 4)
assert_equal (data["txhash"], self.cfg[i].txid)
assert_equal (data["outputidx"], self.cfg[i].vout)
assert_equal (data["message"], "Masternode successfully started")

# Both masternodes should see each other, independent of the
# protocol version used.
lists = [{}] * 2
for i in range (2):
cur = self.nodes[i].listmasternodes ()
while len (cur) < 2:
time.sleep (0.1)
cur = self.nodes[i].listmasternodes ()
for c in cur:
lists[i][c["txhash"]] = c
assert_equal (lists[0], lists[1])

lst = lists[0]
assert_equal (len (lst), 2)
for val in lst.values ():
assert_equal (val["tier"], "COPPER")
assert_equal (val["status"], "ENABLED")


if __name__ == '__main__':
CustomAddressUpdateTest ().main ()
17 changes: 16 additions & 1 deletion divi/qa/rpc-tests/masternode.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class MnConfigLine (object):

def __init__ (self, line):
parts = line.split (" ")
assert_equal (len (parts), 5)
assert len (parts) in [5, 6]

self.line = line

Expand All @@ -22,6 +22,21 @@ def __init__ (self, line):
self.txid = parts[3]
self.vout = int (parts[4])

if len (parts) >= 6:
self.rewardAddr = parts[5]
else:
self.rewardAddr = None

def getLine (self):
"""Returns the config line as string, to put into masternode.conf."""

res = "%s %s %s %s %d" % (self.alias, self.ip, self.privkey,
self.txid, self.vout)
if self.rewardAddr is not None:
res += " %s" % self.rewardAddr

return res


def fund_masternode (node, alias, tier, txid, ip):
"""Calls fundmasternode with the given data and returns the
Expand Down
1 change: 1 addition & 0 deletions divi/qa/rpc-tests/mncollateral.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def run_test (self):
assert_equal (cfg.alias, "spent")
assert_equal (cfg.ip, "1.2.3.4:51476")
assert_equal (cfg.txid, txid)
assert_equal (cfg.rewardAddr, None)
assert_equal (node.gettxout (cfg.txid, cfg.vout)["value"], 300)

# It should still be possible to spend the coins, invalidating the
Expand Down
20 changes: 12 additions & 8 deletions divi/qa/rpc-tests/mnoperation.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ def start_node (self, n):
and masternode config for it."""

configs = [
[c.line for c in self.cfg],
[self.cfg[0].line],
[self.cfg[1].line],
[c.getLine () for c in self.cfg],
[self.cfg[0].getLine ()],
[self.cfg[1].getLine ()],
]

args = self.base_args[:]
Expand Down Expand Up @@ -141,6 +141,8 @@ def fund_masternodes (self):
fund_masternode (self.nodes[0], "mn2", "silver", id2, "localhost:%d" % p2p_port (2)),
]

self.cfg[1].rewardAddr = self.nodes[0].getnewaddress ("reward2")

def start_masternodes (self):
print ("Starting masternodes...")

Expand Down Expand Up @@ -187,8 +189,9 @@ def start_masternodes (self):
assert_equal (lst[1]["tier"], "SILVER")
for i in range (2):
assert_equal (lst[i]["status"], "ENABLED")
assert_equal (lst[i]["addr"],
self.nodes[i + 1].getmasternodestatus ()["addr"])
n = self.nodes[i + 1]
for key in ["addr", "rewardscript"]:
assert_equal (lst[i][key], n.getmasternodestatus ()[key])
assert_equal (lst[i]["txhash"], self.cfg[i].txid)
assert_equal (lst[i]["outidx"], self.cfg[i].vout)

Expand Down Expand Up @@ -263,7 +266,7 @@ def payments_both_active (self):
winners = self.verify_number_of_votes_exist_and_tally_winners(startHeight,endHeight, 2)

addr1 = self.nodes[1].getmasternodestatus ()["addr"]
addr2 = self.nodes[2].getmasternodestatus ()["addr"]
addr2 = self.cfg[1].rewardAddr
assert_equal (len (winners), 2)
assert_greater_than (winners[addr1], 0)
assert_greater_than (winners[addr2], 0)
Expand Down Expand Up @@ -308,8 +311,9 @@ def check_rewards (self):
self.start_node (0)
sync_blocks (self.nodes)

assert_greater_than (self.nodes[0].getbalance ("alloc->mn1"), 0)
assert_greater_than (self.nodes[0].getbalance ("alloc->mn2"), 0)
assert_greater_than (self.nodes[0].getbalance ("alloc->mn1"), 100)
assert_equal (self.nodes[0].getbalance ("alloc->mn2"), 300)
assert_greater_than (self.nodes[0].getbalance ("reward2"), 0)


if __name__ == '__main__':
Expand Down
1 change: 1 addition & 0 deletions divi/qa/rpc-tests/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
'StakingVaultStaking.py',
'StakingVaultDeactivation.py',
'StakingVaultSpam.py',
'custom_address_update.py',
'forknotify.py',
'getchaintips.py',
'httpbasics.py',
Expand Down
3 changes: 3 additions & 0 deletions divi/src/ForkActivation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ const std::unordered_map<Fork, int64_t,std::hash<int>> ACTIVATION_TIMES = {
{Fork::TestByTimestamp, 1000000000},
{Fork::HardenedStakeModifier, unixTimestampForDec31stMidnight},
{Fork::UniformLotteryWinners, unixTimestampForDec31stMidnight},

/** FIXME: Set actual time once scheduled. */
{Fork::CustomRewardAddresses, 2000000000},
};

} // anonymous namespace
Expand Down
7 changes: 7 additions & 0 deletions divi/src/ForkActivation.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ enum Fork
TestByTimestamp,
HardenedStakeModifier,
UniformLotteryWinners,

/**
* Custom reward addresses for masternodes. This is activated like other
* forks based on block time; but it only affects the network protocol
* we require from peers, not the actual consensus logic.
*/
CustomRewardAddresses,
};

/**
Expand Down
Loading