Skip to content

Commit

Permalink
feat: credits flows
Browse files Browse the repository at this point in the history
  • Loading branch information
aaitor committed Aug 10, 2023
1 parent 95eab12 commit e89e980
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 39 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -400,3 +400,5 @@ tsconfig.build.

circuits
*.tgz
.yalc*
.env*
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "node-ts",
"version": "2.0.0-rc1",
"version": "2.0.0-rc3",
"description": "Nevermined Node",
"main": "main.ts",
"scripts": {
Expand All @@ -21,8 +21,8 @@
"test:cov": "jest ./src --setupFiles dotenv/config --coverage",
"integration": "jest ./integration --setupFiles dotenv/config",
"integration:cov": "jest ./integration --setupFiles dotenv/config --coverage",
"prettier": "prettier --config ./.prettierrc.js --write \"**/*.{js,json,md,sol,ts,yml}\"",
"prettier:check": "prettier --check --config ./.prettierrc.json \"**/*.{js,json,md,sol,ts,yml}\"",
"prettier": "prettier --config ./.prettierrc.js --write \"**/*.{json,md,sol,ts,yml}\"",
"prettier:check": "prettier --check --config ./.prettierrc.json \"**/*.{json,md,sol,ts,yml}\"",
"artifacts:mumbai": "sh ./scripts/download-artifacts.sh $CONTRACT_VERSION mumbai"
},
"dependencies": {
Expand All @@ -36,8 +36,8 @@
"@nestjs/typeorm": "^10.0.0",
"@nevermined-io/argo-workflows-api": "^0.1.3",
"@nevermined-io/passport-nevermined": "^0.2.0",
"@nevermined-io/sdk": "^2.0.0-rc2",
"@nevermined-io/sdk-dtp": "^0.6.0",
"@nevermined-io/sdk": "^2.0.0-rc7",
"@nevermined-io/sdk-dtp": "^0.7.0-rc3",
"@sideway/address": "^5.0.0",
"@sideway/formula": "^3.0.1",
"@sideway/pinpoint": "^2.0.0",
Expand Down
31 changes: 28 additions & 3 deletions src/access/access.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class AccessController {
description: 'Bad Request. DID missing',
type: BadRequestException,
})
@ApiBearerAuth('Authorization')
// @ApiBearerAuth('Authorization')
async doAccess(
@Req() req: Request<unknown>,
@Response({ passthrough: true }) res,
Expand Down Expand Up @@ -184,7 +184,20 @@ export class AccessController {
}

const subscriptionDDO = await this.nvmService.nevermined.assets.resolve(did.getDid())
const duration = await this.nvmService.getDuration(subscriptionDDO, template as ServiceType)
const serviceReference =
transferData.serviceIndex && transferData.serviceIndex >= 0
? transferData.serviceIndex
: (template as ServiceType)

const service = subscriptionDDO.findServiceByReference(serviceReference)

const duration = await this.nvmService.getDuration(subscriptionDDO, serviceReference)

Logger.debug(` -- Service Reference ${serviceReference}`)
Logger.debug(` -- Service Index ${service.index}`)
Logger.debug(` -- Duration ${duration}`)
Logger.debug(` -- Template ${template}`)
Logger.debug(` -- NFT Amount ${transferData.nftAmount}`)

let expiration = 0
if (duration > 0) {
Expand All @@ -195,12 +208,14 @@ export class AccessController {
const params: ValidationParams = {
consumer_address: transferData.nftReceiver,
did: did.getDid(),
service_index: service.index,
agreement_id: transferData.agreementId,
nft_amount: BigInt(transferData.nftAmount || '0'),
buyer: (req.user || {}).buyer,
expiration,
}

Logger.debug(` -- Params ${JSON.stringify(params, jsonReplacer)}`)
const plugin = nevermined.assets.servicePlugin[template]
const [from] = await nevermined.accounts.list()

Expand Down Expand Up @@ -251,7 +266,7 @@ export class AccessController {
description: 'Bad Request. DID missing',
type: BadRequestException,
})
@ApiBearerAuth('Authorization')
// @ApiBearerAuth('Authorization')
async doDownload(
@Req() req: Request<unknown>,
@Response({ passthrough: true }) res,
Expand Down Expand Up @@ -322,3 +337,13 @@ export class AccessController {
}
}
}
export const jsonReplacer = (key, value) => {
// Modify the value or return undefined to exclude the property
if (typeof value === 'bigint') {
return value.toString()
}
// } else if (typeof value === 'object') {
// return ''
// }
return value
}
8 changes: 8 additions & 0 deletions src/access/dto/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,12 @@ export class TransferDto {
})
@IsNumber()
nftType: number

@ApiProperty({
description: 'The service index of the NFT to claim',
example: '3',
})
@IsOptional()
@IsNumber()
serviceIndex: number
}
3 changes: 2 additions & 1 deletion src/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ describe('AuthService', () => {
it('should validate the owner and return true if the owner has permission to access', async () => {
const did = 'did:nft:0x123'
const consumer_address = '0x456'
await authService.validateOwner(did, consumer_address)
const params: ValidationParams = { agreement_id: '0x789', did, consumer_address }
await authService.validateOwner(params)
expect(
nvmServiceMock.getNevermined().keeper.conditions.accessCondition.checkPermissions,
).toHaveBeenCalledWith(consumer_address, did)
Expand Down
66 changes: 53 additions & 13 deletions src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
Account,
Babysig,
DDO,
jsonReplacer,
NeverminedNFT1155Type,
} from '@nevermined-io/sdk'
import { NeverminedService } from '../shared/nevermined/nvm.service'
import { JWTPayload } from '@nevermined-io/passport-nevermined'
Expand All @@ -21,14 +23,20 @@ const BASE_URL = '/api/v1/node/services/'
export class AuthService {
constructor(private jwtService: JwtService, private nvmService: NeverminedService) {}

async validateOwner(did: string, consumer_address: string): Promise<void> {
async validateOwner(params: ValidationParams): Promise<void> {
const nevermined = this.nvmService.getNevermined()
const getNftAccess = async () => {
const ddo = await nevermined.assets.resolve(did)
const ddo = await nevermined.assets.resolve(params.did)
Logger.debug(`Validating owner for ${params.did}`)

const getNftAccess = async (ddo: DDO, serviceIndex?: number) => {
if (!ddo) {
return null
}
const service = ddo.findServiceByType('nft-access')
const service =
serviceIndex && serviceIndex > 0
? ddo.findServiceByIndex(serviceIndex)
: ddo.findServiceByType('nft-access')

if (!service) {
return null
}
Expand All @@ -44,15 +52,36 @@ export class AuthService {
}

const granted = await nevermined.keeper.conditions.accessCondition.checkPermissions(
consumer_address,
did,
params.consumer_address,
params.did,
)
if (!granted) {
const limit = await getNftAccess()
const balance = await nevermined.nfts1155.balance(did, new Account(consumer_address))
const limit = await getNftAccess(ddo, params.service_index)
const balance = await nevermined.nfts1155.balance(
params.did,
new Account(params.consumer_address),
)
if (!limit || balance < limit) {
throw new UnauthorizedException(
`Address ${consumer_address} has no permission to access ${did}`,
`Address ${params.consumer_address} has no permission to access ${params.did}`,
)
}
}

const metadataService = ddo.findServiceByType('metadata')
const isNft1155Credit =
metadataService.attributes.main.nftType.toString() ===
NeverminedNFT1155Type.nft1155Credit.toString()
if (isNft1155Credit) {
Logger.debug(`Validating NFT1155 Credit for ${params.did}`)
const [from] = await nevermined.accounts.list()
const plugin = nevermined.nfts1155.servicePlugin['nft-access']

try {
await plugin.track(params, from)
} catch (error) {
throw new UnauthorizedException(
`Address ${params.consumer_address} could not use the credits to access ${params.did}`,
)
}
}
Expand All @@ -64,10 +93,20 @@ export class AuthService {
const plugin =
nevermined.assets.servicePlugin[service] || nevermined.nfts1155.servicePlugin[service]

const granted = await plugin.accept(params)
if (!granted) {
console.debug(`Params: ${JSON.stringify(params, jsonReplacer)}`)
console.debug(`Service Type: ${service}`)
// console.debug(`Plugin: ${JSON.stringify(plugin, jsonReplacer)}`)

try {
const [from] = await nevermined.accounts.list()
await plugin.process(params, from, undefined)
const granted = await plugin.accept(params)
if (!granted) {
await plugin.process(params, from, undefined)
}

await plugin.track(params, from)
} catch (error) {
throw new UnauthorizedException(`Error processing request: ${error.message}`)
}
}

Expand Down Expand Up @@ -96,12 +135,13 @@ export class AuthService {
agreement_id: payload.sub,
buyer: payload.buyer as string,
babysig: payload.babysig as Babysig,
service_index: payload.service_index as number,
}

if (payload.aud === BASE_URL + 'access') {
await this.validateAccess(params, 'access')
} else if (payload.aud === BASE_URL + 'download') {
await this.validateOwner(payload.did as string, payload.iss)
await this.validateOwner(params)
} else if (payload.aud === BASE_URL + 'nft-access') {
await this.validateAccess(params, 'nft-access')
} else if (payload.aud === BASE_URL + 'nft-sales-proof') {
Expand Down
19 changes: 10 additions & 9 deletions src/shared/nevermined/nvm.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,35 +421,36 @@ export class NeverminedService {
* Get the duration of the subscription in number of blocks
*
* @param subscriptionDDO - The DDO of the subscription
* @param serviceType - The service to fetch the duration from. Usually 'nft-sales' and 'nft-sales-proof'
* @param serviceReference - The service reference to fetch the duration from. Usually 'nft-sales' and 'nft-sales-proof'
*
* @throws {@link BadRequestException}
* @returns {@link Promise<number>} The duration in number of blocks
*/
public async getDuration(
subscriptionDDO: DDO,
serviceType: ServiceType = 'nft-sales',
serviceReference: number | ServiceType = 'nft-sales',
): Promise<number> {
// get the nft-sales service
let nftSalesService: Service
try {
nftSalesService = subscriptionDDO.findServiceByType(serviceType)
nftSalesService = subscriptionDDO.findServiceByReference(serviceReference)
} catch (e) {
if (e instanceof DDOServiceNotFoundError) {
throw new BadRequestException(
`${subscriptionDDO.id} does not contain an '${serviceType}' service`,
`${subscriptionDDO.id} does not contain an '${serviceReference}' service`,
)
} else {
throw e
}
}

// get the nft-holder condition
const transferNftCondition = DDO.findServiceConditionByName(nftSalesService, 'transferNFT')
const duration = transferNftCondition.parameters.find((p) => p.name === '_duration')

// get the duration parameter from the transferNFT condition
// const transferNftCondition = DDO.findServiceConditionByName(nftSalesService, 'transferNFT')
// const duration = transferNftCondition.parameters.find((p) => p.name === '_duration')
// return Number(duration?.value) || 0
const duration = DDO.getParameterFromCondition(nftSalesService, 'transferNFT', '_duration')
// non-subscription nfts have no expiration
return Number(duration?.value) || 0
return Number(duration) || 0
}

/**
Expand Down
14 changes: 6 additions & 8 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1386,10 +1386,10 @@
jose "^4.11.2"
passport-strategy "^1.0.0"

"@nevermined-io/sdk-dtp@^0.6.0":
version "0.6.0"
resolved "https://registry.yarnpkg.com/@nevermined-io/sdk-dtp/-/sdk-dtp-0.6.0.tgz#73a02849943f1b15b5e20dff828a6809d65afcad"
integrity sha512-LPqscWEtHCkBXgQDBmIbyhOhTa4N2XvvE9gt5JTRKfxEwaCh2r1i3nb6nka0ZLOfQ2021fqDsUfXj8zDYVLsZA==
"@nevermined-io/sdk-dtp@^0.7.0-rc2":
version "0.7.0-rc2"
resolved "https://registry.yarnpkg.com/@nevermined-io/sdk-dtp/-/sdk-dtp-0.7.0-rc2.tgz#828d4bbb30f9c0ecea5ba744cec79170fb359046"
integrity sha512-CIYdcSqMkCwff28O9hWVAGfet471mNgTAw3XIsQk7Rv28MOVB5O/90e3xOSvbyNSd6DHgsgH02CNweM/CQ0/UA==
dependencies:
circomlibjs "^0.1.1"
eciesjs "^0.3.15"
Expand All @@ -1398,10 +1398,8 @@
snarkjs "^0.4.26"
web3-utils "^1.7.4"

"@nevermined-io/sdk@^2.0.0-rc2":
version "2.0.0-rc2"
resolved "https://registry.yarnpkg.com/@nevermined-io/sdk/-/sdk-2.0.0-rc2.tgz#d0ad7f629eaf67d04971759a9e225dc8cb81917d"
integrity sha512-wEUDbIL9Z1iRSxyEzpv3rFkZcm4VT9HXLZYv9zaLNnyzyaO+Y0piE5TwZ5q1o+WZMFB6WbcUiQTYbuLBfgnY4A==
"@nevermined-io/sdk@file:.yalc/@nevermined-io/sdk":
version "2.0.0-rc5"
dependencies:
"@apollo/client" "^3.7.16"
assert "^2.0.0"
Expand Down

0 comments on commit e89e980

Please sign in to comment.