Get BCH Node Info from Your Browser in Json Format Using Python (RPC API)

2 90
Avatar for ClearSky
1 year ago

Exposing your node RPC to the internet is discouraged, however we still can use it locally when developing to make our life easier. In this tutorial I'll demonstrate how to get some information from a Bitcoin Cash node using Python and A minimal web framework called Cherrpy.

Prerequisites

  • Bitcoin Cash full node, (testnet4) can do ~40MB

  • Python

  • Pipenv

    This utorial assume you have Ubuntu Linux, though you can adapt it to your OS as it's only Python stuff.

Preparing for Development

Let us create a directory to host our project in and get inside it:

mkdir blockchain_project && cd blockchain_project

Creating a virtual environment

Let us create a virtual environment using pipenv to make things cleaner and easier:

pipenv shell

Pipenv will create a new virtual environment that is isolated from other python stuff in our system so our work is contained in it.

Installing Cherrypy

Cherrypy is a minimal python web framework we install it by doing:

pipenv install cherrypy

Setting the full node

If you don't have RPC already enabled you should enable it.

Usually node settings resides in a file called bitcoin.conf ,The default location for configuration file is ~/.bitcoin/bitcoin.conf in linux, for other systems check bitcoin wiki related section.

It should contain something like:

txindex=1
server=1
whitelist=127.0.0.1
rpcuser=localuser
rpcpassword=your_rpc_password
testnet4=1
rpcallowip=127.0.0.1/0

[test4]
port=29333
rpcport=29332

Here we inform the node to:

  1. Enable indexing (optimal)

  2. Enable RPC server

  3. Set RPC user name

  4. Set RPC password

  5. Enable testnet 4

  6. In test4 section we specify testnet4 RPC port

Restart your full node software after modifying the configurations.

Checking RPC

RPC should be enabled for our program to work. We check that our node configuration file has RPC enabled and that is active using curl

curl --silent --user localuser --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getblockchaininfo", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:29332 | jq 

Should ask for your RPC password:

Enter host password for user 'localuser':

Then should show what looks like this:

{
  "result": {
    "chain": "test4",
    "blocks": 112704,
    "headers": 112704,
    "bestblockhash": "0000000012b46b0383471ca3fbd2bd7a97293312d55e471f2e4dc5e728cfc1b3",
    "difficulty": 4.31893188237608,
    "mediantime": 1662957827,
    "verificationprogress": 0.9999998822062223,
    "initialblockdownload": false,
    "chainwork": "0000000000000000000000000000000000000000000000000161bd16c51e437a",
    "size_on_disk": 36726279,
    "pruned": false,
    "warnings": ""
  },
  "error": null,
  "id": "curltest"
}

This seems like a successful which indicates that our RPC server is ready.

Apparently here we used localuser as RPC user but you should change it to your RPC user and also should change RPC URL from http://127.0.0.1:29332 to your own if you used a different configurations.

Writing our App

Now let us copy the following text and save it in a file called blockchain_app.py :

import cherrypy # Imported the web framework
import requests # Library to communicate with RPC
import json # Library to parse and form Json


# Variables to access RPC
url = 'http://localhost:29332/'
rpc_user='localuser'
rpc_password='your_rpc_password'


# Function to call RPC with a provided method
# Example: If we provided `getblockchaininfo` it will request that method data 
# from the RPC. We can also request many other methods like `getbalance` or 
# others available in our RPC server

def call_rpc(method):
    """
    Function to call RPC with a provided method
    """
    # Construct payload from provided method in json format 
    payload = json.dumps({"method": method}) # have the method in 
    # Headers to be provided in requests function query
    headers = {'content-type': 'application/json', 'cache-control': 'no-cache'}
    # log headers using cherrypy log, useful for debuging
    cherrypy.log(f'headers: {headers}')
    # Try to call RPC and show exceptions if there is an issue
    try:
        response = requests.request(
            "POST",
            url,
            data=payload,
            headers=headers,
            auth=(rpc_user, rpc_password)
            )
        # The function will return the response of the request in json format
        return json.loads(response.text)
    # If there is an issue related to Request it will report request issue
    except requests.exceptions.RequestException as e:
        cherrypy.log(f"Request issue: {e}")
    # If there is an issue it will report it
    except Exception as e:
        cherrypy.log(f"Error: {e}")


# Cherrpy App class
class App(object):
    # Exposing index function to the web
    @cherrypy.expose
    # Set the index page function to be a json output page
    @cherrypy.tools.json_out()
    def index(self):
        # Run the call_rpc function with `getblockchaininfo` method
        return call_rpc('getblockchaininfo')      

# Run the App
cherrypy.quickstart(App(), '/')

Both sections should be in the same file, I put it separate to fix read.cash syntax highlighting issue. As you may notice the file contains comments that explain each part of the code.

How the App Work

We basically imported the needed modules like cherrypy and setup the variables that contains RPC stuff like url, username and password.

import cherrypy # Imported the web framework
import requests # Library to communicate with RPC
import json # Library to parse and form Json


# Variables to access RPC
url = 'http://localhost:29332/'
rpc_user='localuser'
rpc_password='your_rpc_password'

Then we created a function called call_rpc that will take the method required like getblockchaininfo and trying to deliver it to the RPC server while handling errors with python exceptions.

def call_rpc(method):
    """
    Function to call RPC with a provided method
    """
    # Construct payload from provided method in json format 
    payload = json.dumps({"method": method}) # have the method in 
    # Headers to be provided in requests function query
    headers = {'content-type': 'application/json', 'cache-control': 'no-cache'}
    # log headers using cherrypy log, useful for debuging
    cherrypy.log(f'headers: {headers}')
    # Try to call RPC and show exceptions if there is an issue
    try:
        response = requests.request(
            "POST",
            url,
            data=payload,
            headers=headers,
            auth=(rpc_user, rpc_password)
            )
        # The function will return the response of the request in json format
        return json.loads(response.text)
    # If there is an issue related to Request it will report request issue
    except requests.exceptions.RequestException as e:
        cherrypy.log(f"Request issue: {e}")
    # If there is an issue it will report it
    except Exception as e:
        cherrypy.log(f"Error: {e}")

Then we have the Cherrpy section where we expose functions that call the call_rpc function to obtain some data and return it in Json format

# Cherrpy App class
class App(object):
    # Exposing index function to the web
    @cherrypy.expose
    # Set the index page function to be a json output page
    @cherrypy.tools.json_out()
    def index(self):
        # Run the call_rpc function with `getblockchaininfo` method
        return call_rpc('getblockchaininfo')      

# Run the App
cherrypy.quickstart(App(), '/')

That is it.

Running the App

Let us run it from the shell:

python3 blockchain_app.py

We should see output like this:

[13/Sep/2022:11:39:32] ENGINE Listening for SIGTERM.
[13/Sep/2022:11:39:32] ENGINE Listening for SIGHUP.
[13/Sep/2022:11:39:32] ENGINE Listening for SIGUSR1.
[13/Sep/2022:11:39:32] ENGINE Bus STARTING
CherryPy Checker:
The Application mounted at '' has an empty config.

[13/Sep/2022:11:39:32] ENGINE Started monitor thread 'Autoreloader'.
[13/Sep/2022:11:39:32] ENGINE Serving on http://127.0.0.1:8080
[13/Sep/2022:11:39:32] ENGINE Bus STARTED

Where http://127.0.0.1:8080 is the address you can visit to get see our Web app from the browser on the same machine.

If you open the link you may see something like this:

So we now exposed part of internal RPC to the web and things seems to work.

More end points

This was a minimal application. You can see more advanced version with other endpoints on the fly by just almost copy paste, for example you can add the following inside the App class:

    @cherrypy.expose
    @cherrypy.tools.json_out()
    def getblockchaininfo(self):
        return call_rpc('getblockchaininfo')

    @cherrypy.expose
    @cherrypy.tools.json_out()
    def listtransactions(self):
        return call_rpc('listtransactions')

    @cherrypy.expose
    @cherrypy.tools.json_out()
    def getbalance(self):
        return call_rpc('getbalance')

    @cherrypy.expose
    @cherrypy.tools.json_out()
    def getmininginfo(self):
        return call_rpc('getmininginfo')
        
    @cherrypy.expose
    @cherrypy.tools.json_out()
    def getpeerinfo(self):
        return call_rpc('getpeerinfo')
        
    @cherrypy.expose
    @cherrypy.tools.json_out()
    def getnewaddress(self):
        return call_rpc('getnewaddress')

you can call it by visiting a URL with the function name like:

You can find a list of RPC commands in Bitcoin Cash Node (BCHN) Documentations:

https://docs.bitcoincashnode.org/doc/json-rpc/

Change Port or Allow Access on All Interfaces

You can change the default port for the app from 8080 to 8081 by having:

cherrypy.config.update({'server.socket_port': 8081})

Just before the cherrypy.quickstart(App(), '/') section.

Also you can change the app to listen to request not just from 127.0.0.1 by having:

cherrypy.server.socket_host = '192.168.1.250'


Also above the cherrpy.quickstart section. You may set the IP to 0.0.0.0 to listen to all interfaces.

Full App Code

A snippet contains the full app code is on this Gitlab gist:

https://gitlab.com/-/snippets/2407788

This Tutorial

This tutorial is part of my work to fulfill my commitment to BCH community to write some Python guides in part of my Flipstarter pledge.

Appreciate the community support that allowed my after my creator blessing to do this.

3
$ 6.00
$ 5.00 from @Marser
$ 1.00 from @btcfork
Avatar for ClearSky
1 year ago

Comments

I'm not a developer either, but I believe that there are various tools associated with programming languages that can be useful for all people, not just specialists. If you don't know anything about JSON, it's at least worth Googling it because it's a reliable way to store and share files. Using various tools, you can convert text from a JSON file to a String, and it won't take more than a minute or so.

$ 0.00
2 months ago

I don't understand these programming languages at all. Probably, if I were a programmer or at least a developer, I'd have to learn these languages. But now it passes me by.

$ 0.00
3 months ago