Easily Communicate with BlockChain and Build Apps
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.
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