{"version":3,"file":"write-blob.mjs","names":["metadata","options"],"sources":["../../src/flows/write-blob.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { fromBase64, toBase64 } from '@mysten/bcs';\nimport type { Signer } from '@mysten/sui/cryptography';\nimport { Transaction } from '@mysten/sui/transactions';\n\nimport type { Blob } from '../contracts/walrus/blob.js';\nimport { WalrusClientError } from '../error.js';\nimport type {\n\tEncodingType,\n\tProtocolMessageCertificate,\n\tSliversForNode,\n\tWriteBlobFlow,\n\tWriteBlobFlowOptions,\n\tWriteBlobFlowRegisterOptions,\n\tWriteBlobFlowRunOptions,\n\tWriteBlobFlowUploadOptions,\n\tWriteBlobStep,\n\tWriteBlobStepCertified,\n\tWriteBlobStepEncoded,\n\tWriteBlobStepRegistered,\n\tWriteBlobStepUploaded,\n} from '../types.js';\nimport { CertificateBcs, parseCertificateFromBase64 } from '../utils/bcs.js';\nimport type { WalrusClient } from '../client.js';\n\nexport interface WriteBlobFlowContext {\n\thasUploadRelay(): boolean;\n\texecuteTransaction(\n\t\ttransaction: Transaction,\n\t\tsigner: Signer,\n\t\taction: string,\n\t): Promise<{ digest: string }>;\n\tgetCreatedBlob(digest: string): Promise<(typeof Blob)['$inferType']>;\n\tloadBlobObject(objectId: string): Promise<(typeof Blob)['$inferType']>;\n}\n\ntype BlobMetadataResult = {\n\tblobId: string;\n\trootHash: Uint8Array;\n\tmetadata: { encodingType: string; unencodedLength: bigint };\n\tnonce: Uint8Array;\n\tblobDigest: () => Promise<Uint8Array>;\n};\n\ntype EncodeBlobResult = {\n\tblobId: string;\n\trootHash: Uint8Array;\n\tmetadata: Awaited<ReturnType<WalrusClient['encodeBlob']>>['metadata'];\n\tsliversByNode: SliversForNode[];\n};\n\ntype EncodeState = {\n\tmetadata: BlobMetadataResult | EncodeBlobResult;\n\tsize: number;\n\tdata?: Uint8Array;\n};\n\ntype RegisterState = EncodeState & { deletable: boolean };\n\ntype UploadState = {\n\tblobObject: (typeof Blob)['$inferType'];\n\tblobId: string;\n\tdeletable: boolean;\n\tcertificate: ProtocolMessageCertificate;\n};\n\ntype CertifyState = {\n\tblobObject: (typeof Blob)['$inferType'];\n\tblobId: string;\n\ttransaction: Transaction;\n};\n\nexport function createWriteBlobFlow(\n\tclient: WalrusClient,\n\tctx: WriteBlobFlowContext,\n\toptions: WriteBlobFlowOptions,\n): WriteBlobFlow {\n\tconst { resume } = options;\n\tlet encodeState: EncodeState | undefined;\n\tlet registerState: RegisterState | undefined;\n\tlet uploadState: UploadState | undefined;\n\tlet certifyState: CertifyState | undefined;\n\n\tconst resumeBlobObjectId = resume && 'blobObjectId' in resume ? resume.blobObjectId : undefined;\n\tconst resumeTxDigest = resume && 'txDigest' in resume ? resume.txDigest : undefined;\n\tconst resumeNonce =\n\t\tresume && 'nonce' in resume && resume.nonce ? fromBase64(resume.nonce) : undefined;\n\tconst resumeBlobId = resume ? resume.blobId : undefined;\n\n\tconst encode = async (): Promise<WriteBlobStepEncoded> => {\n\t\tif (!encodeState) {\n\t\t\tconst { blob } = options;\n\t\t\tconst hasUploadRelay = ctx.hasUploadRelay();\n\t\t\tconst metadata = hasUploadRelay\n\t\t\t\t? await client.computeBlobMetadata({\n\t\t\t\t\t\tbytes: blob,\n\t\t\t\t\t\tnonce: resumeNonce,\n\t\t\t\t\t})\n\t\t\t\t: await client.encodeBlob(blob);\n\n\t\t\tif (resumeBlobId && metadata.blobId !== resumeBlobId) {\n\t\t\t\tthrow new WalrusClientError(\n\t\t\t\t\t`Resume blobId mismatch: expected ${resumeBlobId}, got ${metadata.blobId}. The blob content may have changed.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tencodeState = {\n\t\t\t\tmetadata,\n\t\t\t\tsize: blob.length,\n\t\t\t\tdata: hasUploadRelay ? blob : undefined,\n\t\t\t};\n\t\t}\n\n\t\tconst { metadata, size } = encodeState!;\n\t\treturn {\n\t\t\tstep: 'encoded' as const,\n\t\t\tblobId: metadata.blobId,\n\t\t\trootHash: toBase64(metadata.rootHash),\n\t\t\tunencodedSize: size,\n\t\t\t...('nonce' in metadata ? { nonce: toBase64(metadata.nonce) } : {}),\n\t\t};\n\t};\n\n\tconst register = ({ epochs, deletable, owner, attributes }: WriteBlobFlowRegisterOptions) => {\n\t\tif (!encodeState) {\n\t\t\tthrow new Error('encode must be executed before calling register');\n\t\t}\n\n\t\tconst { metadata, size } = encodeState;\n\t\tconst transaction = new Transaction();\n\t\ttransaction.setSenderIfNotSet(owner);\n\n\t\tif (ctx.hasUploadRelay()) {\n\t\t\tconst meta = metadata as BlobMetadataResult;\n\t\t\ttransaction.add(\n\t\t\t\tclient.sendUploadRelayTip({\n\t\t\t\t\tsize,\n\t\t\t\t\tblobDigest: meta.blobDigest,\n\t\t\t\t\tnonce: meta.nonce,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\ttransaction.transferObjects(\n\t\t\t[\n\t\t\t\tclient.registerBlob({\n\t\t\t\t\tsize,\n\t\t\t\t\tepochs,\n\t\t\t\t\tblobId: metadata.blobId,\n\t\t\t\t\trootHash: metadata.rootHash,\n\t\t\t\t\tdeletable,\n\t\t\t\t\tattributes,\n\t\t\t\t}),\n\t\t\t],\n\t\t\towner,\n\t\t);\n\n\t\tregisterState = { ...encodeState, deletable };\n\n\t\treturn transaction;\n\t};\n\n\tconst upload = async (options?: WriteBlobFlowUploadOptions): Promise<WriteBlobStepUploaded> => {\n\t\tif (!encodeState) {\n\t\t\tthrow new Error('encode must be executed before calling upload');\n\t\t}\n\n\t\tconst { metadata, data } = encodeState;\n\t\tconst deletable = options?.deletable ?? registerState?.deletable ?? false;\n\n\t\tlet blobObject: (typeof Blob)['$inferType'];\n\t\tif (resumeBlobObjectId) {\n\t\t\tblobObject = await client.getBlobObject(resumeBlobObjectId);\n\t\t} else if (options?.digest) {\n\t\t\tblobObject = await ctx.getCreatedBlob(options.digest);\n\t\t} else {\n\t\t\tthrow new Error('Either resume.blobObjectId or upload digest must be provided');\n\t\t}\n\n\t\tlet certificate: ProtocolMessageCertificate;\n\t\tif (ctx.hasUploadRelay()) {\n\t\t\tconst meta = metadata as BlobMetadataResult;\n\t\t\tcertificate = (\n\t\t\t\tawait client.writeBlobToUploadRelay({\n\t\t\t\t\tblobId: metadata.blobId,\n\t\t\t\t\tblob: data!,\n\t\t\t\t\tnonce: meta.nonce,\n\t\t\t\t\ttxDigest: options?.digest ?? resumeTxDigest!,\n\t\t\t\t\tblobObjectId: blobObject.id,\n\t\t\t\t\tdeletable,\n\t\t\t\t\tencodingType: meta.metadata.encodingType as EncodingType,\n\t\t\t\t\tsignal: options?.signal,\n\t\t\t\t})\n\t\t\t).certificate;\n\t\t} else {\n\t\t\tconst meta = metadata as EncodeBlobResult;\n\t\t\tconst confirmations = await client.writeEncodedBlobToNodes({\n\t\t\t\tblobId: metadata.blobId,\n\t\t\t\tobjectId: blobObject.id,\n\t\t\t\tmetadata: meta.metadata,\n\t\t\t\tsliversByNode: meta.sliversByNode,\n\t\t\t\tdeletable,\n\t\t\t\tsignal: options?.signal,\n\t\t\t});\n\n\t\t\tcertificate = await client.certificateFromConfirmations({\n\t\t\t\tconfirmations,\n\t\t\t\tblobId: metadata.blobId,\n\t\t\t\tblobObjectId: blobObject.id,\n\t\t\t\tdeletable,\n\t\t\t});\n\t\t}\n\n\t\tuploadState = { blobObject, blobId: metadata.blobId, deletable, certificate };\n\n\t\tconst txDigest = options?.digest ?? resumeTxDigest;\n\t\treturn {\n\t\t\tstep: 'uploaded' as const,\n\t\t\tblobId: metadata.blobId,\n\t\t\tblobObjectId: blobObject.id,\n\t\t\t...(txDigest ? { txDigest } : {}),\n\t\t\tcertificate: CertificateBcs.serialize(certificate).toBase64(),\n\t\t};\n\t};\n\n\tconst certify = (): Transaction => {\n\t\tif (!uploadState) {\n\t\t\tthrow new Error('upload must be executed before calling certify');\n\t\t}\n\n\t\tconst { blobObject, blobId, deletable, certificate } = uploadState;\n\t\tconst transaction = client.certifyBlobTransaction({\n\t\t\tcertificate,\n\t\t\tblobId,\n\t\t\tblobObjectId: blobObject.id,\n\t\t\tdeletable,\n\t\t});\n\n\t\tcertifyState = { blobObject, blobId, transaction };\n\t\treturn transaction;\n\t};\n\n\tconst executeRegister = async ({\n\t\tsigner,\n\t\t...options\n\t}: WriteBlobFlowRegisterOptions & { signer: Signer }): Promise<WriteBlobStepRegistered> => {\n\t\tconst transaction = register(options);\n\t\tconst { digest } = await ctx.executeTransaction(transaction, signer, 'register blob');\n\n\t\treturn {\n\t\t\tstep: 'registered' as const,\n\t\t\tblobId: encodeState!.metadata.blobId,\n\t\t\tblobObjectId: (await ctx.getCreatedBlob(digest)).id,\n\t\t\ttxDigest: digest,\n\t\t\t...('nonce' in encodeState!.metadata ? { nonce: toBase64(encodeState!.metadata.nonce) } : {}),\n\t\t};\n\t};\n\n\tconst executeCertify = async ({\n\t\tsigner,\n\t}: {\n\t\tsigner: Signer;\n\t}): Promise<WriteBlobStepCertified> => {\n\t\tconst transaction = certify();\n\t\tawait ctx.executeTransaction(transaction, signer, 'certify blob');\n\n\t\tconst blobObject = await ctx.loadBlobObject(uploadState!.blobObject.id);\n\t\treturn {\n\t\t\tstep: 'certified' as const,\n\t\t\tblobId: uploadState!.blobId,\n\t\t\tblobObjectId: blobObject.id,\n\t\t\tblobObject,\n\t\t};\n\t};\n\n\tconst getBlob = async (): Promise<WriteBlobStepCertified> => {\n\t\tif (!certifyState && !uploadState) {\n\t\t\tthrow new Error('upload or certify must be executed before calling getBlob');\n\t\t}\n\n\t\tconst state = certifyState ?? uploadState!;\n\t\tconst blobObject = await ctx.loadBlobObject(state.blobObject.id);\n\t\treturn {\n\t\t\tstep: 'certified' as const,\n\t\t\tblobId: state.blobId,\n\t\t\tblobObjectId: blobObject.id,\n\t\t\tblobObject,\n\t\t};\n\t};\n\n\tconst restoreUploadState = async (resumeData: WriteBlobStepUploaded, deletable: boolean) => {\n\t\tconst blobObject = await client.getBlobObject(resumeData.blobObjectId);\n\t\tuploadState = {\n\t\t\tblobObject,\n\t\t\tblobId: resumeData.blobId,\n\t\t\tdeletable,\n\t\t\tcertificate: parseCertificateFromBase64(resumeData.certificate),\n\t\t};\n\t};\n\n\t/** @yields {WriteBlobStep} */\n\tasync function* run(options: WriteBlobFlowRunOptions): AsyncGenerator<WriteBlobStep> {\n\t\tconst resumeStep = resume?.step;\n\t\tconst stepOrder = ['encoded', 'registered', 'uploaded', 'certified'] as const;\n\t\tconst resumeIndex = resumeStep ? stepOrder.indexOf(resumeStep) : -1;\n\n\t\t// Already certified — nothing to do\n\t\tif (resumeIndex >= stepOrder.indexOf('certified')) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (resumeIndex < stepOrder.indexOf('encoded')) {\n\t\t\tyield await encode();\n\t\t} else if (resumeIndex < stepOrder.indexOf('uploaded')) {\n\t\t\tawait encode(); // Re-encode to populate internal state for upload\n\t\t}\n\n\t\tlet registerDigest: string | undefined;\n\t\tif (!resumeBlobObjectId) {\n\t\t\tconst regResult = await executeRegister({\n\t\t\t\tsigner: options.signer,\n\t\t\t\tepochs: options.epochs,\n\t\t\t\tdeletable: options.deletable,\n\t\t\t\towner: options.owner ?? options.signer.toSuiAddress(),\n\t\t\t\tattributes: options.attributes,\n\t\t\t});\n\t\t\tregisterDigest = regResult.txDigest;\n\t\t\tyield regResult;\n\t\t} else {\n\t\t\tregisterState = {\n\t\t\t\t...encodeState!,\n\t\t\t\tdeletable: options.deletable,\n\t\t\t};\n\t\t}\n\n\t\tif (resumeIndex < stepOrder.indexOf('uploaded')) {\n\t\t\tconst uploadResult = await upload({\n\t\t\t\tdigest: registerDigest ?? resumeTxDigest,\n\t\t\t\tsignal: options.signal,\n\t\t\t});\n\t\t\tyield uploadResult;\n\t\t} else {\n\t\t\tawait restoreUploadState(resume as WriteBlobStepUploaded, options.deletable);\n\t\t}\n\n\t\tconst certResult = await executeCertify({ signer: options.signer });\n\t\tyield certResult;\n\t}\n\n\treturn {\n\t\tencode,\n\t\tregister,\n\t\tupload,\n\t\tcertify,\n\t\tgetBlob,\n\t\texecuteRegister,\n\t\texecuteCertify,\n\t\trun,\n\t};\n}\n"],"mappings":"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"}