Created
December 11, 2017 00:51
-
-
Save ysqi/62820e5955a3e3b56efb8b396585f44a to your computer and use it in GitHub Desktop.
Decode bitcoin block chain dat file, and get block data deail content
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 ( | |
"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
3D2oetdNuZUqQHPJmcMDDHYoqkyNVsFk9r