@@ -976,6 +976,21 @@ bool ContextualCheckZerocoinMint(const CTransaction& tx, const PublicCoin& coin,
976976}
977977
978978bool ContextualCheckZerocoinSpend (const CTransaction& tx, const CoinSpend& spend, CBlockIndex* pindex, const uint256& hashBlock)
979+ {
980+ if (!ContextualCheckZerocoinSpendNoSerialCheck (tx, spend, pindex, hashBlock)){
981+ return false ;
982+ }
983+
984+ // Reject serial's that are already in the blockchain
985+ int nHeightTx = 0 ;
986+ if (IsSerialInBlockchain (spend.getCoinSerialNumber (), nHeightTx))
987+ return error (" %s : zPIV spend with serial %s is already in block %d\n " , __func__,
988+ spend.getCoinSerialNumber ().GetHex (), nHeightTx);
989+
990+ return true ;
991+ }
992+
993+ bool ContextualCheckZerocoinSpendNoSerialCheck (const CTransaction& tx, const CoinSpend& spend, CBlockIndex* pindex, const uint256& hashBlock)
979994{
980995 // Check to see if the zPIV is properly signed
981996 if (pindex->nHeight >= Params ().Zerocoin_Block_V2_Start ()) {
@@ -991,12 +1006,6 @@ bool ContextualCheckZerocoinSpend(const CTransaction& tx, const CoinSpend& spend
9911006 }
9921007 }
9931008
994- // Reject serial's that are already in the blockchain
995- int nHeightTx = 0 ;
996- if (IsSerialInBlockchain (spend.getCoinSerialNumber (), nHeightTx))
997- return error (" %s : zPIV spend with serial %s is already in block %d\n " , __func__,
998- spend.getCoinSerialNumber ().GetHex (), nHeightTx);
999-
10001009 // Reject serial's that are not in the acceptable value range
10011010 bool fUseV1Params = spend.getVersion () < libzerocoin::PrivateCoin::PUBKEY_VERSION;
10021011 if (pindex->nHeight > Params ().Zerocoin_Block_EnforceSerialRange () &&
@@ -4503,17 +4512,39 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
45034512 }
45044513
45054514 int nHeight = pindex->nHeight ;
4506-
4515+ int splitHeight = - 1 ;
45074516
45084517 if (isPoS) {
45094518 LOCK (cs_main);
45104519
4520+ const bool isBlockFromFork = pindexPrev != nullptr && !chainActive.Contains (pindexPrev);
4521+ CTransaction &stakeTxIn = block.vtx [1 ];
4522+
4523+ // Check validity of the coinStake.
4524+ if (!stakeTxIn.IsCoinStake ())
4525+ return error (" %s: no coin stake on vtx pos 1" , __func__);
4526+
4527+
45114528 // Check whether is a fork or not
4512- if (pindexPrev != nullptr && !chainActive. Contains (pindexPrev) ) {
4529+ if (isBlockFromFork ) {
45134530
45144531 // Start at the block we're adding on to
45154532 CBlockIndex *prev = pindexPrev;
4516- CTransaction &stakeTxIn = block.vtx [1 ];
4533+
4534+ // Inputs
4535+ std::vector<CTxIn> pivInputs;
4536+ std::vector<CTxIn> zPIVInputs;
4537+
4538+ for (CTxIn stakeIn : stakeTxIn.vin ) {
4539+ if (stakeIn.scriptSig .IsZerocoinSpend ()){
4540+ zPIVInputs.push_back (stakeIn);
4541+ }else {
4542+ pivInputs.push_back (stakeIn);
4543+ }
4544+ }
4545+ const bool hasPIVInputs = !pivInputs.empty ();
4546+ const bool hasZPIVInputs = !zPIVInputs.empty ();
4547+ vector<CBigNum> vBlockSerials;
45174548 CBlock bl;
45184549 // Go backwards on the forked chain up to the split
45194550 do {
@@ -4526,20 +4557,101 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
45264557 for (CTransaction t : bl.vtx ) {
45274558 for (CTxIn in: t.vin ) {
45284559 // Loop through every input of the staking tx
4529- for (CTxIn stakeIn : stakeTxIn. vin ) {
4560+ for (CTxIn stakeIn : pivInputs ) {
45304561 // if it's already spent
4531- if (stakeIn.prevout == in.prevout ) {
4532- // reject the block
4533- return state.DoS (100 ,
4534- error (" %s: input already spent on a previous block" , __func__));
4562+ // First regular staking check
4563+ if (hasPIVInputs) {
4564+ if (stakeIn.prevout == in.prevout ) {
4565+ // reject the block
4566+ return state.DoS (100 ,
4567+ error (" %s: input already spent on a previous block" ,
4568+ __func__));
4569+ }
4570+ }
4571+ // Second, if there is zPoS staking then store the serials for later check
4572+ if (hasZPIVInputs) {
4573+ if (in.scriptSig .IsZerocoinSpend ()){
4574+ CoinSpend spend = TxInToZerocoinSpend (in);
4575+ vBlockSerials.push_back (spend.getCoinSerialNumber ());
4576+ }
45354577 }
45364578 }
45374579 }
45384580 }
4581+
45394582 prev = prev->pprev ;
45404583
45414584 } while (!chainActive.Contains (prev));
4585+
4586+ // Split height
4587+ splitHeight = prev->nHeight ;
4588+
4589+ // Now that this loop if completed. Check if we have zPIV inputs.
4590+ if (hasZPIVInputs){
4591+
4592+ for (CTxIn zPivInput : zPIVInputs) {
4593+ CoinSpend spend = TxInToZerocoinSpend (zPivInput);
4594+
4595+ // First check if the serials were not already spent on the forked blocks.
4596+ CBigNum coinSerial = spend.getCoinSerialNumber ();
4597+ for (CBigNum serial : vBlockSerials){
4598+ if (serial == coinSerial){
4599+ return state.DoS (100 ,
4600+ error (" %s: serial double spent on fork" ,
4601+ __func__));
4602+ }
4603+ }
4604+ // Now check if the serial exists before the chain split.
4605+ int nHeightTx = 0 ;
4606+ if (IsSerialInBlockchain (spend.getCoinSerialNumber (), nHeightTx)){
4607+ // if the height is nHeightTx > chainSplit means that the spent occurred after the chain split
4608+ if (nHeightTx <= splitHeight){
4609+ return state.DoS (100 ,
4610+ error (" %s: serial double spent on main chain" ,
4611+ __func__));
4612+ }
4613+ }
4614+
4615+ if (!ContextualCheckZerocoinSpendNoSerialCheck (stakeTxIn, spend, pindex, 0 ))
4616+ return state.DoS (100 ,error (" %s: ContextualCheckZerocoinSpend failed for tx %s" , __func__,
4617+ stakeTxIn.GetHash ().GetHex ()), REJECT_INVALID, " bad-txns-invalid-zpiv" );
4618+
4619+ // Now only the ZKP left..
4620+ // As the spend maturity is 200, the acc value must be accumulated, otherwise it's not ready to be spent
4621+ CBigNum bnAccumulatorValue = 0 ;
4622+ if (!zerocoinDB->ReadAccumulatorValue (spend.getAccumulatorChecksum (), bnAccumulatorValue)) {
4623+ return state.DoS (100 , error (" %s: stake zerocoinspend not ready to be spent" , __func__));
4624+ }
4625+
4626+ Accumulator accumulator (Params ().Zerocoin_Params (chainActive.Height () < Params ().Zerocoin_Block_V2_Start ()),
4627+ spend.getDenomination (), bnAccumulatorValue);
4628+
4629+ // Check that the coinspend is valid
4630+ if (!spend.Verify (accumulator))
4631+ return state.DoS (100 , error (" %s: zerocoin spend did not verify" , __func__));
4632+
4633+ }
4634+ }
4635+
45424636 }
4637+
4638+
4639+ // If the stake is not a zPoS then let's check if the inputs were spent on the main chain
4640+ const CCoinsViewCache coins (pcoinsTip);
4641+ if (!stakeTxIn.IsZerocoinSpend ()) {
4642+ for (CTxIn in: stakeTxIn.vin ) {
4643+ const CCoins* coin = coins.AccessCoins (in.prevout .hash );
4644+ if (coin && !coin->IsAvailable (in.prevout .n )){
4645+ // If this is not available get the height of the spent and validate it with the forked height
4646+ // Check if this occurred before the chain split
4647+ if (!(isBlockFromFork && coin->nHeight > splitHeight)){
4648+ // Coins not available
4649+ return error (" %s: coin stake inputs already spent in main chain" , __func__);
4650+ }
4651+ }
4652+ }
4653+ }
4654+
45434655 }
45444656
45454657 // Write block to history file
0 commit comments