Merge pull request #1672 from gmaxwell/filter_listunspent
Listunspent txout address filtering and listaddressgroupings
This commit is contained in:
commit
c68c4bc7a4
@ -230,6 +230,7 @@ static const CRPCCommand vRPCCommands[] =
|
||||
{ "getblockhash", &getblockhash, false, false },
|
||||
{ "gettransaction", &gettransaction, false, false },
|
||||
{ "listtransactions", &listtransactions, false, false },
|
||||
{ "listaddressgroupings", &listaddressgroupings, false, false },
|
||||
{ "signmessage", &signmessage, false, false },
|
||||
{ "verifymessage", &verifymessage, false, false },
|
||||
{ "getwork", &getwork, true, false },
|
||||
@ -1138,6 +1139,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
|
||||
if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]);
|
||||
if (strMethod == "listunspent" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "listunspent" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "listunspent" && n > 2) ConvertTo<Array>(params[2]);
|
||||
if (strMethod == "getrawtransaction" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "createrawtransaction" && n > 0) ConvertTo<Array>(params[0]);
|
||||
if (strMethod == "createrawtransaction" && n > 1) ConvertTo<Object>(params[1]);
|
||||
|
@ -114,6 +114,7 @@ extern json_spirit::Value addmultisigaddress(const json_spirit::Array& params, b
|
||||
extern json_spirit::Value listreceivedbyaddress(const json_spirit::Array& params, bool fHelp);
|
||||
extern json_spirit::Value listreceivedbyaccount(const json_spirit::Array& params, bool fHelp);
|
||||
extern json_spirit::Value listtransactions(const json_spirit::Array& params, bool fHelp);
|
||||
extern json_spirit::Value listaddressgroupings(const json_spirit::Array& params, bool fHelp);
|
||||
extern json_spirit::Value listaccounts(const json_spirit::Array& params, bool fHelp);
|
||||
extern json_spirit::Value listsinceblock(const json_spirit::Array& params, bool fHelp);
|
||||
extern json_spirit::Value gettransaction(const json_spirit::Array& params, bool fHelp);
|
||||
|
@ -136,24 +136,40 @@ Value getrawtransaction(const Array& params, bool fHelp)
|
||||
|
||||
Value listunspent(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() > 2)
|
||||
if (fHelp || params.size() > 3)
|
||||
throw runtime_error(
|
||||
"listunspent [minconf=1] [maxconf=999999]\n"
|
||||
"listunspent [minconf=1] [maxconf=9999999] [\"address\",...]\n"
|
||||
"Returns array of unspent transaction outputs\n"
|
||||
"with between minconf and maxconf (inclusive) confirmations.\n"
|
||||
"Optionally filtered to only include txouts paid to specified addresses.\n"
|
||||
"Results are an array of Objects, each of which has:\n"
|
||||
"{txid, vout, scriptPubKey, amount, confirmations}");
|
||||
|
||||
RPCTypeCheck(params, list_of(int_type)(int_type));
|
||||
RPCTypeCheck(params, list_of(int_type)(int_type)(array_type));
|
||||
|
||||
int nMinDepth = 1;
|
||||
if (params.size() > 0)
|
||||
nMinDepth = params[0].get_int();
|
||||
|
||||
int nMaxDepth = 999999;
|
||||
int nMaxDepth = 9999999;
|
||||
if (params.size() > 1)
|
||||
nMaxDepth = params[1].get_int();
|
||||
|
||||
set<CBitcoinAddress> setAddress;
|
||||
if (params.size() > 2)
|
||||
{
|
||||
Array inputs = params[2].get_array();
|
||||
BOOST_FOREACH(Value& input, inputs)
|
||||
{
|
||||
CBitcoinAddress address(input.get_str());
|
||||
if (!address.IsValid())
|
||||
throw JSONRPCError(-5, string("Invalid Bitcoin address: ")+input.get_str());
|
||||
if (setAddress.count(address))
|
||||
throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+input.get_str());
|
||||
setAddress.insert(address);
|
||||
}
|
||||
}
|
||||
|
||||
Array results;
|
||||
vector<COutput> vecOutputs;
|
||||
pwalletMain->AvailableCoins(vecOutputs, false);
|
||||
@ -162,6 +178,16 @@ Value listunspent(const Array& params, bool fHelp)
|
||||
if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
|
||||
continue;
|
||||
|
||||
if(setAddress.size())
|
||||
{
|
||||
CTxDestination address;
|
||||
if(!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
|
||||
continue;
|
||||
|
||||
if (!setAddress.count(address))
|
||||
continue;
|
||||
}
|
||||
|
||||
int64 nValue = out.tx->vout[out.i].nValue;
|
||||
const CScript& pk = out.tx->vout[out.i].scriptPubKey;
|
||||
Object entry;
|
||||
@ -222,7 +248,7 @@ Value createrawtransaction(const Array& params, bool fHelp)
|
||||
{
|
||||
CBitcoinAddress address(s.name_);
|
||||
if (!address.IsValid())
|
||||
throw JSONRPCError(-5, string("Invalid Bitcoin address:")+s.name_);
|
||||
throw JSONRPCError(-5, string("Invalid Bitcoin address: ")+s.name_);
|
||||
|
||||
if (setAddress.count(address))
|
||||
throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_);
|
||||
|
@ -274,6 +274,37 @@ Value sendtoaddress(const Array& params, bool fHelp)
|
||||
return wtx.GetHash().GetHex();
|
||||
}
|
||||
|
||||
Value listaddressgroupings(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp)
|
||||
throw runtime_error(
|
||||
"listaddressgroupings\n"
|
||||
"Lists groups of addresses which have had their common ownership\n"
|
||||
"made public by common use as inputs or as the resulting change\n"
|
||||
"in past transactions");
|
||||
|
||||
Array jsonGroupings;
|
||||
map<CTxDestination, int64> balances = pwalletMain->GetAddressBalances();
|
||||
BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings())
|
||||
{
|
||||
Array jsonGrouping;
|
||||
BOOST_FOREACH(CTxDestination address, grouping)
|
||||
{
|
||||
Array addressInfo;
|
||||
addressInfo.push_back(CBitcoinAddress(address).ToString());
|
||||
addressInfo.push_back(ValueFromAmount(balances[address]));
|
||||
{
|
||||
LOCK(pwalletMain->cs_wallet);
|
||||
if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
|
||||
addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second);
|
||||
}
|
||||
jsonGrouping.push_back(addressInfo);
|
||||
}
|
||||
jsonGroupings.push_back(jsonGrouping);
|
||||
}
|
||||
return jsonGroupings;
|
||||
}
|
||||
|
||||
Value signmessage(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 2)
|
||||
|
123
src/wallet.cpp
123
src/wallet.cpp
@ -1608,6 +1608,129 @@ int64 CWallet::GetOldestKeyPoolTime()
|
||||
return keypool.nTime;
|
||||
}
|
||||
|
||||
std::map<CTxDestination, int64> CWallet::GetAddressBalances()
|
||||
{
|
||||
map<CTxDestination, int64> balances;
|
||||
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet)
|
||||
{
|
||||
CWalletTx *pcoin = &walletEntry.second;
|
||||
|
||||
if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
|
||||
continue;
|
||||
|
||||
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
|
||||
continue;
|
||||
|
||||
int nDepth = pcoin->GetDepthInMainChain();
|
||||
if (nDepth < (pcoin->IsFromMe() ? 0 : 1))
|
||||
continue;
|
||||
|
||||
for (unsigned int i = 0; i < pcoin->vout.size(); i++)
|
||||
{
|
||||
CTxDestination addr;
|
||||
if (!IsMine(pcoin->vout[i]))
|
||||
continue;
|
||||
if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr))
|
||||
continue;
|
||||
|
||||
int64 n = pcoin->IsSpent(i) ? 0 : pcoin->vout[i].nValue;
|
||||
|
||||
if (!balances.count(addr))
|
||||
balances[addr] = 0;
|
||||
balances[addr] += n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return balances;
|
||||
}
|
||||
|
||||
set< set<CTxDestination> > CWallet::GetAddressGroupings()
|
||||
{
|
||||
set< set<CTxDestination> > groupings;
|
||||
set<CTxDestination> grouping;
|
||||
|
||||
BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet)
|
||||
{
|
||||
CWalletTx *pcoin = &walletEntry.second;
|
||||
|
||||
if (pcoin->vin.size() > 0 && IsMine(pcoin->vin[0]))
|
||||
{
|
||||
// group all input addresses with each other
|
||||
BOOST_FOREACH(CTxIn txin, pcoin->vin)
|
||||
{
|
||||
CTxDestination address;
|
||||
if(!ExtractDestination(mapWallet[txin.prevout.hash].vout[txin.prevout.n].scriptPubKey, address))
|
||||
continue;
|
||||
grouping.insert(address);
|
||||
}
|
||||
|
||||
// group change with input addresses
|
||||
BOOST_FOREACH(CTxOut txout, pcoin->vout)
|
||||
if (IsChange(txout))
|
||||
{
|
||||
CWalletTx tx = mapWallet[pcoin->vin[0].prevout.hash];
|
||||
CTxDestination txoutAddr;
|
||||
if(!ExtractDestination(txout.scriptPubKey, txoutAddr))
|
||||
continue;
|
||||
grouping.insert(txoutAddr);
|
||||
}
|
||||
groupings.insert(grouping);
|
||||
grouping.clear();
|
||||
}
|
||||
|
||||
// group lone addrs by themselves
|
||||
for (unsigned int i = 0; i < pcoin->vout.size(); i++)
|
||||
if (IsMine(pcoin->vout[i]))
|
||||
{
|
||||
CTxDestination address;
|
||||
if(!ExtractDestination(pcoin->vout[i].scriptPubKey, address))
|
||||
continue;
|
||||
grouping.insert(address);
|
||||
groupings.insert(grouping);
|
||||
grouping.clear();
|
||||
}
|
||||
}
|
||||
|
||||
set< set<CTxDestination>* > uniqueGroupings; // a set of pointers to groups of addresses
|
||||
map< CTxDestination, set<CTxDestination>* > setmap; // map addresses to the unique group containing it
|
||||
BOOST_FOREACH(set<CTxDestination> grouping, groupings)
|
||||
{
|
||||
// make a set of all the groups hit by this new group
|
||||
set< set<CTxDestination>* > hits;
|
||||
map< CTxDestination, set<CTxDestination>* >::iterator it;
|
||||
BOOST_FOREACH(CTxDestination address, grouping)
|
||||
if ((it = setmap.find(address)) != setmap.end())
|
||||
hits.insert((*it).second);
|
||||
|
||||
// merge all hit groups into a new single group and delete old groups
|
||||
set<CTxDestination>* merged = new set<CTxDestination>(grouping);
|
||||
BOOST_FOREACH(set<CTxDestination>* hit, hits)
|
||||
{
|
||||
merged->insert(hit->begin(), hit->end());
|
||||
uniqueGroupings.erase(hit);
|
||||
delete hit;
|
||||
}
|
||||
uniqueGroupings.insert(merged);
|
||||
|
||||
// update setmap
|
||||
BOOST_FOREACH(CTxDestination element, *merged)
|
||||
setmap[element] = merged;
|
||||
}
|
||||
|
||||
set< set<CTxDestination> > ret;
|
||||
BOOST_FOREACH(set<CTxDestination>* uniqueGrouping, uniqueGroupings)
|
||||
{
|
||||
ret.insert(*uniqueGrouping);
|
||||
delete uniqueGrouping;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
CPubKey CReserveKey::GetReservedKey()
|
||||
{
|
||||
if (nIndex == -1)
|
||||
|
@ -176,6 +176,9 @@ public:
|
||||
int64 GetOldestKeyPoolTime();
|
||||
void GetAllReserveKeys(std::set<CKeyID>& setAddress);
|
||||
|
||||
std::set< std::set<CTxDestination> > GetAddressGroupings();
|
||||
std::map<CTxDestination, int64> GetAddressBalances();
|
||||
|
||||
bool IsMine(const CTxIn& txin) const;
|
||||
int64 GetDebit(const CTxIn& txin) const;
|
||||
bool IsMine(const CTxOut& txout) const
|
||||
|
Loading…
Reference in New Issue
Block a user