Easily Communicate with BlockChain and Build Apps

1 81
Avatar for ClearSky
1 year ago

Communicating with blockchain via a popular and an easy method, Electrum Cash Protocol.

Introduction

Bitcoin nodes are main part of the network, without nodes you can not send, receive or get info about transaction or coins. Nodes are large, not everyone can run them and they aren't usually set to face queries from public networks. However many node operators hosts a middleware tool that allows people to query for information like balance, transaction history or broadcast transactions.

In this article I'm providing Example of usage for methods in Electrum Cash Protocol which is used in popular wallets like Electron Cash. Examples are sorted based on the protocol document, so don't panic if you find something you don't understand. There are really simple ones that anyone with little programming skills can try.

Querying the Electrum Server

In my example I'm using Python but you can adopt them to any language. First lest us try using simple socket connection:

import socket
from time import sleep

hostname = 'chipnet.c3-soft.com'

port = 64001

data = """{"method":"server.version","params":[],"id":194}"""


with socket.create_connection((hostname, port)) as sock:
    sock.send(data.encode('utf-8')+b'\n')
    sleep(0.5)
    print(sock.recv(1024))

If you run this command you should see:

b'{"id":194,"jsonrpc":"2.0","result":["Fulcrum 1.9.0","1.4"]}\r\n'

Query with SSL

Now let us try using SSL connection:

import socket
import ssl
from time import sleep

hostname = 'chipnet.c3-soft.com'
port = 64002

context = ssl.create_default_context()

data = """{"method":"server.version","params":[],"id":194}"""

with socket.create_connection((hostname, port)) as sock:
    with context.wrap_socket(sock, server_hostname=hostname) as ssock:
        ssock.send(data.encode('utf-8')+b'\n')
        sleep(0.5)
        response_byte = ssock.recv(1024)
        response = response_byte.decode()
        print(response)

Notice we changed the port from 64001 to 64002. Electrum server could operate on different ports based on network (mainnet, testnet, chipnet) or their own choice. We also decoded the output from byte format to json using decode().

Results:

{"id":194,"jsonrpc":"2.0","result":["Fulcrum 1.9.0","1.4"]}

I'm using chipnet.c3-soft.com server, you can find other servers inside Electron Cash wallet under Tools >> Network >> Servers.

Methods

You can read methods of the protocol in more details on the following link, I'm not going to copy all of it here :)
https://electrum-cash-protocol.readthedocs.io/en/latest/protocol-methods.html

Example Queries

Now, regarding python code that I'm using for these simple queries, it's sufficient to just change the data part. So from now on I'll just post the data part.

Balance for BCH and Tokens

data = """{"method":"blockchain.address.get_balance","params":["bchtest:zry77fz5ph8supxplr57h6lfx0jlw5kxjc39ctq8zc"],"id":194}"""

Returns if I run the script with $python3 /tmp/test.py |jq . :

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": {
    "confirmed": 10108742,
    "unconfirmed": 0
  }
}

This query will exclude tokens balance by default, BCH is expected to deploy native token support in May 15 2023 so you may want to learn about their BCH balance. We can run t his query:

data = """{"method":"blockchain.address.get_balance","params":["bchtest:zry77fz5ph8supxplr57h6lfx0jlw5kxjc39ctq8zc", "include_tokens"],"id":194}"""

Notice we added include_tokens as a parameter. You can also modify it to only show tokens balance with tokens_only. If we do we see:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": {
    "confirmed": 1000,
    "unconfirmed": 0
  }
}

Address History

data = """{"method":"blockchain.address.get_history","params":["bchtest:zry77fz5ph8supxplr57h6lfx0jlw5kxjc39ctq8zc"],"id":194}"""

Returns a list of transactions involving the provided address:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": [
    {
      "height": 130619,
      "tx_hash": "d3987427832a14a5a9998587cf57693fdebfeee5254a28d927448bf394228eac"
    },
    {
      "height": 130620,
      "tx_hash": "fd16466978638a0b721df9ba728f2b18e219d84a035a19548ce8056ad16d1adb"
    }
  ]
}

Return Transaction in Mempool for an Address

data = """{"method":"blockchain.address.get_mempool","params":["bchtest:zry77fz5ph8supxplr57h6lfx0jlw5kxjc39ctq8zc"],"id":194}"""

Result:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": [
    {
      "fee": 219,
      "height": -1,
      "tx_hash": "10c17fa8fa096855f0b63f8643e3cb6788d97e9f5f36043aa22cf8b443ed2222"
    }
  ]
}

It will return nothing if no unconfirmed transaction is waiting in the Mempool.

Get Address Script Hash

Didn't dive much to learn what Script Hash is but it works :)

data = """{"method":"blockchain.address.get_scripthash","params":["bchtest:zry77fz5ph8supxplr57h6lfx0jlw5kxjc39ctq8zc"],"id":194}"""

Result:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": "e135101e310f33833cd0c6982e5d678ca0ff41b110e0bc0168c2c145245af8fb"
}

Get an Address UTXOs

data = """{"method":"blockchain.address.listunspent","params":["bchtest:zry77fz5ph8supxplr57h6lfx0jlw5kxjc39ctq8zc"],"id":194}"""

Result

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": [
    {
      "height": 130620,
      "tx_hash": "fd16466978638a0b721df9ba728f2b18e219d84a035a19548ce8056ad16d1adb",
      "tx_pos": 1,
      "value": 10098743
    },
    {
      "height": 130702,
      "tx_hash": "10c17fa8fa096855f0b63f8643e3cb6788d97e9f5f36043aa22cf8b443ed2222",
      "tx_pos": 0,
      "value": 9999
    }
  ]
}

Get an Address UTXOs Including Tokens

The blockchain.address.listunspent method also accept token related parameters. Here we use include_tokens.

data = """{"method":"blockchain.address.listunspent","params":["bchtest:zry77fz5ph8supxplr57h6lfx0jlw5kxjc39ctq8zc", "include_tokens"],"id":194}"""

Result:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": [
    {
      "height": 130620,
      "token_data": {
        "amount": "1555",
        "category": "d3987427832a14a5a9998587cf57693fdebfeee5254a28d927448bf394228eac"
      },
      "tx_hash": "fd16466978638a0b721df9ba728f2b18e219d84a035a19548ce8056ad16d1adb",
      "tx_pos": 0,
      "value": 1000
    },
    {
      "height": 130620,
      "tx_hash": "fd16466978638a0b721df9ba728f2b18e219d84a035a19548ce8056ad16d1adb",
      "tx_pos": 1,
      "value": 10098743
    },
    {
      "height": 130702,
      "tx_hash": "10c17fa8fa096855f0b63f8643e3cb6788d97e9f5f36043aa22cf8b443ed2222",
      "tx_pos": 0,
      "value": 9999
    }
  ]
}

Subscribing to a Bitcoin Cash Address

I believe this needs a bit different code, so will leave it for a later article maybe d.v.

Return the Block Header

This is a bit advanced but we can run a simple form of the query

data = """{"method":"blockchain.block.header","params":["130713"],"id":194}"""

Where 130713 is the block number

Result

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": "00000020b50f525372a75dd5af367999f0967df7c8ac65770836a3b3f7febb7d0000000022fc3087af74b7b721ff5fd77b1f13c29bdc9fadf640aa1044ec4d9f90a5d20d0eb6c3637d90001d66721859"
}

Return Chunk of Block Headers

I put here block number 130713 in parameters:

data = """{"method":"blockchain.block.headers","params":["130713", "3"],"id":194}"""

Result:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": {
    "count": 3,
    "hex": "00000020b50f525372a75dd5af367999f0967df7c8ac65770836a3b3f7febb7d0000000022fc3087af74b7b721ff5fd77b1f13c29bdc9fadf640aa1044ec4d9f90a5d20d0eb6c3637d90001d66721859000000205ff85ddb0ea6bbf32969741740e296a1a9928e4a016183231d48942e000000004ef8322b25207002a2383049251b6eea14c4f141d1ad93b5eb57d8dbc82410b779b7c3636f88001d00c0702d00000020150af46c2931607ca5113d150480d1f5eb3c27c7f94ee8447541942000000000e7529f9e2ac9c482bdf80415a32f19de6bffb25f27814e9e66d3aee2d566b117d3b7c3635782001d05eadc83",
    "max": 2016
  }
}

Estimate Fees

For me it was always returning the same result for any parameters I used, it's a fixed value.

data = """{"method":"blockchain.estimatefee","params":["5"],"id":194}"""
{
  "id": 194,
  "jsonrpc": "2.0",
  "result": 1e-05
}

Get the Latest Block’s Height and Header

data = """{"method":"blockchain.headers.get_tip","params":[],"id":194}"""

Returns:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": {
    "height": 130715,
    "hex": "00000020150af46c2931607ca5113d150480d1f5eb3c27c7f94ee8447541942000000000e7529f9e2ac9c482bdf80415a32f19de6bffb25f27814e9e66d3aee2d566b117d3b7c3635782001d05eadc83"
  }
}


Subscribing to New Blocks

I believe this needs a bit different code, so will leave it for a later article maybe d.v.

Scripthash Related Methods

I'll also pass on those as it's a bit advanced subject for me and seems similar in application to the standard address related methods.

Broadcast a Transaction

You can use it to broadcast raw transactions.

data = """{"method":"blockchain.transaction.broadcast","params":["01000000012222ed43b4f82ca23a04365f9f7ed98867cbe343863fb6f0556809faa87fc110010000006441e034f331841fe54db54a5c5ebfcbab66ad8682fe285992130737ae7bf0a5603858511416eb6003aad3f5aa818c9400148d66f72b859eeb519718c2ecd8e2dd044121028aa60b3a3b4b590d5480fea4411e3e5ae6c959226064a336a11a7ae1a03154b5feffffff027d250000000000001976a914e9a1bdba9c8a6845b31f825c1444527006c93f9c88acdece9900000000001976a9143e8b21680884ce1d5a5c619fb69a971a676f1ffa88ac9cfe0100"],"id":194}"""

Returns a transaction ID:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": "882307117ca84c81b680ef944de1cdabbe4ddda147bdbf535943d771852df2b2"
}

I've created a raw transaction using Electron Cash. In the send tab you do the usual steps and chose Preview instead of Send, then you sign, copy the transaction and Broadcast it using your code.

Get Double Spend Proof

data = """{"method":"blockchain.transaction.dsproof.get","params":["882307117ca84c81b680ef944de1cdabbe4ddda147bdbf535943d771852df2b2"],"id":194}"""

Returns Null if no double spent was associated with the transaction

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": null
}

If a double spend was deteced it will retun something similar to this:

```json
{
  "dspid": "587d18bf8a64ede9c7450fdaeab27b9b3c46cfa8948f4c145f889601153c56b0",
  "txid": "5b59ce35093fbd13549cd6f203d4b5b01762d70e75b8e9733dfc463e0ff8cc13",
  "hex": "410c56078977120e828e4aacdd813a818d17c47d94183aa176d62c805d47697dddddf46c2ab68ee1e46a3e17aa7da548c38ec43416422d433b1782eb3298356df441",
  "outpoint": {
    "txid": "f6e2a16ba665d5402dad147fe35872961bc6961da62345a2171ee001cfcf7600",
    "vout": 0
  },
  "descendants": [
    "36fbb099e6de59d23477727e3199c65caae35ded957660f56fc681a6d81d5570",
    "5b59ce35093fbd13549cd6f203d4b5b01762d70e75b8e9733dfc463e0ff8cc13"
  ]
}

Get List of Double Spends in Mempool

data = """{"method":"blockchain.transaction.dsproof.list","params":[],"id":194}"""

If it finds any it will return something like this:

[
  "e67cc122f3c28a4243c3a1b14b38a9474c22ba928af9a194ca2b85426f0fd1bb",
  "077f0cc2439f2e48567c72eeeba5a447f8649c00c3d18ab6516eccfd4119726f",
  "ccc2f0d90b7067a83566024d4df842f0b6cb8180e18d642fcc85cae8acadbd58"
]

You can use those double spend IDs to get more information about it using blockchain.transaction.dsproof.get method.

Subscribe to a Transaction Double Spend

This also another subscribe method that will be left for later d.v.

Get Raw Transaction from Transaction ID

data = """{"method":"blockchain.transaction.get","params":["882307117ca84c81b680ef944de1cdabbe4ddda147bdbf535943d771852df2b2"],"id":194}"""

Result:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": "01000000012222ed43b4f82ca23a04365f9f7ed98867cbe343863fb6f0556809faa87fc110010000006441e034f331841fe54db54a5c5ebfcbab66ad8682fe285992130737ae7bf0a5603858511416eb6003aad3f5aa818c9400148d66f72b859eeb519718c2ecd8e2dd044121028aa60b3a3b4b590d5480fea4411e3e5ae6c959226064a336a11a7ae1a03154b5feffffff027d250000000000001976a914e9a1bdba9c8a6845b31f825c1444527006c93f9c88acdece9900000000001976a9143e8b21680884ce1d5a5c619fb69a971a676f1ffa88ac9cfe0100"
}

You can also get more verbose output by adding true to the parameters:

data = """{"method":"blockchain.transaction.get","params":["882307117ca84c81b680ef944de1cdabbe4ddda147bdbf535943d771852df2b2", true],"id":194}"""

However as the output is expected to be large you may have to increase the limit in sock.recv(1024) to sock.recv(2048).

Block Height for a Confirmed Transaction

data = """{"method":"blockchain.transaction.get_height","params":["882307117ca84c81b680ef944de1cdabbe4ddda147bdbf535943d771852df2b2"],"id":194}"""

It finds the provided transaction in block number 130717:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": 130717
}

Get Merkle Branch of a Transaction

data = """{"method":"blockchain.transaction.get_merkle","params":["882307117ca84c81b680ef944de1cdabbe4ddda147bdbf535943d771852df2b2"],"id":194}"""

Return:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": {
    "block_height": 130717,
    "merkle": [
      "98c62086c368072827d813c98e249b2efca3ba0af9c158a158dc20660299e3f2"
    ],
    "pos": 1
  }
}

You can also specify the height. Example:

data = """{"method":"blockchain.transaction.get_merkle","params":["882307117ca84c81b680ef944de1cdabbe4ddda147bdbf535943d771852df2b2", "130715"],"id":194}"""


Result:

{
  "error": {
    "code": 1,
    "message": "No transaction matching the requested hash found at height 130715"
  },
  "id": 194,
  "jsonrpc": "2.0"
}

We knew from the previous query that the block is 130717 but I requested the transaction in a different block so I got the previous error.

Get Transaction by Position in Block

data = """{"method":"blockchain.transaction.id_from_pos","params":["130717", "1"],"id":194}"""

Result:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": "882307117ca84c81b680ef944de1cdabbe4ddda147bdbf535943d771852df2b2"
}

Get UTXO by Transaction Hash

We provided transaction number and the UTXO’s transaction output number

data = """{"method":"blockchain.utxo.get_info","params":["882307117ca84c81b680ef944de1cdabbe4ddda147bdbf535943d771852df2b2", "1"],"id":194}"""

Result:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": {
    "confirmed_height": 130717,
    "scripthash": "91595a8149d7d27b77506f4e1c6d3b8ffb9fbac1557ed71da77264486bc47caa",
    "value": 10079966
  }
}

Get Server Donation Address

data = """{"method":"server.donation_address","params":[],"id":194}"""

Result:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": "bchtest:qq9rw090p2eu9drv6ptztwx4ghpftwfa0gyqvlvx2q"
}


Get Server Features

data = """{"method":"server.features","params":[],"id":194}"""

Result:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": {
    "cashtokens": true,
    "dsproof": true,
    "genesis_hash": "000000001dd410c49a788668ce26751718cc797474d3152a5fc073dd44fd9f7b",
    "hash_function": "sha256",
    "hosts": {
      "chipnet.c3-soft.com": {
        "ssl_port": 64002,
        "tcp_port": 64001,
        "ws_port": 64003,
        "wss_port": 64004
      }
    },
    "protocol_max": "1.5",
    "protocol_min": "1.4",
    "pruning": null,
    "server_version": "Fulcrum 1.9.0"
  }
}

Ping Server

data = """{"method":"server.ping","params":[],"id":194}"""

Result:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": null
}

Get Server Version

data = """{"method":"server.version","params":[],"id":194}"""

Result:

{
  "id": 194,
  "jsonrpc": "2.0",
  "result": [
    "Fulcrum 1.9.0",
    "1.4"
  ]
}


Notes

I focused on Bitcoin Cash and specially the staging Chipnet network but many options are shared between other networks, the Electrum server and it's protocol is a very popular tool in Crypto projects.

There are some wallets that connects directly to the network nodes and doesn't depends on an Electrum server. Each method has it's advantages and disadvantages.

What next?

I know some of those might be hard to understand but some are very easy and you can jump in and ask questions in BCH channels if you are interested in learning. I suggest BCH developers and Builders channel on Telegram or on Matrix

I've wrote some other tutorials about programming related to crypto projects. Check them out.

7
$ 11.09
$ 10.25 from @sahidmiller
$ 0.53 from @Telesfor
$ 0.30 from @Omar
+ 1
Avatar for ClearSky
1 year ago

Comments

Online gokken biedt een spannende en toegankelijke manier voor mensen om te genieten van casinospellen, met de nodige regelgeving en veiligheidsmaatregelen om een veilige en eerlijke speelomgeving te waarborgen. Zoals met elk type gokken, is het belangrijk om verantwoordelijk te spelen en de risico's te kenne

$ 0.00
1 week ago