[JAVA] Issue decoding the "note" field of a transaction

Hi there,
i followed this tutorial to encode and decode a json in order to put it into the note field of a transaction and read it later.

The json is created in this way:

String hash = CryptoUtils.hashToString(message, SHA_256);
JsonObject obj = new JsonObject();
obj.addProperty(“strHash”, hash);

Then i encode it to fit the note field as showed in the tutorial:

byte notes = Encoder.encodeToMsgPack(obj.toString());

Finally i pass “notes” to the transaction.

After the transaction is confirmed and i retrieved it, i’m using this code to decode the notes:

try {
String s = Encoder.decodeFromMsgPack(transaction.getNoteb64(), String.class);
} catch (IOException e) {
Log.d(TAG, "onFinishedTransactionInfoRequest: ", e);
}

But i get this error:

java.lang.IllegalStateException: Invalid type=INT
at org.msgpack.jackson.dataformat.MessagePackParser.getText(MessagePackParser.java:386)
at com.fasterxml.jackson.core.base.ParserMinimalBase.getValueAsString(ParserMinimalBase.java:404)
at com.fasterxml.jackson.core.base.ParserMinimalBase.getValueAsString(ParserMinimalBase.java:390)
at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:56)
at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:11)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3814)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2945)
at com.algorand.algosdk.util.Encoder.decodeFromMsgPack(Encoder.java:72)
at it.sapienza.contact_tracing_app.fragments.HomeFragment.onFinishedTransactionInfoRequest(HomeFragment.java:91)
at it.sapienza.contact_tracing_app.async.TransactionsInfoAsyncTask.onPostExecute(TransactionsInfoAsyncTask.java:59)
at it.sapienza.contact_tracing_app.async.TransactionsInfoAsyncTask.onPostExecute(TransactionsInfoAsyncTask.java:16)
at android.os.AsyncTask.finish(AsyncTask.java:755)
at android.os.AsyncTask.access$900(AsyncTask.java:192)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:772)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:237)
at android.app.ActivityThread.main(ActivityThread.java:7860)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1075)

Can anyone suggest me what i’m doing wrong? Thanks!

I am not sure about the origin of the error.

However, if your data is already string (obj.toString()), there is no need to further encode it with msgpack. Concretely, if you want to store structured data in the note field, you should either:

The MsgPack solution has the advantage of being more compact and being the “standard” way of encoding objects on Algorand. However, JSON is usually simpler.

Here is an example of using msgpack. You can also do as Fabrice says. I put that code in to but it is commented out.

package com.algorand.algosdk.tutorials;

import com.algorand.algosdk.account.Account;
import com.algorand.algosdk.crypto.Address;
import com.algorand.algosdk.transaction.SignedTransaction;
import com.algorand.algosdk.transaction.Transaction;
import com.algorand.algosdk.util.Encoder;
import com.algorand.algosdk.v2.client.common.AlgodClient;
import com.algorand.algosdk.v2.client.common.Response;
import com.algorand.algosdk.v2.client.model.PendingTransactionResponse;
import com.algorand.algosdk.v2.client.model.TransactionParametersResponse;

import java.io.ByteArrayOutputStream;
import java.io.StringReader;

import javax.json.*;

public class YourFirstTransaction {
    public AlgodClient client = null;
    // utility function to connect to a node
    private AlgodClient connectToNetwork(){

        // Initialize an algod client
        //final String ALGOD_API_ADDR = "localhost";
        final Integer ALGOD_PORT = 8080; // 4001;
        //final String ALGOD_API_TOKEN = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
        final String ALGOD_API_ADDR = "localhost";
        final String ALGOD_API_TOKEN = "f1dee49e36a82face92fdb21cd3d340a1b369925cd12f3ee7371378f1665b9b1";
        
        AlgodClient client = (AlgodClient) new AlgodClient(ALGOD_API_ADDR, ALGOD_PORT, ALGOD_API_TOKEN);
        return client;
    }
    // utility function to wait on a transaction to be confirmed    
    public void waitForConfirmation( String txID ) throws Exception{
        if( client == null ) this.client = connectToNetwork();
        Long lastRound = client.GetStatus().execute().body().lastRound;
        while(true) {
            try {
                //Check the pending tranactions
                Response<PendingTransactionResponse> pendingInfo = client.PendingTransactionInformation(txID).execute();
                if (pendingInfo.body().confirmedRound != null && pendingInfo.body().confirmedRound > 0) {
                    //Got the completed Transaction
                    System.out.println("Transaction " + txID + " confirmed in round " + pendingInfo.body().confirmedRound);
                    break;
                } 
                lastRound++;
                client.WaitForBlock(lastRound).execute();
            } catch (Exception e) {
                throw( e );
            }
        }
    }

    public void gettingStartedExample() throws Exception {

        if( client == null ) this.client = connectToNetwork();

        // Import your private key mnemonic and address
        final String PASSPHRASE = "neutral blade diesel guard punch glide pepper cancel wise soul legend second capital load hover extra witness forward enlist flee pitch taxi impulse absent common";
        com.algorand.algosdk.account.Account myAccount = new Account(PASSPHRASE);
        System.out.println("My Address: " + myAccount.getAddress());

        String myAddress = myAccount.getAddress().toString();

        com.algorand.algosdk.v2.client.model.Account accountInfo = client.AccountInformation(myAccount.getAddress()).execute().body();

        System.out.println(String.format("Account Balance: %d microAlgos", accountInfo.amount));
        
        JsonObject obj = Json.createObjectBuilder().add("empName", "Jai")
        .add("empAge", "25")
        .add("empSalary", "40000")
        .build();
     
        try {
            // Construct the transaction
            final String RECEIVER = "GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A";
            //String note = "Hello World";
            TransactionParametersResponse params = client.TransactionParams().execute().body();
            Transaction txn = Transaction.PaymentTransactionBuilder()
            .sender(myAddress)
            // not using msgpack
            //.note(obj.toString().getBytes())
            // using msgpack
            .note(Encoder.encodeToMsgPack(obj.toString()))
            .amount(100000)
            .receiver(new Address(RECEIVER))
            .suggestedParams(params)
            .build();


            // Sign the transaction
            SignedTransaction signedTxn = myAccount.signTransaction(txn);
            System.out.println("Signed transaction with txid: " + signedTxn.transactionID);

            // Submit the transaction to the network
            byte[] encodedTxBytes = Encoder.encodeToMsgPack(signedTxn);
            String id = client.RawTransaction().rawtxn(encodedTxBytes).execute().body().txId;
            System.out.println("Successfully sent tx with ID: " + id);

            // Wait for transaction confirmation
            waitForConfirmation(id);

            //Read the transaction
            PendingTransactionResponse pTrx = client.PendingTransactionInformation(id).execute().body();
            System.out.println("Transaction information (with notes): " + pTrx.toString());
            //not encoded in msgpack
            //System.out.println("Decoded note: " + new String(pTrx.txn.tx.note));
            //encoded with msgpack
            String jo = Encoder.decodeFromMsgPack(pTrx.txn.tx.note, String.class);
            JsonReader jsonReader = Json.createReader(new StringReader(jo));
            JsonObject objarr = jsonReader.readObject();
            jsonReader.close();
            System.out.println("Decoded note: " + jo + ": Json : " + objarr.getString("empName"));
        } catch (Exception e) {
            System.err.println("Exception when calling algod#transactionInformation: " + e.getMessage());
        }
    }

    public static void main(String args[]) throws Exception {
        YourFirstTransaction t = new YourFirstTransaction();
        t.gettingStartedExample();
    }
}

Thank you guys.
It turned out the problem was that i was reading an old transaction where the note field contained data in a different format instead of the transaction i built with the JSON. Sorry!