mirror of
https://github.com/monero-project/monero.git
synced 2025-12-15 23:13:55 -05:00
Integrate CLSAGs into monero
They are allowed from v12, and MLSAGs are rejected from v13.
This commit is contained in:
parent
8cd1d6df8f
commit
82ee01699c
31 changed files with 1083 additions and 195 deletions
|
|
@ -45,7 +45,6 @@
|
|||
#include "ringct/rctTypes.h"
|
||||
#include "ringct/rctOps.h"
|
||||
|
||||
//namespace cryptonote {
|
||||
namespace boost
|
||||
{
|
||||
namespace serialization
|
||||
|
|
@ -244,6 +243,15 @@ namespace boost
|
|||
// a & x.II; // not serialized, we can recover it from the tx vin
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, rct::clsag &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.s;
|
||||
a & x.c1;
|
||||
// a & x.I; // not serialized, we can recover it from the tx vin
|
||||
a & x.D;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, rct::ecdhTuple &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
|
|
@ -264,6 +272,9 @@ namespace boost
|
|||
inline void serialize(Archive &a, rct::multisig_out &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.c;
|
||||
if (ver < 1)
|
||||
return;
|
||||
a & x.mu_p;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
|
|
@ -294,7 +305,7 @@ namespace boost
|
|||
a & x.type;
|
||||
if (x.type == rct::RCTTypeNull)
|
||||
return;
|
||||
if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2)
|
||||
if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG)
|
||||
throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type");
|
||||
// a & x.message; message is not serialized, as it can be reconstructed from the tx data
|
||||
// a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets
|
||||
|
|
@ -312,6 +323,8 @@ namespace boost
|
|||
if (x.rangeSigs.empty())
|
||||
a & x.bulletproofs;
|
||||
a & x.MGs;
|
||||
if (ver >= 1u)
|
||||
a & x.CLSAGs;
|
||||
if (x.rangeSigs.empty())
|
||||
a & x.pseudoOuts;
|
||||
}
|
||||
|
|
@ -322,7 +335,7 @@ namespace boost
|
|||
a & x.type;
|
||||
if (x.type == rct::RCTTypeNull)
|
||||
return;
|
||||
if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2)
|
||||
if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeCLSAG)
|
||||
throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type");
|
||||
// a & x.message; message is not serialized, as it can be reconstructed from the tx data
|
||||
// a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets
|
||||
|
|
@ -336,7 +349,9 @@ namespace boost
|
|||
if (x.p.rangeSigs.empty())
|
||||
a & x.p.bulletproofs;
|
||||
a & x.p.MGs;
|
||||
if (x.type == rct::RCTTypeBulletproof || x.type == rct::RCTTypeBulletproof2)
|
||||
if (ver >= 1u)
|
||||
a & x.p.CLSAGs;
|
||||
if (x.type == rct::RCTTypeBulletproof || x.type == rct::RCTTypeBulletproof2 || x.type == rct::RCTTypeCLSAG)
|
||||
a & x.p.pseudoOuts;
|
||||
}
|
||||
|
||||
|
|
@ -377,4 +392,6 @@ namespace boost
|
|||
}
|
||||
}
|
||||
|
||||
//}
|
||||
BOOST_CLASS_VERSION(rct::rctSigPrunable, 1)
|
||||
BOOST_CLASS_VERSION(rct::rctSig, 1)
|
||||
BOOST_CLASS_VERSION(rct::multisig_out, 1)
|
||||
|
|
|
|||
|
|
@ -436,7 +436,7 @@ namespace cryptonote
|
|||
{
|
||||
CHECK_AND_ASSERT_MES(tx.pruned, std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support non pruned txes");
|
||||
CHECK_AND_ASSERT_MES(tx.version >= 2, std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support v1 txes");
|
||||
CHECK_AND_ASSERT_MES(tx.rct_signatures.type >= rct::RCTTypeBulletproof2,
|
||||
CHECK_AND_ASSERT_MES(tx.rct_signatures.type >= rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG,
|
||||
std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support older range proof types");
|
||||
CHECK_AND_ASSERT_MES(!tx.vin.empty(), std::numeric_limits<uint64_t>::max(), "empty vin");
|
||||
CHECK_AND_ASSERT_MES(tx.vin[0].type() == typeid(cryptonote::txin_to_key), std::numeric_limits<uint64_t>::max(), "empty vin");
|
||||
|
|
@ -458,9 +458,12 @@ namespace cryptonote
|
|||
extra = 32 * (9 + 2 * nrl) + 2;
|
||||
weight += extra;
|
||||
|
||||
// calculate deterministic MLSAG data size
|
||||
// calculate deterministic CLSAG/MLSAG data size
|
||||
const size_t ring_size = boost::get<cryptonote::txin_to_key>(tx.vin[0]).key_offsets.size();
|
||||
extra = tx.vin.size() * (ring_size * (1 + 1) * 32 + 32 /* cc */);
|
||||
if (tx.rct_signatures.type == rct::RCTTypeCLSAG)
|
||||
extra = tx.vin.size() * (ring_size + 2) * 32;
|
||||
else
|
||||
extra = tx.vin.size() * (ring_size * (1 + 1) * 32 + 32 /* cc */);
|
||||
weight += extra;
|
||||
|
||||
// calculate deterministic pseudoOuts size
|
||||
|
|
|
|||
|
|
@ -179,6 +179,7 @@
|
|||
#define HF_VERSION_ENFORCE_MIN_AGE 12
|
||||
#define HF_VERSION_EFFECTIVE_SHORT_TERM_MEDIAN_IN_PENALTY 12
|
||||
#define HF_VERSION_EXACT_COINBASE 13
|
||||
#define HF_VERSION_CLSAG 13
|
||||
|
||||
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
|
||||
|
||||
|
|
|
|||
|
|
@ -3015,6 +3015,30 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
|
|||
}
|
||||
}
|
||||
|
||||
// from v13, allow CLSAGs
|
||||
if (hf_version < HF_VERSION_CLSAG) {
|
||||
if (tx.version >= 2) {
|
||||
if (tx.rct_signatures.type == rct::RCTTypeCLSAG)
|
||||
{
|
||||
MERROR_VER("Ringct type " << (unsigned)rct::RCTTypeCLSAG << " is not allowed before v" << HF_VERSION_CLSAG);
|
||||
tvc.m_invalid_output = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// from v14, allow only CLSAGs
|
||||
if (hf_version > HF_VERSION_CLSAG) {
|
||||
if (tx.version >= 2) {
|
||||
if (tx.rct_signatures.type <= rct::RCTTypeBulletproof2)
|
||||
{
|
||||
MERROR_VER("Ringct type " << (unsigned)tx.rct_signatures.type << " is not allowed from v" << (HF_VERSION_CLSAG + 1));
|
||||
tvc.m_invalid_output = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
|
|
@ -3055,7 +3079,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2)
|
||||
else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2 || rv.type == rct::RCTTypeCLSAG)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys");
|
||||
rv.mixRing.resize(pubkeys.size());
|
||||
|
|
@ -3068,6 +3092,14 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (rv.type == rct::RCTTypeCLSAG)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(rv.p.CLSAGs.size() == tx.vin.size(), false, "Bad CLSAGs size");
|
||||
for (size_t n = 0; n < tx.vin.size(); ++n)
|
||||
{
|
||||
rv.p.CLSAGs[n].I = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + boost::lexical_cast<std::string>(rv.type));
|
||||
|
|
@ -3096,6 +3128,17 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (rv.type == rct::RCTTypeCLSAG)
|
||||
{
|
||||
if (!tx.pruned)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(rv.p.CLSAGs.size() == tx.vin.size(), false, "Bad CLSAGs size");
|
||||
for (size_t n = 0; n < tx.vin.size(); ++n)
|
||||
{
|
||||
rv.p.CLSAGs[n].I = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + boost::lexical_cast<std::string>(rv.type));
|
||||
|
|
@ -3377,6 +3420,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
|
|||
case rct::RCTTypeSimple:
|
||||
case rct::RCTTypeBulletproof:
|
||||
case rct::RCTTypeBulletproof2:
|
||||
case rct::RCTTypeCLSAG:
|
||||
{
|
||||
// check all this, either reconstructed (so should really pass), or not
|
||||
{
|
||||
|
|
@ -3412,14 +3456,20 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
|
|||
}
|
||||
}
|
||||
|
||||
if (rv.p.MGs.size() != tx.vin.size())
|
||||
const size_t n_sigs = rv.type == rct::RCTTypeCLSAG ? rv.p.CLSAGs.size() : rv.p.MGs.size();
|
||||
if (n_sigs != tx.vin.size())
|
||||
{
|
||||
MERROR_VER("Failed to check ringct signatures: mismatched MGs/vin sizes");
|
||||
return false;
|
||||
}
|
||||
for (size_t n = 0; n < tx.vin.size(); ++n)
|
||||
{
|
||||
if (rv.p.MGs[n].II.empty() || memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32))
|
||||
bool error;
|
||||
if (rv.type == rct::RCTTypeCLSAG)
|
||||
error = memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.CLSAGs[n].I, 32);
|
||||
else
|
||||
error = rv.p.MGs[n].II.empty() || memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32);
|
||||
if (error)
|
||||
{
|
||||
MERROR_VER("Failed to check ringct signatures: mismatched key image");
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -928,6 +928,7 @@ namespace cryptonote
|
|||
break;
|
||||
case rct::RCTTypeBulletproof:
|
||||
case rct::RCTTypeBulletproof2:
|
||||
case rct::RCTTypeCLSAG:
|
||||
if (!is_canonical_bulletproof_layout(rv.p.bulletproofs))
|
||||
{
|
||||
MERROR_VER("Bulletproof does not have canonical form");
|
||||
|
|
@ -955,7 +956,7 @@ namespace cryptonote
|
|||
{
|
||||
if (!tx_info[n].result)
|
||||
continue;
|
||||
if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof2)
|
||||
if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof2 && tx_info[n].tx->rct_signatures.type != rct::RCTTypeCLSAG)
|
||||
continue;
|
||||
if (assumed_bad || !rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1857,7 +1857,7 @@ namespace hw {
|
|||
|
||||
// ====== Aout, Bout, AKout, C, v, k ======
|
||||
kv_offset = data_offset;
|
||||
if (type==rct::RCTTypeBulletproof2) {
|
||||
if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG) {
|
||||
C_offset = kv_offset+ (8)*outputs_size;
|
||||
} else {
|
||||
C_offset = kv_offset+ (32+32)*outputs_size;
|
||||
|
|
@ -1874,7 +1874,7 @@ namespace hw {
|
|||
offset = set_command_header(INS_VALIDATE, 0x02, i+1);
|
||||
//options
|
||||
this->buffer_send[offset] = (i==outputs_size-1)? 0x00:0x80 ;
|
||||
this->buffer_send[offset] |= (type==rct::RCTTypeBulletproof2)?0x02:0x00;
|
||||
this->buffer_send[offset] |= (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG)?0x02:0x00;
|
||||
offset += 1;
|
||||
//is_subaddress
|
||||
this->buffer_send[offset] = outKeys.is_subaddress;
|
||||
|
|
@ -1895,7 +1895,7 @@ namespace hw {
|
|||
memmove(this->buffer_send+offset, data+C_offset,32);
|
||||
offset += 32;
|
||||
C_offset += 32;
|
||||
if (type==rct::RCTTypeBulletproof2) {
|
||||
if (type==rct::RCTTypeBulletproof2 || type==rct::RCTTypeCLSAG) {
|
||||
//k
|
||||
memset(this->buffer_send+offset, 0, 32);
|
||||
offset += 32;
|
||||
|
|
|
|||
|
|
@ -309,7 +309,7 @@ namespace tx {
|
|||
throw std::invalid_argument("RV not initialized");
|
||||
}
|
||||
auto tp = m_ct.rv->type;
|
||||
return tp == rct::RCTTypeBulletproof || tp == rct::RCTTypeBulletproof2;
|
||||
return tp == rct::RCTTypeBulletproof || tp == rct::RCTTypeBulletproof2 || tp == rct::RCTTypeCLSAG;
|
||||
}
|
||||
|
||||
bool is_offloading() const {
|
||||
|
|
|
|||
|
|
@ -67,6 +67,9 @@ const hardfork_t mainnet_hard_forks[] = {
|
|||
|
||||
// version 12 starts from block 1978433, which is on or around the 30th of November, 2019. Fork time finalised on 2019-10-18.
|
||||
{ 12, 1978433, 0, 1571419280 },
|
||||
|
||||
{ 13, 2210000, 0, 1598180817 },
|
||||
{ 14, 2210720, 0, 1598180818 },
|
||||
};
|
||||
const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]);
|
||||
const uint64_t mainnet_hard_fork_version_1_till = 1009826;
|
||||
|
|
@ -110,5 +113,7 @@ const hardfork_t stagenet_hard_forks[] = {
|
|||
{ 10, 269000, 0, 1550153694 },
|
||||
{ 11, 269720, 0, 1550225678 },
|
||||
{ 12, 454721, 0, 1571419280 },
|
||||
{ 13, 699045, 0, 1598180817 },
|
||||
{ 14, 699765, 0, 1598180818 },
|
||||
};
|
||||
const size_t num_stagenet_hard_forks = sizeof(stagenet_hard_forks) / sizeof(stagenet_hard_forks[0]);
|
||||
|
|
|
|||
|
|
@ -168,11 +168,14 @@ namespace rct {
|
|||
|
||||
// Generate a CLSAG signature
|
||||
// See paper by Goodell et al. (https://eprint.iacr.org/2019/654)
|
||||
clsag CLSAG_Gen(const key &message, const keyV & P, const key & p, const keyV & C, const key & z, const unsigned int l, const multisig_kLRki *kLRki) {
|
||||
clsag CLSAG_Gen(const key &message, const keyV & P, const key & p, const keyV & C, const key & z, const unsigned int l, const multisig_kLRki *kLRki, key *mscout, key *mspout) {
|
||||
clsag sig;
|
||||
size_t n = P.size(); // ring size
|
||||
CHECK_AND_ASSERT_THROW_MES(n == C.size(), "Signing and commitment key vector sizes must match!");
|
||||
CHECK_AND_ASSERT_THROW_MES(l < n, "Signing index out of range!");
|
||||
CHECK_AND_ASSERT_THROW_MES(scalarmultBase(z) == C[l], "C does not match z!");
|
||||
CHECK_AND_ASSERT_THROW_MES((kLRki && mscout) || (!kLRki && !mscout), "Only one of kLRki/mscout is present");
|
||||
CHECK_AND_ASSERT_THROW_MES((mscout && mspout) || !kLRki, "Multisig pointers are not all present");
|
||||
|
||||
// Key images
|
||||
ge_p3 H_p3;
|
||||
|
|
@ -309,9 +312,18 @@ namespace rct {
|
|||
sc_muladd(s0_add_z_mu_C.bytes,mu_C.bytes,z.bytes,s0_p_mu_P.bytes);
|
||||
sc_mulsub(sig.s[l].bytes,c.bytes,s0_add_z_mu_C.bytes,a.bytes);
|
||||
|
||||
if (mscout)
|
||||
*mscout = c;
|
||||
if (mspout)
|
||||
*mspout = mu_P;
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
clsag CLSAG_Gen(const key &message, const keyV & P, const key & p, const keyV & C, const key & z, const unsigned int l) {
|
||||
return CLSAG_Gen(message, P, p, C, z, l, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
// Verify a CLSAG signature
|
||||
// See paper by Goodell et al. (https://eprint.iacr.org/2019/654)
|
||||
bool CLSAG_Ver(const key &message, const keyV & P, const keyV & C, const clsag & sig)
|
||||
|
|
@ -665,7 +677,7 @@ namespace rct {
|
|||
hashes.push_back(hash2rct(h));
|
||||
|
||||
keyV kv;
|
||||
if (rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2)
|
||||
if (rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG)
|
||||
{
|
||||
kv.reserve((6*2+9) * rv.p.bulletproofs.size());
|
||||
for (const auto &p: rv.p.bulletproofs)
|
||||
|
|
@ -793,6 +805,35 @@ namespace rct {
|
|||
return result;
|
||||
}
|
||||
|
||||
clsag proveRctCLSAGSimple(const key &message, const ctkeyV &pubs, const ctkey &inSk, const key &a, const key &Cout, const multisig_kLRki *kLRki, key *mscout, key *mspout, unsigned int index, hw::device &hwdev) {
|
||||
//setup vars
|
||||
size_t rows = 1;
|
||||
size_t cols = pubs.size();
|
||||
CHECK_AND_ASSERT_THROW_MES(cols >= 1, "Empty pubs");
|
||||
CHECK_AND_ASSERT_THROW_MES((kLRki && mscout) || (!kLRki && !mscout), "Only one of kLRki/mscout is present");
|
||||
keyV tmp(rows + 1);
|
||||
keyV sk(rows + 1);
|
||||
size_t i;
|
||||
keyM M(cols, tmp);
|
||||
|
||||
keyV P, C;
|
||||
P.reserve(pubs.size());
|
||||
C.reserve(pubs.size());
|
||||
for (const ctkey &k: pubs)
|
||||
{
|
||||
P.push_back(k.dest);
|
||||
rct::key tmp;
|
||||
subKeys(tmp, k.mask, Cout);
|
||||
C.push_back(tmp);
|
||||
}
|
||||
|
||||
sk[0] = copy(inSk.dest);
|
||||
sc_sub(sk[1].bytes, inSk.mask.bytes, a.bytes);
|
||||
clsag result = CLSAG_Gen(message, P, sk[0], C, sk[1], index, kLRki, mscout, mspout);
|
||||
memwipe(sk.data(), sk.size() * sizeof(key));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//Ring-ct MG sigs
|
||||
//Prove:
|
||||
|
|
@ -872,6 +913,33 @@ namespace rct {
|
|||
catch (...) { return false; }
|
||||
}
|
||||
|
||||
bool verRctCLSAGSimple(const key &message, const clsag &clsag, const ctkeyV & pubs, const key & C) {
|
||||
try
|
||||
{
|
||||
PERF_TIMER(verRctCLSAGSimple);
|
||||
//setup vars
|
||||
const size_t cols = pubs.size();
|
||||
CHECK_AND_ASSERT_MES(cols >= 1, false, "Empty pubs");
|
||||
keyV Pi(cols), Ci(cols);
|
||||
ge_p3 Cp3;
|
||||
CHECK_AND_ASSERT_MES_L1(ge_frombytes_vartime(&Cp3, C.bytes) == 0, false, "point conv failed");
|
||||
ge_cached Ccached;
|
||||
ge_p3_to_cached(&Ccached, &Cp3);
|
||||
ge_p1p1 p1;
|
||||
//create the matrix to mg sig
|
||||
for (size_t i = 0; i < cols; i++) {
|
||||
Pi[i] = pubs[i].dest;
|
||||
ge_p3 p3;
|
||||
CHECK_AND_ASSERT_MES_L1(ge_frombytes_vartime(&p3, pubs[i].mask.bytes) == 0, false, "point conv failed");
|
||||
ge_sub(&p1, &p3, &Ccached);
|
||||
ge_p1p1_to_p3(&p3, &p1);
|
||||
ge_p3_tobytes(Ci[i].bytes, &p3);
|
||||
}
|
||||
return CLSAG_Ver(message, Pi, Ci, clsag);
|
||||
}
|
||||
catch (...) { return false; }
|
||||
}
|
||||
|
||||
|
||||
//These functions get keys from blockchain
|
||||
//replace these when connecting blockchain
|
||||
|
|
@ -964,7 +1032,7 @@ namespace rct {
|
|||
//mask amount and mask
|
||||
rv.ecdhInfo[i].mask = copy(outSk[i].mask);
|
||||
rv.ecdhInfo[i].amount = d2h(amounts[i]);
|
||||
hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2);
|
||||
hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG);
|
||||
}
|
||||
|
||||
//set txn fee
|
||||
|
|
@ -1012,7 +1080,27 @@ namespace rct {
|
|||
}
|
||||
|
||||
rctSig rv;
|
||||
rv.type = bulletproof ? (rct_config.bp_version == 0 || rct_config.bp_version >= 2 ? RCTTypeBulletproof2 : RCTTypeBulletproof) : RCTTypeSimple;
|
||||
if (bulletproof)
|
||||
{
|
||||
switch (rct_config.bp_version)
|
||||
{
|
||||
case 0:
|
||||
case 3:
|
||||
rv.type = RCTTypeCLSAG;
|
||||
break;
|
||||
case 2:
|
||||
rv.type = RCTTypeBulletproof2;
|
||||
break;
|
||||
case 1:
|
||||
rv.type = RCTTypeBulletproof;
|
||||
break;
|
||||
default:
|
||||
ASSERT_MES_AND_THROW("Unsupported BP version: " << rct_config.bp_version);
|
||||
}
|
||||
}
|
||||
else
|
||||
rv.type = RCTTypeSimple;
|
||||
|
||||
rv.message = message;
|
||||
rv.outPk.resize(destinations.size());
|
||||
if (!bulletproof)
|
||||
|
|
@ -1102,7 +1190,7 @@ namespace rct {
|
|||
//mask amount and mask
|
||||
rv.ecdhInfo[i].mask = copy(outSk[i].mask);
|
||||
rv.ecdhInfo[i].amount = d2h(outamounts[i]);
|
||||
hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2);
|
||||
hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG);
|
||||
}
|
||||
|
||||
//set txn fee
|
||||
|
|
@ -1112,7 +1200,10 @@ namespace rct {
|
|||
rv.mixRing = mixRing;
|
||||
keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
|
||||
pseudoOuts.resize(inamounts.size());
|
||||
rv.p.MGs.resize(inamounts.size());
|
||||
if (rv.type == RCTTypeCLSAG)
|
||||
rv.p.CLSAGs.resize(inamounts.size());
|
||||
else
|
||||
rv.p.MGs.resize(inamounts.size());
|
||||
key sumpouts = zero(); //sum pseudoOut masks
|
||||
keyV a(inamounts.size());
|
||||
for (i = 0 ; i < inamounts.size() - 1; i++) {
|
||||
|
|
@ -1126,9 +1217,20 @@ namespace rct {
|
|||
|
||||
key full_message = get_pre_mlsag_hash(rv,hwdev);
|
||||
if (msout)
|
||||
msout->c.resize(inamounts.size());
|
||||
for (i = 0 ; i < inamounts.size(); i++) {
|
||||
rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, index[i], hwdev);
|
||||
{
|
||||
msout->c.resize(inamounts.size());
|
||||
msout->mu_p.resize(rv.type == RCTTypeCLSAG ? inamounts.size() : 0);
|
||||
}
|
||||
for (i = 0 ; i < inamounts.size(); i++)
|
||||
{
|
||||
if (rv.type == RCTTypeCLSAG)
|
||||
{
|
||||
rv.p.CLSAGs[i] = proveRctCLSAGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, msout ? &msout->mu_p[i] : NULL, index[i], hwdev);
|
||||
}
|
||||
else
|
||||
{
|
||||
rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, index[i], hwdev);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
|
@ -1233,13 +1335,22 @@ namespace rct {
|
|||
{
|
||||
CHECK_AND_ASSERT_MES(rvp, false, "rctSig pointer is NULL");
|
||||
const rctSig &rv = *rvp;
|
||||
CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2,
|
||||
CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG,
|
||||
false, "verRctSemanticsSimple called on non simple rctSig");
|
||||
const bool bulletproof = is_rct_bulletproof(rv.type);
|
||||
if (bulletproof)
|
||||
{
|
||||
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");
|
||||
if (rv.type == RCTTypeCLSAG)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(rv.p.MGs.empty(), false, "MGs are not empty for CLSAG");
|
||||
CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.CLSAGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.CLSAGs");
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(rv.p.CLSAGs.empty(), false, "CLSAGs are not empty for MLSAG");
|
||||
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");
|
||||
}
|
||||
else
|
||||
|
|
@ -1333,7 +1444,7 @@ namespace rct {
|
|||
{
|
||||
PERF_TIMER(verRctNonSemanticsSimple);
|
||||
|
||||
CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2,
|
||||
CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG,
|
||||
false, "verRctNonSemanticsSimple called on non simple rctSig");
|
||||
const bool bulletproof = is_rct_bulletproof(rv.type);
|
||||
// semantics check is early, and mixRing/MGs aren't resolved yet
|
||||
|
|
@ -1356,14 +1467,19 @@ namespace rct {
|
|||
results.resize(rv.mixRing.size());
|
||||
for (size_t i = 0 ; i < rv.mixRing.size() ; i++) {
|
||||
tpool.submit(&waiter, [&, i] {
|
||||
results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]);
|
||||
if (rv.type == RCTTypeCLSAG)
|
||||
{
|
||||
results[i] = verRctCLSAGSimple(message, rv.p.CLSAGs[i], rv.mixRing[i], pseudoOuts[i]);
|
||||
}
|
||||
else
|
||||
results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]);
|
||||
});
|
||||
}
|
||||
waiter.wait(&tpool);
|
||||
|
||||
for (size_t i = 0; i < results.size(); ++i) {
|
||||
if (!results[i]) {
|
||||
LOG_PRINT_L1("verRctMGSimple failed for input " << i);
|
||||
LOG_PRINT_L1("verRctMGSimple/verRctCLSAGSimple failed for input " << i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1400,7 +1516,7 @@ namespace rct {
|
|||
|
||||
//mask amount and mask
|
||||
ecdhTuple ecdh_info = rv.ecdhInfo[i];
|
||||
hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2);
|
||||
hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG);
|
||||
mask = ecdh_info.mask;
|
||||
key amount = ecdh_info.amount;
|
||||
key C = rv.outPk[i].mask;
|
||||
|
|
@ -1424,13 +1540,13 @@ namespace rct {
|
|||
}
|
||||
|
||||
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask, hw::device &hwdev) {
|
||||
CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2, false, "decodeRct called on non simple rctSig");
|
||||
CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG, false, "decodeRct called on non simple rctSig");
|
||||
CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index");
|
||||
CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
|
||||
|
||||
//mask amount and mask
|
||||
ecdhTuple ecdh_info = rv.ecdhInfo[i];
|
||||
hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2);
|
||||
hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG);
|
||||
mask = ecdh_info.mask;
|
||||
key amount = ecdh_info.amount;
|
||||
key C = rv.outPk[i].mask;
|
||||
|
|
@ -1453,12 +1569,13 @@ namespace rct {
|
|||
return decodeRctSimple(rv, sk, i, mask, hwdev);
|
||||
}
|
||||
|
||||
bool signMultisig(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) {
|
||||
bool signMultisigMLSAG(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) {
|
||||
CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2,
|
||||
false, "unsupported rct type");
|
||||
CHECK_AND_ASSERT_MES(indices.size() == k.size(), false, "Mismatched k/indices sizes");
|
||||
CHECK_AND_ASSERT_MES(k.size() == rv.p.MGs.size(), false, "Mismatched k/MGs size");
|
||||
CHECK_AND_ASSERT_MES(k.size() == msout.c.size(), false, "Mismatched k/msout.c size");
|
||||
CHECK_AND_ASSERT_MES(rv.p.CLSAGs.empty(), false, "CLSAGs not empty for MLSAGs");
|
||||
if (rv.type == RCTTypeFull)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "MGs not a single element");
|
||||
|
|
@ -1468,6 +1585,8 @@ namespace rct {
|
|||
CHECK_AND_ASSERT_MES(!rv.p.MGs[n].ss[indices[n]].empty(), false, "empty ss line");
|
||||
}
|
||||
|
||||
// MLSAG: each player contributes a share to the secret-index ss: k - cc*secret_key_share
|
||||
// cc: msout.c[n], secret_key_share: secret_key
|
||||
for (size_t n = 0; n < indices.size(); ++n) {
|
||||
rct::key diff;
|
||||
sc_mulsub(diff.bytes, msout.c[n].bytes, secret_key.bytes, k[n].bytes);
|
||||
|
|
@ -1475,4 +1594,33 @@ namespace rct {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool signMultisigCLSAG(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) {
|
||||
CHECK_AND_ASSERT_MES(rv.type == RCTTypeCLSAG, false, "unsupported rct type");
|
||||
CHECK_AND_ASSERT_MES(indices.size() == k.size(), false, "Mismatched k/indices sizes");
|
||||
CHECK_AND_ASSERT_MES(k.size() == rv.p.CLSAGs.size(), false, "Mismatched k/MGs size");
|
||||
CHECK_AND_ASSERT_MES(k.size() == msout.c.size(), false, "Mismatched k/msout.c size");
|
||||
CHECK_AND_ASSERT_MES(rv.p.MGs.empty(), false, "MGs not empty for CLSAGs");
|
||||
CHECK_AND_ASSERT_MES(msout.c.size() == msout.mu_p.size(), false, "Bad mu_p size");
|
||||
for (size_t n = 0; n < indices.size(); ++n) {
|
||||
CHECK_AND_ASSERT_MES(indices[n] < rv.p.CLSAGs[n].s.size(), false, "Index out of range");
|
||||
}
|
||||
|
||||
// CLSAG: each player contributes a share to the secret-index ss: k - cc*mu_p*secret_key_share
|
||||
// cc: msout.c[n], mu_p, msout.mu_p[n], secret_key_share: secret_key
|
||||
for (size_t n = 0; n < indices.size(); ++n) {
|
||||
rct::key diff, sk;
|
||||
sc_mul(sk.bytes, msout.mu_p[n].bytes, secret_key.bytes);
|
||||
sc_mulsub(diff.bytes, msout.c[n].bytes, sk.bytes, k[n].bytes);
|
||||
sc_add(rv.p.CLSAGs[n].s[indices[n]].bytes, rv.p.CLSAGs[n].s[indices[n]].bytes, diff.bytes);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool signMultisig(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) {
|
||||
if (rv.type == RCTTypeCLSAG)
|
||||
return signMultisigCLSAG(rv, indices, k, msout, secret_key);
|
||||
else
|
||||
return signMultisigMLSAG(rv, indices, k, msout, secret_key);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,9 +77,9 @@ namespace rct {
|
|||
mgSig MLSAG_Gen(const key &message, const keyM & pk, const keyV & xx, const multisig_kLRki *kLRki, key *mscout, const unsigned int index, size_t dsRows, hw::device &hwdev);
|
||||
bool MLSAG_Ver(const key &message, const keyM &pk, const mgSig &sig, size_t dsRows);
|
||||
|
||||
clsag CLSAG_Gen(const key &message, const keyV & P, const key & p, const keyV & C, const key & z, const unsigned int l, const multisig_kLRki *kLRki);
|
||||
clsag CLSAG_Gen(const key &message, const keyV & P, const key & p, const keyV & C, const key & z, const unsigned int l, const multisig_kLRki *kLRki, key *mscout, key *mspout);
|
||||
clsag CLSAG_Gen(const key &message, const keyV & P, const key & p, const keyV & C, const key & z, const unsigned int l);
|
||||
bool CLSAG_Ver(const key &message, const keyV & P, const keyV & C, const clsag & sig);
|
||||
//mgSig MLSAG_Gen_Old(const keyM & pk, const keyV & xx, const int index);
|
||||
|
||||
//proveRange and verRange
|
||||
//proveRange gives C, and mask such that \sumCi = C
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ namespace rct {
|
|||
case RCTTypeSimple:
|
||||
case RCTTypeBulletproof:
|
||||
case RCTTypeBulletproof2:
|
||||
case RCTTypeCLSAG:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
|
@ -207,6 +208,7 @@ namespace rct {
|
|||
{
|
||||
case RCTTypeBulletproof:
|
||||
case RCTTypeBulletproof2:
|
||||
case RCTTypeCLSAG:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -113,9 +113,14 @@ namespace rct {
|
|||
|
||||
struct multisig_out {
|
||||
std::vector<key> c; // for all inputs
|
||||
std::vector<key> mu_p; // for all inputs
|
||||
std::vector<key> c0; // for all inputs
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(c)
|
||||
FIELD(mu_p)
|
||||
if (!mu_p.empty() && mu_p.size() != c.size())
|
||||
return false;
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
|
|
@ -175,6 +180,8 @@ namespace rct {
|
|||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(s)
|
||||
FIELD(c1)
|
||||
// FIELD(I) - not serialized, it can be reconstructed
|
||||
FIELD(D)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
|
|
@ -249,6 +256,7 @@ namespace rct {
|
|||
RCTTypeSimple = 2,
|
||||
RCTTypeBulletproof = 3,
|
||||
RCTTypeBulletproof2 = 4,
|
||||
RCTTypeCLSAG = 5,
|
||||
};
|
||||
enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof };
|
||||
struct RCTConfig {
|
||||
|
|
@ -277,7 +285,7 @@ namespace rct {
|
|||
FIELD(type)
|
||||
if (type == RCTTypeNull)
|
||||
return ar.stream().good();
|
||||
if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2)
|
||||
if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG)
|
||||
return false;
|
||||
VARINT_FIELD(txnFee)
|
||||
// inputs/outputs not saved, only here for serialization help
|
||||
|
|
@ -306,7 +314,7 @@ namespace rct {
|
|||
return false;
|
||||
for (size_t i = 0; i < outputs; ++i)
|
||||
{
|
||||
if (type == RCTTypeBulletproof2)
|
||||
if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG)
|
||||
{
|
||||
ar.begin_object();
|
||||
if (!typename Archive<W>::is_saving())
|
||||
|
|
@ -353,6 +361,7 @@ namespace rct {
|
|||
std::vector<rangeSig> rangeSigs;
|
||||
std::vector<Bulletproof> bulletproofs;
|
||||
std::vector<mgSig> MGs; // simple rct has N, full has 1
|
||||
std::vector<clsag> CLSAGs;
|
||||
keyV pseudoOuts; //C - for simple rct
|
||||
|
||||
// when changing this function, update cryptonote::get_pruned_transaction_weight
|
||||
|
|
@ -361,12 +370,12 @@ namespace rct {
|
|||
{
|
||||
if (type == RCTTypeNull)
|
||||
return ar.stream().good();
|
||||
if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2)
|
||||
if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG)
|
||||
return false;
|
||||
if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2)
|
||||
if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG)
|
||||
{
|
||||
uint32_t nbp = bulletproofs.size();
|
||||
if (type == RCTTypeBulletproof2)
|
||||
if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG)
|
||||
VARINT_FIELD(nbp)
|
||||
else
|
||||
FIELD(nbp)
|
||||
|
|
@ -401,55 +410,98 @@ namespace rct {
|
|||
ar.end_array();
|
||||
}
|
||||
|
||||
ar.tag("MGs");
|
||||
ar.begin_array();
|
||||
// we keep a byte for size of MGs, because we don't know whether this is
|
||||
// a simple or full rct signature, and it's starting to annoy the hell out of me
|
||||
size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2) ? inputs : 1;
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs);
|
||||
if (MGs.size() != mg_elements)
|
||||
return false;
|
||||
for (size_t i = 0; i < mg_elements; ++i)
|
||||
if (type == RCTTypeCLSAG)
|
||||
{
|
||||
// we save the MGs contents directly, because we want it to save its
|
||||
// arrays and matrices without the size prefixes, and the load can't
|
||||
// know what size to expect if it's not in the data
|
||||
ar.begin_object();
|
||||
ar.tag("ss");
|
||||
ar.tag("CLSAGs");
|
||||
ar.begin_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mixin + 1, MGs[i].ss);
|
||||
if (MGs[i].ss.size() != mixin + 1)
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, CLSAGs);
|
||||
if (CLSAGs.size() != inputs)
|
||||
return false;
|
||||
for (size_t j = 0; j < mixin + 1; ++j)
|
||||
for (size_t i = 0; i < inputs; ++i)
|
||||
{
|
||||
// we save the CLSAGs contents directly, because we want it to save its
|
||||
// arrays without the size prefixes, and the load can't know what size
|
||||
// to expect if it's not in the data
|
||||
ar.begin_object();
|
||||
ar.tag("s");
|
||||
ar.begin_array();
|
||||
size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2) ? 1 : inputs) + 1;
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j]);
|
||||
if (MGs[i].ss[j].size() != mg_ss2_elements)
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mixin + 1, CLSAGs[i].s);
|
||||
if (CLSAGs[i].s.size() != mixin + 1)
|
||||
return false;
|
||||
for (size_t k = 0; k < mg_ss2_elements; ++k)
|
||||
for (size_t j = 0; j <= mixin; ++j)
|
||||
{
|
||||
FIELDS(MGs[i].ss[j][k])
|
||||
if (mg_ss2_elements - k > 1)
|
||||
FIELDS(CLSAGs[i].s[j])
|
||||
if (mixin + 1 - j > 1)
|
||||
ar.delimit_array();
|
||||
}
|
||||
ar.end_array();
|
||||
|
||||
if (mixin + 1 - j > 1)
|
||||
ar.delimit_array();
|
||||
ar.tag("c1");
|
||||
FIELDS(CLSAGs[i].c1)
|
||||
|
||||
// CLSAGs[i].I not saved, it can be reconstructed
|
||||
ar.tag("D");
|
||||
FIELDS(CLSAGs[i].D)
|
||||
ar.end_object();
|
||||
|
||||
if (inputs - i > 1)
|
||||
ar.delimit_array();
|
||||
}
|
||||
|
||||
ar.end_array();
|
||||
}
|
||||
else
|
||||
{
|
||||
ar.tag("MGs");
|
||||
ar.begin_array();
|
||||
// we keep a byte for size of MGs, because we don't know whether this is
|
||||
// a simple or full rct signature, and it's starting to annoy the hell out of me
|
||||
size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2) ? inputs : 1;
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs);
|
||||
if (MGs.size() != mg_elements)
|
||||
return false;
|
||||
for (size_t i = 0; i < mg_elements; ++i)
|
||||
{
|
||||
// we save the MGs contents directly, because we want it to save its
|
||||
// arrays and matrices without the size prefixes, and the load can't
|
||||
// know what size to expect if it's not in the data
|
||||
ar.begin_object();
|
||||
ar.tag("ss");
|
||||
ar.begin_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mixin + 1, MGs[i].ss);
|
||||
if (MGs[i].ss.size() != mixin + 1)
|
||||
return false;
|
||||
for (size_t j = 0; j < mixin + 1; ++j)
|
||||
{
|
||||
ar.begin_array();
|
||||
size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2) ? 1 : inputs) + 1;
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j]);
|
||||
if (MGs[i].ss[j].size() != mg_ss2_elements)
|
||||
return false;
|
||||
for (size_t k = 0; k < mg_ss2_elements; ++k)
|
||||
{
|
||||
FIELDS(MGs[i].ss[j][k])
|
||||
if (mg_ss2_elements - k > 1)
|
||||
ar.delimit_array();
|
||||
}
|
||||
ar.end_array();
|
||||
|
||||
if (mixin + 1 - j > 1)
|
||||
ar.delimit_array();
|
||||
}
|
||||
ar.end_array();
|
||||
|
||||
ar.tag("cc");
|
||||
FIELDS(MGs[i].cc)
|
||||
// MGs[i].II not saved, it can be reconstructed
|
||||
ar.end_object();
|
||||
|
||||
if (mg_elements - i > 1)
|
||||
ar.delimit_array();
|
||||
}
|
||||
ar.end_array();
|
||||
|
||||
ar.tag("cc");
|
||||
FIELDS(MGs[i].cc)
|
||||
// MGs[i].II not saved, it can be reconstructed
|
||||
ar.end_object();
|
||||
|
||||
if (mg_elements - i > 1)
|
||||
ar.delimit_array();
|
||||
}
|
||||
ar.end_array();
|
||||
if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2)
|
||||
if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG)
|
||||
{
|
||||
ar.tag("pseudoOuts");
|
||||
ar.begin_array();
|
||||
|
|
@ -479,12 +531,12 @@ namespace rct {
|
|||
|
||||
keyV& get_pseudo_outs()
|
||||
{
|
||||
return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 ? p.pseudoOuts : pseudoOuts;
|
||||
return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG ? p.pseudoOuts : pseudoOuts;
|
||||
}
|
||||
|
||||
keyV const& get_pseudo_outs() const
|
||||
{
|
||||
return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 ? p.pseudoOuts : pseudoOuts;
|
||||
return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG ? p.pseudoOuts : pseudoOuts;
|
||||
}
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
|
|
@ -651,6 +703,7 @@ VARIANT_TAG(debug_archive, rct::rctSig, "rct::rctSig");
|
|||
VARIANT_TAG(debug_archive, rct::Bulletproof, "rct::bulletproof");
|
||||
VARIANT_TAG(debug_archive, rct::multisig_kLRki, "rct::multisig_kLRki");
|
||||
VARIANT_TAG(debug_archive, rct::multisig_out, "rct::multisig_out");
|
||||
VARIANT_TAG(debug_archive, rct::clsag, "rct::clsag");
|
||||
|
||||
VARIANT_TAG(binary_archive, rct::key, 0x90);
|
||||
VARIANT_TAG(binary_archive, rct::key64, 0x91);
|
||||
|
|
@ -667,6 +720,7 @@ VARIANT_TAG(binary_archive, rct::rctSig, 0x9b);
|
|||
VARIANT_TAG(binary_archive, rct::Bulletproof, 0x9c);
|
||||
VARIANT_TAG(binary_archive, rct::multisig_kLRki, 0x9d);
|
||||
VARIANT_TAG(binary_archive, rct::multisig_out, 0x9e);
|
||||
VARIANT_TAG(binary_archive, rct::clsag, 0x9f);
|
||||
|
||||
VARIANT_TAG(json_archive, rct::key, "rct_key");
|
||||
VARIANT_TAG(json_archive, rct::key64, "rct_key64");
|
||||
|
|
@ -683,5 +737,6 @@ VARIANT_TAG(json_archive, rct::rctSig, "rct_rctSig");
|
|||
VARIANT_TAG(json_archive, rct::Bulletproof, "rct_bulletproof");
|
||||
VARIANT_TAG(json_archive, rct::multisig_kLRki, "rct_multisig_kLR");
|
||||
VARIANT_TAG(json_archive, rct::multisig_out, "rct_multisig_out");
|
||||
VARIANT_TAG(json_archive, rct::clsag, "rct_clsag");
|
||||
|
||||
#endif /* RCTTYPES_H */
|
||||
|
|
|
|||
|
|
@ -1306,7 +1306,7 @@ namespace cryptonote
|
|||
case 1: res.pow_algorithm = "CNv1 (Cryptonight variant 1)"; break;
|
||||
case 2: case 3: res.pow_algorithm = "CNv2 (Cryptonight variant 2)"; break;
|
||||
case 4: case 5: res.pow_algorithm = "CNv4 (Cryptonight variant 4)"; break;
|
||||
case 6: res.pow_algorithm = "RandomX"; break;
|
||||
case 6: case 7: res.pow_algorithm = "RandomX"; break;
|
||||
default: res.pow_algorithm = "I'm not sure actually"; break;
|
||||
}
|
||||
if (res.is_background_mining_enabled)
|
||||
|
|
|
|||
|
|
@ -1692,6 +1692,7 @@ uint64_t WalletImpl::estimateTransactionFee(const std::vector<std::pair<std::str
|
|||
destinations.size() + 1,
|
||||
extra_size,
|
||||
m_wallet->use_fork_rules(8, 0),
|
||||
m_wallet->use_fork_rules(HF_VERSION_CLSAG, 0),
|
||||
m_wallet->get_base_fee(),
|
||||
m_wallet->get_fee_multiplier(m_wallet->adjust_priority(static_cast<uint32_t>(priority))),
|
||||
m_wallet->get_fee_quantization_mask());
|
||||
|
|
|
|||
|
|
@ -243,6 +243,22 @@ namespace
|
|||
add_reason(reason, "tx was not relayed");
|
||||
return reason;
|
||||
}
|
||||
|
||||
size_t get_num_outputs(const std::vector<cryptonote::tx_destination_entry> &dsts, const std::vector<tools::wallet2::transfer_details> &transfers, const std::vector<size_t> &selected_transfers)
|
||||
{
|
||||
size_t outputs = dsts.size();
|
||||
uint64_t needed_money = 0;
|
||||
for (const auto& dt: dsts)
|
||||
needed_money += dt.amount;
|
||||
uint64_t found_money = 0;
|
||||
for(size_t idx: selected_transfers)
|
||||
found_money += transfers[idx].amount();
|
||||
if (found_money != needed_money)
|
||||
++outputs; // change
|
||||
if (outputs < 2)
|
||||
++outputs; // extra 0 dummy output
|
||||
return outputs;
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
|
|
@ -795,7 +811,7 @@ void drop_from_short_history(std::list<crypto::hash> &short_chain_history, size_
|
|||
}
|
||||
}
|
||||
|
||||
size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
|
||||
size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag)
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
|
|
@ -829,8 +845,11 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
|
|||
else
|
||||
size += (2*64*32+32+64*32) * n_outputs;
|
||||
|
||||
// MGs
|
||||
size += n_inputs * (64 * (mixin+1) + 32);
|
||||
// MGs/CLSAGs
|
||||
if (clsag)
|
||||
size += n_inputs * (32 * (mixin+1) + 64);
|
||||
else
|
||||
size += n_inputs * (64 * (mixin+1) + 32);
|
||||
|
||||
// mixRing - not serialized, can be reconstructed
|
||||
/* size += 2 * 32 * (mixin+1) * n_inputs; */
|
||||
|
|
@ -848,17 +867,17 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
|
|||
return size;
|
||||
}
|
||||
|
||||
size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
|
||||
size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag)
|
||||
{
|
||||
if (use_rct)
|
||||
return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof);
|
||||
return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
|
||||
else
|
||||
return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size;
|
||||
}
|
||||
|
||||
uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
|
||||
uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag)
|
||||
{
|
||||
size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
|
||||
size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
|
||||
if (use_rct && bulletproof && n_outputs > 2)
|
||||
{
|
||||
const uint64_t bp_base = 368;
|
||||
|
|
@ -879,6 +898,11 @@ uint8_t get_bulletproof_fork()
|
|||
return 8;
|
||||
}
|
||||
|
||||
uint8_t get_clsag_fork()
|
||||
{
|
||||
return HF_VERSION_CLSAG;
|
||||
}
|
||||
|
||||
uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, size_t blob_size, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
|
||||
{
|
||||
if (use_per_byte_fee)
|
||||
|
|
@ -1752,6 +1776,7 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
|
|||
case rct::RCTTypeSimple:
|
||||
case rct::RCTTypeBulletproof:
|
||||
case rct::RCTTypeBulletproof2:
|
||||
case rct::RCTTypeCLSAG:
|
||||
return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev);
|
||||
case rct::RCTTypeFull:
|
||||
return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev);
|
||||
|
|
@ -7354,16 +7379,16 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto
|
|||
return sign_multisig_tx_to_file(exported_txs, filename, txids);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const
|
||||
uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const
|
||||
{
|
||||
if (use_per_byte_fee)
|
||||
{
|
||||
const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
|
||||
const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
|
||||
return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask);
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
|
||||
const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof, clsag);
|
||||
return calculate_fee(base_fee, estimated_tx_size, fee_multiplier);
|
||||
}
|
||||
}
|
||||
|
|
@ -9066,7 +9091,10 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
|
|||
ptx.construction_data.extra = tx.extra;
|
||||
ptx.construction_data.unlock_time = unlock_time;
|
||||
ptx.construction_data.use_rct = true;
|
||||
ptx.construction_data.rct_config = { tx.rct_signatures.p.bulletproofs.empty() ? rct::RangeProofBorromean : rct::RangeProofPaddedBulletproof, use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1};
|
||||
ptx.construction_data.rct_config = {
|
||||
tx.rct_signatures.p.bulletproofs.empty() ? rct::RangeProofBorromean : rct::RangeProofPaddedBulletproof,
|
||||
use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1
|
||||
};
|
||||
ptx.construction_data.dests = dsts;
|
||||
// record which subaddress indices are being used as inputs
|
||||
ptx.construction_data.subaddr_account = subaddr_account;
|
||||
|
|
@ -9752,9 +9780,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
|||
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
|
||||
const bool use_rct = use_fork_rules(4, 0);
|
||||
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
|
||||
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
|
||||
const rct::RCTConfig rct_config {
|
||||
bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean,
|
||||
bulletproof ? (use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0
|
||||
bulletproof ? (use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0
|
||||
};
|
||||
|
||||
const uint64_t base_fee = get_base_fee();
|
||||
|
|
@ -9790,7 +9819,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
|||
// early out if we know we can't make it anyway
|
||||
// we could also check for being within FEE_PER_KB, but if the fee calculation
|
||||
// ever changes, this might be missed, so let this go through
|
||||
const uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof));
|
||||
const uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof, clsag));
|
||||
uint64_t balance_subtotal = 0;
|
||||
uint64_t unlocked_balance_subtotal = 0;
|
||||
for (uint32_t index_minor : subaddr_indices)
|
||||
|
|
@ -9808,8 +9837,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
|||
LOG_PRINT_L2("Candidate subaddress index for spending: " << i);
|
||||
|
||||
// determine threshold for fractional amount
|
||||
const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof);
|
||||
const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof);
|
||||
const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag);
|
||||
const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag);
|
||||
THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
|
||||
const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
|
||||
const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
|
||||
|
|
@ -9906,7 +9935,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
|||
{
|
||||
// this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
|
||||
// will get us a known fee.
|
||||
uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
|
||||
uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, clsag, base_fee, fee_multiplier, fee_quantization_mask);
|
||||
preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee, subaddr_account, subaddr_indices);
|
||||
if (!preferred_inputs.empty())
|
||||
{
|
||||
|
|
@ -10018,7 +10047,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
|||
}
|
||||
else
|
||||
{
|
||||
while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof) < TX_WEIGHT_TARGET(upper_transaction_weight_limit))
|
||||
while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag) < TX_WEIGHT_TARGET(upper_transaction_weight_limit))
|
||||
{
|
||||
// we can fully pay that destination
|
||||
LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
|
||||
|
|
@ -10030,7 +10059,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
|||
++original_output_index;
|
||||
}
|
||||
|
||||
if (available_amount > 0 && !dsts.empty() && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
|
||||
if (available_amount > 0 && !dsts.empty() && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
|
||||
// we can partially fill that destination
|
||||
LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
|
||||
" for " << print_money(available_amount) << "/" << print_money(dsts[0].amount));
|
||||
|
|
@ -10054,7 +10083,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
|||
}
|
||||
else
|
||||
{
|
||||
const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof);
|
||||
const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag);
|
||||
try_tx = dsts.empty() || (estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
|
||||
THROW_WALLET_EXCEPTION_IF(try_tx && tx.dsts.empty(), error::tx_too_big, estimated_rct_tx_weight, upper_transaction_weight_limit);
|
||||
}
|
||||
|
|
@ -10064,7 +10093,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
|||
cryptonote::transaction test_tx;
|
||||
pending_tx test_ptx;
|
||||
|
||||
needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
|
||||
const size_t num_outputs = get_num_outputs(tx.dsts, m_transfers, tx.selected_transfers);
|
||||
needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, base_fee, fee_multiplier, fee_quantization_mask);
|
||||
|
||||
uint64_t inputs = 0, outputs = needed_fee;
|
||||
for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
|
||||
|
|
@ -10313,10 +10343,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
|
|||
// determine threshold for fractional amount
|
||||
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
|
||||
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
|
||||
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
|
||||
const uint64_t base_fee = get_base_fee();
|
||||
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
|
||||
const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof);
|
||||
const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof);
|
||||
const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof, clsag);
|
||||
const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof, clsag);
|
||||
THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
|
||||
const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
|
||||
const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
|
||||
|
|
@ -10422,9 +10453,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
|||
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE);
|
||||
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 clsag = use_fork_rules(get_clsag_fork(), 0);
|
||||
const rct::RCTConfig rct_config {
|
||||
bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean,
|
||||
bulletproof ? (use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0,
|
||||
bulletproof ? (use_fork_rules(HF_VERSION_CLSAG, -10) ? 3 : use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0,
|
||||
};
|
||||
const uint64_t base_fee = get_base_fee();
|
||||
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
|
||||
|
|
@ -10453,7 +10485,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
|||
uint64_t fee_dust_threshold;
|
||||
if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
|
||||
{
|
||||
const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof);
|
||||
const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, clsag);
|
||||
fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_multiplier, fee_quantization_mask);
|
||||
}
|
||||
else
|
||||
|
|
@ -10484,14 +10516,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
|||
// here, check if we need to sent tx and start a new one
|
||||
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
|
||||
<< upper_transaction_weight_limit);
|
||||
const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof);
|
||||
const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof, clsag);
|
||||
bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
|
||||
|
||||
if (try_tx) {
|
||||
cryptonote::transaction test_tx;
|
||||
pending_tx test_ptx;
|
||||
|
||||
needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
|
||||
const size_t num_outputs = get_num_outputs(tx.dsts, m_transfers, tx.selected_transfers);
|
||||
needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, num_outputs, extra.size(), bulletproof, clsag, base_fee, fee_multiplier, fee_quantization_mask);
|
||||
|
||||
// add N - 1 outputs for correct initial fee estimation
|
||||
for (size_t i = 0; i < ((outputs > 1) ? outputs - 1 : outputs); ++i)
|
||||
|
|
@ -11353,7 +11386,7 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
|
|||
crypto::secret_key scalar1;
|
||||
crypto::derivation_to_scalar(found_derivation, n, scalar1);
|
||||
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
|
||||
rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2);
|
||||
rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG);
|
||||
const rct::key C = tx.rct_signatures.outPk[n].mask;
|
||||
rct::key Ctmp;
|
||||
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask");
|
||||
|
|
@ -11997,7 +12030,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
|
|||
crypto::secret_key shared_secret;
|
||||
crypto::derivation_to_scalar(derivation, proof.index_in_tx, shared_secret);
|
||||
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[proof.index_in_tx];
|
||||
rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2);
|
||||
rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG);
|
||||
amount = rct::h2d(ecdh_info.amount);
|
||||
}
|
||||
total += amount;
|
||||
|
|
@ -14036,8 +14069,9 @@ std::pair<size_t, uint64_t> wallet2::estimate_tx_size_and_weight(bool use_rct, i
|
|||
n_outputs = 2; // extra dummy output
|
||||
|
||||
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
|
||||
size_t size = estimate_tx_size(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof);
|
||||
uint64_t weight = estimate_tx_weight(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof);
|
||||
const bool clsag = use_fork_rules(get_clsag_fork(), 0);
|
||||
size_t size = estimate_tx_size(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag);
|
||||
uint64_t weight = estimate_tx_weight(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof, clsag);
|
||||
return std::make_pair(size, weight);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -1400,7 +1400,7 @@ private:
|
|||
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels);
|
||||
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees);
|
||||
|
||||
uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const;
|
||||
uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const;
|
||||
uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1);
|
||||
uint64_t get_base_fee();
|
||||
uint64_t get_fee_quantization_mask();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue