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

feat: add asset edit functionality #58

Merged
merged 25 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
aeada59
feat: add asset edit functionality
Abrom8 Sep 20, 2023
03e18dd
feat: add missing edit functions for services
Abrom8 Oct 27, 2023
5f37136
feat: add getPublisherTrustedAlgorithms helper
Abrom8 Nov 2, 2023
06fe332
fix: test config
LucaMilanese90 Nov 9, 2023
0b4591e
chore: fix typo
LucaMilanese90 Nov 9, 2023
92ee736
feat: update edit tests
LucaMilanese90 Nov 9, 2023
79f3bd7
chore: update package-lock
LucaMilanese90 Nov 10, 2023
0d7adf8
refactor: add resolvePublisherTrustedAlgorithms to publish and edit flow
Abrom8 Nov 15, 2023
d7a8d8d
fix: handle undefined trustedAlgorithmAssets
Abrom8 Nov 16, 2023
a745656
docs: add Trusted Algorithms docs link
Abrom8 Nov 16, 2023
d11e4fd
reafctor: rework asset retrieval and creation
Abrom8 Nov 17, 2023
e33eaad
refactor: change lifecyclestate flow
Abrom8 Nov 19, 2023
f079bb0
refactor: create checkIfFilesObjectChanged function
Abrom8 Nov 20, 2023
6676d0c
refactor: extract createDatatokenAndPricing call from publish and edit
Abrom8 Nov 20, 2023
0b68d27
docs: add TODO comment for price change via datatoken replacement
Abrom8 Nov 20, 2023
7a1b68c
refactor: expose editServicePrice via Nautilus
Abrom8 Nov 20, 2023
023fe2d
Merge remote-tracking branch 'origin/feat/edit' into feat/update-edit…
LucaMilanese90 Nov 23, 2023
d4ffe10
refactor: improve getAssets return type
Abrom8 Nov 23, 2023
ee5fcb3
refactor: add early continue to resolvePublisherTrustedAlgorithms
Abrom8 Nov 23, 2023
4314263
refactor: relocate transformAquariusAssetToDDO into utils folder
Abrom8 Nov 23, 2023
d1a4fe5
feat: update tests
LucaMilanese90 Nov 23, 2023
4f46325
refactor: add AssetSpecificProps type
Abrom8 Nov 24, 2023
e2cc885
Merge remote-tracking branch 'origin/feat/edit' into feat/update-edit…
LucaMilanese90 Nov 23, 2023
f7c5068
Merge pull request #60 from deltaDAO/feat/update-edit-tests
moritzkirstein Nov 24, 2023
f1a2ba6
docs: add TODO for AssetSpecificProps type
Abrom8 Nov 24, 2023
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
6 changes: 0 additions & 6 deletions src/@types/Nautilus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,11 @@ import {
NftCreateDataWithoutOwner,
PricingConfig
} from './Publish'
import { NautilusDDO } from '../Nautilus/Asset/NautilusDDO'

export interface NautilusOptions {
skipDefaultConfig: boolean
}

export type AssetBuilderConfig = {
nautilusDDO: NautilusDDO
aquariusAsset: Asset
}

export type ServiceBuilderConfig =
| {
serviceType: ServiceTypes
Expand Down
5 changes: 5 additions & 0 deletions src/@types/Publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,8 @@ export interface PublishResponse {
ddo: DDO
setMetadataTxReceipt: providers.TransactionReceipt
}

export type TrustedAlgorithmAsset = {
did: string
serviceIds?: string[]
}
20 changes: 8 additions & 12 deletions src/Nautilus/Asset/AssetBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Asset } from '@oceanprotocol/lib'
import {
AssetBuilderConfig,
CredentialListTypes,
IAssetBuilder,
LifecycleStates
Expand All @@ -12,21 +12,17 @@ import {
NautilusService,
ServiceTypes
} from './Service/NautilusService'
import { NautilusDDO } from './NautilusDDO'

export class AssetBuilder implements IAssetBuilder {
private asset: NautilusAsset

constructor(config?: AssetBuilderConfig) {
if (
(!config?.nautilusDDO && config?.aquariusAsset) ||
(config?.nautilusDDO && !config?.aquariusAsset)
) {
throw new Error('Invalid AssetBuilder cunstructor parameter combination')
}
if (config?.nautilusDDO && config?.aquariusAsset) {
this.asset = new NautilusAsset(config.nautilusDDO)
this.asset.owner = config.aquariusAsset.nft.owner
this.asset.lifecycleState = config.aquariusAsset.nft.state
constructor(aquariusAsset?: Asset) {
if (aquariusAsset) {
const nautilusDDO = NautilusDDO.createFromAquariusAsset(aquariusAsset)
this.asset = new NautilusAsset(nautilusDDO)
this.asset.owner = aquariusAsset.nft.owner
this.asset.lifecycleState = aquariusAsset.nft.state
} else {
this.asset = new NautilusAsset()
}
Expand Down
21 changes: 1 addition & 20 deletions src/Nautilus/Asset/NautilusAsset.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Nft, NftCreateData } from '@oceanprotocol/lib'
import { NftCreateData } from '@oceanprotocol/lib'
import { NftCreateDataWithoutOwner, PricingConfig } from '../../@types/Publish'
import { NautilusDDO } from './NautilusDDO'
import { nftInitialCreateData } from './constants/nft.constants'
import { LifecycleStates } from '../../@types'
import { Signer } from 'ethers'

export type PricingConfigWithoutOwner = {
type: PricingConfig['type']
Expand All @@ -29,24 +28,6 @@ export class NautilusAsset {
this.initNftData()
}

public static async setLifecycleState(
signer: Signer,
nftAddress: string,
publisherAccount: string,
state: LifecycleStates
) {
const nft = new Nft(signer)

const stateTxReceipt = await nft.setMetadataState(
nftAddress,
publisherAccount,
state
)
const stateTx = await stateTxReceipt.wait()

return stateTx
}

private initNftData() {
this.nftCreateData = nftInitialCreateData
}
Expand Down
53 changes: 24 additions & 29 deletions src/Nautilus/Asset/NautilusDDO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import {
Asset,
Credentials,
DDO,
LoggerInstance,
Service,
generateDid
} from '@oceanprotocol/lib'
import { LifecycleStates, MetadataConfig } from '../../@types'
import { MetadataConfig } from '../../@types'
import {
dateToStringNoMS,
getAllPromisesOnArray,
Expand All @@ -18,8 +17,6 @@ import {
NautilusService,
ServiceTypes
} from './Service/NautilusService'
import { getAsset } from '../../utils/aquarius'
import { Nautilus } from '../Nautilus'

export class NautilusDDO {
id: string
Expand All @@ -37,6 +34,12 @@ export class NautilusDDO {
deny: []
}

static createFromAquariusAsset(aquariusAsset: Asset): NautilusDDO {
const ddo = this.transformAquariusAssetToDDO(aquariusAsset)

return this.createFromDDO(ddo)
}

static createFromDDO(ddo: DDO): NautilusDDO {
const nautilusDDO = new NautilusDDO()
nautilusDDO.ddo = ddo
Expand All @@ -55,26 +58,6 @@ export class NautilusDDO {
return nautilusDDO
}

static async createFromDID(
did: string,
nautilus: Nautilus
): Promise<{ aquariusAsset: Asset; nautilusDDO: NautilusDDO }> {
const config = nautilus.getOceanConfig()

const asset = await getAsset(config.metadataCacheUri, did)
if (!asset)
throw new Error(
`No asset found for ${asset} in cache ${config.metadataCacheUri}`
)
if (asset.nft.state === LifecycleStates.REVOKED_BY_PUBLISHER)
LoggerInstance.warn('Unable to fetch asset: Revoked by publisher')

const ddo: DDO = asset as DDO // TODO remove data fields from Aquarius Asset which do not belong to DOO

const nautilusDDO = this.createFromDDO(ddo)
return { aquariusAsset: asset, nautilusDDO }
}

getOriginalDDO() {
return this.ddo
}
Expand Down Expand Up @@ -114,11 +97,7 @@ export class NautilusDDO {

// remove service from existing services if id changes to prevent old service after edit
for (const service of this.services) {
const isFilesObjectChanged = !!(
(service.editExistingService && service.filesEdited) ||
(service.editExistingService && service.serviceEndpointEdited) ||
service.pricing
)
const isFilesObjectChanged = service.checkIfFilesObjectChanged()

if (service.id && isFilesObjectChanged) {
this.removeServices.push(service.id)
Expand Down Expand Up @@ -260,4 +239,20 @@ export class NautilusDDO {
// Otherwise we return the replacement
return replacementService
}

static transformAquariusAssetToDDO(aquariusAsset: Asset): DDO {
const ddo = structuredClone(aquariusAsset)

const unwantedAssetAttributes = [
Abrom8 marked this conversation as resolved.
Show resolved Hide resolved
'nft',
'datatokens',
'event',
'stats',
'purgatory'
]
for (const attribute of unwantedAssetAttributes) {
delete ddo[attribute]
}
return ddo as DDO
}
}
60 changes: 15 additions & 45 deletions src/Nautilus/Asset/Service/NautilusService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
Arweave,
FixedRateExchange,
GraphqlQuery,
Ipfs,
Service,
Expand All @@ -9,7 +8,10 @@ import {
UrlFile,
getHash
} from '@oceanprotocol/lib'
import { DatatokenCreateParamsWithoutOwner } from '../../../@types/Publish'
import {
DatatokenCreateParamsWithoutOwner,
TrustedAlgorithmAsset
} from '../../../@types/Publish'
import {
getEncryptedFiles,
getFileInfo,
Expand All @@ -18,10 +20,6 @@ import {
import { NautilusConsumerParameter } from '../ConsumerParameters'
import { PricingConfigWithoutOwner } from '../NautilusAsset'
import { params as DatatokenConstantParams } from '../constants/datatoken.constants'
import { Nautilus } from '../../Nautilus'
import { Signer } from 'ethers'
import { getAsset } from '../../../utils/aquarius'
import { getAccessDetails } from '../../../utils/helpers/access-details'

export {
Arweave,
Expand Down Expand Up @@ -85,6 +83,8 @@ export class NautilusService<
publisherTrustedAlgorithms: []
}

addedPublisherTrustedAlgorithms: TrustedAlgorithmAsset[] = []

consumerParameters?: NautilusConsumerParameter[] = []
additionalInformation?: { [key: string]: any }

Expand All @@ -101,40 +101,6 @@ export class NautilusService<
this.datatokenCreateParams = DatatokenConstantParams
}

public static async editPrice(
nautilus: Nautilus,
signer: Signer,
did: string,
serviceId: string,
newPrice: string
) {
const config = nautilus.getOceanConfig()

const aquariusAsset = await getAsset(config.metadataCacheUri, did)

const service: Service = aquariusAsset.services.find(
(service) => service.id === serviceId
)

const fixedRateInstance = new FixedRateExchange(
config.fixedRateExchangeAddress,
signer
)

const accessDetails = await getAccessDetails(
config.subgraphUri,
service.datatokenAddress
)

const tx = await fixedRateInstance.setRate(
accessDetails.addressOrId,
newPrice
)
const txReceipt = await tx.wait()

return txReceipt
}

async getOceanService(
chainId: number,
nftAddress: string,
Expand All @@ -149,11 +115,7 @@ export class NautilusService<
const datatokenAddress = dtAddress || this.datatokenAddress
if (!datatokenAddress) throw new Error('datatokenAddress is required')

const isFilesObjectChanged = !!(
(this.editExistingService && this.filesEdited) ||
(this.editExistingService && this.serviceEndpointEdited) ||
this.pricing
)
const isFilesObjectChanged = this.checkIfFilesObjectChanged()

let encryptedFiles: string

Expand Down Expand Up @@ -216,4 +178,12 @@ export class NautilusService<

return true
}

checkIfFilesObjectChanged(): boolean {
return (
(this.editExistingService &&
(this.filesEdited || this.serviceEndpointEdited)) ||
!!this.pricing
)
}
}
54 changes: 25 additions & 29 deletions src/Nautilus/Asset/Service/ServiceBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PublisherTrustedAlgorithm, Service, Asset } from '@oceanprotocol/lib'
import { Service } from '@oceanprotocol/lib'
import { IServiceBuilder, ServiceBuilderConfig } from '../../../@types/Nautilus'
import {
ConsumerParameterBuilder,
Expand All @@ -12,7 +12,8 @@ import {
} from './NautilusService'
import {
ConsumerParameterSelectOption,
DatatokenCreateParamsWithoutOwner
DatatokenCreateParamsWithoutOwner,
TrustedAlgorithmAsset
} from '../../../@types/Publish'
import { PricingConfigWithoutOwner } from '../NautilusAsset'

Expand Down Expand Up @@ -172,39 +173,34 @@ export class ServiceBuilder<
return this
}

addTrustedAlgorithms(algorithms: PublisherTrustedAlgorithm[]) {
addTrustedAlgorithms(trustedAlgorithmAssets: TrustedAlgorithmAsset[]) {
if (this.service.type !== 'compute') {
throw new Error('Illegal operation, asset is not a compute asset!')
}

// Check if the algorithms array is empty
if (algorithms.length === 0) {
throw new Error('No algorithms provided.')
if (!trustedAlgorithmAssets || trustedAlgorithmAssets.length === 0) {
throw new Error('No TrustedAlgorithmAssets provided.')
}

// Initialize the publisherTrustedAlgorithms array if it doesn't exist
if (!this.service.compute.publisherTrustedAlgorithms) {
this.service.compute.publisherTrustedAlgorithms = []
}

algorithms.forEach((algorithm) => {
const index = this.service.compute.publisherTrustedAlgorithms.findIndex(
(existingAlgorithm) => existingAlgorithm.did === algorithm.did
)

if (index === -1) {
// Algorithm with the same DID doesn't exist, add it
this.service.compute.publisherTrustedAlgorithms.push(algorithm)
trustedAlgorithmAssets.forEach((trustedAlgorithmAsset) => {
const existingIndex =
this.service.addedPublisherTrustedAlgorithms.findIndex(
(existingAsset) => existingAsset.did === trustedAlgorithmAsset.did
)

if (existingIndex > -1) {
// Merge serviceIds
this.service.addedPublisherTrustedAlgorithms[existingIndex].serviceIds =
Array.from(
new Set([
...(this.service.addedPublisherTrustedAlgorithms[existingIndex]
.serviceIds || []),
...(trustedAlgorithmAsset.serviceIds || [])
])
)
} else {
// If either checksum is different, replace the existing algorithm
const existing = this.service.compute.publisherTrustedAlgorithms[index]
if (
existing.containerSectionChecksum !==
algorithm.containerSectionChecksum ||
existing.filesChecksum !== algorithm.filesChecksum
) {
this.service.compute.publisherTrustedAlgorithms[index] = algorithm
}
// Add new trusted algorithm asset
this.service.addedPublisherTrustedAlgorithms.push(trustedAlgorithmAsset)
}
})

Expand Down Expand Up @@ -306,7 +302,7 @@ export class ServiceBuilder<
setPricing(pricing: PricingConfigWithoutOwner) {
if (this.service.editExistingService)
throw new Error(
'Can not set new Pricing configs for existing assets. Use static editPrice() function.'
'Can not set new pricing configs for existing services using the builder. Use nautilus.setServicePrice() method instead.'
)
this.service.pricing = pricing

Expand Down
Loading