예제 #1
0
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;;
}
AccumulatorProofOfKnowledge::AccumulatorProofOfKnowledge(const AccumulatorAndProofParams* p,
        const Commitment& commitmentToCoin, const AccumulatorWitness& witness,
        Accumulator& a): params(p) {

	Bignum sg = params->accumulatorPoKCommitmentGroup.g;
	Bignum sh = params->accumulatorPoKCommitmentGroup.h;

	Bignum g_n = params->accumulatorQRNCommitmentGroup.g;
	Bignum h_n = params->accumulatorQRNCommitmentGroup.h;

	Bignum e = commitmentToCoin.getContents();
	Bignum r = commitmentToCoin.getRandomness();

	Bignum r_1 = Bignum::randBignum(params->accumulatorModulus/4);
	Bignum r_2 = Bignum::randBignum(params->accumulatorModulus/4);
	Bignum r_3 = Bignum::randBignum(params->accumulatorModulus/4);

	this->C_e = g_n.pow_mod(e, params->accumulatorModulus) * h_n.pow_mod(r_1, params->accumulatorModulus);
	this->C_u = witness.getValue() * h_n.pow_mod(r_2, params->accumulatorModulus);
	this->C_r = g_n.pow_mod(r_2, params->accumulatorModulus) * h_n.pow_mod(r_3, params->accumulatorModulus);

	Bignum r_alpha = Bignum::randBignum(params->maxCoinValue * Bignum(2).pow(params->k_prime + params->k_dprime));
	if(!(Bignum::randBignum(Bignum(3)) % 2)) {
		r_alpha = 0-r_alpha;
	}

	Bignum r_gamma = Bignum::randBignum(params->accumulatorPoKCommitmentGroup.modulus);
	Bignum r_phi = Bignum::randBignum(params->accumulatorPoKCommitmentGroup.modulus);
	Bignum r_psi = Bignum::randBignum(params->accumulatorPoKCommitmentGroup.modulus);
	Bignum r_sigma = Bignum::randBignum(params->accumulatorPoKCommitmentGroup.modulus);
	Bignum r_xi = Bignum::randBignum(params->accumulatorPoKCommitmentGroup.modulus);

	Bignum r_epsilon =  Bignum::randBignum((params->accumulatorModulus/4) * Bignum(2).pow(params->k_prime + params->k_dprime));
	if(!(Bignum::randBignum(Bignum(3)) % 2)) {
		r_epsilon = 0-r_epsilon;
	}
	Bignum r_eta = Bignum::randBignum((params->accumulatorModulus/4) * Bignum(2).pow(params->k_prime + params->k_dprime));
	if(!(Bignum::randBignum(Bignum(3)) % 2)) {
		r_eta = 0-r_eta;
	}
	Bignum r_zeta = Bignum::randBignum((params->accumulatorModulus/4) * Bignum(2).pow(params->k_prime + params->k_dprime));
	if(!(Bignum::randBignum(Bignum(3)) % 2)) {
		r_zeta = 0-r_zeta;
	}

	Bignum r_beta = Bignum::randBignum((params->accumulatorModulus/4) * params->accumulatorPoKCommitmentGroup.modulus * Bignum(2).pow(params->k_prime + params->k_dprime));
	if(!(Bignum::randBignum(Bignum(3)) % 2)) {
		r_beta = 0-r_beta;
	}
	Bignum r_delta = Bignum::randBignum((params->accumulatorModulus/4) * params->accumulatorPoKCommitmentGroup.modulus * Bignum(2).pow(params->k_prime + params->k_dprime));
	if(!(Bignum::randBignum(Bignum(3)) % 2)) {
		r_delta = 0-r_delta;
	}

	this->st_1 = (sg.pow_mod(r_alpha, params->accumulatorPoKCommitmentGroup.modulus) * sh.pow_mod(r_phi, params->accumulatorPoKCommitmentGroup.modulus)) % params->accumulatorPoKCommitmentGroup.modulus;
	this->st_2 = (((commitmentToCoin.getCommitmentValue() * sg.inverse(params->accumulatorPoKCommitmentGroup.modulus)).pow_mod(r_gamma, params->accumulatorPoKCommitmentGroup.modulus)) * sh.pow_mod(r_psi, params->accumulatorPoKCommitmentGroup.modulus)) % params->accumulatorPoKCommitmentGroup.modulus;
	this->st_3 = ((sg * commitmentToCoin.getCommitmentValue()).pow_mod(r_sigma, params->accumulatorPoKCommitmentGroup.modulus) * sh.pow_mod(r_xi, params->accumulatorPoKCommitmentGroup.modulus)) % params->accumulatorPoKCommitmentGroup.modulus;

	this->t_1 = (h_n.pow_mod(r_zeta, params->accumulatorModulus) * g_n.pow_mod(r_epsilon, params->accumulatorModulus)) % params->accumulatorModulus;
	this->t_2 = (h_n.pow_mod(r_eta, params->accumulatorModulus) * g_n.pow_mod(r_alpha, params->accumulatorModulus)) % params->accumulatorModulus;
	this->t_3 = (C_u.pow_mod(r_alpha, params->accumulatorModulus) * ((h_n.inverse(params->accumulatorModulus)).pow_mod(r_beta, params->accumulatorModulus))) % params->accumulatorModulus;
	this->t_4 = (C_r.pow_mod(r_alpha, params->accumulatorModulus) * ((h_n.inverse(params->accumulatorModulus)).pow_mod(r_delta, params->accumulatorModulus)) * ((g_n.inverse(params->accumulatorModulus)).pow_mod(r_beta, params->accumulatorModulus))) % params->accumulatorModulus;

	CHashWriter hasher(0,0);
	hasher << *params << sg << sh << g_n << h_n << commitmentToCoin.getCommitmentValue() << C_e << C_u << C_r << st_1 << st_2 << st_3 << t_1 << t_2 << t_3 << t_4;

	//According to the proof, this hash should be of length k_prime bits.  It is currently greater than that, which should not be a problem, but we should check this.
	Bignum c = Bignum(hasher.GetHash());

	this->s_alpha = r_alpha - c*e;
	this->s_beta = r_beta - c*r_2*e;
	this->s_zeta = r_zeta - c*r_3;
	this->s_sigma = r_sigma - c*((e+1).inverse(params->accumulatorPoKCommitmentGroup.groupOrder));
	this->s_eta = r_eta - c*r_1;
	this->s_epsilon = r_epsilon - c*r_2;
	this->s_delta = r_delta - c*r_3*e;
	this->s_xi = r_xi + c*r*((e+1).inverse(params->accumulatorPoKCommitmentGroup.groupOrder));
	this->s_phi = (r_phi - c*r) % params->accumulatorPoKCommitmentGroup.groupOrder;
	this->s_gamma = r_gamma - c*((e-1).inverse(params->accumulatorPoKCommitmentGroup.groupOrder));
	this->s_psi = r_psi + c*r*((e-1).inverse(params->accumulatorPoKCommitmentGroup.groupOrder));
}
예제 #3
0
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);
    }
}
예제 #4
0
bool GenerateAccumulatorWitness(const PublicCoin &coin, Accumulator& accumulator, AccumulatorWitness& witness, int nSecurityLevel, int& nMintsAdded, string& strError)
{
    uint256 txid;
    if (!zerocoinDB->ReadCoinMint(coin.getValue(), txid)) {
        LogPrint("zero","%s failed to read mint from db\n", __func__);
        return false;
    }

    CTransaction txMinted;
    uint256 hashBlock;
    if (!GetTransaction(txid, txMinted, hashBlock)) {
        LogPrint("zero","%s failed to read tx\n", __func__);
        return false;
    }

    int nHeightMintAdded= mapBlockIndex[hashBlock]->nHeight;
    uint256 nCheckpointBeforeMint = 0;
    CBlockIndex* pindex = chainActive[nHeightMintAdded];
    int nChanges = 0;

    //find the checksum when this was added to the accumulator officially, which will be two checksum changes later
    //reminder that checksums are generated when the block height is a multiple of 10
    while (pindex->nHeight < chainActive.Tip()->nHeight - 1) {
        if (pindex->nHeight == nHeightMintAdded) {
            pindex = chainActive[pindex->nHeight + 1];
            continue;
        }

        //check if the next checksum was generated
        if (pindex->nHeight % 10 == 0) {
            nChanges++;

            if (nChanges == 1) {
                nCheckpointBeforeMint = pindex->nAccumulatorCheckpoint;
                break;
            }
        }
        pindex = chainActive.Next(pindex);
    }

    //the height to start accumulating coins to add to witness
    int nAccStartHeight = nHeightMintAdded - (nHeightMintAdded % 10);

    //If the checkpoint is from the recalculated checkpoint period, then adjust it
    int nHeight_LastGoodCheckpoint = Params().Zerocoin_Block_LastGoodCheckpoint();
    int nHeight_Recalculate = Params().Zerocoin_Block_RecalculateAccumulators();
    if (pindex->nHeight < nHeight_Recalculate - 10 && pindex->nHeight > nHeight_LastGoodCheckpoint) {
        //The checkpoint before the mint will be the last good checkpoint
        nCheckpointBeforeMint = chainActive[nHeight_LastGoodCheckpoint]->nAccumulatorCheckpoint;
        nAccStartHeight = nHeight_LastGoodCheckpoint - 10;
    }

    //Get the accumulator that is right before the cluster of blocks containing our mint was added to the accumulator
    CBigNum bnAccValue = 0;
    if (GetAccumulatorValueFromDB(nCheckpointBeforeMint, coin.getDenomination(), bnAccValue)) {
        if (bnAccValue > 0) {
            accumulator.setValue(bnAccValue);
            witness.resetValue(accumulator, coin);
        }
    }

    //security level: this is an important prevention of tracing the coins via timing. Security level represents how many checkpoints
    //of accumulated coins are added *beyond* the checkpoint that the mint being spent was added too. If each spend added the exact same
    //amounts of checkpoints after the mint was accumulated, then you could know the range of blocks that the mint originated from.
    if (nSecurityLevel < 100) {
        //add some randomness to the user's selection so that it is not always the same
        nSecurityLevel += CBigNum::randBignum(10).getint();

        //security level 100 represents adding all available coins that have been accumulated - user did not select this
        if (nSecurityLevel >= 100)
            nSecurityLevel = 99;
    }

    //add the pubcoins (zerocoinmints that have been published to the chain) up to the next checksum starting from the block
    pindex = chainActive[nAccStartHeight];
    int nChainHeight = chainActive.Height();
    int nHeightStop = nChainHeight % 10;
    nHeightStop = nChainHeight - nHeightStop - 20; // at least two checkpoints deep
    int nCheckpointsAdded = 0;
    nMintsAdded = 0;
    while (pindex->nHeight < nHeightStop + 1) {
        if (pindex->nHeight != nAccStartHeight && pindex->pprev->nAccumulatorCheckpoint != pindex->nAccumulatorCheckpoint)
            ++nCheckpointsAdded;

        //if a new checkpoint was generated on this block, and we have added the specified amount of checkpointed accumulators,
        //then initialize the accumulator at this point and break
        if (!InvalidCheckpointRange(pindex->nHeight) && (pindex->nHeight >= nHeightStop || (nSecurityLevel != 100 && nCheckpointsAdded >= nSecurityLevel))) {
            uint32_t nChecksum = ParseChecksum(chainActive[pindex->nHeight + 10]->nAccumulatorCheckpoint, coin.getDenomination());
            CBigNum bnAccValue = 0;
            if (!zerocoinDB->ReadAccumulatorValue(nChecksum, bnAccValue)) {
                LogPrintf("%s : failed to find checksum in database for accumulator\n", __func__);
                return false;
            }
            accumulator.setValue(bnAccValue);
            break;
        }

        // if this block contains mints of the denomination that is being spent, then add them to the witness
        if (pindex->MintedDenomination(coin.getDenomination())) {
            //grab mints from this block
            CBlock block;
            if(!ReadBlockFromDisk(block, pindex)) {
                LogPrintf("%s: failed to read block from disk while adding pubcoins to witness\n", __func__);
                return false;
            }

            list<PublicCoin> listPubcoins;
            if(!BlockToPubcoinList(block, listPubcoins, true)) {
                LogPrintf("%s: failed to get zerocoin mintlist from block %n\n", __func__, pindex->nHeight);
                return false;
            }

            //add the mints to the witness
            for (const PublicCoin pubcoin : listPubcoins) {
                if (pubcoin.getDenomination() != coin.getDenomination())
                    continue;

                if (pindex->nHeight == nHeightMintAdded && pubcoin.getValue() == coin.getValue())
                    continue;

                witness.addRawValue(pubcoin.getValue());
                ++nMintsAdded;
            }
        }

        pindex = chainActive[pindex->nHeight + 1];
    }

    if (nMintsAdded < Params().Zerocoin_RequiredAccumulation()) {
        strError = _(strprintf("Less than %d mints added, unable to create spend", Params().Zerocoin_RequiredAccumulation()).c_str());
        LogPrintf("%s : %s\n", __func__, strError);
        return false;
    }

    // calculate how many mints of this denomination existed in the accumulator we initialized
    int nZerocoinStartHeight = GetZerocoinStartHeight();
    pindex = chainActive[nZerocoinStartHeight];
    while (pindex->nHeight < nAccStartHeight) {
        nMintsAdded += count(pindex->vMintDenominationsInBlock.begin(), pindex->vMintDenominationsInBlock.end(), coin.getDenomination());
        pindex = chainActive[pindex->nHeight + 1];
    }

    LogPrint("zero","%s : %d mints added to witness\n", __func__, nMintsAdded);
    return true;
}
예제 #5
0
bool CalculateAccumulatorWitnessFor(
        const ZerocoinParams* params,
        int startHeight,
        int maxCalulationRange,
        CoinDenomination den,
        const CBloomFilter& filter,
        Accumulator& accumulator,
        AccumulatorWitness& witness,
        int& nMintsAdded,
        string& strError,
        list<CBigNum>& ret,
        int &heightStop
){
    // Lock
    if (!LockMethod()) return false;

    try {
        // Dummy coin init
        PublicCoin temp(params, 0, den);
        // Dummy Acc init
        Accumulator testingAcc(params, den);

        //get the checkpoint added at the next multiple of 10
        int nHeightCheckpoint = startHeight + (10 - (startHeight % 10));

        // Get the base accumulator
        // TODO: This needs to be changed to the partial witness calculation on the next version.
        CBigNum bnAccValue = 0;
        if (GetAccumulatorValue(nHeightCheckpoint, den, bnAccValue)) {
            accumulator.setValue(bnAccValue);
            witness.resetValue(accumulator, temp);
        }

        // 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 (nHeightStop - startHeight > maxCalulationRange) {
            int stop = (startHeight + maxCalulationRange);
            int nHeightStop = stop % 10;
            nHeightStop = stop - nHeightStop - 20;
        }
        heightStop = nHeightStop;

        nMintsAdded = 0;

        // Starts on top of the witness that the node sent
        libzerocoin::Accumulator witnessAccumulator(params, den, witness.getValue());

        if(!calculateAccumulatedBlocksFor(
                startHeight,
                nHeightStop,
                pindex,
                bnAccValue,
                accumulator,
                den,
                filter,
                witnessAccumulator,
                ret,
                strError
        ))
            return error("CalculateAccumulatorWitnessFor(): Calculate accumulated coins failed");

        // reset the value
        witness.resetValue(witnessAccumulator, temp);

        // calculate how many mints of this denomination existed in the accumulator we initialized
        nMintsAdded += ComputeAccumulatedCoins(startHeight, den);
        LogPrint("zero", "%s : %d mints added to witness\n", __func__, nMintsAdded);

        return true;

    } catch (ChecksumInDbNotFoundException e) {
        return error("%s: ChecksumInDbNotFoundException: %s", __func__, e.message);
    } catch (GetPubcoinException e) {
        return error("%s: GetPubcoinException: %s", __func__, e.message);
    }
}