Last active
August 29, 2015 14:03
-
-
Save GeertJohan/b9cdf1e5ffb815b776d8 to your computer and use it in GitHub Desktop.
This program calculates how many mined coins were sent to which address.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"github.com/conformal/btcrpcclient" | |
"log" | |
"sort" | |
) | |
// blockCount defines the number of blocks back into history we wish to inspect. | |
const blockCount = 50000 | |
// miner holds information about a miner | |
// although the concept of a miner being directly related to an address is somewhat flawed, it's still used for this program | |
type miner struct { | |
address string //base58check encoded address | |
coins float64 // number of coins sent to this address | |
} | |
// minerList holds miner informations and is sortable (implements sort.Interface) | |
type minerList []*miner | |
// Len, Less and Swap are required for sort.Sort (sort.Interface) | |
func (ml minerList) Len() int { return len(ml) } | |
func (ml minerList) Less(i, j int) bool { return ml[j].coins < ml[i].coins } | |
func (ml minerList) Swap(i, j int) { ml[i], ml[j] = ml[j], ml[i] } | |
// create global map and list for miners | |
// map to lookup a miner by addr | |
// list to (in the end) sort all miners and print them in order when calculations are completed | |
var minersByAddr = make(map[string]*miner) | |
var miners = make(minerList, 0) | |
func main() { | |
// create new client instance | |
nlgclient, err := btcrpcclient.New(&btcrpcclient.ConnConfig{ | |
HttpPostMode: true, | |
DisableTLS: true, | |
Host: "127.0.0.1:8332", | |
User: "someUser", | |
Pass: "somePassword", | |
}, nil) | |
if err != nil { | |
log.Fatalf("error creating new nlg client: %v", err) | |
} | |
// get latest block number | |
bestBlockNum, err := nlgclient.GetBlockCount() | |
if err != nil { | |
log.Fatalf("error getting best block: %v", err) | |
} | |
log.Printf("best block: %d", bestBlockNum) | |
// check block number to be high enough so we can iterate backlog | |
if bestBlockNum < blockCount { | |
log.Fatalf("bestBlockNum < %d. will not continue.", blockCount) | |
} | |
// loop over blocknumber, decreace until `blockCount` iterations are completed | |
for i := bestBlockNum; i > bestBlockNum-blockCount; i-- { | |
// get hash for current block iteration | |
blockHash, err := nlgclient.GetBlockHash(int64(i)) | |
if err != nil { | |
log.Fatalf("error getting blockHash for block %d: %v", i, err) | |
} | |
// get block details | |
block, err := nlgclient.GetBlock(blockHash) | |
if err != nil { | |
log.Fatalf("error getting block %d (%s): %v", i, blockHash.String(), err) | |
} | |
// block result does not contain full transaction so we get a reference to request the full tx | |
rewardTxRef, err := block.Tx(0) | |
if err != nil { | |
log.Fatalf("error getting reward tx ref for block %d: %v", i, err) | |
} | |
// get the raw transaction from client | |
rewardTx, err := nlgclient.GetRawTransactionVerbose(rewardTxRef.Sha()) | |
if err != nil { | |
log.Fatalf("error getting reward tx for block %d: %v", i, err) | |
} | |
// check that the tx is indeed the reward/coinbase tx | |
if len(rewardTx.Vin) != 1 { | |
log.Fatalln("err: tx cannot be reward. len(vin) != 1") // this should not happen at all.. will still log them.. | |
} | |
if !rewardTx.Vin[0].IsCoinBase() { | |
log.Fatalln("tx is not reward") // this should not happen at all.. will still log them.. | |
} | |
// loop over all vOut's | |
for _, vout := range rewardTx.Vout { | |
// we don't really expect multisig coinbases. but it could happen... for now those are not in chain (and are ignored) | |
if len(vout.ScriptPubKey.Addresses) > 1 { | |
log.Println("multisig reward?? strange.. but okay.. we won't parse this for now..") | |
continue | |
} | |
// some vout's don't have output addr. | |
// TODO: figure out why some vout's are empty and don't have output addres in the coinbase. it's strange.. | |
if len(vout.ScriptPubKey.Addresses) == 0 { | |
log.Printf("strange. rewardTx for block %d has no vout address?", i) | |
continue | |
} | |
// get addr | |
addr := vout.ScriptPubKey.Addresses | |
// lookup miner from minersByAddr map | |
m := minersByAddr[addr] | |
// if miner is not in the map (nil): create a new one | |
if m == nil { | |
// create new miner instance | |
m = &miner{ | |
address: addr, | |
} | |
// add new miner instance to miners list and minersByAddr map | |
minersByAddr[addr] = m | |
miners = append(miners, m) | |
} | |
// add vout value to miner's coins | |
m.coins += vout.Value | |
} | |
} | |
// sort the miners (minersList implements sort.Interface) | |
sort.Sort(miners) | |
// output list of miners to stdout | |
for i, m := range miners { | |
fmt.Printf("%d: %s: %f\n", i+1, m.address, m.coins) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment