aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cryptonote_basic/cryptonote_boost_serialization.h8
-rw-r--r--src/cryptonote_core/blockchain.cpp35
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp18
-rw-r--r--src/ringct/rctSigs.cpp91
-rw-r--r--src/ringct/rctTypes.h40
-rw-r--r--src/serialization/json_object.cpp41
-rw-r--r--src/serialization/json_object.h3
-rw-r--r--src/wallet/wallet2.cpp17
-rw-r--r--src/wallet/wallet2.h2
-rw-r--r--tests/core_tests/rct.cpp2
-rw-r--r--tests/performance_tests/check_tx_signature.h2
11 files changed, 195 insertions, 64 deletions
diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h
index 2abc3122c..760edf9b9 100644
--- a/src/cryptonote_basic/cryptonote_boost_serialization.h
+++ b/src/cryptonote_basic/cryptonote_boost_serialization.h
@@ -280,11 +280,11 @@ namespace boost
a & x.type;
if (x.type == rct::RCTTypeNull)
return;
- if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple)
+ if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeSimpleBulletproof)
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
- if (x.type == rct::RCTTypeSimple)
+ if (x.type == rct::RCTTypeSimple || x.type == rct::RCTTypeSimpleBulletproof)
a & x.pseudoOuts;
a & x.ecdhInfo;
serializeOutPk(a, x.outPk, ver);
@@ -306,11 +306,11 @@ namespace boost
a & x.type;
if (x.type == rct::RCTTypeNull)
return;
- if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple)
+ if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeSimpleBulletproof)
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
- if (x.type == rct::RCTTypeSimple)
+ if (x.type == rct::RCTTypeSimple || x.type == rct::RCTTypeSimpleBulletproof)
a & x.pseudoOuts;
a & x.ecdhInfo;
serializeOutPk(a, x.outPk, ver);
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 3d586a704..836856bae 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -127,6 +127,7 @@ static const struct {
{ 5, 802660, 0, 1472415036 + 86400*180 }, // add 5 months on testnet to shut the update warning up since there's a large gap to v6
{ 6, 971400, 0, 1501709789 },
+ { 7, 1057028, 0, 1512211236 },
};
static const uint64_t testnet_hard_fork_version_1_till = 624633;
@@ -2387,8 +2388,10 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ const uint8_t hf_version = m_hardfork->get_current_version();
+
// from hard fork 2, we forbid dust and compound outputs
- if (m_hardfork->get_current_version() >= 2) {
+ if (hf_version >= 2) {
for (auto &o: tx.vout) {
if (tx.version == 1)
{
@@ -2401,7 +2404,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
}
// in a v2 tx, all outputs must have 0 amount
- if (m_hardfork->get_current_version() >= 3) {
+ if (hf_version >= 3) {
if (tx.version >= 2) {
for (auto &o: tx.vout) {
if (o.amount != 0) {
@@ -2413,7 +2416,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
}
// from v4, forbid invalid pubkeys
- if (m_hardfork->get_current_version() >= 4) {
+ if (hf_version >= 4) {
for (const auto &o: tx.vout) {
if (o.target.type() == typeid(txout_to_key)) {
const txout_to_key& out_to_key = boost::get<txout_to_key>(o.target);
@@ -2425,6 +2428,16 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
}
}
+ // from v7, allow bulletproofs
+ if (hf_version < 7 || !m_testnet) {
+ if (!tx.rct_signatures.p.bulletproofs.empty())
+ {
+ MERROR("Bulletproofs are not allowed before v7 or on mainnet");
+ tvc.m_invalid_output = true;
+ return false;
+ }
+ }
+
return true;
}
//------------------------------------------------------------------
@@ -2450,7 +2463,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
rv.message = rct::hash2rct(tx_prefix_hash);
// mixRing - full and simple store it in opposite ways
- if (rv.type == rct::RCTTypeFull)
+ if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof)
{
rv.mixRing.resize(pubkeys[0].size());
for (size_t m = 0; m < pubkeys[0].size(); ++m)
@@ -2464,7 +2477,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
}
}
}
- else if (rv.type == rct::RCTTypeSimple)
+ else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeSimpleBulletproof)
{
rv.mixRing.resize(pubkeys.size());
for (size_t n = 0; n < pubkeys.size(); ++n)
@@ -2482,14 +2495,14 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
}
// II
- if (rv.type == rct::RCTTypeFull)
+ if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof)
{
rv.p.MGs.resize(1);
rv.p.MGs[0].II.resize(tx.vin.size());
for (size_t n = 0; n < tx.vin.size(); ++n)
rv.p.MGs[0].II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
}
- else if (rv.type == rct::RCTTypeSimple)
+ else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeSimpleBulletproof)
{
CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size");
for (size_t n = 0; n < tx.vin.size(); ++n)
@@ -2753,7 +2766,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
MERROR_VER("Null rct signature on non-coinbase tx");
return false;
}
- case rct::RCTTypeSimple: {
+ case rct::RCTTypeSimple:
+ case rct::RCTTypeSimpleBulletproof:
+ {
// check all this, either recontructed (so should really pass), or not
{
if (pubkeys.size() != rv.mixRing.size())
@@ -2809,7 +2824,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
}
break;
}
- case rct::RCTTypeFull: {
+ case rct::RCTTypeFull:
+ case rct::RCTTypeFullBulletproof:
+ {
// check all this, either recontructed (so should really pass), or not
{
bool size_matches = true;
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index edc2dfdaa..5cfa4b3e9 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -625,6 +625,22 @@ namespace cryptonote
}
for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n)
rv.outPk[n].dest = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key);
+
+ const bool bulletproof = rv.type == rct::RCTTypeFullBulletproof || rv.type == rct::RCTTypeSimpleBulletproof;
+ if (bulletproof)
+ {
+ if (rv.p.bulletproofs.size() != tx.vout.size())
+ {
+ LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad bulletproofs size in tx " << tx_hash << ", rejected");
+ tvc.m_verifivation_failed = true;
+ return false;
+ }
+ for (size_t n = 0; n < rv.outPk.size(); ++n)
+ {
+ rv.p.bulletproofs[n].V.resize(1);
+ rv.p.bulletproofs[n].V[0] = rv.outPk[n].mask;
+ }
+ }
}
if (keeped_by_block && get_blockchain_storage().is_within_compiled_block_hash_area())
@@ -828,6 +844,7 @@ namespace cryptonote
MERROR_VER("Unexpected Null rctSig type");
return false;
case rct::RCTTypeSimple:
+ case rct::RCTTypeSimpleBulletproof:
if (!rct::verRctSimple(rv, true))
{
MERROR_VER("rct signature semantics check failed");
@@ -835,6 +852,7 @@ namespace cryptonote
}
break;
case rct::RCTTypeFull:
+ case rct::RCTTypeFullBulletproof:
if (!rct::verRct(rv, true))
{
MERROR_VER("rct signature semantics check failed");
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index ddd26e3ad..cfb4aaf97 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -47,7 +47,8 @@ namespace rct {
{
mask = rct::skGen();
Bulletproof proof = bulletproof_PROVE(amount, mask);
- C = proof.V;
+ CHECK_AND_ASSERT_THROW_MES(proof.V.size() == 1, "V has not exactly one element");
+ C = proof.V[0];
return proof;
}
@@ -344,16 +345,41 @@ namespace rct {
hashes.push_back(hash2rct(h));
keyV kv;
- kv.reserve((64*3+1) * rv.p.rangeSigs.size());
- for (auto r: rv.p.rangeSigs)
+ if (rv.type == RCTTypeSimpleBulletproof || rv.type == RCTTypeFullBulletproof)
{
- for (size_t n = 0; n < 64; ++n)
- kv.push_back(r.asig.s0[n]);
- for (size_t n = 0; n < 64; ++n)
- kv.push_back(r.asig.s1[n]);
- kv.push_back(r.asig.ee);
- for (size_t n = 0; n < 64; ++n)
- kv.push_back(r.Ci[n]);
+ kv.reserve((6*2+10) * rv.p.bulletproofs.size());
+ for (const auto &p: rv.p.bulletproofs)
+ {
+ for (size_t n = 0; n < p.V.size(); ++n)
+ kv.push_back(p.V[n]);
+ kv.push_back(p.A);
+ kv.push_back(p.S);
+ kv.push_back(p.T1);
+ kv.push_back(p.T2);
+ kv.push_back(p.taux);
+ kv.push_back(p.mu);
+ for (size_t n = 0; n < p.L.size(); ++n)
+ kv.push_back(p.L[n]);
+ for (size_t n = 0; n < p.R.size(); ++n)
+ kv.push_back(p.R[n]);
+ kv.push_back(p.a);
+ kv.push_back(p.b);
+ kv.push_back(p.t);
+ }
+ }
+ else
+ {
+ kv.reserve((64*3+1) * rv.p.rangeSigs.size());
+ for (const auto &r: rv.p.rangeSigs)
+ {
+ for (size_t n = 0; n < 64; ++n)
+ kv.push_back(r.asig.s0[n]);
+ for (size_t n = 0; n < 64; ++n)
+ kv.push_back(r.asig.s1[n]);
+ kv.push_back(r.asig.ee);
+ for (size_t n = 0; n < 64; ++n)
+ kv.push_back(r.Ci[n]);
+ }
}
hashes.push_back(cn_fast_hash(kv));
return cn_fast_hash(hashes);
@@ -581,10 +607,13 @@ namespace rct {
}
rctSig rv;
- rv.type = RCTTypeFull;
+ rv.type = bulletproof ? RCTTypeFullBulletproof : RCTTypeFull;
rv.message = message;
rv.outPk.resize(destinations.size());
- rv.p.rangeSigs.resize(destinations.size());
+ if (bulletproof)
+ rv.p.bulletproofs.resize(destinations.size());
+ else
+ rv.p.rangeSigs.resize(destinations.size());
rv.ecdhInfo.resize(destinations.size());
size_t i = 0;
@@ -650,7 +679,7 @@ namespace rct {
}
rctSig rv;
- rv.type = RCTTypeSimple;
+ rv.type = bulletproof ? RCTTypeSimpleBulletproof : RCTTypeSimple;
rv.message = message;
rv.outPk.resize(destinations.size());
if (bulletproof)
@@ -738,10 +767,10 @@ namespace rct {
// must know the destination private key to find the correct amount, else will return a random number
bool verRct(const rctSig & rv, bool semantics) {
PERF_TIMER(verRct);
- CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig");
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "verRct called on non-full rctSig");
if (semantics)
{
- if (rv.p.rangeSigs.empty())
+ if (rv.type == RCTTypeFullBulletproof)
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.bulletproofs.size(), false, "Mismatched sizes of outPk and rv.p.bulletproofs");
else
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs");
@@ -764,7 +793,7 @@ namespace rct {
for (size_t i = 0; i < rv.outPk.size(); i++) {
tpool.submit(&waiter, [&, i] {
if (rv.p.rangeSigs.empty())
- results[i] = bulletproof_VERIFY(rv.p.bulletproofs[i]); // TODO
+ results[i] = bulletproof_VERIFY(rv.p.bulletproofs[i]);
else
results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
});
@@ -806,10 +835,10 @@ namespace rct {
{
PERF_TIMER(verRctSimple);
- CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "verRctSimple called on non simple rctSig");
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctSimple called on non simple rctSig");
if (semantics)
{
- if (rv.p.rangeSigs.empty())
+ if (rv.type == RCTTypeSimpleBulletproof)
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.bulletproofs.size(), false, "Mismatched sizes of outPk and rv.p.bulletproofs");
else
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs");
@@ -905,9 +934,17 @@ namespace rct {
// 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
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask) {
- CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig");
- CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "decodeRct called on non-full rctSig");
CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index");
+ if (rv.type == RCTTypeFullBulletproof)
+ {
+ CHECK_AND_ASSERT_THROW_MES(rv.p.bulletproofs.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.p.bulletproofs and rv.ecdhInfo");
+ CHECK_AND_ASSERT_THROW_MES(rv.p.bulletproofs[i].V.size() == 1, "Unexpected sizes of rv.p.bulletproofs[i].V");
+ }
+ else
+ {
+ 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];
@@ -933,16 +970,24 @@ namespace rct {
}
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask) {
- CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "decodeRct called on non simple rctSig");
- CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "decodeRct called on non simple rctSig");
CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index");
+ if (rv.type == RCTTypeSimpleBulletproof)
+ {
+ CHECK_AND_ASSERT_THROW_MES(rv.p.bulletproofs.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.p.bulletproofs and rv.ecdhInfo");
+ CHECK_AND_ASSERT_THROW_MES(rv.p.bulletproofs[i].V.size() == 1, "Unexpected sizes of rv.p.bulletproofs[i].V");
+ }
+ else
+ {
+ 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];
ecdhDecode(ecdh_info, sk);
mask = ecdh_info.mask;
key amount = ecdh_info.amount;
- key C = rv.outPk[i].mask;
+ key C = (rv.type == RCTTypeSimpleBulletproof) ? rv.p.bulletproofs[i].V.front() : rv.outPk[i].mask;
DP("C");
DP(C);
key Ctmp;
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
index aa906c0ef..50dfdb432 100644
--- a/src/ringct/rctTypes.h
+++ b/src/ringct/rctTypes.h
@@ -164,17 +164,19 @@ namespace rct {
struct Bulletproof
{
- rct::key V, A, S, T1, T2;
+ rct::keyV V;
+ rct::key A, S, T1, T2;
rct::key taux, mu;
rct::keyV L, R;
rct::key a, b, t;
Bulletproof() {}
Bulletproof(const rct::key &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t):
- V(V), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {}
+ V({V}), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {}
BEGIN_SERIALIZE_OBJECT()
- FIELD(V)
+ // Commitments aren't saved, they're restored via outPk
+ // FIELD(V)
FIELD(A)
FIELD(S)
FIELD(T1)
@@ -203,6 +205,8 @@ namespace rct {
RCTTypeNull = 0,
RCTTypeFull = 1,
RCTTypeSimple = 2,
+ RCTTypeFullBulletproof = 3,
+ RCTTypeSimpleBulletproof = 4,
};
struct rctSigBase {
uint8_t type;
@@ -220,13 +224,13 @@ namespace rct {
FIELD(type)
if (type == RCTTypeNull)
return true;
- if (type != RCTTypeFull && type != RCTTypeSimple)
+ if (type != RCTTypeFull && type != RCTTypeFullBulletproof && type != RCTTypeSimple && type != RCTTypeSimpleBulletproof)
return false;
VARINT_FIELD(txnFee)
// inputs/outputs not saved, only here for serialization help
// FIELD(message) - not serialized, it can be reconstructed
// FIELD(mixRing) - not serialized, it can be reconstructed
- if (type == RCTTypeSimple)
+ if (type == RCTTypeSimple || type == RCTTypeSimpleBulletproof)
{
ar.tag("pseudoOuts");
ar.begin_array();
@@ -280,18 +284,18 @@ namespace rct {
{
if (type == RCTTypeNull)
return true;
- if (type != RCTTypeFull && type != RCTTypeSimple)
+ if (type != RCTTypeFull && type != RCTTypeFullBulletproof && type != RCTTypeSimple && type != RCTTypeSimpleBulletproof)
return false;
- ar.tag("rangeSigs");
- ar.begin_array();
- PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, rangeSigs);
- if (!rangeSigs.empty())
+ if (type == RCTTypeSimpleBulletproof || type == RCTTypeFullBulletproof)
{
- if (rangeSigs.size() != outputs)
+ ar.tag("bp");
+ ar.begin_array();
+ PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, bulletproofs);
+ if (bulletproofs.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
{
- FIELDS(rangeSigs[i])
+ FIELDS(bulletproofs[i])
if (outputs - i > 1)
ar.delimit_array();
}
@@ -299,14 +303,14 @@ namespace rct {
}
else
{
- ar.tag("bp");
+ ar.tag("rangeSigs");
ar.begin_array();
- PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, bulletproofs);
- if (bulletproofs.size() != outputs)
+ PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, rangeSigs);
+ if (rangeSigs.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
{
- FIELDS(bulletproofs[i])
+ FIELDS(rangeSigs[i])
if (outputs - i > 1)
ar.delimit_array();
}
@@ -317,7 +321,7 @@ namespace rct {
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 ? inputs : 1;
+ size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeSimpleBulletproof) ? inputs : 1;
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs);
if (MGs.size() != mg_elements)
return false;
@@ -335,7 +339,7 @@ namespace rct {
for (size_t j = 0; j < mixin + 1; ++j)
{
ar.begin_array();
- size_t mg_ss2_elements = (type == RCTTypeSimple ? 1 : inputs) + 1;
+ size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeSimpleBulletproof) ? 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;
diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp
index 6e6e51528..2c86d4054 100644
--- a/src/serialization/json_object.cpp
+++ b/src/serialization/json_object.cpp
@@ -1007,6 +1007,7 @@ void toJsonValue(rapidjson::Document& doc, const rct::rctSigPrunable& sig, rapid
val.SetObject();
INSERT_INTO_JSON_OBJECT(val, doc, rangeSigs, sig.rangeSigs);
+ INSERT_INTO_JSON_OBJECT(val, doc, bulletproofs, sig.bulletproofs);
INSERT_INTO_JSON_OBJECT(val, doc, MGs, sig.MGs);
}
@@ -1018,6 +1019,7 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig)
}
GET_FROM_JSON_OBJECT(val, sig.rangeSigs, rangeSigs);
+ GET_FROM_JSON_OBJECT(val, sig.bulletproofs, bulletproofs);
GET_FROM_JSON_OBJECT(val, sig.MGs, MGs);
}
@@ -1052,6 +1054,45 @@ void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig)
}
}
+void toJsonValue(rapidjson::Document& doc, const rct::Bulletproof& p, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, V, p.V);
+ INSERT_INTO_JSON_OBJECT(val, doc, A, p.A);
+ INSERT_INTO_JSON_OBJECT(val, doc, S, p.S);
+ INSERT_INTO_JSON_OBJECT(val, doc, T1, p.T1);
+ INSERT_INTO_JSON_OBJECT(val, doc, T2, p.T2);
+ INSERT_INTO_JSON_OBJECT(val, doc, taux, p.taux);
+ INSERT_INTO_JSON_OBJECT(val, doc, mu, p.mu);
+ INSERT_INTO_JSON_OBJECT(val, doc, L, p.L);
+ INSERT_INTO_JSON_OBJECT(val, doc, R, p.R);
+ INSERT_INTO_JSON_OBJECT(val, doc, a, p.a);
+ INSERT_INTO_JSON_OBJECT(val, doc, b, p.b);
+ INSERT_INTO_JSON_OBJECT(val, doc, t, p.t);
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, p.V, V);
+ GET_FROM_JSON_OBJECT(val, p.A, A);
+ GET_FROM_JSON_OBJECT(val, p.S, S);
+ GET_FROM_JSON_OBJECT(val, p.T1, T1);
+ GET_FROM_JSON_OBJECT(val, p.T2, T2);
+ GET_FROM_JSON_OBJECT(val, p.taux, taux);
+ GET_FROM_JSON_OBJECT(val, p.mu, mu);
+ GET_FROM_JSON_OBJECT(val, p.L, L);
+ GET_FROM_JSON_OBJECT(val, p.R, R);
+ GET_FROM_JSON_OBJECT(val, p.a, a);
+ GET_FROM_JSON_OBJECT(val, p.b, b);
+ GET_FROM_JSON_OBJECT(val, p.t, t);
+}
+
void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val)
{
val.SetObject();
diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h
index 7b9519c48..5dca7b249 100644
--- a/src/serialization/json_object.h
+++ b/src/serialization/json_object.h
@@ -274,6 +274,9 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig);
void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val);
void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig);
+void toJsonValue(rapidjson::Document& doc, const rct::Bulletproof& p, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p);
+
void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val);
void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 5be57d33c..e29e1051c 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -823,8 +823,10 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
switch (rv.type)
{
case rct::RCTTypeSimple:
+ case rct::RCTTypeSimpleBulletproof:
return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask);
case rct::RCTTypeFull:
+ case rct::RCTTypeFullBulletproof:
return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask);
default:
LOG_ERROR("Unsupported rct type: " << rv.type);
@@ -3774,9 +3776,10 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f
LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size());
signed_txes.ptx.push_back(pending_tx());
tools::wallet2::pending_tx &ptx = signed_txes.ptx.back();
+ bool bulletproof = sd.use_rct && !ptx.tx.rct_signatures.p.bulletproofs.empty();
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
- 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);
+ 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, bulletproof);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_testnet);
// 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,
@@ -4659,7 +4662,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,
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)
+ uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, bool bulletproof)
{
using namespace cryptonote;
// throw if attempting a transaction with no destinations
@@ -4775,7 +4778,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
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, true);
+ 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);
LOG_PRINT_L2("constructed tx, r="<<r);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_testnet);
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
@@ -5734,7 +5737,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
tx.selected_transfers.size() << " inputs");
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- test_tx, test_ptx);
+ test_tx, test_ptx, bulletproof);
else
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);
@@ -5777,7 +5780,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
while (needed_fee > test_ptx.fee) {
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- test_tx, test_ptx);
+ test_tx, test_ptx, bulletproof);
else
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);
@@ -5984,7 +5987,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
tx.selected_transfers.size() << " outputs");
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- test_tx, test_ptx);
+ test_tx, test_ptx, bulletproof);
else
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);
@@ -6001,7 +6004,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
tx.dsts[0].amount = available_for_fee - needed_fee;
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- test_tx, test_ptx);
+ test_tx, test_ptx, bulletproof);
else
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);
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index eeec7c338..866b95853 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -536,7 +536,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);
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,
- uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx);
+ uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, bool bulletproof);
void commit_tx(pending_tx& ptx_vector);
void commit_tx(std::vector<pending_tx>& ptx_vector);
diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp
index 50f65cc67..e5047baf2 100644
--- a/tests/core_tests/rct.cpp
+++ b/tests/core_tests/rct.cpp
@@ -132,7 +132,7 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
crypto::secret_key amount_key;
crypto::derivation_to_scalar(derivation, o, amount_key);
- if (rct_txes[n].rct_signatures.type == rct::RCTTypeSimple)
+ if (rct_txes[n].rct_signatures.type == rct::RCTTypeSimple || rct_txes[n].rct_signatures.type == rct::RCTTypeSimpleBulletproof)
rct::decodeRctSimple(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4]);
else
rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4]);
diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h
index 02555fae8..afc2bdc45 100644
--- a/tests/performance_tests/check_tx_signature.h
+++ b/tests/performance_tests/check_tx_signature.h
@@ -80,7 +80,7 @@ public:
{
if (rct)
{
- if (m_tx.rct_signatures.type == rct::RCTTypeFull)
+ if (m_tx.rct_signatures.type == rct::RCTTypeFull || m_tx.rct_signatures.type == rct::RCTTypeFullBulletproof)
return rct::verRct(m_tx.rct_signatures);
else
return rct::verRctSimple(m_tx.rct_signatures);