|
4 | 4 |
|
5 | 5 | #include "libzerocoin/Denominations.h" |
6 | 6 | #include "libzerocoin/Coin.h" |
| 7 | +#include "libzerocoin/CoinRandomnessSchnorrSignature.h" |
7 | 8 | #include "amount.h" |
8 | 9 | #include "chainparams.h" |
9 | 10 | #include "coincontrol.h" |
@@ -56,64 +57,234 @@ BOOST_AUTO_TEST_CASE(zerocoin_spend_test) |
56 | 57 |
|
57 | 58 | } |
58 | 59 |
|
| 60 | +BOOST_AUTO_TEST_CASE(zerocoin_schnorr_signature_test) |
| 61 | +{ |
| 62 | + const int NUM_OF_TESTS = 50; |
| 63 | + SelectParams(CBaseChainParams::MAIN); |
| 64 | + libzerocoin::ZerocoinParams *ZCParams_v1 = Params().Zerocoin_Params(true); |
| 65 | + (void)ZCParams_v1; |
| 66 | + libzerocoin::ZerocoinParams *ZCParams_v2 = Params().Zerocoin_Params(false); |
| 67 | + (void)ZCParams_v2; |
| 68 | + |
| 69 | + for (int i=0; i<NUM_OF_TESTS; i++) { |
| 70 | + |
| 71 | + // mint a v1 coin |
| 72 | + CBigNum s = CBigNum::randBignum(ZCParams_v1->coinCommitmentGroup.groupOrder); |
| 73 | + CBigNum r = CBigNum::randBignum(ZCParams_v1->coinCommitmentGroup.groupOrder); |
| 74 | + CBigNum c = ZCParams_v1->coinCommitmentGroup.g.pow_mod(s, ZCParams_v1->coinCommitmentGroup.modulus).mul_mod( |
| 75 | + ZCParams_v1->coinCommitmentGroup.h.pow_mod(r, ZCParams_v1->coinCommitmentGroup.modulus), ZCParams_v1->coinCommitmentGroup.modulus); |
| 76 | + for (uint32_t attempt = 0; attempt < MAX_COINMINT_ATTEMPTS; attempt++) { |
| 77 | + if (c.isPrime(ZEROCOIN_MINT_PRIME_PARAM)) break; |
| 78 | + CBigNum r_delta = CBigNum::randBignum(ZCParams_v1->coinCommitmentGroup.groupOrder); |
| 79 | + r = (r + r_delta) % ZCParams_v1->coinCommitmentGroup.groupOrder; |
| 80 | + c = ZCParams_v1->coinCommitmentGroup.g.pow_mod(s, ZCParams_v1->coinCommitmentGroup.modulus).mul_mod( |
| 81 | + ZCParams_v1->coinCommitmentGroup.h.pow_mod(r, ZCParams_v1->coinCommitmentGroup.modulus), ZCParams_v1->coinCommitmentGroup.modulus); |
| 82 | + } |
| 83 | + BOOST_CHECK_MESSAGE(c.isPrime(ZEROCOIN_MINT_PRIME_PARAM), "Unable to mint v1 coin"); |
| 84 | + libzerocoin::PrivateCoin privCoin_v1(ZCParams_v1, libzerocoin::CoinDenomination::ZQ_ONE, s, r); |
| 85 | + const CBigNum randomness_v1 = privCoin_v1.getRandomness(); |
| 86 | + const CBigNum pubCoinValue_v1 = privCoin_v1.getPublicCoin().getValue(); |
| 87 | + const CBigNum serialNumber_v1 = privCoin_v1.getSerialNumber(); |
| 88 | + |
| 89 | + // mint a v2 coin |
| 90 | + libzerocoin::PrivateCoin privCoin_v2(ZCParams_v2, libzerocoin::CoinDenomination::ZQ_ONE, true); |
| 91 | + const CBigNum randomness_v2 = privCoin_v2.getRandomness(); |
| 92 | + const CBigNum pubCoinValue_v2 = privCoin_v2.getPublicCoin().getValue(); |
| 93 | + const CBigNum serialNumber_v2 = privCoin_v2.getSerialNumber(); |
| 94 | + |
| 95 | + // get a random msghash |
| 96 | + const uint256 msghash = CBigNum::randKBitBignum(256).getuint256(); |
| 97 | + |
| 98 | + // sign the msghash with the randomness of the v1 coin |
| 99 | + libzerocoin::CoinRandomnessSchnorrSignature crss_v1(ZCParams_v1, randomness_v1, msghash); |
| 100 | + CDataStream ser_crss_v1(SER_NETWORK, PROTOCOL_VERSION); |
| 101 | + ser_crss_v1 << crss_v1; |
| 102 | + |
| 103 | + // sign the msghash with the randomness of the v2 coin |
| 104 | + libzerocoin::CoinRandomnessSchnorrSignature crss_v2(ZCParams_v2, randomness_v2, msghash); |
| 105 | + CDataStream ser_crss_v2(SER_NETWORK, PROTOCOL_VERSION); |
| 106 | + ser_crss_v2 << crss_v2; |
| 107 | + |
| 108 | + // unserialize the v1 signature into a fresh object and verify it |
| 109 | + libzerocoin::CoinRandomnessSchnorrSignature new_crss_v1(ser_crss_v1); |
| 110 | + BOOST_CHECK_MESSAGE( |
| 111 | + new_crss_v1.Verify(ZCParams_v1, serialNumber_v1, pubCoinValue_v1, msghash), |
| 112 | + "Failed to verify schnorr signature with v1 coin" |
| 113 | + ); |
| 114 | + |
| 115 | + // unserialize the v2 signature into a fresh object and verify it |
| 116 | + libzerocoin::CoinRandomnessSchnorrSignature new_crss_v2(ser_crss_v2); |
| 117 | + BOOST_CHECK_MESSAGE( |
| 118 | + new_crss_v2.Verify(ZCParams_v2, serialNumber_v2, pubCoinValue_v2, msghash), |
| 119 | + "Failed to verify schnorr signature with v2 coin" |
| 120 | + ); |
| 121 | + |
| 122 | + // verify failure on different msghash |
| 123 | + uint256 msghash2; |
| 124 | + do { |
| 125 | + msghash2 = CBigNum::randKBitBignum(256).getuint256(); |
| 126 | + } while (msghash2 == msghash); |
| 127 | + BOOST_CHECK_MESSAGE( |
| 128 | + !new_crss_v1.Verify(ZCParams_v1, serialNumber_v1, pubCoinValue_v1, msghash2), |
| 129 | + "schnorr signature with v1 coin verifies on wrong msghash" |
| 130 | + ); |
| 131 | + BOOST_CHECK_MESSAGE( |
| 132 | + !new_crss_v2.Verify(ZCParams_v2, serialNumber_v2, pubCoinValue_v2, msghash2), |
| 133 | + "schnorr signature with v2 coin verifies on wrong msghash" |
| 134 | + ); |
| 135 | + |
| 136 | + // verify failure swapping serials |
| 137 | + BOOST_CHECK_MESSAGE( |
| 138 | + !new_crss_v1.Verify(ZCParams_v1, serialNumber_v2, pubCoinValue_v1, msghash), |
| 139 | + "schnorr signature with v1 coin verifies on wrong serial" |
| 140 | + ); |
| 141 | + BOOST_CHECK_MESSAGE( |
| 142 | + !new_crss_v2.Verify(ZCParams_v2, serialNumber_v1, pubCoinValue_v2, msghash), |
| 143 | + "schnorr signature with v2 coin verifies on wrong serial" |
| 144 | + ); |
| 145 | + |
| 146 | + // verify failure swapping public coins |
| 147 | + BOOST_CHECK_MESSAGE( |
| 148 | + !new_crss_v1.Verify(ZCParams_v1, serialNumber_v1, pubCoinValue_v2, msghash), |
| 149 | + "schnorr signature with v1 coin verifies on wrong public coin value" |
| 150 | + ); |
| 151 | + BOOST_CHECK_MESSAGE( |
| 152 | + !new_crss_v2.Verify(ZCParams_v2, serialNumber_v2, pubCoinValue_v1, msghash), |
| 153 | + "schnorr signature with v2 coin verifies on wrong public coin value" |
| 154 | + ); |
| 155 | + |
| 156 | + } |
| 157 | + |
| 158 | +} |
| 159 | + |
59 | 160 | BOOST_AUTO_TEST_CASE(zerocoin_public_spend_test) |
60 | 161 | { |
61 | 162 | SelectParams(CBaseChainParams::MAIN); |
62 | | - libzerocoin::ZerocoinParams *ZCParams = Params().Zerocoin_Params(false); |
63 | | - (void)ZCParams; |
| 163 | + libzerocoin::ZerocoinParams *ZCParams_v1 = Params().Zerocoin_Params(true); |
| 164 | + libzerocoin::ZerocoinParams *ZCParams_v2 = Params().Zerocoin_Params(false); |
| 165 | + (void)ZCParams_v1; |
| 166 | + (void)ZCParams_v2; |
64 | 167 |
|
65 | | - libzerocoin::PrivateCoin privCoin(ZCParams, libzerocoin::CoinDenomination::ZQ_ONE, true); |
66 | | - const CPrivKey privKey = privCoin.getPrivKey(); |
| 168 | + // create v1 coin |
| 169 | + CBigNum s = CBigNum::randBignum(ZCParams_v1->coinCommitmentGroup.groupOrder); |
| 170 | + CBigNum r = CBigNum::randBignum(ZCParams_v1->coinCommitmentGroup.groupOrder); |
| 171 | + CBigNum c = ZCParams_v1->coinCommitmentGroup.g.pow_mod(s, ZCParams_v1->coinCommitmentGroup.modulus).mul_mod( |
| 172 | + ZCParams_v1->coinCommitmentGroup.h.pow_mod(r, ZCParams_v1->coinCommitmentGroup.modulus), ZCParams_v1->coinCommitmentGroup.modulus); |
| 173 | + for (uint32_t attempt = 0; attempt < MAX_COINMINT_ATTEMPTS; attempt++) { |
| 174 | + if (c.isPrime(ZEROCOIN_MINT_PRIME_PARAM)) break; |
| 175 | + CBigNum r_delta = CBigNum::randBignum(ZCParams_v1->coinCommitmentGroup.groupOrder); |
| 176 | + r = (r + r_delta) % ZCParams_v1->coinCommitmentGroup.groupOrder; |
| 177 | + c = ZCParams_v1->coinCommitmentGroup.g.pow_mod(s, ZCParams_v1->coinCommitmentGroup.modulus).mul_mod( |
| 178 | + ZCParams_v1->coinCommitmentGroup.h.pow_mod(r, ZCParams_v1->coinCommitmentGroup.modulus), ZCParams_v1->coinCommitmentGroup.modulus); |
| 179 | + } |
| 180 | + BOOST_CHECK_MESSAGE(c.isPrime(ZEROCOIN_MINT_PRIME_PARAM), "Unable to mint v1 coin"); |
| 181 | + libzerocoin::PrivateCoin privCoin_v1(ZCParams_v1, libzerocoin::CoinDenomination::ZQ_ONE, s, r); |
67 | 182 |
|
68 | | - CZerocoinMint mint = CZerocoinMint( |
69 | | - privCoin.getPublicCoin().getDenomination(), |
70 | | - privCoin.getPublicCoin().getValue(), |
71 | | - privCoin.getRandomness(), |
72 | | - privCoin.getSerialNumber(), |
| 183 | + CZerocoinMint mint_v1 = CZerocoinMint( |
| 184 | + privCoin_v1.getPublicCoin().getDenomination(), |
| 185 | + privCoin_v1.getPublicCoin().getValue(), |
| 186 | + privCoin_v1.getRandomness(), |
| 187 | + privCoin_v1.getSerialNumber(), |
73 | 188 | false, |
74 | | - privCoin.getVersion(), |
| 189 | + 1, |
75 | 190 | nullptr); |
76 | | - mint.SetPrivKey(privKey); |
77 | 191 |
|
| 192 | + // create v2 coin |
| 193 | + libzerocoin::PrivateCoin privCoin_v2(ZCParams_v2, libzerocoin::CoinDenomination::ZQ_ONE, true); |
| 194 | + CPrivKey privKey = privCoin_v2.getPrivKey(); |
78 | 195 |
|
79 | | - // Mint tx |
80 | | - CTransaction prevTx; |
| 196 | + CZerocoinMint mint_v2 = CZerocoinMint( |
| 197 | + privCoin_v2.getPublicCoin().getDenomination(), |
| 198 | + privCoin_v2.getPublicCoin().getValue(), |
| 199 | + privCoin_v2.getRandomness(), |
| 200 | + privCoin_v2.getSerialNumber(), |
| 201 | + false, |
| 202 | + privCoin_v2.getVersion(), |
| 203 | + &privKey); |
81 | 204 |
|
82 | | - CScript scriptSerializedCoin = CScript() |
83 | | - << OP_ZEROCOINMINT << privCoin.getPublicCoin().getValue().getvch().size() << privCoin.getPublicCoin().getValue().getvch(); |
84 | | - CTxOut out = CTxOut(libzerocoin::ZerocoinDenominationToAmount(privCoin.getPublicCoin().getDenomination()), scriptSerializedCoin); |
85 | | - prevTx.vout.push_back(out); |
| 205 | + // Mint txs |
| 206 | + CTransaction prevTx_v1; |
| 207 | + CTransaction prevTx_v2; |
86 | 208 |
|
87 | | - mint.SetOutputIndex(0); |
88 | | - mint.SetTxHash(prevTx.GetHash()); |
| 209 | + CScript scriptSerializedCoin_v1 = CScript() |
| 210 | + << OP_ZEROCOINMINT << privCoin_v1.getPublicCoin().getValue().getvch().size() << privCoin_v1.getPublicCoin().getValue().getvch(); |
| 211 | + CTxOut out_v1 = CTxOut(libzerocoin::ZerocoinDenominationToAmount(privCoin_v1.getPublicCoin().getDenomination()), scriptSerializedCoin_v1); |
| 212 | + prevTx_v1.vout.push_back(out_v1); |
89 | 213 |
|
90 | | - // Spend tx |
91 | | - CMutableTransaction tx; |
92 | | - tx.vout.resize(1); |
93 | | - tx.vout[0].nValue = 1*CENT; |
94 | | - tx.vout[0].scriptPubKey = GetScriptForDestination(CBitcoinAddress("D9Ti4LEhF1n6dR2hGd2SyNADD51AVgva6q").Get()); |
| 214 | + CScript scriptSerializedCoin_v2 = CScript() |
| 215 | + << OP_ZEROCOINMINT << privCoin_v2.getPublicCoin().getValue().getvch().size() << privCoin_v2.getPublicCoin().getValue().getvch(); |
| 216 | + CTxOut out_v2 = CTxOut(libzerocoin::ZerocoinDenominationToAmount(privCoin_v2.getPublicCoin().getDenomination()), scriptSerializedCoin_v2); |
| 217 | + prevTx_v2.vout.push_back(out_v2); |
95 | 218 |
|
96 | | - CTxIn in; |
97 | | - if (!ZPIVModule::createInput(in, mint, tx.GetHash())){ |
98 | | - BOOST_CHECK_MESSAGE(false, "Failed to create zc input"); |
99 | | - } |
| 219 | + mint_v1.SetOutputIndex(0); |
| 220 | + mint_v1.SetTxHash(prevTx_v1.GetHash()); |
100 | 221 |
|
101 | | - PublicCoinSpend publicSpend(ZCParams); |
102 | | - if (!ZPIVModule::validateInput(in, out, tx, publicSpend)){ |
103 | | - BOOST_CHECK_MESSAGE(false, "Failed to validate zc input"); |
104 | | - } |
| 222 | + mint_v2.SetOutputIndex(0); |
| 223 | + mint_v2.SetTxHash(prevTx_v2.GetHash()); |
| 224 | + |
| 225 | + // Spend txs |
| 226 | + CMutableTransaction tx1, tx2, tx3; |
| 227 | + tx1.vout.resize(1); |
| 228 | + tx1.vout[0].nValue = 1*CENT; |
| 229 | + tx1.vout[0].scriptPubKey = GetScriptForDestination(CBitcoinAddress("D9Ti4LEhF1n6dR2hGd2SyNADD51AVgva6q").Get()); |
| 230 | + tx2.vout.resize(1); |
| 231 | + tx2.vout[0].nValue = 1*CENT; |
| 232 | + tx2.vout[0].scriptPubKey = GetScriptForDestination(CBitcoinAddress("D9Ti4LEhF1n6dR2hGd2SyNADD51AVgva6q").Get()); |
| 233 | + tx3.vout.resize(1); |
| 234 | + tx3.vout[0].nValue = 1*CENT; |
| 235 | + tx3.vout[0].scriptPubKey = GetScriptForDestination(CBitcoinAddress("D9Ti4LEhF1n6dR2hGd2SyNADD51AVgva6q").Get()); |
| 236 | + |
| 237 | + CTxIn in1, in2, in3; |
| 238 | + |
| 239 | + // check spendVersion = 3 for v2 coins |
| 240 | + // ----------------------------------- |
| 241 | + int spendVersion = 3; |
| 242 | + BOOST_CHECK_MESSAGE(ZPIVModule::createInput(in1, mint_v2, tx1.GetHash(), spendVersion), |
| 243 | + "Failed to create zc input for mint v2 and spendVersion 3"); |
| 244 | + |
| 245 | + std::cout << "Spend v3 size: " << ::GetSerializeSize(in1, SER_NETWORK, PROTOCOL_VERSION) << " bytes" << std::endl; |
| 246 | + |
| 247 | + PublicCoinSpend publicSpend1(ZCParams_v2); |
| 248 | + BOOST_CHECK_MESSAGE(ZPIVModule::validateInput(in1, out_v2, tx1, publicSpend1), |
| 249 | + "Failed to validate zc input for mint v2 and spendVersion 3"); |
| 250 | + |
| 251 | + // Verify that it fails with a different denomination |
| 252 | + in1.nSequence = 500; |
| 253 | + PublicCoinSpend publicSpend1b(ZCParams_v2); |
| 254 | + BOOST_CHECK_MESSAGE(!ZPIVModule::validateInput(in1, out_v2, tx1, publicSpend1b), "Different denomination for mint v2 and spendVersion 3"); |
| 255 | + |
| 256 | + // check spendVersion = 4 for v2 coins |
| 257 | + // ----------------------------------- |
| 258 | + spendVersion = 4; |
| 259 | + BOOST_CHECK_MESSAGE(ZPIVModule::createInput(in2, mint_v2, tx2.GetHash(), spendVersion), |
| 260 | + "Failed to create zc input for mint v2 and spendVersion 4"); |
| 261 | + |
| 262 | + std::cout << "Spend v4 (coin v2) size: " << ::GetSerializeSize(in2, SER_NETWORK, PROTOCOL_VERSION) << " bytes" << std::endl; |
| 263 | + |
| 264 | + PublicCoinSpend publicSpend2(ZCParams_v2); |
| 265 | + BOOST_CHECK_MESSAGE(ZPIVModule::validateInput(in2, out_v2, tx2, publicSpend2), |
| 266 | + "Failed to validate zc input for mint v2 and spendVersion 4"); |
| 267 | + |
| 268 | + // Verify that it fails with a different denomination |
| 269 | + in2.nSequence = 500; |
| 270 | + PublicCoinSpend publicSpend2b(ZCParams_v2); |
| 271 | + BOOST_CHECK_MESSAGE(!ZPIVModule::validateInput(in2, out_v2, tx2, publicSpend2b), "Different denomination for mint v2 and spendVersion 4"); |
| 272 | + |
| 273 | + // check spendVersion = 4 for v1 coins |
| 274 | + // ----------------------------------- |
| 275 | + BOOST_CHECK_MESSAGE(ZPIVModule::createInput(in3, mint_v1, tx3.GetHash(), spendVersion), |
| 276 | + "Failed to create zc input for mint v1 and spendVersion 4"); |
105 | 277 |
|
106 | | - PublicCoinSpend publicSpendTest(ZCParams); |
107 | | - BOOST_CHECK_MESSAGE(ZPIVModule::parseCoinSpend(in, tx, out, publicSpendTest), "Failed to parse public spend"); |
108 | | - libzerocoin::CoinSpend *spend = &publicSpendTest; |
| 278 | + std::cout << "Spend v4 (coin v1) size: " << ::GetSerializeSize(in3, SER_NETWORK, PROTOCOL_VERSION) << " bytes" << std::endl; |
109 | 279 |
|
110 | | - BOOST_CHECK_MESSAGE(publicSpendTest.HasValidSignature(), "Failed to validate public spend signature"); |
111 | | - BOOST_CHECK_MESSAGE(spend->HasValidSignature(), "Failed to validate spend signature"); |
| 280 | + PublicCoinSpend publicSpend3(ZCParams_v1); |
| 281 | + BOOST_CHECK_MESSAGE(ZPIVModule::validateInput(in3, out_v1, tx3, publicSpend3), |
| 282 | + "Failed to validate zc input for mint v1 and spendVersion 4"); |
112 | 283 |
|
113 | | - // Verify that fails with a different denomination |
114 | | - in.nSequence = 500; |
115 | | - PublicCoinSpend publicSpend2(ZCParams); |
116 | | - BOOST_CHECK_MESSAGE(!ZPIVModule::validateInput(in, out, tx, publicSpend2), "Different denomination"); |
| 284 | + // Verify that it fails with a different denomination |
| 285 | + in3.nSequence = 500; |
| 286 | + PublicCoinSpend publicSpend3b(ZCParams_v1); |
| 287 | + BOOST_CHECK_MESSAGE(!ZPIVModule::validateInput(in3, out_v1, tx3, publicSpend3b), "Different denomination for mint v1 and spendVersion 4"); |
117 | 288 |
|
118 | 289 | } |
119 | 290 |
|
|
0 commit comments