Skip to content

Commit

Permalink
Merge pull request #149 from blockfrost/fix/cip14-import
Browse files Browse the repository at this point in the history
fix: cip14 import
  • Loading branch information
vladimirvolek committed Feb 19, 2024
2 parents 52f847a + 2859009 commit cc36e87
Show file tree
Hide file tree
Showing 11 changed files with 255 additions and 9 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
"@blockfrost/blockfrost-utils": "2.8.0",
"@blockfrost/openapi": "0.1.60",
"@emurgo/cardano-serialization-lib-nodejs": "11.5.0",
"@emurgo/cip14-js": "^3.0.1",
"@fastify/cors": "^8.3.0",
"@fastify/postgres": "^5.2.0",
"@sentry/node": "^7.69.0",
Expand All @@ -44,6 +43,7 @@
},
"devDependencies": {
"@blockfrost/blockfrost-tests": "1.11.0",
"@types/blake2b": "^2.1.3",
"@types/config": "3.3.1",
"@types/express": "^4.17.17",
"@types/node": "^20.6.2",
Expand All @@ -54,6 +54,7 @@
"@typescript-eslint/eslint-plugin": "^6.7.2",
"@typescript-eslint/parser": "6.7.2",
"@vitest/coverage-v8": "^1.2.2",
"blake2b": "^2.1.4",
"eslint": "8.49.0",
"eslint-config-prettier": "9.0.0",
"eslint-plugin-import": "^2.28.1",
Expand Down
2 changes: 1 addition & 1 deletion src/routes/assets/asset/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
getSchemaForEndpoint,
validateCIP68Metadata,
} from '@blockfrost/openapi';
import AssetFingerprint from '@emurgo/cip14-js';
import { AssetFingerprint } from '../../../utils/cip14.js';
import { FastifyInstance, FastifyRequest } from 'fastify';
import { SQLQuery } from '../../../sql/index.js';
import * as QueryTypes from '../../../types/queries/assets.js';
Expand Down
59 changes: 59 additions & 0 deletions src/utils/cip14.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copy pasted from https://github.com/Emurgo/cip14-js due to ESM issues with the lib
import blake2b from 'blake2b';
import { bech32 } from 'bech32';

/// note: this function can't be inverted due to the hash

const DATA = 'asset';

export class AssetFingerprint {
readonly hashBuf: Uint8Array;

private constructor(hashBuf: Uint8Array) {
this.hashBuf = hashBuf;
}

static fromHash(hash: Uint8Array): AssetFingerprint {
return new AssetFingerprint(hash);
}

static fromParts(policyId: Uint8Array, assetName: Uint8Array): AssetFingerprint {
// see https://github.com/cardano-foundation/CIPs/pull/64
const hashBuf = blake2b(20)
.update(new Uint8Array([...policyId, ...assetName]))
.digest('binary');

return AssetFingerprint.fromHash(hashBuf);
}

static fromBech32(fingerprint: string): AssetFingerprint {
const { prefix, words } = bech32.decode(fingerprint);

if (prefix !== DATA) {
throw new Error('Invalid asset fingerprint');
}

const hashBuf = Buffer.from(bech32.fromWords(words));

return AssetFingerprint.fromHash(hashBuf);
}

fingerprint(): string {
const words = bech32.toWords(this.hashBuf);

return bech32.encode(DATA, words);
}

hash(): string {
return Buffer.from(this.hashBuf).toString('hex');
}

prefix(): string {
return DATA;
}

// The last six characters of the data part form a checksum and contain no information
checksum(): string {
return this.fingerprint().slice(-6);
}
}
139 changes: 139 additions & 0 deletions test/unit/tests/utils/cip14.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { AssetFingerprint } from '../../../../src/utils/cip14.js';
import { describe, expect, test } from 'vitest';

function createFingerprint(policyId: string, assetName: string): string {
const fingerprint = AssetFingerprint.fromParts(
Buffer.from(policyId, 'hex'),
Buffer.from(assetName, 'hex'),
);

return fingerprint.fingerprint();
}

describe('cip14', () => {
test('Fingerprint is correctly generated', () => {
expect(
createFingerprint('7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373', ''),
).toEqual('asset1rjklcrnsdzqp65wjgrg55sy9723kw09mlgvlc3');

expect(
createFingerprint('7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc37e', ''),
).toEqual('asset1nl0puwxmhas8fawxp8nx4e2q3wekg969n2auw3');

expect(
createFingerprint('1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209', ''),
).toEqual('asset1uyuxku60yqe57nusqzjx38aan3f2wq6s93f6ea');

expect(
createFingerprint('7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373', '504154415445'),
).toEqual('asset13n25uv0yaf5kus35fm2k86cqy60z58d9xmde92');

expect(
createFingerprint('1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209', '504154415445'),
).toEqual('asset1hv4p5tv2a837mzqrst04d0dcptdjmluqvdx9k3');

expect(
createFingerprint(
'1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209',
'7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373',
),
).toEqual('asset1aqrdypg669jgazruv5ah07nuyqe0wxjhe2el6f');

expect(
createFingerprint(
'7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373',
'1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209',
),
).toEqual('asset17jd78wukhtrnmjh3fngzasxm8rck0l2r4hhyyt');

expect(
createFingerprint(
'7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373',
'0000000000000000000000000000000000000000000000000000000000000000',
),
).toEqual('asset1pkpwyknlvul7az0xx8czhl60pyel45rpje4z8w');
});

function roundtripFromHash(policyId: string, assetName: string): string {
const fingerprint = AssetFingerprint.fromParts(
Buffer.from(policyId, 'hex'),
Buffer.from(assetName, 'hex'),
);

const hash = Buffer.from(fingerprint.hash(), 'hex');

const reconstructed = AssetFingerprint.fromHash(hash);

return reconstructed.fingerprint();
}
test('Can generate fingerprint with hash', () => {
expect(
roundtripFromHash('7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373', ''),
).toEqual('asset1rjklcrnsdzqp65wjgrg55sy9723kw09mlgvlc3');

expect(
roundtripFromHash('7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc37e', ''),
).toEqual('asset1nl0puwxmhas8fawxp8nx4e2q3wekg969n2auw3');

expect(
roundtripFromHash('1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209', ''),
).toEqual('asset1uyuxku60yqe57nusqzjx38aan3f2wq6s93f6ea');

expect(
roundtripFromHash('7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373', '504154415445'),
).toEqual('asset13n25uv0yaf5kus35fm2k86cqy60z58d9xmde92');

expect(
roundtripFromHash('1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209', '504154415445'),
).toEqual('asset1hv4p5tv2a837mzqrst04d0dcptdjmluqvdx9k3');

expect(
roundtripFromHash(
'1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209',
'7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373',
),
).toEqual('asset1aqrdypg669jgazruv5ah07nuyqe0wxjhe2el6f');

expect(
roundtripFromHash(
'7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373',
'1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209',
),
).toEqual('asset17jd78wukhtrnmjh3fngzasxm8rck0l2r4hhyyt');

expect(
roundtripFromHash(
'7eae28af2208be856f7a119668ae52a49b73725e326dc16579dcc373',
'0000000000000000000000000000000000000000000000000000000000000000',
),
).toEqual('asset1pkpwyknlvul7az0xx8czhl60pyel45rpje4z8w');
});

test('can get hash from bech32', () => {
expect(
AssetFingerprint.fromBech32('asset1rjklcrnsdzqp65wjgrg55sy9723kw09mlgvlc3').hash(),
).toEqual('1cadfc0e7068801d51d240d14a4085f2a3673cbb');

expect(
AssetFingerprint.fromBech32('asset1nl0puwxmhas8fawxp8nx4e2q3wekg969n2auw3').hash(),
).toEqual('9fde1e38dbbf6074f5c609e66ae5408bb3641745');

expect(
AssetFingerprint.fromBech32('asset1uyuxku60yqe57nusqzjx38aan3f2wq6s93f6ea').hash(),
).toEqual('e1386b734f20334f4f9000a4689fbd9c52a70350');
});

test('can get checksum from bech32', () => {
expect(
AssetFingerprint.fromBech32('asset1rjklcrnsdzqp65wjgrg55sy9723kw09mlgvlc3').checksum(),
).toEqual('lgvlc3');

expect(
AssetFingerprint.fromBech32('asset1nl0puwxmhas8fawxp8nx4e2q3wekg969n2auw3').checksum(),
).toEqual('n2auw3');

expect(
AssetFingerprint.fromBech32('asset1uyuxku60yqe57nusqzjx38aan3f2wq6s93f6ea').checksum(),
).toEqual('93f6ea');
});
});
Loading

0 comments on commit cc36e87

Please sign in to comment.