CoinSpend::CoinSpend(const Params* p, const PrivateCoin& coin, Accumulator& a, const AccumulatorWitness& witness, const SpendMetaData& m): params(p), denomination(coin.getPublicCoin().getDenomination()), coinSerialNumber((coin.getSerialNumber())), accumulatorPoK(&p->accumulatorParams), serialNumberSoK(p), commitmentPoK(&p->serialNumberSoKCommitmentGroup, &p->accumulatorParams.accumulatorPoKCommitmentGroup) { // Sanity check: let's verify that the Witness is valid with respect to // the coin and Accumulator provided. if (!(witness.VerifyWitness(a, coin.getPublicCoin()))) { throw ZerocoinException("Accumulator witness does not verify"); } // 1: Generate two separate commitments to the public coin (C), each under // a different set of public parameters. We do this because the RSA accumulator // has specific requirements for the commitment parameters that are not // compatible with the group we use for the serial number proof. // Specifically, our serial number proof requires the order of the commitment group // to be the same as the modulus of the upper group. The Accumulator proof requires a // group with a significantly larger order. const Commitment fullCommitmentToCoinUnderSerialParams(&p->serialNumberSoKCommitmentGroup, coin.getPublicCoin().getValue()); this->serialCommitmentToCoinValue = fullCommitmentToCoinUnderSerialParams.getCommitmentValue(); const Commitment fullCommitmentToCoinUnderAccParams(&p->accumulatorParams.accumulatorPoKCommitmentGroup, coin.getPublicCoin().getValue()); this->accCommitmentToCoinValue = fullCommitmentToCoinUnderAccParams.getCommitmentValue(); // 2. Generate a ZK proof that the two commitments contain the same public coin. this->commitmentPoK = CommitmentProofOfKnowledge(&p->serialNumberSoKCommitmentGroup, &p->accumulatorParams.accumulatorPoKCommitmentGroup, fullCommitmentToCoinUnderSerialParams, fullCommitmentToCoinUnderAccParams); cout << "GNOSIS DEBUG: commitmentPoK is " << this->commitmentPoK.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) << " bytes" << endl;; // Now generate the two core ZK proofs: // 3. Proves that the committed public coin is in the Accumulator (PoK of "witness") this->accumulatorPoK = AccumulatorProofOfKnowledge(&p->accumulatorParams, fullCommitmentToCoinUnderAccParams, witness, a); cout << "GNOSIS DEBUG: accPoK is " << this->accumulatorPoK.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) << " bytes" << endl;; // 4. Proves that the coin is correct w.r.t. serial number and hidden coin secret // (This proof is bound to the coin 'metadata', i.e., transaction hash) this->serialNumberSoK = SerialNumberSignatureOfKnowledge(p, coin, fullCommitmentToCoinUnderSerialParams, signatureHash(m)); cout << "GNOSIS DEBUG: snSoK is " << this->serialNumberSoK.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) << " bytes" << endl;; }
bool GenerateAccumulatorWitness( const PublicCoin &coin, Accumulator& accumulator, AccumulatorWitness& witness, int& nMintsAdded, string& strError, CBlockIndex* pindexCheckpoint) { try { // Lock LogPrint("zero", "%s: generating\n", __func__); if (!LockMethod()) return false; LogPrint("zero", "%s: after lock\n", __func__); int nHeightMintAdded = SearchMintHeightOf(coin.getValue()); //get the checkpoint added at the next multiple of 10 int nHeightCheckpoint = nHeightMintAdded + (10 - (nHeightMintAdded % 10)); //the height to start accumulating coins to add to witness int nAccStartHeight = nHeightMintAdded - (nHeightMintAdded % 10); //Get the accumulator that is right before the cluster of blocks containing our mint was added to the accumulator CBigNum bnAccValue = 0; if (GetAccumulatorValue(nHeightCheckpoint, coin.getDenomination(), bnAccValue)) { if(!bnAccValue && Params().NetworkID() == CBaseChainParams::REGTEST){ accumulator.setInitialValue(); witness.resetValue(accumulator, coin); }else { accumulator.setValue(bnAccValue); witness.resetValue(accumulator, coin); } } //add the pubcoins from the blockchain up to the next checksum starting from the block CBlockIndex *pindex = chainActive[nHeightCheckpoint - 10]; int nChainHeight = chainActive.Height(); int nHeightStop = nChainHeight % 10; nHeightStop = nChainHeight - nHeightStop - 20; // at least two checkpoints deep //If looking for a specific checkpoint if (pindexCheckpoint) nHeightStop = pindexCheckpoint->nHeight - 10; //Iterate through the chain and calculate the witness int nCheckpointsAdded = 0; nMintsAdded = 0; libzerocoin::Accumulator witnessAccumulator = accumulator; if(!calculateAccumulatedBlocksFor( nAccStartHeight, nHeightStop, nHeightMintAdded, pindex, nCheckpointsAdded, bnAccValue, accumulator, witnessAccumulator, coin, strError )){ return error("GenerateAccumulatorWitness(): Calculate accumulated coins failed"); } witness.resetValue(witnessAccumulator, coin); if (!witness.VerifyWitness(accumulator, coin)) return error("%s: failed to verify witness", __func__); // A certain amount of accumulated coins are required if (nMintsAdded < Params().Zerocoin_RequiredAccumulation()) { strError = _(strprintf("Less than %d mints added, unable to create spend", Params().Zerocoin_RequiredAccumulation()).c_str()); return error("%s : %s", __func__, strError); } // calculate how many mints of this denomination existed in the accumulator we initialized nMintsAdded += ComputeAccumulatedCoins(nAccStartHeight, coin.getDenomination()); LogPrint("zero", "%s : %d mints added to witness\n", __func__, nMintsAdded); return true; // TODO: I know that could merge all of this exception but maybe it's not really good.. think if we should have a different treatment for each one } catch (searchMintHeightException e) { return error("%s: searchMintHeightException: %s", __func__, e.message); } catch (ChecksumInDbNotFoundException e) { return error("%s: ChecksumInDbNotFoundException: %s", __func__, e.message); } catch (GetPubcoinException e) { return error("%s: GetPubcoinException: %s", __func__, e.message); } }