UNPKG

aladinnetwork-blockstack

Version:

The Aladin Javascript library for authentication, identity, and storage.

925 lines 48.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const bitcoinjs_lib_1 = require("bitcoinjs-lib"); const utils_1 = require("./utils"); const skeletons_1 = require("./skeletons"); const config_1 = require("../config"); const errors_1 = require("../errors"); const signers_1 = require("./signers"); const dummyConsensusHash = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; const dummyZonefileHash = 'ffffffffffffffffffffffffffffffffffffffff'; /** * @ignore */ function addOwnerInput(utxos, ownerAddress, txB, addChangeOut = true) { // add an owner UTXO and a change out. if (utxos.length <= 0) { throw new Error('Owner has no UTXOs for UPDATE.'); } utxos.sort((a, b) => a.value - b.value); const ownerUTXO = utxos[0]; const ownerInput = txB.addInput(ownerUTXO.tx_hash, ownerUTXO.tx_output_n); if (addChangeOut) { txB.addOutput(ownerAddress, ownerUTXO.value); } return { index: ownerInput, value: ownerUTXO.value }; } /** * @ignore */ function fundTransaction(txB, paymentAddress, utxos, feeRate, inAmounts, changeIndex = null) { // change index for the payer. if (changeIndex === null) { changeIndex = txB.addOutput(paymentAddress, utils_1.DUST_MINIMUM); } // fund the transaction fee. const txFee = utils_1.estimateTXBytes(txB, 0, 0) * feeRate; const outAmounts = utils_1.sumOutputValues(txB); const change = utils_1.addUTXOsToFund(txB, utxos, txFee + outAmounts - inAmounts, feeRate); const txInner = utils_1.getTransactionInsideBuilder(txB); const txOut = txInner.outs[changeIndex]; txOut.value += change; return txB; } /** * @ignore */ function returnTransactionHex(txB, buildIncomplete = false) { if (buildIncomplete) { return txB.buildIncomplete().toHex(); } else { return txB.build().toHex(); } } /** * @ignore */ function getTransactionSigner(input) { if (typeof input === 'string') { return signers_1.PubkeyHashSigner.fromHexString(input); } else { return input; } } /** * Estimates cost of a preorder transaction for a domain name. * @param {String} fullyQualifiedName - the name to preorder * @param {String} destinationAddress - the address to receive the name (this * must be passed as the 'registrationAddress' in the register transaction) * @param {String} paymentAddress - the address funding the preorder * @param {Number} paymentUtxos - the number of UTXOs we expect will be required * from the payment address. * @returns {Promise} - a promise which resolves to the satoshi cost to fund * the preorder. This includes a 5500 satoshi dust output for the preorder. * Even though this is a change output, the payer must supply enough funds * to generate this output, so we include it in the cost. * @private */ function estimatePreorder(fullyQualifiedName, destinationAddress, paymentAddress, paymentUtxos = 1) { const network = config_1.config.network; const preorderPromise = network.getNamePrice(fullyQualifiedName) .then(namePrice => skeletons_1.makePreorderSkeleton(fullyQualifiedName, dummyConsensusHash, paymentAddress, network.getDefaultBurnAddress(), namePrice, destinationAddress)); return Promise.all([network.getFeeRate(), preorderPromise]) .then(([feeRate, preorderTX]) => { const outputsValue = utils_1.sumOutputValues(preorderTX); const txFee = feeRate * utils_1.estimateTXBytes(preorderTX, paymentUtxos, 0); return txFee + outputsValue; }); } /** * Estimates cost of a register transaction for a domain name. * @param {String} fullyQualifiedName - the name to register * @param {String} registerAddress - the address to receive the name * @param {String} paymentAddress - the address funding the register * @param {Boolean} includingZonefile - whether or not we will broadcast * a zonefile hash as part of the register * @param {Number} paymentUtxos - the number of UTXOs we expect will be required * from the payment address. * @returns {Promise} - a promise which resolves to the satoshi cost to fund * the register. * @private */ function estimateRegister(fullyQualifiedName, registerAddress, paymentAddress, includingZonefile = false, paymentUtxos = 1) { const network = config_1.config.network; let valueHash; if (includingZonefile) { valueHash = dummyZonefileHash; } const registerTX = skeletons_1.makeRegisterSkeleton(fullyQualifiedName, registerAddress, valueHash); return network.getFeeRate() .then((feeRate) => { const outputsValue = utils_1.sumOutputValues(registerTX); // 1 additional output for payer change const txFee = feeRate * utils_1.estimateTXBytes(registerTX, paymentUtxos, 1); return txFee + outputsValue; }); } /** * Estimates cost of an update transaction for a domain name. * @param {String} fullyQualifiedName - the name to update * @param {String} ownerAddress - the owner of the name * @param {String} paymentAddress - the address funding the update * @param {Number} paymentUtxos - the number of UTXOs we expect will be required * from the payment address. * @returns {Promise} - a promise which resolves to the satoshi cost to fund * the update. * @private */ function estimateUpdate(fullyQualifiedName, ownerAddress, paymentAddress, paymentUtxos = 1) { const network = config_1.config.network; const updateTX = skeletons_1.makeUpdateSkeleton(fullyQualifiedName, dummyConsensusHash, dummyZonefileHash); return network.getFeeRate() .then((feeRate) => { const outputsValue = utils_1.sumOutputValues(updateTX); // 1 additional input for the owner // 2 additional outputs for owner / payer change const txFee = feeRate * utils_1.estimateTXBytes(updateTX, 1 + paymentUtxos, 2); return txFee + outputsValue; }); } /** * Estimates cost of an transfer transaction for a domain name. * @param {String} fullyQualifiedName - the name to transfer * @param {String} destinationAddress - the next owner of the name * @param {String} ownerAddress - the current owner of the name * @param {String} paymentAddress - the address funding the transfer * @param {Number} paymentUtxos - the number of UTXOs we expect will be required * from the payment address. * @returns {Promise} - a promise which resolves to the satoshi cost to fund * the transfer. * @private */ function estimateTransfer(fullyQualifiedName, destinationAddress, ownerAddress, paymentAddress, paymentUtxos = 1) { const network = config_1.config.network; const transferTX = skeletons_1.makeTransferSkeleton(fullyQualifiedName, dummyConsensusHash, destinationAddress); return network.getFeeRate() .then((feeRate) => { const outputsValue = utils_1.sumOutputValues(transferTX); // 1 additional input for the owner // 2 additional outputs for owner / payer change const txFee = feeRate * utils_1.estimateTXBytes(transferTX, 1 + paymentUtxos, 2); return txFee + outputsValue; }); } /** * Estimates cost of an transfer transaction for a domain name. * @param {String} fullyQualifiedName - the name to renew * @param {String} destinationAddress - the next owner of the name * @param {String} ownerAddress - the current owner of the name * @param {String} paymentAddress - the address funding the transfer * @param {Boolean} includingZonefile - whether or not we will broadcast a zonefile hash in the renewal operation * @param {Number} paymentUtxos - the number of UTXOs we expect will be required * from the payment address. * @returns {Promise} - a promise which resolves to the satoshi cost to fund * the transfer. * @private */ function estimateRenewal(fullyQualifiedName, destinationAddress, ownerAddress, paymentAddress, includingZonefile = false, paymentUtxos = 1) { const network = config_1.config.network; let valueHash; if (includingZonefile) { valueHash = dummyZonefileHash; } const renewalPromise = network.getNamePrice(fullyQualifiedName) .then(namePrice => skeletons_1.makeRenewalSkeleton(fullyQualifiedName, destinationAddress, ownerAddress, network.getDefaultBurnAddress(), namePrice, valueHash)); return Promise.all([network.getFeeRate(), renewalPromise]) .then(([feeRate, renewalTX]) => { const outputsValue = utils_1.sumOutputValues(renewalTX); // 1 additional input for the owner // and renewal skeleton includes all outputs for owner change, but not for payer change. const txFee = feeRate * utils_1.estimateTXBytes(renewalTX, 1 + paymentUtxos, 1); return txFee + outputsValue - 5500; // don't count the dust change for old owner. }); } /** * Estimates cost of a revoke transaction for a domain name. * @param {String} fullyQualifiedName - the name to revoke * @param {String} ownerAddress - the current owner of the name * @param {String} paymentAddress the address funding the revoke * @param {Number} paymentUtxos - the number of UTXOs we expect will be required * from the payment address. * @returns {Promise} - a promise which resolves to the satoshi cost to fund the * revoke. * @private */ function estimateRevoke(fullyQualifiedName, ownerAddress, paymentAddress, paymentUtxos = 1) { const network = config_1.config.network; const revokeTX = skeletons_1.makeRevokeSkeleton(fullyQualifiedName); return Promise.all([network.getFeeRate()]) .then(([feeRate]) => { const outputsValue = utils_1.sumOutputValues(revokeTX); // 1 additional input for owner // 1 additional output for payer change const txFee = feeRate * utils_1.estimateTXBytes(revokeTX, 1 + paymentUtxos, 2); return txFee + outputsValue; }); } /** * Estimates cost of a namespace preorder transaction for a namespace * @param {String} namespaceID - the namespace to preorder * @param {String} revealAddress - the address to receive the namespace (this * must be passed as the 'revealAddress' in the namespace-reveal transaction) * @param {String} paymentAddress - the address funding the preorder * @param {Number} paymentUtxos - the number of UTXOs we expect will be required * from the payment address. * @returns {Promise} - a promise which resolves to the satoshi cost to fund * the preorder. This includes a 5500 satoshi dust output for the preorder. * Even though this is a change output, the payer must supply enough funds * to generate this output, so we include it in the cost. * @private */ function estimateNamespacePreorder(namespaceID, revealAddress, paymentAddress, paymentUtxos = 1) { const network = config_1.config.network; const preorderPromise = network.getNamespacePrice(namespaceID) .then(namespacePrice => skeletons_1.makeNamespacePreorderSkeleton(namespaceID, dummyConsensusHash, paymentAddress, revealAddress, namespacePrice)); return Promise.all([network.getFeeRate(), preorderPromise]) .then(([feeRate, preorderTX]) => { const outputsValue = utils_1.sumOutputValues(preorderTX); const txFee = feeRate * utils_1.estimateTXBytes(preorderTX, paymentUtxos, 0); return txFee + outputsValue; }); } /** * Estimates cost of a namesapce reveal transaction for a namespace * @param {AladinNamespace} namespace - the namespace to reveal * @param {String} revealAddress - the address to receive the namespace * (this must have been passed as 'revealAddress' to a prior namespace * preorder) * @param {String} paymentAddress - the address that pays for this transaction * @param {Number} paymentUtxos - the number of UTXOs we expect will be required * from the payment address * @returns {Promise} - a promise which resolves to the satoshi cost to * fund the reveal. This includes a 5500 satoshi dust output for the * preorder. Even though this is a change output, the payer must have * enough funds to generate this output, so we include it in the cost. * @private */ function estimateNamespaceReveal(namespace, revealAddress, paymentAddress, paymentUtxos = 1) { const network = config_1.config.network; const revealTX = skeletons_1.makeNamespaceRevealSkeleton(namespace, revealAddress); return network.getFeeRate() .then((feeRate) => { const outputsValue = utils_1.sumOutputValues(revealTX); // 1 additional output for payer change const txFee = feeRate * utils_1.estimateTXBytes(revealTX, paymentUtxos, 1); return txFee + outputsValue; }); } /** * Estimates the cost of a namespace-ready transaction for a namespace * @param {String} namespaceID - the namespace to ready * @param {Number} revealUtxos - the number of UTXOs we expect will * be required from the reveal address * @returns {Promise} - a promise which resolves to the satoshi cost to * fund this namespacey-ready transaction. * @private */ function estimateNamespaceReady(namespaceID, revealUtxos = 1) { const network = config_1.config.network; const readyTX = skeletons_1.makeNamespaceReadySkeleton(namespaceID); return network.getFeeRate() .then((feeRate) => { const outputsValue = utils_1.sumOutputValues(readyTX); const txFee = feeRate * utils_1.estimateTXBytes(readyTX, revealUtxos, 1); return txFee + outputsValue; }); } /** * Estimates the cost of a name-import transaction * @param {String} name - the fully-qualified name * @param {String} recipientAddr - the recipient * @param {String} zonefileHash - the zone file hash * @param {Number} importUtxos - the number of UTXOs we expect will * be required from the importer address * @returns {Promise} - a promise which resolves to the satoshi cost * to fund this name-import transaction * @private */ function estimateNameImport(name, recipientAddr, zonefileHash, importUtxos = 1) { const network = config_1.config.network; const importTX = skeletons_1.makeNameImportSkeleton(name, recipientAddr, zonefileHash); return network.getFeeRate() .then((feeRate) => { const outputsValue = utils_1.sumOutputValues(importTX); const txFee = feeRate * utils_1.estimateTXBytes(importTX, importUtxos, 1); return txFee + outputsValue; }); } /** * Estimates the cost of an announce transaction * @param {String} messageHash - the hash of the message * @param {Number} senderUtxos - the number of utxos we expect will * be required from the importer address * @returns {Promise} - a promise which resolves to the satoshi cost * to fund this announce transaction * @private */ function estimateAnnounce(messageHash, senderUtxos = 1) { const network = config_1.config.network; const announceTX = skeletons_1.makeAnnounceSkeleton(messageHash); return network.getFeeRate() .then((feeRate) => { const outputsValue = utils_1.sumOutputValues(announceTX); const txFee = feeRate * utils_1.estimateTXBytes(announceTX, senderUtxos, 1); return txFee + outputsValue; }); } /** * Estimates the cost of a token-transfer transaction * @param {String} recipientAddress - the recipient of the tokens * @param {String} tokenType - the type of token to spend * @param {Object} tokenAmount - a 64-bit unsigned BigInteger encoding the number of tokens * to spend * @param {String} scratchArea - an arbitrary string to store with the transaction * @param {Number} senderUtxos - the number of utxos we expect will * be required from the importer address * @param {Number} additionalOutputs - the number of outputs we expect to add beyond * just the recipient output (default = 1, if the token owner is also the bitcoin funder) * @returns {Promise} - a promise which resolves to the satoshi cost to * fund this token-transfer transaction * @private */ function estimateTokenTransfer(recipientAddress, tokenType, tokenAmount, scratchArea, senderUtxos = 1, additionalOutputs = 1) { const network = config_1.config.network; const tokenTransferTX = skeletons_1.makeTokenTransferSkeleton(recipientAddress, dummyConsensusHash, tokenType, tokenAmount, scratchArea); return network.getFeeRate() .then((feeRate) => { const outputsValue = utils_1.sumOutputValues(tokenTransferTX); const txFee = feeRate * utils_1.estimateTXBytes(tokenTransferTX, senderUtxos, additionalOutputs); return txFee + outputsValue; }); } /** * Generates a preorder transaction for a domain name. * @param {String} fullyQualifiedName - the name to pre-order * @param {String} destinationAddress - the address to receive the name (this * must be passed as the 'registrationAddress' in the register transaction) * @param {String | TransactionSigner} paymentKeyIn - a hex string of * the private key used to fund the transaction or a transaction signer object * @param {boolean} buildIncomplete - optional boolean, defaults to false, * indicating whether the function should attempt to return an unsigned (or not fully signed) * transaction. Useful for passing around a TX for multi-sig input signing. * @returns {Promise} - a promise which resolves to the hex-encoded transaction. * this function *does not* perform the requisite safety checks -- please see * the safety module for those. * @private */ function makePreorder(fullyQualifiedName, destinationAddress, paymentKeyIn, buildIncomplete = false) { const network = config_1.config.network; const namespace = fullyQualifiedName.split('.').pop(); const paymentKey = getTransactionSigner(paymentKeyIn); return paymentKey.getAddress().then((preorderAddress) => { const preorderPromise = Promise.all([network.getConsensusHash(), network.getNamePrice(fullyQualifiedName), network.getNamespaceBurnAddress(namespace)]) .then(([consensusHash, namePrice, burnAddress]) => skeletons_1.makePreorderSkeleton(fullyQualifiedName, consensusHash, preorderAddress, burnAddress, namePrice, destinationAddress)); return Promise.all([network.getUTXOs(preorderAddress), network.getFeeRate(), preorderPromise]) .then(([utxos, feeRate, preorderSkeleton]) => { const txB = bitcoinjs_lib_1.TransactionBuilder.fromTransaction(preorderSkeleton, network.layer1); txB.setVersion(1); const changeIndex = 1; // preorder skeleton always creates a change output at index = 1 const signingTxB = fundTransaction(txB, preorderAddress, utxos, feeRate, 0, changeIndex); return utils_1.signInputs(signingTxB, paymentKey); }) .then(signingTxB => returnTransactionHex(signingTxB, buildIncomplete)); }); } /** * Generates an update transaction for a domain name. * @param {String} fullyQualifiedName - the name to update * @param {String | TransactionSigner} ownerKeyIn - a hex string of the * owner key, or a transaction signer object. This will provide one * UTXO input, and also recieve a dust output. * @param {String | TransactionSigner} paymentKeyIn - a hex string, or a * transaction signer object, of the private key used to fund the * transaction's txfees * @param {String} zonefile - the zonefile data to update (this will be hashed * to include in the transaction), the zonefile itself must be published * after the UPDATE propagates. * @param {String} valueHash - if given, this is the hash to store (instead of * zonefile). zonefile will be ignored if this is given. * @param {boolean} buildIncomplete - optional boolean, defaults to false, * indicating whether the function should attempt to return an unsigned (or not fully signed) * transaction. Useful for passing around a TX for multi-sig input signing. * @returns {Promise} - a promise which resolves to the hex-encoded transaction. * this function *does not* perform the requisite safety checks -- please see * the safety module for those. * @private */ function makeUpdate(fullyQualifiedName, ownerKeyIn, paymentKeyIn, zonefile, valueHash = '', buildIncomplete = false) { const network = config_1.config.network; if (!valueHash && !zonefile) { return Promise.reject(new Error('Need zonefile or valueHash arguments')); } if (valueHash.length === 0) { if (!zonefile) { return Promise.reject(new Error('Need zonefile or valueHash arguments')); } valueHash = utils_1.hash160(Buffer.from(zonefile)).toString('hex'); } else if (valueHash.length !== 40) { return Promise.reject(new Error(`Invalid valueHash ${valueHash}`)); } const paymentKey = getTransactionSigner(paymentKeyIn); const ownerKey = getTransactionSigner(ownerKeyIn); return Promise.all([ownerKey.getAddress(), paymentKey.getAddress()]) .then(([ownerAddress, paymentAddress]) => { const txPromise = network.getConsensusHash() .then(consensusHash => skeletons_1.makeUpdateSkeleton(fullyQualifiedName, consensusHash, valueHash)) .then((updateTX) => { const txB = bitcoinjs_lib_1.TransactionBuilder.fromTransaction(updateTX, network.layer1); txB.setVersion(1); return txB; }); return Promise.all([txPromise, network.getUTXOs(paymentAddress), network.getUTXOs(ownerAddress), network.getFeeRate()]) .then(([txB, payerUtxos, ownerUtxos, feeRate]) => { const ownerInput = addOwnerInput(ownerUtxos, ownerAddress, txB); const signingTxB = fundTransaction(txB, paymentAddress, payerUtxos, feeRate, ownerInput.value); return utils_1.signInputs(signingTxB, paymentKey, [{ index: ownerInput.index, signer: ownerKey }]); }); }) .then(signingTxB => returnTransactionHex(signingTxB, buildIncomplete)); } /** * Generates a register transaction for a domain name. * @param {String} fullyQualifiedName - the name to register * @param {String} registerAddress - the address to receive the name (this * must have been passed as the 'destinationAddress' in the preorder transaction) * this address will receive a dust UTXO * @param {String | TransactionSigner} paymentKeyIn - a hex string of * the private key (or a TransactionSigner object) used to fund the * transaction (this *must* be the same as the payment address used * to fund the preorder) * @param {String} zonefile - the zonefile data to include (this will be hashed * to include in the transaction), the zonefile itself must be published * after the UPDATE propagates. * @param {String} valueHash - the hash of the zone file data to include. * It will be used instead of zonefile, if given * @param {boolean} buildIncomplete - optional boolean, defaults to false, * indicating whether the function should attempt to return an unsigned (or not fully signed) * transaction. Useful for passing around a TX for multi-sig input signing. * @returns {Promise} - a promise which resolves to the hex-encoded transaction. * this function *does not* perform the requisite safety checks -- please see * the safety module for those. * @private */ function makeRegister(fullyQualifiedName, registerAddress, paymentKeyIn, zonefile = null, valueHash = null, buildIncomplete = false) { const network = config_1.config.network; if (!valueHash && !!zonefile) { valueHash = utils_1.hash160(Buffer.from(zonefile)).toString('hex'); } else if (!!valueHash && valueHash.length !== 40) { return Promise.reject(new Error(`Invalid zonefile hash ${valueHash}`)); } const registerSkeleton = skeletons_1.makeRegisterSkeleton(fullyQualifiedName, registerAddress, valueHash); const txB = bitcoinjs_lib_1.TransactionBuilder.fromTransaction(registerSkeleton, network.layer1); txB.setVersion(1); const paymentKey = getTransactionSigner(paymentKeyIn); return paymentKey.getAddress().then(paymentAddress => Promise.all([network.getUTXOs(paymentAddress), network.getFeeRate()]) .then(([utxos, feeRate]) => { const signingTxB = fundTransaction(txB, paymentAddress, utxos, feeRate, 0); return utils_1.signInputs(signingTxB, paymentKey); })) .then(signingTxB => returnTransactionHex(signingTxB, buildIncomplete)); } /** * Generates a transfer transaction for a domain name. * @param {String} fullyQualifiedName - the name to transfer * @param {String} destinationAddress - the address to receive the name. * this address will receive a dust UTXO * @param {String | TransactionSigner} ownerKeyIn - a hex string of * the current owner's private key (or a TransactionSigner object) * @param {String | TransactionSigner} paymentKeyIn - a hex string of * the private key used to fund the transaction (or a * TransactionSigner object) * @param {Boolean} keepZonefile - if true, then preserve the name's zone file * @param {boolean} buildIncomplete - optional boolean, defaults to false, * indicating whether the function should attempt to return an unsigned (or not fully signed) * transaction. Useful for passing around a TX for multi-sig input signing. * @returns {Promise} - a promise which resolves to the hex-encoded transaction. * this function *does not* perform the requisite safety checks -- please see * the safety module for those. * @private */ function makeTransfer(fullyQualifiedName, destinationAddress, ownerKeyIn, paymentKeyIn, keepZonefile = false, buildIncomplete = false) { const network = config_1.config.network; const paymentKey = getTransactionSigner(paymentKeyIn); const ownerKey = getTransactionSigner(ownerKeyIn); return Promise.all([ownerKey.getAddress(), paymentKey.getAddress()]) .then(([ownerAddress, paymentAddress]) => { const txPromise = network.getConsensusHash() .then(consensusHash => skeletons_1.makeTransferSkeleton(fullyQualifiedName, consensusHash, destinationAddress, keepZonefile)) .then((transferTX) => { const txB = bitcoinjs_lib_1.TransactionBuilder .fromTransaction(transferTX, network.layer1); txB.setVersion(1); return txB; }); return Promise.all([txPromise, network.getUTXOs(paymentAddress), network.getUTXOs(ownerAddress), network.getFeeRate()]) .then(([txB, payerUtxos, ownerUtxos, feeRate]) => { const ownerInput = addOwnerInput(ownerUtxos, ownerAddress, txB); const signingTxB = fundTransaction(txB, paymentAddress, payerUtxos, feeRate, ownerInput.value); return utils_1.signInputs(signingTxB, paymentKey, [{ index: ownerInput.index, signer: ownerKey }]); }); }) .then(signingTxB => returnTransactionHex(signingTxB, buildIncomplete)); } /** * Generates a revoke transaction for a domain name. * @param {String} fullyQualifiedName - the name to revoke * @param {String | TransactionSigner} ownerKeyIn - a hex string of * the current owner's private key (or a TransactionSigner object) * @param {String | TransactionSigner} paymentKeyIn - a hex string of * the private key used to fund the transaction (or a * TransactionSigner object) * @param {boolean} buildIncomplete - optional boolean, defaults to false, * indicating whether the function should attempt to return an unsigned (or not fully signed) * transaction. Useful for passing around a TX for multi-sig input signing. * @returns {Promise} - a promise which resolves to the hex-encoded transaction. * this function *does not* perform the requisite safety checks -- please see * the safety module for those. * @private */ function makeRevoke(fullyQualifiedName, ownerKeyIn, paymentKeyIn, buildIncomplete = false) { const network = config_1.config.network; const paymentKey = getTransactionSigner(paymentKeyIn); const ownerKey = getTransactionSigner(ownerKeyIn); return Promise.all([ownerKey.getAddress(), paymentKey.getAddress()]) .then(([ownerAddress, paymentAddress]) => { const revokeTX = skeletons_1.makeRevokeSkeleton(fullyQualifiedName); const txPromise = bitcoinjs_lib_1.TransactionBuilder.fromTransaction(revokeTX, network.layer1); txPromise.setVersion(1); return Promise.all([txPromise, network.getUTXOs(paymentAddress), network.getUTXOs(ownerAddress), network.getFeeRate()]) .then(([txB, payerUtxos, ownerUtxos, feeRate]) => { const ownerInput = addOwnerInput(ownerUtxos, ownerAddress, txB); const signingTxB = fundTransaction(txB, paymentAddress, payerUtxos, feeRate, ownerInput.value); return utils_1.signInputs(signingTxB, paymentKey, [{ index: ownerInput.index, signer: ownerKey }]); }); }) .then(signingTxB => returnTransactionHex(signingTxB, buildIncomplete)); } /** * Generates a renewal transaction for a domain name. * @param {String} fullyQualifiedName - the name to transfer * @param {String} destinationAddress - the address to receive the name after renewal * this address will receive a dust UTXO * @param {String | TransactionSigner} ownerKeyIn - a hex string of * the current owner's private key (or a TransactionSigner object) * @param {String | TransactionSigner} paymentKeyIn - a hex string of * the private key used to fund the renewal (or a TransactionSigner * object) * @param {String} zonefile - the zonefile data to include, if given (this will be hashed * to include in the transaction), the zonefile itself must be published * after the RENEWAL propagates. * @param {String} valueHash - the raw zone file hash to include (this will be used * instead of zonefile, if given). * @param {boolean} buildIncomplete - optional boolean, defaults to false, * indicating whether the function should attempt to return an unsigned (or not fully signed) * transaction. Useful for passing around a TX for multi-sig input signing. * @returns {Promise} - a promise which resolves to the hex-encoded transaction. * this function *does not* perform the requisite safety checks -- please see * the safety module for those. * @private */ function makeRenewal(fullyQualifiedName, destinationAddress, ownerKeyIn, paymentKeyIn, zonefile = null, valueHash = null, buildIncomplete = false) { const network = config_1.config.network; if (!valueHash && !!zonefile) { valueHash = utils_1.hash160(Buffer.from(zonefile)).toString('hex'); } const namespace = fullyQualifiedName.split('.').pop(); const paymentKey = getTransactionSigner(paymentKeyIn); const ownerKey = getTransactionSigner(ownerKeyIn); return Promise.all([ownerKey.getAddress(), paymentKey.getAddress()]) .then(([ownerAddress, paymentAddress]) => { const txPromise = Promise.all([network.getNamePrice(fullyQualifiedName), network.getNamespaceBurnAddress(namespace)]) .then(([namePrice, burnAddress]) => skeletons_1.makeRenewalSkeleton(fullyQualifiedName, destinationAddress, ownerAddress, burnAddress, namePrice, valueHash)) .then((tx) => { const txB = bitcoinjs_lib_1.TransactionBuilder.fromTransaction(tx, network.layer1); txB.setVersion(1); return txB; }); return Promise.all([txPromise, network.getUTXOs(paymentAddress), network.getUTXOs(ownerAddress), network.getFeeRate()]) .then(([txB, payerUtxos, ownerUtxos, feeRate]) => { const ownerInput = addOwnerInput(ownerUtxos, ownerAddress, txB, false); const txInner = utils_1.getTransactionInsideBuilder(txB); const ownerOutput = txInner.outs[2]; const ownerOutputAddr = bitcoinjs_lib_1.address.fromOutputScript(ownerOutput.script, network.layer1); if (ownerOutputAddr !== ownerAddress) { return Promise.reject(new Error(`Original owner ${ownerAddress} should have an output at ` + `index 2 in transaction was ${ownerOutputAddr}`)); } ownerOutput.value = ownerInput.value; const signingTxB = fundTransaction(txB, paymentAddress, payerUtxos, feeRate, ownerInput.value); return utils_1.signInputs(signingTxB, paymentKey, [{ index: ownerInput.index, signer: ownerKey }]); }); }) .then(signingTxB => returnTransactionHex(signingTxB, buildIncomplete)); } /** * Generates a namespace preorder transaction for a namespace * @param {String} namespaceID - the namespace to pre-order * @param {String} revealAddress - the address to receive the namespace (this * must be passed as the 'revealAddress' in the namespace-reveal transaction) * @param {String | TransactionSigner} paymentKeyIn - a hex string of * the private key used to fund the transaction (or a * TransactionSigner object) * @param {boolean} buildIncomplete - optional boolean, defaults to false, * indicating whether the function should attempt to return an unsigned (or not fully signed) * transaction. Useful for passing around a TX for multi-sig input signing. * @returns {Promise} - a promise which resolves to the hex-encoded transaction. * this function *does not* perform the requisite safety checks -- please see * the safety module for those. * @private * * @ignore */ function makeNamespacePreorder(namespaceID, revealAddress, paymentKeyIn, buildIncomplete = false) { const network = config_1.config.network; const paymentKey = getTransactionSigner(paymentKeyIn); return paymentKey.getAddress().then((preorderAddress) => { const preorderPromise = Promise.all([network.getConsensusHash(), network.getNamespacePrice(namespaceID)]) .then(([consensusHash, namespacePrice]) => skeletons_1.makeNamespacePreorderSkeleton(namespaceID, consensusHash, preorderAddress, revealAddress, namespacePrice)); return Promise.all([network.getUTXOs(preorderAddress), network.getFeeRate(), preorderPromise]) .then(([utxos, feeRate, preorderSkeleton]) => { const txB = bitcoinjs_lib_1.TransactionBuilder.fromTransaction(preorderSkeleton, network.layer1); txB.setVersion(1); const changeIndex = 1; // preorder skeleton always creates a change output at index = 1 const signingTxB = fundTransaction(txB, preorderAddress, utxos, feeRate, 0, changeIndex); return utils_1.signInputs(signingTxB, paymentKey); }) .then(signingTxB => returnTransactionHex(signingTxB, buildIncomplete)); }); } /** * Generates a namespace reveal transaction for a namespace * @param {AladinNamespace} namespace - the namespace to reveal * @param {String} revealAddress - the address to receive the namespace (this * must be passed as the 'revealAddress' in the namespace-reveal transaction) * @param {String | TransactionSigner} paymentKeyIn - a hex string (or * a TransactionSigner object) of the private key used to fund the * transaction * @param {boolean} buildIncomplete - optional boolean, defaults to false, * indicating whether the function should attempt to return an unsigned (or not fully signed) * transaction. Useful for passing around a TX for multi-sig input signing. * @returns {Promise} - a promise which resolves to the hex-encoded transaction. * this function *does not* perform the requisite safety checks -- please see * the safety module for those. * @private */ function makeNamespaceReveal(namespace, revealAddress, paymentKeyIn, buildIncomplete = false) { const network = config_1.config.network; if (!namespace.check()) { return Promise.reject(new Error('Invalid namespace')); } const namespaceRevealTX = skeletons_1.makeNamespaceRevealSkeleton(namespace, revealAddress); const paymentKey = getTransactionSigner(paymentKeyIn); return paymentKey.getAddress().then(preorderAddress => Promise.all([network.getUTXOs(preorderAddress), network.getFeeRate()]) .then(([utxos, feeRate]) => { const txB = bitcoinjs_lib_1.TransactionBuilder .fromTransaction(namespaceRevealTX, network.layer1); txB.setVersion(1); const signingTxB = fundTransaction(txB, preorderAddress, utxos, feeRate, 0); return utils_1.signInputs(signingTxB, paymentKey); })) .then(signingTxB => returnTransactionHex(signingTxB, buildIncomplete)); } /** * Generates a namespace ready transaction for a namespace * @param {String} namespaceID - the namespace to launch * @param {String | TransactionSigner} revealKeyIn - the private key * of the 'revealAddress' used to reveal the namespace * @param {boolean} buildIncomplete - optional boolean, defaults to false, * indicating whether the function should attempt to return an unsigned (or not fully signed) * transaction. Useful for passing around a TX for multi-sig input signing. * @returns {Promise} - a promise which resolves to the hex-encoded transaction. * this function *does not* perform the requisite safety checks -- please see * the safety module for those. * @private */ function makeNamespaceReady(namespaceID, revealKeyIn, buildIncomplete = false) { const network = config_1.config.network; const namespaceReadyTX = skeletons_1.makeNamespaceReadySkeleton(namespaceID); const revealKey = getTransactionSigner(revealKeyIn); return revealKey.getAddress().then(revealAddress => Promise.all([network.getUTXOs(revealAddress), network.getFeeRate()]) .then(([utxos, feeRate]) => { const txB = bitcoinjs_lib_1.TransactionBuilder.fromTransaction(namespaceReadyTX, network.layer1); txB.setVersion(1); const signingTxB = fundTransaction(txB, revealAddress, utxos, feeRate, 0); return utils_1.signInputs(signingTxB, revealKey); })) .then(signingTxB => returnTransactionHex(signingTxB, buildIncomplete)); } /** * Generates a name import transaction for a namespace * @param {String} name - the name to import * @param {String} recipientAddr - the address to receive the name * @param {String} zonefileHash - the hash of the zonefile to give this name * @param {String | TransactionSigner} importerKeyIn - the private key * that pays for the import * @param {boolean} buildIncomplete - optional boolean, defaults to false, * indicating whether the function should attempt to return an unsigned (or not fully signed) * transaction. Useful for passing around a TX for multi-sig input signing. * @returns {Promise} - a promise which resolves to the hex-encoded transaction. * this function does not perform the requisite safety checks -- please see * the safety module for those. * @private */ function makeNameImport(name, recipientAddr, zonefileHash, importerKeyIn, buildIncomplete = false) { const network = config_1.config.network; const nameImportTX = skeletons_1.makeNameImportSkeleton(name, recipientAddr, zonefileHash); const importerKey = getTransactionSigner(importerKeyIn); return importerKey.getAddress().then(importerAddress => Promise.all([network.getUTXOs(importerAddress), network.getFeeRate()]) .then(([utxos, feeRate]) => { const txB = bitcoinjs_lib_1.TransactionBuilder.fromTransaction(nameImportTX, network.layer1); const signingTxB = fundTransaction(txB, importerAddress, utxos, feeRate, 0); return utils_1.signInputs(signingTxB, importerKey); })) .then(signingTxB => returnTransactionHex(signingTxB, buildIncomplete)); } /** * Generates an announce transaction * @param {String} messageHash - the hash of the message to send. Should be * an already-announced zone file hash * @param {String | TransactionSigner} senderKeyIn - the private key * that pays for the transaction. Should be the key that owns the * name that the message recipients subscribe to * @param {boolean} buildIncomplete - optional boolean, defaults to false, * indicating whether the function should attempt to return an unsigned (or not fully signed) * transaction. Useful for passing around a TX for multi-sig input signing. * @returns {Promise} - a promise which resolves to the hex-encoded transaction. * this function does not perform the requisite safety checks -- please see the * safety module for those. * @private */ function makeAnnounce(messageHash, senderKeyIn, buildIncomplete = false) { const network = config_1.config.network; const announceTX = skeletons_1.makeAnnounceSkeleton(messageHash); const senderKey = getTransactionSigner(senderKeyIn); return senderKey.getAddress().then(senderAddress => Promise.all([network.getUTXOs(senderAddress), network.getFeeRate()]) .then(([utxos, feeRate]) => { const txB = bitcoinjs_lib_1.TransactionBuilder.fromTransaction(announceTX, network.layer1); const signingTxB = fundTransaction(txB, senderAddress, utxos, feeRate, 0); return utils_1.signInputs(signingTxB, senderKey); })) .then(signingTxB => returnTransactionHex(signingTxB, buildIncomplete)); } /** * Generates a token-transfer transaction * @param {String} recipientAddress - the address to receive the tokens * @param {String} tokenType - the type of tokens to send * @param {Object} tokenAmount - the BigInteger encoding of an unsigned 64-bit number of * tokens to send * @param {String} scratchArea - an arbitrary string to include with the transaction * @param {String | TransactionSigner} senderKeyIn - the hex-encoded private key to send * the tokens * @param {String | TransactionSigner} btcFunderKeyIn - the hex-encoded private key to fund * the bitcoin fees for the transaction. Optional -- if not passed, will attempt to * fund with sender key. * @param {boolean} buildIncomplete - optional boolean, defaults to false, * indicating whether the function should attempt to return an unsigned (or not fully signed) * transaction. Useful for passing around a TX for multi-sig input signing. * @returns {Promise} - a promise which resolves to the hex-encoded transaction. * This function does not perform the requisite safety checks -- please see the * safety module for those. * @private */ function makeTokenTransfer(recipientAddress, tokenType, tokenAmount, scratchArea, senderKeyIn, btcFunderKeyIn, buildIncomplete = false) { const network = config_1.config.network; const separateFunder = !!btcFunderKeyIn; const senderKey = getTransactionSigner(senderKeyIn); const btcKey = btcFunderKeyIn ? getTransactionSigner(btcFunderKeyIn) : senderKey; const txPromise = network.getConsensusHash() .then(consensusHash => skeletons_1.makeTokenTransferSkeleton(recipientAddress, consensusHash, tokenType, tokenAmount, scratchArea)); return Promise.all([senderKey.getAddress(), btcKey.getAddress()]) .then(([senderAddress, btcAddress]) => { const btcUTXOsPromise = separateFunder ? network.getUTXOs(btcAddress) : Promise.resolve([]); return Promise.all([ network.getUTXOs(senderAddress), btcUTXOsPromise, network.getFeeRate(), txPromise ]).then(([senderUTXOs, btcUTXOs, feeRate, tokenTransferTX]) => { const txB = bitcoinjs_lib_1.TransactionBuilder.fromTransaction(tokenTransferTX, network.layer1); if (separateFunder) { const payerInput = addOwnerInput(senderUTXOs, senderAddress, txB); const signingTxB = fundTransaction(txB, btcAddress, btcUTXOs, feeRate, payerInput.value); return utils_1.signInputs(signingTxB, btcKey, [{ index: payerInput.index, signer: senderKey }]); } else { const signingTxB = fundTransaction(txB, senderAddress, senderUTXOs, feeRate, 0); return utils_1.signInputs(signingTxB, senderKey); } }); }) .then(signingTxB => returnTransactionHex(signingTxB, buildIncomplete)); } /** * Generates a bitcoin spend to a specified address. This will fund up to `amount` * of satoshis from the payer's UTXOs. It will generate a change output if and only * if the amount of leftover change is *greater* than the additional fees associated * with the extra output. If the requested amount is not enough to fund the transaction's * associated fees, then this will reject with a InvalidAmountError * * UTXOs are selected largest to smallest, and UTXOs which cannot fund the fees associated * with their own input will not be included. * * If you specify an amount > the total balance of the payer address, then this will * generate a maximum spend transaction * * @param {String} destinationAddress - the address to receive the bitcoin payment * @param {String | TransactionSigner} paymentKeyIn - the private key * used to fund the bitcoin spend * @param {number} amount - the amount in satoshis for the payment address to * spend in this transaction * @param {boolean} buildIncomplete - optional boolean, defaults to false, * indicating whether the function should attempt to return an unsigned (or not fully signed) * transaction. Useful for passing around a TX for multi-sig input signing. * @returns {Promise} - a promise which resolves to the hex-encoded transaction. * @private */ function makeBitcoinSpend(destinationAddress, paymentKeyIn, amount, buildIncomplete = false) { if (amount <= 0) { return Promise.reject(new errors_1.InvalidParameterError('amount', 'amount must be greater than zero')); } const network = config_1.config.network; const paymentKey = getTransactionSigner(paymentKeyIn); return paymentKey.getAddress().then(paymentAddress => Promise.all([network.getUTXOs(paymentAddress), network.getFeeRate()]) .then(([utxos, feeRate]) => { const txB = new bitcoinjs_lib_1.TransactionBuilder(network.layer1); txB.setVersion(1); const destinationIndex = txB.addOutput(destinationAddress, 0); // will add utxos up to _amount_ and return the amount of leftover _change_ let change; try { change = utils_1.addUTXOsToFund(txB, utxos, amount, feeRate, false); } catch (err) { if (err.name === 'NotEnoughFundsError') { // actual amount funded = amount requested - remainder amount -= err.leftToFund; change = 0; } else { throw err; } } let feesToPay = feeRate * utils_1.estimateTXBytes(txB, 0, 0); const feeForChange = feeRate * (utils_1.estimateTXBytes(txB, 0, 1)) - feesToPay; // it's worthwhile to add a change output if (change > feeForChange) { feesToPay += feeForChange; txB.addOutput(paymentAddress, change); } // now let's compute how much output is leftover once we pay the fees. const outputAmount = amount - feesToPay; if (outputAmount < utils_1.DUST_MINIMUM) { throw new errors_1.InvalidAmountError(feesToPay, amount); } // we need to manually set the output values now const txInner = utils_1.getTransactionInsideBuilder(txB); const txOut = txInner.outs[destinationIndex]; txOut.value = outputAmount; // ready to sign. return utils_1.signInputs(txB, paymentKey); })) .then(signingTxB => returnTransactionHex(signingTxB, buildIncomplete)); } exports.transactions = { makeRenewal, makeUpdate, makePreorder, makeRegister, makeTransfer, makeRevoke, makeNamespacePreorder, makeNamespaceReveal, makeNamespaceReady, makeBitcoinSpend, makeNameImport, makeAnnounce, makeTokenTransfer, AladinNamespace: skeletons_1.AladinNamespace, estimatePreorder, estimateRegister, estimateTransfer, estimateUpdate, estimateRenewal, estimateRevoke, estimateNamespacePreorder, estimateNamespaceReveal, estimateNamespaceReady, estimateNameImport, estimateAnnounce, estimateTokenTransfer }; //# sourceMappingURL=txbuild.js.map