From 2ac5eafbca1ebe20492f4feaf496125264007fae Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Thu, 30 Mar 2017 15:03:50 -0400 Subject: [PATCH] Remove spendable 0-value outputs from consensus, wallet --- src/blind.cpp | 3 ++- src/script/sigcache.cpp | 9 +++++++++ src/test/blind_tests.cpp | 6 +++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/blind.cpp b/src/blind.cpp index ccd8dacab3..5d52470623 100644 --- a/src/blind.cpp +++ b/src/blind.cpp @@ -148,7 +148,8 @@ bool GenerateRangeproof(std::vector& vchRangeproof, const std::ve memcpy(assetsMessage+32, assetblindptrs[assetblindptrs.size()-1], 32); // Sign rangeproof - int res = secp256k1_rangeproof_sign(secp256k1_blind_context, &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, assetsMessage, sizeof(assetsMessage), scriptPubKey.size() ? &scriptPubKey.front() : NULL, scriptPubKey.size(), &gen); + // If min_value is 0, scriptPubKey must be unspendable + int res = secp256k1_rangeproof_sign(secp256k1_blind_context, &vchRangeproof[0], &nRangeProofLen, scriptPubKey.IsUnspendable() ? 0 : 1, &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, assetsMessage, sizeof(assetsMessage), scriptPubKey.size() ? &scriptPubKey.front() : NULL, scriptPubKey.size(), &gen); vchRangeproof.resize(nRangeProofLen); // TODO: do something smarter here return (res == 1); diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index e75e7bdc53..9d7f7e6637 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -166,6 +166,15 @@ bool CachingRangeProofChecker::VerifyRangeProof(const std::vector return false; } + // An rangeproof is not valid if the output is spendable but the minimum number + // is 0. This is to prevent people passing 0-value tokens around, or conjuring + // reissuance tokens from nothing then attempting to reissue an asset. + // ie reissuance doesn't require revealing value of reissuance output + // Issuances proofs are always "unspendable" as they commit to an empty script. + if (min_value == 0 && !scriptPubKey.IsUnspendable()) { + return false; + } + return true; } diff --git a/src/test/blind_tests.cpp b/src/test/blind_tests.cpp index 273d97aee6..5fce0d6c49 100644 --- a/src/test/blind_tests.cpp +++ b/src/test/blind_tests.cpp @@ -105,8 +105,8 @@ BOOST_AUTO_TEST_CASE(naive_blinding_test) output_pubkeys.push_back(CPubKey()); BOOST_CHECK(BlindTransaction(input_blinds, input_asset_blinds, input_assets, input_amounts, output_blinds, output_asset_blinds, output_pubkeys, vDummy, vDummy, tx3) == 0); - // Add a dummy output. - tx3.vout.push_back(CTxOut(bitcoinID, 0, CScript() << OP_TRUE)); + // Add a dummy output. Must be unspendable since it's 0-valued. + tx3.vout.push_back(CTxOut(bitcoinID, 0, CScript() << OP_RETURN)); output_pubkeys.push_back(pubkeyDummy); BOOST_CHECK(BlindTransaction(input_blinds, input_asset_blinds, input_assets, input_amounts, output_blinds, output_asset_blinds, output_pubkeys, vDummy, vDummy, tx3) == 2); BOOST_CHECK(!tx3.vout[0].nValue.IsExplicit()); @@ -121,7 +121,7 @@ BOOST_AUTO_TEST_CASE(naive_blinding_test) BOOST_CHECK(unblinded_id == bitcoinID); CAsset temp_asset; uint256 temp_asset_blinder; - BOOST_CHECK(UnblindConfidentialPair(keyDummy, tx3.vout[2].nValue, tx3.vout[2].nAsset, tx3.vout[2].nNonce, scriptCommit, tx3.wit.vtxoutwit[2].vchRangeproof, unblinded_amount, blindDummy, temp_asset, temp_asset_blinder) == 1); + BOOST_CHECK(UnblindConfidentialPair(keyDummy, tx3.vout[2].nValue, tx3.vout[2].nAsset, tx3.vout[2].nNonce, CScript() << OP_RETURN, tx3.wit.vtxoutwit[2].vchRangeproof, unblinded_amount, blindDummy, temp_asset, temp_asset_blinder) == 1); BOOST_CHECK(unblinded_amount == 0); CCoinsModifier in3 = cache.ModifyCoins(ArithToUint256(3));