github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/kat/src/test/ethereum/assets/authored/private/described-structured.ts (about)

     1  // Copyright © 2021 Kaleido, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  import assert from 'assert';
    16  import { createHash, randomBytes } from 'crypto';
    17  import nock from 'nock';
    18  import request from 'supertest';
    19  import { promisify } from 'util';
    20  import { IDBAssetDefinition, IDBAssetInstance, IEventAssetDefinitionCreated, IEventAssetInstanceBatchCreated } from '../../../../../lib/interfaces';
    21  import * as utils from '../../../../../lib/utils';
    22  import { app, mockEventStreamWebSocket } from '../../../../common';
    23  import { testContent, testDescription, testIndexes } from '../../../../samples';
    24  const delay = promisify(setTimeout);
    25  
    26  export const testAuthoredPrivateDescribedStructured = () => {
    27  describe('Assets: authored - private - described - structured', async () => {
    28  
    29    let assetDefinitionID: string;
    30    const assetDefinitionName = 'authored - private - described - structured';
    31    const timestamp = utils.getTimestamp();
    32    const batchHashSha256 = '0x' + createHash('sha256').update(randomBytes(10)).digest().toString('hex');
    33    const batchHashIPFSMulti = utils.sha256ToIPFSHash(batchHashSha256);
    34  
    35    let batchMaxRecordsToRestore: number;
    36    beforeEach(() => {
    37      nock.cleanAll();
    38      // Force batches to close immediately
    39      batchMaxRecordsToRestore = utils.constants.BATCH_MAX_RECORDS;
    40      utils.constants.BATCH_MAX_RECORDS = 1;
    41    });
    42  
    43    afterEach(() => {
    44      assert.deepStrictEqual(nock.pendingMocks(), []);
    45      utils.constants.BATCH_MAX_RECORDS = batchMaxRecordsToRestore;
    46    });
    47  
    48    describe('Create asset definition', () => {
    49  
    50      it('Checks that the asset definition can be added', async () => {
    51  
    52        nock('https://apigateway.kaleido.io')
    53          .post('/createAssetDefinition?kld-from=0x0000000000000000000000000000000000000001&kld-sync=false')
    54          .reply(200, { id: 'my-receipt-id' });
    55  
    56        nock('https://ipfs.kaleido.io')
    57          .post('/api/v0/add')
    58          .reply(200, { Hash: 'QmQX1g8GwrMuACuMfQmKXzeYd7yXMXPpcUaFMqLUzSv1yL' });
    59  
    60        const result = await request(app)
    61          .post('/api/v1/assets/definitions')
    62          .send({
    63            name: assetDefinitionName,
    64            author: '0x0000000000000000000000000000000000000001',
    65            isContentPrivate: true,
    66            isContentUnique: true,
    67            descriptionSchema: testDescription.schema.object,
    68            contentSchema: testContent.schema.object,
    69            indexes: testIndexes
    70          })
    71          .expect(200);
    72        assert.deepStrictEqual(result.body.status, 'submitted');
    73        assetDefinitionID = result.body.assetDefinitionID;
    74  
    75        const getAssetDefinitionsResponse = await request(app)
    76          .get('/api/v1/assets/definitions')
    77          .expect(200);
    78        const assetDefinition = getAssetDefinitionsResponse.body.find((assetDefinition: IDBAssetDefinition) => assetDefinition.name === 'authored - private - described - structured');
    79        assert.strictEqual(assetDefinition.assetDefinitionID, assetDefinitionID);
    80        assert.strictEqual(assetDefinition.author, '0x0000000000000000000000000000000000000001');
    81        assert.strictEqual(assetDefinition.isContentPrivate, true);
    82        assert.strictEqual(assetDefinition.isContentUnique, true);
    83        assert.deepStrictEqual(assetDefinition.descriptionSchema, testDescription.schema.object);
    84        assert.deepStrictEqual(assetDefinition.contentSchema, testContent.schema.object);
    85        assert.strictEqual(assetDefinition.name, 'authored - private - described - structured');
    86        assert.strictEqual(assetDefinition.receipt, 'my-receipt-id');
    87        assert.strictEqual(typeof assetDefinition.submitted, 'number');
    88  
    89        const getAssetDefinitionResponse = await request(app)
    90          .get(`/api/v1/assets/definitions/${assetDefinitionID}`)
    91          .expect(200);
    92        assert.deepStrictEqual(assetDefinition, getAssetDefinitionResponse.body);
    93      });
    94  
    95      it('Checks that the event stream notification for confirming the asset definition creation is handled', async () => {
    96        const eventPromise = new Promise<void>((resolve) => {
    97          mockEventStreamWebSocket.once('send', message => {
    98            assert.strictEqual(message, '{"type":"ack","topic":"dev"}');
    99            resolve();
   100          })
   101        });
   102  
   103        nock('https://ipfs.kaleido.io')
   104          .get('/ipfs/QmQX1g8GwrMuACuMfQmKXzeYd7yXMXPpcUaFMqLUzSv1yL')
   105          .reply(200, {
   106            assetDefinitionID: assetDefinitionID,
   107            name: assetDefinitionName,
   108            isContentPrivate: true,
   109            isContentUnique: true,
   110            descriptionSchema: testDescription.schema.object,
   111            contentSchema: testContent.schema.object
   112          });
   113  
   114        const data: IEventAssetDefinitionCreated = {
   115          author: '0x0000000000000000000000000000000000000001',
   116          assetDefinitionHash: '0x205ee35b47f713845ea616c805e346cb90e9de82e56069f0de318c59e57d867b',
   117          timestamp: timestamp.toString()
   118        };
   119        mockEventStreamWebSocket.emit('message', JSON.stringify([{
   120          signature: utils.contractEventSignatures.ASSET_DEFINITION_CREATED,
   121          data,
   122          blockNumber: '123',
   123          transactionHash: '0x0000000000000000000000000000000000000000000000000000000000000000'
   124        }]));
   125        await eventPromise;
   126      });
   127  
   128      it('Checks that the asset definition is confirmed', async () => {
   129        const getAssetDefinitionsResponse = await request(app)
   130          .get('/api/v1/assets/definitions')
   131          .expect(200);
   132        const assetDefinition = getAssetDefinitionsResponse.body.find((assetDefinition: IDBAssetDefinition) => assetDefinition.name === 'authored - private - described - structured');
   133        assert.strictEqual(assetDefinition.assetDefinitionID, assetDefinitionID);
   134        assert.strictEqual(assetDefinition.author, '0x0000000000000000000000000000000000000001');
   135        assert.strictEqual(assetDefinition.isContentPrivate, true);
   136        assert.strictEqual(assetDefinition.isContentUnique, true);
   137        assert.deepStrictEqual(assetDefinition.descriptionSchema, testDescription.schema.object);
   138        assert.deepStrictEqual(assetDefinition.contentSchema, testContent.schema.object);
   139        assert.strictEqual(typeof assetDefinition.submitted, 'number');
   140        assert.strictEqual(assetDefinition.name, 'authored - private - described - structured');
   141        assert.strictEqual(assetDefinition.timestamp, timestamp);
   142        assert.strictEqual(assetDefinition.receipt, 'my-receipt-id');
   143        assert.strictEqual(assetDefinition.blockNumber, 123);
   144        assert.strictEqual(assetDefinition.transactionHash, '0x0000000000000000000000000000000000000000000000000000000000000000');
   145  
   146        const getAssetDefinitionResponse = await request(app)
   147          .get(`/api/v1/assets/definitions/${assetDefinitionID}`)
   148          .expect(200);
   149        assert.deepStrictEqual(assetDefinition, getAssetDefinitionResponse.body);
   150      });
   151  
   152    });
   153  
   154    describe('Asset instances', async () => {
   155  
   156      let assetInstanceID: string;
   157  
   158      it('Checks that an asset instance can be created, within a batch', async () => {
   159  
   160        nock('https://apigateway.kaleido.io')
   161          .post('/createAssetInstanceBatch?kld-from=0x0000000000000000000000000000000000000001&kld-sync=false')
   162          .reply(200, { id: 'my-receipt-id' });
   163  
   164        nock('https://ipfs.kaleido.io')
   165          .post('/api/v0/add')
   166          .reply(200, { Hash: batchHashIPFSMulti })
   167  
   168        const result = await request(app)
   169          .post(`/api/v1/assets/${assetDefinitionID}`)
   170          .send({
   171            author: '0x0000000000000000000000000000000000000001',
   172            description: testDescription.sample.object,
   173            content: testContent.sample.object
   174          })
   175          .expect(200);
   176        assert.deepStrictEqual(result.body.status, 'submitted');
   177        assetInstanceID = result.body.assetInstanceID;
   178  
   179        const getAssetInstancesResponse = await request(app)
   180          .get(`/api/v1/assets/${assetDefinitionID}`)
   181          .expect(200);
   182        const assetInstance = getAssetInstancesResponse.body.find((assetInstance: IDBAssetInstance) => assetInstance.assetInstanceID === assetInstanceID);
   183        assert.strictEqual(assetInstance.author, '0x0000000000000000000000000000000000000001');
   184        assert.strictEqual(assetInstance.assetDefinitionID, assetDefinitionID);
   185        assert.deepStrictEqual(assetInstance.description, testDescription.sample.object);
   186        assert.deepStrictEqual(assetInstance.content, testContent.sample.object);
   187        assert.strictEqual(typeof assetInstance.submitted, 'number');
   188        assert.strictEqual(typeof assetInstance.batchID, 'string');
   189  
   190        // Expect the batch to have been submitted
   191        let getBatchResponse: any;
   192        for (let i = 0; i < 10; i++) {
   193          getBatchResponse = await request(app)
   194            .get(`/api/v1/batches/${assetInstance.batchID}`)
   195            .expect(200);
   196          if (getBatchResponse.body.completed) break;
   197          await delay(1);
   198        }
   199        assert.strictEqual(typeof getBatchResponse.body.completed, 'number');
   200        assert.strictEqual(typeof getBatchResponse.body.batchHash, 'string');
   201        assert.strictEqual(getBatchResponse.body.receipt, 'my-receipt-id');
   202        assert.strictEqual(getBatchResponse.body.batchHash, batchHashSha256);
   203        // As this is a private asset, the content must not have been written to IPFS in the batch
   204        assert.strictEqual(getBatchResponse.body.records[0].content, undefined);
   205        // The full description payload should be in the batch data written to IPFS in the batch
   206        assert.deepStrictEqual(getBatchResponse.body.records[0].description, testDescription.sample.object);
   207  
   208        const getAssetInstanceResponse = await request(app)
   209          .get(`/api/v1/assets/${assetDefinitionID}/${assetInstanceID}`)
   210          .expect(200);
   211        assert.deepStrictEqual(assetInstance, getAssetInstanceResponse.body);
   212  
   213      });
   214  
   215      it('Checks that the event stream notification for confirming the asset instance creation is handled', async () => {
   216        const eventPromise = new Promise<void>((resolve) => {
   217          mockEventStreamWebSocket.once('send', message => {
   218            assert.strictEqual(message, '{"type":"ack","topic":"dev"}');
   219            resolve();
   220          })
   221        });
   222        const data: IEventAssetInstanceBatchCreated = {
   223          author: '0x0000000000000000000000000000000000000001',
   224          batchHash: batchHashSha256,
   225          timestamp: timestamp.toString()
   226        };
   227        mockEventStreamWebSocket.emit('message', JSON.stringify([{
   228          signature: utils.contractEventSignatures.ASSET_INSTANCE_BATCH_CREATED,
   229          data,
   230          blockNumber: '123',
   231          transactionHash: '0x0000000000000000000000000000000000000000000000000000000000000000'
   232        }]));
   233        await eventPromise;
   234      });
   235  
   236      it('Checks that the asset instance is confirmed', async () => {
   237        const getAssetInstancesResponse = await request(app)
   238          .get(`/api/v1/assets/${assetDefinitionID}`)
   239          .expect(200);
   240        const assetInstance = getAssetInstancesResponse.body.find((assetInstance: IDBAssetInstance) => assetInstance.assetInstanceID === assetInstanceID);
   241        assert.strictEqual(assetInstance.author, '0x0000000000000000000000000000000000000001');
   242        assert.strictEqual(assetInstance.assetDefinitionID, assetDefinitionID);
   243        assert.deepStrictEqual(assetInstance.description, testDescription.sample.object);
   244        assert.deepStrictEqual(assetInstance.content, testContent.sample.object);
   245        assert.strictEqual(assetInstance.timestamp, timestamp);
   246        assert.strictEqual(typeof assetInstance.submitted, 'number');
   247        assert.strictEqual(assetInstance.receipt, undefined); // the batch has the receipt
   248        assert.strictEqual(assetInstance.blockNumber, 123);
   249        assert.strictEqual(assetInstance.transactionHash, '0x0000000000000000000000000000000000000000000000000000000000000000');
   250  
   251        const getAssetInstanceResponse = await request(app)
   252          .get(`/api/v1/assets/${assetDefinitionID}/${assetInstanceID}`)
   253          .expect(200);
   254        assert.deepStrictEqual(assetInstance, getAssetInstanceResponse.body);
   255      });
   256  
   257      it('sets a property on an asset, which will be public and batched even though the asset is private', async () => {
   258  
   259        nock('https://apigateway.kaleido.io')
   260          .post('/createAssetInstanceBatch?kld-from=0x0000000000000000000000000000000000000001&kld-sync=false')
   261          .reply(200, { id: 'my-receipt-id' });
   262  
   263        nock('https://ipfs.kaleido.io')
   264          .post('/api/v0/add')
   265          .reply(200, { Hash: batchHashIPFSMulti })
   266  
   267        const {body: result} = await request(app)
   268          .put(`/api/v1/assets/${assetDefinitionID}/${assetInstanceID}`)
   269          .send({
   270            action: 'set-property',
   271            key: 'key',
   272            value: 'value',
   273            author: '0x0000000000000000000000000000000000000001',
   274          })
   275          .expect(200);
   276        assert.deepStrictEqual(result.status, 'submitted');
   277        
   278        const {body: asset} = await request(app)
   279          .get(`/api/v1/assets/${assetDefinitionID}/${assetInstanceID}`)
   280          .expect(200);
   281        const prop = asset.properties['0x0000000000000000000000000000000000000001']['key'];
   282        assert.strictEqual(prop.value, 'value');
   283        assert(prop.submitted > 0);
   284        const {batchID} = prop;
   285  
   286        // Expect the batch to have been submitted
   287        let getBatchResponse: any;
   288        for (let i = 0; i < 10; i++) {
   289          getBatchResponse = await request(app)
   290            .get(`/api/v1/batches/${batchID}`)
   291            .expect(200);
   292          if (getBatchResponse.body.completed) break;
   293          await delay(1);
   294        }
   295    
   296        assert.strictEqual(typeof getBatchResponse.body.completed, 'number');
   297        assert.strictEqual(typeof getBatchResponse.body.batchHash, 'string');
   298        assert.strictEqual(getBatchResponse.body.receipt, 'my-receipt-id');
   299        assert.strictEqual(getBatchResponse.body.batchHash, batchHashSha256);
   300        // As properties are always public, the full content will have been written to IPFS in the batch
   301        assert.deepStrictEqual(getBatchResponse.body.records[0].recordType, 'property');
   302        assert.deepStrictEqual(getBatchResponse.body.records[0].key, 'key');
   303        assert.deepStrictEqual(getBatchResponse.body.records[0].value, 'value');
   304  
   305      });
   306  
   307    });
   308  
   309  });
   310  
   311  };