ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<CTransaction>& vtx_missing) const { assert(!header.IsNull()); block = header; block.vtx.resize(txn_available.size()); size_t tx_missing_offset = 0; for (size_t i = 0; i < txn_available.size(); i++) { if (!txn_available[i]) { if (vtx_missing.size() <= tx_missing_offset) return READ_STATUS_INVALID; block.vtx[i] = vtx_missing[tx_missing_offset++]; } else block.vtx[i] = *txn_available[i]; } if (vtx_missing.size() != tx_missing_offset) return READ_STATUS_INVALID; CValidationState state; if (!CheckBlock(block, state, Params().GetConsensus())) { if (state.CorruptionPossible()) return READ_STATUS_FAILED; // Possible Short ID collision return READ_STATUS_INVALID; } LogPrint("cmpctblock", "Successfully reconstructed block %s with %lu txn prefilled, %lu txn from mempool and %lu txn requested\n", header.GetHash().ToString(), prefilled_count, mempool_count, vtx_missing.size()); if (vtx_missing.size() < 5) { for (const CTransaction& tx : vtx_missing) LogPrint("cmpctblock", "Reconstructed block %s required tx %s\n", header.GetHash().ToString(), tx.GetHash().ToString()); } return READ_STATUS_OK; }
static UniValue submitheader(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "submitheader \"hexdata\"\n" "\nDecode the given hexdata as a header and submit it as a candidate chain tip if valid." "\nThrows when the header is invalid.\n" "\nArguments\n" "1. \"hexdata\" (string, required) the hex-encoded block header data\n" "\nResult:\n" "None" "\nExamples:\n" + HelpExampleCli("submitheader", "\"aabbcc\"") + HelpExampleRpc("submitheader", "\"aabbcc\"")); } CBlockHeader h; if (!DecodeHexBlockHeader(h, request.params[0].get_str())) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block header decode failed"); } { LOCK(cs_main); if (!LookupBlockIndex(h.hashPrevBlock)) { throw JSONRPCError(RPC_VERIFY_ERROR, "Must submit previous header (" + h.hashPrevBlock.GetHex() + ") first"); } } CValidationState state; ProcessNewBlockHeaders({h}, state, Params(), /* ppindex */ nullptr, /* first_invalid */ nullptr); if (state.IsValid()) return NullUniValue; if (state.IsError()) { throw JSONRPCError(RPC_VERIFY_ERROR, FormatStateMessage(state)); } throw JSONRPCError(RPC_VERIFY_ERROR, state.GetRejectReason()); }
bool CheckProUpRevTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) { if (tx.nType != TRANSACTION_PROVIDER_UPDATE_REVOKE) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-type"); } CProUpRevTx 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"); } // ptx.nReason < CProUpRevTx::REASON_NOT_SPECIFIED is always `false` since // ptx.nReason is unsigned and CProUpRevTx::REASON_NOT_SPECIFIED == 0 if (ptx.nReason > CProUpRevTx::REASON_LAST) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-reason"); } if (pindexPrev) { auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash()); auto dmn = mnList.GetMN(ptx.proTxHash); if (!dmn) return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); if (!CheckInputsHash(tx, ptx, state)) return false; if (!CheckHashSig(ptx, dmn->pdmnState->pubKeyOperator, state)) return false; } return true; }
Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransaction&& mtx, std::vector<std::string>& errors, uint256& bumped_txid) { LOCK2(cs_main, wallet->cs_wallet); if (!errors.empty()) { return Result::MISC_ERROR; } auto it = txid.IsNull() ? wallet->mapWallet.end() : wallet->mapWallet.find(txid); if (it == wallet->mapWallet.end()) { errors.push_back("Invalid or non-wallet transaction id"); return Result::MISC_ERROR; } CWalletTx& oldWtx = it->second; // make sure the transaction still has no descendants and hasn't been mined in the meantime Result result = PreconditionChecks(wallet, oldWtx, errors); if (result != Result::OK) { return result; } CWalletTx wtxBumped(wallet, MakeTransactionRef(std::move(mtx))); // commit/broadcast the tx CReserveKey reservekey(wallet); wtxBumped.mapValue = oldWtx.mapValue; wtxBumped.mapValue["replaces_txid"] = oldWtx.GetHash().ToString(); wtxBumped.vOrderForm = oldWtx.vOrderForm; wtxBumped.strFromAccount = oldWtx.strFromAccount; wtxBumped.fTimeReceivedIsTxTime = true; wtxBumped.fFromMe = true; CValidationState state; if (!wallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) { // NOTE: CommitTransaction never returns false, so this should never happen. errors.push_back(strprintf("The transaction was rejected: %s", state.GetRejectReason())); return Result::WALLET_ERROR; } bumped_txid = wtxBumped.GetHash(); if (state.IsInvalid()) { // This can happen if the mempool rejected the transaction. Report // what happened in the "errors" response. errors.push_back(strprintf("Error: The transaction was rejected: %s", FormatStateMessage(state))); } // mark the original tx as bumped if (!wallet->MarkReplaced(oldWtx.GetHash(), wtxBumped.GetHash())) { // TODO: see if JSON-RPC has a standard way of returning a response // along with an exception. It would be good to return information about // wtxBumped to the caller even if marking the original transaction // replaced does not succeed for some reason. errors.push_back("Created new bumpfee transaction but could not mark the original transaction as replaced"); } return Result::OK; }
bool CFeeBumper::commit(CWallet *pWallet) { AssertLockHeld(pWallet->cs_wallet); if (!vErrors.empty() || currentResult != BumpFeeResult::OK) { return false; } if (txid.IsNull() || !pWallet->mapWallet.count(txid)) { vErrors.push_back("Invalid or non-wallet transaction id"); currentResult = BumpFeeResult::MISC_ERROR; return false; } CWalletTx& oldWtx = pWallet->mapWallet[txid]; // make sure the transaction still has no descendants and hasen't been mined in the meantime if (!preconditionChecks(pWallet, oldWtx)) { return false; } CWalletTx wtxBumped(pWallet, MakeTransactionRef(std::move(mtx))); // commit/broadcast the tx CReserveKey reservekey(pWallet); wtxBumped.mapValue = oldWtx.mapValue; wtxBumped.mapValue["replaces_txid"] = oldWtx.GetHash().ToString(); wtxBumped.vOrderForm = oldWtx.vOrderForm; wtxBumped.strFromAccount = oldWtx.strFromAccount; wtxBumped.fTimeReceivedIsTxTime = true; wtxBumped.fFromMe = true; CValidationState state; if (!pWallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) { // NOTE: CommitTransaction never returns false, so this should never happen. vErrors.push_back(strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason())); return false; } bumpedTxid = wtxBumped.GetHash(); if (state.IsInvalid()) { // This can happen if the mempool rejected the transaction. Report // what happened in the "errors" response. vErrors.push_back(strprintf("Error: The transaction was rejected: %s", FormatStateMessage(state))); } // mark the original tx as bumped if (!pWallet->MarkReplaced(oldWtx.GetHash(), wtxBumped.GetHash())) { // TODO: see if JSON-RPC has a standard way of returning a response // along with an exception. It would be good to return information about // wtxBumped to the caller even if marking the original transaction // replaced does not succeed for some reason. vErrors.push_back("Error: Created new bumpfee transaction but could not mark the original transaction as replaced."); } return true; }
void CheckFalse(int ndos, const std::string &msg) { CValidationState state; bool ret; int v; ret = handler->CheckValid( CTransaction(transactions[member_hash]), state, NULL); ret &= handler->CheckFormat( CTransaction(transactions[member_hash]), state, NULL); BOOST_CHECK_MESSAGE( ret == false && state.IsInvalid(v) && v == ndos, msg); }
static bool CheckHashSig(const ProTx& proTx, const CBLSPublicKey& pubKey, CValidationState& state) { if (!proTx.sig.VerifyInsecure(pubKey, ::SerializeHash(proTx))) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false); } return true; }
ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing) { assert(!header.IsNull()); uint256 hash; if (header.nVersionPoW2Witness == 0) hash = header.GetHashLegacy(); else hash = header.GetHashPoW2(); block = header; block.vtx.resize(txn_available.size()); size_t tx_missing_offset = 0; for (size_t i = 0; i < txn_available.size(); i++) { if (!txn_available[i]) { if (vtx_missing.size() <= tx_missing_offset) return READ_STATUS_INVALID; block.vtx[i] = vtx_missing[tx_missing_offset++]; } else block.vtx[i] = std::move(txn_available[i]); } // Make sure we can't call FillBlock again. header.SetNull(); txn_available.clear(); if (vtx_missing.size() != tx_missing_offset) return READ_STATUS_INVALID; CValidationState state; if (!CheckBlock(block, state, Params().GetConsensus())) { // TODO: We really want to just check merkle tree manually here, // but that is expensive, and CheckBlock caches a block's // "checked-status" (in the CBlock?). CBlock should be able to // check its own merkle root and cache that check. if (state.CorruptionPossible()) return READ_STATUS_FAILED; // Possible Short ID collision return READ_STATUS_CHECKBLOCK_FAILED; } LogPrint(BCLog::CMPCTBLOCK, "Successfully reconstructed block %s with %lu txn prefilled, %lu txn from mempool (incl at least %lu from extra pool) and %lu txn requested\n", hash.ToString(), prefilled_count, mempool_count, extra_count, vtx_missing.size()); if (vtx_missing.size() < 5) { for (const auto& tx : vtx_missing) { LogPrint(BCLog::CMPCTBLOCK, "Reconstructed block %s required tx %s\n", hash.ToString(), tx->GetHash().ToString()); } } return READ_STATUS_OK; }
// NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller static UniValue BIP22ValidationResult(const CValidationState& state) { if (state.IsValid()) return NullUniValue; if (state.IsError()) throw JSONRPCError(RPC_VERIFY_ERROR, FormatStateMessage(state)); if (state.IsInvalid()) { std::string strRejectReason = state.GetRejectReason(); if (strRejectReason.empty()) return "rejected"; return strRejectReason; } // Should be impossible return "valid?"; }
static bool CheckHashSig(const ProTx& proTx, const CKeyID& keyID, CValidationState& state) { std::string strError; if (!CHashSigner::VerifyHash(::SerializeHash(proTx), keyID, proTx.vchSig, strError)) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false, strError); } return true; }
static bool CheckStringSig(const ProTx& proTx, const CKeyID& keyID, CValidationState& state) { std::string strError; if (!CMessageSigner::VerifyMessage(keyID, proTx.vchSig, proTx.MakeSignString(), strError)) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false, strError); } return true; }
static bool CheckInputsHash(const CTransaction& tx, const ProTx& proTx, CValidationState& state) { uint256 inputsHash = CalcTxInputsHash(tx); if (inputsHash != proTx.inputsHash) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-inputs-hash"); } return true; }
bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, CWalletScanState &wss, string& strType, string& strErr) { try { // Unserialize // Taking advantage of the fact that pair serialization // is just the two items serialized one after the other ssKey >> strType; if (strType == "name") { string strAddress; ssKey >> strAddress; ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()]; } else if (strType == "tx") { uint256 hash; ssKey >> hash; CWalletTx& wtx = pwallet->mapWallet[hash]; ssValue >> wtx; CValidationState state; if (CheckTransaction(wtx, state) && (wtx.GetHash() == hash) && state.IsValid()) wtx.BindWallet(pwallet); else { pwallet->mapWallet.erase(hash); return false; } // Undo serialize changes in 31600 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) { if (!ssValue.empty()) { char fTmp; char fUnused; ssValue >> fTmp >> fUnused >> wtx.strFromAccount; strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str()); wtx.fTimeReceivedIsTxTime = fTmp; } else {
bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee) { // are the actual inputs available? if (!inputs.HaveInputs(tx)) { return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-missingorspent", false, strprintf("%s: inputs missing/spent", __func__)); } CAmount nValueIn = 0; for (unsigned int i = 0; i < tx.vin.size(); ++i) { const COutPoint &prevout = tx.vin[i].prevout; const Coin& coin = inputs.AccessCoin(prevout); assert(!coin.IsSpent()); // If prev is coinbase, check that it's matured if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) { return state.Invalid(false, REJECT_INVALID, "bad-txns-premature-spend-of-coinbase", strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight)); } // Check for negative or overflow input values nValueIn += coin.out.nValue; if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) { return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange"); } } const CAmount value_out = tx.GetValueOut(); if (nValueIn < value_out) { return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out))); } // Tally transaction fees const CAmount txfee_aux = nValueIn - value_out; if (!MoneyRange(txfee_aux)) { return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange"); } txfee = txfee_aux; return true; }
bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fCheckDuplicateInputs) { // Basic checks that don't depend on any context if (tx.vin.empty()) return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty"); if (tx.vout.empty()) return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); // Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability) if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT) return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); // Check for negative or overflow output values CAmount nValueOut = 0; for (const auto& txout : tx.vout) { if (txout.nValue < 0) return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative"); if (txout.nValue > MAX_MONEY) return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge"); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge"); } // Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock if (fCheckDuplicateInputs) { std::set<COutPoint> vInOutPoints; for (const auto& txin : tx.vin) { if (!vInOutPoints.insert(txin.prevout).second) return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate"); } } if (tx.IsCoinBase()) { if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) return state.DoS(100, false, REJECT_INVALID, "bad-cb-length"); } else { for (const auto& txin : tx.vin) if (txin.prevout.IsNull()) return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null"); } return true; }
/** * Ensure that the mempool won't accept coinbase transactions. */ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup) { CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; CMutableTransaction coinbaseTx; coinbaseTx.nVersion = 1; coinbaseTx.vin.resize(1); coinbaseTx.vout.resize(1); coinbaseTx.vin[0].scriptSig = CScript() << OP_11 << OP_EQUAL; coinbaseTx.vout[0].nValue = 1 * CENT; coinbaseTx.vout[0].scriptPubKey = scriptPubKey; BOOST_CHECK(CTransaction(coinbaseTx).IsCoinBase()); CValidationState state; LOCK(cs_main); unsigned int initialPoolSize = mempool.size(); BOOST_CHECK_EQUAL( false, AcceptToMemoryPool(mempool, state, MakeTransactionRef(coinbaseTx), nullptr /* pfMissingInputs */, nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */)); // Check that the transaction hasn't been added to mempool. BOOST_CHECK_EQUAL(mempool.size(), initialPoolSize); // Check that the validation state reflects the unsuccessful attempt. BOOST_CHECK(state.IsInvalid()); BOOST_CHECK_EQUAL(state.GetRejectReason(), "coinbase"); int nDoS; BOOST_CHECK_EQUAL(state.IsInvalid(nDoS), true); BOOST_CHECK_EQUAL(nDoS, 100); }
void MultisigDialog::commitMultisigTx() { CMutableTransaction tx(multisigTx); try{ #ifdef ENABLE_WALLET CWalletTx wtx(pwalletMain, tx); CReserveKey keyChange(pwalletMain); if (!pwalletMain->CommitTransaction(wtx, keyChange)) throw runtime_error(string("Transaction rejected - Failed to commit")); #else uint256 hashTx = tx.GetHash(); CCoinsViewCache& view = *pcoinsTip; const CCoins* existingCoins = view.AccessCoins(hashTx); bool fOverrideFees = false; bool fHaveMempool = mempool.exists(hashTx); bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000; if (!fHaveMempool && !fHaveChain) { // push to local node and sync with wallets CValidationState state; if (!AcceptToMemoryPool(mempool, state, tx, false, NULL, !fOverrideFees)) { if (state.IsInvalid()) throw runtime_error(strprintf("Transaction rejected - %i: %s", state.GetRejectCode(), state.GetRejectReason())); else throw runtime_error(string("Transaction rejected - ") + state.GetRejectReason()); } } else if (fHaveChain) { throw runtime_error("transaction already in block chain"); } RelayTransaction(tx); #endif //disable commit if successfully committed ui->commitButton->setEnabled(false); ui->signButtonStatus->setText(strprintf("Transaction has been successfully published with transaction ID:\n %s", tx.GetHash().GetHex()).c_str()); }catch(const runtime_error& e){ ui->signButtonStatus->setText(e.what()); } }
bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) { if (tx.nType != TRANSACTION_PROVIDER_UPDATE_SERVICE) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-type"); } CProUpServTx 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 (!CheckService(ptx.proTxHash, ptx, state)) { return false; } if (pindexPrev) { auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash()); auto mn = mnList.GetMN(ptx.proTxHash); if (!mn) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); } // don't allow updating to addresses already used by other MNs if (mnList.HasUniqueProperty(ptx.addr) && mnList.GetUniquePropertyMN(ptx.addr)->proTxHash != ptx.proTxHash) { return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-addr"); } if (ptx.scriptOperatorPayout != CScript()) { if (mn->nOperatorReward == 0) { // don't allow to set operator reward payee in case no operatorReward was set return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee"); } if (!ptx.scriptOperatorPayout.IsPayToPublicKeyHash() && !ptx.scriptOperatorPayout.IsPayToScriptHash()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee"); } } // we can only check the signature if pindexPrev != NULL and the MN is known if (!CheckInputsHash(tx, ptx, state)) { return false; } if (!CheckHashSig(ptx, mn->pdmnState->pubKeyOperator, state)) { return false; } } return true; }
static bool CheckService(const uint256& proTxHash, const ProTx& proTx, CValidationState& state) { if (!proTx.addr.IsValid()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr"); } if (Params().NetworkIDString() != CBaseChainParams::REGTEST && !proTx.addr.IsRoutable()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr"); } int mainnetDefaultPort = Params(CBaseChainParams::MAIN).GetDefaultPort(); if (Params().NetworkIDString() == CBaseChainParams::MAIN) { if (proTx.addr.GetPort() != mainnetDefaultPort) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr-port"); } } else if (proTx.addr.GetPort() == mainnetDefaultPort) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr-port"); } if (!proTx.addr.IsIPv4()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr"); } return true; }
ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<CTransaction>& vtx_missing) const { assert(!header.IsNull()); block = header; block.vtx.resize(txn_available.size()); size_t tx_missing_offset = 0; for (size_t i = 0; i < txn_available.size(); i++) { if (!txn_available[i]) { if (vtx_missing.size() <= tx_missing_offset) return READ_STATUS_INVALID; block.vtx[i] = vtx_missing[tx_missing_offset++]; } else block.vtx[i] = *txn_available[i]; } if (vtx_missing.size() != tx_missing_offset) return READ_STATUS_INVALID; CValidationState state; if (!CheckBlock(block, state, Params().GetConsensus())) { // TODO: We really want to just check merkle tree manually here, // but that is expensive, and CheckBlock caches a block's // "checked-status" (in the CBlock?). CBlock should be able to // check its own merkle root and cache that check. if (state.CorruptionPossible()) return READ_STATUS_FAILED; // Possible Short ID collision return READ_STATUS_INVALID; } LogPrint("cmpctblock", "Successfully reconstructed block %s with %lu txn prefilled, %lu txn from mempool and %lu txn requested\n", header.GetHash().ToString(), prefilled_count, mempool_count, vtx_missing.size()); if (vtx_missing.size() < 5) { for(const CTransaction& tx : vtx_missing) LogPrint("cmpctblock", "Reconstructed block %s required tx %s\n", header.GetHash().ToString(), tx.GetHash().ToString()); } return READ_STATUS_OK; }
bool CheckLLMQCommitment(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) { CFinalCommitmentTxPayload qcTx; if (!GetTxPayload(tx, qcTx)) { return state.DoS(100, false, REJECT_INVALID, "bad-qc-payload"); } if (qcTx.nVersion == 0 || qcTx.nVersion > CFinalCommitmentTxPayload::CURRENT_VERSION) { return state.DoS(100, false, REJECT_INVALID, "bad-qc-version"); } if (qcTx.nHeight != pindexPrev->nHeight + 1) { return state.DoS(100, false, REJECT_INVALID, "bad-qc-height"); } if (!mapBlockIndex.count(qcTx.commitment.quorumHash)) { return state.DoS(100, false, REJECT_INVALID, "bad-qc-quorum-hash"); } const CBlockIndex* pindexQuorum = mapBlockIndex[qcTx.commitment.quorumHash]; if (pindexQuorum != pindexPrev->GetAncestor(pindexQuorum->nHeight)) { // not part of active chain return state.DoS(100, false, REJECT_INVALID, "bad-qc-quorum-hash"); } if (!Params().GetConsensus().llmqs.count((Consensus::LLMQType)qcTx.commitment.llmqType)) { return state.DoS(100, false, REJECT_INVALID, "bad-qc-type"); } const auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)qcTx.commitment.llmqType); if (qcTx.commitment.IsNull()) { if (!qcTx.commitment.VerifyNull()) { return state.DoS(100, false, REJECT_INVALID, "bad-qc-invalid-null"); } return true; } auto members = CLLMQUtils::GetAllQuorumMembers(params.type, qcTx.commitment.quorumHash); if (!qcTx.commitment.Verify(members, false)) { return state.DoS(100, false, REJECT_INVALID, "bad-qc-invalid"); } return true; }
void CZMQNotificationInterface::BlockChecked(const CBlock& block, const CValidationState& state) { if (state.IsInvalid()) { return; } for (std::list<CZMQAbstractNotifier*>::iterator i = notifiers.begin(); i!=notifiers.end(); ) { CZMQAbstractNotifier *notifier = *i; if (notifier->NotifyBlock(block)) { i++; } else { notifier->Shutdown(); i = notifiers.erase(i); } } }
bool CMasternodeBroadcast::CheckInputsAndAdd(int& nDoS) { // we are a masternode with the same vin (i.e. already activated) and this mnb is ours (matches our Masternode privkey) // so nothing to do here for us if(fMasterNode && vin.prevout == activeMasternode.vin.prevout && pubkey2 == activeMasternode.pubKeyMasternode) return true; // search existing Masternode list CMasternode* pmn = mnodeman.Find(vin); if(pmn != NULL) { // nothing to do here if we already know about this masternode and it's enabled if(pmn->IsEnabled()) return true; // if it's not enabled, remove old MN first and continue else mnodeman.Remove(pmn->vin); } CValidationState state; CMutableTransaction tx = CMutableTransaction(); CTxOut vout = CTxOut(999.99*COIN, darkSendPool.collateralPubKey); tx.vin.push_back(vin); tx.vout.push_back(vout); { TRY_LOCK(cs_main, lockMain); if(!lockMain) { // not mnb fault, let it to be checked again later mnodeman.mapSeenMasternodeBroadcast.erase(GetHash()); masternodeSync.mapSeenSyncMNB.erase(GetHash()); return false; } if(!AcceptableInputs(mempool, state, CTransaction(tx), false, NULL)) { //set nDos state.IsInvalid(nDoS); return false; } } LogPrint("masternode", "mnb - Accepted Masternode entry\n"); if(GetInputAge(vin) < MASTERNODE_MIN_CONFIRMATIONS){ LogPrintf("mnb - Input must have at least %d confirmations\n", MASTERNODE_MIN_CONFIRMATIONS); // maybe we miss few blocks, let this mnb to be checked again later mnodeman.mapSeenMasternodeBroadcast.erase(GetHash()); masternodeSync.mapSeenSyncMNB.erase(GetHash()); return false; } // verify that sig time is legit in past // should be at least not earlier than block when 1000 DASH tx got MASTERNODE_MIN_CONFIRMATIONS uint256 hashBlock = 0; CTransaction tx2; GetTransaction(vin.prevout.hash, tx2, hashBlock, true); BlockMap::iterator mi = mapBlockIndex.find(hashBlock); if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex* pMNIndex = (*mi).second; // block for 1000 DASH tx -> 1 confirmation CBlockIndex* pConfIndex = chainActive[pMNIndex->nHeight + MASTERNODE_MIN_CONFIRMATIONS - 1]; // block where tx got MASTERNODE_MIN_CONFIRMATIONS if(pConfIndex->GetBlockTime() > sigTime) { LogPrintf("mnb - Bad sigTime %d for Masternode %20s %105s (%i conf block is at %d)\n", sigTime, addr.ToString(), vin.ToString(), MASTERNODE_MIN_CONFIRMATIONS, pConfIndex->GetBlockTime()); return false; } } LogPrintf("mnb - Got NEW Masternode entry - %s - %s - %s - %lli \n", GetHash().ToString(), addr.ToString(), vin.ToString(), sigTime); CMasternode mn(*this); mnodeman.Add(mn); // if it matches our Masternode privkey, then we've been remotely activated if(pubkey2 == activeMasternode.pubKeyMasternode && protocolVersion == PROTOCOL_VERSION){ activeMasternode.EnableHotColdMasterNode(vin, addr); } bool isLocal = addr.IsRFC1918() || addr.IsLocal(); if(Params().NetworkID() == CBaseChainParams::REGTEST) isLocal = false; if(!isLocal) Relay(); return true; }
WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &transaction) { QByteArray transaction_array; /* store serialized transaction */ { LOCK2(cs_main, wallet->cs_wallet); CWalletTx *newTx = transaction.getTransaction(); QList<SendCoinsRecipient> recipients = transaction.getRecipients(); Q_FOREACH(const SendCoinsRecipient &rcp, recipients) { if (rcp.paymentRequest.IsInitialized()) { // Make sure any payment requests involved are still valid. if (PaymentServer::verifyExpired(rcp.paymentRequest.getDetails())) { return PaymentRequestExpired; } // Store PaymentRequests in wtx.vOrderForm in wallet. std::string key("PaymentRequest"); std::string value; rcp.paymentRequest.SerializeToString(&value); newTx->vOrderForm.push_back(make_pair(key, value)); } else if (!rcp.message.isEmpty()) // Message from normal dash:URI (dash:XyZ...?message=example) { newTx->vOrderForm.push_back(make_pair("Message", rcp.message.toStdString())); } } CReserveKey *keyChange = transaction.getPossibleKeyChange(); CValidationState state; if(!wallet->CommitTransaction(*newTx, *keyChange, g_connman.get(), state, recipients[0].fUseInstantSend ? NetMsgType::TXLOCKREQUEST : NetMsgType::TX)) return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(state.GetRejectReason())); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << *newTx->tx; transaction_array.append(&(ssTx[0]), ssTx.size()); } // Add addresses / update labels that we've sent to to the address book, // and emit coinsSent signal for each recipient Q_FOREACH(const SendCoinsRecipient &rcp, transaction.getRecipients()) { // Don't touch the address book when we have a payment request if (!rcp.paymentRequest.IsInitialized()) { std::string strAddress = rcp.address.toStdString(); CTxDestination dest = CBitcoinAddress(strAddress).Get(); std::string strLabel = rcp.label.toStdString(); { LOCK(wallet->cs_wallet); std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest); // Check if we have a new address or an updated label if (mi == wallet->mapAddressBook.end()) { wallet->SetAddressBook(dest, strLabel, "send"); } else if (mi->second.name != strLabel) { wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose } } } Q_EMIT coinsSent(wallet, rcp, transaction_array); } checkBalanceChanged(); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits return SendCoinsReturn(OK); }
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; }
bool CMempoolComponent::LoadMempool(void) { const CChainParams &chainparams = Params(); int64_t nExpiryTimeout = Args().GetArg<uint32_t>("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60; FILE *filestr = fsbridge::fopen(Args().GetDataDir() / "mempool.dat", "rb"); CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); if (file.IsNull()) { return rLogError("Failed to open mempool file from disk. Continuing anyway"); } int64_t count = 0; int64_t skipped = 0; int64_t failed = 0; int64_t nNow = GetTime(); try { uint64_t version; file >> version; if (version != MEMPOOL_DUMP_VERSION) { return false; } uint64_t num; file >> num; while (num--) { CTransactionRef tx; int64_t nTime; int64_t nFeeDelta; file >> tx; file >> nTime; file >> nFeeDelta; CAmount amountdelta = nFeeDelta; if (amountdelta) { mempool.PrioritiseTransaction(tx->GetHash(), amountdelta); } CValidationState state; if (nTime + nExpiryTimeout > nNow) { LOCK(cs); mempool.AcceptToMemoryPoolWithTime(chainparams, state, tx, true, nullptr, nTime, nullptr, false, 0); if (state.IsValid()) { ++count; } else { ++failed; } } else { ++skipped; } if (GetApp()->ShutdownRequested()) return false; } std::map<uint256, CAmount> mapDeltas; file >> mapDeltas; for (const auto &i : mapDeltas) { mempool.PrioritiseTransaction(i.first, i.second); } } catch (const std::exception &e) { return rLogError("Failed to deserialize mempool data on disk: %s. Continuing anyway", e.what()); } NLogFormat("Imported mempool transactions from disk: %i successes, %i failed, %i expired", count, failed, skipped); return true; }
void ProcessMessageMasternode(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) { if (strCommand == "dsee") { //DarkSend Election Entry if(fLiteMode) return; //disable all darksend/masternode related functionality bool fIsInitialDownload = IsInitialBlockDownload(); if(fIsInitialDownload) return; CTxIn vin; CService addr; CPubKey pubkey; CPubKey pubkey2; vector<unsigned char> vchSig; int64_t sigTime; int count; int current; int64_t lastUpdated; int protocolVersion; std::string strMessage; // 70047 and greater vRecv >> vin >> addr >> vchSig >> sigTime >> pubkey >> pubkey2 >> count >> current >> lastUpdated >> protocolVersion; // make sure signature isn't in the future (past is OK) if (sigTime > GetAdjustedTime() + 60 * 60) { LogPrintf("dsee - Signature rejected, too far into the future %s\n", vin.ToString().c_str()); return; } bool isLocal = addr.IsRFC1918() || addr.IsLocal(); //if(Params().MineBlocksOnDemand()) isLocal = false; std::string vchPubKey(pubkey.begin(), pubkey.end()); std::string vchPubKey2(pubkey2.begin(), pubkey2.end()); strMessage = addr.ToString() + boost::lexical_cast<std::string>(sigTime) + vchPubKey + vchPubKey2 + boost::lexical_cast<std::string>(protocolVersion); if(protocolVersion < MIN_MN_PROTO_VERSION) { LogPrintf("dsee - ignoring outdated masternode %s protocol version %d\n", vin.ToString().c_str(), protocolVersion); return; } CScript pubkeyScript; pubkeyScript =GetScriptForDestination(pubkey.GetID()); if(pubkeyScript.size() != 25) { LogPrintf("dsee - pubkey the wrong size\n"); Misbehaving(pfrom->GetId(), 100); return; } CScript pubkeyScript2; pubkeyScript2 =GetScriptForDestination(pubkey2.GetID()); if(pubkeyScript2.size() != 25) { LogPrintf("dsee - pubkey2 the wrong size\n"); Misbehaving(pfrom->GetId(), 100); return; } std::string errorMessage = ""; if(!darkSendSigner.VerifyMessage(pubkey, vchSig, strMessage, errorMessage)){ LogPrintf("dsee - Got bad masternode address signature\n"); Misbehaving(pfrom->GetId(), 100); return; } //search existing masternode list, this is where we update existing masternodes with new dsee broadcasts LOCK(cs_masternodes); BOOST_FOREACH(CMasterNode& mn, vecMasternodes) { if(mn.vin.prevout == vin.prevout) { // count == -1 when it's a new entry // e.g. We don't want the entry relayed/time updated when we're syncing the list // mn.pubkey = pubkey, IsVinAssociatedWithPubkey is validated once below, // after that they just need to match if(count == -1 && mn.pubkey == pubkey && !mn.UpdatedWithin(MASTERNODE_MIN_DSEE_SECONDS)){ mn.UpdateLastSeen(); if(mn.now < sigTime){ //take the newest entry LogPrintf("dsee - Got updated entry for %s\n", addr.ToString().c_str()); mn.pubkey2 = pubkey2; mn.now = sigTime; mn.sig = vchSig; mn.protocolVersion = protocolVersion; mn.addr = addr; RelayDarkSendElectionEntry(vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion); } } return; } } // make sure the vout that was signed is related to the transaction that spawned the masternode // - this is expensive, so it's only done once per masternode if(!darkSendSigner.IsVinAssociatedWithPubkey(vin, pubkey)) { LogPrintf("dsee - Got mismatched pubkey and vin\n"); Misbehaving(pfrom->GetId(), 100); return; } if(fDebug) LogPrintf("dsee - Got NEW masternode entry %s\n", addr.ToString().c_str()); // make sure it's still unspent // - this is checked later by .check() in many places and by ThreadCheckDarkSendPool() CValidationState state; CTransaction tx = CTransaction(); CTxOut vout = CTxOut((GetMNCollateral(pindexBest->nHeight)-1)*COIN, darkSendPool.collateralPubKey); tx.vin.push_back(vin); tx.vout.push_back(vout); //if(AcceptableInputs(mempool, state, tx)){ bool* pfMissingInputs = false; if(AcceptableInputs(mempool, tx, false, pfMissingInputs)){ if(fDebug) LogPrintf("dsee - Accepted masternode entry %i %i\n", count, current); if(GetInputAge(vin) < MASTERNODE_MIN_CONFIRMATIONS){ LogPrintf("dsee - Input must have least %d confirmations\n", MASTERNODE_MIN_CONFIRMATIONS); Misbehaving(pfrom->GetId(), 20); return; } // use this as a peer addrman.Add(CAddress(addr), pfrom->addr, 2*60*60); // add our masternode CMasterNode mn(addr, vin, pubkey, vchSig, sigTime, pubkey2, protocolVersion); mn.UpdateLastSeen(lastUpdated); vecMasternodes.push_back(mn); // if it matches our masternodeprivkey, then we've been remotely activated if(pubkey2 == activeMasternode.pubKeyMasternode && protocolVersion == PROTOCOL_VERSION){ activeMasternode.EnableHotColdMasterNode(vin, addr); } if(count == -1 && !isLocal) RelayDarkSendElectionEntry(vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion); } else { LogPrintf("dsee - Rejected masternode entry %s\n", addr.ToString().c_str()); int nDoS = 0; if (state.IsInvalid(nDoS)) { LogPrintf("dsee - %s from %s %s was not accepted into the memory pool\n", tx.GetHash().ToString().c_str(), pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str()); if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); } } } else if (strCommand == "dseep") { //DarkSend Election Entry Ping
UniValue gobject(const JSONRPCRequest& request) { std::string strCommand; if (request.params.size() >= 1) strCommand = request.params[0].get_str(); if (request.fHelp || ( #ifdef ENABLE_WALLET strCommand != "prepare" && #endif // ENABLE_WALLET strCommand != "vote-many" && strCommand != "vote-conf" && strCommand != "vote-name" && strCommand != "submit" && strCommand != "count" && strCommand != "deserialize" && strCommand != "get" && strCommand != "getvotes" && strCommand != "getcurrentvotes" && strCommand != "list" && strCommand != "diff" && strCommand != "check" )) throw std::runtime_error( "gobject \"command\"...\n" "Manage governance objects\n" "\nAvailable commands:\n" " check - Validate governance object data (proposal only)\n" #ifdef ENABLE_WALLET " prepare - Prepare governance object by signing and creating tx\n" #endif // ENABLE_WALLET " submit - Submit governance object to network\n" " deserialize - Deserialize governance object from hex string to JSON\n" " count - Count governance objects and votes (additional param: 'json' or 'all', default: 'json')\n" " get - Get governance object by hash\n" " getvotes - Get all votes for a governance object hash (including old votes)\n" " getcurrentvotes - Get only current (tallying) votes for a governance object hash (does not include old votes)\n" " list - List governance objects (can be filtered by signal and/or object type)\n" " diff - List differences since last diff\n" " vote-name - Vote on a governance object by masternode name (using masternode.conf setup)\n" " vote-conf - Vote on a governance object by masternode configured in syscoin.conf\n" " vote-many - Vote on a governance object by all masternodes (using masternode.conf setup)\n" ); if(strCommand == "count") { std::string strMode{"json"}; if (request.params.size() == 2) { strMode = request.params[1].get_str(); } if (request.params.size() > 2 || (strMode != "json" && strMode != "all")) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'gobject count ( \"json\"|\"all\" )'"); } return strMode == "json" ? governance.ToJson() : governance.ToString(); } /* ------ Example Governance Item ------ gobject submit 6e622bb41bad1fb18e7f23ae96770aeb33129e18bd9efe790522488e580a0a03 0 1 1464292854 "beer-reimbursement" 5b5b22636f6e7472616374222c207b2270726f6a6563745f6e616d65223a20225c22626565722d7265696d62757273656d656e745c22222c20227061796d656e745f61646472657373223a20225c225879324c4b4a4a64655178657948726e34744744514238626a6876464564615576375c22222c2022656e645f64617465223a202231343936333030343030222c20226465736372697074696f6e5f75726c223a20225c227777772e646173687768616c652e6f72672f702f626565722d7265696d62757273656d656e745c22222c2022636f6e74726163745f75726c223a20225c22626565722d7265696d62757273656d656e742e636f6d2f3030312e7064665c22222c20227061796d656e745f616d6f756e74223a20223233342e323334323232222c2022676f7665726e616e63655f6f626a6563745f6964223a2037342c202273746172745f64617465223a202231343833323534303030227d5d5d1 */ // DEBUG : TEST DESERIALIZATION OF GOVERNANCE META DATA if(strCommand == "deserialize") { if (request.params.size() != 2) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'gobject deserialize <data-hex>'"); } std::string strHex = request.params[1].get_str(); std::vector<unsigned char> v = ParseHex(strHex); std::string s(v.begin(), v.end()); UniValue u(UniValue::VOBJ); u.read(s); return u.write().c_str(); } // VALIDATE A GOVERNANCE OBJECT PRIOR TO SUBMISSION if(strCommand == "check") { if (request.params.size() != 2) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'gobject check <data-hex>'"); } // ASSEMBLE NEW GOVERNANCE OBJECT FROM USER PARAMETERS uint256 hashParent; int nRevision = 1; int64_t nTime = GetAdjustedTime(); std::string strDataHex = request.params[1].get_str(); CGovernanceObject govobj(hashParent, nRevision, nTime, uint256(), strDataHex); if(govobj.GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL) { CProposalValidator validator(strDataHex); if(!validator.Validate()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid proposal data, error messages:" + validator.GetErrorMessages()); } } else { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid object type, only proposals can be validated"); } UniValue objResult(UniValue::VOBJ); objResult.push_back(Pair("Object status", "OK")); return objResult; } #ifdef ENABLE_WALLET // PREPARE THE GOVERNANCE OBJECT BY CREATING A COLLATERAL TRANSACTION if(strCommand == "prepare") { if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; if (request.params.size() != 5) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'gobject prepare <parent-hash> <revision> <time> <data-hex>'"); } // ASSEMBLE NEW GOVERNANCE OBJECT FROM USER PARAMETERS uint256 hashParent; // -- attach to root node (root node doesn't really exist, but has a hash of zero) if(request.params[1].get_str() == "0") { hashParent = uint256(); } else { hashParent = ParseHashV(request.params[1], "fee-txid, parameter 1"); } std::string strRevision = request.params[2].get_str(); std::string strTime = request.params[3].get_str(); int nRevision = atoi(strRevision); int64_t nTime = atoi64(strTime); std::string strDataHex = request.params[4].get_str(); // CREATE A NEW COLLATERAL TRANSACTION FOR THIS SPECIFIC OBJECT CGovernanceObject govobj(hashParent, nRevision, nTime, uint256(), strDataHex); if(govobj.GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL) { CProposalValidator validator(strDataHex); if(!validator.Validate()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid proposal data, error messages:" + validator.GetErrorMessages()); } } if(govobj.GetObjectType() == GOVERNANCE_OBJECT_TRIGGER) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Trigger objects need not be prepared (however only masternodes can create them)"); } if(govobj.GetObjectType() == GOVERNANCE_OBJECT_WATCHDOG) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Watchdogs are deprecated"); } { LOCK(cs_main); std::string strError = ""; if (!govobj.IsValidLocally(strError, false)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Governance object is not valid - " + govobj.GetHash().ToString() + " - " + strError); } EnsureWalletIsUnlocked(); CWalletTx wtx; if(!pwalletMain->GetBudgetSystemCollateralTX(wtx, govobj.GetHash(), govobj.GetMinCollateralFee(), false)) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Error making collateral transaction for governance object. Please check your wallet balance and make sure your wallet is unlocked."); } // -- make our change address CReserveKey reservekey(pwalletMain); // -- send the tx to the network CValidationState state; if (!pwalletMain->CommitTransaction(wtx, reservekey, g_connman.get(), state, NetMsgType::TX)) { throw JSONRPCError(RPC_INTERNAL_ERROR, "CommitTransaction failed! Reason given: " + state.GetRejectReason()); } DBG( std::cout << "gobject: prepare " << " GetDataAsPlainString = " << govobj.GetDataAsPlainString() << ", hash = " << govobj.GetHash().GetHex() << ", txidFee = " << wtx.GetHash().GetHex() << std::endl; ); return wtx.GetHash().ToString(); }
void ProcessMessageMasternode(CNode* pfrom, std::string& strCommand, CDataStream& vRecv) { if (strCommand == "dsee") { //DarkSend Election Entry if (pfrom->nVersion != darkSendPool.MIN_PEER_PROTO_VERSION) { return; } bool fIsInitialDownload = IsInitialBlockDownload(); if(fIsInitialDownload) return; CTxIn vin; CService addr; CPubKey pubkey; CPubKey pubkey2; vector<unsigned char> vchSig; int64 sigTime; int count; int current; int64 lastUpdated; vRecv >> vin >> addr >> vchSig >> sigTime >> pubkey >> pubkey2 >> count >> current >> lastUpdated; bool isLocal = false; // addr.IsRFC1918(); std::string vchPubKey(pubkey.begin(), pubkey.end()); std::string vchPubKey2(pubkey2.begin(), pubkey2.end()); CScript pubkeyScript; pubkeyScript.SetDestination(pubkey.GetID()); if(pubkeyScript.size() != 25) { LogPrintf("dsee - pubkey the wrong size\n"); pfrom->Misbehaving(100); return; } std::string strMessage = addr.ToString() + boost::lexical_cast<std::string>(sigTime) + vchPubKey + vchPubKey2; CScript pubkeyScript2; pubkeyScript2.SetDestination(pubkey2.GetID()); if(pubkeyScript2.size() != 25) { LogPrintf("dsee - pubkey the wrong size\n"); pfrom->Misbehaving(100); return; } std::string errorMessage = ""; if(!darkSendSigner.VerifyMessage(pubkey, vchSig, strMessage, errorMessage)){ LogPrintf("dsee - Got bad masternode address signature\n"); pfrom->Misbehaving(100); return; } if((fTestNet && addr.GetPort() != 19999) || (!fTestNet && addr.GetPort() != 9999)) return; //LogPrintf("Searching existing masternodes : %s - %s\n", addr.ToString().c_str(), vin.ToString().c_str()); BOOST_FOREACH(CMasterNode& mn, darkSendMasterNodes) { //LogPrintf(" -- %s\n", mn.vin.ToString().c_str()); if(mn.vin.prevout == vin.prevout) { //count == -1 when it's a new entry // e.g. We don't want the entry relayed/time updated when we're syncing the list if(count == -1 && !mn.UpdatedWithin(MASTERNODE_MIN_SECONDS)){ mn.UpdateLastSeen(); if(mn.now < sigTime){ //take the newest entry LogPrintf("dsee - Got updated entry for %s\n", addr.ToString().c_str()); mn.pubkey2 = pubkey2; mn.now = sigTime; mn.sig = vchSig; if(pubkey2 == activeMasternode.pubkeyMasterNode2){ activeMasternode.EnableHotColdMasterNode(vin, sigTime, addr); } RelayDarkSendElectionEntry(vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated); } } return; } } if(!darkSendSigner.IsVinAssociatedWithPubkey(vin, pubkey)) { LogPrintf("dsee - Got mismatched pubkey and vin\n"); pfrom->Misbehaving(100); return; } LogPrintf("dsee - Got NEW masternode entry %s\n", addr.ToString().c_str()); CValidationState state; CTransaction tx = CTransaction(); CTxOut vout = CTxOut(999.99*COIN, darkSendPool.collateralPubKey); tx.vin.push_back(vin); tx.vout.push_back(vout); if(tx.AcceptableInputs(state, true)){ LogPrintf("dsee - Accepted masternode entry %i %i\n", count, current); if(GetInputAge(vin) < MASTERNODE_MIN_CONFIRMATIONS){ LogPrintf("dsee - Input must have least %d confirmations\n", MASTERNODE_MIN_CONFIRMATIONS); pfrom->Misbehaving(20); return; } addrman.Add(CAddress(addr), pfrom->addr, 2*60*60); CMasterNode mn(addr, vin, pubkey, vchSig, sigTime, pubkey2); mn.UpdateLastSeen(lastUpdated); darkSendMasterNodes.push_back(mn); if(pubkey2 == activeMasternode.pubkeyMasterNode2){ activeMasternode.EnableHotColdMasterNode(vin, sigTime, addr); } if(count == -1 && !isLocal) RelayDarkSendElectionEntry(vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated); } else { LogPrintf("dsee - Rejected masternode entry\n"); int nDoS = 0; if (state.IsInvalid(nDoS)) { LogPrintf("dsee - %s from %s %s was not accepted into the memory pool\n", tx.GetHash().ToString().c_str(), pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str()); if (nDoS > 0) pfrom->Misbehaving(nDoS); } } } else if (strCommand == "dseep") { //DarkSend Election Entry Ping
bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) { if (tx.nType != TRANSACTION_PROVIDER_UPDATE_REGISTRAR) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-type"); } CProUpRegTx 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.nMode != 0) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-mode"); } if (!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"); } if (pindexPrev) { auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash()); auto dmn = mnList.GetMN(ptx.proTxHash); if (!dmn) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); } // don't allow reuse of payee key for other keys (don't allow people to put the payee key onto an online server) if (payoutDest == CTxDestination(dmn->pdmnState->keyIDOwner) || payoutDest == CTxDestination(ptx.keyIDVoting)) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-reuse"); } Coin coin; if (!GetUTXOCoin(dmn->collateralOutpoint, coin)) { // this should never happen (there would be no dmn otherwise) return state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral"); } // don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server) CTxDestination collateralTxDest; if (!ExtractDestination(coin.out.scriptPubKey, collateralTxDest)) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral-dest"); } if (collateralTxDest == CTxDestination(dmn->pdmnState->keyIDOwner) || collateralTxDest == CTxDestination(ptx.keyIDVoting)) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-reuse"); } if (mnList.HasUniqueProperty(ptx.pubKeyOperator)) { auto otherDmn = mnList.GetUniquePropertyMN(ptx.pubKeyOperator); if (ptx.proTxHash != otherDmn->proTxHash) { return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-key"); } } if (!deterministicMNManager->IsDIP3Enforced(pindexPrev->nHeight)) { if (dmn->pdmnState->keyIDOwner != ptx.keyIDVoting) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-not-same"); } } if (!CheckInputsHash(tx, ptx, state)) { return false; } if (!CheckHashSig(ptx, dmn->pdmnState->keyIDOwner, state)) { return false; } } return true; }