1

Am trying to create a batch of items(units). I want a user to indicate the number of units they want in a batch then the transaction creates the batch first then creates the number of units required. This is my code:

async function createBatch(batchTx) {
// get a code from the generator
let now = new Date();
let tokenData = {
    brand: batchTx.brand,
    unitCount: batchTx.unitCount,
    created: now,
    expiry: batchTx.expiryDate
}
let code = _generate_code(tokenData, 'Batch');

// create a new Batch token and add it to the registry
let factory = getFactory();
let token = factory.newResource('org.myOrganization', 'Token', String(code));
token.created = now;
token.updated = now;

let tokenAssetRegistry = await getAssetRegistry('org.myOrganization.Token');
await tokenAssetRegistry.add(token);

// create a batch using the token and code created above
let batch = factory.newResource('org.myOrganization', 'Batch', token.code);
batch.brand = batchTx.brand;
batch.expiryDate = batchTx.expiryDate;
batch.token = token;
batch.owner = batchTx.owner;
batch.created = now;
batch.updated = now;

let batchAssetRegistry = await getAssetRegistry('org.myOrganization.Batch');
await batchAssetRegistry.add(batch);

// update token  with new batch
let tokenAssetRegistry1 = await getAssetRegistry('org.myOrganization.Token');
token.batch = batch;
tokenAssetRegistry1.update(token);

// CREATE UNITS
// get a code from the generator
let i;
for(i=0; i < batchTx.unitCount; i++) {
    let unitTokenData = {
        batch: batch,
        created: now
    };

    let unitCode = _generate_code(unitTokenData, 'Unit');
    // create a new Unit token and add it to the registry
    let unitToken = factory.newResource('org.myOrganization', 'Token', String(unitCode));
    unitToken.created = now;
    unitToken.updated = now;

    let tokenAssetRegistry2 = await getAssetRegistry('org.myOrganization.Token');
    await tokenAssetRegistry2.add(unitToken);

    // create units
    let unit = factory.newResource('org.myOrganization', 'Unit', String(unitToken.code));
    unit.batch = batch;
    unit.token = unitToken;
    unit.owner = batchTx.owner;
    unit.created = now;
    unit.updated = now;

    let unitAssetRegistry = await getAssetRegistry('org.myOrganization.Unit');
    await unitAssetRegistry.add(unit);
    }

}

The problem is that the batch is created okay but when it comes to units, instead of creating 3 units if batchTx.unitCount was 3, it only creates one. Is there anything am getting wrong about how composer works, or maybe some of my promise resolution is wrong? Any help in solving this will be much appreciated

söze
  • 500
  • 1
  • 3
  • 13

1 Answers1

3

(UPDATED):

  1. You can't (in the same transaction - above) update a new token asset (following the earlier add) that (add) has not been committed yet to the ledger. But you can simply wait for the batch `add' to complete and then do one singular:

    let tokenAssetRegistry = await getAssetRegistry('org.myOrganization.Token');
    await tokenAssetRegistry.add(token);
    
  2. your code is non-deterministic, in using new Date() - its not going to be the same between endorsing peers (get different results). Not seeing if you 'pare' the date string elsewhere (to reduce the element of non-determinism). Why not use the `transaction timestamp (link below).

  3. The same non-determinism applies to the unitToken section of your code. On non-determinism - suggest to see this for an example of getting deterministic datetime. Getting timestamps in deterministic way in Hyperledger Composer transactions

  4. I would recommend reading this on how to handle promises in your for loop -> https://medium.com/@antonioval/making-array-iteration-easy-when-using-async-await-6315c3225838 to manage the promises returned (I think yes, this is why your loop isn't completing)|. Your code is inefficient, if that's of any value in that you can instead create units as an array of resources then use one singular unitAssetRegistry.addAll() - after the for loop completes. An example of this is in this sample network here -> https://github.com/hyperledger/composer-sample-networks/blob/master/packages/fund-clearing-network/lib/clearing.js#L191 (using updateArray in this case, and the registry .updateAll() method).

Paul O'Mahony
  • 6,740
  • 1
  • 10
  • 15
  • Thanks Paul. I understand the second and third points on non-determinism. What am having trouble understanding is why the unit creation in the for loop only results in one being created not n as specified in the unitCount value – söze Jul 25 '18 at 13:35
  • Also, what is the recommended pattern for solving such a situation where one needs to update something after adding it: Do we need a new transaction for this? – söze Jul 25 '18 at 13:43
  • I've updated my answer, on promise resolution. Recommended pattern is for the `add` to be committed (as you can't read your own writes) - and updates to the same assets (once committed to the ledger) are handled separately, eg as transactions (so you can query if the asset is there using `await myAssetRegistry.get(asset.getIdentifier())` then do the updates you require). – Paul O'Mahony Jul 25 '18 at 14:16
  • Adding the units to an array and doing `registry.addAll(array)` worked for me. Thanks – söze Jul 27 '18 at 12:25