changeset 3375:e5fe6fcc1f59 draft

Add address groupings RPC from the coincontrol patches. Signed-off-by: Gregory Maxwell <greg@xiph.org>
author coderrr <coderrr.contact@gmail.com>
date Wed, 01 Aug 2012 12:48:42 -0400
parents 82ec22980618
children a15769159fbd
files src/bitcoinrpc.cpp src/bitcoinrpc.h src/rpcwallet.cpp src/wallet.cpp src/wallet.h
diffstat 5 files changed, 163 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -231,6 +231,7 @@
     { "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 },
--- a/src/bitcoinrpc.h
+++ b/src/bitcoinrpc.h
@@ -114,6 +114,7 @@
 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);
--- a/src/rpcwallet.cpp
+++ b/src/rpcwallet.cpp
@@ -274,6 +274,33 @@
     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)
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -1620,6 +1620,129 @@
     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)
--- 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 @@
     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 @@
         return true;
     }
 
+    std::string GetAddressOfTxOut(int n)
+    {
+        CTxDestination addr;
+        ExtractDestination(vout[n].scriptPubKey, addr);
+        return CBitcoinAddress(addr).ToString();
+    }
+
     bool WriteToDisk();
 
     int64 GetTxTime() const;