Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "chainparams.h"
#include "checkpoints.h"
#include "coins.h"
#include "core_io.h"
#include "consensus/validation.h"
#include "instantx.h"
#include "validation.h"
Expand Down Expand Up @@ -1620,6 +1621,113 @@ UniValue reconsiderblock(const JSONRPCRequest& request)
return NullUniValue;
}

UniValue getspecialtxes(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 5)
throw std::runtime_error(
"getspecialtxes \"blockhash\" ( type count skip verbosity ) \n"
"Returns an array of special transactions found in the specified block\n"
"\nIf verbosity is 0, returns tx hash for each transaction.\n"
"If verbosity is 1, returns hex-encoded data for each transaction.\n"
"If verbosity is 2, returns an Object with information for each transaction.\n"
"\nArguments:\n"
"1. \"blockhash\" (string, required) The block hash\n"
"2. type (numeric, optional, default=-1) Filter special txes by type, -1 means all types\n"
"3. count (numeric, optional, default=10) The number of transactions to return\n"
"4. skip (numeric, optional, default=0) The number of transactions to skip\n"
"5. verbosity (numeric, optional, default=0) 0 for hashes, 1 for hex-encoded data, and 2 for json object\n"
"\nResult (for verbosity = 0):\n"
"[\n"
" \"txid\" : \"xxxx\", (string) The transaction id\n"
"]\n"
"\nResult (for verbosity = 1):\n"
"[\n"
" \"data\", (string) A string that is serialized, hex-encoded data for the transaction\n"
"]\n"
"\nResult (for verbosity = 2):\n"
"[ (array of Objects) The transactions in the format of the getrawtransaction RPC.\n"
" ...,\n"
"]\n"
"\nExamples:\n"
+ HelpExampleCli("getspecialtxes", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"")
+ HelpExampleRpc("getspecialtxes", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"")
);

LOCK(cs_main);

std::string strHash = request.params[0].get_str();
uint256 hash(uint256S(strHash));

int nTxType = -1;
if (request.params.size() > 1) {
nTxType = request.params[1].get_int();
}

int nCount = 10;
if (request.params.size() > 2) {
nCount = request.params[2].get_int();
if (nCount < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
}

int nSkip = 0;
if (request.params.size() > 3) {
nSkip = request.params[3].get_int();
if (nSkip < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative skip");
}

int nVerbosity = 0;
if (request.params.size() > 4) {
nVerbosity = request.params[4].get_int();
if (nVerbosity < 0 || nVerbosity > 2) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbosity must be in range 0..2");
}
}

if (mapBlockIndex.count(hash) == 0)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");

CBlock block;
CBlockIndex* pblockindex = mapBlockIndex[hash];

if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0)
throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)");

if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()))
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");

int nTxNum = 0;
UniValue result(UniValue::VARR);
for(const auto& tx : block.vtx)
{
if (tx->nVersion != 3 || tx->nType == TRANSACTION_NORMAL // ensure it's in fact a special tx
|| (nTxType != -1 && tx->nType != nTxType)) { // ensure special tx type matches filter, if given
continue;
}

nTxNum++;
if (nTxNum <= nSkip) continue;
if (nTxNum > nSkip + nCount) break;

switch (nVerbosity)
{
case 0 : result.push_back(tx->GetHash().GetHex()); break;
case 1 : result.push_back(EncodeHexTx(*tx)); break;
case 2 :
{
UniValue objTx(UniValue::VOBJ);
TxToJSON(*tx, uint256(), objTx);
result.push_back(objTx);
break;
}
default : throw JSONRPCError(RPC_INTERNAL_ERROR, "Unsupported verbosity");
}
}

return result;
}

static const CRPCCommand commands[] =
{ // category name actor (function) okSafe argNames
// --------------------- ------------------------ ----------------------- ------ ----------
Expand All @@ -1638,6 +1746,7 @@ static const CRPCCommand commands[] =
{ "blockchain", "getmempoolentry", &getmempoolentry, true, {"txid"} },
{ "blockchain", "getmempoolinfo", &getmempoolinfo, true, {} },
{ "blockchain", "getrawmempool", &getrawmempool, true, {"verbose"} },
{ "blockchain", "getspecialtxes", &getspecialtxes, true, {"blockhash", "type", "count", "skip", "verbosity"} },
{ "blockchain", "gettxout", &gettxout, true, {"txid","n","include_mempool"} },
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true, {} },
{ "blockchain", "pruneblockchain", &pruneblockchain, true, {"height"} },
Expand Down
4 changes: 4 additions & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getaddressdeltas", 0, "addresses" },
{ "getaddressutxos", 0, "addresses" },
{ "getaddressmempool", 0, "addresses" },
{ "getspecialtxes", 1, "type" },
{ "getspecialtxes", 2, "count" },
{ "getspecialtxes", 3, "skip" },
{ "getspecialtxes", 4, "verbosity" },
// Echo with conversion (For testing only)
{ "echojson", 0, "arg0" },
{ "echojson", 1, "arg1" },
Expand Down
48 changes: 25 additions & 23 deletions src/txmempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,12 +441,13 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
vTxHashes.emplace_back(hash, newit);
newit->vTxHashesIdx = vTxHashes.size() - 1;

// Invalid ProTxes should never get this far because transactions should be
// fully checked by AcceptToMemoryPool() at this point, so we just assume that
// everything is fine here.
if (tx.nType == TRANSACTION_PROVIDER_REGISTER) {
CProRegTx proTx;
if (!GetTxPayload(tx, proTx)) {
LogPrintf("%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString());
return false;
}
bool ok = GetTxPayload(tx, proTx);
assert(ok);
if (!proTx.collateralOutpoint.hash.IsNull()) {
mapProTxRefs.emplace(tx.GetHash(), proTx.collateralOutpoint.hash);
}
Expand All @@ -458,36 +459,29 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
}
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
CProUpServTx proTx;
if (!GetTxPayload(tx, proTx)) {
LogPrintf("%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString());
return false;
}
bool ok = GetTxPayload(tx, proTx);
assert(ok);
mapProTxRefs.emplace(proTx.proTxHash, tx.GetHash());
mapProTxAddresses.emplace(proTx.addr, tx.GetHash());
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
CProUpRegTx proTx;
if (!GetTxPayload(tx, proTx)) {
LogPrintf("%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString());
return false;
}
bool ok = GetTxPayload(tx, proTx);
assert(ok);
mapProTxRefs.emplace(proTx.proTxHash, tx.GetHash());
mapProTxBlsPubKeyHashes.emplace(proTx.pubKeyOperator.GetHash(), tx.GetHash());

auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx.proTxHash);
assert(dmn); // we should never get such a ProTx into the mempool
assert(dmn);
newit->validForProTxKey = ::SerializeHash(dmn->pdmnState->pubKeyOperator);
if (dmn->pdmnState->pubKeyOperator != proTx.pubKeyOperator) {
newit->isKeyChangeProTx = true;
}
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) {
CProUpRevTx proTx;
if (!GetTxPayload(tx, proTx)) {
LogPrintf("%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString());
return false;
}
bool ok = GetTxPayload(tx, proTx);
assert(ok);
mapProTxRefs.emplace(proTx.proTxHash, tx.GetHash());
auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx.proTxHash);
assert(dmn); // we should never get such a ProTx into the mempool
assert(dmn);
newit->validForProTxKey = ::SerializeHash(dmn->pdmnState->pubKeyOperator);
if (dmn->pdmnState->pubKeyOperator != CBLSPublicKey()) {
newit->isKeyChangeProTx = true;
Expand Down Expand Up @@ -1306,9 +1300,13 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const {
return true; // i.e. can't decode payload == conflict
}

// only allow one operator key change in the mempool
// this method should only be called with validated ProTxs
auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx.proTxHash);
assert(dmn); // this method should only be called with validated ProTxs
if (!dmn) {
LogPrintf("%s: ERROR: Masternode is not in the list, proTxHash: %s", __func__, proTx.proTxHash.ToString());
return true; // i.e. failed to find validated ProTx == conflict
}
// only allow one operator key change in the mempool
if (dmn->pdmnState->pubKeyOperator != proTx.pubKeyOperator) {
if (hasKeyChangeInMempool(proTx.proTxHash)) {
return true;
Expand All @@ -1324,9 +1322,13 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const {
return true; // i.e. can't decode payload == conflict
}

// only allow one operator key change in the mempool
// this method should only be called with validated ProTxs
auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx.proTxHash);
assert(dmn); // this method should only be called with validated ProTxs
if (!dmn) {
LogPrintf("%s: ERROR: Masternode is not in the list, proTxHash: %s", __func__, proTx.proTxHash.ToString());
return true; // i.e. failed to find validated ProTx == conflict
}
// only allow one operator key change in the mempool
if (dmn->pdmnState->pubKeyOperator != CBLSPublicKey()) {
if (hasKeyChangeInMempool(proTx.proTxHash)) {
return true;
Expand Down
8 changes: 4 additions & 4 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -665,10 +665,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
if (fRequireStandard && !IsStandardTx(tx, reason))
return state.DoS(0, false, REJECT_NONSTANDARD, reason);

if (pool.existsProviderTxConflict(tx)) {
return state.DoS(0, false, REJECT_DUPLICATE, "protx-dup");
}

// Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
Expand Down Expand Up @@ -869,6 +865,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
if (!CheckSpecialTx(tx, chainActive.Tip(), state))
return false;

if (pool.existsProviderTxConflict(tx)) {
return state.DoS(0, false, REJECT_DUPLICATE, "protx-dup");
}

// If we aren't going to actually accept it but just were verifying it, we are fine already
if(fDryRun) return true;

Expand Down