Riding blockchain

Andrei Zhozhin

| 9 minutes

It is very hard to ignore blockchain these days as it is a core for many coll technologies these days (starting from digital currencies like Bitcoin and Etherium finishing by NFT for artwork).

Let’s look at Etherium blockchain as one of the major competitors and try to write some code to understand how to work with it programmatically.

Basics

Blockchain

For simplicity, Blockchain is a sequence of blocks (that can hold any data) that are linked together via hashes.

Chain of blocks

The next block prevHash is the same as the current block hash. This structure protects blockchain from amendment because if the attacker change data in one block all consequent blocks would be invalidated as all hashes of all blocks would not match.

There are many properties (we will see all of them later) of the block but here we need to understand only a few to get an idea of how the whole thing works:

  • number - block number, should be greater than previous
  • parentHash - value of hash property from previous block. 0x0 for genesis block as there are no previous.
  • nonce - a number used once, a number that participates in mining process to find hash smaller than required (defined by difficulty).
  • hash - a hash value of the whole block
  • transactions

Hash

Hash is one of the cornerstones of cryptography. This is a one-way function that takes the input array (could be very big) of data and return a short array (fixed size) of numbers that represents the input array. You can think about it as a data fingerprint that is “unique” (it is almost impossible to find two data sets that have the same hash). As the function is one way there is no possibility to restore original data using hash function value.

Hash function

There are many types of hash functions, some of them might be known like MD5, SHA-1, SHA-256, etc. In Etherium blockchain Keccak-256 hash function is used to calculate hashes.

Here is a table of different hashes for the same input text (Hello World!):

Hash function Value
MD5 ed076287532e86365e841e92bfc50d8c
SHA-1 2ef7bde608ce5404e97d5f042f95f89f1c232871
SHA-256 7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069
Keccak-256 3ea2f1d0abf3fc66cf29eebb70cbd4e7fe762ef8a09bcc06c8edf641230afec0

Mining

To “mine” a block you need to find a valid hash for the block, but it is not that trivial. There is difficulty in the process that preventing finding that hash quickly - defined constrain for the hash value - it should be less than defined target. The difficulty is used to calculate the target.

All information in the block is “static” and there is only one property that the miner can change: nonce. So the process looks the following (starting with nonce = 0):

  • calculate hash of the block using a current nonce value
  • check if hash below target (if it is block is “mined”)
  • if it is not - nonce = nonce + 1 and repeat from first step

There is also a timestamp property in the block, so the goal of the miner is to find nonce within 1 second, otherwise, the timestamp would increment and the whole process would need to be restarted from nonce = 0.

We would avoid real mining for our development purposes and our blockchain is running with difficulty = 0

Run local blockchain

For our experiments, we would run the local blockchain. To do that we would use Ganache. You can download it from official site. After installation, you need to run it and select “Quickstart (Etherium)” to get the new local network up and running.

Ganache local blockchain

So, now we have a blockchain in the following state:

  • current block: 0 - as we just started we don’t have any transactions yet, but the network initialized with some start accounts and balances (this is called genesis block)
  • we have 10 accounts with starting balances 100.00 ETH

Now we can start to do something with this network.

Connecting to etherium blockchain

I would be using python library web3.py to interact with etherium blockchain but there is also popular web3.js for javascript that could work in browser and would be a tool of choice for web apps.

Let’s install it and write some code to connect to the network

pip install web3

This code could connect to local blockchain on http://127.0.0.1:7545 and get information about block 0.

from web3 import Web3
w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:7545'))

print(w3.isConnected())
block = w3.eth.get_block('latest')
print(block)

The output of the code above (I’ve edited it a little bit to remove some clutter)

True
AttributeDict({
  'number': 0, 
  'hash': HexBytes('0x5785...65ff'), 
  'parentHash': HexBytes('0x00...00'), 
  'mixHash': HexBytes('0x00...00'), 
  'nonce': HexBytes('0x0000000000000000'), 
  'sha3Uncles': HexBytes('0x1dcc...9347'), 
  'logsBloom': HexBytes('0x00...00'), 
  'transactionsRoot': HexBytes('0x56e8...b421'), 
  'stateRoot': HexBytes('0x14aa...c546'), 
  'receiptsRoot': HexBytes('0x56e8...b421'), 
  'miner': '0x00...00', 
  'difficulty': 0, 
  'totalDifficulty': 0, 
  'extraData': HexBytes('0x'), 
  'size': 1000, 
  'gasLimit': 6721975, 
  'gasUsed': 0, 
  'timestamp': 1634397979, 
  'transactions': [], 'uncles': []
})

Please note that difficulty and totalDifficulty are 0(zeros) as we are running a local development blockchain and we don’t want to waste time on finding a valid hash. We just calculate hash with nonce equal to zero and we are done.

Let’s try to read balances for our pre-populated accounts (just couple of them):

account0 = w3.eth.accounts[0]
balance0 = w3.eth.get_balance(account0)
print(f"Account0 add: {account0}, balance: {balance0}")
account1 = w3.eth.accounts[1]
balance1 = w3.eth.get_balance(account1)
print(f"Account1 add: {account1}, balance: {balance1}")

Output

Account0 addr: 0x2e3162B333b3739e7B8aA0EBB704D0Af39F52E28, balance: 100000000000000000000
Account1 addr: 0xaf85CAE5dE1c432c60F69689f0f80F43e385E008, balance: 100000000000000000000

You might ask why UI shows balance as 100.00 ETH but here we get the HUGE number 100000000000000000000, this is because 1 ETH = 10^18 Wei, Wei is the smallest piece of ETH, so we see balance as is, but UI convert Wei to ETH to show it in the nice form to the user.

An important note here: as blockchain is “readable” by anyone you can check the balance of any account, but because you don’t know the association of accounts and people your privacy is not disclosed.

Transactions

Having some accounts with some ether is good but we need to do transactions to get the benefit of the network. To send ether from the account we need to own it - in terms of blockchain, it means owning a private key to be able to sign transactions and validate ownership.

In Ganache you can click on the key icon next to the account to see the private key.

Private key for account0

Now when we know private key we can create account from code to be able to sign. Etherium address could be extracted from private key, so if we would create local account with private key of account0 we can see it’s address

private_key0 = "008755021c8870b213a3e2ae908f85460c1955e0e660f5594fe0fa2c5e4c6170"
acct0 = w3.eth.account.privateKeyToAccount(private_key0)
print(acct0.address)

Output (it is matched to UI and we haven’t specified it explicitly in the code)

0x2e3162B333b3739e7B8aA0EBB704D0Af39F52E28

Now let’s create, sign, and send transaction: move 1 ETH from account0 to account1

amount = 1 # this is amount in ETH, it would be converted to Wei
address_to = '0xaf85CAE5dE1c432c60F69689f0f80F43e385E008' # account1
nonce = w3.eth.getTransactionCount(acct0.address)
tx = {
  'from': acct0.address,
  'to': address_to,
  'value': w3.toWei(amount, 'ether'),
  'gas': 21000,
  'gasPrice': w3.eth.gas_price,
  'nonce': nonce,
  'chainId': 1337
}
signed_tx = w3.eth.account.signTransaction(tx, private_key=private_key0)
print (signed_tx)
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"Acct0 balance: {w3.eth.get_balance(w3.eth.accounts[0])}")
print(f"Acct1 balance: {w3.eth.get_balance(w3.eth.accounts[1])}")

A signed transaction would look like the following:

SignedTransaction(
  rawTransaction=HexBytes('0xf86e808504a817........adb009cf'), 
  hash=HexBytes('0x1e52fc8133c2200c36a80218887d14371349d02f910a91a0d78ab7f35de0cbaa'),
  r=106121795418760483923630680996704157212173332009377596846518426889201626712370, 
  s=38161356266923782681465688369360602713211657523927194070106774189916816411087, 
  v=2710
)

There are three important properties r, s, and v - these are values of transaction signature. Values r and s are outputs of an ECDSA (Elliptical Curve Digital Signature Algorithm) and v is the recovery id.

Balances after transaction execution:

Acct0 balance: 98999580000000000000
Acct1 balance: 101000000000000000000

You may notice that the account balance for Account1 is exactly 101 ETH, but the balance for Account0 is less than 99 ETH as you would expect. We have value 98.99958 ETH as every transaction has fee, the fee is calculated based on the amount of gas consumed by transaction and gas price.

Gas is the number of operations that Etherium Virtual Machine has performed to execute the transaction, and we have associated gas price that was charged from the sender of the transaction to pay for it.

Transfer transaction consumed 21,000 gas (with gas price: 20,000,000,000 Wei), so total we have 420,000,000,000,000 Wei or 0.00042 ETH and now our transaction balanced.

Our first transaction was included in block1 here is how it looks like in UI:

Block1 with our first transaction

Getting information for our transaction programmatically

tx_hash = '0x1e52fc8133c2200c36a80218887d14371349d02f910a91a0d78ab7f35de0cbaa'
tx = w3.eth.getTransaction(tx_hash)
print(tx)

Output with transaction details

AttributeDict({
  'hash': HexBytes('0x1e52fc8133c2200c36a80218887d14371349d02f910a91a0d78ab7f35de0cbaa'), 
  'nonce': 0, 
  'blockHash': 
    HexBytes('0xd81ebff41bc43eba00a66342dc5f6cf3446baa1c4371a6fe22ecfdbe6a1ab663'), 
  'blockNumber': 1, 
  'transactionIndex': 0, 
  'from': '0x2e3162B333b3739e7B8aA0EBB704D0Af39F52E28', 
  'to': '0xaf85CAE5dE1c432c60F69689f0f80F43e385E008', 
  'value': 1000000000000000000, 
  'gas': 21000, 
  'gasPrice': 20000000000, 
  'input': '0x', 
  'v': 2710, 
  'r': HexBytes('0xea9ecec2e48084cb143580cab3165badc7aaa3f34dc5127e7dfde5d2ba810932'), 
  's': HexBytes('0x545e8fedd6a6687d8afd3fb7d0b48084a71abfbc09f01d307ada2ba9adb009cf')
})

Gas cost per operations

Every operation has its own gas price and here are some of them:

Operation Gas Description
ADD/SUB 3 Arithmetic op
MUL/DIV 5 Arithmetic op
POP 2 Stack op
PUSH 3 Stack op
BALANCE 400 Get balance of account
CREATE 32,000 Create new account

Standard price for transfer is 21000 units of gas.

As every transaction has gasLimit property set to particular value (finite), it is protecting whole network from code that works infinitely as every operation consume some amount and when amount reach zero transaction is aborted.

Exploring mainnet

We were playing with our local blockchain now we can have a look on the real blockchain with real money. There are several official networks in Etherium:

  • Mainnet - main production network (Proof of work)
  • Testnets
    • Görli - Proof of authority testnet
    • Kovan - Proof of authority testnet (OpenEtherium client)
    • Rinkeby - Proof of authority testnet (Geth client)
    • Ropsten - Proof of work testnet

To get access to the blockchain you either need to run a blockchain node (full or light), or you can use the gateway. Let’s use one of the gateways to access Etherium Mainnet - Infura. Registration takes less than 1 min and you can create your project and credentials. Infura create project

Click “Create Project” and enter the name of your project. And you’ll see the connection details.

Infura test project details

Once we have a project we have an endpoint URL that we can use with the web3 client to get the last block details.

from web3 import Web3

w3 = Web3(Web3.HTTPProvider("https://mainnet.infura.io/v3/7f0bXXXXXXXXXXXXXXXXXXXXXXXX8def"))
print(w3.isConnected())
block = w3.eth.get_block('latest')
print(block)

The latest block in the mainnet (at the time of execution):

AttributeDict({
  'baseFeePerGas': 72596235616, 
  'difficulty': 9657477492851337, 
  'extraData': HexBytes('0xe4b883e5bda9e7a59ee4bb99e9b1bc3d0a22'), 
  'gasLimit': 30058619, 
  'gasUsed': 17895327, 
  'hash': HexBytes('0x329cdcd9390bcf0918390a140ea8c7078cdc731d84a49715a065d7d8cc402b25'), 
  'logsBloom': HexBytes('0x982b59...2104b2'), 
  'miner': '0x829BD824B016326A401d083B33D092293333A830', 
  'mixHash': HexBytes('0xbaed2628927b1c463b6a699e974227965d324cd754fe758f2f05a1968df80dd4'), 
  'nonce': HexBytes('0x4b396a138872722b'), 
  'number': 13431334, 
  'parentHash': 
    HexBytes('0xe3d747c5c6611fb05bd0fa4f55dc623f1d6838875e5877d40fcbc07aeed51f6f'), 
  'receiptsRoot': 
    HexBytes('0x11818ef5a019097686d50a8b61d559d7eef1c083ba79e59380e1a144762e897d'), 
  'sha3Uncles': 
    HexBytes('0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347'), 
  'size': 125737, 
  'stateRoot': 
    HexBytes('0x85b790c15a6ba568bf541f39dcafdab7631f56479f16a5fc38e873441f986cc4'), 
  'timestamp': 1634418143, 
  'totalDifficulty': 32541291813132683718384, 
  'transactions': [
    HexBytes('0xfa40b1a5207ebaa3f54fc910272595da515016a4ddc76806853eecc10d9fd78c'), 
    HexBytes('0x188da7edcebe2cd486cefa3b2344b02ff968294ac34786b0d2b85b329b43e50e'), 
    HexBytes('0x0eff5a78ef28c4343e436f1b82ff9220f87922159c90ea1728a4101ef21c80ac'), 
    ...], 
'transactionsRoot': 
  HexBytes('0x99be96af7c2e3bc4d1a25b04a3f92d204d3bd5b2925de774af14750fb9b91628'), 
'uncles': []})

Now you can explore real networks programmatically and transfer ether.

Summary

We have installed local blockchain, got some (10) accounts for free to play with, wrote some code to read balance, send a transaction, and read information transactions. We also connected to production Etherium mainnet and got information about the latest block.

In the next article, I’ll cover smart contracts and tokens. Stay tuned.

Related content

Elliptic curve cryptography
Resume as code