More Gateway asset-transfer-basic tweaks to support docs (#556)

Signed-off-by: Mark S. Lewis <mark_lewis@uk.ibm.com>
This commit is contained in:
Mark S. Lewis 2021-12-10 08:51:21 +00:00 committed by GitHub
parent 9df7e9f86d
commit 72559dfbb5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 56 additions and 37 deletions

View file

@ -31,6 +31,7 @@ const tlsCertPath = path.resolve(cryptoPath, 'peers', 'peer0.org1.example.com',
const peerEndpoint = 'localhost:7051'; const peerEndpoint = 'localhost:7051';
const utf8Decoder = new TextDecoder(); const utf8Decoder = new TextDecoder();
const assetId = `asset${Date.now()}`;
async function main(): Promise<void> { async function main(): Promise<void> {
// The gRPC client connection should be shared by all Gateway connections to this endpoint. // The gRPC client connection should be shared by all Gateway connections to this endpoint.
@ -59,7 +60,7 @@ async function main(): Promise<void> {
await createAsset(contract); await createAsset(contract);
// Update an existing asset asynchronously. // Update an existing asset asynchronously.
await updateAssetAsync(contract); await transferAssetAsync(contract);
// Get the asset details by assetID. // Get the asset details by assetID.
await readAssetByID(contract); await readAssetByID(contract);
@ -124,9 +125,8 @@ async function getAllAssets(contract: Contract): Promise<void> {
* Submit a transaction synchronously, blocking until it has been committed to the ledger. * Submit a transaction synchronously, blocking until it has been committed to the ledger.
*/ */
async function createAsset(contract: Contract): Promise<void> { async function createAsset(contract: Contract): Promise<void> {
console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments'); console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID, Color, Size, Owner and AppraisedValue arguments');
const assetId = `asset${Date.now()}`;
await contract.submitTransaction( await contract.submitTransaction(
'CreateAsset', 'CreateAsset',
assetId, assetId,
@ -143,14 +143,15 @@ async function createAsset(contract: Contract): Promise<void> {
* Submit transaction asynchronously, allowing the application to process the smart contract response (e.g. update a UI) * Submit transaction asynchronously, allowing the application to process the smart contract response (e.g. update a UI)
* while waiting for the commit notification. * while waiting for the commit notification.
*/ */
async function updateAssetAsync(contract: Contract): Promise<void> { async function transferAssetAsync(contract: Contract): Promise<void> {
console.log('\n--> Async Submit Transaction: UpdateAsset, updates existing asset with ID, color, owner, size, and appraisedValue arguments'); console.log('\n--> Async Submit Transaction: TransferAsset, updates existing asset owner');
const commit = await contract.submitAsync('UpdateAsset', { const commit = await contract.submitAsync('TransferAsset', {
arguments: ['asset1', 'blue', '5', 'Tomoko', '400'], arguments: [assetId, 'Saptha'],
}); });
const oldOwner = utf8Decoder.decode(commit.getResult());
console.log('*** Transaction submitted successfully'); console.log(`*** Successfully submitted transaction to transfer ownership from ${oldOwner} to Saptha`);
console.log('*** Waiting for transaction commit'); console.log('*** Waiting for transaction commit');
const status = await commit.getStatus(); const status = await commit.getStatus();
@ -162,9 +163,9 @@ async function updateAssetAsync(contract: Contract): Promise<void> {
} }
async function readAssetByID(contract: Contract): Promise<void> { async function readAssetByID(contract: Contract): Promise<void> {
console.log('\n--> Evaluate Transaction: ReadAsset, function returns "asset1" attributes'); console.log('\n--> Evaluate Transaction: ReadAsset, function returns asset attributes');
const resultBytes = await contract.evaluateTransaction('ReadAsset', 'asset1'); const resultBytes = await contract.evaluateTransaction('ReadAsset', assetId);
const resultJson = utf8Decoder.decode(resultBytes); const resultJson = utf8Decoder.decode(resultBytes);
const result = JSON.parse(resultJson); const result = JSON.parse(resultJson);

View file

@ -161,20 +161,27 @@ func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface,
return assetJSON != nil, nil return assetJSON != nil, nil
} }
// TransferAsset updates the owner field of asset with given id in world state. // TransferAsset updates the owner field of asset with given id in world state, and returns the old owner.
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error { func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) (string, error) {
asset, err := s.ReadAsset(ctx, id) asset, err := s.ReadAsset(ctx, id)
if err != nil { if err != nil {
return err return "", err
} }
oldOwner := asset.Owner
asset.Owner = newOwner asset.Owner = newOwner
assetJSON, err := json.Marshal(asset) assetJSON, err := json.Marshal(asset)
if err != nil { if err != nil {
return err return "", err
} }
return ctx.GetStub().PutState(id, assetJSON) err = ctx.GetStub().PutState(id, assetJSON)
if err != nil {
return "", err
}
return oldOwner, nil
} }
// GetAllAssets returns all assets found in world state // GetAllAssets returns all assets found in world state
@ -223,9 +230,9 @@ func main() {
} }
server := &shim.ChaincodeServer{ server := &shim.ChaincodeServer{
CCID: config.CCID, CCID: config.CCID,
Address: config.Address, Address: config.Address,
CC: chaincode, CC: chaincode,
TLSProps: getTLSProperties(), TLSProps: getTLSProperties(),
} }
@ -265,9 +272,9 @@ func getTLSProperties() shim.TLSProperties {
} }
return shim.TLSProperties{ return shim.TLSProperties{
Disabled: tlsDisabled, Disabled: tlsDisabled,
Key: keyBytes, Key: keyBytes,
Cert: certBytes, Cert: certBytes,
ClientCACerts: clientCACertBytes, ClientCACerts: clientCACertBytes,
} }
} }
@ -284,7 +291,7 @@ func getEnvOrDefault(env, defaultVal string) string {
// cannot be parsed! // cannot be parsed!
func getBoolOrDefault(value string, defaultVal bool) bool { func getBoolOrDefault(value string, defaultVal bool) bool {
parsed, err := strconv.ParseBool(value) parsed, err := strconv.ParseBool(value)
if err!= nil { if err != nil {
return defaultVal return defaultVal
} }
return parsed return parsed

View file

@ -142,20 +142,27 @@ func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface,
return assetJSON != nil, nil return assetJSON != nil, nil
} }
// TransferAsset updates the owner field of asset with given id in world state. // TransferAsset updates the owner field of asset with given id in world state, and returns the old owner.
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error { func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) (string, error) {
asset, err := s.ReadAsset(ctx, id) asset, err := s.ReadAsset(ctx, id)
if err != nil { if err != nil {
return err return "", err
} }
oldOwner := asset.Owner
asset.Owner = newOwner asset.Owner = newOwner
assetJSON, err := json.Marshal(asset) assetJSON, err := json.Marshal(asset)
if err != nil { if err != nil {
return err return "", err
} }
return ctx.GetStub().PutState(id, assetJSON) err = ctx.GetStub().PutState(id, assetJSON)
if err != nil {
return "", err
}
return oldOwner, nil
} }
// GetAllAssets returns all assets found in world state // GetAllAssets returns all assets found in world state

View file

@ -143,11 +143,11 @@ func TestTransferAsset(t *testing.T) {
chaincodeStub.GetStateReturns(bytes, nil) chaincodeStub.GetStateReturns(bytes, nil)
assetTransfer := chaincode.SmartContract{} assetTransfer := chaincode.SmartContract{}
err = assetTransfer.TransferAsset(transactionContext, "", "") _, err = assetTransfer.TransferAsset(transactionContext, "", "")
require.NoError(t, err) require.NoError(t, err)
chaincodeStub.GetStateReturns(nil, fmt.Errorf("unable to retrieve asset")) chaincodeStub.GetStateReturns(nil, fmt.Errorf("unable to retrieve asset"))
err = assetTransfer.TransferAsset(transactionContext, "", "") _, err = assetTransfer.TransferAsset(transactionContext, "", "")
require.EqualError(t, err, "failed to read from world state: unable to retrieve asset") require.EqualError(t, err, "failed to read from world state: unable to retrieve asset")
} }

View file

@ -185,10 +185,10 @@ public final class AssetTransfer implements ContractInterface {
* @param ctx the transaction context * @param ctx the transaction context
* @param assetID the ID of the asset being transferred * @param assetID the ID of the asset being transferred
* @param newOwner the new owner * @param newOwner the new owner
* @return the updated asset * @return the old owner
*/ */
@Transaction(intent = Transaction.TYPE.SUBMIT) @Transaction(intent = Transaction.TYPE.SUBMIT)
public Asset TransferAsset(final Context ctx, final String assetID, final String newOwner) { public String TransferAsset(final Context ctx, final String assetID, final String newOwner) {
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
String assetJSON = stub.getStringState(assetID); String assetJSON = stub.getStringState(assetID);
@ -205,7 +205,7 @@ public final class AssetTransfer implements ContractInterface {
String sortedJson = genson.serialize(newAsset); String sortedJson = genson.serialize(newAsset);
stub.putStringState(assetID, sortedJson); stub.putStringState(assetID, sortedJson);
return newAsset; return asset.getOwner();
} }
/** /**

View file

@ -224,9 +224,9 @@ public final class AssetTransferTest {
when(stub.getStringState("asset1")) when(stub.getStringState("asset1"))
.thenReturn("{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"Tomoko\", \"appraisedValue\": 300 }"); .thenReturn("{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"Tomoko\", \"appraisedValue\": 300 }");
Asset asset = contract.TransferAsset(ctx, "asset1", "Dr Evil"); String oldOwner = contract.TransferAsset(ctx, "asset1", "Dr Evil");
assertThat(asset).isEqualTo(new Asset("asset1", "blue", 5, "Dr Evil", 300)); assertThat(oldOwner).isEqualTo("Tomoko");
} }
@Test @Test

View file

@ -135,9 +135,11 @@ class AssetTransfer extends Contract {
async TransferAsset(ctx, id, newOwner) { async TransferAsset(ctx, id, newOwner) {
const assetString = await this.ReadAsset(ctx, id); const assetString = await this.ReadAsset(ctx, id);
const asset = JSON.parse(assetString); const asset = JSON.parse(assetString);
const oldOwner = asset.Owner;
asset.Owner = newOwner; asset.Owner = newOwner;
// we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive' // we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive'
return ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(asset)))); ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(asset))));
return oldOwner;
} }
// GetAllAssets returns all assets found in the world state. // GetAllAssets returns all assets found in the world state.

View file

@ -135,14 +135,16 @@ export class AssetTransferContract extends Contract {
return assetJSON && assetJSON.length > 0; return assetJSON && assetJSON.length > 0;
} }
// TransferAsset updates the owner field of asset with given id in the world state. // TransferAsset updates the owner field of asset with given id in the world state, and returns the old owner.
@Transaction() @Transaction()
public async TransferAsset(ctx: Context, id: string, newOwner: string): Promise<void> { public async TransferAsset(ctx: Context, id: string, newOwner: string): Promise<string> {
const assetString = await this.ReadAsset(ctx, id); const assetString = await this.ReadAsset(ctx, id);
const asset = JSON.parse(assetString); const asset = JSON.parse(assetString);
const oldOwner = asset.Owner;
asset.Owner = newOwner; asset.Owner = newOwner;
// we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive' // we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive'
await ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(asset)))); await ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(asset))));
return oldOwner;
} }
// GetAllAssets returns all assets found in the world state. // GetAllAssets returns all assets found in the world state.