Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Decode bitcoin block chain dat file, and get block data deail content
package main
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"fmt"
"io"
"log"
"math"
"os"
"time"
)
const (
MARGIC uint32 = 0xd9b4bef9
)
func main() {
log.SetFlags(log.Ltime | log.Lmicroseconds | log.Lshortfile)
//filename := "./data/blk_0_to_4.dat"
filename := "/Users/ysqi/Library/Application Support/Bitcoin/blocks/blk00001.dat"
blocks := decodeBlocks(filename, 10)
for _, b := range blocks {
fmt.Printf("block version %x, block hash:%s, prev hash=%s \n", b.Head.Version, b.Head.Hash, b.Head.PrevBlock)
fmt.Printf("\t bits:%x,nonce:%x,number of tx:%d, merkle root:%s \n", b.Head.Bits, b.Head.Nonce, len(b.Transactions), b.Head.MerkleRoot)
var sum uint64
for _, t := range b.Transactions {
for i, input := range t.TxIn {
fmt.Printf("\t input %2d: Sequence:%d,index=%d,phash=%s \n",
i, input.Sequence, input.PreviousOutPoint.Index, input.PreviousOutPoint.Hasx)
}
for i, o := range t.TxOut {
sum += o.Value
fmt.Printf("\t output %d,value=%d \n", i, o.Value)
}
}
fmt.Printf("\t %f \n", ToBit(sum))
}
}
func ToBit(satoshi uint64) float64 {
return float64(satoshi) / math.Pow10(8)
}
func decodeBlocks(name string, needBlocks int) []*Block {
f, err := os.Open(name)
if err != nil {
log.Fatal(err)
}
defer f.Close()
var blooks []*Block
for {
mg := ReadUint32(f)
if mg != MARGIC {
Failt(fmt.Errorf("expect margic %x,not %x", margic, mg))
}
//get block size
blockSize := ReadUint32(f)
//get block bytes
blockData := ReadBytes(f, uint64(blockSize))
// create a byte reader for single block
r := bytes.NewReader(blockData)
// head
head := decodeHeader(r)
head.Hash = DoubleHashH(blockData[:80])
// transaction
txCount := ReadVarInt(r)
txs := make([]*MsgTx, txCount)
for i := uint64(0); i < txCount; i++ {
txs[i] = decoderTX(r)
}
blooks = append(blooks, &Block{
Head: head,
Transactions: txs,
})
if len(blooks) >= needBlocks {
break
}
}
return blooks
}
func decodeHeader(r io.Reader) (h BlockHeader) {
h.Version = ReadUint32(r)
h.PrevBlock = ReadHash(r)
h.MerkleRoot = ReadHash(r)
h.Timestamp = time.Unix(int64(ReadUint32(r)), 0)
h.Bits = ReadUint32(r)
h.Nonce = ReadUint32(r)
return
}
type Block struct {
Head BlockHeader
Transactions []*MsgTx
}
type BlockHeader struct {
Hash Hash
Version uint32
PrevBlock Hash
MerkleRoot Hash
Timestamp time.Time
Bits uint32 // difficulty @see: https://en.bitcoin.it/wiki/Difficulty
Nonce uint32
}
type MsgTx struct {
Version uint32
TxIn []*TxIn
TxOut []*TxOut
LockTime uint32
}
// TxIn defines a bitcoin transaction input.
type TxIn struct {
PreviousOutPoint OutPoint
SignatureScript []byte
Sequence uint32
}
// TxOut defines a bitcoin transaction output.
type TxOut struct {
Value uint64
PkScript []byte
}
type OutPoint struct {
Hasx Hash
Index uint32
}
func decoderTX(r io.Reader) *MsgTx {
msg := &MsgTx{}
msg.Version = ReadUint32(r)
txInCount := ReadVarInt(r)
msg.TxIn = make([]*TxIn, txInCount)
for i := uint64(0); i < txInCount; i++ {
tx := TxIn{}
msg.TxIn[i] = &tx
// prev
tx.PreviousOutPoint.Hasx = ReadHash(r)
tx.PreviousOutPoint.Index = ReadUint32(r)
scriptLen := ReadVarInt(r)
tx.SignatureScript = ReadBytes(r, scriptLen)
tx.Sequence = ReadUint32(r)
}
txOutCount := ReadVarInt(r)
msg.TxOut = make([]*TxOut, txOutCount)
for i := uint64(0); i < txOutCount; i++ {
tx := TxOut{}
msg.TxOut[i] = &tx
tx.Value = ReadUint64(r)
scriptLen := ReadVarInt(r)
tx.PkScript = ReadBytes(r, scriptLen)
}
msg.LockTime = ReadUint32(r)
return msg
}
type Hash [HashSize]byte
const (
HashSize = 32
)
// String returns the Hash as the hexadecimal string of the byte-reversed
// hash.
func (hash Hash) String() string {
for i := 0; i < HashSize/2; i++ {
hash[i], hash[HashSize-1-i] = hash[HashSize-1-i], hash[i]
}
return hex.EncodeToString(hash[:])
}
func DoubleHashH(b []byte) Hash {
first := sha256.Sum256(b)
return Hash(sha256.Sum256(first[:]))
}
func Failt(err error) {
log.Output(2, err.Error())
os.Exit(1)
}
func ReadHash(r io.Reader) Hash {
var h Hash
_, err := io.ReadFull(r, h[:])
if err != nil {
Failt(err)
}
return h
}
func ReadBytes(r io.Reader, size uint64) []byte {
b := make([]byte, size)
_, err := io.ReadFull(r, b)
if err != nil {
Failt(err)
}
return b
}
func ReadUint32(r io.Reader) uint32 {
var v uint32
err := binary.Read(r, binary.LittleEndian, &v)
if err != nil {
Failt(err)
}
return v
}
func ReadUint64(r io.Reader) uint64 {
var v uint64
err := binary.Read(r, binary.LittleEndian, &v)
if err != nil {
Failt(err)
}
return v
}
// ReadVarInt reads a variable length integer from r and returns it as a uint64.
func ReadVarInt(r io.Reader) uint64 {
var discriminant uint8
err := binary.Read(r, binary.LittleEndian, &discriminant)
if err != nil {
Failt(err)
}
var rv uint64
switch discriminant {
case 0xff:
err = binary.Read(r, binary.LittleEndian, &rv)
if err != nil {
Failt(err)
}
// The encoding is not canonical if the value could have been
// encoded using fewer bytes.
min := uint64(0x100000000)
if rv < min {
Failt(fmt.Errorf("var int %x < min value %x", rv, min))
}
case 0xfe:
var v uint32
err = binary.Read(r, binary.LittleEndian, &v)
if err != nil {
Failt(err)
}
rv = uint64(v)
// The encoding is not canonical if the value could have been
// encoded using fewer bytes.
min := uint64(0x10000)
if rv < min {
Failt(fmt.Errorf("var int %x < min value %x", rv, min))
}
case 0xfd:
var v uint16
err = binary.Read(r, binary.LittleEndian, &v)
if err != nil {
Failt(err)
}
rv = uint64(v)
// The encoding is not canonical if the value could have been
// encoded using fewer bytes.
min := uint64(0xfd)
if rv < min {
Failt(fmt.Errorf("var int %x < min value %x", rv, min))
}
default:
rv = uint64(discriminant)
}
return rv
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.