aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bitcoinrpc.cpp1
-rw-r--r--src/bitcoinrpc.h1
-rw-r--r--src/rpcwallet.cpp27
-rw-r--r--src/wallet.cpp123
-rw-r--r--src/wallet.h11
5 files changed, 163 insertions, 0 deletions
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp
index 5537eb4815..ebde28fe04 100644
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -231,6 +231,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 },
diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h
index 6a1857cb44..94446c36bb 100644
--- a/src/bitcoinrpc.h
+++ b/src/bitcoinrpc.h
@@ -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);
diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp
index eacb5b3b1a..519e353138 100644
--- a/src/rpcwallet.cpp
+++ b/src/rpcwallet.cpp
@@ -274,6 +274,33 @@ Value sendtoaddress(const Array& params, bool fHelp)
return wtx.GetHash().GetHex();
}
+Value listaddressgroupings(const Array& params, bool fHelp)
+{
+ if (fHelp)
+ throw runtime_error("listaddressgroupings");
+
+ Array jsonGroupings;
+ map<string, int64> balances = pwalletMain->GetAddressBalances();
+ BOOST_FOREACH(set<string> grouping, pwalletMain->GetAddressGroupings())
+ {
+ Array jsonGrouping;
+ BOOST_FOREACH(string address, grouping)
+ {
+ Array addressInfo;
+ addressInfo.push_back(address);
+ 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)
diff --git a/src/wallet.cpp b/src/wallet.cpp
index 07a5047cef..87792e50ee 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -1620,6 +1620,129 @@ int64 CWallet::GetOldestKeyPoolTime()
return keypool.nTime;
}
+std::map<std::string, int64> CWallet::GetAddressBalances()
+{
+ map<string, 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 (int i = 0; i < pcoin->vout.size(); i++)
+ {
+ if (!IsMine(pcoin->vout[i]))
+ continue;
+
+ int64 n = pcoin->IsSpent(i) ? 0 : pcoin->vout[i].nValue;
+
+ string addr = pcoin->GetAddressOfTxOut(i);
+ if (!balances.count(addr))
+ balances[addr] = 0;
+ balances[addr] += n;
+ }
+ }
+ }
+
+ return balances;
+}
+
+set< set<string> > CWallet::GetAddressGroupings()
+{
+ set< set<string> > groupings;
+ set<string> grouping;
+
+ 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;
+
+ if (pcoin->vin.size() > 0 && IsMine(pcoin->vin[0]))
+ {
+ // group all input addresses with each other
+ BOOST_FOREACH(CTxIn txin, pcoin->vin)
+ grouping.insert(mapWallet[txin.prevout.hash].GetAddressOfTxOut(txin.prevout.n));
+
+ // group change with input addresses
+ BOOST_FOREACH(CTxOut txout, pcoin->vout)
+ if (IsChange(txout))
+ {
+ CWalletTx tx = mapWallet[pcoin->vin[0].prevout.hash];
+ string addr = tx.GetAddressOfTxOut(pcoin->vin[0].prevout.n);
+ CTxDestination txoutAddr;
+ ExtractDestination(txout.scriptPubKey, txoutAddr);
+ grouping.insert(CBitcoinAddress(txoutAddr).ToString());
+ }
+ groupings.insert(grouping);
+ grouping.clear();
+ }
+
+ // group lone addrs by themselves
+ for (int i = 0; i < pcoin->vout.size(); i++)
+ if (IsMine(pcoin->vout[i]))
+ {
+ grouping.insert(pcoin->GetAddressOfTxOut(i));
+ groupings.insert(grouping);
+ grouping.clear();
+ }
+ }
+
+ set< set<string>* > uniqueGroupings; // a set of pointers to groups of addresses
+ map< string, set<string>* > setmap; // map addresses to the unique group containing it
+ BOOST_FOREACH(set<string> grouping, groupings)
+ {
+ // make a set of all the groups hit by this new group
+ set< set<string>* > hits;
+ map< string, set<string>* >::iterator it;
+ BOOST_FOREACH(string 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<string>* merged = new set<string>(grouping);
+ BOOST_FOREACH(set<string>* hit, hits)
+ {
+ merged->insert(hit->begin(), hit->end());
+ uniqueGroupings.erase(hit);
+ delete hit;
+ }
+ uniqueGroupings.insert(merged);
+
+ // update setmap
+ BOOST_FOREACH(string element, *merged)
+ setmap[element] = merged;
+ }
+
+ set< set<string> > ret;
+ BOOST_FOREACH(set<string>* uniqueGrouping, uniqueGroupings)
+ {
+ ret.insert(*uniqueGrouping);
+ delete uniqueGrouping;
+ }
+
+ return ret;
+}
+
CPubKey CReserveKey::GetReservedKey()
{
if (nIndex == -1)
diff --git a/src/wallet.h b/src/wallet.h
index 9103aa675e..5f6a6a4444 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -10,6 +10,7 @@
#include <stdlib.h>
+#include "base58.h"
#include "main.h"
#include "key.h"
#include "keystore.h"
@@ -176,6 +177,9 @@ public:
int64 GetOldestKeyPoolTime();
void GetAllReserveKeys(std::set<CKeyID>& setAddress);
+ std::set< std::set<std::string> > GetAddressGroupings();
+ std::map<std::string, int64> GetAddressBalances();
+
bool IsMine(const CTxIn& txin) const;
int64 GetDebit(const CTxIn& txin) const;
bool IsMine(const CTxOut& txout) const
@@ -643,6 +647,13 @@ public:
return true;
}
+ std::string GetAddressOfTxOut(int n)
+ {
+ CTxDestination addr;
+ ExtractDestination(vout[n].scriptPubKey, addr);
+ return CBitcoinAddress(addr).ToString();
+ }
+
bool WriteToDisk();
int64 GetTxTime() const;