@okxweb3/coin-kaspa
Version:
A Kaspa SDK for building Web3 wallets and applications.
280 lines • 10.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Transaction = exports.signMessage = exports.calcTxHash = exports.transfer = void 0;
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(crypto_lib_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(crypto_lib_1.base.fromHex(output.scriptPublicKey.scriptPublicKey));
});
hashWriter.writeUInt64LE(BigInt(tx.lockTime));
hashWriter.writeHash(crypto_lib_1.base.fromHex(tx.subnetworkId));
hashWriter.writeUInt64LE(0n);
hashWriter.writeVarBytes(Buffer.alloc(0));
return crypto_lib_1.base.toHex(crypto_lib_1.base.blake2(hashWriter.toBuffer(), 256, TransactionIDKey));
}
exports.calcTxHash = calcTxHash;
function signMessage(message, privateKey) {
const hash = crypto_lib_1.base.blake2(Buffer.from(message), 256, PersonalMessageSigningHashKey);
const signature = crypto_lib_1.signUtil.schnorr.secp256k1.schnorr.sign(hash, crypto_lib_1.base.toHex(crypto_lib_1.base.fromHex(privateKey)));
return crypto_lib_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: payToAddrScript(input.address),
amount: input.amount,
});
totalInput += BigInt(input.amount);
});
let totalOutput = 0n;
txData.outputs.forEach(output => {
this.outputs.push({
scriptPublicKey: {
version: 0,
scriptPublicKey: crypto_lib_1.base.toHex(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: crypto_lib_1.base.toHex(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, crypto_lib_1.base.toHex(crypto_lib_1.base.fromHex(privateKey)));
input.signatureScript = crypto_lib_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;
function payToAddrScript(address) {
const { payload } = (0, address_1.decodeAddress)(address);
return Buffer.concat([Buffer.from([0x20]), payload, Buffer.from([0xac])], 34);
}
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(crypto_lib_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(crypto_lib_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 crypto_lib_1.base.blake2(this.toBuffer(), 256, TransactionSigningHashKey);
}
}
//# sourceMappingURL=transaction.js.map