Home > Mobile >  [Hyperledger Fabric][Kotlin] Passing client request parameters as ByteArray to chaincode
[Hyperledger Fabric][Kotlin] Passing client request parameters as ByteArray to chaincode

Time:10-12

I've been recently working with Hyperledger Fabric using kotlin as a development language. I have the local test network fully working and after some time i have pretty good understanding on how everything works. However, im still stuck passing arguments to the chaincode as Strings because every time i try to pass objects as ByteArrays it implodes somehow along the way before it reaches my chaincode method. Here is a sample of the client sending the data and the chaincode entrypoint:

Client

    val tl = MyClass()
    tl.machineKey = "machineHashKey"
    val path = File("/var/cenas.txt")
    tl.loadFileFromPath(path.toPath())
   
    try {
        contract!!.submitTransaction(
            "testLimitSet", tl.toByteArray()
        )
    }catch (e: Exception){
        printException(e)
    }finally {
        println("*** Transaction completed")
    }

ChainCode:

@Transaction(intent = Transaction.TYPE.SUBMIT)
fun testLimitSet(ctx: Context, testLimit: ByteArray){
    println("Here 1!")
    val id = IdentityChecks.checkIdentity(ctx)
    println("Here 2!")
    val x509 = id.second
    println("Here 3!")
}

MyClass simply contains 2 text fields and 1 byteArray. I've already managed to serialize it and deserialize it locally.

The log on the chaincode machine is the following:

Thread[fabric-txinvoke:3,5,main] 16:39:28:613 INFO    org.hyperledger.fabric.contract.ContractRouter processRequest                    Got routing:testLimitSet:pt.fraunhofer.newgen.chaincode.NewGenMainContract
Thread[fabric-txinvoke:3,5,main] 16:39:28:625 SEVERE  org.hyperledger.fabric.Logger error                                              A JSONArray text must start with '[' at 1 [character 2 line 1]org.json.JSONException: A JSONArray text must start with '[' at 1 [character 2 line 1]
at org.json.JSONTokener.syntaxError(JSONTokener.java:507)
at org.json.JSONArray.<init>(JSONArray.java:109)
at org.json.JSONArray.<init>(JSONArray.java:162)
at org.hyperledger.fabric.contract.execution.JSONTransactionSerializer.convert(JSONTransactionSerializer.java:267)
at org.hyperledger.fabric.contract.execution.JSONTransactionSerializer.fromBuffer(JSONTransactionSerializer.java:165)
at org.hyperledger.fabric.contract.execution.impl.ContractExecutionService.convertArgs(ContractExecutionService.java:99)
at org.hyperledger.fabric.contract.execution.impl.ContractExecutionService.executeRequest(ContractExecutionService.java:57)
at org.hyperledger.fabric.contract.ContractRouter.processRequest(ContractRouter.java:123)
at org.hyperledger.fabric.contract.ContractRouter.invoke(ContractRouter.java:134)
at org.hyperledger.fabric.shim.impl.ChaincodeInvocationTask.call(ChaincodeInvocationTask.java:106)
at org.hyperledger.fabric.shim.impl.InvocationTaskManager.lambda$newTask$17(InvocationTaskManager.java:265)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)

I think im most likely using the ByteArray arguments improperly. But i haven't found any chaincode examples in java/kotlin receiving data as ByteArrays.

Thanks in advance

CodePudding user response:

When using the contract api in your chaincode implementation it uses JSON as the serialisation mechanism and thus expects information sent from the client to be in stringified JSON format.

The client sdks don't do this for you as for example you can write chaincode without the contract api and define your own serialiation mechanism.

With the line

        contract!!.submitTransaction(
            "testLimitSet", tl.toByteArray()
        )

you are sending an array of bytes as the string which won't mean anything to a JSON Parser. You need to convert tl.toByteArray() into a stringified JSON representation of that data, for example a stringified JSON representation of an array with bytes 01,02,03 might look like '["\u0001","\u0002","\u0003"]'

However I am not really familiar with the java chaincode libraries so whether this will actually work I don't know given that the JSON representation allows for values larger that bytes and that may cause the contract api to deserialize the JSON into something that isn't type compatible with what you declare on your chaincode ie testLimit: ByteArray you may need to change the type or perhaps send the binary data in a different manner, for example encode it as a base64 string then send a string instead

CodePudding user response:

I've solved the problem by creating a string representation of the ByteArray. It is not the solution i originally wanted, but it works well nevertheless. I still find it strange to the SDK to provide this method:

byte[] submitTransaction(String name, byte[]... args) 

and im not able to use it because it doesn't reach the chaincode endpoint with the problem described above. My solution uses this instead:

byte[] submitTransaction(String name, String[]... args) 

Here is my current solution:

val tl = MyClass()
tl.machineKey = "machineHashKey"
val path = File("/var/cenas.txt")
tl.loadFileFromPath(path.toPath())
   
try {
    val payload = tl.toByteArrayString()
    contract!!.submitTransaction(
        "testLimitSet", payload
    )
}catch (e: Exception){
    printException(e)
}finally {
    println("*** Transaction completed")
}

Where:

fun toByteArrayString(): String {
    return toHexString(this.toByteArray())
}

// String <- Hex(ByteArray)
fun toHexString(ba: ByteArray): String {
    return BaseEncoding.base16().encode(ba)
}

// String -> Hex(ByteArray)
fun toByteArray(s: String): ByteArray {
    return BaseEncoding.base16().decode(s)
}

Note that this is using

import com.google.common.io.BaseEncoding

but there are multiple implementations that can be used for this

  • Related