UNPKG

@okxweb3/coin-kaspa

Version:

A Kaspa SDK for building Web3 wallets and applications.

282 lines 10.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Transaction = exports.signMessage = exports.calcTxHash = exports.transfer = void 0; const coin_base_1 = require("@okxweb3/coin-base"); const crypto_lib_1 = require("@okxweb3/crypto-lib"); const address_1 = require("./lib/address"); const TransactionSigningHashKey = Buffer.from('TransactionSigningHash'); const TransactionIDKey = Buffer.from('TransactionID'); const PersonalMessageSigningHashKey = Buffer.from('PersonalMessageSigningHash'); function transfer(txData, privateKey) { const transaction = Transaction.fromTxData(txData).sign(privateKey); return transaction.getMessage(); } exports.transfer = transfer; function calcTxHash(tx) { const hashWriter = new HashWriter(); hashWriter.writeUInt16LE(tx.version); hashWriter.writeUInt64LE(BigInt(tx.inputs.length)); tx.inputs.forEach((input) => { hashWriter.writeHash(coin_base_1.base.fromHex(input.previousOutpoint.transactionId)); hashWriter.writeUInt32LE(input.previousOutpoint.index); hashWriter.writeVarBytes(Buffer.alloc(0)); hashWriter.writeUInt64LE(BigInt(input.sequence)); }); hashWriter.writeUInt64LE(BigInt(tx.outputs.length)); tx.outputs.forEach((output) => { hashWriter.writeUInt64LE(BigInt(output.amount)); hashWriter.writeUInt16LE(output.scriptPublicKey.version); hashWriter.writeVarBytes(coin_base_1.base.fromHex(output.scriptPublicKey.scriptPublicKey)); }); hashWriter.writeUInt64LE(BigInt(tx.lockTime)); hashWriter.writeHash(coin_base_1.base.fromHex(tx.subnetworkId)); hashWriter.writeUInt64LE(0n); hashWriter.writeVarBytes(Buffer.alloc(0)); return coin_base_1.base.toHex(coin_base_1.base.blake2(hashWriter.toBuffer(), 256, TransactionIDKey)); } exports.calcTxHash = calcTxHash; function signMessage(message, privateKey) { const hash = coin_base_1.base.blake2(Buffer.from(message), 256, PersonalMessageSigningHashKey); const signature = crypto_lib_1.signUtil.schnorr.secp256k1.schnorr.sign(hash, coin_base_1.base.toHex(coin_base_1.base.fromHex(privateKey))); return coin_base_1.base.toHex(signature); } exports.signMessage = signMessage; class Transaction { static fromTxData(txData) { return new Transaction(txData); } constructor(txData) { this.version = 0; this.inputs = []; this.outputs = []; this.lockTime = '0'; this.subnetworkId = '0000000000000000000000000000000000000000'; this.utxos = []; let totalInput = 0n; txData.inputs.forEach((input) => { this.inputs.push({ previousOutpoint: { transactionId: input.txId, index: input.vOut, }, signatureScript: '', sequence: '0', sigOpCount: 1, }); this.utxos.push({ pkScript: (0, address_1.payToAddrScript)(input.address), amount: input.amount, }); totalInput += BigInt(input.amount); }); let totalOutput = 0n; txData.outputs.forEach((output) => { this.outputs.push({ scriptPublicKey: { version: 0, scriptPublicKey: coin_base_1.base.toHex((0, address_1.payToAddrScript)(output.address)), }, amount: output.amount, }); totalOutput += BigInt(output.amount); }); const changeAmount = totalInput - totalOutput - BigInt(txData.fee); if (changeAmount >= BigInt(txData.dustSize || 546)) { this.outputs.push({ scriptPublicKey: { version: 0, scriptPublicKey: coin_base_1.base.toHex((0, address_1.payToAddrScript)(txData.address)), }, amount: changeAmount.toString(), }); } } sign(privateKey) { this.inputs.forEach((input, i) => { const sigHash = calculateSigHash(this, SIGHASH_ALL, i, {}); const signature = crypto_lib_1.signUtil.schnorr.secp256k1.schnorr.sign(sigHash, coin_base_1.base.toHex(coin_base_1.base.fromHex(privateKey))); input.signatureScript = coin_base_1.base.toHex(Buffer.concat([ Buffer.from([0x41]), signature, Buffer.from([SIGHASH_ALL]), ])); }); return this; } getMessage() { return JSON.stringify({ transaction: { version: this.version, inputs: this.inputs, outputs: this.outputs, lockTime: this.lockTime, subnetworkId: this.subnetworkId, }, allowOrphan: false, }); } } exports.Transaction = Transaction; const SIGHASH_ALL = 0b00000001; const SIGHASH_NONE = 0b00000010; const SIGHASH_SINGLE = 0b00000100; const SIGHASH_ANYONECANPAY = 0b10000000; const SIGHASH_MASK = 0b00000111; function isSigHashNone(hashType) { return (hashType & SIGHASH_MASK) === SIGHASH_NONE; } function isSigHashSingle(hashType) { return (hashType & SIGHASH_MASK) === SIGHASH_SINGLE; } function isSigHashAnyoneCanPay(hashType) { return (hashType & SIGHASH_ANYONECANPAY) === SIGHASH_ANYONECANPAY; } function calculateSigHash(transaction, hashType, inputIndex, reusedValues = {}) { const hashWriter = new HashWriter(); hashWriter.writeUInt16LE(transaction.version); hashWriter.writeHash(getPreviousOutputsHash(transaction, hashType, reusedValues)); hashWriter.writeHash(getSequencesHash(transaction, hashType, reusedValues)); hashWriter.writeHash(getSigOpCountsHash(transaction, hashType, reusedValues)); const input = transaction.inputs[inputIndex]; const utxo = transaction.utxos[inputIndex]; hashOutpoint(hashWriter, input); hashWriter.writeUInt16LE(0); hashWriter.writeVarBytes(utxo.pkScript); hashWriter.writeUInt64LE(BigInt(utxo.amount)); hashWriter.writeUInt64LE(BigInt(input.sequence)); hashWriter.writeUInt8(1); hashWriter.writeHash(getOutputsHash(transaction, inputIndex, hashType, reusedValues)); hashWriter.writeUInt64LE(BigInt(transaction.lockTime)); hashWriter.writeHash(zeroSubnetworkID()); hashWriter.writeUInt64LE(0n); hashWriter.writeHash(zeroHash()); hashWriter.writeUInt8(hashType); return hashWriter.finalize(); } function zeroHash() { return Buffer.alloc(32); } function zeroSubnetworkID() { return Buffer.alloc(20); } function getPreviousOutputsHash(transaction, hashType, reusedValues) { if (isSigHashAnyoneCanPay(hashType)) { return zeroHash(); } if (!reusedValues.previousOutputsHash) { const hashWriter = new HashWriter(); transaction.inputs.forEach((input) => hashOutpoint(hashWriter, input)); reusedValues.previousOutputsHash = hashWriter.finalize(); } return reusedValues.previousOutputsHash; } function getSequencesHash(transaction, hashType, reusedValues) { if (isSigHashSingle(hashType) || isSigHashAnyoneCanPay(hashType) || isSigHashNone(hashType)) { return zeroHash(); } if (!reusedValues.sequencesHash) { const hashWriter = new HashWriter(); transaction.inputs.forEach((input) => hashWriter.writeUInt64LE(BigInt(input.sequence))); reusedValues.sequencesHash = hashWriter.finalize(); } return reusedValues.sequencesHash; } function getSigOpCountsHash(transaction, hashType, reusedValues) { if (isSigHashAnyoneCanPay(hashType)) { return zeroHash(); } if (!reusedValues.sigOpCountsHash) { const hashWriter = new HashWriter(); transaction.inputs.forEach((_) => hashWriter.writeUInt8(1)); reusedValues.sigOpCountsHash = hashWriter.finalize(); } return reusedValues.sigOpCountsHash; } function getOutputsHash(transaction, inputIndex, hashType, reusedValues) { if (isSigHashNone(hashType)) { return zeroHash(); } if (isSigHashSingle(hashType)) { if (inputIndex >= transaction.outputs.length) { return zeroHash(); } const hashWriter = new HashWriter(); return hashWriter.finalize(); } if (!reusedValues.outputsHash) { const hashWriter = new HashWriter(); transaction.outputs.forEach((output) => hashTxOut(hashWriter, output)); reusedValues.outputsHash = hashWriter.finalize(); } return reusedValues.outputsHash; } function hashOutpoint(hashWriter, input) { hashWriter.writeHash(coin_base_1.base.fromHex(input.previousOutpoint.transactionId)); hashWriter.writeUInt32LE(input.previousOutpoint.index); } function hashTxOut(hashWriter, output) { hashWriter.writeUInt64LE(BigInt(output.amount)); hashWriter.writeUInt16LE(0); hashWriter.writeVarBytes(coin_base_1.base.fromHex(output.scriptPublicKey.scriptPublicKey)); } class HashWriter { constructor() { this.bufLen = 0; this.bufs = []; } toBuffer() { return this.concat(); } concat() { return Buffer.concat(this.bufs, this.bufLen); } write(buf) { this.bufs.push(buf); this.bufLen += buf.length; return this; } writeReverse(buf) { this.bufs.push(buf.reverse()); this.bufLen += buf.length; return this; } writeHash(hash) { this.write(hash); return this; } writeVarBytes(buf) { this.writeUInt64LE(BigInt(buf.length)); this.write(buf); return this; } writeUInt8(n) { const buf = Buffer.alloc(1); buf.writeUInt8(n); this.write(buf); return this; } writeUInt16LE(n) { const buf = Buffer.alloc(2); buf.writeUInt16LE(n); this.write(buf); return this; } writeUInt32LE(n) { const buf = Buffer.alloc(4); buf.writeUInt32LE(n, 0); this.write(buf); return this; } writeUInt64LE(bn) { const buf = Buffer.alloc(8); buf.writeBigUInt64LE(bn); this.write(buf); return this; } finalize() { return coin_base_1.base.blake2(this.toBuffer(), 256, TransactionSigningHashKey); } } //# sourceMappingURL=transaction.js.map