UNPKG

@blooo/hw-app-concordium

Version:

Ledger Hardware Wallet Concordium Application API

662 lines (578 loc) 35.5 kB
import BIPPath from "bip32-path"; import { encodeDataBlob, encodeInt32, encodeInt8, encodeWord16, encodeWord64, serializeAccountTransaction, serializeAccountTransactionHeader } from "./utils"; import { Buffer as NodeBuffer } from 'buffer/index'; import { AccountTransaction, IConfigureBakerTransaction, IConfigureDelegationTransaction, ICredentialDeploymentTransaction, IDeployModuleTransaction, IInitContractTransaction, IPublicInfoForIpTransaction, IRegisterDataTransaction, ISimpleTransferTransaction, ISimpleTransferWithMemoTransaction, ISimpleTransferWithScheduleAndMemoTransaction, ISimpleTransferWithScheduleTransaction, ITransferToPublicTransaction, IUpdateContractTransaction, IUpdateCredentialsTransaction, IPLTTransaction } from "./type"; import { AccountAddress, DataBlob } from "@concordium/web-sdk"; import { encodeWord8, encodeWord8FromString, serializeMap, serializeVerifyKey } from "@concordium/web-sdk/lib/esm/serializationHelpers"; import { serializeCredentialDeploymentInfo } from "@concordium/web-sdk/lib/esm/serialization"; // Transaction-related constants const MAX_CHUNK_SIZE = 255; const MAX_SCHEDULE_CHUNK_SIZE = 15; const HEADER_LENGTH = 60; const TRANSACTION_KIND_LENGTH = 1; const INDEX_LENGTH = 1; const ONE_OCTET_LENGTH = 1; const BITMAP_LENGTH = 2; // Payload-related constants const STAKING_PAYLOAD_LENGTH = 8; const RESTAKE_EARNINGS_PAYLOAD_LENGTH = 1; const OPEN_FOR_DELEGATION_PAYLOAD_LENGTH = 1; const SUSPENDED_LENGTH = 1; // Key-related constants const KEYS_AGGREGATION_LENGTH = 160; const KEYS_ELECTION_AND_SIGNATURE_LENGTH = 192; const KEYS_PAYLOAD_LENGTH = KEYS_ELECTION_AND_SIGNATURE_LENGTH + KEYS_AGGREGATION_LENGTH; const KEY_LENGTH = 32; // Metadata and commission-related constants const METADATA_URL_LENGTH = 2; const TRANSACTION_FEE_COMMISSION_LENGTH = 4; const BAKING_REWARD_COMMISSION_LENGTH = 4; const FINALIZATION_REWARD_COMMISSION_LENGTH = 4; // Deploy module constants const VERSION_LENGTH = 4; const SOURCE_LENGTH_LENGTH = 4; const AMOUNT_LENGTH = 8; const MODULE_REF_LENGTH = 32; const UPDATE_INDEX_LENGTH = 8; const UPDATE_SUB_INDEX_LENGTH = 8; // Credential and identity-related constants const REG_ID_LENGTH = 48; const IP_IDENTITY_LENGTH = 4; const AR_DATA_LENGTH = 2; const ID_CRED_PUB_SHARE_LENGTH = 96; const VALID_TO_LENGTH = 3; const CREATED_AT_LENGTH = 3; const ATTRIBUTES_LENGTH = 2; const TAG_LENGTH = 1; const VALUE_LENGTH = 1; const PROOF_LENGTH_LENGTH = 4; const CREDENTIAL_ID_LENGTH = 48; /** * Serializes a BIP32 path into a buffer. * @param {number[]} path - The BIP32 path as an array of numbers. * @returns {Buffer} - The serialized path as a buffer. */ const serializePath = (path: number[]): Buffer => { const buf = Buffer.alloc(1 + path.length * 4); buf.writeUInt8(path.length, 0); for (const [i, num] of path.entries()) { buf.writeUInt32BE(num, 1 + i * 4); } return buf; }; /** * Splits a BIP32 path string into an array of numbers. * @param {string} path - The BIP32 path as a string. * @returns {number[]} - The path as an array of numbers. */ export const splitPath = (path: string): number[] => { const result: number[] = []; const components = path.split("/"); components.forEach((element) => { let number = parseInt(element, 10); if (isNaN(number)) { return; } if (element.length > 1 && element.endsWith("'")) { number += 0x80000000; } result.push(number); }); return result; }; /** * Converts a BIP32 path string to a buffer. * @param {string} originalPath - The BIP32 path as a string. * @returns {Buffer} - The path as a buffer. */ export const pathToBuffer = (originalPath: string): Buffer => { const path = originalPath; const pathNums: number[] = BIPPath.fromString(path).toPathArray(); return serializePath(pathNums); }; /** * Serializes transaction payloads with a derivation path. * @param {string} path - The BIP32 path as a string. * @param {Buffer} rawTx - The raw transaction data. * @returns {Buffer[]} - An array of serialized payload buffers. */ export const serializeTransactionPayloadsWithDerivationPath = (path: string, rawTx: Buffer): Buffer[] => { const paths = splitPath(path); let offset = 0; const payloads: Buffer[] = []; let pathBuffer = Buffer.alloc(1 + paths.length * 4); pathBuffer[0] = paths.length; paths.forEach((element, index) => { pathBuffer.writeUInt32BE(element, 1 + 4 * index); }); while (offset !== rawTx.length) { const first = offset === 0; let chunkSize = offset + MAX_CHUNK_SIZE > rawTx.length ? rawTx.length - offset : MAX_CHUNK_SIZE; // Allocate buffer for the first chunk with pathBuffer size const buffer = Buffer.alloc(first ? pathBuffer.length + chunkSize : chunkSize); if (first) { // Copy pathBuffer to the beginning of the first chunk pathBuffer.copy(buffer, 0); rawTx.copy(buffer, pathBuffer.length, offset, offset + chunkSize); } else { rawTx.copy(buffer, 0, offset, offset + chunkSize); } payloads.push(buffer); offset += chunkSize; } return payloads; }; /** * Serializes transaction payloads without a derivation path. * @param {Buffer} rawTx - The raw transaction data. * @returns {Buffer[]} - An array of serialized payload buffers. */ export const serializeTransactionPayloads = (rawTx: Buffer): Buffer[] => { let offset = 0; const payloads: Buffer[] = []; while (offset !== rawTx.length) { let chunkSize = offset + MAX_CHUNK_SIZE > rawTx.length ? rawTx.length - offset : MAX_CHUNK_SIZE; const buffer = Buffer.alloc( chunkSize ); rawTx.copy(buffer, 0, offset, offset + chunkSize); payloads.push(buffer); offset += chunkSize; } return payloads; }; /** * Serializes an account transaction with a derivation path. * @param {AccountTransaction} txn - The transaction to serialize. * @param {string} path - The BIP32 path as a string. * @returns {{ payloads: Buffer[] }} - An object containing serialized payloads. */ export const serializeTransaction = (txn: AccountTransaction, path: string): { payloads: Buffer[] } => { const txSerialized = serializeAccountTransaction(txn); const payloads = serializeTransactionPayloadsWithDerivationPath(path, txSerialized); return { payloads }; } /** * Serializes a PLT transaction. * @param txn The PLT transaction to serialize. * @param path The BIP32 path as a string. * @returns An object containing serialized payloads. */ export const serializePltTransaction = (txn: IPLTTransaction, path: string): Buffer[] => { if (!txn.payload.tokenId) { throw new Error('tokenId is undefined in PLT transaction payload'); } // Handle both string and TokenId object cases const tokenName: string = typeof txn.payload.tokenId === 'string' ? txn.payload.tokenId : txn.payload.tokenId.value; const serializedTokenName = encodeDataBlob(tokenName).subarray(1); // Handle both string and Cbor object cases const operationsBuffer = typeof txn.payload.operations === 'string' ? Buffer.from(txn.payload.operations, 'hex') : txn.payload.operations.bytes; const operationsLength = encodeInt32(operationsBuffer.length); const serializedType = Buffer.from(Uint8Array.of(txn.transactionKind)); const payloadSize = serializedType.length + operationsBuffer.length + serializedTokenName.length; const serializedHeader = serializeAccountTransactionHeader(txn, payloadSize); const serializedTransaction = Buffer.concat([serializedHeader, serializedType, serializedTokenName, operationsLength, operationsBuffer]); const payload = serializeTransactionPayloadsWithDerivationPath(path, serializedTransaction); return payload; }; /** * Serializes a simple transfer transaction. * @param {ISimpleTransferTransaction} txn - The transaction to serialize. * @param {string} path - The BIP32 path as a string. * @returns {{ payloads: Buffer[] }} - An object containing serialized payloads. */ export const serializeSimpleTransfer = (txn: ISimpleTransferTransaction, path: string): { payloads: Buffer[] } => { return serializeTransaction(txn, path); }; /** * Serializes a simple transfer transaction with a memo. * @param {ISimpleTransferWithMemoTransaction} txn - The transaction to serialize. * @param {string} path - The BIP32 path as a string. * @returns {{ payloadHeaderAddressMemoLength: Buffer[], payloadsMemo: Buffer[], payloadsAmount: Buffer[] }} - An object containing serialized payloads. */ export const serializeSimpleTransferWithMemo = (txn: ISimpleTransferWithMemoTransaction, path: string): { payloadHeaderAddressMemoLength: Buffer[], payloadsMemo: Buffer[], payloadsAmount: Buffer[] } => { const serializedType = Buffer.from(Uint8Array.of(txn.transactionKind)); const serializedToAddress = AccountAddress.toBuffer(txn.payload.toAddress); const serializedAmount = encodeWord64(txn.payload.amount.microCcdAmount); const serializedMemo = encodeDataBlob(txn.payload.memo); const memoLength = serializedMemo.subarray(0, 2); const payloadSize = serializedType.length + serializedMemo.length + serializedAmount.length + serializedToAddress.length; const serializedHeader = serializeAccountTransactionHeader(txn, payloadSize); const serializedHeaderAddressMemoLength = Buffer.concat([serializedHeader, serializedType, serializedToAddress, memoLength]); const payloadHeaderAddressMemoLength = serializeTransactionPayloadsWithDerivationPath(path, serializedHeaderAddressMemoLength); const payloadsMemo = serializeTransactionPayloads(serializedMemo.subarray(2)); const payloadsAmount = serializeTransactionPayloads(serializedAmount); return { payloadHeaderAddressMemoLength, payloadsMemo, payloadsAmount }; }; /** * Serializes a transfer transaction with a schedule. * @param {ISimpleTransferWithScheduleTransaction} txn - The transaction to serialize. * @param {string} path - The BIP32 path as a string. * @returns {{ payloadHeaderAddressScheduleLength: Buffer[], payloadsSchedule: Buffer[] }} - An object containing serialized payloads. */ export const serializeTransferWithSchedule = (txn: ISimpleTransferWithScheduleTransaction, path: string): { payloadHeaderAddressScheduleLength: Buffer[], payloadsSchedule: Buffer[] } => { const serializedType = Buffer.from(Uint8Array.of(txn.transactionKind)); const toAddressBuffer = AccountAddress.toBuffer(txn.payload.toAddress); const scheduleLength = encodeInt8(txn.payload.schedule.length); const scheduleBuffer = txn.payload.schedule.map((item: { timestamp: string, amount: string }) => { const timestampBuffer = encodeWord64(item.timestamp); const amountBuffer = encodeWord64(item.amount); return Buffer.concat([timestampBuffer, amountBuffer]); }); const serializedSchedule = Buffer.concat([...scheduleBuffer]); const payloadSize = serializedType.length + scheduleLength.length + serializedSchedule.length + toAddressBuffer.length; const serializedHeader = serializeAccountTransactionHeader(txn, payloadSize); const serializedHeaderAddressScheduleLength = Buffer.concat([serializedHeader, serializedType, toAddressBuffer, scheduleLength]); const payloadHeaderAddressScheduleLength = serializeTransactionPayloadsWithDerivationPath(path, serializedHeaderAddressScheduleLength); const payloadsSchedule: Buffer[] = []; let remainingPairs = txn.payload.schedule.length for (let i = 0; i < scheduleBuffer.length; i += MAX_SCHEDULE_CHUNK_SIZE) { const offset = remainingPairs > MAX_SCHEDULE_CHUNK_SIZE ? MAX_SCHEDULE_CHUNK_SIZE : remainingPairs const scheduleChunk = serializeTransactionPayloads(serializedSchedule.subarray(i * 16, (i + offset) * 16)); payloadsSchedule.push(...scheduleChunk); remainingPairs = txn.payload.schedule.length - MAX_SCHEDULE_CHUNK_SIZE } return { payloadHeaderAddressScheduleLength, payloadsSchedule }; }; /** * Serializes a configure delegation transaction. * @param {IConfigureDelegationTransaction} txn - The transaction to serialize. * @param {string} path - The BIP32 path as a string. * @returns {{ payloads: Buffer[] }} - An object containing serialized payloads. */ export const serializeConfigureDelegation = (txn: IConfigureDelegationTransaction, path: string): { payloads: Buffer[] } => { return serializeTransaction(txn, path); }; /** * Serializes a configure baker transaction. * @param {IConfigureBakerTransaction} txn - The transaction to serialize. * @param {string} path - The BIP32 path as a string. * @returns {{ payloadHeaderKindAndBitmap: Buffer, payloadFirstBatch: Buffer, payloadAggregationKeys: Buffer, payloadUrlLength: Buffer, payloadURL: Buffer, payloadCommissionFee: Buffer }} - An object containing serialized payloads. */ export const serializeConfigureBaker = (txn: IConfigureBakerTransaction, path: string): { payloadHeaderKindAndBitmap: Buffer, payloadFirstBatch: Buffer, payloadAggregationKeys: Buffer, payloadUrlLength: Buffer, payloadURL: Buffer, payloadCommissionFee: Buffer, payloadSuspended: Buffer } => { let stake: Buffer = Buffer.alloc(0); let restakeEarnings: Buffer = Buffer.alloc(0); let openForDelegation: Buffer = Buffer.alloc(0); let keys: Buffer = Buffer.alloc(0); let metadataUrl: Buffer = Buffer.alloc(0); let url: Buffer = Buffer.alloc(0); let transactionFeeCommission: Buffer = Buffer.alloc(0); let bakingRewardCommission: Buffer = Buffer.alloc(0); let finalizationRewardCommission: Buffer = Buffer.alloc(0); let suspended: Buffer = Buffer.alloc(0); let offset: number = 0; const txSerialized = serializeAccountTransaction(txn); const headerKindAndBitmap = txSerialized.subarray(0, HEADER_LENGTH + TRANSACTION_KIND_LENGTH + BITMAP_LENGTH); offset += HEADER_LENGTH + TRANSACTION_KIND_LENGTH + BITMAP_LENGTH; if (txn.payload.hasOwnProperty('stake')) { stake = txSerialized.subarray(offset, offset + STAKING_PAYLOAD_LENGTH); offset += STAKING_PAYLOAD_LENGTH; } if (txn.payload.hasOwnProperty('restakeEarnings')) { restakeEarnings = txSerialized.subarray(offset, offset + RESTAKE_EARNINGS_PAYLOAD_LENGTH); offset += RESTAKE_EARNINGS_PAYLOAD_LENGTH; } if (txn.payload.hasOwnProperty('openForDelegation')) { openForDelegation = txSerialized.subarray(offset, offset + OPEN_FOR_DELEGATION_PAYLOAD_LENGTH); offset += OPEN_FOR_DELEGATION_PAYLOAD_LENGTH; } if (txn.payload.hasOwnProperty('keys')) { keys = txSerialized.subarray(offset, offset + KEYS_PAYLOAD_LENGTH); offset += KEYS_PAYLOAD_LENGTH; } if (txn.payload.hasOwnProperty('metadataUrl')) { metadataUrl = txSerialized.subarray(offset, offset + METADATA_URL_LENGTH); offset += METADATA_URL_LENGTH; url = txSerialized.subarray(offset, offset + metadataUrl.readUInt16BE(0)); offset += metadataUrl.readUInt16BE(0); } if (txn.payload.hasOwnProperty('transactionFeeCommission')) { transactionFeeCommission = txSerialized.subarray(offset, offset + TRANSACTION_FEE_COMMISSION_LENGTH); offset += TRANSACTION_FEE_COMMISSION_LENGTH; } if (txn.payload.hasOwnProperty('bakingRewardCommission')) { bakingRewardCommission = txSerialized.subarray(offset, offset + BAKING_REWARD_COMMISSION_LENGTH); offset += BAKING_REWARD_COMMISSION_LENGTH; } if (txn.payload.hasOwnProperty('finalizationRewardCommission')) { finalizationRewardCommission = txSerialized.subarray(offset, offset + FINALIZATION_REWARD_COMMISSION_LENGTH); } if (txn.payload.hasOwnProperty('suspended')) { suspended = txSerialized.subarray(offset, offset + SUSPENDED_LENGTH); } const payloadHeaderKindAndBitmap = serializeTransactionPayloadsWithDerivationPath(path, headerKindAndBitmap); const payloadFirstBatch = Buffer.concat([stake, restakeEarnings, openForDelegation, keys.subarray(0, KEYS_ELECTION_AND_SIGNATURE_LENGTH)]); const payloadAggregationKeys = keys.subarray(KEYS_ELECTION_AND_SIGNATURE_LENGTH); const payloadUrlLength = metadataUrl; const payloadURL = url; const payloadCommissionFee = Buffer.concat([transactionFeeCommission, bakingRewardCommission, finalizationRewardCommission]); const payloadSuspended = suspended; return { payloadHeaderKindAndBitmap: payloadHeaderKindAndBitmap[0], payloadFirstBatch, payloadAggregationKeys, payloadUrlLength, payloadURL, payloadCommissionFee, payloadSuspended }; }; /** * Serializes a transfer transaction with a schedule and memo. * @param {ISimpleTransferWithScheduleAndMemoTransaction} txn - The transaction to serialize. * @param {string} path - The BIP32 path as a string. * @returns {{ payloadHeaderAddressScheduleLengthAndMemoLength: Buffer[], payloadMemo: Buffer[], payloadsSchedule: Buffer[] }} - An object containing serialized payloads. */ export const serializeTransferWithScheduleAndMemo = (txn: ISimpleTransferWithScheduleAndMemoTransaction, path: string): { payloadHeaderAddressScheduleLengthAndMemoLength: Buffer[], payloadMemo: Buffer[], payloadsSchedule: Buffer[] } => { const toAddressBuffer = AccountAddress.toBuffer(txn.payload.toAddress); const scheduleLength = encodeInt8(txn.payload.schedule.length); const scheduleBufferArray = txn.payload.schedule.map((item: { timestamp: string, amount: string }) => { const timestampBuffer = encodeWord64(item.timestamp); const amountBuffer = encodeWord64(item.amount); return Buffer.concat([timestampBuffer, amountBuffer]); }); const serializedSchedule = Buffer.concat([...scheduleBufferArray]); const serializedMemo = encodeDataBlob(txn.payload.memo); const serializedType = Buffer.from(Uint8Array.of(txn.transactionKind)); const payloadSize = serializedType.length + scheduleLength.length + serializedSchedule.length + toAddressBuffer.length + serializedMemo.length; const serializedHeader = serializeAccountTransactionHeader(txn, payloadSize); const serializedHeaderAddressScheduleLengthAndMemoLength = Buffer.concat([serializedHeader, serializedType, toAddressBuffer, scheduleLength, serializedMemo.subarray(0, 2)]); const payloadHeaderAddressScheduleLengthAndMemoLength = serializeTransactionPayloadsWithDerivationPath(path, serializedHeaderAddressScheduleLengthAndMemoLength); const payloadMemo = serializeTransactionPayloads(serializedMemo.subarray(2)); const payloadsSchedule: Buffer[] = []; let remainingPairs = txn.payload.schedule.length for (let i = 0; i < scheduleBufferArray.length; i += MAX_SCHEDULE_CHUNK_SIZE) { const offset = remainingPairs > MAX_SCHEDULE_CHUNK_SIZE ? MAX_SCHEDULE_CHUNK_SIZE : remainingPairs const scheduleChunk = serializeTransactionPayloads(serializedSchedule.subarray(i * 16, (i + offset) * 16)); payloadsSchedule.push(...scheduleChunk); remainingPairs = txn.payload.schedule.length - MAX_SCHEDULE_CHUNK_SIZE } return { payloadHeaderAddressScheduleLengthAndMemoLength, payloadMemo, payloadsSchedule }; }; /** * Serializes a register data transaction. * @param {IRegisterDataTransaction} txn - The transaction to serialize. * @param {string} path - The BIP32 path as a string. * @returns {{ payloadHeader: Buffer[], payloadsData: Buffer[] }} - An object containing serialized payloads. */ export const serializeRegisterData = (txn: IRegisterDataTransaction, path: string): { payloadHeader: Buffer[], payloadsData: Buffer[] } => { const serializedData = encodeDataBlob(txn.payload.data); const serializedType = Buffer.from(Uint8Array.of(txn.transactionKind)); const payloadSize = serializedType.length + serializedData.length; const serializedHeader = serializeAccountTransactionHeader(txn, payloadSize); const serializedHeaderAndKind = Buffer.concat([serializedHeader, serializedType, serializedData.subarray(0, 2)]); const payloadHeader = serializeTransactionPayloadsWithDerivationPath(path, serializedHeaderAndKind); const payloadsData = serializeTransactionPayloads(serializedData.subarray(2)); return { payloadHeader, payloadsData }; }; /** * Serializes a transfer to public transaction. * @param {ITransferToPublicTransaction} txn - The transaction to serialize. * @param {string} path - The BIP32 path as a string. * @returns {{ payloadHeader: Buffer[], payloadsAmountAndProofsLength: Buffer[], payloadsProofs: Buffer[] }} - An object containing serialized payloads. */ export const serializeTransferToPublic = (txn: ITransferToPublicTransaction, path: string): { payloadHeader: Buffer[], payloadsAmountRecipientAndProofsLength: Buffer[], payloadsProofs: Buffer[] } => { const remainingAmount = Buffer.from(txn.payload.remainingAmount, 'hex'); const transferAmount = encodeWord64(txn.payload.transferAmount.microCcdAmount); const recipient = AccountAddress.toBuffer(txn.payload.recipient); const index = encodeWord64(txn.payload.index); const proofs = Buffer.from(txn.payload.proofs, 'hex'); const proofsLength = encodeWord16(proofs.length); const serializedType = Buffer.from(Uint8Array.of(txn.transactionKind)); const payloadSize = remainingAmount.length + transferAmount.length + index.length + proofsLength.length + proofs.length + serializedType.length; const serializedHeader = serializeAccountTransactionHeader(txn, payloadSize); const serializedHeaderAndKind = Buffer.concat([serializedHeader, serializedType]); const serializedAmountRecipientAndProofsLength = Buffer.concat([remainingAmount, transferAmount, recipient, index, proofsLength]); const payloadHeader = serializeTransactionPayloadsWithDerivationPath(path, serializedHeaderAndKind); const payloadsAmountRecipientAndProofsLength = serializeTransactionPayloads(serializedAmountRecipientAndProofsLength); const payloadsProofs = serializeTransactionPayloads(proofs); return { payloadHeader, payloadsAmountRecipientAndProofsLength, payloadsProofs }; }; /** * Serializes a deploy module transaction. * @param {IDeployModuleTransaction} txn - The transaction to serialize. * @param {string} path - The BIP32 path as a string. * @returns {{ payloads: Buffer[] }} - An object containing serialized payloads. */ export const serializeDeployModule = (txn: IDeployModuleTransaction, path: string): { payloadsHeaderAndVersion: Buffer[], payloadSource: Buffer } => { const txSerialized = serializeAccountTransaction(txn); const headerAndVersion = txSerialized.subarray(0, HEADER_LENGTH + TRANSACTION_KIND_LENGTH + VERSION_LENGTH + SOURCE_LENGTH_LENGTH); const payloadSource = txSerialized.subarray(HEADER_LENGTH + TRANSACTION_KIND_LENGTH + VERSION_LENGTH + SOURCE_LENGTH_LENGTH); const payloadsHeaderAndVersion = serializeTransactionPayloadsWithDerivationPath(path, headerAndVersion); return { payloadsHeaderAndVersion, payloadSource }; }; /** * Serializes an init contract transaction. * @param {IInitContractTransaction} txn - The transaction to serialize. * @param {string} path - The BIP32 path as a string. * @returns {{ payloads: Buffer[] }} - An object containing serialized payloads. */ export const serializeInitContract = (txn: IInitContractTransaction, path: string): { payloadsHeaderAndData: Buffer[], payloadsName: Buffer[], payloadsParam: Buffer[] } => { const txSerialized = serializeAccountTransaction(txn); let offset = 0; const headerAndData = txSerialized.subarray(0, HEADER_LENGTH + TRANSACTION_KIND_LENGTH + AMOUNT_LENGTH + MODULE_REF_LENGTH); offset += HEADER_LENGTH + TRANSACTION_KIND_LENGTH + AMOUNT_LENGTH + MODULE_REF_LENGTH; const payloadsHeaderAndData = serializeTransactionPayloadsWithDerivationPath(path, headerAndData); const nameLength = txSerialized.subarray(offset, offset + 2 * ONE_OCTET_LENGTH); offset += 2 * ONE_OCTET_LENGTH; const name = txSerialized.subarray(offset, offset + nameLength.readUInt16BE(0)); offset += nameLength.readUInt16BE(0); const payloadsName = serializeTransactionPayloads(Buffer.concat([nameLength, name])); const paramLength = txSerialized.subarray(offset, offset + 2 * ONE_OCTET_LENGTH); offset += 2 * ONE_OCTET_LENGTH; const param = txSerialized.subarray(offset, offset + paramLength.readUInt16BE(0)); offset += paramLength.readUInt16BE(0); const payloadsParam = serializeTransactionPayloads(Buffer.concat([paramLength, param])); return { payloadsHeaderAndData, payloadsName, payloadsParam }; }; /** * Serializes an update contract transaction. * @param {IUpdateContractTransaction} txn - The transaction to serialize. * @param {string} path - The BIP32 path as a string. * @returns {{ payloads: Buffer[] }} - An object containing serialized payloads. */ export const serializeUpdateContract = (txn: IUpdateContractTransaction, path: string): { payloadsHeaderAndData: Buffer[], payloadsName: Buffer[], payloadsParam: Buffer[] } => { const txSerialized = serializeAccountTransaction(txn); let offset = 0; const headerAndData = txSerialized.subarray(0, HEADER_LENGTH + TRANSACTION_KIND_LENGTH + AMOUNT_LENGTH + UPDATE_INDEX_LENGTH + UPDATE_SUB_INDEX_LENGTH); offset += HEADER_LENGTH + TRANSACTION_KIND_LENGTH + AMOUNT_LENGTH + UPDATE_INDEX_LENGTH + UPDATE_SUB_INDEX_LENGTH; const payloadsHeaderAndData = serializeTransactionPayloadsWithDerivationPath(path, headerAndData); const nameLength = txSerialized.subarray(offset, offset + 2 * ONE_OCTET_LENGTH); offset += 2 * ONE_OCTET_LENGTH; const name = txSerialized.subarray(offset, offset + nameLength.readUInt16BE(0)); offset += nameLength.readUInt16BE(0); const payloadsName = serializeTransactionPayloads(Buffer.concat([nameLength, name])); const paramLength = txSerialized.subarray(offset, offset + 2 * ONE_OCTET_LENGTH); offset += 2 * ONE_OCTET_LENGTH; const param = txSerialized.subarray(offset, offset + paramLength.readUInt16BE(0)); offset += paramLength.readUInt16BE(0); const payloadsParam = serializeTransactionPayloads(Buffer.concat([paramLength, param])); return { payloadsHeaderAndData, payloadsName, payloadsParam }; }; /** * Serializes a credential deployment transaction. * @param {ICredentialDeploymentTransaction} txn - The transaction to serialize. * @param {string} path - The BIP32 path as a string. * @returns {{ payloadDerivationPath: Buffer, numberOfVerificationKeys: Buffer, keyIndexAndSchemeAndVerificationKey: Buffer, thresholdAndRegIdAndIPIdentity: Buffer, encIdCredPubShareAndKey: Buffer, validToAndCreatedAtAndAttributesLength: Buffer, attributesLength: Buffer, tag: Buffer[], valueLength: Buffer[], value: Buffer[], proofLength: Buffer, proofs: Buffer }} - An object containing serialized payloads. */ export const serializeCredentialDeployment = (txn: ICredentialDeploymentTransaction, path: string): { payloadDerivationPath: Buffer, numberOfVerificationKeys: Buffer, keyIndexAndSchemeAndVerificationKey: Buffer, thresholdAndRegIdAndIPIdentity: Buffer, encIdCredPubShareAndKey: Buffer, validToAndCreatedAtAndAttributesLength: Buffer, attributesLength: Buffer, tag: Buffer[], valueLength: Buffer[], value: Buffer[], proofLength: Buffer, proofs: Buffer } => { let offset = 0; const txSerialized = serializeCredentialDeploymentInfo(txn); const payloadDerivationPath = pathToBuffer(path); let tag: Buffer[] = []; let valueLength: Buffer[] = []; let value: Buffer[] = []; let proofLength: Buffer = Buffer.alloc(0); let proofs: Buffer = Buffer.alloc(0); const numberOfVerificationKeys = Buffer.from(txSerialized.subarray(offset, offset + INDEX_LENGTH)); offset += INDEX_LENGTH; const keyIndexAndSchemeAndVerificationKey = Buffer.from(txSerialized.subarray(offset, offset + 2 * ONE_OCTET_LENGTH + KEY_LENGTH)); offset += 2 * ONE_OCTET_LENGTH + KEY_LENGTH; const thresholdAndRegIdAndIPIdentity = Buffer.from(txSerialized.subarray(offset, offset + 2 * ONE_OCTET_LENGTH + REG_ID_LENGTH + IP_IDENTITY_LENGTH + AR_DATA_LENGTH)); offset += 2 * ONE_OCTET_LENGTH + REG_ID_LENGTH + IP_IDENTITY_LENGTH + AR_DATA_LENGTH; const encIdCredPubShareAndKey = Buffer.from(txSerialized.subarray(offset, offset + 4 * ONE_OCTET_LENGTH + ID_CRED_PUB_SHARE_LENGTH)); offset += 4 * ONE_OCTET_LENGTH + ID_CRED_PUB_SHARE_LENGTH; const validToAndCreatedAtAndAttributesLength = Buffer.from(txSerialized.subarray(offset, offset + ATTRIBUTES_LENGTH + VALID_TO_LENGTH + CREATED_AT_LENGTH)); offset += ATTRIBUTES_LENGTH + VALID_TO_LENGTH + CREATED_AT_LENGTH; const attributesLength = validToAndCreatedAtAndAttributesLength.subarray(-ATTRIBUTES_LENGTH); tag = []; valueLength = []; value = []; for (let j = 0; j < attributesLength.readUInt16BE(0); j++) { tag.push(Buffer.from(txSerialized.subarray(offset, offset + TAG_LENGTH))); offset += TAG_LENGTH; valueLength.push(Buffer.from(txSerialized.subarray(offset, offset + VALUE_LENGTH))); offset += VALUE_LENGTH; value.push(Buffer.from(txSerialized.subarray(offset, offset + valueLength[j].readUInt8(0)))); offset += valueLength[j].readUInt8(0); } proofLength = Buffer.from(txSerialized.subarray(offset, offset + PROOF_LENGTH_LENGTH)); offset += PROOF_LENGTH_LENGTH; proofs = Buffer.from(txSerialized.subarray(offset, offset + proofLength.readUInt32BE(0))); return { payloadDerivationPath, numberOfVerificationKeys, keyIndexAndSchemeAndVerificationKey, thresholdAndRegIdAndIPIdentity, encIdCredPubShareAndKey, validToAndCreatedAtAndAttributesLength, attributesLength, tag, valueLength, value, proofLength, proofs }; }; /** * Serializes an update credentials transaction. * @param {IUpdateCredentialsTransaction} txn - The transaction to serialize. * @param {string} path - The BIP32 path as a string. * @returns {{ payloadHeaderKindAndIndexLength: Buffer[], credentialIndex: Buffer[], numberOfVerificationKeys: Buffer[], keyIndexAndSchemeAndVerificationKey: Buffer[], thresholdAndRegIdAndIPIdentity: Buffer[], encIdCredPubShareAndKey: Buffer[], validToAndCreatedAtAndAttributesLength: Buffer[], attributesLength: Buffer[], tag: Buffer[][], valueLength: Buffer[][], value: Buffer[][], proofLength: Buffer[], proofs: Buffer[], credentialIdCount: Buffer, credentialIds: Buffer[], threshold: Buffer }} - An object containing serialized payloads. */ export const serializeUpdateCredentials = (txn: IUpdateCredentialsTransaction, path: string): { payloadHeaderKindAndIndexLength: Buffer[], credentialIndex: Buffer[], numberOfVerificationKeys: Buffer[], keyIndexAndSchemeAndVerificationKey: Buffer[], thresholdAndRegIdAndIPIdentity: Buffer[], encIdCredPubShareAndKey: Buffer[], validToAndCreatedAtAndAttributesLength: Buffer[], attributesLength: Buffer[], tag: Buffer[][], valueLength: Buffer[][], value: Buffer[][], proofLength: Buffer[], proofs: Buffer[], credentialIdCount: Buffer, credentialIds: Buffer[], threshold: Buffer } => { let offset = 0; const txSerialized = serializeAccountTransaction(txn); const headerKindAndIndexLength = txSerialized.subarray(offset, offset + HEADER_LENGTH + TRANSACTION_KIND_LENGTH + INDEX_LENGTH); const payloadHeaderKindAndIndexLength = serializeTransactionPayloadsWithDerivationPath(path, headerKindAndIndexLength); offset += HEADER_LENGTH + TRANSACTION_KIND_LENGTH + INDEX_LENGTH; let credentialIndex: Buffer[] = []; let numberOfVerificationKeys: Buffer[] = []; let keyIndexAndSchemeAndVerificationKey: Buffer[] = []; let thresholdAndRegIdAndIPIdentity: Buffer[] = []; let encIdCredPubShareAndKey: Buffer[] = []; let validToAndCreatedAtAndAttributesLength: Buffer[] = []; let attributesLength: Buffer[] = []; let tag: Buffer[][] = [[]]; let valueLength: Buffer[][] = [[]]; let value: Buffer[][] = [[]]; let proofLength: Buffer[] = []; let proofs: Buffer[] = []; for (let i = 0; i < txn.payload.newCredentials.length; i++) { credentialIndex[i] = txSerialized.subarray(offset, offset + INDEX_LENGTH); offset += INDEX_LENGTH; numberOfVerificationKeys[i] = txSerialized.subarray(offset, offset + INDEX_LENGTH); offset += INDEX_LENGTH; keyIndexAndSchemeAndVerificationKey[i] = txSerialized.subarray(offset, offset + 2 * ONE_OCTET_LENGTH + KEY_LENGTH); offset += 2 * ONE_OCTET_LENGTH + KEY_LENGTH; thresholdAndRegIdAndIPIdentity[i] = txSerialized.subarray(offset, offset + 2 * ONE_OCTET_LENGTH + REG_ID_LENGTH + IP_IDENTITY_LENGTH + AR_DATA_LENGTH); offset += 2 * ONE_OCTET_LENGTH + REG_ID_LENGTH + IP_IDENTITY_LENGTH + AR_DATA_LENGTH; encIdCredPubShareAndKey[i] = txSerialized.subarray(offset, offset + 4 * ONE_OCTET_LENGTH + ID_CRED_PUB_SHARE_LENGTH); offset += 4 * ONE_OCTET_LENGTH + ID_CRED_PUB_SHARE_LENGTH; validToAndCreatedAtAndAttributesLength[i] = txSerialized.subarray(offset, offset + ATTRIBUTES_LENGTH + VALID_TO_LENGTH + CREATED_AT_LENGTH); offset += ATTRIBUTES_LENGTH + VALID_TO_LENGTH + CREATED_AT_LENGTH; attributesLength[i] = validToAndCreatedAtAndAttributesLength[i].subarray(-ATTRIBUTES_LENGTH); tag[i] = []; valueLength[i] = []; value[i] = []; for (let j = 0; j < attributesLength[i].readUInt16BE(0); j++) { tag[i].push(txSerialized.subarray(offset, offset + TAG_LENGTH)); offset += TAG_LENGTH; valueLength[i].push(txSerialized.subarray(offset, offset + VALUE_LENGTH)); offset += VALUE_LENGTH; value[i].push(txSerialized.subarray(offset, offset + valueLength[i][j].readUInt8(0))); offset += valueLength[i][j].readUInt8(0); } proofLength[i] = txSerialized.subarray(offset, offset + PROOF_LENGTH_LENGTH); offset += PROOF_LENGTH_LENGTH; proofs[i] = txSerialized.subarray(offset, offset + proofLength[i].readUInt32BE(0)); offset += proofLength[i].readUInt32BE(0); } const credentialIdCount = txSerialized.subarray(offset, offset + ONE_OCTET_LENGTH); offset += ONE_OCTET_LENGTH; const credentialIds: Buffer[] = []; for (let i = 0; i < credentialIdCount.readUInt8(0); i++) { credentialIds.push(txSerialized.subarray(offset, offset + CREDENTIAL_ID_LENGTH)); offset += CREDENTIAL_ID_LENGTH; } const threshold = txSerialized.subarray(offset, offset + ONE_OCTET_LENGTH); return { payloadHeaderKindAndIndexLength, credentialIndex, numberOfVerificationKeys, keyIndexAndSchemeAndVerificationKey, thresholdAndRegIdAndIPIdentity, encIdCredPubShareAndKey, validToAndCreatedAtAndAttributesLength, attributesLength, tag, valueLength, value, proofLength, proofs, credentialIdCount, credentialIds, threshold }; }; /** * Serializes public information for an IP transaction. * @param {IPublicInfoForIpTransaction} txn - The transaction to serialize. * @param {string} path - The BIP32 path as a string. * @returns {{ payloadIdCredPubAndRegIdAndKeysLenght: Buffer, payloadKeys: Buffer[], payloadThreshold: Buffer }} - An object containing serialized payloads. */ export const serializePublicInfoForIp = (txn: IPublicInfoForIpTransaction, path: string): { payloadIdCredPubAndRegIdAndKeysLenght: Buffer, payloadKeys: Buffer[], payloadThreshold: Buffer } => { const pathBuffer = pathToBuffer(path); const serializedIdCredPub = Buffer.from(txn.idCredPub, 'hex'); const serializedRegId = Buffer.from(txn.regId, 'hex'); const serializedPublicKeys = serializeMap(txn.publicKeys.keys, encodeWord8, encodeWord8FromString, serializeVerifyKey); const payloadThreshold = encodeInt8(txn.publicKeys.threshold); const payloadIdCredPubAndRegIdAndKeysLenght = Buffer.concat([pathBuffer, serializedIdCredPub, serializedRegId, serializedPublicKeys.subarray(0, 1)]); let payloadKeys: Buffer[] = []; for (let i = 0; i < Object.keys(txn.publicKeys.keys).length; i++) { payloadKeys.push(Buffer.concat([serializedPublicKeys.subarray(i * (KEY_LENGTH + 2 * ONE_OCTET_LENGTH) + 1, (i + 1) * (KEY_LENGTH + 2 * ONE_OCTET_LENGTH) + 1)])); } return { payloadIdCredPubAndRegIdAndKeysLenght, payloadKeys, payloadThreshold }; };