Line data Source code
1 : // Copyright (c) 2011-2013 The PPCoin developers
2 : // Copyright (c) 2013-2014 The NovaCoin Developers
3 : // Copyright (c) 2014-2018 The BlackCoin Developers
4 : // Copyright (c) 2015-2021 The PIVX Core developers
5 : // Distributed under the MIT/X11 software license, see the accompanying
6 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
7 :
8 : #include "kernel.h"
9 :
10 : #include "db.h"
11 : #include "legacy/stakemodifier.h"
12 : #include "policy/policy.h"
13 : #include "script/interpreter.h"
14 : #include "stakeinput.h"
15 : #include "util/system.h"
16 : #include "utilmoneystr.h"
17 : #include "validation.h"
18 : #include "zpiv/zpos.h"
19 :
20 : /**
21 : * CStakeKernel Constructor
22 : *
23 : * @param[in] pindexPrev index of the parent of the kernel block
24 : * @param[in] stakeInput input for the coinstake of the kernel block
25 : * @param[in] nBits target difficulty bits of the kernel block
26 : * @param[in] nTimeTx time of the kernel block
27 : */
28 19490 : CStakeKernel::CStakeKernel(const CBlockIndex* const pindexPrev, CStakeInput* stakeInput, unsigned int nBits, int nTimeTx):
29 19490 : stakeUniqueness(stakeInput->GetUniqueness()),
30 : nTime(nTimeTx),
31 : nBits(nBits),
32 19490 : stakeValue(stakeInput->GetValue())
33 : {
34 : // Set kernel stake modifier
35 19490 : if (!Params().GetConsensus().NetworkUpgradeActive(pindexPrev->nHeight + 1, Consensus::UPGRADE_V3_4)) {
36 244 : uint64_t nStakeModifier = 0;
37 244 : if (!GetOldStakeModifier(stakeInput, nStakeModifier))
38 0 : LogPrintf("%s : ERROR: Failed to get kernel stake modifier\n", __func__);
39 : // Modifier v1
40 244 : stakeModifier << nStakeModifier;
41 : } else {
42 : // Modifier v2
43 38492 : stakeModifier << pindexPrev->GetStakeModifierV2();
44 : }
45 19490 : const CBlockIndex* pindexFrom = stakeInput->GetIndexFrom();
46 19490 : nTimeBlockFrom = pindexFrom->nTime;
47 19490 : }
48 :
49 : // Return stake kernel hash
50 19490 : uint256 CStakeKernel::GetHash() const
51 : {
52 19490 : CDataStream ss(stakeModifier);
53 19490 : ss << nTimeBlockFrom << stakeUniqueness << nTime;
54 38980 : return Hash(ss.begin(), ss.end());
55 : }
56 :
57 : // Check that the kernel hash meets the target required
58 19446 : bool CStakeKernel::CheckKernelHash(bool fSkipLog) const
59 : {
60 : // Get weighted target
61 19446 : arith_uint256 bnTarget;
62 19446 : bnTarget.SetCompact(nBits);
63 58338 : bnTarget *= (arith_uint256(stakeValue) / 100);
64 :
65 : // Check PoS kernel hash
66 19446 : const arith_uint256& hashProofOfStake = UintToArith256(GetHash());
67 19446 : const bool res = hashProofOfStake < bnTarget;
68 :
69 19446 : if (!fSkipLog || res) {
70 42783 : LogPrint(BCLog::STAKING, "%s : Proof Of Stake:" /* Continued */
71 : "\nstakeModifier=%s" /* Continued */
72 : "\nnTimeBlockFrom=%d" /* Continued */
73 : "\nssUniqueID=%s" /* Continued */
74 : "\nnTimeTx=%d" /* Continued */
75 : "\nhashProofOfStake=%s" /* Continued */
76 : "\nnBits=%d" /* Continued */
77 : "\nweight=%d" /* Continued */
78 : "\nbnTarget=%s (res: %d)\n\n",
79 : __func__, HexStr(stakeModifier), nTimeBlockFrom, HexStr(stakeUniqueness), nTime, hashProofOfStake.GetHex(),
80 : nBits, stakeValue, bnTarget.GetHex(), res);
81 : }
82 19446 : return res;
83 : }
84 :
85 :
86 : /*
87 : * PoS Validation
88 : */
89 :
90 : // helper function for CheckProofOfStake and GetStakeKernelHash
91 6764 : static bool LoadStakeInput(const CBlock& block, std::unique_ptr<CStakeInput>& stake, int nHeight)
92 : {
93 : // Check that this is a PoS block
94 6764 : if (!block.IsProofOfStake())
95 0 : return error("called on non PoS block");
96 :
97 : // Construct the stakeinput object
98 6764 : const CTxIn& txin = block.vtx[1]->vin[0];
99 13528 : stake = txin.IsZerocoinSpend() ?
100 0 : std::unique_ptr<CStakeInput>(CLegacyZPivStake::NewZPivStake(txin, nHeight)) :
101 13528 : std::unique_ptr<CStakeInput>(CPivStake::NewPivStake(txin, nHeight, block.nTime));
102 :
103 6764 : return stake != nullptr;
104 : }
105 :
106 : /*
107 : * Stake Check if stakeInput can stake a block on top of pindexPrev
108 : *
109 : * @param[in] pindexPrev index of the parent block of the block being staked
110 : * @param[in] stakeInput input for the coinstake
111 : * @param[in] nBits target difficulty bits
112 : * @param[in] nTimeTx new blocktime
113 : * @return bool true if stake kernel hash meets target protocol
114 : */
115 12726 : bool Stake(const CBlockIndex* pindexPrev, CStakeInput* stakeInput, unsigned int nBits, int64_t& nTimeTx)
116 : {
117 12726 : if (!stakeInput) return false;
118 :
119 : // Get the new time slot (and verify it's not the same as previous block)
120 12726 : const bool fRegTest = Params().IsRegTestNet();
121 12726 : nTimeTx = (fRegTest ? GetAdjustedTime() : GetCurrentTimeSlot());
122 12726 : if (nTimeTx <= pindexPrev->nTime && !fRegTest) return false;
123 :
124 : // Verify Proof Of Stake
125 25452 : CStakeKernel stakeKernel(pindexPrev, stakeInput, nBits, nTimeTx);
126 12726 : return stakeKernel.CheckKernelHash(true);
127 : }
128 :
129 :
130 : /*
131 : * CheckProofOfStake Check if block has valid proof of stake
132 : *
133 : * @param[in] block block being verified
134 : * @param[out] strError string error (if any, else empty)
135 : * @param[in] pindexPrev index of the parent block
136 : * (if nullptr, it will be searched in mapBlockIndex)
137 : * @return bool true if the block has a valid proof of stake
138 : */
139 6720 : bool CheckProofOfStake(const CBlock& block, std::string& strError, const CBlockIndex* pindexPrev)
140 : {
141 6720 : const int nHeight = pindexPrev->nHeight + 1;
142 : // Initialize stake input
143 6720 : std::unique_ptr<CStakeInput> stakeInput;
144 6720 : if (!LoadStakeInput(block, stakeInput, nHeight)) {
145 0 : strError = "stake input initialization failed";
146 : return false;
147 : }
148 :
149 : // Verify Proof Of Stake
150 13440 : CStakeKernel stakeKernel(pindexPrev, stakeInput.get(), block.nBits, block.nTime);
151 6720 : if (!stakeKernel.CheckKernelHash()) {
152 0 : strError = "kernel hash check fails";
153 : return false;
154 : }
155 :
156 : // zPoS disabled (ContextCheck) before blocks V7, and the tx input signature is in CoinSpend
157 6720 : if (stakeInput->IsZPIV()) return true;
158 :
159 : // Verify tx input signature
160 13440 : CTxOut stakePrevout;
161 6720 : if (!stakeInput->GetTxOutFrom(stakePrevout)) {
162 6720 : strError = "unable to get stake prevout for coinstake";
163 : return false;
164 : }
165 6720 : const auto& tx = block.vtx[1];
166 6720 : const CTxIn& txin = tx->vin[0];
167 6720 : ScriptError serror;
168 13440 : if (!VerifyScript(txin.scriptSig, stakePrevout.scriptPubKey, STANDARD_SCRIPT_VERIFY_FLAGS,
169 6720 : TransactionSignatureChecker(tx.get(), 0, stakePrevout.nValue), tx->GetRequiredSigVersion(), &serror)) {
170 1 : strError = strprintf("signature fails: %s", serror ? ScriptErrorString(serror) : "");
171 1 : return false;
172 : }
173 :
174 : // All good
175 : return true;
176 : }
177 :
178 :
179 : /*
180 : * GetStakeKernelHash Return stake kernel of a block
181 : *
182 : * @param[out] hashRet hash of the kernel (set by this function)
183 : * @param[in] block block with the kernel to return
184 : * @param[in] pindexPrev index of the parent block
185 : * (if nullptr, it will be searched in mapBlockIndex)
186 : * @return bool false if kernel cannot be initialized, true otherwise
187 : */
188 44 : bool GetStakeKernelHash(uint256& hashRet, const CBlock& block, const CBlockIndex* pindexPrev)
189 : {
190 : // Initialize stake input
191 44 : std::unique_ptr<CStakeInput> stakeInput;
192 44 : if (!LoadStakeInput(block, stakeInput, pindexPrev->nHeight + 1))
193 0 : return error("%s : stake input initialization failed", __func__);
194 :
195 88 : CStakeKernel stakeKernel(pindexPrev, stakeInput.get(), block.nBits, block.nTime);
196 44 : hashRet = stakeKernel.GetHash();
197 44 : return true;
198 : }
199 :
|