Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/rpcwallet.cpp
Show First 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | static void WalletTxToJSON(interfaces::Chain &chain, | ||||
int confirms = wtx.GetDepthInMainChain(locked_chain); | int confirms = wtx.GetDepthInMainChain(locked_chain); | ||||
entry.pushKV("confirmations", confirms); | entry.pushKV("confirmations", confirms); | ||||
if (wtx.IsCoinBase()) { | if (wtx.IsCoinBase()) { | ||||
entry.pushKV("generated", true); | entry.pushKV("generated", true); | ||||
} | } | ||||
if (confirms > 0) { | if (confirms > 0) { | ||||
entry.pushKV("blockhash", wtx.hashBlock.GetHex()); | entry.pushKV("blockhash", wtx.hashBlock.GetHex()); | ||||
entry.pushKV("blockindex", wtx.nIndex); | entry.pushKV("blockindex", wtx.nIndex); | ||||
entry.pushKV("blocktime", | int64_t block_time; | ||||
LookupBlockIndex(wtx.hashBlock)->GetBlockTime()); | bool found_block = | ||||
chain.findBlock(wtx.hashBlock, nullptr /* block */, &block_time); | |||||
assert(found_block); | |||||
entry.pushKV("blocktime", block_time); | |||||
} else { | } else { | ||||
entry.pushKV("trusted", wtx.IsTrusted(locked_chain)); | entry.pushKV("trusted", wtx.IsTrusted(locked_chain)); | ||||
} | } | ||||
uint256 hash = wtx.GetId(); | uint256 hash = wtx.GetId(); | ||||
entry.pushKV("txid", hash.GetHex()); | entry.pushKV("txid", hash.GetHex()); | ||||
UniValue conflicts(UniValue::VARR); | UniValue conflicts(UniValue::VARR); | ||||
for (const uint256 &conflict : wtx.GetConflicts()) { | for (const uint256 &conflict : wtx.GetConflicts()) { | ||||
conflicts.push_back(conflict.GetHex()); | conflicts.push_back(conflict.GetHex()); | ||||
▲ Show 20 Lines • Show All 1,792 Lines • ▼ Show 20 Lines | static UniValue listsinceblock(const Config &config, | ||||
// Make sure the results are valid at least up to the most recent block | // Make sure the results are valid at least up to the most recent block | ||||
// the user could have gotten from another RPC command prior to now | // the user could have gotten from another RPC command prior to now | ||||
pwallet->BlockUntilSyncedToCurrentChain(); | pwallet->BlockUntilSyncedToCurrentChain(); | ||||
auto locked_chain = pwallet->chain().lock(); | auto locked_chain = pwallet->chain().lock(); | ||||
LOCK(pwallet->cs_wallet); | LOCK(pwallet->cs_wallet); | ||||
// Block index of the specified block or the common ancestor, if the block | // Height of the specified block or the common ancestor, if the block | ||||
// provided was in a deactivated chain. | // provided was in a deactivated chain. | ||||
const CBlockIndex *pindex = nullptr; | Optional<int> height; | ||||
// Block index of the specified block, even if it's in a deactivated chain. | // Height of the specified block, even if it's in a deactivated chain. | ||||
const CBlockIndex *paltindex = nullptr; | Optional<int> altheight; | ||||
int target_confirms = 1; | int target_confirms = 1; | ||||
isminefilter filter = ISMINE_SPENDABLE; | isminefilter filter = ISMINE_SPENDABLE; | ||||
BlockHash blockId; | |||||
if (!request.params[0].isNull() && !request.params[0].get_str().empty()) { | if (!request.params[0].isNull() && !request.params[0].get_str().empty()) { | ||||
BlockHash blockId(ParseHashV(request.params[0], "blockhash")); | blockId = BlockHash(ParseHashV(request.params[0], "blockhash")); | ||||
height = locked_chain->findFork(blockId, &altheight); | |||||
paltindex = pindex = LookupBlockIndex(blockId); | if (!height) { | ||||
if (!pindex) { | |||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); | ||||
} | } | ||||
if (::ChainActive()[pindex->nHeight] != pindex) { | |||||
// the block being asked for is a part of a deactivated chain; | |||||
// we don't want to depend on its perceived height in the block | |||||
// chain, we want to instead use the last common ancestor | |||||
pindex = ::ChainActive().FindFork(pindex); | |||||
} | |||||
} | } | ||||
if (!request.params[1].isNull()) { | if (!request.params[1].isNull()) { | ||||
target_confirms = request.params[1].get_int(); | target_confirms = request.params[1].get_int(); | ||||
if (target_confirms < 1) { | if (target_confirms < 1) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); | ||||
} | } | ||||
} | } | ||||
if (!request.params[2].isNull() && request.params[2].get_bool()) { | if (!request.params[2].isNull() && request.params[2].get_bool()) { | ||||
filter = filter | ISMINE_WATCH_ONLY; | filter = filter | ISMINE_WATCH_ONLY; | ||||
} | } | ||||
bool include_removed = | bool include_removed = | ||||
(request.params[3].isNull() || request.params[3].get_bool()); | (request.params[3].isNull() || request.params[3].get_bool()); | ||||
int depth = pindex ? (1 + ::ChainActive().Height() - pindex->nHeight) : -1; | const Optional<int> tip_height = locked_chain->getHeight(); | ||||
int depth = tip_height && height ? (1 + *tip_height - *height) : -1; | |||||
UniValue transactions(UniValue::VARR); | UniValue transactions(UniValue::VARR); | ||||
for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) { | for (const std::pair<const TxId, CWalletTx> &pairWtx : pwallet->mapWallet) { | ||||
CWalletTx tx = pairWtx.second; | CWalletTx tx = pairWtx.second; | ||||
if (depth == -1 || tx.GetDepthInMainChain(*locked_chain) < depth) { | if (depth == -1 || tx.GetDepthInMainChain(*locked_chain) < depth) { | ||||
ListTransactions(*locked_chain, pwallet, tx, 0, true, transactions, | ListTransactions(*locked_chain, pwallet, tx, 0, true, transactions, | ||||
filter, nullptr /* filter_label */); | filter, nullptr /* filter_label */); | ||||
} | } | ||||
} | } | ||||
const Consensus::Params ¶ms = config.GetChainParams().GetConsensus(); | |||||
// when a reorg'd block is requested, we also list any relevant transactions | // when a reorg'd block is requested, we also list any relevant transactions | ||||
// in the blocks of the chain that was detached | // in the blocks of the chain that was detached | ||||
UniValue removed(UniValue::VARR); | UniValue removed(UniValue::VARR); | ||||
while (include_removed && paltindex && paltindex != pindex) { | while (include_removed && altheight && *altheight > *height) { | ||||
CBlock block; | CBlock block; | ||||
if (!ReadBlockFromDisk(block, paltindex, params)) { | if (!pwallet->chain().findBlock(blockId, &block) || block.IsNull()) { | ||||
throw JSONRPCError(RPC_INTERNAL_ERROR, | throw JSONRPCError(RPC_INTERNAL_ERROR, | ||||
"Can't read block from disk"); | "Can't read block from disk"); | ||||
} | } | ||||
for (const CTransactionRef &tx : block.vtx) { | for (const CTransactionRef &tx : block.vtx) { | ||||
auto it = pwallet->mapWallet.find(tx->GetId()); | auto it = pwallet->mapWallet.find(tx->GetId()); | ||||
if (it != pwallet->mapWallet.end()) { | if (it != pwallet->mapWallet.end()) { | ||||
// We want all transactions regardless of confirmation count to | // We want all transactions regardless of confirmation count to | ||||
// appear here, even negative confirmation ones, hence the big | // appear here, even negative confirmation ones, hence the big | ||||
// negative. | // negative. | ||||
ListTransactions(*locked_chain, pwallet, it->second, -100000000, | ListTransactions(*locked_chain, pwallet, it->second, -100000000, | ||||
true, removed, filter, | true, removed, filter, | ||||
nullptr /* filter_label */); | nullptr /* filter_label */); | ||||
} | } | ||||
} | } | ||||
paltindex = paltindex->pprev; | blockId = block.hashPrevBlock; | ||||
--*altheight; | |||||
} | } | ||||
CBlockIndex *pblockLast = | int last_height = tip_height ? *tip_height + 1 - target_confirms : -1; | ||||
::ChainActive()[::ChainActive().Height() + 1 - target_confirms]; | BlockHash lastblock = last_height >= 0 | ||||
uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256(); | ? locked_chain->getBlockHash(last_height) | ||||
: BlockHash(); | |||||
UniValue ret(UniValue::VOBJ); | UniValue ret(UniValue::VOBJ); | ||||
ret.pushKV("transactions", transactions); | ret.pushKV("transactions", transactions); | ||||
if (include_removed) { | if (include_removed) { | ||||
ret.pushKV("removed", removed); | ret.pushKV("removed", removed); | ||||
} | } | ||||
ret.pushKV("lastblock", lastblock.GetHex()); | ret.pushKV("lastblock", lastblock.GetHex()); | ||||
▲ Show 20 Lines • Show All 2,002 Lines • ▼ Show 20 Lines | UniValue rescanblockchain(const Config &config, const JSONRPCRequest &request) { | ||||
WalletRescanReserver reserver(pwallet); | WalletRescanReserver reserver(pwallet); | ||||
if (!reserver.reserve()) { | if (!reserver.reserve()) { | ||||
throw JSONRPCError( | throw JSONRPCError( | ||||
RPC_WALLET_ERROR, | RPC_WALLET_ERROR, | ||||
"Wallet is currently rescanning. Abort existing rescan or wait."); | "Wallet is currently rescanning. Abort existing rescan or wait."); | ||||
} | } | ||||
CBlockIndex *pindexStart = nullptr; | int start_height = 0; | ||||
CBlockIndex *pindexStop = nullptr; | BlockHash start_block, stop_block; | ||||
CBlockIndex *pChainTip = nullptr; | |||||
{ | { | ||||
auto locked_chain = pwallet->chain().lock(); | auto locked_chain = pwallet->chain().lock(); | ||||
pindexStart = ::ChainActive().Genesis(); | Optional<int> tip_height = locked_chain->getHeight(); | ||||
pChainTip = ::ChainActive().Tip(); | |||||
if (!request.params[0].isNull()) { | if (!request.params[0].isNull()) { | ||||
pindexStart = ::ChainActive()[request.params[0].get_int()]; | start_height = request.params[0].get_int(); | ||||
if (!pindexStart) { | if (start_height < 0 || !tip_height || start_height > *tip_height) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
"Invalid start_height"); | "Invalid start_height"); | ||||
} | } | ||||
} | } | ||||
Optional<int> stop_height; | |||||
if (!request.params[1].isNull()) { | if (!request.params[1].isNull()) { | ||||
pindexStop = ::ChainActive()[request.params[1].get_int()]; | stop_height = request.params[1].get_int(); | ||||
if (!pindexStop) { | if (*stop_height < 0 || !tip_height || *stop_height > *tip_height) { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, | throw JSONRPCError(RPC_INVALID_PARAMETER, | ||||
"Invalid stop_height"); | "Invalid stop_height"); | ||||
} else if (pindexStop->nHeight < pindexStart->nHeight) { | } else if (*stop_height < start_height) { | ||||
throw JSONRPCError( | throw JSONRPCError( | ||||
RPC_INVALID_PARAMETER, | RPC_INVALID_PARAMETER, | ||||
"stop_height must be greater than start_height"); | "stop_height must be greater than start_height"); | ||||
} | } | ||||
} | } | ||||
} | |||||
// We can't rescan beyond non-pruned blocks, stop and throw an error | // We can't rescan beyond non-pruned blocks, stop and throw an error | ||||
if (fPruneMode) { | if (locked_chain->findPruned(start_height, stop_height)) { | ||||
auto locked_chain = pwallet->chain().lock(); | throw JSONRPCError( | ||||
CBlockIndex *block = pindexStop ? pindexStop : pChainTip; | RPC_MISC_ERROR, | ||||
while (block && block->nHeight >= pindexStart->nHeight) { | "Can't rescan beyond pruned data. Use RPC call " | ||||
if (!block->nStatus.hasData()) { | "getblockchaininfo to determine your pruned height."); | ||||
throw JSONRPCError(RPC_MISC_ERROR, | } | ||||
"Can't rescan beyond pruned data. Use RPC " | |||||
"call getblockchaininfo to determine your " | if (tip_height) { | ||||
"pruned height."); | start_block = locked_chain->getBlockHash(start_height); | ||||
if (stop_height) { | |||||
stop_block = locked_chain->getBlockHash(*stop_height); | |||||
} | } | ||||
block = block->pprev; | |||||
} | } | ||||
} | } | ||||
const CBlockIndex *failed_block, *stopBlock; | |||||
CWallet::ScanResult result = pwallet->ScanForWalletTransactions( | CWallet::ScanResult result = pwallet->ScanForWalletTransactions( | ||||
pindexStart, pindexStop, reserver, failed_block, stopBlock, true); | start_block, stop_block, reserver, true /* fUpdate */); | ||||
switch (result) { | switch (result.status) { | ||||
case CWallet::ScanResult::SUCCESS: | case CWallet::ScanResult::SUCCESS: | ||||
// stopBlock set by ScanForWalletTransactions | |||||
break; | break; | ||||
case CWallet::ScanResult::FAILURE: | case CWallet::ScanResult::FAILURE: | ||||
throw JSONRPCError( | throw JSONRPCError( | ||||
RPC_MISC_ERROR, | RPC_MISC_ERROR, | ||||
"Rescan failed. Potentially corrupted data files."); | "Rescan failed. Potentially corrupted data files."); | ||||
case CWallet::ScanResult::USER_ABORT: | case CWallet::ScanResult::USER_ABORT: | ||||
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted."); | throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted."); | ||||
// no default case, so the compiler can warn about missing cases | // no default case, so the compiler can warn about missing cases | ||||
} | } | ||||
UniValue response(UniValue::VOBJ); | UniValue response(UniValue::VOBJ); | ||||
response.pushKV("start_height", pindexStart->nHeight); | response.pushKV("start_height", start_height); | ||||
response.pushKV("stop_height", stopBlock->nHeight); | response.pushKV("stop_height", | ||||
result.stop_height ? *result.stop_height : UniValue()); | |||||
return response; | return response; | ||||
} | } | ||||
class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue> { | class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue> { | ||||
public: | public: | ||||
CWallet *const pwallet; | CWallet *const pwallet; | ||||
void ProcessSubScript(const CScript &subscript, UniValue &obj, | void ProcessSubScript(const CScript &subscript, UniValue &obj, | ||||
▲ Show 20 Lines • Show All 876 Lines • Show Last 20 Lines |