@c4tplatform/caminojs
Version:
Camino Platform JS Library
425 lines • 66.7 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UTXOSet = exports.AssetAmountDestination = exports.UTXO = void 0;
/**
* @packageDocumentation
* @module API-PlatformVM-UTXOs
*/
const buffer_1 = require("buffer/");
const bintools_1 = __importDefault(require("../../utils/bintools"));
const bn_js_1 = __importDefault(require("bn.js"));
const outputs_1 = require("./outputs");
const inputs_1 = require("./inputs");
const helperfunctions_1 = require("../../utils/helperfunctions");
const utxos_1 = require("../../common/utxos");
const constants_1 = require("./constants");
const assetamount_1 = require("../../common/assetamount");
const serialization_1 = require("../../utils/serialization");
const errors_1 = require("../../utils/errors");
/**
* @ignore
*/
const bintools = bintools_1.default.getInstance();
const serialization = serialization_1.Serialization.getInstance();
const zeroBN = new bn_js_1.default(0);
/**
* Class for representing a single UTXO.
*/
class UTXO extends utxos_1.StandardUTXO {
constructor() {
super(...arguments);
this._typeName = "UTXO";
this._typeID = undefined;
}
//serialize is inherited
deserialize(fields, encoding = "hex") {
super.deserialize(fields, encoding);
this.output = (0, outputs_1.SelectOutputClass)(fields["output"]["_typeID"]);
this.output.deserialize(fields["output"], encoding);
}
fromBuffer(bytes, offset = 0) {
this.codecID = bintools.copyFrom(bytes, offset, offset + 2);
offset += 2;
this.txid = bintools.copyFrom(bytes, offset, offset + 32);
offset += 32;
this.outputidx = bintools.copyFrom(bytes, offset, offset + 4);
offset += 4;
this.assetID = bintools.copyFrom(bytes, offset, offset + 32);
offset += 32;
const outputid = bintools
.copyFrom(bytes, offset, offset + 4)
.readUInt32BE(0);
offset += 4;
this.output = (0, outputs_1.SelectOutputClass)(outputid);
return this.output.fromBuffer(bytes, offset);
}
/**
* Takes a base-58 or hex string containing a [[UTXO]], parses it, populates the class, and returns the length of the StandardUTXO in bytes.
*
* @param serialized A base-58 string containing a raw [[UTXO]]
* @param format The format of the encoded [[UTXO]] (cb58 or hex). Defaults to cb58 per existing codebase
*
* @returns The length of the raw [[UTXO]]
*
* @remarks
* Default encoding format is cb58, if providing hex encoded string please specify format as 'hex'
*/
fromString(serialized, format = "cb58") {
switch (format) {
case "cb58": {
/* istanbul ignore next */
return this.fromBuffer(bintools.cb58Decode(serialized));
}
case "hex": {
let decoded = serialization.decoder(serialized, "hex", "hex", "cb58");
this.fromString(decoded);
return this.toBuffer().length;
}
default: {
throw new errors_1.UnknownFormatError(`Specified format '${format}' is unknown, should be hex or cb58.`);
}
}
}
/**
* Returns a base-58 representation of the [[UTXO]].
*
* @param format The format of the encoded [[UTXO]] (cb58 or hex). Defaults to cb58 per existing codebase
*
* @remarks
* Default encoding format to cb58, if you want a hex encoded output please specify format as 'hex'
*/
toString(format = "cb58") {
switch (format) {
case "cb58": {
/* istanbul ignore next */
return bintools.cb58Encode(this.toBuffer());
}
case "hex": {
return serialization.encoder(bintools.cb58Encode(this.toBuffer()), "hex", "cb58", "hex");
}
default: {
throw new errors_1.UnknownFormatError(`Specified format '${format}' is unknown, should be hex or cb58.`);
}
}
}
clone() {
const utxo = new UTXO();
utxo.fromBuffer(this.toBuffer());
return utxo;
}
create(codecID = constants_1.PlatformVMConstants.LATESTCODEC, txid = undefined, outputidx = undefined, assetID = undefined, output = undefined) {
return new UTXO(codecID, txid, outputidx, assetID, output);
}
}
exports.UTXO = UTXO;
class AssetAmountDestination extends assetamount_1.StandardAssetAmountDestination {
constructor(destinations, destinationsThreshold, senders, signers, changeAddresses, changeAddressesThreshold) {
super(destinations, destinationsThreshold, senders, changeAddresses, changeAddressesThreshold);
this.outputOwners = [];
this.getSigners = () => this.signers;
this.setOutputOwners = (owners) => (this.outputOwners = owners);
this.getOutputOwners = () => this.outputOwners;
this.signers = signers;
}
}
exports.AssetAmountDestination = AssetAmountDestination;
/**
* Class representing a set of [[UTXO]]s.
*/
class UTXOSet extends utxos_1.StandardUTXOSet {
constructor() {
super(...arguments);
this._typeName = "UTXOSet";
this._typeID = undefined;
this.getConsumableUXTO = (asOf = (0, helperfunctions_1.UnixNow)(), stakeable = false) => {
return this.getAllUTXOs().filter((utxo) => {
if (stakeable) {
// stakeable transactions can consume any UTXO.
return true;
}
const output = utxo.getOutput();
if (!(output instanceof outputs_1.StakeableLockOut)) {
// non-stakeable transactions can consume any UTXO that isn't locked.
return true;
}
const stakeableOutput = output;
if (stakeableOutput.getStakeableLocktime().lt(asOf)) {
// If the stakeable outputs locktime has ended, then this UTXO can still
// be consumed by a non-stakeable transaction.
return true;
}
// This output is locked and can't be consumed by a non-stakeable
// transaction.
return false;
});
};
this.getLockedTxIDs = () => {
const d = new Set(), b = new Set();
this.getAllUTXOs().forEach((utxo) => {
const output = utxo.getOutput();
if (output instanceof outputs_1.LockedOut) {
var id = output.getLockedIDs().getDepositTxID();
if (!id.isEmpty())
d.add(id.encode("cb58"));
id = output.getLockedIDs().getBondTxID();
if (!id.isEmpty())
b.add(id.encode("cb58"));
}
});
return { depositIDs: [...d.keys()], bondIDs: [...b.keys()] };
};
this.getMinimumSpendable = (aad, asOf = zeroBN, lockTime = zeroBN, lockMode = "Unlocked") => __awaiter(this, void 0, void 0, function* () {
if (asOf.isZero())
asOf = (0, helperfunctions_1.UnixNow)();
let utxoArray = this.getConsumableUXTO(asOf, lockMode == "Stake");
let tmpUTXOArray = [];
if (lockMode == "Stake") {
// If this is a stakeable transaction then have StakeableLockOut come before SECPTransferOutput
// so that users first stake locked tokens before staking unlocked tokens
utxoArray.forEach((utxo) => {
// StakeableLockOuts
if (utxo.getOutput().getTypeID() === 22) {
tmpUTXOArray.push(utxo);
}
});
// Sort the StakeableLockOuts by StakeableLocktime so that the greatest StakeableLocktime are spent first
tmpUTXOArray.sort((a, b) => {
let stakeableLockOut1 = a.getOutput();
let stakeableLockOut2 = b.getOutput();
return (stakeableLockOut2.getStakeableLocktime().toNumber() -
stakeableLockOut1.getStakeableLocktime().toNumber());
});
utxoArray.forEach((utxo) => {
// SECPTransferOutputs
if (utxo.getOutput().getTypeID() === 7) {
tmpUTXOArray.push(utxo);
}
});
utxoArray = tmpUTXOArray;
}
// outs is a map from assetID to a tuple of (lockedStakeable, unlocked)
// which are arrays of outputs.
const outs = {};
// We only need to iterate over UTXOs until we have spent sufficient funds
// to met the requested amounts.
utxoArray.forEach((utxo) => {
const assetID = utxo.getAssetID();
const assetKey = assetID.toString("hex");
const fromAddresses = aad.getSenders();
const output = utxo.getOutput();
const amountOutput = output instanceof outputs_1.ParseableOutput ? output.getOutput() : output;
if (!(amountOutput instanceof outputs_1.AmountOutput) ||
!aad.assetExists(assetKey) ||
!output.meetsThreshold(fromAddresses, asOf)) {
// We should only try to spend fungible assets.
// We should only spend {{ assetKey }}.
// We need to be able to spend the output.
return;
}
const assetAmount = aad.getAssetAmount(assetKey);
if (assetAmount.isFinished()) {
// We've already spent the needed UTXOs for this assetID.
return;
}
if (!(assetKey in outs)) {
// If this is the first time spending this assetID, we need to
// initialize the outs object correctly.
outs[`${assetKey}`] = {
lockedStakeable: [],
unlocked: []
};
}
// amount is the amount of funds available from this UTXO.
const amount = amountOutput.getAmount();
// Set up the SECP input with the same amount as the output.
let input = new inputs_1.SECPTransferInput(amount);
let locked = false;
if (output instanceof outputs_1.StakeableLockOut) {
const stakeableOutput = output;
const stakeableLocktime = stakeableOutput.getStakeableLocktime();
if (stakeableLocktime.gt(asOf)) {
// Add a new input and mark it as being locked.
input = new inputs_1.StakeableLockIn(amount, stakeableLocktime, new inputs_1.ParseableInput(input));
// Mark this UTXO as having been re-locked.
locked = true;
}
}
assetAmount.spendAmount(amount, locked);
if (locked) {
// Track the UTXO as locked.
outs[`${assetKey}`].lockedStakeable.push(output);
}
else {
// Track the UTXO as unlocked.
outs[`${assetKey}`].unlocked.push(output);
}
// Get the indices of the outputs that should be used to authorize the
// spending of this input.
// TODO: getSpenders should return an array of indices rather than an
// array of addresses.
const spenders = amountOutput.getSpenders(fromAddresses, asOf);
spenders.forEach((spender) => {
const idx = amountOutput.getAddressIdx(spender);
if (idx === -1) {
// This should never happen, which is why the error is thrown rather
// than being returned. If this were to ever happen this would be an
// error in the internal logic rather having called this function with
// invalid arguments.
/* istanbul ignore next */
throw new errors_1.AddressError("Error - UTXOSet.getMinimumSpendable: no such " +
`address in output: ${spender}`);
}
input.addSignatureIdx(idx, spender);
});
const txID = utxo.getTxID();
const outputIdx = utxo.getOutputIdx();
const transferInput = new inputs_1.TransferableInput(txID, outputIdx, assetID, input);
aad.addInput(transferInput);
});
if (!aad.canComplete()) {
// After running through all the UTXOs, we still weren't able to get all
// the necessary funds, so this transaction can't be made.
return new errors_1.InsufficientFundsError("Error - UTXOSet.getMinimumSpendable: insufficient " +
"funds to create the transaction");
}
// TODO: We should separate the above functionality into a single function
// that just selects the UTXOs to consume.
const zero = new bn_js_1.default(0);
// assetAmounts is an array of asset descriptions and how much is left to
// spend for them.
const assetAmounts = aad.getAmounts();
assetAmounts.forEach((assetAmount) => {
// change is the amount that should be returned back to the source of the
// funds.
const change = assetAmount.getChange();
// isStakeableLockChange is if the change is locked or not.
const isStakeableLockChange = assetAmount.getStakeableLockChange();
// lockedChange is the amount of locked change that should be returned to
// the sender
const lockedChange = isStakeableLockChange ? change : zero.clone();
const assetID = assetAmount.getAssetID();
const assetKey = assetAmount.getAssetIDString();
const lockedOutputs = outs[`${assetKey}`].lockedStakeable;
lockedOutputs.forEach((lockedOutput, i) => {
const stakeableLocktime = lockedOutput.getStakeableLocktime();
// We know that parseableOutput contains an AmountOutput because the
// first loop filters for fungible assets.
const output = lockedOutput.getOutput();
let outputAmountRemaining = output.getAmount();
// The only output that could generate change is the last output.
// Otherwise, any further UTXOs wouldn't have needed to be spent.
if (i == lockedOutputs.length - 1 && lockedChange.gt(zero)) {
// update outputAmountRemaining to no longer hold the change that we
// are returning.
outputAmountRemaining = outputAmountRemaining.sub(lockedChange);
let newLockedChangeOutput = (0, outputs_1.SelectOutputClass)(lockedOutput.getOutputID(), lockedChange, output.getAddresses(), output.getLocktime(), output.getThreshold(), stakeableLocktime);
const transferOutput = new outputs_1.TransferableOutput(assetID, newLockedChangeOutput);
aad.addChange(transferOutput);
}
// We know that outputAmountRemaining > 0. Otherwise, we would never
// have consumed this UTXO, as it would be only change.
const newLockedOutput = (0, outputs_1.SelectOutputClass)(lockedOutput.getOutputID(), outputAmountRemaining, output.getAddresses(), output.getLocktime(), output.getThreshold(), stakeableLocktime);
const transferOutput = new outputs_1.TransferableOutput(assetID, newLockedOutput);
aad.addOutput(transferOutput);
});
// unlockedChange is the amount of unlocked change that should be returned
// to the sender
const unlockedChange = isStakeableLockChange ? zero.clone() : change;
if (unlockedChange.gt(zero)) {
const newChangeOutput = new outputs_1.SECPTransferOutput(unlockedChange, aad.getChangeAddresses(), zero.clone(), // make sure that we don't lock the change output.
aad.getChangeAddressesThreshold());
const transferOutput = new outputs_1.TransferableOutput(assetID, newChangeOutput);
aad.addChange(transferOutput);
}
// totalAmountSpent is the total amount of tokens consumed.
const totalAmountSpent = assetAmount.getSpent();
// stakeableLockedAmount is the total amount of locked tokens consumed.
const stakeableLockedAmount = assetAmount.getStakeableLockSpent();
// totalUnlockedSpent is the total amount of unlocked tokens consumed.
const totalUnlockedSpent = totalAmountSpent.sub(stakeableLockedAmount);
// amountBurnt is the amount of unlocked tokens that must be burn.
const amountBurnt = assetAmount.getBurn();
// totalUnlockedAvailable is the total amount of unlocked tokens available
// to be produced.
const totalUnlockedAvailable = totalUnlockedSpent.sub(amountBurnt);
// unlockedAmount is the amount of unlocked tokens that should be sent.
const unlockedAmount = totalUnlockedAvailable.sub(unlockedChange);
if (unlockedAmount.gt(zero)) {
const newOutput = new outputs_1.SECPTransferOutput(unlockedAmount, aad.getDestinations(), lockTime, aad.getDestinationsThreshold());
const transferOutput = new outputs_1.TransferableOutput(assetID, newOutput);
aad.addOutput(transferOutput);
}
});
return undefined;
});
this.getUndepositable = (aad, undeposits) => __awaiter(this, void 0, void 0, function* () {
throw new Error("Its stub, not implemented");
});
}
//serialize is inherited
deserialize(fields, encoding = "hex") {
super.deserialize(fields, encoding);
let utxos = {};
for (let utxoid in fields["utxos"]) {
let utxoidCleaned = serialization.decoder(utxoid, encoding, "base58", "base58");
utxos[`${utxoidCleaned}`] = new UTXO();
utxos[`${utxoidCleaned}`].deserialize(fields["utxos"][`${utxoid}`], encoding);
}
let addressUTXOs = {};
for (let address in fields["addressUTXOs"]) {
let addressCleaned = serialization.decoder(address, encoding, "cb58", "hex");
let utxobalance = {};
for (let utxoid in fields["addressUTXOs"][`${address}`]) {
let utxoidCleaned = serialization.decoder(utxoid, encoding, "base58", "base58");
utxobalance[`${utxoidCleaned}`] = serialization.decoder(fields["addressUTXOs"][`${address}`][`${utxoid}`], encoding, "decimalString", "BN");
}
addressUTXOs[`${addressCleaned}`] = utxobalance;
}
this.utxos = utxos;
this.addressUTXOs = addressUTXOs;
}
parseUTXO(utxo) {
const utxovar = new UTXO();
// force a copy
if (typeof utxo === "string") {
utxovar.fromBuffer(bintools.cb58Decode(utxo));
}
else if (utxo instanceof utxos_1.StandardUTXO) {
utxovar.fromBuffer(utxo.toBuffer()); // forces a copy
}
else {
/* istanbul ignore next */
throw new errors_1.UTXOError("Error - UTXO.parseUTXO: utxo parameter is not a UTXO or string");
}
return utxovar;
}
create(...args) {
return new UTXOSet();
}
clone() {
const newset = this.create();
const allUTXOs = this.getAllUTXOs();
newset.addArray(allUTXOs);
return newset;
}
_feeCheck(fee, feeAssetID) {
return (typeof fee !== "undefined" &&
typeof feeAssetID !== "undefined" &&
fee.gt(new bn_js_1.default(0)) &&
feeAssetID instanceof buffer_1.Buffer);
}
}
exports.UTXOSet = UTXOSet;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXR4b3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBpcy9wbGF0Zm9ybXZtL3V0eG9zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7OztBQUFBOzs7R0FHRztBQUNILG9DQUFnQztBQUNoQyxvRUFBMkM7QUFDM0Msa0RBQXNCO0FBQ3RCLHVDQVFrQjtBQUNsQixxQ0FLaUI7QUFDakIsaUVBQXFEO0FBQ3JELDhDQUFrRTtBQUNsRSwyQ0FBaUQ7QUFDakQsMERBR2lDO0FBR2pDLDZEQUE2RTtBQUM3RSwrQ0FNMkI7QUFJM0I7O0dBRUc7QUFDSCxNQUFNLFFBQVEsR0FBYSxrQkFBUSxDQUFDLFdBQVcsRUFBRSxDQUFBO0FBQ2pELE1BQU0sYUFBYSxHQUFrQiw2QkFBYSxDQUFDLFdBQVcsRUFBRSxDQUFBO0FBRWhFLE1BQU0sTUFBTSxHQUFHLElBQUksZUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFBO0FBRXhCOztHQUVHO0FBQ0gsTUFBYSxJQUFLLFNBQVEsb0JBQVk7SUFBdEM7O1FBQ1ksY0FBUyxHQUFHLE1BQU0sQ0FBQTtRQUNsQixZQUFPLEdBQUcsU0FBUyxDQUFBO0lBc0cvQixDQUFDO0lBcEdDLHdCQUF3QjtJQUV4QixXQUFXLENBQUMsTUFBYyxFQUFFLFdBQStCLEtBQUs7UUFDOUQsS0FBSyxDQUFDLFdBQVcsQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUE7UUFDbkMsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFBLDJCQUFpQixFQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFBO1FBQzVELElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsRUFBRSxRQUFRLENBQUMsQ0FBQTtJQUNyRCxDQUFDO0lBRUQsVUFBVSxDQUFDLEtBQWEsRUFBRSxTQUFpQixDQUFDO1FBQzFDLElBQUksQ0FBQyxPQUFPLEdBQUcsUUFBUSxD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QUMsSUFBSSxDQUFDLENBQUMsQ0FBQTtTQUM5QzthQUFNLElBQUksSUFBSSxZQUFZLG9CQUFZLEVBQUU7WUFDdkMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQSxDQUFDLGdCQUFnQjtTQUNyRDthQUFNO1lBQ0wsMEJBQTBCO1lBQzFCLE1BQU0sSUFBSSxrQkFBUyxDQUNqQixnRUFBZ0UsQ0FDakUsQ0FBQTtTQUNGO1FBQ0QsT0FBTyxPQUFPLENBQUE7SUFDaEIsQ0FBQztJQUVELE1BQU0sQ0FBQyxHQUFHLElBQVc7UUFDbkIsT0FBTyxJQUFJLE9BQU8sRUFBVSxDQUFBO0lBQzlCLENBQUM7SUFFRCxLQUFLO1FBQ0gsTUFBTSxNQUFNLEdBQVksSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFBO1FBQ3JDLE1BQU0sUUFBUSxHQUFXLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQTtRQUMzQyxNQUFNLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFBO1FBQ3pCLE9BQU8sTUFBYyxDQUFBO0lBQ3ZCLENBQUM7SUFFRCxTQUFTLENBQUMsR0FBTyxFQUFFLFVBQWtCO1FBQ25DLE9BQU8sQ0FDTCxPQUFPLEdBQUcsS0FBSyxXQUFXO1lBQzFCLE9BQU8sVUFBVSxLQUFLLFdBQVc7WUFDakMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxJQUFJLGVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNqQixVQUFVLFlBQVksZUFBTSxDQUM3QixDQUFBO0lBQ0gsQ0FBQztDQStURjtBQXBaRCwwQkFvWkMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBwYWNrYWdlRG9jdW1lbnRhdGlvblxuICogQG1vZHVsZSBBUEktUGxhdGZvcm1WTS1VVFhPc1xuICovXG5pbXBvcnQgeyBCdWZmZXIgfSBmcm9tIFwiYnVmZmVyL1wiXG5pbXBvcnQgQmluVG9vbHMgZnJvbSBcIi4uLy4uL3V0aWxzL2JpbnRvb2xzXCJcbmltcG9ydCBCTiBmcm9tIFwiYm4uanNcIlxuaW1wb3J0IHtcbiAgQW1vdW50T3V0cHV0LFxuICBTZWxlY3RPdXRwdXRDbGFzcyxcbiAgVHJhbnNmZXJhYmxlT3V0cHV0LFxuICBQYXJzZWFibGVPdXRwdXQsXG4gIFN0YWtlYWJsZUxvY2tPdXQsXG4gIFNFQ1BUcmFuc2Zlck91dHB1dCxcbiAgTG9ja2VkT3V0XG59IGZyb20gXCIuL291dHB1dHNcIlxuaW1wb3J0IHtcbiAgU0VDUFRyYW5zZmVySW5wdXQsXG4gIFN0YWtlYWJsZUxvY2tJbixcbiAgVHJhbnNmZXJhYmxlSW5wdXQsXG4gIFBhcnNlYWJsZUlucHV0XG59IGZyb20gXCIuL2lucHV0c1wiXG5pbXBvcnQgeyBVbml4Tm93IH0gZnJvbSBcIi4uLy4uL3V0aWxzL2hlbHBlcmZ1bmN0aW9uc1wiXG5pbXBvcnQgeyBTdGFuZGFyZFVUWE8sIFN0YW5kYXJkVVRYT1NldCB9IGZyb20gXCIuLi8uLi9jb21tb24vdXR4b3NcIlxuaW1wb3J0IHsgUGxhdGZvcm1WTUNvbnN0YW50cyB9IGZyb20gXCIuL2NvbnN0YW50c1wiXG5pbXBvcnQge1xuICBTdGFuZGFyZEFzc2V0QW1vdW50RGVzdGluYXRpb24sXG4gIEFzc2V0QW1vdW50XG59IGZyb20gXCIuLi8uLi9jb21tb24vYXNzZXRhbW91bnRcIlxuaW1wb3J0IHsgQmFzZUlucHV0IH0gZnJvbSBcIi4uLy4uL2NvbW1vbi9pbnB1dFwiXG5pbXBvcnQgeyBCYXNlT3V0cHV0LCBPdXRwdXRPd25lcnMgfSBmcm9tIFwiLi4vLi4vY29tbW9uL291dHB1dFwiXG5pbXBvcnQgeyBTZXJpYWxpemF0aW9uLCBTZXJpYWxpemVkRW5jb2RpbmcgfSBmcm9tIFwiLi4vLi4vdXRpbHMvc2VyaWFsaXphdGlvblwiXG5pbXBvcnQge1xuICBVVFhPRXJyb3IsXG4gIEFkZHJlc3NFcnJvcixcbiAgSW5zdWZmaWNpZW50RnVuZHNFcnJvcixcbiAgVGltZUVycm9yLFxuICBVbmtub3duRm9ybWF0RXJyb3Jcbn0gZnJvbSBcIi4uLy4uL3V0aWxzL2Vycm9yc1wiXG5pbXBvcnQgeyBMb2NrTW9kZSB9IGZyb20gXCIuL2J1aWxkZXJcIlxuaW1wb3J0IHsgVW5kZXBvc2l0IH0gZnJvbSBcIi4vaW50ZXJmYWNlc1wiXG5cbi8qKlxuICogQGlnbm9yZVxuICovXG5jb25zdCBiaW50b29sczogQmluVG9vbHMgPSBCaW5Ub29scy5nZXRJbnN0YW5jZSgpXG5jb25zdCBzZXJpYWxpemF0aW9uOiBTZXJpYWxpemF0aW9uID0gU2VyaWFsaXphdGlvbi5nZXRJbnN0YW5jZSgpXG5cbmNvbnN0IHplcm9CTiA9IG5ldyBCTigwKVxuXG4vKipcbiAqIENsYXNzIGZvciByZXByZXNlbnRpbmcgYSBzaW5nbGUgVVRYTy5cbiAqL1xuZXhwb3J0IGNsYXNzIFVUWE8gZXh0ZW5kcyBTdGFuZGFyZFVUWE8ge1xuICBwcm90ZWN0ZWQgX3R5cGVOYW1lID0gXCJVVFhPXCJcbiAgcHJvdGVjdGVkIF90eXBlSUQgPSB1bmRlZmluZWRcblxuICAvL3NlcmlhbGl6ZSBpcyBpbmhlcml0ZWRcblxuICBkZXNlcmlhbGl6ZShmaWVsZHM6IG9iamVjdCwgZW5jb2Rpbmc6IFNlcmlhbGl6ZWRFbmNvZGluZyA9IFwiaGV4XCIpIHtcbiAgICBzdXBlci5kZXNlcmlhbGl6ZShmaWVsZHMsIGVuY29kaW5nKVxuICAgIHRoaXMub3V0cHV0ID0gU2VsZWN0T3V0cHV0Q2xhc3MoZmllbGRzW1wib3V0cHV0XCJdW1wiX3R5cGVJRFwiXSlcbiAgICB0aGlzLm91dHB1dC5kZXNlcmlhbGl6ZShmaWVsZHNbXCJvdXRwdXRcIl0sIGVuY29kaW5nKVxuICB9XG5cbiAgZnJvbUJ1ZmZlcihieXRlczogQnVmZmVyLCBvZmZzZXQ6IG51bWJlciA9IDApOiBudW1iZXIge1xuICAgIHRoaXMuY29kZWNJRCA9IGJpbnRvb2xzLmNvcHlGcm9tKGJ5dGVzLCBvZmZzZXQsIG9mZnNldCArIDIpXG4gICAgb2Zmc2V0ICs9IDJcbiAgICB0aGlzLnR4aWQgPSBiaW50b29scy5jb3B5RnJvbShieXRlcywgb2Zmc2V0LCBvZmZzZXQgKyAzMilcbiAgICBvZmZzZXQgKz0gMzJcbiAgICB0aGlzLm91dHB1dGlkeCA9IGJpbnRvb2xzLmNvcHlGcm9tKGJ5dGVzLCBvZmZzZXQsIG9mZnNldCArIDQpXG4gICAgb2Zmc2V0ICs9IDRcbiAgICB0aGlzLmFzc2V0SUQgPSBiaW50b29scy5jb3B5RnJvbShieXRlcywgb2Zmc2V0LCBvZmZzZXQgKyAzMilcbiAgICBvZmZzZXQgKz0gMzJcbiAgICBjb25zdCBvdXRwdXRpZDogbnVtYmVyID0gYmludG9vbHNcbiAgICAgIC5jb3B5RnJvbShieXRlcywgb2Zmc2V0LCBvZmZzZXQgKyA0KVxuICAgICAgLnJlYWRVSW50MzJCRSgwKVxuICAgIG9mZnNldCArPSA0XG4gICAgdGhpcy5vdXRwdXQgPSBTZWxlY3RPdXRwdXRDbGFzcyhvdXRwdXRpZClcbiAgICByZXR1cm4gdGhpcy5vdXRwdXQuZnJvbUJ1ZmZlcihieXRlcywgb2Zmc2V0KVxuICB9XG5cbiAgLyoqXG4gICAqIFRha2VzIGEgYmFzZS01OCBvciBoZXggc3RyaW5nIGNvbnRhaW5pbmcgYSBbW1VUWE9dXSwgcGFyc2VzIGl0LCBwb3B1bGF0ZXMgdGhlIGNsYXNzLCBhbmQgcmV0dXJucyB0aGUgbGVuZ3RoIG9mIHRoZSBTdGFuZGFyZFVUWE8gaW4gYnl0ZXMuXG4gICAqXG4gICAqIEBwYXJhbSBzZXJpYWxpemVkIEEgYmFzZS01OCBzdHJpbmcgY29udGFpbmluZyBhIHJhdyBbW1VUWE9dXVxuICAgKiBAcGFyYW0gZm9ybWF0IFRoZSBmb3JtYXQgb2YgdGhlIGVuY29kZWQgW1tVVFhPXV0gKGNiNTggb3IgaGV4KS4gRGVmYXVsdHMgdG8gY2I1OCBwZXIgZXhpc3RpbmcgY29kZWJhc2VcbiAgICpcbiAgICogQHJldHVybnMgVGhlIGxlbmd0aCBvZiB0aGUgcmF3IFtbVVRYT11dXG4gICAqXG4gICAqIEByZW1hcmtzXG4gICAqIERlZmF1bHQgZW5jb2RpbmcgZm9ybWF0IGlzIGNiNTgsIGlmIHByb3ZpZGluZyBoZXggZW5jb2RlZCBzdHJpbmcgcGxlYXNlIHNwZWNpZnkgZm9ybWF0IGFzICdoZXgnXG4gICAqL1xuICBmcm9tU3RyaW5nKHNlcmlhbGl6ZWQ6IHN0cmluZywgZm9ybWF0OiBzdHJpbmcgPSBcImNiNThcIik6IG51bWJlciB7XG4gICAgc3dpdGNoIChmb3JtYXQpIHtcbiAgICAgIGNhc2UgXCJjYjU4XCI6IHtcbiAgICAgICAgLyogaXN0YW5idWwgaWdub3JlIG5leHQgKi9cbiAgICAgICAgcmV0dXJuIHRoaXMuZnJvbUJ1ZmZlcihiaW50b29scy5jYjU4RGVjb2RlKHNlcmlhbGl6ZWQpKVxuICAgICAgfVxuICAgICAgY2FzZSBcImhleFwiOiB7XG4gICAgICAgIGxldCBkZWNvZGVkID0gc2VyaWFsaXphdGlvbi5kZWNvZGVyKHNlcmlhbGl6ZWQsIFwiaGV4XCIsIFwiaGV4XCIsIFwiY2I1OFwiKVxuICAgICAgICB0aGlzLmZyb21TdHJpbmcoZGVjb2RlZClcbiAgICAgICAgcmV0dXJuIHRoaXMudG9CdWZmZXIoKS5sZW5ndGhcbiAgICAgIH1cbiAgICAgIGRlZmF1bHQ6IHtcbiAgICAgICAgdGhyb3cgbmV3IFVua25vd25Gb3JtYXRFcnJvcihcbiAgICAgICAgICBgU3BlY2lmaWVkIGZvcm1hdCAnJHtmb3JtYXR9JyBpcyB1bmtub3duLCBzaG91bGQgYmUgaGV4IG9yIGNiNTguYFxuICAgICAgICApXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYSBiYXNlLTU4IHJlcHJlc2VudGF0aW9uIG9mIHRoZSBbW1VUWE9dXS5cbiAgICpcbiAgICogQHBhcmFtIGZvcm1hdCBUaGUgZm9ybWF0IG9mIHRoZSBlbmNvZGVkIFtbVVRYT11dIChjYjU4IG9yIGhleCkuIERlZmF1bHRzIHRvIGNiNTggcGVyIGV4aXN0aW5nIGNvZGViYXNlXG4gICAqXG4gICAqIEByZW1hcmtzXG4gICAqIERlZmF1bHQgZW5jb2RpbmcgZm9ybWF0IHRvIGNiNTgsIGlmIHlvdSB3YW50IGEgaGV4IGVuY29kZWQgb3V0cHV0IHBsZWFzZSBzcGVjaWZ5IGZvcm1hdCBhcyAnaGV4J1xuICAgKi9cbiAgdG9TdHJpbmcoZm9ybWF0OiBzdHJpbmcgPSBcImNiNThcIik6IHN0cmluZyB7XG4gICAgc3dpdGNoIChmb3JtYXQpIHtcbiAgICAgIGNhc2UgXCJjYjU4XCI6IHtcbiAgICAgICAgLyogaXN0YW5idWwgaWdub3JlIG5leHQgKi9cbiAgICAgICAgcmV0dXJuIGJpbnRvb2xzLmNiNThFbmNvZGUodGhpcy50b0J1ZmZlcigpKVxuICAgICAgfVxuICAgICAgY2FzZSBcImhleFwiOiB7XG4gICAgICAgIHJldHVybiBzZXJpYWxpemF0aW9uLmVuY29kZXIoXG4gICAgICAgICAgYmludG9vbHMuY2I1OEVuY29kZSh0aGlzLnRvQnVmZmVyKCkpLFxuICAgICAgICAgIFwiaGV4XCIsXG4gICAgICAgICAgXCJjYjU4XCIsXG4gICAgICAgICAgXCJoZXhcIlxuICAgICAgICApXG4gICAgICB9XG4gICAgICBkZWZhdWx0OiB7XG4gICAgICAgIHRocm93IG5ldyBVbmtub3duRm9ybWF0RXJyb3IoXG4gICAgICAgICAgYFNwZWNpZmllZCBmb3JtYXQgJyR7Zm9ybWF0fScgaXMgdW5rbm93biwgc2hvdWxkIGJlIGhleCBvciBjYjU4LmBcbiAgICAgICAgKVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGNsb25lKCk6IHRoaXMge1xuICAgIGNvbnN0IHV0eG86IFVUWE8gPSBuZXcgVVRYTygpXG4gICAgdXR4by5mcm9tQnVmZmVyKHRoaXMudG9CdWZmZXIoKSlcbiAgICByZXR1cm4gdXR4byBhcyB0aGlzXG4gIH1cblxuICBjcmVhdGUoXG4gICAgY29kZWNJRDogbnVtYmVyID0gUGxhdGZvcm1WTUNvbnN0YW50cy5MQVRFU1RDT0RFQyxcbiAgICB0eGlkOiBCdWZmZXIgPSB1bmRlZmluZWQsXG4gICAgb3V0cHV0aWR4OiBCdWZmZXIgfCBudW1iZXIgPSB1bmRlZmluZWQsXG4gICAgYXNzZXRJRDogQnVmZmVyID0gdW5kZWZpbmVkLFxuICAgIG91dHB1dDogQmFzZU91dHB1dCA9IHVuZGVmaW5lZFxuICApOiB0aGlzIHtcbiAgICByZXR1cm4gbmV3IFVUWE8oY29kZWNJRCwgdHhpZCwgb3V0cHV0aWR4LCBhc3NldElELCBvdXRwdXQpIGFzIHRoaXNcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgQXNzZXRBbW91bnREZXN0aW5hdGlvbiBleHRlbmRzIFN0YW5kYXJkQXNzZXRBbW91bnREZXN0aW5hdGlvbjxcbiAgVHJhbnNmZXJhYmxlT3V0cHV0LFxuICBUcmFuc2ZlcmFibGVJbnB1dFxuPiB7XG4gIHByb3RlY3RlZCBzaWduZXJzOiBCdWZmZXJbXVxuICBwcm90ZWN0ZWQgb3V0cHV0T3duZXJzOiBPdXRwdXRPd25lcnNbXSA9IFtdXG5cbiAgZ2V0U2lnbmVycyA9ICgpOiBCdWZmZXJbXSA9PiB0aGlzLnNpZ25lcnNcblxuICBzZXRPdXRwdXRPd25lcnMgPSAob3duZXJzOiBPdXRwdXRPd25lcnNbXSkgPT4gKHRoaXMub3V0cHV0T3duZXJzID0gb3duZXJzKVxuICBnZXRPdXRwdXRPd25lcnMgPSAoKTogT3V0cHV0T3duZXJzW10gPT4gdGhpcy5vdXRwdXRPd25lcnNcblxuICBjb25zdHJ1Y3RvcihcbiAgICBkZXN0aW5hdGlvbnM6IEJ1ZmZlcltdLFxuICAgIGRlc3RpbmF0aW9uc1RocmVzaG9sZDogbnVtYmVyLFxuICAgIHNlbmRlcnM6IEJ1ZmZlcltdLFxuICAgIHNpZ25lcnM6IEJ1ZmZlcltdLFxuICAgIGNoYW5nZUFkZHJlc3NlczogQnVmZmVyW10sXG4gICAgY2hhbmdlQWRkcmVzc2VzVGhyZXNob2xkOiBudW1iZXJcbiAgKSB7XG4gICAgc3VwZXIoXG4gICAgICBkZXN0aW5hdGlvbnMsXG4gICAgICBkZXN0aW5hdGlvbnNUaHJlc2hvbGQsXG4gICAgICBzZW5kZXJzLFxuICAgICAgY2hhbmdlQWRkcmVzc2VzLFxuICAgICAgY2hhbmdlQWRkcmVzc2VzVGhyZXNob2xkXG4gICAgKVxuICAgIHRoaXMuc2lnbmVycyA9IHNpZ25lcnNcbiAgfVxufVxuXG4vKipcbiAqIENsYXNzIHJlcHJlc2VudGluZyBhIHNldCBvZiBbW1VUWE9dXXMuXG4gKi9cbmV4cG9ydCBjbGFzcyBVVFhPU2V0IGV4dGVuZHMgU3RhbmRhcmRVVFhPU2V0PFVUWE8+IHtcbiAgcHJvdGVjdGVkIF90eXBlTmFtZSA9IFwiVVRYT1NldFwiXG4gIHByb3RlY3RlZCBfdHlwZUlEID0gdW5kZWZpbmVkXG5cbiAgLy9zZXJpYWxpemUgaXMgaW5oZXJpdGVkXG5cbiAgZGVzZXJpYWxpemUoZmllbGRzOiBvYmplY3QsIGVuY29kaW5nOiBTZXJpYWxpemVkRW5jb2RpbmcgPSBcImhleFwiKSB7XG4gICAgc3VwZXIuZGVzZXJpYWxpemUoZmllbGRzLCBlbmNvZGluZylcbiAgICBsZXQgdXR4b3MgPSB7fVxuICAgIGZvciAobGV0IHV0eG9pZCBpbiBmaWVsZHNbXCJ1dHhvc1wiXSkge1xuICAgICAgbGV0IHV0eG9pZENsZWFuZWQ6IHN0cmluZyA9IHNlcmlhbGl6YXRpb24uZGVjb2RlcihcbiAgICAgICAgdXR4b2lkLFxuICAgICAgICBlbmNvZGluZyxcbiAgICAgICAgXCJiYXNlNThcIixcbiAgICAgICAgXCJiYXNlNThcIlxuICAgICAgKVxuICAgICAgdXR4b3NbYCR7dXR4b2lkQ2xlYW5lZH1gXSA9IG5ldyBVVFhPKClcbiAgICAgIHV0eG9zW2Ake3V0eG9pZENsZWFuZWR9YF0uZGVzZXJpYWxpemUoXG4gICAgICAgIGZpZWxkc1tcInV0eG9zXCJdW2Ake3V0eG9pZH1gXSxcbiAgICAgICAgZW5jb2RpbmdcbiAgICAgIClcbiAgICB9XG4gICAgbGV0IGFkZHJlc3NVVFhPcyA9IHt9XG4gICAgZm9yIChsZXQgYWRkcmVzcyBpbiBmaWVsZHNbXCJhZGRyZXNzVVRYT3NcIl0pIHtcbiAgICAgIGxldCBhZGRyZXNzQ2xlYW5lZDogc3RyaW5nID0gc2VyaWFsaXphdGlvbi5kZWNvZGVyKFxuICAgICAgICBhZGRyZXNzLFxuICAgICAgICBlbmNvZGluZyxcbiAgICAgICAgXCJjYjU4XCIsXG4gICAgICAgIFwiaGV4XCJcbiAgICAgIClcbiAgICAgIGxldCB1dHhvYmFsYW5jZSA9IHt9XG4gICAgICBmb3IgKGxldCB1dHhvaWQgaW4gZmllbGRzW1wiYWRkcmVzc1VUWE9zXCJdW2Ake2FkZHJlc3N9YF0pIHtcbiAgICAgICAgbGV0IHV0eG9pZENsZWFuZWQ6IHN0cmluZyA9IHNlcmlhbGl6YXRpb24uZGVjb2RlcihcbiAgICAgICAgICB1dHhvaWQsXG4gICAgICAgICAgZW5jb2RpbmcsXG4gICAgICAgICAgXCJiYXNlNThcIixcbiAgICAgICAgICBcImJhc2U1OFwiXG4gICAgICAgIClcbiAgICAgICAgdXR4b2JhbGFuY2VbYCR7dXR4b2lkQ2xlYW5lZH1gXSA9IHNlcmlhbGl6YXRpb24uZGVjb2RlcihcbiAgICAgICAgICBmaWVsZHNbXCJhZGRyZXNzVVRYT3NcIl1bYCR7YWRkcmVzc31gXVtgJHt1dHhvaWR9YF0sXG4gICAgICAgICAgZW5jb2RpbmcsXG4gICAgICAgICAgXCJkZWNpbWFsU3RyaW5nXCIsXG4gICAgICAgICAgXCJCTlwiXG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIGFkZHJlc3NVVFhPc1tgJHthZGRyZXNzQ2xlYW5lZH1gXSA9IHV0eG9iYWxhbmNlXG4gICAgfVxuICAgIHRoaXMudXR4b3MgPSB1dHhvc1xuICAgIHRoaXMuYWRkcmVzc1VUWE9zID0gYWRkcmVzc1VUWE9zXG4gIH1cblxuICBwYXJzZVVUWE8odXR4bzogVVRYTyB8IHN0cmluZyk6IFVUWE8ge1xuICAgIGNvbnN0IHV0eG92YXI6IFVUWE8gPSBuZXcgVVRYTygpXG4gICAgLy8gZm9yY2UgYSBjb3B5XG4gICAgaWYgKHR5cGVvZiB1dHhvID09PSBcInN0cmluZ1wiKSB7XG4gICAgICB1dHhvdmFyLmZyb21CdWZmZXIoYmludG9vbHMuY2I1OERlY29kZSh1dHhvKSlcbiAgICB9IGVsc2UgaWYgKHV0eG8gaW5zdGFuY2VvZiBTdGFuZGFyZFVUWE8pIHtcbiAgICAgIHV0eG92YXIuZnJvbUJ1ZmZlcih1dHhvLnRvQnVmZmVyKCkpIC8vIGZvcmNlcyBhIGNvcHlcbiAgICB9IGVsc2Uge1xuICAgICAgLyogaXN0YW5idWwgaWdub3JlIG5leHQgKi9cbiAgICAgIHRocm93IG5ldyBVVFhPRXJyb3IoXG4gICAgICAgIFwiRXJyb3IgLSBVVFhPLnBhcnNlVVRYTzogdXR4byBwYXJhbWV0ZXIgaXMgbm90IGEgVVRYTyBvciBzdHJpbmdcIlxuICAgICAgKVxuICAgIH1cbiAgICByZXR1cm4gdXR4b3ZhclxuICB9XG5cbiAgY3JlYXRlKC4uLmFyZ3M6IGFueVtdKTogdGhpcyB7XG4gICAgcmV0dXJuIG5ldyBVVFhPU2V0KCkgYXMgdGhpc1xuICB9XG5cbiAgY2xvbmUoKTogdGhpcyB7XG4gICAgY29uc3QgbmV3c2V0OiBVVFhPU2V0ID0gdGhpcy5jcmVhdGUoKVxuICAgIGNvbnN0IGFsbFVUWE9zOiBVVFhPW10gPSB0aGlzLmdldEFsbFVUWE9zKClcbiAgICBuZXdzZXQuYWRkQXJyYXkoYWxsVVRYT3MpXG4gICAgcmV0dXJuIG5ld3NldCBhcyB0aGlzXG4gIH1cblxuICBfZmVlQ2hlY2soZmVlOiBCTiwgZmVlQXNzZXRJRDogQnVmZmVyKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIChcbiAgICAgIHR5cGVvZiBmZWUgIT09IFwidW5kZWZpbmVkXCIgJiZcbiAgICAgIHR5cGVvZiBm