Skip to content

Instantly share code, notes, and snippets.

@christianb93
Last active April 2, 2018 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save christianb93/fbb7d6728389fac1faeb7bb92d3ebd22 to your computer and use it in GitHub Desktop.
Save christianb93/fbb7d6728389fac1faeb7bb92d3ebd22 to your computer and use it in GitHub Desktop.
Get a bitcoin block from blockchain.info and calculate its Merkle root
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import sys\n",
"import binascii\n",
"import hashlib\n",
"import requests"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We first put together some utility functions. We need a function to calculate the HASH256 of a sequence of bytes and we need a function to reverse the byte order of a hex string to go forth and back between big endian and little endian representation."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"#\n",
"# The hash function\n",
"#\n",
"def hashFunction(s):\n",
" x = bytes.fromhex(s)\n",
" h = hashlib.sha256(hashlib.sha256(x).digest()).digest()\n",
" return binascii.hexlify(h).decode('ascii')\n",
"\n",
"\n",
"#\n",
"# Reverse the byte order of a hex string\n",
"#\n",
"def reverseByteOrder(s):\n",
" r = \"\"\n",
" for _ in range(len(s)):\n",
" r = s[0:2] + r\n",
" s = s[2:]\n",
" return r"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next we define the functions that will actually calculate the Merkle root. Within that calculation, we do everything in little endian byte order."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"#\n",
"# A function to recursively calculate a Merkle tree\n",
"# Note that this will return the Merkle root in little \n",
"# endian byte order and assume that leaves are provided in\n",
"# little endian byte order as well\n",
"#\n",
"def merkleRoot(leaves):\n",
" if 0 == len(leaves):\n",
" raise ValueError(\"Should not be called with an empty list\")\n",
" #\n",
" # If there is only one leave, we are done\n",
" #\n",
" if 1 == len(leaves):\n",
" return leaves[0]\n",
" #\n",
" # If the number of leaves is odd, append the last element again\n",
" #\n",
" if 1 == (len(leaves) % 2):\n",
" leaves.append(leaves[-1])\n",
" n = []\n",
" for i in range(len(leaves) // 2):\n",
" x = leaves[2*i] + leaves[2*i+1]\n",
" n.append(hashFunction(x))\n",
" return merkleRoot(n)\n",
"\n",
"#\n",
"# Get the Merkle root for a block in JSON format\n",
"#\n",
"def blockMerkleRoot(block):\n",
" leaves=[]\n",
" for _txn in block['tx']:\n",
" leaves.append(reverseByteOrder(_txn['hash']))\n",
" return merkleRoot(leaves)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To actually get a block, we will use the blockchain explorer provided by blockchain.info and the JSON based API that it offers."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"#\n",
"# Get a block from blockchain.info\n",
"#\n",
"def getBlock(blockid):\n",
" url = 'https://blockchain.info/en/block/' + blockid + '?format=json'\n",
" r = requests.get(url)\n",
" return r.json()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"block = getBlock(\"000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd\")"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'bits': 486604799,\n",
" 'block_index': 14851,\n",
" 'fee': 0,\n",
" 'hash': '000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd',\n",
" 'height': 2,\n",
" 'main_chain': True,\n",
" 'mrkl_root': '9b0fc92260312ce44e74ef369f5c66bbb85848f2eddd5a7a1cde251e54ccfdd5',\n",
" 'n_tx': 1,\n",
" 'nonce': 1639830024,\n",
" 'prev_block': '00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048',\n",
" 'size': 215,\n",
" 'time': 1231469744,\n",
" 'tx': [{'hash': '9b0fc92260312ce44e74ef369f5c66bbb85848f2eddd5a7a1cde251e54ccfdd5',\n",
" 'inputs': [{'script': '04ffff001d010b',\n",
" 'sequence': 4294967295,\n",
" 'witness': ''}],\n",
" 'lock_time': 0,\n",
" 'out': [{'addr': '1HLoD9E4SDFFPDiYfNYnkBLQ85Y51J3Zb1',\n",
" 'n': 0,\n",
" 'script': '41047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77ac',\n",
" 'spent': False,\n",
" 'tx_index': 14855,\n",
" 'type': 0,\n",
" 'value': 5000000000}],\n",
" 'relayed_by': '0.0.0.0',\n",
" 'size': 134,\n",
" 'time': 1231469744,\n",
" 'tx_index': 14855,\n",
" 'ver': 1,\n",
" 'vin_sz': 1,\n",
" 'vout_sz': 1,\n",
" 'weight': 536}],\n",
" 'ver': 1}"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"block"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"m = blockMerkleRoot(block)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'d5fdcc541e25de1c7a5addedf24858b8bb665c9f36ef744ee42c316022c90f9b'"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"m"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"assert(reverseByteOrder(m) == block['mrkl_root'])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment