UNPKG

@truenetworkio/sdk

Version:

True Network SDK is the abstracted interface for interacting with True Network nodes.

519 lines (518 loc) 21.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Schema = exports.stringToSchemaType = exports.Text = exports.Bool = exports.Hash = exports.F64 = exports.F32 = exports.I64 = exports.U64 = exports.I32 = exports.U32 = exports.I16 = exports.U16 = exports.I8 = exports.U8 = exports.Char = exports.SchemaType = void 0; const hashing_1 = require("../utils/hashing"); const state_1 = require("../pallets/credentials/state"); const extrinsic_1 = require("../pallets/credentials/extrinsic"); const __1 = require(".."); class SchemaType { hexToLittleEndianNumber(hex) { // Remove '0x' prefix if present hex = hex.replace('0x', ''); // Ensure the hex string has the correct length while (hex.length < this.sizeInBytes * 2) { hex = hex + '0'; } // Convert to little-endian const bytes = []; for (let i = 0; i < hex.length; i += 2) { bytes.push(parseInt(hex.substr(i, 2), 16)); } // Create a DataView to handle the conversion const buffer = new ArrayBuffer(this.sizeInBytes); const view = new DataView(buffer); // Set bytes in little-endian order bytes.forEach((byte, index) => { view.setUint8(index, byte); }); // Read the value based on size switch (this.sizeInBytes) { case 1: return view.getUint8(0); case 2: return view.getUint16(0, true); case 4: return view.getUint32(0, true); default: return view.getUint32(0, true); } } hexToLittleEndianBigInt(hex) { // Remove '0x' prefix if present hex = hex.replace('0x', ''); // Ensure the hex string has the correct length while (hex.length < this.sizeInBytes * 2) { hex = hex + '0'; } // Convert to little-endian const bytes = []; for (let i = 0; i < hex.length; i += 2) { bytes.push(parseInt(hex.substr(i, 2), 16)); } // Create a DataView to handle the conversion const buffer = new ArrayBuffer(this.sizeInBytes); const view = new DataView(buffer); // Set bytes in little-endian order bytes.forEach((byte, index) => { view.setUint8(index, byte); }); return view.getBigUint64(0, true); } } exports.SchemaType = SchemaType; class CharType extends SchemaType { constructor() { super(...arguments); this.sizeInBytes = 1; this.id = 0; } isValid(v) { return v.length === 1; } serialize(v) { return (0, hashing_1.toLittleEndianHex)(v.charCodeAt(0), this.sizeInBytes); } deserialize(v) { return String.fromCharCode(parseInt(v, 16)); } } class U8Type extends SchemaType { constructor() { super(...arguments); this.sizeInBytes = 1; this.id = 1; } isValid(v) { return Number.isInteger(v) && v >= 0 && v <= 255; } serialize(v) { return (0, hashing_1.toLittleEndianHex)(v, this.sizeInBytes); } deserialize(v) { return this.hexToLittleEndianNumber(v); } } class I8Type extends SchemaType { constructor() { super(...arguments); this.sizeInBytes = 1; this.id = 2; } isValid(v) { return Number.isInteger(v) && v >= -128 && v <= 127; } serialize(v) { return (0, hashing_1.toLittleEndianHex)(v & 0xFF, this.sizeInBytes); } deserialize(v) { const num = this.hexToLittleEndianNumber(v); return num > 127 ? num - 256 : num; } } class U16Type extends SchemaType { constructor() { super(...arguments); this.sizeInBytes = 2; this.id = 3; } isValid(v) { return Number.isInteger(v) && v >= 0 && v <= 65535; } serialize(v) { return (0, hashing_1.toLittleEndianHex)(v, this.sizeInBytes); } deserialize(v) { return this.hexToLittleEndianNumber(v); } } class I16Type extends SchemaType { constructor() { super(...arguments); this.sizeInBytes = 2; this.id = 4; } isValid(v) { return Number.isInteger(v) && v >= -32768 && v <= 32767; } serialize(v) { return (0, hashing_1.toLittleEndianHex)(v & 0xFFFF, this.sizeInBytes); } deserialize(v) { const num = this.hexToLittleEndianNumber(v); return num > 32767 ? num - 65536 : num; } } class U32Type extends SchemaType { constructor() { super(...arguments); this.sizeInBytes = 4; this.id = 5; } isValid(v) { return Number.isInteger(v) && v >= 0 && v <= 4294967295; } serialize(v) { return (0, hashing_1.toLittleEndianHex)(v, this.sizeInBytes); } deserialize(v) { return this.hexToLittleEndianNumber(v); } } class I32Type extends SchemaType { constructor() { super(...arguments); this.sizeInBytes = 4; this.id = 6; } isValid(v) { return Number.isInteger(v) && v >= -2147483648 && v <= 2147483647; } serialize(v) { return (0, hashing_1.toLittleEndianHex)(v >>> 0, this.sizeInBytes); } deserialize(v) { const num = this.hexToLittleEndianNumber(v); return num > 2147483647 ? num - 4294967296 : num; } } class U64Type extends SchemaType { constructor() { super(...arguments); this.sizeInBytes = 8; this.id = 7; } isValid(v) { const bigIntValue = BigInt(v); return bigIntValue >= BigInt(0) && bigIntValue <= BigInt("18446744073709551615"); } serialize(v) { return (0, hashing_1.toLittleEndianHex)(BigInt(v), this.sizeInBytes); } deserialize(v) { return this.hexToLittleEndianBigInt(v); } } class I64Type extends SchemaType { constructor() { super(...arguments); this.sizeInBytes = 8; this.id = 8; } isValid(v) { const bigIntValue = BigInt(v); return bigIntValue >= BigInt("-9223372036854775808") && bigIntValue <= BigInt("9223372036854775807"); } serialize(v) { return (0, hashing_1.toLittleEndianHex)(BigInt(v) & BigInt("0xFFFFFFFFFFFFFFFF"), this.sizeInBytes); } deserialize(v) { const num = this.hexToLittleEndianBigInt(v); return num > BigInt("9223372036854775807") ? num - BigInt("18446744073709551616") : num; } } class F32Type extends SchemaType { constructor() { super(...arguments); this.sizeInBytes = 4; this.id = 9; } isValid(v) { if (!Number.isFinite(v)) return false; const min = -3.4028234663852886e+38; const max = 3.4028234663852886e+38; return v >= min && v <= max; } serialize(v) { const buffer = new ArrayBuffer(4); const view = new DataView(buffer); view.setFloat32(0, v, true); return "0x" + Array.from(new Uint8Array(buffer)) .map(b => b.toString(16).padStart(2, '0')) .join(''); } deserialize(v) { // Remove '0x' prefix if present v = v.replace('0x', ''); // Ensure the hex string has the correct length while (v.length < this.sizeInBytes * 2) { v = v + '0'; } const bytes = []; for (let i = 0; i < v.length; i += 2) { bytes.push(parseInt(v.substr(i, 2), 16)); } const buffer = new ArrayBuffer(4); const view = new DataView(buffer); bytes.forEach((byte, index) => { view.setUint8(index, byte); }); return view.getFloat32(0, true); } } class F64Type extends SchemaType { constructor() { super(...arguments); this.sizeInBytes = 8; this.id = 10; } isValid(v) { return !isNaN(v) && Number.isFinite(v); } serialize(v) { const buffer = new ArrayBuffer(8); const view = new DataView(buffer); view.setFloat64(0, v, true); return "0x" + Array.from(new Uint8Array(buffer)) .map(b => b.toString(16).padStart(2, '0')) .join(''); } deserialize(v) { // Remove '0x' prefix if present v = v.replace('0x', ''); // Ensure the hex string has the correct length while (v.length < this.sizeInBytes * 2) { v = v + '0'; } const bytes = []; for (let i = 0; i < v.length; i += 2) { bytes.push(parseInt(v.substr(i, 2), 16)); } const buffer = new ArrayBuffer(8); const view = new DataView(buffer); bytes.forEach((byte, index) => { view.setUint8(index, byte); }); return view.getFloat64(0, true); } } class HashType extends SchemaType { constructor() { super(...arguments); this.sizeInBytes = 32; this.id = 11; } isValid(v) { return /^0x[a-fA-F0-9]{64}$/.test(v); } serialize(v) { return v.slice(2); } // Remove '0x' prefix deserialize(v) { return `0x${v}`; } } class BoolType extends SchemaType { constructor() { super(...arguments); this.sizeInBytes = 1; this.id = 12; } isValid(v) { return true; } serialize(v) { return (0, hashing_1.toLittleEndianHex)(v ? 1 : 0, this.sizeInBytes); ; } deserialize(v) { return parseInt(v, 16) == 1; } } class StringType extends SchemaType { constructor() { super(...arguments); this.sizeInBytes = 128; this.id = 13; } isValid(v) { return v.length < 128; } serialize(v) { return v; } deserialize(v) { return v; } } exports.Char = new CharType(); exports.U8 = new U8Type(); exports.I8 = new I8Type(); exports.U16 = new U16Type(); exports.I16 = new I16Type(); exports.U32 = new U32Type(); exports.I32 = new I32Type(); exports.U64 = new U64Type(); exports.I64 = new I64Type(); exports.F32 = new F32Type(); exports.F64 = new F64Type(); exports.Hash = new HashType(); exports.Bool = new BoolType(); exports.Text = new StringType(); const stringToSchemaType = (type) => { switch (type) { case 'Char': return exports.Char; case 'U8': return exports.U8; case 'I8': return exports.I8; case 'U16': return exports.U16; case 'I16': return exports.I16; case 'U32': return exports.U32; case 'I32': return exports.I32; case 'U64': return exports.U64; case 'I64': return exports.I64; case 'F32': return exports.F32; case 'F64': return exports.F64; case 'Hash': return exports.Hash; case 'Bool': return exports.Bool; case 'Text': return exports.Text; default: return undefined; } }; exports.stringToSchemaType = stringToSchemaType; class Schema { // Set a default value for the Schema Object. constructor(def) { this.type = {}; this.def = def; this.schemaHash = this.getSchemaHash(); } static create(def) { return new Schema(def); } getAttestations(api, address) { return __awaiter(this, void 0, void 0, function* () { const data = yield (0, state_1.getAttestationForSchema)(api.network, address, api.issuerHash, this); if (!data) throw Error("Attestation doesn't not exist."); // Convert array data to structured schema object. const response = []; const sortedEntries = Object.entries(this.def).sort((a, b) => b[0].localeCompare(a[0])); data.forEach((d, i) => { let objs = {}; sortedEntries.forEach((entry, index) => { const [key, schemaType] = entry; const value = d[index]; // Convert the value to string if it's a number const stringValue = typeof value === 'number' ? value.toString(16).padStart(schemaType.sizeInBytes * 2, '0') : value; if (typeof stringValue !== 'string') { throw new Error(`Unexpected data type for ${key}: ${typeof value}`); } objs[key] = schemaType.deserialize(stringValue); }); response.push(objs); }); return response; }); } getSortedEntries(item) { return Object.entries(item).sort((a, b) => b[0].localeCompare(a[0])); } getEntries() { return this.getSortedEntries(this.def); } // Returns the Schema Object to pass in the polkadot js api. getSchemaObject() { return this.getSortedEntries(this.def).map(item => [item[0], item[1].id]); } // Generate Schema Hash. getSchemaHash() { const encoder = new TextEncoder(); const bytes = []; this.getSortedEntries(this.def).forEach(i => { const bA0 = encoder.encode(i[0]); const bA1 = (0, hashing_1.numberToUint8Array)(i[1].id); // Concatenate the byte arrays to the bytes array bytes.push(bA0); bytes.push(bA1); }); // Calculate the total length of bytes const totalLength = bytes.reduce((acc, curr) => acc + curr.length, 0); // Create a new Uint8Array with the correct length const combinedBytes = new Uint8Array(totalLength); // Copy the concatenated byte arrays to the combinedBytes array let offset = 0; bytes.forEach(byteArray => { combinedBytes.set(byteArray, offset); offset += byteArray.length; }); return `0x${(0, hashing_1.bytesToBlakeTwo256Hash)(combinedBytes)}`; } ifExistAlready(api) { return __awaiter(this, void 0, void 0, function* () { return (0, state_1.checkIfSchemaExist)(api.network, this.schemaHash); }); } register(api) { return __awaiter(this, void 0, void 0, function* () { if (!api.issuerHash) throw Error("Issuer Hash is not defined in TrueApi, either pass the hash in constructor or call api.registerIssuer(name: string)."); if (yield this.ifExistAlready(api)) { // Schema already exist. return this.schemaHash; } return yield (0, extrinsic_1.createSchema)(api.network, api.account, api.issuerHash, this); }); } validate(data) { for (const [key, value] of Object.entries(data)) { if (!this.def[key].isValid(value)) { throw new Error(`Invalid value for ${key}: ${value}`); } } } attest(api, user, attestation) { return __awaiter(this, void 0, void 0, function* () { this.validate(attestation); // Check if issuer hash exists in the api. if (!api.issuerHash) throw Error("issuerHash property does not exist on TrueApi, try registering an issuer."); // Serialize the attestation values. const values = this.getSortedEntries(attestation).map(([key, value]) => { return this.def[key].serialize(value); }); // Check if schema exists, if not register & attest. if (!(yield this.ifExistAlready(api))) { // Do a combined transaction of register & attest on-chain. const schemaTx = yield (0, extrinsic_1.createSchemaTx)(api.network, api.account, api.issuerHash, this); const attestationTx = yield (0, extrinsic_1.createAttestationTx)(api.network, api.account, api.issuerHash, this, user, values); return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { yield api.network.tx.utility.batch([schemaTx, attestationTx]).signAndSend(api.account, ({ status, events }) => { events.forEach(({ event: { method } }) => { if (method == 'ExtrinsicFailed') { reject(Error(`Transaction failed, error attesting on-chain for the users. \ntx: ${status.hash}`)); } }); let attestationId = -1; if (status.isInBlock) { console.log(`\nTransaction is inBlock at blockHash ${status.asInBlock}`); events.forEach(({ event: { method, data } }) => { if (method == 'AttestationCreated') { console.log('Attestation Created: InBlock'); const jsonData = data.toJSON(); if (jsonData) { attestationId = jsonData[3]; } } if (method == 'ExtrinsicFailed') { reject(Error(`\nTransaction failed, error attesting on-chain for the user. \ntx: ${status.hash}`)); } }); resolve({ attestationId, prismUrl: `${__1.prismUrl}/query/${status.asInBlock.toString()}`, transaction: { hash: status.asInBlock.toString(), explorerUrl: `https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Framan.truenetwork.io%2Fws#/explorer/query/${status.asInBlock.toString()}`, events: events } }); } }); })); } return yield (0, extrinsic_1.createAttestation)(api.network, api.account, api.issuerHash, this, user, values); }); } updateAttestation(api, user, attestationId, attestation) { return __awaiter(this, void 0, void 0, function* () { this.validate(attestation); // Check if issuer hash exists in the api. if (!api.issuerHash) throw Error("issuerHash property does not exist on TrueApi, try registering an issuer."); // Serialize the attestation values. const values = this.getSortedEntries(attestation).map(([key, value]) => { return this.def[key].serialize(value); }); // Check if schema exists, if not register & attest. if (!(yield this.ifExistAlready(api))) { // Do a combined transaction of register & attest on-chain. const schemaTx = yield (0, extrinsic_1.createSchemaTx)(api.network, api.account, api.issuerHash, this); const attestationTx = yield (0, extrinsic_1.createAttestationTx)(api.network, api.account, api.issuerHash, this, user, values); return new Promise((resolve, _) => __awaiter(this, void 0, void 0, function* () { yield api.network.tx.utility.batch([schemaTx, attestationTx]).signAndSend(api.account, ({ status, events }) => { events.forEach(({ event: { method } }) => { if (method == 'ExtrinsicFailed') { throw Error(`\nTransaction failed, error attesting on-chain for the users. \ntx: ${status.hash}`); } }); if (status.isInBlock) { console.log(`\nTransaction is inBlock at blockHash ${status.asInBlock}`); resolve({ attestationId, prismUrl: `${__1.prismUrl}/query/${status.asInBlock.toString()}`, transaction: { hash: status.asInBlock.toString(), explorerUrl: `https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Framan.truenetwork.io%2Fws#/explorer/query/${status.asInBlock.toString()}`, events: events } }); } }); })); } return yield (0, extrinsic_1.updateAttestation)(api.network, api.account, api.issuerHash, this, user, attestationId, values); }); } } exports.Schema = Schema;