mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 07:25:10 +00:00
Asset transfer private-data go-chaincode - Add unit tests (#290)
* Adding golang application for asset-transfer-basic sample. (#211) Signed-off-by: Chongxin Luo <Chongxin.Luo@ibm.com> Improved private data Go Chaincode in idiomatic go. Adding go chaincode unit tests Signed-off-by: Sijo Cherian <sijo@ibm.com> * Added unit tests for query-asset chaincode functions Signed-off-by: Sijo Cherian <sijo@ibm.com> * Improved README Signed-off-by: Sijo Cherian <sijo@ibm.com> * Added unit tests for query-asset chaincode functions Signed-off-by: Sijo Cherian <sijo@ibm.com> * Fixed json.Marsal usage per review comments, Improved DeleteAsset validation Added owner collection check for DeleteAsset chaincode JS app now demos a new expected error on DeleteAsset by a non-owner org Signed-off-by: Sijo Cherian <sijo@ibm.com> Co-authored-by: Dereck <Chongxin.Luo@ibm.com> Co-authored-by: Sijo Cherian <sijo@ibm.com>
This commit is contained in:
parent
67811efc92
commit
c1424748b0
12 changed files with 4417 additions and 64 deletions
|
|
@ -153,7 +153,21 @@ async function main() {
|
|||
result = await contractOrg1.evaluateTransaction('ReadAssetPrivateDetails', org1PrivateCollectionName, assetID1);
|
||||
console.log(' result: ' + prettyJSONString(result.toString()));
|
||||
|
||||
|
||||
// Attempt Transfer the asset to Org2 , without Org2 adding AgreeToTransfer //
|
||||
// Transaction should return an error: "failed transfer verification ..."
|
||||
let buyerDetails = { assetID: assetID1, buyerMSP: mspOrg2 };
|
||||
try {
|
||||
console.log('\n--> Attempt Submit Transaction: TransferAsset ' + assetID1);
|
||||
statefulTxn = contractOrg1.createTransaction('TransferAsset');
|
||||
tmapData = Buffer.from(JSON.stringify(buyerDetails));
|
||||
statefulTxn.setTransient({
|
||||
asset_owner: tmapData
|
||||
});
|
||||
result = await statefulTxn.submit();
|
||||
console.log('******** FAILED: above operation expected to return an error');
|
||||
} catch (error) {
|
||||
console.log(` Successfully caught the error: \n ${error}`);
|
||||
}
|
||||
console.log('\n~~~~~~~~~~~~~~~~ As Org2 Client ~~~~~~~~~~~~~~~~');
|
||||
console.log('\n--> Evaluate Transaction: ReadAsset ' + assetID1);
|
||||
result = await contractOrg2.evaluateTransaction('ReadAsset', assetID1);
|
||||
|
|
@ -194,7 +208,7 @@ async function main() {
|
|||
// Transfer the asset to Org2 //
|
||||
// To transfer the asset, the owner needs to pass the MSP ID of new asset owner, and initiate the transfer
|
||||
console.log('\n--> Submit Transaction: TransferAsset ' + assetID1);
|
||||
let buyerDetails = { assetID: assetID1, buyerMSP: mspOrg2 };
|
||||
|
||||
statefulTxn = contractOrg1.createTransaction('TransferAsset');
|
||||
tmapData = Buffer.from(JSON.stringify(buyerDetails));
|
||||
statefulTxn.setTransient({
|
||||
|
|
@ -202,7 +216,6 @@ async function main() {
|
|||
});
|
||||
result = await statefulTxn.submit();
|
||||
|
||||
|
||||
//Again ReadAsset : results will show that the buyer identity now owns the asset:
|
||||
console.log('\n--> Evaluate Transaction: ReadAsset ' + assetID1);
|
||||
result = await contractOrg1.evaluateTransaction('ReadAsset', assetID1);
|
||||
|
|
@ -221,11 +234,23 @@ async function main() {
|
|||
console.log(' result: ' + prettyJSONString(result.toString()));
|
||||
|
||||
console.log('\n********* Demo deleting asset **************');
|
||||
// Delete Asset2
|
||||
console.log('--> Submit Transaction: DeleteAsset ' + assetID2);
|
||||
statefulTxn = contractOrg1.createTransaction('DeleteAsset');
|
||||
|
||||
let dataForDelete = { assetID: assetID2 };
|
||||
try {
|
||||
//Non-owner Org2 should not be able to DeleteAsset. Expect an error from DeleteAsset
|
||||
console.log('--> Attempt Transaction: as Org2 DeleteAsset ' + assetID2);
|
||||
statefulTxn = contractOrg2.createTransaction('DeleteAsset');
|
||||
tmapData = Buffer.from(JSON.stringify(dataForDelete));
|
||||
statefulTxn.setTransient({
|
||||
asset_delete: tmapData
|
||||
});
|
||||
result = await statefulTxn.submit();
|
||||
console.log('******** FAILED : expected to return an error');
|
||||
} catch (error) {
|
||||
console.log(` Successfully caught the error: \n ${error}`);
|
||||
}
|
||||
// Delete Asset2 as Org1
|
||||
console.log('--> Submit Transaction: as Org1 DeleteAsset ' + assetID2);
|
||||
statefulTxn = contractOrg1.createTransaction('DeleteAsset');
|
||||
tmapData = Buffer.from(JSON.stringify(dataForDelete));
|
||||
statefulTxn.setTransient({
|
||||
asset_delete: tmapData
|
||||
|
|
|
|||
|
|
@ -38,7 +38,11 @@ You can use the test network script to deploy the private data smart contract to
|
|||
./network.sh deployCC -ccn private -ccep "OR('Org1MSP.peer','Org2MSP.peer')" -cccg ../asset-transfer-private-data/chaincode-go/collections_config.json
|
||||
```
|
||||
|
||||
Note that we are using the `-ccep` flag to deploy the private data smart contract with a chaincode endorsement policy of `"OR('Org1MSP.peer','Org2MSP.peer')"`. This allows Org1 and Org2 to create an asset without receiving an endorsement from the other organization. The command also uses the `-cccg` flag to provide the path to the collection configuration file.
|
||||
The above command deploys the go chaincode with short name `private`, and specifies the private data collection configuration from file `collections_config.json` using `-cccg` flag.
|
||||
Note that we are using the `-ccep` flag to deploy the private data smart contract with a chaincode endorsement policy of `"OR('Org1MSP.peer','Org2MSP.peer')"`. This allows Org1 and Org2 to create an asset without receiving an endorsement from the other organization.
|
||||
|
||||
Now you are ready to call the deployed smart contract.
|
||||
Note that this sample workflow steps below, can also be executed via the application at `asset-transfer-private-data/application-javascript` folder, in fewer steps. To execute the workflow via CLI, read on.
|
||||
|
||||
## Register identities
|
||||
|
||||
|
|
@ -147,7 +151,7 @@ The query will return the value of the asset:
|
|||
|
||||
### Buyer from Org2 agrees to buy the asset
|
||||
|
||||
The buyer identity from Org2 is interested in buying the asset. Set the following environment variables to operate as the buyer:
|
||||
The buyer identity from Org2 is interested in buying the asset. In a new terminal, set the following environment variables to operate as the buyer:
|
||||
|
||||
```
|
||||
export CORE_PEER_LOCALMSPID="Org2MSP"
|
||||
|
|
@ -156,20 +160,17 @@ export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.e
|
|||
export CORE_PEER_ADDRESS=localhost:9051
|
||||
```
|
||||
|
||||
Now that we are operating as a member of Org2, we can demonstrate that the asset appraisal is not stored on the Org2 peer:
|
||||
Now that we are operating as a member of Org2, we can demonstrate that the asset appraisal is not stored in Org2MSPPrivateCollection, on the Org2 peer:
|
||||
```
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n private -c '{"function":"ReadAssetPrivateDetails","Args":["Org2MSPPrivateCollection","asset1"]}'
|
||||
```
|
||||
The buyer only finds that asset1 does exist in the Org1 collection:
|
||||
```
|
||||
Error: endorsement failure during invoke. response: status:500 message:"appraisal value for asset1 does not exist in private data collection"
|
||||
```
|
||||
The empty response shows that, the asset1 private details, does not exist in buyer private collection.
|
||||
|
||||
Nor is a member of Org2 able to read the Org1 private data collection:
|
||||
Nor can a member of Org2, able to read the Org1 private data collection:
|
||||
```
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n private -c '{"function":"ReadAssetPrivateDetails","Args":["Org1MSPPrivateCollection","asset1"]}'
|
||||
```
|
||||
By setting `"memberOnlyRead": true` in the collection configuration file, we specify that only members of Org1 can read data from the collection. A member who tries to read the collection would only get the following response.
|
||||
By setting `"memberOnlyRead": true` in the collection configuration file, we specify that only members of Org1 can read data from the collection. A Org2 member who tries to read the collection would only get the following response.
|
||||
```
|
||||
Error: endorsement failure during query. response: status:500 message:"failed to read from asset details GET_STATE failed: transaction ID: 10d39a7d0b340455a19ca4198146702d68d884d41a0e60936f1599c1ddb9c99d: tx creator does not have read access permission on privatedata in chaincodeName:private collectionName: Org1MSPPrivateCollection"
|
||||
```
|
||||
|
|
@ -189,9 +190,9 @@ The invoke will return the following value:
|
|||
{"assetID":"asset1","appraisedValue":100}
|
||||
```
|
||||
|
||||
## Transfer the asset to Org2
|
||||
## Org1 member transfers the asset to Org2
|
||||
|
||||
Now that buyer has agreed to buy the asset for appraised value, the owner from Org1 can transfer the asset to Org2. Set the following environment variables to operate as Org1:
|
||||
Now that buyer has agreed to buy the asset for appraised value, the owner from Org1 can transfer the asset to Org2. In the first terminal (with the following environment variables to operate as Org1):
|
||||
```
|
||||
export CORE_PEER_LOCALMSPID="Org1MSP"
|
||||
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/owner@org1.example.com/msp
|
||||
|
|
@ -199,7 +200,12 @@ export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.e
|
|||
export CORE_PEER_ADDRESS=localhost:7051
|
||||
```
|
||||
|
||||
To transfer the asset, the owner needs to pass the MSP ID of new asset owner. The transfer function will read the client ID of the interested buyer from the transfer agreement.
|
||||
Now that buyer has agreed to buy the asset for appraised value, the owner from Org1 can read the data added by `AgreeToTransfer` to see buyer identity.
|
||||
```
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n private -c '{"function":"ReadTransferAgreement","Args":["asset1"]}'
|
||||
```
|
||||
|
||||
The owner from Org1 can now transfer the asset to Org2. To transfer the asset, the owner needs to pass the MSP ID of new asset owner Org. The transfer function will read the client ID of the interested buyer user from the transfer agreement.
|
||||
```
|
||||
export ASSET_OWNER=$(echo -n "{\"assetID\":\"asset1\",\"buyerMSP\":\"Org2MSP\"}" | base64 | tr -d \\n)
|
||||
```
|
||||
|
|
@ -209,7 +215,7 @@ The owner of the asset needs to initiate the transfer.
|
|||
```
|
||||
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n private -c '{"function":"TransferAsset","Args":[]}' --transient "{\"asset_owner\":\"$ASSET_OWNER\"}" --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
|
||||
```
|
||||
You can query `asset1` to see the results of the transfer.
|
||||
You can ReadAsset `asset1` to see the results of the transfer.
|
||||
```
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n private -c '{"function":"ReadAsset","Args":["asset1"]}'
|
||||
```
|
||||
|
|
@ -229,10 +235,7 @@ You can also confirm that transfer removed the private details from the Org1 col
|
|||
```
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n private -c '{"function":"ReadAssetPrivateDetails","Args":["Org1MSPPrivateCollection","asset1"]}'
|
||||
```
|
||||
Your query will return the following result:
|
||||
```
|
||||
Error: endorsement failure during query. response: status:500 message:"appraisal value for asset1 does not exist in private data collection"
|
||||
```
|
||||
Your query will return empty result, since the asset private data is removed from the Org1 private data collection.
|
||||
|
||||
## Clean up
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Copyright IBM Corp. All Rights Reserved.
|
|||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package main
|
||||
package chaincode
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
|
@ -20,7 +20,7 @@ func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, a
|
|||
log.Printf("ReadAsset: collection %v, ID %v", assetCollection, assetID)
|
||||
assetJSON, err := ctx.GetStub().GetPrivateData(assetCollection, assetID) //get the asset from chaincode state
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read from asset %v", err)
|
||||
return nil, fmt.Errorf("failed to read asset: %v", err)
|
||||
}
|
||||
|
||||
//No Asset found, return empty response
|
||||
|
|
@ -44,7 +44,7 @@ func (s *SmartContract) ReadAssetPrivateDetails(ctx contractapi.TransactionConte
|
|||
log.Printf("ReadAssetPrivateDetails: collection %v, ID %v", collection, assetID)
|
||||
assetDetailsJSON, err := ctx.GetStub().GetPrivateData(collection, assetID) // Get the asset from chaincode state
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read from asset details %v", err)
|
||||
return nil, fmt.Errorf("failed to read asset details: %v", err)
|
||||
}
|
||||
if assetDetailsJSON == nil {
|
||||
log.Printf("AssetPrivateDetails for %v does not exist in collection %v", assetID, collection)
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
Copyright IBM Corp. All Rights Reserved.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package chaincode_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hyperledger/fabric-protos-go/ledger/queryresult"
|
||||
|
||||
"github.com/hyperledger/fabric-samples/asset-transfer-private-data/chaincode-go/chaincode"
|
||||
"github.com/hyperledger/fabric-samples/asset-transfer-private-data/chaincode-go/chaincode/mocks"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
/*
|
||||
For details on generating the mocks, see comments in the file asset_transfer_test.go
|
||||
*/
|
||||
func TestReadAsset(t *testing.T) {
|
||||
transactionContext, chaincodeStub := prepMocksAsOrg1()
|
||||
assetTransferCC := chaincode.SmartContract{}
|
||||
|
||||
assetBytes, err := assetTransferCC.ReadAsset(transactionContext, "id1")
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, assetBytes)
|
||||
|
||||
chaincodeStub.GetPrivateDataReturns(nil, fmt.Errorf("unable to retrieve asset"))
|
||||
assetBytes, err = assetTransferCC.ReadAsset(transactionContext, "id1")
|
||||
require.EqualError(t, err, "failed to read asset: unable to retrieve asset")
|
||||
|
||||
testAsset := &chaincode.Asset{
|
||||
ID: "id1",
|
||||
Type: "testfulasset",
|
||||
Color: "gray",
|
||||
Size: 7,
|
||||
Owner: myOrg1Clientid,
|
||||
}
|
||||
setReturnPrivateDataInStub(t, chaincodeStub, testAsset)
|
||||
assetRead, err := assetTransferCC.ReadAsset(transactionContext, "id1")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, testAsset, assetRead)
|
||||
}
|
||||
|
||||
func TestReadAssetPrivateDetails(t *testing.T) {
|
||||
transactionContext, chaincodeStub := prepMocksAsOrg1()
|
||||
assetTransferCC := chaincode.SmartContract{}
|
||||
|
||||
assetBytes, err := assetTransferCC.ReadAssetPrivateDetails(transactionContext, myOrg1PrivCollection, "id1")
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, assetBytes)
|
||||
|
||||
//read from the collection with no access
|
||||
chaincodeStub.GetPrivateDataReturns(nil, fmt.Errorf("collection not found"))
|
||||
assetBytes, err = assetTransferCC.ReadAssetPrivateDetails(transactionContext, myOrg2PrivCollection, "id1")
|
||||
require.EqualError(t, err, "failed to read asset details: collection not found")
|
||||
|
||||
returnPrivData := &chaincode.AssetPrivateDetails{
|
||||
ID: "id1",
|
||||
AppraisedValue: 5,
|
||||
}
|
||||
setReturnAssetPrivateDetailsInStub(t, chaincodeStub, returnPrivData)
|
||||
assetRead, err := assetTransferCC.ReadAssetPrivateDetails(transactionContext, myOrg1PrivCollection, "id1")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, returnPrivData, assetRead)
|
||||
}
|
||||
|
||||
func TestReadTransferAgreement(t *testing.T) {
|
||||
transactionContext, chaincodeStub := prepMocksAsOrg1()
|
||||
assetTransferCC := chaincode.SmartContract{}
|
||||
|
||||
//TransferAgreement does not exist
|
||||
assetBytes, err := assetTransferCC.ReadTransferAgreement(transactionContext, "id1")
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, assetBytes)
|
||||
|
||||
chaincodeStub.GetPrivateDataReturns([]byte(myOrg2Clientid), nil)
|
||||
expectedData := &chaincode.TransferAgreement{
|
||||
ID: "id1",
|
||||
BuyerID: myOrg2Clientid,
|
||||
}
|
||||
dataRead, err := assetTransferCC.ReadTransferAgreement(transactionContext, "id1")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedData, dataRead)
|
||||
}
|
||||
|
||||
func TestQueryAssetByOwner(t *testing.T) {
|
||||
transactionContext, chaincodeStub := prepMocksAsOrg1()
|
||||
|
||||
asset := &chaincode.Asset{Type: "valuableasset", ID: "asset1", Owner: "user1"}
|
||||
asset1Bytes, err := json.Marshal(asset)
|
||||
require.NoError(t, err)
|
||||
|
||||
iterator := &mocks.StateQueryIterator{}
|
||||
iterator.HasNextReturnsOnCall(0, true)
|
||||
iterator.HasNextReturnsOnCall(1, false)
|
||||
iterator.NextReturns(&queryresult.KV{Value: asset1Bytes}, nil)
|
||||
chaincodeStub.GetPrivateDataQueryResultReturns(iterator, nil)
|
||||
|
||||
assetTransferCC := &chaincode.SmartContract{}
|
||||
assets, err := assetTransferCC.QueryAssetByOwner(transactionContext, "valuableasset", "user1")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []*chaincode.Asset{asset}, assets)
|
||||
|
||||
iterator.HasNextReturns(true)
|
||||
iterator.NextReturns(nil, fmt.Errorf("failed retrieving next item"))
|
||||
assets, err = assetTransferCC.QueryAssetByOwner(transactionContext, "valuableasset", "user1")
|
||||
require.EqualError(t, err, "failed retrieving next item")
|
||||
require.Nil(t, assets)
|
||||
|
||||
}
|
||||
|
||||
func TestQueryAssets(t *testing.T) {
|
||||
transactionContext, chaincodeStub := prepMocksAsOrg1()
|
||||
//Iterator with no records
|
||||
iterator := &mocks.StateQueryIterator{}
|
||||
iterator.HasNextReturns(false)
|
||||
chaincodeStub.GetPrivateDataQueryResultReturns(iterator, nil)
|
||||
|
||||
assetTransferCC := &chaincode.SmartContract{}
|
||||
assets, err := assetTransferCC.QueryAssets(transactionContext, "querystr")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []*chaincode.Asset{}, assets)
|
||||
|
||||
iterator = &mocks.StateQueryIterator{}
|
||||
chaincodeStub.GetPrivateDataQueryResultReturns(iterator, nil)
|
||||
iterator.HasNextReturns(true)
|
||||
iterator.NextReturns(nil, fmt.Errorf("failed retrieving next item"))
|
||||
assets, err = assetTransferCC.QueryAssets(transactionContext, "querystr")
|
||||
require.EqualError(t, err, "failed retrieving next item")
|
||||
require.Nil(t, assets)
|
||||
|
||||
asset := &chaincode.Asset{Type: "valuableasset", ID: "asset1", Owner: "user1"}
|
||||
asset1Bytes, err := json.Marshal(asset)
|
||||
require.NoError(t, err)
|
||||
|
||||
iterator = &mocks.StateQueryIterator{}
|
||||
chaincodeStub.GetPrivateDataQueryResultReturns(iterator, nil)
|
||||
iterator.HasNextReturnsOnCall(0, true)
|
||||
iterator.HasNextReturnsOnCall(1, false)
|
||||
iterator.NextReturns(&queryresult.KV{Value: asset1Bytes}, nil)
|
||||
|
||||
assets, err = assetTransferCC.QueryAssets(transactionContext, "querystr")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []*chaincode.Asset{asset}, assets)
|
||||
}
|
||||
|
||||
func TestGetAssetByRange(t *testing.T) {
|
||||
transactionContext, chaincodeStub := prepMocksAsOrg1()
|
||||
//Iterator with no records
|
||||
iterator := &mocks.StateQueryIterator{}
|
||||
iterator.HasNextReturns(false)
|
||||
chaincodeStub.GetPrivateDataByRangeReturns(iterator, nil)
|
||||
|
||||
assetTransferCC := &chaincode.SmartContract{}
|
||||
assets, err := assetTransferCC.GetAssetByRange(transactionContext, "st", "end")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []*chaincode.Asset{}, assets)
|
||||
|
||||
iterator = &mocks.StateQueryIterator{}
|
||||
chaincodeStub.GetPrivateDataByRangeReturns(iterator, nil)
|
||||
iterator.HasNextReturns(true)
|
||||
iterator.NextReturns(nil, fmt.Errorf("failed retrieving next item"))
|
||||
assets, err = assetTransferCC.GetAssetByRange(transactionContext, "st", "end")
|
||||
require.EqualError(t, err, "failed retrieving next item")
|
||||
require.Nil(t, assets)
|
||||
|
||||
asset := &chaincode.Asset{Type: "valuableasset", ID: "asset1", Owner: "user1"}
|
||||
asset1Bytes, err := json.Marshal(asset)
|
||||
require.NoError(t, err)
|
||||
|
||||
iterator = &mocks.StateQueryIterator{}
|
||||
chaincodeStub.GetPrivateDataByRangeReturns(iterator, nil)
|
||||
iterator.HasNextReturnsOnCall(0, true)
|
||||
iterator.HasNextReturnsOnCall(1, false)
|
||||
iterator.NextReturns(&queryresult.KV{Value: asset1Bytes}, nil)
|
||||
|
||||
assets, err = assetTransferCC.GetAssetByRange(transactionContext, "st", "end")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []*chaincode.Asset{asset}, assets)
|
||||
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ Copyright IBM Corp. All Rights Reserved.
|
|||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package main
|
||||
package chaincode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
@ -19,6 +19,11 @@ import (
|
|||
const assetCollection = "assetCollection"
|
||||
const transferAgreementObjectType = "transferAgreement"
|
||||
|
||||
// SmartContract of this fabric sample
|
||||
type SmartContract struct {
|
||||
contractapi.Contract
|
||||
}
|
||||
|
||||
// Asset describes main asset details that are visible to all organizations
|
||||
type Asset struct {
|
||||
Type string `json:"objectType"` //Type is used to distinguish the various types of objects in state database
|
||||
|
|
@ -40,11 +45,6 @@ type TransferAgreement struct {
|
|||
BuyerID string `json:"buyerID"`
|
||||
}
|
||||
|
||||
// SmartContract of this fabric sample
|
||||
type SmartContract struct {
|
||||
contractapi.Contract
|
||||
}
|
||||
|
||||
// CreateAsset creates a new asset by placing the main asset details in the assetCollection
|
||||
// that can be read by both organizations. The appraisal value is stored in the owners org specific collection.
|
||||
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface) error {
|
||||
|
|
@ -116,7 +116,7 @@ func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface)
|
|||
}
|
||||
|
||||
// Make submitting client the owner
|
||||
asset := &Asset{
|
||||
asset := Asset{
|
||||
Type: assetInput.Type,
|
||||
ID: assetInput.ID,
|
||||
Color: assetInput.Color,
|
||||
|
|
@ -138,7 +138,7 @@ func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface)
|
|||
}
|
||||
|
||||
// Save asset details to collection visible to owning organization
|
||||
assetPrivateDetails := &AssetPrivateDetails{
|
||||
assetPrivateDetails := AssetPrivateDetails{
|
||||
ID: assetInput.ID,
|
||||
AppraisedValue: assetInput.AppraisedValue,
|
||||
}
|
||||
|
|
@ -202,6 +202,14 @@ func (s *SmartContract) AgreeToTransfer(ctx contractapi.TransactionContextInterf
|
|||
return fmt.Errorf("appraisedValue field must be a positive integer")
|
||||
}
|
||||
|
||||
// Read asset from the private data collection
|
||||
asset, err := s.ReadAsset(ctx, valueJSON.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading asset: %v", err)
|
||||
}
|
||||
if asset == nil {
|
||||
return fmt.Errorf("%v does not exist", valueJSON.ID)
|
||||
}
|
||||
// Verify that the client is submitting request to peer in their organization
|
||||
err = verifyClientOrgMatchesPeerOrg(ctx)
|
||||
if err != nil {
|
||||
|
|
@ -273,9 +281,11 @@ func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterfac
|
|||
// Read asset from the private data collection
|
||||
asset, err := s.ReadAsset(ctx, assetTransferInput.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get asset: %v", err)
|
||||
return fmt.Errorf("error reading asset: %v", err)
|
||||
}
|
||||
if asset == nil {
|
||||
return fmt.Errorf("%v does not exist", assetTransferInput.ID)
|
||||
}
|
||||
|
||||
// Verify that the client is submitting request to peer in their organization
|
||||
err = verifyClientOrgMatchesPeerOrg(ctx)
|
||||
if err != nil {
|
||||
|
|
@ -433,10 +443,18 @@ func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface)
|
|||
return fmt.Errorf("asset not found: %v", assetDeleteInput.ID)
|
||||
}
|
||||
|
||||
var assetToDelete Asset
|
||||
err = json.Unmarshal([]byte(valAsbytes), &assetToDelete)
|
||||
ownerCollection, err := getCollectionName(ctx) // Get owners collection
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal JSON: %v", err)
|
||||
return fmt.Errorf("failed to infer private collection name for the org: %v", err)
|
||||
}
|
||||
|
||||
//check the asset is in the caller org's private collection
|
||||
valAsbytes, err = ctx.GetStub().GetPrivateData(ownerCollection, assetDeleteInput.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read asset from owner's Collection: %v", err)
|
||||
}
|
||||
if valAsbytes == nil {
|
||||
return fmt.Errorf("asset not found in owner's private Collection %v: %v", ownerCollection, assetDeleteInput.ID)
|
||||
}
|
||||
|
||||
// delete the asset from state
|
||||
|
|
@ -446,12 +464,7 @@ func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface)
|
|||
}
|
||||
|
||||
// Finally, delete private details of asset
|
||||
ownerCollection, err := getCollectionName(ctx) // Get owners collection
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to infer private collection name for the org: %v", err)
|
||||
}
|
||||
|
||||
err = ctx.GetStub().DelPrivateData(ownerCollection, assetDeleteInput.ID) // Delete the asset
|
||||
err = ctx.GetStub().DelPrivateData(ownerCollection, assetDeleteInput.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -486,7 +499,7 @@ func (s *SmartContract) DeleteTranferAgreement(ctx contractapi.TransactionContex
|
|||
}
|
||||
|
||||
if len(assetDeleteInput.ID) == 0 {
|
||||
return fmt.Errorf("ID field must be a non-empty string")
|
||||
return fmt.Errorf("transient input ID field must be a non-empty string")
|
||||
}
|
||||
|
||||
// Verify that the client is submitting request to peer in their organization
|
||||
|
|
@ -561,17 +574,3 @@ func verifyClientOrgMatchesPeerOrg(ctx contractapi.TransactionContextInterface)
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
chaincode, err := contractapi.NewChaincode(new(SmartContract))
|
||||
|
||||
if err != nil {
|
||||
log.Panicf("error creating the chaincode: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := chaincode.Start(); err != nil {
|
||||
log.Panicf("error starting the chaincode: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,444 @@
|
|||
/*
|
||||
Copyright IBM Corp. All Rights Reserved.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package chaincode_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hyperledger/fabric-chaincode-go/pkg/cid"
|
||||
"github.com/hyperledger/fabric-chaincode-go/shim"
|
||||
"github.com/hyperledger/fabric-contract-api-go/contractapi"
|
||||
|
||||
"github.com/hyperledger/fabric-samples/asset-transfer-private-data/chaincode-go/chaincode"
|
||||
"github.com/hyperledger/fabric-samples/asset-transfer-private-data/chaincode-go/chaincode/mocks"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
/*
|
||||
These unit tests use mocks to simulate chaincode-api & fabric interactions
|
||||
The mocks are generated using counterfeiter directives in the comments (starting with "go:generate counterfeiter")
|
||||
All files in mocks/* are generated by running following, in the directory with your directive:
|
||||
`go generate`
|
||||
*/
|
||||
|
||||
//go:generate counterfeiter -o mocks/transaction.go -fake-name TransactionContext . transactionContext
|
||||
type transactionContext interface {
|
||||
contractapi.TransactionContextInterface
|
||||
}
|
||||
|
||||
//go:generate counterfeiter -o mocks/chaincodestub.go -fake-name ChaincodeStub . chaincodeStub
|
||||
type chaincodeStub interface {
|
||||
shim.ChaincodeStubInterface
|
||||
}
|
||||
|
||||
//go:generate counterfeiter -o mocks/statequeryiterator.go -fake-name StateQueryIterator . stateQueryIterator
|
||||
type stateQueryIterator interface {
|
||||
shim.StateQueryIteratorInterface
|
||||
}
|
||||
|
||||
//go:generate counterfeiter -o mocks/clientIdentity.go -fake-name ClientIdentity . clientIdentity
|
||||
type clientIdentity interface {
|
||||
cid.ClientIdentity
|
||||
}
|
||||
|
||||
const assetCollectionName = "assetCollection"
|
||||
const transferAgreementObjectType = "transferAgreement"
|
||||
const myOrg1Msp = "Org1Testmsp"
|
||||
const myOrg1Clientid = "myOrg1Userid"
|
||||
const myOrg1PrivCollection = "Org1TestmspPrivateCollection"
|
||||
const myOrg2Msp = "Org2Testmsp"
|
||||
const myOrg2Clientid = "myOrg2Userid"
|
||||
const myOrg2PrivCollection = "Org2TestmspPrivateCollection"
|
||||
|
||||
type assetTransientInput struct {
|
||||
Type string `json:"objectType"`
|
||||
ID string `json:"assetID"`
|
||||
Color string `json:"color"`
|
||||
Size int `json:"size"`
|
||||
AppraisedValue int `json:"appraisedValue"`
|
||||
}
|
||||
|
||||
type assetTransferTransientInput struct {
|
||||
ID string `json:"assetID"`
|
||||
BuyerMSP string `json:"buyerMSP"`
|
||||
}
|
||||
|
||||
func TestCreateAssetBadInput(t *testing.T) {
|
||||
transactionContext, chaincodeStub := prepMocksAsOrg1()
|
||||
assetTransferCC := chaincode.SmartContract{}
|
||||
|
||||
// No transient map
|
||||
err := assetTransferCC.CreateAsset(transactionContext)
|
||||
require.EqualError(t, err, "asset not found in the transient map input")
|
||||
|
||||
// transient map with incomplete asset data
|
||||
assetPropMap := map[string][]byte{
|
||||
"asset_properties": []byte("ill formatted property"),
|
||||
}
|
||||
chaincodeStub.GetTransientReturns(assetPropMap, nil)
|
||||
err = assetTransferCC.CreateAsset(transactionContext)
|
||||
require.Error(t, err, "Expected error: transient map with incomplete asset data")
|
||||
require.Contains(t, err.Error(), "failed to unmarshal JSON")
|
||||
|
||||
testAsset := &assetTransientInput{
|
||||
Type: "testfulasset",
|
||||
}
|
||||
setReturnAssetPropsInTransientMap(t, chaincodeStub, testAsset)
|
||||
err = assetTransferCC.CreateAsset(transactionContext)
|
||||
require.EqualError(t, err, "assetID field must be a non-empty string")
|
||||
|
||||
testAsset = &assetTransientInput{
|
||||
ID: "id1",
|
||||
Color: "gray",
|
||||
}
|
||||
setReturnAssetPropsInTransientMap(t, chaincodeStub, testAsset)
|
||||
err = assetTransferCC.CreateAsset(transactionContext)
|
||||
require.EqualError(t, err, "objectType field must be a non-empty string")
|
||||
|
||||
// case when asset exists, GetPrivateData returns a valid data from ledger
|
||||
testAsset = &assetTransientInput{
|
||||
ID: "id1",
|
||||
Type: "testfulasset",
|
||||
Color: "gray",
|
||||
Size: 7,
|
||||
AppraisedValue: 500,
|
||||
}
|
||||
setReturnAssetPropsInTransientMap(t, chaincodeStub, testAsset)
|
||||
chaincodeStub.GetPrivateDataReturns([]byte{}, nil)
|
||||
err = assetTransferCC.CreateAsset(transactionContext)
|
||||
require.EqualError(t, err, "this asset already exists: id1")
|
||||
}
|
||||
|
||||
func TestCreateAssetSuccessful(t *testing.T) {
|
||||
transactionContext, chaincodeStub := prepMocksAsOrg1()
|
||||
assetTransferCC := chaincode.SmartContract{}
|
||||
testAsset := &assetTransientInput{
|
||||
ID: "id1",
|
||||
Type: "testfulasset",
|
||||
Color: "gray",
|
||||
Size: 7,
|
||||
AppraisedValue: 500,
|
||||
}
|
||||
setReturnAssetPropsInTransientMap(t, chaincodeStub, testAsset)
|
||||
err := assetTransferCC.CreateAsset(transactionContext)
|
||||
require.NoError(t, err)
|
||||
//Validate PutPrivateData calls
|
||||
calledCollection, calledId, _ := chaincodeStub.PutPrivateDataArgsForCall(0)
|
||||
require.Equal(t, assetCollectionName, calledCollection)
|
||||
require.Equal(t, "id1", calledId)
|
||||
|
||||
expectedPrivateDetails := &chaincode.AssetPrivateDetails{
|
||||
ID: "id1",
|
||||
AppraisedValue: 500,
|
||||
}
|
||||
assetBytes, err := json.Marshal(expectedPrivateDetails)
|
||||
calledCollection, calledId, calledAssetBytes := chaincodeStub.PutPrivateDataArgsForCall(1)
|
||||
require.Equal(t, myOrg1PrivCollection, calledCollection)
|
||||
require.Equal(t, "id1", calledId)
|
||||
require.Equal(t, assetBytes, calledAssetBytes)
|
||||
}
|
||||
|
||||
func TestAgreeToTransferBadInput(t *testing.T) {
|
||||
transactionContext, chaincodeStub := prepMocksAsOrg1()
|
||||
assetTransferCC := chaincode.SmartContract{}
|
||||
|
||||
assetPrivDetail := &chaincode.AssetPrivateDetails{
|
||||
ID: "id1",
|
||||
//no AppraisedValue
|
||||
}
|
||||
setReturnAssetPrivateDetailsInTransientMap(t, chaincodeStub, assetPrivDetail)
|
||||
origAsset := chaincode.Asset{
|
||||
ID: "id1",
|
||||
Type: "testfulasset",
|
||||
Color: "gray",
|
||||
Size: 7,
|
||||
Owner: myOrg1Clientid,
|
||||
}
|
||||
setReturnPrivateDataInStub(t, chaincodeStub, &origAsset)
|
||||
|
||||
err := assetTransferCC.AgreeToTransfer(transactionContext)
|
||||
require.EqualError(t, err, "appraisedValue field must be a positive integer")
|
||||
|
||||
assetPrivDetail = &chaincode.AssetPrivateDetails{
|
||||
//no ID
|
||||
AppraisedValue: 500,
|
||||
}
|
||||
setReturnAssetPrivateDetailsInTransientMap(t, chaincodeStub, assetPrivDetail)
|
||||
err = assetTransferCC.AgreeToTransfer(transactionContext)
|
||||
require.EqualError(t, err, "assetID field must be a non-empty string")
|
||||
|
||||
assetPrivDetail = &chaincode.AssetPrivateDetails{
|
||||
ID: "id1",
|
||||
AppraisedValue: 500,
|
||||
}
|
||||
setReturnAssetPrivateDetailsInTransientMap(t, chaincodeStub, assetPrivDetail)
|
||||
//asset does not exist
|
||||
setReturnPrivateDataInStub(t, chaincodeStub, nil)
|
||||
err = assetTransferCC.AgreeToTransfer(transactionContext)
|
||||
require.EqualError(t, err, "id1 does not exist")
|
||||
}
|
||||
|
||||
func TestAgreeToTransferSuccessful(t *testing.T) {
|
||||
transactionContext, chaincodeStub := prepMocksAsOrg1()
|
||||
assetTransferCC := chaincode.SmartContract{}
|
||||
assetPrivDetail := &chaincode.AssetPrivateDetails{
|
||||
ID: "id1",
|
||||
AppraisedValue: 500,
|
||||
}
|
||||
setReturnAssetPrivateDetailsInTransientMap(t, chaincodeStub, assetPrivDetail)
|
||||
origAsset := chaincode.Asset{
|
||||
ID: "id1",
|
||||
Type: "testfulasset",
|
||||
Color: "gray",
|
||||
Size: 7,
|
||||
Owner: myOrg1Clientid,
|
||||
}
|
||||
setReturnPrivateDataInStub(t, chaincodeStub, &origAsset)
|
||||
chaincodeStub.CreateCompositeKeyReturns(transferAgreementObjectType+"id1", nil)
|
||||
err := assetTransferCC.AgreeToTransfer(transactionContext)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedDataBytes, err := json.Marshal(assetPrivDetail)
|
||||
calledCollection, calledId, calledWithDataBytes := chaincodeStub.PutPrivateDataArgsForCall(0)
|
||||
require.Equal(t, myOrg1PrivCollection, calledCollection)
|
||||
require.Equal(t, "id1", calledId)
|
||||
require.Equal(t, expectedDataBytes, calledWithDataBytes)
|
||||
|
||||
calledCollection, calledId, calledWithDataBytes = chaincodeStub.PutPrivateDataArgsForCall(1)
|
||||
require.Equal(t, assetCollectionName, calledCollection)
|
||||
require.Equal(t, transferAgreementObjectType+"id1", calledId)
|
||||
require.Equal(t, []byte(myOrg1Clientid), calledWithDataBytes)
|
||||
}
|
||||
func TestTransferAssetBadInput(t *testing.T) {
|
||||
transactionContext, chaincodeStub := prepMocksAsOrg1()
|
||||
assetTransferCC := chaincode.SmartContract{}
|
||||
|
||||
assetNewOwner := &assetTransferTransientInput{
|
||||
ID: "id1",
|
||||
BuyerMSP: "",
|
||||
}
|
||||
setReturnAssetOwnerInTransientMap(t, chaincodeStub, assetNewOwner)
|
||||
setReturnPrivateDataInStub(t, chaincodeStub, &chaincode.Asset{})
|
||||
err := assetTransferCC.TransferAsset(transactionContext)
|
||||
require.EqualError(t, err, "buyerMSP field must be a non-empty string")
|
||||
|
||||
assetNewOwner = &assetTransferTransientInput{
|
||||
ID: "id1",
|
||||
BuyerMSP: myOrg2Msp,
|
||||
}
|
||||
setReturnAssetOwnerInTransientMap(t, chaincodeStub, assetNewOwner)
|
||||
//asset does not exist
|
||||
setReturnPrivateDataInStub(t, chaincodeStub, nil)
|
||||
err = assetTransferCC.TransferAsset(transactionContext)
|
||||
require.EqualError(t, err, "id1 does not exist")
|
||||
}
|
||||
|
||||
func TestTransferAssetSuccessful(t *testing.T) {
|
||||
transactionContext, chaincodeStub := prepMocksAsOrg1()
|
||||
assetTransferCC := chaincode.SmartContract{}
|
||||
assetNewOwner := &assetTransferTransientInput{
|
||||
ID: "id1",
|
||||
BuyerMSP: myOrg2Msp,
|
||||
}
|
||||
setReturnAssetOwnerInTransientMap(t, chaincodeStub, assetNewOwner)
|
||||
origAsset := chaincode.Asset{
|
||||
ID: "id1",
|
||||
Type: "testfulasset",
|
||||
Color: "gray",
|
||||
Size: 7,
|
||||
Owner: myOrg1Clientid,
|
||||
}
|
||||
setReturnPrivateDataInStub(t, chaincodeStub, &origAsset)
|
||||
//to ensure we pass data hash verification
|
||||
chaincodeStub.GetPrivateDataHashReturns([]byte("datahash"), nil)
|
||||
//to ensure that ReadTransferAgreement call returns org2 client ID
|
||||
chaincodeStub.GetPrivateDataReturnsOnCall(1, []byte(myOrg2Clientid), nil)
|
||||
chaincodeStub.CreateCompositeKeyReturns(transferAgreementObjectType+"id1", nil)
|
||||
|
||||
err := assetTransferCC.TransferAsset(transactionContext)
|
||||
require.NoError(t, err)
|
||||
//Validate PutPrivateData calls
|
||||
expectedNewAsset := origAsset
|
||||
expectedNewAsset.Owner = myOrg2Clientid
|
||||
expectedNewAssetBytes, err := json.Marshal(expectedNewAsset)
|
||||
require.NoError(t, err)
|
||||
calledCollection, calledId, calledWithAssetBytes := chaincodeStub.PutPrivateDataArgsForCall(0)
|
||||
require.Equal(t, assetCollectionName, calledCollection)
|
||||
require.Equal(t, "id1", calledId)
|
||||
require.Equal(t, expectedNewAssetBytes, calledWithAssetBytes)
|
||||
calledCollection, calledId = chaincodeStub.DelPrivateDataArgsForCall(0)
|
||||
require.Equal(t, myOrg1PrivCollection, calledCollection)
|
||||
require.Equal(t, "id1", calledId)
|
||||
|
||||
calledCollection, calledId = chaincodeStub.DelPrivateDataArgsForCall(1)
|
||||
require.Equal(t, assetCollectionName, calledCollection)
|
||||
require.Equal(t, transferAgreementObjectType+"id1", calledId)
|
||||
|
||||
}
|
||||
|
||||
func TestTransferAssetByNonOwner(t *testing.T) {
|
||||
transactionContext, chaincodeStub := prepMocksAsOrg1()
|
||||
assetTransferCC := chaincode.SmartContract{}
|
||||
assetNewOwner := &assetTransferTransientInput{
|
||||
ID: "id1",
|
||||
BuyerMSP: myOrg1Msp,
|
||||
}
|
||||
setReturnAssetOwnerInTransientMap(t, chaincodeStub, assetNewOwner)
|
||||
//Try to transfer asset owned by Org2
|
||||
org2Asset := chaincode.Asset{
|
||||
ID: "id1",
|
||||
Type: "testfulasset",
|
||||
Color: "gray",
|
||||
Size: 7,
|
||||
Owner: myOrg2Clientid,
|
||||
}
|
||||
setReturnPrivateDataInStub(t, chaincodeStub, &org2Asset)
|
||||
err := assetTransferCC.TransferAsset(transactionContext)
|
||||
require.EqualError(t, err, "failed transfer verification: error: submitting client identity does not own asset")
|
||||
}
|
||||
|
||||
func TestTransferAssetWithoutAnAgreement(t *testing.T) {
|
||||
transactionContext, chaincodeStub := prepMocksAsOrg1()
|
||||
assetTransferCC := chaincode.SmartContract{}
|
||||
assetNewOwner := &assetTransferTransientInput{
|
||||
ID: "id1",
|
||||
BuyerMSP: myOrg1Msp,
|
||||
}
|
||||
setReturnAssetOwnerInTransientMap(t, chaincodeStub, assetNewOwner)
|
||||
orgAsset := chaincode.Asset{
|
||||
ID: "id1",
|
||||
Type: "testfulasset",
|
||||
Color: "gray",
|
||||
Size: 7,
|
||||
Owner: myOrg1Clientid,
|
||||
}
|
||||
setReturnPrivateDataInStub(t, chaincodeStub, &orgAsset)
|
||||
//to ensure we pass data hash verification
|
||||
chaincodeStub.GetPrivateDataHashReturns([]byte("datahash"), nil)
|
||||
chaincodeStub.CreateCompositeKeyReturns(transferAgreementObjectType+"id1", nil)
|
||||
//ReadTransferAgreement call returns no buyer client ID
|
||||
chaincodeStub.GetPrivateDataReturnsOnCall(1, []byte{}, nil)
|
||||
|
||||
err := assetTransferCC.TransferAsset(transactionContext)
|
||||
require.EqualError(t, err, "BuyerID not found in TransferAgreement for id1")
|
||||
}
|
||||
|
||||
func TestTransferAssetNonMatchingAppraisalValue(t *testing.T) {
|
||||
transactionContext, chaincodeStub := prepMocksAsOrg1()
|
||||
assetTransferCC := chaincode.SmartContract{}
|
||||
assetNewOwner := &assetTransferTransientInput{
|
||||
ID: "id1",
|
||||
BuyerMSP: myOrg2Msp,
|
||||
}
|
||||
setReturnAssetOwnerInTransientMap(t, chaincodeStub, assetNewOwner)
|
||||
|
||||
orgAsset := chaincode.Asset{
|
||||
ID: "id1",
|
||||
Type: "testfulasset",
|
||||
Color: "gray",
|
||||
Size: 7,
|
||||
Owner: myOrg1Clientid,
|
||||
}
|
||||
setReturnPrivateDataInStub(t, chaincodeStub, &orgAsset)
|
||||
chaincodeStub.CreateCompositeKeyReturns(transferAgreementObjectType+"id1", nil)
|
||||
//data hash different in each collection
|
||||
chaincodeStub.GetPrivateDataHashReturnsOnCall(0, []byte("datahash1"), nil)
|
||||
chaincodeStub.GetPrivateDataHashReturnsOnCall(1, []byte("datahash2"), nil)
|
||||
|
||||
err := assetTransferCC.TransferAsset(transactionContext)
|
||||
require.Error(t, err, "Expected failed hash verification")
|
||||
require.Contains(t, err.Error(), "failed transfer verification: hash for appraised value")
|
||||
}
|
||||
|
||||
func prepMocksAsOrg1() (*mocks.TransactionContext, *mocks.ChaincodeStub) {
|
||||
return prepMocks(myOrg1Msp, myOrg1Clientid)
|
||||
}
|
||||
func prepMocksAsOrg2() (*mocks.TransactionContext, *mocks.ChaincodeStub) {
|
||||
return prepMocks(myOrg2Msp, myOrg2Clientid)
|
||||
}
|
||||
func prepMocks(orgMSP, clientId string) (*mocks.TransactionContext, *mocks.ChaincodeStub) {
|
||||
chaincodeStub := &mocks.ChaincodeStub{}
|
||||
transactionContext := &mocks.TransactionContext{}
|
||||
transactionContext.GetStubReturns(chaincodeStub)
|
||||
|
||||
clientIdentity := &mocks.ClientIdentity{}
|
||||
clientIdentity.GetMSPIDReturns(orgMSP, nil)
|
||||
clientIdentity.GetIDReturns(clientId, nil)
|
||||
//set matching msp ID using peer shim env variable
|
||||
os.Setenv("CORE_PEER_LOCALMSPID", orgMSP)
|
||||
transactionContext.GetClientIdentityReturns(clientIdentity)
|
||||
return transactionContext, chaincodeStub
|
||||
}
|
||||
|
||||
func setReturnAssetPrivateDetailsInTransientMap(t *testing.T, chaincodeStub *mocks.ChaincodeStub, assetPrivDetail *chaincode.AssetPrivateDetails) []byte {
|
||||
assetOwnerBytes := []byte{}
|
||||
if assetPrivDetail != nil {
|
||||
var err error
|
||||
assetOwnerBytes, err = json.Marshal(assetPrivDetail)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
assetPropMap := map[string][]byte{
|
||||
"asset_value": assetOwnerBytes,
|
||||
}
|
||||
chaincodeStub.GetTransientReturns(assetPropMap, nil)
|
||||
return assetOwnerBytes
|
||||
}
|
||||
|
||||
func setReturnAssetOwnerInTransientMap(t *testing.T, chaincodeStub *mocks.ChaincodeStub, assetOwner *assetTransferTransientInput) []byte {
|
||||
assetOwnerBytes := []byte{}
|
||||
if assetOwner != nil {
|
||||
var err error
|
||||
assetOwnerBytes, err = json.Marshal(assetOwner)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
assetPropMap := map[string][]byte{
|
||||
"asset_owner": assetOwnerBytes,
|
||||
}
|
||||
chaincodeStub.GetTransientReturns(assetPropMap, nil)
|
||||
return assetOwnerBytes
|
||||
}
|
||||
|
||||
func setReturnAssetPropsInTransientMap(t *testing.T, chaincodeStub *mocks.ChaincodeStub, testAsset *assetTransientInput) []byte {
|
||||
assetBytes := []byte{}
|
||||
if testAsset != nil {
|
||||
var err error
|
||||
assetBytes, err = json.Marshal(testAsset)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
assetPropMap := map[string][]byte{
|
||||
"asset_properties": assetBytes,
|
||||
}
|
||||
chaincodeStub.GetTransientReturns(assetPropMap, nil)
|
||||
return assetBytes
|
||||
}
|
||||
|
||||
func setReturnPrivateDataInStub(t *testing.T, chaincodeStub *mocks.ChaincodeStub, testAsset *chaincode.Asset) []byte {
|
||||
if testAsset == nil {
|
||||
chaincodeStub.GetPrivateDataReturns(nil, nil)
|
||||
return nil
|
||||
} else {
|
||||
var err error
|
||||
assetBytes, err := json.Marshal(testAsset)
|
||||
require.NoError(t, err)
|
||||
chaincodeStub.GetPrivateDataReturns(assetBytes, nil)
|
||||
return assetBytes
|
||||
}
|
||||
}
|
||||
|
||||
func setReturnAssetPrivateDetailsInStub(t *testing.T, chaincodeStub *mocks.ChaincodeStub, testAsset *chaincode.AssetPrivateDetails) []byte {
|
||||
if testAsset == nil {
|
||||
chaincodeStub.GetPrivateDataReturns(nil, nil)
|
||||
return nil
|
||||
} else {
|
||||
var err error
|
||||
assetBytes, err := json.Marshal(testAsset)
|
||||
require.NoError(t, err)
|
||||
chaincodeStub.GetPrivateDataReturns(assetBytes, nil)
|
||||
return assetBytes
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,399 @@
|
|||
// Code generated by counterfeiter. DO NOT EDIT.
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type ClientIdentity struct {
|
||||
AssertAttributeValueStub func(string, string) error
|
||||
assertAttributeValueMutex sync.RWMutex
|
||||
assertAttributeValueArgsForCall []struct {
|
||||
arg1 string
|
||||
arg2 string
|
||||
}
|
||||
assertAttributeValueReturns struct {
|
||||
result1 error
|
||||
}
|
||||
assertAttributeValueReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
GetAttributeValueStub func(string) (string, bool, error)
|
||||
getAttributeValueMutex sync.RWMutex
|
||||
getAttributeValueArgsForCall []struct {
|
||||
arg1 string
|
||||
}
|
||||
getAttributeValueReturns struct {
|
||||
result1 string
|
||||
result2 bool
|
||||
result3 error
|
||||
}
|
||||
getAttributeValueReturnsOnCall map[int]struct {
|
||||
result1 string
|
||||
result2 bool
|
||||
result3 error
|
||||
}
|
||||
GetIDStub func() (string, error)
|
||||
getIDMutex sync.RWMutex
|
||||
getIDArgsForCall []struct {
|
||||
}
|
||||
getIDReturns struct {
|
||||
result1 string
|
||||
result2 error
|
||||
}
|
||||
getIDReturnsOnCall map[int]struct {
|
||||
result1 string
|
||||
result2 error
|
||||
}
|
||||
GetMSPIDStub func() (string, error)
|
||||
getMSPIDMutex sync.RWMutex
|
||||
getMSPIDArgsForCall []struct {
|
||||
}
|
||||
getMSPIDReturns struct {
|
||||
result1 string
|
||||
result2 error
|
||||
}
|
||||
getMSPIDReturnsOnCall map[int]struct {
|
||||
result1 string
|
||||
result2 error
|
||||
}
|
||||
GetX509CertificateStub func() (*x509.Certificate, error)
|
||||
getX509CertificateMutex sync.RWMutex
|
||||
getX509CertificateArgsForCall []struct {
|
||||
}
|
||||
getX509CertificateReturns struct {
|
||||
result1 *x509.Certificate
|
||||
result2 error
|
||||
}
|
||||
getX509CertificateReturnsOnCall map[int]struct {
|
||||
result1 *x509.Certificate
|
||||
result2 error
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) AssertAttributeValue(arg1 string, arg2 string) error {
|
||||
fake.assertAttributeValueMutex.Lock()
|
||||
ret, specificReturn := fake.assertAttributeValueReturnsOnCall[len(fake.assertAttributeValueArgsForCall)]
|
||||
fake.assertAttributeValueArgsForCall = append(fake.assertAttributeValueArgsForCall, struct {
|
||||
arg1 string
|
||||
arg2 string
|
||||
}{arg1, arg2})
|
||||
fake.recordInvocation("AssertAttributeValue", []interface{}{arg1, arg2})
|
||||
fake.assertAttributeValueMutex.Unlock()
|
||||
if fake.AssertAttributeValueStub != nil {
|
||||
return fake.AssertAttributeValueStub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
fakeReturns := fake.assertAttributeValueReturns
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) AssertAttributeValueCallCount() int {
|
||||
fake.assertAttributeValueMutex.RLock()
|
||||
defer fake.assertAttributeValueMutex.RUnlock()
|
||||
return len(fake.assertAttributeValueArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) AssertAttributeValueCalls(stub func(string, string) error) {
|
||||
fake.assertAttributeValueMutex.Lock()
|
||||
defer fake.assertAttributeValueMutex.Unlock()
|
||||
fake.AssertAttributeValueStub = stub
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) AssertAttributeValueArgsForCall(i int) (string, string) {
|
||||
fake.assertAttributeValueMutex.RLock()
|
||||
defer fake.assertAttributeValueMutex.RUnlock()
|
||||
argsForCall := fake.assertAttributeValueArgsForCall[i]
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) AssertAttributeValueReturns(result1 error) {
|
||||
fake.assertAttributeValueMutex.Lock()
|
||||
defer fake.assertAttributeValueMutex.Unlock()
|
||||
fake.AssertAttributeValueStub = nil
|
||||
fake.assertAttributeValueReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) AssertAttributeValueReturnsOnCall(i int, result1 error) {
|
||||
fake.assertAttributeValueMutex.Lock()
|
||||
defer fake.assertAttributeValueMutex.Unlock()
|
||||
fake.AssertAttributeValueStub = nil
|
||||
if fake.assertAttributeValueReturnsOnCall == nil {
|
||||
fake.assertAttributeValueReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
})
|
||||
}
|
||||
fake.assertAttributeValueReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetAttributeValue(arg1 string) (string, bool, error) {
|
||||
fake.getAttributeValueMutex.Lock()
|
||||
ret, specificReturn := fake.getAttributeValueReturnsOnCall[len(fake.getAttributeValueArgsForCall)]
|
||||
fake.getAttributeValueArgsForCall = append(fake.getAttributeValueArgsForCall, struct {
|
||||
arg1 string
|
||||
}{arg1})
|
||||
fake.recordInvocation("GetAttributeValue", []interface{}{arg1})
|
||||
fake.getAttributeValueMutex.Unlock()
|
||||
if fake.GetAttributeValueStub != nil {
|
||||
return fake.GetAttributeValueStub(arg1)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2, ret.result3
|
||||
}
|
||||
fakeReturns := fake.getAttributeValueReturns
|
||||
return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetAttributeValueCallCount() int {
|
||||
fake.getAttributeValueMutex.RLock()
|
||||
defer fake.getAttributeValueMutex.RUnlock()
|
||||
return len(fake.getAttributeValueArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetAttributeValueCalls(stub func(string) (string, bool, error)) {
|
||||
fake.getAttributeValueMutex.Lock()
|
||||
defer fake.getAttributeValueMutex.Unlock()
|
||||
fake.GetAttributeValueStub = stub
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetAttributeValueArgsForCall(i int) string {
|
||||
fake.getAttributeValueMutex.RLock()
|
||||
defer fake.getAttributeValueMutex.RUnlock()
|
||||
argsForCall := fake.getAttributeValueArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetAttributeValueReturns(result1 string, result2 bool, result3 error) {
|
||||
fake.getAttributeValueMutex.Lock()
|
||||
defer fake.getAttributeValueMutex.Unlock()
|
||||
fake.GetAttributeValueStub = nil
|
||||
fake.getAttributeValueReturns = struct {
|
||||
result1 string
|
||||
result2 bool
|
||||
result3 error
|
||||
}{result1, result2, result3}
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetAttributeValueReturnsOnCall(i int, result1 string, result2 bool, result3 error) {
|
||||
fake.getAttributeValueMutex.Lock()
|
||||
defer fake.getAttributeValueMutex.Unlock()
|
||||
fake.GetAttributeValueStub = nil
|
||||
if fake.getAttributeValueReturnsOnCall == nil {
|
||||
fake.getAttributeValueReturnsOnCall = make(map[int]struct {
|
||||
result1 string
|
||||
result2 bool
|
||||
result3 error
|
||||
})
|
||||
}
|
||||
fake.getAttributeValueReturnsOnCall[i] = struct {
|
||||
result1 string
|
||||
result2 bool
|
||||
result3 error
|
||||
}{result1, result2, result3}
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetID() (string, error) {
|
||||
fake.getIDMutex.Lock()
|
||||
ret, specificReturn := fake.getIDReturnsOnCall[len(fake.getIDArgsForCall)]
|
||||
fake.getIDArgsForCall = append(fake.getIDArgsForCall, struct {
|
||||
}{})
|
||||
fake.recordInvocation("GetID", []interface{}{})
|
||||
fake.getIDMutex.Unlock()
|
||||
if fake.GetIDStub != nil {
|
||||
return fake.GetIDStub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2
|
||||
}
|
||||
fakeReturns := fake.getIDReturns
|
||||
return fakeReturns.result1, fakeReturns.result2
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetIDCallCount() int {
|
||||
fake.getIDMutex.RLock()
|
||||
defer fake.getIDMutex.RUnlock()
|
||||
return len(fake.getIDArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetIDCalls(stub func() (string, error)) {
|
||||
fake.getIDMutex.Lock()
|
||||
defer fake.getIDMutex.Unlock()
|
||||
fake.GetIDStub = stub
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetIDReturns(result1 string, result2 error) {
|
||||
fake.getIDMutex.Lock()
|
||||
defer fake.getIDMutex.Unlock()
|
||||
fake.GetIDStub = nil
|
||||
fake.getIDReturns = struct {
|
||||
result1 string
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetIDReturnsOnCall(i int, result1 string, result2 error) {
|
||||
fake.getIDMutex.Lock()
|
||||
defer fake.getIDMutex.Unlock()
|
||||
fake.GetIDStub = nil
|
||||
if fake.getIDReturnsOnCall == nil {
|
||||
fake.getIDReturnsOnCall = make(map[int]struct {
|
||||
result1 string
|
||||
result2 error
|
||||
})
|
||||
}
|
||||
fake.getIDReturnsOnCall[i] = struct {
|
||||
result1 string
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetMSPID() (string, error) {
|
||||
fake.getMSPIDMutex.Lock()
|
||||
ret, specificReturn := fake.getMSPIDReturnsOnCall[len(fake.getMSPIDArgsForCall)]
|
||||
fake.getMSPIDArgsForCall = append(fake.getMSPIDArgsForCall, struct {
|
||||
}{})
|
||||
fake.recordInvocation("GetMSPID", []interface{}{})
|
||||
fake.getMSPIDMutex.Unlock()
|
||||
if fake.GetMSPIDStub != nil {
|
||||
return fake.GetMSPIDStub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2
|
||||
}
|
||||
fakeReturns := fake.getMSPIDReturns
|
||||
return fakeReturns.result1, fakeReturns.result2
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetMSPIDCallCount() int {
|
||||
fake.getMSPIDMutex.RLock()
|
||||
defer fake.getMSPIDMutex.RUnlock()
|
||||
return len(fake.getMSPIDArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetMSPIDCalls(stub func() (string, error)) {
|
||||
fake.getMSPIDMutex.Lock()
|
||||
defer fake.getMSPIDMutex.Unlock()
|
||||
fake.GetMSPIDStub = stub
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetMSPIDReturns(result1 string, result2 error) {
|
||||
fake.getMSPIDMutex.Lock()
|
||||
defer fake.getMSPIDMutex.Unlock()
|
||||
fake.GetMSPIDStub = nil
|
||||
fake.getMSPIDReturns = struct {
|
||||
result1 string
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetMSPIDReturnsOnCall(i int, result1 string, result2 error) {
|
||||
fake.getMSPIDMutex.Lock()
|
||||
defer fake.getMSPIDMutex.Unlock()
|
||||
fake.GetMSPIDStub = nil
|
||||
if fake.getMSPIDReturnsOnCall == nil {
|
||||
fake.getMSPIDReturnsOnCall = make(map[int]struct {
|
||||
result1 string
|
||||
result2 error
|
||||
})
|
||||
}
|
||||
fake.getMSPIDReturnsOnCall[i] = struct {
|
||||
result1 string
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetX509Certificate() (*x509.Certificate, error) {
|
||||
fake.getX509CertificateMutex.Lock()
|
||||
ret, specificReturn := fake.getX509CertificateReturnsOnCall[len(fake.getX509CertificateArgsForCall)]
|
||||
fake.getX509CertificateArgsForCall = append(fake.getX509CertificateArgsForCall, struct {
|
||||
}{})
|
||||
fake.recordInvocation("GetX509Certificate", []interface{}{})
|
||||
fake.getX509CertificateMutex.Unlock()
|
||||
if fake.GetX509CertificateStub != nil {
|
||||
return fake.GetX509CertificateStub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2
|
||||
}
|
||||
fakeReturns := fake.getX509CertificateReturns
|
||||
return fakeReturns.result1, fakeReturns.result2
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetX509CertificateCallCount() int {
|
||||
fake.getX509CertificateMutex.RLock()
|
||||
defer fake.getX509CertificateMutex.RUnlock()
|
||||
return len(fake.getX509CertificateArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetX509CertificateCalls(stub func() (*x509.Certificate, error)) {
|
||||
fake.getX509CertificateMutex.Lock()
|
||||
defer fake.getX509CertificateMutex.Unlock()
|
||||
fake.GetX509CertificateStub = stub
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetX509CertificateReturns(result1 *x509.Certificate, result2 error) {
|
||||
fake.getX509CertificateMutex.Lock()
|
||||
defer fake.getX509CertificateMutex.Unlock()
|
||||
fake.GetX509CertificateStub = nil
|
||||
fake.getX509CertificateReturns = struct {
|
||||
result1 *x509.Certificate
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) GetX509CertificateReturnsOnCall(i int, result1 *x509.Certificate, result2 error) {
|
||||
fake.getX509CertificateMutex.Lock()
|
||||
defer fake.getX509CertificateMutex.Unlock()
|
||||
fake.GetX509CertificateStub = nil
|
||||
if fake.getX509CertificateReturnsOnCall == nil {
|
||||
fake.getX509CertificateReturnsOnCall = make(map[int]struct {
|
||||
result1 *x509.Certificate
|
||||
result2 error
|
||||
})
|
||||
}
|
||||
fake.getX509CertificateReturnsOnCall[i] = struct {
|
||||
result1 *x509.Certificate
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.assertAttributeValueMutex.RLock()
|
||||
defer fake.assertAttributeValueMutex.RUnlock()
|
||||
fake.getAttributeValueMutex.RLock()
|
||||
defer fake.getAttributeValueMutex.RUnlock()
|
||||
fake.getIDMutex.RLock()
|
||||
defer fake.getIDMutex.RUnlock()
|
||||
fake.getMSPIDMutex.RLock()
|
||||
defer fake.getMSPIDMutex.RUnlock()
|
||||
fake.getX509CertificateMutex.RLock()
|
||||
defer fake.getX509CertificateMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
}
|
||||
return copiedInvocations
|
||||
}
|
||||
|
||||
func (fake *ClientIdentity) recordInvocation(key string, args []interface{}) {
|
||||
fake.invocationsMutex.Lock()
|
||||
defer fake.invocationsMutex.Unlock()
|
||||
if fake.invocations == nil {
|
||||
fake.invocations = map[string][][]interface{}{}
|
||||
}
|
||||
if fake.invocations[key] == nil {
|
||||
fake.invocations[key] = [][]interface{}{}
|
||||
}
|
||||
fake.invocations[key] = append(fake.invocations[key], args)
|
||||
}
|
||||
|
|
@ -0,0 +1,232 @@
|
|||
// Code generated by counterfeiter. DO NOT EDIT.
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/hyperledger/fabric-protos-go/ledger/queryresult"
|
||||
)
|
||||
|
||||
type StateQueryIterator struct {
|
||||
CloseStub func() error
|
||||
closeMutex sync.RWMutex
|
||||
closeArgsForCall []struct {
|
||||
}
|
||||
closeReturns struct {
|
||||
result1 error
|
||||
}
|
||||
closeReturnsOnCall map[int]struct {
|
||||
result1 error
|
||||
}
|
||||
HasNextStub func() bool
|
||||
hasNextMutex sync.RWMutex
|
||||
hasNextArgsForCall []struct {
|
||||
}
|
||||
hasNextReturns struct {
|
||||
result1 bool
|
||||
}
|
||||
hasNextReturnsOnCall map[int]struct {
|
||||
result1 bool
|
||||
}
|
||||
NextStub func() (*queryresult.KV, error)
|
||||
nextMutex sync.RWMutex
|
||||
nextArgsForCall []struct {
|
||||
}
|
||||
nextReturns struct {
|
||||
result1 *queryresult.KV
|
||||
result2 error
|
||||
}
|
||||
nextReturnsOnCall map[int]struct {
|
||||
result1 *queryresult.KV
|
||||
result2 error
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) Close() error {
|
||||
fake.closeMutex.Lock()
|
||||
ret, specificReturn := fake.closeReturnsOnCall[len(fake.closeArgsForCall)]
|
||||
fake.closeArgsForCall = append(fake.closeArgsForCall, struct {
|
||||
}{})
|
||||
fake.recordInvocation("Close", []interface{}{})
|
||||
fake.closeMutex.Unlock()
|
||||
if fake.CloseStub != nil {
|
||||
return fake.CloseStub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
fakeReturns := fake.closeReturns
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) CloseCallCount() int {
|
||||
fake.closeMutex.RLock()
|
||||
defer fake.closeMutex.RUnlock()
|
||||
return len(fake.closeArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) CloseCalls(stub func() error) {
|
||||
fake.closeMutex.Lock()
|
||||
defer fake.closeMutex.Unlock()
|
||||
fake.CloseStub = stub
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) CloseReturns(result1 error) {
|
||||
fake.closeMutex.Lock()
|
||||
defer fake.closeMutex.Unlock()
|
||||
fake.CloseStub = nil
|
||||
fake.closeReturns = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) CloseReturnsOnCall(i int, result1 error) {
|
||||
fake.closeMutex.Lock()
|
||||
defer fake.closeMutex.Unlock()
|
||||
fake.CloseStub = nil
|
||||
if fake.closeReturnsOnCall == nil {
|
||||
fake.closeReturnsOnCall = make(map[int]struct {
|
||||
result1 error
|
||||
})
|
||||
}
|
||||
fake.closeReturnsOnCall[i] = struct {
|
||||
result1 error
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) HasNext() bool {
|
||||
fake.hasNextMutex.Lock()
|
||||
ret, specificReturn := fake.hasNextReturnsOnCall[len(fake.hasNextArgsForCall)]
|
||||
fake.hasNextArgsForCall = append(fake.hasNextArgsForCall, struct {
|
||||
}{})
|
||||
fake.recordInvocation("HasNext", []interface{}{})
|
||||
fake.hasNextMutex.Unlock()
|
||||
if fake.HasNextStub != nil {
|
||||
return fake.HasNextStub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
fakeReturns := fake.hasNextReturns
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) HasNextCallCount() int {
|
||||
fake.hasNextMutex.RLock()
|
||||
defer fake.hasNextMutex.RUnlock()
|
||||
return len(fake.hasNextArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) HasNextCalls(stub func() bool) {
|
||||
fake.hasNextMutex.Lock()
|
||||
defer fake.hasNextMutex.Unlock()
|
||||
fake.HasNextStub = stub
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) HasNextReturns(result1 bool) {
|
||||
fake.hasNextMutex.Lock()
|
||||
defer fake.hasNextMutex.Unlock()
|
||||
fake.HasNextStub = nil
|
||||
fake.hasNextReturns = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) HasNextReturnsOnCall(i int, result1 bool) {
|
||||
fake.hasNextMutex.Lock()
|
||||
defer fake.hasNextMutex.Unlock()
|
||||
fake.HasNextStub = nil
|
||||
if fake.hasNextReturnsOnCall == nil {
|
||||
fake.hasNextReturnsOnCall = make(map[int]struct {
|
||||
result1 bool
|
||||
})
|
||||
}
|
||||
fake.hasNextReturnsOnCall[i] = struct {
|
||||
result1 bool
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) Next() (*queryresult.KV, error) {
|
||||
fake.nextMutex.Lock()
|
||||
ret, specificReturn := fake.nextReturnsOnCall[len(fake.nextArgsForCall)]
|
||||
fake.nextArgsForCall = append(fake.nextArgsForCall, struct {
|
||||
}{})
|
||||
fake.recordInvocation("Next", []interface{}{})
|
||||
fake.nextMutex.Unlock()
|
||||
if fake.NextStub != nil {
|
||||
return fake.NextStub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2
|
||||
}
|
||||
fakeReturns := fake.nextReturns
|
||||
return fakeReturns.result1, fakeReturns.result2
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) NextCallCount() int {
|
||||
fake.nextMutex.RLock()
|
||||
defer fake.nextMutex.RUnlock()
|
||||
return len(fake.nextArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) NextCalls(stub func() (*queryresult.KV, error)) {
|
||||
fake.nextMutex.Lock()
|
||||
defer fake.nextMutex.Unlock()
|
||||
fake.NextStub = stub
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) NextReturns(result1 *queryresult.KV, result2 error) {
|
||||
fake.nextMutex.Lock()
|
||||
defer fake.nextMutex.Unlock()
|
||||
fake.NextStub = nil
|
||||
fake.nextReturns = struct {
|
||||
result1 *queryresult.KV
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) NextReturnsOnCall(i int, result1 *queryresult.KV, result2 error) {
|
||||
fake.nextMutex.Lock()
|
||||
defer fake.nextMutex.Unlock()
|
||||
fake.NextStub = nil
|
||||
if fake.nextReturnsOnCall == nil {
|
||||
fake.nextReturnsOnCall = make(map[int]struct {
|
||||
result1 *queryresult.KV
|
||||
result2 error
|
||||
})
|
||||
}
|
||||
fake.nextReturnsOnCall[i] = struct {
|
||||
result1 *queryresult.KV
|
||||
result2 error
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.closeMutex.RLock()
|
||||
defer fake.closeMutex.RUnlock()
|
||||
fake.hasNextMutex.RLock()
|
||||
defer fake.hasNextMutex.RUnlock()
|
||||
fake.nextMutex.RLock()
|
||||
defer fake.nextMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
}
|
||||
return copiedInvocations
|
||||
}
|
||||
|
||||
func (fake *StateQueryIterator) recordInvocation(key string, args []interface{}) {
|
||||
fake.invocationsMutex.Lock()
|
||||
defer fake.invocationsMutex.Unlock()
|
||||
if fake.invocations == nil {
|
||||
fake.invocations = map[string][][]interface{}{}
|
||||
}
|
||||
if fake.invocations[key] == nil {
|
||||
fake.invocations[key] = [][]interface{}{}
|
||||
}
|
||||
fake.invocations[key] = append(fake.invocations[key], args)
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
// Code generated by counterfeiter. DO NOT EDIT.
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/hyperledger/fabric-chaincode-go/pkg/cid"
|
||||
"github.com/hyperledger/fabric-chaincode-go/shim"
|
||||
)
|
||||
|
||||
type TransactionContext struct {
|
||||
GetClientIdentityStub func() cid.ClientIdentity
|
||||
getClientIdentityMutex sync.RWMutex
|
||||
getClientIdentityArgsForCall []struct {
|
||||
}
|
||||
getClientIdentityReturns struct {
|
||||
result1 cid.ClientIdentity
|
||||
}
|
||||
getClientIdentityReturnsOnCall map[int]struct {
|
||||
result1 cid.ClientIdentity
|
||||
}
|
||||
GetStubStub func() shim.ChaincodeStubInterface
|
||||
getStubMutex sync.RWMutex
|
||||
getStubArgsForCall []struct {
|
||||
}
|
||||
getStubReturns struct {
|
||||
result1 shim.ChaincodeStubInterface
|
||||
}
|
||||
getStubReturnsOnCall map[int]struct {
|
||||
result1 shim.ChaincodeStubInterface
|
||||
}
|
||||
invocations map[string][][]interface{}
|
||||
invocationsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fake *TransactionContext) GetClientIdentity() cid.ClientIdentity {
|
||||
fake.getClientIdentityMutex.Lock()
|
||||
ret, specificReturn := fake.getClientIdentityReturnsOnCall[len(fake.getClientIdentityArgsForCall)]
|
||||
fake.getClientIdentityArgsForCall = append(fake.getClientIdentityArgsForCall, struct {
|
||||
}{})
|
||||
fake.recordInvocation("GetClientIdentity", []interface{}{})
|
||||
fake.getClientIdentityMutex.Unlock()
|
||||
if fake.GetClientIdentityStub != nil {
|
||||
return fake.GetClientIdentityStub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
fakeReturns := fake.getClientIdentityReturns
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *TransactionContext) GetClientIdentityCallCount() int {
|
||||
fake.getClientIdentityMutex.RLock()
|
||||
defer fake.getClientIdentityMutex.RUnlock()
|
||||
return len(fake.getClientIdentityArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *TransactionContext) GetClientIdentityCalls(stub func() cid.ClientIdentity) {
|
||||
fake.getClientIdentityMutex.Lock()
|
||||
defer fake.getClientIdentityMutex.Unlock()
|
||||
fake.GetClientIdentityStub = stub
|
||||
}
|
||||
|
||||
func (fake *TransactionContext) GetClientIdentityReturns(result1 cid.ClientIdentity) {
|
||||
fake.getClientIdentityMutex.Lock()
|
||||
defer fake.getClientIdentityMutex.Unlock()
|
||||
fake.GetClientIdentityStub = nil
|
||||
fake.getClientIdentityReturns = struct {
|
||||
result1 cid.ClientIdentity
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *TransactionContext) GetClientIdentityReturnsOnCall(i int, result1 cid.ClientIdentity) {
|
||||
fake.getClientIdentityMutex.Lock()
|
||||
defer fake.getClientIdentityMutex.Unlock()
|
||||
fake.GetClientIdentityStub = nil
|
||||
if fake.getClientIdentityReturnsOnCall == nil {
|
||||
fake.getClientIdentityReturnsOnCall = make(map[int]struct {
|
||||
result1 cid.ClientIdentity
|
||||
})
|
||||
}
|
||||
fake.getClientIdentityReturnsOnCall[i] = struct {
|
||||
result1 cid.ClientIdentity
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *TransactionContext) GetStub() shim.ChaincodeStubInterface {
|
||||
fake.getStubMutex.Lock()
|
||||
ret, specificReturn := fake.getStubReturnsOnCall[len(fake.getStubArgsForCall)]
|
||||
fake.getStubArgsForCall = append(fake.getStubArgsForCall, struct {
|
||||
}{})
|
||||
fake.recordInvocation("GetStub", []interface{}{})
|
||||
fake.getStubMutex.Unlock()
|
||||
if fake.GetStubStub != nil {
|
||||
return fake.GetStubStub()
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1
|
||||
}
|
||||
fakeReturns := fake.getStubReturns
|
||||
return fakeReturns.result1
|
||||
}
|
||||
|
||||
func (fake *TransactionContext) GetStubCallCount() int {
|
||||
fake.getStubMutex.RLock()
|
||||
defer fake.getStubMutex.RUnlock()
|
||||
return len(fake.getStubArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *TransactionContext) GetStubCalls(stub func() shim.ChaincodeStubInterface) {
|
||||
fake.getStubMutex.Lock()
|
||||
defer fake.getStubMutex.Unlock()
|
||||
fake.GetStubStub = stub
|
||||
}
|
||||
|
||||
func (fake *TransactionContext) GetStubReturns(result1 shim.ChaincodeStubInterface) {
|
||||
fake.getStubMutex.Lock()
|
||||
defer fake.getStubMutex.Unlock()
|
||||
fake.GetStubStub = nil
|
||||
fake.getStubReturns = struct {
|
||||
result1 shim.ChaincodeStubInterface
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *TransactionContext) GetStubReturnsOnCall(i int, result1 shim.ChaincodeStubInterface) {
|
||||
fake.getStubMutex.Lock()
|
||||
defer fake.getStubMutex.Unlock()
|
||||
fake.GetStubStub = nil
|
||||
if fake.getStubReturnsOnCall == nil {
|
||||
fake.getStubReturnsOnCall = make(map[int]struct {
|
||||
result1 shim.ChaincodeStubInterface
|
||||
})
|
||||
}
|
||||
fake.getStubReturnsOnCall[i] = struct {
|
||||
result1 shim.ChaincodeStubInterface
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *TransactionContext) Invocations() map[string][][]interface{} {
|
||||
fake.invocationsMutex.RLock()
|
||||
defer fake.invocationsMutex.RUnlock()
|
||||
fake.getClientIdentityMutex.RLock()
|
||||
defer fake.getClientIdentityMutex.RUnlock()
|
||||
fake.getStubMutex.RLock()
|
||||
defer fake.getStubMutex.RUnlock()
|
||||
copiedInvocations := map[string][][]interface{}{}
|
||||
for key, value := range fake.invocations {
|
||||
copiedInvocations[key] = value
|
||||
}
|
||||
return copiedInvocations
|
||||
}
|
||||
|
||||
func (fake *TransactionContext) recordInvocation(key string, args []interface{}) {
|
||||
fake.invocationsMutex.Lock()
|
||||
defer fake.invocationsMutex.Unlock()
|
||||
if fake.invocations == nil {
|
||||
fake.invocations = map[string][][]interface{}{}
|
||||
}
|
||||
if fake.invocations[key] == nil {
|
||||
fake.invocations[key] = [][]interface{}{}
|
||||
}
|
||||
fake.invocations[key] = append(fake.invocations[key], args)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
module github.com/hyperledger/fabric-samples/asset-transfer-private-data/chaincode-go/go
|
||||
module github.com/hyperledger/fabric-samples/asset-transfer-private-data/chaincode-go
|
||||
|
||||
go 1.14
|
||||
|
||||
|
|
@ -11,14 +11,15 @@ require (
|
|||
github.com/golang/protobuf v1.4.2 // indirect
|
||||
github.com/hyperledger/fabric-chaincode-go v0.0.0-20200511190512-bcfeb58dd83a
|
||||
github.com/hyperledger/fabric-contract-api-go v1.1.0
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20200707132912-fee30f3ccd23 // indirect
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20200707132912-fee30f3ccd23
|
||||
github.com/mailru/easyjson v0.7.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.6.0 // indirect
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect
|
||||
golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666 // indirect
|
||||
google.golang.org/genproto v0.0.0-20200721032028-5044d0edf986 // indirect
|
||||
google.golang.org/grpc v1.30.0 // indirect
|
||||
google.golang.org/protobuf v1.25.0 // indirect
|
||||
google.golang.org/protobuf v1.25.0
|
||||
gopkg.in/yaml.v2 v2.3.0 // indirect
|
||||
)
|
||||
|
|
|
|||
23
asset-transfer-private-data/chaincode-go/main.go
Normal file
23
asset-transfer-private-data/chaincode-go/main.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/hyperledger/fabric-contract-api-go/contractapi"
|
||||
"github.com/hyperledger/fabric-samples/asset-transfer-private-data/chaincode-go/chaincode"
|
||||
)
|
||||
|
||||
func main() {
|
||||
assetChaincode, err := contractapi.NewChaincode(&chaincode.SmartContract{})
|
||||
if err != nil {
|
||||
log.Panicf("Error creating asset-transfer-private-data chaincode: %v", err)
|
||||
}
|
||||
|
||||
if err := assetChaincode.Start(); err != nil {
|
||||
log.Panicf("Error starting asset-transfer-private-data chaincode: %v", err)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue