bulletproofs: add multi output bulletproofs to rct
This commit is contained in:
parent
f34e2e20bd
commit
9ce9f8caf6
|
@ -3059,6 +3059,22 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
|
||||||
MERROR_VER("Unsupported rct type: " << rv.type);
|
MERROR_VER("Unsupported rct type: " << rv.type);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for bulletproofs, check they're only multi-output after v8
|
||||||
|
if (rv.type == rct::RCTTypeFullBulletproof || rv.type == rct::RCTTypeSimpleBulletproof)
|
||||||
|
{
|
||||||
|
if (hf_version < 8)
|
||||||
|
{
|
||||||
|
for (const rct::Bulletproof &proof: rv.p.bulletproofs)
|
||||||
|
{
|
||||||
|
if (proof.V.size() > 1)
|
||||||
|
{
|
||||||
|
MERROR_VER("Multi output bulletproofs are invalid before v8");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,7 +195,7 @@ namespace cryptonote
|
||||||
return addr.m_view_public_key;
|
return addr.m_view_public_key;
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout, bool shuffle_outs)
|
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, rct::RangeProofType range_proof_type, rct::multisig_out *msout, bool shuffle_outs)
|
||||||
{
|
{
|
||||||
hw::device &hwdev = sender_account_keys.get_device();
|
hw::device &hwdev = sender_account_keys.get_device();
|
||||||
|
|
||||||
|
@ -589,9 +589,9 @@ namespace cryptonote
|
||||||
get_transaction_prefix_hash(tx, tx_prefix_hash);
|
get_transaction_prefix_hash(tx, tx_prefix_hash);
|
||||||
rct::ctkeyV outSk;
|
rct::ctkeyV outSk;
|
||||||
if (use_simple_rct)
|
if (use_simple_rct)
|
||||||
tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, bulletproof, hwdev);
|
tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, range_proof_type, hwdev);
|
||||||
else
|
else
|
||||||
tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, bulletproof, hwdev); // same index assumption
|
tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, range_proof_type, hwdev); // same index assumption
|
||||||
memwipe(inSk.data(), inSk.size() * sizeof(rct::ctkey));
|
memwipe(inSk.data(), inSk.size() * sizeof(rct::ctkey));
|
||||||
|
|
||||||
CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout");
|
CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout");
|
||||||
|
@ -604,7 +604,7 @@ namespace cryptonote
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout)
|
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, rct::RangeProofType range_proof_type, rct::multisig_out *msout)
|
||||||
{
|
{
|
||||||
hw::device &hwdev = sender_account_keys.get_device();
|
hw::device &hwdev = sender_account_keys.get_device();
|
||||||
hwdev.open_tx(tx_key);
|
hwdev.open_tx(tx_key);
|
||||||
|
@ -622,7 +622,7 @@ namespace cryptonote
|
||||||
additional_tx_keys.push_back(keypair::generate(sender_account_keys.get_device()).sec);
|
additional_tx_keys.push_back(keypair::generate(sender_account_keys.get_device()).sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, bulletproof, msout);
|
bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, range_proof_type, msout);
|
||||||
hwdev.close_tx();
|
hwdev.close_tx();
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -634,7 +634,7 @@ namespace cryptonote
|
||||||
crypto::secret_key tx_key;
|
crypto::secret_key tx_key;
|
||||||
std::vector<crypto::secret_key> additional_tx_keys;
|
std::vector<crypto::secret_key> additional_tx_keys;
|
||||||
std::vector<tx_destination_entry> destinations_copy = destinations;
|
std::vector<tx_destination_entry> destinations_copy = destinations;
|
||||||
return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, false, NULL);
|
return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBorromean, NULL);
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
bool generate_genesis_block(
|
bool generate_genesis_block(
|
||||||
|
|
|
@ -90,8 +90,8 @@ namespace cryptonote
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr);
|
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr);
|
||||||
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time);
|
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time);
|
||||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL, bool shuffle_outs = true);
|
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, rct::multisig_out *msout = NULL, bool shuffle_outs = true);
|
||||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL);
|
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, rct::RangeProofType range_proof_type = rct::RangeProofBorromean, rct::multisig_out *msout = NULL);
|
||||||
|
|
||||||
bool generate_genesis_block(
|
bool generate_genesis_block(
|
||||||
block& bl
|
block& bl
|
||||||
|
|
|
@ -45,30 +45,6 @@ using namespace std;
|
||||||
#define CHECK_AND_ASSERT_MES_L1(expr, ret, message) {if(!(expr)) {MCERROR("verify", message); return ret;}}
|
#define CHECK_AND_ASSERT_MES_L1(expr, ret, message) {if(!(expr)) {MCERROR("verify", message); return ret;}}
|
||||||
|
|
||||||
namespace rct {
|
namespace rct {
|
||||||
bool is_simple(int type)
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case RCTTypeSimple:
|
|
||||||
case RCTTypeSimpleBulletproof:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_bulletproof(int type)
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case RCTTypeSimpleBulletproof:
|
|
||||||
case RCTTypeFullBulletproof:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount)
|
Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount)
|
||||||
{
|
{
|
||||||
mask = rct::skGen();
|
mask = rct::skGen();
|
||||||
|
@ -78,6 +54,15 @@ namespace rct {
|
||||||
return proof;
|
return proof;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector<uint64_t> &amounts)
|
||||||
|
{
|
||||||
|
masks = rct::skvGen(amounts.size());
|
||||||
|
Bulletproof proof = bulletproof_PROVE(amounts, masks);
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(proof.V.size() == amounts.size(), "V does not have the expected size");
|
||||||
|
C = proof.V;
|
||||||
|
return proof;
|
||||||
|
}
|
||||||
|
|
||||||
bool verBulletproof(const Bulletproof &proof)
|
bool verBulletproof(const Bulletproof &proof)
|
||||||
{
|
{
|
||||||
try { return bulletproof_VERIFY(proof); }
|
try { return bulletproof_VERIFY(proof); }
|
||||||
|
@ -389,7 +374,7 @@ namespace rct {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
binary_archive<true> ba(ss);
|
binary_archive<true> ba(ss);
|
||||||
CHECK_AND_ASSERT_THROW_MES(!rv.mixRing.empty(), "Empty mixRing");
|
CHECK_AND_ASSERT_THROW_MES(!rv.mixRing.empty(), "Empty mixRing");
|
||||||
const size_t inputs = is_simple(rv.type) ? rv.mixRing.size() : rv.mixRing[0].size();
|
const size_t inputs = is_rct_simple(rv.type) ? rv.mixRing.size() : rv.mixRing[0].size();
|
||||||
const size_t outputs = rv.ecdhInfo.size();
|
const size_t outputs = rv.ecdhInfo.size();
|
||||||
key prehash;
|
key prehash;
|
||||||
CHECK_AND_ASSERT_THROW_MES(const_cast<rctSig&>(rv).serialize_rctsig_base(ba, inputs, outputs),
|
CHECK_AND_ASSERT_THROW_MES(const_cast<rctSig&>(rv).serialize_rctsig_base(ba, inputs, outputs),
|
||||||
|
@ -659,7 +644,8 @@ namespace rct {
|
||||||
// must know the destination private key to find the correct amount, else will return a random number
|
// must know the destination private key to find the correct amount, else will return a random number
|
||||||
// Note: For txn fees, the last index in the amounts vector should contain that
|
// Note: For txn fees, the last index in the amounts vector should contain that
|
||||||
// Thus the amounts vector will be "one" longer than the destinations vectort
|
// Thus the amounts vector will be "one" longer than the destinations vectort
|
||||||
rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, bool bulletproof, hw::device &hwdev) {
|
rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, RangeProofType range_proof_type, hw::device &hwdev) {
|
||||||
|
const bool bulletproof = range_proof_type != RangeProofBorromean;
|
||||||
CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations");
|
CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations");
|
||||||
CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations");
|
CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations");
|
||||||
CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing");
|
CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing");
|
||||||
|
@ -672,9 +658,7 @@ namespace rct {
|
||||||
rv.type = bulletproof ? RCTTypeFullBulletproof : RCTTypeFull;
|
rv.type = bulletproof ? RCTTypeFullBulletproof : RCTTypeFull;
|
||||||
rv.message = message;
|
rv.message = message;
|
||||||
rv.outPk.resize(destinations.size());
|
rv.outPk.resize(destinations.size());
|
||||||
if (bulletproof)
|
if (!bulletproof)
|
||||||
rv.p.bulletproofs.resize(destinations.size());
|
|
||||||
else
|
|
||||||
rv.p.rangeSigs.resize(destinations.size());
|
rv.p.rangeSigs.resize(destinations.size());
|
||||||
rv.ecdhInfo.resize(destinations.size());
|
rv.ecdhInfo.resize(destinations.size());
|
||||||
|
|
||||||
|
@ -684,18 +668,46 @@ namespace rct {
|
||||||
for (i = 0; i < destinations.size(); i++) {
|
for (i = 0; i < destinations.size(); i++) {
|
||||||
//add destination to sig
|
//add destination to sig
|
||||||
rv.outPk[i].dest = copy(destinations[i]);
|
rv.outPk[i].dest = copy(destinations[i]);
|
||||||
//compute range proof
|
//compute range proof (bulletproofs are done later)
|
||||||
if (bulletproof)
|
if (!bulletproof)
|
||||||
rv.p.bulletproofs[i] = proveRangeBulletproof(rv.outPk[i].mask, outSk[i].mask, amounts[i]);
|
{
|
||||||
else
|
|
||||||
rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]);
|
rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]);
|
||||||
#ifdef DBG
|
#ifdef DBG
|
||||||
if (bulletproof)
|
|
||||||
CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs[i]), "verBulletproof failed on newly created proof");
|
|
||||||
else
|
|
||||||
CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof");
|
CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof");
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rv.p.bulletproofs.clear();
|
||||||
|
if (bulletproof)
|
||||||
|
{
|
||||||
|
std::vector<uint64_t> proof_amounts;
|
||||||
|
size_t amounts_proved = 0;
|
||||||
|
while (amounts_proved < amounts.size())
|
||||||
|
{
|
||||||
|
size_t batch_size = 1;
|
||||||
|
if (range_proof_type == RangeProofMultiOutputBulletproof)
|
||||||
|
while (batch_size * 2 + amounts_proved <= amounts.size())
|
||||||
|
batch_size *= 2;
|
||||||
|
rct::keyV C, masks;
|
||||||
|
std::vector<uint64_t> batch_amounts(batch_size);
|
||||||
|
for (i = 0; i < batch_size; ++i)
|
||||||
|
batch_amounts[i] = amounts[i + amounts_proved];
|
||||||
|
rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts));
|
||||||
|
#ifdef DBG
|
||||||
|
CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
|
||||||
|
#endif
|
||||||
|
for (i = 0; i < batch_size; ++i)
|
||||||
|
{
|
||||||
|
rv.outPk[i + amounts_proved].mask = C[i];
|
||||||
|
outSk[i + amounts_proved].mask = masks[i];
|
||||||
|
}
|
||||||
|
amounts_proved += batch_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < outSk.size(); ++i)
|
||||||
|
{
|
||||||
//mask amount and mask
|
//mask amount and mask
|
||||||
rv.ecdhInfo[i].mask = copy(outSk[i].mask);
|
rv.ecdhInfo[i].mask = copy(outSk[i].mask);
|
||||||
rv.ecdhInfo[i].amount = d2h(amounts[i]);
|
rv.ecdhInfo[i].amount = d2h(amounts[i]);
|
||||||
|
@ -725,12 +737,13 @@ namespace rct {
|
||||||
ctkeyM mixRing;
|
ctkeyM mixRing;
|
||||||
ctkeyV outSk;
|
ctkeyV outSk;
|
||||||
tie(mixRing, index) = populateFromBlockchain(inPk, mixin);
|
tie(mixRing, index) = populateFromBlockchain(inPk, mixin);
|
||||||
return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, kLRki, msout, index, outSk, false, hwdev);
|
return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, kLRki, msout, index, outSk, RangeProofBorromean, hwdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
//RCT simple
|
//RCT simple
|
||||||
//for post-rct only
|
//for post-rct only
|
||||||
rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, const std::vector<unsigned int> & index, ctkeyV &outSk, bool bulletproof, hw::device &hwdev) {
|
rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, const std::vector<unsigned int> & index, ctkeyV &outSk, RangeProofType range_proof_type, hw::device &hwdev) {
|
||||||
|
const bool bulletproof = range_proof_type != RangeProofBorromean;
|
||||||
CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts");
|
CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts");
|
||||||
CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk");
|
CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk");
|
||||||
CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations");
|
CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations");
|
||||||
|
@ -822,7 +835,7 @@ namespace rct {
|
||||||
mixRing[i].resize(mixin+1);
|
mixRing[i].resize(mixin+1);
|
||||||
index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin);
|
index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin);
|
||||||
}
|
}
|
||||||
return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, kLRki, msout, index, outSk, false, hwdev);
|
return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, kLRki, msout, index, outSk, RangeProofBorromean, hwdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
//RingCT protocol
|
//RingCT protocol
|
||||||
|
@ -838,10 +851,11 @@ namespace rct {
|
||||||
bool verRct(const rctSig & rv, bool semantics) {
|
bool verRct(const rctSig & rv, bool semantics) {
|
||||||
PERF_TIMER(verRct);
|
PERF_TIMER(verRct);
|
||||||
CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "verRct called on non-full rctSig");
|
CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "verRct called on non-full rctSig");
|
||||||
|
const bool bulletproof = is_rct_bulletproof(rv.type);
|
||||||
if (semantics)
|
if (semantics)
|
||||||
{
|
{
|
||||||
if (rv.type == RCTTypeFullBulletproof)
|
if (bulletproof)
|
||||||
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.bulletproofs.size(), false, "Mismatched sizes of outPk and rv.p.bulletproofs");
|
CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs");
|
||||||
else
|
else
|
||||||
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs");
|
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs");
|
||||||
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
|
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
|
||||||
|
@ -858,21 +872,19 @@ namespace rct {
|
||||||
if (semantics) {
|
if (semantics) {
|
||||||
tools::threadpool& tpool = tools::threadpool::getInstance();
|
tools::threadpool& tpool = tools::threadpool::getInstance();
|
||||||
tools::threadpool::waiter waiter;
|
tools::threadpool::waiter waiter;
|
||||||
std::deque<bool> results(rv.outPk.size(), false);
|
std::deque<bool> results(bulletproof ? rv.p.bulletproofs.size() : rv.outPk.size(), false);
|
||||||
DP("range proofs verified?");
|
DP("range proofs verified?");
|
||||||
for (size_t i = 0; i < rv.outPk.size(); i++) {
|
if (bulletproof)
|
||||||
tpool.submit(&waiter, [&, i] {
|
for (size_t i = 0; i < rv.p.bulletproofs.size(); i++)
|
||||||
if (rv.p.rangeSigs.empty())
|
tpool.submit(&waiter, [&, i] { results[i] = verBulletproof(rv.p.bulletproofs[i]); });
|
||||||
results[i] = verBulletproof(rv.p.bulletproofs[i]);
|
|
||||||
else
|
else
|
||||||
results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
|
for (size_t i = 0; i < rv.outPk.size(); i++)
|
||||||
}, true);
|
tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); });
|
||||||
}
|
|
||||||
waiter.wait(&tpool);
|
waiter.wait(&tpool);
|
||||||
|
|
||||||
for (size_t i = 0; i < rv.outPk.size(); ++i) {
|
for (size_t i = 0; i < results.size(); ++i) {
|
||||||
if (!results[i]) {
|
if (!results[i]) {
|
||||||
LOG_PRINT_L1("Range proof verified failed for output " << i);
|
LOG_PRINT_L1("Range proof verified failed for proof " << i);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -912,11 +924,12 @@ namespace rct {
|
||||||
PERF_TIMER(verRctSimple);
|
PERF_TIMER(verRctSimple);
|
||||||
|
|
||||||
CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctSimple called on non simple rctSig");
|
CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctSimple called on non simple rctSig");
|
||||||
|
const bool bulletproof = is_rct_bulletproof(rv.type);
|
||||||
if (semantics)
|
if (semantics)
|
||||||
{
|
{
|
||||||
if (rv.type == RCTTypeSimpleBulletproof)
|
if (bulletproof)
|
||||||
{
|
{
|
||||||
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.bulletproofs.size(), false, "Mismatched sizes of outPk and rv.p.bulletproofs");
|
CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs");
|
||||||
CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.MGs");
|
CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.MGs");
|
||||||
CHECK_AND_ASSERT_MES(rv.pseudoOuts.empty(), false, "rv.pseudoOuts is not empty");
|
CHECK_AND_ASSERT_MES(rv.pseudoOuts.empty(), false, "rv.pseudoOuts is not empty");
|
||||||
}
|
}
|
||||||
|
@ -931,7 +944,7 @@ namespace rct {
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// semantics check is early, and mixRing/MGs aren't resolved yet
|
// semantics check is early, and mixRing/MGs aren't resolved yet
|
||||||
if (rv.type == RCTTypeSimpleBulletproof)
|
if (bulletproof)
|
||||||
CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.p.pseudoOuts and mixRing");
|
CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.p.pseudoOuts and mixRing");
|
||||||
else
|
else
|
||||||
CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing");
|
CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing");
|
||||||
|
@ -943,7 +956,7 @@ namespace rct {
|
||||||
tools::threadpool& tpool = tools::threadpool::getInstance();
|
tools::threadpool& tpool = tools::threadpool::getInstance();
|
||||||
tools::threadpool::waiter waiter;
|
tools::threadpool::waiter waiter;
|
||||||
|
|
||||||
const keyV &pseudoOuts = is_bulletproof(rv.type) ? rv.p.pseudoOuts : rv.pseudoOuts;
|
const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
|
||||||
|
|
||||||
if (semantics) {
|
if (semantics) {
|
||||||
key sumOutpks = identity();
|
key sumOutpks = identity();
|
||||||
|
@ -967,20 +980,18 @@ namespace rct {
|
||||||
}
|
}
|
||||||
|
|
||||||
results.clear();
|
results.clear();
|
||||||
results.resize(rv.outPk.size());
|
results.resize(bulletproof ? rv.p.bulletproofs.size() : rv.outPk.size());
|
||||||
for (size_t i = 0; i < rv.outPk.size(); i++) {
|
if (bulletproof)
|
||||||
tpool.submit(&waiter, [&, i] {
|
for (size_t i = 0; i < rv.p.bulletproofs.size(); i++)
|
||||||
if (rv.p.rangeSigs.empty())
|
tpool.submit(&waiter, [&, i] { results[i] = verBulletproof(rv.p.bulletproofs[i]); });
|
||||||
results[i] = verBulletproof(rv.p.bulletproofs[i]);
|
|
||||||
else
|
else
|
||||||
results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
|
for (size_t i = 0; i < rv.p.rangeSigs.size(); i++)
|
||||||
}, true);
|
tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); });
|
||||||
}
|
|
||||||
waiter.wait(&tpool);
|
waiter.wait(&tpool);
|
||||||
|
|
||||||
for (size_t i = 0; i < results.size(); ++i) {
|
for (size_t i = 0; i < results.size(); ++i) {
|
||||||
if (!results[i]) {
|
if (!results[i]) {
|
||||||
LOG_PRINT_L1("Range proof verified failed for output " << i);
|
LOG_PRINT_L1("Range proof verified failed for proof " << i);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,10 +119,10 @@ namespace rct {
|
||||||
//decodeRct: (c.f. https://eprint.iacr.org/2015/1098 section 5.1.1)
|
//decodeRct: (c.f. https://eprint.iacr.org/2015/1098 section 5.1.1)
|
||||||
// uses the attached ecdh info to find the amounts represented by each output commitment
|
// uses the attached ecdh info to find the amounts represented by each output commitment
|
||||||
// must know the destination private key to find the correct amount, else will return a random number
|
// must know the destination private key to find the correct amount, else will return a random number
|
||||||
rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, bool bulletproof, hw::device &hwdev);
|
rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, RangeProofType range_proof_type, hw::device &hwdev);
|
||||||
rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, const int mixin, hw::device &hwdev);
|
rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, const int mixin, hw::device &hwdev);
|
||||||
rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, xmr_amount txnFee, unsigned int mixin, hw::device &hwdev);
|
rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, xmr_amount txnFee, unsigned int mixin, hw::device &hwdev);
|
||||||
rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, const std::vector<unsigned int> & index, ctkeyV &outSk, bool bulletproof, hw::device &hwdev);
|
rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, const std::vector<unsigned int> & index, ctkeyV &outSk, RangeProofType range_proof_type, hw::device &hwdev);
|
||||||
bool verRct(const rctSig & rv, bool semantics);
|
bool verRct(const rctSig & rv, bool semantics);
|
||||||
static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); }
|
static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); }
|
||||||
bool verRctSimple(const rctSig & rv, bool semantics);
|
bool verRctSimple(const rctSig & rv, bool semantics);
|
||||||
|
|
|
@ -209,4 +209,36 @@ namespace rct {
|
||||||
return vali;
|
return vali;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_rct_simple(int type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case RCTTypeSimple:
|
||||||
|
case RCTTypeSimpleBulletproof:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_rct_bulletproof(int type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case RCTTypeSimpleBulletproof:
|
||||||
|
case RCTTypeFullBulletproof:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t n_bulletproof_amounts(const std::vector<Bulletproof> &proofs)
|
||||||
|
{
|
||||||
|
size_t n = 0;
|
||||||
|
for (const Bulletproof &proof: proofs)
|
||||||
|
n += proof.V.size();
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,6 +213,8 @@ namespace rct {
|
||||||
END_SERIALIZE()
|
END_SERIALIZE()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
size_t n_bulletproof_amounts(const std::vector<Bulletproof> &proofs);
|
||||||
|
|
||||||
//A container to hold all signatures necessary for RingCT
|
//A container to hold all signatures necessary for RingCT
|
||||||
// rangeSigs holds all the rangeproof data of a transaction
|
// rangeSigs holds all the rangeproof data of a transaction
|
||||||
// MG holds the MLSAG signature of a transaction
|
// MG holds the MLSAG signature of a transaction
|
||||||
|
@ -227,6 +229,7 @@ namespace rct {
|
||||||
RCTTypeFullBulletproof = 3,
|
RCTTypeFullBulletproof = 3,
|
||||||
RCTTypeSimpleBulletproof = 4,
|
RCTTypeSimpleBulletproof = 4,
|
||||||
};
|
};
|
||||||
|
enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof };
|
||||||
struct rctSigBase {
|
struct rctSigBase {
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
key message;
|
key message;
|
||||||
|
@ -310,15 +313,19 @@ namespace rct {
|
||||||
{
|
{
|
||||||
ar.tag("bp");
|
ar.tag("bp");
|
||||||
ar.begin_array();
|
ar.begin_array();
|
||||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, bulletproofs);
|
uint32_t nbp = bulletproofs.size();
|
||||||
if (bulletproofs.size() != outputs)
|
FIELD(nbp)
|
||||||
|
PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs);
|
||||||
|
if (bulletproofs.size() > outputs)
|
||||||
return false;
|
return false;
|
||||||
for (size_t i = 0; i < outputs; ++i)
|
for (size_t i = 0; i < nbp; ++i)
|
||||||
{
|
{
|
||||||
FIELDS(bulletproofs[i])
|
FIELDS(bulletproofs[i])
|
||||||
if (outputs - i > 1)
|
if (nbp - i > 1)
|
||||||
ar.delimit_array();
|
ar.delimit_array();
|
||||||
}
|
}
|
||||||
|
if (n_bulletproof_amounts(bulletproofs) != outputs)
|
||||||
|
return false;
|
||||||
ar.end_array();
|
ar.end_array();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -519,6 +526,9 @@ namespace rct {
|
||||||
//int[64] to uint long long
|
//int[64] to uint long long
|
||||||
xmr_amount b2d(bits amountb);
|
xmr_amount b2d(bits amountb);
|
||||||
|
|
||||||
|
bool is_rct_simple(int type);
|
||||||
|
bool is_rct_bulletproof(int type);
|
||||||
|
|
||||||
static inline const rct::key &pk2rct(const crypto::public_key &pk) { return (const rct::key&)pk; }
|
static inline const rct::key &pk2rct(const crypto::public_key &pk) { return (const rct::key&)pk; }
|
||||||
static inline const rct::key &sk2rct(const crypto::secret_key &sk) { return (const rct::key&)sk; }
|
static inline const rct::key &sk2rct(const crypto::secret_key &sk) { return (const rct::key&)sk; }
|
||||||
static inline const rct::key &ki2rct(const crypto::key_image &ki) { return (const rct::key&)ki; }
|
static inline const rct::key &ki2rct(const crypto::key_image &ki) { return (const rct::key&)ki; }
|
||||||
|
|
|
@ -5268,10 +5268,18 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
|
||||||
LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size());
|
LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size());
|
||||||
signed_txes.ptx.push_back(pending_tx());
|
signed_txes.ptx.push_back(pending_tx());
|
||||||
tools::wallet2::pending_tx &ptx = signed_txes.ptx.back();
|
tools::wallet2::pending_tx &ptx = signed_txes.ptx.back();
|
||||||
|
rct::RangeProofType range_proof_type = rct::RangeProofBorromean;
|
||||||
|
if (sd.use_bulletproofs)
|
||||||
|
{
|
||||||
|
range_proof_type = rct::RangeProofBulletproof;
|
||||||
|
for (const rct::Bulletproof &proof: ptx.tx.rct_signatures.p.bulletproofs)
|
||||||
|
if (proof.V.size() > 1)
|
||||||
|
range_proof_type = rct::RangeProofMultiOutputBulletproof;
|
||||||
|
}
|
||||||
crypto::secret_key tx_key;
|
crypto::secret_key tx_key;
|
||||||
std::vector<crypto::secret_key> additional_tx_keys;
|
std::vector<crypto::secret_key> additional_tx_keys;
|
||||||
rct::multisig_out msout;
|
rct::multisig_out msout;
|
||||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, sd.use_bulletproofs, m_multisig ? &msout : NULL);
|
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, range_proof_type, m_multisig ? &msout : NULL);
|
||||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
|
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
|
||||||
// we don't test tx size, because we don't know the current limit, due to not having a blockchain,
|
// we don't test tx size, because we don't know the current limit, due to not having a blockchain,
|
||||||
// and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway,
|
// and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway,
|
||||||
|
@ -5684,7 +5692,15 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
|
||||||
cryptonote::transaction tx;
|
cryptonote::transaction tx;
|
||||||
rct::multisig_out msout = ptx.multisig_sigs.front().msout;
|
rct::multisig_out msout = ptx.multisig_sigs.front().msout;
|
||||||
auto sources = sd.sources;
|
auto sources = sd.sources;
|
||||||
bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, sd.use_bulletproofs, &msout, false);
|
rct::RangeProofType range_proof_type = rct::RangeProofBorromean;
|
||||||
|
if (sd.use_bulletproofs)
|
||||||
|
{
|
||||||
|
range_proof_type = rct::RangeProofBulletproof;
|
||||||
|
for (const rct::Bulletproof &proof: ptx.tx.rct_signatures.p.bulletproofs)
|
||||||
|
if (proof.V.size() > 1)
|
||||||
|
range_proof_type = rct::RangeProofMultiOutputBulletproof;
|
||||||
|
}
|
||||||
|
bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, range_proof_type, &msout, false);
|
||||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
|
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
|
||||||
|
|
||||||
THROW_WALLET_EXCEPTION_IF(get_transaction_prefix_hash (tx) != get_transaction_prefix_hash(ptx.tx),
|
THROW_WALLET_EXCEPTION_IF(get_transaction_prefix_hash (tx) != get_transaction_prefix_hash(ptx.tx),
|
||||||
|
@ -7054,7 +7070,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
|
||||||
std::vector<crypto::secret_key> additional_tx_keys;
|
std::vector<crypto::secret_key> additional_tx_keys;
|
||||||
rct::multisig_out msout;
|
rct::multisig_out msout;
|
||||||
LOG_PRINT_L2("constructing tx");
|
LOG_PRINT_L2("constructing tx");
|
||||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, false, m_multisig ? &msout : NULL);
|
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBulletproof, m_multisig ? &msout : NULL);
|
||||||
LOG_PRINT_L2("constructed tx, r="<<r);
|
LOG_PRINT_L2("constructed tx, r="<<r);
|
||||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
|
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
|
||||||
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
|
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
|
||||||
|
@ -7103,7 +7119,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
|
||||||
|
|
||||||
void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
|
void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
|
||||||
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
|
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
|
||||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, bool bulletproof)
|
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, rct::RangeProofType range_proof_type)
|
||||||
{
|
{
|
||||||
using namespace cryptonote;
|
using namespace cryptonote;
|
||||||
// throw if attempting a transaction with no destinations
|
// throw if attempting a transaction with no destinations
|
||||||
|
@ -7259,7 +7275,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
|
||||||
rct::multisig_out msout;
|
rct::multisig_out msout;
|
||||||
LOG_PRINT_L2("constructing tx");
|
LOG_PRINT_L2("constructing tx");
|
||||||
auto sources_copy = sources;
|
auto sources_copy = sources;
|
||||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, bulletproof, m_multisig ? &msout : NULL);
|
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, range_proof_type, m_multisig ? &msout : NULL);
|
||||||
LOG_PRINT_L2("constructed tx, r="<<r);
|
LOG_PRINT_L2("constructed tx, r="<<r);
|
||||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype);
|
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype);
|
||||||
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
|
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
|
||||||
|
@ -7304,7 +7320,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
|
||||||
LOG_PRINT_L2("Creating supplementary multisig transaction");
|
LOG_PRINT_L2("Creating supplementary multisig transaction");
|
||||||
cryptonote::transaction ms_tx;
|
cryptonote::transaction ms_tx;
|
||||||
auto sources_copy_copy = sources_copy;
|
auto sources_copy_copy = sources_copy;
|
||||||
bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources_copy_copy, splitted_dsts, change_dts.addr, extra, ms_tx, unlock_time,tx_key, additional_tx_keys, true, bulletproof, &msout, false);
|
bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources_copy_copy, splitted_dsts, change_dts.addr, extra, ms_tx, unlock_time,tx_key, additional_tx_keys, true, range_proof_type, &msout, false);
|
||||||
LOG_PRINT_L2("constructed tx, r="<<r);
|
LOG_PRINT_L2("constructed tx, r="<<r);
|
||||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
|
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
|
||||||
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
|
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
|
||||||
|
@ -8010,6 +8026,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||||
uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit();
|
uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit();
|
||||||
const bool use_rct = use_fork_rules(4, 0);
|
const bool use_rct = use_fork_rules(4, 0);
|
||||||
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
|
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
|
||||||
|
const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofMultiOutputBulletproof : rct::RangeProofBorromean;
|
||||||
|
|
||||||
const uint64_t fee_per_kb = get_per_kb_fee();
|
const uint64_t fee_per_kb = get_per_kb_fee();
|
||||||
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
|
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
|
||||||
|
@ -8326,7 +8343,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||||
tx.selected_transfers.size() << " inputs");
|
tx.selected_transfers.size() << " inputs");
|
||||||
if (use_rct)
|
if (use_rct)
|
||||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||||
test_tx, test_ptx, bulletproof);
|
test_tx, test_ptx, range_proof_type);
|
||||||
else
|
else
|
||||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
||||||
|
@ -8369,7 +8386,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||||
while (needed_fee > test_ptx.fee) {
|
while (needed_fee > test_ptx.fee) {
|
||||||
if (use_rct)
|
if (use_rct)
|
||||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||||
test_tx, test_ptx, bulletproof);
|
test_tx, test_ptx, range_proof_type);
|
||||||
else
|
else
|
||||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
||||||
|
@ -8442,7 +8459,7 @@ skip_tx:
|
||||||
extra, /* const std::vector<uint8_t>& extra, */
|
extra, /* const std::vector<uint8_t>& extra, */
|
||||||
test_tx, /* OUT cryptonote::transaction& tx, */
|
test_tx, /* OUT cryptonote::transaction& tx, */
|
||||||
test_ptx, /* OUT cryptonote::transaction& tx, */
|
test_ptx, /* OUT cryptonote::transaction& tx, */
|
||||||
bulletproof);
|
range_proof_type);
|
||||||
} else {
|
} else {
|
||||||
transfer_selected(tx.dsts,
|
transfer_selected(tx.dsts,
|
||||||
tx.selected_transfers,
|
tx.selected_transfers,
|
||||||
|
@ -8581,6 +8598,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||||
|
|
||||||
const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
|
const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
|
||||||
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
|
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
|
||||||
|
const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofBulletproof : rct::RangeProofBorromean;
|
||||||
const uint64_t fee_per_kb = get_per_kb_fee();
|
const uint64_t fee_per_kb = get_per_kb_fee();
|
||||||
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
|
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
|
||||||
|
|
||||||
|
@ -8636,7 +8654,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||||
tx.selected_transfers.size() << " outputs");
|
tx.selected_transfers.size() << " outputs");
|
||||||
if (use_rct)
|
if (use_rct)
|
||||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||||
test_tx, test_ptx, bulletproof);
|
test_tx, test_ptx, range_proof_type);
|
||||||
else
|
else
|
||||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
||||||
|
@ -8653,7 +8671,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||||
tx.dsts[0].amount = available_for_fee - needed_fee;
|
tx.dsts[0].amount = available_for_fee - needed_fee;
|
||||||
if (use_rct)
|
if (use_rct)
|
||||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||||
test_tx, test_ptx, bulletproof);
|
test_tx, test_ptx, range_proof_type);
|
||||||
else
|
else
|
||||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
|
||||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
||||||
|
@ -8692,7 +8710,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||||
pending_tx test_ptx;
|
pending_tx test_ptx;
|
||||||
if (use_rct) {
|
if (use_rct) {
|
||||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
|
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
|
||||||
test_tx, test_ptx, bulletproof);
|
test_tx, test_ptx, range_proof_type);
|
||||||
} else {
|
} else {
|
||||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
|
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
|
||||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
||||||
|
|
|
@ -729,7 +729,7 @@ namespace tools
|
||||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx);
|
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx);
|
||||||
void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
|
void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
|
||||||
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
|
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
|
||||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, bool bulletproof);
|
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, rct::RangeProofType range_proof_type);
|
||||||
|
|
||||||
void commit_tx(pending_tx& ptx_vector);
|
void commit_tx(pending_tx& ptx_vector);
|
||||||
void commit_tx(std::vector<pending_tx>& ptx_vector);
|
void commit_tx(std::vector<pending_tx>& ptx_vector);
|
||||||
|
@ -1968,7 +1968,7 @@ namespace tools
|
||||||
crypto::secret_key tx_key;
|
crypto::secret_key tx_key;
|
||||||
std::vector<crypto::secret_key> additional_tx_keys;
|
std::vector<crypto::secret_key> additional_tx_keys;
|
||||||
rct::multisig_out msout;
|
rct::multisig_out msout;
|
||||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, false, m_multisig ? &msout : NULL);
|
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBorromean, m_multisig ? &msout : NULL);
|
||||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
|
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
|
||||||
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
|
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
|
||||||
|
|
||||||
|
|
|
@ -285,7 +285,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
|
||||||
#endif
|
#endif
|
||||||
std::vector<crypto::secret_key> additional_tx_secret_keys;
|
std::vector<crypto::secret_key> additional_tx_secret_keys;
|
||||||
auto sources_copy = sources;
|
auto sources_copy = sources;
|
||||||
r = construct_tx_and_get_tx_key(miner_account[creator].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), tx, 0, tx_key, additional_tx_secret_keys, true, false, msoutp);
|
r = construct_tx_and_get_tx_key(miner_account[creator].get_keys(), subaddresses, sources, destinations, boost::none, std::vector<uint8_t>(), tx, 0, tx_key, additional_tx_secret_keys, true, rct::RangeProofBorromean, msoutp);
|
||||||
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
|
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
|
||||||
|
|
||||||
#ifndef NO_MULTISIG
|
#ifndef NO_MULTISIG
|
||||||
|
|
|
@ -72,7 +72,7 @@ public:
|
||||||
std::vector<crypto::secret_key> additional_tx_keys;
|
std::vector<crypto::secret_key> additional_tx_keys;
|
||||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
|
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
|
||||||
subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0};
|
subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0};
|
||||||
if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_tx, 0, tx_key, additional_tx_keys, rct, bulletproof))
|
if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_tx, 0, tx_key, additional_tx_keys, rct, bulletproof ? rct::RangeProofMultiOutputBulletproof : rct::RangeProofBorromean))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
get_transaction_prefix_hash(m_tx, m_tx_prefix_hash);
|
get_transaction_prefix_hash(m_tx, m_tx_prefix_hash);
|
||||||
|
|
|
@ -31,7 +31,9 @@
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
#include "ringct/rctOps.h"
|
#include "ringct/rctOps.h"
|
||||||
|
#include "ringct/rctSigs.h"
|
||||||
#include "ringct/bulletproofs.h"
|
#include "ringct/bulletproofs.h"
|
||||||
|
#include "device/device.hpp"
|
||||||
#include "misc_log_ex.h"
|
#include "misc_log_ex.h"
|
||||||
|
|
||||||
TEST(bulletproofs, valid_zero)
|
TEST(bulletproofs, valid_zero)
|
||||||
|
@ -72,6 +74,67 @@ TEST(bulletproofs, valid_multi_random)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(bulletproofs, multi_splitting)
|
||||||
|
{
|
||||||
|
rct::ctkeyV sc, pc;
|
||||||
|
rct::ctkey sctmp, pctmp;
|
||||||
|
|
||||||
|
std::tie(sctmp, pctmp) = rct::ctskpkGen(6000);
|
||||||
|
sc.push_back(sctmp);
|
||||||
|
pc.push_back(pctmp);
|
||||||
|
|
||||||
|
std::tie(sctmp, pctmp) = rct::ctskpkGen(7000);
|
||||||
|
sc.push_back(sctmp);
|
||||||
|
pc.push_back(pctmp);
|
||||||
|
|
||||||
|
const int mixin = 3, max_outputs = 16;
|
||||||
|
|
||||||
|
for (int n_outputs = 1; n_outputs <= max_outputs; ++n_outputs)
|
||||||
|
{
|
||||||
|
std::vector<uint64_t> amounts;
|
||||||
|
rct::keyV amount_keys;
|
||||||
|
rct::keyV destinations;
|
||||||
|
rct::key Sk, Pk;
|
||||||
|
uint64_t available = 6000 + 7000;
|
||||||
|
uint64_t amount;
|
||||||
|
rct::ctkeyM mixRing(mixin+1);
|
||||||
|
|
||||||
|
//add output
|
||||||
|
for (size_t i = 0; i < n_outputs; ++i)
|
||||||
|
{
|
||||||
|
amount = rct::randXmrAmount(available);
|
||||||
|
amounts.push_back(amount);
|
||||||
|
amount_keys.push_back(rct::hash_to_scalar(rct::zero()));
|
||||||
|
rct::skpkGen(Sk, Pk);
|
||||||
|
destinations.push_back(Pk);
|
||||||
|
available -= amount;
|
||||||
|
}
|
||||||
|
if (!amounts.empty())
|
||||||
|
amounts.back() += available;
|
||||||
|
|
||||||
|
for (size_t j = 0; j <= mixin; ++j)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < sc.size(); ++i)
|
||||||
|
{
|
||||||
|
if (j == 1)
|
||||||
|
mixRing[j].push_back(pc[i]);
|
||||||
|
else
|
||||||
|
mixRing[j].push_back({rct::scalarmultBase(rct::skGen()), rct::scalarmultBase(rct::skGen())});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rct::ctkeyV outSk;
|
||||||
|
rct::rctSig s = rct::genRct(rct::zero(), sc, destinations, amounts, mixRing, amount_keys, NULL, NULL, 1, outSk, rct::RangeProofMultiOutputBulletproof, hw::get_device("default"));
|
||||||
|
ASSERT_TRUE(rct::verRct(s));
|
||||||
|
for (size_t i = 0; i < n_outputs; ++i)
|
||||||
|
{
|
||||||
|
rct::key mask;
|
||||||
|
rct::decodeRct(s, amount_keys[i], i, mask, hw::get_device("default"));
|
||||||
|
ASSERT_TRUE(mask == outSk[i].mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST(bulletproofs, invalid_8)
|
TEST(bulletproofs, invalid_8)
|
||||||
{
|
{
|
||||||
|
|
|
@ -75,7 +75,7 @@ namespace
|
||||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
|
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
|
||||||
subaddresses[from.m_account_address.m_spend_public_key] = {0,0};
|
subaddresses[from.m_account_address.m_spend_public_key] = {0,0};
|
||||||
|
|
||||||
if (!cryptonote::construct_tx_and_get_tx_key(from, subaddresses, actual_sources, to, boost::none, {}, tx, 0, tx_key, extra_keys, rct, bulletproof))
|
if (!cryptonote::construct_tx_and_get_tx_key(from, subaddresses, actual_sources, to, boost::none, {}, tx, 0, tx_key, extra_keys, rct, bulletproof ? rct::RangeProofBulletproof : rct::RangeProofBorromean))
|
||||||
throw std::runtime_error{"transaction construction error"};
|
throw std::runtime_error{"transaction construction error"};
|
||||||
|
|
||||||
return tx;
|
return tx;
|
||||||
|
|
Loading…
Reference in New Issue