Author

Topic: Aggregating addresses to wallets (using armoryengine) (Read 1113 times)

legendary
Activity: 2618
Merit: 1007
Thanks for the help, I changed the script now a little bit to the following (runs now with armory 0.81):
Code:
from armoryengine import *

BDM_LoadBlockchainFile()

topBlock = TheBDM.getTopBlockHeight()

trackaddr = dict({'pirateat40 GPUMAX':['1PSf86KnLuzM7Ris5kDhTEZwooR3p2iyfV'],
                  'Armory Client Developer':['1QBDLYTDFHHZAABYSKGKPWKLSXZWCCJQBX']})

addratstart = 0
for item in trackaddr.itervalues():
     addratstart += len(item)

addratend = 0

iterations = 0

while addratstart != addratend:
  iterations += 1
  addratstart = 0
  for item in trackaddr.itervalues():
     addratstart += len(item)
  for h in xrange(0, topBlock+1):
    if h%5000 == 0:
      print '\tScanned %d blocks' % h
   
    header = TheBDM.getHeaderByHeight(h)
    txList = header.getTxRefPtrList()
    for txref in txList:
      tx = txref.getTxCopy()
      foundaddr = False
      addressowner = ''
      for nin in range(tx.getNumTxIn()):
        txin = tx.getTxIn(nin)
        try:
          senderAddr20 = TheBDM.getSenderAddr20(txin)
          address = hash160_to_addrStr(senderAddr20)
          #print address
          for owner in trackaddr.keys():
            if address in trackaddr[owner]:
              foundaddr = True
              addressowner = owner
              print "FOUND TRANSACTION! At block " + str (h) + ' owned by ' + addressowner
              break
        except:
          pass
      if foundaddr is True:
        for nin in range(tx.getNumTxIn()):
          txin = tx.getTxIn(nin)
          try:
            senderAddr20 = TheBDM.getSenderAddr20(txin)
            address = hash160_to_addrStr(senderAddr20)
            trackaddr[addressowner].append(address)
          except:
            pass
        trackaddr[addressowner] = list(set(trackaddr[addressowner]))
  addratend = 0
  for item in trackaddr.itervalues():
     addratend += len(item)

print trackaddr
print "Went through the blockchain %d times!" % iterations
print "Found %d addresses in total" % addratend
for owner in trackaddr.keys():
     print owner + ' has ' + str(len(trackaddr[owner])) + ' addresses'

The pirate address comes from https://bitcointalksearch.org/topic/m.980130 btw. and the donation address was public anyways and one of the first ones I randomly found while looking for an address.

It's easy now to enter more "name:address" relationships and search for all of them, though it might get weird if you try to give 2 different names to the same cluster of addresses I fear... I proabably need a list of names as well for each entity there. Also I haven't tested very tough cookies (GLBSE, MtGox(!)) so I don't know if it scales well. For tracking smaller/medium sized wallets it should be enough though.
legendary
Activity: 1428
Merit: 1093
Core Armory Developer
Version 0.80 was a reworking of the underlying blockchain engine, whereby I had to change some of the interfaces slightly to be more scalable.  As such, "TxRef" objects are mainly just pointers to blk000X.dat file locations.  All the methods are now part of "Tx" objects which are copies of the tx-data.   As such, the following code needs to change

FROM

Quote

    txList = header.getTxRefPtrList()
    for tx in txList:
      foundaddr = False
      for nin in range(tx.getNumTxIn()):
      ...


TO


Quote

    txList = header.getTxRefPtrList()
    for txref in txList:
      tx = txref.getTxCopy()
      foundaddr = False
      for nin in range(tx.getNumTxIn()):
      ...


And I notice you are tracking my address in your example...
legendary
Activity: 2618
Merit: 1007
Since there's currently a lot of fuzz about pirateat40's operations (is it a ponzi? is it legit? where does he get his coins from/back, if he really sells them OTC? how is GPUMax fitting in?), I think one of the best ways to analyze the situation is to get an address that surely belongs to him, hope for combined inputs if he sends something from that address, assume these belong to him as well (as he'd need to have private keys for that) and work our way from there until we have all linked addresses to a single bitcoin address.
Also something like this might also be useful for other purposes and I haven't found many projects concerning this (one would be toolongdidntread.com)...

After that, we can assume this as his wallet and check the balance, money flows etc. (which is currently a bit beyond what I did so far).

The first problem I ran into however is:
Is my code so far really correct? I don't want to make accusations or draw conclusions when it turns out that I included an MtGox address out of stupidity...

Anyways, here's the code to track any address (supplied in the "trackaddr" list) for multiple inputs. It will iterate over the whole blockchain multiple times until no more new inputs are found. Because of recent blockchain spam, I limited it currently to block 160000 (enough for the address I have so far) and there's probably a nicer way than running ~10 times over the whole chain. I have a fast CPU however, so it doesn't really bother me atm, the bigger concern is if it is really doing what it's supposed to do and has no chance of picking up and including some random other addresses.

Code (with Armory's donation address up to block 170000):
Code:
from armoryengine import *

BDM_LoadBlockchainFile()

topBlock = TheBDM.getTopBlockHeight()

trackaddr = ['1QBDLYTDFHHZAABYSKGKPWKLSXZWCCJQBX']
taintedblocks = []

addratstart = len(trackaddr)
addratend = 0

iterations = 0
transactioncount = 0

while addratstart != addratend:
  transactioncount = 0
  iterations += 1
  addratstart = len(trackaddr)
  for h in xrange(0, 170000): #topBlock+1):
    if h%10000 == 0:
      print '\tScanned %d blocks' % h
      print 'Found %d addresses so far.' % len(trackaddr)
    
    header = TheBDM.getHeaderByHeight(h)
    txList = header.getTxRefPtrList()
    for tx in txList:
      foundaddr = False
      for nin in range(tx.getNumTxIn()):
        txin = tx.getTxInRef(nin)
        try:
          senderAddr20 = TheBDM.getSenderAddr20(txin)
          address = hash160_to_addrStr(senderAddr20)
          #print address
          if address in trackaddr:
            foundaddr = True
            print "FOUND TRANSACTION! At block " + str (h)
        except:
          pass
      if foundaddr is True:
        for nin in range(tx.getNumTxIn()):
          txin = tx.getTxInRef(nin)
          try:
            senderAddr20 = TheBDM.getSenderAddr20(txin)
            address = hash160_to_addrStr(senderAddr20)
            trackaddr.append(address)
          except:
            pass
        trackaddr = list(set(trackaddr))
        transactioncount += 1
        taintedblocks.append(h)
        taintedblocks = list(set(taintedblocks))
  addratend = len(trackaddr)

print trackaddr
print taintedblocks
print "Went through the blockchain %d times!" % iterations
print "Found %d transactions" % transactioncount
print "Found %d blocks where these transactions were included" % len(taintedblocks)
print "Found %d addresses" % addratend

Oh, and I won't post any results (it's easy with armory to include all found addresses to a "wallet" for example and read the spendable balance of that at each block) publicly or in PMs until I have verified this code to be correct! Also pirate seems to be quite paranoid about posting bitcoin addresses belonging to him, so good luck with finding some (unless you're a customer at BTCS&T, then you can of course use your deposit address).

Edit:
I was using armory 0.76 rc1 - the current 0.81 version throws:
Code:
Traceback (most recent call last):
  File "!findwallet.py", line 29, in
    for nin in range(tx.getNumTxIn()):
  File "xxxxxxxxx\Armory_0_81\CppBlockUtils.py"
, line 813, in
    __getattr__ = lambda self, name: _swig_getattr(self, TxRef, name)
  File "xxxxxxxxx\Armory_0_81\CppBlockUtils.py"
, line 51, in _swig_getattr
    raise AttributeError(name)
AttributeError: getNumTxIn
Jump to: