123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- /* Import core modules. */
- const _ = require('lodash')
- const bch = require('bitcore-lib-cash')
- const debug = require('debug')('shuffle:utils')
- /* Import local modules. */
- const getCoinDetails = require('./_utils/getCoinDetails')
- const getKeypairFromWif = require('./_utils/getKeypairFromWif')
- const prepareShuffleInsAndOuts = require('./_utils/prepareShuffleInsAndOuts')
- /**
- * Check Sufficient Funds
- */
- const checkSufficientFunds = function (inputs, amount) {
- debug('Funds verification (amount):', amount)
- // Currently this is done in the `getCoinDetails` function
- // and it's called just before we add the player to the round.
- // It's also done as we're building the shuffle transaction.
- // I want to break this functionality out so we can check from
- // any time.
- }
- /**
- * Verify Transaction Signature
- */
- const verifyTransactionSignature = function (
- shuffleTxInstance, inputSigData, publicKeyHexOfSigner
- ) {
- // debug('Verify transaction signature',
- // 'shuffleTxInstance', shuffleTxInstance,
- // 'inputSigData', inputSigData,
- // 'publicKeyHexOfSigner', publicKeyHexOfSigner
- // )
- /* Set input to sign. */
- const inputToSign = _.reduce(
- shuffleTxInstance.inputs, function (keeper, oneInput, arrayIndex) {
- // If we already found the right input, pass it through
- // without bothering to check the others;
- if (keeper) {
- return keeper
- }
- const asJson = oneInput.toObject()
- if (inputSigData.prevTxId === asJson.prevTxId && Number(inputSigData.vout) === Number(asJson.outputIndex)) {
- return {
- input: oneInput,
- inputIndex: arrayIndex
- }
- } else {
- return undefined
- }
- }, undefined)
- // debug('Input to sign:', inputToSign)
- /* Validate input to sign. */
- if (!inputToSign) {
- return false
- }
- /* Set signer public key. */
- const signerPublicKey = bch.PublicKey(publicKeyHexOfSigner)
- /* Set signature instance. */
- const signatureInstance = bch.crypto.Signature
- .fromTxFormat(Buffer.from(inputSigData.signature, 'hex'))
- // debug('Signature instance:', signatureInstance)
- /* Set signature object. */
- const signatureObject = {
- signature: signatureInstance,
- publicKey: signerPublicKey,
- inputIndex: inputToSign.inputIndex,
- sigtype: signatureInstance.nhashtype
- }
- // debug('Signature object:', signatureObject)
- /* Initialize verification results. */
- let verificationResults = false
- try {
- verificationResults = inputToSign.input
- .isValidSignature(shuffleTxInstance, signatureObject)
- } catch (nope) {
- console.error(nope) // eslint-disable-line no-console
- verificationResults = false
- }
- // debug('Verification results:', verificationResults)
- /* Validate verification results. */
- if (verificationResults) {
- return {
- success: true,
- inputIndex: signatureObject.inputIndex,
- signature: signatureObject
- }
- } else {
- return {
- success: false
- }
- }
- }
- /**
- * Get Shuffle Transaction and Signature
- *
- * Builds the partially signed transaction that will eventually be broadcast
- * to the the network.
- *
- * It returns a serialized (as JSON ) version of the transaction before any
- * signatures are added as well as the fully formed transaction with only our
- * signature applied.
- */
- const getShuffleTxAndSignature = function (options) {
- // debug('Get Shuffle Tx and Signature (options):', options)
- /* Set inputs. */
- const inputs = options.inputs
- /* Set outputs. */
- const outputs = options.outputs
- /* Set shuffle transaction. */
- const shuffleTransaction = new bch.Transaction()
- /* Initialize my input. */
- let myInput
- /* Loop through ALL inputs. */
- for (let oneInput of inputs) {
- /* Set player public key. */
- const playerPubKey = bch.PublicKey(oneInput.player.coin.publicKey)
- /* Set transaction input. */
- const txInput = new bch.Transaction.UnspentOutput({
- txid: oneInput.txid,
- outputIndex: oneInput.vout,
- address: playerPubKey.toAddress(),
- scriptPubKey: bch.Script.fromAddress(playerPubKey.toAddress()),
- satoshis: oneInput.satoshis
- })
- debug('Transaction input:', txInput)
- /* Set shuffle transaction. */
- shuffleTransaction.from(txInput)
- // WARNING: For some stupid reason, bitcoincashjs's `PublicKeyHashInput`
- // instances are showing the outputIndex field which should be
- // type number or string as type 'undefined'.
- // Idfk, just be aware.
- const grabIt = _.find(shuffleTransaction.inputs, function (txInput) {
- /* Set buffer string. */
- const bufferString = txInput.prevTxId.toString('hex')
- return oneInput.txid === bufferString && Number(oneInput.vout) === Number(txInput.outputIndex)
- })
- /* Fix the sequence number. */
- Object.assign(grabIt, { sequenceNumber: 0xfffffffe })
- /* Add public key to input's script. */
- grabIt.setScript(bch.Script('21' + oneInput.player.coin.publicKey))
- /* Validate our input. */
- if (oneInput.player.isMe) {
- // debug('My (oneInput):', oneInput)
- myInput = oneInput
- }
- }
- /* Loop through ALL outputs. */
- for (let oneOutput of outputs) {
- // debug('Shuffle transaction (oneOutput)',
- // oneOutput,
- // 'legacyAddress', oneOutput.legacyAddress,
- // 'cashAddress', oneOutput.cashAddress,
- // 'satoshis', oneOutput.satoshis
- // )
- shuffleTransaction.to(oneOutput.cashAddress, oneOutput.satoshis)
- }
- /* Set version 1. */
- shuffleTransaction.setVersion(1)
- /* Set pre-signed transaction. */
- const preSignedTx = shuffleTransaction.toObject()
- debug('Get shuffle transaction and signature (preSignedTx):', preSignedTx)
- /* Sign transaction. */
- shuffleTransaction.sign(
- new bch.PrivateKey.fromWIF(myInput.player.coin.wif))
- /* Set signature instance. */
- const sigInstance = shuffleTransaction
- .getSignatures(myInput.player.coin.wif)[0]
- // debug('Signature instance:', sigInstance)
- /* Return transaction / signature package. */
- return {
- serialized: preSignedTx,
- tx: shuffleTransaction,
- signature: sigInstance.signature.toTxFormat().toString('hex')
- }
- }
- /**
- * Build Shuffle Transaction
- *
- * NOTE: FOR DEVELOPMENT PURPOSE ONLY
- * ----------------------------
- * An intermediary function, used to switch between the different
- * transaction building methods currently being evaluated.
- */
- const buildShuffleTransaction = async function (options) {
- // debug('Build shuffle transaction (options):', options)
- /* Initialize ins and outs. */
- let insAndOuts
- try {
- insAndOuts = await this.prepareShuffleInsAndOuts({
- players: options.players,
- feeSatoshis: options.feeSatoshis
- })
- } catch (nope) {
- console.error('cannot prepare inputs and outputs for shuffle Transaction') // eslint-disable-line no-console
- throw nope
- }
- debug('Build shuffle transaction (insAndOuts):', insAndOuts)
- /* Set shuffle transaction data. */
- const shuffleTxData = await this.getShuffleTxAndSignature({
- inputs: insAndOuts.inputs,
- outputs: insAndOuts.outputs
- })
- debug('Build shuffle transaction (shuffleTxData):', shuffleTxData)
- /* Return the results. */
- return {
- tx: shuffleTxData.tx,
- inputs: insAndOuts.inputs,
- outputs: insAndOuts.outputs,
- serialized: shuffleTxData.serialized,
- signatureBase64: Buffer
- .from(shuffleTxData.signature, 'utf-8')
- .toString('base64')
- }
- }
- /* Export module. */
- module.exports = {
- getKeypairFromWif,
- checkSufficientFunds,
- getCoinDetails,
- verifyTransactionSignature,
- prepareShuffleInsAndOuts,
- getShuffleTxAndSignature,
- buildShuffleTransaction
- }
|