diff --git a/.changeset/calm-cougars-kiss.md b/.changeset/calm-cougars-kiss.md new file mode 100644 index 00000000..e69a68cd --- /dev/null +++ b/.changeset/calm-cougars-kiss.md @@ -0,0 +1,5 @@ +--- +'@bnb-chain/greenfield-js-sdk': minor +--- + +feat: Remove approval when creating object diff --git a/examples/nextjs/src/components/object/create/index.tsx b/examples/nextjs/src/components/object/create/index.tsx index ee990a1c..11aded1b 100644 --- a/examples/nextjs/src/components/object/create/index.tsx +++ b/examples/nextjs/src/components/object/create/index.tsx @@ -1,8 +1,14 @@ import { client } from '@/client'; import { getOffchainAuthKeys } from '@/utils/offchainAuth'; +import { + bytesFromBase64, + Long, + RedundancyType, + VisibilityType, +} from '@bnb-chain/greenfield-js-sdk'; +import { ReedSolomon } from '@bnb-chain/reed-solomon'; import { ChangeEvent, useState } from 'react'; import { useAccount } from 'wagmi'; -import { ReedSolomon } from '@bnb-chain/reed-solomon'; export const CreateObject = () => { const { address, connector } = useAccount(); @@ -52,13 +58,6 @@ export const CreateObject = () => { return; } - const provider = await connector?.getProvider(); - const offChainData = await getOffchainAuthKeys(address, provider); - if (!offChainData) { - alert('No offchain, please create offchain pairs first'); - return; - } - // const checksumWorker = getCheckSumsWorker(); // const multiCal = await checksumWorker.generateCheckSumV2(file); // console.log('multiCal', multiCal); @@ -67,28 +66,16 @@ export const CreateObject = () => { const fileBytes = await file.arrayBuffer(); const expectCheckSums = rs.encode(new Uint8Array(fileBytes)); - console.log('offChainData', offChainData); - - const createObjectTx = await client.object.createObject( - { - bucketName: createObjectInfo.bucketName, - objectName: createObjectInfo.objectName, - creator: address, - visibility: 'VISIBILITY_TYPE_PRIVATE', - fileType: file.type, - redundancyType: 'REDUNDANCY_EC_TYPE', - contentLength: fileBytes.byteLength, - expectCheckSums: expectCheckSums, - }, - { - type: 'EDDSA', - domain: window.location.origin, - seed: offChainData.seedString, - address, - // type: 'ECDSA', - // privateKey: ACCOUNT_PRIVATEKEY, - }, - ); + const createObjectTx = await client.object.createObject({ + bucketName: createObjectInfo.bucketName, + objectName: createObjectInfo.objectName, + creator: address, + visibility: VisibilityType.VISIBILITY_TYPE_PRIVATE, + contentType: file.type, + redundancyType: RedundancyType.REDUNDANCY_EC_TYPE, + payloadSize: Long.fromInt(fileBytes.byteLength), + expectChecksums: expectCheckSums.map((x) => bytesFromBase64(x)), + }); const simulateInfo = await createObjectTx.simulate({ denom: 'BNB', @@ -163,19 +150,13 @@ export const CreateObject = () => { return; } - const createFolderTx = await client.object.createFolder( - { - bucketName: createObjectInfo.bucketName, - objectName: createObjectInfo.objectName + '/', - creator: address, - }, - { - type: 'EDDSA', - domain: window.location.origin, - seed: offChainData.seedString, - address, - }, - ); + const createFolderTx = await client.object.createFolder({ + bucketName: createObjectInfo.bucketName, + objectName: createObjectInfo.objectName + '/', + creator: address, + redundancyType: RedundancyType.REDUNDANCY_EC_TYPE, + visibility: VisibilityType.VISIBILITY_TYPE_PRIVATE, + }); const simulateInfo = await createFolderTx.simulate({ denom: 'BNB', diff --git a/packages/js-sdk/src/api/objects.ts b/packages/js-sdk/src/api/objects.ts index 97442185..679cfbb0 100644 --- a/packages/js-sdk/src/api/objects.ts +++ b/packages/js-sdk/src/api/objects.ts @@ -5,10 +5,6 @@ import { PrincipalType, principalTypeFromJSON, } from '@bnb-chain/greenfield-cosmos-types/greenfield/permission/common'; -import { - redundancyTypeFromJSON, - visibilityTypeFromJSON, -} from '@bnb-chain/greenfield-cosmos-types/greenfield/storage/common'; import { QueryHeadObjectResponse, QueryNFTRequest, @@ -24,11 +20,10 @@ import { MsgPutPolicy, MsgUpdateObjectInfo, } from '@bnb-chain/greenfield-cosmos-types/greenfield/storage/tx'; -import { bytesFromBase64 } from '@bnb-chain/greenfield-cosmos-types/helpers'; +import { base64FromBytes, bytesFromBase64 } from '@bnb-chain/greenfield-cosmos-types/helpers'; import { hexlify } from '@ethersproject/bytes'; import { ed25519 } from '@noble/curves/ed25519'; import { Headers } from 'cross-fetch'; -import { bytesToUtf8, hexToBytes } from 'ethereum-cryptography/utils'; import { container, delay, inject, injectable } from 'tsyringe'; import { GRNToString, @@ -45,7 +40,6 @@ import { getSortQuery, HTTPHeaderRegPubKey, } from '../clients/spclient/auth'; -import { getApprovalMetaInfo } from '../clients/spclient/spApis/approval'; import { getGetObjectMetaInfo } from '../clients/spclient/spApis/getObject'; import { getObjectMetaInfo, @@ -71,8 +65,6 @@ import { MsgDeleteObjectSDKTypeEIP712 } from '../messages/greenfield/storage/Msg import { MsgUpdateObjectInfoSDKTypeEIP712 } from '../messages/greenfield/storage/MsgUpdateObjectInfo'; import { AuthType, - CreateObjectApprovalRequest, - CreateObjectApprovalResponse, GetListObjectPoliciesRequest, GetListObjectPoliciesResponse, GetPrivewObject, @@ -88,6 +80,7 @@ import { GetObjectMetaRequest, GetObjectMetaResponse } from '../types/sp/GetObje import { ListObjectsByBucketNameResponse } from '../types/sp/ListObjectsByBucketName'; import { PutObjectRequest } from '../types/sp/PutObject'; import { + checkObjectName, generateUrlByBucketName, verifyBucketName, verifyObjectName, @@ -97,15 +90,7 @@ import { Sp } from './sp'; import { Storage } from './storage'; export interface IObject { - getCreateObjectApproval( - configParam: CreateObjectApprovalRequest, - authType: AuthType, - ): Promise>; - - createObject( - getApprovalParams: CreateObjectApprovalRequest, - authType: AuthType, - ): Promise; + createObject(msg: MsgCreateObject): Promise; uploadObject(configParam: PutObjectRequest, authType: AuthType): Promise>; @@ -138,11 +123,7 @@ export interface IObject { ): Promise>; createFolder( - getApprovalParams: Omit< - CreateObjectApprovalRequest, - 'contentLength' | 'fileType' | 'expectCheckSums' - >, - authType: AuthType, + msg: Omit, ): Promise; putObjectPolicy( @@ -193,128 +174,38 @@ export class Objects implements IObject { private queryClient: RpcQueryClient = container.resolve(RpcQueryClient); private spClient = container.resolve(SpClient); - public async getCreateObjectApproval(params: CreateObjectApprovalRequest, authType: AuthType) { - const { - bucketName, - creator, - objectName, - visibility = 'VISIBILITY_TYPE_PUBLIC_READ', - duration = 3000, - fileType = 'application/octet-stream', - redundancyType = 'REDUNDANCY_EC_TYPE', - contentLength, - expectCheckSums, - } = params; - - try { - assertAuthType(authType); - verifyBucketName(bucketName); - verifyObjectName(objectName); - assertStringRequire(creator, 'empty creator address'); - - let endpoint = params.endpoint; - if (!endpoint) { - endpoint = await this.sp.getSPUrlByBucket(bucketName); - } - const { reqMeta, optionsWithOutHeaders, url } = - getApprovalMetaInfo(endpoint, 'CreateObject', { - bucket_name: bucketName, - content_type: fileType, - creator: creator, - expect_checksums: expectCheckSums, - object_name: objectName, - payload_size: contentLength.toString(), - primary_sp_approval: { - expired_height: '0', - global_virtual_group_family_id: 0, - sig: null, - }, - redundancy_type: redundancyType, - visibility, - }); - - const signHeaders = await this.spClient.signHeaders(reqMeta, authType); - - const result = await this.spClient.callApi( - url, - { - ...optionsWithOutHeaders, - headers: signHeaders, - }, - duration, - { - code: -1, - message: 'Get create object approval error.', - }, - ); - - const signedMsgString = result.headers.get('X-Gnfd-Signed-Msg') || ''; - const signedMsg = JSON.parse( - bytesToUtf8(hexToBytes(signedMsgString)), - ) as CreateObjectApprovalResponse; + public async createObject(msg: MsgCreateObject) { + verifyBucketName(msg.bucketName); + verifyObjectName(msg.objectName); + checkObjectName(msg.objectName); + assertStringRequire(msg.creator, 'empty creator address'); - return { - code: 0, - message: 'Get create object approval success.', - body: result.headers.get('X-Gnfd-Signed-Msg') ?? '', - statusCode: result.status, - signedMsg, - }; - } catch (error: any) { - throw { - code: -1, - message: error.message, - statusCode: error?.statusCode || NORMAL_ERROR_CODE, - }; - } - } + const createObjMsg: MsgCreateObject = { + ...msg, + primarySpApproval: { + globalVirtualGroupFamilyId: 0, + expiredHeight: Long.fromInt(0), + sig: Uint8Array.from([]), + }, + }; - private async createObjectTx(msg: MsgCreateObject, signedMsg: CreateObjectApprovalResponse) { return await this.txClient.tx( MsgCreateObjectTypeUrl, msg.creator, MsgCreateObjectSDKTypeEIP712, { - ...signedMsg, - type: MsgCreateObjectTypeUrl, + ...MsgCreateObject.toSDK(createObjMsg), primary_sp_approval: { - expired_height: signedMsg.primary_sp_approval.expired_height, - global_virtual_group_family_id: - signedMsg.primary_sp_approval.global_virtual_group_family_id, - sig: signedMsg.primary_sp_approval.sig, + expired_height: '0', + global_virtual_group_family_id: 0, }, + expect_checksums: createObjMsg.expectChecksums.map((e) => base64FromBytes(e)), + payload_size: createObjMsg.payloadSize.toNumber(), }, - MsgCreateObject.encode(msg).finish(), + MsgCreateObject.encode(createObjMsg).finish(), ); } - public async createObject(getApprovalParams: CreateObjectApprovalRequest, authType: AuthType) { - assertAuthType(authType); - - const { signedMsg } = await this.getCreateObjectApproval(getApprovalParams, authType); - if (!signedMsg) { - throw new Error('Get create object approval error'); - } - - const msg: MsgCreateObject = { - bucketName: signedMsg.bucket_name, - creator: signedMsg.creator, - objectName: signedMsg.object_name, - contentType: signedMsg.content_type, - payloadSize: Long.fromString(signedMsg.payload_size), - visibility: visibilityTypeFromJSON(signedMsg.visibility), - expectChecksums: signedMsg.expect_checksums.map((e: string) => bytesFromBase64(e)), - redundancyType: redundancyTypeFromJSON(signedMsg.redundancy_type), - primarySpApproval: { - expiredHeight: Long.fromString(signedMsg.primary_sp_approval.expired_height), - sig: bytesFromBase64(signedMsg.primary_sp_approval.sig || ''), - globalVirtualGroupFamilyId: signedMsg.primary_sp_approval.global_virtual_group_family_id, - }, - }; - - return await this.createObjectTx(msg, signedMsg); - } - public async uploadObject( params: PutObjectRequest, authType: AuthType, @@ -580,14 +471,9 @@ export class Objects implements IObject { } public async createFolder( - getApprovalParams: Omit< - CreateObjectApprovalRequest, - 'contentLength' | 'fileType' | 'expectCheckSums' - >, - authType: AuthType, + msg: Omit, ) { - assertAuthType(authType); - if (!getApprovalParams.objectName.endsWith('/')) { + if (!msg.objectName.endsWith('/')) { throw new Error( 'failed to create folder. Folder names must end with a forward slash (/) character', ); @@ -597,17 +483,16 @@ export class Objects implements IObject { * const file = new File([], 'scc', { type: 'text/plain' }); const fileBytes = await file.arrayBuffer(); console.log('fileBytes', fileBytes); - const hashResult = await FileHandler.getPieceHashRoots(new Uint8Array(fileBytes)); - console.log('hashResult', hashResult); - const { contentLength, expectCheckSums } = hashResult; + const rs = new ReedSolomon(); + const fileBytes = await file.arrayBuffer(); + const expectCheckSums = rs.encode(new Uint8Array(fileBytes)); */ - const params: CreateObjectApprovalRequest = { - bucketName: getApprovalParams.bucketName, - objectName: getApprovalParams.objectName, - contentLength: 0, - fileType: 'text/plain', - expectCheckSums: [ + const newMsg: MsgCreateObject = { + ...msg, + payloadSize: Long.fromInt(0), + contentType: 'text/plain', + expectChecksums: [ '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=', '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=', '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=', @@ -615,11 +500,10 @@ export class Objects implements IObject { '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=', '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=', '47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=', - ], - creator: getApprovalParams.creator, + ].map((x) => bytesFromBase64(x)), }; - return this.createObject(params, authType); + return this.createObject(newMsg); } public async putObjectPolicy( diff --git a/packages/js-sdk/src/messages/greenfield/storage/MsgCreateObject.ts b/packages/js-sdk/src/messages/greenfield/storage/MsgCreateObject.ts index b1b92a4e..22d95523 100644 --- a/packages/js-sdk/src/messages/greenfield/storage/MsgCreateObject.ts +++ b/packages/js-sdk/src/messages/greenfield/storage/MsgCreateObject.ts @@ -50,9 +50,9 @@ export const MsgCreateObjectSDKTypeEIP712 = { name: 'global_virtual_group_family_id', type: 'uint32', }, - { - name: 'sig', - type: 'bytes', - }, + // { + // name: 'sig', + // type: 'bytes', + // }, ], }; diff --git a/packages/js-sdk/src/types/sp/ObjectApproval.ts b/packages/js-sdk/src/types/sp/ObjectApproval.ts deleted file mode 100644 index da504c2c..00000000 --- a/packages/js-sdk/src/types/sp/ObjectApproval.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - RedundancyType, - VisibilityType, -} from '@bnb-chain/greenfield-cosmos-types/greenfield/storage/common'; - -export type CreateObjectApprovalRequest = { - bucketName: string; - objectName: string; - creator: string; - visibility?: keyof typeof VisibilityType; - fileType: string; - redundancyType?: keyof typeof RedundancyType; - duration?: number; - contentLength: number; - expectCheckSums: string[]; - endpoint?: string; -}; - -export type CreateObjectApprovalResponse = { - creator: string; - bucket_name: string; - object_name: string; - payload_size: string; - visibility: keyof typeof VisibilityType; - content_type: string; - primary_sp_approval: { - expired_height: string; - sig: string | null; - global_virtual_group_family_id: number; - }; - expect_checksums: string[]; - // expect_secondary_sp_addresses: string[]; - redundancy_type: keyof typeof RedundancyType; - // charged_read_quota: string; -}; diff --git a/packages/js-sdk/src/types/sp/index.ts b/packages/js-sdk/src/types/sp/index.ts index cd13cd0d..d5c701e2 100644 --- a/packages/js-sdk/src/types/sp/index.ts +++ b/packages/js-sdk/src/types/sp/index.ts @@ -15,7 +15,6 @@ export * from './ListObjectsByIDs'; export * from './ListUserGroups'; export * from './ListUserOwnedGroups'; export * from './MigrateBucket'; -export * from './ObjectApproval'; export * from './PutObject'; export * from './ReadQuota'; export * from './RequestNonce'; diff --git a/packages/js-sdk/src/utils/asserts/s3.ts b/packages/js-sdk/src/utils/asserts/s3.ts index 87a6a5d7..2dd86491 100644 --- a/packages/js-sdk/src/utils/asserts/s3.ts +++ b/packages/js-sdk/src/utils/asserts/s3.ts @@ -115,20 +115,20 @@ const generateUrlByBucketName = (endpoint = '', bucketName: string) => { }; const isSQLInjection = (input: string) => { - // Define patterns that may indicate SQL injection, especially those with a semicolon followed by common SQL keywords + // define patterns that may indicate SQL injection const patterns = [ - '(?i).*;.*select', // Matches any string with a semicolon followed by "select" - '(?i).*;.*insert', // Matches any string with a semicolon followed by "insert" - '(?i).*;.*update', // Matches any string with a semicolon followed by "update" - '(?i).*;.*delete', // Matches any string with a semicolon followed by "delete" - '(?i).*;.*drop', // Matches any string with a semicolon followed by "drop" - '(?i).*;.*alter', // Matches any string with a semicolon followed by "alter" - '/\\*.*\\*/', // Matches SQL block comment + /;.*select/, // Matches any string with a semicolon followed by "select" + /;.*insert/, // Matches any string with a semicolon followed by "insert" + /;.*update/, // Matches any string with a semicolon followed by "update" + /;.*delete/, // Matches any string with a semicolon followed by "delete" + /;.*drop/, // Matches any string with a semicolon followed by "drop" + /;.*alter/, // Matches any string with a semicolon followed by "alter" + /\/\*[\s\S]*?\*\//, // Matches SQL block comment ]; for (const pattern of patterns) { - const regex = new RegExp(pattern); - if (regex.test(input)) { + const match = pattern.test(input); + if (match) { return true; } }