From 4a75ec6248bae5a11391319bfdaca7f434fde385 Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Mon, 2 Sep 2024 21:16:51 -0400 Subject: [PATCH 1/5] Fixing bad bumping for AppData. --- .../js/test/externalPlugins/appData.test.ts | 426 ++++++++++++++++++ .../mpl-core/src/plugins/plugin_registry.rs | 20 +- programs/mpl-core/src/plugins/utils.rs | 36 +- programs/mpl-core/src/processor/update.rs | 16 +- 4 files changed, 448 insertions(+), 50 deletions(-) diff --git a/clients/js/test/externalPlugins/appData.test.ts b/clients/js/test/externalPlugins/appData.test.ts index 2d59c4e5..3c0c2cdf 100644 --- a/clients/js/test/externalPlugins/appData.test.ts +++ b/clients/js/test/externalPlugins/appData.test.ts @@ -18,6 +18,8 @@ import { create, createCollection, updateCollectionPlugin, + removePlugin, + removeExternalPluginAdapterV1, } from '../../src'; const DATA_AUTHORITIES: PluginAuthorityType[] = [ @@ -832,3 +834,427 @@ test('it cannot update app data on collection using update authority when differ ], }); }); + +test('Data offsets are correctly bumped when moving other plugins', async (t) => { + const umi = await createUmi(); + const asset = await createAsset(umi, { + plugins: [ + { type: 'FreezeDelegate', frozen: false }, + { + type: 'AppData', + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + }, + ], + }); + + await writeData(umi, { + asset: asset.publicKey, + key: { + type: 'AppData', + dataAuthority: { type: 'UpdateAuthority' }, + }, + data: new Uint8Array([1, 2, 3, 4]), + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + freezeDelegate: { + frozen: false, + authority: { type: 'Owner' }, + offset: 119n, + }, + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([1, 2, 3, 4]), + offset: 121n, + }, + ], + }); + + await removePlugin(umi, { + asset: asset.publicKey, + plugin: { type: 'FreezeDelegate' }, + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + freezeDelegate: undefined, + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([1, 2, 3, 4]), + offset: 119n, + }, + ], + }); +}); + +test('Data offsets are correctly bumped when moving other external plugins', async (t) => { + const umi = await createUmi(); + const asset = await createAsset(umi, { + plugins: [ + { + type: 'AppData', + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + }, + { + type: 'AppData', + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + }, + ], + }); + + await writeData(umi, { + asset: asset.publicKey, + key: { + type: 'AppData', + dataAuthority: { type: 'UpdateAuthority' }, + }, + data: new Uint8Array([1, 2, 3, 4]), + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + offset: 119n, + }, + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([1, 2, 3, 4]), + offset: 122n, + }, + ], + }); + + await removeExternalPluginAdapterV1(umi, { + asset: asset.publicKey, + key: { + __kind: 'AppData', + fields: [{ __kind: 'Owner' }], + }, + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + freezeDelegate: undefined, + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([1, 2, 3, 4]), + offset: 119n, + }, + ], + }); +}); + +test('Data offsets are correctly bumped when removing other external plugins with data', async (t) => { + const umi = await createUmi(); + const asset = await createAsset(umi, { + plugins: [ + { + type: 'AppData', + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + }, + { + type: 'AppData', + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + }, + ], + }); + + await writeData(umi, { + asset: asset.publicKey, + key: { + type: 'AppData', + dataAuthority: { type: 'Owner' }, + }, + data: new Uint8Array([1, 2, 3, 4]), + }).sendAndConfirm(umi); + + await writeData(umi, { + asset: asset.publicKey, + key: { + type: 'AppData', + dataAuthority: { type: 'UpdateAuthority' }, + }, + data: new Uint8Array([5, 6, 7, 8]), + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([1, 2, 3, 4]), + offset: 119n, + }, + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([5, 6, 7, 8]), + offset: 126n, + }, + ], + }); + + await removeExternalPluginAdapterV1(umi, { + asset: asset.publicKey, + key: { + __kind: 'AppData', + fields: [{ __kind: 'Owner' }], + }, + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + freezeDelegate: undefined, + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([5, 6, 7, 8]), + offset: 119n, + }, + ], + }); +}); + +test('Data offsets are correctly bumped when rewriting other external plugins to be smaller', async (t) => { + const umi = await createUmi(); + const asset = await createAsset(umi, { + plugins: [ + { + type: 'AppData', + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + }, + { + type: 'AppData', + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + }, + ], + }); + + await writeData(umi, { + asset: asset.publicKey, + key: { + type: 'AppData', + dataAuthority: { type: 'Owner' }, + }, + data: new Uint8Array([1, 2, 3, 4]), + }).sendAndConfirm(umi); + + await writeData(umi, { + asset: asset.publicKey, + key: { + type: 'AppData', + dataAuthority: { type: 'UpdateAuthority' }, + }, + data: new Uint8Array([5, 6, 7, 8]), + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([1, 2, 3, 4]), + offset: 119n, + }, + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([5, 6, 7, 8]), + offset: 126n, + }, + ], + }); + + await writeData(umi, { + asset: asset.publicKey, + key: { + type: 'AppData', + dataAuthority: { type: 'Owner' }, + }, + data: new Uint8Array([1, 2]), + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + freezeDelegate: undefined, + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([1, 2]), + offset: 119n, + }, + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([5, 6, 7, 8]), + offset: 124n, + }, + ], + }); +}); + +test('Data offsets are correctly bumped when rewriting other external plugins to be larger', async (t) => { + const umi = await createUmi(); + const asset = await createAsset(umi, { + plugins: [ + { + type: 'AppData', + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + }, + { + type: 'AppData', + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + }, + ], + }); + + await writeData(umi, { + asset: asset.publicKey, + key: { + type: 'AppData', + dataAuthority: { type: 'Owner' }, + }, + data: new Uint8Array([1, 2, 3, 4]), + }).sendAndConfirm(umi); + + await writeData(umi, { + asset: asset.publicKey, + key: { + type: 'AppData', + dataAuthority: { type: 'UpdateAuthority' }, + }, + data: new Uint8Array([5, 6, 7, 8]), + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([1, 2, 3, 4]), + offset: 119n, + }, + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([5, 6, 7, 8]), + offset: 126n, + }, + ], + }); + + await writeData(umi, { + asset: asset.publicKey, + key: { + type: 'AppData', + dataAuthority: { type: 'Owner' }, + }, + data: new Uint8Array([1, 2, 3, 4, 1, 2]), + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + freezeDelegate: undefined, + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([1, 2, 3, 4, 1, 2]), + offset: 119n, + }, + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([5, 6, 7, 8]), + offset: 128n, + }, + ], + }); +}); diff --git a/programs/mpl-core/src/plugins/plugin_registry.rs b/programs/mpl-core/src/plugins/plugin_registry.rs index f45b4813..046f1813 100644 --- a/programs/mpl-core/src/plugins/plugin_registry.rs +++ b/programs/mpl-core/src/plugins/plugin_registry.rs @@ -87,20 +87,22 @@ impl PluginRegistryV1 { for record in &mut self.external_registry { if record.offset > offset { + solana_program::msg!("Bumping Record: {:?}", record); record.offset = (record.offset as isize) .checked_add(size_diff) .ok_or(MplCoreError::NumericalOverflow)? as usize; - } - if let Some(data_offset) = record.data_offset { - if data_offset > offset { - record.data_offset = Some( - (data_offset as isize) - .checked_add(size_diff) - .ok_or(MplCoreError::NumericalOverflow)? - as usize, - ); + if let Some(data_offset) = record.data_offset { + if data_offset > offset { + solana_program::msg!("Bumping Data: {:?}", record); + record.data_offset = Some( + (data_offset as isize) + .checked_add(size_diff) + .ok_or(MplCoreError::NumericalOverflow)? + as usize, + ); + } } } } diff --git a/programs/mpl-core/src/plugins/utils.rs b/programs/mpl-core/src/plugins/utils.rs index 5dd60a9a..66d311d8 100644 --- a/programs/mpl-core/src/plugins/utils.rs +++ b/programs/mpl-core/src/plugins/utils.rs @@ -490,7 +490,7 @@ pub fn update_external_plugin_adapter_data<'a, T: DataBlob + SolanaAccount>( .ok_or(MplCoreError::NumericalOverflow)?; // Update any offsets that will change. - plugin_registry.bump_offsets(data_offset, size_diff)?; + plugin_registry.bump_offsets(record.offset, size_diff)?; let new_registry_offset = (plugin_header.plugin_registry_offset as isize) .checked_add(size_diff) @@ -633,17 +633,7 @@ pub fn delete_plugin<'a, T: DataBlob>( header.save(account, asset.get_size())?; // Move offsets for existing registry records. - for record in &mut plugin_registry.registry { - if plugin_offset < record.offset { - record.offset -= serialized_plugin.len() - } - } - - for record in &mut plugin_registry.external_registry { - if plugin_offset < record.offset { - record.offset -= serialized_plugin.len() - } - } + plugin_registry.bump_offsets(plugin_offset, -(serialized_plugin.len() as isize))?; plugin_registry.save(account, new_registry_offset)?; @@ -681,10 +671,14 @@ pub fn delete_external_plugin_adapter<'a, T: DataBlob>( let plugin_offset = registry_record.offset; let plugin = ExternalPluginAdapter::load(account, plugin_offset)?; let serialized_plugin = plugin.try_to_vec()?; + let serialized_plugin_len = serialized_plugin + .len() + .checked_add(registry_record.data_len.unwrap_or(0)) + .ok_or(MplCoreError::NumericalOverflow)?; // Get the offset of the plugin after the one being removed. let next_plugin_offset = plugin_offset - .checked_add(serialized_plugin.len()) + .checked_add(serialized_plugin_len) .ok_or(MplCoreError::NumericalOverflow)?; // Calculate the new size of the account. @@ -692,12 +686,12 @@ pub fn delete_external_plugin_adapter<'a, T: DataBlob>( .data_len() .checked_sub(serialized_registry_record.len()) .ok_or(MplCoreError::NumericalOverflow)? - .checked_sub(serialized_plugin.len()) + .checked_sub(serialized_plugin_len) .ok_or(MplCoreError::NumericalOverflow)?; let new_registry_offset = header .plugin_registry_offset - .checked_sub(serialized_plugin.len()) + .checked_sub(serialized_plugin_len) .ok_or(MplCoreError::NumericalOverflow)?; let data_to_move = header @@ -717,17 +711,7 @@ pub fn delete_external_plugin_adapter<'a, T: DataBlob>( header.save(account, asset.get_size())?; // Move offsets for existing registry records. - for record in &mut plugin_registry.external_registry { - if plugin_offset < record.offset { - record.offset -= serialized_plugin.len() - } - } - - for record in &mut plugin_registry.registry { - if plugin_offset < record.offset { - record.offset -= serialized_plugin.len() - } - } + plugin_registry.bump_offsets(plugin_offset, -(serialized_plugin_len as isize))?; plugin_registry.save(account, new_registry_offset)?; diff --git a/programs/mpl-core/src/processor/update.rs b/programs/mpl-core/src/processor/update.rs index e51f3d38..8ef0abd2 100644 --- a/programs/mpl-core/src/processor/update.rs +++ b/programs/mpl-core/src/processor/update.rs @@ -368,21 +368,7 @@ fn process_update<'a, T: DataBlob + SolanaAccount>( plugin_header.save(account, new_core_size as usize)?; // Move offsets for existing registry records. - for record in &mut plugin_registry.external_registry { - let new_offset = (record.offset as isize) - .checked_add(size_diff) - .ok_or(MplCoreError::NumericalOverflow)?; - - record.offset = new_offset as usize; - } - - for record in &mut plugin_registry.registry { - let new_offset = (record.offset as isize) - .checked_add(size_diff) - .ok_or(MplCoreError::NumericalOverflow)?; - - record.offset = new_offset as usize; - } + plugin_registry.bump_offsets(new_core_size as usize, size_diff)?; plugin_registry.save(account, new_registry_offset as usize)?; } else { From beb2302b7c016a2e81d113aee87da40111dc3397 Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Mon, 2 Sep 2024 22:37:13 -0400 Subject: [PATCH 2/5] Adding data section tests. --- .../externalPlugins/linkedAppData.test.ts | 346 ++++++++++++++++++ 1 file changed, 346 insertions(+) diff --git a/clients/js/test/externalPlugins/linkedAppData.test.ts b/clients/js/test/externalPlugins/linkedAppData.test.ts index 6caa942a..f7f344fa 100644 --- a/clients/js/test/externalPlugins/linkedAppData.test.ts +++ b/clients/js/test/externalPlugins/linkedAppData.test.ts @@ -20,6 +20,8 @@ import { createCollection, updateCollectionPlugin, addPlugin, + addExternalPluginAdapterV1, + removeExternalPluginAdapterV1, } from '../../src'; const DATA_AUTHORITIES: PluginAuthorityType[] = [ @@ -924,3 +926,347 @@ test('it cannot update linked app data on collection using update authority when ], }); }); + +test('Data offsets are correctly bumped when removing Data Section with data', async (t) => { + const umi = await createUmi(); + const { asset, collection } = await createAssetWithCollection( + umi, + {}, + { + plugins: [ + { + type: 'LinkedAppData', + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + }, + ], + } + ); + + await writeData(umi, { + asset: asset.publicKey, + collection: collection.publicKey, + key: { + type: 'LinkedAppData', + dataAuthority: { type: 'Owner' }, + }, + data: new Uint8Array([1, 2, 3, 4]), + }).sendAndConfirm(umi); + + await addExternalPluginAdapterV1(umi, { + asset: asset.publicKey, + collection: collection.publicKey, + initInfo: { + __kind: 'AppData', + fields: [ + { + dataAuthority: { __kind: 'UpdateAuthority' }, + initPluginAuthority: null, + schema: ExternalPluginAdapterSchema.Binary, + }, + ], + }, + }).sendAndConfirm(umi); + + await writeData(umi, { + asset: asset.publicKey, + collection: collection.publicKey, + key: { + type: 'AppData', + dataAuthority: { type: 'UpdateAuthority' }, + }, + data: new Uint8Array([5, 6, 7, 8]), + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Collection', address: collection.publicKey }, + dataSections: [ + { + type: 'DataSection', + parentKey: { type: 'LinkedAppData', dataAuthority: { type: 'Owner' } }, + authority: { type: 'None' }, + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([1, 2, 3, 4]), + offset: 119n, + }, + ], + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([5, 6, 7, 8]), + offset: 127n, + }, + ], + }); + + await removeExternalPluginAdapterV1(umi, { + asset: asset.publicKey, + collection: collection.publicKey, + key: { + __kind: 'DataSection', + fields: [{ __kind: 'LinkedAppData', fields: [{ __kind: 'Owner' }] }], + }, + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Collection', address: collection.publicKey }, + freezeDelegate: undefined, + dataSections: undefined, + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([5, 6, 7, 8]), + offset: 119n, + }, + ], + }); +}); + +test('Data offsets are correctly bumped when rewriting Data Section to be smaller', async (t) => { + const umi = await createUmi(); + const { asset, collection } = await createAssetWithCollection( + umi, + {}, + { + plugins: [ + { + type: 'LinkedAppData', + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + }, + ], + } + ); + + await writeData(umi, { + asset: asset.publicKey, + collection: collection.publicKey, + key: { + type: 'LinkedAppData', + dataAuthority: { type: 'Owner' }, + }, + data: new Uint8Array([1, 2, 3, 4]), + }).sendAndConfirm(umi); + + await addExternalPluginAdapterV1(umi, { + asset: asset.publicKey, + collection: collection.publicKey, + initInfo: { + __kind: 'AppData', + fields: [ + { + dataAuthority: { __kind: 'UpdateAuthority' }, + initPluginAuthority: null, + schema: ExternalPluginAdapterSchema.Binary, + }, + ], + }, + }).sendAndConfirm(umi); + + await writeData(umi, { + asset: asset.publicKey, + collection: collection.publicKey, + key: { + type: 'AppData', + dataAuthority: { type: 'UpdateAuthority' }, + }, + data: new Uint8Array([5, 6, 7, 8]), + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Collection', address: collection.publicKey }, + dataSections: [ + { + type: 'DataSection', + parentKey: { type: 'LinkedAppData', dataAuthority: { type: 'Owner' } }, + authority: { type: 'None' }, + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([1, 2, 3, 4]), + offset: 119n, + }, + ], + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([5, 6, 7, 8]), + offset: 127n, + }, + ], + }); + + await writeData(umi, { + asset: asset.publicKey, + collection: collection.publicKey, + key: { + type: 'LinkedAppData', + dataAuthority: { type: 'Owner' }, + }, + data: new Uint8Array([1, 2]), + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Collection', address: collection.publicKey }, + dataSections: [ + { + type: 'DataSection', + parentKey: { type: 'LinkedAppData', dataAuthority: { type: 'Owner' } }, + authority: { type: 'None' }, + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([1, 2]), + offset: 119n, + }, + ], + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([5, 6, 7, 8]), + offset: 125n, + }, + ], + }); +}); + +test('Data offsets are correctly bumped when rewriting other Data Section to be larger', async (t) => { + const umi = await createUmi(); + const { asset, collection } = await createAssetWithCollection( + umi, + {}, + { + plugins: [ + { + type: 'LinkedAppData', + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + }, + ], + } + ); + + await writeData(umi, { + asset: asset.publicKey, + collection: collection.publicKey, + key: { + type: 'LinkedAppData', + dataAuthority: { type: 'Owner' }, + }, + data: new Uint8Array([1, 2, 3, 4]), + }).sendAndConfirm(umi); + + await addExternalPluginAdapterV1(umi, { + asset: asset.publicKey, + collection: collection.publicKey, + initInfo: { + __kind: 'AppData', + fields: [ + { + dataAuthority: { __kind: 'UpdateAuthority' }, + initPluginAuthority: null, + schema: ExternalPluginAdapterSchema.Binary, + }, + ], + }, + }).sendAndConfirm(umi); + + await writeData(umi, { + asset: asset.publicKey, + collection: collection.publicKey, + key: { + type: 'AppData', + dataAuthority: { type: 'UpdateAuthority' }, + }, + data: new Uint8Array([5, 6, 7, 8]), + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Collection', address: collection.publicKey }, + dataSections: [ + { + type: 'DataSection', + parentKey: { type: 'LinkedAppData', dataAuthority: { type: 'Owner' } }, + authority: { type: 'None' }, + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([1, 2, 3, 4]), + offset: 119n, + }, + ], + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([5, 6, 7, 8]), + offset: 127n, + }, + ], + }); + + await writeData(umi, { + asset: asset.publicKey, + collection: collection.publicKey, + key: { + type: 'LinkedAppData', + dataAuthority: { type: 'Owner' }, + }, + data: new Uint8Array([1, 2, 3, 4, 1, 2]), + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Collection', address: collection.publicKey }, + dataSections: [ + { + type: 'DataSection', + parentKey: { type: 'LinkedAppData', dataAuthority: { type: 'Owner' } }, + authority: { type: 'None' }, + dataAuthority: { type: 'Owner' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([1, 2, 3, 4, 1, 2]), + offset: 119n, + }, + ], + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([5, 6, 7, 8]), + offset: 129n, + }, + ], + }); +}); From 876397054dfb548e142e1b560a2de6358327ad4f Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Tue, 3 Sep 2024 09:27:17 -0400 Subject: [PATCH 3/5] Fixing update. --- clients/js/test/externalPlugins/appData.test.ts | 2 +- programs/mpl-core/src/processor/update.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clients/js/test/externalPlugins/appData.test.ts b/clients/js/test/externalPlugins/appData.test.ts index 3c0c2cdf..07639115 100644 --- a/clients/js/test/externalPlugins/appData.test.ts +++ b/clients/js/test/externalPlugins/appData.test.ts @@ -835,7 +835,7 @@ test('it cannot update app data on collection using update authority when differ }); }); -test('Data offsets are correctly bumped when moving other plugins', async (t) => { +test('Data offsets are correctly bumped when removing other plugins', async (t) => { const umi = await createUmi(); const asset = await createAsset(umi, { plugins: [ diff --git a/programs/mpl-core/src/processor/update.rs b/programs/mpl-core/src/processor/update.rs index 8ef0abd2..23c6d9db 100644 --- a/programs/mpl-core/src/processor/update.rs +++ b/programs/mpl-core/src/processor/update.rs @@ -368,7 +368,7 @@ fn process_update<'a, T: DataBlob + SolanaAccount>( plugin_header.save(account, new_core_size as usize)?; // Move offsets for existing registry records. - plugin_registry.bump_offsets(new_core_size as usize, size_diff)?; + plugin_registry.bump_offsets(core_size as usize, size_diff)?; plugin_registry.save(account, new_registry_offset as usize)?; } else { From cccf18aabdbfffd498da83e4b28e91ad2654a8ee Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Tue, 3 Sep 2024 10:08:09 -0400 Subject: [PATCH 4/5] Adding additional write to test. --- .../js/test/externalPlugins/appData.test.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/clients/js/test/externalPlugins/appData.test.ts b/clients/js/test/externalPlugins/appData.test.ts index 07639115..7bde853f 100644 --- a/clients/js/test/externalPlugins/appData.test.ts +++ b/clients/js/test/externalPlugins/appData.test.ts @@ -901,6 +901,33 @@ test('Data offsets are correctly bumped when removing other plugins', async (t) }, ], }); + + await writeData(umi, { + asset: asset.publicKey, + key: { + type: 'AppData', + dataAuthority: { type: 'UpdateAuthority' }, + }, + data: new Uint8Array([5, 6, 7, 8]), + }).sendAndConfirm(umi); + + await assertAsset(t, umi, { + ...DEFAULT_ASSET, + asset: asset.publicKey, + owner: umi.identity.publicKey, + updateAuthority: { type: 'Address', address: umi.identity.publicKey }, + freezeDelegate: undefined, + appDatas: [ + { + type: 'AppData', + authority: { type: 'UpdateAuthority' }, + dataAuthority: { type: 'UpdateAuthority' }, + schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([5, 6, 7, 8]), + offset: 119n, + }, + ], + }); }); test('Data offsets are correctly bumped when moving other external plugins', async (t) => { From 4aa786103284f8bccb1cd9940e2c3fefcbc8b54e Mon Sep 17 00:00:00 2001 From: Blockiosaurus Date: Tue, 3 Sep 2024 11:48:21 -0400 Subject: [PATCH 5/5] Updating SDK and tests to return data length and offset. --- .../js/src/plugins/externalPluginAdapters.ts | 27 +++++++++++--- .../js/test/externalPlugins/appData.test.ts | 35 +++++++++++++++++++ .../externalPlugins/linkedAppData.test.ts | 22 ++++++++++++ 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/clients/js/src/plugins/externalPluginAdapters.ts b/clients/js/src/plugins/externalPluginAdapters.ts index b276055d..11d957b0 100644 --- a/clients/js/src/plugins/externalPluginAdapters.ts +++ b/clients/js/src/plugins/externalPluginAdapters.ts @@ -1,4 +1,9 @@ -import { AccountMeta, Context, PublicKey } from '@metaplex-foundation/umi'; +import { + AccountMeta, + Context, + isSome, + PublicKey, +} from '@metaplex-foundation/umi'; import { lifecycleHookFromBase, LifecycleHookInitInfoArgs, @@ -59,7 +64,9 @@ import { export type ExternalPluginAdapterTypeString = BaseExternalPluginAdapterKey['__kind']; -export type BaseExternalPluginAdapter = BasePlugin & LifecycleChecksContainer; +export type BaseExternalPluginAdapter = BasePlugin & + ExternalPluginAdapterData & + LifecycleChecksContainer; export type ExternalPluginAdapters = | LifecycleHookPlugin @@ -125,8 +132,8 @@ export const externalPluginAdapterManifests = { }; export type ExternalPluginAdapterData = { - dataLen: bigint; - dataOffset: bigint; + dataLen?: bigint; + dataOffset?: bigint; }; export function externalRegistryRecordsToExternalPluginAdapterList( @@ -156,6 +163,10 @@ export function externalRegistryRecordsToExternalPluginAdapterList( } result.lifecycleHooks.push({ type: 'LifecycleHook', + dataOffset: isSome(record.dataOffset) + ? record.dataOffset.value + : undefined, + dataLen: isSome(record.dataLen) ? record.dataLen.value : undefined, ...mappedPlugin, ...lifecycleHookFromBase( deserializedPlugin.fields[0], @@ -169,6 +180,10 @@ export function externalRegistryRecordsToExternalPluginAdapterList( } result.appDatas.push({ type: 'AppData', + dataOffset: isSome(record.dataOffset) + ? record.dataOffset.value + : undefined, + dataLen: isSome(record.dataLen) ? record.dataLen.value : undefined, ...mappedPlugin, ...appDataFromBase(deserializedPlugin.fields[0], record, accountData), }); @@ -214,6 +229,10 @@ export function externalRegistryRecordsToExternalPluginAdapterList( } result.dataSections.push({ type: 'DataSection', + dataOffset: isSome(record.dataOffset) + ? record.dataOffset.value + : undefined, + dataLen: isSome(record.dataLen) ? record.dataLen.value : undefined, ...mappedPlugin, ...dataSectionFromBase( deserializedPlugin.fields[0], diff --git a/clients/js/test/externalPlugins/appData.test.ts b/clients/js/test/externalPlugins/appData.test.ts index 7bde853f..f4713b84 100644 --- a/clients/js/test/externalPlugins/appData.test.ts +++ b/clients/js/test/externalPlugins/appData.test.ts @@ -875,6 +875,8 @@ test('Data offsets are correctly bumped when removing other plugins', async (t) schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([1, 2, 3, 4]), offset: 121n, + dataOffset: 124n, + dataLen: 4n, }, ], }); @@ -898,6 +900,8 @@ test('Data offsets are correctly bumped when removing other plugins', async (t) schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([1, 2, 3, 4]), offset: 119n, + dataOffset: 122n, + dataLen: 4n, }, ], }); @@ -925,6 +929,8 @@ test('Data offsets are correctly bumped when removing other plugins', async (t) schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([5, 6, 7, 8]), offset: 119n, + dataOffset: 122n, + dataLen: 4n, }, ], }); @@ -967,7 +973,10 @@ test('Data offsets are correctly bumped when moving other external plugins', asy authority: { type: 'UpdateAuthority' }, dataAuthority: { type: 'Owner' }, schema: ExternalPluginAdapterSchema.Binary, + data: new Uint8Array([]), offset: 119n, + dataOffset: 122n, + dataLen: 0n, }, { type: 'AppData', @@ -976,6 +985,8 @@ test('Data offsets are correctly bumped when moving other external plugins', asy schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([1, 2, 3, 4]), offset: 122n, + dataOffset: 125n, + dataLen: 4n, }, ], }); @@ -1002,6 +1013,8 @@ test('Data offsets are correctly bumped when moving other external plugins', asy schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([1, 2, 3, 4]), offset: 119n, + dataOffset: 122n, + dataLen: 4n, }, ], }); @@ -1055,6 +1068,8 @@ test('Data offsets are correctly bumped when removing other external plugins wit schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([1, 2, 3, 4]), offset: 119n, + dataOffset: 122n, + dataLen: 4n, }, { type: 'AppData', @@ -1063,6 +1078,8 @@ test('Data offsets are correctly bumped when removing other external plugins wit schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([5, 6, 7, 8]), offset: 126n, + dataOffset: 129n, + dataLen: 4n, }, ], }); @@ -1089,6 +1106,8 @@ test('Data offsets are correctly bumped when removing other external plugins wit schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([5, 6, 7, 8]), offset: 119n, + dataOffset: 122n, + dataLen: 4n, }, ], }); @@ -1142,6 +1161,8 @@ test('Data offsets are correctly bumped when rewriting other external plugins to schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([1, 2, 3, 4]), offset: 119n, + dataOffset: 122n, + dataLen: 4n, }, { type: 'AppData', @@ -1150,6 +1171,8 @@ test('Data offsets are correctly bumped when rewriting other external plugins to schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([5, 6, 7, 8]), offset: 126n, + dataOffset: 129n, + dataLen: 4n, }, ], }); @@ -1177,6 +1200,8 @@ test('Data offsets are correctly bumped when rewriting other external plugins to schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([1, 2]), offset: 119n, + dataOffset: 122n, + dataLen: 2n, }, { type: 'AppData', @@ -1185,6 +1210,8 @@ test('Data offsets are correctly bumped when rewriting other external plugins to schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([5, 6, 7, 8]), offset: 124n, + dataOffset: 127n, + dataLen: 4n, }, ], }); @@ -1238,6 +1265,8 @@ test('Data offsets are correctly bumped when rewriting other external plugins to schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([1, 2, 3, 4]), offset: 119n, + dataOffset: 122n, + dataLen: 4n, }, { type: 'AppData', @@ -1246,6 +1275,8 @@ test('Data offsets are correctly bumped when rewriting other external plugins to schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([5, 6, 7, 8]), offset: 126n, + dataOffset: 129n, + dataLen: 4n, }, ], }); @@ -1273,6 +1304,8 @@ test('Data offsets are correctly bumped when rewriting other external plugins to schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([1, 2, 3, 4, 1, 2]), offset: 119n, + dataOffset: 122n, + dataLen: 6n, }, { type: 'AppData', @@ -1281,6 +1314,8 @@ test('Data offsets are correctly bumped when rewriting other external plugins to schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([5, 6, 7, 8]), offset: 128n, + dataOffset: 131n, + dataLen: 4n, }, ], }); diff --git a/clients/js/test/externalPlugins/linkedAppData.test.ts b/clients/js/test/externalPlugins/linkedAppData.test.ts index f7f344fa..d324a379 100644 --- a/clients/js/test/externalPlugins/linkedAppData.test.ts +++ b/clients/js/test/externalPlugins/linkedAppData.test.ts @@ -992,6 +992,8 @@ test('Data offsets are correctly bumped when removing Data Section with data', a schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([1, 2, 3, 4]), offset: 119n, + dataOffset: 123n, + dataLen: 4n, }, ], appDatas: [ @@ -1002,6 +1004,8 @@ test('Data offsets are correctly bumped when removing Data Section with data', a schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([5, 6, 7, 8]), offset: 127n, + dataOffset: 130n, + dataLen: 4n, }, ], }); @@ -1030,6 +1034,8 @@ test('Data offsets are correctly bumped when removing Data Section with data', a schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([5, 6, 7, 8]), offset: 119n, + dataOffset: 122n, + dataLen: 4n, }, ], }); @@ -1100,6 +1106,8 @@ test('Data offsets are correctly bumped when rewriting Data Section to be smalle schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([1, 2, 3, 4]), offset: 119n, + dataOffset: 123n, + dataLen: 4n, }, ], appDatas: [ @@ -1110,6 +1118,8 @@ test('Data offsets are correctly bumped when rewriting Data Section to be smalle schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([5, 6, 7, 8]), offset: 127n, + dataOffset: 130n, + dataLen: 4n, }, ], }); @@ -1138,6 +1148,8 @@ test('Data offsets are correctly bumped when rewriting Data Section to be smalle schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([1, 2]), offset: 119n, + dataOffset: 123n, + dataLen: 2n, }, ], appDatas: [ @@ -1148,6 +1160,8 @@ test('Data offsets are correctly bumped when rewriting Data Section to be smalle schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([5, 6, 7, 8]), offset: 125n, + dataOffset: 128n, + dataLen: 4n, }, ], }); @@ -1218,6 +1232,8 @@ test('Data offsets are correctly bumped when rewriting other Data Section to be schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([1, 2, 3, 4]), offset: 119n, + dataOffset: 123n, + dataLen: 4n, }, ], appDatas: [ @@ -1228,6 +1244,8 @@ test('Data offsets are correctly bumped when rewriting other Data Section to be schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([5, 6, 7, 8]), offset: 127n, + dataOffset: 130n, + dataLen: 4n, }, ], }); @@ -1256,6 +1274,8 @@ test('Data offsets are correctly bumped when rewriting other Data Section to be schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([1, 2, 3, 4, 1, 2]), offset: 119n, + dataOffset: 123n, + dataLen: 6n, }, ], appDatas: [ @@ -1266,6 +1286,8 @@ test('Data offsets are correctly bumped when rewriting other Data Section to be schema: ExternalPluginAdapterSchema.Binary, data: new Uint8Array([5, 6, 7, 8]), offset: 129n, + dataOffset: 132n, + dataLen: 4n, }, ], });