aboutsummaryrefslogtreecommitdiff
path: root/src/ringct
diff options
context:
space:
mode:
authorRiccardo Spagni <ric@spagni.net>2017-12-17 21:05:16 +0200
committerRiccardo Spagni <ric@spagni.net>2017-12-17 21:05:16 +0200
commit1cc745113015dda0710e966fceb351e1f50c62e0 (patch)
tree168da14eda885978e872d247e435f7ea6475d3ee /src/ringct
parentMerge pull request #2950 (diff)
parentchange the N-1/N multisig second message signer for auth (diff)
downloadmonero-1cc745113015dda0710e966fceb351e1f50c62e0.tar.xz
Merge pull request #2134
ceabc4f9 change the N-1/N multisig second message signer for auth (moneromooo-monero) 55c2845d core_tests: multisig test now tests multiple inputs (moneromooo-monero) 98db7ee4 wallet: factor multisig info parsing (moneromooo-monero) 31a97e76 wallet: use raw encrypted data in multisig import/export RPC (moneromooo-monero) 2fa707d1 wallet: add multisig sign/submit RPC (moneromooo-monero) e36f5b60 Match surae's recommendation to derive multisig keys (moneromooo-monero) a36c261d wallet2: fix slow multisig unit tests with subaddress patch (moneromooo-monero) fa569712 make multisig work with subaddresses (moneromooo-monero) dffa0dce simplewallet: add export_raw_multisig command (moneromooo-monero) 7f4c220b simplewallet: add multisig to wallet type in wallet_info output (moneromooo-monero) 26529038 wallet: guard against partly initialized multisig wallet (moneromooo-monero) 66e34e85 add multisig core test and factor multisig building blocks (moneromooo-monero) f4eda44c N-1/N multisig (moneromooo-monero) cd64c799 multisig address generation RPC (moneromooo-monero) fff871a4 gen_multisig: generates multisig wallets if participants trust each other (moneromooo-monero) 95a21a79 wallet2: allow empty wallet filename to avoid saving data (moneromooo-monero) b84b3565 tests: add multisig unit tests (moneromooo-monero) 4c313324 Add N/N multisig tx generation and signing (moneromooo-monero) 6d219a92 wallet: add multisig key generation (moneromooo-monero)
Diffstat (limited to 'src/ringct')
-rw-r--r--src/ringct/rctSigs.cpp106
-rw-r--r--src/ringct/rctSigs.h16
-rw-r--r--src/ringct/rctTypes.h28
3 files changed, 121 insertions, 29 deletions
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index 38b213e8b..24ab08778 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -122,7 +122,7 @@ namespace rct {
// Gen creates a signature which proves that for some column in the keymatrix "pk"
// the signer knows a secret key for each row in that column
// Ver verifies that the MG sig was created correctly
- mgSig MLSAG_Gen(const key &message, const keyM & pk, const keyV & xx, const unsigned int index, size_t dsRows) {
+ 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) {
mgSig rv;
size_t cols = pk.size();
CHECK_AND_ASSERT_THROW_MES(cols >= 2, "Error! What is c if cols = 1!");
@@ -134,6 +134,8 @@ namespace rct {
}
CHECK_AND_ASSERT_THROW_MES(xx.size() == rows, "Bad xx size");
CHECK_AND_ASSERT_THROW_MES(dsRows <= rows, "Bad dsRows size");
+ CHECK_AND_ASSERT_THROW_MES((kLRki && mscout) || (!kLRki && !mscout), "Only one of kLRki/mscout is present");
+ CHECK_AND_ASSERT_THROW_MES(!kLRki || dsRows == 1, "Multisig requires exactly 1 dsRows");
size_t i = 0, j = 0, ii = 0;
key c, c_old, L, R, Hi;
@@ -148,13 +150,22 @@ namespace rct {
toHash[0] = message;
DP("here1");
for (i = 0; i < dsRows; i++) {
- skpkGen(alpha[i], aG[i]); //need to save alphas for later..
- Hi = hashToPoint(pk[index][i]);
- aHP[i] = scalarmultKey(Hi, alpha[i]);
toHash[3 * i + 1] = pk[index][i];
- toHash[3 * i + 2] = aG[i];
- toHash[3 * i + 3] = aHP[i];
- rv.II[i] = scalarmultKey(Hi, xx[i]);
+ if (kLRki) {
+ // multisig
+ alpha[i] = kLRki->k;
+ toHash[3 * i + 2] = kLRki->L;
+ toHash[3 * i + 3] = kLRki->R;
+ rv.II[i] = kLRki->ki;
+ }
+ else {
+ Hi = hashToPoint(pk[index][i]);
+ skpkGen(alpha[i], aG[i]); //need to save alphas for later..
+ aHP[i] = scalarmultKey(Hi, alpha[i]);
+ toHash[3 * i + 2] = aG[i];
+ toHash[3 * i + 3] = aHP[i];
+ rv.II[i] = scalarmultKey(Hi, xx[i]);
+ }
precomp(Ip[i].k, rv.II[i]);
}
size_t ndsRows = 3 * dsRows; //non Double Spendable Rows (see identity chains paper)
@@ -198,7 +209,9 @@ namespace rct {
}
for (j = 0; j < rows; j++) {
sc_mulsub(rv.ss[index][j].bytes, c.bytes, xx[j].bytes, alpha[j].bytes);
- }
+ }
+ if (mscout)
+ *mscout = c;
return rv;
}
@@ -393,7 +406,7 @@ namespace rct {
// this shows that sum inputs = sum outputs
//Ver:
// verifies the above sig is created corretly
- mgSig proveRctMG(const key &message, const ctkeyM & pubs, const ctkeyV & inSk, const ctkeyV &outSk, const ctkeyV & outPk, unsigned int index, key txnFeeKey) {
+ mgSig proveRctMG(const key &message, const ctkeyM & pubs, const ctkeyV & inSk, const ctkeyV &outSk, const ctkeyV & outPk, const multisig_kLRki *kLRki, key *mscout, unsigned int index, key txnFeeKey) {
mgSig mg;
//setup vars
size_t cols = pubs.size();
@@ -405,6 +418,7 @@ namespace rct {
}
CHECK_AND_ASSERT_THROW_MES(inSk.size() == rows, "Bad inSk size");
CHECK_AND_ASSERT_THROW_MES(outSk.size() == outPk.size(), "Bad outSk/outPk size");
+ CHECK_AND_ASSERT_THROW_MES((kLRki && mscout) || (!kLRki && !mscout), "Only one of kLRki/mscout is present");
keyV sk(rows + 1);
keyV tmp(rows + 1);
@@ -437,7 +451,7 @@ namespace rct {
for (size_t j = 0; j < outPk.size(); j++) {
sc_sub(sk[rows].bytes, sk[rows].bytes, outSk[j].mask.bytes); //subtract output masks in last row..
}
- return MLSAG_Gen(message, M, sk, index, rows);
+ return MLSAG_Gen(message, M, sk, kLRki, mscout, index, rows);
}
@@ -448,12 +462,13 @@ namespace rct {
// inSk is x, a_in corresponding to signing index
// a_out, Cout is for the output commitment
// index is the signing index..
- mgSig proveRctMGSimple(const key &message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, unsigned int index) {
+ mgSig proveRctMGSimple(const key &message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, const multisig_kLRki *kLRki, key *mscout, unsigned int index) {
mgSig mg;
//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;
@@ -464,7 +479,7 @@ namespace rct {
sk[0] = copy(inSk.dest);
sc_sub(sk[1].bytes, inSk.mask.bytes, a.bytes);
}
- return MLSAG_Gen(message, M, sk, index, rows);
+ return MLSAG_Gen(message, M, sk, kLRki, mscout, index, rows);
}
@@ -598,13 +613,14 @@ namespace rct {
// must know the destination private key to find the correct amount, else will return a random number
// Note: For txn fees, the last index in the amounts vector should contain that
// Thus the amounts vector will be "one" longer than the destinations vectort
- rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk, bool bulletproof) {
+ rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, bool bulletproof) {
CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations");
CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations");
CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing");
for (size_t n = 0; n < mixRing.size(); ++n) {
CHECK_AND_ASSERT_THROW_MES(mixRing[n].size() == inSk.size(), "Bad mixRing size");
}
+ CHECK_AND_ASSERT_THROW_MES((kLRki && msout) || (!kLRki && !msout), "Only one of kLRki/msout is present");
rctSig rv;
rv.type = bulletproof ? RCTTypeFullBulletproof : RCTTypeFull;
@@ -653,21 +669,23 @@ namespace rct {
key txnFeeKey = scalarmultH(d2h(rv.txnFee));
rv.mixRing = mixRing;
- rv.p.MGs.push_back(proveRctMG(get_pre_mlsag_hash(rv), rv.mixRing, inSk, outSk, rv.outPk, index, txnFeeKey));
+ if (msout)
+ msout->c.resize(1);
+ rv.p.MGs.push_back(proveRctMG(get_pre_mlsag_hash(rv), rv.mixRing, inSk, outSk, rv.outPk, kLRki, msout ? &msout->c[0] : NULL, index, txnFeeKey));
return rv;
}
- rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> & amounts, const keyV &amount_keys, const int mixin) {
+ rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> & amounts, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, const int mixin) {
unsigned int index;
ctkeyM mixRing;
ctkeyV outSk;
tie(mixRing, index) = populateFromBlockchain(inPk, mixin);
- return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, index, outSk, false);
+ return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, kLRki, msout, index, outSk, false);
}
//RCT simple
//for post-rct only
- rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk, bool bulletproof) {
+ rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, const std::vector<unsigned int> & index, ctkeyV &outSk, bool bulletproof) {
CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts");
CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk");
CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations");
@@ -677,6 +695,10 @@ namespace rct {
for (size_t n = 0; n < mixRing.size(); ++n) {
CHECK_AND_ASSERT_THROW_MES(index[n] < mixRing[n].size(), "Bad index into mixRing");
}
+ CHECK_AND_ASSERT_THROW_MES((kLRki && msout) || (!kLRki && !msout), "Only one of kLRki/msout is present");
+ if (kLRki && msout) {
+ CHECK_AND_ASSERT_THROW_MES(kLRki->size() == inamounts.size(), "Mismatched kLRki/inamounts sizes");
+ }
rctSig rv;
rv.type = bulletproof ? RCTTypeSimpleBulletproof : RCTTypeSimple;
@@ -736,13 +758,15 @@ namespace rct {
DP(rv.pseudoOuts[i]);
key full_message = get_pre_mlsag_hash(rv);
+ 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], rv.pseudoOuts[i], index[i]);
+ rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], rv.pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, index[i]);
}
return rv;
}
- rctSig genRctSimple(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin) {
+ rctSig genRctSimple(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, xmr_amount txnFee, unsigned int mixin) {
std::vector<unsigned int> index;
index.resize(inPk.size());
ctkeyM mixRing;
@@ -752,7 +776,7 @@ namespace rct {
mixRing[i].resize(mixin+1);
index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin);
}
- return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk, false);
+ return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, kLRki, msout, index, outSk, false);
}
//RingCT protocol
@@ -822,8 +846,14 @@ namespace rct {
return true;
}
- catch(...)
+ catch (const std::exception &e)
{
+ LOG_PRINT_L1("Error in verRct: " << e.what());
+ return false;
+ }
+ catch (...)
+ {
+ LOG_PRINT_L1("Error in verRct, but not an actual exception");
return false;
}
}
@@ -920,7 +950,16 @@ namespace rct {
return true;
}
// we can get deep throws from ge_frombytes_vartime if input isn't valid
- catch (...) { return false; }
+ catch (const std::exception &e)
+ {
+ LOG_PRINT_L1("Error in verRct: " << e.what());
+ return false;
+ }
+ catch (...)
+ {
+ LOG_PRINT_L1("Error in verRct, but not an actual exception");
+ return false;
+ }
}
//RingCT protocol
@@ -988,4 +1027,27 @@ namespace rct {
key mask;
return decodeRctSimple(rv, sk, i, mask);
}
+
+ bool signMultisig(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 == RCTTypeFullBulletproof || rv.type == RCTTypeSimpleBulletproof,
+ 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");
+ if (rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof)
+ {
+ CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "MGs not a single element");
+ }
+ for (size_t n = 0; n < indices.size(); ++n) {
+ CHECK_AND_ASSERT_MES(indices[n] < rv.p.MGs[n].ss.size(), false, "Index out of range");
+ CHECK_AND_ASSERT_MES(!rv.p.MGs[n].ss[indices[n]].empty(), false, "empty ss line");
+ }
+
+ 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);
+ sc_add(rv.p.MGs[n].ss[indices[n]][0].bytes, rv.p.MGs[n].ss[indices[n]][0].bytes, diff.bytes);
+ }
+ return true;
+ }
}
diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h
index 46c9cb2df..e83083a98 100644
--- a/src/ringct/rctSigs.h
+++ b/src/ringct/rctSigs.h
@@ -73,7 +73,7 @@ namespace rct {
// the signer knows a secret key for each row in that column
// Ver verifies that the MG sig was created correctly
keyV keyImageV(const keyV &xx);
- mgSig MLSAG_Gen(const key &message, const keyM & pk, const keyV & xx, const unsigned int index, size_t dsRows);
+ 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);
bool MLSAG_Ver(const key &message, const keyM &pk, const mgSig &sig, size_t dsRows);
//mgSig MLSAG_Gen_Old(const keyM & pk, const keyV & xx, const int index);
@@ -95,8 +95,8 @@ namespace rct {
// this shows that sum inputs = sum outputs
//Ver:
// verifies the above sig is created corretly
- mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const keyV &outMasks, const ctkeyV & outPk, unsigned int index, key txnFee, const key &message);
- mgSig proveRctMGSimple(const key & message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, unsigned int index);
+ mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const keyV &outMasks, const ctkeyV & outPk, const multisig_kLRki *kLRki, key *mscout, unsigned int index, key txnFee, const key &message);
+ mgSig proveRctMGSimple(const key & message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, const multisig_kLRki *kLRki, key *mscout, unsigned int index);
bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFee, const key &message);
bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C);
@@ -118,10 +118,10 @@ namespace rct {
//decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1)
// 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
- rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk, bool bulletproof);
- rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const keyV &amount_keys, const int mixin);
- rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin);
- rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk, bool bulletproof);
+ rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, bool bulletproof);
+ rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, const int mixin);
+ rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, xmr_amount txnFee, unsigned int mixin);
+ rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, const std::vector<unsigned int> & index, ctkeyV &outSk, bool bulletproof);
bool verRct(const rctSig & rv, bool semantics);
static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); }
bool verRctSimple(const rctSig & rv, bool semantics);
@@ -130,6 +130,8 @@ namespace rct {
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i);
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask);
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i);
+
+ bool signMultisig(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key);
}
#endif /* RCTSIGS_H */
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
index 50dfdb432..5ea2dcc7c 100644
--- a/src/ringct/rctTypes.h
+++ b/src/ringct/rctTypes.h
@@ -99,6 +99,22 @@ namespace rct {
typedef std::vector<ctkey> ctkeyV;
typedef std::vector<ctkeyV> ctkeyM;
+ //used for multisig data
+ struct multisig_kLRki {
+ key k;
+ key L;
+ key R;
+ key ki;
+ };
+
+ struct multisig_out {
+ std::vector<key> c; // for all inputs
+
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(c)
+ END_SERIALIZE()
+ };
+
//data for passing the amount to the receiver secretly
// If the pedersen commitment to an amount is C = aG + bH,
// "mask" contains a 32 byte key a
@@ -501,9 +517,15 @@ inline std::ostream &operator <<(std::ostream &o, const rct::key &v) {
}
+namespace std
+{
+ template<> struct hash<rct::key> { std::size_t operator()(const rct::key &k) const { return reinterpret_cast<const std::size_t&>(k); } };
+}
+
BLOB_SERIALIZER(rct::key);
BLOB_SERIALIZER(rct::key64);
BLOB_SERIALIZER(rct::ctkey);
+BLOB_SERIALIZER(rct::multisig_kLRki);
BLOB_SERIALIZER(rct::boroSig);
VARIANT_TAG(debug_archive, rct::key, "rct::key");
@@ -519,6 +541,8 @@ VARIANT_TAG(debug_archive, rct::rangeSig, "rct::rangeSig");
VARIANT_TAG(debug_archive, rct::boroSig, "rct::boroSig");
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(binary_archive, rct::key, 0x90);
VARIANT_TAG(binary_archive, rct::key64, 0x91);
@@ -533,6 +557,8 @@ VARIANT_TAG(binary_archive, rct::rangeSig, 0x99);
VARIANT_TAG(binary_archive, rct::boroSig, 0x9a);
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(json_archive, rct::key, "rct_key");
VARIANT_TAG(json_archive, rct::key64, "rct_key64");
@@ -547,5 +573,7 @@ VARIANT_TAG(json_archive, rct::rangeSig, "rct_rangeSig");
VARIANT_TAG(json_archive, rct::boroSig, "rct_boroSig");
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");
#endif /* RCTTYPES_H */