@caminojs-nikos/caminojs
Version:
Camino Platform JS Library
422 lines • 66.1 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;
});
}
//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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXR4b3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBpcy9wbGF0Zm9ybXZtL3V0eG9zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7OztBQUFBOzs7R0FHRztBQUNILG9DQUFnQztBQUNoQyxvRUFBMkM7QUFDM0Msa0RBQXNCO0FBQ3RCLHVDQVFrQjtBQUNsQixxQ0FLaUI7QUFDakIsaUVBQXFEO0FBQ3JELDhDQUFrRTtBQUNsRSwyQ0FBaUQ7QUFDakQsMERBR2lDO0FBR2pDLDZEQUE2RTtBQUM3RSwrQ0FNMkI7QUFHM0I7O0dBRUc7QUFDSCxNQUFNLFFBQVEsR0FBYSxrQkFBUSxDQUFDLFdBQVcsRUFBRSxDQUFBO0FBQ2pELE1BQU0sYUFBYSxHQUFrQiw2QkFBYSxDQUFDLFdBQVcsRUFBRSxDQUFBO0FBRWhFLE1BQU0sTUFBTSxHQUFHLElBQUksZUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFBO0FBRXhCOztHQUVHO0FBQ0gsTUFBYSxJQUFLLFNBQVEsb0JBQVk7SUFBdEM7O1FBQ1ksY0FBUyxHQUFHLE1BQU0sQ0FBQTtRQUNsQixZQUFPLEdBQUcsU0FBUyxDQUFBO0lBc0cvQixDQUFDO0lBcEdDLHdCQUF3QjtJQUV4QixXQUFXLENBQUMsTUFBYyxFQUFFLFdBQStCLEtBQUs7UUFDOUQsS0FBSyxDQUFDLFdBQVcsQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUE7UUFDbkMsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFBLDJCQUFpQixFQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFBO1FBQzVELElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsRUFBRSxRQUFRLENBQUMsQ0FBQTtJQUNyRCxDQUFDO0lBRUQsVUFBVSxDQUFDLEtBQWEsRUFBRSxTQUFpQixDQUFDO1FBQzFDLElBQUksQ0FBQyxPQUFPLEdBQUcsUUFBUSxD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LENBQUMsZ0JBQWdCO1NBQ3JEO2FBQU07WUFDTCwwQkFBMEI7WUFDMUIsTUFBTSxJQUFJLGtCQUFTLENBQ2pCLGdFQUFnRSxDQUNqRSxDQUFBO1NBQ0Y7UUFDRCxPQUFPLE9BQU8sQ0FBQTtJQUNoQixDQUFDO0lBRUQsTUFBTSxDQUFDLEdBQUcsSUFBVztRQUNuQixPQUFPLElBQUksT0FBTyxFQUFVLENBQUE7SUFDOUIsQ0FBQztJQUVELEtBQUs7UUFDSCxNQUFNLE1BQU0sR0FBWSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUE7UUFDckMsTUFBTSxRQUFRLEdBQVcsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFBO1FBQzNDLE1BQU0sQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUE7UUFDekIsT0FBTyxNQUFjLENBQUE7SUFDdkIsQ0FBQztJQUVELFNBQVMsQ0FBQyxHQUFPLEVBQUUsVUFBa0I7UUFDbkMsT0FBTyxDQUNMLE9BQU8sR0FBRyxLQUFLLFdBQVc7WUFDMUIsT0FBTyxVQUFVLEtBQUssV0FBVztZQUNqQyxHQUFHLENBQUMsRUFBRSxDQUFDLElBQUksZUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ2pCLFVBQVUsWUFBWSxlQUFNLENBQzdCLENBQUE7SUFDSCxDQUFDO0NBd1RGO0FBN1lELDBCQTZZQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQHBhY2thZ2VEb2N1bWVudGF0aW9uXG4gKiBAbW9kdWxlIEFQSS1QbGF0Zm9ybVZNLVVUWE9zXG4gKi9cbmltcG9ydCB7IEJ1ZmZlciB9IGZyb20gXCJidWZmZXIvXCJcbmltcG9ydCBCaW5Ub29scyBmcm9tIFwiLi4vLi4vdXRpbHMvYmludG9vbHNcIlxuaW1wb3J0IEJOIGZyb20gXCJibi5qc1wiXG5pbXBvcnQge1xuICBBbW91bnRPdXRwdXQsXG4gIFNlbGVjdE91dHB1dENsYXNzLFxuICBUcmFuc2ZlcmFibGVPdXRwdXQsXG4gIFBhcnNlYWJsZU91dHB1dCxcbiAgU3Rha2VhYmxlTG9ja091dCxcbiAgU0VDUFRyYW5zZmVyT3V0cHV0LFxuICBMb2NrZWRPdXRcbn0gZnJvbSBcIi4vb3V0cHV0c1wiXG5pbXBvcnQge1xuICBTRUNQVHJhbnNmZXJJbnB1dCxcbiAgU3Rha2VhYmxlTG9ja0luLFxuICBUcmFuc2ZlcmFibGVJbnB1dCxcbiAgUGFyc2VhYmxlSW5wdXRcbn0gZnJvbSBcIi4vaW5wdXRzXCJcbmltcG9ydCB7IFVuaXhOb3cgfSBmcm9tIFwiLi4vLi4vdXRpbHMvaGVscGVyZnVuY3Rpb25zXCJcbmltcG9ydCB7IFN0YW5kYXJkVVRYTywgU3RhbmRhcmRVVFhPU2V0IH0gZnJvbSBcIi4uLy4uL2NvbW1vbi91dHhvc1wiXG5pbXBvcnQgeyBQbGF0Zm9ybVZNQ29uc3RhbnRzIH0gZnJvbSBcIi4vY29uc3RhbnRzXCJcbmltcG9ydCB7XG4gIFN0YW5kYXJkQXNzZXRBbW91bnREZXN0aW5hdGlvbixcbiAgQXNzZXRBbW91bnRcbn0gZnJvbSBcIi4uLy4uL2NvbW1vbi9hc3NldGFtb3VudFwiXG5pbXBvcnQgeyBCYXNlSW5wdXQgfSBmcm9tIFwiLi4vLi4vY29tbW9uL2lucHV0XCJcbmltcG9ydCB7IEJhc2VPdXRwdXQsIE91dHB1dE93bmVycyB9IGZyb20gXCIuLi8uLi9jb21tb24vb3V0cHV0XCJcbmltcG9ydCB7IFNlcmlhbGl6YXRpb24sIFNlcmlhbGl6ZWRFbmNvZGluZyB9IGZyb20gXCIuLi8uLi91dGlscy9zZXJpYWxpemF0aW9uXCJcbmltcG9ydCB7XG4gIFVUWE9FcnJvcixcbiAgQWRkcmVzc0Vycm9yLFxuICBJbnN1ZmZpY2llbnRGdW5kc0Vycm9yLFxuICBUaW1lRXJyb3IsXG4gIFVua25vd25Gb3JtYXRFcnJvclxufSBmcm9tIFwiLi4vLi4vdXRpbHMvZXJyb3JzXCJcbmltcG9ydCB7IExvY2tNb2RlIH0gZnJvbSBcIi4vYnVpbGRlclwiXG5cbi8qKlxuICogQGlnbm9yZVxuICovXG5jb25zdCBiaW50b29sczogQmluVG9vbHMgPSBCaW5Ub29scy5nZXRJbnN0YW5jZSgpXG5jb25zdCBzZXJpYWxpemF0aW9uOiBTZXJpYWxpemF0aW9uID0gU2VyaWFsaXphdGlvbi5nZXRJbnN0YW5jZSgpXG5cbmNvbnN0IHplcm9CTiA9IG5ldyBCTigwKVxuXG4vKipcbiAqIENsYXNzIGZvciByZXByZXNlbnRpbmcgYSBzaW5nbGUgVVRYTy5cbiAqL1xuZXhwb3J0IGNsYXNzIFVUWE8gZXh0ZW5kcyBTdGFuZGFyZFVUWE8ge1xuICBwcm90ZWN0ZWQgX3R5cGVOYW1lID0gXCJVVFhPXCJcbiAgcHJvdGVjdGVkIF90eXBlSUQgPSB1bmRlZmluZWRcblxuICAvL3NlcmlhbGl6ZSBpcyBpbmhlcml0ZWRcblxuICBkZXNlcmlhbGl6ZShmaWVsZHM6IG9iamVjdCwgZW5jb2Rpbmc6IFNlcmlhbGl6ZWRFbmNvZGluZyA9IFwiaGV4XCIpIHtcbiAgICBzdXBlci5kZXNlcmlhbGl6ZShmaWVsZHMsIGVuY29kaW5nKVxuICAgIHRoaXMub3V0cHV0ID0gU2VsZWN0T3V0cHV0Q2xhc3MoZmllbGRzW1wib3V0cHV0XCJdW1wiX3R5cGVJRFwiXSlcbiAgICB0aGlzLm91dHB1dC5kZXNlcmlhbGl6ZShmaWVsZHNbXCJvdXRwdXRcIl0sIGVuY29kaW5nKVxuICB9XG5cbiAgZnJvbUJ1ZmZlcihieXRlczogQnVmZmVyLCBvZmZzZXQ6IG51bWJlciA9IDApOiBudW1iZXIge1xuICAgIHRoaXMuY29kZWNJRCA9IGJpbnRvb2xzLmNvcHlGcm9tKGJ5dGVzLCBvZmZzZXQsIG9mZnNldCArIDIpXG4gICAgb2Zmc2V0ICs9IDJcbiAgICB0aGlzLnR4aWQgPSBiaW50b29scy5jb3B5RnJvbShieXRlcywgb2Zmc2V0LCBvZmZzZXQgKyAzMilcbiAgICBvZmZzZXQgKz0gMzJcbiAgICB0aGlzLm91dHB1dGlkeCA9IGJpbnRvb2xzLmNvcHlGcm9tKGJ5dGVzLCBvZmZzZXQsIG9mZnNldCArIDQpXG4gICAgb2Zmc2V0ICs9IDRcbiAgICB0aGlzLmFzc2V0SUQgPSBiaW50b29scy5jb3B5RnJvbShieXRlcywgb2Zmc2V0LCBvZmZzZXQgKyAzMilcbiAgICBvZmZzZXQgKz0gMzJcbiAgICBjb25zdCBvdXRwdXRpZDogbnVtYmVyID0gYmludG9vbHNcbiAgICAgIC5jb3B5RnJvbShieXRlcywgb2Zmc2V0LCBvZmZzZXQgKyA0KVxuICAgICAgLnJlYWRVSW50MzJCRSgwKVxuICAgIG9mZnNldCArPSA0XG4gICAgdGhpcy5vdXRwdXQgPSBTZWxlY3RPdXRwdXRDbGFzcyhvdXRwdXRpZClcbiAgICByZXR1cm4gdGhpcy5vdXRwdXQuZnJvbUJ1ZmZlcihieXRlcywgb2Zmc2V0KVxuICB9XG5cbiAgLyoqXG4gICAqIFRha2VzIGEgYmFzZS01OCBvciBoZXggc3RyaW5nIGNvbnRhaW5pbmcgYSBbW1VUWE9dXSwgcGFyc2VzIGl0LCBwb3B1bGF0ZXMgdGhlIGNsYXNzLCBhbmQgcmV0dXJucyB0aGUgbGVuZ3RoIG9mIHRoZSBTdGFuZGFyZFVUWE8gaW4gYnl0ZXMuXG4gICAqXG4gICAqIEBwYXJhbSBzZXJpYWxpemVkIEEgYmFzZS01OCBzdHJpbmcgY29udGFpbmluZyBhIHJhdyBbW1VUWE9dXVxuICAgKiBAcGFyYW0gZm9ybWF0IFRoZSBmb3JtYXQgb2YgdGhlIGVuY29kZWQgW1tVVFhPXV0gKGNiNTggb3IgaGV4KS4gRGVmYXVsdHMgdG8gY2I1OCBwZXIgZXhpc3RpbmcgY29kZWJhc2VcbiAgICpcbiAgICogQHJldHVybnMgVGhlIGxlbmd0aCBvZiB0aGUgcmF3IFtbVVRYT11dXG4gICAqXG4gICAqIEByZW1hcmtzXG4gICAqIERlZmF1bHQgZW5jb2RpbmcgZm9ybWF0IGlzIGNiNTgsIGlmIHByb3ZpZGluZyBoZXggZW5jb2RlZCBzdHJpbmcgcGxlYXNlIHNwZWNpZnkgZm9ybWF0IGFzICdoZXgnXG4gICAqL1xuICBmcm9tU3RyaW5nKHNlcmlhbGl6ZWQ6IHN0cmluZywgZm9ybWF0OiBzdHJpbmcgPSBcImNiNThcIik6IG51bWJlciB7XG4gICAgc3dpdGNoIChmb3JtYXQpIHtcbiAgICAgIGNhc2UgXCJjYjU4XCI6IHtcbiAgICAgICAgLyogaXN0YW5idWwgaWdub3JlIG5leHQgKi9cbiAgICAgICAgcmV0dXJuIHRoaXMuZnJvbUJ1ZmZlcihiaW50b29scy5jYjU4RGVjb2RlKHNlcmlhbGl6ZWQpKVxuICAgICAgfVxuICAgICAgY2FzZSBcImhleFwiOiB7XG4gICAgICAgIGxldCBkZWNvZGVkID0gc2VyaWFsaXphdGlvbi5kZWNvZGVyKHNlcmlhbGl6ZWQsIFwiaGV4XCIsIFwiaGV4XCIsIFwiY2I1OFwiKVxuICAgICAgICB0aGlzLmZyb21TdHJpbmcoZGVjb2RlZClcbiAgICAgICAgcmV0dXJuIHRoaXMudG9CdWZmZXIoKS5sZW5ndGhcbiAgICAgIH1cbiAgICAgIGRlZmF1bHQ6IHtcbiAgICAgICAgdGhyb3cgbmV3IFVua25vd25Gb3JtYXRFcnJvcihcbiAgICAgICAgICBgU3BlY2lmaWVkIGZvcm1hdCAnJHtmb3JtYXR9JyBpcyB1bmtub3duLCBzaG91bGQgYmUgaGV4IG9yIGNiNTguYFxuICAgICAgICApXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYSBiYXNlLTU4IHJlcHJlc2VudGF0aW9uIG9mIHRoZSBbW1VUWE9dXS5cbiAgICpcbiAgICogQHBhcmFtIGZvcm1hdCBUaGUgZm9ybWF0IG9mIHRoZSBlbmNvZGVkIFtbVVRYT11dIChjYjU4IG9yIGhleCkuIERlZmF1bHRzIHRvIGNiNTggcGVyIGV4aXN0aW5nIGNvZGViYXNlXG4gICAqXG4gICAqIEByZW1hcmtzXG4gICAqIERlZmF1bHQgZW5jb2RpbmcgZm9ybWF0IHRvIGNiNTgsIGlmIHlvdSB3YW50IGEgaGV4IGVuY29kZWQgb3V0cHV0IHBsZWFzZSBzcGVjaWZ5IGZvcm1hdCBhcyAnaGV4J1xuICAgKi9cbiAgdG9TdHJpbmcoZm9ybWF0OiBzdHJpbmcgPSBcImNiNThcIik6IHN0cmluZyB7XG4gICAgc3dpdGNoIChmb3JtYXQpIHtcbiAgICAgIGNhc2UgXCJjYjU4XCI6IHtcbiAgICAgICAgLyogaXN0YW5idWwgaWdub3JlIG5leHQgKi9cbiAgICAgICAgcmV0dXJuIGJpbnRvb2xzLmNiNThFbmNvZGUodGhpcy50b0J1ZmZlcigpKVxuICAgICAgfVxuICAgICAgY2FzZSBcImhleFwiOiB7XG4gICAgICAgIHJldHVybiBzZXJpYWxpemF0aW9uLmVuY29kZXIoXG4gICAgICAgICAgYmludG9vbHMuY2I1OEVuY29kZSh0aGlzLnRvQnVmZmVyKCkpLFxuICAgICAgICAgIFwiaGV4XCIsXG4gICAgICAgICAgXCJjYjU4XCIsXG4gICAgICAgICAgXCJoZXhcIlxuICAgICAgICApXG4gICAgICB9XG4gICAgICBkZWZhdWx0OiB7XG4gICAgICAgIHRocm93IG5ldyBVbmtub3duRm9ybWF0RXJyb3IoXG4gICAgICAgICAgYFNwZWNpZmllZCBmb3JtYXQgJyR7Zm9ybWF0fScgaXMgdW5rbm93biwgc2hvdWxkIGJlIGhleCBvciBjYjU4LmBcbiAgICAgICAgKVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGNsb25lKCk6IHRoaXMge1xuICAgIGNvbnN0IHV0eG86IFVUWE8gPSBuZXcgVVRYTygpXG4gICAgdXR4by5mcm9tQnVmZmVyKHRoaXMudG9CdWZmZXIoKSlcbiAgICByZXR1cm4gdXR4byBhcyB0aGlzXG4gIH1cblxuICBjcmVhdGUoXG4gICAgY29kZWNJRDogbnVtYmVyID0gUGxhdGZvcm1WTUNvbnN0YW50cy5MQVRFU1RDT0RFQyxcbiAgICB0eGlkOiBCdWZmZXIgPSB1bmRlZmluZWQsXG4gICAgb3V0cHV0aWR4OiBCdWZmZXIgfCBudW1iZXIgPSB1bmRlZmluZWQsXG4gICAgYXNzZXRJRDogQnVmZmVyID0gdW5kZWZpbmVkLFxuICAgIG91dHB1dDogQmFzZU91dHB1dCA9IHVuZGVmaW5lZFxuICApOiB0aGlzIHtcbiAgICByZXR1cm4gbmV3IFVUWE8oY29kZWNJRCwgdHhpZCwgb3V0cHV0aWR4LCBhc3NldElELCBvdXRwdXQpIGFzIHRoaXNcbiAgfVxufVxuXG5leHBvcnQgY2xhc3MgQXNzZXRBbW91bnREZXN0aW5hdGlvbiBleHRlbmRzIFN0YW5kYXJkQXNzZXRBbW91bnREZXN0aW5hdGlvbjxcbiAgVHJhbnNmZXJhYmxlT3V0cHV0LFxuICBUcmFuc2ZlcmFibGVJbnB1dFxuPiB7XG4gIHByb3RlY3RlZCBzaWduZXJzOiBCdWZmZXJbXVxuICBwcm90ZWN0ZWQgb3V0cHV0T3duZXJzOiBPdXRwdXRPd25lcnNbXSA9IFtdXG5cbiAgZ2V0U2lnbmVycyA9ICgpOiBCdWZmZXJbXSA9PiB0aGlzLnNpZ25lcnNcblxuICBzZXRPdXRwdXRPd25lcnMgPSAob3duZXJzOiBPdXRwdXRPd25lcnNbXSkgPT4gKHRoaXMub3V0cHV0T3duZXJzID0gb3duZXJzKVxuICBnZXRPdXRwdXRPd25lcnMgPSAoKTogT3V0cHV0T3duZXJzW10gPT4gdGhpcy5vdXRwdXRPd25lcnNcblxuICBjb25zdHJ1Y3RvcihcbiAgICBkZXN0aW5hdGlvbnM6IEJ1ZmZlcltdLFxuICAgIGRlc3RpbmF0aW9uc1RocmVzaG9sZDogbnVtYmVyLFxuICAgIHNlbmRlcnM6IEJ1ZmZlcltdLFxuICAgIHNpZ25lcnM6IEJ1ZmZlcltdLFxuICAgIGNoYW5nZUFkZHJlc3NlczogQnVmZmVyW10sXG4gICAgY2hhbmdlQWRkcmVzc2VzVGhyZXNob2xkOiBudW1iZXJcbiAgKSB7XG4gICAgc3VwZXIoXG4gICAgICBkZXN0aW5hdGlvbnMsXG4gICAgICBkZXN0aW5hdGlvbnNUaHJlc2hvbGQsXG4gICAgICBzZW5kZXJzLFxuICAgICAgY2hhbmdlQWRkcmVzc2VzLFxuICAgICAgY2hhbmdlQWRkcmVzc2VzVGhyZXNob2xkXG4gICAgKVxuICAgIHRoaXMuc2lnbmVycyA9IHNpZ25lcnNcbiAgfVxufVxuXG4vKipcbiAqIENsYXNzIHJlcHJlc2VudGluZyBhIHNldCBvZiBbW1VUWE9dXXMuXG4gKi9cbmV4cG9ydCBjbGFzcyBVVFhPU2V0IGV4dGVuZHMgU3RhbmRhcmRVVFhPU2V0PFVUWE8+IHtcbiAgcHJvdGVjdGVkIF90eXBlTmFtZSA9IFwiVVRYT1NldFwiXG4gIHByb3RlY3RlZCBfdHlwZUlEID0gdW5kZWZpbmVkXG5cbiAgLy9zZXJpYWxpemUgaXMgaW5oZXJpdGVkXG5cbiAgZGVzZXJpYWxpemUoZmllbGRzOiBvYmplY3QsIGVuY29kaW5nOiBTZXJpYWxpemVkRW5jb2RpbmcgPSBcImhleFwiKSB7XG4gICAgc3VwZXIuZGVzZXJpYWxpemUoZmllbGRzLCBlbmNvZGluZylcbiAgICBsZXQgdXR4b3MgPSB7fVxuICAgIGZvciAobGV0IHV0eG9pZCBpbiBmaWVsZHNbXCJ1dHhvc1wiXSkge1xuICAgICAgbGV0IHV0eG9pZENsZWFuZWQ6IHN0cmluZyA9IHNlcmlhbGl6YXRpb24uZGVjb2RlcihcbiAgICAgICAgdXR4b2lkLFxuICAgICAgICBlbmNvZGluZyxcbiAgICAgICAgXCJiYXNlNThcIixcbiAgICAgICAgXCJiYXNlNThcIlxuICAgICAgKVxuICAgICAgdXR4b3NbYCR7dXR4b2lkQ2xlYW5lZH1gXSA9IG5ldyBVVFhPKClcbiAgICAgIHV0eG9zW2Ake3V0eG9pZENsZWFuZWR9YF0uZGVzZXJpYWxpemUoXG4gICAgICAgIGZpZWxkc1tcInV0eG9zXCJdW2Ake3V0eG9pZH1gXSxcbiAgICAgICAgZW5jb2RpbmdcbiAgICAgIClcbiAgICB9XG4gICAgbGV0IGFkZHJlc3NVVFhPcyA9IHt9XG4gICAgZm9yIChsZXQgYWRkcmVzcyBpbiBmaWVsZHNbXCJhZGRyZXNzVVRYT3NcIl0pIHtcbiAgICAgIGxldCBhZGRyZXNzQ2xlYW5lZDogc3RyaW5nID0gc2VyaWFsaXphdGlvbi5kZWNvZGVyKFxuICAgICAgICBhZGRyZXNzLFxuICAgICAgICBlbmNvZGluZyxcbiAgICAgICAgXCJjYjU4XCIsXG4gICAgICAgIFwiaGV4XCJcbiAgICAgIClcbiAgICAgIGxldCB1dHhvYmFsYW5jZSA9IHt9XG4gICAgICBmb3IgKGxldCB1dHhvaWQgaW4gZmllbGRzW1wiYWRkcmVzc1VUWE9zXCJdW2Ake2FkZHJlc3N9YF0pIHtcbiAgICAgICAgbGV0IHV0eG9pZENsZWFuZWQ6IHN0cmluZyA9IHNlcmlhbGl6YXRpb24uZGVjb2RlcihcbiAgICAgICAgICB1dHhvaWQsXG4gICAgICAgICAgZW5jb2RpbmcsXG4gICAgICAgICAgXCJiYXNlNThcIixcbiAgICAgICAgICBcImJhc2U1OFwiXG4gICAgICAgIClcbiAgICAgICAgdXR4b2JhbGFuY2VbYCR7dXR4b2lkQ2xlYW5lZH1gXSA9IHNlcmlhbGl6YXRpb24uZGVjb2RlcihcbiAgICAgICAgICBmaWVsZHNbXCJhZGRyZXNzVVRYT3NcIl1bYCR7YWRkcmVzc31gXVtgJHt1dHhvaWR9YF0sXG4gICAgICAgICAgZW5jb2RpbmcsXG4gICAgICAgICAgXCJkZWNpbWFsU3RyaW5nXCIsXG4gICAgICAgICAgXCJCTlwiXG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIGFkZHJlc3NVVFhPc1tgJHthZGRyZXNzQ2xlYW5lZH1gXSA9IHV0eG9iYWxhbmNlXG4gICAgfVxuICAgIHRoaXMudXR4b3MgPSB1dHhvc1xuICAgIHRoaXMuYWRkcmVzc1VUWE9zID0gYWRkcmVzc1VUWE9zXG4gIH1cblxuICBwYXJzZVVUWE8odXR4bzogVVRYTyB8IHN0cmluZyk6IFVUWE8ge1xuICAgIGNvbnN0IHV0eG92YXI6IFVUWE8gPSBuZXcgVVRYTygpXG4gICAgLy8gZm9yY2UgYSBjb3B5XG4gICAgaWYgKHR5cGVvZiB1dHhvID09PSBcInN0cmluZ1wiKSB7XG4gICAgICB1dHhvdmFyLmZyb21CdWZmZXIoYmludG9vbHMuY2I1OERlY29kZSh1dHhvKSlcbiAgICB9IGVsc2UgaWYgKHV0eG8gaW5zdGFuY2VvZiBTdGFuZGFyZFVUWE8pIHtcbiAgICAgIHV0eG92YXIuZnJvbUJ1ZmZlcih1dHhvLnRvQnVmZmVyKCkpIC8vIGZvcmNlcyBhIGNvcHlcbiAgICB9IGVsc2Uge1xuICAgICAgLyogaXN0YW5idWwgaWdub3JlIG5leHQgKi9cbiAgICAgIHRocm93IG5ldyBVVFhPRXJyb3IoXG4gICAgICAgIFwiRXJyb3IgLSBVVFhPLnBhcnNlVVRYTzogdXR4byBwYXJhbWV0ZXIgaXMgbm90IGEgVVRYTyBvciBzdHJpbmdcIlxuICAgICAgKVxuICAgIH1cbiAgICByZXR1cm4gdXR4b3ZhclxuICB9XG5cbiAgY3JlYXRlKC4uLmFyZ3M6IGFueVtdKTogdGhpcyB7XG4gICAgcmV0dXJuIG5ldyBVVFhPU2V0KCkgYXMgdGhpc1xuICB9XG5cbiAgY2xvbmUoKTogdGhpcyB7XG4gICAgY29uc3QgbmV3c2V0OiBVVFhPU2V0ID0gdGhpcy5jcmVhdGUoKVxuICAgIGNvbnN0IGFsbFVUWE9zOiBVVFhPW10gPSB0aGlzLmdldEFsbFVUWE9zKClcbiAgICBuZXdzZXQuYWRkQXJyYXkoYWxsVVRYT3MpXG4gICAgcmV0dXJuIG5ld3NldCBhcyB0aGlzXG4gIH1cblxuICBfZmVlQ2hlY2soZmVlOiBCTiwgZmVlQXNzZXRJRDogQnVmZmVyKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIChcbiAgICAgIHR5cGVvZiBmZWUgIT09IFwidW5kZWZpbmVkXCIgJiZcbiAgICAgIHR5cGVvZiBmZWVBc3NldElEICE9PSBcInVuZGVmaW5lZFwiICYmXG4gICAgICBmZWUuZ3QobmV3IEJOKDApKSAmJlxuICAgICAgZmVlQXNzZXRJRCBpbnN0YW5jZW9mIEJ1ZmZlclxuICAgIClcbiAgfVxuXG4gIGdldENvbnN1bWFibGVVWFRPID0gKFxuICAgIGFzT2Y6IEJOID0gVW5peE5vdygpLFxuICAgIHN0YWtlYWJsZTogYm9vbGVhbiA9IGZhbHNlXG4gICk6IFVUWE9bXSA9PiB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0QWxsVVRYT3MoKS5maWx0ZXIoKHV0eG86IFVUWE8pID0+IHtcbiAgICAgIGlmIChzdGFrZWFibG