CH

September 11, 2016

Extracting and Hashing Bitcoin Block Headers

Filed under: bitcoin, common lisp — Benjamin Vulpes @ 12:28 a.m.

Presented herein: some code for deserializing, reserializing and hashing Bitcoin block headers. This work leans heavily onĀ Frode Vatvedt Fjeld's binary-types and Nathan Froyd's ironclad Common Lisp libraries, and would have taken oodles of time longer without John Ratcliff's How to Parse the Bitcoin BlockChain (sic).

Over a year ago, Stan suggested that I use binary-types to do something similar to his blkcut utility. Some time after that, he patched TRB, providing a handle to dump an arbitrary block on demand. The project to which the below code belongs, and which leans on the block dumper dumpblock patch is still downright larval, but the block header parsing is baked enough to share.

First, the header definition:

(binary-types:define-binary-class bitcoin-block-header ()
  ((version :binary-type binary-types:u32 :accessor version)
   (previous-block-hash :binary-type binary-types:u256 :accessor previous-block-hash)
   (merkle-root :binary-type binary-types:u256 :accessor merkle-root)
   (time-stamp :binary-type binary-types:u32 :accessor time-stamp)
   (target :binary-type binary-types:u32 :accessor target)
   (nonce :binary-type binary-types:u32 :accessor nonce)))

The double hash:

(defmethod double-sha256 ((header bitcoin-block-header))
  (binary-types:with-binary-output-to-vector
      (header-bytes (make-array 80 :fill-pointer 0 :element-type '(unsigned-byte 8)))
    (let ((binary-types:*endian* :little-endian))
      (binary-types:write-binary 'bitcoin-block-header header-bytes header)
      (reverse
       (ironclad:digest-sequence
        :sha256
        (ironclad:digest-sequence :sha256 header-bytes))))))

I am utterly pleased with binary-types. Writing the binary-class definition for the header is adequate to prime the pump for both ingest and egress using read-binary and write-binary.

And a demonstration (ripped verbatim from the test suite):

(stefil:deftest can-hash-headers ()
  (binary-types:with-binary-file
      (stream (merge-pathnames #p "blocks/0.bin" (uiop:getcwd)) :direction :input)
    (let* ((binary-types:*endian* :little-endian)
           (header (binary-types:read-binary 'bitcoin-block-header stream))
           (expected "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")
           (actual (ironclad:byte-array-to-hex-string (double-sha256 header))))
      (stefil:is (string= expected actual)))))

Called at the REPL:

(can-hash-headers)
.
T
#

No Comments »

No comments yet.

RSS feed for comments on this post. TrackBack URL

Reply

« veh patch: overall improvements --- CORRECTION: multiple channel patches for irc/logbot »