virtual void edge( uint64_t value, const uint8_t *upTXHash, uint64_t outputIndex, const uint8_t *outputScript, uint64_t outputScriptSize, const uint8_t *downTXHash, uint64_t inputIndex, const uint8_t *inputScript, uint64_t inputScriptSize ) { uint8_t addrType[3]; uint160_t pubKeyHash; int type = solveOutputScript(pubKeyHash.v, outputScript, outputScriptSize, addrType); if(unlikely(type<0)) return; uint64_t a; auto i = addrMap.find(pubKeyHash.v); if(unlikely(addrMap.end()!=i)) a = i->second; else { Addr *addr = (Addr*)allocHash160(); memcpy(addr->v, pubKeyHash.v, kRIPEMD160ByteSize); addrMap[addr->v] = a = allAddrs.size(); allAddrs.push_back(addr); } vertices.push_back(a); }
virtual int init( int argc, const char *argv[] ) { optparse::Values &values = parser.parse_args(argc, argv); auto args = parser.args(); for(size_t i=1; i<args.size(); ++i) { loadKeyList(rootHashes, args[i].c_str()); } if(0==rootHashes.size()) { const char *addr = "1dice8EMZmqKvrGE4Qc9bUFf9PX3xaYDp"; warning("no addresses specified, using satoshi's dice address %s", addr); loadKeyList(rootHashes, addr); } addrMap.setEmptyKey(gEmptyKey); addrMap.resize(15 * 1000 * 1000); allAddrs.reserve(15 * 1000 * 1000); info("Building address equivalence graph ..."); startTime = usecs(); return 0; }
virtual int init( int argc, const char *argv[] ) { optparse::Values &values = parser.parse_args(argc, argv); auto args = parser.args(); for(size_t i=1; i<args.size(); ++i) { loadKeyList(rootHashes, args[i].c_str()); } if(0==rootHashes.size()) { const char *addr = getInterestingAddr(); loadKeyList(rootHashes, addr); } addrMap.setEmptyKey(gEmptyKey); addrMap.resize(15 * 1000 * 1000); allAddrs.reserve(15 * 1000 * 1000); info("Building address equivalence graph ..."); startTime = usecs(); return 0; }
virtual int init( int argc, const char *argv[] ) { offset = 0; curBlock = 0; lastBlock = 0; firstBlock = 0; addrMap.setEmptyKey(emptyKey); addrMap.resize(15 * 1000 * 1000); allAddrs.reserve(15 * 1000 * 1000); optparse::Values &values = parser.parse_args(argc, argv); cutoffBlock = values.get("atBlock"); showAddr = values.get("withAddr"); detailed = values.get("detailed"); limit = values.get("limit"); auto args = parser.args(); for(size_t i=1; i<args.size(); ++i) { loadKeyList(restricts, args[i].c_str()); } if(0<=cutoffBlock) { info("only taking into account transactions before block %" PRIu64 "\n", cutoffBlock); } if(0!=restricts.size()) { info( "restricting output to %" PRIu64 " addresses ...\n", (uint64_t)restricts.size() ); auto e = restricts.end(); auto i = restricts.begin(); restrictMap.setEmptyKey(emptyKey); while(e!=i) { const uint160_t &h = *(i++); restrictMap[h.v] = 1; } } else { if(detailed) { warning("asking for --detailed for *all* addresses in the blockchain will be *very* slow"); warning("as a matter of fact, it likely won't ever finish unless you have *lots* of RAM"); } } info("analyzing blockchain ..."); return 0; }
virtual void wrapup() { size_t size = boost::num_vertices(graph); info( "done, %.2f secs, found %" PRIu64 " address(es) \n", 1e-6*(usecs() - startTime), size ); info("Clustering ... "); startTime = usecs(); std::vector<uint64_t> cc(size); uint64_t nbCC = boost::connected_components(graph, &cc[0]); info( "done, %.2f secs, found %" PRIu64 " clusters.\n", 1e-6*(usecs() - startTime), nbCC ); auto e = rootHashes.end(); auto i = rootHashes.begin(); while(e!=i) { uint64_t count = 0; const uint8_t *keyHash = (i++)->v; uint8_t b58[128]; hash160ToAddr(b58, keyHash); info("Address cluster for address %s:", b58); auto j = addrMap.find(keyHash); if(unlikely(addrMap.end()==j)) { warning("specified key was never used to spend coins"); showFullAddr(keyHash); printf("\n"); count = 1; } else { uint64_t addrIndex = j->second; uint64_t homeComponentIndex = cc[addrIndex]; for(size_t k=0; likely(k<cc.size()); ++k) { uint64_t componentIndex = cc[k]; if(unlikely(homeComponentIndex==componentIndex)) { Addr *addr = allAddrs[k]; showFullAddr(addr->v); printf("\n"); ++count; } } } info("%" PRIu64 " addresse(s)\n", count); } }
void move( const uint8_t *script, uint64_t scriptSize, const uint8_t *txHash, int64_t value, const uint8_t *downTXHash = 0 ) { uint8_t addrType[3]; uint160_t pubKeyHash; int type = solveOutputScript(pubKeyHash.v, script, scriptSize, addrType); if(unlikely(type<0)) return; Addr *addr; auto i = addrMap.find(pubKeyHash.v); if(unlikely(addrMap.end()!=i)) addr = i->second; else { addr = allocAddr(); memcpy(addr->hash.v, pubKeyHash.v, kRIPEMD160ByteSize); addr->sum = 0; addrMap[addr->hash.v] = addr; allAddrs.push_back(addr); } addr->lastTouched = blockTime; addr->sum += value; static uint64_t cnt = 0; if(unlikely(0==((cnt++)&0xFFFFF))) { if( curBlock && lastBlock && firstBlock ) { double progress = curBlock->height/(double)lastBlock->height; info( "%8" PRIu64 " blocks, " "%8.3f MegaMoves , " "%8.3f MegaAddrs , " "%5.2f%%", curBlock->height, cnt*1e-6, addrMap.size()*1e-6, 100.0*progress ); } } }
virtual int init( int argc, char *argv[] ) { curBlock = 0; lastBlock = 0; firstBlock = 0; addrMap.setEmptyKey(emptyKey); addrMap.resize(15 * 1000 * 1000); allAddrs.reserve(15 * 1000 * 1000); option::Stats stats(usageDescriptor, argc, argv); option::Option *buffer = new option::Option[stats.buffer_max]; option::Option *options = new option::Option[stats.options_max]; option::Parser parse(usageDescriptor, argc, argv, options, buffer); if(parse.error()) exit(1); for(int i=0; i<parse.nonOptionsCount(); ++i) { loadKeyList(restricts, parse.nonOption(i)); } if(0!=restricts.size()) { info( "restricting output to %" PRIu64 " addresses ...\n", (uint64_t)restricts.size() ); auto e = restricts.end(); auto i = restricts.begin(); restrictMap.setEmptyKey(emptyKey); while(e!=i) { const uint160_t &h = *(i++); restrictMap[h.v] = 1; } } info("analyzing blockchain ..."); delete [] options; delete [] buffer; return 0; }
virtual void startBlock( const Block *b, uint64_t chainSize ) { curBlock = b; const uint8_t *p = b->data; const uint8_t *sz = -4 + p; LOAD(uint32_t, size, sz); offset += size; double now = usecs(); static double startTime = 0; static double lastStatTime = 0; double elapsed = now - lastStatTime; bool longEnough = (5*1000*1000<elapsed); bool closeEnough = ((chainSize - offset)<80); if(unlikely(longEnough || closeEnough)) { if(0==startTime) { startTime = now; } double progress = offset/(double)chainSize; double elasedSinceStart = 1e-6*(now - startTime); double speed = progress / elasedSinceStart; info( "%8" PRIu64 " blocks, " "%8.3f MegaAddrs , " "%6.2f%% , " "elapsed = %5.2fs , " "eta = %5.2fs , " , curBlock->height, addrMap.size()*1e-6, 100.0*progress, elasedSinceStart, (1.0/speed) - elasedSinceStart ); lastStatTime = now; } SKIP(uint32_t, version, p); SKIP(uint256_t, prevBlkHash, p); SKIP(uint256_t, blkMerkleRoot, p); LOAD(uint32_t, bTime, p); blockTime = bTime; if(0<=cutoffBlock && cutoffBlock<=curBlock->height) { wrapup(); } }
virtual void start( const Block *, const Block * ) { if(csv) { printf( "\"Time\"," " \"Address\"," " \"TXId\"," " \"TXAmount\"," " \"NewBalance\"" "\n" ); } else { info("Dumping all transactions for %d address(es)\n", (int)addrMap.size()); printf(" Time (GMT) Address Transaction OldBalance Amount NewBalance\n"); printf(" =======================================================================================================================================================================================================================\n"); } }
virtual int init( int argc, const char *argv[] ) { sum = 0; adds = 0; subs = 0; nbTX = 0; optparse::Values &values = parser.parse_args(argc, argv); csv = values.get("csv"); auto args = parser.args(); for(size_t i=1; i<args.size(); ++i) { loadKeyList(rootHashes, args[i].c_str()); } if(0==rootHashes.size()) { #if defined(LITECOIN) const char *addr = "LKvTVnkK2rAkJXfgPdkaDRgvEGvazxWS9o"; warning("no addresses specified, using popular address %s", addr); #else const char *addr = "1dice8EMZmqKvrGE4Qc9bUFf9PX3xaYDp"; warning("no addresses specified, using satoshi's dice address %s", addr); #endif loadKeyList(rootHashes, addr); } auto e = rootHashes.end(); auto i = rootHashes.begin(); addrMap.setEmptyKey(emptyKey); while(e!=i) { const uint160_t &h = *(i++); addrMap[h.v] = 1; } return 0; }
void move( const uint8_t *script, uint64_t scriptSize, const uint8_t *txHash, uint64_t value, bool add, const uint8_t *downTXHash = 0 ) { uint8_t addrType[3]; uint160_t pubKeyHash; int type = solveOutputScript(pubKeyHash.v, script, scriptSize, addrType); if(unlikely(type<0)) return; bool match = (addrMap.end() != addrMap.find(pubKeyHash.v)); if(unlikely(match)) { int64_t newSum = sum + value*(add ? 1 : -1); if(csv) { printf("%6" PRIu64 ", \"", bTime/86400 + 25569); showHex(pubKeyHash.v, kRIPEMD160ByteSize, false); printf("\", \""); showHex(downTXHash ? downTXHash : txHash); printf( "\",%17.08f,%17.08f\n", (add ? 1e-8 : -1e-8)*value, newSum*1e-8 ); } else { struct tm gmTime; time_t blockTime = bTime; gmtime_r(&blockTime, &gmTime); char timeBuf[256]; asctime_r(&gmTime, timeBuf); size_t sz =strlen(timeBuf); if(0<sz) timeBuf[sz-1] = 0; printf(" %s ", timeBuf); showHex(pubKeyHash.v, kRIPEMD160ByteSize, false); printf(" "); showHex(downTXHash ? downTXHash : txHash); printf( " %24.08f %c %24.08f = %24.08f\n", sum*1e-8, add ? '+' : '-', value*1e-8, newSum*1e-8 ); } (add ? adds : subs) += value; sum = newSum; ++nbTX; } }
void move( const uint8_t *script, uint64_t scriptSize, const uint8_t *upTXHash, int64_t outputIndex, int64_t value, const uint8_t *downTXHash = 0, uint64_t inputIndex = -1 ) { uint8_t addrType[3]; uint160_t pubKeyHash; int type = solveOutputScript(pubKeyHash.v, script, scriptSize, addrType); if(unlikely(type<0)) return; if(0!=restrictMap.size()) { auto r = restrictMap.find(pubKeyHash.v); if(restrictMap.end()==r) { return; } } Addr *addr; auto i = addrMap.find(pubKeyHash.v); if(unlikely(addrMap.end()!=i)) { addr = i->second; } else { addr = allocAddr(); memcpy(addr->hash.v, pubKeyHash.v, kRIPEMD160ByteSize); addr->outputVec = 0; addr->nbOut = 0; addr->nbIn = 0; addr->sum = 0; if(detailed) { addr->outputVec = new OutputVec; } addrMap[addr->hash.v] = addr; allAddrs.push_back(addr); } if(0<value) { addr->lastIn = blockTime; ++(addr->nbIn); } else { addr->lastOut = blockTime; ++(addr->nbOut); } addr->sum += value; if(detailed) { struct Output output; output.value = value; output.time = blockTime; output.upTXHash = upTXHash; output.downTXHash = downTXHash; output.inputIndex = inputIndex; output.outputIndex = outputIndex; addr->outputVec->push_back(output); } }