diff --git a/demo/src/demo.ts b/demo/src/demo.ts index cd3c0855..50cdbcce 100644 --- a/demo/src/demo.ts +++ b/demo/src/demo.ts @@ -73,10 +73,10 @@ async function main() { } catch (e: any) { console.log(e.errorCode, '-', e.message) } - console.log('SDK', newSpace.spaceId) + console.log('SDK', newSpace.identifier) console.log( 'SDK', - Cord.Utils.Identifier.getIdentifierHash(newSpace.spaceId, 'space:cord:') + Cord.Utils.Identifier.getIdentifierHash(newSpace.identifier, 'space:cord:') ) // Step 2: Create a new Schema console.log(`\n\nāœ‰ļø Adding a new Schema \n`) @@ -86,10 +86,11 @@ async function main() { let newSchema = Cord.Schema.fromSchemaProperties( newSchemaContent, - employeeIdentity + employeeIdentity, + newSpace.identifier ) - let schemaCreationExtrinsic = await newSchema.create(newSpace.spaceId) + let schemaCreationExtrinsic = await newSchema.create() console.log(`šŸ“§ Schema Details `) console.dir(newSchema, { depth: null, colors: true }) @@ -121,7 +122,7 @@ async function main() { country: 'India', credit: 1000, } - let schemaStream = Cord.Content.fromContentProperties( + let schemaStream = Cord.Content.fromContentStructure( newSchema, content, employeeIdentity.address @@ -129,16 +130,17 @@ async function main() { console.log(`šŸ“§ Stream Details `) console.dir(schemaStream, { depth: null, colors: true }) - let newStreamContent = Cord.MarkContent.fromContentProperties( + let newStreamContent = Cord.ContentStream.fromContentProperties( schemaStream, - employeeIdentity + employeeIdentity, + { space: newSpace.identifier } ) console.log(`\nšŸ“§ Hashed Stream `) console.dir(newStreamContent, { depth: null, colors: true }) - let newStream = Cord.Stream.fromMarkContentProperties(newStreamContent) + let newStream = Cord.Stream.fromContentStreamProperties(newStreamContent) - let streamCreationExtrinsic = await newStream.create(newSpace.spaceId) + let streamCreationExtrinsic = await newStream.create() console.log(`\nšŸ“§ Stream On-Chain Details`) console.dir(newStream, { depth: null, colors: true }) diff --git a/packages/api/package.json b/packages/api/package.json index 348e69f5..7109bd06 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -44,7 +44,6 @@ "webpack-cli": "^4.5.0" }, "dependencies": { - "@cord.network/exchange": "workspace:*", "@cord.network/messaging": "workspace:*", "@cord.network/modules": "workspace:*", "@cord.network/network": "workspace:*", diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 3b0f5081..fa6a27bd 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -4,6 +4,4 @@ export * as Messaging from '@cord.network/messaging' export { ChainUtils } from '@cord.network/network' export * as ChainHelpers from '@cord.network/network' export * as Utils from '@cord.network/utils' -export * as Exchange from '@cord.network/exchange' -export { Request, Share } from '@cord.network/exchange' export * from '@cord.network/types' diff --git a/packages/config/src/ConfigService.ts b/packages/config/src/ConfigService.ts index 8be33248..cc4fbe73 100644 --- a/packages/config/src/ConfigService.ts +++ b/packages/config/src/ConfigService.ts @@ -52,7 +52,7 @@ let configuration: configOpts = { function checkAddress(): void { if (!configuration.address) { - throw SDKErrors.ERROR_WS_ADDRESS_NOT_SET() + throw new SDKErrors.ERROR_WS_ADDRESS_NOT_SET() } } diff --git a/packages/credential/src/exportToVerifiableCredential.ts b/packages/credential/src/exportToVerifiableCredential.ts index 8219cc4f..abf1cb11 100644 --- a/packages/credential/src/exportToVerifiableCredential.ts +++ b/packages/credential/src/exportToVerifiableCredential.ts @@ -57,12 +57,12 @@ export function fromMark( rootHash, issuerSignature, content, - contentId, + identifier, } = input.request // write root hash to id const id = toCredentialIRI( - Identifier.getIdentifierKey(contentId, STREAM_PREFIX) + Identifier.getIdentifierKey(identifier, STREAM_PREFIX) ) // transform & annotate stream to be json-ld and VC conformant diff --git a/packages/exchange/package.json b/packages/exchange/package.json deleted file mode 100644 index 06356bcc..00000000 --- a/packages/exchange/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "@cord.network/exchange", - "version": "0.5.2-5", - "description": "Exchange API", - "main": "./lib/index.js", - "typings": "./lib/index.d.ts", - "files": [ - "lib/**/*" - ], - "scripts": { - "clean": "rimraf ./lib", - "build": "yarn clean && yarn build:ts", - "build:ts": "tsc --declaration -p tsconfig.build.json" - }, - "repository": "github:dhiway/cord.js", - "engines": { - "node": ">=14.0" - }, - "author": "Dhiway", - "bugs": "https://github.com/dhiway/cord.js/issues", - "homepage": "https://github.com/dhiway/cord.js#readme", - "devDependencies": { - "rimraf": "^3.0.2", - "typescript": "^4.5.4" - }, - "dependencies": { - "@cord.network/config": "workspace:*", - "@cord.network/messaging": "workspace:*", - "@cord.network/modules": "workspace:*", - "@cord.network/network": "workspace:*", - "@cord.network/types": "workspace:*", - "@cord.network/utils": "workspace:*" - } -} diff --git a/packages/exchange/src/Request.ts b/packages/exchange/src/Request.ts deleted file mode 100644 index 4ba0b40c..00000000 --- a/packages/exchange/src/Request.ts +++ /dev/null @@ -1,136 +0,0 @@ -/** - * @packageDocumentation - * @module Request - */ - -import { - // Mark, - Schema, - SDKErrors, - Identity, -} from '@cord.network/modules' -// import { ConfigService } from '@cord.network/config' -import type { - IPublicIdentity, - IMessage, - // IMark, - // IMarkContent, - // IMessage, - IRequestStreamForCredential, -} from '@cord.network/types' -import { Message } from '@cord.network/messaging' -import { UUID, Crypto } from '@cord.network/utils' - -export interface IPresentationReq { - properties: string[] - id?: Schema['schemaId'] - proofs?: boolean - requestUpdatedAfter?: Date -} - -export interface IPartialRequest { - id: Schema['schemaId'] - properties: string[] -} - -export interface IRequestSession { - requestedProperties: IPartialRequest[] -} - -/** - * A helper class to initiate a verification by creating a presentation request which is built - * on a specific [[MType]] and attributes of the [[Stream]] the verifier requires to see. - */ -export class PresentationRequestBuilder { - private partialReq: IPartialRequest[] - constructor() { - this.partialReq = [] - } - - /** - * Initiates a verification by creating a presentation request for a specific [[Schema]]. - * Note that you are required to call [[finalize]] on the request to conclude it. - * - * @param p The parameter object. - * @param p.id The ID of the [[Schema]]. - * @param p.properties A list of properties of the [[Mark]]s requested. - * @param p.proofs An optional boolean representing whether the verifier requests to see the proofs of the issuers which signed the [[Credentials]]s. - * The default value for this is the current date. - * @returns A [[PresentationRequestBuilder]] on which you need to call [[finalize]] to complete the presentation request. - */ - public requestPresentation({ - id, - properties, - proofs, - }: IPresentationReq): PresentationRequestBuilder { - const rawProperties = properties.map((attr) => `${attr}`) - - if (typeof id !== 'undefined') { - rawProperties.push('content.id') - } - if (proofs === true) { - rawProperties.push('proof') - } - if (!id) throw new SDKErrors.ERROR_SCHEMA_ID_NOT_PROVIDED() - this.partialReq.push({ - id: id, - properties: rawProperties, - }) - return this - } - - /** - * Concludes the presentation request. - * - * @param requester The [[Identity]] of the verifier used to sign. - * @param holder The [[IPublicIdentity]] for which the message should be encrypted (note: the message will be return unencrypted. Use Message.getEncryptedMessage to encrypt the message). - * @returns A session and a message object. - * The **session** object will be used in [[verifyPresentation]] and should be kept private by the verifier. - * The **message** object should be sent to the Holder and used in [[createPresentation]]. - */ - public finalize( - purpose: string, - requester: Identity, - holder: IPublicIdentity, - validUntil: number, - relatedData: boolean - ): { - session: IRequestSession - message: IMessage - } { - const session = { - requestedProperties: this.partialReq, - } - const message = new Message( - { - request: UUID.generate(), - type: Message.BodyType.REQUEST_CREDENTIAL, - content: this.partialReq.map((pr): IRequestStreamForCredential => { - return { - id: pr.id, - requiredProperties: pr.properties, - } - }), - purpose, - validUntil, - relatedData, - }, - requester, - holder - ) - const hash = Crypto.hashStr(JSON.stringify(message)) - message.body.requestorSignature = requester.signStr(hash) - return { - session: session, - message: message, - } - } -} -/** - * Initiates a verification by creating a request on the Verifier's side. - * - * @returns A [[PresentationRequestBuilder]] based on a [[MType]] and a list of required disclosed attributes of the [[MarkedStream]]s. - */ -export function newRequestBuilder(): PresentationRequestBuilder { - return new PresentationRequestBuilder() -} diff --git a/packages/exchange/src/Share.ts b/packages/exchange/src/Share.ts deleted file mode 100644 index eba6cd99..00000000 --- a/packages/exchange/src/Share.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * @packageDocumentation - * @module Share - */ - -import { Mark, Identity, SDKErrors } from '@cord.network/modules' -import type { - IMessage, - IPublicIdentity, - IPresentationOptions, - IPresentationSigningOptions, -} from '@cord.network/types' -import { Message } from '@cord.network/messaging' -// import { Crypto } from '@cord.network/utils' - -/** - * Creates a presentation for an arbitrary amount of [[MarkedStream]]s which can be verified in [[verifyPresentation]]. - * - * @param identity The Holder [[Identity]] which owns the [[MarkedStream]]s. - * @param message The message which represents multiple [[MType]]s, [[IRequestStreamsForMTypes]]s and whether privacy - * enhancement is supported. - * @param verifier The [[IPublicIdentity]] of the verifier that requested the presentation. - * @param credentials The [[MarkedStream]]s which should be verified. - * @throws [[ERROR_PE_MISMATCH]], [[ERROR_MESSAGE_TYPE]], [[ERROR_PE_CREDENTIAL_MISSING]]. - * @returns A message which represents either an array of [[MarkedStream]]s if privacy enhancement is not supported - * or a combined presentation. Both of these options can be verified. - */ -export function createPresentation( - identity: Identity, - message: IMessage, - verifier: IPublicIdentity, - credentials: Mark[], - { - showAttributes, - hideAttributes = [], - signer, - request = message.request, - }: IPresentationOptions & Partial = {} -): Message { - // did we get the right message type? - // if (message.body.type !== Message.BodyType.REQUEST_CREDENTIAL) { - // throw new SDKErrors.ERROR_MESSAGE_TYPE( - // message.body.type, - // Message.BodyType.REQUEST_CREDENTIAL - // ) - // } - - // create presentation for each credential - const credentialStreams = credentials.map((cred, i) => { - const presentation = cred.createPresentation({ - showAttributes, - hideAttributes, - signer, - request, - }) - - return presentation - }) - - const resMessage = new Message( - { - request: message.body.request, - type: Message.BodyType.SUBMIT_CREDENTIAL, - content: credentialStreams, - purpose: message.body.purpose, - validUntil: message.body.validUntil, - relatedData: message.body.relatedData, - requestorSignature: message.body.requestorSignature, - }, - identity, - verifier - ) - - return resMessage -} diff --git a/packages/exchange/src/Verify.ts b/packages/exchange/src/Verify.ts deleted file mode 100644 index c37d3bfe..00000000 --- a/packages/exchange/src/Verify.ts +++ /dev/null @@ -1,98 +0,0 @@ -/** - * @packageDocumentation - * @module Request - */ - -import { Mark, SDKErrors } from '@cord.network/modules' -import { ConfigService } from '@cord.network/config' -import type { IMark, IMarkContent, IMessage } from '@cord.network/types' -import { Message } from '@cord.network/messaging' -import { IRequestSession } from './Request' - -const log = ConfigService.LoggingFactory.getLogger('Request') - -/** -// * Initiates a verification by creating a request on the Verifier's side. -// * -// * @returns A [[PresentationRequestBuilder]] based on a [[MType]] and a list of required disclosed attributes of the [[MarkedStream]]s. -// */ -// export function newRequestBuilder(): PresentationRequestBuilder { -// return new PresentationRequestBuilder() -// } - -/** - * [ASYNC] Checks that the submitted marks fulfil the ones requested upon presentation creation. - * - * @param markedStreams The attested streams submitted by the holder. - * @param session The stored session object. - * @returns An object describing whether the verification was successful. - */ -async function verifySharedPresentation( - credStreams: Mark[], - session: IRequestSession -): Promise<{ - verified: boolean - streams: Array> -}> { - if (credStreams.length !== session.requestedProperties.length) { - log.info( - `Rejected presentation because the number of shared streams (${credStreams.length}) did not match the number of requested streams (${session.requestedProperties.length}).` - ) - return { - verified: false, - streams: [], - } - } - - const allVerified = await Promise.all( - session.requestedProperties.map(async (requested, i) => { - const ac = credStreams[i] - const providedProperties = ac.getAttributes() - const rawProperties = Array.from(providedProperties.keys()).map( - (prop) => `${prop}` - ) - rawProperties.push('content.id') - rawProperties.push('content.issuer') - return ( - requested.properties.every((p) => { - return rawProperties.includes(p) - }) && ac.verify() - ) - }) - ) - const verified = !allVerified.includes(false) - return { verified, streams: verified ? credStreams : [] } -} - -/** - * [ASYNC] Verifies the Holder's presentation of [[MarkedStream]]s. - * - * @param message The Holder's presentation of the [[MarkedStream]]s that should be verified, the result of [[createPresentation]]. - * @param session The Verifier's private verification session created in [[finalize]]. - * @throws [[ERROR_MESSAGE_TYPE]]. - * @returns An object containing the keys - * **verified** (which describes whether the [[MarkedStream]]s could be verified) - * and **streams** (an array of [[Stream]]s restricted on the disclosed attributes selected in [[requestPresentationForMtype]]). - */ -export async function verifyPresentation( - message: IMessage, - session: IRequestSession -): Promise<{ - verified: boolean - streams: Array> -}> { - // if (message.body.type !== Message.BodyType.SUBMIT_CREDENTIAL) - // throw new SDKErrors.ERROR_MESSAGE_TYPE( - // message.body.type, - // Message.BodyType.SUBMIT_CREDENTIAL - // ) - const credentialStreams: IMark[] = message.body.content.map( - (credentials: any, i: any) => { - return credentials.credentials[i] - } - ) - const credStreams = credentialStreams.map(Mark.fromMark) - - // currently only supporting id-ed credentials - return verifySharedPresentation(credStreams, session) -} diff --git a/packages/exchange/src/index.ts b/packages/exchange/src/index.ts deleted file mode 100644 index 34b2c53a..00000000 --- a/packages/exchange/src/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -// import * as Requestor from './actors/Requestor' -// import * as Issuer from './actors/Issuer' -import * as Share from './Share' -import * as Request from './Request' -import * as Verify from './Verify' - -import type * as types from './types' - -export { Share, Request, Verify, types } diff --git a/packages/exchange/src/types.ts b/packages/exchange/src/types.ts deleted file mode 100644 index 44cc8c7d..00000000 --- a/packages/exchange/src/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @packageDocumentation - * @module ActorsTypes - */ - -export { IPresentationReq, IPartialRequest, IRequestSession } from './Request' -// export { RequestorStreamSession } from './Share' -// export { IStreamStatusHandle } from './Issuer' diff --git a/packages/exchange/tsconfig.build.json b/packages/exchange/tsconfig.build.json deleted file mode 100644 index ab6a2e29..00000000 --- a/packages/exchange/tsconfig.build.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": "../../tsconfig.build.json", - - "compilerOptions": { - "outDir": "./lib" - }, - - "include": [ - "src/**/*.ts", "src/**/*.js" - ], - - "exclude": [ - "coverage", - "**/*.spec.ts", - ] -} \ No newline at end of file diff --git a/packages/messaging/src/Message.utils.ts b/packages/messaging/src/Message.utils.ts index e8b73dc4..32d4a9fd 100644 --- a/packages/messaging/src/Message.utils.ts +++ b/packages/messaging/src/Message.utils.ts @@ -8,7 +8,7 @@ import { MarkUtils, ContentUtils, SchemaUtils, - MarkContentUtils, + ContentStreamUtils, } from '@cord.network/modules' import type { IMark, @@ -30,7 +30,7 @@ import { Message } from './Message.js' export function errorCheckMessageBody(body: MessageBody): boolean | void { switch (body.type) { case Message.BodyType.REQUEST_STREAM: { - MarkContentUtils.errorCheck(body.content.requestStream) + ContentStreamUtils.errorCheck(body.content.requestStream) if (body.content.prerequisiteStreams) { body.content.prerequisiteStreams.map( (content: IContent | PartialContent) => @@ -159,7 +159,7 @@ export function compressMessage(body: MessageBody): CompressedMessageBody { switch (body.type) { case Message.BodyType.REQUEST_STREAM: { compressedContents = [ - MarkContentUtils.compress(body.content.requestStream), + ContentStreamUtils.compress(body.content.requestStream), body.content.prerequisiteStreams ? body.content.prerequisiteStreams.map((content) => ContentUtils.compress(content) @@ -215,7 +215,7 @@ export function decompressMessage(body: CompressedMessageBody): MessageBody { switch (body[0]) { case Message.BodyType.REQUEST_STREAM: { decompressedContents = { - requestStream: MarkContentUtils.decompress(body[1][0]), + requestStream: ContentStreamUtils.decompress(body[1][0]), prerequisiteStreams: body[1][1] ? body[1][1].map((stream) => ContentUtils.decompress(stream)) : undefined, diff --git a/packages/modules/src/index.ts b/packages/modules/src/index.ts index 21cf99b3..7ec908cf 100644 --- a/packages/modules/src/index.ts +++ b/packages/modules/src/index.ts @@ -8,7 +8,6 @@ export { Balance, BalanceUtils } from './balance/index.js' export { Content, ContentUtils } from './content/index.js' export { Space, SpaceUtils } from './space/index.js' export { Stream, StreamUtils, StreamDetailUtils } from './stream/index.js' -export { Product, ProductUtils } from './product/index.js' export { ContentStream, ContentStreamUtils } from './contentstream/index.js' export { Mark, MarkUtils, Presenation } from './mark/index.js' export { Identity, IURLResolver, PublicIdentity } from './identity/index.js' diff --git a/packages/modules/src/product/Product.chain.ts b/packages/modules/src/product/Product.chain.ts deleted file mode 100644 index 7ffcbc50..00000000 --- a/packages/modules/src/product/Product.chain.ts +++ /dev/null @@ -1,199 +0,0 @@ -/** - * @packageDocumentation - * @module Product - */ -import { Option, Struct, Vec, u8, u32 } from '@polkadot/types' -import type { - IProduct, - IProductDetails, - SubmittableExtrinsic, -} from '@cord.network/types' -import { DecoderUtils } from '@cord.network/utils' -import type { AccountId, BlockNumber, Hash } from '@polkadot/types/interfaces' -import { ConfigService } from '@cord.network/config' -import { ChainApiConnection } from '@cord.network/network' -import { ProductDetails } from './Product.js' -import { hexToString } from './Product.utils.js' - -const log = ConfigService.LoggingFactory.getLogger('Mark') - -/** - * Generate the extrinsic to store the provided [[IProduct]]. - * - * @param stream The stream to anchor on the chain. - * @returns The [[SubmittableExtrinsic]] for the `create` call. - */ -export async function create(stream: IProduct): Promise { - const blockchain = await ChainApiConnection.getConnectionOrConnect() - const tx: SubmittableExtrinsic = blockchain.api.tx.product.create( - stream.id, - stream.issuer, - stream.hash, - stream.cid, - stream.schema - ) - return tx -} - -/** - * Generate the extrinsic to store the provided [[IProduct]]. - * - * @param stream The stream to anchor on the chain. - * @returns The [[SubmittableExtrinsic]] for the `create` call. - */ -export async function list(stream: IProduct): Promise { - const blockchain = await ChainApiConnection.getConnectionOrConnect() - const tx: SubmittableExtrinsic = blockchain.api.tx.product.list( - stream.id, - stream.issuer, - stream.hash, - stream.store_id, - stream.price, - stream.cid, - stream.schema, - stream.link - ) - return tx -} - -/** - * Generate the extrinsic to store the provided [[IProduct]]. - * - * @param stream The stream to anchor on the chain. - * @returns The [[SubmittableExtrinsic]] for the `create` call. - */ -export async function order(stream: IProduct): Promise { - const blockchain = await ChainApiConnection.getConnectionOrConnect() - const tx: SubmittableExtrinsic = blockchain.api.tx.product.order( - stream.id, - stream.issuer, - stream.hash, - stream.store_id, - stream.price, - stream.cid, - stream.schema, - stream.link - ) - return tx -} - -export async function order_return( - streamId: string, - issuer: string -): Promise { - const blockchain = await ChainApiConnection.getConnectionOrConnect() - const tx: SubmittableExtrinsic = blockchain.api.tx.product.order( - streamId, - issuer - ) - return tx -} - -export async function order_rating( - stream: IProduct -): Promise { - const blockchain = await ChainApiConnection.getConnectionOrConnect() - const tx: SubmittableExtrinsic = blockchain.api.tx.product.rating( - stream.id, - stream.issuer, - stream.hash, - stream.store_id, - stream.price, - stream.cid, - stream.schema, - stream.link, - stream.rating - ) - return tx -} - -export interface AnchoredProductDetails extends Struct { - readonly tx_hash: Hash - readonly cid: Option> - readonly parent_cid: Option> - readonly store_id: Option - readonly link: Option - readonly schema: Option - readonly issuer: AccountId - readonly price: u32 - readonly rating: u8 - readonly block: BlockNumber - readonly status: boolean -} - -function decodeProduct( - encodedProduct: Option, - streamId: string -): ProductDetails | null { - DecoderUtils.assertCodecIsType(encodedProduct, [ - 'Option', - ]) - if (encodedProduct.isSome) { - const anchoredProduct = encodedProduct.unwrap() - const stream: IProductDetails = { - id: streamId, - tx_hash: anchoredProduct.tx_hash.toString(), - cid: anchoredProduct.cid - ? hexToString(anchoredProduct.cid.toString()) - : null, - parent_cid: anchoredProduct.parent_cid - ? hexToString(anchoredProduct.parent_cid.toString()) - : null, - store_id: anchoredProduct.store_id.toString() || null, - schema: anchoredProduct.schema.toString() || null, - link: anchoredProduct.link.toString() || null, - issuer: anchoredProduct.issuer.toString(), - price: anchoredProduct.price.toString() || null, - rating: anchoredProduct.price.toString() || null, - block: anchoredProduct.block.toString(), - status: anchoredProduct.status.valueOf(), - } - - return ProductDetails.fromProductDetails(stream) - } - return null -} - -async function queryRaw( - streamId: string -): Promise> { - const blockchain = await ChainApiConnection.getConnectionOrConnect() - const result = await blockchain.api.query.stream.streams< - Option - >(streamId) - return result -} - -/** - * Query a stream from the chain given the stream Id. - * - * @param streamId The Id of the stream anchored. - * @returns Either the retrieved [[ProductDetails]] or null. - */ -export async function query(streamId: string): Promise { - const encoded = await queryRaw(streamId) - return decodeProduct(encoded, streamId) -} - -/** - * Generate the extrinsic to set the status of a given stream. The submitter can be the owner of the stream or an authorized delegator of the schema. - * - * @param streamId The stream Is. - * @param issuer The submitter - * @param status The stream status - * @returns The [[SubmittableExtrinsic]] for the `set_status` call. - */ -export async function set_status( - streamId: string, - issuer: string, - status: boolean -): Promise { - const blockchain = await ChainApiConnection.getConnectionOrConnect() - log.debug(() => `Revoking stream with ID ${streamId}`) - const tx: SubmittableExtrinsic = blockchain.api.tx.stream.set_status( - streamId, - issuer, - status - ) - return tx -} diff --git a/packages/modules/src/product/Product.ts b/packages/modules/src/product/Product.ts deleted file mode 100644 index e20d85dc..00000000 --- a/packages/modules/src/product/Product.ts +++ /dev/null @@ -1,306 +0,0 @@ -/** - * A [[Product]] creates a sharable stream. [[Product]]s are **written on the CORD chain** and are **revocable**. - * The [[Product]] streams can be used as the base for [[Link]] streams. - * - * @packageDocumentation - * @module Product - * @preferred - */ - -import type { SubmittableExtrinsic } from '@polkadot/api/promise/types' -import type { - // IPublicIdentity, - IProduct, - IProductDetails, - IMarkContent, - CompressedProduct, -} from '@cord.network/types' -import { - set_status, - query, - create, - list, - order, - order_return, - order_rating, -} from './Product.chain.js' -import * as ProductUtils from './Product.utils.js' -// import Storage from '@cord.network/storage' -// import SchemaUtils from '../schema/Schema.utils' - -export class Product implements IProduct { - /** - * [STATIC] [ASYNC] Queries the chain for a given stream entry, by `identifier`. - * - * @param identifier - The identifier of the stream. - * @returns A promise containing the [[ProductProduct] or null. - * @example ```javascript - * Product.query('0xd8024cdc147c4fa9221cd177').then((stream) => { - * // now we can for example revoke `stream` - * }); - * ``` - */ - public static async query( - identifier: string - ): Promise { - return query(identifier) - } - - /** - * [STATIC] [ASYNC] Revokes a stream stream Also available as an instance method. - * @param identifier - The ID of the stream stream. - * @param status - bool value to set the status of the stream stream. - * @returns A promise containing the unsigned SubmittableExtrinsic (submittable transaction). - * @example ```javascript - * Product.revoke('0xd8024cdc147c4fa9221cd177', true).then(() => { - * // the stream status tx was created, sign and send it! - * ChainUtils.signAndSendTx(tx, identity); - * }); - * ``` - */ - public static async set_status( - identifier: string, - issuer: string, - status: boolean - ): Promise { - return set_status(identifier, issuer, status) - } - - /** - * [STATIC] Builds an instance of [[ProductProduct]], from a simple object with the same properties. - * Used for deserialization. - * - * @param input - The base object from which to create the stream stream. - * @returns A new [[Product]] object. - * @example ```javascript - * // create a Product stream object, so we can call methods on it (`serialized` is a serialized Product object ) - * Product.fromProduct(JSON.parse(serialized)); - * ``` - */ - public static fromProduct(input: IProduct): Product { - return new Product(input) - } - - /** - * [STATIC] Builds a new instance of an [[Product]], from a complete set of input required for an stream. - * - * @param content - The base request for stream. - * @param link - ID of the [[Space]] this [[Journal]] is linked to. - * @param creatorPublicIdentity - Public Identity of the issuer, used to anchor the underlying stream. - * @returns A new [[Product]] object. - * @example ```javascript - * // create a complete new stream from the `ProductProduct` and all other needed properties - * Product.fromContentAndPublicIdentity(request, issuerPublicIdentity); - * ``` - */ - public static fromProductContentAnchor( - content: IMarkContent, - cid: string, - store_id?: string, - price?: number, - rating?: number - ): Product { - return new Product({ - id: ProductUtils.getIdentifier(content.contentId), - hash: content.rootHash, - cid: cid, - store_id: store_id, - schema: content.content.schemaId, - price: price, - rating: rating, - link: content.link, - issuer: content.content.issuer, - status: true, - }) - } - - /** - * [STATIC] Custom Type Guard to determine input being of type IProduct using the ProductUtils errorCheck. - * - * @param input The potentially only partial IProduct. - * @returns Boolean whether input is of type IProduct. - */ - public static isIProduct(input: unknown): input is IProduct { - try { - ProductUtils.errorCheck(input as IProduct) - } catch (error) { - return false - } - return true - } - - public id: IProduct['id'] - public hash: IProduct['hash'] - public cid: IProduct['cid'] - public store_id?: string | undefined - public schema: IProduct['schema'] - public price?: number | undefined - public rating?: number | undefined - public link: IProduct['link'] - public issuer: IProduct['issuer'] - public status: IProduct['status'] - - /** - * Builds a new [[Product]] instance. - * - * @param stream - The base object from which to create the stream. - * @example ```javascript - * // create an stream, e.g. to store it on-chain - * const stream = new Product(stream); - * ``` - */ - public constructor(stream: IProduct) { - ProductUtils.errorCheck(stream) - this.id = stream.id - this.hash = stream.hash - this.cid = stream.cid - this.store_id = stream.store_id - this.schema = stream.schema - this.price = stream.price - this.rating = stream.rating - this.link = stream.link - this.issuer = stream.issuer - this.status = stream.status - } - - /** - * [ASYNC] Stores the stream on chain. - * @param cid - The IPFS CID of the stream stream. - * @returns A promise containing the unsigned SubmittableExtrinsic (submittable transaction). - * @example ```javascript - * // Use `store` to store an stream on chain, and to create an `ProductedProduct` upon success: - * stream.store(cid).then(() => { - * // the stream store tx was successfully prepared, so now we can sign and send it and subsequently create an `ProductedProduct`. - * }); - * ``` - */ - public async create(): Promise { - return create(this) - } - - public async list(): Promise { - return list(this) - } - - public async order(): Promise { - return order(this) - } - - public async order_return(): Promise { - return order_return(this.id, this.issuer) - } - - public async order_rating(): Promise { - return order_rating(this) - } - - /** - * [ASYNC] Set status (active/revoked) a journal stream. - * - * @param status - bool value to set the status of the journal stream. - * @returns A promise containing the unsigned SubmittableExtrinsic (submittable transaction). - * @example ```javascript - * stream.set_status(false).then((tx) => { - * // the stream entry status tx was created, sign and send it! - * ChainUtils.signAndSendTx(tx, identity); - * }); - * ``` - */ - public async set_status(status: boolean): Promise { - return set_status(this.id, this.issuer, status) - } - - /** - * [STATIC] [ASYNC] Queries an stream from the chain and checks its validity. - * - * @param stream - The Product to verify. - * @param identifier - The ID that corresponds to the stream to check. Defaults to the streamHash for the stream onto which "verify" is called. - * @returns A promise containing whether the stream is valid. - * @example ```javascript - * Product.checkValidity(stream).then((isVerified) => { - * // `isVerified` is true if the stream is verified, false otherwise - * }); - * ``` - */ - public static async checkValidity( - stream: IProduct, - identifier: string = stream.id - ): Promise { - // Query stream by stream identifier. null if no stream is found on-chain for this hash - const chainProduct: ProductDetails | null = await Product.query(identifier) - return !!( - chainProduct !== null && - chainProduct.issuer === stream.issuer && - chainProduct.tx_hash === stream.hash && - chainProduct.status - ) - } - - public async checkValidity(): Promise { - return Product.checkValidity(this) - } - - /** - * Compresses an [[Product]] object. - * - * @returns An array that contains the same properties of an [[Product]]. - */ - public compress(): CompressedProduct { - return ProductUtils.compress(this) - } - - /** - * [STATIC] Builds an [[Product]] from the compressed array. - * - * @param stream The [[CompressedProduct]] that should get decompressed. - * @returns A new [[Product]] object. - */ - public static decompress(stream: CompressedProduct): Product { - const decompressedProduct = ProductUtils.decompress(stream) - return Product.fromProduct(decompressedProduct) - } -} - -export class ProductDetails implements IProductDetails { - public static fromProductDetails(input: IProductDetails): ProductDetails { - return new ProductDetails(input) - } - /** - * Builds a new [[Product]] instance. - * - * @param stream - The base object from which to create the stream. - * @example ```javascript - * // create an stream, e.g. to store it on-chain - * const stream = new Product(stream); - * ``` - */ - - public id: IProductDetails['id'] - public tx_hash: IProductDetails['tx_hash'] - public cid: IProductDetails['cid'] - public store_id: IProductDetails['store_id'] - public parent_cid: IProductDetails['parent_cid'] - public schema: IProductDetails['schema'] - public link: IProductDetails['link'] - public issuer: IProductDetails['issuer'] - public price: IProductDetails['price'] - public rating: IProductDetails['rating'] - public block: IProductDetails['block'] - public status: IProductDetails['status'] - - public constructor(details: IProductDetails) { - // ProductUtils.errorCheck(details) - this.id = details.id - this.tx_hash = details.tx_hash - this.cid = details.cid - this.parent_cid = details.parent_cid - this.store_id = details.store_id - this.schema = details.schema - this.link = details.link - this.issuer = details.issuer - this.price = details.price - this.rating = details.rating - this.block = details.block - this.status = details.status - } -} diff --git a/packages/modules/src/product/Product.utils.ts b/packages/modules/src/product/Product.utils.ts deleted file mode 100644 index ffc11c29..00000000 --- a/packages/modules/src/product/Product.utils.ts +++ /dev/null @@ -1,113 +0,0 @@ -/** - * @packageDocumentation - * @module ProductUtils - */ - -import type { IProduct, CompressedProduct } from '@cord.network/types' -import { Crypto, DataUtils, SDKErrors } from '@cord.network/utils' - -/** - * Checks whether the input meets all the required criteria of an [[IProduct]] object. - * Throws on invalid input. - * - * @param input The potentially only partial [[IProduct]]. - * - */ -export function errorCheck(input: IProduct): void { - if (!input.id) { - throw new SDKErrors.ERROR_STREAM_ID_NOT_PROVIDED() - } else DataUtils.validateHash(input.id, 'Product ID') - - if (!input.hash) { - throw new SDKErrors.ERROR_STREAM_HASH_NOT_PROVIDED() - } else DataUtils.validateHash(input.hash, 'Product hash') - - if (!input.schema) { - throw new SDKErrors.ERROR_STREAM_SCHEMA_ID_NOT_PROVIDED() - } else DataUtils.validateHash(input.schema, 'Schema link') - - //TODO: Fix this - // if (!input.link) { - // throw SDKErrors.ERROR_MARK_JOURNAL_ID_NOT_PROVIDED() - // } else DataUtils.validateHash(input.link, 'Mark link') - - if (!input.issuer) { - throw new SDKErrors.ERROR_STREAM_OWNER_NOT_PROVIDED() - } else DataUtils.validateAddress(input.issuer, 'Product controller') - - if (typeof input.status !== 'boolean') { - throw new SDKErrors.ERROR_REVOCATION_BIT_MISSING() - } -} - -/** - * Compresses an [[Mark]] object into an array for storage and/or messaging. - * - * @param stream An [[Mark]] object that will be sorted and stripped for messaging or storage. - * - * @returns An ordered array of an [[Mark]]. - */ - -export function compress(stream: IProduct): CompressedProduct { - errorCheck(stream) - return [ - stream.id, - stream.hash, - stream.cid, - stream.store_id, - stream.schema, - stream.price, - stream.rating, - stream.link, - stream.issuer, - stream.status, - ] -} - -/** - * Decompresses an [[Mark]] from storage and/or message into an object. - * - * @param stream A compressed [[Mark]] array that is decompressed back into an object. - * @throws [[ERROR_DECOMPRESSION_ARRAY]] when the stream is not an array or its length is not equal to 5. - * - * @returns An object that has the same properties as an [[Mark]]. - */ - -export function decompress(stream: CompressedProduct): IProduct { - if (!Array.isArray(stream) || stream.length !== 10) { - throw new SDKErrors.ERROR_DECOMPRESSION_ARRAY('Mark') - } - return { - id: stream[0], - hash: stream[1], - cid: stream[2], - store_id: stream[3], - schema: stream[4], - price: stream[5], - rating: stream[6], - link: stream[7], - issuer: stream[8], - status: stream[9], - } -} - -export function getIdForProduct(hash: string): string { - return getIdForProduct(Crypto.hashObjectAsStr(hash)) -} - -export function getIdWithPrefix(hash: string): string { - return `stream:cord:${hash}` -} - -export function getIdentifier(identifier: string): string { - return identifier.split('stream:cord:').join('') -} - -/** - * Convert from hex to string - * @param hex Hex string with prefix `0x` - * @returns With string back - */ -export function hexToString(hex: string): string { - return Buffer.from(hex.substring(2), 'hex').toString() -} diff --git a/packages/modules/src/product/index.ts b/packages/modules/src/product/index.ts deleted file mode 100644 index d911dc0a..00000000 --- a/packages/modules/src/product/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { Product } from './Product.js' -export * as ProductUtils from './Product.utils.js' diff --git a/packages/modules/src/schema/Schema.chain.ts b/packages/modules/src/schema/Schema.chain.ts index 2d7951e4..f3ecfba1 100644 --- a/packages/modules/src/schema/Schema.chain.ts +++ b/packages/modules/src/schema/Schema.chain.ts @@ -32,7 +32,7 @@ export async function create(schema: ISchema): Promise { const tx: SubmittableExtrinsic = blockchain.api.tx.schema.create( schema.controller, schema.schemaHash, - schema.space, + Identifier.getIdentifierKey(schema.identifier, SPACE_PREFIX), schema.controllerSignature ) return tx diff --git a/packages/modules/src/schema/TypeSchema.ts b/packages/modules/src/schema/TypeSchema.ts index 9d825d4a..6da00aca 100644 --- a/packages/modules/src/schema/TypeSchema.ts +++ b/packages/modules/src/schema/TypeSchema.ts @@ -80,7 +80,7 @@ export const SchemaWrapperModel = { properties: SchemaModel.properties, required: SchemaModel.required, }, - schemaId: { + identifier: { type: 'string', format: 'uri', pattern: '^schema:cord:4[0-9a-zA-Z]+$', @@ -89,13 +89,11 @@ export const SchemaWrapperModel = { type: 'string', }, controller: { type: ['string', 'null'] }, - // version: { - // type: 'string', - // }, + space: { type: ['string', 'null'] }, controllerSignature: { type: ['string', 'null'] }, }, additionalProperties: false, - required: ['schema', 'schemaId', 'schemaHash'], + required: ['schema', 'identifier', 'schemaHash'], } export const MetadataModel = { @@ -179,10 +177,11 @@ export const MetadataModel = { required: ['title', 'properties'], additionalProperties: false, }, - schemaId: { type: 'string', minLength: 1 }, + identifier: { type: 'string', minLength: 1 }, schemaHash: { type: 'string', minLength: 1 }, version: { type: 'string', minLength: 1 }, + space: { type: 'string', minLength: 1 }, }, - required: ['metadata', 'schemaId', 'schemaHash'], + required: ['metadata', 'identifier', 'schemaHash'], additionalProperties: false, } diff --git a/packages/network/src/chain/Chain.spec.ts b/packages/network/src/chain/Chain.spec.ts index 674cb098..739fcd4a 100644 --- a/packages/network/src/chain/Chain.spec.ts +++ b/packages/network/src/chain/Chain.spec.ts @@ -1,389 +1,389 @@ -/** - * @group unit/blockchain - */ - -/* eslint-disable dot-notation */ -import { SDKErrors } from '@cord.network/utils' -import { Text } from '@polkadot/types' -import type { SignerPayload } from '@polkadot/types/interfaces/types' -import type { SignerPayloadJSON } from '@polkadot/types/types/extrinsic' -import { BN } from '@polkadot/util' -import { Keyring } from '@polkadot/keyring' - -import type { - IIdentity, - ISubmittableResult, - SubmittableExtrinsic, - SubscriptionPromise, -} from '@cord.network/types' - -import { getConnectionOrConnect } from '../chainApiConnection/ChainApiConnection' -import { Chain } from './Chain' -import { - EXTRINSIC_FAILED, - isRecoverableTxError, - IS_ERROR, - IS_FINALIZED, - parseSubscriptionOptions, - submitSignedTx, -} from './Chain.utils' - -let api: any - -jest.mock('../blockchainApiConnection/ChainApiConnection') - -describe('queries', () => { - beforeAll(() => { - const api = - require('../blockchainApiConnection/ChainApiConnection').__mocked_api - api.rpc.system.version.mockResolvedValue(new Text(TYPE_REGISTRY, '1.0.0')) - api.rpc.system.chain.mockResolvedValue(new Text(TYPE_REGISTRY, 'mockchain')) - api.rpc.system.name.mockResolvedValue(new Text(TYPE_REGISTRY, 'CORD node')) - - api.rpc.chain.subscribeNewHeads = jest.fn(async (listener) => { - listener('mockHead') - return jest.fn() - }) - }) - - it('should get stats', async () => { - const blockchain = await getConnectionOrConnect() - - await expect(blockchain.getStats()).resolves.toMatchObject({ - chain: 'mockchain', - nodeName: 'CORD node', - nodeVersion: '1.0.0', - }) - }) - - it('should listen to blocks', async () => { - const listener = jest.fn() - const blockchain = await getConnectionOrConnect() - const unsubscribe = await blockchain.listenToBlocks(listener) - expect(listener).toBeCalledWith('mockHead') - expect(unsubscribe()).toBeUndefined() - }) -}) - -describe('Tx logic', () => { - let alice: IIdentity - let bob: IIdentity - const api = - require('../blockchainApiConnection/ChainApiConnection').__mocked_api - const setDefault = - require('../blockchainApiConnection/ChainApiConnection').__setDefaultResult - const dispatchNonceRetrieval = async (address: string): Promise => { - const chain = await getConnectionOrConnect() - return chain.getNonce(address) - } - beforeAll(async () => { - const keyring = new Keyring({ - type: 'ed25519', - // CORD has registered the ss58 prefix 29 - ss58Format: 29, - }) - const alicePair = keyring.createFromUri('//Alice') - alice = { - signKeyringPair: alicePair, - address: alicePair.address, - } as IIdentity - const bobPair = keyring.createFromUri('//Bob') - bob = { - signKeyringPair: bobPair, - address: bobPair.address, - } as IIdentity - }) - - describe('getNonce', () => { - it('should increment nonce for account', async () => { - const chain = new Chain(api) - const initialNonce = await chain.getNonce(alice.address) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - expect(chain['accountNonces'].get(alice.address)!.toNumber()).toEqual( - initialNonce.toNumber() + 1 - ) - }) - - it('should return incrementing nonces', async () => { - const promisedNonces: Array> = [] - const chain = new Chain(api) - for (let i = 0; i < 25; i += 1) { - promisedNonces.push(chain.getNonce(alice.address)) - } - const nonces = await Promise.all(promisedNonces) - expect(nonces.length).toEqual(25) - nonces.forEach((value, index) => { - expect(value.toNumber()).toEqual(index) - }) - }) - - it('should return nonces from different closures', async () => { - const promisedNonces: Array> = [] - for (let i = 0; i < 10; i += 1) { - promisedNonces.push(dispatchNonceRetrieval(alice.address)) - promisedNonces.push(dispatchNonceRetrieval(alice.address)) - } - const nonces = await Promise.all(promisedNonces) - expect(nonces.length).toEqual(20) - nonces.forEach((value, index) => { - expect(value.toNumber()).toEqual(index) - }) - }) - - it('should return separate incrementing nonces per account', async () => { - const alicePromisedNonces: Array> = [] - const bobPromisedNonces: Array> = [] - const chain = new Chain(api) - for (let i = 0; i < 50; i += 1) { - if (i % 2 === 0) { - alicePromisedNonces.push(chain.getNonce(alice.address)) - } else bobPromisedNonces.push(chain.getNonce(bob.address)) - } - const aliceNonces = await Promise.all(alicePromisedNonces) - const bobNonces = await Promise.all(bobPromisedNonces) - expect(aliceNonces.length).toEqual(25) - expect(bobNonces.length).toEqual(25) - aliceNonces.forEach((value, index) => { - expect(value.toNumber()).toEqual(index) - }) - bobNonces.forEach((value, index) => { - expect(value.toNumber()).toEqual(index) - }) - }) - - it('should return the highest read Nonce (mapped Index 1st read)', async () => { - const chain = new Chain(api) - const indexMap = jest - .spyOn(chain['accountNonces'], 'get') - .mockReturnValue(new BN(1191220)) - const nonce = await chain.getNonce(alice.address) - - expect(nonce.toNumber()).toEqual(1191220) - indexMap.mockRestore() - }) - - it('should return the highest read Nonce (mapped Index 2nd read)', async () => { - const chain = new Chain(api) - const indexMap = jest - .spyOn(chain['accountNonces'], 'get') - .mockReturnValue(new BN(11912201)) - .mockReturnValueOnce(undefined) - const nonce = await chain.getNonce(alice.address) - - expect(nonce.toNumber()).toEqual(11912201) - indexMap.mockRestore() - }) - - it('should return the highest read Nonce (chain Index > secondQuery)', async () => { - const chain = new Chain(api) - api.rpc.system.accountNextIndex.mockResolvedValue(new BN(11912202)) - - const indexMap = jest - .spyOn(chain['accountNonces'], 'get') - .mockReturnValue(new BN(11912201)) - .mockReturnValueOnce(undefined) - const nonce = await chain.getNonce(alice.address) - - expect(nonce.toNumber()).toEqual(11912202) - indexMap.mockRestore() - }) - - it('should return the highest read Nonce (chain Index, !secondQuery)', async () => { - const chain = new Chain(api) - api.rpc.system.accountNextIndex.mockResolvedValue( - chain.api.registry.createType('Index', '11912203') - ) - - const indexMap = jest - .spyOn(chain['accountNonces'], 'get') - .mockReturnValue(undefined) - const nonce = await chain.getNonce(alice.address) - - expect(nonce.toNumber()).toEqual(11912203) - indexMap.mockRestore() - }) - - it('should reject when chain returns error', async () => { - api.rpc.system.accountNextIndex.mockRejectedValue('Reason') - const chain = new Chain(api) - await expect(chain.getNonce(alice.address)).rejects.toThrow( - Error(`Chain failed to retrieve nonce for : ${alice.address}`) - ) - }) - }) - - describe('reSignTx', () => { - const submittable: SubmittableExtrinsic = { - signature: { - toHuman: jest.fn(), - }, - addSignature: jest.fn(), - nonce: { toHuman: jest.fn() }, - method: { data: 'unchanged', toHex: jest.fn() }, - } as unknown as SubmittableExtrinsic - - it('fetches updated Nonce and applies updated signature to Extrinsic', async () => { - api.createType = jest - .fn() - .mockReturnValue({ - sign: jest.fn().mockReturnValue({ signature: 'signature' }), - }) - .mockReturnValueOnce({ - toPayload: jest - .fn() - .mockReturnValue({} as unknown as SignerPayloadJSON), - } as unknown as SignerPayload) - const chain = new Chain(api) - const getNonceSpy = jest - .spyOn(chain, 'getNonce') - .mockResolvedValue(new BN(1)) - const deleteEntrySpy = jest.spyOn(chain['accountNonces'], 'delete') - const reSigned = await chain.reSignTx(alice, submittable) - expect(deleteEntrySpy).toHaveBeenCalledWith(alice.address) - expect(reSigned.method.data).toEqual(submittable.method.data) - expect(getNonceSpy).toHaveBeenCalledWith(alice.address) - expect(submittable.addSignature).toHaveBeenCalledWith( - alice.address, - expect.anything(), - expect.anything() - ) - }) - }) - - describe('utils exported function submitSignedTx', () => { - it('catches ERROR_TRANSACTION_USURPED and rejects Promise with ERROR_TRANSACTION_RECOVERABLE', async () => { - setDefault({ isUsurped: true }) - const chain = new Chain(api) - const tx = chain.api.tx.balances.transfer(bob.address, 100) - tx.signAsync(alice.signKeyringPair) - await expect( - submitSignedTx(tx, parseSubscriptionOptions()) - ).rejects.toThrow(SDKErrors.ERROR_TRANSACTION_RECOVERABLE()) - }, 20_000) - - it('catches priority error and rejects Promise with ERROR_TRANSACTION_RECOVERABLE', async () => { - setDefault() - const chain = new Chain(api) - const tx = chain.api.tx.balances.transfer(bob.address, 100) - tx.signAsync(alice.signKeyringPair) - tx.send = jest.fn().mockRejectedValue(Error('1014: Priority is too low:')) - await expect( - submitSignedTx(tx, parseSubscriptionOptions()) - ).rejects.toThrow(SDKErrors.ERROR_TRANSACTION_RECOVERABLE()) - }, 20_000) - - it('catches Already Imported error and rejects Promise with ERROR_TRANSACTION_RECOVERABLE', async () => { - setDefault() - const chain = new Chain(api) - const tx = chain.api.tx.balances.transfer(bob.address, 100) - tx.signAsync(alice.signKeyringPair) - tx.send = jest - .fn() - .mockRejectedValue(Error('Transaction Already Imported')) - await expect( - submitSignedTx(tx, parseSubscriptionOptions()) - ).rejects.toThrow(SDKErrors.ERROR_TRANSACTION_RECOVERABLE()) - }, 20_000) - - it('catches Outdated/Stale Tx error and rejects Promise with ERROR_TRANSACTION_RECOVERABLE', async () => { - setDefault() - const chain = new Chain(api) - const tx = chain.api.tx.balances.transfer(bob.address, 100) - tx.signAsync(alice.signKeyringPair) - tx.send = jest - .fn() - .mockRejectedValue( - Error('1010: Invalid Transaction: Transaction is outdated') - ) - await expect( - submitSignedTx(tx, parseSubscriptionOptions()) - ).rejects.toThrow(SDKErrors.ERROR_TRANSACTION_RECOVERABLE()) - }, 20_000) - }) - - describe('Chain class method submitSignedTx', () => { - it('Retries to send up to two times if recoverable error is caught', async () => { - setDefault({ isUsurped: true }) - const chain = new Chain(api) - const tx = chain.api.tx.balances.transfer(bob.address, 100) - tx.signAsync(alice.signKeyringPair) - const reSignSpy = jest - .spyOn(chain, 'reSignTx') - .mockImplementation(async (id, Tx) => { - return Tx - }) - await expect(chain.submitSignedTxWithReSign(tx, alice)).rejects.toThrow( - SDKErrors.ERROR_TRANSACTION_RECOVERABLE() - ) - - expect(reSignSpy).toHaveBeenCalledTimes(2) - }) - }) -}) - -describe('parseSubscriptionOptions', () => { - it('takes incomplete SubscriptionPromiseOptions and sets default values where needed', async () => { - const testfunction: SubscriptionPromise.ResultEvaluator = () => true - expect(JSON.stringify(parseSubscriptionOptions())).toEqual( - JSON.stringify({ - resolveOn: IS_FINALIZED, - rejectOn: (result: ISubmittableResult) => - IS_ERROR(result) || EXTRINSIC_FAILED(result) || IS_USURPED(result), - timeout: undefined, - }) - ) - expect( - JSON.stringify(parseSubscriptionOptions({ resolveOn: testfunction })) - ).toEqual( - JSON.stringify({ - resolveOn: testfunction, - rejectOn: (result: ISubmittableResult) => - IS_ERROR(result) || EXTRINSIC_FAILED(result) || IS_USURPED(result), - timeout: undefined, - }) - ) - expect( - JSON.stringify( - parseSubscriptionOptions({ - resolveOn: testfunction, - rejectOn: testfunction, - }) - ) - ).toEqual( - JSON.stringify({ - resolveOn: testfunction, - rejectOn: testfunction, - timeout: undefined, - }) - ) - expect( - JSON.stringify( - parseSubscriptionOptions({ - resolveOn: testfunction, - timeout: 10, - }) - ) - ).toEqual( - JSON.stringify({ - resolveOn: testfunction, - rejectOn: (result: ISubmittableResult) => - IS_ERROR(result) || EXTRINSIC_FAILED(result) || IS_USURPED(result), - timeout: 10, - }) - ) - expect( - JSON.stringify( - parseSubscriptionOptions({ - timeout: 10, - }) - ) - ).toEqual( - JSON.stringify({ - resolveOn: IS_FINALIZED, - rejectOn: (result: ISubmittableResult) => - IS_ERROR(result) || EXTRINSIC_FAILED(result) || IS_USURPED(result), - timeout: 10, - }) - ) - }) -}) +// /** +// * @group unit/blockchain +// */ + +// /* eslint-disable dot-notation */ +// import { SDKErrors } from '@cord.network/utils' +// import { Text } from '@polkadot/types' +// import type { SignerPayload } from '@polkadot/types/interfaces/types' +// import type { SignerPayloadJSON } from '@polkadot/types/types/extrinsic' +// import { BN } from '@polkadot/util' +// import { Keyring } from '@polkadot/keyring' + +// import type { +// IIdentity, +// ISubmittableResult, +// SubmittableExtrinsic, +// SubscriptionPromise, +// } from '@cord.network/types' + +// import { getConnectionOrConnect } from '../chainApiConnection/ChainApiConnection' +// import { Chain } from './Chain' +// import { +// EXTRINSIC_FAILED, +// isRecoverableTxError, +// IS_ERROR, +// IS_FINALIZED, +// parseSubscriptionOptions, +// submitSignedTx, +// } from './Chain.utils' + +// let api: any + +// jest.mock('../blockchainApiConnection/ChainApiConnection') + +// describe('queries', () => { +// beforeAll(() => { +// const api = +// require('../blockchainApiConnection/ChainApiConnection').__mocked_api +// api.rpc.system.version.mockResolvedValue(new Text(TYPE_REGISTRY, '1.0.0')) +// api.rpc.system.chain.mockResolvedValue(new Text(TYPE_REGISTRY, 'mockchain')) +// api.rpc.system.name.mockResolvedValue(new Text(TYPE_REGISTRY, 'CORD node')) + +// api.rpc.chain.subscribeNewHeads = jest.fn(async (listener) => { +// listener('mockHead') +// return jest.fn() +// }) +// }) + +// it('should get stats', async () => { +// const blockchain = await getConnectionOrConnect() + +// await expect(blockchain.getStats()).resolves.toMatchObject({ +// chain: 'mockchain', +// nodeName: 'CORD node', +// nodeVersion: '1.0.0', +// }) +// }) + +// it('should listen to blocks', async () => { +// const listener = jest.fn() +// const blockchain = await getConnectionOrConnect() +// const unsubscribe = await blockchain.listenToBlocks(listener) +// expect(listener).toBeCalledWith('mockHead') +// expect(unsubscribe()).toBeUndefined() +// }) +// }) + +// describe('Tx logic', () => { +// let alice: IIdentity +// let bob: IIdentity +// const api = +// require('../blockchainApiConnection/ChainApiConnection').__mocked_api +// const setDefault = +// require('../blockchainApiConnection/ChainApiConnection').__setDefaultResult +// const dispatchNonceRetrieval = async (address: string): Promise => { +// const chain = await getConnectionOrConnect() +// return chain.getNonce(address) +// } +// beforeAll(async () => { +// const keyring = new Keyring({ +// type: 'ed25519', +// // CORD has registered the ss58 prefix 29 +// ss58Format: 29, +// }) +// const alicePair = keyring.createFromUri('//Alice') +// alice = { +// signKeyringPair: alicePair, +// address: alicePair.address, +// } as IIdentity +// const bobPair = keyring.createFromUri('//Bob') +// bob = { +// signKeyringPair: bobPair, +// address: bobPair.address, +// } as IIdentity +// }) + +// describe('getNonce', () => { +// it('should increment nonce for account', async () => { +// const chain = new Chain(api) +// const initialNonce = await chain.getNonce(alice.address) +// // eslint-disable-next-line @typescript-eslint/no-non-null-assertion +// expect(chain['accountNonces'].get(alice.address)!.toNumber()).toEqual( +// initialNonce.toNumber() + 1 +// ) +// }) + +// it('should return incrementing nonces', async () => { +// const promisedNonces: Array> = [] +// const chain = new Chain(api) +// for (let i = 0; i < 25; i += 1) { +// promisedNonces.push(chain.getNonce(alice.address)) +// } +// const nonces = await Promise.all(promisedNonces) +// expect(nonces.length).toEqual(25) +// nonces.forEach((value, index) => { +// expect(value.toNumber()).toEqual(index) +// }) +// }) + +// it('should return nonces from different closures', async () => { +// const promisedNonces: Array> = [] +// for (let i = 0; i < 10; i += 1) { +// promisedNonces.push(dispatchNonceRetrieval(alice.address)) +// promisedNonces.push(dispatchNonceRetrieval(alice.address)) +// } +// const nonces = await Promise.all(promisedNonces) +// expect(nonces.length).toEqual(20) +// nonces.forEach((value, index) => { +// expect(value.toNumber()).toEqual(index) +// }) +// }) + +// it('should return separate incrementing nonces per account', async () => { +// const alicePromisedNonces: Array> = [] +// const bobPromisedNonces: Array> = [] +// const chain = new Chain(api) +// for (let i = 0; i < 50; i += 1) { +// if (i % 2 === 0) { +// alicePromisedNonces.push(chain.getNonce(alice.address)) +// } else bobPromisedNonces.push(chain.getNonce(bob.address)) +// } +// const aliceNonces = await Promise.all(alicePromisedNonces) +// const bobNonces = await Promise.all(bobPromisedNonces) +// expect(aliceNonces.length).toEqual(25) +// expect(bobNonces.length).toEqual(25) +// aliceNonces.forEach((value, index) => { +// expect(value.toNumber()).toEqual(index) +// }) +// bobNonces.forEach((value, index) => { +// expect(value.toNumber()).toEqual(index) +// }) +// }) + +// it('should return the highest read Nonce (mapped Index 1st read)', async () => { +// const chain = new Chain(api) +// const indexMap = jest +// .spyOn(chain['accountNonces'], 'get') +// .mockReturnValue(new BN(1191220)) +// const nonce = await chain.getNonce(alice.address) + +// expect(nonce.toNumber()).toEqual(1191220) +// indexMap.mockRestore() +// }) + +// it('should return the highest read Nonce (mapped Index 2nd read)', async () => { +// const chain = new Chain(api) +// const indexMap = jest +// .spyOn(chain['accountNonces'], 'get') +// .mockReturnValue(new BN(11912201)) +// .mockReturnValueOnce(undefined) +// const nonce = await chain.getNonce(alice.address) + +// expect(nonce.toNumber()).toEqual(11912201) +// indexMap.mockRestore() +// }) + +// it('should return the highest read Nonce (chain Index > secondQuery)', async () => { +// const chain = new Chain(api) +// api.rpc.system.accountNextIndex.mockResolvedValue(new BN(11912202)) + +// const indexMap = jest +// .spyOn(chain['accountNonces'], 'get') +// .mockReturnValue(new BN(11912201)) +// .mockReturnValueOnce(undefined) +// const nonce = await chain.getNonce(alice.address) + +// expect(nonce.toNumber()).toEqual(11912202) +// indexMap.mockRestore() +// }) + +// it('should return the highest read Nonce (chain Index, !secondQuery)', async () => { +// const chain = new Chain(api) +// api.rpc.system.accountNextIndex.mockResolvedValue( +// chain.api.registry.createType('Index', '11912203') +// ) + +// const indexMap = jest +// .spyOn(chain['accountNonces'], 'get') +// .mockReturnValue(undefined) +// const nonce = await chain.getNonce(alice.address) + +// expect(nonce.toNumber()).toEqual(11912203) +// indexMap.mockRestore() +// }) + +// it('should reject when chain returns error', async () => { +// api.rpc.system.accountNextIndex.mockRejectedValue('Reason') +// const chain = new Chain(api) +// await expect(chain.getNonce(alice.address)).rejects.toThrow( +// Error(`Chain failed to retrieve nonce for : ${alice.address}`) +// ) +// }) +// }) + +// describe('reSignTx', () => { +// const submittable: SubmittableExtrinsic = { +// signature: { +// toHuman: jest.fn(), +// }, +// addSignature: jest.fn(), +// nonce: { toHuman: jest.fn() }, +// method: { data: 'unchanged', toHex: jest.fn() }, +// } as unknown as SubmittableExtrinsic + +// it('fetches updated Nonce and applies updated signature to Extrinsic', async () => { +// api.createType = jest +// .fn() +// .mockReturnValue({ +// sign: jest.fn().mockReturnValue({ signature: 'signature' }), +// }) +// .mockReturnValueOnce({ +// toPayload: jest +// .fn() +// .mockReturnValue({} as unknown as SignerPayloadJSON), +// } as unknown as SignerPayload) +// const chain = new Chain(api) +// const getNonceSpy = jest +// .spyOn(chain, 'getNonce') +// .mockResolvedValue(new BN(1)) +// const deleteEntrySpy = jest.spyOn(chain['accountNonces'], 'delete') +// const reSigned = await chain.reSignTx(alice, submittable) +// expect(deleteEntrySpy).toHaveBeenCalledWith(alice.address) +// expect(reSigned.method.data).toEqual(submittable.method.data) +// expect(getNonceSpy).toHaveBeenCalledWith(alice.address) +// expect(submittable.addSignature).toHaveBeenCalledWith( +// alice.address, +// expect.anything(), +// expect.anything() +// ) +// }) +// }) + +// describe('utils exported function submitSignedTx', () => { +// it('catches ERROR_TRANSACTION_USURPED and rejects Promise with ERROR_TRANSACTION_RECOVERABLE', async () => { +// setDefault({ isUsurped: true }) +// const chain = new Chain(api) +// const tx = chain.api.tx.balances.transfer(bob.address, 100) +// tx.signAsync(alice.signKeyringPair) +// await expect( +// submitSignedTx(tx, parseSubscriptionOptions()) +// ).rejects.toThrow(SDKErrors.ERROR_TRANSACTION_RECOVERABLE()) +// }, 20_000) + +// it('catches priority error and rejects Promise with ERROR_TRANSACTION_RECOVERABLE', async () => { +// setDefault() +// const chain = new Chain(api) +// const tx = chain.api.tx.balances.transfer(bob.address, 100) +// tx.signAsync(alice.signKeyringPair) +// tx.send = jest.fn().mockRejectedValue(Error('1014: Priority is too low:')) +// await expect( +// submitSignedTx(tx, parseSubscriptionOptions()) +// ).rejects.toThrow(SDKErrors.ERROR_TRANSACTION_RECOVERABLE()) +// }, 20_000) + +// it('catches Already Imported error and rejects Promise with ERROR_TRANSACTION_RECOVERABLE', async () => { +// setDefault() +// const chain = new Chain(api) +// const tx = chain.api.tx.balances.transfer(bob.address, 100) +// tx.signAsync(alice.signKeyringPair) +// tx.send = jest +// .fn() +// .mockRejectedValue(Error('Transaction Already Imported')) +// await expect( +// submitSignedTx(tx, parseSubscriptionOptions()) +// ).rejects.toThrow(SDKErrors.ERROR_TRANSACTION_RECOVERABLE()) +// }, 20_000) + +// it('catches Outdated/Stale Tx error and rejects Promise with ERROR_TRANSACTION_RECOVERABLE', async () => { +// setDefault() +// const chain = new Chain(api) +// const tx = chain.api.tx.balances.transfer(bob.address, 100) +// tx.signAsync(alice.signKeyringPair) +// tx.send = jest +// .fn() +// .mockRejectedValue( +// Error('1010: Invalid Transaction: Transaction is outdated') +// ) +// await expect( +// submitSignedTx(tx, parseSubscriptionOptions()) +// ).rejects.toThrow(SDKErrors.ERROR_TRANSACTION_RECOVERABLE()) +// }, 20_000) +// }) + +// describe('Chain class method submitSignedTx', () => { +// it('Retries to send up to two times if recoverable error is caught', async () => { +// setDefault({ isUsurped: true }) +// const chain = new Chain(api) +// const tx = chain.api.tx.balances.transfer(bob.address, 100) +// tx.signAsync(alice.signKeyringPair) +// const reSignSpy = jest +// .spyOn(chain, 'reSignTx') +// .mockImplementation(async (id, Tx) => { +// return Tx +// }) +// await expect(chain.submitSignedTxWithReSign(tx, alice)).rejects.toThrow( +// SDKErrors.ERROR_TRANSACTION_RECOVERABLE() +// ) + +// expect(reSignSpy).toHaveBeenCalledTimes(2) +// }) +// }) +// }) + +// describe('parseSubscriptionOptions', () => { +// it('takes incomplete SubscriptionPromiseOptions and sets default values where needed', async () => { +// const testfunction: SubscriptionPromise.ResultEvaluator = () => true +// expect(JSON.stringify(parseSubscriptionOptions())).toEqual( +// JSON.stringify({ +// resolveOn: IS_FINALIZED, +// rejectOn: (result: ISubmittableResult) => +// IS_ERROR(result) || EXTRINSIC_FAILED(result) || IS_USURPED(result), +// timeout: undefined, +// }) +// ) +// expect( +// JSON.stringify(parseSubscriptionOptions({ resolveOn: testfunction })) +// ).toEqual( +// JSON.stringify({ +// resolveOn: testfunction, +// rejectOn: (result: ISubmittableResult) => +// IS_ERROR(result) || EXTRINSIC_FAILED(result) || IS_USURPED(result), +// timeout: undefined, +// }) +// ) +// expect( +// JSON.stringify( +// parseSubscriptionOptions({ +// resolveOn: testfunction, +// rejectOn: testfunction, +// }) +// ) +// ).toEqual( +// JSON.stringify({ +// resolveOn: testfunction, +// rejectOn: testfunction, +// timeout: undefined, +// }) +// ) +// expect( +// JSON.stringify( +// parseSubscriptionOptions({ +// resolveOn: testfunction, +// timeout: 10, +// }) +// ) +// ).toEqual( +// JSON.stringify({ +// resolveOn: testfunction, +// rejectOn: (result: ISubmittableResult) => +// IS_ERROR(result) || EXTRINSIC_FAILED(result) || IS_USURPED(result), +// timeout: 10, +// }) +// ) +// expect( +// JSON.stringify( +// parseSubscriptionOptions({ +// timeout: 10, +// }) +// ) +// ).toEqual( +// JSON.stringify({ +// resolveOn: IS_FINALIZED, +// rejectOn: (result: ISubmittableResult) => +// IS_ERROR(result) || EXTRINSIC_FAILED(result) || IS_USURPED(result), +// timeout: 10, +// }) +// ) +// }) +// }) diff --git a/packages/types/src/Message.ts b/packages/types/src/Message.ts index 987e54bc..e88d4805 100644 --- a/packages/types/src/Message.ts +++ b/packages/types/src/Message.ts @@ -14,7 +14,10 @@ import type { } from './Content.js' import type { ISchema } from './Schema.js' import type { IPublicIdentity } from './PublicIdentity.js' -import type { CompressedMarkContent, IMarkContent } from './ContentStream.js' +import type { + CompressedContentStream, + IContentStream, +} from './ContentStream.js' import { IPresentation } from './Presentation.js' export enum MessageBodyType { @@ -92,7 +95,7 @@ export interface IAnchorStream extends IMessageBodyBase { type: MessageBodyType.ANCHOR_STREAM } export interface IRejectStream extends IMessageBodyBase { - content: IMarkContent['contentId'] + content: IContentStream['identifier'] type: MessageBodyType.REJECT_STREAM } @@ -105,11 +108,11 @@ export interface ISubmitCredential extends IMessageBodyBase { type: MessageBodyType.SUBMIT_CREDENTIAL } export interface IAcceptCredential extends IMessageBodyBase { - content: Array + content: Array type: MessageBodyType.ACCEPT_CREDENTIAL } export interface IRejectCredential extends IMessageBodyBase { - content: Array + content: Array type: MessageBodyType.REJECT_CREDENTIAL } @@ -123,7 +126,7 @@ export type CompressedAnchorStream = [ ] export type CompressedRejectStream = [ MessageBodyType.REJECT_STREAM, - IMarkContent['contentId'] + IContentStream['identifier'] ] export type CompressedRequestCredential = [ @@ -136,15 +139,15 @@ export type CompressedSubmitCredential = [ ] export type CompressedAcceptCredential = [ MessageBodyType.ACCEPT_CREDENTIAL, - Array + Array ] export type CompressedRejectCredential = [ MessageBodyType.REJECT_CREDENTIAL, - Array + Array ] export interface IRequestStreamContent { - requestStream: IMarkContent + requestStream: IContentStream prerequisiteStreams?: Array } // Seems this can be removed @@ -152,26 +155,26 @@ export interface IAnchorStreamContent { stream: IStream } export interface IRequestStreamForCredential { - id: ISchema['schemaId'] + id: ISchema['identifier'] acceptedIssuer?: Array requiredProperties?: string[] } export type CompressedPartialContentStream = [ - IContent['schemaId'], + IContent['schema'], IContent['issuer'] | undefined, IContent['holder'] | undefined, IContents | undefined ] export type CompressedRequestCredentialContent = [ - ISchema['schemaId'], + ISchema['identifier'], Array | undefined, string[] | undefined ] export type CompressedRequestStreamContent = [ - CompressedMarkContent, + CompressedContentStream, Array | undefined ] diff --git a/packages/types/src/Product.ts b/packages/types/src/Product.ts deleted file mode 100644 index 36fc6db0..00000000 --- a/packages/types/src/Product.ts +++ /dev/null @@ -1,69 +0,0 @@ -/** - * @packageDocumentation - * @module IProduct - */ -import type { ISchema } from './Schema.js' -import type { IPublicIdentity } from './PublicIdentity.js' - -export interface IProduct { - id: string - hash: string - cid: string - store_id?: string - schema?: ISchema['schemaId'] - price?: number - rating?: number - link?: string - issuer: IPublicIdentity['address'] - status: boolean -} - -export type CompressedProduct = [ - IProduct['id'], - IProduct['hash'], - IProduct['cid'], - IProduct['store_id'], - IProduct['schema'], - IProduct['price'], - IProduct['rating'], - IProduct['link'], - IProduct['issuer'], - IProduct['status'] -] - -export interface IProductDetails { - id: IProduct['id'] - tx_hash: IProduct['hash'] - cid: string | null - parent_cid: string | null - store_id: IProduct['id'] | null - schema: ISchema['schemaId'] | null - link: IProduct['id'] | null - issuer: IPublicIdentity['address'] - price: IProduct['id'] | null - rating: IProduct['id'] | null - block: string - status: boolean -} - -export interface IProductLinks { - id: IProduct['id'] - store_id: IProduct['id'] - issuer: IPublicIdentity['address'] -} - -export enum ProductCommitOf { - Create, - List, - Update, - Order, - Return, - Rating, - StatusChange, -} -export interface IProductCommits { - tx_hash: IProduct['hash'] - cid: string | null - block: string - commit: ProductCommitOf -} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 2da4859c..bfa0ed09 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -12,7 +12,6 @@ export * from './SchemaMetadata.js' export * from './Content.js' export * from './ContentStream.js' export * from './Stream.js' -export * from './Product.js' export * from './Mark.js' export * from './Balance.js' export * from './Chain.js' diff --git a/tsconfig.json b/tsconfig.json index 0c358175..9fae67b5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,6 @@ "baseUrl": "./packages", "sourceMap": true, "paths": { - "@cord.network/exchange": ["exchange/src"], "@cord.network/network": ["network/src"], "@cord.network/config": ["config/src"], "@cord.network/modules": ["modules/src"], diff --git a/yarn.lock b/yarn.lock index 02f1c591..6baf93fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -583,7 +583,6 @@ __metadata: version: 0.0.0-use.local resolution: "@cord.network/api@workspace:packages/api" dependencies: - "@cord.network/exchange": "workspace:*" "@cord.network/messaging": "workspace:*" "@cord.network/modules": "workspace:*" "@cord.network/network": "workspace:*" @@ -637,21 +636,6 @@ __metadata: languageName: unknown linkType: soft -"@cord.network/exchange@workspace:*, @cord.network/exchange@workspace:packages/exchange": - version: 0.0.0-use.local - resolution: "@cord.network/exchange@workspace:packages/exchange" - dependencies: - "@cord.network/config": "workspace:*" - "@cord.network/messaging": "workspace:*" - "@cord.network/modules": "workspace:*" - "@cord.network/network": "workspace:*" - "@cord.network/types": "workspace:*" - "@cord.network/utils": "workspace:*" - rimraf: ^3.0.2 - typescript: ^4.5.4 - languageName: unknown - linkType: soft - "@cord.network/messaging@workspace:*, @cord.network/messaging@workspace:packages/messaging": version: 0.0.0-use.local resolution: "@cord.network/messaging@workspace:packages/messaging"