Skip to content

Commit

Permalink
Merge pull request #523 from aguycalled/coldstaking-pool-fee
Browse files Browse the repository at this point in the history
Coldstaking pool fee
  • Loading branch information
proletesseract committed Aug 20, 2019
2 parents 06109e8 + 6c83d95 commit f449aec
Show file tree
Hide file tree
Showing 13 changed files with 178 additions and 31 deletions.
1 change: 1 addition & 0 deletions qa/pull-tester/rpc-tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@
'getstakinginfo.py',
'coldstaking_staking.py',
'coldstaking_spending.py',
'coldstaking_fee.py',
'staticr-staking-amount.py',
'hardfork-451.py',
'hardfork-452.py',
Expand Down
65 changes: 65 additions & 0 deletions qa/rpc-tests/coldstaking_fee.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env python3
# Copyright (c) 2019 The Navcoin 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 NavCoinTestFramework
from test_framework.util import *

class ColdStakingFeeTest(NavCoinTestFramework):
def __init__(self):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 3

def setup_network(self, split=False):
self.nodes = []
self.nodes.append(start_node(0, self.options.tmpdir, ["-addressindex","-pooladdress=n1hcSEk4ReyLwbStTKodGCq4kEwhJVxXwC","-poolfee=50"]))
self.nodes.append(start_node(1, self.options.tmpdir, ["-addressindex","-pooladdress=n1hcSEk4ReyLwbStTKodGCq4kEwhJVxXwC","-poolfee=100"]))
self.nodes.append(start_node(2, self.options.tmpdir, ["-addressindex","-pooladdress=n1hcSEk4ReyLwbStTKodGCq4kEwhJVxXwC","-poolfee=101"]))

def run_test(self):
slow_gen(self.nodes[0], 300)
assert (get_bip9_status(self.nodes[0], "coldstaking")["status"] == "active")
slow_gen(self.nodes[1], 300)
assert (get_bip9_status(self.nodes[1], "coldstaking")["status"] == "active")
slow_gen(self.nodes[2], 300)
assert (get_bip9_status(self.nodes[2], "coldstaking")["status"] == "active")

csaddress1 = self.nodes[0].getcoldstakingaddress(self.nodes[0].getnewaddress(),"n1wgKgwFPZYcQrm8qBtPrBvz2piqCwc1ry")
csaddress2 = self.nodes[1].getcoldstakingaddress(self.nodes[1].getnewaddress(),"n1wgKgwFPZYcQrm8qBtPrBvz2piqCwc1ry")
csaddress3 = self.nodes[2].getcoldstakingaddress(self.nodes[2].getnewaddress(),"n1wgKgwFPZYcQrm8qBtPrBvz2piqCwc1ry")

self.nodes[0].sendtoaddress(csaddress1, self.nodes[0].getbalance(), "", "", "", True)
self.nodes[1].sendtoaddress(csaddress2, self.nodes[1].getbalance(), "", "", "", True)
self.nodes[2].sendtoaddress(csaddress3, self.nodes[2].getbalance(), "", "", "", True)

self.nodes[0].generatetoaddress(100, "n1wgKgwFPZYcQrm8qBtPrBvz2piqCwc1ry")
self.nodes[1].generatetoaddress(100, "n1wgKgwFPZYcQrm8qBtPrBvz2piqCwc1ry")
self.nodes[2].generatetoaddress(100, "n1wgKgwFPZYcQrm8qBtPrBvz2piqCwc1ry")

tx=self.nodes[0].sendtoaddress(csaddress1, self.nodes[0].getbalance(), "", "", "", True)
fees0=self.nodes[0].gettransaction(tx)["fee"]*100000000/2
tx2=self.nodes[1].sendtoaddress(csaddress2, self.nodes[1].getbalance(), "", "", "", True)
fees1=self.nodes[1].gettransaction(tx2)["fee"]*100000000
self.nodes[2].sendtoaddress(csaddress3, self.nodes[2].getbalance(), "", "", "", True)

while self.nodes[0].getblockcount() < 401:
time.sleep(1)

node0balance = self.nodes[0].getaddressbalance('n1hcSEk4ReyLwbStTKodGCq4kEwhJVxXwC')["balance"]

while self.nodes[1].getblockcount() < 401:
time.sleep(1)

node1balance = self.nodes[1].getaddressbalance('n1hcSEk4ReyLwbStTKodGCq4kEwhJVxXwC')["balance"]

assert(node0balance > 0)
assert(node1balance > 0)
assert_equal((node0balance+fees0) % 100000000, 0)
assert_equal((node1balance+fees1) % 200000000, 0)

assert("Coinstake tried to move cold staking coins to a non authorised script" in open(self.options.tmpdir + '/node2/devnet/debug.log').read())

if __name__ == '__main__':
ColdStakingFeeTest().main()
21 changes: 21 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ class CMainParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_QUORUM_CFUND].nStartTime = 1543622400; // Dec 1st, 2018
consensus.vDeployments[Consensus::DEPLOYMENT_QUORUM_CFUND].nTimeout = 1575158400; // Dec 1st, 2019

// Deployment of Cold Staking Pool Fee
consensus.vDeployments[Consensus::DEPLOYMENT_POOL_FEE].bit = 18;
consensus.vDeployments[Consensus::DEPLOYMENT_POOL_FEE].nStartTime = 1559390400; // Jun 1st, 2018
consensus.vDeployments[Consensus::DEPLOYMENT_POOL_FEE].nTimeout = 1622548800; // Jun 1st, 2021


/**
* The message start string is designed to be unlikely to occur in normal data.
* The characters are rarely used upper ASCII, not valid as UTF-8, and produce
Expand Down Expand Up @@ -375,6 +381,11 @@ class CTestNetParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_QUORUM_CFUND].nStartTime = 1543622400; // Dec 1st, 2018
consensus.vDeployments[Consensus::DEPLOYMENT_QUORUM_CFUND].nTimeout = 1622548800; // Jun 1st, 2021

// Deployment of Cold Staking Pool Fee
consensus.vDeployments[Consensus::DEPLOYMENT_POOL_FEE].bit = 18;
consensus.vDeployments[Consensus::DEPLOYMENT_POOL_FEE].nStartTime = 1559390400; // Jun 1st, 2018
consensus.vDeployments[Consensus::DEPLOYMENT_POOL_FEE].nTimeout = 1622548800; // Jun 1st, 2021

/**
* The message start string is designed to be unlikely to occur in normal data.
* The characters are rarely used upper ASCII, not valid as UTF-8, and produce
Expand Down Expand Up @@ -550,6 +561,11 @@ class CDevNetParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_QUORUM_CFUND].nStartTime = 1543622400; // Dec 1st, 2018
consensus.vDeployments[Consensus::DEPLOYMENT_QUORUM_CFUND].nTimeout = 1651363200; // May 1st, 2022

// Deployment of Cold Staking Pool Fee
consensus.vDeployments[Consensus::DEPLOYMENT_POOL_FEE].bit = 18;
consensus.vDeployments[Consensus::DEPLOYMENT_POOL_FEE].nStartTime = 1559390400; // Jun 1st, 2018
consensus.vDeployments[Consensus::DEPLOYMENT_POOL_FEE].nTimeout = 1622548800; // Jun 1st, 2021

/**
* The message start string is designed to be unlikely to occur in normal data.
* The characters are rarely used upper ASCII, not valid as UTF-8, and produce
Expand Down Expand Up @@ -735,6 +751,11 @@ class CRegTestParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_QUORUM_CFUND].nStartTime = 1543622400; // Dec 1st, 2018
consensus.vDeployments[Consensus::DEPLOYMENT_QUORUM_CFUND].nTimeout = 1575158400; // Dec 1st, 2019

// Deployment of Cold Staking Pool Fee
consensus.vDeployments[Consensus::DEPLOYMENT_POOL_FEE].bit = 18;
consensus.vDeployments[Consensus::DEPLOYMENT_POOL_FEE].nStartTime = 1559390400; // Jun 1st, 2018
consensus.vDeployments[Consensus::DEPLOYMENT_POOL_FEE].nTimeout = 1622548800; // Jun 1st, 2021

/**
* The message start string is designed to be unlikely to occur in normal data.
* The characters are rarely used upper ASCII, not valid as UTF-8, and produce
Expand Down
1 change: 1 addition & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ enum DeploymentPos
DEPLOYMENT_NTPSYNC,
DEPLOYMENT_STATIC_REWARD,
DEPLOYMENT_QUORUM_CFUND,
DEPLOYMENT_POOL_FEE,
MAX_VERSION_BITS_DEPLOYMENTS
};

Expand Down
69 changes: 52 additions & 17 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2915,7 +2915,6 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
if (pindex->IsProofOfStake())
setStakeSeen.insert(make_pair(pindex->prevoutStake, pindex->nStakeTime));


// Check proof of stake
if (block.nBits != GetNextTargetRequired(pindex->pprev, block.IsProofOfStake())){
return state.DoS(1,error("ContextualCheckBlock() : incorrect %s at height %d (%d)", !block.IsProofOfStake() ? "proof-of-work" : "proof-of-stake",pindex->pprev->nHeight, block.nBits), REJECT_INVALID, "bad-diffbits");
Expand All @@ -2928,25 +2927,25 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
{
arith_uint256 targetProofOfStake;
// Signature will be checked in CheckInputs(), we can avoid it here (fCheckSignature = false)
if (!CheckProofOfStake(pindex->pprev, block.vtx[1], block.nBits, hashProof, targetProofOfStake, NULL, false))
if (!CheckProofOfStake(pindex->pprev, block.vtx[1], block.nBits, hashProof, targetProofOfStake, NULL, view, false))
{
return error("ConnectBlock() : check proof-of-stake signature failed for block %s", block.GetHash().GetHex());
return error("ContextualCheckBlock() : check proof-of-stake signature failed for block %s", block.GetHash().GetHex());
}
}

if (block.IsProofOfWork())
hashProof = UintToArith256(block.GetPoWHash());

if (!pindex->SetStakeEntropyBit(block.GetStakeEntropyBit()))
return state.DoS(1,error("ConnectBlock() : SetStakeEntropyBit() failed"), REJECT_INVALID, "bad-entropy-bit");
return state.DoS(1,error("ContextualCheckBlock() : SetStakeEntropyBit() failed"), REJECT_INVALID, "bad-entropy-bit");

// Record proof hash value
pindex->hashProof = hashProof;

uint64_t nStakeModifier = 0;
bool fGeneratedStakeModifier = false;
if (!ComputeNextStakeModifier(pindex->pprev, nStakeModifier, fGeneratedStakeModifier))
return state.DoS(1, error("ConnectBlock() : ComputeNextStakeModifier() failed"), REJECT_INVALID, "bad-stake-modifier");
return state.DoS(1, error("ContextualCheckBlock() : ComputeNextStakeModifier() failed"), REJECT_INVALID, "bad-stake-modifier");

pindex->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);

Expand Down Expand Up @@ -4620,6 +4619,12 @@ bool IsColdStakingEnabled(const CBlockIndex* pindexPrev, const Consensus::Params
return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_COLDSTAKING, versionbitscache) == THRESHOLD_ACTIVE);
}

bool IsColdStakingPoolFeeEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params)
{
LOCK(cs_main);
return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_POOL_FEE, versionbitscache) == THRESHOLD_ACTIVE);
}

bool IsStaticRewardLocked(const CBlockIndex* pindexPrev, const Consensus::Params& params)
{
LOCK(cs_main);
Expand Down Expand Up @@ -8604,24 +8609,54 @@ bool CheckStakeKernelHash(CBlockIndex* pindexPrev, unsigned int nBits, CBlockInd
}

//Check kernel hash target and coinstake signature
bool CheckProofOfStake(CBlockIndex* pindexPrev, const CTransaction& tx, unsigned int nBits, arith_uint256& hashProofOfStake, arith_uint256& targetProofOfStake, std::vector<CScriptCheck> *pvChecks, bool fCHeckSignature)
bool CheckProofOfStake(CBlockIndex* pindexPrev, const CTransaction& tx, unsigned int nBits, arith_uint256& hashProofOfStake, arith_uint256& targetProofOfStake, std::vector<CScriptCheck> *pvChecks, CCoinsViewCache& view, bool fCHeckSignature)
{
if (!tx.IsCoinStake())
return error("CheckProofOfStake() : called on non-coinstake %s", tx.GetHash().ToString());
return error("%s: called on non-coinstake %s", __func__, tx.GetHash().ToString());

// Kernel (input 0) must match the stake hash target per coin age (nBits)
const CTxIn& txin = tx.vin[0];

CTransaction txPrev;
uint256 hashBlock = uint256();
if (!GetTransaction(txin.prevout.hash, txPrev, Params().GetConsensus(), hashBlock, true))
return error("CheckProofOfStake() : INFO: read txPrev failed %s",txin.prevout.hash.GetHex()); // previous transaction not in main chain, may occur during initial download
return error("%s: INFO: read txPrev failed %s",__func__, txin.prevout.hash.GetHex()); // previous transaction not in main chain, may occur during initial download

bool fColdStaking = txPrev.vout[txin.prevout.n].scriptPubKey.IsColdStaking();
bool fPoolEnabled = IsColdStakingPoolFeeEnabled(pindexPrev, Params().GetConsensus());
CScript kernelScript = txPrev.vout[txin.prevout.n].scriptPubKey;

if (fPoolEnabled && tx.vin.size() > 1)
{
for (unsigned int i = 1; i < tx.vin.size(); i++)
{
CTransaction txPrev_;
uint256 hashBlock_ = uint256();
if (!GetTransaction(tx.vin[i].prevout.hash, txPrev_, Params().GetConsensus(), hashBlock_, true))
return error("%s: INFO: read txPrev failed %s",__func__, tx.vin[i].prevout.hash.GetHex()); // previous transaction not in main chain, may occur during initial download

if (txPrev.vout[txin.prevout.n].scriptPubKey.IsColdStaking())
for(unsigned int i = 1; i < tx.vout.size() - 1; i++) // First output is empty, last is CFund contribution
if(tx.vout[i].scriptPubKey != txPrev.vout[txin.prevout.n].scriptPubKey)
return error(strprintf("CheckProofOfStake(): Coinstake output %d tried to move cold staking coins to a non authorised script. (%s vs. %s)",
i, ScriptToAsmStr(txPrev.vout[txin.prevout.n].scriptPubKey), ScriptToAsmStr(tx.vout[i].scriptPubKey)));
fColdStaking |= txPrev_.vout[tx.vin[i].prevout.n].scriptPubKey.IsColdStaking();
if (fColdStaking && txPrev_.vout[tx.vin[i].prevout.n].scriptPubKey != kernelScript)
return error("%s: Coinstake spends inputs from more than one different kernel", __func__);
}
}

if (fColdStaking)
{
CAmount valueIn = view.GetValueIn(tx);
CAmount valueOut = 0;

for(unsigned int i = 1; i < tx.vout.size() - (fPoolEnabled?0:1); i++) // First output is empty, last is CFund contribution
if(fPoolEnabled && tx.vout[i].scriptPubKey == txPrev.vout[txin.prevout.n].scriptPubKey)
valueOut += tx.vout[i].nValue;
else if(!fPoolEnabled && tx.vout[i].scriptPubKey != txPrev.vout[txin.prevout.n].scriptPubKey)
return error(strprintf("%s: Coinstake output %d tried to move cold staking coins to a non authorised script. (%s vs. %s)",
__func__, i, ScriptToAsmStr(txPrev.vout[txin.prevout.n].scriptPubKey), ScriptToAsmStr(tx.vout[i].scriptPubKey)));

if (fPoolEnabled && valueIn > valueOut)
return error("%s: Coinstake tried to move cold staking coins to a non authorised script.",__func__);

}

if (pvChecks)
pvChecks->reserve(tx.vin.size());
Expand All @@ -8641,19 +8676,19 @@ bool CheckProofOfStake(CBlockIndex* pindexPrev, const CTransaction& tx, unsigned
pvChecks->push_back(CScriptCheck());
check.swap(pvChecks->back());
} else if (!check())
return error("CheckProofOfStake() : script-verify-failed %s",ScriptErrorString(check.GetScriptError()));
return error("%s: script-verify-failed %s",__func__, ScriptErrorString(check.GetScriptError()));
}

if (mapBlockIndex.count(hashBlock) == 0)
return fDebug? error("CheckProofOfStake() : read block failed") : false; // unable to read block of previous transaction
return fDebug? error("%s: read block failed",__func__) : false; // unable to read block of previous transaction

CBlockIndex* pblockindex = mapBlockIndex[hashBlock];

if (txin.prevout.hash != txPrev.GetHash())
return error("CheckProofOfStake(): Coinstake input does not match previous output %s",txin.prevout.hash.GetHex());
return error("%s: Coinstake input does not match previous output %s",__func__, txin.prevout.hash.GetHex());

if (!CheckStakeKernelHash(pindexPrev, nBits, *pblockindex, txPrev, txin.prevout, tx.nTime, hashProofOfStake, targetProofOfStake, fDebug))
return error("CheckProofOfStake() : INFO: check kernel failed on coinstake %s, hashProof=%s", tx.GetHash().ToString(), hashProofOfStake.ToString()); // may occur during initial download or if behind on block chain sync
return error("%s: INFO: check kernel failed on coinstake %s, hashProof=%s",__func__, tx.GetHash().ToString(), hashProofOfStake.ToString()); // may occur during initial download or if behind on block chain sync

return true;
}
Expand Down
3 changes: 2 additions & 1 deletion src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ bool IsReducedCFundQuorumEnabled(const CBlockIndex* pindexPrev, const Consensus:

/** Check whether ColdStaking has been activated. */
bool IsColdStakingEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params);
bool IsColdStakingPoolFeeEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params);

/** When there are blocks in the active chain with missing data, rewind the chainstate and remove them from the block index */
bool RewindBlockIndex(const CChainParams& params);
Expand Down Expand Up @@ -616,7 +617,7 @@ bool CheckStakeKernelHash(CBlockIndex* pindexPrev, unsigned int nBits, CBlockInd

// Check kernel hash target and coinstake signature
// Sets hashProofOfStake on success return
bool CheckProofOfStake(CBlockIndex* pindexPrev, const CTransaction& tx, unsigned int nBits, arith_uint256& hashProofOfStake, arith_uint256& targetProofOfStake, std::vector<CScriptCheck> *pvChecks, bool fCHeckSignature = false);
bool CheckProofOfStake(CBlockIndex* pindexPrev, const CTransaction& tx, unsigned int nBits, arith_uint256& hashProofOfStake, arith_uint256& targetProofOfStake, std::vector<CScriptCheck> *pvChecks, CCoinsViewCache& view, bool fCHeckSignature = false);

// Check whether the coinstake timestamp meets protocol
bool CheckCoinStakeTimestamp(int nHeight, int64_t nTimeBlock, int64_t nTimeTx);
Expand Down
2 changes: 1 addition & 1 deletion src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -979,7 +979,7 @@ bool CheckStake(CBlock* pblock, CWallet& wallet, const CChainParams& chainparams
return error("CheckStake(): could not find previous block");

// verify hash target and signature of coinstake tx
if (!CheckProofOfStake(mapBlockIndex[pblock->hashPrevBlock], pblock->vtx[1], pblock->nBits, proofHash, hashTarget, NULL))
if (!CheckProofOfStake(mapBlockIndex[pblock->hashPrevBlock], pblock->vtx[1], pblock->nBits, proofHash, hashTarget, NULL, *pcoinsTip, false))
return error("CheckStake() : proof-of-stake checking failed");

//// debug print
Expand Down
19 changes: 14 additions & 5 deletions src/qt/transactionrecord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
{
QList<TransactionRecord> parts;
int64_t nTime = wtx.GetTxTime();
isminefilter dCFilter = (wtx.IsCoinStake() && wtx.vout[1].scriptPubKey.IsColdStaking()) ? wallet->IsMine(wtx.vout[1]) : ISMINE_ALL;
CAmount nCredit = wtx.GetCredit(dCFilter);
isminefilter dCFilter = wtx.IsCoinStake() ? wallet->IsMine(wtx.vout[1]) : ISMINE_ALL;
CAmount nCredit = wtx.GetCredit(dCFilter, false);
CAmount nDebit = wtx.GetDebit(dCFilter);
CAmount nCFundCredit = wtx.GetDebit(ISMINE_ALL);
CAmount nNet = nCredit - nDebit;
Expand All @@ -58,8 +58,17 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
unsigned int i = 0;
unsigned int rewardIdx = 0;
if (wtx.IsCoinStake())
// If coinstake has no cfund contribution, get details of last vout or else use second to last
rewardIdx = wtx.vout.size() - (wtx.GetValueOutCFund() == 0 ? 1 : 2);
{
for (unsigned int j = wtx.vout.size(); j--;)
{
if (wallet->IsMine(wtx.vout[j]))
{
rewardIdx = j;
break;
}

}
}

BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
Expand Down Expand Up @@ -101,7 +110,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
}

sub.type = TransactionRecord::Staked;
sub.credit = nNet > 0 ? nNet : wtx.GetValueOut() - nDebit - wtx.GetValueOutCFund();
sub.credit = nNet;
}
if(wtx.fAnon)
{
Expand Down
Loading

0 comments on commit f449aec

Please sign in to comment.