Any Bitcoin Cash and Python enthusiast should now Bitcash. With Bitcash you can easily interact with the BCH blockchain. Here I'll leave some useful scripts to show how to send and find data stored in the blockchain with OP_RETURN.
Send a memo
Memo.cash is a nice social media based on BCH, completely trustless and decentralized. Sending a memo is very easy, just keep in mind the format described by the protocol:
from bitcash.wallet import Key
from bitcash.transaction import get_op_pushdata_code, calc_txid
from bitcash.utils import bytes_to_hex, hex_to_bytes
from bitcash.network.services import NetworkAPI
def send_memo(key, message):
POST_MEMO_PREFIX = "026d02"
PUSHDATA_CODE = bytes_to_hex(get_op_pushdata_code(message))
encoded_message = hex_to_bytes(POST_MEMO_PREFIX + PUSHDATA_CODE + bytes_to_hex(message.encode('utf-8')))
if len(encoded_message) <= 220:
memo_tx = key.create_transaction([], message = encoded_message, leftover = key.address, custom_pushdata = True)
NetworkAPI.broadcast_tx(memo_tx)
key.get_balance()
return(calc_txid(memo_tx))
else:
return "Error: message longer than 220 bytes"
Once you initialize a key with Bitcash, the function send_memo will allow you to send data up to 220 bytes. But how can you find data stored on the blockchain, once is sent? It's time to meet BitDB.
Search on the BCH blockchain with BitDB
BitDB is a powerful tool for searching data within the BCH blockchain. The result is a JSON file, so we can manage huge amounts of data.
I'll show you 3 useful functions:
raw_search_string: returns a JSON file with transactions containing the target string specified.
search_string: returns a dictionary with every transaction ID that contains the target string. This is more easy to use if you don't wanna bother with JSON files.
get_opreturn_by_txid: as the name says, you get the OP_RETURN data given a txid. Combined with the former function, you can search for a partial string and get the full string this way.
get_opreturn_by_address: simply get the data post from a given address (in cash format, not legacy). Combined with the send_memo function, you can use the BCH blockchain as your own database.
import requests, base64, json
MAIN_ENDPOINT = 'https://bitdb.bch.sx/q/'
def json_to_base64(json_file):
return base64.b64encode(json.dumps(json_file).encode('utf-8'))
def raw_search_string(target_string, limit):
#Search a string in any op_return containing txs and returns a json file with raw info
query = {"v": 3, "q": {"find": {"$text": { "$search": target_string },"out.b0": { "op": 106 }}, "limit": limit }}
b64query = json_to_base64(query)
r = requests.get(MAIN_ENDPOINT + b64query.decode())
if r.status_code == 200:
return r.json()
else:
raise Exception ("Something went bad. Status code is " + r.status_code)
def search_string(target_string, limit):
#Returns a dictionary with the TX id and strings that cointains the target string. Not case sensitive.
results = {}
json_results = raw_search_string(target_string, limit)
if json_results["u"] != []:
results["unconfirmed_txs"] = {}
for tx in json_results["u"]:
current_tx_id = tx["tx"]["h"]
results["unconfirmed_txs"][current_tx_id] = []
for output in tx["out"]:
for data in output:
if type(output[data]) == str and target_string.lower() in output[data].lower():
results["unconfirmed_txs"][current_tx_id].append(output[data])
if json_results["c"] != []:
results["confirmed_txs"] = {}
for tx in json_results["c"]:
current_tx_id = tx["tx"]["h"]
results["confirmed_txs"][current_tx_id] = []
for output in tx["out"]:
for data in output:
if type(output[data]) == str and target_string.lower() in output[data].lower():
results["confirmed_txs"][current_tx_id].append(output[data])
return results
def get_opreturn_by_txid(txid):
query = {"v": 3, "q": {"find": {"tx.h": txid}}}
b64query = json_to_base64(query)
r = requests.get(MAIN_ENDPOINT + b64query.decode())
if r.json()["u"] != []:
for output in r.json()["u"][0]["out"]:
if output["b0"] == {'op': 106}:
return output["str"]
elif r.json()["c"] != []:
for output in r.json()["c"][0]["out"]:
if output["b0"] == {'op': 106}:
return output["str"]
return "No OP_RETURN found in this transaction"
else:
return "No transaction found with this TX ID"
def get_opreturn_by_address(address):
#Find OP_return data send from a given address. A cash address must be provided, not legacy. Oldests TXs first.
results = []
if address[:12] == "bitcoincash:":
address = address[12:]
query = {"v": 3,"q": {"aggregate": [{"$match": {"out.b0": { "op": 106 }, "in.e.a": address }},{"$match": {"out.b0": { "op": 106 }}},{"$unwind": "$out"},{"$match": {"in.e.a": address }},{"$limit": 100000}],"sort": {"blk.t": 1}, "limit": 100000}}
b64query = json_to_base64(query)
r = requests.get(MAIN_ENDPOINT + b64query.decode())
if r.json()["c"] != []:
for output in r.json()["c"]:
if output["out"]["b0"] == {'op': 106}:
results.append(output["str"])
if r.json()["u"] != []:
for output in r.json()["u"]:
if output["out"]["b0"] == {'op': 106}:
results.append(output["str"])
return results
That's all folks, I hope these little scripts are useful for beginners!
That's interesting, I'm learning Python now)