Skip to content

Instantly share code, notes, and snippets.

@tlrobinson
Last active January 3, 2016 13:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tlrobinson/8469121 to your computer and use it in GitHub Desktop.
Save tlrobinson/8469121 to your computer and use it in GitHub Desktop.
Bitcoin blockchain parser. Note this is extremely slow and fails to parse certain blocks. Try https://github.com/znort987/blockparser for a fast blockchain parser.
fs = require "fs"
path = require "path"
binary = require "binary"
BLOCKS_DIR = "/Users/tlrobinson/Library/Application Support/Bitcoin/blocks"
MAGIC = new Buffer([0xf9, 0xbe, 0xb4, 0xd9])
exports.varInt = varInt = (key) ->
@word8lu(key)
@tap (vars) ->
switch vars[key]
when 0xfd then @word16lu(key)
when 0xfe then @word32lu(key)
when 0xff then @word64lu(key)
# Works with Buffers
exports.chainSync = chainSync = ->
@scan("magicID", MAGIC)
@word32lu("blockLength")
block.call @
# Works with Streams
exports.chainAsync = chainAsync = ->
@scan("magicID", MAGIC)
@word32lu("blockLength")
@buffer("block", "blockLength")
@tap (vars) ->
try
block.call(binary.parse(vars.block))
.tap (vars) ->
console.log vars
catch err
console.log "BLOCK FAILED TO PARSE:", err, vars
exports.block = block = ->
@word32lu("version").tap (vars) ->
throw new Error("version="+vars.version) if vars.version isnt 1
@buffer("previous", 32)
@buffer("merkle", 32)
@word32lu("timestamp")
@word32lu("difficulty")
@word32lu("nonce")
varInt.call(@, 'txCount')
txCount = 0
@loop (end, vars) ->
return end() if txCount++ >= vars.txCount
@into "tx#{txCount}", ->
@word32lu("version").tap (vars) ->
throw new Error("txversion="+vars.version) if vars.version isnt 1
inputCount = 0
@word8lu('inputCount')
@loop (end, vars) ->
return end() if inputCount++ >= vars.inputCount
@into "input#{inputCount}", ->
@buffer("txHash", 32)
@word32lu("txIndex")
varInt.call(@, 'scriptLength')
@buffer('script', 'scriptLength')
@word32lu("sequenceNumber")
outputCount = 0
@word8lu('outputCount')
@loop (end, vars) ->
return end() if outputCount++ >= vars.outputCount
@into "output#{outputCount}", ->
@word64lu("value")
varInt.call(@, 'scriptLength')
@buffer('script', 'scriptLength')
@word32lu("txLockTime")
input = fs.createReadStream path.join(BLOCKS_DIR, "blk00000.dat")
parser = binary().loop chainAsync
input.pipe parser
@tlrobinson
Copy link
Author

e.x. genesis block:

{ version: 1,
  previous: <Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>,
  merkle: <Buffer 3b a3 ed fd 7a 7b 12 b2 7a c7 2c 3e 67 76 8f 61 7f c8 1b c3 88 8a 51 32 3a 9f b8 aa 4b 1e 5e 4a>,
  timestamp: 1231006505,
  difficulty: 486604799,
  nonce: 2083236893,
  txCount: 1,
  tx1: 
   { version: 1,
     inputCount: 1,
     input1: 
      { txHash: <Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>,
        txIndex: 4294967295,
        scriptLength: 77,
        script: <Buffer 04 ff ff 00 1d 01 04 45 54 68 65 20 54 69 6d 65 73 20 30 33 2f 4a 61 6e 2f 32 30 30 39 20 43 68 61 6e 63 65 6c 6c 6f 72 20 6f 6e 20 62 72 69 6e 6b 20 6f ...>,
        sequenceNumber: 4294967295 },
     outputCount: 1,
     output1: 
      { value: 5000000000,
        scriptLength: 67,
        script: <Buffer 41 04 67 8a fd b0 fe 55 48 27 19 67 f1 a6 71 30 b7 10 5c d6 a8 28 e0 39 09 a6 79 62 e0 ea 1f 61 de b6 49 f6 bc 3f 4c ef 38 c4 f3 55 04 e5 1e c1 12 de 5c ...> },
     txLockTime: 0 } }

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