void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check) { bool fCoinbase = tx.IsCoinBase(); const uint256& txid = tx.GetHash(); for (size_t i = 0; i < tx.vout.size(); ++i) { bool overwrite = check ? cache.HaveCoin(COutPoint(txid, i)) : fCoinbase; // Always set the possible_overwrite flag to AddCoin for coinbase txn, in order to correctly // deal with the pre-BIP30 occurrences of duplicate coinbase transactions. cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), overwrite); } }
BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) { std::string coinbaseAddress = coinbaseKey.GetPubKey().GetID().ToString(); // Confirm ListCoins initially returns 1 coin grouped under coinbaseKey // address. std::map<CTxDestination, std::vector<COutput>> list; { LOCK2(cs_main, wallet->cs_wallet); list = wallet->ListCoins(); } BOOST_CHECK_EQUAL(list.size(), 1U); BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(list.begin()->second.size(), 1U); // Check initial balance from one mature coinbase transaction. BOOST_CHECK_EQUAL(50 * COIN, wallet->GetAvailableBalance()); // Add a transaction creating a change address, and confirm ListCoins still // returns the coin associated with the change address underneath the // coinbaseKey pubkey, even though the change address has a different // pubkey. AddTx(CRecipient{GetScriptForRawPubKey({}), 1 * COIN, false /* subtract fee */}); { LOCK2(cs_main, wallet->cs_wallet); list = wallet->ListCoins(); } BOOST_CHECK_EQUAL(list.size(), 1U); BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U); // Lock both coins. Confirm number of available coins drops to 0. { LOCK2(cs_main, wallet->cs_wallet); std::vector<COutput> available; wallet->AvailableCoins(available); BOOST_CHECK_EQUAL(available.size(), 2U); } for (const auto& group : list) { for (const auto& coin : group.second) { LOCK(wallet->cs_wallet); wallet->LockCoin(COutPoint(coin.tx->GetHash(), coin.i)); } } { LOCK2(cs_main, wallet->cs_wallet); std::vector<COutput> available; wallet->AvailableCoins(available); BOOST_CHECK_EQUAL(available.size(), 0U); } // Confirm ListCoins still returns same result as before, despite coins // being locked. { LOCK2(cs_main, wallet->cs_wallet); list = wallet->ListCoins(); } BOOST_CHECK_EQUAL(list.size(), 1U); BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U); }
void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReason reason) { // Remove transaction from memory pool { LOCK(cs); setEntries txToRemove; txiter origit = mapTx.find(origTx.GetHash()); if (origit != mapTx.end()) { txToRemove.insert(origit); } else { // When recursively removing but origTx isn't in the mempool // be sure to remove any children that are in the pool. This can // happen during chain re-orgs if origTx isn't re-accepted into // the mempool for any reason. for (unsigned int i = 0; i < origTx.vout.size(); i++) { auto it = mapNextTx.find(COutPoint(origTx.GetHash(), i)); if (it == mapNextTx.end()) continue; txiter nextit = mapTx.find(it->second->GetHash()); assert(nextit != mapTx.end()); txToRemove.insert(nextit); } } setEntries setAllRemoves; for (txiter it : txToRemove) { CalculateDescendants(it, setAllRemoves); } RemoveStaged(setAllRemoves, false, reason); } }
CTxIn MultisigInputEntry::getInput() { int nOutput = ui->transactionOutput->currentIndex(); CTxIn input(COutPoint(txHash, nOutput)); return input; }
void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight) { bool fCoinbase = tx.IsCoinBase(); const uint256& txid = tx.GetHash(); for (size_t i = 0; i < tx.vout.size(); ++i) { // Pass fCoinbase as the possible_overwrite flag to AddCoin, in order to correctly // deal with the pre-BIP30 occurrances of duplicate coinbase transactions. cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), fCoinbase); } }
UtxoData::iterator FindRandomFrom(const std::set<COutPoint> &utxoSet) { assert(utxoSet.size()); auto utxoSetIt = utxoSet.lower_bound(COutPoint(InsecureRand256(), 0)); if (utxoSetIt == utxoSet.end()) { utxoSetIt = utxoSet.begin(); } auto utxoDataIt = utxoData.find(*utxoSetIt); assert(utxoDataIt != utxoData.end()); return utxoDataIt; }
CNameData NameIterationTester::getNextData () { const CScript addr = getTestAddress (); const valtype name = ValtypeFromString ("dummy"); const valtype value = ValtypeFromString ("abc"); const CScript updateScript = CNameScript::buildNameUpdate (addr, name, value); const CNameScript nameOp(updateScript); CNameData res; res.fromScript (++counter, COutPoint (uint256 (), 0), nameOp); return res; }
// construct an invalid block (but with a valid header) const std::shared_ptr<const CBlock> BadBlock(const uint256& prev_hash) { auto pblock = Block(prev_hash); CMutableTransaction coinbase_spend; coinbase_spend.vin.push_back(CTxIn(COutPoint(pblock->vtx[0]->GetHash(), 0), CScript(), 0)); coinbase_spend.vout.push_back(pblock->vtx[0]->vout[0]); CTransactionRef tx = MakeTransactionRef(coinbase_spend); pblock->vtx.push_back(tx); auto ret = FinalizeBlock(pblock); return ret; }
// vHashesToUpdate is the set of transaction hashes from a disconnected block // which has been re-added to the mempool. // for each entry, look for descendants that are outside vHashesToUpdate, and // add fee/size information for such descendants to the parent. // for each such descendant, also update the ancestor state to include the parent. void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashesToUpdate) { LOCK(cs); // For each entry in vHashesToUpdate, store the set of in-mempool, but not // in-vHashesToUpdate transactions, so that we don't have to recalculate // descendants when we come across a previously seen entry. cacheMap mapMemPoolDescendantsToUpdate; // Use a set for lookups into vHashesToUpdate (these entries are already // accounted for in the state of their ancestors) std::set<uint256> setAlreadyIncluded(vHashesToUpdate.begin(), vHashesToUpdate.end()); // Iterate in reverse, so that whenever we are looking at a transaction // we are sure that all in-mempool descendants have already been processed. // This maximizes the benefit of the descendant cache and guarantees that // setMemPoolChildren will be updated, an assumption made in // UpdateForDescendants. for (const uint256 &hash : reverse_iterate(vHashesToUpdate)) { // we cache the in-mempool children to avoid duplicate updates setEntries setChildren; // calculate children from mapNextTx txiter it = mapTx.find(hash); if (it == mapTx.end()) { continue; } auto iter = mapNextTx.lower_bound(COutPoint(hash, 0)); // First calculate the children, and update setMemPoolChildren to // include them, and update their setMemPoolParents to include this tx. for (; iter != mapNextTx.end() && iter->first->hash == hash; ++iter) { const uint256 &childHash = iter->second->GetHash(); txiter childIter = mapTx.find(childHash); assert(childIter != mapTx.end()); // We can skip updating entries we've encountered before or that // are in the block (which are already accounted for). if (setChildren.insert(childIter).second && !setAlreadyIncluded.count(childHash)) { UpdateChild(it, childIter, true); UpdateParent(childIter, it, true); } } UpdateForDescendants(it, mapMemPoolDescendantsToUpdate, setAlreadyIncluded); } }
//spend void MultisigDialog::on_createButton_clicked() { if(!model) return; vector<CTxIn> vUserIn; vector<CTxOut> vUserOut; try{ //Add inputs from Coin Control if any are selected if (CoinControlDialog::coinControl->HasSelected()) { vector<COutPoint> vSelected; CoinControlDialog::coinControl->ListSelected(vSelected); for (auto outpoint : vSelected) vUserIn.emplace_back(CTxIn(outpoint)); }else{//check for raw inputs for(int i = 0; i < ui->inputsList->count(); i++){ QWidget* input = qobject_cast<QWidget*>(ui->inputsList->itemAt(i)->widget()); QLineEdit* txIdLine = input->findChild<QLineEdit*>("txInputId"); if(txIdLine->text().isEmpty()){ ui->createButtonStatus->setStyleSheet("QLabel { color: red; }"); ui->createButtonStatus->setText(tr("Invalid Tx Hash.")); return; } QSpinBox* txVoutLine = input->findChild<QSpinBox*>("txInputVout"); int nOutput = txVoutLine->value(); if(nOutput < 0){ ui->createButtonStatus->setStyleSheet("QLabel { color: red; }"); ui->createButtonStatus->setText(tr("Vout position must be positive.")); return; } uint256 txid = uint256S(txIdLine->text().toStdString()); CTxIn in(COutPoint(txid, nOutput)); vUserIn.emplace_back(in); } } //validate destinations bool validInput = true; for(int i = 0; i < ui->destinationsList->count(); i++){ QWidget* dest = qobject_cast<QWidget*>(ui->destinationsList->itemAt(i)->widget()); QValidatedLineEdit* addr = dest->findChild<QValidatedLineEdit*>("destinationAddress"); BitcoinAmountField* amt = dest->findChild<BitcoinAmountField*>("destinationAmount"); CBitcoinAddress address; bool validDest = true; if(!model->validateAddress(addr->text())){ addr->setValid(false); validDest = false; }else{ address = CBitcoinAddress(addr->text().toStdString()); } if(!amt->validate()){ amt->setValid(false); validDest = false; } if(!validDest){ validInput = false; continue; } CScript scriptPubKey = GetScriptForDestination(address.Get()); CTxOut out(amt->value(), scriptPubKey); vUserOut.push_back(out); } //if all user data valid create a multisig tx if(validInput){ //clear member variable multisigTx = CMutableTransaction(); string error; string fee; if(!createMultisigTransaction(vUserIn, vUserOut, fee, error)){ throw runtime_error(error); } //display status string ui->createButtonStatus->setStyleSheet("QTextEdit{ color: black }"); QString status(strprintf("Transaction has successfully created with a fee of %s.\n" "The transaction has been automatically imported to the sign tab.\n" "Please continue on to sign the tx from this wallet, to access the hex to send to other owners.", fee).c_str()); ui->createButtonStatus->setText(status); ui->transactionHex->setText(QString::fromStdString(EncodeHexTx(multisigTx))); } }catch(const runtime_error& e){ ui->createButtonStatus->setStyleSheet("QTextEdit{ color: red }"); ui->createButtonStatus->setText(tr(e.what())); } }
bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) { if (tx.nType != TRANSACTION_PROVIDER_REGISTER) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-type"); } CProRegTx ptx; if (!GetTxPayload(tx, ptx)) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload"); } if (ptx.nVersion == 0 || ptx.nVersion > CProRegTx::CURRENT_VERSION) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-version"); } if (ptx.nType != 0) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-type"); } if (ptx.nMode != 0) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-mode"); } if (ptx.keyIDOwner.IsNull() || !ptx.pubKeyOperator.IsValid() || ptx.keyIDVoting.IsNull()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-null"); } if (!ptx.scriptPayout.IsPayToPublicKeyHash() && !ptx.scriptPayout.IsPayToScriptHash()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee"); } CTxDestination payoutDest; if (!ExtractDestination(ptx.scriptPayout, payoutDest)) { // should not happen as we checked script types before return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-dest"); } // don't allow reuse of payout key for other keys (don't allow people to put the payee key onto an online server) if (payoutDest == CTxDestination(ptx.keyIDOwner) || payoutDest == CTxDestination(ptx.keyIDVoting)) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-reuse"); } // It's allowed to set addr to 0, which will put the MN into PoSe-banned state and require a ProUpServTx to be issues later // If any of both is set, it must be valid however if (ptx.addr != CService() && !CheckService(tx.GetHash(), ptx, state)) { return false; } if (ptx.nOperatorReward > 10000) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-reward"); } CTxDestination collateralTxDest; CKeyID keyForPayloadSig; COutPoint collateralOutpoint; if (!ptx.collateralOutpoint.hash.IsNull()) { Coin coin; if (!GetUTXOCoin(ptx.collateralOutpoint, coin) || coin.out.nValue != 1000 * COIN) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral"); } if (!ExtractDestination(coin.out.scriptPubKey, collateralTxDest)) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-dest"); } // Extract key from collateral. This only works for P2PK and P2PKH collaterals and will fail for P2SH. // Issuer of this ProRegTx must prove ownership with this key by signing the ProRegTx if (!CBitcoinAddress(collateralTxDest).GetKeyID(keyForPayloadSig)) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-pkh"); } collateralOutpoint = ptx.collateralOutpoint; } else { if (ptx.collateralOutpoint.n >= tx.vout.size()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-index"); } if (tx.vout[ptx.collateralOutpoint.n].nValue != 1000 * COIN) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral"); } if (!ExtractDestination(tx.vout[ptx.collateralOutpoint.n].scriptPubKey, collateralTxDest)) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-dest"); } collateralOutpoint = COutPoint(tx.GetHash(), ptx.collateralOutpoint.n); } // don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server) // this check applies to internal and external collateral, but internal collaterals are not necessarely a P2PKH if (collateralTxDest == CTxDestination(ptx.keyIDOwner) || collateralTxDest == CTxDestination(ptx.keyIDVoting)) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-reuse"); } if (pindexPrev) { auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash()); // only allow reusing of addresses when it's for the same collateral (which replaces the old MN) if (mnList.HasUniqueProperty(ptx.addr) && mnList.GetUniquePropertyMN(ptx.addr)->collateralOutpoint != collateralOutpoint) { return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-addr"); } // never allow duplicate keys, even if this ProTx would replace an existing MN if (mnList.HasUniqueProperty(ptx.keyIDOwner) || mnList.HasUniqueProperty(ptx.pubKeyOperator)) { return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-key"); } if (!deterministicMNManager->IsDIP3Enforced(pindexPrev->nHeight)) { if (ptx.keyIDOwner != ptx.keyIDVoting) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-not-same"); } } } if (!CheckInputsHash(tx, ptx, state)) { return false; } if (!keyForPayloadSig.IsNull()) { // collateral is not part of this ProRegTx, so we must verify ownership of the collateral if (!CheckStringSig(ptx, keyForPayloadSig, state)) { return false; } } else { // collateral is part of this ProRegTx, so we know the collateral is owned by the issuer if (!ptx.vchSig.empty()) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig"); } } return true; }
void updateMasternode(interfaces::Node& node, const COutPoint& outpoint, int status) { qDebug() << "MasternodeTablePriv::updateMasternode" + QString::fromStdString(strprintf("%s-%d", outpoint.hash.ToString(), outpoint.n)) + " " + QString::number(status); // Find bounds of this masternode in model QString strOutpoint = QString::fromStdString(outpoint.ToStringShort()); QList<MasternodeTableEntry>::iterator lower = qLowerBound( cachedMasternodeTable.begin(), cachedMasternodeTable.end(), strOutpoint, outpointEntryLessThan()); QList<MasternodeTableEntry>::iterator upper = qUpperBound( cachedMasternodeTable.begin(), cachedMasternodeTable.end(), strOutpoint, outpointEntryLessThan()); int lowerIndex = (lower - cachedMasternodeTable.begin()); int upperIndex = (upper - cachedMasternodeTable.begin()); bool inModel = (lower != upper); switch(status) { case CT_NEW: if(inModel) { //must be our own one and we are just at a clean start -> try to update interfaces::Masternode masternode = node.getMasternode(outpoint); lower->txhash = QString::fromStdString(masternode.outpoint.hash.ToString()); lower->n = masternode.outpoint.n; lower->alias = QString::fromStdString(masternode.alias); lower->address = QString::fromStdString(masternode.address); lower->protocol = masternode.protocol; lower->daemon = masternode.daemon; lower->sentinel = masternode.sentinel; lower->status = QString::fromStdString(masternode.status); lower->active = masternode.active; lower->lastseen = masternode.last_seen; lower->payee = QString::fromStdString(masternode.payee); lower->banscore = masternode.banscore; parent->emitDataChanged(lowerIndex); break; } { // Find masternode on platform interfaces::Masternode masternode = node.getMasternode(outpoint); if(masternode.outpoint == COutPoint()) { qWarning() << "MasternodeTablePriv::updateMasternode: Warning: Got CT_NEW, but masternode is not on platform: " + QString::fromStdString(strprintf("%s-%d", outpoint.hash.ToString(), outpoint.n)) + " " + QString::number(status); break; } // Added -- insert at the right position MasternodeTableEntry::Type addressType = translateMasternodeType( QString::fromStdString(masternode.alias)); MasternodeTableEntry toInsert = MasternodeTableEntry( addressType, QString::fromStdString(masternode.outpoint.hash.ToString()), masternode.outpoint.n, QString::fromStdString(masternode.alias), QString::fromStdString(masternode.address), masternode.protocol, masternode.daemon, masternode.sentinel, QString::fromStdString(masternode.status), masternode.active, masternode.last_seen, QString::fromStdString(masternode.payee), masternode.banscore); parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex); cachedMasternodeTable.insert(lowerIndex, toInsert); parent->endInsertRows(); } break; case CT_DELETED: if(!inModel) { qWarning() << "MasternodeTablePriv::updateMasternode: Warning: Got CT_DELETED, but masternode is not in model: " + QString::fromStdString(strprintf("%s-%d", outpoint.hash.ToString(), outpoint.n)) + " " + QString::number(status); break; } // Removed -- remove entire masternode from table parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); cachedMasternodeTable.erase(lower, upper); parent->endRemoveRows(); break; case CT_UPDATED: if(!inModel) { qWarning() << "MasternodeTablePriv::updateMasternode: Warning: Got CT_UPDATED, but entry is not in model: "+ QString::fromStdString(strprintf("%s-%d", outpoint.hash.ToString(), outpoint.n)) + " " + QString::number(status); break; } { // Find masternode on platform interfaces::Masternode masternode = node.getMasternode(outpoint); //don't remove our own nodes if(lower->alias == "" && masternode.outpoint == COutPoint()) { // did we receive a wrong signal? The node got highes priority, delete the entry qWarning() << "MasternodeTablePriv::updateMasternode: Warning: Got CT_UPDATED, but masternode is not on platform: " + QString::fromStdString(strprintf("%s-%d", outpoint.hash.ToString(), outpoint.n)) + " " + QString::number(status); parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1); cachedMasternodeTable.erase(lower, upper); parent->endRemoveRows(); break; } MasternodeTableEntry::Type addressType = translateMasternodeType( QString::fromStdString(masternode.alias)); lower->type = addressType; lower->txhash = QString::fromStdString(masternode.outpoint.hash.ToString()); lower->n = masternode.outpoint.n; lower->alias = QString::fromStdString(masternode.alias); lower->address = QString::fromStdString(masternode.address); lower->protocol = masternode.protocol; lower->daemon = masternode.daemon; lower->sentinel = masternode.sentinel; lower->status = QString::fromStdString(masternode.status); lower->active = masternode.active; lower->lastseen = masternode.last_seen; lower->payee = QString::fromStdString(masternode.payee); lower->banscore = masternode.banscore; parent->emitDataChanged(lowerIndex); } break; } }
// Right now this is only testing eviction performance in an extremely small // mempool. Code needs to be written to generate a much wider variety of // unique transactions for a more meaningful performance measurement. static void MempoolEviction(benchmark::State& state) { CMutableTransaction tx1 = CMutableTransaction(); tx1.vin.resize(1); tx1.vin[0].scriptSig = CScript() << OP_1; tx1.vout.resize(1); tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL; tx1.vout[0].nValue = 10 * COIN; CMutableTransaction tx2 = CMutableTransaction(); tx2.vin.resize(1); tx2.vin[0].scriptSig = CScript() << OP_2; tx2.vout.resize(1); tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL; tx2.vout[0].nValue = 10 * COIN; CMutableTransaction tx3 = CMutableTransaction(); tx3.vin.resize(1); tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0); tx3.vin[0].scriptSig = CScript() << OP_2; tx3.vout.resize(1); tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL; tx3.vout[0].nValue = 10 * COIN; CMutableTransaction tx4 = CMutableTransaction(); tx4.vin.resize(2); tx4.vin[0].prevout.SetNull(); tx4.vin[0].scriptSig = CScript() << OP_4; tx4.vin[1].prevout.SetNull(); tx4.vin[1].scriptSig = CScript() << OP_4; tx4.vout.resize(2); tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL; tx4.vout[0].nValue = 10 * COIN; tx4.vout[1].scriptPubKey = CScript() << OP_4 << OP_EQUAL; tx4.vout[1].nValue = 10 * COIN; CMutableTransaction tx5 = CMutableTransaction(); tx5.vin.resize(2); tx5.vin[0].prevout = COutPoint(tx4.GetHash(), 0); tx5.vin[0].scriptSig = CScript() << OP_4; tx5.vin[1].prevout.SetNull(); tx5.vin[1].scriptSig = CScript() << OP_5; tx5.vout.resize(2); tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL; tx5.vout[0].nValue = 10 * COIN; tx5.vout[1].scriptPubKey = CScript() << OP_5 << OP_EQUAL; tx5.vout[1].nValue = 10 * COIN; CMutableTransaction tx6 = CMutableTransaction(); tx6.vin.resize(2); tx6.vin[0].prevout = COutPoint(tx4.GetHash(), 1); tx6.vin[0].scriptSig = CScript() << OP_4; tx6.vin[1].prevout.SetNull(); tx6.vin[1].scriptSig = CScript() << OP_6; tx6.vout.resize(2); tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL; tx6.vout[0].nValue = 10 * COIN; tx6.vout[1].scriptPubKey = CScript() << OP_6 << OP_EQUAL; tx6.vout[1].nValue = 10 * COIN; CMutableTransaction tx7 = CMutableTransaction(); tx7.vin.resize(2); tx7.vin[0].prevout = COutPoint(tx5.GetHash(), 0); tx7.vin[0].scriptSig = CScript() << OP_5; tx7.vin[1].prevout = COutPoint(tx6.GetHash(), 0); tx7.vin[1].scriptSig = CScript() << OP_6; tx7.vout.resize(2); tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL; tx7.vout[0].nValue = 10 * COIN; tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL; tx7.vout[1].nValue = 10 * COIN; CTxMemPool pool(CFeeRate(1000)); while (state.KeepRunning()) { AddTx(tx1, 10000LL, pool); AddTx(tx2, 5000LL, pool); AddTx(tx3, 20000LL, pool); AddTx(tx4, 7000LL, pool); AddTx(tx5, 1000LL, pool); AddTx(tx6, 1100LL, pool); AddTx(tx7, 9000LL, pool); pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); pool.TrimToSize(GetVirtualTransactionSize(tx1)); } }
void CoinControlDialog::updateView() { if (!model || !model->getOptionsModel() || !model->getAddressTableModel()) return; bool treeMode = ui->radioTreeMode->isChecked(); ui->treeWidget->clear(); ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox ui->treeWidget->setAlternatingRowColors(!treeMode); QFlags<Qt::ItemFlag> flgCheckbox = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate; int nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); std::map<QString, std::vector<COutput> > mapCoins; model->listCoins(mapCoins); for (const std::pair<QString, std::vector<COutput>>& coins : mapCoins) { CCoinControlWidgetItem *itemWalletAddress = new CCoinControlWidgetItem(); itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); QString sWalletAddress = coins.first; QString sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress); if (sWalletLabel.isEmpty()) sWalletLabel = tr("(no label)"); if (treeMode) { // wallet address ui->treeWidget->addTopLevelItem(itemWalletAddress); itemWalletAddress->setFlags(flgTristate); itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); // label itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel); // address itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress); } CAmount nSum = 0; int nChildren = 0; for (const COutput& out : coins.second) { nSum += out.tx->tx->vout[out.i].nValue; nChildren++; CCoinControlWidgetItem *itemOutput; if (treeMode) itemOutput = new CCoinControlWidgetItem(itemWalletAddress); else itemOutput = new CCoinControlWidgetItem(ui->treeWidget); itemOutput->setFlags(flgCheckbox); itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked); // address CTxDestination outputAddress; QString sAddress = ""; if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, outputAddress)) { sAddress = QString::fromStdString(CWiFicoinAddress(outputAddress).ToString()); // if listMode or change => show wificoin address. In tree mode, address is not shown again for direct wallet address outputs if (!treeMode || (!(sAddress == sWalletAddress))) itemOutput->setText(COLUMN_ADDRESS, sAddress); } // label if (!(sAddress == sWalletAddress)) // change { // tooltip from where the change comes from itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)").arg(sWalletLabel).arg(sWalletAddress)); itemOutput->setText(COLUMN_LABEL, tr("(change)")); } else if (!treeMode) { QString sLabel = model->getAddressTableModel()->labelForAddress(sAddress); if (sLabel.isEmpty()) sLabel = tr("(no label)"); itemOutput->setText(COLUMN_LABEL, sLabel); } // amount itemOutput->setText(COLUMN_AMOUNT, WiFicoinUnits::format(nDisplayUnit, out.tx->tx->vout[out.i].nValue)); itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.tx->tx->vout[out.i].nValue)); // padding so that sorting works correctly // date itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.tx->GetTxTime())); itemOutput->setData(COLUMN_DATE, Qt::UserRole, QVariant((qlonglong)out.tx->GetTxTime())); // confirmations itemOutput->setText(COLUMN_CONFIRMATIONS, QString::number(out.nDepth)); itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, QVariant((qlonglong)out.nDepth)); // transaction hash uint256 txhash = out.tx->GetHash(); itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(txhash.GetHex())); // vout index itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i)); // disable locked coins if (model->isLockedCoin(txhash, out.i)) { COutPoint outpt(txhash, out.i); coinControl->UnSelect(outpt); // just to be sure itemOutput->setDisabled(true); itemOutput->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed")); } // set checkbox if (coinControl->IsSelected(COutPoint(txhash, out.i))) itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked); } // amount if (treeMode) { itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")"); itemWalletAddress->setText(COLUMN_AMOUNT, WiFicoinUnits::format(nDisplayUnit, nSum)); itemWalletAddress->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)nSum)); } } // expand all partially selected if (treeMode) { for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked) ui->treeWidget->topLevelItem(i)->setExpanded(true); } // sort view sortView(sortColumn, sortOrder); ui->treeWidget->setEnabled(true); }
void MasternodeTableModel::updateMasternode(const QString &_hash, const int &_n, int status) { COutPoint outpoint = COutPoint(uint256S(_hash.toStdString()), _n); priv->updateMasternode(clientModel->node(), outpoint, status); }
UniValue sendrawtransaction(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( "sendrawtransaction \"hexstring\" ( allowhighfees )\n" "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n" "\nAlso see createrawtransaction and signrawtransaction calls.\n" "\nArguments:\n" "1. \"hexstring\" (string, required) The hex string of the raw transaction)\n" "2. allowhighfees (boolean, optional, default=false) Allow high fees\n" "\nResult:\n" "\"hex\" (string) The transaction hash in hex\n" "\nExamples:\n" "\nCreate a transaction\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") + "Sign the transaction, and get back the hex\n" + HelpExampleCli("signrawtransaction", "\"myhex\"") + "\nSend the transaction (signed hex)\n" + HelpExampleCli("sendrawtransaction", "\"signedhex\"") + "\nAs a json rpc call\n" + HelpExampleRpc("sendrawtransaction", "\"signedhex\"") ); ObserveSafeMode(); LOCK(cs_main); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}); // parse hex string from parameter CMutableTransaction mtx; if (!DecodeHexTx(mtx, request.params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); CTransactionRef tx(MakeTransactionRef(std::move(mtx))); const uint256& hashTx = tx->GetHash(); CAmount nMaxRawTxFee = maxTxFee; if (!request.params[1].isNull() && request.params[1].get_bool()) nMaxRawTxFee = 0; CCoinsViewCache &view = *pcoinsTip; bool fHaveChain = false; for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) { const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o)); fHaveChain = !existingCoin.IsSpent(); } bool fHaveMempool = mempool.exists(hashTx); if (!fHaveMempool && !fHaveChain) { // push to local node and sync with wallets CValidationState state; bool fMissingInputs; bool fLimitFree = true; if (!AcceptToMemoryPool(mempool, state, std::move(tx), fLimitFree, &fMissingInputs, nullptr, false, nMaxRawTxFee)) { if (state.IsInvalid()) { throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); } else { if (fMissingInputs) { throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs"); } throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason()); } } } else if (fHaveChain) { throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); } if(!g_connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); CInv inv(MSG_TX, hashTx); g_connman->ForEachNode([&inv](CNode* pnode) { pnode->PushInventory(inv); }); return hashTx.GetHex(); }
static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) { if (!CheckWarmup(req)) return false; std::string param; const RetFormat rf = ParseDataFormat(param, strURIPart); std::vector<std::string> uriParts; if (param.length() > 1) { std::string strUriParams = param.substr(1); boost::split(uriParts, strUriParams, boost::is_any_of("/")); } // throw exception in case of an empty request std::string strRequestMutable = req->ReadBody(); if (strRequestMutable.length() == 0 && uriParts.size() == 0) return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request"); bool fInputParsed = false; bool fCheckMemPool = false; std::vector<COutPoint> vOutPoints; // parse/deserialize input // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ... if (uriParts.size() > 0) { //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...) if (uriParts.size() > 0 && uriParts[0] == "checkmempool") fCheckMemPool = true; for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++) { uint256 txid; int32_t nOutput; std::string strTxid = uriParts[i].substr(0, uriParts[i].find("-")); std::string strOutput = uriParts[i].substr(uriParts[i].find("-")+1); if (!ParseInt32(strOutput, &nOutput) || !IsHex(strTxid)) return RESTERR(req, HTTP_BAD_REQUEST, "Parse error"); txid.SetHex(strTxid); vOutPoints.push_back(COutPoint(txid, (uint32_t)nOutput)); } if (vOutPoints.size() > 0) fInputParsed = true; else return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request"); } switch (rf) { case RF_HEX: { // convert hex to bin, continue then with bin part std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable); strRequestMutable.assign(strRequestV.begin(), strRequestV.end()); } case RF_BINARY: { try { //deserialize only if user sent a request if (strRequestMutable.size() > 0) { if (fInputParsed) //don't allow sending input over URI and HTTP RAW DATA return RESTERR(req, HTTP_BAD_REQUEST, "Combination of URI scheme inputs and raw post data is not allowed"); CDataStream oss(SER_NETWORK, PROTOCOL_VERSION); oss << strRequestMutable; oss >> fCheckMemPool; oss >> vOutPoints; } } catch (const std::ios_base::failure& e) { // abort in case of unreadable binary data return RESTERR(req, HTTP_BAD_REQUEST, "Parse error"); } break; } case RF_JSON: { if (!fInputParsed) return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request"); break; } default: { return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); } } // limit max outpoints if (vOutPoints.size() > MAX_GETUTXOS_OUTPOINTS) return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS, vOutPoints.size())); // check spentness and form a bitmap (as well as a JSON capable human-readable string representation) std::vector<unsigned char> bitmap; std::vector<CCoin> outs; std::string bitmapStringRepresentation; std::vector<bool> hits; bitmap.resize((vOutPoints.size() + 7) / 8); { LOCK2(cs_main, mempool.cs); CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); CCoinsViewCache& viewChain = *pcoinsTip; CCoinsViewMemPool viewMempool(&viewChain, mempool); if (fCheckMemPool) view.SetBackend(viewMempool); // switch cache backend to db+mempool in case user likes to query mempool for (size_t i = 0; i < vOutPoints.size(); i++) { bool hit = false; Coin coin; if (view.GetCoin(vOutPoints[i], coin) && !mempool.isSpent(vOutPoints[i])) { hit = true; outs.emplace_back(std::move(coin)); } hits.push_back(hit); bitmapStringRepresentation.append(hit ? "1" : "0"); // form a binary string representation (human-readable for json output) bitmap[i / 8] |= ((uint8_t)hit) << (i % 8); } } switch (rf) { case RF_BINARY: { // serialize data // use exact same output as mentioned in Bip64 CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION); ssGetUTXOResponse << chainActive.Height() << chainActive.Tip()->GetBlockHash() << bitmap << outs; std::string ssGetUTXOResponseString = ssGetUTXOResponse.str(); req->WriteHeader("Content-Type", "application/octet-stream"); req->WriteReply(HTTP_OK, ssGetUTXOResponseString); return true; } case RF_HEX: { CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION); ssGetUTXOResponse << chainActive.Height() << chainActive.Tip()->GetBlockHash() << bitmap << outs; std::string strHex = HexStr(ssGetUTXOResponse.begin(), ssGetUTXOResponse.end()) + "\n"; req->WriteHeader("Content-Type", "text/plain"); req->WriteReply(HTTP_OK, strHex); return true; } case RF_JSON: { UniValue objGetUTXOResponse(UniValue::VOBJ); // pack in some essentials // use more or less the same output as mentioned in Bip64 objGetUTXOResponse.push_back(Pair("chainHeight", chainActive.Height())); objGetUTXOResponse.push_back(Pair("chaintipHash", chainActive.Tip()->GetBlockHash().GetHex())); objGetUTXOResponse.push_back(Pair("bitmap", bitmapStringRepresentation)); UniValue utxos(UniValue::VARR); for (const CCoin& coin : outs) { UniValue utxo(UniValue::VOBJ); utxo.push_back(Pair("height", (int32_t)coin.nHeight)); utxo.push_back(Pair("value", ValueFromAmount(coin.out.nValue))); // include the script in a json output UniValue o(UniValue::VOBJ); ScriptPubKeyToUniv(coin.out.scriptPubKey, o, true); utxo.push_back(Pair("scriptPubKey", o)); utxos.push_back(utxo); } objGetUTXOResponse.push_back(Pair("utxos", utxos)); // return json string std::string strJSON = objGetUTXOResponse.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); return true; } default: { return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); } } // not reached return true; // continue to process further HTTP reqs on this cxn }
bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& _state, CDeterministicMNList& mnListRet, bool debugLogs) { AssertLockHeld(cs); int nHeight = pindexPrev->nHeight + 1; CDeterministicMNList oldList = GetListForBlock(pindexPrev->GetBlockHash()); CDeterministicMNList newList = oldList; newList.SetBlockHash(uint256()); // we can't know the final block hash, so better not return a (invalid) block hash newList.SetHeight(nHeight); auto payee = oldList.GetMNPayee(); // we iterate the oldList here and update the newList // this is only valid as long these have not diverged at this point, which is the case as long as we don't add // code above this loop that modifies newList oldList.ForEachMN(false, [&](const CDeterministicMNCPtr& dmn) { if (!dmn->pdmnState->confirmedHash.IsNull()) { // already confirmed return; } // this works on the previous block, so confirmation will happen one block after nMasternodeMinimumConfirmations // has been reached, but the block hash will then point to the block at nMasternodeMinimumConfirmations int nConfirmations = pindexPrev->nHeight - dmn->pdmnState->nRegisteredHeight; if (nConfirmations >= Params().GetConsensus().nMasternodeMinimumConfirmations) { CDeterministicMNState newState = *dmn->pdmnState; newState.UpdateConfirmedHash(dmn->proTxHash, pindexPrev->GetBlockHash()); newList.UpdateMN(dmn->proTxHash, std::make_shared<CDeterministicMNState>(newState)); } }); DecreasePoSePenalties(newList); // we skip the coinbase for (int i = 1; i < (int)block.vtx.size(); i++) { const CTransaction& tx = *block.vtx[i]; if (tx.nVersion != 3) { // only interested in special TXs continue; } if (tx.nType == TRANSACTION_PROVIDER_REGISTER) { CProRegTx proTx; if (!GetTxPayload(tx, proTx)) { assert(false); // this should have been handled already } auto dmn = std::make_shared<CDeterministicMN>(); dmn->proTxHash = tx.GetHash(); // collateralOutpoint is either pointing to an external collateral or to the ProRegTx itself if (proTx.collateralOutpoint.hash.IsNull()) { dmn->collateralOutpoint = COutPoint(tx.GetHash(), proTx.collateralOutpoint.n); } else { dmn->collateralOutpoint = proTx.collateralOutpoint; } Coin coin; if (!proTx.collateralOutpoint.hash.IsNull() && (!GetUTXOCoin(dmn->collateralOutpoint, coin) || coin.out.nValue != 1000 * COIN)) { // should actually never get to this point as CheckProRegTx should have handled this case. // We do this additional check nevertheless to be 100% sure return _state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral"); } auto replacedDmn = newList.GetMNByCollateral(dmn->collateralOutpoint); if (replacedDmn != nullptr) { // This might only happen with a ProRegTx that refers an external collateral // In that case the new ProRegTx will replace the old one. This means the old one is removed // and the new one is added like a completely fresh one, which is also at the bottom of the payment list newList.RemoveMN(replacedDmn->proTxHash); if (debugLogs) { LogPrintf("CDeterministicMNManager::%s -- MN %s removed from list because collateral was used for a new ProRegTx. collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d\n", __func__, replacedDmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort(), nHeight, newList.GetAllMNsCount()); } } if (newList.HasUniqueProperty(proTx.addr)) { return _state.DoS(100, false, REJECT_CONFLICT, "bad-protx-dup-addr"); } if (newList.HasUniqueProperty(proTx.keyIDOwner) || newList.HasUniqueProperty(proTx.pubKeyOperator)) { return _state.DoS(100, false, REJECT_CONFLICT, "bad-protx-dup-key"); } dmn->nOperatorReward = proTx.nOperatorReward; dmn->pdmnState = std::make_shared<CDeterministicMNState>(proTx); CDeterministicMNState dmnState = *dmn->pdmnState; dmnState.nRegisteredHeight = nHeight; if (proTx.addr == CService()) { // start in banned pdmnState as we need to wait for a ProUpServTx dmnState.nPoSeBanHeight = nHeight; } dmn->pdmnState = std::make_shared<CDeterministicMNState>(dmnState); newList.AddMN(dmn); if (debugLogs) { LogPrintf("CDeterministicMNManager::%s -- MN %s added at height %d: %s\n", __func__, tx.GetHash().ToString(), nHeight, proTx.ToString()); } } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) { CProUpServTx proTx; if (!GetTxPayload(tx, proTx)) { assert(false); // this should have been handled already } if (newList.HasUniqueProperty(proTx.addr) && newList.GetUniquePropertyMN(proTx.addr)->proTxHash != proTx.proTxHash) { return _state.DoS(100, false, REJECT_CONFLICT, "bad-protx-dup-addr"); } CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash); if (!dmn) { return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); } auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState); newState->addr = proTx.addr; newState->scriptOperatorPayout = proTx.scriptOperatorPayout; if (newState->nPoSeBanHeight != -1) { // only revive when all keys are set if (newState->pubKeyOperator.IsValid() && !newState->keyIDVoting.IsNull() && !newState->keyIDOwner.IsNull()) { newState->nPoSePenalty = 0; newState->nPoSeBanHeight = -1; newState->nPoSeRevivedHeight = nHeight; if (debugLogs) { LogPrintf("CDeterministicMNManager::%s -- MN %s revived at height %d\n", __func__, proTx.proTxHash.ToString(), nHeight); } } } newList.UpdateMN(proTx.proTxHash, newState); if (debugLogs) { LogPrintf("CDeterministicMNManager::%s -- MN %s updated at height %d: %s\n", __func__, proTx.proTxHash.ToString(), nHeight, proTx.ToString()); } } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) { CProUpRegTx proTx; if (!GetTxPayload(tx, proTx)) { assert(false); // this should have been handled already } CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash); if (!dmn) { return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); } auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState); if (newState->pubKeyOperator != proTx.pubKeyOperator) { // reset all operator related fields and put MN into PoSe-banned state in case the operator key changes newState->ResetOperatorFields(); newState->BanIfNotBanned(nHeight); } newState->pubKeyOperator = proTx.pubKeyOperator; newState->keyIDVoting = proTx.keyIDVoting; newState->scriptPayout = proTx.scriptPayout; newList.UpdateMN(proTx.proTxHash, newState); if (debugLogs) { LogPrintf("CDeterministicMNManager::%s -- MN %s updated at height %d: %s\n", __func__, proTx.proTxHash.ToString(), nHeight, proTx.ToString()); } } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) { CProUpRevTx proTx; if (!GetTxPayload(tx, proTx)) { assert(false); // this should have been handled already } CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash); if (!dmn) { return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); } auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState); newState->ResetOperatorFields(); newState->BanIfNotBanned(nHeight); newState->nRevocationReason = proTx.nReason; newList.UpdateMN(proTx.proTxHash, newState); if (debugLogs) { LogPrintf("CDeterministicMNManager::%s -- MN %s revoked operator key at height %d: %s\n", __func__, proTx.proTxHash.ToString(), nHeight, proTx.ToString()); } } else if (tx.nType == TRANSACTION_QUORUM_COMMITMENT) { llmq::CFinalCommitmentTxPayload qc; if (!GetTxPayload(tx, qc)) { assert(false); // this should have been handled already } if (!qc.commitment.IsNull()) { HandleQuorumCommitment(qc.commitment, newList, debugLogs); } } } // we skip the coinbase for (int i = 1; i < (int)block.vtx.size(); i++) { const CTransaction& tx = *block.vtx[i]; // check if any existing MN collateral is spent by this transaction for (const auto& in : tx.vin) { auto dmn = newList.GetMNByCollateral(in.prevout); if (dmn && dmn->collateralOutpoint == in.prevout) { newList.RemoveMN(dmn->proTxHash); if (debugLogs) { LogPrintf("CDeterministicMNManager::%s -- MN %s removed from list because collateral was spent. collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d\n", __func__, dmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort(), nHeight, newList.GetAllMNsCount()); } } } } // The payee for the current block was determined by the previous block's list but it might have disappeared in the // current block. We still pay that MN one last time however. if (payee && newList.HasMN(payee->proTxHash)) { auto newState = std::make_shared<CDeterministicMNState>(*newList.GetMN(payee->proTxHash)->pdmnState); newState->nLastPaidHeight = nHeight; newList.UpdateMN(payee->proTxHash, newState); } mnListRet = std::move(newList); return true; }
void CTxMemPool::check(const CCoinsViewCache *pcoins) const { LOCK(cs); if (nCheckFrequency == 0) return; if (GetRand(std::numeric_limits<uint32_t>::max()) >= nCheckFrequency) return; LogPrint(BCLog::MEMPOOL, "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); uint64_t checkTotal = 0; uint64_t innerUsage = 0; CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins)); const int64_t spendheight = GetSpendHeight(mempoolDuplicate); std::list<const CTxMemPoolEntry*> waitingOnDependants; for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { unsigned int i = 0; checkTotal += it->GetTxSize(); innerUsage += it->DynamicMemoryUsage(); const CTransaction& tx = it->GetTx(); txlinksMap::const_iterator linksiter = mapLinks.find(it); assert(linksiter != mapLinks.end()); const TxLinks &links = linksiter->second; innerUsage += memusage::DynamicUsage(links.parents) + memusage::DynamicUsage(links.children); bool fDependsWait = false; setEntries setParentCheck; int64_t parentSizes = 0; int64_t parentSigOpCost = 0; for (const CTxIn &txin : tx.vin) { // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash); if (it2 != mapTx.end()) { const CTransaction& tx2 = it2->GetTx(); assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull()); fDependsWait = true; if (setParentCheck.insert(it2).second) { parentSizes += it2->GetTxSize(); parentSigOpCost += it2->GetSigOpCost(); } } else { assert(pcoins->HaveCoin(txin.prevout)); } // Check whether its inputs are marked in mapNextTx. auto it3 = mapNextTx.find(txin.prevout); assert(it3 != mapNextTx.end()); assert(it3->first == &txin.prevout); assert(it3->second == &tx); i++; } assert(setParentCheck == GetMemPoolParents(it)); // Verify ancestor state is correct. setEntries setAncestors; uint64_t nNoLimit = std::numeric_limits<uint64_t>::max(); std::string dummy; CalculateMemPoolAncestors(*it, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy); uint64_t nCountCheck = setAncestors.size() + 1; uint64_t nSizeCheck = it->GetTxSize(); CAmount nFeesCheck = it->GetModifiedFee(); int64_t nSigOpCheck = it->GetSigOpCost(); for (txiter ancestorIt : setAncestors) { nSizeCheck += ancestorIt->GetTxSize(); nFeesCheck += ancestorIt->GetModifiedFee(); nSigOpCheck += ancestorIt->GetSigOpCost(); } assert(it->GetCountWithAncestors() == nCountCheck); assert(it->GetSizeWithAncestors() == nSizeCheck); assert(it->GetSigOpCostWithAncestors() == nSigOpCheck); assert(it->GetModFeesWithAncestors() == nFeesCheck); // Check children against mapNextTx CTxMemPool::setEntries setChildrenCheck; auto iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetHash(), 0)); int64_t childSizes = 0; for (; iter != mapNextTx.end() && iter->first->hash == it->GetTx().GetHash(); ++iter) { txiter childit = mapTx.find(iter->second->GetHash()); assert(childit != mapTx.end()); // mapNextTx points to in-mempool transactions if (setChildrenCheck.insert(childit).second) { childSizes += childit->GetTxSize(); } } assert(setChildrenCheck == GetMemPoolChildren(it)); // Also check to make sure size is greater than sum with immediate children. // just a sanity check, not definitive that this calc is correct... assert(it->GetSizeWithDescendants() >= childSizes + it->GetTxSize()); if (fDependsWait) waitingOnDependants.push_back(&(*it)); else { CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight); } } unsigned int stepsSinceLastRemove = 0; while (!waitingOnDependants.empty()) { const CTxMemPoolEntry* entry = waitingOnDependants.front(); waitingOnDependants.pop_front(); CValidationState state; if (!mempoolDuplicate.HaveInputs(entry->GetTx())) { waitingOnDependants.push_back(entry); stepsSinceLastRemove++; assert(stepsSinceLastRemove < waitingOnDependants.size()); } else { CheckInputsAndUpdateCoins(entry->GetTx(), mempoolDuplicate, spendheight); stepsSinceLastRemove = 0; } } for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) { uint256 hash = it->second->GetHash(); indexed_transaction_set::const_iterator it2 = mapTx.find(hash); const CTransaction& tx = it2->GetTx(); assert(it2 != mapTx.end()); assert(&tx == it->second); } assert(totalTxSize == checkTotal); assert(innerUsage == cachedInnerUsage); }
std::string TxToString(uint256 BlockHash, const CTransaction& tx) { CAmount Input = 0; CAmount Output = tx.GetValueOut(); std::string InputsContentCells[] = {_("#"), _("Taken from"), _("Address"), _("Amount")}; std::string InputsContent = makeHTMLTableRow(InputsContentCells, sizeof(InputsContentCells) / sizeof(std::string)); std::string OutputsContentCells[] = {_("#"), _("Redeemed in"), _("Address"), _("Amount")}; std::string OutputsContent = makeHTMLTableRow(OutputsContentCells, sizeof(OutputsContentCells) / sizeof(std::string)); if (tx.IsCoinBase()) { std::string InputsContentCells[] = { "0", "coinbase", "-", ValueToString(Output)}; InputsContent += makeHTMLTableRow(InputsContentCells, sizeof(InputsContentCells) / sizeof(std::string)); } else for (unsigned int i = 0; i < tx.vin.size(); i++) { COutPoint Out = tx.vin[i].prevout; CTxOut PrevOut = getPrevOut(tx.vin[i].prevout); if (PrevOut.nValue < 0) Input = -Params().MaxMoneyOut(); else Input += PrevOut.nValue; std::string InputsContentCells[] = { itostr(i), "<span>" + makeHRef(Out.hash.GetHex()) + ":" + itostr(Out.n) + "</span>", ScriptToString(PrevOut.scriptPubKey, true), ValueToString(PrevOut.nValue)}; InputsContent += makeHTMLTableRow(InputsContentCells, sizeof(InputsContentCells) / sizeof(std::string)); } uint256 TxHash = tx.GetHash(); for (unsigned int i = 0; i < tx.vout.size(); i++) { const CTxOut& Out = tx.vout[i]; uint256 HashNext = uint256S("0"); unsigned int nNext = 0; bool fAddrIndex = false; getNextIn(COutPoint(TxHash, i), HashNext, nNext); std::string OutputsContentCells[] = { itostr(i), (HashNext == uint256S("0")) ? (fAddrIndex ? _("no") : _("unknown")) : "<span>" + makeHRef(HashNext.GetHex()) + ":" + itostr(nNext) + "</span>", ScriptToString(Out.scriptPubKey, true), ValueToString(Out.nValue)}; OutputsContent += makeHTMLTableRow(OutputsContentCells, sizeof(OutputsContentCells) / sizeof(std::string)); } InputsContent = table + InputsContent + "</table>"; OutputsContent = table + OutputsContent + "</table>"; std::string Hash = TxHash.GetHex(); std::string Labels[] = { _("In Block"), "", _("Size"), itostr(GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)), _("Input"), tx.IsCoinBase() ? "-" : ValueToString(Input), _("Output"), ValueToString(Output), _("Fees"), tx.IsCoinBase() ? "-" : ValueToString(Input - Output), _("Timestamp"), "", _("Hash"), "<pre>" + Hash + "</pre>", }; // std::map<uint256, CBlockIndex*>::iterator iter = mapBlockIndex.find(BlockHash); BlockMap::iterator iter = mapBlockIndex.find(BlockHash); if (iter != mapBlockIndex.end()) { CBlockIndex* pIndex = iter->second; Labels[0 * 2 + 1] = makeHRef(itostr(pIndex->nHeight)); Labels[5 * 2 + 1] = TimeToString(pIndex->nTime); } std::string Content; Content += "<h2>" + _("Transaction") + " <span>" + Hash + "</span></h2>"; Content += makeHTMLTable(Labels, sizeof(Labels) / (2 * sizeof(std::string)), 2); Content += "</br>"; Content += "<h3>" + _("Inputs") + "</h3>"; Content += InputsContent; Content += "</br>"; Content += "<h3>" + _("Outputs") + "</h3>"; Content += OutputsContent; return Content; }
CTxIn::CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn, unsigned int nSequenceIn) { prevout = COutPoint(hashPrevTx, nOut); scriptSig = scriptSigIn; nSequence = nSequenceIn; }
UniValue createrawtransaction(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) throw std::runtime_error( "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime ) ( replaceable )\n" "\nCreate a transaction spending the given inputs and creating new outputs.\n" "Outputs can be addresses or data.\n" "Returns hex-encoded raw transaction.\n" "Note that the transaction's inputs are not signed, and\n" "it is not stored in the wallet or transmitted to the network.\n" "\nArguments:\n" "1. \"inputs\" (array, required) A json array of json objects\n" " [\n" " {\n" " \"txid\":\"id\", (string, required) The transaction id\n" " \"vout\":n, (numeric, required) The output number\n" " \"sequence\":n (numeric, optional) The sequence number\n" " } \n" " ,...\n" " ]\n" "2. \"outputs\" (object, required) a json object with outputs\n" " {\n" " \"address\": x.xxx, (numeric or string, required) The key is the bitcoin address, the numeric value (can be string) is the " + CURRENCY_UNIT + " amount\n" " \"data\": \"hex\" (string, required) The key is \"data\", the value is hex encoded data\n" " ,...\n" " }\n" "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n" " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n" "\nResult:\n" "\"transaction\" (string) hex string of the transaction\n" "\nExamples:\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"") + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"data\\\":\\\"00010203\\\"}\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"data\\\":\\\"00010203\\\"}\"") ); RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VOBJ, UniValue::VNUM}, true); if (request.params[0].isNull() || request.params[1].isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); UniValue inputs = request.params[0].get_array(); UniValue sendTo = request.params[1].get_obj(); CMutableTransaction rawTx; if (!request.params[2].isNull()) { int64_t nLockTime = request.params[2].get_int64(); if (nLockTime < 0 || nLockTime > std::numeric_limits<uint32_t>::max()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range"); rawTx.nLockTime = nLockTime; } bool rbfOptIn = request.params[3].isTrue(); for (unsigned int idx = 0; idx < inputs.size(); idx++) { const UniValue& input = inputs[idx]; const UniValue& o = input.get_obj(); uint256 txid = ParseHashO(o, "txid"); const UniValue& vout_v = find_value(o, "vout"); if (!vout_v.isNum()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); int nOutput = vout_v.get_int(); if (nOutput < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); uint32_t nSequence; if (rbfOptIn) { nSequence = MAX_BIP125_RBF_SEQUENCE; } else if (rawTx.nLockTime) { nSequence = std::numeric_limits<uint32_t>::max() - 1; } else { nSequence = std::numeric_limits<uint32_t>::max(); } // set the sequence number if passed in the parameters object const UniValue& sequenceObj = find_value(o, "sequence"); if (sequenceObj.isNum()) { int64_t seqNr64 = sequenceObj.get_int64(); if (seqNr64 < 0 || seqNr64 > std::numeric_limits<uint32_t>::max()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range"); } else { nSequence = (uint32_t)seqNr64; } } CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); rawTx.vin.push_back(in); } std::set<CTxDestination> destinations; std::vector<std::string> addrList = sendTo.getKeys(); for (const std::string& name_ : addrList) { if (name_ == "data") { std::vector<unsigned char> data = ParseHexV(sendTo[name_].getValStr(),"Data"); CTxOut out(0, CScript() << OP_RETURN << data); rawTx.vout.push_back(out); } else { CTxDestination destination = DecodeDestination(name_); if (!IsValidDestination(destination)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_); } if (!destinations.insert(destination).second) { throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); } CScript scriptPubKey = GetScriptForDestination(destination); CAmount nAmount = AmountFromValue(sendTo[name_]); CTxOut out(nAmount, scriptPubKey); rawTx.vout.push_back(out); } } if (!request.params[3].isNull() && rbfOptIn != SignalsOptInRBF(rawTx)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option"); } return EncodeHexTx(rawTx); }