nerve-sdk-js
Version:
nerve nerve-js nerve-sdk nerve-js-sdk
897 lines (753 loc) • 29 kB
JavaScript
"use strict";
var __createBinding = void 0 && (void 0).__createBinding || (Object.create ? function (o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = {
enumerable: true,
get: function get() {
return m[k];
}
};
}
Object.defineProperty(o, k2, desc);
} : function (o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
});
var __setModuleDefault = void 0 && (void 0).__setModuleDefault || (Object.create ? function (o, v) {
Object.defineProperty(o, "default", {
enumerable: true,
value: v
});
} : function (o, v) {
o["default"] = v;
});
var __importStar = void 0 && (void 0).__importStar || function () {
var _ownKeys = function ownKeys(o) {
_ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) {
if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
}
return ar;
};
return _ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = _ownKeys(mod), i = 0; i < k.length; i++) {
if (k[i] !== "default") __createBinding(result, mod, k[i]);
}
__setModuleDefault(result, mod);
return result;
};
}();
Object.defineProperty(exports, "__esModule", {
value: true
});
var tbc = __importStar(require("tbc-lib-js"));
var FT = require("./ft");
class MultiSig {
/**
* Create a multi-signature transaction
* @param address_from The address from which the transaction is sent
* @param pubKeys An array of public keys involved in the multi-signature
* @param signatureCount The number of signatures required to authorize the transaction
* @param publicKeyCount The total number of public keys in the multi-signature
* @param amount_tbc The amount to be sent in TBC
* @param utxos An array of unspent transaction outputs to be used as inputs
* @param privateKey The private key used to sign the transaction
* @returns The raw serialized transaction string
*/
static createMultiSigWallet(address_from, pubKeys, signatureCount, publicKeyCount, tbc_amount, utxos, privateKey) {
var address = MultiSig.getMultiSigAddress(pubKeys, signatureCount, publicKeyCount);
var script_asm = MultiSig.getMultiSigLockScript(address);
var tx = new tbc.Transaction();
tx.from(utxos);
tx.addOutput(new tbc.Transaction.Output({
script: tbc.Script.fromASM(script_asm),
satoshis: Math.floor(tbc_amount * 1e6)
}));
for (var i = 0; i < publicKeyCount; i++) {
tx.addOutput(new tbc.Transaction.Output({
script: MultiSig.buildHoldScript(pubKeys[i]),
satoshis: 200
}));
}
tx.addOutput(new tbc.Transaction.Output({
script: MultiSig.buildTapeScript(address, pubKeys),
satoshis: 0
})).change(address_from);
var txSize = tx.getEstimateSize();
if (txSize < 1000) {
tx.fee(80);
} else {
tx.feePerKb(100);
}
tx.sign(privateKey).seal();
var raw = tx.uncheckedSerialize();
return raw;
}
/**
* Create a P2PKH to multi-signature transaction
* @param address_from The address from which the transaction is sent
* @param address_to The address to which the transaction is sent
* @param amount_tbc The amount to be sent in TBC
* @param utxos An array of unspent transaction outputs to be used as inputs
* @param privateKey The private key used to sign the transaction
* @returns The raw serialized transaction string
*/
static p2pkhToMultiSig_sendTBC(address_from, address_to, amount_tbc, utxos, privateKey, additionalInfo) {
var script_asm = MultiSig.getMultiSigLockScript(address_to);
var amount_satoshis = Math.floor(amount_tbc * Math.pow(10, 6));
var tx = new tbc.Transaction().from(utxos).addOutput(new tbc.Transaction.Output({
script: tbc.Script.fromASM(script_asm),
satoshis: amount_satoshis
}));
if (additionalInfo) {
//Additional infromation output
var additionalInfoScript = tbc.Script.fromASM('OP_FALSE OP_RETURN');
additionalInfoScript = additionalInfoScript.add(additionalInfo);
tx.addOutput(new tbc.Transaction.Output({
script: additionalInfoScript,
satoshis: 0
}));
}
tx.change(address_from);
var txSize = tx.getEstimateSize();
if (txSize < 1000) {
tx.fee(80);
} else {
tx.feePerKb(100);
}
tx.sign(privateKey).seal();
var raw = tx.uncheckedSerialize();
return raw;
}
/**
* Build a multi-signature transaction
* @param address_from The address from which the transaction is sent
* @param address_to The address to which the transaction is sent
* @param amount_tbc The amount to be sent in TBC
* @param utxos An array of unspent transaction outputs to be used as inputs
* @returns The raw serialized transaction string
*/
static buildMultiSigTransaction_sendTBC(address_from, address_to, amount_tbc, utxos, additionalInfo) {
var script_asm_from = MultiSig.getMultiSigLockScript(address_from);
var amount_satoshis = Math.floor(amount_tbc * Math.pow(10, 6));
var count = 0;
var amounts = [];
for (var i = 0; i < utxos.length; i++) {
count += utxos[i].satoshis;
amounts.push(utxos[i].satoshis);
}
var tx = new tbc.Transaction().from(utxos);
tx.addOutput(new tbc.Transaction.Output({
script: tbc.Script.fromASM(script_asm_from),
satoshis: count - amount_satoshis - 1000
})).to(address_to, amount_satoshis); //Additional infromation output
if (additionalInfo) {
var additionalInfoScript = tbc.Script.fromASM('OP_FALSE OP_RETURN');
additionalInfoScript = additionalInfoScript.add(additionalInfo);
tx.addOutput(new tbc.Transaction.Output({
script: additionalInfoScript,
satoshis: 0
}));
}
var txraw = tx.uncheckedSerialize();
return {
txraw,
amounts
};
}
/**
* Build a multi-signature transaction
* @param address_from The address from which the transaction is sent
* @param address_to The address to which the transaction is sent
* @param amount_tbc The amount to be sent in TBC
* @param utxos An array of unspent transaction outputs to be used as inputs
* @returns The raw serialized transaction string
*/
static buildMultiSigTransaction_sendTBCToMultiSig(address_from, address_to, amount_tbc, utxos, additionalInfo) {
var script_asm_from = MultiSig.getMultiSigLockScript(address_from);
var script_asm_to = MultiSig.getMultiSigLockScript(address_to);
var amount_satoshis = Math.floor(amount_tbc * Math.pow(10, 6));
var count = 0;
var amounts = [];
for (var i = 0; i < utxos.length; i++) {
count += utxos[i].satoshis;
amounts.push(utxos[i].satoshis);
}
var tx_source = new tbc.Transaction().from(utxos);
tx_source.addOutput(new tbc.Transaction.Output({
script: tbc.Script.fromASM(script_asm_to),
satoshis: amount_satoshis
}));
tx_source.addOutput(new tbc.Transaction.Output({
script: tbc.Script.fromASM(script_asm_from),
satoshis: count - amount_satoshis - 1000
})); //Additional infromation output
if (additionalInfo) {
var additionalInfoScript = tbc.Script.fromASM('OP_FALSE OP_RETURN');
additionalInfoScript = additionalInfoScript.add(additionalInfo);
tx_source.addOutput(new tbc.Transaction.Output({
script: additionalInfoScript,
satoshis: 0
}));
}
var tx_source_raw = tx_source.uncheckedSerialize();
if (count - amount_satoshis - 1100 > 80) {
var tx = new tbc.Transaction();
tx.addInputFromPrevTx(tx_source, 1).addOutput(new tbc.Transaction.Output({
script: tbc.Script.fromASM(script_asm_from),
satoshis: count - amount_satoshis - 1100
}));
var tx_raw = tx.uncheckedSerialize();
return [{
txraw: tx_source_raw,
amounts
}, {
txraw: tx_raw,
amounts: [count - amount_satoshis - 1000]
}];
} else {
return [{
txraw: tx_source_raw,
amounts
}];
}
}
/**
* Sign a multi-signature transaction
* @param address_from The address from which the transaction is sent
* @param multiSigTxraw The raw serialized transaction string
* @param privateKey The private key used to sign the transaction
* @returns An array of signatures
*/
static signMultiSigTransaction_sendTBC(address_from, multiSigTxraw, privateKey) {
var script_asm = MultiSig.getMultiSigLockScript(address_from);
var {
txraw,
amounts
} = multiSigTxraw;
var tx = new tbc.Transaction(txraw);
for (var i = 0; i < amounts.length; i++) {
tx.inputs[i].output = new tbc.Transaction.Output({
script: tbc.Script.fromASM(script_asm),
satoshis: amounts[i]
});
}
var sigs = [];
for (var _i = 0; _i < amounts.length; _i++) {
sigs[_i] = tx.getSignature(_i, privateKey);
}
return sigs;
}
/**
* Sign multi-signature transactions
* @param address_from The address from which the transaction is sent
* @param multiSigTxraws The raw serialized transaction string
* @param privateKey The private key used to sign the transaction
* @returns An array of signatures
*/
static batchSignMultiSigTransaction_sendTBC(address_from, multiSigTxraws, privateKey) {
var allSigs = [];
for (var txIndex = 0; txIndex < multiSigTxraws.length; txIndex++) {
var sigs = MultiSig.signMultiSigTransaction_sendTBC(address_from, multiSigTxraws[txIndex], privateKey);
allSigs.push(sigs);
}
return allSigs;
}
/**
* Finish a multi-signature transaction
* @param txraw The raw serialized transaction string
* @param sigs An array of signatures
* @param pubkeys An array of public keys
* @returns The raw serialized transaction string
*/
static finishMultiSigTransaction_sendTBC(txraw, sigs, pubKeys) {
var multiPubKeys = "";
for (var i = 0; i < pubKeys.length; i++) {
multiPubKeys = multiPubKeys + pubKeys[i];
}
var tx = new tbc.Transaction(txraw);
var _loop = function _loop(j) {
tx.setInputScript({
inputIndex: j
}, tx => {
var signature = "";
for (var _i2 = 0; _i2 < sigs[j].length; _i2++) {
if (_i2 < sigs[j].length - 1) {
signature = signature + sigs[j][_i2] + " ";
} else {
signature = signature + sigs[j][_i2];
}
}
var unlockingScript = tbc.Script.fromASM("OP_0 ".concat(signature, " ").concat(multiPubKeys));
return unlockingScript;
});
};
for (var j = 0; j < sigs.length; j++) {
_loop(j);
}
return tx.uncheckedSerialize();
}
/**
* Finish multi-signature transactions
* @param txraws The raw serialized transaction string
* @param sigs An array of signatures
* @param pubkeys An array of public keys
* @returns The raw serialized transaction string
*/
static batchFinishMultiSigTransaction_sendTBC(txraws, sigs, pubKeys) {
var finishedTxs = [];
for (var txIndex = 0; txIndex < txraws.length; txIndex++) {
var finishedTx = MultiSig.finishMultiSigTransaction_sendTBC(txraws[txIndex], sigs[txIndex], pubKeys);
finishedTxs.push(finishedTx);
}
return finishedTxs;
}
/**
* Transfer FT from a multi-signature address to another address
* @param address_from The address from which the transaction is sent
* @param address_to The address to which the transaction is sent
* @param ft The FT contract
* @param ft_amount The amount to be sent in FT
* @param utxo The UTXO to be used as input
* @param ftutxos An array of UTXOs to be used as inputs
* @param preTX An array of previous transactions
* @param prepreTxData An array of previous transaction data
* @param privateKey The private key used to sign the transaction
* @returns The raw serialized transaction string
*/
static p2pkhToMultiSig_transferFT(address_from, address_to, ft, ft_amount, utxo, ftutxos, preTXs, prepreTxDatas, privateKey, tbc_amount, additionalInfo) {
var code = ft.codeScript;
var tape = ft.tapeScript;
var decimal = ft.decimal;
var tapeAmountSetIn = [];
if (ft_amount < 0) {
throw new Error("Invalid amount");
}
var amountbn = BigInt(Math.floor(ft_amount * Math.pow(10, decimal)));
var tapeAmountSum = BigInt(0);
for (var i = 0; i < ftutxos.length; i++) {
tapeAmountSetIn.push(ftutxos[i].ftBalance);
tapeAmountSum += BigInt(tapeAmountSetIn[i]);
}
if (amountbn > tapeAmountSum) {
throw new Error("Insufficient balance, please add more FT UTXOs");
}
if (decimal > 18) {
throw new Error("The maximum value for decimal cannot exceed 18");
}
var maxAmount = Math.floor(Math.pow(10, 18 - decimal));
if (ft_amount > maxAmount) {
throw new Error("When decimal is ".concat(decimal, ", the maximum amount cannot exceed ").concat(maxAmount));
}
var {
amountHex,
changeHex
} = FT.buildTapeAmount(amountbn, tapeAmountSetIn);
var script_asm = MultiSig.getMultiSigLockScript(address_to);
var tx = new tbc.Transaction().from(ftutxos).from(utxo);
if (tbc_amount && Number(tbc_amount) > 0) {
var amount_satoshis = Math.floor(tbc_amount * Math.pow(10, 6));
tx.addOutput(new tbc.Transaction.Output({
script: tbc.Script.fromASM(script_asm),
satoshis: amount_satoshis
}));
}
var hash = tbc.crypto.Hash.sha256ripemd160(tbc.crypto.Hash.sha256(tbc.Script.fromASM(script_asm).toBuffer())).toString("hex");
var codeScript = FT.buildFTtransferCode(code, hash);
tx.addOutput(new tbc.Transaction.Output({
script: codeScript,
satoshis: 2000
}));
var tapeScript = FT.buildFTtransferTape(tape, amountHex);
tx.addOutput(new tbc.Transaction.Output({
script: tapeScript,
satoshis: 0
}));
if (amountbn < tapeAmountSum) {
var changeCodeScript = FT.buildFTtransferCode(code, address_from);
tx.addOutput(new tbc.Transaction.Output({
script: changeCodeScript,
satoshis: 2000
}));
var changeTapeScript = FT.buildFTtransferTape(tape, changeHex);
tx.addOutput(new tbc.Transaction.Output({
script: changeTapeScript,
satoshis: 0
}));
}
if (additionalInfo) {
//Additional infromation output
var additionalInfoScript = tbc.Script.fromASM('OP_FALSE OP_RETURN');
additionalInfoScript = additionalInfoScript.add(additionalInfo);
tx.addOutput(new tbc.Transaction.Output({
script: additionalInfoScript,
satoshis: 0
}));
}
tx.change(address_from);
var txSize = tx.getEstimateSize();
if (txSize < 1000) {
tx.fee(80);
} else {
tx.feePerKb(100);
}
var _loop2 = function _loop2(_i3) {
tx.setInputScript({
inputIndex: _i3
}, tx => {
var unlockingScript = ft.getFTunlock(privateKey, tx, preTXs[_i3], prepreTxDatas[_i3], _i3, ftutxos[_i3].outputIndex);
return unlockingScript;
});
};
for (var _i3 = 0; _i3 < ftutxos.length; _i3++) {
_loop2(_i3);
}
tx.sign(privateKey).seal();
return tx.uncheckedSerialize();
}
/**
* Build a multi-signature transaction for transferring FT
* @param address_from The address from which the transaction is sent
* @param address_to The address to which the transaction is sent
* @param ft The FT contract
* @param ft_amount The amount to be sent in FT
* @param utxo The UTXO to be used as input
* @param ftutxos An array of UTXOs to be used as inputs
* @param preTX An array of previous transactions
* @param prepreTxData An array of previous transaction data
* @param privateKey The private key used to sign the transaction
* @returns The raw serialized transaction string
*/
static buildMultiSigTransaction_transferFT(address_from, address_to, ft, ft_amount, utxo, ftutxos, preTXs, prepreTxDatas, contractTX, privateKey, tbc_amount, additionalInfo) {
var code = ft.codeScript;
var tape = ft.tapeScript;
var decimal = ft.decimal;
var tapeAmountSetIn = [];
var script_asm_from = MultiSig.getMultiSigLockScript(address_from);
var hash_from = tbc.crypto.Hash.sha256ripemd160(tbc.crypto.Hash.sha256(tbc.Script.fromASM(script_asm_from).toBuffer())).toString("hex");
var amountbn = BigInt(Math.floor(ft_amount * Math.pow(10, decimal)));
var tapeAmountSum = BigInt(0);
for (var i = 0; i < ftutxos.length; i++) {
tapeAmountSetIn.push(ftutxos[i].ftBalance);
tapeAmountSum += BigInt(tapeAmountSetIn[i]);
}
if (amountbn > tapeAmountSum) {
throw new Error("Insufficient balance, please add more FT UTXOs");
}
if (decimal > 18) {
throw new Error("The maximum value for decimal cannot exceed 18");
}
var maxAmount = Math.floor(Math.pow(10, 18 - decimal));
if (ft_amount > maxAmount) {
throw new Error("When decimal is ".concat(decimal, ", the maximum amount cannot exceed ").concat(maxAmount));
}
var {
amountHex,
changeHex
} = FT.buildTapeAmount(amountbn, tapeAmountSetIn, 1);
var tx = new tbc.Transaction().from(utxo).from(ftutxos);
var amount_satoshis = 0;
if (tbc_amount) {
amount_satoshis = Math.floor(tbc_amount * Math.pow(10, 6));
}
switch (ftutxos.length) {
case 1:
tx.addOutput(new tbc.Transaction.Output({
script: tbc.Script.fromASM(script_asm_from),
satoshis: utxo.satoshis - amount_satoshis - 4000
}));
break;
case 2:
tx.addOutput(new tbc.Transaction.Output({
script: tbc.Script.fromASM(script_asm_from),
satoshis: utxo.satoshis - amount_satoshis - 5500
}));
break;
case 3:
tx.addOutput(new tbc.Transaction.Output({
script: tbc.Script.fromASM(script_asm_from),
satoshis: utxo.satoshis - amount_satoshis - 7000
}));
break;
case 4:
tx.addOutput(new tbc.Transaction.Output({
script: tbc.Script.fromASM(script_asm_from),
satoshis: utxo.satoshis - amount_satoshis - 8500
}));
break;
case 5:
tx.addOutput(new tbc.Transaction.Output({
script: tbc.Script.fromASM(script_asm_from),
satoshis: utxo.satoshis - amount_satoshis - 10000
}));
break;
}
var codeScript;
if (address_to.startsWith("1")) {
codeScript = FT.buildFTtransferCode(code, address_to);
} else {
var hash_to = tbc.crypto.Hash.sha256ripemd160(tbc.crypto.Hash.sha256(tbc.Script.fromASM(MultiSig.getMultiSigLockScript(address_to)).toBuffer())).toString("hex");
codeScript = FT.buildFTtransferCode(code, hash_to);
}
tx.addOutput(new tbc.Transaction.Output({
script: codeScript,
satoshis: 2000
}));
var tapeScript = FT.buildFTtransferTape(tape, amountHex);
tx.addOutput(new tbc.Transaction.Output({
script: tapeScript,
satoshis: 0
}));
if (amountbn < tapeAmountSum) {
var changeCodeScript = FT.buildFTtransferCode(code, hash_from);
tx.addOutput(new tbc.Transaction.Output({
script: changeCodeScript,
satoshis: 2000
}));
var changeTapeScript = FT.buildFTtransferTape(tape, changeHex);
tx.addOutput(new tbc.Transaction.Output({
script: changeTapeScript,
satoshis: 0
}));
}
if (tbc_amount) {
tx.addOutput(new tbc.Transaction.Output({
script: tbc.Script.buildPublicKeyHashOut(address_to),
satoshis: amount_satoshis
}));
} //Additional infromation output
if (additionalInfo) {
var additionalInfoScript = tbc.Script.fromASM('OP_FALSE OP_RETURN');
additionalInfoScript = additionalInfoScript.add(additionalInfo);
tx.addOutput(new tbc.Transaction.Output({
script: additionalInfoScript,
satoshis: 0
}));
}
var _loop3 = function _loop3(_i4) {
tx.setInputScript({
inputIndex: _i4 + 1
}, tx_source => {
var unlockingScript = ft.getFTunlockSwap(privateKey, tx_source, preTXs[_i4], prepreTxDatas[_i4], contractTX, _i4 + 1, ftutxos[_i4].outputIndex);
return unlockingScript;
});
};
for (var _i4 = 0; _i4 < ftutxos.length; _i4++) {
_loop3(_i4);
}
var txraw = tx.uncheckedSerialize();
return {
txraw,
amounts: [utxo.satoshis]
};
}
/**
* Sign a multi-signature transaction for transferring FT
* @param address_from The address from which the transaction is sent
* @param multiSigTxraw The raw serialized transaction string
* @param privateKey The private key used to sign the transaction
* @returns An array of signatures
*/
static signMultiSigTransaction_transferFT(multiSig_address, multiSigTxraw, privateKey) {
var script_asm = MultiSig.getMultiSigLockScript(multiSig_address);
var {
txraw,
amounts
} = multiSigTxraw;
var tx = new tbc.Transaction(txraw);
tx.inputs[0].output = new tbc.Transaction.Output({
script: tbc.Script.fromASM(script_asm),
satoshis: amounts[0]
});
var sigs = [];
sigs[0] = tx.getSignature(0, privateKey);
return sigs;
}
/**
* Sign multi-signature transactions for transferring FT
* @param multiSig_address The multi-signature address
* @param multiSigTxraws The raw serialized transaction strings
* @param privateKey The private key used to sign the transaction
* @returns An array of signatures
*/
static batchSignMultiSigTransaction_transferFT(multiSig_address, multiSigTxraws, privateKey) {
var allSigs = [];
for (var txIndex = 0; txIndex < multiSigTxraws.length; txIndex++) {
var sigs = MultiSig.signMultiSigTransaction_transferFT(multiSig_address, multiSigTxraws[txIndex], privateKey);
allSigs.push(sigs);
}
return allSigs;
}
/**
* Finish a multi-signature transaction for transferring FT
* @param txraw The raw serialized transaction string
* @param sigs An array of signatures
* @param pubkeys The public keys
* @returns The raw serialized transaction string
*/
static finishMultiSigTransaction_transferFT(txraw, sigs, pubKeys) {
var multiPubKeys = "";
for (var i = 0; i < pubKeys.length; i++) {
multiPubKeys = multiPubKeys + pubKeys[i];
}
var tx = new tbc.Transaction(txraw);
tx.setInputScript({
inputIndex: 0
}, tx => {
var signature = "";
for (var _i5 = 0; _i5 < sigs[0].length; _i5++) {
if (_i5 < sigs[0].length - 1) {
signature = signature + sigs[0][_i5] + " ";
} else {
signature = signature + sigs[0][_i5];
}
}
var unlockingScript = tbc.Script.fromASM("OP_0 ".concat(signature, " ").concat(multiPubKeys));
return unlockingScript;
});
return tx.uncheckedSerialize();
}
/**
* Finish multi-signature transactions for transferring FT
* @param txraws The raw serialized transaction strings
* @param sigs An array of signatures
* @param pubkeys The public keys
* @returns The raw serialized transaction strings
*/
static batchFinishMultiSigTransaction_transferFT(txraws, sigs, pubKeys) {
var finishedTxs = [];
for (var txIndex = 0; txIndex < txraws.length; txIndex++) {
var finishedTx = MultiSig.finishMultiSigTransaction_transferFT(txraws[txIndex], sigs[txIndex], pubKeys);
finishedTxs.push(finishedTx);
}
return finishedTxs;
}
/**
* Get multi-signature address
* @param pubkeys Public keys
* @param signatureCount Number of signatures
* @param publicKeyCount Number of public keys
* @returns Multi-signature address
*/
static getMultiSigAddress(pubKeys, signatureCount, publicKeyCount) {
if (signatureCount < 1 || signatureCount > 6) {
throw new Error("Invalid signatureCount.");
} else if (publicKeyCount < 3 || publicKeyCount > 10) {
throw new Error("Invalid publicKeyCount.");
} else if (signatureCount > publicKeyCount) {
throw new Error("SignatureCount must be less than publicKeyCount.");
}
var hash = MultiSig.getHash(pubKeys);
var prefix = signatureCount << 4 | publicKeyCount & 0x0f;
var versionBuffer = Buffer.from([prefix]);
var addressBuffer = Buffer.concat([versionBuffer, hash]);
var addressHash = tbc.crypto.Hash.sha256sha256(addressBuffer);
var checksum = addressHash.subarray(0, 4);
var addressWithChecksum = Buffer.concat([addressBuffer, checksum]);
return tbc.encoding.Base58.encode(addressWithChecksum);
}
/**
* Get the signature and public key count from a multi-signature address
* @param address Multi-signature address
* @returns Signature and public key count
*/
static getSignatureAndPublicKeyCount(address) {
var buf = Buffer.from(tbc.encoding.Base58.decode(address));
var prefix = buf[0];
var signatureCount = prefix >> 4 & 0x0f;
var publicKeyCount = prefix & 0x0f;
return {
signatureCount,
publicKeyCount
};
}
/**
* Verify a multi-signature address
* @param pubkeys Public keys
* @param address Multi-signature address
* @returns True if the address is valid, false otherwise
*/
static verifyMultiSigAddress(pubKeys, address) {
var hash_from_pubkeys = MultiSig.getHash(pubKeys).toString("hex");
var buf = Buffer.from(tbc.encoding.Base58.decode(address));
var hash_from_address = Buffer.from(buf.subarray(1, 21)).toString("hex");
return hash_from_pubkeys === hash_from_address;
}
/**
* Generate a multi-signature lock script(script_asm) from a multi-signature address
* @param address Multi-signature address
* @returns Lock script for the multi-signature contract
* @throws Error if signature count or public key count is invalid
*
* The generated lock script performs the following:
* 1. Splits the input public keys
* 2. Duplicates and concatenates the public keys
* 3. Verifies the hash matches the address
* 4. Checks that the required number of signatures are valid
*/
static getMultiSigLockScript(address) {
var buf = Buffer.from(tbc.encoding.Base58.decode(address));
var {
signatureCount,
publicKeyCount
} = MultiSig.getSignatureAndPublicKeyCount(address);
if (signatureCount < 1 || signatureCount > 6) {
throw new Error("Invalid signatureCount.");
} else if (publicKeyCount < 3 || publicKeyCount > 10) {
throw new Error("Invalid publicKeyCount.");
} else if (signatureCount > publicKeyCount) {
throw new Error("SignatureCount must be less than publicKeyCount.");
}
var hash = Buffer.from(buf.subarray(1, 21)).toString("hex");
var lockScriptPrefix = "";
for (var i = 0; i < publicKeyCount - 1; i++) {
lockScriptPrefix = lockScriptPrefix + "21 OP_SPLIT ";
}
for (var _i6 = 0; _i6 < publicKeyCount; _i6++) {
lockScriptPrefix = lockScriptPrefix + "OP_".concat(publicKeyCount - 1, " OP_PICK ");
}
for (var _i7 = 0; _i7 < publicKeyCount - 1; _i7++) {
lockScriptPrefix = lockScriptPrefix + "OP_CAT ";
}
var script_asm = "OP_".concat(signatureCount, " OP_SWAP ") + lockScriptPrefix + "OP_HASH160 ".concat(hash, " OP_EQUALVERIFY OP_").concat(publicKeyCount, " OP_CHECKMULTISIG");
return script_asm;
}
/**
* Get the combine hash from a multi-signature address
* @param address Multi-signature address
* @returns Combine hash
*/
static getCombineHash(address) {
var combine_hash = tbc.crypto.Hash.sha256ripemd160(tbc.crypto.Hash.sha256(tbc.Script.fromASM(MultiSig.getMultiSigLockScript(address)).toBuffer())).toString("hex") + "01";
return combine_hash;
}
static getHash(pubKeys) {
var multiPubKeys = "";
for (var i = 0; i < pubKeys.length; i++) {
multiPubKeys = multiPubKeys + pubKeys[i];
}
var buf = Buffer.from(multiPubKeys, "hex");
var hash = tbc.crypto.Hash.sha256ripemd160(buf);
return hash;
}
static buildHoldScript(pubKey) {
var publicKeyHash = tbc.crypto.Hash.sha256ripemd160(Buffer.from(pubKey, "hex")).toString("hex");
return new tbc.Script("OP_DUP OP_HASH160" + " 0x14 0x" + publicKeyHash + " OP_EQUALVERIFY OP_CHECKSIG OP_RETURN 0x08 0x6d756c7469736967");
}
static buildTapeScript(address, pubKeys) {
var data = {
address: address,
pubkeys: pubKeys
};
var dataHex = Buffer.from(JSON.stringify(data)).toString("hex");
return tbc.Script.fromASM("OP_FALSE OP_RETURN " + dataHex + " 4d54617065");
}
}
module.exports = MultiSig;