Skip to content

Instantly share code, notes, and snippets.

@h4x3rotab
Last active January 26, 2018 06:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save h4x3rotab/fc5e16f98429f7eebe39de904977aa5b to your computer and use it in GitHub Desktop.
Save h4x3rotab/fc5e16f98429f7eebe39de904977aa5b to your computer and use it in GitHub Desktop.
Analysis of stolen coins from a miner's wallet
import decimal
import requests
# load all related transactions
target = 'GRTi7YMuJvMFJbcbp8a4YnjKTZ6UJBpGJA'
rsps = [requests.get('http://explorer.bitcoingold.org/insight-api/txs/?address=%s&pageNum=%d'
% (target, i)) for i in range(8)] # 8 pages in total. got it manually
all_data = [resp.json() for resp in rsps]
txes = [tx for data in all_data for tx in data['txs']]
# check the delta for transactions
def outputs(tx):
send = [vin for vin in tx['vin'] if vin['addr'] == target]
recv = [vout for vout in tx['vout'] if vout['scriptPubKey']['addresses'][0] == target]
return send, recv
btc_sat = decimal.Decimal('1e8')
for tx in txes:
send, recv = outputs(tx)
total_send = sum(decimal.Decimal(vout['valueSat']) / btc_sat for vout in send)
total_recv = sum(decimal.Decimal(vout['value']) for vout in recv)
balance = total_recv - total_send
print(tx['blockheight'], tx['txid'], total_recv, total_send)
if balance > 0:
print('recv:', balance)
elif balance < 0:
print('send:', -balance)
else:
print('nothing happens')
'''
Finding 1
Sometimes a set of addresses received the exact same amount of money from somebody twice in a single
block:
- 505858 5c3cb806a19a679777140faa5b4e71a5da00799ab366f07db5deb7cef4c8be0d 0.01746099 0
- 505858 6250d96c346fcf735e929076ae060df756672fd81d204522b7f085a2b6e8b7a3 0.01746099 0
- 505828 574bc146fdf74c51672fbfdff208fc9b72f866e834ccc1388f42ec28e3313645 0.01230015 0
- 505828 fd96c2fb9517471019013b6f6f54df68a55e1c9deaf864eda1b17e90f4192a22 0.01230015 0
- 505099 e11db19d7ac936dc18244774512431b68c0e731ff24aa41f52848204f9559d29 0.01813379 0
- 505099 7613baaa46490a75703cb46dae46e1066e3809b1fb34fa0609fe9531e54e0977 0.01813379 0
- 505025 46fbde2b7f0704b7275ec60205833ff50112cb94f12d6a04eb0aa10e30289089 0.01224912 0
- 505025 8810880d79e8bdf589a850baf2bf23785df6e231af930c47c4dac0cbf2b8edb3 0.01224912 0
The inputs are not same but the outputs the the two txes are exact the same. Coincidence?
After some check, I confirmed all the transactions are from GWViUY2b3HAYWY9BbeGeFjc6rKdrBffzHa,
which should belong to pool.gold.
Finding 2
Someone spent the incoming utxo right after he saw it from the mempool (tx from pool):
- 506710 8e5df7a991a1af820ff898328eebecab4e6139fd930375c735f1c06899aab19f 0 0.01479572
- 506710 368b2dd019ba5c7a9fe56ce6c2b425ee9bac7e564f33e27bedf1689b30b845ca 0.01479572 0
- 506688 50860f07c2f9a6f6f2ea16f04cf76adaf7b292efc623305ad97e59277b5260fd 0 0.01100774
- 506688 199d8f657218722e59da190e9020bceabdb44bf9136d90bec609713c4e521514 0.01100774 0
- 506671 ebe9f6709d37bb85bdb08d0c79eb8d44e73cc0976aec719c93030593e58cc8a6 0 0.01373011
- 506671 503cc50f0817fa7972a41f702019fa139740f1907eac391ce4b328140834dc20 0.01373011 0
- 506620 5739ee01fc03323e218297fa182c08b6ed8a103169980f1a875560b16bb89658 0 0.01383092
- 506620 b1035e314bea092a780d17407fd83079ed8b0875ef2187a7765b033d42405d9a 0.01383092 0
- 506542 2df89bd54b8fb940623c3c4e08ebb61388833a8a107f85c16b96b2a4303d6507 0 0.01121386
- 506542 e90c6531f4615eb222de9f60d2532fc39a1e79ef702258fed873cabcf2bf10db 0.01121386 0
Earlier withdraws have a bit delay:
- 506529 25906c6c2fea5f889b8204c0422c117ba6d457981d57e24cbd72d974849b3282 0 0.01264837
- 506515 30259462cdb25ca352f71d60a94ffd32b1553f3151b22a28fcd84d2f0341d04a 0.01264837 0
- 506458 f423d7eab2f336a4abc453dfbd92eb26d7a31d2bf72bb83bf33c698eaf6d259c 0 0.01335401
- 506414 29307743cc86ce263c18c5873ae903ac9318ee1ec64997d21dad8dc38a48f71a 0.01335401 0
First sweep:
- 506148 f5260eb0a23b6d77c4ecb477234bfc6dc28ea6fa2a85800232db471d5770dbe9 0 0.72877034
'''
# check if any input is coinbase
for tx in txes:
send, recv = outputs(tx)
if recv:
assert 'coinbase' not in tx['vin'][0]
'''
All the inputs are not coinbase.
'''
# check output of the sweeps
for tx in txes:
send, recv = outputs(tx)
if send:
if len(tx['vout']) != 1:
print(tx)
continue
assert len(tx['vout'][0]['scriptPubKey']['addresses']) == 1
addr = tx['vout'][0]['scriptPubKey']['addresses'][0]
print(tx['blockheight'], tx['txid'], addr)
'''
Except the first sweep, all the coins goes to the same wallet: GezU4t9Nq4893SrGT8FLT5keZoJPYoeqcP
The first sweep is:
-> GNpj941c1JprsWjbBTcWTbhNuxfcJ54a1W: 0.72
-> GNpj941c1JprsWjbBTcWTbhNuxfcJ54a1W: 0.00868393
-> GVsEunZckaBqN6vYrftMXH758UjrsRqMb8: 0.71131378
=> GezU4t9Nq4893SrGT8FLT5keZoJPYoeqcP
-> GWUrTF6eVNyon9d2FHmwsua3cgTZYBfMBE 0.00868393 BTG (S)
-> GezU4t9Nq4893SrGT8FLT5keZoJPYoeqcP
'''
# check the input of 2df89bd54b8fb940623c3c4e08ebb61388833a8a107f85c16b96b2a4303d6507
tx1 = [tx for tx in txes
if tx['txid'] == '2df89bd54b8fb940623c3c4e08ebb61388833a8a107f85c16b96b2a4303d6507'][0]
txid2 = tx1['vin'][0]['txid']
tx2 = requests.get('https://explorer.bitcoingold.org/insight-api/tx/%s' % txid2).json()
txid_coinbase = tx2['vin'][0]['txid']
tx_coinbase = requests.get('https://explorer.bitcoingold.org/insight-api/tx/%s'
% txid_coinbase).json()
print('tx block:', tx1['blockheight'])
print('coinbase:', tx_coinbase['blockheight'])
print(tx_coinbase['vin'])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment