@bsv/sdk
Version:
BSV Blockchain Software Development Kit
1,163 lines • 59.4 kB
JavaScript
import { SecurityLevels } from '../Wallet.interfaces.js';
import Certificate from '../../auth/certificates/Certificate.js';
import * as Utils from '../../primitives/utils.js';
import calls from './WalletWireCalls.js';
import { WalletError } from '../WalletError.js';
/**
* A way to make remote calls to a wallet over a wallet wire.
*/
export default class WalletWireTransceiver {
wire;
constructor(wire) {
this.wire = wire;
}
async transmit(call, originator = '', params = []) {
const frameWriter = new Utils.Writer();
frameWriter.writeUInt8(calls[call]);
const originatorArray = Utils.toArray(originator, 'utf8');
frameWriter.writeUInt8(originatorArray.length);
frameWriter.write(originatorArray);
if (params.length > 0) {
frameWriter.write(params);
}
const frame = frameWriter.toArray();
const result = await this.wire.transmitToWallet(frame);
const resultReader = new Utils.Reader(result);
const errorByte = resultReader.readUInt8();
if (errorByte === 0) {
const resultFrame = resultReader.read();
return resultFrame;
}
else {
// Deserialize the error message length
const errorMessageLength = resultReader.readVarIntNum();
const errorMessageBytes = resultReader.read(errorMessageLength);
const errorMessage = Utils.toUTF8(errorMessageBytes);
// Deserialize the stack trace length
const stackTraceLength = resultReader.readVarIntNum();
const stackTraceBytes = resultReader.read(stackTraceLength);
const stackTrace = Utils.toUTF8(stackTraceBytes);
// Construct a custom wallet error
const e = new WalletError(errorMessage, errorByte, stackTrace);
throw e;
}
}
async createAction(args, originator) {
const paramWriter = new Utils.Writer();
// Serialize description
const descriptionBytes = Utils.toArray(args.description, 'utf8');
paramWriter.writeVarIntNum(descriptionBytes.length);
paramWriter.write(descriptionBytes);
// input BEEF
if (args.inputBEEF != null) {
paramWriter.writeVarIntNum(args.inputBEEF.length);
paramWriter.write(args.inputBEEF);
}
else {
paramWriter.writeVarIntNum(-1);
}
// Serialize inputs
if (args.inputs != null) {
paramWriter.writeVarIntNum(args.inputs.length);
for (const input of args.inputs) {
// outpoint
paramWriter.write(this.encodeOutpoint(input.outpoint));
// unlockingScript / unlockingScriptLength
if (input.unlockingScript != null && input.unlockingScript !== '') {
const unlockingScriptBytes = Utils.toArray(input.unlockingScript, 'hex');
paramWriter.writeVarIntNum(unlockingScriptBytes.length);
paramWriter.write(unlockingScriptBytes);
}
else {
paramWriter.writeVarIntNum(-1);
paramWriter.writeVarIntNum(input.unlockingScriptLength ?? 0);
}
// inputDescription
const inputDescriptionBytes = Utils.toArray(input.inputDescription, 'utf8');
paramWriter.writeVarIntNum(inputDescriptionBytes.length);
paramWriter.write(inputDescriptionBytes);
// sequenceNumber
if (typeof input.sequenceNumber === 'number') {
paramWriter.writeVarIntNum(input.sequenceNumber);
}
else {
paramWriter.writeVarIntNum(-1);
}
}
}
else {
paramWriter.writeVarIntNum(-1);
}
// Serialize outputs
if (args.outputs != null) {
paramWriter.writeVarIntNum(args.outputs.length);
for (const output of args.outputs) {
// lockingScript
const lockingScriptBytes = Utils.toArray(output.lockingScript, 'hex');
paramWriter.writeVarIntNum(lockingScriptBytes.length);
paramWriter.write(lockingScriptBytes);
// satoshis
paramWriter.writeVarIntNum(output.satoshis);
// outputDescription
const outputDescriptionBytes = Utils.toArray(output.outputDescription, 'utf8');
paramWriter.writeVarIntNum(outputDescriptionBytes.length);
paramWriter.write(outputDescriptionBytes);
// basket
if (output.basket != null && output.basket !== '') {
const basketBytes = Utils.toArray(output.basket, 'utf8');
paramWriter.writeVarIntNum(basketBytes.length);
paramWriter.write(basketBytes);
}
else {
paramWriter.writeVarIntNum(-1);
}
// customInstructions
if (output.customInstructions != null && output.customInstructions !== '') {
const customInstructionsBytes = Utils.toArray(output.customInstructions, 'utf8');
paramWriter.writeVarIntNum(customInstructionsBytes.length);
paramWriter.write(customInstructionsBytes);
}
else {
paramWriter.writeVarIntNum(-1);
}
// tags
if (output.tags != null) {
paramWriter.writeVarIntNum(output.tags.length);
for (const tag of output.tags) {
const tagBytes = Utils.toArray(tag, 'utf8');
paramWriter.writeVarIntNum(tagBytes.length);
paramWriter.write(tagBytes);
}
}
else {
paramWriter.writeVarIntNum(-1);
}
}
}
else {
paramWriter.writeVarIntNum(-1);
}
// Serialize lockTime
if (typeof args.lockTime === 'number') {
paramWriter.writeVarIntNum(args.lockTime);
}
else {
paramWriter.writeVarIntNum(-1);
}
// Serialize version
if (typeof args.version === 'number') {
paramWriter.writeVarIntNum(args.version);
}
else {
paramWriter.writeVarIntNum(-1);
}
// Serialize labels
if (args.labels != null) {
paramWriter.writeVarIntNum(args.labels.length);
for (const label of args.labels) {
const labelBytes = Utils.toArray(label, 'utf8');
paramWriter.writeVarIntNum(labelBytes.length);
paramWriter.write(labelBytes);
}
}
else {
paramWriter.writeVarIntNum(-1);
}
// Serialize options
if (args.options != null) {
paramWriter.writeInt8(1); // options present
// signAndProcess
if (typeof args.options.signAndProcess === 'boolean') {
paramWriter.writeInt8(args.options.signAndProcess ? 1 : 0);
}
else {
paramWriter.writeInt8(-1);
}
// acceptDelayedBroadcast
if (typeof args.options.acceptDelayedBroadcast === 'boolean') {
paramWriter.writeInt8(args.options.acceptDelayedBroadcast ? 1 : 0);
}
else {
paramWriter.writeInt8(-1);
}
// trustSelf
if (args.options.trustSelf === 'known') {
paramWriter.writeInt8(1);
}
else {
paramWriter.writeInt8(-1);
}
// knownTxids
if (args.options.knownTxids != null) {
paramWriter.writeVarIntNum(args.options.knownTxids.length);
for (const txid of args.options.knownTxids) {
const txidBytes = Utils.toArray(txid, 'hex');
paramWriter.write(txidBytes);
}
}
else {
paramWriter.writeVarIntNum(-1);
}
// returnTXIDOnly
if (typeof args.options.returnTXIDOnly === 'boolean') {
paramWriter.writeInt8(args.options.returnTXIDOnly ? 1 : 0);
}
else {
paramWriter.writeInt8(-1);
}
// noSend
if (typeof args.options.noSend === 'boolean') {
paramWriter.writeInt8(args.options.noSend ? 1 : 0);
}
else {
paramWriter.writeInt8(-1);
}
// noSendChange
if (args.options.noSendChange != null) {
paramWriter.writeVarIntNum(args.options.noSendChange.length);
for (const outpoint of args.options.noSendChange) {
paramWriter.write(this.encodeOutpoint(outpoint));
}
}
else {
paramWriter.writeVarIntNum(-1);
}
// sendWith
if (args.options.sendWith != null) {
paramWriter.writeVarIntNum(args.options.sendWith.length);
for (const txid of args.options.sendWith) {
const txidBytes = Utils.toArray(txid, 'hex');
paramWriter.write(txidBytes);
}
}
else {
paramWriter.writeVarIntNum(-1);
}
// randomizeOutputs
if (typeof args.options.randomizeOutputs === 'boolean') {
paramWriter.writeInt8(args.options.randomizeOutputs ? 1 : 0);
}
else {
paramWriter.writeInt8(-1);
}
}
else {
paramWriter.writeInt8(0); // options not present
}
// Transmit and parse response
const result = await this.transmit('createAction', originator, paramWriter.toArray());
const resultReader = new Utils.Reader(result);
const response = {};
// Parse txid
const txidFlag = resultReader.readInt8();
if (txidFlag === 1) {
const txidBytes = resultReader.read(32);
response.txid = Utils.toHex(txidBytes);
}
// Parse tx
const txFlag = resultReader.readInt8();
if (txFlag === 1) {
const txLength = resultReader.readVarIntNum();
response.tx = resultReader.read(txLength);
}
// Parse noSendChange
const noSendChangeLength = resultReader.readVarIntNum();
if (noSendChangeLength >= 0) {
response.noSendChange = [];
for (let i = 0; i < noSendChangeLength; i++) {
const outpoint = this.readOutpoint(resultReader);
response.noSendChange.push(outpoint);
}
}
// Parse sendWithResults
const sendWithResultsLength = resultReader.readVarIntNum();
if (sendWithResultsLength >= 0) {
response.sendWithResults = [];
for (let i = 0; i < sendWithResultsLength; i++) {
const txidBytes = resultReader.read(32);
const txid = Utils.toHex(txidBytes);
const statusCode = resultReader.readInt8();
let status = 'unproven';
if (statusCode === 1)
status = 'unproven';
else if (statusCode === 2)
status = 'sending';
else if (statusCode === 3)
status = 'failed';
response.sendWithResults.push({ txid, status });
}
}
// Parse signableTransaction
const signableTransactionFlag = resultReader.readInt8();
if (signableTransactionFlag === 1) {
const txLength = resultReader.readVarIntNum();
const tx = resultReader.read(txLength);
const referenceLength = resultReader.readVarIntNum();
const referenceBytes = resultReader.read(referenceLength);
response.signableTransaction = {
tx,
reference: Utils.toBase64(referenceBytes)
};
}
return response;
}
async signAction(args, originator) {
const paramWriter = new Utils.Writer();
// Serialize spends
const spendIndexes = Object.keys(args.spends);
paramWriter.writeVarIntNum(spendIndexes.length);
for (const index of spendIndexes) {
paramWriter.writeVarIntNum(Number(index));
const spend = args.spends[Number(index)];
// unlockingScript
const unlockingScriptBytes = Utils.toArray(spend.unlockingScript, 'hex');
paramWriter.writeVarIntNum(unlockingScriptBytes.length);
paramWriter.write(unlockingScriptBytes);
// sequenceNumber
if (typeof spend.sequenceNumber === 'number') {
paramWriter.writeVarIntNum(spend.sequenceNumber);
}
else {
paramWriter.writeVarIntNum(-1);
}
}
// Serialize reference
const referenceBytes = Utils.toArray(args.reference, 'base64');
paramWriter.writeVarIntNum(referenceBytes.length);
paramWriter.write(referenceBytes);
// Serialize options
if (args.options != null) {
paramWriter.writeInt8(1); // options present
// acceptDelayedBroadcast
if (typeof args.options.acceptDelayedBroadcast === 'boolean') {
paramWriter.writeInt8(args.options.acceptDelayedBroadcast ? 1 : 0);
}
else {
paramWriter.writeInt8(-1);
}
// returnTXIDOnly
if (typeof args.options.returnTXIDOnly === 'boolean') {
paramWriter.writeInt8(args.options.returnTXIDOnly ? 1 : 0);
}
else {
paramWriter.writeInt8(-1);
}
// noSend
if (typeof args.options.noSend === 'boolean') {
paramWriter.writeInt8(args.options.noSend ? 1 : 0);
}
else {
paramWriter.writeInt8(-1);
}
// sendWith
if (args.options.sendWith != null) {
paramWriter.writeVarIntNum(args.options.sendWith.length);
for (const txid of args.options.sendWith) {
const txidBytes = Utils.toArray(txid, 'hex');
paramWriter.write(txidBytes);
}
}
else {
paramWriter.writeVarIntNum(-1);
}
}
else {
paramWriter.writeInt8(0); // options not present
}
// Transmit and parse response
const result = await this.transmit('signAction', originator, paramWriter.toArray());
const resultReader = new Utils.Reader(result);
const response = {};
// Parse txid
const txidFlag = resultReader.readInt8();
if (txidFlag === 1) {
const txidBytes = resultReader.read(32);
response.txid = Utils.toHex(txidBytes);
}
// Parse tx
const txFlag = resultReader.readInt8();
if (txFlag === 1) {
const txLength = resultReader.readVarIntNum();
response.tx = resultReader.read(txLength);
}
// Parse sendWithResults
const sendWithResultsLength = resultReader.readVarIntNum();
if (sendWithResultsLength >= 0) {
response.sendWithResults = [];
for (let i = 0; i < sendWithResultsLength; i++) {
const txidBytes = resultReader.read(32);
const txid = Utils.toHex(txidBytes);
const statusCode = resultReader.readInt8();
let status = 'unproven';
if (statusCode === 1)
status = 'unproven';
else if (statusCode === 2)
status = 'sending';
else if (statusCode === 3)
status = 'failed';
response.sendWithResults.push({ txid, status });
}
}
return response;
}
async abortAction(args, originator) {
await this.transmit('abortAction', originator, Utils.toArray(args.reference, 'base64'));
return { aborted: true };
}
async listActions(args, originator) {
const paramWriter = new Utils.Writer();
// Serialize labels
paramWriter.writeVarIntNum(args.labels.length);
for (const label of args.labels) {
const labelBytes = Utils.toArray(label, 'utf8');
paramWriter.writeVarIntNum(labelBytes.length);
paramWriter.write(labelBytes);
}
// Serialize labelQueryMode
if (args.labelQueryMode === 'any') {
paramWriter.writeInt8(1);
}
else if (args.labelQueryMode === 'all') {
paramWriter.writeInt8(2);
}
else {
paramWriter.writeInt8(-1);
}
// Serialize include options
const includeOptions = [
args.includeLabels,
args.includeInputs,
args.includeInputSourceLockingScripts,
args.includeInputUnlockingScripts,
args.includeOutputs,
args.includeOutputLockingScripts
];
for (const option of includeOptions) {
if (typeof option === 'boolean') {
paramWriter.writeInt8(option ? 1 : 0);
}
else {
paramWriter.writeInt8(-1);
}
}
// Serialize limit and offset
if (typeof args.limit === 'number') {
paramWriter.writeVarIntNum(args.limit);
}
else {
paramWriter.writeVarIntNum(-1);
}
if (typeof args.offset === 'number') {
paramWriter.writeVarIntNum(args.offset);
}
else {
paramWriter.writeVarIntNum(-1);
}
// Serialize seekPermission
paramWriter.writeInt8(typeof args.seekPermission === 'boolean'
? args.seekPermission
? 1
: 0
: -1);
// Transmit and parse response
const result = await this.transmit('listActions', originator, paramWriter.toArray());
const resultReader = new Utils.Reader(result);
const totalActions = resultReader.readVarIntNum();
const actions = [];
for (let i = 0; i < totalActions; i++) {
// Parse action fields
const txidBytes = resultReader.read(32);
const txid = Utils.toHex(txidBytes);
const satoshis = resultReader.readVarIntNum();
const statusCode = resultReader.readInt8();
let status;
switch (statusCode) {
case 1:
status = 'completed';
break;
case 2:
status = 'unprocessed';
break;
case 3:
status = 'sending';
break;
case 4:
status = 'unproven';
break;
case 5:
status = 'unsigned';
break;
case 6:
status = 'nosend';
break;
case 7:
status = 'nonfinal';
break;
case 8:
status = 'failed';
break;
default:
throw new Error(`Unknown status code: ${statusCode}`);
}
const isOutgoing = resultReader.readInt8() === 1;
const descriptionLength = resultReader.readVarIntNum();
const descriptionBytes = resultReader.read(descriptionLength);
const description = Utils.toUTF8(descriptionBytes);
const action = {
txid,
satoshis,
status,
isOutgoing,
description,
version: 0,
lockTime: 0
};
// Parse labels
const labelsLength = resultReader.readVarIntNum();
if (labelsLength >= 0) {
action.labels = [];
for (let j = 0; j < labelsLength; j++) {
const labelLength = resultReader.readVarIntNum();
const labelBytes = resultReader.read(labelLength);
action.labels.push(Utils.toUTF8(labelBytes));
}
}
// Parse version and lockTime
action.version = resultReader.readVarIntNum();
action.lockTime = resultReader.readVarIntNum();
// Parse inputs
const inputsLength = resultReader.readVarIntNum();
if (inputsLength >= 0) {
action.inputs = [];
for (let k = 0; k < inputsLength; k++) {
const sourceOutpoint = this.readOutpoint(resultReader);
const sourceSatoshis = resultReader.readVarIntNum();
// sourceLockingScript
const sourceLockingScriptLength = resultReader.readVarIntNum();
let sourceLockingScript;
if (sourceLockingScriptLength >= 0) {
const sourceLockingScriptBytes = resultReader.read(sourceLockingScriptLength);
sourceLockingScript = Utils.toHex(sourceLockingScriptBytes);
}
// unlockingScript
const unlockingScriptLength = resultReader.readVarIntNum();
let unlockingScript;
if (unlockingScriptLength >= 0) {
const unlockingScriptBytes = resultReader.read(unlockingScriptLength);
unlockingScript = Utils.toHex(unlockingScriptBytes);
}
// inputDescription
const inputDescriptionLength = resultReader.readVarIntNum();
const inputDescriptionBytes = resultReader.read(inputDescriptionLength);
const inputDescription = Utils.toUTF8(inputDescriptionBytes);
// sequenceNumber
const sequenceNumber = resultReader.readVarIntNum();
action.inputs.push({
sourceOutpoint,
sourceSatoshis,
sourceLockingScript,
unlockingScript,
inputDescription,
sequenceNumber
});
}
}
// Parse outputs
const outputsLength = resultReader.readVarIntNum();
if (outputsLength >= 0) {
action.outputs = [];
for (let l = 0; l < outputsLength; l++) {
const outputIndex = resultReader.readVarIntNum();
const satoshis = resultReader.readVarIntNum();
// lockingScript
const lockingScriptLength = resultReader.readVarIntNum();
let lockingScript;
if (lockingScriptLength >= 0) {
const lockingScriptBytes = resultReader.read(lockingScriptLength);
lockingScript = Utils.toHex(lockingScriptBytes);
}
const spendable = resultReader.readInt8() === 1;
// outputDescription
const outputDescriptionLength = resultReader.readVarIntNum();
const outputDescriptionBytes = resultReader.read(outputDescriptionLength);
const outputDescription = Utils.toUTF8(outputDescriptionBytes);
// basket
const basketLength = resultReader.readVarIntNum();
let basket;
if (basketLength >= 0) {
const basketBytes = resultReader.read(basketLength);
basket = Utils.toUTF8(basketBytes);
}
// tags
const tagsLength = resultReader.readVarIntNum();
const tags = [];
if (tagsLength >= 0) {
for (let m = 0; m < tagsLength; m++) {
const tagLength = resultReader.readVarIntNum();
const tagBytes = resultReader.read(tagLength);
tags.push(Utils.toUTF8(tagBytes));
}
}
// customInstructions
const customInstructionsLength = resultReader.readVarIntNum();
let customInstructions;
if (customInstructionsLength >= 0) {
const customInstructionsBytes = resultReader.read(customInstructionsLength);
customInstructions = Utils.toUTF8(customInstructionsBytes);
}
action.outputs.push({
outputIndex,
satoshis,
lockingScript,
spendable,
outputDescription,
basket,
tags,
customInstructions
});
}
}
actions.push(action);
}
return {
totalActions,
actions
};
}
async internalizeAction(args, originator) {
const paramWriter = new Utils.Writer();
paramWriter.writeVarIntNum(args.tx.length);
paramWriter.write(args.tx);
paramWriter.writeVarIntNum(args.outputs.length);
for (const out of args.outputs) {
paramWriter.writeVarIntNum(out.outputIndex);
if (out.protocol === 'wallet payment') {
if (out.paymentRemittance == null) {
throw new Error('Payment remittance is required for wallet payment');
}
paramWriter.writeUInt8(1);
paramWriter.write(Utils.toArray(out.paymentRemittance.senderIdentityKey, 'hex'));
const derivationPrefixAsArray = Utils.toArray(out.paymentRemittance.derivationPrefix, 'base64');
paramWriter.writeVarIntNum(derivationPrefixAsArray.length);
paramWriter.write(derivationPrefixAsArray);
const derivationSuffixAsArray = Utils.toArray(out.paymentRemittance.derivationSuffix, 'base64');
paramWriter.writeVarIntNum(derivationSuffixAsArray.length);
paramWriter.write(derivationSuffixAsArray);
}
else {
paramWriter.writeUInt8(2);
const basketAsArray = Utils.toArray(out.insertionRemittance?.basket, 'utf8');
paramWriter.writeVarIntNum(basketAsArray.length);
paramWriter.write(basketAsArray);
if (typeof out.insertionRemittance?.customInstructions === 'string' && out.insertionRemittance.customInstructions !== '') {
const customInstructionsAsArray = Utils.toArray(out.insertionRemittance.customInstructions, 'utf8');
paramWriter.writeVarIntNum(customInstructionsAsArray.length);
paramWriter.write(customInstructionsAsArray);
}
else {
paramWriter.writeVarIntNum(-1);
}
if (typeof out.insertionRemittance?.tags === 'object') {
paramWriter.writeVarIntNum(out.insertionRemittance.tags.length);
for (const tag of out.insertionRemittance.tags) {
const tagAsArray = Utils.toArray(tag, 'utf8');
paramWriter.writeVarIntNum(tagAsArray.length);
paramWriter.write(tagAsArray);
}
}
else {
paramWriter.writeVarIntNum(0);
}
}
}
if (typeof args.labels === 'object') {
paramWriter.writeVarIntNum(args.labels.length);
for (const l of args.labels) {
const labelAsArray = Utils.toArray(l, 'utf8');
paramWriter.writeVarIntNum(labelAsArray.length);
paramWriter.write(labelAsArray);
}
}
else {
paramWriter.writeVarIntNum(-1);
}
const descriptionAsArray = Utils.toArray(args.description);
paramWriter.writeVarIntNum(descriptionAsArray.length);
paramWriter.write(descriptionAsArray);
// Serialize seekPermission
paramWriter.writeInt8(typeof args.seekPermission === 'boolean'
? args.seekPermission
? 1
: 0
: -1);
await this.transmit('internalizeAction', originator, paramWriter.toArray());
return { accepted: true };
}
async listOutputs(args, originator) {
const paramWriter = new Utils.Writer();
const basketAsArray = Utils.toArray(args.basket, 'utf8');
paramWriter.writeVarIntNum(basketAsArray.length);
paramWriter.write(basketAsArray);
if (typeof args.tags === 'object') {
paramWriter.writeVarIntNum(args.tags.length);
for (const tag of args.tags) {
const tagAsArray = Utils.toArray(tag, 'utf8');
paramWriter.writeVarIntNum(tagAsArray.length);
paramWriter.write(tagAsArray);
}
}
else {
paramWriter.writeVarIntNum(0);
}
if (args.tagQueryMode === 'all') {
paramWriter.writeInt8(1);
}
else if (args.tagQueryMode === 'any') {
paramWriter.writeInt8(2);
}
else {
paramWriter.writeInt8(-1);
}
if (args.include === 'locking scripts') {
paramWriter.writeInt8(1);
}
else if (args.include === 'entire transactions') {
paramWriter.writeInt8(2);
}
else {
paramWriter.writeInt8(-1);
}
if (typeof args.includeCustomInstructions === 'boolean') {
paramWriter.writeInt8(args.includeCustomInstructions ? 1 : 0);
}
else {
paramWriter.writeInt8(-1);
}
if (typeof args.includeTags === 'boolean') {
paramWriter.writeInt8(args.includeTags ? 1 : 0);
}
else {
paramWriter.writeInt8(-1);
}
if (typeof args.includeLabels === 'boolean') {
paramWriter.writeInt8(args.includeLabels ? 1 : 0);
}
else {
paramWriter.writeInt8(-1);
}
if (typeof args.limit === 'number') {
paramWriter.writeVarIntNum(args.limit);
}
else {
paramWriter.writeVarIntNum(-1);
}
if (typeof args.offset === 'number') {
paramWriter.writeVarIntNum(args.offset);
}
else {
paramWriter.writeVarIntNum(-1);
}
// Serialize seekPermission
paramWriter.writeInt8(typeof args.seekPermission === 'boolean'
? args.seekPermission
? 1
: 0
: -1);
const result = await this.transmit('listOutputs', originator, paramWriter.toArray());
const resultReader = new Utils.Reader(result);
const totalOutputs = resultReader.readVarIntNum();
const beefLength = resultReader.readVarIntNum();
let BEEF;
if (beefLength >= 0) {
BEEF = resultReader.read(beefLength);
}
const outputs = [];
for (let i = 0; i < totalOutputs; i++) {
const outpoint = this.readOutpoint(resultReader);
const satoshis = resultReader.readVarIntNum();
const output = {
spendable: true,
outpoint,
satoshis
};
const scriptLength = resultReader.readVarIntNum();
if (scriptLength >= 0) {
output.lockingScript = Utils.toHex(resultReader.read(scriptLength));
}
const customInstructionsLength = resultReader.readVarIntNum();
if (customInstructionsLength >= 0) {
output.customInstructions = Utils.toUTF8(resultReader.read(customInstructionsLength));
}
const tagsLength = resultReader.readVarIntNum();
if (tagsLength !== -1) {
const tags = [];
for (let i = 0; i < tagsLength; i++) {
const tagLength = resultReader.readVarIntNum();
tags.push(Utils.toUTF8(resultReader.read(tagLength)));
}
output.tags = tags;
}
const labelsLength = resultReader.readVarIntNum();
if (labelsLength !== -1) {
const labels = [];
for (let i = 0; i < labelsLength; i++) {
const labelLength = resultReader.readVarIntNum();
labels.push(Utils.toUTF8(resultReader.read(labelLength)));
}
output.labels = labels;
}
outputs.push(output);
}
return {
totalOutputs,
BEEF,
outputs
};
}
async relinquishOutput(args, originator) {
const paramWriter = new Utils.Writer();
const basketAsArray = Utils.toArray(args.basket, 'utf8');
paramWriter.writeVarIntNum(basketAsArray.length);
paramWriter.write(basketAsArray);
paramWriter.write(this.encodeOutpoint(args.output));
await this.transmit('relinquishOutput', originator, paramWriter.toArray());
return { relinquished: true };
}
encodeOutpoint(outpoint) {
const writer = new Utils.Writer();
const [txid, index] = outpoint.split('.');
writer.write(Utils.toArray(txid, 'hex'));
writer.writeVarIntNum(Number(index));
return writer.toArray();
}
readOutpoint(reader) {
const txid = Utils.toHex(reader.read(32));
const index = reader.readVarIntNum();
return `${txid}.${index}`;
}
async getPublicKey(args, originator) {
const paramWriter = new Utils.Writer();
paramWriter.writeUInt8(args.identityKey ? 1 : 0);
if (!args.identityKey) {
paramWriter.write(this.encodeKeyRelatedParams(args.protocolID ??= [SecurityLevels.Silent, 'default'], args.keyID ??= '', args.counterparty, args.privileged, args.privilegedReason));
if (typeof args.forSelf === 'boolean') {
paramWriter.writeInt8(args.forSelf ? 1 : 0);
}
else {
paramWriter.writeInt8(-1);
}
}
else {
paramWriter.write(this.encodePrivilegedParams(args.privileged, args.privilegedReason));
}
// Serialize seekPermission
paramWriter.writeInt8(typeof args.seekPermission === 'boolean'
? args.seekPermission
? 1
: 0
: -1);
const result = await this.transmit('getPublicKey', originator, paramWriter.toArray());
return {
publicKey: Utils.toHex(result)
};
}
async revealCounterpartyKeyLinkage(args, originator) {
const paramWriter = new Utils.Writer();
paramWriter.write(this.encodePrivilegedParams(args.privileged, args.privilegedReason));
paramWriter.write(Utils.toArray(args.counterparty, 'hex'));
paramWriter.write(Utils.toArray(args.verifier, 'hex'));
const result = await this.transmit('revealCounterpartyKeyLinkage', originator, paramWriter.toArray());
const resultReader = new Utils.Reader(result);
const prover = Utils.toHex(resultReader.read(33));
const verifier = Utils.toHex(resultReader.read(33));
const counterparty = Utils.toHex(resultReader.read(33));
const revelationTimeLength = resultReader.readVarIntNum();
const revelationTime = Utils.toUTF8(resultReader.read(revelationTimeLength));
const encryptedLinkageLength = resultReader.readVarIntNum();
const encryptedLinkage = resultReader.read(encryptedLinkageLength);
const encryptedLinkageProofLength = resultReader.readVarIntNum();
const encryptedLinkageProof = resultReader.read(encryptedLinkageProofLength);
return {
prover,
verifier,
counterparty,
revelationTime,
encryptedLinkage,
encryptedLinkageProof
};
}
async revealSpecificKeyLinkage(args, originator) {
const paramWriter = new Utils.Writer();
paramWriter.write(this.encodeKeyRelatedParams(args.protocolID, args.keyID, args.counterparty, args.privileged, args.privilegedReason));
paramWriter.write(Utils.toArray(args.verifier, 'hex'));
const result = await this.transmit('revealSpecificKeyLinkage', originator, paramWriter.toArray());
const resultReader = new Utils.Reader(result);
const prover = Utils.toHex(resultReader.read(33));
const verifier = Utils.toHex(resultReader.read(33));
const counterparty = Utils.toHex(resultReader.read(33));
const securityLevel = resultReader.readUInt8();
const protocolLength = resultReader.readVarIntNum();
const protocol = Utils.toUTF8(resultReader.read(protocolLength));
const keyIDLength = resultReader.readVarIntNum();
const keyID = Utils.toUTF8(resultReader.read(keyIDLength));
const encryptedLinkageLength = resultReader.readVarIntNum();
const encryptedLinkage = resultReader.read(encryptedLinkageLength);
const encryptedLinkageProofLength = resultReader.readVarIntNum();
const encryptedLinkageProof = resultReader.read(encryptedLinkageProofLength);
const proofType = resultReader.readUInt8();
return {
prover,
verifier,
counterparty,
protocolID: [securityLevel, protocol],
keyID,
encryptedLinkage,
encryptedLinkageProof,
proofType
};
}
async encrypt(args, originator) {
const paramWriter = new Utils.Writer();
paramWriter.write(this.encodeKeyRelatedParams(args.protocolID, args.keyID, args.counterparty, args.privileged, args.privilegedReason));
paramWriter.writeVarIntNum(args.plaintext.length);
paramWriter.write(args.plaintext);
// Serialize seekPermission
paramWriter.writeInt8(typeof args.seekPermission === 'boolean'
? args.seekPermission
? 1
: 0
: -1);
return {
ciphertext: await this.transmit('encrypt', originator, paramWriter.toArray())
};
}
async decrypt(args, originator) {
const paramWriter = new Utils.Writer();
paramWriter.write(this.encodeKeyRelatedParams(args.protocolID, args.keyID, args.counterparty, args.privileged, args.privilegedReason));
paramWriter.writeVarIntNum(args.ciphertext.length);
paramWriter.write(args.ciphertext);
// Serialize seekPermission
paramWriter.writeInt8(typeof args.seekPermission === 'boolean'
? args.seekPermission
? 1
: 0
: -1);
return {
plaintext: await this.transmit('decrypt', originator, paramWriter.toArray())
};
}
async createHmac(args, originator) {
const paramWriter = new Utils.Writer();
paramWriter.write(this.encodeKeyRelatedParams(args.protocolID, args.keyID, args.counterparty, args.privileged, args.privilegedReason));
paramWriter.writeVarIntNum(args.data.length);
paramWriter.write(args.data);
// Serialize seekPermission
paramWriter.writeInt8(typeof args.seekPermission === 'boolean'
? args.seekPermission
? 1
: 0
: -1);
return {
hmac: await this.transmit('createHmac', originator, paramWriter.toArray())
};
}
async verifyHmac(args, originator) {
const paramWriter = new Utils.Writer();
paramWriter.write(this.encodeKeyRelatedParams(args.protocolID, args.keyID, args.counterparty, args.privileged, args.privilegedReason));
paramWriter.write(args.hmac);
paramWriter.writeVarIntNum(args.data.length);
paramWriter.write(args.data);
// Serialize seekPermission
paramWriter.writeInt8(typeof args.seekPermission === 'boolean'
? args.seekPermission
? 1
: 0
: -1);
await this.transmit('verifyHmac', originator, paramWriter.toArray());
return { valid: true };
}
async createSignature(args, originator) {
const paramWriter = new Utils.Writer();
paramWriter.write(this.encodeKeyRelatedParams(args.protocolID, args.keyID, args.counterparty, args.privileged, args.privilegedReason));
if (typeof args.data === 'object') {
paramWriter.writeUInt8(1);
paramWriter.writeVarIntNum(args.data.length);
paramWriter.write(args.data);
}
else {
paramWriter.writeUInt8(2);
paramWriter.write(args.hashToDirectlySign ??= []);
}
// Serialize seekPermission
paramWriter.writeInt8(typeof args.seekPermission === 'boolean'
? args.seekPermission
? 1
: 0
: -1);
return {
signature: await this.transmit('createSignature', originator, paramWriter.toArray())
};
}
async verifySignature(args, originator) {
const paramWriter = new Utils.Writer();
paramWriter.write(this.encodeKeyRelatedParams(args.protocolID, args.keyID, args.counterparty, args.privileged, args.privilegedReason));
if (typeof args.forSelf === 'boolean') {
paramWriter.writeInt8(args.forSelf ? 1 : 0);
}
else {
paramWriter.writeInt8(-1);
}
paramWriter.writeVarIntNum(args.signature.length);
paramWriter.write(args.signature);
if (typeof args.data === 'object') {
paramWriter.writeUInt8(1);
paramWriter.writeVarIntNum(args.data.length);
paramWriter.write(args.data);
}
else {
paramWriter.writeUInt8(2);
paramWriter.write(args.hashToDirectlyVerify ?? []);
}
// Serialize seekPermission
paramWriter.writeInt8(typeof args.seekPermission === 'boolean'
? args.seekPermission
? 1
: 0
: -1);
await this.transmit('verifySignature', originator, paramWriter.toArray());
return { valid: true };
}
encodeKeyRelatedParams(protocolID, keyID, counterparty, privileged, privilegedReason) {
const paramWriter = new Utils.Writer();
paramWriter.writeUInt8(protocolID[0]);
const protocolAsArray = Utils.toArray(protocolID[1], 'utf8');
paramWriter.writeVarIntNum(protocolAsArray.length);
paramWriter.write(protocolAsArray);
const keyIDAsArray = Utils.toArray(keyID, 'utf8');
paramWriter.writeVarIntNum(keyIDAsArray.length);
paramWriter.write(keyIDAsArray);
if (typeof counterparty !== 'string') {
paramWriter.writeUInt8(0);
}
else if (counterparty === 'self') {
paramWriter.writeUInt8(11);
}
else if (counterparty === 'anyone') {
paramWriter.writeUInt8(12);
}
else {
paramWriter.write(Utils.toArray(counterparty, 'hex'));
}
paramWriter.write(this.encodePrivilegedParams(privileged, privilegedReason));
return paramWriter.toArray();
}
async acquireCertificate(args, originator) {
const paramWriter = new Utils.Writer();
paramWriter.write(Utils.toArray(args.type, 'base64'));
paramWriter.write(Utils.toArray(args.certifier, 'hex'));
const fieldEntries = Object.entries(args.fields);
paramWriter.writeVarIntNum(fieldEntries.length);
for (const [key, value] of fieldEntries) {
const keyAsArray = Utils.toArray(key, 'utf8');
const valueAsArray = Utils.toArray(value, 'utf8');
paramWriter.writeVarIntNum(keyAsArray.length);
paramWriter.write(keyAsArray);
paramWriter.writeVarIntNum(valueAsArray.length);
paramWriter.write(valueAsArray);
}
paramWriter.write(this.encodePrivilegedParams(args.privileged, args.privilegedReason));
paramWriter.writeUInt8(args.acquisitionProtocol === 'direct' ? 1 : 2);
if (args.acquisitionProtocol === 'direct') {
paramWriter.write(Utils.toArray(args.serialNumber, 'base64'));
paramWriter.write(this.encodeOutpoint(args.revocationOutpoint ?? ''));
const signatureAsArray = Utils.toArray(args.signature, 'hex');
paramWriter.writeVarIntNum(signatureAsArray.length);
paramWriter.write(signatureAsArray);
const keyringRevealerAsArray = args.keyringRevealer !== 'certifier'
? Utils.toArray(args.keyringRevealer, 'hex')
: [11];
paramWriter.write(keyringRevealerAsArray);
const keyringKeys = Object.keys(args.keyringForSubject ?? {});
paramWriter.writeVarIntNum(keyringKeys.length);
for (let i = 0; i < keyringKeys.length; i++) {
const keyringKeysAsArray = Utils.toArray(keyringKeys[i], 'utf8');
paramWriter.writeVarIntNum(keyringKeysAsArray.length);
paramWriter.write(keyringKeysAsArray);
const keyringForSubjectAsArray = Utils.toArray(args.keyringForSubject?.[keyringKeys[i]], 'base64');
paramWriter.writeVarIntNum(keyringForSubjectAsArray.length);
paramWriter.write(keyringForSubjectAsArray);
}
}
else {
const certifierUrlAsArray = Utils.toArray(args.certifierUrl, 'utf8');
paramWriter.writeVarIntNum(certifierUrlAsArray.length);
paramWriter.write(certifierUrlAsArray);
}
const result = await this.transmit('acquireCertificate', originator, paramWriter.toArray());
const cert = Certificate.fromBinary(result);
return {
...cert,
signature: cert.signature
};
}
encodePrivilegedParams(privileged, privilegedReason) {
const paramWriter = new Utils.Writer();
if (typeof privileged === 'boolean') {
paramWriter.writeInt8(privileged ? 1 : 0);
}
else {
paramWriter.writeInt8(-1);
}
if (typeof privilegedReason === 'string') {
const privilegedReasonAsArray = Utils.toArray(privilegedReason, 'utf8');
paramWriter.writeInt8(privilegedReasonAsArray.length);
paramWriter.write(privilegedReasonAsArray);
}
else {
paramWriter.writeInt8(-1);
}
return paramWriter.toArray();
}
async listCertificates(args, originator) {
const paramWriter = new Utils.Writer();
paramWriter.writeVarIntNum(args.certifiers.length);
for (let i = 0; i < args.certifiers.length; i++) {
paramWriter.write(Utils.toArray(args.certifiers[i], 'hex'));
}
paramWriter.writeVarIntNum(args.types.length);
for (let i = 0; i < args.types.length; i++) {
paramWriter.write(Utils.toArray(args.types[i], 'base64'));
}
if (typeof args.limit === 'number') {
paramWriter.writeVarIntNum(args.limit);
}
else {
paramWriter.writeVarIntNum(-1);
}
if (typeof args.offset === 'number') {
paramWriter.writeVarIntNum(args.offset);
}
else {
paramWriter.writeVarIntNum(-1);
}
paramWriter.write(this.encodePrivilegedParams(args.privileged, args.privilegedReason));
const result = await this.transmit('listCertificates', originator, paramWriter.toArray());
const resultReader = new Utils.Reader(result);
const totalCertificates = resultReader.readVarIntNum();
const certificates = [];
for (let i = 0; i < totalCertificates; i++) {
const certificateLength = resultReader.readVarIntNum();
const certificateBin = resultReader.read(certificateLength);
const cert = Certificate.fromBinary(certificateBin);
const keyringForVerifier = {};
if (resultReader.readInt8() === 1) {
const numFields = resultReader.readVarIntNum();
for (let i = 0; i < numFields; i++) {
const fieldKeyLength = resultReader.readVarIntNum();
const fieldKey = Utils.toUTF8(resultReader.read(fieldKeyLength));
const fieldValueLength = resultReader.readVarIntNum();
keyringForVerifier[fieldKey] = Utils.toBase64(resultReader.read(fieldValueLength));
}
}
const verifierLength = resultReader.readVarIntNum();
let v