bitgo
Version:
BitGo JavaScript SDK
398 lines • 68.7 kB
JavaScript
"use strict";
/**
* @prettier
*/
Object.defineProperty(exports, "__esModule", { value: true });
const _ = require("lodash");
const should = require("should");
const utxo_lib_1 = require("@bitgo/utxo-lib");
const sdk_test_1 = require("@bitgo/sdk-test");
const bitgo_1 = require("../../../../src/bitgo");
const account_lib_1 = require("@bitgo/account-lib");
const ethAbi = require("ethereumjs-abi");
const ethUtil = require("ethereumjs-util");
const statics_1 = require("@bitgo/statics");
const sdk_core_1 = require("@bitgo/sdk-core");
describe('ETH-like coins', () => {
_.forEach(['tetc', 'tcelo', 'trbtc'], (coinName) => {
describe(`${coinName}`, () => {
let bitgo;
let basecoin;
let coin;
const sendMultisigTypes = ['address', 'uint256', 'bytes', 'uint256', 'uint256', 'bytes'];
const sendMultisigTokenTypes = ['address', 'uint256', 'address', 'uint256', 'uint256', 'bytes'];
const signatureSaltMap = {
native: {
tetc: 'ETC',
tcelo: 'CELO',
trbtc: 'RSK',
},
token: {
tetc: 'ETC-ERC20',
tcelo: 'CELO-ERC20',
trbtc: 'RSK-ERC20',
},
};
/**
* Get the operation hash that the user key signed
* @param tx The transaction to calculate operatino hash from
* @return The operation hash
*/
const getOperationHash = (tx) => {
const { data } = tx.toJson();
const { tokenContractAddress, expireTime, sequenceId, amount, to } = account_lib_1.Eth.Utils.decodeTransferData(data);
if (coin instanceof statics_1.ContractAddressDefinedToken) {
return ethAbi.soliditySHA3(...[
['string', 'address', 'uint', 'address', 'uint', 'uint'],
[
signatureSaltMap.token[coinName],
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore BG-34579: known compatibility issue with @types/ethereumjs-util
new ethUtil.BN(ethUtil.stripHexPrefix(to), 16),
amount,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore BG-34579: known compatibility issue with @types/ethereumjs-util
new ethUtil.BN(ethUtil.stripHexPrefix(tokenContractAddress), 16),
expireTime,
sequenceId,
],
]);
}
else {
return ethAbi.soliditySHA3(...[
['string', 'address', 'uint', 'uint', 'uint'],
[
signatureSaltMap.native[coinName],
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore BG-34579: known compatibility issue with @types/ethereumjs-util
new ethUtil.BN(ethUtil.stripHexPrefix(to), 16),
amount,
expireTime,
sequenceId,
],
]);
}
};
/**
* Recover the signing address of a signature
* @param tx The transaction to recover a signer from
* @return The eth address of the signer
*/
const recoverSigner = function (tx) {
const { signature } = account_lib_1.Eth.Utils.decodeTransferData(tx.toJson().data);
const { v, r, s } = ethUtil.fromRpcSig(signature);
const operationHash = getOperationHash(tx);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore known compatibility issue with @types/ethereumjs-util
const pubKeyBuffer = ethUtil.ecrecover(operationHash, v, r, s);
return ethUtil.bufferToHex(ethUtil.pubToAddress(ethUtil.importPublic(pubKeyBuffer)));
};
/**
* Build an unsigned account-lib multi-signature send transactino
* @param destination The destination address of the transaction
* @param contractAddress The address of the smart contract processing the transaction
* @param contractSequenceId The sequence id of the contract
* @param nonce The nonce of the sending address
* @param expireTime The expire time of the transaction
* @param amount The amount to send to the recipient
* @param gasPrice The gas price of the transaction
* @param gasLimit The gas limit of the transaction
*/
const buildUnsignedTransaction = async function ({ destination, contractAddress, contractSequenceId = 1, nonce = 0, expireTime = Math.floor(new Date().getTime() / 1000), amount = '100000', gasPrice = '10000', gasLimit = '20000', }) {
const txBuilder = (0, account_lib_1.getBuilder)(coinName);
txBuilder.type(sdk_core_1.TransactionType.Send);
txBuilder.fee({
fee: gasPrice,
gasLimit: gasLimit,
});
txBuilder.counter(nonce);
txBuilder.contract(contractAddress);
const transferBuilder = txBuilder.transfer();
transferBuilder
.coin(coinName)
.expirationTime(expireTime)
.amount(amount)
.to(destination)
.contractSequenceId(contractSequenceId);
return await txBuilder.build();
};
before(function () {
bitgo = sdk_test_1.TestBitGo.decorate(bitgo_1.BitGo, { env: 'mock' });
bitgo.initializeTestVars();
basecoin = bitgo.coin(coinName);
coin = statics_1.coins.get(coinName);
});
describe('Is valid address', () => {
it('Should find valid addresses to be valid', () => {
basecoin.isValidAddress('0x2af9152fc4afd89a8124731bdfb8710c8751f3ed').should.equal(true);
basecoin.isValidAddress('0x2af9152FC4afd89A8124731BdFb8710c8751f3eD').should.equal(true);
});
it('Should find invalid addresses to be invalid', () => {
basecoin.isValidAddress('0x2af9152fc4afd89a8124731bdfb8710c8751f3edd').should.equal(false);
basecoin.isValidAddress('0x2af9152fc4afd89a8124731bdfb8710c8751f3e').should.equal(false);
basecoin.isValidAddress('2af9152fc4afd89a8124731bdfb8710c8751f3ed').should.equal(false);
basecoin.isValidAddress('notanaddress').should.equal(false);
basecoin.isValidAddress('not an address').should.equal(false);
basecoin.isValidAddress('3KgL6DTUb6gEoqSwMMJzyf96ekH8oZtWtZ').should.equal(false);
});
xit('Should not throw when verifying valid addresses', function () {
// FIXME(BG-43225): not implemented
});
xit('Should throw when verifying invalid addresses', function () {
// FIXME(BG-43225): not implemented
});
});
describe('Is valid pub', () => {
it('Should find valid pubs to be valid', () => {
basecoin
.isValidPub('xpub661MyMwAqRbcF9Nc7TbBo1rZAagiWEVPWKbDKThNG8zqjk76HAKLkaSbTn6dK2dQPfuD7xjicxCZVWvj67fP5nQ9W7QURmoMVAX8m6jZsGp')
.should.equal(true);
basecoin
.isValidPub('04614C070C6D1C18A6A2D6EE2BBBE1FF291A0ABA8ED6B55023C03BE42583AC23A743BCB5EF9DB59E14FD7025A9A5D93C6BA89EEFEB40215BF24933D4F2935D14CB')
.should.equal(true);
basecoin.isValidPub('034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa').should.equal(true);
});
it('Should find invalid pubs to be invalid', () => {
basecoin.isValidPub('0x2af9152fc4afd89a8124731bdfb8710c8751f3e').should.equal(false);
basecoin.isValidPub('0x2af9152fc4afd89a8124731bdfb8710c8751f3ed').should.equal(false);
basecoin.isValidPub('2af9152fc4afd89a8124731bdfb8710c8751f3ed').should.equal(false);
basecoin.isValidPub('notapub').should.equal(false);
basecoin.isValidPub('not a pub').should.equal(false);
basecoin.isValidPub('3KgL6DTUb6gEoqSwMMJzyf96ekH8oZtWtZ').should.equal(false);
});
});
describe('Generate keypair', () => {
it('Should generate valid keypair without seed', () => {
const { pub, prv } = basecoin.generateKeyPair();
basecoin.isValidPub(pub).should.equal(true);
const bitgoKey = utxo_lib_1.bip32.fromBase58(prv);
basecoin.isValidPub(bitgoKey.neutered().toBase58()).should.equal(true);
});
it('Should generate valid keypair with seed', () => {
const seed = Buffer.from('c3b09c24731be2851b641d9d5b3f60fa129695c24071768d15654bea207b7bb6', 'hex');
const { pub, prv } = basecoin.generateKeyPair(seed);
basecoin.isValidPub(pub).should.equal(true);
const bitgoKey = utxo_lib_1.bip32.fromBase58(prv);
basecoin.isValidPub(bitgoKey.neutered().toBase58()).should.equal(true);
});
});
describe('Sign transaction:', () => {
const xprv = 'xprv9s21ZrQH143K3D8TXfvAJgHVfTEeQNW5Ys9wZtnUZkqPzFzSjbEJrWC1vZ4GnXCvR7rQL2UFX3RSuYeU9MrERm1XBvACow7c36vnz5iYyj2';
it('should sign transaction internally', async function () {
const key = new account_lib_1.Eth.KeyPair({ prv: xprv });
const destination = '0xfaa8f14f46a99eb439c50e0c3b835cc21dad51b4';
const contractAddress = '0x9e2c5712ab4caf402a98c4bf58c79a0dfe718ad1';
const amount = '100000';
const inputExpireTime = Math.floor(new Date().getTime() / 1000);
const inputSequenceId = 1;
const unsignedTransaction = await buildUnsignedTransaction({
destination,
contractAddress,
amount,
expireTime: inputExpireTime,
contractSequenceId: inputSequenceId,
});
const tx = await basecoin.signTransaction({
prv: key.getKeys().prv,
txPrebuild: {
txHex: unsignedTransaction.toBroadcastFormat(),
},
});
const txBuilder = basecoin.getTransactionBuilder();
txBuilder.from(tx.halfSigned.txHex);
const transaction = await txBuilder.build();
const txJson = transaction.toJson();
txJson.to.should.equal(contractAddress);
let decodedData;
let recipient;
let value;
let data;
let expireTime;
let sequenceId;
if (coin instanceof statics_1.ContractAddressDefinedToken) {
decodedData = ethAbi.rawDecode(sendMultisigTokenTypes, Buffer.from(txJson.data.slice(10), 'hex'));
[recipient, value /* tokenContractAddress */, , expireTime, sequenceId] = decodedData;
data = Buffer.from('');
}
else {
decodedData = ethAbi.rawDecode(sendMultisigTypes, Buffer.from(txJson.data.slice(10), 'hex'));
[recipient, value, data, expireTime, sequenceId] = decodedData;
}
ethUtil.addHexPrefix(recipient).should.equal(destination);
value.toString(10).should.equal(amount);
inputExpireTime.should.equal(parseInt(expireTime.toString('hex'), 16));
inputSequenceId.should.equal(parseInt(sequenceId.toString('hex'), 16));
data.length.should.equal(0);
const recoveredAddress = recoverSigner(transaction);
recoveredAddress.should.equal(key.getAddress());
});
it('should sign transaction internally with an xprv', async function () {
const key = new account_lib_1.Eth.KeyPair({ prv: xprv });
const destination = '0xfaa8f14f46a99eb439c50e0c3b835cc21dad51b4';
const contractAddress = '0x9e2c5712ab4caf402a98c4bf58c79a0dfe718ad1';
const amount = '100000';
const inputExpireTime = Math.floor(new Date().getTime() / 1000);
const inputSequenceId = 1;
const unsignedTransaction = await buildUnsignedTransaction({
destination,
contractAddress,
amount,
expireTime: inputExpireTime,
contractSequenceId: inputSequenceId,
});
const tx = await basecoin.signTransaction({
prv: xprv,
txPrebuild: {
txHex: unsignedTransaction.toBroadcastFormat(),
},
});
const txBuilder = basecoin.getTransactionBuilder();
txBuilder.from(tx.halfSigned.txHex);
const transaction = await txBuilder.build();
const txJson = transaction.toJson();
txJson.to.should.equal(contractAddress);
let decodedData;
let recipient;
let value;
let data;
let expireTime;
let sequenceId;
if (coin instanceof statics_1.ContractAddressDefinedToken) {
decodedData = ethAbi.rawDecode(sendMultisigTokenTypes, Buffer.from(txJson.data.slice(10), 'hex'));
[recipient, value /* tokenContractAddress */, , expireTime, sequenceId] = decodedData;
data = Buffer.from('');
}
else {
decodedData = ethAbi.rawDecode(sendMultisigTypes, Buffer.from(txJson.data.slice(10), 'hex'));
[recipient, value, data, expireTime, sequenceId] = decodedData;
}
ethUtil.addHexPrefix(recipient).should.equal(destination);
value.toString(10).should.equal(amount);
inputExpireTime.should.equal(parseInt(expireTime.toString('hex'), 16));
inputSequenceId.should.equal(parseInt(sequenceId.toString('hex'), 16));
data.length.should.equal(0);
const recoveredAddress = recoverSigner(transaction);
recoveredAddress.should.equal(key.getAddress());
});
it('should sign a half signed transaction', async function () {
const key = new account_lib_1.Eth.KeyPair({ prv: xprv });
const destination = '0xfaa8f14f46a99eb439c50e0c3b835cc21dad51b4';
const contractAddress = '0x9e2c5712ab4caf402a98c4bf58c79a0dfe718ad1';
const amount = '100000';
const inputExpireTime = Math.floor(new Date().getTime() / 1000);
const inputSequenceId = 1;
const unsignedTransaction = await buildUnsignedTransaction({
destination,
contractAddress,
amount,
expireTime: inputExpireTime,
contractSequenceId: inputSequenceId,
});
const tx = await basecoin.signTransaction({
prv: key.getKeys().prv,
txPrebuild: {
txHex: unsignedTransaction.toBroadcastFormat(),
},
});
const fullySignedTx = await basecoin.signTransaction({
prv: key.getKeys().prv,
txPrebuild: {
txHex: tx.halfSigned.txHex,
},
});
fullySignedTx.halfSigned.recipients.length.should.equal(1);
fullySignedTx.halfSigned.recipients[0].address.should.equal(destination);
fullySignedTx.halfSigned.recipients[0].amount.should.equal(amount);
const txBuilder = basecoin.getTransactionBuilder();
txBuilder.from(fullySignedTx.halfSigned.txHex);
const transaction = await txBuilder.build();
const txJson = transaction.toJson();
txJson.to.should.equal(contractAddress);
let decodedData;
let recipient;
let value;
let data;
let expireTime;
let sequenceId;
if (coin instanceof statics_1.ContractAddressDefinedToken) {
decodedData = ethAbi.rawDecode(sendMultisigTokenTypes, Buffer.from(txJson.data.slice(10), 'hex'));
[recipient, value /* tokenContractAddress */, , expireTime, sequenceId] = decodedData;
data = Buffer.from('');
}
else {
decodedData = ethAbi.rawDecode(sendMultisigTypes, Buffer.from(txJson.data.slice(10), 'hex'));
[recipient, value, data, expireTime, sequenceId] = decodedData;
}
ethUtil.addHexPrefix(recipient).should.equal(destination);
value.toString(10).should.equal(amount);
inputExpireTime.should.equal(parseInt(expireTime.toString('hex'), 16));
inputSequenceId.should.equal(parseInt(sequenceId.toString('hex'), 16));
data.length.should.equal(0);
const recoveredAddress = recoverSigner(transaction);
recoveredAddress.should.equal(key.getAddress());
});
it('should fail to sign transaction with invalid tx hex', async function () {
const key = new account_lib_1.Eth.KeyPair({ prv: xprv });
await basecoin
.signTransaction({
prv: key.getKeys().prv,
txPrebuild: {
txHex: '0xinvalid',
},
})
.should.be.rejected();
});
});
describe('Explain transaction:', () => {
const xprv = 'xprv9s21ZrQH143K3D8TXfvAJgHVfTEeQNW5Ys9wZtnUZkqPzFzSjbEJrWC1vZ4GnXCvR7rQL2UFX3RSuYeU9MrERm1XBvACow7c36vnz5iYyj2';
it('should fail if the params object is missing parameters', async function () {
const explainParams = {
feeInfo: { fee: 1 },
txHex: null,
};
await basecoin.explainTransaction(explainParams).should.be.rejectedWith('missing explain tx parameters');
});
it('explain an unsigned transfer transaction', async function () {
const destination = '0xfaa8f14f46a99eb439c50e0c3b835cc21dad51b4';
const contractAddress = '0x9e2c5712ab4caf402a98c4bf58c79a0dfe718ad1';
const unsignedTransaction = await buildUnsignedTransaction({
destination,
contractAddress,
});
const explainParams = {
halfSigned: {
txHex: unsignedTransaction.toBroadcastFormat(),
},
feeInfo: { fee: 1 },
};
const explanation = await basecoin.explainTransaction(explainParams);
should.exist(explanation.id);
// TODO check other fields once account-lib properly explains transaction
});
it('explain a signed transfer transaction', async function () {
const key = new account_lib_1.Eth.KeyPair({ prv: xprv });
const destination = '0xfaa8f14f46a99eb439c50e0c3b835cc21dad51b4';
const contractAddress = '0x9e2c5712ab4caf402a98c4bf58c79a0dfe718ad1';
const unsignedTransaction = await buildUnsignedTransaction({
destination,
contractAddress,
});
const signedTx = await basecoin.signTransaction({
prv: key.getKeys().prv,
txPrebuild: {
txHex: unsignedTransaction.toBroadcastFormat(),
},
});
const explainParams = {
txHex: signedTx.halfSigned.txHex,
feeInfo: { fee: 1 },
};
const explanation = await basecoin.explainTransaction(explainParams);
should.exist(explanation.id);
// TODO check other fields once account-lib properly explains transaction
});
});
});
});
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWJzdHJhY3RFdGhDb2luLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vdGVzdC92Mi91bml0L2NvaW5zL2Fic3RyYWN0RXRoQ29pbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7O0dBRUc7O0FBRUgsNEJBQTRCO0FBQzVCLGlDQUFrQztBQUNsQyw4Q0FBd0M7QUFDeEMsOENBQTRDO0FBQzVDLGlEQUE4QztBQUM5QyxvREFBcUQ7QUFDckQseUNBQXlDO0FBQ3pDLDJDQUEyQztBQUMzQyw0Q0FBb0U7QUFDcEUsOENBQW1FO0FBRW5FLFFBQVEsQ0FBQyxnQkFBZ0IsRUFBRSxHQUFHLEVBQUU7SUFDOUIsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLE1BQU0sRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLEVBQUUsQ0FBQyxRQUFRLEVBQUUsRUFBRTtRQUNqRCxRQUFRLENBQUMsR0FBRyxRQUFRLEVBQUUsRUFBRSxHQUFHLEVBQUU7WUFDM0IsSUFBSSxLQUFLLENBQUM7WUFDVixJQUFJLFFBQVEsQ0FBQztZQUNiLElBQUksSUFBSSxDQUFDO1lBRVQsTUFBTSxpQkFBaUIsR0FBRyxDQUFDLFNBQVMsRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDekYsTUFBTSxzQkFBc0IsR0FBRyxDQUFDLFNBQVMsRUFBRSxTQUFTLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDaEcsTUFBTSxnQkFBZ0IsR0FBRztnQkFDdkIsTUFBTSxFQUFFO29CQUNOLElBQUksRUFBRSxLQUFLO29CQUNYLEtBQUssRUFBRSxNQUFNO29CQUNiLEtBQUssRUFBRSxLQUFLO2lCQUNiO2dCQUNELEtBQUssRUFBRTtvQkFDTCxJQUFJLEVBQUUsV0FBVztvQkFDakIsS0FBSyxFQUFFLFlBQVk7b0JBQ25CLEtBQUssRUFBRSxXQUFXO2lCQUNu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SUFBSSxpQkFBRyxDQUFDLE9BQU8sQ0FBQyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO29CQUMzQyxNQUFNLFdBQVcsR0FBRyw0Q0FBNEMsQ0FBQztvQkFDakUsTUFBTSxlQUFlLEdBQUcsNENBQTRDLENBQUM7b0JBRXJFLE1BQU0sbUJBQW1CLEdBQUcsTUFBTSx3QkFBd0IsQ0FBQzt3QkFDekQsV0FBVzt3QkFDWCxlQUFlO3FCQUNoQixDQUFDLENBQUM7b0JBRUgsTUFBTSxRQUFRLEdBQUcsTUFBTSxRQUFRLENBQUMsZUFBZSxDQUFDO3dCQUM5QyxHQUFHLEVBQUUsR0FBRyxDQUFDLE9BQU8sRUFBRSxDQUFDLEdBQUc7d0JBQ3RCLFVBQVUsRUFBRTs0QkFDVixLQUFLLEVBQUUsbUJBQW1CLENBQUMsaUJBQWlCLEVBQUU7eUJBQy9DO3FCQUNGLENBQUMsQ0FBQztvQkFFSCxNQUFNLGFBQWEsR0FBRzt3QkFDcEIsS0FBSyxFQUFFLFFBQVEsQ0FBQyxVQUFVLENBQUMsS0FBSzt3QkFDaEMsT0FBTyxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRTtxQkFDcEIsQ0FBQztvQkFDRixNQUFNLFdBQVcsR0FBRyxNQUFNLFFBQVEsQ0FBQyxrQkFBa0IsQ0FBQyxhQUFhLENBQUMsQ0FBQztvQkFDckUsTUFBTSxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUM7b0JBQzdCLHlFQUF5RTtnQkFDM0UsQ0FBQyxDQUFDLENBQUM7WUFDTCxDQUFDLENBQUMsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQHByZXR0aWVyXG4gKi9cblxuaW1wb3J0ICogYXMgXyBmcm9tICdsb2Rhc2gnO1xuaW1wb3J0IHNob3VsZCA9IHJlcXVpcmUoJ3Nob3VsZCcpO1xuaW1wb3J0IHsgYmlwMzIgfSBmcm9tICdAYml0Z28vdXR4by1saWInO1xuaW1wb3J0IHsgVGVzdEJpdEdvIH0gZnJvbSAnQGJpdGdvL3Nkay10ZXN0JztcbmltcG9ydCB7IEJpdEdvIH0gZnJvbSAnLi4vLi4vLi4vLi4vc3JjL2JpdGdvJztcbmltcG9ydCB7IGdldEJ1aWxkZXIsIEV0aCB9IGZyb20gJ0BiaXRnby9hY2NvdW50LWxpYic7XG5pbXBvcnQgKiBhcyBldGhBYmkgZnJvbSAnZXRoZXJldW1qcy1hYmknO1xuaW1wb3J0ICogYXMgZXRoVXRpbCBmcm9tICdldGhlcmV1bWpzLXV0aWwnO1xuaW1wb3J0IHsgY29pbnMsIENvbnRyYWN0QWRkcmVzc0RlZmluZWRUb2tlbiB9IGZyb20gJ0BiaXRnby9zdGF0aWNzJztcbmltcG9ydCB7IEJhc2VUcmFuc2FjdGlvbiwgVHJhbnNhY3Rpb25UeXBlIH0gZnJvbSAnQGJpdGdvL3Nkay1jb3JlJztcblxuZGVzY3JpYmUoJ0VUSC1saWtlIGNvaW5zJywgKCkgPT4ge1xuICBfLmZvckVhY2goWyd0ZXRjJywgJ3RjZWxvJywgJ3RyYnRjJ10sIChjb2luTmFtZSkgPT4ge1xuICAgIGRlc2NyaWJlKGAke2NvaW5OYW1lfWAsICgpID0+IHtcbiAgICAgIGxldCBiaXRnbztcbiAgICAgIGxldCBiYXNlY29pbjtcbiAgICAgIGxldCBjb2luO1xuXG4gICAgICBjb25zdCBzZW5kTXVsdGlzaWdUeXBlcyA9IFsnYWRkcmVzcycsICd1aW50MjU2JywgJ2J5dGVzJywgJ3VpbnQyNTYnLCAndWludDI1NicsICdieXRlcyddO1xuICAgICAgY29uc3Qgc2VuZE11bHRpc2lnVG9rZW5UeXBlcyA9IFsnYWRkcmVzcycsICd1aW50MjU2JywgJ2FkZHJlc3MnLCAndWludDI1NicsICd1aW50MjU2JywgJ2J5dGVzJ107XG4gICAgICBjb25zdCBzaWduYXR1cmVTYWx0TWFwID0ge1xuICAgICAgICBuYXRpdmU6IHtcbiAgICAgICAgICB0ZXRjOiAnRVRDJyxcbiAgICAgICAgICB0Y2VsbzogJ0NFTE8nLFxuICAgICAgICAgIHRyYnRjOiAnUlNLJyxcbiAgICAgICAgfSxcbiAgICAgICAgdG9rZW46IHtcbiAgICAgICAgICB0ZXRjOiAnRVRDLUVSQzIwJyxcbiAgICAgICAgICB0Y2VsbzogJ0NFTE8tRVJDMjAnLFxuICAgICAgICAgIHRyYnRjOiAnUlNLLUVSQzIwJyxcbiAgICAgICAgfSxcbiAgICAgIH07XG5cbiAgICAgIC8qKlxuICAgICAgICogR2V0IHRoZSBvcGVyYXRpb24gaGFzaCB0aGF0IHRoZSB1c2VyIGtleSBzaWduZWRcbiAgICAgICAqIEBwYXJhbSB0eCBUaGUgdHJhbnNhY3Rpb24gdG8gY2FsY3VsYXRlIG9wZXJhdGlubyBoYXNoIGZyb21cbiAgICAgICAqIEByZXR1cm4gVGhlIG9wZXJhdGlvbiBoYXNoXG4gICAgICAgKi9cbiAgICAgIGNvbnN0IGdldE9wZXJhdGlvbkhhc2ggPSAodHg6IEJhc2VUcmFuc2FjdGlvbik6IHN0cmluZyA9PiB7XG4gICAgICAgIGNvbnN0IHsgZGF0YSB9ID0gdHgudG9Kc29uKCk7XG4gICAgICAgIGNvbnN0IHsgdG9rZW5Db250cmFjdEFkZHJlc3MsIGV4cGlyZVRpbWUsIHNlcXVlbmNlSWQsIGFtb3VudCwgdG8gfSA9IEV0aC5VdGlscy5kZWNvZGVUcmFuc2ZlckRhdGEoZGF0YSk7XG5cbiAgICAgICAgaWYgKGNvaW4gaW5zdGFuY2VvZiBDb250cmFjdEFkZHJlc3NEZWZpbmVkVG9rZW4pIHtcbiAgICAgICAgICByZXR1cm4gZXRoQWJpLnNvbGlkaXR5U0hBMyhcbiAgICAgICAgICAgIC4uLltcbiAgICAgICAgICAgICAgWydzdHJpbmcnLCAnYWRkcmVzcycsICd1aW50JywgJ2FkZHJlc3MnLCAndWludCcsICd1aW50J10sXG4gICAgICAgICAgICAgIFtcbiAgICAgICAgICAgICAgICBzaWduYXR1cmVTYWx0TWFwLnRva2VuW2NvaW5OYW1lXSxcbiAgICAgICAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L2Jhbi10cy1jb21tZW50XG4gICAgICAgICAgICAgICAgLy8gQHRzLWlnbm9yZSBCRy0zNDU3OToga25vd24gY29tcGF0aWJpbGl0eSBpc3N1ZSB3aXRoIEB0eXBlcy9ldGhlcmV1bWpzLXV0aWxcbiAgICAgICAgICAgICAgICBuZXcgZXRoVXRpbC5CTihldGhVdGlsLnN0cmlwSGV4UHJlZml4KHRvKSwgMTYpLFxuICAgICAgICAgICAgICAgIGFtb3VudCxcbiAgICAgICAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L2Jhbi10cy1jb21tZW50XG4gICAgICAgICAgICAgICAgLy8gQHRzLWlnbm9yZSBCRy0zNDU3OToga25vd24gY29tcGF0aWJpbGl0eSBpc3N1ZSB3aXRoIEB0eXBlcy9ldGhlcmV1bWpzLXV0aWxcbiAgICAgICAgICAgICAgICBuZXcgZXRoVXRpbC5CTihldGhVdGlsLnN0cmlwSGV4UHJlZml4KHRva2VuQ29udHJhY3RBZGRyZXNzKSwgMTYpLFxuICAgICAgICAgICAgICAgIGV4cGlyZVRpbWUsXG4gICAgICAgICAgICAgICAgc2VxdWVuY2VJZCxcbiAgICAgICAgICAgICAgXSxcbiAgICAgICAgICAgIF1cbiAgICAgICAgICApO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiBldGhBYmkuc29saWRpdHlTSEEzKFxuICAgICAgICAgICAgLi4uW1xuICAgICAgICAgICAgICBbJ3N0cmluZycsICdhZGRyZXNzJywgJ3VpbnQnLCAndWludCcsICd1aW50J10sXG4gICAgICAgICAgICAgIFtcbiAgICAgICAgICAgICAgICBzaWduYXR1cmVTYWx0TWFwLm5hdGl2ZVtjb2luTmFtZV0sXG4gICAgICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9iYW4tdHMtY29tbWVudFxuICAgICAgICAgICAgICAgIC8vIEB0cy1pZ25vcmUgQkctMzQ1Nzk6IGtub3duIGNvbXBhdGliaWxpdHkgaXNzdWUgd2l0aCBAdHlwZXMvZXRoZXJldW1qcy11dGlsXG4gICAgICAgICAgICAgICAgbmV3IGV0aFV0aWwuQk4oZXRoVXRpbC5zdHJpcEhleFByZWZpeCh0byksIDE2KSxcbiAgICAgICAgICAgICAgICBhbW91bnQsXG4gICAgICAgICAgICAgICAgZXhwaXJlVGltZSxcbiAgICAgICAgICAgICAgICBzZXF1ZW5jZUlkLFxuICAgICAgICAgICAgICBdLFxuICAgICAgICAgICAgXVxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH07XG5cbiAgICAgIC8qKlxuICAgICAgICogUmVjb3ZlciB0aGUgc2lnbmluZyBhZGRyZXNzIG9mIGEgc2lnbmF0dXJlXG4gICAgICAgKiBAcGFyYW0gdHggVGhlIHRyYW5zYWN0aW9uIHRvIHJlY292ZXIgYSBzaWduZXIgZnJvbVxuICAgICAgICogQHJldHVybiBUaGUgZXRoIGFkZHJlc3Mgb2YgdGhlIHNpZ25lclxuICAgICAgICovXG4gICAgICBjb25zdCByZWNvdmVyU2lnbmVyID0gZnVuY3Rpb24gKHR4OiBCYXNlVHJhbnNhY3Rpb24pIHtcbiAgICAgICAgY29uc3QgeyBzaWduYXR1cmUgfSA9IEV0aC5VdGlscy5kZWNvZGVUcmFuc2ZlckRhdGEodHgudG9Kc29uKCkuZGF0YSk7XG4gICAgICAgIGNvbnN0IHsgdiwgciwgcyB9ID0gZXRoVXRpbC5mcm9tUnBjU2lnKHNpZ25hdHVyZSk7XG4gICAgICAgIGNvbnN0IG9wZXJhdGlvbkhhc2ggPSBnZXRPcGVyYXRpb25IYXNoKHR4KTtcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9iYW4tdHMtY29tbWVudFxuICAgICAgICAvLyBAdHMtaWdub3JlIGtub3duIGNvbXBhdGliaWxpdHkgaXNzdWUgd2l0aCBAdHlwZXMvZXRoZXJldW1qcy11dGlsXG4gICAgICAgIGNvbnN0IHB1YktleUJ1ZmZlciA9IGV0aFV0aWwuZWNyZWNvdmVyKG9wZXJhdGlvbkhhc2gsIHYsIHIsIHMpO1xuICAgICAgICByZXR1cm4gZXRoVXRpbC5idWZmZXJUb0hleChldGhVdGlsLnB1YlRvQWRkcmVzcyhldGhVdGlsLmltcG9ydFB1YmxpYyhwdWJLZXlCdWZmZXIpKSk7XG4gICAgICB9O1xuXG4gICAgICAvKipcbiAgICAgICAqIEJ1aWxkIGFuIHVuc2lnbmVkIGFjY291bnQtbGliIG11bHRpLXNpZ25hdHVyZSBzZW5kIHRyYW5zYWN0aW5vXG4gICAgICAgKiBAcGFyYW0gZGVzdGluYXRpb24gVGhlIGRlc3RpbmF0aW9uIGFkZHJlc3Mgb2YgdGhlIHRyYW5zYWN0aW9uXG4gICAgICAgKiBAcGFyYW0gY29udHJhY3RBZGRyZXNzIFRoZSBhZGRyZXNzIG9mIHRoZSBzbWFydCBjb250cmFjdCBwcm9jZXNzaW5nIHRoZSB0cmFuc2FjdGlvblxuICAgICAgICogQHBhcmFtIGNvbnRyYWN0U2VxdWVuY2VJZCBUaGUgc2VxdWVuY2UgaWQgb2YgdGhlIGNvbnRyYWN0XG4gICAgICAgKiBAcGFyYW0gbm9uY2UgVGhlIG5vbmNlIG9mIHRoZSBzZW5kaW5nIGFkZHJlc3NcbiAgICAgICAqIEBwYXJhbSBleHBpcmVUaW1lIFRoZSBleHBpcmUgdGltZSBvZiB0aGUgdHJhbnNhY3Rpb25cbiAgICAgICAqIEBwYXJhbSBhbW91bnQgVGhlIGFtb3VudCB0byBzZW5kIHRvIHRoZSByZWNpcGllbnRcbiAgICAgICAqIEBwYXJhbSBnYXNQcmljZSBUaGUgZ2FzIHByaWNlIG9mIHRoZSB0cmFuc2FjdGlvblxuICAgICAgICogQHBhcmFtIGdhc0xpbWl0IFRoZSBnYXMgbGltaXQgb2YgdGhlIHRyYW5zYWN0aW9uXG4gICAgICAgKi9cbiAgICAgIGNvbnN0IGJ1aWxkVW5zaWduZWRUcmFuc2FjdGlvbiA9IGFzeW5jIGZ1bmN0aW9uICh7XG4gICAgICAgIGRlc3RpbmF0aW9uLFxuICAgICAgICBjb250cmFjdEFkZHJlc3MsXG4gICAgICAgIGNvbnRyYWN0U2VxdWVuY2VJZCA9IDEsXG4gICAgICAgIG5vbmNlID0gMCxcbiAgICAgICAgZXhwaXJlVGltZSA9IE1hdGguZmxvb3IobmV3IERhdGUoKS5nZXRUaW1lKCkgLyAxMDAwKSxcbiAgICAgICAgYW1vdW50ID0gJzEwMDAwMCcsXG4gICAgICAgIGdhc1ByaWNlID0gJzEwMDAwJyxcbiAgICAgICAgZ2FzTGltaXQgPSAnMjAwMDAnLFxuICAgICAgfSkge1xuICAgICAgICBjb25zdCB0eEJ1aWxkZXI6IEV0aC5UcmFuc2FjdGlvbkJ1aWxkZXIgPSBnZXRCdWlsZGVyKGNvaW5OYW1lKSBhcyBFdGguVHJhbnNhY3Rpb25CdWlsZGVyO1xuICAgICAgICB0eEJ1aWxkZXIudHlwZShUcmFuc2FjdGlvblR5cGUuU2VuZCk7XG4gICAgICAgIHR4QnVpbGRlci5mZWUoe1xuICAgICAgICAgIGZlZTogZ2FzUHJpY2UsXG4gICAgICAgICAgZ2FzTGltaXQ6IGdhc0xpbWl0LFxuICAgICAgICB9KTtcbiAgICAgICAgdHhCdWlsZGVyLmNvdW50ZXIobm9uY2UpO1xuICAgICAgICB0eEJ1aWxkZXIuY29udHJhY3QoY29udHJhY3RBZGRyZXNzKTtcbiAgICAgICAgY29uc3QgdHJhbnNmZXJCdWlsZGVyID0gdHhCdWlsZGVyLnRyYW5zZmVyKCkgYXMgRXRoLlRyYW5zZmVyQnVpbGRlcjtcblxuICAgICAgICB0cmFuc2ZlckJ1aWxkZXJcbiAgICAgICAgICAuY29pbihjb2luTmFtZSlcbiAgICAgICAgICAuZXhwaXJhdGlvblRpbWUoZXhwaXJlVGltZSlcbiAgICAgICAgICAuYW1vdW50KGFtb3VudClcbiAgICAgICAgICAudG8oZGVzdGluYXRpb24pXG4gICAgICAgICAgLmNvbnRyYWN0U2VxdWVuY2VJZChjb250cmFjdFNlcXVlbmNlSWQpO1xuXG4gICAgICAgIHJldHVybiBhd2FpdCB0eEJ1aWxkZXIuYnVpbGQo