Last active
April 2, 2018 14:21
-
-
Save christianb93/fbb7d6728389fac1faeb7bb92d3ebd22 to your computer and use it in GitHub Desktop.
Get a bitcoin block from blockchain.info and calculate its Merkle root
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
{ | |
"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