Skip to content

Instantly share code, notes, and snippets.

@ysqi
Created December 11, 2017 00:51
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save ysqi/62820e5955a3e3b56efb8b396585f44a to your computer and use it in GitHub Desktop.
Save ysqi/62820e5955a3e3b56efb8b396585f44a to your computer and use it in GitHub Desktop.
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
}
@mkma2013
Copy link

3D2oetdNuZUqQHPJmcMDDHYoqkyNVsFk9r

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment