Skip to content

Commit

Permalink
208 add metadata read and write (#215)
Browse files Browse the repository at this point in the history
* Update massa-web3 to 5.1.0

* Refactor estimateUploadBatchesGas to use Provider instance and address instead of SmartContract

* Add getWebsiteOwner function to retrieve websites owned by a given address

* Update estimateUploadBatchesGas call to use prover and address

* Use PublicProvider instead of Provider in website read functions

* Add global metadata functions

* Use LAST_UPDATE_KEY constant for metadata in prepareUploadTask

* Add metadata handling to config parsing

* Update import statements in delete.ts

* Enhance upload preparation task to include metadata changes and cost estimation

* Remove unused filesInitCost import from prepareUpload task

* Add new metadata handling to upload command

* Add default values for wallet, password, address, and metadatas in config
  • Loading branch information
thomas-senechal authored Jan 13, 2025
1 parent cc176c6 commit 93aff52
Show file tree
Hide file tree
Showing 13 changed files with 343 additions and 80 deletions.
13 changes: 7 additions & 6 deletions cli/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"dependencies": {
"@commander-js/extra-typings": "^12.1.0",
"@listr2/prompt-adapter-enquirer": "^2.0.11",
"@massalabs/massa-web3": "5.0.1-dev.20241212140726",
"@massalabs/massa-web3": "5.1.0",
"commander": "^12.1.0",
"enquirer": "^2.4.1",
"js-sha256": "^0.11.0",
Expand Down
33 changes: 31 additions & 2 deletions cli/src/commands/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { PublicApiUrl } from '@massalabs/massa-web3'
import { Address, PublicApiUrl } from '@massalabs/massa-web3'
import { OptionValues } from 'commander'
import { readFileSync } from 'fs'

import { Metadata } from '../lib/website/models/Metadata'

export const DEFAULT_CHUNK_SIZE = 64000
const DEFAULT_NODE_URL = PublicApiUrl.Buildnet

Expand All @@ -11,12 +13,25 @@ interface Config {
node_url: string
chunk_size: number
secret_key: string
address: string
metadatas: { [key: string]: string }
}

export function parseConfigFile(filePath: string): Config {
const fileContent = readFileSync(filePath, 'utf-8')
try {
return JSON.parse(fileContent)
const config = JSON.parse(fileContent) as Config

// If address is provided, make sure it's valid
if (config.address) {
try {
Address.fromString(config.address)
} catch (error) {
throw new Error(`Invalid address in config file: ${error}`)
}
}

return config
} catch (error) {
throw new Error(`Failed to parse file: ${error}`)
}
Expand All @@ -35,12 +50,26 @@ export function mergeConfigAndOptions(
commandOptions.node_url || configOptions.node_url || DEFAULT_NODE_URL,
chunk_size: configOptions.chunk_size || DEFAULT_CHUNK_SIZE,
secret_key: configOptions.secret_key || '',
address: configOptions.address || '',
metadatas: configOptions.metadatas
? makeMetadataArray(configOptions.metadatas)
: [],
}
}

function makeMetadataArray(metadatas: { [key: string]: string }): Metadata[] {
return Object.entries(metadatas).map(
([key, value]) => new Metadata(key, value)
)
}

export function setDefaultValues(commandOptions: OptionValues): OptionValues {
return {
node_url: commandOptions.node_url || DEFAULT_NODE_URL,
chunk_size: commandOptions.chunk_size || DEFAULT_CHUNK_SIZE,
wallet: commandOptions.wallet || '',
password: commandOptions.password || '',
address: '',
metadatas: [],
}
}
16 changes: 14 additions & 2 deletions cli/src/commands/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { prepareUploadTask } from '../tasks/prepareUpload'
import { UploadCtx } from '../tasks/tasks'
import { confirmUploadTask, uploadBatchesTask } from '../tasks/upload'

import { divideMetadata } from '../lib/website/metadata'
import { Metadata } from '../lib/website/models/Metadata'
import { makeProviderFromNodeURLAndSecret, validateAddress } from './utils'

export const uploadCommand = new Command('upload')
Expand Down Expand Up @@ -41,13 +43,23 @@ export const uploadCommand = new Command('upload')
options.noIndex
)

if (options.address) {
const address = options.address
if (options.address || globalOptions.address) {
const address = options.address || (globalOptions.address as string)
console.log(`Editing website at address ${address}, no deploy needed`)

validateAddress(address)

ctx.sc = new SmartContract(provider, address)

const { updateRequired } = await divideMetadata(
ctx.provider,
ctx.sc.address,
globalOptions.metadatas as Metadata[]
)

ctx.metadatas.push(...updateRequired)
} else {
ctx.metadatas.push(...(globalOptions.metadatas as Metadata[]))
}

const tasksArray = [
Expand Down
4 changes: 4 additions & 0 deletions cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
setDefaultValues,
} from './commands/config'

import { Metadata } from './lib/website/models/Metadata'

const version = process.env.VERSION || 'dev'
const defaultConfigPath = 'deweb_cli_config.json'

Expand All @@ -38,6 +40,8 @@ interface OptionValues {
node_url: string
wallet: string
password: string
address: string
metadatas: Metadata[]
}

const commandOptions: OptionValues = program.opts() as OptionValues
Expand Down
30 changes: 27 additions & 3 deletions cli/src/lib/index/read.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { bytesToStr, Provider } from '@massalabs/massa-web3'
import { bytesToStr, PublicProvider } from '@massalabs/massa-web3'

import { addressToOwnerBaseKey } from './keys'
import { addressToOwnerBaseKey, indexByOwnerBaseKey } from './keys'
import { getSCAddress } from './utils'

/**
Expand All @@ -10,7 +10,7 @@ import { getSCAddress } from './utils'
* @returns The owner of the website
*/
export async function getWebsiteOwner(
provider: Provider,
provider: PublicProvider,
address: string
): Promise<string> {
const scAddress = getSCAddress((await provider.networkInfos()).chainId)
Expand All @@ -25,3 +25,27 @@ export async function getWebsiteOwner(
const ownerKeySliced = ownerKey.slice(prefix.length)
return bytesToStr(ownerKeySliced)
}

/**
* Get the website addresses owned by an address according to the index smart contract.
* @param provider - PublicProvider instance
* @param owner - The owner's address
* @returns The website addresses owned by the owner
*/
export async function getAddressWebsites(
provider: PublicProvider,
owner: string
): Promise<string[]> {
const scAddress = getSCAddress((await provider.networkInfos()).chainId)
const prefix = indexByOwnerBaseKey(owner)

const keys = await provider.getStorageKeys(scAddress, prefix)
if (keys.length === 0) {
return []
}

return keys.map((key) => {
const keySliced = key.slice(prefix.length)
return bytesToStr(keySliced)
})
}
3 changes: 2 additions & 1 deletion cli/src/lib/website/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
SmartContract,
} from '@massalabs/massa-web3'

import { getGlobalMetadata, listFiles } from './read'
import { listFiles } from './read'
import { getGlobalMetadata } from './metadata'

import { FileDelete } from './models/FileDelete'
import { Metadata } from './models/Metadata'
Expand Down
53 changes: 43 additions & 10 deletions cli/src/lib/website/filesInit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,26 @@ import {
strToBytes,
U32,
} from '@massalabs/massa-web3'

import {
CallManager,
CallStatus,
CallUpdate,
FunctionCall,
} from '../utils/callManager'
import { storageCostForEntry } from '../utils/storage'
import { maxBigInt } from '../utils/utils'
import { FileDelete } from './models/FileDelete'

import { FileInit } from './models/FileInit'
import { Metadata } from './models/Metadata'

import {
FILE_TAG,
fileChunkCountKey,
fileLocationKey,
globalMetadataKey,
} from './storageKeys'
import { FileDelete } from './models/FileDelete'
import { maxBigInt } from '../utils/utils'
import {
FunctionCall,
CallManager,
CallUpdate,
CallStatus,
} from '../utils/callManager'

const functionName = 'filesInit'
export const batchSize = 32
Expand Down Expand Up @@ -154,13 +157,19 @@ export async function sendFilesInits(

// TODO: Improve estimation
// - If a file is already stored, we don't need to send coins for its hash storage
export async function filesInitCost(
export async function prepareCost(
_: SmartContract,
files: FileInit[],
filesToDelete: FileDelete[],
metadatas: Metadata[],
metadatasToDelete: Metadata[]
): Promise<bigint> {
): Promise<{
filePathListCost: bigint
storageCost: bigint
filesToDeleteCost: bigint
metadatasCost: bigint
metadatasToDeleteCost: bigint
}> {
const filePathListCost = files.reduce((acc, chunk) => {
return (
acc +
Expand Down Expand Up @@ -211,6 +220,30 @@ export async function filesInitCost(
)
}, 0n)

return {
filePathListCost,
storageCost,
filesToDeleteCost,
metadatasCost,
metadatasToDeleteCost,
}
}

export async function filesInitCost(
_sc: SmartContract,
files: FileInit[],
filesToDelete: FileDelete[],
metadatas: Metadata[],
metadatasToDelete: Metadata[]
): Promise<bigint> {
const {
filePathListCost,
storageCost,
filesToDeleteCost,
metadatasCost,
metadatasToDeleteCost,
} = await prepareCost(_sc, files, filesToDelete, metadatas, metadatasToDelete)

return BigInt(
filePathListCost +
storageCost +
Expand Down
Loading

0 comments on commit 93aff52

Please sign in to comment.