How to get data (state) using txID Hyperledger fabric - hyperledger-fabric

async createEntity(ctx, entityNumber, entityType, data) {
const entity = {
data,
};
await ctx.stub.putState(entityType + entityNumber, Buffer.from(JSON.stringify(entity)));
return ctx.stub.getTxID();
}
There is such code in chaincode, in response I get for example
612d6a6b5919fbc511e7a7b691cd349eb932f4e8d84ab9394885d3220f2e169a
And having written down some information there, the question is, how do I get this data back using the received txId?

ctx.stub.GetState and ctx.stub.PutState these function write state to levelDB's. if you want to access your data by txID one of workaround which i have used is to store data against that txID. ie
let txID = ctx.stub.getTxID();
await ctx.stub.putState(txID, Buffer.from(JSON.stringify(entity)));

With getState, as you could have expected: https://hyperledger.github.io/fabric-chaincode-node/release-2.0/api/fabric-shim.ChaincodeStub.html#getState__anchor.

A workaround would be to compare the transaction id you get as return value of ctx.stub.getTxID() with all the transaction ids you get using getHistoryForKey(entityType + entityNumber). And the match in txnid is obviously your desired data (state).
But this is still a cumbersome process. Please post if you find a direct way to get data using just the txnId. Would be a treat if such any exists.

Related

Get raw transaction from transaction Id using web3js or something similar in nodejs

So i will give an example let's say i have the transaction id https://etherscan.io/tx/0xafef64d0d03db9f13c6c3f8aec5902167ea680bd0ffa0268d89a426d624b2ae1
In etherscan i can use their menu to see the raw transaction which will be
0xf8a915850516aab3ad82be7c947d1afa7b718fb893db30a3abc0cfc608aacfebb080b844095ea7b300000000000000000000000022b1cbb8d98a01a3b71d034bb899775a76eb1cc2ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff26a003daa35e6aa4a7cd439133411a390ff0796420d5cb39e9e276db75b01218ed41a028605f36c601527bd435b36da5403ee972fc2fb2c8f70959199bcdba0d0e8c77
I can't get this with etherscan api or geth rpc , i have found eth_getRawTransaction but it give just 0x for transfer transaction is there anyway i can find the raw transaction using nodejs code
thank you
I think i need to rlp encode the transaction or something like that , i'm lost
A raw transaction is an object. This hex string represents a serialized transaction object.
Example code below.
Note that ethers requires you to parse out the signature and other params of an already mined/validated transaction, and pass the signature as a second argument. There's probably a more generic way of doing that, I couldn't think of any right now.
const tx = await provider.getTransaction("0xafef64d0d03db9f13c6c3f8aec5902167ea680bd0ffa0268d89a426d624b2ae1");
const unsignedTx = {
to: tx.to,
nonce: tx.nonce,
gasLimit: tx.gasLimit,
gasPrice: tx.gasPrice,
data: tx.data,
value: tx.value,
chainId: tx.chainId
};
const signature = {
v: tx.v,
r: tx.r,
s: tx.s
}
const serialized = ethers.utils.serializeTransaction(unsignedTx, signature);
console.log(serialized);
Docs:
https://docs.ethers.org/v5/api/providers/provider/#Provider-getTransaction
https://docs.ethers.org/v5/api/utils/transactions/#utils-serializeTransaction
Thank you for your answers
for the ones looking if you are hosting your own geth node , it doesn't index all the transactions
You need to run:
geth with --txlookuplimit 0
Then, the rpc call eth_getRawTransactionByHash will do the job
my mistake

Cloud Functions and Cloud Firestore USER ASSOCIATION FUNCTION

Shortly, imagine I have a Cloud Firestore DB where I store some users data such as email, geo-location data (as geopoint) and some other things.
In Cloud Functions I have "myFunc" that runs trying to "link" two users between them based on a geo-query (I use GeoFirestore for it).
Now everything works well, but I cannot figure out how to avoid this kind of situation:
User A calls myFunc trying to find a person to be associated with, and finds User B as a possible one.
At the same time, User B calls myFunc too, trying to find a person to be associated with, BUT finds User C as possible one.
In this case User A would be associated with User B, but User B would be associated with User C.
I already have a field called "associated" set to FALSE on each user initialization, that becomes TRUE whenever a new possible association has been found.
But this code cannot guarantee the right association if User A and User B trigger the function at the same time, because at the moment in which the function triggered by User A will find User B, the "associated" field of B will be still set to false because B is still searching and has not found anybody yet.
I need to find a solution otherwise I'll end up having
wrong associations ( User A pointing at User B, but User B pointing at User C ).
I also thought about adding a snapshotListener to the user who is searching, so in that way if another User would update the searching user's document, I could terminate the function, but I'm not really sure it will work as expected.
I'd be incredibly grateful if you could help me with this problem.
Thanks a lot!
Cheers,
David
HERE IS MY CODE:
exports.myFunction = functions.region('europe-west1').https.onCall( async (data , context) => {
const userDoc = await firestore.collection('myCollection').doc(context.auth.token.email).get();
if (!userDoc.exists) {
return null;
}
const userData = userDoc.data();
if (userData.associated) { // IF THE USER HAS ALREADY BEEN ASSOCIATED
return null;
}
const latitude = userData.g.geopoint["latitude"];
const longitude = userData.g.geopoint["longitude"];
// Create a GeoQuery based on a location
const query = geocollection.near({ center: new firebase.firestore.GeoPoint(latitude, longitude), radius: userData.maxDistance });
// Get query (as Promise)
let otherUser = []; // ARRAY TO SAVE THE FIRST USER FOUND
query.get().then((value) => {
// CHECK EVERY USER DOC
value.docs.map((doc) => {
doc['data'] = doc['data']();
// IF THE USER HAS NOT BEEN ASSOCIATED YET
if (!doc['data'].associated) {
// SAVE ONLY THE FIRST USER FOUND
if (otherUser.length < 1) {
otherUser = doc['data'];
}
}
return null;
});
return value.docs;
}).catch(error => console.log("ERROR FOUND: ", error));
// HERE I HAVE TO RETURN AN .update() OF DATA ON 2 DOCUMENTS, IN ORDER TO UPDATE THE "associated" and the "userAssociated" FIELDS OF THE USER WHO WAS SEARCHING AND THE USER FOUND
return ........update({
associated: true,
userAssociated: otherUser.name
});
}); // END FUNCTION
You should use a Transaction in your Cloud Function. Since Cloud Functions are using the Admin SDK in the back-end, Transactions in a Cloud Function use pessimistic concurrency controls.
Pessimistic transactions use database locks to prevent other operations from modifying data.
See the doc form more details. In particular, you will read that:
In the server client libraries, transactions place locks on the
documents they read. A transaction's lock on a document blocks other
transactions, batched writes, and non-transactional writes from
changing that document. A transaction releases its document locks at
commit time. It also releases its locks if it times out or fails for
any reason.
When a transaction locks a document, other write operations must wait
for the transaction to release its lock. Transactions acquire their
locks in chronological order.

Hyperledger Fabric 2.0 Unable to parse results from evaluateTransaction

Using Hyperledger Fabric 2.0 as of, I'm trying to evaluate a transaction result like so:
const network = await gateway.getNetwork('evm');
// Get the contract from the network.
const contract = network.getContract('cc');
const result = await contract.evaluateTransaction('getEVMAddress') ;
And the getEVMAddress function is defined as follows:
async getEVMAddress(stub) {
console.info('============= START : getEVMAddress ===========');
const evmAsBytes = await stub.getState('EVMADDRESS');
if (!evmAsBytes || evmAsBytes.length === 0) {
throw new Error(`EVMADDRESS does not exist`);
}
var evmAddress = JSON.parse(evmAsBytes);
console.info('============= END : getEVMAddress ===========');
return JSON.stringify(evmAddress);
}
Which is storing a single simple string. I don't know what's happening, I already tried decoding with BlockDecoder but I seem unable to query results outside the ledger. Do note that there are no errors if we brint the string from inside the chain code, its results are just fine, but outside the chain code I only receive a Buffer with extraneous data within which I'm unable to parse correctly.
Thanks in advance.
while retrieving data from the ledger you will by default receive buffered data which you must covert to string.
Try to convert your output to string using toString() method.
var s= result.toString(). // converts buffer to string.
After that use var object = JSON.parse(s). //converts your string to JSON object.
Finally you can view a specific column of EVM table using,
var c= s['coulmn_name']; //column name is the name of the column in your record that you want in your EVM table.
The solution was to send the string back from a Buffer with Buffer.from(string)

In Hyperledger Fabric NodeJS smart contract, should all parameters be passed as a string?

In the fabric-sample repository of Hyperledger Fabric, specifically the sample application of commercial paper:
https://github.com/hyperledger/fabric-samples/blob/release-1.4/commercial-paper/organization/magnetocorp/application/issue.js
There is a line that calls the issue function:
const issueResponse = await contract.submitTransaction('issue', 'MagnetoCorp', '00001', '2020-05-31', '2020-11-30', '5000000');
The last parameter is supposedly an integer but the value 5000000 is passed as a string.
Is this a requirement in writing a NodeJS smart contract in Hyperledger Fabric that all parameters should be passed as a string?
Yes, the function parameters must be passed as string.
If you want to pass a JSON to a function you can stringify your data and parse it inside the method.
For example:
data = "{\"field1\":\"value1\",\"field2\":\"value2\"}"
var response = await contract.submitTransaction('myFunction', data);
And then in your chaincode
async myFunction(ctx, data){
data = JSON.parse(data);
//... your logics
}

Hyperledger Fabric - invokeChaincode in NodeSDK returning [Object object]

I am trying to perform a query on a chaincode(cc02) from another chaincode (cc01), both residing on the same channel. When I try invoking the query function by calling stub.invokeChaincode(...), the command is returning a [Object object] instead of the result of the query. Can someone please tell what the mistake in this is?
More details
Minimal version of the querying function in cc01 reads:
async queryOtherContract(stub, args) {
let chaincodeName = args[0]; //cc02
let fcn = args[1]; //query
let fcnArgs = args[2]; //key
let channel = args[3]; //defaultchannel
let queryResponse = await stub.invokeChaincode(chaincodeName, [fcn, fcnArgs], channel);
console.log('Query response: ', JSON.stringify(queryResponse));
}
Output:
Query response: {"status":200,"message":"","payload":{"buffer":{"type":"Buffer","data":[8,6...108]},"offset":9,"markedOffset":-1,"limit":59,"littleEndian":true,"noAssert":false}}
The payload Buffer decodes to [Object object]
The queried function from cc02 is as follows:
async query(stub, args) {
let key = args[0]; //key
let valueAsBytes = await stub.getState(key);
let valString = valueAsBytes.toString('utf8');
console.log('Value String: ', valString);
return shim.success(Buffer.from(valString));
}
Output: Value String: Value001
I have tried different variations as well including sending valueAsBytes directly as well as returning valString directly instead of wrapping it in the shim function. What am I doing wrong in this?
It is returning a buffer so you need to do the following:
Replace the following line:
return shim.success(valString);
with:
return shim.success(Buffer.from(valString));
The first chaincode (cc01) gets an object response and not a string.
I encountered a similar issue while calling chaincode invoke query from another chaincode in a network with only a single channel. It seems that calls to invokeChaincode:
var response = stub.invokeChaincode("MyChaincode", ["query", key])
Return not just the payload of the query but also modify the query by appending the transaction id and the channel along with the read/write set. (This is with v1.2 packages and network.)
response.payload.toString() was returning for me:
�{"carId":"CAR_0001"}"#48147a5a84e591671363053e58e4c56fe5d3e42c4adce1ecb2ce92f9922fd5b6:mychannel
Where the transaction id for the query is: 48147a5a84e591671363053e58e4c56fe5d3e42c4adce1ecb2ce92f9922fd5b6
and my channel name: mychannel. Not really sure what � represents.
Even though the payload passed to shim.success(payload) I confirmed to be:
{"carId":"CAR_0001"}
This was incredibly confusing, since I do not know why it modifies the actual payload string. My workaround was to extract my json string with indexOf and lastIndexOf bracket from the response.
According to the docs:
If the called chaincode is on the same channel, it simply adds the
called chaincode read set and write set to the calling transaction.
But the documentation does not mention modifying the payload or anything about the transaction id or channel name.
Hope this helps with querying chaincode from other chaincode on the same channel. Please correct me if I have incorrectly said something.

Resources