blockstack
Version:
The Blockstack Javascript library for authentication, identity, and storage.
1,091 lines (928 loc) • 54 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.transactions = undefined;
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _bitcoinjsLib = require('bitcoinjs-lib');
var _bitcoinjsLib2 = _interopRequireDefault(_bitcoinjsLib);
var _bigi = require('bigi');
var _bigi2 = _interopRequireDefault(_bigi);
var _utils = require('./utils');
var _skeletons = require('./skeletons');
var _config = require('../config');
var _errors = require('../errors');
var _signers = require('./signers');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var dummyConsensusHash = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
var dummyZonefileHash = 'ffffffffffffffffffffffffffffffffffffffff';
function addOwnerInput(utxos, ownerAddress, txB) {
var addChangeOut = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
// add an owner UTXO and a change out.
if (utxos.length <= 0) {
throw new Error('Owner has no UTXOs for UPDATE.');
}
utxos.sort(function (a, b) {
return a.value - b.value;
});
var ownerUTXO = utxos[0];
var ownerInput = txB.addInput(ownerUTXO.tx_hash, ownerUTXO.tx_output_n);
if (addChangeOut) {
txB.addOutput(ownerAddress, ownerUTXO.value);
}
return { index: ownerInput, value: ownerUTXO.value };
}
function fundTransaction(txB, paymentAddress, utxos, feeRate, inAmounts) {
var changeIndex = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : null;
// change index for the payer.
if (changeIndex === null) {
changeIndex = txB.addOutput(paymentAddress, _utils.DUST_MINIMUM);
}
// fund the transaction fee.
var txFee = (0, _utils.estimateTXBytes)(txB, 0, 0) * feeRate;
var outAmounts = (0, _utils.sumOutputValues)(txB);
var change = (0, _utils.addUTXOsToFund)(txB, utxos, txFee + outAmounts - inAmounts, feeRate);
txB.__tx.outs[changeIndex].value += change;
return txB;
}
function returnTransactionHex(txB) {
var buildIncomplete = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (buildIncomplete) {
return txB.buildIncomplete().toHex();
} else {
return txB.build().toHex();
}
}
function getTransactionSigner(input) {
if (typeof input === 'string') {
return _signers.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) {
var paymentUtxos = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
var network = _config.config.network;
var preorderPromise = network.getNamePrice(fullyQualifiedName).then(function (namePrice) {
return (0, _skeletons.makePreorderSkeleton)(fullyQualifiedName, dummyConsensusHash, paymentAddress, network.getDefaultBurnAddress(), namePrice, destinationAddress);
});
return Promise.all([network.getFeeRate(), preorderPromise]).then(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2),
feeRate = _ref2[0],
preorderTX = _ref2[1];
var outputsValue = (0, _utils.sumOutputValues)(preorderTX);
var txFee = feeRate * (0, _utils.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) {
var includingZonefile = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var paymentUtxos = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
var network = _config.config.network;
var valueHash = void 0;
if (includingZonefile) {
valueHash = dummyZonefileHash;
}
var registerTX = (0, _skeletons.makeRegisterSkeleton)(fullyQualifiedName, registerAddress, valueHash);
return network.getFeeRate().then(function (feeRate) {
var outputsValue = (0, _utils.sumOutputValues)(registerTX);
// 1 additional output for payer change
var txFee = feeRate * (0, _utils.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) {
var paymentUtxos = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
var network = _config.config.network;
var updateTX = (0, _skeletons.makeUpdateSkeleton)(fullyQualifiedName, dummyConsensusHash, dummyZonefileHash);
return network.getFeeRate().then(function (feeRate) {
var outputsValue = (0, _utils.sumOutputValues)(updateTX);
// 1 additional input for the owner
// 2 additional outputs for owner / payer change
var txFee = feeRate * (0, _utils.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) {
var paymentUtxos = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
var network = _config.config.network;
var transferTX = (0, _skeletons.makeTransferSkeleton)(fullyQualifiedName, dummyConsensusHash, destinationAddress);
return network.getFeeRate().then(function (feeRate) {
var outputsValue = (0, _utils.sumOutputValues)(transferTX);
// 1 additional input for the owner
// 2 additional outputs for owner / payer change
var txFee = feeRate * (0, _utils.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) {
var includingZonefile = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
var paymentUtxos = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 1;
var network = _config.config.network;
var valueHash = void 0;
if (includingZonefile) {
valueHash = dummyZonefileHash;
}
var renewalPromise = network.getNamePrice(fullyQualifiedName).then(function (namePrice) {
return (0, _skeletons.makeRenewalSkeleton)(fullyQualifiedName, destinationAddress, ownerAddress, network.getDefaultBurnAddress(), namePrice, valueHash);
});
return Promise.all([network.getFeeRate(), renewalPromise]).then(function (_ref3) {
var _ref4 = _slicedToArray(_ref3, 2),
feeRate = _ref4[0],
renewalTX = _ref4[1];
var outputsValue = (0, _utils.sumOutputValues)(renewalTX);
// 1 additional input for the owner
// and renewal skeleton includes all outputs for owner change, but not for payer change.
var txFee = feeRate * (0, _utils.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) {
var paymentUtxos = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
var network = _config.config.network;
var revokeTX = (0, _skeletons.makeRevokeSkeleton)(fullyQualifiedName);
return Promise.all([network.getFeeRate()]).then(function (_ref5) {
var _ref6 = _slicedToArray(_ref5, 1),
feeRate = _ref6[0];
var outputsValue = (0, _utils.sumOutputValues)(revokeTX);
// 1 additional input for owner
// 1 additional output for payer change
var txFee = feeRate * (0, _utils.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) {
var paymentUtxos = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
var network = _config.config.network;
var preorderPromise = network.getNamespacePrice(namespaceID).then(function (namespacePrice) {
return (0, _skeletons.makeNamespacePreorderSkeleton)(namespaceID, dummyConsensusHash, paymentAddress, revealAddress, namespacePrice);
});
return Promise.all([network.getFeeRate(), preorderPromise]).then(function (_ref7) {
var _ref8 = _slicedToArray(_ref7, 2),
feeRate = _ref8[0],
preorderTX = _ref8[1];
var outputsValue = (0, _utils.sumOutputValues)(preorderTX);
var txFee = feeRate * (0, _utils.estimateTXBytes)(preorderTX, paymentUtxos, 0);
return txFee + outputsValue;
});
}
/**
* Estimates cost of a namesapce reveal transaction for a namespace
* @param {BlockstackNamespace} 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) {
var paymentUtxos = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
var network = _config.config.network;
var revealTX = (0, _skeletons.makeNamespaceRevealSkeleton)(namespace, revealAddress);
return network.getFeeRate().then(function (feeRate) {
var outputsValue = (0, _utils.sumOutputValues)(revealTX);
// 1 additional output for payer change
var txFee = feeRate * (0, _utils.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) {
var revealUtxos = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
var network = _config.config.network;
var readyTX = (0, _skeletons.makeNamespaceReadySkeleton)(namespaceID);
return network.getFeeRate().then(function (feeRate) {
var outputsValue = (0, _utils.sumOutputValues)(readyTX);
var txFee = feeRate * (0, _utils.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) {
var importUtxos = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
var network = _config.config.network;
var importTX = (0, _skeletons.makeNameImportSkeleton)(name, recipientAddr, zonefileHash);
return network.getFeeRate().then(function (feeRate) {
var outputsValue = (0, _utils.sumOutputValues)(importTX);
var txFee = feeRate * (0, _utils.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) {
var senderUtxos = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
var network = _config.config.network;
var announceTX = (0, _skeletons.makeAnnounceSkeleton)(messageHash);
return network.getFeeRate().then(function (feeRate) {
var outputsValue = (0, _utils.sumOutputValues)(announceTX);
var txFee = feeRate * (0, _utils.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) {
var senderUtxos = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
var additionalOutputs = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 1;
var network = _config.config.network;
var tokenTransferTX = (0, _skeletons.makeTokenTransferSkeleton)(recipientAddress, dummyConsensusHash, tokenType, tokenAmount, scratchArea);
return network.getFeeRate().then(function (feeRate) {
var outputsValue = (0, _utils.sumOutputValues)(tokenTransferTX);
var txFee = feeRate * (0, _utils.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) {
var buildIncomplete = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var network = _config.config.network;
var namespace = fullyQualifiedName.split('.').pop();
var paymentKey = getTransactionSigner(paymentKeyIn);
return paymentKey.getAddress().then(function (preorderAddress) {
var preorderPromise = Promise.all([network.getConsensusHash(), network.getNamePrice(fullyQualifiedName), network.getNamespaceBurnAddress(namespace)]).then(function (_ref9) {
var _ref10 = _slicedToArray(_ref9, 3),
consensusHash = _ref10[0],
namePrice = _ref10[1],
burnAddress = _ref10[2];
return (0, _skeletons.makePreorderSkeleton)(fullyQualifiedName, consensusHash, preorderAddress, burnAddress, namePrice, destinationAddress);
});
return Promise.all([network.getUTXOs(preorderAddress), network.getFeeRate(), preorderPromise]).then(function (_ref11) {
var _ref12 = _slicedToArray(_ref11, 3),
utxos = _ref12[0],
feeRate = _ref12[1],
preorderSkeleton = _ref12[2];
var txB = _bitcoinjsLib2.default.TransactionBuilder.fromTransaction(preorderSkeleton, network.layer1);
txB.setVersion(1);
var changeIndex = 1; // preorder skeleton always creates a change output at index = 1
var signingTxB = fundTransaction(txB, preorderAddress, utxos, feeRate, 0, changeIndex);
return (0, _utils.signInputs)(signingTxB, paymentKey);
}).then(function (signingTxB) {
return 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) {
var valueHash = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : '';
var buildIncomplete = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
var network = _config.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 = (0, _utils.hash160)(Buffer.from(zonefile)).toString('hex');
} else if (valueHash.length !== 40) {
return Promise.reject(new Error('Invalid valueHash ' + valueHash));
}
var paymentKey = getTransactionSigner(paymentKeyIn);
var ownerKey = getTransactionSigner(ownerKeyIn);
return Promise.all([ownerKey.getAddress(), paymentKey.getAddress()]).then(function (_ref13) {
var _ref14 = _slicedToArray(_ref13, 2),
ownerAddress = _ref14[0],
paymentAddress = _ref14[1];
var txPromise = network.getConsensusHash().then(function (consensusHash) {
return (0, _skeletons.makeUpdateSkeleton)(fullyQualifiedName, consensusHash, valueHash);
}).then(function (updateTX) {
var txB = _bitcoinjsLib2.default.TransactionBuilder.fromTransaction(updateTX, network.layer1);
txB.setVersion(1);
return txB;
});
return Promise.all([txPromise, network.getUTXOs(paymentAddress), network.getUTXOs(ownerAddress), network.getFeeRate()]).then(function (_ref15) {
var _ref16 = _slicedToArray(_ref15, 4),
txB = _ref16[0],
payerUtxos = _ref16[1],
ownerUtxos = _ref16[2],
feeRate = _ref16[3];
var ownerInput = addOwnerInput(ownerUtxos, ownerAddress, txB);
var signingTxB = fundTransaction(txB, paymentAddress, payerUtxos, feeRate, ownerInput.value);
return (0, _utils.signInputs)(signingTxB, paymentKey, [{ index: ownerInput.index, signer: ownerKey }]);
});
}).then(function (signingTxB) {
return 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) {
var zonefile = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
var valueHash = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
var buildIncomplete = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
var network = _config.config.network;
if (!valueHash && !!zonefile) {
valueHash = (0, _utils.hash160)(Buffer.from(zonefile)).toString('hex');
} else if (!!valueHash && valueHash.length !== 40) {
return Promise.reject(new Error('Invalid zonefile hash ' + valueHash));
}
var registerSkeleton = (0, _skeletons.makeRegisterSkeleton)(fullyQualifiedName, registerAddress, valueHash);
var txB = _bitcoinjsLib2.default.TransactionBuilder.fromTransaction(registerSkeleton, network.layer1);
txB.setVersion(1);
var paymentKey = getTransactionSigner(paymentKeyIn);
return paymentKey.getAddress().then(function (paymentAddress) {
return Promise.all([network.getUTXOs(paymentAddress), network.getFeeRate()]).then(function (_ref17) {
var _ref18 = _slicedToArray(_ref17, 2),
utxos = _ref18[0],
feeRate = _ref18[1];
var signingTxB = fundTransaction(txB, paymentAddress, utxos, feeRate, 0);
return (0, _utils.signInputs)(signingTxB, paymentKey);
});
}).then(function (signingTxB) {
return 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) {
var keepZonefile = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
var buildIncomplete = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
var network = _config.config.network;
var paymentKey = getTransactionSigner(paymentKeyIn);
var ownerKey = getTransactionSigner(ownerKeyIn);
return Promise.all([ownerKey.getAddress(), paymentKey.getAddress()]).then(function (_ref19) {
var _ref20 = _slicedToArray(_ref19, 2),
ownerAddress = _ref20[0],
paymentAddress = _ref20[1];
var txPromise = network.getConsensusHash().then(function (consensusHash) {
return (0, _skeletons.makeTransferSkeleton)(fullyQualifiedName, consensusHash, destinationAddress, keepZonefile);
}).then(function (transferTX) {
var txB = _bitcoinjsLib2.default.TransactionBuilder.fromTransaction(transferTX, network.layer1);
txB.setVersion(1);
return txB;
});
return Promise.all([txPromise, network.getUTXOs(paymentAddress), network.getUTXOs(ownerAddress), network.getFeeRate()]).then(function (_ref21) {
var _ref22 = _slicedToArray(_ref21, 4),
txB = _ref22[0],
payerUtxos = _ref22[1],
ownerUtxos = _ref22[2],
feeRate = _ref22[3];
var ownerInput = addOwnerInput(ownerUtxos, ownerAddress, txB);
var signingTxB = fundTransaction(txB, paymentAddress, payerUtxos, feeRate, ownerInput.value);
return (0, _utils.signInputs)(signingTxB, paymentKey, [{ index: ownerInput.index, signer: ownerKey }]);
});
}).then(function (signingTxB) {
return 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) {
var buildIncomplete = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var network = _config.config.network;
var paymentKey = getTransactionSigner(paymentKeyIn);
var ownerKey = getTransactionSigner(ownerKeyIn);
return Promise.all([ownerKey.getAddress(), paymentKey.getAddress()]).then(function (_ref23) {
var _ref24 = _slicedToArray(_ref23, 2),
ownerAddress = _ref24[0],
paymentAddress = _ref24[1];
var revokeTX = (0, _skeletons.makeRevokeSkeleton)(fullyQualifiedName);
var txPromise = _bitcoinjsLib2.default.TransactionBuilder.fromTransaction(revokeTX, network.layer1);
txPromise.setVersion(1);
return Promise.all([txPromise, network.getUTXOs(paymentAddress), network.getUTXOs(ownerAddress), network.getFeeRate()]).then(function (_ref25) {
var _ref26 = _slicedToArray(_ref25, 4),
txB = _ref26[0],
payerUtxos = _ref26[1],
ownerUtxos = _ref26[2],
feeRate = _ref26[3];
var ownerInput = addOwnerInput(ownerUtxos, ownerAddress, txB);
var signingTxB = fundTransaction(txB, paymentAddress, payerUtxos, feeRate, ownerInput.value);
return (0, _utils.signInputs)(signingTxB, paymentKey, [{ index: ownerInput.index, signer: ownerKey }]);
});
}).then(function (signingTxB) {
return 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) {
var zonefile = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
var valueHash = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : null;
var buildIncomplete = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false;
var network = _config.config.network;
if (!valueHash && !!zonefile) {
valueHash = (0, _utils.hash160)(Buffer.from(zonefile)).toString('hex');
}
var namespace = fullyQualifiedName.split('.').pop();
var paymentKey = getTransactionSigner(paymentKeyIn);
var ownerKey = getTransactionSigner(ownerKeyIn);
return Promise.all([ownerKey.getAddress(), paymentKey.getAddress()]).then(function (_ref27) {
var _ref28 = _slicedToArray(_ref27, 2),
ownerAddress = _ref28[0],
paymentAddress = _ref28[1];
var txPromise = Promise.all([network.getNamePrice(fullyQualifiedName), network.getNamespaceBurnAddress(namespace)]).then(function (_ref29) {
var _ref30 = _slicedToArray(_ref29, 2),
namePrice = _ref30[0],
burnAddress = _ref30[1];
return (0, _skeletons.makeRenewalSkeleton)(fullyQualifiedName, destinationAddress, ownerAddress, burnAddress, namePrice, valueHash);
}).then(function (tx) {
var txB = _bitcoinjsLib2.default.TransactionBuilder.fromTransaction(tx, network.layer1);
txB.setVersion(1);
return txB;
});
return Promise.all([txPromise, network.getUTXOs(paymentAddress), network.getUTXOs(ownerAddress), network.getFeeRate()]).then(function (_ref31) {
var _ref32 = _slicedToArray(_ref31, 4),
txB = _ref32[0],
payerUtxos = _ref32[1],
ownerUtxos = _ref32[2],
feeRate = _ref32[3];
var ownerInput = addOwnerInput(ownerUtxos, ownerAddress, txB, false);
var ownerOutput = txB.__tx.outs[2];
var ownerOutputAddr = _bitcoinjsLib2.default.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;
var signingTxB = fundTransaction(txB, paymentAddress, payerUtxos, feeRate, ownerInput.value);
return (0, _utils.signInputs)(signingTxB, paymentKey, [{ index: ownerInput.index, signer: ownerKey }]);
});
}).then(function (signingTxB) {
return 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
*/
function makeNamespacePreorder(namespaceID, revealAddress, paymentKeyIn) {
var buildIncomplete = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var network = _config.config.network;
var paymentKey = getTransactionSigner(paymentKeyIn);
return paymentKey.getAddress().then(function (preorderAddress) {
var preorderPromise = Promise.all([network.getConsensusHash(), network.getNamespacePrice(namespaceID)]).then(function (_ref33) {
var _ref34 = _slicedToArray(_ref33, 2),
consensusHash = _ref34[0],
namespacePrice = _ref34[1];
return (0, _skeletons.makeNamespacePreorderSkeleton)(namespaceID, consensusHash, preorderAddress, revealAddress, namespacePrice);
});
return Promise.all([network.getUTXOs(preorderAddress), network.getFeeRate(), preorderPromise]).then(function (_ref35) {
var _ref36 = _slicedToArray(_ref35, 3),
utxos = _ref36[0],
feeRate = _ref36[1],
preorderSkeleton = _ref36[2];
var txB = _bitcoinjsLib2.default.TransactionBuilder.fromTransaction(preorderSkeleton, network.layer1);
txB.setVersion(1);
var changeIndex = 1; // preorder skeleton always creates a change output at index = 1
var signingTxB = fundTransaction(txB, preorderAddress, utxos, feeRate, 0, changeIndex);
return (0, _utils.signInputs)(signingTxB, paymentKey);
}).then(function (signingTxB) {
return returnTransactionHex(signingTxB, buildIncomplete);
});
});
}
/**
* Generates a namespace reveal transaction for a namespace
* @param {BlockstackNamespace} 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) {
var buildIncomplete = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var network = _config.config.network;
if (!namespace.check()) {
return Promise.reject(new Error('Invalid namespace'));
}
var namespaceRevealTX = (0, _skeletons.makeNamespaceRevealSkeleton)(namespace, revealAddress);
var paymentKey = getTransactionSigner(paymentKeyIn);
return paymentKey.getAddress().then(function (preorderAddress) {
return Promise.all([network.getUTXOs(preorderAddress), network.getFeeRate()]).then(function (_ref37) {
var _ref38 = _slicedToArray(_ref37, 2),
utxos = _ref38[0],
feeRate = _ref38[1];
var txB = _bitcoinjsLib2.default.TransactionBuilder.fromTransaction(namespaceRevealTX, network.layer1);
txB.setVersion(1);
var signingTxB = fundTransaction(txB, preorderAddress, utxos, feeRate, 0);
return (0, _utils.signInputs)(signingTxB, paymentKey);
});
}).then(function (signingTxB) {
return 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) {
var buildIncomplete = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var network = _config.config.network;
var namespaceReadyTX = (0, _skeletons.makeNamespaceReadySkeleton)(namespaceID);
var revealKey = getTransactionSigner(revealKeyIn);
return revealKey.getAddress().then(function (revealAddress) {
return Promise.all([network.getUTXOs(revealAddress), network.getFeeRate()]).then(function (_ref39) {
var _ref40 = _slicedToArray(_ref39, 2),
utxos = _ref40[0],
feeRate = _ref40[1];
var txB = _bitcoinjsLib2.default.TransactionBuilder.fromTransaction(namespaceReadyTX, network.layer1);
txB.setVersion(1);
var signingTxB = fundTransaction(txB, revealAddress, utxos, feeRate, 0);
return (0, _utils.signInputs)(signingTxB, revealKey);
});
}).then(function (signingTxB) {
return 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) {
var buildIncomplete = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
var network = _config.config.network;
var nameImportTX = (0, _skeletons.makeNameImportSkeleton)(name, recipientAddr, zonefileHash);
var importerKey = getTransactionSigner(importerKeyIn);
return importerKey.getAddress().then(function (importerAddress) {
return Promise.all([network.getUTXOs(importerAddress), network.getFeeRate()]).then(function (_ref41) {
var _ref42 = _slicedToArray(_ref41, 2),
utxos = _ref42[0],
feeRate = _ref42[1];
var txB = _bitcoinjsLib2.default.TransactionBuilder.fromTransaction(nameImportTX, network.layer1);
var signingTxB = fundTransaction(txB, importerAddress, utxos, feeRate, 0);
return (0, _utils.signInputs)(signingTxB, importerKey);
});
}).then(function (signingTxB) {
return 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) {
var buildIncomplete = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var network = _config.config.network;
var announceTX = (0, _skeletons.makeAnnounceSkeleton)(messageHash);
var senderKey = getTransactionSigner(senderKeyIn);
return senderKey.getAddress().then(function (senderAddress) {
return Promise.all([network.getUTXOs(senderAddress), network.getFeeRate()]).then(function (_ref43) {
var _ref44 = _slicedToArray(_ref43, 2),
utxos = _ref44[0],
feeRate = _ref44[1];
var txB = _bitcoinjsLib2.default.TransactionBuilder.fromTransaction(announceTX, network.layer1);
var signingTxB = fundTransaction(txB, senderAddress, utxos, feeRate, 0);
return (0, _utils.signInputs)(signingTxB, senderKey);
});
}).then(function (signingTxB) {
return 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) {
var buildIncomplete = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false;
var network = _config.config.network;
var separateFunder = !!btcFunderKeyIn;
var senderKey = getTransactionSigner(senderKeyIn);
var btcKey = btcFunderKeyIn ? getTransactionSigner(btcFunderKeyIn) : senderKey;
var txPromise = network.getConsensusHash().then(function (consensusHash) {
return (0, _skeletons.makeTokenTransferSkeleton)(recipientAddress, consensusHash, tokenType, tokenAmount, scratchArea);
});
return Promise.all([senderKey.getAddress(), btcKey.getAddress()]).then(function (_ref45) {
var _ref46 = _slicedToArray(_ref45, 2),
senderAddress = _ref46[0],
btcAddress = _ref46[1];
var btcUTXOsPromise = separateFunder ? network.getUTXOs(btcAddress) : Promise.resolve([]);
var networkPromises = [network.getUTXOs(senderAddress), btcUTXOsPromise, network.getFeeRate(), txPromise];
return Promise.all(networkPromises).then(function (_ref47) {
var _ref48 = _slicedToArray(_ref47, 4),
senderUTXOs = _ref48[0],
btcUTXOs = _ref48[1],
feeRate = _ref48[2],
tokenTransferTX = _ref48[3];
var txB = _bitcoinjsLib2.default.TransactionBuilder.fromTransaction(tokenTransferTX, network.layer1);
if (separateFunder) {
var payerInput = addOwnerInput(senderUTXOs, senderAddress, txB);
var signingTxB = fundTransaction(txB, btcAddress, btcUTXOs, feeRate, payerInput.value);
return (0, _utils.signInputs)(signingTxB, btcKey, [{ index: payerInput.index, signer: senderKey }]);
} else {
var _signingTxB = fundTransaction(txB, senderAddress, senderUTXOs, feeRate, 0);
return (0, _utils.signInputs)(_signingTxB, senderKey);
}
});
}).then(function (signingTxB) {
return 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.
*
*