From f426a31239f708775e5ab2829ccd0e206da43988 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Mon, 13 May 2024 11:11:56 +0100 Subject: [PATCH 1/4] feat: export function to get verification method --- .../methods/external-data-source-utilities.md | 209 ++++++++++++++++++ src/index.ts | 78 +++++-- src/lib/utils.ts | 11 +- 3 files changed, 273 insertions(+), 25 deletions(-) create mode 100644 docs/methods/external-data-source-utilities.md diff --git a/docs/methods/external-data-source-utilities.md b/docs/methods/external-data-source-utilities.md new file mode 100644 index 00000000..ef1c824b --- /dev/null +++ b/docs/methods/external-data-source-utilities.md @@ -0,0 +1,209 @@ +--- +sidebar_position: 1 +--- + +# External Data Source utilities (`VerifiableURI` and `JSONURI`) + +## encodeDataSourceWithHash + +```js +const myErc725 = new ERC725(); +myErc725.encodeDataSourceWithHash(verification, dataSource); +``` + +OR + +```js +ERC725.encodeDataSourceWithHash(verification, dataSource); +``` + +Encode a verifiableURI providing the hashing function of the json file (method), the hash of the json file (data) and the url where the json file is stored. + +#### Parameters + +| Name | Type | Description | +| :------------- | :---------------------------- | :----------------------------------------------------------------------------------------------------------------------- | +| `verification` | `undefined` or `Verification` | Verification is an object containing the hashing function of the json file (method) and the hash of the json file (data) | +| `dataSource` | `string` | The url where the json file is stored. | + +
+ Types details + +```js +interface Verification { + method: SUPPORTED_VERIFICATION_METHODS | string; + data: string; + source?: string; +} + +type SUPPORTED_VERIFICATION_METHODS = + | SUPPORTED_VERIFICATION_METHOD_STRINGS + | SUPPORTED_VERIFICATION_METHOD_HASHES; + +enum SUPPORTED_VERIFICATION_METHOD_STRINGS { + KECCAK256_UTF8 = 'keccak256(utf8)', + KECCAK256_BYTES = 'keccak256(bytes)', +} + +enum SUPPORTED_VERIFICATION_METHOD_HASHES { + HASH_KECCAK256_UTF8 = '0x6f357c6a', + HASH_KECCAK256_BYTES = '0x8019f9b1', +} +``` + +
+ +#### Returns + +| Name | Type | Description | +| :-------------- | :----- | :---------------- | +| `verifiableURI` | string | The verifiableURI | + +#### Examples + +
+ Encode a VerifiableURI providing the hashing function, the JSON hash and the uploaded URL + +```javascript title="Encode a VerifiableURI providing the hashing function, the JSON hash and the uploaded URL" +const verifiableURI = myErc725.encodeDataSourceWithHash( + { + method: 'keccak256(utf8)', + data: '0x820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361', + }, + 'ifps://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx', +); +/** +0x00006f357c6a0020820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178 +*/ +``` + +
+ +## decodeDataSourceWithHash + +```js +const myErc725 = new ERC725(); +myErc725.decodeDataSourceWithHash(verifiableURI); +``` + +```js +ERC725.decodeDataSourceWithHash(verifiableURI); +``` + +Decode a verifiableURI into the hash function of the json file, the hash of the json file and the url where the json file is stored. + +#### Parameters + +| Name | Type | Description | +| :-------------- | :------- | :---------------- | +| `verifiableURI` | `string` | The verifiableURI | + +#### Returns + +| Name | Type | Description | +| :--------------------- | :---------------- | :--------------------------------------------------------------------------------------------------------- | +| `decodedVerifiableURI` | `URLDataWithHash` | Object containing the hash function, the hash of the JSON file and the link where the json file is stored. | + +
+ Types details + +```js +interface URLDataWithHash { + verification: Verification; + url: string +} + +interface Verification { +method: SUPPORTED_VERIFICATION_METHODS | string; +data: string; +source?: string; +} + +type SUPPORTED_VERIFICATION_METHODS = +| SUPPORTED_VERIFICATION_METHOD_STRINGS +| SUPPORTED_VERIFICATION_METHOD_HASHES; + +enum SUPPORTED_VERIFICATION_METHOD_STRINGS { +KECCAK256_UTF8 = 'keccak256(utf8)', +KECCAK256_BYTES = 'keccak256(bytes)', +} + +enum SUPPORTED_VERIFICATION_METHOD_HASHES { +HASH_KECCAK256_UTF8 = '0x6f357c6a', +HASH_KECCAK256_BYTES = '0x8019f9b1', +} + +``` + +
+ +#### Examples + +
+ Decode a VerifiableURI + +```javascript title="Decode a VerifiableURI" +const decodedVerifiableURI = myErc725.decodeDataSourceWithHash( + '0x00006f357c6a0020820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178', +); +/** +verification: { + data: '820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361', + method: 'keccak256(utf8)', + } +url: 'ifps://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx' +*/ +``` + +
+ +## getVerificationMethod + +```js +const myErc725 = new ERC725(); +myErc725.getVerificationMethod(nameOrSig); +``` + +```js +ERC725.getVerificationMethod(nameOrSig); +``` + +```js +import { getVerificationMethod } from '@erc725/erc725.js'; +getVerificationMethod(nameOrSig); +``` + +Get the verification method definition, including the name, signature and related method. + +method: (data: string | object | Uint8Array | null) => string; +name: SUPPORTED_VERIFICATION_METHOD_STRINGS; +sig: SUPPORTED_VERIFICATION_METHODS; + +#### Parameters + +| Name | Type | Description | +| :---------- | :----- | :------------------------------------------ | +| `nameOrSig` | string | The 4 bytes hex of the verification method. | + +#### Returns + +| Name | Type | Description | +| :--- | :----- | :-------------------------------------------------------------------------------------- | +| | object | An object containing the name, signature and method related to the verification method. | + +### Example + +```javascript title="Example of the method" +getVerificationMethod('0x6f357c6a'); +/* +{ + method: [Function: keccak256Method], + name: 'keccak256(utf8)', + sig: '0x6f357c6a' +} +*/ +``` + +## hashData + +## isDataAuthentic diff --git a/src/index.ts b/src/index.ts index 28de3ffe..8b118d1f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,6 +17,7 @@ * @author Robert McLeod <@robertdavid010> * @author Fabian Vogelsteller * @author Hugo Masclet <@Hugoo> + * @author Jean Cavallera <@CJ42> * @date 2020 */ @@ -30,12 +31,17 @@ import { convertIPFSGatewayUrl, generateSchemasFromDynamicKeys, duplicateMultiTypeERC725SchemaEntry, + getVerificationMethod, } from './lib/utils'; import { getSchema } from './lib/schemaParser'; import { isValidSignature } from './lib/isValidSignature'; -import { DEFAULT_GAS_VALUE } from './constants/constants'; +import { + DEFAULT_GAS_VALUE, + SUPPORTED_VERIFICATION_METHODS, + SUPPORTED_VERIFICATION_METHOD_STRINGS, +} from './constants/constants'; import { encodeKeyName, isDynamicKeyName } from './lib/encodeKeyName'; // Types @@ -87,7 +93,7 @@ export { }; export { ERC725Config, KeyValuePair, ProviderTypes } from './types'; -export { encodeData, encodeArrayKey } from './lib/utils'; +export { encodeData, encodeArrayKey, getVerificationMethod } from './lib/utils'; export { decodeData } from './lib/decodeData'; export { encodeKeyName, isDynamicKeyName } from './lib/encodeKeyName'; export { decodeMappingKey } from './lib/decodeMappingKey'; @@ -684,27 +690,8 @@ export class ERC725 { return mapPermission(permission); } - encodeDataSourceWithHash( - verification: undefined | Verification, - dataSource: string, - ): string { - return encodeDataSourceWithHash(verification, dataSource); - } - - static encodeDataSourceWithHash( - verification: undefined | Verification, - dataSource: string, - ): string { - return encodeDataSourceWithHash(verification, dataSource); - } - - decodeDataSourceWithHash(value: string): URLDataWithHash { - return decodeDataSourceWithHash(value); - } - - static decodeDataSourceWithHash(value: string): URLDataWithHash { - return decodeDataSourceWithHash(value); - } + // Encoding methods + // ---------------- /** * @param type The valueType to encode the value as @@ -765,6 +752,51 @@ export class ERC725 { ): string | URLDataWithHash | number | boolean | null { return decodeValueContent(valueContent, value); } + + // External Data Source utilities (`VerifiableURI` and `JSONURI`) + // ---------------------------------------------------------------- + + encodeDataSourceWithHash( + verification: undefined | Verification, + dataSource: string, + ): string { + return encodeDataSourceWithHash(verification, dataSource); + } + + static encodeDataSourceWithHash( + verification: undefined | Verification, + dataSource: string, + ): string { + return encodeDataSourceWithHash(verification, dataSource); + } + + decodeDataSourceWithHash(value: string): URLDataWithHash { + return decodeDataSourceWithHash(value); + } + + static decodeDataSourceWithHash(value: string): URLDataWithHash { + return decodeDataSourceWithHash(value); + } + + static getVerificationMethod(nameOrSig: string): + | { + method: (data: string | object | Uint8Array | null) => string; + name: SUPPORTED_VERIFICATION_METHOD_STRINGS; + sig: SUPPORTED_VERIFICATION_METHODS; + } + | undefined { + return getVerificationMethod(nameOrSig); + } + + getVerificationMethod(nameOrSig: string): + | { + method: (data: string | object | Uint8Array | null) => string; + name: SUPPORTED_VERIFICATION_METHOD_STRINGS; + sig: SUPPORTED_VERIFICATION_METHODS; + } + | undefined { + return getVerificationMethod(nameOrSig); + } } export default ERC725; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index e8e49baf..4c4b6386 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -47,6 +47,7 @@ import { SUPPORTED_VERIFICATION_METHODS, SUPPORTED_VERIFICATION_METHODS_LIST, COMPACT_BYTES_ARRAY_STRING, + SUPPORTED_VERIFICATION_METHOD_STRINGS, } from '../constants/constants'; import { decodeValueContent, @@ -157,7 +158,7 @@ export function encodeKeyValue( /** * - * @param key The schema key of a schema with keyType = 'Array' + * @param key A data key either as a 32 bytes long value, or a data key name of keyType = 'Array' * @param index An integer representing the intended array index * @return The raw bytes key for the array element */ @@ -524,7 +525,13 @@ export function encodeData( ); } -export function getVerificationMethod(nameOrSig: string) { +export function getVerificationMethod(nameOrSig: string): + | { + method: (data: string | object | Uint8Array | null) => string; + name: SUPPORTED_VERIFICATION_METHOD_STRINGS; + sig: SUPPORTED_VERIFICATION_METHODS; + } + | undefined { const verificationMethod = Object.values(HASH_METHODS).find( ({ name, sig }) => name === nameOrSig || sig === nameOrSig, ); From 0c7ba987540e0684e803ca8d722aa4b0b5d04fde Mon Sep 17 00:00:00 2001 From: CJ42 Date: Mon, 13 May 2024 16:09:34 +0100 Subject: [PATCH 2/4] feat: export method `isDataAuthentic` as static, single method or member of class --- .../methods/external-data-source-utilities.md | 179 +++++++++++++++++- src/index.ts | 22 ++- src/lib/getDataFromExternalSources.test.ts | 130 +++++++++++++ src/lib/getDataFromExternalSources.ts | 2 + src/lib/utils.ts | 2 +- 5 files changed, 331 insertions(+), 4 deletions(-) diff --git a/docs/methods/external-data-source-utilities.md b/docs/methods/external-data-source-utilities.md index ef1c824b..503c1345 100644 --- a/docs/methods/external-data-source-utilities.md +++ b/docs/methods/external-data-source-utilities.md @@ -204,6 +204,181 @@ getVerificationMethod('0x6f357c6a'); */ ``` -## hashData - ## isDataAuthentic + +```js +const myErc725 = new ERC725(); +ERC725.isDataAuthentic(data, verificationOptions); +``` + +```js +ERC725.isDataAuthentic(data, verificationOptions); +``` + +```js +import { isDataAuthentic } from '@erc725/erc725.js'; + +isDataAuthentic(data, verificationOptions); +``` + +Hashes the `data` passed as parameter using the specified hashing functions (available under `method` in the `verificationOption` object) and compares the result with the provided hash. + +:::info +This method will console an error if the hash provided as `data` and the expected hash obtained using the verification method do not match. +::: + +#### Parameters + +| Name | Type | Description | +| :-------------------- | :----------------------- | :---------------------------------- | +| `data` | `string` or `Uint8Array` | The data to be hashed and verified. | +| `verificationOptions` | `Verification` | An object as defined below | + +
+ Types details + +```js + KECCAK256_UTF8 = , + KECCAK256_BYTES = , + HASH_KECCAK256_UTF8 = , + HASH_KECCAK256_BYTES = , + +export interface Verification { + data: string; + method: 'keccak256(utf8)' | 'keccak256(bytes)' | '0x6f357c6a' | '0x8019f9b1' | string; + source?: string; +} +``` + +
+ +#### Returns + +| Name | Type | Description | +| :--- | :-------- | :-------------------------------------------------------------------------------------------- | +| | `boolean` | `true` if the data is authentic according to the verification method used, `false` otherwise. | + +### Example + +
+ JSON data to verify from data.json + +```json +[ + { + "name": "LSP3Profile", + "key": "0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5", + "value": { + "LSP3Profile": { + "name": "test", + "description": "", + "tags": ["profile"], + "links": [], + "profileImage": [ + { + "width": 1024, + "height": 709, + "verification": { + "method": "keccak256(bytes)", + "data": "0x6a0a28680d65b69f5696859be7e0fcebbbcf0df47f1f767926de35402c7d525c" + }, + "url": "ipfs://QmVUYyft3j2JVrG4RzDe1Qx7K5gNtJGFhrExHQFeiRXz1C" + }, + { + "width": 640, + "height": 443, + "verification": { + "method": "keccak256(bytes)", + "data": "0x7cd399f2a2552aa5cd21b1584a98db3efa39c701c311c38a60c680343cfa6d82" + }, + "url": "ipfs://QmeU8FUZC9F1qMYmcWyBhfGqaf7g3kLzGb4xBpoCfyVLZW" + }, + { + "width": 320, + "height": 221, + "verification": { + "method": "keccak256(bytes)", + "data": "0x272d2e57ae1710ac7c5e3d1c9f9d24f48954ad43d0e821f8bd041a4734e309a5" + }, + "url": "ipfs://QmdViKPWYhZv7u86z7HBTgAkTAwEkNSRi1VkYEU8K5yUsH" + }, + { + "width": 180, + "height": 124, + "verification": { + "method": "keccak256(bytes)", + "data": "0x1a464ff7e0eff05da98ed309a25195d8666b6211a5dfa2214865c3fd50ead810" + }, + "url": "ipfs://QmXZUCW6MqCNfYJEFsi54Vkj6PRrUoiPjzTuA2mWtas3RJ" + } + ], + "backgroundImage": [ + { + "width": 1800, + "height": 1012, + "verification": { + "method": "keccak256(bytes)", + "data": "0x3f6be73b35d348fb8f0b87a47d8c8b6b9db8858ee044cb13734cdfe5d28031d8" + }, + "url": "ipfs://QmfLCPmL31f31RRB4R7yoTg3Hsk5PjrWyS3ZaaYyhRPT4n" + }, + { + "width": 1024, + "height": 576, + "verification": { + "method": "keccak256(bytes)", + "data": "0xcb57ed802bcd7dc4964395a609b3a0f557c5f46a602b28b058b9587bb77bb54f" + }, + "url": "ipfs://QmPoPEaoGNVYhiMTwBWp6XzLPRXyuLjZWnuMobdCbfqsU9" + }, + { + "width": 640, + "height": 360, + "verification": { + "method": "keccak256(bytes)", + "data": "0x57e8039288c3e1a7f891c839e03805984ab36524b710656f072492c1c8ebd967" + }, + "url": "ipfs://QmU3pDA4eDNPMeARsJXxKaZsMC5MgFLgzGQccnydbU9WLV" + }, + { + "width": 320, + "height": 180, + "verification": { + "method": "keccak256(bytes)", + "data": "0x2bebf9baac33d719bbd3b481b1af468701409ad7578f84be04e8f7563d5a1509" + }, + "url": "ipfs://QmcKtenPsRvrqZJQ1gLCdUFkex4i9DGp7RFvucb9nbkzsz" + }, + { + "width": 180, + "height": 101, + "verification": { + "method": "keccak256(bytes)", + "data": "0xe32154c03c892d7c41c91220b8757ec5b7847eb2dd91413f7238b0c25f55b475" + }, + "url": "ipfs://QmU7ueJ467E9HRahaqQmSPhvkTkMhCLXRxV45P4kmMk6vm" + } + ] + } + } + } +] +``` + +
+ +```typescript title="isDataAuthentic example" +import jsonData from './data.json'; + +isDataAuthentic(jsonData, { + data: '0xdb864ed42104cee179785036cb4ff1183ebc57e5532ae766ad8533fa48acfbb3', + method: 'keccak256(utf8)', +}); +// true + +isDataAuthentic(jsonData, { + data: '0xdeadbeef00000000000000000000000000000000000000000000000000000000', + method: 'keccak256(utf8)', +}); +// false +``` diff --git a/src/index.ts b/src/index.ts index 8b118d1f..eef1967f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,6 +32,7 @@ import { generateSchemasFromDynamicKeys, duplicateMultiTypeERC725SchemaEntry, getVerificationMethod, + isDataAuthentic, } from './lib/utils'; import { getSchema } from './lib/schemaParser'; @@ -93,7 +94,12 @@ export { }; export { ERC725Config, KeyValuePair, ProviderTypes } from './types'; -export { encodeData, encodeArrayKey, getVerificationMethod } from './lib/utils'; +export { + encodeData, + encodeArrayKey, + getVerificationMethod, + isDataAuthentic, +} from './lib/utils'; export { decodeData } from './lib/decodeData'; export { encodeKeyName, isDynamicKeyName } from './lib/encodeKeyName'; export { decodeMappingKey } from './lib/decodeMappingKey'; @@ -797,6 +803,20 @@ export class ERC725 { | undefined { return getVerificationMethod(nameOrSig); } + + static isDataAuthentic( + data: string | Uint8Array, + verificationOptions: Verification, + ): boolean { + return isDataAuthentic(data, verificationOptions); + } + + isDataAuthentic( + data: string | Uint8Array, + verificationOptions: Verification, + ): boolean { + return isDataAuthentic(data, verificationOptions); + } } export default ERC725; diff --git a/src/lib/getDataFromExternalSources.test.ts b/src/lib/getDataFromExternalSources.test.ts index c8cd7659..2c3992e9 100644 --- a/src/lib/getDataFromExternalSources.test.ts +++ b/src/lib/getDataFromExternalSources.test.ts @@ -52,4 +52,134 @@ describe('getDataFromExternalSources', () => { ); }).to.not.throw(); }); + + it("should return the right data when the schema's valueContent is VerifiableURI", async () => { + const schema: ERC725JSONSchema = { + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + keyType: 'Singleton', + valueType: 'bytes', + valueContent: 'VerifiableURI', + }; + + const dataFromChain: DecodeDataOutput[] = [ + { + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + value: { + verification: { + data: '0xdb864ed42104cee179785036cb4ff1183ebc57e5532ae766ad8533fa48acfbb3', + method: 'keccak256(utf8)', + }, + url: 'ipfs://QmdMGUxuQsm1U9Qs8oJSn5PfY4B1apGG75YBRxQPybtRVm', + }, + }, + ]; + + const result = await getDataFromExternalSources( + [schema], + dataFromChain, + 'https://api.universalprofile.cloud/ipfs/', + ); + + expect(result).to.deep.equal([ + { + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + value: { + LSP3Profile: { + name: 'test', + description: '', + tags: ['profile'], + links: [], + profileImage: [ + { + width: 1024, + height: 709, + verification: { + method: 'keccak256(bytes)', + data: '0x6a0a28680d65b69f5696859be7e0fcebbbcf0df47f1f767926de35402c7d525c', + }, + url: 'ipfs://QmVUYyft3j2JVrG4RzDe1Qx7K5gNtJGFhrExHQFeiRXz1C', + }, + { + width: 640, + height: 443, + verification: { + method: 'keccak256(bytes)', + data: '0x7cd399f2a2552aa5cd21b1584a98db3efa39c701c311c38a60c680343cfa6d82', + }, + url: 'ipfs://QmeU8FUZC9F1qMYmcWyBhfGqaf7g3kLzGb4xBpoCfyVLZW', + }, + { + width: 320, + height: 221, + verification: { + method: 'keccak256(bytes)', + data: '0x272d2e57ae1710ac7c5e3d1c9f9d24f48954ad43d0e821f8bd041a4734e309a5', + }, + url: 'ipfs://QmdViKPWYhZv7u86z7HBTgAkTAwEkNSRi1VkYEU8K5yUsH', + }, + { + width: 180, + height: 124, + verification: { + method: 'keccak256(bytes)', + data: '0x1a464ff7e0eff05da98ed309a25195d8666b6211a5dfa2214865c3fd50ead810', + }, + url: 'ipfs://QmXZUCW6MqCNfYJEFsi54Vkj6PRrUoiPjzTuA2mWtas3RJ', + }, + ], + backgroundImage: [ + { + width: 1800, + height: 1012, + verification: { + method: 'keccak256(bytes)', + data: '0x3f6be73b35d348fb8f0b87a47d8c8b6b9db8858ee044cb13734cdfe5d28031d8', + }, + url: 'ipfs://QmfLCPmL31f31RRB4R7yoTg3Hsk5PjrWyS3ZaaYyhRPT4n', + }, + { + width: 1024, + height: 576, + verification: { + method: 'keccak256(bytes)', + data: '0xcb57ed802bcd7dc4964395a609b3a0f557c5f46a602b28b058b9587bb77bb54f', + }, + url: 'ipfs://QmPoPEaoGNVYhiMTwBWp6XzLPRXyuLjZWnuMobdCbfqsU9', + }, + { + width: 640, + height: 360, + verification: { + method: 'keccak256(bytes)', + data: '0x57e8039288c3e1a7f891c839e03805984ab36524b710656f072492c1c8ebd967', + }, + url: 'ipfs://QmU3pDA4eDNPMeARsJXxKaZsMC5MgFLgzGQccnydbU9WLV', + }, + { + width: 320, + height: 180, + verification: { + method: 'keccak256(bytes)', + data: '0x2bebf9baac33d719bbd3b481b1af468701409ad7578f84be04e8f7563d5a1509', + }, + url: 'ipfs://QmcKtenPsRvrqZJQ1gLCdUFkex4i9DGp7RFvucb9nbkzsz', + }, + { + width: 180, + height: 101, + verification: { + method: 'keccak256(bytes)', + data: '0xe32154c03c892d7c41c91220b8757ec5b7847eb2dd91413f7238b0c25f55b475', + }, + url: 'ipfs://QmU7ueJ467E9HRahaqQmSPhvkTkMhCLXRxV45P4kmMk6vm', + }, + ], + }, + }, + }, + ]); + }); }); diff --git a/src/lib/getDataFromExternalSources.ts b/src/lib/getDataFromExternalSources.ts index 7c898417..f7bde427 100644 --- a/src/lib/getDataFromExternalSources.ts +++ b/src/lib/getDataFromExternalSources.ts @@ -129,12 +129,14 @@ export const getDataFromExternalSources = ( if (/^(\[.*\]|\{.*\})\s*$/s.test(key)) { const json = arrToBufArr(receivedData).toString(); const value = JSON.parse(json); + if (isDataAuthentic(value, urlDataWithHash.verification)) { return { ...dataEntry, value }; } if (isDataAuthentic(receivedData, urlDataWithHash.verification)) { return { ...dataEntry, value }; } + throw new Error('result did not correctly validate'); } } catch { diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 4c4b6386..bb1ad658 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -158,7 +158,7 @@ export function encodeKeyValue( /** * - * @param key A data key either as a 32 bytes long value, or a data key name of keyType = 'Array' + * @param key The schema key of a schema with keyType = 'Array' * @param index An integer representing the intended array index * @return The raw bytes key for the array element */ From 335608f2abb7e1a9f83b91cf5be636b63412b630 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Fri, 17 May 2024 09:10:11 +0200 Subject: [PATCH 3/4] docs: remove dedicated docs page for external data source utilities --- .../methods/external-data-source-utilities.md | 384 ------------------ 1 file changed, 384 deletions(-) delete mode 100644 docs/methods/external-data-source-utilities.md diff --git a/docs/methods/external-data-source-utilities.md b/docs/methods/external-data-source-utilities.md deleted file mode 100644 index 503c1345..00000000 --- a/docs/methods/external-data-source-utilities.md +++ /dev/null @@ -1,384 +0,0 @@ ---- -sidebar_position: 1 ---- - -# External Data Source utilities (`VerifiableURI` and `JSONURI`) - -## encodeDataSourceWithHash - -```js -const myErc725 = new ERC725(); -myErc725.encodeDataSourceWithHash(verification, dataSource); -``` - -OR - -```js -ERC725.encodeDataSourceWithHash(verification, dataSource); -``` - -Encode a verifiableURI providing the hashing function of the json file (method), the hash of the json file (data) and the url where the json file is stored. - -#### Parameters - -| Name | Type | Description | -| :------------- | :---------------------------- | :----------------------------------------------------------------------------------------------------------------------- | -| `verification` | `undefined` or `Verification` | Verification is an object containing the hashing function of the json file (method) and the hash of the json file (data) | -| `dataSource` | `string` | The url where the json file is stored. | - -
- Types details - -```js -interface Verification { - method: SUPPORTED_VERIFICATION_METHODS | string; - data: string; - source?: string; -} - -type SUPPORTED_VERIFICATION_METHODS = - | SUPPORTED_VERIFICATION_METHOD_STRINGS - | SUPPORTED_VERIFICATION_METHOD_HASHES; - -enum SUPPORTED_VERIFICATION_METHOD_STRINGS { - KECCAK256_UTF8 = 'keccak256(utf8)', - KECCAK256_BYTES = 'keccak256(bytes)', -} - -enum SUPPORTED_VERIFICATION_METHOD_HASHES { - HASH_KECCAK256_UTF8 = '0x6f357c6a', - HASH_KECCAK256_BYTES = '0x8019f9b1', -} -``` - -
- -#### Returns - -| Name | Type | Description | -| :-------------- | :----- | :---------------- | -| `verifiableURI` | string | The verifiableURI | - -#### Examples - -
- Encode a VerifiableURI providing the hashing function, the JSON hash and the uploaded URL - -```javascript title="Encode a VerifiableURI providing the hashing function, the JSON hash and the uploaded URL" -const verifiableURI = myErc725.encodeDataSourceWithHash( - { - method: 'keccak256(utf8)', - data: '0x820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361', - }, - 'ifps://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx', -); -/** -0x00006f357c6a0020820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178 -*/ -``` - -
- -## decodeDataSourceWithHash - -```js -const myErc725 = new ERC725(); -myErc725.decodeDataSourceWithHash(verifiableURI); -``` - -```js -ERC725.decodeDataSourceWithHash(verifiableURI); -``` - -Decode a verifiableURI into the hash function of the json file, the hash of the json file and the url where the json file is stored. - -#### Parameters - -| Name | Type | Description | -| :-------------- | :------- | :---------------- | -| `verifiableURI` | `string` | The verifiableURI | - -#### Returns - -| Name | Type | Description | -| :--------------------- | :---------------- | :--------------------------------------------------------------------------------------------------------- | -| `decodedVerifiableURI` | `URLDataWithHash` | Object containing the hash function, the hash of the JSON file and the link where the json file is stored. | - -
- Types details - -```js -interface URLDataWithHash { - verification: Verification; - url: string -} - -interface Verification { -method: SUPPORTED_VERIFICATION_METHODS | string; -data: string; -source?: string; -} - -type SUPPORTED_VERIFICATION_METHODS = -| SUPPORTED_VERIFICATION_METHOD_STRINGS -| SUPPORTED_VERIFICATION_METHOD_HASHES; - -enum SUPPORTED_VERIFICATION_METHOD_STRINGS { -KECCAK256_UTF8 = 'keccak256(utf8)', -KECCAK256_BYTES = 'keccak256(bytes)', -} - -enum SUPPORTED_VERIFICATION_METHOD_HASHES { -HASH_KECCAK256_UTF8 = '0x6f357c6a', -HASH_KECCAK256_BYTES = '0x8019f9b1', -} - -``` - -
- -#### Examples - -
- Decode a VerifiableURI - -```javascript title="Decode a VerifiableURI" -const decodedVerifiableURI = myErc725.decodeDataSourceWithHash( - '0x00006f357c6a0020820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361696670733a2f2f516d597231564a4c776572673670456f73636468564775676f3339706136727963455a4c6a7452504466573834554178', -); -/** -verification: { - data: '820464ddfac1bec070cc14a8daf04129871d458f2ca94368aae8391311af6361', - method: 'keccak256(utf8)', - } -url: 'ifps://QmYr1VJLwerg6pEoscdhVGugo39pa6rycEZLjtRPDfW84UAx' -*/ -``` - -
- -## getVerificationMethod - -```js -const myErc725 = new ERC725(); -myErc725.getVerificationMethod(nameOrSig); -``` - -```js -ERC725.getVerificationMethod(nameOrSig); -``` - -```js -import { getVerificationMethod } from '@erc725/erc725.js'; -getVerificationMethod(nameOrSig); -``` - -Get the verification method definition, including the name, signature and related method. - -method: (data: string | object | Uint8Array | null) => string; -name: SUPPORTED_VERIFICATION_METHOD_STRINGS; -sig: SUPPORTED_VERIFICATION_METHODS; - -#### Parameters - -| Name | Type | Description | -| :---------- | :----- | :------------------------------------------ | -| `nameOrSig` | string | The 4 bytes hex of the verification method. | - -#### Returns - -| Name | Type | Description | -| :--- | :----- | :-------------------------------------------------------------------------------------- | -| | object | An object containing the name, signature and method related to the verification method. | - -### Example - -```javascript title="Example of the method" -getVerificationMethod('0x6f357c6a'); -/* -{ - method: [Function: keccak256Method], - name: 'keccak256(utf8)', - sig: '0x6f357c6a' -} -*/ -``` - -## isDataAuthentic - -```js -const myErc725 = new ERC725(); -ERC725.isDataAuthentic(data, verificationOptions); -``` - -```js -ERC725.isDataAuthentic(data, verificationOptions); -``` - -```js -import { isDataAuthentic } from '@erc725/erc725.js'; - -isDataAuthentic(data, verificationOptions); -``` - -Hashes the `data` passed as parameter using the specified hashing functions (available under `method` in the `verificationOption` object) and compares the result with the provided hash. - -:::info -This method will console an error if the hash provided as `data` and the expected hash obtained using the verification method do not match. -::: - -#### Parameters - -| Name | Type | Description | -| :-------------------- | :----------------------- | :---------------------------------- | -| `data` | `string` or `Uint8Array` | The data to be hashed and verified. | -| `verificationOptions` | `Verification` | An object as defined below | - -
- Types details - -```js - KECCAK256_UTF8 = , - KECCAK256_BYTES = , - HASH_KECCAK256_UTF8 = , - HASH_KECCAK256_BYTES = , - -export interface Verification { - data: string; - method: 'keccak256(utf8)' | 'keccak256(bytes)' | '0x6f357c6a' | '0x8019f9b1' | string; - source?: string; -} -``` - -
- -#### Returns - -| Name | Type | Description | -| :--- | :-------- | :-------------------------------------------------------------------------------------------- | -| | `boolean` | `true` if the data is authentic according to the verification method used, `false` otherwise. | - -### Example - -
- JSON data to verify from data.json - -```json -[ - { - "name": "LSP3Profile", - "key": "0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5", - "value": { - "LSP3Profile": { - "name": "test", - "description": "", - "tags": ["profile"], - "links": [], - "profileImage": [ - { - "width": 1024, - "height": 709, - "verification": { - "method": "keccak256(bytes)", - "data": "0x6a0a28680d65b69f5696859be7e0fcebbbcf0df47f1f767926de35402c7d525c" - }, - "url": "ipfs://QmVUYyft3j2JVrG4RzDe1Qx7K5gNtJGFhrExHQFeiRXz1C" - }, - { - "width": 640, - "height": 443, - "verification": { - "method": "keccak256(bytes)", - "data": "0x7cd399f2a2552aa5cd21b1584a98db3efa39c701c311c38a60c680343cfa6d82" - }, - "url": "ipfs://QmeU8FUZC9F1qMYmcWyBhfGqaf7g3kLzGb4xBpoCfyVLZW" - }, - { - "width": 320, - "height": 221, - "verification": { - "method": "keccak256(bytes)", - "data": "0x272d2e57ae1710ac7c5e3d1c9f9d24f48954ad43d0e821f8bd041a4734e309a5" - }, - "url": "ipfs://QmdViKPWYhZv7u86z7HBTgAkTAwEkNSRi1VkYEU8K5yUsH" - }, - { - "width": 180, - "height": 124, - "verification": { - "method": "keccak256(bytes)", - "data": "0x1a464ff7e0eff05da98ed309a25195d8666b6211a5dfa2214865c3fd50ead810" - }, - "url": "ipfs://QmXZUCW6MqCNfYJEFsi54Vkj6PRrUoiPjzTuA2mWtas3RJ" - } - ], - "backgroundImage": [ - { - "width": 1800, - "height": 1012, - "verification": { - "method": "keccak256(bytes)", - "data": "0x3f6be73b35d348fb8f0b87a47d8c8b6b9db8858ee044cb13734cdfe5d28031d8" - }, - "url": "ipfs://QmfLCPmL31f31RRB4R7yoTg3Hsk5PjrWyS3ZaaYyhRPT4n" - }, - { - "width": 1024, - "height": 576, - "verification": { - "method": "keccak256(bytes)", - "data": "0xcb57ed802bcd7dc4964395a609b3a0f557c5f46a602b28b058b9587bb77bb54f" - }, - "url": "ipfs://QmPoPEaoGNVYhiMTwBWp6XzLPRXyuLjZWnuMobdCbfqsU9" - }, - { - "width": 640, - "height": 360, - "verification": { - "method": "keccak256(bytes)", - "data": "0x57e8039288c3e1a7f891c839e03805984ab36524b710656f072492c1c8ebd967" - }, - "url": "ipfs://QmU3pDA4eDNPMeARsJXxKaZsMC5MgFLgzGQccnydbU9WLV" - }, - { - "width": 320, - "height": 180, - "verification": { - "method": "keccak256(bytes)", - "data": "0x2bebf9baac33d719bbd3b481b1af468701409ad7578f84be04e8f7563d5a1509" - }, - "url": "ipfs://QmcKtenPsRvrqZJQ1gLCdUFkex4i9DGp7RFvucb9nbkzsz" - }, - { - "width": 180, - "height": 101, - "verification": { - "method": "keccak256(bytes)", - "data": "0xe32154c03c892d7c41c91220b8757ec5b7847eb2dd91413f7238b0c25f55b475" - }, - "url": "ipfs://QmU7ueJ467E9HRahaqQmSPhvkTkMhCLXRxV45P4kmMk6vm" - } - ] - } - } - } -] -``` - -
- -```typescript title="isDataAuthentic example" -import jsonData from './data.json'; - -isDataAuthentic(jsonData, { - data: '0xdb864ed42104cee179785036cb4ff1183ebc57e5532ae766ad8533fa48acfbb3', - method: 'keccak256(utf8)', -}); -// true - -isDataAuthentic(jsonData, { - data: '0xdeadbeef00000000000000000000000000000000000000000000000000000000', - method: 'keccak256(utf8)', -}); -// false -``` From f16e78d0bfddf50414a35c111c7d30814c8d7728 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Wed, 29 May 2024 15:12:32 +0200 Subject: [PATCH 4/4] test: add mock for external data fetching test --- src/lib/getDataFromExternalSources.test.ts | 129 ++++----------------- 1 file changed, 24 insertions(+), 105 deletions(-) diff --git a/src/lib/getDataFromExternalSources.test.ts b/src/lib/getDataFromExternalSources.test.ts index 2c3992e9..b2d710e2 100644 --- a/src/lib/getDataFromExternalSources.test.ts +++ b/src/lib/getDataFromExternalSources.test.ts @@ -15,11 +15,13 @@ /* eslint-disable no-unused-expressions */ import { expect } from 'chai'; +import * as sinon from 'sinon'; import { ERC725JSONSchema } from '../types/ERC725JSONSchema'; import { getDataFromExternalSources } from './getDataFromExternalSources'; import { DecodeDataOutput } from '../types/decodeData'; +import { keccak256 } from 'web3-utils'; const IPFS_GATEWAY_MOCK = 'https://mock-ipfs.mock/ipfs/'; @@ -62,13 +64,15 @@ describe('getDataFromExternalSources', () => { valueContent: 'VerifiableURI', }; + const jsonResult = { LSP3Profile: { name: 'Test', description: 'Cool' } }; + const dataFromChain: DecodeDataOutput[] = [ { name: 'LSP3Profile', key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', value: { verification: { - data: '0xdb864ed42104cee179785036cb4ff1183ebc57e5532ae766ad8533fa48acfbb3', + data: keccak256(JSON.stringify(jsonResult)), method: 'keccak256(utf8)', }, url: 'ipfs://QmdMGUxuQsm1U9Qs8oJSn5PfY4B1apGG75YBRxQPybtRVm', @@ -76,110 +80,25 @@ describe('getDataFromExternalSources', () => { }, ]; - const result = await getDataFromExternalSources( - [schema], - dataFromChain, - 'https://api.universalprofile.cloud/ipfs/', - ); + const fetchStub = sinon.stub(global, 'fetch'); + try { + fetchStub.onCall(0).returns( + Promise.resolve( + new Response(JSON.stringify(jsonResult), { + headers: { 'content-type': 'application/json' }, + }), + ), + ); - expect(result).to.deep.equal([ - { - name: 'LSP3Profile', - key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', - value: { - LSP3Profile: { - name: 'test', - description: '', - tags: ['profile'], - links: [], - profileImage: [ - { - width: 1024, - height: 709, - verification: { - method: 'keccak256(bytes)', - data: '0x6a0a28680d65b69f5696859be7e0fcebbbcf0df47f1f767926de35402c7d525c', - }, - url: 'ipfs://QmVUYyft3j2JVrG4RzDe1Qx7K5gNtJGFhrExHQFeiRXz1C', - }, - { - width: 640, - height: 443, - verification: { - method: 'keccak256(bytes)', - data: '0x7cd399f2a2552aa5cd21b1584a98db3efa39c701c311c38a60c680343cfa6d82', - }, - url: 'ipfs://QmeU8FUZC9F1qMYmcWyBhfGqaf7g3kLzGb4xBpoCfyVLZW', - }, - { - width: 320, - height: 221, - verification: { - method: 'keccak256(bytes)', - data: '0x272d2e57ae1710ac7c5e3d1c9f9d24f48954ad43d0e821f8bd041a4734e309a5', - }, - url: 'ipfs://QmdViKPWYhZv7u86z7HBTgAkTAwEkNSRi1VkYEU8K5yUsH', - }, - { - width: 180, - height: 124, - verification: { - method: 'keccak256(bytes)', - data: '0x1a464ff7e0eff05da98ed309a25195d8666b6211a5dfa2214865c3fd50ead810', - }, - url: 'ipfs://QmXZUCW6MqCNfYJEFsi54Vkj6PRrUoiPjzTuA2mWtas3RJ', - }, - ], - backgroundImage: [ - { - width: 1800, - height: 1012, - verification: { - method: 'keccak256(bytes)', - data: '0x3f6be73b35d348fb8f0b87a47d8c8b6b9db8858ee044cb13734cdfe5d28031d8', - }, - url: 'ipfs://QmfLCPmL31f31RRB4R7yoTg3Hsk5PjrWyS3ZaaYyhRPT4n', - }, - { - width: 1024, - height: 576, - verification: { - method: 'keccak256(bytes)', - data: '0xcb57ed802bcd7dc4964395a609b3a0f557c5f46a602b28b058b9587bb77bb54f', - }, - url: 'ipfs://QmPoPEaoGNVYhiMTwBWp6XzLPRXyuLjZWnuMobdCbfqsU9', - }, - { - width: 640, - height: 360, - verification: { - method: 'keccak256(bytes)', - data: '0x57e8039288c3e1a7f891c839e03805984ab36524b710656f072492c1c8ebd967', - }, - url: 'ipfs://QmU3pDA4eDNPMeARsJXxKaZsMC5MgFLgzGQccnydbU9WLV', - }, - { - width: 320, - height: 180, - verification: { - method: 'keccak256(bytes)', - data: '0x2bebf9baac33d719bbd3b481b1af468701409ad7578f84be04e8f7563d5a1509', - }, - url: 'ipfs://QmcKtenPsRvrqZJQ1gLCdUFkex4i9DGp7RFvucb9nbkzsz', - }, - { - width: 180, - height: 101, - verification: { - method: 'keccak256(bytes)', - data: '0xe32154c03c892d7c41c91220b8757ec5b7847eb2dd91413f7238b0c25f55b475', - }, - url: 'ipfs://QmU7ueJ467E9HRahaqQmSPhvkTkMhCLXRxV45P4kmMk6vm', - }, - ], - }, - }, - }, - ]); + const [{ value: result }] = await getDataFromExternalSources( + [schema], + dataFromChain, + IPFS_GATEWAY_MOCK, + ); + + expect(result).to.deep.equal(jsonResult); + } finally { + fetchStub.restore(); + } }); });