WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl) { qint64 total = 0; QList<SendCoinsRecipient> recipients = transaction.getRecipients(); std::vector<std::pair<CScript, int64_t> > vecSend; if(recipients.empty()) { return OK; } QSet<QString> setAddress; // Used to detect duplicates int nAddresses = 0; // Pre-check input data for validity foreach(const SendCoinsRecipient &rcp, recipients) { if (rcp.paymentRequest.IsInitialized()) { // PaymentRequest... int64_t subtotal = 0; const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); for (int i = 0; i < details.outputs_size(); i++) { const payments::Output& out = details.outputs(i); if (out.amount() <= 0) continue; subtotal += out.amount(); const unsigned char* scriptStr = (const unsigned char*)out.script().data(); CScript scriptPubKey(scriptStr, scriptStr+out.script().size()); vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, out.amount())); } if (subtotal <= 0) { return InvalidAmount; } total += subtotal; } else { // User-entered bitcoin address / amount: if(!validateAddress(rcp.address)) { return InvalidAddress; } if(rcp.amount <= 0) { return InvalidAmount; } setAddress.insert(rcp.address); ++nAddresses; // stealth std::string sAddr = rcp.address.toStdString(); if (rcp.typeInd == AddressTableModel::AT_Stealth) { if ( (!TestNet() && (chainActive.Height() < BLOCK_STEALTH_START)) || (TestNet() && (chainActive.Height() < 200)) ) { emit message(tr("Send Coins"), tr("Stealth addresses not yet supported"), CClientUIInterface::MSG_ERROR); return InvalidAddress; } CStealthAddress sxAddr; if (sxAddr.SetEncoded(sAddr)) { ec_secret ephem_secret; ec_secret secretShared; ec_point pkSendTo; ec_point ephem_pubkey; if (GenerateRandomSecret(ephem_secret) != 0) { LogPrintf("GenerateRandomSecret failed.\n"); return InvalidAddress; }; if (StealthSecret(ephem_secret, sxAddr.scan_pubkey, sxAddr.spend_pubkey, secretShared, pkSendTo) != 0) { LogPrintf("Could not generate receiving public key.\n"); return InvalidAddress; }; CPubKey cpkTo(pkSendTo); if (!cpkTo.IsValid()) { LogPrintf("Invalid public key generated.\n"); return InvalidAddress; }; CKeyID ckidTo = cpkTo.GetID(); CBitcoinAddress addrTo(ckidTo); if (SecretToPublicKey(ephem_secret, ephem_pubkey) != 0) { LogPrintf("Could not generate ephem public key.\n"); return InvalidAddress; }; if (fDebug) { LogPrintf("Stealth send to generated pubkey %ui: %s\n", pkSendTo.size(), HexStr(pkSendTo).c_str()); LogPrintf("hash %s\n", addrTo.ToString().c_str()); LogPrintf("ephem_pubkey %ui: %s\n", ephem_pubkey.size(), HexStr(ephem_pubkey).c_str()); }; CScript scriptPubKey; scriptPubKey.SetDestination(addrTo.Get()); vecSend.push_back(make_pair(scriptPubKey, rcp.amount)); CScript scriptP = CScript() << OP_RETURN << ephem_pubkey; vecSend.push_back(make_pair(scriptP, 0)); continue; }; // else drop through to normal } CScript scriptPubKey; scriptPubKey.SetDestination(CBitcoinAddress(sAddr).Get()); vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, rcp.amount)); total += rcp.amount; } } if(setAddress.size() != nAddresses) { return DuplicateAddress; } qint64 nBalance = getBalance(coinControl); if(total > nBalance) { return AmountExceedsBalance; } if((total + nTransactionFee) > nBalance) { transaction.setTransactionFee(nTransactionFee); return SendCoinsReturn(AmountWithFeeExceedsBalance); } { LOCK2(cs_main, wallet->cs_wallet); transaction.newPossibleKeyChange(wallet); int64_t nFeeRequired = 0; std::string strFailReason; CWalletTx *newTx = transaction.getTransaction(); CReserveKey *keyChange = transaction.getPossibleKeyChange(); // int nChangePos = -1; bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, /*nChangePos,*/ strFailReason, coinControl); /* std::map<int, std::string>::iterator it; for (it = mapStealthNarr.begin(); it != mapStealthNarr.end(); ++it) { int pos = it->first; if (nChangePos > -1 && it->first >= nChangePos) pos++; char key[64]; if (snprintf(key, sizeof(key), "n_%u", pos) < 1) { printf("CreateStealthTransaction(): Error creating narration key."); continue; }; wtx.mapValue[key] = it->second; }; */ transaction.setTransactionFee(nFeeRequired); if(!fCreated) { if((total + nFeeRequired) > nBalance) { return SendCoinsReturn(AmountWithFeeExceedsBalance); } emit message(tr("Send Coins"), QString::fromStdString(strFailReason), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } } return SendCoinsReturn(OK); }
int generateRingSignatureAB(data_chunk &keyImage, uint256 &txnHash, int nRingSize, int nSecretOffset, ec_secret secret, const uint8_t *pPubkeys, data_chunk &sigC, uint8_t *pSigS) { // https://bitcointalk.org/index.php?topic=972541.msg10619684 if (fDebugRingSig) LogPrintf("%s: Ring size %d.\n", __func__, nRingSize); assert(nRingSize < 200); RandAddSeedPerfmon(); memset(pSigS, 0, EC_SECRET_SIZE * nRingSize); int rv = 0; int nBytes; uint256 tmpPkHash; uint256 tmpHash; uint8_t tempData[66]; // hold raw point data to hash ec_secret sAlpha; if (0 != GenerateRandomSecret(sAlpha)) return errorN(1, "%s: GenerateRandomSecret failed.", __func__); CHashWriter ssPkHash(SER_GETHASH, PROTOCOL_VERSION); CHashWriter ssCjHash(SER_GETHASH, PROTOCOL_VERSION); uint256 test; for (int i = 0; i < nRingSize; ++i) { ssPkHash.write((const char*)&pPubkeys[i * EC_COMPRESSED_SIZE], EC_COMPRESSED_SIZE); if (i == nSecretOffset) continue; int k; // NOTE: necessary to clamp? for (k = 0; k < 32; ++k) { if (1 != RAND_bytes(&pSigS[i * EC_SECRET_SIZE], 32)) return errorN(1, "%s: RAND_bytes ERR_get_error %u.", __func__, ERR_get_error()); memcpy(test.begin(), &pSigS[i * EC_SECRET_SIZE], 32); if (test > MIN_SECRET && test < MAX_SECRET) break; }; if (k > 31) return errorN(1, "%s: Failed to generate a valid key.", __func__); }; tmpPkHash = ssPkHash.GetHash(); BN_CTX_start(bnCtx); BIGNUM *bnT = BN_CTX_get(bnCtx); BIGNUM *bnT2 = BN_CTX_get(bnCtx); BIGNUM *bnS = BN_CTX_get(bnCtx); BIGNUM *bnC = BN_CTX_get(bnCtx); BIGNUM *bnCj = BN_CTX_get(bnCtx); BIGNUM *bnA = BN_CTX_get(bnCtx); EC_POINT *ptKi = NULL; EC_POINT *ptPk = NULL; EC_POINT *ptT1 = NULL; EC_POINT *ptT2 = NULL; EC_POINT *ptT3 = NULL; EC_POINT *ptT4 = NULL; if ( !(ptKi = EC_POINT_new(ecGrp)) || !(ptPk = EC_POINT_new(ecGrp)) || !(ptT1 = EC_POINT_new(ecGrp)) || !(ptT2 = EC_POINT_new(ecGrp)) || !(ptT3 = EC_POINT_new(ecGrp)) || !(ptT4 = EC_POINT_new(ecGrp))) { LogPrintf("%s: EC_POINT_new failed.\n", __func__); rv = 1; goto End; }; // get keyimage as point if (!EC_POINT_oct2point(ecGrp, ptKi, &keyImage[0], EC_COMPRESSED_SIZE, bnCtx)) { LogPrintf("%s: extract ptKi failed.\n", __func__); rv = 1; goto End; }; // c_{j+1} = h(P_1,...,P_n,alpha*G,alpha*H(P_j)) if (!bnA || !(BN_bin2bn(&sAlpha.e[0], EC_SECRET_SIZE, bnA))) { LogPrintf("%s: BN_bin2bn failed.\n", __func__); rv = 1; goto End; }; // ptT1 = alpha * G if (!EC_POINT_mul(ecGrp, ptT1, bnA, NULL, NULL, bnCtx)) { LogPrintf("%s: EC_POINT_mul failed.\n", __func__); rv = 1; goto End; }; // ptT3 = H(Pj) if (hashToEC(&pPubkeys[nSecretOffset * EC_COMPRESSED_SIZE], EC_COMPRESSED_SIZE, bnT2, ptT3) != 0) { LogPrintf("%s: hashToEC failed.\n", __func__); rv = 1; goto End; }; ssCjHash.write((const char*)tmpPkHash.begin(), 32); // ptT2 = alpha * H(P_j) // ptT2 = alpha * ptT3 if (!EC_POINT_mul(ecGrp, ptT2, NULL, ptT3, bnA, bnCtx)) { LogPrintf("%s: EC_POINT_mul failed.\n", __func__); rv = 1; goto End; }; if ( !(EC_POINT_point2oct(ecGrp, ptT1, POINT_CONVERSION_COMPRESSED, &tempData[0], 33, bnCtx) == (int) EC_COMPRESSED_SIZE) || !(EC_POINT_point2oct(ecGrp, ptT2, POINT_CONVERSION_COMPRESSED, &tempData[33], 33, bnCtx) == (int) EC_COMPRESSED_SIZE)) { LogPrintf("%s: extract ptL and ptR failed.\n", __func__); rv = 1; goto End; }; ssCjHash.write((const char*)&tempData[0], 66); tmpHash = ssCjHash.GetHash(); if (!bnC || !(BN_bin2bn(tmpHash.begin(), EC_SECRET_SIZE, bnC)) // bnC lags i by 1 || !BN_mod(bnC, bnC, bnOrder, bnCtx)) { LogPrintf("%s: hash -> bnC failed.\n", __func__); rv = 1; goto End; }; // c_{j+2} = h(P_1,...,P_n,s_{j+1}*G+c_{j+1}*P_{j+1},s_{j+1}*H(P_{j+1})+c_{j+1}*I_j) for (int k = 0, ib = (nSecretOffset + 1) % nRingSize, i = (nSecretOffset + 2) % nRingSize; k < nRingSize; ++k, ib=i, i=(i+1) % nRingSize) { if (k == nRingSize - 1) { // s_j = alpha - c_j*x_j mod n. if (!bnT || !BN_bin2bn(&secret.e[0], EC_SECRET_SIZE, bnT)) { LogPrintf("%s: BN_bin2bn failed.\n", __func__); rv = 1; goto End; }; if (!BN_mul(bnT2, bnCj, bnT, bnCtx)) { LogPrintf("%s: BN_mul failed.\n", __func__); rv = 1; goto End; }; if (!BN_mod_sub(bnS, bnA, bnT2, bnOrder, bnCtx)) { LogPrintf("%s: BN_mod_sub failed.\n", __func__); rv = 1; goto End; }; if (!bnS || (nBytes = BN_num_bytes(bnS)) > (int) EC_SECRET_SIZE || BN_bn2bin(bnS, &pSigS[nSecretOffset * EC_SECRET_SIZE + (EC_SECRET_SIZE-nBytes)]) != nBytes) { LogPrintf("%s: bnS -> pSigS failed.\n", __func__); rv = 1; goto End; }; if (nSecretOffset != nRingSize - 1) break; }; if (!bnS || !(BN_bin2bn(&pSigS[ib * EC_SECRET_SIZE], EC_SECRET_SIZE, bnS))) { LogPrintf("%s: BN_bin2bn failed.\n", __func__); rv = 1; goto End; }; // bnC is from last round (ib) if (!EC_POINT_oct2point(ecGrp, ptPk, &pPubkeys[ib * EC_COMPRESSED_SIZE], EC_COMPRESSED_SIZE, bnCtx)) { LogPrintf("%s: EC_POINT_oct2point failed.\n", __func__); rv = 1; goto End; }; // ptT1 = s_{j+1}*G+c_{j+1}*P_{j+1} if (!EC_POINT_mul(ecGrp, ptT1, bnS, ptPk, bnC, bnCtx)) { LogPrintf("%s: EC_POINT_mul failed.\n", __func__); rv = 1; goto End; }; //s_{j+1}*H(P_{j+1})+c_{j+1}*I_j if (hashToEC(&pPubkeys[ib * EC_COMPRESSED_SIZE], EC_COMPRESSED_SIZE, bnT2, ptT2) != 0) { LogPrintf("%s: hashToEC failed.\n", __func__); rv = 1; goto End; }; // ptT3 = s_{j+1}*H(P_{j+1}) if (!EC_POINT_mul(ecGrp, ptT3, NULL, ptT2, bnS, bnCtx)) { LogPrintf("%s: EC_POINT_mul failed.\n", __func__); rv = 1; goto End; }; // ptT4 = c_{j+1}*I_j if (!EC_POINT_mul(ecGrp, ptT4, NULL, ptKi, bnC, bnCtx)) { LogPrintf("%s: EC_POINT_mul failed.\n", __func__); rv = 1; goto End; }; // ptT2 = ptT3 + ptT4 if (!EC_POINT_add(ecGrp, ptT2, ptT3, ptT4, bnCtx)) { LogPrintf("%s: EC_POINT_add failed.\n", __func__); rv = 1; goto End; }; if ( !(EC_POINT_point2oct(ecGrp, ptT1, POINT_CONVERSION_COMPRESSED, &tempData[0], 33, bnCtx) == (int) EC_COMPRESSED_SIZE) || !(EC_POINT_point2oct(ecGrp, ptT2, POINT_CONVERSION_COMPRESSED, &tempData[33], 33, bnCtx) == (int) EC_COMPRESSED_SIZE)) { LogPrintf("%s: extract ptL and ptR failed.\n", __func__); rv = 1; goto End; }; CHashWriter ssCHash(SER_GETHASH, PROTOCOL_VERSION); ssCHash.write((const char*)tmpPkHash.begin(), 32); ssCHash.write((const char*)&tempData[0], 66); tmpHash = ssCHash.GetHash(); if (!bnC || !(BN_bin2bn(tmpHash.begin(), EC_SECRET_SIZE, bnC)) // bnC lags i by 1 || !BN_mod(bnC, bnC, bnOrder, bnCtx)) { LogPrintf("%s: hash -> bnC failed.\n", __func__); rv = 1; goto End; }; if (i == nSecretOffset && !BN_copy(bnCj, bnC)) { LogPrintf("%s: BN_copy failed.\n", __func__); rv = 1; goto End; }; if (i == 0) { memset(tempData, 0, EC_SECRET_SIZE); if ((nBytes = BN_num_bytes(bnC)) > (int) EC_SECRET_SIZE || BN_bn2bin(bnC, &tempData[0 + (EC_SECRET_SIZE-nBytes)]) != nBytes) { LogPrintf("%s: bnC -> sigC failed.\n", __func__); rv = 1; goto End; }; try { sigC.resize(32); } catch (std::exception& e) { LogPrintf("%s: sigC.resize failed.\n", __func__); rv = 1; goto End; }; memcpy(&sigC[0], tempData, EC_SECRET_SIZE); }; }; End: EC_POINT_free(ptKi); EC_POINT_free(ptPk); EC_POINT_free(ptT1); EC_POINT_free(ptT2); EC_POINT_free(ptT3); EC_POINT_free(ptT4); BN_CTX_end(bnCtx); return rv; };
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl) { qint64 total = 0; QList<SendCoinsRecipient> recipients = transaction.getRecipients(); std::vector<std::pair<CScript, int64_t> > vecSend; if(recipients.empty()) { return OK; } QSet<QString> setAddress; // Used to detect duplicates int nAddresses = 0; // Pre-check input data for validity foreach(const SendCoinsRecipient &rcp, recipients) { if (rcp.paymentRequest.IsInitialized()) { // PaymentRequest... int64_t subtotal = 0; const payments::PaymentDetails& details = rcp.paymentRequest.getDetails(); for (int i = 0; i < details.outputs_size(); i++) { const payments::Output& out = details.outputs(i); if (out.amount() <= 0) continue; subtotal += out.amount(); const unsigned char* scriptStr = (const unsigned char*)out.script().data(); CScript scriptPubKey(scriptStr, scriptStr+out.script().size()); vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, out.amount())); } if (subtotal <= 0) { return InvalidAmount; } total += subtotal; } else { // User-entered feathercoin address / amount: if(!validateAddress(rcp.address)) { return InvalidAddress; } if(rcp.amount <= 0) { return InvalidAmount; } setAddress.insert(rcp.address); ++nAddresses; std::string sAddr = rcp.address.toStdString(); if (rcp.typeInd == AddressTableModel::AT_Stealth) { CStealthAddress sxAddr; if (sxAddr.SetEncoded(sAddr)) { ec_secret ephem_secret; ec_secret secretShared; ec_point pkSendTo; ec_point ephem_pubkey; if (GenerateRandomSecret(ephem_secret) != 0) { printf("GenerateRandomSecret failed.\n"); return Aborted; }; LogPrintf("StealthSecret send start....\n"); if (true) { LogPrintf("ephem_secret.e=%s \n",HexStr(&ephem_secret.e[0],&ephem_secret.e[32]).c_str()); //secret LogPrintf("sxAddr.scan_secret= %s\n", HexStr(sxAddr.scan_secret).c_str()); // LogPrintf("sxAddr.scan_pubkey= %s\n", HexStr(sxAddr.scan_pubkey).c_str()); //pubkey[0] LogPrintf("sxAddr.spend_secret= %s\n", HexStr(sxAddr.spend_secret).c_str()); // LogPrintf("sxAddr.spend_pubkey= %s\n",HexStr(sxAddr.spend_pubkey).c_str()); //pkSpend[0] LogPrintf("secretShared.e=%s \n",HexStr(&secretShared.e[0],&secretShared.e[32]).c_str()); //sharedSOut LogPrintf("pkSendTo= %"PRIszu": %s\n", pkSendTo.size(), HexStr(pkSendTo).c_str());//pkOut LogPrintf("send: secret = ephem_secret, pubkey = scan_pubkey....\n"); }; if (StealthSecret(ephem_secret, sxAddr.scan_pubkey, sxAddr.spend_pubkey, secretShared, pkSendTo) != 0) { printf("Could not generate receiving public key.\n"); return Aborted; }; if (true) { LogPrintf("ephem_secret.e=%s \n",HexStr(&ephem_secret.e[0],&ephem_secret.e[32]).c_str()); //secret LogPrintf("sxAddr.scan_secret= %s\n", HexStr(sxAddr.scan_secret).c_str()); // LogPrintf("sxAddr.scan_pubkey= %s\n", HexStr(sxAddr.scan_pubkey).c_str()); //pubkey[0] LogPrintf("sxAddr.spend_secret= %s\n", HexStr(sxAddr.spend_secret).c_str()); // LogPrintf("sxAddr.spend_pubkey= %s\n",HexStr(sxAddr.spend_pubkey).c_str()); //pkSpend[0] LogPrintf("secretShared.e=%s \n",HexStr(&secretShared.e[0],&secretShared.e[32]).c_str()); //sharedSOut LogPrintf("pkSendTo= %"PRIszu": %s\n", pkSendTo.size(), HexStr(pkSendTo).c_str());//pkOut }; CPubKey cpkTo(pkSendTo); if (!cpkTo.IsValid()) { printf("Invalid public key generated.\n"); return Aborted; }; LogPrintf("CPubKey(pkSendTo)=%s \n",cpkTo.GetHash().ToString().c_str()); CKeyID ckidTo = cpkTo.GetID(); CBitcoinAddress addrTo(ckidTo); if (SecretToPublicKey(ephem_secret, ephem_pubkey) != 0) { printf("Could not generate ephem public key.\n"); return Aborted; }; if (true) { LogPrintf("Stealth send to generated pubkey,pkSendTo= %"PRIszu": %s\n", pkSendTo.size(), HexStr(pkSendTo).c_str()); LogPrintf("hash, Address= %s\n", addrTo.ToString().c_str()); LogPrintf("enerate ephem public key,ephem_pubkey= %"PRIszu": %s\n", ephem_pubkey.size(), HexStr(ephem_pubkey).c_str()); }; CScript scriptPubKey; scriptPubKey.SetDestination(addrTo.Get()); LogPrintf("scriptPubKey= %s \n", scriptPubKey.ToString()); vecSend.push_back(make_pair(scriptPubKey, rcp.amount)); CScript scriptP = CScript() << OP_RETURN << ephem_pubkey; vecSend.push_back(make_pair(scriptP, 0)); continue; }; // else drop through to normal } CScript scriptPubKey; scriptPubKey.SetDestination(CBitcoinAddress(sAddr).Get()); vecSend.push_back(std::pair<CScript, int64_t>(scriptPubKey, rcp.amount)); //insert message into blockchain if ((rcp.message.length()>=1)&&(rcp.label=="blockchain")) { std::string strMess = rcp.message.toStdString(); const char* pszMess =strMess.c_str(); CScript scriptP = CScript() << OP_RETURN << vector<unsigned char>((const unsigned char*)pszMess, (const unsigned char*)pszMess + strlen(pszMess)); vecSend.push_back(std::pair<CScript, int64_t>(scriptP, 0)); } //commit openname if ((rcp.message.length()>=1)&&(rcp.label=="openname")) { std::string strMess = rcp.message.toStdString();//已经是16进制码流 CScript scriptP = CScript() << OP_RETURN << ParseHex(strMess); LogPrintf("openname scriptP=%s\n",scriptP.ToString().c_str());//scriptP=OP_RETURN 580861e00414720684e88cb7943fc6751527a94b2e0cdd2a9148d8b13939723d2aca16c75c6d68 vecSend.push_back(std::pair<CScript, int64_t>(scriptP, 0)); } //normal, do nothing total += rcp.amount; } } if(setAddress.size() != nAddresses) { return DuplicateAddress; } qint64 nBalance = getBalance(coinControl); if(total > nBalance) { return AmountExceedsBalance; } if((total + nTransactionFee) > nBalance) { transaction.setTransactionFee(nTransactionFee); return SendCoinsReturn(AmountWithFeeExceedsBalance); } { LOCK2(cs_main, wallet->cs_wallet); transaction.newPossibleKeyChange(wallet); int64_t nFeeRequired = 0; int nChangePos = -1; std::string strFailReason; CWalletTx *newTx = transaction.getTransaction(); CReserveKey *keyChange = transaction.getPossibleKeyChange(); bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, nChangePos, strFailReason, coinControl); transaction.setTransactionFee(nFeeRequired); if(!fCreated) { if((total + nFeeRequired) > nBalance) { return SendCoinsReturn(AmountWithFeeExceedsBalance); } emit message(tr("Send Coins"), QString::fromStdString(strFailReason), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } } return SendCoinsReturn(OK); }
int generateRingSignature(data_chunk &keyImage, uint256 &txnHash, int nRingSize, int nSecretOffset, ec_secret secret, const uint8_t *pPubkeys, uint8_t *pSigc, uint8_t *pSigr) { if (fDebugRingSig) LogPrintf("%s: Ring size %d.\n", __func__, nRingSize); int rv = 0; int nBytes; BN_CTX_start(bnCtx); BIGNUM *bnKS = BN_CTX_get(bnCtx); BIGNUM *bnK1 = BN_CTX_get(bnCtx); BIGNUM *bnK2 = BN_CTX_get(bnCtx); BIGNUM *bnT = BN_CTX_get(bnCtx); BIGNUM *bnH = BN_CTX_get(bnCtx); BIGNUM *bnSum = BN_CTX_get(bnCtx); EC_POINT *ptT1 = NULL; EC_POINT *ptT2 = NULL; EC_POINT *ptT3 = NULL; EC_POINT *ptPk = NULL; EC_POINT *ptKi = NULL; EC_POINT *ptL = NULL; EC_POINT *ptR = NULL; uint8_t tempData[66]; // hold raw point data to hash uint256 commitHash; ec_secret scData1, scData2; CHashWriter ssCommitHash(SER_GETHASH, PROTOCOL_VERSION); ssCommitHash << txnHash; // zero signature memset(pSigc, 0, EC_SECRET_SIZE * nRingSize); memset(pSigr, 0, EC_SECRET_SIZE * nRingSize); // ks = random 256 bit int mod P if (GenerateRandomSecret(scData1) && (rv = errorN(1, "%s: GenerateRandomSecret failed.", __func__))) goto End; if (!bnKS || !(BN_bin2bn(&scData1.e[0], EC_SECRET_SIZE, bnKS))) { LogPrintf("%s: BN_bin2bn failed.\n", __func__); rv = 1; goto End; }; // zero sum if (!bnSum || !(BN_zero(bnSum))) { LogPrintf("%s: BN_zero failed.\n", __func__); rv = 1; goto End; }; if ( !(ptT1 = EC_POINT_new(ecGrp)) || !(ptT2 = EC_POINT_new(ecGrp)) || !(ptT3 = EC_POINT_new(ecGrp)) || !(ptPk = EC_POINT_new(ecGrp)) || !(ptKi = EC_POINT_new(ecGrp)) || !(ptL = EC_POINT_new(ecGrp)) || !(ptR = EC_POINT_new(ecGrp))) { LogPrintf("%s: EC_POINT_new failed.\n", __func__); rv = 1; goto End; }; // get keyimage as point if (!(bnT = BN_bin2bn(&keyImage[0], EC_COMPRESSED_SIZE, bnT)) || !(ptKi) || !(ptKi = EC_POINT_bn2point(ecGrp, bnT, ptKi, bnCtx))) { LogPrintf("%s: extract ptKi failed.\n", __func__); rv = 1; goto End; }; for (int i = 0; i < nRingSize; ++i) { if (i == nSecretOffset) { // k = random 256 bit int mod P // L = k * G // R = k * HashToEC(PKi) if (!EC_POINT_mul(ecGrp, ptL, bnKS, NULL, NULL, bnCtx)) { LogPrintf("%s: EC_POINT_mul failed.\n", __func__); rv = 1; goto End; }; if (hashToEC(&pPubkeys[i * EC_COMPRESSED_SIZE], EC_COMPRESSED_SIZE, bnT, ptT1) != 0) { LogPrintf("%s: hashToEC failed.\n", __func__); rv = 1; goto End; }; if (!EC_POINT_mul(ecGrp, ptR, NULL, ptT1, bnKS, bnCtx)) { LogPrintf("%s: EC_POINT_mul failed.\n", __func__); rv = 1; goto End; }; } else { // k1 = random 256 bit int mod P // k2 = random 256 bit int mod P // Li = k1 * Pi + k2 * G // Ri = k1 * I + k2 * Hp(Pi) // ci = k1 // ri = k2 if (GenerateRandomSecret(scData1) != 0 || !bnK1 || !(BN_bin2bn(&scData1.e[0], EC_SECRET_SIZE, bnK1)) || GenerateRandomSecret(scData2) != 0 || !bnK2 || !(BN_bin2bn(&scData2.e[0], EC_SECRET_SIZE, bnK2))) { LogPrintf("%s: k1 and k2 failed.\n", __func__); rv = 1; goto End; }; // get Pk i as point if (!(bnT = BN_bin2bn(&pPubkeys[i * EC_COMPRESSED_SIZE], EC_COMPRESSED_SIZE, bnT)) || !(ptPk) || !(ptPk = EC_POINT_bn2point(ecGrp, bnT, ptPk, bnCtx))) { LogPrintf("%s: extract ptPk failed.\n", __func__); rv = 1; goto End; }; // ptT1 = k1 * Pi if (!EC_POINT_mul(ecGrp, ptT1, NULL, ptPk, bnK1, bnCtx)) { LogPrintf("%s: EC_POINT_mul failed.\n", __func__); rv = 1; goto End; }; // ptT2 = k2 * G if (!EC_POINT_mul(ecGrp, ptT2, bnK2, NULL, NULL, bnCtx)) { LogPrintf("%s: EC_POINT_mul failed.\n", __func__); rv = 1; goto End; }; // ptL = ptT1 + ptT2 if (!EC_POINT_add(ecGrp, ptL, ptT1, ptT2, bnCtx)) { LogPrintf("%s: EC_POINT_add failed.\n", __func__); rv = 1; goto End; }; // ptT3 = Hp(Pi) if (hashToEC(&pPubkeys[i * EC_COMPRESSED_SIZE], EC_COMPRESSED_SIZE, bnT, ptT3) != 0) { LogPrintf("%s: hashToEC failed.\n", __func__); rv = 1; goto End; }; // ptT1 = k1 * I if (!EC_POINT_mul(ecGrp, ptT1, NULL, ptKi, bnK1, bnCtx)) { LogPrintf("%s: EC_POINT_mul failed.\n", __func__); rv = 1; goto End; }; // ptT2 = k2 * ptT3 if (!EC_POINT_mul(ecGrp, ptT2, NULL, ptT3, bnK2, bnCtx)) { LogPrintf("%s: EC_POINT_mul failed.\n", __func__); rv = 1; goto End; }; // ptR = ptT1 + ptT2 if (!EC_POINT_add(ecGrp, ptR, ptT1, ptT2, bnCtx)) { LogPrintf("%s: EC_POINT_add failed.\n", __func__); rv = 1; goto End; }; memcpy(&pSigc[i * EC_SECRET_SIZE], &scData1.e[0], EC_SECRET_SIZE); memcpy(&pSigr[i * EC_SECRET_SIZE], &scData2.e[0], EC_SECRET_SIZE); // sum = (sum + sigc) % N , sigc == bnK1 if (!BN_mod_add(bnSum, bnSum, bnK1, bnOrder, bnCtx)) { LogPrintf("%s: BN_mod_add failed.\n", __func__); rv = 1; goto End; }; }; // -- add ptL and ptR to hash if ( !(EC_POINT_point2oct(ecGrp, ptL, POINT_CONVERSION_COMPRESSED, &tempData[0], 33, bnCtx) == (int) EC_COMPRESSED_SIZE) || !(EC_POINT_point2oct(ecGrp, ptR, POINT_CONVERSION_COMPRESSED, &tempData[33], 33, bnCtx) == (int) EC_COMPRESSED_SIZE)) { LogPrintf("%s: extract ptL and ptR failed.\n", __func__); rv = 1; goto End; }; ssCommitHash.write((const char*)&tempData[0], 66); }; commitHash = ssCommitHash.GetHash(); if (!(bnH) || !(bnH = BN_bin2bn(commitHash.begin(), EC_SECRET_SIZE, bnH))) { LogPrintf("%s: commitHash -> bnH failed.\n", __func__); rv = 1; goto End; }; if (!BN_mod(bnH, bnH, bnOrder, bnCtx)) // this is necessary { LogPrintf("%s: BN_mod failed.\n", __func__); rv = 1; goto End; }; // sigc[nSecretOffset] = (bnH - bnSum) % N if (!BN_mod_sub(bnT, bnH, bnSum, bnOrder, bnCtx)) { LogPrintf("%s: BN_mod_sub failed.\n", __func__); rv = 1; goto End; }; if ((nBytes = BN_num_bytes(bnT)) > (int)EC_SECRET_SIZE || BN_bn2bin(bnT, &pSigc[nSecretOffset * EC_SECRET_SIZE + (EC_SECRET_SIZE-nBytes)]) != nBytes) { LogPrintf("%s: bnT -> pSigc failed.\n", __func__); rv = 1; goto End; }; // sigr[nSecretOffset] = (bnKS - sigc[nSecretOffset] * bnSecret) % N // reuse bnH for bnSecret if (!bnH || !(BN_bin2bn(&secret.e[0], EC_SECRET_SIZE, bnH))) { LogPrintf("%s: BN_bin2bn failed.\n", __func__); rv = 1; goto End; }; // bnT = sigc[nSecretOffset] * bnSecret , TODO: mod N ? if (!BN_mul(bnT, bnT, bnH, bnCtx)) { LogPrintf("%s: BN_mul failed.\n", __func__); rv = 1; goto End; }; if (!BN_mod_sub(bnT, bnKS, bnT, bnOrder, bnCtx)) { LogPrintf("%s: BN_mod_sub failed.\n", __func__); rv = 1; goto End; }; if ((nBytes = BN_num_bytes(bnT)) > (int) EC_SECRET_SIZE || BN_bn2bin(bnT, &pSigr[nSecretOffset * EC_SECRET_SIZE + (EC_SECRET_SIZE-nBytes)]) != nBytes) { LogPrintf("%s: bnT -> pSigr failed.\n", __func__); rv = 1; goto End; }; End: EC_POINT_free(ptT1); EC_POINT_free(ptT2); EC_POINT_free(ptT3); EC_POINT_free(ptPk); EC_POINT_free(ptKi); EC_POINT_free(ptL); EC_POINT_free(ptR); BN_CTX_end(bnCtx); return rv; };