@okxweb3/coin-bitcoin
Version:
@okxweb3/coin-bitcoin is a Bitcoin SDK for building Web3 wallets and applications. It supports BTC, BSV, DOGE, LTC, and TBTC, enabling private key management, transaction signing, address generation, and inscriptions like BRC-20, Runes, CAT, and Atomicals
324 lines • 15 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.psbtFromKeyVals = exports.checkKeyBuffer = exports.psbtFromBuffer = void 0;
const convert = __importStar(require("../converter"));
const tools_1 = require("../converter/tools");
const varuint = __importStar(require("../converter/varint"));
const typeFields_1 = require("../typeFields");
function psbtFromBuffer(buffer, txGetter) {
let offset = 0;
function varSlice() {
const keyLen = varuint.decode(buffer, offset);
offset += varuint.encodingLength(keyLen);
const key = buffer.slice(offset, offset + keyLen);
offset += keyLen;
return key;
}
function readUInt32BE() {
const num = buffer.readUInt32BE(offset);
offset += 4;
return num;
}
function readUInt8() {
const num = buffer.readUInt8(offset);
offset += 1;
return num;
}
function getKeyValue() {
const key = varSlice();
const value = varSlice();
return {
key,
value,
};
}
function checkEndOfKeyValPairs() {
if (offset >= buffer.length) {
throw new Error('Format Error: Unexpected End of PSBT');
}
const isEnd = buffer.readUInt8(offset) === 0;
if (isEnd) {
offset++;
}
return isEnd;
}
if (readUInt32BE() !== 0x70736274) {
throw new Error('Format Error: Invalid Magic Number');
}
if (readUInt8() !== 0xff) {
throw new Error('Format Error: Magic Number must be followed by 0xff separator');
}
const globalMapKeyVals = [];
const globalKeyIndex = {};
while (!checkEndOfKeyValPairs()) {
const keyVal = getKeyValue();
const hexKey = keyVal.key.toString('hex');
if (globalKeyIndex[hexKey]) {
throw new Error('Format Error: Keys must be unique for global keymap: key ' + hexKey);
}
globalKeyIndex[hexKey] = 1;
globalMapKeyVals.push(keyVal);
}
const unsignedTxMaps = globalMapKeyVals.filter(keyVal => keyVal.key[0] === typeFields_1.GlobalTypes.UNSIGNED_TX);
if (unsignedTxMaps.length !== 1) {
throw new Error('Format Error: Only one UNSIGNED_TX allowed');
}
const unsignedTx = txGetter(unsignedTxMaps[0].value);
const { inputCount, outputCount } = unsignedTx.getInputOutputCounts();
const inputKeyVals = [];
const outputKeyVals = [];
for (const index of (0, tools_1.range)(inputCount)) {
const inputKeyIndex = {};
const input = [];
while (!checkEndOfKeyValPairs()) {
const keyVal = getKeyValue();
const hexKey = keyVal.key.toString('hex');
if (inputKeyIndex[hexKey]) {
throw new Error('Format Error: Keys must be unique for each input: ' +
'input index ' +
index +
' key ' +
hexKey);
}
inputKeyIndex[hexKey] = 1;
input.push(keyVal);
}
inputKeyVals.push(input);
}
for (const index of (0, tools_1.range)(outputCount)) {
const outputKeyIndex = {};
const output = [];
while (!checkEndOfKeyValPairs()) {
const keyVal = getKeyValue();
const hexKey = keyVal.key.toString('hex');
if (outputKeyIndex[hexKey]) {
throw new Error('Format Error: Keys must be unique for each output: ' +
'output index ' +
index +
' key ' +
hexKey);
}
outputKeyIndex[hexKey] = 1;
output.push(keyVal);
}
outputKeyVals.push(output);
}
return psbtFromKeyVals(unsignedTx, {
globalMapKeyVals,
inputKeyVals,
outputKeyVals,
});
}
exports.psbtFromBuffer = psbtFromBuffer;
function checkKeyBuffer(type, keyBuf, keyNum) {
if (!keyBuf.equals(Buffer.from([keyNum]))) {
throw new Error(`Format Error: Invalid ${type} key: ${keyBuf.toString('hex')}`);
}
}
exports.checkKeyBuffer = checkKeyBuffer;
function psbtFromKeyVals(unsignedTx, { globalMapKeyVals, inputKeyVals, outputKeyVals }) {
const globalMap = {
unsignedTx,
};
let txCount = 0;
for (const keyVal of globalMapKeyVals) {
switch (keyVal.key[0]) {
case typeFields_1.GlobalTypes.UNSIGNED_TX:
checkKeyBuffer('global', keyVal.key, typeFields_1.GlobalTypes.UNSIGNED_TX);
if (txCount > 0) {
throw new Error('Format Error: GlobalMap has multiple UNSIGNED_TX');
}
txCount++;
break;
case typeFields_1.GlobalTypes.GLOBAL_XPUB:
if (globalMap.globalXpub === undefined) {
globalMap.globalXpub = [];
}
globalMap.globalXpub.push(convert.globals.globalXpub.decode(keyVal));
break;
default:
if (!globalMap.unknownKeyVals)
globalMap.unknownKeyVals = [];
globalMap.unknownKeyVals.push(keyVal);
}
}
const inputCount = inputKeyVals.length;
const outputCount = outputKeyVals.length;
const inputs = [];
const outputs = [];
for (const index of (0, tools_1.range)(inputCount)) {
const input = {};
for (const keyVal of inputKeyVals[index]) {
convert.inputs.checkPubkey(keyVal);
switch (keyVal.key[0]) {
case typeFields_1.InputTypes.NON_WITNESS_UTXO:
checkKeyBuffer('input', keyVal.key, typeFields_1.InputTypes.NON_WITNESS_UTXO);
if (input.nonWitnessUtxo !== undefined) {
throw new Error('Format Error: Input has multiple NON_WITNESS_UTXO');
}
input.nonWitnessUtxo = convert.inputs.nonWitnessUtxo.decode(keyVal);
break;
case typeFields_1.InputTypes.WITNESS_UTXO:
checkKeyBuffer('input', keyVal.key, typeFields_1.InputTypes.WITNESS_UTXO);
if (input.witnessUtxo !== undefined) {
throw new Error('Format Error: Input has multiple WITNESS_UTXO');
}
input.witnessUtxo = convert.inputs.witnessUtxo.decode(keyVal);
break;
case typeFields_1.InputTypes.PARTIAL_SIG:
if (input.partialSig === undefined) {
input.partialSig = [];
}
input.partialSig.push(convert.inputs.partialSig.decode(keyVal));
break;
case typeFields_1.InputTypes.SIGHASH_TYPE:
checkKeyBuffer('input', keyVal.key, typeFields_1.InputTypes.SIGHASH_TYPE);
if (input.sighashType !== undefined) {
throw new Error('Format Error: Input has multiple SIGHASH_TYPE');
}
input.sighashType = convert.inputs.sighashType.decode(keyVal);
break;
case typeFields_1.InputTypes.REDEEM_SCRIPT:
checkKeyBuffer('input', keyVal.key, typeFields_1.InputTypes.REDEEM_SCRIPT);
if (input.redeemScript !== undefined) {
throw new Error('Format Error: Input has multiple REDEEM_SCRIPT');
}
input.redeemScript = convert.inputs.redeemScript.decode(keyVal);
break;
case typeFields_1.InputTypes.WITNESS_SCRIPT:
checkKeyBuffer('input', keyVal.key, typeFields_1.InputTypes.WITNESS_SCRIPT);
if (input.witnessScript !== undefined) {
throw new Error('Format Error: Input has multiple WITNESS_SCRIPT');
}
input.witnessScript = convert.inputs.witnessScript.decode(keyVal);
break;
case typeFields_1.InputTypes.BIP32_DERIVATION:
if (input.bip32Derivation === undefined) {
input.bip32Derivation = [];
}
input.bip32Derivation.push(convert.inputs.bip32Derivation.decode(keyVal));
break;
case typeFields_1.InputTypes.FINAL_SCRIPTSIG:
checkKeyBuffer('input', keyVal.key, typeFields_1.InputTypes.FINAL_SCRIPTSIG);
input.finalScriptSig = convert.inputs.finalScriptSig.decode(keyVal);
break;
case typeFields_1.InputTypes.FINAL_SCRIPTWITNESS:
checkKeyBuffer('input', keyVal.key, typeFields_1.InputTypes.FINAL_SCRIPTWITNESS);
input.finalScriptWitness = convert.inputs.finalScriptWitness.decode(keyVal);
break;
case typeFields_1.InputTypes.POR_COMMITMENT:
checkKeyBuffer('input', keyVal.key, typeFields_1.InputTypes.POR_COMMITMENT);
input.porCommitment = convert.inputs.porCommitment.decode(keyVal);
break;
case typeFields_1.InputTypes.TAP_KEY_SIG:
checkKeyBuffer('input', keyVal.key, typeFields_1.InputTypes.TAP_KEY_SIG);
input.tapKeySig = convert.inputs.tapKeySig.decode(keyVal);
break;
case typeFields_1.InputTypes.TAP_SCRIPT_SIG:
if (input.tapScriptSig === undefined) {
input.tapScriptSig = [];
}
input.tapScriptSig.push(convert.inputs.tapScriptSig.decode(keyVal));
break;
case typeFields_1.InputTypes.TAP_LEAF_SCRIPT:
if (input.tapLeafScript === undefined) {
input.tapLeafScript = [];
}
input.tapLeafScript.push(convert.inputs.tapLeafScript.decode(keyVal));
break;
case typeFields_1.InputTypes.TAP_BIP32_DERIVATION:
if (input.tapBip32Derivation === undefined) {
input.tapBip32Derivation = [];
}
input.tapBip32Derivation.push(convert.inputs.tapBip32Derivation.decode(keyVal));
break;
case typeFields_1.InputTypes.TAP_INTERNAL_KEY:
checkKeyBuffer('input', keyVal.key, typeFields_1.InputTypes.TAP_INTERNAL_KEY);
input.tapInternalKey = convert.inputs.tapInternalKey.decode(keyVal);
break;
case typeFields_1.InputTypes.TAP_MERKLE_ROOT:
checkKeyBuffer('input', keyVal.key, typeFields_1.InputTypes.TAP_MERKLE_ROOT);
input.tapMerkleRoot = convert.inputs.tapMerkleRoot.decode(keyVal);
break;
default:
if (!input.unknownKeyVals)
input.unknownKeyVals = [];
input.unknownKeyVals.push(keyVal);
}
}
inputs.push(input);
}
for (const index of (0, tools_1.range)(outputCount)) {
const output = {};
for (const keyVal of outputKeyVals[index]) {
convert.outputs.checkPubkey(keyVal);
switch (keyVal.key[0]) {
case typeFields_1.OutputTypes.REDEEM_SCRIPT:
checkKeyBuffer('output', keyVal.key, typeFields_1.OutputTypes.REDEEM_SCRIPT);
if (output.redeemScript !== undefined) {
throw new Error('Format Error: Output has multiple REDEEM_SCRIPT');
}
output.redeemScript = convert.outputs.redeemScript.decode(keyVal);
break;
case typeFields_1.OutputTypes.WITNESS_SCRIPT:
checkKeyBuffer('output', keyVal.key, typeFields_1.OutputTypes.WITNESS_SCRIPT);
if (output.witnessScript !== undefined) {
throw new Error('Format Error: Output has multiple WITNESS_SCRIPT');
}
output.witnessScript = convert.outputs.witnessScript.decode(keyVal);
break;
case typeFields_1.OutputTypes.BIP32_DERIVATION:
if (output.bip32Derivation === undefined) {
output.bip32Derivation = [];
}
output.bip32Derivation.push(convert.outputs.bip32Derivation.decode(keyVal));
break;
case typeFields_1.OutputTypes.TAP_INTERNAL_KEY:
checkKeyBuffer('output', keyVal.key, typeFields_1.OutputTypes.TAP_INTERNAL_KEY);
output.tapInternalKey = convert.outputs.tapInternalKey.decode(keyVal);
break;
case typeFields_1.OutputTypes.TAP_TREE:
checkKeyBuffer('output', keyVal.key, typeFields_1.OutputTypes.TAP_TREE);
output.tapTree = convert.outputs.tapTree.decode(keyVal);
break;
case typeFields_1.OutputTypes.TAP_BIP32_DERIVATION:
if (output.tapBip32Derivation === undefined) {
output.tapBip32Derivation = [];
}
output.tapBip32Derivation.push(convert.outputs.tapBip32Derivation.decode(keyVal));
break;
default:
if (!output.unknownKeyVals)
output.unknownKeyVals = [];
output.unknownKeyVals.push(keyVal);
}
}
outputs.push(output);
}
return { globalMap, inputs, outputs };
}
exports.psbtFromKeyVals = psbtFromKeyVals;
//# sourceMappingURL=fromBuffer.js.map