Skip to content

Commit 3f50919

Browse files
committed
zPoS validations of forked chains before store them + inputs check on prev split main chain
1 parent 64804b4 commit 3f50919

File tree

2 files changed

+128
-15
lines changed

2 files changed

+128
-15
lines changed

src/main.cpp

Lines changed: 126 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,21 @@ bool ContextualCheckZerocoinMint(const CTransaction& tx, const PublicCoin& coin,
976976
}
977977

978978
bool 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

src/main.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,8 @@ void UpdateCoins(const CTransaction& tx, CValidationState& state, CCoinsViewCach
344344
bool CheckTransaction(const CTransaction& tx, bool fZerocoinActive, bool fRejectBadUTXO, CValidationState& state);
345345
bool CheckZerocoinMint(const uint256& txHash, const CTxOut& txout, CValidationState& state, bool fCheckOnly = false);
346346
bool CheckZerocoinSpend(const CTransaction& tx, bool fVerifySignature, CValidationState& state);
347-
bool ContextualCheckZerocoinSpend(const CTransaction& tx, const libzerocoin::CoinSpend& spend, CBlockIndex* pindex);
347+
bool ContextualCheckZerocoinSpend(const CTransaction& tx, const libzerocoin::CoinSpend& spend, CBlockIndex* pindex, const uint256& hashBlock);
348+
bool ContextualCheckZerocoinSpendNoSerialCheck(const CTransaction& tx, const libzerocoin::CoinSpend& spend, CBlockIndex* pindex, const uint256& hashBlock);
348349
bool IsTransactionInChain(const uint256& txId, int& nHeightTx, CTransaction& tx);
349350
bool IsTransactionInChain(const uint256& txId, int& nHeightTx);
350351
bool IsBlockHashInChain(const uint256& hashBlock);

0 commit comments

Comments
 (0)