diff --git a/src/consensus/upgrades.cpp b/src/consensus/upgrades.cpp index e313d831c94785..917511c7e9016d 100644 --- a/src/consensus/upgrades.cpp +++ b/src/consensus/upgrades.cpp @@ -13,66 +13,70 @@ * We are using it in the -nuparams startup arg and input it with spaces is just ugly. */ const struct NUInfo NetworkUpgradeInfo[Consensus::MAX_NETWORK_UPGRADES] = { - { - /*.strName =*/ "Base", - /*.strInfo =*/ "PIVX network", - }, - { - /*.strName =*/ "PoS", - /*.strInfo =*/ "Proof of Stake Consensus activation", - }, - { - /*.strName =*/ "PoS_v2", - /*.strInfo =*/ "New selection for stake modifier", - }, - { - /*.strName =*/ "Zerocoin", - /*.strInfo =*/ "ZeroCoin protocol activation - start block v4", - }, - { - /*.strName =*/ "Zerocoin_v2", - /*.strInfo =*/ "New zerocoin serials and zPOS start", - }, - { - /*.strName =*/ "BIP65", - /*.strInfo =*/ "CLTV (BIP65) activation - start block v5", - }, - { - /*.strName =*/ "Zerocoin_Public", - /*.strInfo =*/ "Activation of zerocoin public spends (spend v3)", - }, - { - /*.strName =*/ "PIVX_v3.4", - /*.strInfo =*/ "New 256-bit stake modifier - start block v6", - }, - { - /*.strName =*/ "PIVX_v4.0", - /*.strInfo =*/ "New message sigs - start block v7 - time protocol - zc spend v4", - }, - { - /*.strName =*/ "v5_shield", - /*.strInfo =*/ "Sapling Shield - start block v8 - start transaction v3", - }, - { - /*.strName =*/ "PIVX_v5.2", - /*.strInfo =*/ "New cold-staking rules", - }, - { - /*.strName =*/ "PIVX_v5.3", - /*.strInfo =*/ "New staking rules", - }, - { - /*.strName =*/ "PIVX_v5.5", - /*.strInfo =*/ "New rewards structure", - }, - { - /*.strName =*/ "v6_evo", - /*.strInfo =*/ "Deterministic Masternodes", - }, - { - /*.strName =*/ "Test_dummy", - /*.strInfo =*/ "Test dummy info", - }, + { + /*.strName =*/"Base", + /*.strInfo =*/"PIVX network", + }, + { + /*.strName =*/"PoS", + /*.strInfo =*/"Proof of Stake Consensus activation", + }, + { + /*.strName =*/"PoS_v2", + /*.strInfo =*/"New selection for stake modifier", + }, + { + /*.strName =*/"Zerocoin", + /*.strInfo =*/"ZeroCoin protocol activation - start block v4", + }, + { + /*.strName =*/"Zerocoin_v2", + /*.strInfo =*/"New zerocoin serials and zPOS start", + }, + { + /*.strName =*/"BIP65", + /*.strInfo =*/"CLTV (BIP65) activation - start block v5", + }, + { + /*.strName =*/"Zerocoin_Public", + /*.strInfo =*/"Activation of zerocoin public spends (spend v3)", + }, + { + /*.strName =*/"PIVX_v3.4", + /*.strInfo =*/"New 256-bit stake modifier - start block v6", + }, + { + /*.strName =*/"PIVX_v4.0", + /*.strInfo =*/"New message sigs - start block v7 - time protocol - zc spend v4", + }, + { + /*.strName =*/"v5_shield", + /*.strInfo =*/"Sapling Shield - start block v8 - start transaction v3", + }, + { + /*.strName =*/"PIVX_v5.2", + /*.strInfo =*/"New cold-staking rules", + }, + { + /*.strName =*/"PIVX_v5.3", + /*.strInfo =*/"New staking rules", + }, + { + /*.strName =*/"PIVX_v5.5", + /*.strInfo =*/"New rewards structure", + }, + { + /*.strName =*/"v6_evo", + /*.strInfo =*/"Deterministic Masternodes", + }, + { + /*.strName =*/"shield_staking", + /*.strInfo =*/"Shield Staking", + }, + { + /*.strName =*/"Test_dummy", + /*.strInfo =*/"Test dummy info", + }, }; UpgradeState NetworkUpgradeState( diff --git a/src/primitives/block.h b/src/primitives/block.h index 5779ed2d7e7f8c..27bf49c60da0f2 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -134,7 +134,7 @@ class CBlock : public CBlockHeader READWRITE(obj.vchBlockSig); // Shield Staking Proof - if (obj.nVersion >= 12 && obj.IsProofOfStake()) { + if (obj.nVersion >= 12 && obj.IsProofOfShieldStake()) { READWRITE(obj.shieldStakeProof); } } diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 1621f0002006c2..4dff921b796569 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -135,7 +135,7 @@ bool CTransaction::IsCoinStake() const { if (vin.empty()) return false; - if (!sapData->vShieldedSpend.empty()) + if (sapData && !sapData->vShieldedSpend.empty()) return false; bool fAllowNull = vin[0].IsZerocoinSpend(); if (vin[0].prevout.IsNull() && !fAllowNull) diff --git a/test/functional/mining_pos_coldStaking.py b/test/functional/mining_pos_coldStaking.py index c073da2563cd47..9306d6d90d9860 100755 --- a/test/functional/mining_pos_coldStaking.py +++ b/test/functional/mining_pos_coldStaking.py @@ -92,7 +92,7 @@ def run_test(self): assert self.nodes[1].lockunspent(False, True, [{"txid": x['txid'], "vout": x['vout']}]) # check that it cannot stake sleep(1) - assert_equal(self.nodes[1].getstakingstatus()["stakeablecoins"], 0) + assert_equal(self.nodes[1].getstakingstatus()["transparent_stakeable_coins"], 0) # create shielded balance for node 0 self.log.info("Shielding some coins for node0...") self.nodes[0].shieldsendmany("from_transparent", [{"address": self.nodes[0].getnewshieldaddress(), @@ -205,7 +205,7 @@ def run_test(self): # ----------------------------------------------------------- print("*** 7 ***") self.log.info("Trying to generate a cold-stake block before whitelisting the owner...") - assert_equal(self.nodes[1].getstakingstatus()["stakeablecoins"], 0) + assert_equal(self.nodes[1].getstakingstatus()["transparent_stakeable_coins"], 0) assert_equal(self.nodes[1].listdelegators(), []) self.log.info("Nice. Cold staker was NOT able to create the block yet.") @@ -232,7 +232,7 @@ def run_test(self): # 9) check that the staker can use the coins to stake a block with internal miner. # -------------------------------------------------------------------------------- print("*** 9 ***") - assert_equal(self.nodes[1].getstakingstatus()["stakeablecoins"], NUM_OF_INPUTS-1) + assert_equal(self.nodes[1].getstakingstatus()["transparent_stakeable_coins"], NUM_OF_INPUTS-1) self.log.info("Generating one valid cold-stake block...") self.mocktime = self.generate_pos(1, self.mocktime) self.log.info("New block created by cold-staking. Trying to submit...") @@ -338,12 +338,12 @@ def run_test(self): assert ret assert_equal(self.nodes[1].listdelegators(), []) assert_equal(self.nodes[1].listdelegators(True)[0]["address"], owner_address) - assert_equal(self.nodes[1].getstakingstatus()["stakeablecoins"], 0) + assert_equal(self.nodes[1].getstakingstatus()["transparent_stakeable_coins"], 0) self.log.info("Re-enable delegation") ret = self.nodes[1].delegatoradd(owner_address) assert ret assert_equal(self.nodes[1].listdelegators()[0]["address"], owner_address) - assert_equal(self.nodes[1].getstakingstatus()["stakeablecoins"], len(stakeable_coins)) + assert_equal(self.nodes[1].getstakingstatus()["transparent_stakeable_coins"], len(stakeable_coins)) self.log.info("Cancel the stake delegation spending the delegated utxos...") delegated_utxos = getDelegatedUtxos(self.nodes[0].listunspent()) # remove one utxo to spend later @@ -379,7 +379,7 @@ def run_test(self): # ----------------------------------------------------------- print("*** 14 ***") self.log.info("Trying to generate one cold-stake block again...") - assert_equal(self.nodes[1].getstakingstatus()["stakeablecoins"], 0) + assert_equal(self.nodes[1].getstakingstatus()["transparent_stakeable_coins"], 0) self.log.info("Good. Cold staker was NOT able to create any more blocks.") # 15) check balances when mature. diff --git a/test/functional/mining_shield_pos.py b/test/functional/mining_shield_pos.py new file mode 100755 index 00000000000000..7a4d287c534242 --- /dev/null +++ b/test/functional/mining_shield_pos.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +# Copyright (c) 2023 The PIVX Core 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 PivxTestFramework +from test_framework.util import ( + assert_equal, + assert_raises_rpc_error, + disconnect_nodes, +) + + +class PIVX_ShieldStakingTest(PivxTestFramework): + + def set_test_params(self): + self.num_nodes = 3 + self.setup_clean_chain = True + + def run_test(self): + self.log.info("Shield stake functional tests") + + # Mine enough blocks to activate shield staking" + a bonus so node0 has enough pivs to mine the final block + self.nodes[0].generate(800) + self.sync_all() + + # Send some shielded notes to node1 + saplingAddr1 = self.nodes[1].getnewshieldaddress() + txid = self.nodes[0].sendtoaddress(saplingAddr1, 40000, "", "", True) + + # Generate more blocks so the shield notes becomes stakeable + self.nodes[0].generate(20) + self.sync_all() + + # Sanity check, shield staking is activated + assert_equal(self.nodes[0].getblockchaininfo()['upgrades']['v5 shield']['status'], 'active') + assert_equal(self.nodes[0].getblockchaininfo()['upgrades']['v6 evo']['status'], 'active') + assert_equal(self.nodes[0].getblockchaininfo()['upgrades']['shield staking']['status'], 'active') + + # Node1 has exactly one shield stakeable note + assert_equal(self.nodes[1].getstakingstatus()['shield_stakeables_notes'], 1) + assert_equal(self.nodes[1].getstakingstatus()['transparent_stakeable_coins'], 0) + + # Before generating the shield stake block isolate node 2: + disconnect_nodes(self.nodes[0], 2) + disconnect_nodes(self.nodes[2], 0) + disconnect_nodes(self.nodes[1], 2) + disconnect_nodes(self.nodes[2], 1) + + # Generate the block and check that reward is 4 shield PIVs + initialBalance = self.nodes[1].getshieldbalance() + shieldStakeBlockHash = self.nodes[1].generate(1)[0] + assert_equal(self.nodes[1].getshieldbalance()-initialBalance, 4) + + # Check that a loose coinShieldStakeTx cannot go in mempool + coinShieldStakeTxHash = self.nodes[1].getblock(shieldStakeBlockHash)['tx'][1] + coinShieldStakeTxHex = self.nodes[1].gettransaction(coinShieldStakeTxHash)['hex'] + assert_raises_rpc_error(-26, "coinshieldstake", self.nodes[2].sendrawtransaction, coinShieldStakeTxHex) + + # Check that node0 accepted the block: + self.sync_all(self.nodes[0:2]) + assert_equal(self.nodes[1].getblockhash(821), shieldStakeBlockHash) + assert_equal(self.nodes[0].getblockhash(821), shieldStakeBlockHash) + + # Finally verify that the reward can be spent: + recipient = [{"address": self.nodes[0].getnewshieldaddress(), "amount": (initialBalance+2)}] + txid = self.nodes[1].shieldsendmany("from_shield", recipient) + self.sync_all(self.nodes[0:2]) + blockHash = self.nodes[0].generate(1)[0] + self.sync_all(self.nodes[0:2]) + assert (txid in self.nodes[0].getblock(blockHash)['tx']) + assert_equal(self.nodes[1].getblockhash(822), blockHash) + + +if __name__ == '__main__': + PIVX_ShieldStakingTest().main() diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 99067f6988c820..20ef615a8df682 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -886,8 +886,8 @@ def generate_pos(self, node_id, btime=None): rpc_conn = self.nodes[node_id] ss = rpc_conn.getstakingstatus() assert ss["walletunlocked"] - assert ss["stakeablecoins"] > 0 - assert ss["stakingbalance"] > 0.0 + assert ss["transparent_stakeable_coins"] > 0 + assert ss["transparent_staking_balance"] > 0.0 if btime is not None: next_btime = btime + 60 fStaked = False @@ -902,7 +902,7 @@ def generate_pos(self, node_id, btime=None): # couldn't generate block. check that this node can still stake (after 60 failures) if failures > 60: ss = rpc_conn.getstakingstatus() - if not (ss["walletunlocked"] and ss["stakeablecoins"] > 0 and ss["stakingbalance"] > 0.0): + if not (ss["walletunlocked"] and ss["transparent_stakeable_coins"] > 0 and ss["transparent_staking_balance"] > 0.0): raise AssertionError("Node %d unable to stake!" % node_id) # try to stake one sec in the future if btime is not None: diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index bcec57836aecba..83270d9d29a0d7 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -94,6 +94,7 @@ 'rpc_bind.py --nonloopback', # ~ 126 sec 'feature_uacomment.py', # ~ 125 sec 'interface_rest.py', # ~ 120 sec + 'mining_shield_pos.py', # ~ 120 sec # vv Tests less than 2m vv 'wallet_upgrade.py', # ~ 119 sec