Read and Write to the Transaction Note Field with Python
Up to 1kb of arbitrary data can be stored in any Transaction. This data can be stored and read from the transactions note field. If this data is encoded using the SDKs encode function you can decode the data from the note field using the decode function. This example walks through both the process of writing to and reading from the transaction note field.
Requirements
Background
When creating an application using Algorand, you can use the note field for arbitrary data storage. This data can be up to 1kb in length and the structure can be determined by the application. The SDKs provide methods to properly encode and decode this data. The complete example on reading and writing a note can be found here.
Steps
- 1. Create an Account and Add Funds
- 2. Create a JSON string and Copy into Transaction
- 3. Utility Function to Verify Transaction
- 4. Send Transaction with JSON string to Blockchain
- 5. Read Transaction from Blockchain and Recover JSON string
- 6. Confirm the Output
- 7. Using Indexer to Query the Note Field
- 8. Completed Code
1. Create an Account and Add Funds
Use goal or the Python SDK to create an account and add funds.
Using goal:
$goal account new -d <your-data-directory> -w <your-wallet>
Using goal or the Python SDK will return an address that we will need to recover in the tutorial. Export the account mnemonic using the following goal
command.
$goal account export -a <account-created-above> -d <your-data-directory> -w <your-wallet>
Using the Python SDK:
To create accounts using the Python SDK see this tutorial to create a standalone account.
This will export the account mnemonic string of random words that you should copy for later use. Note that this is just a tutorial. Hard coding a mnemonic is not advised and proper key management should be used in production applications.
Use the dispenser to add funds to the account. The TestNet dispenser is here.
Learn More
- Add Funds using Dispenser
2. Create a JSON string and Copy into Transaction
In this tutorial, we are just using a JSON string to represent a person object. This can be done using a simple string. This can then be encoded into the transaction by using the encode
method. Before doing that we must connect to the server and recover the account we created and funded in the previous step. In the code below, specify your token, server and port. Additionally set the mnemonic to the string you recovered in step 1.
import json
import base64
from algosdk.v2client import algod
from algosdk import mnemonic
from algosdk.future.transaction import PaymentTxn
def send_note():
# Use sandbox or your address and token
algod_address = "http://localhost:4001"
algod_token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
algod_client = algod.AlgodClient(algod_token, algod_address)
passphrase = "<your-25-word-mnemonic>"
private_key = mnemonic.to_private_key(passphrase)
my_address = mnemonic.to_public_key(passphrase)
print("My address: {}".format(my_address))
params = algod_client.suggested_params()
# comment out the next two (2) lines to use suggested fees
params.flat_fee = True
params.fee = 1000
note = '{"firstName":"John", "LastName":"Doe"}'.encode()
receiver = "GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A"
unsigned_txn = PaymentTxn(my_address, params, receiver, 1000000, None, note)
# sign transaction
signed_txn = unsigned_txn.sign(private_key)
send_note()
3. Utility Function to Verify Transaction
The signed transaction can now be sent to the network. Before doing that we need to add a utility function to the code that will verify the transaction has been written to the blockchain.
Add this wait_for_confirmation
function to the code
# utility for waiting on a transaction confirmation
def wait_for_confirmation(client, transaction_id, timeout):
"""
Wait until the transaction is confirmed or rejected, or until 'timeout'
number of rounds have passed.
Args:
transaction_id (str): the transaction to wait for
timeout (int): maximum number of rounds to wait
Returns:
dict: pending transaction information, or throws an error if the transaction
is not confirmed or rejected in the next timeout rounds
"""
start_round = client.status()["last-round"] + 1;
current_round = start_round
while current_round < start_round + timeout:
try:
pending_txn = client.pending_transaction_info(transaction_id)
except Exception:
return
if pending_txn.get("confirmed-round", 0) > 0:
return pending_txn
elif pending_txn["pool-error"]:
raise Exception(
'pool error: {}'.format(pending_txn["pool-error"]))
client.status_after_block(current_round)
current_round += 1
raise Exception(
'pending tx not found in timeout rounds, timeout value = : {}'.format(timeout))
4. Send Transaction with JSON string to Blockchain
At the end of the code, we can add a function call to broadcast the transaction to the network and also wait for the transaction to be confirmed.
# sign transaction
signed_txn = unsigned_txn.sign(private_key)
# send transaction
txid = algod_client.send_transaction(signed_txn)
print("Send transaction with txID: {}".format(txid))
# wait for confirmation
try:
confirmed_txn = wait_for_confirmation(algod_client, txid, 4)
except Exception as err:
print(err)
return
5. Read Transaction from Blockchain and Recover JSON string
Now that the transaction is confirmed, we can modify the code to read the transaction returned from the wait_for_confirmation
method and recover the person string from the note field using the decode
method. Add this to the bottom of the code in the send_note method.
print("txID: {}".format(txid), " confirmed in round: {}".format(
confirmed_txn.get("confirmed-round", 0)))
print("Transaction information: {}".format(
json.dumps(confirmed_txn, indent=2)))
print("Decoded note: {}".format(base64.b64decode(
confirmed_txn["txn"]["txn"]["note"]).decode()))
person_dict = json.loads(base64.b64decode(
confirmed_txn["txn"]["txn"]["note"]).decode())
print("First Name = {}".format(person_dict['firstName']))
6. Confirm the Output
Your output should look similar to this:
My address: 7DCJZKC4JDUKM25W7TDJ5XRTWGUTH6DOG5WARVA47DOCXQOTB4GMLNVW7I
Signed transaction with txID: IHWWKMQOA4PTM4XAM4VVQBDI2ZRV3DUHEHNC2WLTNAET3RBJZJLQ
txID: IHWWKMQOA4PTM4XAM4VVQBDI2ZRV3DUHEHNC2WLTNAET3RBJZJLQ confirmed in round: 10829545
Transaction information: {
"confirmed-round": 10829545,
"pool-error": "",
"txn": {
"sig": "tr2fP2jvEKqGFZYSB4ml8eK+B4COVXwBiWKbmsN6pYA77JXT7ngol2grzGh6y/z+2Uf5DmH2mNvtSfnNqX/tAg==",
"txn": {
"amt": 1000000,
"fee": 1000,
"fv": 10829543,
"gen": "testnet-v1.0",
"gh": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
"lv": 10830543,
"note": "eyJmaXJzdE5hbWUiOiJKb2huIiwgIkxhc3ROYW1lIjoiRG9lIn0=",
"rcv": "GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A",
"snd": "7DCJZKC4JDUKM25W7TDJ5XRTWGUTH6DOG5WARVA47DOCXQOTB4GMLNVW7I",
"type": "pay"
}
}
}
Decoded note: {"firstName":"John", "LastName":"Doe"}
First Name = John
7. Using Indexer to Query the Note Field
The following complete code can be used to query the note field with Indexer.
# search_transactions_note.py
import base64
import json
# requires Python SDK version 1.3 or higher
from algosdk.v2client import indexer
# instantiate indexer client
myindexer = indexer.IndexerClient(indexer_token="", indexer_address="http://localhost:8980")
import base64
note_prefix = '{"firstName":"John"'.encode()
response = myindexer.search_transactions(
note_prefix=note_prefix, min_round=10968688)
print("note_prefix = " +
json.dumps(response, indent=2, sort_keys=True))
# print first note that matches
if (len(response["transactions"]) > 0):
print("Decoded note: {}".format(base64.b64decode(
response["transactions"][0]["note"]).decode()))
person_dict = json.loads(base64.b64decode(
response["transactions"][0]["note"]).decode())
print("First Name = {}".format(person_dict['firstName']))
8. Completed Code
The complete example on reading and writing a note can be found here and searching on a note using indexer here.