UNPKG

bitgo

Version:
398 lines • 68.7 kB
"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