@blooo/hw-app-concordium
Version:
Ledger Hardware Wallet Concordium Application API
662 lines (578 loc) • 35.5 kB
text/typescript
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 };
};