initial clsag implementation for carrot
This commit is contained in:
@@ -236,6 +236,8 @@ namespace hw {
|
||||
virtual bool clsag_hash(const rct::keyV &data, rct::key &hash) = 0;
|
||||
virtual bool clsag_sign(const rct::key &c, const rct::key &a, const rct::key &p, const rct::key &z, const rct::key &mu_P, const rct::key &mu_C, rct::key &s) = 0;
|
||||
|
||||
virtual bool clsag_prepare_carrot(const rct::key &p, const rct::key &z, rct::key &I, rct::key &D, const rct::key &H, rct::key &a, rct::key &aG, rct::key &b, rct::key &bT, rct::key &aH) = 0;
|
||||
|
||||
virtual bool close_tx(void) = 0;
|
||||
|
||||
virtual bool has_ki_cold_sync(void) const { return false; }
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "device_default.hpp"
|
||||
#include "int-util.h"
|
||||
#include "crypto/wallet/crypto.h"
|
||||
#include "crypto/generators.h"
|
||||
#include "cryptonote_basic/account.h"
|
||||
#include "cryptonote_basic/subaddress_index.h"
|
||||
#include "cryptonote_core/cryptonote_tx_utils.h"
|
||||
@@ -424,6 +425,19 @@ namespace hw {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool device_default::clsag_prepare_carrot(const rct::key &p, const rct::key &z, rct::key &I, rct::key &D, const rct::key &H, rct::key &a, rct::key &aG, rct::key &b, rct::key &bT, rct::key &aH) {
|
||||
rct::skpkGen(a,aG); // aG = a*G
|
||||
rct::scalarmultKey(aH,H,a); // aH = a*H
|
||||
rct::scalarmultKey(I,H,p); // I = p*H
|
||||
rct::scalarmultKey(D,H,z); // D = z*H
|
||||
|
||||
// bT = b*T
|
||||
rct::skGen(b);
|
||||
bT = rct::scalarmultKey(b, rct::pk2rct(crypto::get_T()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool device_default::clsag_hash(const rct::keyV &data, rct::key &hash) {
|
||||
hash = rct::hash_to_scalar(data);
|
||||
return true;
|
||||
@@ -432,9 +446,10 @@ namespace hw {
|
||||
bool device_default::clsag_sign(const rct::key &c, const rct::key &a, const rct::key &p, const rct::key &z, const rct::key &mu_P, const rct::key &mu_C, rct::key &s) {
|
||||
rct::key s0_p_mu_P;
|
||||
sc_mul(s0_p_mu_P.bytes,mu_P.bytes,p.bytes);
|
||||
|
||||
rct::key s0_add_z_mu_C;
|
||||
sc_muladd(s0_add_z_mu_C.bytes,mu_C.bytes,z.bytes,s0_p_mu_P.bytes);
|
||||
sc_mulsub(s.bytes,c.bytes,s0_add_z_mu_C.bytes,a.bytes);
|
||||
sc_muladd(s0_add_z_mu_C.bytes, mu_C.bytes, z.bytes, s0_p_mu_P.bytes);
|
||||
sc_mulsub(s.bytes, c.bytes, s0_add_z_mu_C.bytes, a.bytes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -140,6 +140,7 @@ namespace hw {
|
||||
bool clsag_hash(const rct::keyV &data, rct::key &hash) override;
|
||||
bool clsag_sign(const rct::key &c, const rct::key &a, const rct::key &p, const rct::key &z, const rct::key &mu_P, const rct::key &mu_C, rct::key &s) override;
|
||||
|
||||
bool clsag_prepare_carrot(const rct::key &p, const rct::key &z, rct::key &I, rct::key &D, const rct::key &H, rct::key &a, rct::key &aG, rct::key &b, rct::key &bT, rct::key &aH);
|
||||
bool close_tx(void) override;
|
||||
};
|
||||
|
||||
|
||||
@@ -2255,6 +2255,10 @@ namespace hw {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool device_ledger::clsag_prepare_carrot(const rct::key &p, const rct::key &z, rct::key &I, rct::key &D, const rct::key &H, rct::key &a, rct::key &aG, rct::key &b, rct::key &bT, rct::key &aH) {
|
||||
return true; // Not implemented for Ledger
|
||||
}
|
||||
|
||||
bool device_ledger::clsag_hash(const rct::keyV &data, rct::key &hash) {
|
||||
AUTO_LOCK_CMD();
|
||||
|
||||
|
||||
@@ -282,6 +282,8 @@ namespace hw {
|
||||
bool clsag_hash(const rct::keyV &data, rct::key &hash) override;
|
||||
bool clsag_sign(const rct::key &c, const rct::key &a, const rct::key &p, const rct::key &z, const rct::key &mu_P, const rct::key &mu_C, rct::key &s) override;
|
||||
|
||||
bool clsag_prepare_carrot(const rct::key &p, const rct::key &z, rct::key &I, rct::key &D, const rct::key &H, rct::key &a, rct::key &aG, rct::key &b, rct::key &bT, rct::key &aH) override;
|
||||
|
||||
|
||||
bool close_tx(void) override;
|
||||
|
||||
|
||||
+299
-5
@@ -34,6 +34,7 @@
|
||||
#include "common/perf_timer.h"
|
||||
#include "common/threadpool.h"
|
||||
#include "common/util.h"
|
||||
#include "crypto/generators.h"
|
||||
#include "rctSigs.h"
|
||||
#include "bulletproofs.h"
|
||||
#include "bulletproofs_plus.h"
|
||||
@@ -364,6 +365,152 @@ namespace rct {
|
||||
return sig;
|
||||
}
|
||||
|
||||
clsagCarrot CLSAG_Gen_Carrot(
|
||||
const key &message,
|
||||
const keyV & P,
|
||||
const key & x, // x term of P
|
||||
const key & y, // y term of P
|
||||
const keyV & C,
|
||||
const key & z,
|
||||
const keyV & C_nonzero,
|
||||
const key & C_offset,
|
||||
const unsigned int l,
|
||||
hw::device &hwdev)
|
||||
{
|
||||
clsagCarrot 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(n == C_nonzero.size(), "Signing and commitment key vector sizes must match!");
|
||||
CHECK_AND_ASSERT_THROW_MES(l < n, "Signing index out of range!");
|
||||
|
||||
// Key images
|
||||
ge_p3 H_p3;
|
||||
hash_to_p3(H_p3,P[l]);
|
||||
key H;
|
||||
ge_p3_tobytes(H.bytes,&H_p3);
|
||||
|
||||
key D;
|
||||
|
||||
// Initial values
|
||||
key a;
|
||||
key aG;
|
||||
key aH;
|
||||
key b;
|
||||
key bT;
|
||||
|
||||
hwdev.clsag_prepare_carrot(x,z,sig.I,D,H,a,aG,b,bT,aH);
|
||||
|
||||
geDsmp I_precomp;
|
||||
geDsmp D_precomp;
|
||||
precomp(I_precomp.k,sig.I);
|
||||
precomp(D_precomp.k,D);
|
||||
|
||||
// Offset key image
|
||||
scalarmultKey(sig.D,D,INV_EIGHT);
|
||||
|
||||
// Aggregation hashes
|
||||
keyV mu_P_to_hash(2*n+4); // domain, I, D, P, C, C_offset
|
||||
keyV mu_C_to_hash(2*n+4); // domain, I, D, P, C, C_offset
|
||||
sc_0(mu_P_to_hash[0].bytes);
|
||||
memcpy(mu_P_to_hash[0].bytes,config::HASH_KEY_CLSAG_AGG_0,sizeof(config::HASH_KEY_CLSAG_AGG_0)-1);
|
||||
sc_0(mu_C_to_hash[0].bytes);
|
||||
memcpy(mu_C_to_hash[0].bytes,config::HASH_KEY_CLSAG_AGG_1,sizeof(config::HASH_KEY_CLSAG_AGG_1)-1);
|
||||
for (size_t i = 1; i < n+1; ++i) {
|
||||
mu_P_to_hash[i] = P[i-1];
|
||||
mu_C_to_hash[i] = P[i-1];
|
||||
}
|
||||
for (size_t i = n+1; i < 2*n+1; ++i) {
|
||||
mu_P_to_hash[i] = C_nonzero[i-n-1];
|
||||
mu_C_to_hash[i] = C_nonzero[i-n-1];
|
||||
}
|
||||
mu_P_to_hash[2*n+1] = sig.I;
|
||||
mu_P_to_hash[2*n+2] = sig.D;
|
||||
mu_P_to_hash[2*n+3] = C_offset;
|
||||
mu_C_to_hash[2*n+1] = sig.I;
|
||||
mu_C_to_hash[2*n+2] = sig.D;
|
||||
mu_C_to_hash[2*n+3] = C_offset;
|
||||
key mu_P, mu_C;
|
||||
mu_P = hash_to_scalar(mu_P_to_hash);
|
||||
mu_C = hash_to_scalar(mu_C_to_hash);
|
||||
|
||||
// Initial commitment
|
||||
keyV c_to_hash(2*n+5); // domain, P, C, C_offset, message, aG, aH
|
||||
key c;
|
||||
sc_0(c_to_hash[0].bytes);
|
||||
memcpy(c_to_hash[0].bytes,config::HASH_KEY_CLSAG_ROUND,sizeof(config::HASH_KEY_CLSAG_ROUND)-1);
|
||||
for (size_t i = 1; i < n+1; ++i)
|
||||
{
|
||||
c_to_hash[i] = P[i-1];
|
||||
c_to_hash[i+n] = C_nonzero[i-1];
|
||||
}
|
||||
c_to_hash[2*n+1] = C_offset;
|
||||
c_to_hash[2*n+2] = message;
|
||||
|
||||
c_to_hash[2*n+3] = addKeys(aG, bT); // we use aG + bT instead of aG
|
||||
c_to_hash[2*n+4] = aH;
|
||||
|
||||
hwdev.clsag_hash(c_to_hash, c);
|
||||
|
||||
size_t i;
|
||||
i = (l + 1) % n;
|
||||
if (i == 0)
|
||||
copy(sig.c1, c);
|
||||
|
||||
|
||||
// Decoy indices
|
||||
sig.sx = keyV(n);
|
||||
sig.sy = keyV(n);
|
||||
key c_new;
|
||||
key L;
|
||||
key R;
|
||||
key c_p; // = c[i]*mu_P
|
||||
key c_c; // = c[i]*mu_C
|
||||
geDsmp P_precomp;
|
||||
geDsmp C_precomp;
|
||||
geDsmp H_precomp;
|
||||
ge_p3 Hi_p3;
|
||||
|
||||
while (i != l) {
|
||||
sig.sx[i] = skGen();
|
||||
sig.sy[i] = skGen();
|
||||
sc_0(c_new.bytes);
|
||||
sc_mul(c_p.bytes, mu_P.bytes, c.bytes);
|
||||
sc_mul(c_c.bytes, mu_C.bytes, c.bytes);
|
||||
|
||||
// Precompute points
|
||||
precomp(P_precomp.k, P[i]);
|
||||
precomp(C_precomp.k, C[i]);
|
||||
|
||||
// Compute L
|
||||
addKeys_aGbBcC(L, sig.sx[i], c_p, P_precomp.k, c_c, C_precomp.k);
|
||||
// add the T term
|
||||
key rT = rct::scalarmultKey(b, rct::pk2rct(crypto::get_T()));
|
||||
L = addKeys(L, rT);
|
||||
|
||||
// Compute R
|
||||
hash_to_p3(Hi_p3,P[i]);
|
||||
ge_dsm_precomp(H_precomp.k, &Hi_p3);
|
||||
addKeys_aAbBcC(R, sig.sx[i], H_precomp.k, c_p, I_precomp.k, c_c, D_precomp.k);
|
||||
|
||||
c_to_hash[2*n+3] = L;
|
||||
c_to_hash[2*n+4] = R;
|
||||
hwdev.clsag_hash(c_to_hash, c_new);
|
||||
copy(c, c_new);
|
||||
|
||||
i = (i + 1) % n;
|
||||
if (i == 0)
|
||||
copy(sig.c1, c);
|
||||
}
|
||||
|
||||
// Compute final scalars
|
||||
hwdev.clsag_sign(c, a, x, z, mu_P, mu_C, sig.sx[l]);
|
||||
hwdev.clsag_sign(c, b, y, z, mu_P, mu_C, sig.sy[l]);
|
||||
memwipe(&a, sizeof(key));
|
||||
memwipe(&b, sizeof(key));
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
clsag CLSAG_Gen(const key &message, const keyV & P, const key & p, const keyV & C, const key & z, const keyV & C_nonzero, const key & C_offset, const unsigned int l) {
|
||||
return CLSAG_Gen(message, P, p, C, z, C_nonzero, C_offset, l, hw::get_device("default"));
|
||||
}
|
||||
@@ -810,9 +957,9 @@ namespace rct {
|
||||
keyM M(cols, tmp);
|
||||
|
||||
keyV P, C, C_nonzero;
|
||||
P.reserve(pubs.size());
|
||||
C.reserve(pubs.size());
|
||||
C_nonzero.reserve(pubs.size());
|
||||
P.reserve(pubs.size());
|
||||
C.reserve(pubs.size());
|
||||
C_nonzero.reserve(pubs.size());
|
||||
for (const ctkey &k: pubs)
|
||||
{
|
||||
P.push_back(k.dest);
|
||||
@@ -822,13 +969,41 @@ namespace rct {
|
||||
C.push_back(tmp);
|
||||
}
|
||||
|
||||
sk[0] = copy(inSk.dest);
|
||||
sc_sub(sk[1].bytes, inSk.mask.bytes, a.bytes);
|
||||
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], C_nonzero, Cout, index, hwdev);
|
||||
memwipe(sk.data(), sk.size() * sizeof(key));
|
||||
return result;
|
||||
}
|
||||
|
||||
clsagCarrot proveRctCLSAGSSimpleCarrot(const key &message, const ctkeyV &pubs, const key &x, const key &y, const key &mask, const key &a, const key &Cout, 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");
|
||||
keyV tmp(rows + 1);
|
||||
keyM M(cols, tmp);
|
||||
|
||||
keyV P, C, C_nonzero;
|
||||
P.reserve(pubs.size()); // pubkeys
|
||||
C.reserve(pubs.size()); // commitments to 0.
|
||||
C_nonzero.reserve(pubs.size()); // commitments
|
||||
for (const ctkey &k: pubs)
|
||||
{
|
||||
P.push_back(k.dest);
|
||||
C_nonzero.push_back(k.mask);
|
||||
rct::key tmp;
|
||||
subKeys(tmp, k.mask, Cout);
|
||||
C.push_back(tmp);
|
||||
}
|
||||
|
||||
key sk;
|
||||
sc_sub(sk.bytes, mask.bytes, a.bytes); // private key of the output commitment
|
||||
clsagCarrot result = CLSAG_Gen_Carrot(message, P, x, y, C, sk, C_nonzero, Cout, index, hwdev);
|
||||
memwipe(&sk, sizeof(key));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//Ring-ct MG sigs
|
||||
//Prove:
|
||||
@@ -1021,6 +1196,125 @@ namespace rct {
|
||||
catch (...) { return false; }
|
||||
}
|
||||
|
||||
bool verRctCLSAGSimpleCarrot(const key &message, const clsagCarrot &sig, const ctkeyV & pubs, const key & C_offset) {
|
||||
try
|
||||
{
|
||||
PERF_TIMER(verRctCLSAGSimpleCarrot);
|
||||
const size_t n = pubs.size();
|
||||
|
||||
// Check data
|
||||
CHECK_AND_ASSERT_MES(n >= 1, false, "Empty pubs");
|
||||
CHECK_AND_ASSERT_MES(n == sig.sx.size(), false, "Signature scalar vector x is the wrong size!");
|
||||
CHECK_AND_ASSERT_MES(n == sig.sy.size(), false, "Signature scalar vector y is the wrong size!");
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
CHECK_AND_ASSERT_MES(sc_check(sig.sx[i].bytes) == 0, false, "Bad signature scalar!");
|
||||
CHECK_AND_ASSERT_MES(sc_check(sig.sy[i].bytes) == 0, false, "Bad signature scalar!");
|
||||
}
|
||||
CHECK_AND_ASSERT_MES(sc_check(sig.c1.bytes) == 0, false, "Bad signature commitment!");
|
||||
CHECK_AND_ASSERT_MES(!(sig.I == rct::identity()), false, "Bad key image!");
|
||||
|
||||
// Cache commitment offset for efficient subtraction later
|
||||
ge_p3 C_offset_p3;
|
||||
CHECK_AND_ASSERT_MES(ge_frombytes_vartime(&C_offset_p3, C_offset.bytes) == 0, false, "point conv failed");
|
||||
ge_cached C_offset_cached;
|
||||
ge_p3_to_cached(&C_offset_cached, &C_offset_p3);
|
||||
|
||||
// Prepare key images
|
||||
key c = copy(sig.c1);
|
||||
key D_8 = scalarmult8(sig.D);
|
||||
CHECK_AND_ASSERT_MES(!(D_8 == rct::identity()), false, "Bad auxiliary key image!");
|
||||
geDsmp I_precomp;
|
||||
geDsmp D_precomp;
|
||||
precomp(I_precomp.k,sig.I);
|
||||
precomp(D_precomp.k,D_8);
|
||||
|
||||
// Aggregation hashes
|
||||
keyV mu_P_to_hash(2*n+4); // domain, I, D, P, C, C_offset
|
||||
keyV mu_C_to_hash(2*n+4); // domain, I, D, P, C, C_offset
|
||||
sc_0(mu_P_to_hash[0].bytes);
|
||||
memcpy(mu_P_to_hash[0].bytes,config::HASH_KEY_CLSAG_AGG_0,sizeof(config::HASH_KEY_CLSAG_AGG_0)-1);
|
||||
sc_0(mu_C_to_hash[0].bytes);
|
||||
memcpy(mu_C_to_hash[0].bytes,config::HASH_KEY_CLSAG_AGG_1,sizeof(config::HASH_KEY_CLSAG_AGG_1)-1);
|
||||
for (size_t i = 1; i < n+1; ++i) {
|
||||
mu_P_to_hash[i] = pubs[i-1].dest;
|
||||
mu_C_to_hash[i] = pubs[i-1].dest;
|
||||
}
|
||||
for (size_t i = n+1; i < 2*n+1; ++i) {
|
||||
mu_P_to_hash[i] = pubs[i-n-1].mask;
|
||||
mu_C_to_hash[i] = pubs[i-n-1].mask;
|
||||
}
|
||||
mu_P_to_hash[2*n+1] = sig.I;
|
||||
mu_P_to_hash[2*n+2] = sig.D;
|
||||
mu_P_to_hash[2*n+3] = C_offset;
|
||||
mu_C_to_hash[2*n+1] = sig.I;
|
||||
mu_C_to_hash[2*n+2] = sig.D;
|
||||
mu_C_to_hash[2*n+3] = C_offset;
|
||||
key mu_P, mu_C;
|
||||
mu_P = hash_to_scalar(mu_P_to_hash);
|
||||
mu_C = hash_to_scalar(mu_C_to_hash);
|
||||
|
||||
// Set up round hash
|
||||
keyV c_to_hash(2*n+5); // domain, P, C, C_offset, message, L, R
|
||||
sc_0(c_to_hash[0].bytes);
|
||||
memcpy(c_to_hash[0].bytes,config::HASH_KEY_CLSAG_ROUND,sizeof(config::HASH_KEY_CLSAG_ROUND)-1);
|
||||
for (size_t i = 1; i < n+1; ++i)
|
||||
{
|
||||
c_to_hash[i] = pubs[i-1].dest;
|
||||
c_to_hash[i+n] = pubs[i-1].mask;
|
||||
}
|
||||
c_to_hash[2*n+1] = C_offset;
|
||||
c_to_hash[2*n+2] = message;
|
||||
key c_p; // = c[i]*mu_P
|
||||
key c_c; // = c[i]*mu_C
|
||||
key c_new;
|
||||
key L;
|
||||
key R;
|
||||
geDsmp P_precomp;
|
||||
geDsmp C_precomp;
|
||||
size_t i = 0;
|
||||
ge_p3 hash8_p3;
|
||||
geDsmp hash_precomp;
|
||||
ge_p3 temp_p3;
|
||||
ge_p1p1 temp_p1;
|
||||
|
||||
while (i < n) {
|
||||
sc_0(c_new.bytes);
|
||||
sc_mul(c_p.bytes,mu_P.bytes,c.bytes);
|
||||
sc_mul(c_c.bytes,mu_C.bytes,c.bytes);
|
||||
|
||||
// Precompute points for L/R
|
||||
precomp(P_precomp.k,pubs[i].dest);
|
||||
|
||||
CHECK_AND_ASSERT_MES(ge_frombytes_vartime(&temp_p3, pubs[i].mask.bytes) == 0, false, "point conv failed");
|
||||
ge_sub(&temp_p1,&temp_p3,&C_offset_cached);
|
||||
ge_p1p1_to_p3(&temp_p3,&temp_p1);
|
||||
ge_dsm_precomp(C_precomp.k,&temp_p3);
|
||||
|
||||
// Compute L
|
||||
addKeys_aGbBcC(L, sig.sx[i], c_p, P_precomp.k, c_c, C_precomp.k);
|
||||
// add the T term
|
||||
key rT = rct::scalarmultKey(sig.sy[i], rct::pk2rct(crypto::get_T()));
|
||||
L = addKeys(L, rT);
|
||||
|
||||
// Compute R
|
||||
hash_to_p3(hash8_p3,pubs[i].dest);
|
||||
ge_dsm_precomp(hash_precomp.k, &hash8_p3);
|
||||
addKeys_aAbBcC(R,sig.sx[i],hash_precomp.k,c_p,I_precomp.k,c_c,D_precomp.k);
|
||||
|
||||
c_to_hash[2*n+3] = L;
|
||||
c_to_hash[2*n+4] = R;
|
||||
c_new = hash_to_scalar(c_to_hash);
|
||||
CHECK_AND_ASSERT_MES(!(c_new == rct::zero()), false, "Bad signature hash");
|
||||
copy(c,c_new);
|
||||
|
||||
i = i + 1;
|
||||
}
|
||||
sc_sub(c_new.bytes,c.bytes,sig.c1.bytes);
|
||||
return sc_isnonzero(c_new.bytes) == 0;
|
||||
}
|
||||
catch (...) { return false; }
|
||||
}
|
||||
|
||||
|
||||
//These functions get keys from blockchain
|
||||
//replace these when connecting blockchain
|
||||
|
||||
@@ -85,6 +85,10 @@ namespace rct {
|
||||
clsag proveRctCLSAGSimple(const key &, const ctkeyV &, const ctkey &, const key &, const key &, unsigned int, hw::device &);
|
||||
bool verRctCLSAGSimple(const key &, const clsag &, const ctkeyV &, const key &);
|
||||
|
||||
clsagCarrot CLSAG_Gen_Carrot(const key &message, const keyV & P, const key & x, const key & y, const keyV & C, const key & z, const keyV & C_nonzero, const key & C_offset, const unsigned int l, hw::device &hwdev);
|
||||
clsagCarrot proveRctCLSAGSSimpleCarrot(const key &message, const ctkeyV &pubs, const key &x, const key &y, const key &mask, const key &a, const key &Cout, unsigned int index, hw::device &hwdev);
|
||||
bool verRctCLSAGSimpleCarrot(const key &message, const clsagCarrot &sig, const ctkeyV & pubs, const key & C_offset);
|
||||
|
||||
zk_proof PRProof_Gen(const rct::key &difference);
|
||||
bool PRProof_Ver(const rct::key &C, const zk_proof &proof);
|
||||
|
||||
|
||||
@@ -207,6 +207,24 @@ namespace rct {
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
// CLSAG signature
|
||||
struct clsagCarrot {
|
||||
keyV sx; // x scalars(responses)
|
||||
keyV sy; // y scalars(responses)
|
||||
key c1;
|
||||
|
||||
key I; // signing key image
|
||||
key D; // commitment key image
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(sx)
|
||||
FIELD(sy)
|
||||
FIELD(c1)
|
||||
// FIELD(I) - not serialized, it can be reconstructed
|
||||
FIELD(D)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
//contains the data for an Borromean sig
|
||||
// also contains the "Ci" values such that
|
||||
// \sum Ci = C
|
||||
|
||||
Reference in New Issue
Block a user