Skip to content

Commit

Permalink
[Tests] Fix and re-enable wrapping serials test
Browse files Browse the repository at this point in the history
Github-Pull: #1218
Rebased-From: 039c220
  • Loading branch information
random-zebra authored and Fuzzbawls committed Jan 11, 2020
1 parent fc7069a commit 5c70582
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 89 deletions.
7 changes: 4 additions & 3 deletions test/functional/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@

BASE_SCRIPTS= [
# Scripts that are run by the travis build process.

# Longest test should go first, to favor running tests in parallel
'wallet_basic.py', # ~ 1155 sec
'wallet_backup.py', # ~ 459 sec
Expand All @@ -72,6 +73,7 @@
'interface_rest.py', # ~ 154 sec
'rpc_spork.py', # ~ 149 sec
'feature_proxy.py', # ~ 143 sec
'zerocoin_wrapped_serials.py', # ~ 137 sec
'mining_pos_fakestake.py', # ~ 123 sec

# vv Tests less than 2m vv
Expand Down Expand Up @@ -101,7 +103,8 @@
'rpc_signmessage.py', # ~ 50 sec
'feature_help.py', # ~ 30 sec

# 'zerocoin_wrapped_serials.py',
# Don't append tests at the end to avoid merge conflicts
# Put them in a random line within the section that fits their approximate run-time
# 'feature_block.py',
# 'rpc_fundrawtransaction.py',
# 'wallet_importmulti.py',
Expand All @@ -127,8 +130,6 @@
# 'p2p_unrequested_blocks.py',
# 'feature_config_args.py',

# Don't append tests at the end to avoid merge conflicts
# Put them in a random line within the section that fits their approximate run-time
]

EXTENDED_SCRIPTS = [
Expand Down
193 changes: 107 additions & 86 deletions test/functional/zerocoin_wrapped_serials.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,103 +4,124 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

'''
Covers the 'Wrapped Serials Attack' scenario
Covers the 'Wrapped Serials Attack' scenario for Zerocoin Spends
'''

import random
from time import sleep

from test_framework.authproxy import JSONRPCException
from test_framework.util import assert_equal, assert_greater_than
from test_framework.test_framework import PivxTestFramework
from test_framework.util import (
sync_blocks,
assert_equal,
assert_raises_rpc_error,
set_node_times,
DecimalAmt
)

class zPIVwrappedSerialsTest(PivxTestFramework):

def set_test_params(self):
self.num_nodes = 3
# node 0 and node 1 move the chain (node 0 also sets the sporks)
# node 2 does the spends
self.extra_args = [['-staking=0']]*self.num_nodes
self.extra_args[0].append('-sporkkey=932HEevBSujW2ud7RfB1YF91AFygbBRQj3de3LyaCRqNzKKgWXi')

def setup_chain(self):
# Start with PoS cache: 330 blocks
self._initialize_chain(toPosPhase=True)
self.enable_mocktime()

def log_title(self):
title = "*** Starting %s ***" % self.__class__.__name__
underline = "-" * len(title)
description = "Tests the 'Wrapped Serials Attack' scenario with for Zerocoin Spends"
self.log.info("\n\n%s\n%s\n%s\n", title, underline, description)

def setV4SpendEnforcement(self, fEnable=True):
sporkName = "SPORK_18_ZEROCOIN_PUBLICSPEND_V4"
# update spork 18 with node[0]
if fEnable:
self.log.info("Enabling v4 PublicSpend version with SPORK 18...")
res = self.activate_spork(0, sporkName)
else:
self.log.info("Enabling v3 PublicSpend version with SPORK 18...")
res = self.deactivate_spork(0, sporkName)
assert_equal(res, "success")
sleep(1)
# check that node[1] receives it
assert_equal(fEnable, self.is_spork_active(1, sporkName))
self.log.info("done")

from fake_stake.base_test import PIVX_FakeStakeTest

class zPIVwrappedSerialsTest(PIVX_FakeStakeTest):

def run_test(self):

def get_zerocoin_data(coin):
return coin["s"], coin["r"], coin["k"], coin["id"], coin["d"], coin["t"]

def check_balances(denom, zpiv_bal, piv_bal):
zpiv_bal -= denom
assert_equal(self.nodes[2].getzerocoinbalance()['Total'], zpiv_bal)
piv_bal += denom
wi = self.nodes[2].getwalletinfo()
assert_equal(wi['balance'] + wi['immature_balance'], piv_bal)
return zpiv_bal, piv_bal

def stake_4_blocks(block_time):
for peer in range(2):
for i in range(2):
block_time = self.generate_pos(peer, block_time)
sync_blocks(self.nodes)
return block_time

q = 73829871667027927151400291810255409637272593023945445234219354687881008052707
pow2 = 2**256
self.description = "Covers the 'Wrapped Serials Attack' scenario."
self.init_test()

INITAL_MINED_BLOCKS = 351 # Blocks mined before minting
MORE_MINED_BLOCKS = 31 # Blocks mined after minting (before spending)
DENOM_TO_USE = 1000 # zc denomination used for double spending attack
K_BITSIZE = 128 # bitsize of the range for random K
NUM_OF_K = 5 # number of wrapping serials to try

# 1) Start mining blocks
self.log.info("Mining %d first blocks..." % INITAL_MINED_BLOCKS)
self.node.generate(INITAL_MINED_BLOCKS)
sleep(2)

# 2) Mint zerocoins
self.log.info("Minting %d-denom zPIVs..." % DENOM_TO_USE)
balance = self.node.getbalance("*", 100)
assert_greater_than(balance, DENOM_TO_USE)
total_mints = 0
while balance > DENOM_TO_USE:
try:
self.node.mintzerocoin(DENOM_TO_USE)
except JSONRPCException:
break
sleep(1)
total_mints += 1
self.node.generate(1)
sleep(1)
if total_mints % 5 == 0:
self.log.info("Minted %d coins" % total_mints)
if total_mints >= 20:
break
balance = self.node.getbalance("*", 100)
sleep(2)

# 3) Mine more blocks and collect the mint
self.log.info("Mining %d more blocks..." % MORE_MINED_BLOCKS)
self.node.generate(MORE_MINED_BLOCKS)
sleep(2)
mint = self.node.listmintedzerocoins(True, True)[0]

# 4) Get the raw zerocoin data
exported_zerocoins = self.node.exportzerocoins(False)
zc = [x for x in exported_zerocoins if mint["serial hash"] == x["id"]]
if len(zc) == 0:
raise AssertionError("mint not found")

# 5) Spend the minted coin (mine two more blocks)
self.log.info("Spending the minted coin with serial %s and mining two more blocks..." % zc[0]["s"])
txid = self.node.spendzerocoinmints([mint["serial hash"]])['txid']
self.log.info("Spent on tx %s" % txid)
self.node.generate(2)
sleep(2)

# 6) create the new serials
new_serials = []
for i in range(NUM_OF_K):
K_BITSIZE = 128 # bitsize of the range for random K
self.log_title()
block_time = self.mocktime
set_node_times(self.nodes, block_time)

# Start with cache balances
wi = self.nodes[2].getwalletinfo()
balance = wi['balance'] + wi['immature_balance']
zpiv_balance = self.nodes[2].getzerocoinbalance()['Total']
assert_equal(balance, DecimalAmt(13833.92))
assert_equal(zpiv_balance, 6666)

# Export zerocoin data
listmints = self.nodes[2].listmintedzerocoins(True, True)
serial_ids = [mint["serial hash"] for mint in listmints]
exported_zerocoins = [x for x in self.nodes[2].exportzerocoins(False) if x["id"] in serial_ids]
assert_equal(8, len(exported_zerocoins))

# 1) Spend 1 coin and mine two more blocks
serial_0, randomness_0, privkey_0, id_0, denom_0, tx_0 = get_zerocoin_data(exported_zerocoins[0])
self.log.info("Spending the minted coin with serial %s..." % serial_0[:16])
txid = self.nodes[2].spendzerocoin(denom_0, False, False, "", False)['txid']
# stake 4 blocks - check it gets included on chain and check balances
block_time = stake_4_blocks(block_time)
self.check_tx_in_chain(0, txid)
zpiv_balance, balance = check_balances(denom_0, zpiv_balance, balance)
self.log.info("Coin spent.")

# 2) create 5 new coins
new_coins = []
for i in range(5):
K = random.getrandbits(K_BITSIZE)
new_serials.append(hex(int(zc[0]["s"], 16) + K*q*pow2)[2:])

randomness = zc[0]["r"]
privkey = zc[0]["k"]

# 7) Spend the new zerocoins
for serial in new_serials:
self.log.info("Spending the wrapping serial %s" % serial)
tx = None
try:
tx = self.node.spendrawzerocoin(serial, randomness, DENOM_TO_USE, privkey)
except JSONRPCException as e:
exc_msg = str(e)
if exc_msg == "CoinSpend: failed check (-4)":
self.log.info("GOOD: Transaction did not verify")
else:
raise e

if tx is not None:
self.log.warning("Tx is: %s" % tx)
raise AssertionError("TEST FAILED")

self.log.info("%s PASSED" % self.__class__.__name__)
new_coins.append({
"s": hex(int(serial_0, 16) + K*q*pow2)[2:],
"r": randomness_0,
"d": denom_0,
"p": privkey_0,
"t": tx_0})

# 3) Spend the new zerocoins (V2)
for c in new_coins:
self.log.info("V2 - Spending the wrapping serial %s" % c["s"])
assert_raises_rpc_error(-4, "CoinSpend: failed check",
self.nodes[2].spendrawzerocoin, c["s"], c["r"], c["d"], c["p"], "", c["t"], False)
self.log.info("GOOD: It was not possible")



Expand Down

0 comments on commit 5c70582

Please sign in to comment.