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. https://electrum-cash-protocol.readthedocs.io/en/latest/protocol-methods.html 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 https://t.me/bchbuilders https://matrix.to/#/%23bchbuilders:matrix.org I've wrote some other tutorials about programming related to crypto projects. Check them out.
$11.09
-
@sahidmiller
$10.25
-
@Telesfor
53¢
-
@Omar
30¢
-
@sanctuary.the-one-law
1¢
- +KT
- +BCH
Log in to join in Reading is open to everyone. Replying needs an account.
No comments yet