@coolwallet/ltc
Version:
Coolwallet LiteCoin sdk
241 lines (217 loc) • 8.45 kB
text/typescript
import { error } from '@coolwallet/core';
import * as bufferUtil from './bufferUtil';
import * as cryptoUtil from './cryptoUtil';
import * as params from '../config/params';
import * as bitcoin from 'bitcoinjs-lib';
import * as types from '../config/types';
export function addressToOutScript(address: string): {
scriptType: types.ScriptType;
outScript: Buffer;
outHash?: Buffer;
} {
const input = {
address: address,
network: params.activeNet,
};
let scriptType;
let payment;
if (address.startsWith('L')) {
scriptType = types.ScriptType.P2PKH;
payment = bitcoin.payments.p2pkh(input);
} else if (address.startsWith('M')) {
scriptType = types.ScriptType.P2SH_P2WPKH;
payment = bitcoin.payments.p2sh(input);
} else if (address.startsWith('ltc')) {
scriptType = types.ScriptType.P2WPKH;
payment = bitcoin.payments.p2wpkh(input);
} else {
throw new error.SDKError(addressToOutScript.name, `Unsupport Address '${address}'`);
}
if (!payment.output) throw new error.SDKError(addressToOutScript.name, `No OutScript for Address '${address}'`);
const outScript = payment.output;
const outHash = payment.hash;
return { scriptType, outScript, outHash };
}
export function pubkeyToAddressAndOutScript(
burPublicKey: Buffer,
scriptType: types.ScriptType
): { address: string; outScript: Buffer; hash: Buffer } {
const input = {
pubkey: burPublicKey,
network: params.activeNet,
};
let payment;
if (scriptType === types.ScriptType.P2PKH) {
payment = bitcoin.payments.p2pkh(input);
} else if (scriptType === types.ScriptType.P2SH_P2WPKH) {
payment = bitcoin.payments.p2sh({
redeem: bitcoin.payments.p2wpkh(input),
});
} else if (scriptType === types.ScriptType.P2WPKH) {
payment = bitcoin.payments.p2wpkh(input);
} else {
throw new error.SDKError(pubkeyToAddressAndOutScript.name, `Unsupport ScriptType '${scriptType}'`);
}
if (!payment.address)
throw new error.SDKError(pubkeyToAddressAndOutScript.name, `No Address for ScriptType '${scriptType}'`);
if (!payment.output)
throw new error.SDKError(pubkeyToAddressAndOutScript.name, `No OutScript for ScriptType '${scriptType}'`);
if (!payment.hash)
throw new error.SDKError(pubkeyToAddressAndOutScript.name, `No OutScript for ScriptType '${scriptType}'`);
return { address: payment.address, outScript: payment.output, hash: payment.hash };
}
export function createUnsignedTransactions(
scriptType: types.ScriptType,
inputs: Array<types.Input>,
output: types.Output,
change: types.Change | undefined,
version = 1,
lockTime = 0
): {
preparedData: types.PreparedData;
unsignedTransactions: Array<Buffer>;
} {
const versionBuf = bufferUtil.toUintBuffer(version, 4);
const lockTimeBuf = bufferUtil.toUintBuffer(lockTime, 4);
const inputsCount = bufferUtil.toVarUintBuffer(inputs.length);
const preparedInputs = inputs.map(({ preTxHash, preIndex, preValue, sequence, addressIndex, pubkeyBuf }) => {
if (!pubkeyBuf) throw new error.SDKError(createUnsignedTransactions.name, 'Public Key not exists !!');
const preOutPointBuf = Buffer.concat([
Buffer.from(preTxHash, 'hex').reverse(),
bufferUtil.toUintBuffer(preIndex, 4),
]);
const preValueBuf = bufferUtil.toUintBuffer(preValue, 8);
const sequenceBuf = sequence ? bufferUtil.toUintBuffer(sequence, 4) : Buffer.from('ffffffff', 'hex');
return {
addressIndex,
pubkeyBuf,
preOutPointBuf,
preValueBuf,
sequenceBuf,
};
});
const { scriptType: outputType, outScript: outputScript } = addressToOutScript(output.address);
const outputScriptLen = bufferUtil.toVarUintBuffer(outputScript.length);
const outputArray = [Buffer.concat([bufferUtil.toUintBuffer(output.value, 8), outputScriptLen, outputScript])];
if (change) {
if (!change.pubkeyBuf) throw new error.SDKError(createUnsignedTransactions.name, 'Public Key not exists !!');
const changeValue = bufferUtil.toUintBuffer(change.value, 8);
const { outScript } = pubkeyToAddressAndOutScript(change.pubkeyBuf, scriptType);
const outScriptLen = bufferUtil.toVarUintBuffer(outScript.length);
outputArray.push(Buffer.concat([changeValue, outScriptLen, outScript]));
}
const outputsCount = bufferUtil.toVarUintBuffer(change ? 2 : 1);
const outputsBuf = Buffer.concat(outputArray);
const hashPrevouts = cryptoUtil.hash256(Buffer.concat(preparedInputs.map((input) => input.preOutPointBuf)));
const hashSequence = cryptoUtil.hash256(Buffer.concat(preparedInputs.map((input) => input.sequenceBuf)));
const hashOutputs = cryptoUtil.hash256(outputsBuf);
const unsignedTransactions = preparedInputs.map(({ pubkeyBuf, preOutPointBuf, preValueBuf, sequenceBuf }) => {
if (scriptType === types.ScriptType.P2PKH) {
const { outScript } = pubkeyToAddressAndOutScript(pubkeyBuf, scriptType);
const outScriptLen = bufferUtil.toVarUintBuffer(outScript.length);
return Buffer.concat([
versionBuf,
bufferUtil.toVarUintBuffer(1),
preOutPointBuf,
outScriptLen, // preOutScriptBuf
outScript, // preOutScriptBuf
sequenceBuf,
outputsCount,
outputsBuf,
lockTimeBuf,
Buffer.from('81000000', 'hex'),
]);
} else {
return Buffer.concat([
versionBuf,
hashPrevouts,
hashSequence,
preOutPointBuf,
Buffer.from(`1976a914${cryptoUtil.hash160(pubkeyBuf).toString('hex')}88ac`, 'hex'), // ScriptCode
preValueBuf,
sequenceBuf,
hashOutputs,
lockTimeBuf,
Buffer.from('01000000', 'hex'),
]);
}
});
return {
preparedData: {
versionBuf,
inputsCount,
preparedInputs,
outputType,
outputsCount,
outputsBuf,
lockTimeBuf,
},
unsignedTransactions,
};
}
export function composeFinalTransaction(
scriptType: types.ScriptType,
preparedData: types.PreparedData,
signatures: Array<Buffer>
): Buffer {
const { versionBuf, inputsCount, preparedInputs, outputsCount, outputsBuf, lockTimeBuf } = preparedData;
if (
scriptType !== types.ScriptType.P2PKH &&
scriptType !== types.ScriptType.P2WPKH &&
scriptType !== types.ScriptType.P2SH_P2WPKH
) {
throw new error.SDKError(composeFinalTransaction.name, `Unsupport ScriptType : ${scriptType}`);
}
if (scriptType === types.ScriptType.P2PKH) {
const inputsBuf = Buffer.concat(
preparedInputs.map((data, i) => {
const { pubkeyBuf, preOutPointBuf, sequenceBuf } = data;
const signature = signatures[i];
const inScript = Buffer.concat([
Buffer.from((signature.length + 1).toString(16), 'hex'),
signature,
Buffer.from('81', 'hex'),
Buffer.from(pubkeyBuf.length.toString(16), 'hex'),
pubkeyBuf,
]);
return Buffer.concat([preOutPointBuf, bufferUtil.toVarUintBuffer(inScript.length), inScript, sequenceBuf]);
})
);
return Buffer.concat([versionBuf, inputsCount, inputsBuf, outputsCount, outputsBuf, lockTimeBuf]);
} else {
const flagBuf = Buffer.from('0001', 'hex');
const segwitBuf = Buffer.concat(
preparedInputs.map(({ pubkeyBuf }, i) => {
const signature = signatures[i];
const segwitScript = Buffer.concat([
Buffer.from((signature.length + 1).toString(16), 'hex'),
signature,
Buffer.from('01', 'hex'),
Buffer.from(pubkeyBuf.length.toString(16), 'hex'),
pubkeyBuf,
]);
return Buffer.concat([Buffer.from('02', 'hex'), segwitScript]);
})
);
const inputsBuf = Buffer.concat(
preparedInputs.map(({ pubkeyBuf, preOutPointBuf, sequenceBuf }) => {
if (scriptType === types.ScriptType.P2SH_P2WPKH) {
const { outScript } = pubkeyToAddressAndOutScript(pubkeyBuf, types.ScriptType.P2WPKH);
const inScript = Buffer.concat([Buffer.from(outScript.length.toString(16), 'hex'), outScript]);
return Buffer.concat([preOutPointBuf, bufferUtil.toVarUintBuffer(inScript.length), inScript, sequenceBuf]);
}
return Buffer.concat([preOutPointBuf, Buffer.from('00', 'hex'), sequenceBuf]);
})
);
return Buffer.concat([
versionBuf,
flagBuf,
inputsCount,
inputsBuf,
outputsCount,
outputsBuf,
segwitBuf,
lockTimeBuf,
]);
}
}