CWalletTx GetValidReceive(ZCJoinSplit& params, const libzcash::SpendingKey& sk, CAmount value, bool randomInputs) { CMutableTransaction mtx; mtx.nVersion = 2; // Enable JoinSplits mtx.vin.resize(2); if (randomInputs) { mtx.vin[0].prevout.hash = GetRandHash(); mtx.vin[1].prevout.hash = GetRandHash(); } else { mtx.vin[0].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000001"); mtx.vin[1].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000002"); } mtx.vin[0].prevout.n = 0; mtx.vin[1].prevout.n = 0; // Generate an ephemeral keypair. uint256 joinSplitPubKey; unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey); mtx.joinSplitPubKey = joinSplitPubKey; boost::array<libzcash::JSInput, 2> inputs = { libzcash::JSInput(), // dummy input libzcash::JSInput() // dummy input }; boost::array<libzcash::JSOutput, 2> outputs = { libzcash::JSOutput(sk.address(), value), libzcash::JSOutput(sk.address(), value) }; boost::array<libzcash::Note, 2> output_notes; // Prepare JoinSplits uint256 rt; JSDescription jsdesc {params, mtx.joinSplitPubKey, rt, inputs, outputs, 2*value, 0, false}; mtx.vjoinsplit.push_back(jsdesc); // Empty output script. CScript scriptCode; CTransaction signTx(mtx); uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); // Add the signature assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, dataToBeSigned.begin(), 32, joinSplitPrivKey ) == 0); CTransaction tx {mtx}; CWalletTx wtx {NULL, tx}; return wtx; }
CWalletTx GetValidSpend(ZCJoinSplit& params, const libzcash::SpendingKey& sk, const libzcash::Note& note, CAmount value) { CMutableTransaction mtx; mtx.vout.resize(2); mtx.vout[0].nValue = value; mtx.vout[1].nValue = 0; // Generate an ephemeral keypair. uint256 joinSplitPubKey; unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey); mtx.joinSplitPubKey = joinSplitPubKey; // Fake tree for the unused witness ZCIncrementalMerkleTree tree; libzcash::JSOutput dummyout; libzcash::JSInput dummyin; { if (note.value > value) { libzcash::SpendingKey dummykey = libzcash::SpendingKey::random(); libzcash::PaymentAddress dummyaddr = dummykey.address(); dummyout = libzcash::JSOutput(dummyaddr, note.value - value); } else if (note.value < value) { libzcash::SpendingKey dummykey = libzcash::SpendingKey::random(); libzcash::PaymentAddress dummyaddr = dummykey.address(); libzcash::Note dummynote(dummyaddr.a_pk, (value - note.value), uint256(), uint256()); tree.append(dummynote.cm()); dummyin = libzcash::JSInput(tree.witness(), dummynote, dummykey); } } tree.append(note.cm()); boost::array<libzcash::JSInput, 2> inputs = { libzcash::JSInput(tree.witness(), note, sk), dummyin }; boost::array<libzcash::JSOutput, 2> outputs = { dummyout, // dummy output libzcash::JSOutput() // dummy output }; boost::array<libzcash::Note, 2> output_notes; // Prepare JoinSplits uint256 rt = tree.root(); JSDescription jsdesc {params, mtx.joinSplitPubKey, rt, inputs, outputs, 0, value, false}; mtx.vjoinsplit.push_back(jsdesc); // Empty output script. CScript scriptCode; CTransaction signTx(mtx); uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); // Add the signature assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, dataToBeSigned.begin(), 32, joinSplitPrivKey ) == 0); CTransaction tx {mtx}; CWalletTx wtx {NULL, tx}; return wtx; }
static Status bridgeDoSweep(Wallet &wallet, PendingSweep &sweep, tABC_BitCoin_Event_Callback fAsyncCallback, void *pData) { // Find utxos for this address: AddressSet addresses; addresses.insert(sweep.address); auto utxos = wallet.txCache.get_utxos(addresses); // Bail out if there are no funds to sweep: if (!utxos.size()) { // Tell the GUI if there were funds in the past: if (wallet.txCache.has_history(sweep.address)) { ABC_DebugLog("IncomingSweep callback: wallet %s, value: 0", wallet.id().c_str()); tABC_AsyncBitCoinInfo info; info.pData = pData; info.eventType = ABC_AsyncEventType_IncomingSweep; Status().toError(info.status, ABC_HERE()); info.szWalletUUID = wallet.id().c_str(); info.szTxID = nullptr; info.sweepSatoshi = 0; fAsyncCallback(&info); sweep.done = true; } return Status(); } // Build a transaction: bc::transaction_type tx; tx.version = 1; tx.locktime = 0; // Set up the output: Address address; wallet.addresses.getNew(address); bc::transaction_output_type output; ABC_CHECK(outputScriptForAddress(output.script, address.address)); tx.outputs.push_back(output); // Set up the inputs: uint64_t fee, funds; ABC_CHECK(inputsPickMaximum(fee, funds, tx, utxos)); if (outputIsDust(funds)) return ABC_ERROR(ABC_CC_InsufficientFunds, "Not enough funds"); tx.outputs[0].value = funds; // Now sign that: KeyTable keys; keys[sweep.address] = sweep.key; ABC_CHECK(signTx(tx, wallet.txCache, keys)); // Send: bc::data_chunk raw_tx(satoshi_raw_size(tx)); bc::satoshi_save(tx, raw_tx.begin()); ABC_CHECK(broadcastTx(wallet, raw_tx)); // Calculate transaction information: const auto info = wallet.txCache.txInfo(tx, wallet.addresses.list()); // Save the transaction metadata: Tx meta; meta.ntxid = info.ntxid; meta.txid = info.txid; meta.timeCreation = time(nullptr); meta.internal = true; meta.metadata.amountSatoshi = funds; meta.metadata.amountFeesAirbitzSatoshi = 0; ABC_CHECK(gContext->exchangeCache.satoshiToCurrency( meta.metadata.amountCurrency, info.balance, static_cast<Currency>(wallet.currency()))); ABC_CHECK(wallet.txs.save(meta)); // Update the transaction cache: if (wallet.txCache.insert(tx)) watcherSave(wallet).log(); // Failure is not fatal wallet.balanceDirty(); ABC_CHECK(wallet.addresses.markOutputs(info.ios)); // Done: ABC_DebugLog("IncomingSweep callback: wallet %s, txid: %s, value: %d", wallet.id().c_str(), info.txid.c_str(), output.value); tABC_AsyncBitCoinInfo async; async.pData = pData; async.eventType = ABC_AsyncEventType_IncomingSweep; Status().toError(async.status, ABC_HERE()); async.szWalletUUID = wallet.id().c_str(); async.szTxID = info.txid.c_str(); async.sweepSatoshi = output.value; fAsyncCallback(&async); sweep.done = true; return Status(); }
void static RandomTransaction(CMutableTransaction &tx, bool fSingle, uint32_t consensusBranchId) { tx.fOverwintered = insecure_rand() % 2; if (tx.fOverwintered) { if (insecure_rand() % 2) { tx.nVersionGroupId = SAPLING_VERSION_GROUP_ID; tx.nVersion = sapling_version_dist(rng); } else { tx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID; tx.nVersion = overwinter_version_dist(rng); } tx.nExpiryHeight = (insecure_rand() % 2) ? insecure_rand() : 0; } else { tx.nVersion = insecure_rand() & 0x7FFFFFFF; } tx.vin.clear(); tx.vout.clear(); tx.vShieldedSpend.clear(); tx.vShieldedOutput.clear(); tx.vjoinsplit.clear(); tx.nLockTime = (insecure_rand() % 2) ? insecure_rand() : 0; int ins = (insecure_rand() % 4) + 1; int outs = fSingle ? ins : (insecure_rand() % 4) + 1; int shielded_spends = (insecure_rand() % 4) + 1; int shielded_outs = (insecure_rand() % 4) + 1; int joinsplits = (insecure_rand() % 4); for (int in = 0; in < ins; in++) { tx.vin.push_back(CTxIn()); CTxIn &txin = tx.vin.back(); txin.prevout.hash = GetRandHash(); txin.prevout.n = insecure_rand() % 4; RandomScript(txin.scriptSig); txin.nSequence = (insecure_rand() % 2) ? insecure_rand() : (unsigned int)-1; } for (int out = 0; out < outs; out++) { tx.vout.push_back(CTxOut()); CTxOut &txout = tx.vout.back(); txout.nValue = insecure_rand() % 100000000; RandomScript(txout.scriptPubKey); } if (tx.nVersionGroupId == SAPLING_VERSION_GROUP_ID) { tx.valueBalance = insecure_rand() % 100000000; for (int spend = 0; spend < shielded_spends; spend++) { SpendDescription sdesc; sdesc.cv = GetRandHash(); sdesc.anchor = GetRandHash(); sdesc.nullifier = GetRandHash(); sdesc.rk = GetRandHash(); randombytes_buf(sdesc.zkproof.begin(), sdesc.zkproof.size()); tx.vShieldedSpend.push_back(sdesc); } for (int out = 0; out < shielded_outs; out++) { OutputDescription odesc; odesc.cv = GetRandHash(); odesc.cm = GetRandHash(); odesc.ephemeralKey = GetRandHash(); randombytes_buf(odesc.encCiphertext.begin(), odesc.encCiphertext.size()); randombytes_buf(odesc.outCiphertext.begin(), odesc.outCiphertext.size()); randombytes_buf(odesc.zkproof.begin(), odesc.zkproof.size()); tx.vShieldedOutput.push_back(odesc); } } if (tx.nVersion >= 2) { for (int js = 0; js < joinsplits; js++) { JSDescription jsdesc; if (insecure_rand() % 2 == 0) { jsdesc.vpub_old = insecure_rand() % 100000000; } else { jsdesc.vpub_new = insecure_rand() % 100000000; } jsdesc.anchor = GetRandHash(); jsdesc.nullifiers[0] = GetRandHash(); jsdesc.nullifiers[1] = GetRandHash(); jsdesc.ephemeralKey = GetRandHash(); jsdesc.randomSeed = GetRandHash(); randombytes_buf(jsdesc.ciphertexts[0].begin(), jsdesc.ciphertexts[0].size()); randombytes_buf(jsdesc.ciphertexts[1].begin(), jsdesc.ciphertexts[1].size()); if (tx.fOverwintered && tx.nVersion >= SAPLING_TX_VERSION) { libzcash::GrothProof zkproof; randombytes_buf(zkproof.begin(), zkproof.size()); jsdesc.proof = zkproof; } else { jsdesc.proof = libzcash::PHGRProof::random_invalid(); } jsdesc.macs[0] = GetRandHash(); jsdesc.macs[1] = GetRandHash(); tx.vjoinsplit.push_back(jsdesc); } unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; crypto_sign_keypair(tx.joinSplitPubKey.begin(), joinSplitPrivKey); // Empty output script. CScript scriptCode; CTransaction signTx(tx); uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); assert(crypto_sign_detached(&tx.joinSplitSig[0], NULL, dataToBeSigned.begin(), 32, joinSplitPrivKey ) == 0); } }