Make your Python project interact with memo.cash

12 273

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!

13
$ 2.74
$ 1.00 from @Read.Cash
$ 0.53 from @Telesfor
$ 0.50 from @Darkerduck
+ 6

Comments

That's interesting, I'm learning Python now)

$ 0.00
4 years ago

The full encoded message can be up to 223 bytes.

$ 0.00
4 years ago

Wow thats interesting to know about python.. Good article. Keep going. Waiting for more of your articles.

$ 0.00
4 years ago

Bitcash is really nice!

FYI: we have a much better way to display code, see here: https://read.cash/@Read.Cash/how-to-use-the-editor-at-readcash-e2df60aa#code-blocks

$ 0.00
4 years ago

Thank you, I've just changed it.

$ 0.00
4 years ago

Looks awesome!

$ 0.00
4 years ago

Thank you!

$ 0.00
4 years ago

The full encoded message can be up to 223 bytes.

$ 0.00
4 years ago

There's also bchmemo which uses memocash to make memo posting a 3 step process. May be a bit outdated though.

$ 0.00
4 years ago

Bitcash is a good tool that has the potential to develop the popularity and mass adoption of BCH if well utilized.

$ 0.00
4 years ago

Thats great that you wrote about it.Very informative post.I like your article.Keep writing more for us.

$ 0.00
4 years ago

BCH is a stedily growing cryprocurrency platform. I am not very found of python bit i hope. Something great can be done with it

$ 0.00
4 years ago