-
Notifications
You must be signed in to change notification settings - Fork 173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RPC: Scanforunspent #1547
RPC: Scanforunspent #1547
Changes from 3 commits
34aa666
012bdb6
6893423
5dd666c
44181b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -768,7 +768,7 @@ UniValue consolidateunspent(const UniValue& params, bool fHelp) | |
return result; | ||
} | ||
|
||
// MultiSig Tool | ||
// MultiSig Tools | ||
UniValue consolidatemsunspent(const UniValue& params, bool fHelp) | ||
{ | ||
if (fHelp || params.size() < 5) | ||
|
@@ -1080,6 +1080,249 @@ UniValue consolidatemsunspent(const UniValue& params, bool fHelp) | |
return result; | ||
} | ||
|
||
UniValue scanforunspent(const UniValue& params, bool fHelp) | ||
{ | ||
if (fHelp || params.size() < 3 || params.size() > 5 || params.size() == 4) | ||
throw runtime_error( | ||
"scanforunspent <address> <block-start> <block-end> [bool:export] [export-type]\n" | ||
"\n" | ||
"Searches a block range for a specified address with unspent utxos\n" | ||
"and displays them in a json response with the option of exporting\n" | ||
"to file\n" | ||
"\n" | ||
"Parameters required:\n" | ||
"<address> --------> Multi-signature address\n" | ||
"<block-start> ----> Block number to start search from\n" | ||
"<block-end> ------> Block number to end search on\n" | ||
"\n" | ||
"Optional:\n" | ||
"[export] ---------> Exports to a file in backup-dir/rpc in format of multisigaddress-datetime.type\n" | ||
"[type] -----------> Export to a file with file type (xml or txt -- Required if export true)"); | ||
|
||
// Parameters | ||
bool fExport = false; | ||
|
||
std::string sAddress = params[0].get_str(); | ||
int nBlockStart = params[1].get_int(); | ||
int nBlockEnd = params[2].get_int(); | ||
int nType = 0; | ||
|
||
if (params.size() > 3) | ||
{ | ||
fExport = params[3].get_bool(); | ||
|
||
if (params[4].get_str() == "xml") | ||
nType = 0; | ||
|
||
else if (params[4].get_str() == "txt") | ||
nType = 1; | ||
|
||
else | ||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid export type"); | ||
} | ||
|
||
// Parameter Sanity Check | ||
if (nBlockStart < 1 || nBlockStart > nBestHeight || nBlockStart > nBlockEnd) | ||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block-start"); | ||
|
||
if (nBlockEnd < 1 || nBlockEnd > nBestHeight || nBlockEnd <= nBlockStart) | ||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block-end"); | ||
|
||
CBitcoinAddress Address(sAddress); | ||
|
||
if (!Address.IsValid()) | ||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Gridcoin Address"); | ||
|
||
std::unordered_multimap<int64_t, std::pair<uint256, unsigned int>> uMultisig; | ||
|
||
LOCK(cs_main); | ||
{ | ||
|
||
BlockFinder blockfinder; | ||
|
||
CBlockIndex* pblkindex = blockfinder.FindByHeight((nBlockStart - 1)); | ||
|
||
if (!pblkindex) | ||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); | ||
|
||
while (pblkindex->nHeight < nBlockEnd) | ||
{ | ||
pblkindex = pblkindex->pnext; | ||
|
||
CBlock block; | ||
|
||
if (!block.ReadFromDisk(pblkindex, true)) | ||
throw JSONRPCError(RPC_PARSE_ERROR, "Unable to read block from disk!"); | ||
|
||
// No Transactions in block outside of block creation | ||
if (block.vtx.size() < 3) | ||
continue; | ||
|
||
for (unsigned int i = 2; i < block.vtx.size(); i++) | ||
{ | ||
// Load Transaction | ||
CTransaction tx; | ||
CTxDB txdb("r"); | ||
CTxIndex txindex; | ||
uint256 hash; | ||
|
||
hash = block.vtx[i].GetHash(); | ||
|
||
// Incase a fail here we can just continue thou it shouldn't happen | ||
if (!tx.ReadFromDisk(txdb, COutPoint(hash, 0), txindex)) | ||
continue; | ||
|
||
// Extract the address from the transaction | ||
for (unsigned int j = 0; j < tx.vout.size(); j++) | ||
{ | ||
const CTxOut& txout = tx.vout[j]; | ||
CTxDestination txaddress; | ||
|
||
// Pass failures here thou we shouldn't have any failures | ||
if (!ExtractDestination(txout.scriptPubKey, txaddress)) | ||
continue; | ||
|
||
// If we found a match to multisig address do our work | ||
if (CBitcoinAddress(txaddress) == Address) | ||
{ | ||
// Check if this output is alread spent | ||
COutPoint dummy = COutPoint(tx.GetHash(), j); | ||
|
||
// This is spent so move along | ||
if (!txindex.vSpent[dummy.n].IsNull()) | ||
continue; | ||
|
||
// Add to multimap | ||
uMultisig.insert(std::make_pair(txout.nValue, std::make_pair(tx.GetHash(), j))); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
UniValue result(UniValue::VARR); | ||
UniValue res(UniValue::VOBJ); | ||
UniValue txres(UniValue::VARR); | ||
|
||
res.pushKV("Block Start", nBlockStart); | ||
res.pushKV("Block End", nBlockEnd); | ||
// Check the end results | ||
if (uMultisig.empty()) | ||
res.pushKV("Result", "No utxos found in specified range"); | ||
|
||
else | ||
{ | ||
std::stringstream exportoutput; | ||
std::string spacing = " "; | ||
|
||
if (fExport) | ||
{ | ||
if (nType == 0) | ||
exportoutput << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<id>\n"; | ||
|
||
else | ||
exportoutput << "TXID / VOUT / Value\n"; | ||
} | ||
|
||
int nCount = 0; | ||
int64_t nValue = 0; | ||
|
||
// Process the map | ||
for (const auto& data : uMultisig) | ||
{ | ||
nCount++; | ||
|
||
nValue += data.first; | ||
|
||
UniValue txdata(UniValue::VOBJ); | ||
|
||
txdata.pushKV("txid", data.second.first.ToString()); | ||
txdata.pushKV("vout", (int)data.second.second); | ||
txdata.pushKV("value", ValueFromAmount(data.first)); | ||
|
||
txres.push_back(txdata); | ||
// Parse into type file here | ||
|
||
if (fExport) | ||
{ | ||
if (nType == 0) | ||
{ | ||
exportoutput << spacing << "<tx" << nCount << ">\n"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we want to use XML for a machine-readable format, we may want to move <tx>
<num>1</num>
...
</tx> ...or an attribute: <tx num="1">...</tx> ...or remove it altogether instead of appending it to the tag name. This prevents some XML parsers from logically grouping the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. originally i wanted by but can't have numbers in main part of tag. i'll play around with this idea There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. num="1" doesnt do anything really but cleaner then <tx#> and probably better supported for xml parsers |
||
exportoutput << spacing << spacing << "<txid>" << data.second.first.ToString() << "</txid>\n"; | ||
exportoutput << spacing << spacing << "<vout>" << data.second.second << "</vout>\n"; | ||
exportoutput << spacing << spacing << "<value>" << std::fixed << setprecision(8) << data.first / (double)COIN << "</value>\n"; | ||
exportoutput << spacing << "</tx" << nCount << ">\n"; | ||
} | ||
|
||
else | ||
exportoutput << data.second.first.ToString() << " / " << data.second.second << " / " << std::fixed << setprecision(8) << data.first / (double)COIN << "\n"; | ||
} | ||
} | ||
|
||
res.pushKV("Block Start", nBlockStart); | ||
res.pushKV("Block End", nBlockEnd); | ||
res.pushKV("Total UTXO Count", nCount); | ||
res.pushKV("Total Value", ValueFromAmount(nValue)); | ||
|
||
if (fExport) | ||
{ | ||
// Complete xml file if its xml | ||
if (nType == 0) | ||
exportoutput << "</id>\n"; | ||
|
||
std::ofstream dataout; | ||
|
||
// We will place this in wallet backups as a safer location then in main data directory | ||
boost::filesystem::path exportpath; | ||
|
||
time_t biTime; | ||
struct tm * blTime; | ||
time (&biTime); | ||
blTime = localtime(&biTime); | ||
char boTime[200]; | ||
strftime(boTime, sizeof(boTime), "%Y-%m-%dT%H-%M-%S", blTime); | ||
|
||
std::string exportfile = params[0].get_str() + "-" + std::string(boTime) + "." + params[4].get_str(); | ||
|
||
std::string backupdir = GetArg("-backupdir", ""); | ||
|
||
if (backupdir.empty()) | ||
exportpath = GetDataDir() / "walletbackups/rpc" / exportfile; | ||
|
||
else | ||
exportpath = backupdir + "/" + exportfile; | ||
|
||
boost::filesystem::create_directory(exportpath.parent_path()); | ||
|
||
dataout.open(exportpath.string().c_str()); | ||
|
||
if (!dataout) | ||
{ | ||
res.pushKV("Export failed", "Failed to open stream for export file"); | ||
|
||
fExport = false; | ||
} | ||
|
||
else | ||
{ | ||
const std::string& out = exportoutput.str(); | ||
|
||
dataout << out; | ||
|
||
dataout.close(); | ||
} | ||
|
||
} | ||
} | ||
|
||
if (!txres.empty()) | ||
result.push_back(txres); | ||
|
||
result.push_back(res); | ||
|
||
return result; | ||
} | ||
|
||
UniValue createrawtransaction(const UniValue& params, bool fHelp) | ||
{ | ||
if (fHelp || params.size() != 2) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we move the
LOCK
into the nested scope?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes i'll move in inward when i get back home