@bitgo/utxo-ord
Version:
Utilities for building ordinals with BitGo utxo-lib
192 lines (182 loc) • 19.8 kB
JavaScript
/*
Classes used for tracking sats across transactions.
https://github.com/casey/ord/blob/master/bip.mediawiki#design
> The ordinal numbers of sats in transaction inputs are transferred to output sats in
> first-in-first-out order, according to the size and order of the transactions inputs and outputs.
Sample scenario:
inputs i0, i1, i2
outputs u0, u1
inscriptions r0, r1, r2, r3, r4
createOutputs(
[i0, i1],
[
[u0, [r0, r1]],
[u1, [r2, r3]],
]
);
r4 is donated to the miner
┌────────┬────────┐
│ i0 │ u0 │
│ │ │
│ r0 ┼ │
│ │ │
├────────┤ │
│ i1 │ │
│ r1 ┼ │
│ │ │
│ ├────────┤
│ │ u1 │
│ r2 ┼ │
│ │ │
├────────┤ │
│ i2 │ │
│ r3 ┼ │
│ │ │
│ │ │
│ ├────────┘
│ │
│ r4 ┼
│ │
└────────┘
*/
import { SatRange } from './SatRange';
export class InvalidOrdOutput extends Error {
constructor(message, value, ordinals) {
super(message);
this.value = value;
this.ordinals = ordinals;
}
}
/**
* The ordinal metadata for an output
*/
export class OrdOutput {
/**
* @param value - the input value
* @param ordinals - The ordinal ranges of an output, relative to the first satoshi.
* Required to be ordered and non-overlapping.
* Not required to be exhaustive.
*/
constructor(value, ordinals = []) {
this.value = value;
this.ordinals = ordinals;
const maxRange = this.asSatRange();
ordinals.forEach((r, i) => {
if (!maxRange.isSupersetOf(r)) {
throw new InvalidOrdOutput(`range ${r} outside output maxRange ${maxRange}`, value, ordinals);
}
if (0 < i) {
const prevRange = ordinals[i - 1];
if (r.start <= prevRange.end) {
throw new InvalidOrdOutput(`SatRange #${i - 1} ${prevRange} overlaps SatRange #${i} ${r}`, value, ordinals);
}
}
});
}
/**
* @param other
* @return OrdOutput extended by other.value and SatRanges shifted by this.value
*/
joinedWith(other) {
return new OrdOutput(this.value + other.value, [
...this.ordinals,
...other.ordinals.map((r) => r.shiftedBy(this.value)),
]);
}
/**
* @param ords
* @return single OrdOutput containing all SatRanges, shifted by preceding output values
*/
static joinAll(ords) {
if (ords.length === 0) {
throw new TypeError(`empty input`);
}
return ords.reduce((a, b) => a.joinedWith(b));
}
asSatRange() {
return new SatRange(BigInt(0), this.value - BigInt(1));
}
/**
* @param r
* @return new OrdOutput with all ranges fully contained in _r_. SatRanges are aligned to new start.
*/
fromSatRange(r) {
return new OrdOutput(r.size(), this.ordinals.flatMap((s) => {
if (r.intersectsWith(s)) {
if (!r.isSupersetOf(s)) {
throw new Error(`partial overlap in ${r} and ${s}`);
}
return s.shiftedBy(-r.start);
}
return [];
}));
}
/**
* @param value
* @return first OrdOutput with value `value`, second OrdOutput with remaining value.
* With respective SatRanges
*/
splitAt(value) {
if (this.value < value) {
throw new Error(`must split at value inside range`);
}
return [
this.fromSatRange(new SatRange(BigInt(0), value - BigInt(1))),
this.fromSatRange(new SatRange(value, this.value - BigInt(1))),
];
}
/**
* Like splitAt but returns _null_ where a zero-sized OrdOutput would be
* @param value
*/
splitAtAllowZero(value) {
if (value === BigInt(0)) {
return [null, this.fromSatRange(this.asSatRange())];
}
if (value === this.value) {
return [this.fromSatRange(this.asSatRange()), null];
}
return this.splitAt(value);
}
/**
* Split output successively at values.
* @param values
* @param exact - when set, ensure that value sum matches _this.value_
* @param allowZero - when set, return _null_ for zero-sized values
* @return (OrdOutput | null)[]. Zero-sized outputs are substituted with _null_.
*/
splitAllWithParams(values, { exact = false, allowZero = false }) {
if (values.length === 0) {
throw new Error(`invalid argument`);
}
if (exact) {
const valueSum = values.reduce((a, b) => a + b, BigInt(0));
if (this.value !== valueSum) {
throw new Error(`value sum ${valueSum} does not match this.value ${this.value}`);
}
return this.splitAllWithParams(values.slice(0, -1), { allowZero, exact: false });
}
const [v, ...rest] = values;
const [a, b] = allowZero ? this.splitAtAllowZero(v) : this.splitAt(v);
if (rest.length) {
if (b === null) {
throw new Error(`invalid remainder`);
}
else {
return [a, ...b.splitAllWithParams(rest, { exact, allowZero })];
}
}
else {
return [a, b];
}
}
/**
* Split output successively at values.
* @param values
* @return OrdOutput[] with length _values.length + 1_
*/
splitAll(values) {
return this.splitAllWithParams(values, { exact: false, allowZero: false });
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiT3JkT3V0cHV0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL09yZE91dHB1dC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FrREc7QUFFSCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRXRDLE1BQU0sT0FBTyxnQkFBaUIsU0FBUSxLQUFLO0lBQ3pDLFlBQVksT0FBZSxFQUFTLEtBQWEsRUFBUyxRQUFvQjtRQUM1RSxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFEbUIsVUFBSyxHQUFMLEtBQUssQ0FBUTtRQUFTLGFBQVEsR0FBUixRQUFRLENBQVk7SUFFOUUsQ0FBQztDQUNGO0FBRUQ7O0dBRUc7QUFDSCxNQUFNLE9BQU8sU0FBUztJQUNwQjs7Ozs7T0FLRztJQUNILFlBQW1CLEtBQWEsRUFBUyxXQUF1QixFQUFFO1FBQS9DLFVBQUssR0FBTCxLQUFLLENBQVE7UUFBUyxhQUFRLEdBQVIsUUFBUSxDQUFpQjtRQUNoRSxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7UUFDbkMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRTtZQUN4QixJQUFJLENBQUMsUUFBUSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO2dCQUM5QixNQUFNLElBQUksZ0JBQWdCLENBQUMsU0FBUyxDQUFDLDRCQUE0QixRQUFRLEVBQUUsRUFBRSxLQUFLLEVBQUUsUUFBUSxDQUFDLENBQUM7WUFDaEcsQ0FBQztZQUNELElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUNWLE1BQU0sU0FBUyxHQUFHLFFBQVEsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBQ2xDLElBQUksQ0FBQyxDQUFDLEtBQUssSUFBSSxTQUFTLENBQUMsR0FB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LENBQUMsR0FBRyxTQUFTLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUN0RSxJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNoQixJQUFJLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQztnQkFDZixNQUFNLElBQUksS0FBSyxDQUFDLG1CQUFtQixDQUFDLENBQUM7WUFDdkMsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLE9BQU8sQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUMsa0JBQWtCLENBQUMsSUFBSSxFQUFFLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUMsQ0FBQztZQUNsRSxDQUFDO1FBQ0gsQ0FBQzthQUFNLENBQUM7WUFDTixPQUFPLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ2hCLENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILFFBQVEsQ0FBQyxNQUFnQjtRQUN2QixPQUFPLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxNQUFNLEVBQUUsRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsQ0FBZ0IsQ0FBQztJQUM1RixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuXG5DbGFzc2VzIHVzZWQgZm9yIHRyYWNraW5nIHNhdHMgYWNyb3NzIHRyYW5zYWN0aW9ucy5cblxuaHR0cHM6Ly9naXRodWIuY29tL2Nhc2V5L29yZC9ibG9iL21hc3Rlci9iaXAubWVkaWF3aWtpI2Rlc2lnblxuXG4+IFRoZSBvcmRpbmFsIG51bWJlcnMgb2Ygc2F0cyBpbiB0cmFuc2FjdGlvbiBpbnB1dHMgYXJlIHRyYW5zZmVycmVkIHRvIG91dHB1dCBzYXRzIGluXG4+IGZpcnN0LWluLWZpcnN0LW91dCBvcmRlciwgYWNjb3JkaW5nIHRvIHRoZSBzaXplIGFuZCBvcmRlciBvZiB0aGUgdHJhbnNhY3Rpb25zIGlucHV0cyBhbmQgb3V0cHV0cy5cblxuXG5TYW1wbGUgc2NlbmFyaW86XG4gICBpbnB1dHMgICAgICAgICBpMCwgaTEsIGkyXG4gICBvdXRwdXRzICAgICAgICB1MCwgdTFcbiAgIGluc2NyaXB0aW9ucyAgIHIwLCByMSwgcjIsIHIzLCByNFxuXG5cbmNyZWF0ZU91dHB1dHMoXG4gIFtpMCwgaTFdLFxuICBbXG4gICAgW3UwLCBbcjAsIHIxXV0sXG4gICAgW3UxLCBbcjIsIHIzXV0sXG4gIF1cbik7XG5cbiAgcjQgaXMgZG9uYXRlZCB0byB0aGUgbWluZXJcblxuICDilIzilIDilIDilIDilIDilIDilIDilIDilIDilKzilIDilIDilIDilIDilIDilIDilIDilIDilJBcbiAg4pSCIGkwICAgICDilIIgdTAgICAgIOKUglxuICDilIIgICAgICAgIOKUgiAgICAgICAg4pSCXG4gIOKUgiAgICAgcjAg4pS8ICAgICAgICDilIJcbiAg4pSCICAgICAgICDilIIgICAgICAgIOKUglxuICDilJzilIDilIDilIDilIDilIDilIDilIDilIDilKQgICAgICAgIOKUglxuICDilIIgaTEgICAgIOKUgiAgICAgICAg4pSCXG4gIOKUgiAgICAgcjEg4pS8ICAgICAgICDilIJcbiAg4pSCICAgICAgICDilIIgICAgICAgIOKUglxuICDilIIgICAgICAgIOKUnOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUpFxuICDilIIgICAgICAgIOKUgiB1MSAgICAg4pSCXG4gIOKUgiAgICAgcjIg4pS8ICAgICAgICDilIJcbiAg4pSCICAgICAgICDilIIgICAgICAgIOKUglxuICDilJzilIDilIDilIDilIDilIDilIDilIDilIDilKQgICAgICAgIOKUglxuICDilIIgaTIgICAgIOKUgiAgICAgICAg4pSCXG4gIOKUgiAgICAgcjMg4pS8ICAgICAgICDilIJcbiAg4pSCICAgICAgICDilIIgICAgICAgIOKUglxuICDilIIgICAgICAgIOKUgiAgICAgICAg4pSCXG4gIOKUgiAgICAgICAg4pSc4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSYXG4gIOKUgiAgICAgICAg4pSCXG4gIOKUgiAgICAgcjQg4pS8XG4gIOKUgiAgICAgICAg4pSCXG4gIOKUlOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUmFxuXG4gKi9cblxuaW1wb3J0IHsgU2F0UmFuZ2UgfSBmcm9tICcuL1NhdFJhbmdlJztcblxuZXhwb3J0IGNsYXNzIEludmFsaWRPcmRPdXRwdXQgZXh0ZW5kcyBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2U6IHN0cmluZywgcHVibGljIHZhbHVlOiBiaWdpbnQsIHB1YmxpYyBvcmRpbmFsczogU2F0UmFuZ2VbXSkge1xuICAgIHN1cGVyKG1lc3NhZ2UpO1xuICB9XG59XG5cbi8qKlxuICogVGhlIG9yZGluYWwgbWV0YWRhdGEgZm9yIGFuIG91dHB1dFxuICovXG5leHBvcnQgY2xhc3MgT3JkT3V0cHV0IHtcbiAgLyoqXG4gICAqIEBwYXJhbSB2YWx1ZSAtIHRoZSBpbnB1dCB2YWx1ZVxuICAgKiBAcGFyYW0gb3JkaW5hbHMgLSBUaGUgb3JkaW5hbCByYW5nZXMgb2YgYW4gb3V0cHV0LCByZWxhdGl2ZSB0byB0aGUgZmlyc3Qgc2F0b3NoaS5cbiAgICogICAgICAgICAgICAgICAgICAgUmVxdWlyZWQgdG8gYmUgb3JkZXJlZCBhbmQgbm9uLW92ZXJsYXBwaW5nLlxuICAgKiAgICAgICAgICAgICAgICAgICBOb3QgcmVxdWlyZWQgdG8gYmUgZXhoYXVzdGl2ZS5cbiAgICovXG4gIGNvbnN0cnVjdG9yKHB1YmxpYyB2YWx1ZTogYmlnaW50LCBwdWJsaWMgb3JkaW5hbHM6IFNhdFJhbmdlW10gPSBbXSkge1xuICAgIGNvbnN0IG1heFJhbmdlID0gdGhpcy5hc1NhdFJhbmdlKCk7XG4gICAgb3JkaW5hbHMuZm9yRWFjaCgociwgaSkgPT4ge1xuICAgICAgaWYgKCFtYXhSYW5nZS5pc1N1cGVyc2V0T2YocikpIHtcbiAgICAgICAgdGhyb3cgbmV3IEludmFsaWRPcmRPdXRwdXQoYHJhbmdlICR7cn0gb3V0c2lkZSBvdXRwdXQgbWF4UmFuZ2UgJHttYXhSYW5nZX1gLCB2YWx1ZSwgb3JkaW5hbHMpO1xuICAgICAgfVxuICAgICAgaWYgKDAgPCBpKSB7XG4gICAgICAgIGNvbnN0IHByZXZSYW5nZSA9IG9yZGluYWxzW2kgLSAxXTtcbiAgICAgICAgaWYgKHIuc3RhcnQgPD0gcHJldlJhbmdlLmVuZCkge1xuICAgICAgICAgIHRocm93IG5ldyBJbnZhbGlkT3JkT3V0cHV0KGBTYXRSYW5nZSAjJHtpIC0gMX0gJHtwcmV2UmFuZ2V9IG92ZXJsYXBzIFNhdFJhbmdlICMke2l9ICR7cn1gLCB2YWx1ZSwgb3JkaW5hbHMpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIG90aGVyXG4gICAqIEByZXR1cm4gT3JkT3V0cHV0IGV4dGVuZGVkIGJ5IG90aGVyLnZhbHVlIGFuZCBTYXRSYW5nZXMgc2hpZnRlZCBieSB0aGlzLnZhbHVlXG4gICAqL1xuICBqb2luZWRXaXRoKG90aGVyOiBPcmRPdXRwdXQpOiBPcmRPdXRwdXQge1xuICAgIHJldHVybiBuZXcgT3JkT3V0cHV0KHRoaXMudmFsdWUgKyBvdGhlci52YWx1ZSwgW1xuICAgICAgLi4udGhpcy5vcmRpbmFscyxcbiAgICAgIC4uLm90aGVyLm9yZGluYWxzLm1hcCgocikgPT4gci5zaGlmdGVkQnkodGhpcy52YWx1ZSkpLFxuICAgIF0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSBvcmRzXG4gICAqIEByZXR1cm4gc2luZ2xlIE9yZE91dHB1dCBjb250YWluaW5nIGFsbCBTYXRSYW5nZXMsIHNoaWZ0ZWQgYnkgcHJlY2VkaW5nIG91dHB1dCB2YWx1ZXNcbiAgICovXG4gIHN0YXRpYyBqb2luQWxsKG9yZHM6IE9yZE91dHB1dFtdKTogT3JkT3V0cHV0IHtcbiAgICBpZiAob3Jkcy5sZW5ndGggPT09IDApIHtcbiAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoYGVtcHR5IGlucHV0YCk7XG4gICAgfVxuICAgIHJldHVybiBvcmRzLnJlZHVjZSgoYSwgYikgPT4gYS5qb2luZWRXaXRoKGIpKTtcbiAgfVxuXG4gIGFzU2F0UmFuZ2UoKTogU2F0UmFuZ2Uge1xuICAgIHJldHVybiBuZXcgU2F0UmFuZ2UoQmlnSW50KDApLCB0aGlzLnZhbHVlIC0gQmlnSW50KDEpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0gclxuICAgKiBAcmV0dXJuIG5ldyBPcmRPdXRwdXQgd2l0aCBhbGwgcmFuZ2VzIGZ1bGx5IGNvbnRhaW5lZCBpbiBfcl8uIFNhdFJhbmdlcyBhcmUgYWxpZ25lZCB0byBuZXcgc3RhcnQuXG4gICAqL1xuICBmcm9tU2F0UmFuZ2UocjogU2F0UmFuZ2UpOiBPcmRPdXRwdXQge1xuICAgIHJldHVybiBuZXcgT3JkT3V0cHV0KFxuICAgICAgci5zaXplKCksXG4gICAgICB0aGlzLm9yZGluYWxzLmZsYXRNYXAoKHMpID0+IHtcbiAgICAgICAgaWYgKHIuaW50ZXJzZWN0c1dpdGgocykpIHtcbiAgICAgICAgICBpZiAoIXIuaXNTdXBlcnNldE9mKHMpKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYHBhcnRpYWwgb3ZlcmxhcCBpbiAke3J9IGFuZCAke3N9YCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBzLnNoaWZ0ZWRCeSgtci5zdGFydCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIFtdO1xuICAgICAgfSlcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB2YWx1ZVxuICAgKiBAcmV0dXJuIGZpcnN0IE9yZE91dHB1dCB3aXRoIHZhbHVlIGB2YWx1ZWAsIHNlY29uZCBPcmRPdXRwdXQgd2l0aCByZW1haW5pbmcgdmFsdWUuXG4gICAqICAgICAgICAgV2l0aCByZXNwZWN0aXZlIFNhdFJhbmdlc1xuICAgKi9cbiAgc3BsaXRBdCh2YWx1ZTogYmlnaW50KTogW09yZE91dHB1dCwgT3JkT3V0cHV0XSB7XG4gICAgaWYgKHRoaXMudmFsdWUgPCB2YWx1ZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBtdXN0IHNwbGl0IGF0IHZhbHVlIGluc2lkZSByYW5nZWApO1xuICAgIH1cbiAgICByZXR1cm4gW1xuICAgICAgdGhpcy5mcm9tU2F0UmFuZ2UobmV3IFNhdFJhbmdlKEJpZ0ludCgwKSwgdmFsdWUgLSBCaWdJbnQoMSkpKSxcbiAgICAgIHRoaXMuZnJvbVNhdFJhbmdlKG5ldyBTYXRSYW5nZSh2YWx1ZSwgdGhpcy52YWx1ZSAtIEJpZ0ludCgxKSkpLFxuICAgIF07XG4gIH1cblxuICAvKipcbiAgICogTGlrZSBzcGxpdEF0IGJ1dCByZXR1cm5zIF9udWxsXyB3aGVyZSBhIHplcm8tc2l6ZWQgT3JkT3V0cHV0IHdvdWxkIGJlXG4gICAqIEBwYXJhbSB2YWx1ZVxuICAgKi9cbiAgc3BsaXRBdEFsbG93WmVybyh2YWx1ZTogYmlnaW50KTogW09yZE91dHB1dCB8IG51bGwsIE9yZE91dHB1dCB8IG51bGxdIHtcbiAgICBpZiAodmFsdWUgPT09IEJpZ0ludCgwKSkge1xuICAgICAgcmV0dXJuIFtudWxsLCB0aGlzLmZyb21TYXRSYW5nZSh0aGlzLmFzU2F0UmFuZ2UoKSldO1xuICAgIH1cbiAgICBpZiAodmFsdWUgPT09IHRoaXMudmFsdWUpIHtcbiAgICAgIHJldHVybiBbdGhpcy5mcm9tU2F0UmFuZ2UodGhpcy5hc1NhdFJhbmdlKCkpLCBudWxsXTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuc3BsaXRBdCh2YWx1ZSk7XG4gIH1cblxuICAvKipcbiAgICogU3BsaXQgb3V0cHV0IHN1Y2Nlc3NpdmVseSBhdCB2YWx1ZXMuXG4gICAqIEBwYXJhbSB2YWx1ZXNcbiAgICogQHBhcmFtIGV4YWN0IC0gd2hlbiBzZXQsIGVuc3VyZSB0aGF0IHZhbHVlIHN1bSBtYXRjaGVzIF90aGlzLnZhbHVlX1xuICAgKiBAcGFyYW0gYWxsb3daZXJvIC0gd2hlbiBzZXQsIHJldHVybiBfbnVsbF8gZm9yIHplcm8tc2l6ZWQgdmFsdWVzXG4gICAqIEByZXR1cm4gKE9yZE91dHB1dCB8IG51bGwpW10uIFplcm8tc2l6ZWQgb3V0cHV0cyBhcmUgc3Vic3RpdHV0ZWQgd2l0aCBfbnVsbF8uXG4gICAqL1xuICBzcGxpdEFsbFdpdGhQYXJhbXMoXG4gICAgdmFsdWVzOiBiaWdpbnRbXSxcbiAgICB7IGV4YWN0ID0gZmFsc2UsIGFsbG93WmVybyA9IGZhbHNlIH06IHsgYWxsb3daZXJvPzogYm9vbGVhbjsgZXhhY3Q/OiBib29sZWFuIH1cbiAgKTogKE9yZE91dHB1dCB8IG51bGwpW10ge1xuICAgIGlmICh2YWx1ZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYGludmFsaWQgYXJndW1lbnRgKTtcbiAgICB9XG4gICAgaWYgKGV4YWN0KSB7XG4gICAgICBjb25zdCB2YWx1ZVN1bSA9IHZhbHVlcy5yZWR1Y2UoKGEsIGIpID0+IGEgKyBiLCBCaWdJbnQoMCkpO1xuICAgICAgaWYgKHRoaXMudmFsdWUgIT09IHZhbHVlU3VtKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgdmFsdWUgc3VtICR7dmFsdWVTdW19IGRvZXMgbm90IG1hdGNoIHRoaXMudmFsdWUgJHt0aGlzLnZhbHVlfWApO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMuc3BsaXRBbGxXaXRoUGFyYW1zKHZhbHVlcy5zbGljZSgwLCAtMSksIHsgYWxsb3daZXJvLCBleGFjdDogZmFsc2UgfSk7XG4gICAgfVxuICAgIGNvbnN0IFt2LCAuLi5yZXN0XSA9IHZhbHVlcztcbiAgICBjb25zdCBbYSwgYl0gPSBhbGxvd1plcm8gPyB0aGlzLnNwbGl0QXRBbGxvd1plcm8odikgOiB0aGlzLnNwbGl0QXQodik7XG4gICAgaWYgKHJlc3QubGVuZ3RoKSB7XG4gICAgICBpZiAoYiA9PT0gbnVsbCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYGludmFsaWQgcmVtYWluZGVyYCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gW2EsIC4uLmIuc3BsaXRBbGxXaXRoUGFyYW1zKHJlc3QsIHsgZXhhY3QsIGFsbG93WmVybyB9KV07XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBbYSwgYl07XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFNwbGl0IG91dHB1dCBzdWNjZXNzaXZlbHkgYXQgdmFsdWVzLlxuICAgKiBAcGFyYW0gdmFsdWVzXG4gICAqIEByZXR1cm4gT3JkT3V0cHV0W10gd2l0aCBsZW5ndGggX3ZhbHVlcy5sZW5ndGggKyAxX1xuICAgKi9cbiAgc3BsaXRBbGwodmFsdWVzOiBiaWdpbnRbXSk6IE9yZE91dHB1dFtdIHtcbiAgICByZXR1cm4gdGhpcy5zcGxpdEFsbFdpdGhQYXJhbXModmFsdWVzLCB7IGV4YWN0OiBmYWxzZSwgYWxsb3daZXJvOiBmYWxzZSB9KSBhcyBPcmRPdXRwdXRbXTtcbiAgfVxufVxuIl19