About the author:
Andrey Makovenko is a Ruby Developer at Syndicode with a strong Blockchain/Cryptocurrency engineering background. He is passionate about all the layers of software: backend, frontend, and operations, but mostly focused on backend development.
What is blockchain?
Blockchain (by its idea) is a decentralized distributed digital network(chain) used for storing the information(blocks) safely. It can be used in many ways, but in this article, we’ll cover the blockchain of Bitcoin.
What is Bitcoin?
Bitcoin is a cryptocurrency. Bitcoin transactions are cryptographically verified and saved by blockchain.
Why would you need to monitor the blockchain?
You need to monitor blockchain if you want to analyze it for some reason (e.g., money movement) or if you want to receive/send bitcoin payments (deposits and withdraws) in your application.
Let’s begin!
We need to install bitcoin-core and synchronize it with testnet blockchain, run:
➜ bitcoind -testnet -server -rpcuser=user -rpcpassword=password
(It requires authentication, so I set “user” and “password” for example)
It will start synchronizing your local blockchain node. Currently (May 4, 2020) it requires 26 Gb of disk space for testnet* and around 127 Gb of disk space for mainnet**).
*testnet is like a staging server of blockchain. As a software, it is identical to the original blockchain, but it is widely used for testing new software and ideas without having a potential problem of losing “real” bitcoins.
**mainnet is like a production of blockchain. It is the original software used by Bitcoin.
We’ll start with a simple “daemon” script doing some cyclic work:
# monitoring logic goes here
class Monitor
def self.start!
# continuosly do the same work with 5 seconds pause
loop do
puts 'awake...'
# do something
puts 'sleep...'
sleep(5)
end
end
end
Monitor.start!
Let’s try to start it and see what happens:
➜ ruby bitcoin_monitor.rb
awake...
sleep...
awake...
sleep...
awake...
sleep...
…
Cool! It runs the same code with 5 seconds pause just like we wanted!
Let’s add some basic logic to communicate with “bitcoind”:
# Bitcoin module
module Bitcoin
# Bitcoin blockchain interaction
class Blockchain
def initialize
# connection to the bitcoind JSON rpc server
@connection = Faraday.new('http://127.0.0.1:18332/').tap do |connection|
# authentication variables we declared when starting bitcoind
connection.basic_auth('user', 'password')
end
end
# get latest block number
def latest_block
rpc_call(:getblockcount)
end
private
# json rpc call wrapper
def rpc_call(method, params = [])
response = @connection.post do |req|
req.body = { jsonrpc: '1.0', method: method, params: params }.to_json
end
JSON.parse(response.body)['result']
end
end
end
NOTICE: In real application you should not set credentials directly in the code, better is to set them as environment variables. Also, they should me much stronger than ‘user’ and ‘password’.
We’ve added a Bitcoin::Blockchain class, basic JSON RPC communication logic and a “latest_block” method. It will be used to get the latest block number in the blockchain.
Let’s call it from Monitor class and print to the screen:
class Monitor
def self.start!
# define a Bitcoin::Blockchain class instance
blockchain = Bitcoin::Blockchain.new
# continuosly do the same work with 5 seconds pause
loop do
puts 'awake...'
puts blockchain.latest_block
puts 'sleep...'
sleep(5)
end
end
end
Let’s run it again!
...
awake...
1722237
sleep...
awake...
1722238
sleep...
awake...
1722238
sleep…
...
Awesome!! Now we are getting the latest block number from Bitcoin blockchain every 5 seconds and already can monitor when new blocks are added.
Let’s change our Monitor class code this way:
# monitoring logic goes here
class Monitor
def initialize
@latest_block = nil
end
def start!
# define a Bitcoin::Blockchain class instance
@blockchain = Bitcoin::Blockchain.new
@latest_block = nil
# continuously lookup for new blocks with 5 seconds pause
loop do
puts 'awake...'
# check if current @latest_block is blockchain_latest_block
if latest_block?(@blockchain.latest_block_number)
puts 'No new blocks found'
puts 'sleep...'
sleep(5)
next
end
# print the new block number without any pause
puts "New block found: #{@latest_block}"
end
end
# a method to check if provided block is the latest in local blockchain
def latest_block?(blockchain_latest_block)
return true if @latest_block == blockchain_latest_block
@latest_block = @blockchain.latest_block_number
false
end
end
Alright, now we only print the new block number and have no 5 seconds pause for new blocks.
So how do we get some useful information from Bitcoin blockchain?
Let’s add this new method to our Bitcoin::Blockchain class:
# gets a block with a particular header hash
def get_block(block_number)
# returns the header hash of a block at the given height
block_hash = rpc_call(:getblockhash, [block_number])
rpc_call(:getblock, [block_hash, 2])
end
This will return us all the information about a block at a given height(or block number as we named it before). It consists of many fields, but most useful for us are transactions(‘tx’), so let’s print their information for each new block appearing in blockchain(all new transactions are added to the blockchain with new blocks).
Let’s add this code to “Monitor” class:
# a wrapper to extract transactions information
def block_transactions(transactions)
return nil if transactions.empty?
transactions_formatted = []
transactions.each do |transaction|
transaction['vout'].each do |subtransaction|
tx_formatted = {}
next if subtransaction['scriptPubKey']['addresses'].nil?
tx_formatted[:amount] = subtransaction['value']
tx_formatted[:receiver] = subtransaction['scriptPubKey']['addresses'][0]
transactions_formatted << tx_formatted
end
end
transactions_formatted
end
And call it at the end of our loop in “.start!” method:
# print block transactions
puts' Block transactions:'
puts block_transactions(@blockchain.get_block(@latest_block)['tx'])
And let’s rerun our script:
➜ blockchain_monitoring ruby bitcoin_monitor.rb
awake...
"New block found: 1722894"
[{:amount=>0.19637722, :receiver=>["2N5oMBJB1mmNnr3UrQrEzazXBqwagRAs8vG"]},
{:amount=>1.0e-05, :receiver=>["2N2jYboVb42pY2L8o7Md48agKpZQocfKRg4"]},
{:amount=>0.13315513, :receiver=>["mupisbLSNHtzqcYeDpZKU6L12q7nE4Mhez"]},
{:amount=>0.0064, :receiver=>["mq3Qjwc4N6EstUVaSviu1nnAv8L8uExtof"]},
{:amount=>5.1e-05, :receiver=>["mk8aUY9DgFMx7VfDck5oQ7FjJNhn8u3snP"]},
{:amount=>0.00038238, :receiver=>["msRFQV9qwQr4R1h3PfVudvT6cTce7EhZnz"]},
{:amount=>0.00025098, :receiver=>["2NFZqS93SaZJYPcSpERDEpTcQkDVSGMeUXg"]},
...
That’s it! Now we’re aware about all new transactions appearing in blockchain.
To sum up
As you have already seen, it is pretty easy to communicate with blockchain and monitor it. The only problem could be a disk space required for the mainnet (the best would be to have it stored in an SSD).
What can you do with this information? It depends on your needs… You can analyze the “money” movement across the blockchain, daily, weekly and monthly volumes, etc.
It can also be used as a part of the code to receive bitcoin payments in your application, but we will cover this topic more in-depth in the next articles.