Let's calculate the classic bitcoin (crypto) block hash from scratch (zero).
Let's start with the genesis block, that is block #0
with the unique block hash id 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
.
Note: You can search and browse bitcoin blocks using (online) block explorers. Example:
- blockchain.info/block/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
- blockexplorer.com/block/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
- and others.
The classic bitcoin (crypto) block hash gets calculated from the 80-byte block header:
Field | Size (Bytes) | Comments |
---|---|---|
version | 4 byte | Block version number |
prev | 32 byte | 256-bit hash of the previous block header |
merkleroot | 32 byte | 256-bit hash of all transactions in the block |
time | 4 bytes | Current timestamp as seconds since 1970-01-01 00:00 |
bits | 4 bytes | Current difficulty target in compact binary format |
nonce | 4 bytes | 32-bit number of the (mined) lucky lottery number used once |
Note: 32 byte x 8 bit = 256 bit
Using the data for the genesis block the setup is:
version = 1
prev = '0000000000000000000000000000000000000000000000000000000000000000'
merkleroot = '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'
time = 1231006505
bits = '1d00ffff'
nonce = 2083236893
Remember: To convert from Epoch time (seconds since January 1st, 1970) to classic time use:
Time.at( 1231006505 ).utc
#=> "2009-01-03 18:15:05"
Yes, the bitcoin classic started on January 3rd, 2009 at 18h 15m 5s (2009-01-03 18:15:05). Or in the other direction use:
Time.utc( 2009, 1, 3, 18, 15, 5 ).to_i
#=> 1231006505
What's UTC? Coordinated Universal Time is the "standard" world time. Note: UTC does NOT observe daylight saving time.
Binary Bytes - Little End(ian) vs Big End(ian)
In theory calculating the block hash is as easy as:
## pseudo-code
header = "..." # 80 bytes (binary)
d1 = sha256( header )
d2 = sha256( d1 )
d2.to_s # convert 32-byte (256-bit) binary to hex string
#=> "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
Note: Classic bitcoin uses a double hash, that is,
for even higher security the hash gets hashed twice with the SHA256 algorithm
e.g. sha256(sha256(header))
.
In practice let's deal with the different byte order conversions from big endian (most significant bit first) to little endian (least significant bit first) and back again.
Tip: Read more about Endianness @ Wikipedia.
Let's put together the (binary) 80-byte header using the
int4
and hex32
big-endian to little-endian byte order (to binary bytes)
conversion helpers:
header =
int4( version ) +
hex32( prev ) +
hex32( merkleroot ) +
int4( time ) +
int4( bits.to_i(16) ) +
int4( nonce )
header.size
#=> 80
bin_to_hex( header )
# => "01000000" +
# "0000000000000000000000000000000000000000000000000000000000000000" +
# "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a" +
# "29ab5f49" +
# "ffff001d" +
# "1dac2b7c"
And run the hash calculations:
d1 = Digest::SHA256.digest( header )
d2 = Digest::SHA256.digest( d1 )
bin_to_hex32( d2 )
#=> '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'
Bingo! The resulting block hash is
000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
.
Let's backtrack and add the missing
binary conversion helpers, that is, int4
, hex32
, bin_to_hex32
and bin_to_hex
.
def int4( num ) ## integer 4 byte(32bit) to binary (little endian)
[num].pack( 'V' )
end
def hex32( hex ) ## hex string 32 byte(256bit) / 64 hex chars to binary
[hex].pack( 'H*' ).reverse ## change byte order (w/ reverse)
end
def bin_to_hex32( bytes )
bytes.reverse.unpack( 'H*' )[0] ## note: change byte order (w/ reverse)
end
def bin_to_hex( bytes )
bytes.unpack( 'H*' )[0]
end
To convert integers (4 bytes / 32 bit) to binary bytes (in little endian) use:
int4( version )
#=> "\x01\x00\x00\x00"
bin_to_hex( int4( version ))
#=> "01000000"
compare to "classic" hex string (in big endian):
pp "%08x" % version
#=> "00000001"
What's better? Big-endian 00000001
or little-endian 01000000
?
What's better? Ruby or Python? Red or Blue? Bitshilling or Bitcoin?
Let's celebrate that there's more than one way to do it :-). Onwards.
To convert a hex string (32 byte / 256 bit / 64 hex chars) to binary bytes (in little endian) use:
hex32( merkleroot )
#=> ";\xA3\xED\xFDz{\x12\xB2z\xC7,>gv\x8Fa\x7F\xC8\e\xC3\x88\x8A..."
bin_to_hex( hex32( merkleroot ))
#=> "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"
and to convert back from binary bytes (in little endian) to a hex string use:
bin_to_hex32( hex32( merkleroot )) # to little-endian and binary and back again
#=> "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
What's better? Big-endian 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
or little-endian 3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a
?
What's that pack
/unpack
magic?
See the ruby documentation
for Array#pack
and String#unpack
for the binary data packing and unpacking machinery
To sum up all together now. Let's use the Block #125552 used as a sample in the Bitcoin Block hashing algorithm wiki page:
version = 1
prev = '00000000000008a3a41b85b8b29ad444def299fee21793cd8b9e567eab02cd81'
merkleroot = '2b12fcf1b09288fcaff797d71e950e71ae42b91e8bdb2304758dfcffc2b620e3'
time = 1305998791 ## 2011-05-21 17:26:31
bits = '1a44b9f2'
nonce = 2504433986
header = int4( version ) +
hex32( prev ) +
hex32( merkleroot ) +
int4( time ) +
int4( bits.to_i(16) ) +
int4( nonce )
d1 = Digest::SHA256.digest( header )
d2 = Digest::SHA256.digest( d1 )
bin_to_hex32( d2 )
#=> "00000000000000001e8d6829a8a21adc5d38d0a473b144b6765798e61f98bd1d"
Bonus. For easy (re)use let's package up the bitcoin block header code into a class:
require 'digest'
module Bitcoin
class Header
attr_reader :version
attr_reader :prev
attr_reader :merkleroot
attr_reader :time
attr_reader :bits
attr_reader :nonce
def initialize( version, prev, merkleroot, time, bits, nonce )
@version = version
@prev = prev
@merkleroot = merkleroot
@time = time
@bits = bits
@nonce = nonce
end
## lets add a convenience c'tor helper
def self.from_hash( h )
new( h[:version],
h[:prev],
h[:merkleroot],
h[:time],
h[:bits],
h[:nonce] )
end
def to_bin
i4( version ) +
h32( prev ) +
h32( merkleroot ) +
i4( time ) +
i4( bits.to_i(16) ) +
i4( nonce )
end
def hash
bytes = sha256(sha256( to_bin ))
bin_to_h32( bytes )
end
def sha256( bytes )
Digest::SHA256.digest( bytes )
end
## binary pack/unpack conversion helpers
def i4( num ) ## integer (4 byte / 32bit) to binary (in little endian)
[num].pack( 'V' )
end
def h32( hex ) ## hex string (32 byte / 256 bit / 64 hex chars) to binary
[hex].pack( 'H*' ).reverse ## change byte order (w/ reverse)
end
def bin_to_h32( bytes )
bytes.reverse.unpack( 'H*' )[0] ## note: change byte order (w/ reverse)
end
end # class Header
end # module Bitcoin
and let's test drive it with the genesis block #0 and block #125552:
b0 =
Bitcoin::Header.from_hash(
version: 1,
prev: '0000000000000000000000000000000000000000000000000000000000000000',
merkleroot: '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b',
time: 1231006505,
bits: '1d00ffff',
nonce: 2083236893 )
b0.hash
#=> "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
b125552 =
Bitcoin::Header.from_hash(
version: 1,
prev: '00000000000008a3a41b85b8b29ad444def299fee21793cd8b9e567eab02cd81',
merkleroot: '2b12fcf1b09288fcaff797d71e950e71ae42b91e8bdb2304758dfcffc2b620e3',
time: 1305998791,
bits: '1a44b9f2',
nonce: 2504433986 )
b125552.hash
#=> "00000000000000001e8d6829a8a21adc5d38d0a473b144b6765798e61f98bd1d"