diff --git a/src/blind.cpp b/src/blind.cpp index 357a137620..0d13819aee 100644 --- a/src/blind.cpp +++ b/src/blind.cpp @@ -17,8 +17,6 @@ public: secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); assert(ctx != NULL); - secp256k1_pedersen_context_initialize(ctx); - secp256k1_rangeproof_context_initialize(ctx); secp256k1_blind_context = ctx; } @@ -47,9 +45,12 @@ bool UnblindOutput(const CKey &key, const CTxOut& txout, CAmount& amount_out, ui uint256 nonce = key.ECDH(ephemeral_key); CSHA256().Write(nonce.begin(), 32).Finalize(nonce.begin()); unsigned char msg[4096]; - int msg_size; + size_t msg_size; uint64_t min_value, max_value, amount; - int res = secp256k1_rangeproof_rewind(secp256k1_blind_context, blinding_factor_out.begin(), &amount, msg, &msg_size, nonce.begin(), &min_value, &max_value, &txout.nValue.vchCommitment[0], &txout.nValue.vchRangeproof[0], txout.nValue.vchRangeproof.size()); + secp256k1_pedersen_commitment commit; + if(!secp256k1_pedersen_commitment_parse(secp256k1_blind_context, &commit, &txout.nValue.vchCommitment[0])) + return false; + int res = secp256k1_rangeproof_rewind(secp256k1_blind_context, blinding_factor_out.begin(), &amount, msg, &msg_size, nonce.begin(), &min_value, &max_value, &commit, &txout.nValue.vchRangeproof[0], txout.nValue.vchRangeproof.size(), NULL, 0, secp256k1_generator_h); if (!res || amount > (uint64_t)MAX_MONEY || !MoneyRange((CAmount)amount)) { amount_out = 0; blinding_factor_out = uint256(); @@ -130,7 +131,10 @@ bool BlindOutputs(const std::vector& input_blinding_factors, std::vect // Create blinded value CTxOutValue& value = tx.vout[nOut].nValue; CAmount amount = value.GetAmount(); - assert(secp256k1_pedersen_commit(secp256k1_blind_context, &value.vchCommitment[0], (unsigned char*)blindptrs.back(), amount)); + secp256k1_pedersen_commitment commit; + assert(secp256k1_pedersen_commit(secp256k1_blind_context, &commit, (unsigned char*)blindptrs.back(), amount, secp256k1_generator_h)); + secp256k1_pedersen_commitment_serialize(secp256k1_blind_context, &value.vchCommitment[0], &commit); + assert(value.IsValid()); // Generate ephemeral key for ECDH nonce generation CKey ephemeral_key; ephemeral_key.MakeNewKey(true); @@ -141,10 +145,12 @@ bool BlindOutputs(const std::vector& input_blinding_factors, std::vect uint256 nonce = ephemeral_key.ECDH(output_pubkeys[nOut]); CSHA256().Write(nonce.begin(), 32).Finalize(nonce.begin()); // Create range proof - int nRangeProofLen = 5134; + size_t nRangeProofLen = 5134; // TODO: smarter min_value selection value.vchRangeproof.resize(nRangeProofLen); - int res = secp256k1_rangeproof_sign(secp256k1_blind_context, &value.vchRangeproof[0], &nRangeProofLen, 0, &value.vchCommitment[0], blindptrs.back(), nonce.begin(), std::min(std::max((int)GetArg("-ct_exponent", 0), -1),18), std::min(std::max((int)GetArg("-ct_bits", 32), 1), 51), amount); + unsigned char message; + size_t msg_len = 0; + int res = secp256k1_rangeproof_sign(secp256k1_blind_context, &value.vchRangeproof[0], &nRangeProofLen, 0, &commit, blindptrs.back(), nonce.begin(), std::min(std::max((int)GetArg("-ct_exponent", 0), -1),18), std::min(std::max((int)GetArg("-ct_bits", 32), 1), 51), amount, &message, msg_len, NULL, 0, secp256k1_generator_h); value.vchRangeproof.resize(nRangeProofLen); // TODO: do something smarter here assert(res); diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 2a5c5da079..c1df4044e1 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -65,8 +65,12 @@ bool CTxOutValue::IsValid() const if (vchCommitment[i]) return false; return true; + // Alpha used 2 and 3 for value commitments case 2: case 3: + return false; + case 8: + case 9: return true; default: return false; diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index c35b29645a..e72f2c346e 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -139,7 +139,10 @@ bool CachingRangeProofChecker::VerifyRangeProof(const std::vector } uint64_t min_value, max_value; - if (!secp256k1_rangeproof_verify(secp256k1_ctx_verify_amounts, &min_value, &max_value, &vchCommitment[0], vchRangeProof.data(), vchRangeProof.size())) { + secp256k1_pedersen_commitment commit; + if (secp256k1_pedersen_commitment_parse(secp256k1_ctx_verify_amounts, &commit, &vchCommitment[0]) != 1) + return false; + if (!secp256k1_rangeproof_verify(secp256k1_ctx_verify_amounts, &min_value, &max_value, &commit, vchRangeProof.data(), vchRangeProof.size(), NULL, 0, secp256k1_generator_h)) { return false; } diff --git a/src/validation.cpp b/src/validation.cpp index c517645902..de81b783fa 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -554,9 +554,7 @@ class Secp256k1Ctx public: Secp256k1Ctx() { assert(secp256k1_ctx_verify_amounts == NULL); - secp256k1_ctx_verify_amounts = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); - secp256k1_pedersen_context_initialize(secp256k1_ctx_verify_amounts); - secp256k1_rangeproof_context_initialize(secp256k1_ctx_verify_amounts); + secp256k1_ctx_verify_amounts = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); assert(secp256k1_ctx_verify_amounts != NULL); } @@ -585,15 +583,14 @@ public: class CBalanceCheck : public CCheck { private: - std::vector vchData; - std::vector vpchCommitsIn, vpchCommitsOut; - CAmount nPlainAmount; + std::vector vData; + std::vector vpCommitsIn, vpCommitsOut; public: - CBalanceCheck(std::vector& vchData_, std::vector& vpchCommitsIn_, std::vector& vpchCommitsOut_, const CAmount& nPlainAmount_) : nPlainAmount(nPlainAmount_) { - vchData.swap(vchData_); - vpchCommitsIn.swap(vpchCommitsIn_); - vpchCommitsOut.swap(vpchCommitsOut_); + CBalanceCheck(std::vector& vData_, std::vector& vpCommitsIn_, std::vector& vpCommitsOut_) { + vData.swap(vData_); + vpCommitsIn.swap(vpCommitsIn_); + vpCommitsOut.swap(vpCommitsOut_); } bool operator()(); @@ -622,7 +619,7 @@ bool CRangeCheck::operator()() bool CBalanceCheck::operator()() { - if (!secp256k1_pedersen_verify_tally(secp256k1_ctx_verify_amounts, vpchCommitsIn.data(), vpchCommitsIn.size(), vpchCommitsOut.data(), vpchCommitsOut.size(), nPlainAmount)) { + if (!secp256k1_pedersen_verify_tally(secp256k1_ctx_verify_amounts, vpCommitsIn.data(), vpCommitsIn.size(), vpCommitsOut.data(), vpCommitsOut.size())) { fAmountError = true; return false; } @@ -636,15 +633,21 @@ bool CBalanceCheck::operator()() bool VerifyAmounts(const CCoinsViewCache& cache, const CTransaction& tx, const CAmount& excess, std::vector* pvChecks, const bool cacheStore) { - bool fNeedNoRangeProof = false; CAmount nPlainAmount = excess; + unsigned int blindedInputs = 0; + unsigned int blindedOutputs = 0; { - std::vector vchData; - std::vector vpchCommitsIn, vpchCommitsOut; + std::vector vData; + std::vector vpCommitsIn, vpCommitsOut; + bool fNullRangeproof = false; - vchData.resize(CTxOutValue::nCommitmentSize * (tx.vin.size() + tx.vout.size())); - unsigned char *p = vchData.data(); + vData.resize((tx.vin.size() + tx.vout.size() + 1)); // 1 for fee + secp256k1_pedersen_commitment *p = &vData[0]; + secp256k1_pedersen_commitment commit; + // This is used to add in the explicit values + unsigned char explBlinds[32]; + memset(explBlinds, 0, sizeof(explBlinds)); if (!tx.IsCoinBase()) { for (size_t i = 0; i < tx.vin.size(); ++i) @@ -657,10 +660,13 @@ bool VerifyAmounts(const CCoinsViewCache& cache, const CTransaction& tx, const C } else { + blindedInputs += 1; assert(val.vchCommitment.size() == CTxOutValue::nCommitmentSize); - memcpy(p, &val.vchCommitment[0], CTxOutValue::nCommitmentSize); - vpchCommitsIn.push_back(p); - p += CTxOutValue::nCommitmentSize; + if (secp256k1_pedersen_commitment_parse(secp256k1_ctx_verify_amounts, &commit, &val.vchCommitment[0]) != 1) + return false; + memcpy(p, &commit, sizeof(secp256k1_pedersen_commitment)); + vpCommitsIn.push_back(p); + p++; } } } @@ -677,30 +683,45 @@ bool VerifyAmounts(const CCoinsViewCache& cache, const CTransaction& tx, const C } else { - memcpy(p, &val.vchCommitment[0], CTxOutValue::nCommitmentSize); - vpchCommitsOut.push_back(p); - p += CTxOutValue::nCommitmentSize; + blindedOutputs += 1; + if (secp256k1_pedersen_commitment_parse(secp256k1_ctx_verify_amounts, &commit, &val.vchCommitment[0]) != 1) + return false; if (val.vchRangeproof.empty()) fNullRangeproof = true; + memcpy(p, &commit, sizeof(secp256k1_pedersen_commitment)); + vpCommitsOut.push_back(p); + p++; } } // If there are no encrypted input or output values, we can do simple math - if (vpchCommitsIn.size() + vpchCommitsOut.size() == 0) + if (blindedInputs + blindedOutputs == 0) return (nPlainAmount == 0); - fNeedNoRangeProof = ((!vpchCommitsIn.empty()) && vpchCommitsOut.size() == 1 && nPlainAmount <= 0 && fNullRangeproof); + // Add fee to tally + if (nPlainAmount != 0) { + if (secp256k1_pedersen_commit(secp256k1_ctx_verify_amounts, &commit, explBlinds, nPlainAmount > 0 ? nPlainAmount : -nPlainAmount, secp256k1_generator_h) != 1) + return false; - if (!QueueCheck(pvChecks, new CBalanceCheck(vchData, vpchCommitsIn, vpchCommitsOut, nPlainAmount))) { - return false; + memcpy(p, &commit, sizeof(secp256k1_pedersen_commitment)); + if (nPlainAmount > 0) + vpCommitsOut.push_back(p); + else + vpCommitsIn.push_back(p); + p++; } + + + if (!QueueCheck(pvChecks, new CBalanceCheck(vData, vpCommitsIn, vpCommitsOut))) { + return false; } // Rangeproof is optional in this case - if (fNeedNoRangeProof) + if (blindedInputs > 0 && blindedOutputs == 1 && nPlainAmount <= 0 && fNullRangeproof) return true; + } for (size_t i = 0; i < tx.vout.size(); ++i) { const CTxOutValue& val = tx.vout[i].nValue;