Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add score module SDK and update demo-score #133

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 133 additions & 66 deletions demo/src/demo-score.ts
Original file line number Diff line number Diff line change
@@ -1,93 +1,160 @@
import * as Cord from '@cord.network/sdk'
import { ScoreType } from '@cord.network/types'
import { UUID } from '@cord.network/utils'
import { UUID, Crypto } from '@cord.network/utils'
import { generateKeypairs } from './utils/generateKeypairs'
import { createDid } from './utils/generateDid'
import { addRegistryAdminDelegate } from './utils/generateRegistry'
import { randomUUID } from 'crypto'
import { addAuthority } from './utils/createAuthorities'
import { createAccount } from './utils/createAccount'
import { updateScore } from './utils/updateScore'
import { ScoreType, IJournalContent, EntryType } from '@cord.network/types'

async function main() {
await Cord.init({ address: 'ws://127.0.0.1:9944' })

// Step 1: Setup Org Identity
console.log(`\n❄️ Demo Identities (KeyRing)`)
//3x4DHc1rxVAEqKWSx1DAAA8wZxLB4VhiRbMV997niBckUwSi
const sellerIdentity = Cord.Identity.buildFromURI('//Entity', {
signingKeyPairType: 'sr25519',
})
console.log(
`🏛 Seller Entity (${sellerIdentity.signingKeyType}): ${sellerIdentity.address}`
const networkAddress = 'ws://127.0.0.1:63554'
Cord.ConfigService.set({ submitTxResolveOn: Cord.Chain.IS_IN_BLOCK })
await Cord.connect(networkAddress)

const api = Cord.ConfigService.get('api')

console.log(`\n❄️ New Member`)
const authorityAuthorIdentity = Crypto.makeKeypairFromUri(
'//Alice',
'sr25519'
)
// Setup author authority account.
const { account: authorIdentity } = await createAccount()
console.log(`🏦 Member (${authorIdentity.type}): ${authorIdentity.address}`)
await addAuthority(authorityAuthorIdentity, authorIdentity.address)
console.log(`🔏 Member permissions updated`)
console.log('✅ Network Member added!')

// Step 2: Setup Identities
console.log(`\n❄️ Demo Identities (KeyRing)`)
const { mnemonic: issuerMnemonic, document: issuerDid } = await createDid(
authorIdentity
)
const deliveryIdentity = Cord.Identity.buildFromURI('//Delivery', {
signingKeyPairType: 'sr25519',
})
const issuerKeys = generateKeypairs(issuerMnemonic)
console.log(
`🏛 Delivery Entity (${deliveryIdentity.signingKeyType}): ${deliveryIdentity.address}`
`🏛 Issuer (${issuerDid?.assertionMethod![0].type}): ${issuerDid.uri}`
)
const collectorIdentity = Cord.Identity.buildFromURI('//BuyerApp', {
signingKeyPairType: 'ed25519',
})

// Create Delegate One DID
const { mnemonic: delegateOneMnemonic, document: delegateOneDid } =
await createDid(authorIdentity)

const delegateOneKeys = generateKeypairs(delegateOneMnemonic)

console.log(
`🧑🏻‍💼 Score Collector (${collectorIdentity.signingKeyType}): ${collectorIdentity.address}`
`🏛 Delegate (${delegateOneDid?.assertionMethod![0].type}): ${
delegateOneDid.uri
}`
)
const requestorIdentity = Cord.Identity.buildFromURI('//SellerApp', {
signingKeyPairType: 'ed25519',
})

console.log('✅ Identities created!')

// Entities
console.log(`\n❄️ Demo Entities`)
const sellerIdentity = Crypto.makeKeypairFromUri('//Entity', 'sr25519')
console.log(
`👩‍⚕️ Score Requestor (${requestorIdentity.signingKeyType}): ${requestorIdentity.address}`
`🏛 Seller Entity (${sellerIdentity.type}): ${sellerIdentity.address}`
)
await addAuthority(authorityAuthorIdentity, sellerIdentity.address)

const { mnemonic: sellerMnemonic, document: sellerDid } = await createDid(
sellerIdentity
)
const transactionAuthor = Cord.Identity.buildFromURI('//Bob', {
signingKeyPairType: 'sr25519',
})

const collectorIdentity = Crypto.makeKeypairFromUri('//BuyerApp', 'sr25519')
console.log(
`🏢 Transaction Author (${transactionAuthor.signingKeyType}): ${transactionAuthor.address}`
`🧑🏻‍💼 Score Collector (${collectorIdentity.type}): ${collectorIdentity.address}`
)
console.log('✅ Identities created!')
await addAuthority(authorityAuthorIdentity, collectorIdentity.address)

// Step 2: Create a jounal entry
console.log(`\n❄️ Journal Entry `)
let journalContent = {
entity: sellerIdentity.address,
uid: UUID.generate().toString(),
tid: UUID.generate().toString(),
collector: collectorIdentity.address,
requestor: requestorIdentity.address,
scoreType: ScoreType.overall,
score: 3.7,
const { mnemonic: collectorMnemonic, document: collectorDid } =
await createDid(collectorIdentity)

console.log('✅ Entities created!')

console.log(`\n❄️ Registry Creation `)

const registryTitle = `Registry v3.${randomUUID().substring(0, 4)}`
const registryDetails: Cord.IContents = {
title: registryTitle,
description: 'Registry for for scoring',
}
console.dir(journalContent, { depth: null, colors: true })

let newJournalEntry = Cord.Score.fromJournalProperties(
journalContent,
sellerIdentity
)
const registryType: Cord.IRegistryType = {
details: registryDetails,
creator: issuerDid.uri,
}

let journalCreationExtrinsic = await Cord.Score.entries(newJournalEntry)
console.log(`\n❄️ Transformed Journal Entry `)
console.dir(newJournalEntry, { depth: null, colors: true })
const txRegistry: Cord.IRegistry =
Cord.Registry.fromRegistryProperties(registryType)

let registry
try {
await Cord.Chain.signAndSubmitTx(
journalCreationExtrinsic,
transactionAuthor,
{
resolveOn: Cord.Chain.IS_IN_BLOCK,
rejectOn: Cord.Chain.IS_ERROR,
}
await Cord.Registry.verifyStored(txRegistry)
console.log('Registry already stored. Skipping creation')
} catch {
console.log('Regisrty not present. Creating it now...')
// Authorize the tx.
const tx = api.tx.registry.create(txRegistry.details, null)
const extrinsic = await Cord.Did.authorizeTx(
issuerDid.uri,
tx,
async ({ data }) => ({
signature: issuerKeys.assertionMethod.sign(data),
keyType: issuerKeys.assertionMethod.type,
}),
authorIdentity.address
)
console.log('✅ Journal Entry added!')
} catch (e: any) {
console.log(e.errorCode, '-', e.message)
console.log('\n', txRegistry)
// Write to chain then return the Schema.
await Cord.Chain.signAndSubmitTx(extrinsic, authorIdentity)
registry = txRegistry
}
console.log('\n✅ Registry created!')

console.log(`\n❄️ Query Chain Scores `)
const chainScore = await Cord.Score.query(
journalContent.entity,
journalContent.scoreType
// Step 4: Add Delelegate One as Registry Admin
console.log(`\n❄️ Registry Admin Delegate Authorization `)
const registryAuthority = await addRegistryAdminDelegate(
authorIdentity,
issuerDid.uri,
registry['identifier'],
delegateOneDid.uri,
async ({ data }) => ({
signature: issuerKeys.capabilityDelegation.sign(data),
keyType: issuerKeys.capabilityDelegation.type,
})
)
console.dir(chainScore, { depth: null, colors: true })
console.log(`\n✅ Registry Authorization - ${registryAuthority} - created!`)

const chainAvgScore = await Cord.Score.queryAverage(
journalContent.entity,
journalContent.scoreType
console.log(`\n❄️ Journal Entry `)
let journalContent: IJournalContent = {
entity: sellerDid.uri.replace('did:cord:', ''),
adi-a11y marked this conversation as resolved.
Show resolved Hide resolved
tid: UUID.generatev4().toString(),
collector: collectorDid.uri.replace('did:cord:', ''),
rating_type: ScoreType.overall,
rating: 12.116,
entry_type: EntryType.debit,
count: 5,
}
console.dir(journalContent, { depth: null, colors: true })
console.log('\n✅ Journal Entry created!')

console.log('\nAnchoring the score on the blockchain...')
const scoreIdentifier = await updateScore(
journalContent,
registryAuthority,
authorIdentity,
delegateOneDid.uri,
delegateOneKeys
)

console.log(
'\n✅ The score has been successfully anchored on the blockchain \nIdentifier:',
scoreIdentifier
)
console.dir(chainAvgScore, { depth: null, colors: true })
}

main()
Expand Down
56 changes: 56 additions & 0 deletions demo/src/utils/updateScore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import * as Cord from '@cord.network/sdk'
import { IJournalContent, IRatingInput } from '@cord.network/types'

/**
* This function anchors the score on the blockchain
* @param journalContent - Score entry details
* @param registryAuthority - Registry authority
* @param authorIdentity - The account that will be used to sign and submit the extrinsic.
* @param authorDid - DID of the entity which anchors the transaction.
* @param authorKeys - Keys which are used to sign.
* @returns the hash of the score entry if the operation is executed successfully.
*/

export async function updateScore(
journalContent: IJournalContent,
registryAuthority: String,
authorIdentity: Cord.CordKeyringPair,
authorDid: Cord.DidUri,
authorKeys: Cord.CordKeyringPair
) {
const api = Cord.ConfigService.get('api')

journalContent.rating = await Cord.Scoring.adjustAndRoundRating(
journalContent.rating
)
const digest = await Cord.Scoring.generateDigestFromJournalContent(
journalContent
)
const authorization = registryAuthority.replace('auth:cord:', '')
const ratingInput: IRatingInput = {
entry: journalContent,
digest: digest,
creator: authorIdentity.address,
}
const journalCreationExtrinsic = await api.tx.score.addRating(
ratingInput,
authorization
)

const authorizedStreamTx = await Cord.Did.authorizeTx(
authorDid,
journalCreationExtrinsic,
async ({ data }) => ({
signature: authorKeys.assertionMethod.sign(data),
keyType: authorKeys.assertionMethod.type,
}),
authorIdentity.address
)

try {
await Cord.Chain.signAndSubmitTx(authorizedStreamTx, authorIdentity)
return Cord.Scoring.getUriForScore(journalContent)
} catch (error) {
return error.message
}
}
1 change: 1 addition & 0 deletions packages/did/src/DidDetails/FullDidDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const methodMapping: Record<string, VerificationKeyRelationship | undefined> = {
'did.submitDidCall': undefined,
didLookup: 'authentication',
didName: 'authentication',
score:'authentication',
adi-a11y marked this conversation as resolved.
Show resolved Hide resolved
}

function getKeyRelationshipForMethod(
Expand Down
1 change: 1 addition & 0 deletions packages/modules/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * as Registry from './registry/index.js'
export * as Content from './content/index.js'
export * as Stream from './stream/index.js'
export * as Document from './document/index.js'
export * as Scoring from './scoring/index.js'
adi-a11y marked this conversation as resolved.
Show resolved Hide resolved
export { connect, disconnect, init } from './cordconfig/index.js'
export { SDKErrors } from '@cord.network/utils'
// export { Identity, PublicIdentity } from './identity/index.js'
Expand Down
65 changes: 65 additions & 0 deletions packages/modules/src/scoring/Scoring.chain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
ScoreType,
IScoreDetails,
SCORE_MODULUS,
IJournalContent,
} from '@cord.network/types'
import { ConfigService } from '@cord.network/config'
import { Identifier, SDKErrors } from '@cord.network/utils'
import * as Did from '@cord.network/did'

adi-a11y marked this conversation as resolved.
Show resolved Hide resolved
export async function fetchJournalFromChain(
scoreId: string,
scoreType: ScoreType
): Promise<IJournalContent | null> {
const api = ConfigService.get('api')
const cordScoreId = Identifier.uriToIdentifier(scoreId)
const encodedScoreEntry = await api.query.score.journal(
cordScoreId,
scoreType
)
const decodedScoreEntry = fromChain(encodedScoreEntry)
if (decodedScoreEntry === null) {
throw new SDKErrors.ScoreMissingError(
`There is not a Score of type ${scoreType} with the provided ID "${scoreId}" on chain.`
)
} else return decodedScoreEntry
}

export function fromChain(
encodedEntry: any
): IJournalContent | null {
if (encodedEntry.isSome) {
const unwrapped = encodedEntry.unwrap()
return {
entity: Did.fromChain(unwrapped.entry.entity),
tid: JSON.stringify(unwrapped.entry.tid.toHuman()),
collector: Did.fromChain(unwrapped.entry.collector),
rating_type: unwrapped.entry.ratingType.toString(),
rating: parseInt(unwrapped.entry.rating.toString()) / SCORE_MODULUS,
entry_type: unwrapped.entry.entryType.toString(),
count: parseInt(unwrapped.entry.count.toString()),
}
} else {
return null
}
}

export async function fetchScore(
entityUri: string,
scoreType: ScoreType
): Promise<IScoreDetails> {
const api = ConfigService.get('api')
const encoded = await api.query.score.scores(entityUri, scoreType)
if (encoded.isSome) {
const decoded = encoded.unwrap()
return {
rating: JSON.parse(decoded.rating.toString()),
count: JSON.parse(decoded.count.toString()),
}
} else
throw new SDKErrors.ScoreMissingError(
`There is not a Score of type ${scoreType} with the provided ID "${entityUri}" on chain.`
)
}

Loading