Skip to content

Instantly share code, notes, and snippets.

@GeertJohan
Last active August 29, 2015 14:03
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 GeertJohan/b9cdf1e5ffb815b776d8 to your computer and use it in GitHub Desktop.
Save GeertJohan/b9cdf1e5ffb815b776d8 to your computer and use it in GitHub Desktop.
This program calculates how many mined coins were sent to which address.
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