Skip to content
Merged
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
9 changes: 0 additions & 9 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,6 @@ class CMainParams : public CChainParams
nFakeSerialBlockheightEnd = 1686229;
nSupplyBeforeFakeSerial = 4131563 * COIN; // zerocoin supply at block nFakeSerialBlockheightEnd

// Cold Staking enforcement
nColdStakingStart = 2880000;

/**
* Build the genesis block. Note that the output of the genesis coinbase cannot
* be spent as it did not originally exist in the database.
Expand Down Expand Up @@ -347,9 +344,6 @@ class CTestNetParams : public CMainParams
nFakeSerialBlockheightEnd = -1;
nSupplyBeforeFakeSerial = 0;

// Cold Staking enforcement
nColdStakingStart = 2106100;

//! Modify the testnet genesis block so the timestamp is valid for a later start.
genesis.nTime = 1454124731;
genesis.nNonce = 2402015;
Expand Down Expand Up @@ -448,9 +442,6 @@ class CRegTestParams : public CTestNetParams
// Fake Serial Attack
nFakeSerialBlockheightEnd = -1;

// Cold Staking enforcement
nColdStakingStart = 251;

//! Modify the regtest genesis block so the timestamp is valid for a later start.
genesis.nTime = 1454124731;
genesis.nNonce = 2402015;
Expand Down
4 changes: 1 addition & 3 deletions src/chainparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,6 @@ class CChainParams
bool IsStakeModifierV2(const int nHeight) const { return nHeight >= nBlockStakeModifierlV2; }
int NewSigsActive(const int nHeight) const { return nHeight >= nBlockEnforceNewMessageSignatures; }
int Zerocoin_PublicSpendVersion(const int nHeight) const;
bool Cold_Staking_Enabled(const int height) const { return height >= nColdStakingStart; }
int Block_Enforce_Cold_Staking() const { return nColdStakingStart; }

// fake serial attack
int Zerocoin_Block_EndFakeSerial() const { return nFakeSerialBlockheightEnd; }
Expand Down Expand Up @@ -232,7 +230,7 @@ class CChainParams
int nPublicZCSpendsV4;
int nBlockStakeModifierlV2;
int nBlockEnforceNewMessageSignatures;
int nColdStakingStart;

CAmount nMinColdStakingAmount;

// fake serial attack
Expand Down
21 changes: 14 additions & 7 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1212,10 +1212,10 @@ bool CheckTransaction(const CTransaction& tx, bool fZerocoinActive, bool fReject
if(!CheckZerocoinMint(tx.GetHash(), txout, state, true))
return state.DoS(100, error("CheckTransaction() : invalid zerocoin mint"));
}
// check cold staking enforcement and value out
// check cold staking enforcement (for delegations) and value out
if (txout.scriptPubKey.IsPayToColdStaking()) {
if (!fColdStakingActive)
return state.DoS(100, error("%s: cold staking not active", __func__), REJECT_INVALID, "bad-txns-cold-stake");
return state.DoS(10, error("%s: cold staking not active", __func__), REJECT_INVALID, "bad-txns-cold-stake");
if (txout.nValue < minColdStakingAmount)
return state.DoS(100, error("%s: dust amount (%d) not allowed for cold staking. Min amount: %d",
__func__, txout.nValue, minColdStakingAmount), REJECT_INVALID, "bad-txns-cold-stake");
Expand Down Expand Up @@ -1355,9 +1355,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa
return state.DoS(10, error("%s : Zerocoin transactions are temporarily disabled for maintenance",
__func__), REJECT_INVALID, "bad-tx");

// Cold staking and zerocoin enforcement
// Check transaction
int chainHeight = chainActive.Height();
bool fColdStakingActive = Params().Cold_Staking_Enabled(chainHeight);
bool fColdStakingActive = sporkManager.IsSporkActive(SPORK_17_COLDSTAKING_ENFORCEMENT);
if (!CheckTransaction(tx, chainHeight >= Params().Zerocoin_StartHeight(), true, state, isBlockBetweenFakeSerialAttackRange(chainHeight), fColdStakingActive))
return state.DoS(100, error("%s : CheckTransaction failed", __func__), REJECT_INVALID, "bad-tx");

Expand Down Expand Up @@ -4496,6 +4496,12 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
LogPrintf("%s : skipping transaction locking checks\n", __func__);
}

// Cold Staking enforcement (true during sync - reject P2CS outputs when false)
bool fColdStakingActive = true;

// Zerocoin activation
bool fZerocoinActive = block.GetBlockTime() > Params().Zerocoin_StartTime();

// masternode payments / budgets
CBlockIndex* pindexPrev = chainActive.Tip();
int nHeight = 0;
Expand All @@ -4522,7 +4528,10 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
REJECT_INVALID, "bad-p2cs-outs");
}

// Valid masternode/budget payment
// set Cold Staking Spork
fColdStakingActive = sporkManager.IsSporkActive(SPORK_17_COLDSTAKING_ENFORCEMENT);

// check masternode/budget payment
if (!IsBlockPayeeValid(block, nHeight)) {
mapRejectedBlocks.insert(std::make_pair(block.GetHash(), GetTime()));
return state.DoS(0, error("%s : Couldn't find masternode/budget payment", __func__),
Expand All @@ -4535,8 +4544,6 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
}

// Check transactions
bool fZerocoinActive = block.GetBlockTime() > Params().Zerocoin_StartTime();
bool fColdStakingActive = Params().Cold_Staking_Enabled(nHeight);
std::vector<CBigNum> vBlockSerials;
// TODO: Check if this is ok... blockHeight is always the tip or should we look for the prevHash and get the height?
int blockHeight = chainActive.Height() + 1;
Expand Down
2 changes: 2 additions & 0 deletions src/spork.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ std::vector<CSporkDef> sporkDefs = {
MAKE_SPORK_DEF(SPORK_14_NEW_PROTOCOL_ENFORCEMENT, 4070908800ULL), // OFF
MAKE_SPORK_DEF(SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2, 4070908800ULL), // OFF
MAKE_SPORK_DEF(SPORK_16_ZEROCOIN_MAINTENANCE_MODE, 4070908800ULL), // OFF
MAKE_SPORK_DEF(SPORK_17_COLDSTAKING_ENFORCEMENT, 4070908800ULL), // OFF
};

CSporkManager sporkManager;
Expand Down Expand Up @@ -172,6 +173,7 @@ bool CSporkManager::UpdateSpork(SporkId nSporkID, int64_t nValue)
fNewSigs = chainActive.NewSigsActive();
}


CSporkMessage spork = CSporkMessage(nSporkID, nValue, GetTime());

if(spork.Sign(strMasterPrivKey, fNewSigs)){
Expand Down
1 change: 1 addition & 0 deletions src/spork.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "obfuscation.h"
#include "protocol.h"


class CSporkMessage;
class CSporkManager;

Expand Down
1 change: 1 addition & 0 deletions src/sporkid.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ enum SporkId : int32_t {
SPORK_14_NEW_PROTOCOL_ENFORCEMENT = 10013,
SPORK_15_NEW_PROTOCOL_ENFORCEMENT_2 = 10014,
SPORK_16_ZEROCOIN_MAINTENANCE_MODE = 10015,
SPORK_17_COLDSTAKING_ENFORCEMENT = 10017,

SPORK_INVALID = -1
};
Expand Down
11 changes: 5 additions & 6 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -560,11 +560,10 @@ UniValue CreateColdStakeDelegation(const UniValue& params, CWalletTx& wtxNew, CR
if (params.size() > 5 && !params[5].isNull())
fForceNotEnabled = params[5].get_bool();

int nBestHeight = chainActive.Height();
if (!Params().Cold_Staking_Enabled(nBestHeight) && !fForceNotEnabled) {
std::string errMsg = strprintf("Cold Staking not enforced yet at block %d.\n"
"If the wallet is syncing, you may force the stake delegation setting fForceNotEnabled to true.\n"
"WARNING: If the network hasn't reached the activation height, this tx will be rejected resulting in a ban.\n", nBestHeight);
if (!sporkManager.IsSporkActive(SPORK_17_COLDSTAKING_ENFORCEMENT) && !fForceNotEnabled) {
std::string errMsg = "Cold Staking disabled with SPORK 17.\n"
"You may force the stake delegation setting fForceNotEnabled to true.\n"
"WARNING: If relayed before activation, this tx will be rejected resulting in a ban.\n";
throw JSONRPCError(RPC_WALLET_ERROR, errMsg);
}

Expand Down Expand Up @@ -657,7 +656,7 @@ UniValue delegatestake(const UniValue& params, bool fHelp)
"4. \"fExternalOwner\" (boolean, optional, default = false) use the provided 'owneraddress' anyway, even if not present in this wallet.\n"
" WARNING: The owner of the keys to 'owneraddress' will be the only one allowed to spend these coins.\n"
"5. \"fUseDelegated\" (boolean, optional, default = false) include already delegated inputs if needed."
"6. \"fForceNotEnabled\" (boolean, optional, default = false) force the creation before the activation height (during sync)."
"6. \"fForceNotEnabled\" (boolean, optional, default = false) force the creation even if SPORK 17 is disabled (for tests)."

"\nResult:\n"
"{\n"
Expand Down
4 changes: 2 additions & 2 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2103,7 +2103,7 @@ bool CWallet::SelectStakeCoins(std::list<std::unique_ptr<CStakeInput> >& listInp
std::vector<COutput> vCoins;

// include cold, exclude delegated
const bool fIncludeCold = Params().Cold_Staking_Enabled(blockHeight) && GetBoolArg("-coldstaking", true);
const bool fIncludeCold = sporkManager.IsSporkActive(SPORK_17_COLDSTAKING_ENFORCEMENT) && GetBoolArg("-coldstaking", true);
AvailableCoins(vCoins, true, NULL, false, STAKABLE_COINS, false, 1, fIncludeCold, false);

CAmount nAmountSelected = 0;
Expand Down Expand Up @@ -2184,7 +2184,7 @@ bool CWallet::MintableCoins()

std::vector<COutput> vCoins;
// include cold, exclude delegated
const bool fIncludeCold = Params().Cold_Staking_Enabled(chainHeight) && GetBoolArg("-coldstaking", true);
const bool fIncludeCold = sporkManager.IsSporkActive(SPORK_17_COLDSTAKING_ENFORCEMENT) && GetBoolArg("-coldstaking", true);
AvailableCoins(vCoins, true, NULL, false, STAKABLE_COINS, false, 1, fIncludeCold, false);

int64_t time = GetAdjustedTime();
Expand Down
63 changes: 51 additions & 12 deletions test/functional/feature_coldStaking.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
self.extra_args = [['-staking=1']] * self.num_nodes
self.extra_args[0].append('-sporkkey=932HEevBSujW2ud7RfB1YF91AFygbBRQj3de3LyaCRqNzKKgWXi')


def setup_network(self):
Expand Down Expand Up @@ -60,27 +61,50 @@ def init_test(self):
self.test_nodes[i].wait_for_verack()



def setColdStakingEnforcement(self, fEnable=True):
new_val = 1563253447 if fEnable else 4070908800
# update spork 17 and mine 1 more block
mess = "Enabling" if fEnable else "Disabling"
mess += " cold staking with SPORK 17..."
self.log.info(mess)
res = self.nodes[0].spork("SPORK_17_COLDSTAKING_ENFORCEMENT", new_val)
self.log.info(res)
assert (res == "success")
time.sleep(1)
sync_chain(self.nodes)


def isColdStakingEnforced(self):
# verify from node[1]
active = self.nodes[1].spork("active")
return active["SPORK_17_COLDSTAKING_ENFORCEMENT"]



def run_test(self):
self.description = "Performs tests on the Cold Staking P2CS implementation"
self.init_test()
LAST_POW_BLOCK = 250
NUM_OF_INPUTS = 20
INPUT_VALUE = 50
INITAL_MINED_BLOCKS = 200
INITAL_MINED_BLOCKS = LAST_POW_BLOCK + 1

# nodes[0] - coin-owner
# nodes[1] - cold-staker

# 1) nodes[0] mines 20 blocks. nodes[2] mines 180 blocks.
# 1) nodes[0] mines 20 blocks. nodes[2] mines 231 blocks.
# -----------------------------------------------------------
# Check that SPORK 17 is disabled
assert (not self.isColdStakingEnforced())
print("*** 1 ***")
self.log.info("Mining %d blocks..." % INITAL_MINED_BLOCKS)
self.nodes[0].generate(20)
self.generateBlock(20, 0)
sync_chain(self.nodes)
self.log.info("20 Blocks mined.")
self.generateBlock(180)
self.generateBlock(INITAL_MINED_BLOCKS-20)
sync_chain(self.nodes)
self.log.info("200 Blocks mined.")
self.log.info("251 Blocks mined.")


# 2) nodes[0] generates a owner address
Expand All @@ -100,9 +124,11 @@ def run_test(self):
assert_raises_rpc_error(-4, "The transaction was rejected!",
self.nodes[0].delegatestake, staker_address, INPUT_VALUE, owner_address, False, False, True)
self.log.info("Good. Cold Staking NOT ACTIVE yet.")
self.log.info("Mining 51 blocks to get to cold staking activation...")
self.generateBlock(51)
sync_chain(self.nodes)

# Enable SPORK
self.setColdStakingEnforcement()
# double check
assert (self.isColdStakingEnforced())


# 4) nodes[0] delegates a number of inputs for nodes[1] to stake em.
Expand Down Expand Up @@ -196,7 +222,7 @@ def run_test(self):
print("*** 8 ***")
assert_equal(self.nodes[1].getstakingstatus()["mintablecoins"], True)
self.log.info("Generating one valid cold-stake block...")
self.nodes[1].generate(1)
self.generateBlock(1, 1)
self.log.info("New block created by cold-staking. Trying to submit...")
newblockhash = self.nodes[1].getbestblockhash()
self.log.info("Block %s submitted" % newblockhash)
Expand Down Expand Up @@ -300,17 +326,30 @@ def run_test(self):
print("*** 12 ***")
self.log.info("Cancel the stake delegation spending the cold stakes...")
delegated_utxos = getDelegatedUtxos(self.nodes[0].listunspent())
# remove one utxo to spend later
final_spend = delegated_utxos.pop()
txhash = self.spendUTXOsWithNode(delegated_utxos, 0)
assert(txhash != None)
self.log.info("Good. Owner was able to void the stake delegations - tx: %s" % str(txhash))
self.generateBlock()
sync_chain(self.nodes)

# deactivate SPORK 17 and check that the owner can still spend the last utxo
self.setColdStakingEnforcement(False)
assert (not self.isColdStakingEnforced())
txhash = self.spendUTXOsWithNode([final_spend], 0)
assert(txhash != None)
self.log.info("Good. Owner was able to void the last stake delegation (with SPORK 17 disabled) - tx: %s" % str(txhash))
self.generateBlock()
sync_chain(self.nodes)

# check balances after big spend.
self.expected_balance = 2 * (INPUT_VALUE + 250)
self.checkBalances()
self.log.info("Balances check out after the delegations have been voided.")
assert_equal(2, len(self.nodes[0].listcoldutxos()))
# re-activate SPORK17
self.setColdStakingEnforcement()
assert (self.isColdStakingEnforced())


# 13) check that coinstaker is empty and can no longer stake.
Expand All @@ -321,11 +360,11 @@ def run_test(self):
self.log.info("Cigar. Cold staker was NOT able to create any more blocks.\n")


def generateBlock(self, n=1):
def generateBlock(self, n=1, nodeid=2):
fStaked = False
while (not fStaked):
try:
self.nodes[2].generate(n)
self.nodes[nodeid].generate(n)
fStaked = True
except JSONRPCException as e:
if ("Couldn't create new block" in str(e)):
Expand Down