@muirglacier/testcontainers
Version:
A collection of TypeScript + JavaScript tools and libraries for DeFi Blockchain developers to build decentralized finance for Bitcoin
346 lines • 14.6 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());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MasterNodeRegTestContainer = void 0;
const jellyfish_network_1 = require("@muirglacier/jellyfish-network");
const utils_1 = require("../../utils");
const DeFiDContainer_1 = require("../DeFiDContainer");
const index_1 = require("./index");
/**
* RegTest with MasterNode preconfigured
*/
class MasterNodeRegTestContainer extends index_1.RegTestContainer {
/**
* @param {string} [masternodeKey=RegTestFoundationKeys[0]] pair to use for minting
* @param {string} [image=DeFiDContainer.image] docker image name
* @param {DockerOptions} [options]
*/
constructor(masternodeKey = jellyfish_network_1.RegTestFoundationKeys[0], image = DeFiDContainer_1.DeFiDContainer.image, options) {
super(image, options);
this.masternodeKey = masternodeKey;
}
/**
* Additional debug options turned on for traceability.
*/
getCmd(opts) {
return [
...super.getCmd(opts),
'-dummypos=0',
'-spv=1',
'-anchorquorum=2',
`-masternode_operator=${this.masternodeKey.operator.address}`
];
}
/**
* @param {number} nblocks to generate
* @param {string} address to generate to
* @param {number} maxTries
*/
generate(nblocks, address = this.masternodeKey.operator.address, maxTries = 1000000) {
return __awaiter(this, void 0, void 0, function* () {
for (let minted = 0, tries = 0; minted < nblocks && tries < maxTries; tries++) {
const result = yield this.call('generatetoaddress', [1, address, 1]);
if (result === 1) {
minted += 1;
}
}
});
}
/**
* @param {number} nblocks to generate
* @param {number} timeout
* @param {string} address
*/
waitForGenerate(nblocks, timeout = 590000, address = this.masternodeKey.operator.address) {
return __awaiter(this, void 0, void 0, function* () {
const target = (yield this.getBlockCount()) + nblocks;
return yield utils_1.waitForCondition(() => __awaiter(this, void 0, void 0, function* () {
const count = yield this.getBlockCount();
if (count > target) {
return true;
}
yield this.generate(1);
return false;
}), timeout, 100, 'waitForGenerate');
});
}
/**
* This will automatically import the necessary private key for master to mint tokens
*/
start(startOptions = {}) {
const _super = Object.create(null, {
start: { get: () => super.start }
});
return __awaiter(this, void 0, void 0, function* () {
yield _super.start.call(this, startOptions);
yield this.call('importprivkey', [this.masternodeKey.operator.privKey, 'operator', true]);
yield this.call('importprivkey', [this.masternodeKey.owner.privKey, 'owner', true]);
});
}
/**
* Wait for block height by minting towards the target
*
* @param {number} height to wait for
* @param {number} [timeout=90000] in ms
*/
waitForBlockHeight(height, timeout = 590000) {
return __awaiter(this, void 0, void 0, function* () {
return yield utils_1.waitForCondition(() => __awaiter(this, void 0, void 0, function* () {
const count = yield this.getBlockCount();
if (count > height) {
return true;
}
yield this.generate(1);
return false;
}), timeout, 100, 'waitForBlockHeight');
});
}
/**
* Wait for master node wallet coin to be mature for spending.
*
* A coinbase transaction must be 100 blocks deep before you can spend its outputs. This is a
* safeguard to prevent outputs that originate from the coinbase transaction from becoming
* un-spendable (in the event the mined block moves out of the active chain due to a fork).
*
* @param {number} [timeout=180000] in ms
*/
waitForWalletCoinbaseMaturity(timeout = 180000) {
return __awaiter(this, void 0, void 0, function* () {
return yield this.waitForBlockHeight(100, timeout);
});
}
/**
* Wait for in wallet balance to be greater than an amount.
* This allow test that require fund to wait for fund to be filled up before running the tests.
* This method will trigger block generate to get to the required balance faster.
* Set `timeout` to higher accordingly when large balance required.
*
* @param {number} balance to wait for in wallet to be greater than or equal
* @param {number} [timeout=300000] in ms
* @see waitForWalletCoinbaseMaturity
*/
waitForWalletBalanceGTE(balance, timeout = 300000) {
return __awaiter(this, void 0, void 0, function* () {
return yield utils_1.waitForCondition(() => __awaiter(this, void 0, void 0, function* () {
const getbalance = yield this.call('getbalance');
if (getbalance >= balance) {
return true;
}
yield this.generate(1);
return false;
}), timeout, 100, 'waitForWalletBalanceGTE');
});
}
/**
* Wait for anchor teams
*
* @param {number} nodesLength
* @param {number} [timeout=30000] in ms
* @return {Promise<void>}
*/
/* istanbul ignore next, TODO(canonbrother) */
waitForAnchorTeams(nodesLength, timeout = 30000) {
return __awaiter(this, void 0, void 0, function* () {
return yield utils_1.waitForCondition(() => __awaiter(this, void 0, void 0, function* () {
const anchorTeams = yield this.call('getanchorteams');
if (anchorTeams.auth.length === nodesLength && anchorTeams.confirm.length === nodesLength) {
return true;
}
return false;
}), timeout, 100, 'waitForAnchorTeams');
});
}
/**
* Wait for anchor auths
*
* @param {number} nodesLength
* @param {number} [timeout=30000] in ms
* @return {Promise<void>}
*/
/* istanbul ignore next, TODO(canonbrother) */
waitForAnchorAuths(nodesLength, timeout = 30000) {
return __awaiter(this, void 0, void 0, function* () {
return yield utils_1.waitForCondition(() => __awaiter(this, void 0, void 0, function* () {
const auths = yield this.call('spv_listanchorauths');
if (auths.length > 0 && auths[0].signers === nodesLength) {
return true;
}
return false;
}), timeout, 100, 'waitForAnchorAuths');
});
}
/**
* Wait for anchor reward confirms
*
* @param {number} [timeout=30000] in ms
* @return {Promise<void>}
*/
waitForAnchorRewardConfirms(timeout = 30000) {
return __awaiter(this, void 0, void 0, function* () {
// extra info here
// max signers in regtest is 3, others are 5
// majority is defined as 66% above
const majority = 2;
return yield utils_1.waitForCondition(() => __awaiter(this, void 0, void 0, function* () {
const confirms = yield this.call('spv_listanchorrewardconfirms');
if (confirms.length === 1 && confirms[0].signers >= majority) {
return true;
}
return false;
}), timeout, 100, 'waitForAnchorRewardConfrims');
});
}
/**
* Wait for price become valid
*
* @param {string} fixedIntervalPriceId
* @param {number} [timeout=30000] in ms
* @return {Promise<void>}
*/
waitForPriceValid(fixedIntervalPriceId, timeout = 30000) {
return __awaiter(this, void 0, void 0, function* () {
return yield utils_1.waitForCondition(() => __awaiter(this, void 0, void 0, function* () {
const data = yield this.call('getfixedintervalprice', [fixedIntervalPriceId]);
// eslint-disable-next-line
if (!data.isLive) {
yield this.generate(1);
return false;
}
return true;
}), timeout, 100, 'waitForPriceValid');
});
}
/**
* Wait for price become invalid
*
* @param {string} fixedIntervalPriceId
* @param {number} [timeout=30000] in ms
* @return {Promise<void>}
*/
waitForPriceInvalid(fixedIntervalPriceId, timeout = 30000) {
return __awaiter(this, void 0, void 0, function* () {
return yield utils_1.waitForCondition(() => __awaiter(this, void 0, void 0, function* () {
const data = yield this.call('getfixedintervalprice', [fixedIntervalPriceId]);
// eslint-disable-next-line
if (data.isLive) {
yield this.generate(1);
return false;
}
return true;
}), timeout, 100, 'waitForPriceInvalid');
});
}
waitForVaultState(vaultId, state, timeout = 30000) {
return __awaiter(this, void 0, void 0, function* () {
return yield utils_1.waitForCondition(() => __awaiter(this, void 0, void 0, function* () {
const vault = yield this.call('getvault', [vaultId]);
if (vault.state !== state) {
yield this.generate(1);
return false;
}
return true;
}), timeout, 100, 'waitForVaultState');
});
}
/**
* Wait for active price
*
* @param {string} fixedIntervalPriceId
* @param {string} activePrice
* @param {number} [timeout=30000] in ms
* @return {Promise<void>}
*/
waitForActivePrice(fixedIntervalPriceId, activePrice, timeout = 30000) {
return __awaiter(this, void 0, void 0, function* () {
return yield utils_1.waitForCondition(() => __awaiter(this, void 0, void 0, function* () {
const data = yield this.call('getfixedintervalprice', [fixedIntervalPriceId]);
// eslint-disable-next-line
if (data.activePrice.toString() !== activePrice) {
yield this.generate(1);
return false;
}
return true;
}), timeout, 100, 'waitForActivePrice');
});
}
/**
* Wait for next price
*
* @param {string} fixedIntervalPriceId
* @param {string} nextPrice
* @param {number} [timeout=30000] in ms
* @return {Promise<void>}
*/
waitForNextPrice(fixedIntervalPriceId, nextPrice, timeout = 30000) {
return __awaiter(this, void 0, void 0, function* () {
return yield utils_1.waitForCondition(() => __awaiter(this, void 0, void 0, function* () {
const data = yield this.call('getfixedintervalprice', [fixedIntervalPriceId]);
// eslint-disable-next-line
if (data.nextPrice.toString() !== nextPrice) {
yield this.generate(1);
return false;
}
return true;
}), timeout, 100, 'waitForNextPrice');
});
}
/**
* Fund an address with an amount and wait for 1 confirmation.
* Funded address don't have to be tracked within the node wallet.
* This allows for light wallet implementation testing.
*
* @param {string} address to fund
* @param {number} amount to fund an address, take note of number precision issues, BigNumber not included in pkg.
* @return {Promise<{txid: string, vout: number}>} txid and index of the transaction
* @see waitForWalletCoinbaseMaturity
* @see waitForWalletBalanceGTE
*/
fundAddress(address, amount) {
return __awaiter(this, void 0, void 0, function* () {
const txid = yield this.call('sendtoaddress', [address, amount]);
yield this.generate(1);
const { vout } = yield this.call('getrawtransaction', [txid, true]);
for (const out of vout) {
if (out.scriptPubKey.addresses.includes(address)) {
return {
txid,
vout: out.n
};
}
}
throw new Error('getrawtransaction will always return the required vout');
});
}
/**
* Create a new bech32 address and get the associated priv key for it.
* The address is created in the wallet and the priv key is dumped out.
* This is to facilitate raw tx feature testing, if you need an address that is not associated with the wallet,
* use jellyfish-crypto instead.
*
* This is not a deterministic feature, each time you run this, you get a different set of address and keys.
*
* @return {Promise<{ address: string, privKey: string, pubKey: string }>} a new address and it's associated privKey
*/
newAddressKeys() {
return __awaiter(this, void 0, void 0, function* () {
const address = yield this.call('getnewaddress', ['', 'bech32']);
const privKey = yield this.call('dumpprivkey', [address]);
const getaddressinfo = yield this.call('getaddressinfo', [address]);
return {
address,
privKey,
pubKey: getaddressinfo.pubkey
};
});
}
}
exports.MasterNodeRegTestContainer = MasterNodeRegTestContainer;
//# sourceMappingURL=Masternode.js.map