Reputation: 51
After enrolling, installing and instantiating the chaincode fabric/example/chaincode/go/chaincode_example02, I run the following steps.
peer chaincode instantiate --orderer orderer0:7050 --tls true --path example02 --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/orderer/localMspConfig/cacerts/ordererOrg0.pem --chainID mychannel --name example02cc --version 1.0 --ctor '{"Args":["init","A","1000","B","2000"]}'
peer chaincode query --chainID mychannel --name example02cc --ctor '{"Args":["query","A"]}'
peer chaincode query --chainID mychannel --name example02cc --ctor '{"Args":["query","B"]}'
So far, I confirm that A is equal to 1000 and B is equal to 2000. Afterwards, The result will be variable if I invoke the following step with different timings.
peer chaincode invoke --orderer orderer0:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/orderer/localMspConfig/cacerts/ordererOrg0.pem --chainID mychannel --name example02cc --ctor '{"Args":["invoke","A","B","1"]}'
Specifically, A will be equal to 998 and B will be equal to 2002 if I run the previous step twice with a 10 second pause. A will be equal to 990 and B will be equal to 2010 if I run the previous step ten times with 10 second pause between every step. However, without any pause, A will be equal to 999 and B will be equal to 2001 if I run the previous step twice. A will be equal to 999 and B will be equal to 2001 if I run the previous step ten times without pause between every step.
I have tested several times with different arguments. Furthermore, I have tested other chaincodes. It seems like that the chaincode only accept the first invoking request, and discards subsequent invoking requests. So, the questions are:
Upvotes: 0
Views: 1707
Reputation: 156
From the point of view of fabric this is actually normal behavior - although may seem a bit counterintuitive first.
What you see happening follows logically from the documented transaction flow (+ knowing that there is some batching performed on the orderer with batch-timeouts). Let us assume that during simulation (endorsement), variable A is read and then marked for re-setting by the chaincode simulation. What value was read and what value you want the variable set to become part of the proposed transaction (+ whether endorsers accept the transaction + crypto-stuff). Then we go through submission to orderer + distribution to channel peers + channel peers checking (among others) whether the original "read value assumptions" of the proposal still hold. (See 5.: Validation on the referenced peers.) If the value of A has changed "since simulation", then the transaction will not be valid.
The impact of timing on this is the following. If there is enough "leeway" between two invokes, the following happens:
The crucial thing is that what happens if you create and submit the second proposal before the effects of the first would have been committed to the Ledger. (So without "waiting enough".)
Such effects are not exactly new, we did get bitten by similar stuff with fabric 0.6. Fortunately, there are a few "clean" ways out of this; I suggest to read up on "tokenization" in the Blockchain context. In the context of example02, you can make all "units" have a GUID and track essentially ownership vectors; or you can go full-UTXO, Bitcoin style. Option b) can be to write the Tx requests themselves into the Ledger (Tx1:+20, Tx2: -40, Tx3: +65, ...) and base the comuptation of "current acccount state" on Ledger-stored Tx logs, although this can get a bit messy.
And do note that massive (and painless) concurrency is eminently possible, as long as the "working sets" of Tx requests do not overlap much. For many domains, this is possible; e.g. for a cryptocurrency, it is not the same currency unit that is being passed around like hot potatoes, but a large set of currency units having many transactions, but levelled out across them.
On your specific questions:
Upvotes: 1
Reputation: 774
The chaincode need to do endorsement and then commit it the ledger state before the next invoke.
When you call peer chaincode invoke ...
, fabric response quickly after the endorsement has finished. But the commit will still take some time. If you run the second invoke directly after the first invoke, the second invoke will be endorse correctly, but no commit happened.
So, for your questions:
You can try to invoke chaincode by java-sdk or node-sdk. take the javasdk for example, the progress is first send transactionProposalRequest to chaincode, which is the endorsement process in fabric. And after the endorsement is finished, and your endorment policy is correctly passed. the transaction is send to fabirc by sdk-client, this API will return an CompletableFuture<BlockEvent.TransactionEvent>
, which is similar to the Promise in js. when the transaction is done, the CompletableFuture.thenApply() is triggled. You may check the src/test/java/org.hyperledger.fabric.sdkintergration/End2endIT.java for example.
You may write a batch in your chaincode. This can support multiple invoke and query at once, it solves your question in some degree. The batch is designed as follow, new a m map[string]string
and toDelete []string
in your cc, when invoke put your key/val pairs in m
, and when query get key/val from m
first, if not found, query from stub, and when delete requirement, delete from m
and put the key to toDelete
, after all requirements are finished, commit all data in m
and toDelete
at once. I tried this mechanism in my project, it work good.
blockchain is designed not for high concurrent. It is designed for confidentiality, scalability and security.
see 2
Upvotes: 2