// Microbenchmark for verification of a basic P2WPKH script. Can be easily // modified to measure performance of other types of scripts. static void VerifyScriptBench(benchmark::State& state) { const int flags = SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH; const int witnessversion = 0; // Keypair. CKey key; static const std::array<unsigned char, 32> vchKey = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }; key.Set(vchKey.begin(), vchKey.end(), false); CPubKey pubkey = key.GetPubKey(); uint160 pubkeyHash; CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(pubkeyHash.begin()); // Script. CScript scriptPubKey = CScript() << witnessversion << ToByteVector(pubkeyHash); CScript scriptSig; CScript witScriptPubkey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkeyHash) << OP_EQUALVERIFY << OP_CHECKSIG; const CMutableTransaction& txCredit = BuildCreditingTransaction(scriptPubKey); CMutableTransaction txSpend = BuildSpendingTransaction(scriptSig, txCredit); CScriptWitness& witness = txSpend.vin[0].scriptWitness; witness.stack.emplace_back(); key.Sign(SignatureHash(witScriptPubkey, txSpend, 0, SIGHASH_ALL, txCredit.vout[0].nValue, SigVersion::WITNESS_V0), witness.stack.back()); witness.stack.back().push_back(static_cast<unsigned char>(SIGHASH_ALL)); witness.stack.push_back(ToByteVector(pubkey)); // Benchmark. while (state.KeepRunning()) { ScriptError err; bool success = VerifyScript( txSpend.vin[0].scriptSig, txCredit.vout[0].scriptPubKey, &txSpend.vin[0].scriptWitness, flags, MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue), &err); assert(err == SCRIPT_ERR_OK); assert(success); #if defined(HAVE_CONSENSUS_LIB) CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); stream << txSpend; int csuccess = fujicoinconsensus_verify_script_with_amount( txCredit.vout[0].scriptPubKey.data(), txCredit.vout[0].scriptPubKey.size(), txCredit.vout[0].nValue, (const unsigned char*)stream.data(), stream.size(), 0, flags, nullptr); assert(csuccess == 1); #endif } }
static bool Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, ScriptError& err) { // Create dummy to/from transactions: CMutableTransaction txFrom; txFrom.vout.resize(1); txFrom.vout[0].scriptPubKey = scriptPubKey; CMutableTransaction txTo; txTo.vin.resize(1); txTo.vout.resize(1); txTo.vin[0].prevout.n = 0; txTo.vin[0].prevout.hash = txFrom.GetHash(); txTo.vin[0].scriptSig = scriptSig; txTo.vout[0].nValue = 1; return VerifyScript(scriptSig, scriptPubKey, nullptr, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0, txFrom.vout[0].nValue), &err); }
// quick check for an already fully signed tx bool MultisigDialog::isFullyVerified(CMutableTransaction& tx){ try{ int nIn = 0; for(CTxIn& txin : tx.vin){ CTransaction txVin; uint256 hashBlock; if (!GetTransaction(txin.prevout.hash, txVin, hashBlock, true)){ throw runtime_error("txin could not be found"); } if (hashBlock == 0){ throw runtime_error("txin is unconfirmed"); } //get pubkey from this input as output in last tx CScript prevPubKey = txVin.vout[txin.prevout.n].scriptPubKey; if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&tx, nIn))){ return false; } nIn++; } }catch(const runtime_error& e){ return false; } return true; }
bool MultisigDialog::signMultisigTx(CMutableTransaction& tx, string& errorOut, QVBoxLayout* keyList) { //will be set false if all inputs are not fully signed(valid) bool fComplete = true; //if keyslist is not default value AND has items in list then true bool fGivenKeys = (keyList != nullptr) && (keyList->count() > 0); try{ //copy of vin for reference before vin is mutated vector<CTxIn> oldVin(tx.vin); CBasicKeyStore privKeystore; //if keys were given, attempt to collect redeem and scriptpubkey if(fGivenKeys){ for(int i = 0; i < keyList->count(); i++){ QWidget* keyFrame = qobject_cast<QWidget*>(keyList->itemAt(i)->widget()); QLineEdit* key = keyFrame->findChild<QLineEdit*>("key"); CBitcoinSecret vchSecret; if (!vchSecret.SetString(key->text().toStdString())) throw runtime_error("Invalid private key"); CKey cKey = vchSecret.GetKey(); if (!cKey.IsValid()) throw runtime_error("Private key outside allowed range"); privKeystore.AddKey(cKey); } for(CTxIn& txin : tx.vin){ //get inputs CTransaction txVin; uint256 hashBlock; if (!GetTransaction(txin.prevout.hash, txVin, hashBlock, true)) throw runtime_error("txin could not be found"); if (hashBlock == 0) throw runtime_error("txin is unconfirmed"); //get pubkey from input CScript prevPubKey = txVin.vout[txin.prevout.n].scriptPubKey; //get payment destination CTxDestination address; if(!ExtractDestination(prevPubKey, address)){ throw runtime_error("Could not find address for destination."); } //get redeem script related to destination CScriptID hash = boost::get<CScriptID>(address); CScript redeemScript; if (!pwalletMain->GetCScript(hash, redeemScript)){ errorOut = "could not redeem"; } privKeystore.AddCScript(redeemScript); } }else{ if (model->getEncryptionStatus() == model->Locked) { if (!model->requestUnlock(true).isValid()) { // Unlock wallet was cancelled throw runtime_error("Error: Your wallet is locked. Please enter the wallet passphrase first."); } } } //choose between local wallet and provided const CKeyStore& keystore = fGivenKeys ? privKeystore : *pwalletMain; //attempt to sign each input from local wallet int nIn = 0; for(CTxIn& txin : tx.vin){ //get inputs CTransaction txVin; uint256 hashBlock; if (!GetTransaction(txin.prevout.hash, txVin, hashBlock, true)) throw runtime_error("txin could not be found"); if (hashBlock == 0) throw runtime_error("txin is unconfirmed"); txin.scriptSig.clear(); CScript prevPubKey = txVin.vout[txin.prevout.n].scriptPubKey; //sign what we can SignSignature(keystore, prevPubKey, tx, nIn); //merge in any previous signatures txin.scriptSig = CombineSignatures(prevPubKey, tx, nIn, txin.scriptSig, oldVin[nIn].scriptSig); if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&tx, nIn))){ fComplete = false; } nIn++; } ui->signButtonStatus->setText(buildMultisigTxStatusString(fComplete, tx)); }catch(const runtime_error& e){ errorOut = string(e.what()); fComplete = false; } return fComplete; }
static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) { int nHashType = SIGHASH_ALL; if (flagStr.size() > 0) if (!findSighashFlags(nHashType, flagStr)) throw std::runtime_error("unknown sighash flag/sign option"); // mergedTx will end up with all the signatures; it // starts as a clone of the raw tx: CMutableTransaction mergedTx{tx}; const CMutableTransaction txv{tx}; CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); if (!registers.count("privatekeys")) throw std::runtime_error("privatekeys register variable must be set."); CBasicKeyStore tempKeystore; UniValue keysObj = registers["privatekeys"]; for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) { if (!keysObj[kidx].isStr()) throw std::runtime_error("privatekey not a std::string"); CKey key = DecodeSecret(keysObj[kidx].getValStr()); if (!key.IsValid()) { throw std::runtime_error("privatekey not valid"); } tempKeystore.AddKey(key); } // Add previous txouts given in the RPC call: if (!registers.count("prevtxs")) throw std::runtime_error("prevtxs register variable must be set."); UniValue prevtxsObj = registers["prevtxs"]; { for (unsigned int previdx = 0; previdx < prevtxsObj.size(); previdx++) { UniValue prevOut = prevtxsObj[previdx]; if (!prevOut.isObject()) throw std::runtime_error("expected prevtxs internal object"); std::map<std::string, UniValue::VType> types = { {"txid", UniValue::VSTR}, {"vout", UniValue::VNUM}, {"scriptPubKey", UniValue::VSTR}, }; if (!prevOut.checkObject(types)) throw std::runtime_error("prevtxs internal object typecheck fail"); uint256 txid = ParseHashStr(prevOut["txid"].get_str(), "txid"); int nOut = atoi(prevOut["vout"].getValStr()); if (nOut < 0) throw std::runtime_error("vout must be positive"); COutPoint out(txid, nOut); std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey")); CScript scriptPubKey(pkData.begin(), pkData.end()); { const Coin& coin = view.AccessCoin(out); if (!coin.IsSpent() && coin.out.scriptPubKey != scriptPubKey) { std::string err("Previous output scriptPubKey mismatch:\n"); err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+ ScriptToAsmStr(scriptPubKey); throw std::runtime_error(err); } Coin newcoin; newcoin.out.scriptPubKey = scriptPubKey; newcoin.out.nValue = 0; if (prevOut.exists("amount")) { newcoin.out.nValue = AmountFromValue(prevOut["amount"]); } newcoin.nHeight = 1; view.AddCoin(out, std::move(newcoin), true); } // if redeemScript given and private keys given, // add redeemScript to the tempKeystore so it can be signed: if ((scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash()) && prevOut.exists("redeemScript")) { UniValue v = prevOut["redeemScript"]; std::vector<unsigned char> rsData(ParseHexUV(v, "redeemScript")); CScript redeemScript(rsData.begin(), rsData.end()); tempKeystore.AddCScript(redeemScript); } } } const CKeyStore& keystore = tempKeystore; bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); // Sign what we can: for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn& txin = mergedTx.vin[i]; const Coin& coin = view.AccessCoin(txin.prevout); if (coin.IsSpent()) { continue; } const CScript& prevPubKey = coin.out.scriptPubKey; const CAmount& amount = coin.out.nValue; SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mergedTx.vout.size())) ProduceSignature(keystore, MutableTransactionSignatureCreator(&mergedTx, i, amount, nHashType), prevPubKey, sigdata); // ... and merge in other signatures: sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i)); UpdateInput(txin, sigdata); } tx = mergedTx; }
static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) { int nHashType = SIGHASH_ALL; if (flagStr.size() > 0) if (!findSighashFlags(nHashType, flagStr)) throw std::runtime_error("unknown sighash flag/sign option"); std::vector<CTransaction> txVariants; txVariants.push_back(tx); // mergedTx will end up with all the signatures; it // starts as a clone of the raw tx: CMutableTransaction mergedTx(txVariants[0]); bool fComplete = true; CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); if (!registers.count("privatekeys")) throw std::runtime_error("privatekeys register variable must be set."); CBasicKeyStore tempKeystore; UniValue keysObj = registers["privatekeys"]; for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) { if (!keysObj[kidx].isStr()) throw std::runtime_error("privatekey not a std::string"); CBitcoinSecret vchSecret; bool fGood = vchSecret.SetString(keysObj[kidx].getValStr()); if (!fGood) throw std::runtime_error("privatekey not valid"); CKey key = vchSecret.GetKey(); tempKeystore.AddKey(key); } // Add previous txouts given in the RPC call: if (!registers.count("prevtxs")) throw std::runtime_error("prevtxs register variable must be set."); UniValue prevtxsObj = registers["prevtxs"]; { for (unsigned int previdx = 0; previdx < prevtxsObj.size(); previdx++) { UniValue prevOut = prevtxsObj[previdx]; if (!prevOut.isObject()) throw std::runtime_error("expected prevtxs internal object"); std::map<std::string,UniValue::VType> types = boost::assign::map_list_of("txid", UniValue::VSTR)("vout",UniValue::VNUM)("scriptPubKey",UniValue::VSTR); if (!prevOut.checkObject(types)) throw std::runtime_error("prevtxs internal object typecheck fail"); uint256 txid = ParseHashUV(prevOut["txid"], "txid"); int nOut = atoi(prevOut["vout"].getValStr()); if (nOut < 0) throw std::runtime_error("vout must be positive"); std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey")); CScript scriptPubKey(pkData.begin(), pkData.end()); { CCoinsModifier coins = view.ModifyCoins(txid); if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) { std::string err("Previous output scriptPubKey mismatch:\n"); err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+ ScriptToAsmStr(scriptPubKey); throw std::runtime_error(err); } if ((unsigned int)nOut >= coins->vout.size()) coins->vout.resize(nOut+1); coins->vout[nOut].scriptPubKey = scriptPubKey; coins->vout[nOut].nValue = 0; if (prevOut.exists("amount")) { coins->vout[nOut].nValue = AmountFromValue(prevOut["amount"]); } } // if redeemScript given and private keys given, // add redeemScript to the tempKeystore so it can be signed: if ((scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash()) && prevOut.exists("redeemScript")) { UniValue v = prevOut["redeemScript"]; std::vector<unsigned char> rsData(ParseHexUV(v, "redeemScript")); CScript redeemScript(rsData.begin(), rsData.end()); tempKeystore.AddCScript(redeemScript); } } } const CKeyStore& keystore = tempKeystore; bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); // Sign what we can: for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn& txin = mergedTx.vin[i]; const CCoins* coins = view.AccessCoins(txin.prevout.hash); if (!coins || !coins->IsAvailable(txin.prevout.n)) { fComplete = false; continue; } const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; const CAmount& amount = coins->vout[txin.prevout.n].nValue; SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mergedTx.vout.size())) ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata); // ... and merge in other signatures: BOOST_FOREACH(const CTransaction& txv, txVariants) sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i)); UpdateTransaction(mergedTx, i, sigdata); if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount))) fComplete = false; } if (fComplete) { // do nothing... for now // perhaps store this for later optional JSON output } tx = mergedTx; }