thebigguy-contract
Version:
A library to generate P2SH scripts and create spend transactions for permissionless share-based distribution of UTXOs
99 lines • 4.64 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createTx = createTx;
const ecash_lib_1 = require("ecash-lib");
const payment_1 = require("./payment");
const script_1 = require("./script");
const utils_1 = require("./utils");
/**
* Generates a valid transaction for any UTXO locked by the expected script.
* The `fee´ and `parties` used to lock the UTXO must be known and provided
* so tha the correct outputs for the transaction can be determined.
*
* @param ecc An instance of `ecash-lib`'s ECC implementation for reusability
* @param prvKey The private key that will be used to sign transactions
* @param utxo The outpoint and value being spent
* @param fee The fee used for the script locking the `utxo´
* @param parties The list of parties used for the script locking the `utxo`
*
* @returns A signed transaction that can be serialized and broadcasted.
*/
function createTx(ecc, prvKey, utxo, fee, parties) {
// produce contract script
const contract = (0, script_1.createScript)(ecc, prvKey, fee, parties);
// create transaction with dynamic outputs based on value
const tx = new ecash_lib_1.Tx({
outputs: (0, payment_1.createOutputs)(utxo.value, fee, contract, parties),
inputs: [
{
prevOut: {
txid: utxo.txid,
outIdx: utxo.outIdx,
},
signData: {
sats: BigInt(utxo.value),
redeemScript: contract
}
}
]
});
// produce serialized previouts
const serializedPrevouts = (0, utils_1.serializePrevouts)(tx.inputs);
// produce serialized outputs
const serializedOutputs = (0, utils_1.serializeOutputs)(tx.outputs);
// get the ALL_BIP143 preimage for the input
//
// The ALL_BIP143 flag must be used because the script needs to receive both
// the prevouts and outputs as input and those need to be validated against
// hashPrevouts and hashOutputs.
//
const sigHashType = ecash_lib_1.ALL_BIP143;
const unsignedTx = ecash_lib_1.UnsignedTx.fromTx(tx);
const unsignedTxInput = new ecash_lib_1.UnsignedTxInput({ inputIdx: 0, unsignedTx });
const inputPreimage = unsignedTxInput.sigHashPreimage(sigHashType);
// sign the preimage (double SHA256, as per BIP 143)
//
// This is the key aspect since the signature can be used both for CHECKSIG and
// CHEKDATASIG. In combination, it ensures that the bytes given as input can be
// treated as the same preimage used for transaction validation. In turn, this
// allows trusting other provided inputs like the outputs and their values.
//
const signature = (0, ecash_lib_1.flagSignature)(ecc.schnorrSign(prvKey, (0, ecash_lib_1.sha256d)(inputPreimage.bytes)), sigHashType);
// produce the P2SH spend script (inputs + script)
const spendScript = ecash_lib_1.Script.fromOps([
(0, ecash_lib_1.pushBytesOp)(serializedPrevouts),
(0, ecash_lib_1.pushBytesOp)(serializedOutputs),
(0, ecash_lib_1.pushBytesOp)(signature),
(0, ecash_lib_1.pushBytesOp)(inputPreimage.bytes.slice(0, 4)),
(0, ecash_lib_1.pushBytesOp)(inputPreimage.bytes.slice(4, 36)),
(0, ecash_lib_1.pushBytesOp)(inputPreimage.bytes.slice(36, 68)),
(0, ecash_lib_1.pushBytesOp)(inputPreimage.bytes.slice(68, 104)),
(0, ecash_lib_1.pushBytesOp)(inputPreimage.bytes.slice(104, -52)),
(0, ecash_lib_1.pushBytesOp)(inputPreimage.bytes.slice(-52, -44)),
(0, ecash_lib_1.pushBytesOp)(inputPreimage.bytes.slice(-44, -40)),
(0, ecash_lib_1.pushBytesOp)(inputPreimage.bytes.slice(-40, -8)),
(0, ecash_lib_1.pushBytesOp)(inputPreimage.bytes.slice(-8)),
(0, ecash_lib_1.pushBytesOp)(inputPreimage.redeemScript.bytecode),
]);
// produce signed transaction
//
// The signatory is precomputed in the previous preimage signature. This
// process validates fees and dust, and produces a new transaction that
// includes the redeem script for the input.
//
const signedTx = new ecash_lib_1.TxBuilder({
outputs: tx.outputs,
inputs: [{
input: tx.inputs[0],
signatory: () => spendScript
}]
}).sign({ ecc });
// make a final verification of the fee
const minFee = signedTx.serSize();
if (fee < minFee) {
throw new Error(`The fee must be at least 1 sat per byte, that is, ${minFee} or more`);
}
// return signed transaction
return signedTx;
}
//# sourceMappingURL=tx.js.map