PHP WebShell
Текущая директория: /opt/BitGoJS/modules/babylonlabs-io-btc-staking-ts/build/src/utils/staking
Просмотр файла: index.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.toBuffers = exports.validateStakingTimelock = exports.validateParams = exports.validateStakingTxInputData = exports.findMatchingTxOutputIndex = exports.deriveSlashingOutput = exports.deriveUnbondingOutputInfo = exports.deriveStakingOutputInfo = exports.buildStakingTransactionOutputs = void 0;
const bitcoinjs_lib_1 = require("bitcoinjs-lib");
const internalPubkey_1 = require("../../constants/internalPubkey");
const error_1 = require("../../error");
const btc_1 = require("../btc");
const unbonding_1 = require("../../constants/unbonding");
/**
* Build the staking output for the transaction which contains p2tr output
* with staking scripts.
*
* @param {StakingScripts} scripts - The staking scripts.
* @param {networks.Network} network - The Bitcoin network.
* @param {number} amount - The amount to stake.
* @returns {TransactionOutput[]} - The staking transaction outputs.
* @throws {Error} - If the staking output cannot be built.
*/
const buildStakingTransactionOutputs = (scripts, network, amount) => {
const stakingOutputInfo = (0, exports.deriveStakingOutputInfo)(scripts, network);
const transactionOutputs = [
{
scriptPubKey: stakingOutputInfo.scriptPubKey,
value: amount,
},
];
if (scripts.dataEmbedScript) {
// Add the data embed output to the transaction
transactionOutputs.push({
scriptPubKey: scripts.dataEmbedScript,
value: 0,
});
}
return transactionOutputs;
};
exports.buildStakingTransactionOutputs = buildStakingTransactionOutputs;
/**
* Derive the staking output address from the staking scripts.
*
* @param {StakingScripts} scripts - The staking scripts.
* @param {networks.Network} network - The Bitcoin network.
* @returns {StakingOutput} - The staking output address and scriptPubKey.
* @throws {StakingError} - If the staking output address cannot be derived.
*/
const deriveStakingOutputInfo = (scripts, network) => {
// Build outputs
const scriptTree = [
{
output: scripts.slashingScript,
},
[{ output: scripts.unbondingScript }, { output: scripts.timelockScript }],
];
// Create an pay-2-taproot (p2tr) output using the staking script
const stakingOutput = bitcoinjs_lib_1.payments.p2tr({
internalPubkey: internalPubkey_1.internalPubkey,
scriptTree,
network,
});
if (!stakingOutput.address) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_OUTPUT, "Failed to build staking output");
}
return {
outputAddress: stakingOutput.address,
scriptPubKey: bitcoinjs_lib_1.address.toOutputScript(stakingOutput.address, network),
};
};
exports.deriveStakingOutputInfo = deriveStakingOutputInfo;
/**
* Derive the unbonding output address and scriptPubKey from the staking scripts.
*
* @param {StakingScripts} scripts - The staking scripts.
* @param {networks.Network} network - The Bitcoin network.
* @returns {OutputInfo} - The unbonding output address and scriptPubKey.
* @throws {StakingError} - If the unbonding output address cannot be derived.
*/
const deriveUnbondingOutputInfo = (scripts, network) => {
const outputScriptTree = [
{
output: scripts.slashingScript,
},
{ output: scripts.unbondingTimelockScript },
];
const unbondingOutput = bitcoinjs_lib_1.payments.p2tr({
internalPubkey: internalPubkey_1.internalPubkey,
scriptTree: outputScriptTree,
network,
});
if (!unbondingOutput.address) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_OUTPUT, "Failed to build unbonding output");
}
return {
outputAddress: unbondingOutput.address,
scriptPubKey: bitcoinjs_lib_1.address.toOutputScript(unbondingOutput.address, network),
};
};
exports.deriveUnbondingOutputInfo = deriveUnbondingOutputInfo;
/**
* Derive the slashing output address and scriptPubKey from the staking scripts.
*
* @param {StakingScripts} scripts - The unbonding timelock scripts, we use the
* unbonding timelock script as the timelock of the slashing transaction.
* This is due to slashing tx timelock is the same as the unbonding timelock.
* @param {networks.Network} network - The Bitcoin network.
* @returns {OutputInfo} - The slashing output address and scriptPubKey.
* @throws {StakingError} - If the slashing output address cannot be derived.
*/
const deriveSlashingOutput = (scripts, network) => {
const slashingOutput = bitcoinjs_lib_1.payments.p2tr({
internalPubkey: internalPubkey_1.internalPubkey,
scriptTree: { output: scripts.unbondingTimelockScript },
network,
});
const slashingOutputAddress = slashingOutput.address;
if (!slashingOutputAddress) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_OUTPUT, "Failed to build slashing output address");
}
return {
outputAddress: slashingOutputAddress,
scriptPubKey: bitcoinjs_lib_1.address.toOutputScript(slashingOutputAddress, network),
};
};
exports.deriveSlashingOutput = deriveSlashingOutput;
/**
* Find the matching output index for the given transaction.
*
* @param {Transaction} tx - The transaction.
* @param {string} outputAddress - The output address.
* @param {networks.Network} network - The Bitcoin network.
* @returns {number} - The output index.
* @throws {Error} - If the matching output is not found.
*/
const findMatchingTxOutputIndex = (tx, outputAddress, network) => {
const index = tx.outs.findIndex(output => {
return bitcoinjs_lib_1.address.fromOutputScript(output.script, network) === outputAddress;
});
if (index === -1) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_OUTPUT, `Matching output not found for address: ${outputAddress}`);
}
return index;
};
exports.findMatchingTxOutputIndex = findMatchingTxOutputIndex;
/**
* Validate the staking transaction input data.
*
* @param {number} stakingAmountSat - The staking amount in satoshis.
* @param {number} timelock - The staking time in blocks.
* @param {StakingParams} params - The staking parameters.
* @param {UTXO[]} inputUTXOs - The input UTXOs.
* @param {number} feeRate - The Bitcoin fee rate in sat/vbyte
* @throws {StakingError} - If the input data is invalid.
*/
const validateStakingTxInputData = (stakingAmountSat, timelock, params, inputUTXOs, feeRate) => {
if (stakingAmountSat < params.minStakingAmountSat ||
stakingAmountSat > params.maxStakingAmountSat) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_INPUT, "Invalid staking amount");
}
if (timelock < params.minStakingTimeBlocks ||
timelock > params.maxStakingTimeBlocks) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_INPUT, "Invalid timelock");
}
if (inputUTXOs.length == 0) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_INPUT, "No input UTXOs provided");
}
if (feeRate <= 0) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_INPUT, "Invalid fee rate");
}
};
exports.validateStakingTxInputData = validateStakingTxInputData;
/**
* Validate the staking parameters.
* Extend this method to add additional validation for staking parameters based
* on the staking type.
* @param {StakingParams} params - The staking parameters.
* @throws {StakingError} - If the parameters are invalid.
*/
const validateParams = (params) => {
// Check covenant public keys
if (params.covenantNoCoordPks.length == 0) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_PARAMS, "Could not find any covenant public keys");
}
if (params.covenantNoCoordPks.length < params.covenantQuorum) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_PARAMS, "Covenant public keys must be greater than or equal to the quorum");
}
params.covenantNoCoordPks.forEach((pk) => {
if (!(0, btc_1.isValidNoCoordPublicKey)(pk)) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_PARAMS, "Covenant public key should contains no coordinate");
}
});
// Check other parameters
if (params.unbondingTime <= 0) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_PARAMS, "Unbonding time must be greater than 0");
}
if (params.unbondingFeeSat <= 0) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_PARAMS, "Unbonding fee must be greater than 0");
}
if (params.maxStakingAmountSat < params.minStakingAmountSat) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_PARAMS, "Max staking amount must be greater or equal to min staking amount");
}
if (params.minStakingAmountSat < params.unbondingFeeSat + unbonding_1.MIN_UNBONDING_OUTPUT_VALUE) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_PARAMS, `Min staking amount must be greater than unbonding fee plus ${unbonding_1.MIN_UNBONDING_OUTPUT_VALUE}`);
}
if (params.maxStakingTimeBlocks < params.minStakingTimeBlocks) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_PARAMS, "Max staking time must be greater or equal to min staking time");
}
if (params.minStakingTimeBlocks <= 0) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_PARAMS, "Min staking time must be greater than 0");
}
if (params.covenantQuorum <= 0) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_PARAMS, "Covenant quorum must be greater than 0");
}
if (params.slashing) {
if (params.slashing.slashingRate <= 0) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_PARAMS, "Slashing rate must be greater than 0");
}
if (params.slashing.slashingRate > 1) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_PARAMS, "Slashing rate must be less or equal to 1");
}
if (params.slashing.slashingPkScriptHex.length == 0) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_PARAMS, "Slashing public key script is missing");
}
if (params.slashing.minSlashingTxFeeSat <= 0) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_PARAMS, "Minimum slashing transaction fee must be greater than 0");
}
}
};
exports.validateParams = validateParams;
/**
* Validate the staking timelock.
*
* @param {number} stakingTimelock - The staking timelock.
* @param {StakingParams} params - The staking parameters.
* @throws {StakingError} - If the staking timelock is invalid.
*/
const validateStakingTimelock = (stakingTimelock, params) => {
if (stakingTimelock < params.minStakingTimeBlocks ||
stakingTimelock > params.maxStakingTimeBlocks) {
throw new error_1.StakingError(error_1.StakingErrorCode.INVALID_INPUT, "Staking transaction timelock is out of range");
}
};
exports.validateStakingTimelock = validateStakingTimelock;
/**
* toBuffers converts an array of strings to an array of buffers.
*
* @param {string[]} inputs - The input strings.
* @returns {Buffer[]} - The buffers.
* @throws {StakingError} - If the values cannot be converted to buffers.
*/
const toBuffers = (inputs) => {
try {
return inputs.map((i) => Buffer.from(i, "hex"));
}
catch (error) {
throw error_1.StakingError.fromUnknown(error, error_1.StakingErrorCode.INVALID_INPUT, "Cannot convert values to buffers");
}
};
exports.toBuffers = toBuffers;
Выполнить команду
Для локальной разработки. Не используйте в интернете!