Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
275 changes: 42 additions & 233 deletions src/cjs/psbt.cjs

Large diffs are not rendered by default.

193 changes: 193 additions & 0 deletions src/cjs/psbt/internal/finalize.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
'use strict';
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.getFinalScripts = getFinalScripts;
const payments = __importStar(require('../../payments/index.cjs'));
const psbtutils_js_1 = require('../psbtutils.cjs');
const psbtutils_js_2 = require('../psbtutils.cjs');
const scriptType_js_1 = require('./scriptType.cjs');
const tools = __importStar(require('uint8array-tools'));
function getFinalScripts(inputIndex, input, script, isSegwit, isP2SH, isP2WSH) {
const scriptType = (0, scriptType_js_1.classifyScript)(script, {
isP2WPKH: psbtutils_js_2.isP2WPKH,
isP2PKH: psbtutils_js_2.isP2PKH,
isP2MS: psbtutils_js_2.isP2MS,
isP2PK: psbtutils_js_2.isP2PK,
});
if (!canFinalize(input, script, scriptType))
throw new Error(`Can not finalize input #${inputIndex}`);
return prepareFinalScripts(
script,
scriptType,
input.partialSig,
isSegwit,
isP2SH,
isP2WSH,
);
}
function prepareFinalScripts(
script,
scriptType,
partialSig,
isSegwit,
isP2SH,
isP2WSH,
) {
let finalScriptSig;
let finalScriptWitness;
const payment = getPayment(script, scriptType, partialSig);
const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
if (isSegwit) {
if (p2wsh) {
finalScriptWitness = (0, psbtutils_js_1.witnessStackToScriptWitness)(
p2wsh.witness,
);
} else {
finalScriptWitness = (0, psbtutils_js_1.witnessStackToScriptWitness)(
payment.witness,
);
}
if (p2sh) {
finalScriptSig = p2sh.input;
}
} else {
if (p2sh) {
finalScriptSig = p2sh.input;
} else {
finalScriptSig = payment.input;
}
}
return {
finalScriptSig,
finalScriptWitness,
};
}
function canFinalize(input, script, scriptType) {
switch (scriptType) {
case 'pubkey':
case 'pubkeyhash':
case 'witnesspubkeyhash':
return hasSigs(1, input.partialSig);
case 'multisig':
const p2ms = payments.p2ms({ output: script });
return hasSigs(p2ms.m, input.partialSig, p2ms.pubkeys);
default:
return false;
}
}
function hasSigs(neededSigs, partialSig, pubkeys) {
if (!partialSig) return false;
let sigs;
if (pubkeys) {
sigs = pubkeys
.map(pkey => {
const pubkey = compressPubkey(pkey);
return partialSig.find(
pSig => tools.compare(pSig.pubkey, pubkey) === 0,
);
})
.filter(v => !!v);
} else {
sigs = partialSig;
}
if (sigs.length > neededSigs) throw new Error('Too many signatures');
return sigs.length === neededSigs;
}
function getSortedSigs(script, partialSig) {
const p2ms = payments.p2ms({ output: script });
return p2ms.pubkeys
.map(pk => {
return (
partialSig.filter(ps => {
return tools.compare(ps.pubkey, pk) === 0;
})[0] || {}
).signature;
})
.filter(v => !!v);
}
function getPayment(script, scriptType, partialSig) {
let payment;
switch (scriptType) {
case 'multisig':
const sigs = getSortedSigs(script, partialSig);
payment = payments.p2ms({
output: script,
signatures: sigs,
});
break;
case 'pubkey':
payment = payments.p2pk({
output: script,
signature: partialSig[0].signature,
});
break;
case 'pubkeyhash':
payment = payments.p2pkh({
output: script,
pubkey: partialSig[0].pubkey,
signature: partialSig[0].signature,
});
break;
case 'witnesspubkeyhash':
payment = payments.p2wpkh({
output: script,
pubkey: partialSig[0].pubkey,
signature: partialSig[0].signature,
});
break;
}
return payment;
}
function compressPubkey(pubkey) {
if (pubkey.length === 65) {
const parity = pubkey[64] & 1;
const newKey = pubkey.slice(0, 33);
newKey[0] = 2 | parity;
return newKey;
}
return pubkey.slice();
}
5 changes: 5 additions & 0 deletions src/cjs/psbt/internal/finalize.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { PsbtInput } from 'bip174';
export declare function getFinalScripts(inputIndex: number, input: PsbtInput, script: Uint8Array, isSegwit: boolean, isP2SH: boolean, isP2WSH: boolean): {
finalScriptSig: Uint8Array | undefined;
finalScriptWitness: Uint8Array | undefined;
};
62 changes: 62 additions & 0 deletions src/cjs/psbt/internal/scriptType.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.classifyScript = classifyScript;
exports.getMeaningfulScript = getMeaningfulScript;
exports.checkInvalidP2WSH = checkInvalidP2WSH;
function classifyScript(script, deps) {
if (deps.isP2WPKH(script)) return 'witnesspubkeyhash';
if (deps.isP2PKH(script)) return 'pubkeyhash';
if (deps.isP2MS(script)) return 'multisig';
if (deps.isP2PK(script)) return 'pubkey';
return 'nonstandard';
}
function getMeaningfulScript(
script,
index,
ioType,
redeemScript,
witnessScript,
deps,
) {
const isP2SH = deps.isP2SHScript(script);
const isP2SHP2WSH =
isP2SH && redeemScript && deps.isP2WSHScript(redeemScript);
const isP2WSH = deps.isP2WSHScript(script);
if (isP2SH && redeemScript === undefined)
throw new Error('scriptPubkey is P2SH but redeemScript missing');
if ((isP2WSH || isP2SHP2WSH) && witnessScript === undefined)
throw new Error(
'scriptPubkey or redeemScript is P2WSH but witnessScript missing',
);
let meaningfulScript;
if (isP2SHP2WSH) {
meaningfulScript = witnessScript;
deps.checkRedeemScript(index, script, redeemScript, ioType);
deps.checkWitnessScript(index, redeemScript, witnessScript, ioType);
checkInvalidP2WSH(meaningfulScript, deps.isP2WPKH, deps.isP2SHScript);
} else if (isP2WSH) {
meaningfulScript = witnessScript;
deps.checkWitnessScript(index, script, witnessScript, ioType);
checkInvalidP2WSH(meaningfulScript, deps.isP2WPKH, deps.isP2SHScript);
} else if (isP2SH) {
meaningfulScript = redeemScript;
deps.checkRedeemScript(index, script, redeemScript, ioType);
} else {
meaningfulScript = script;
}
return {
meaningfulScript,
type: isP2SHP2WSH
? 'p2sh-p2wsh'
: isP2SH
? 'p2sh'
: isP2WSH
? 'p2wsh'
: 'raw',
};
}
function checkInvalidP2WSH(script, isP2WPKH, isP2SHScript) {
if (isP2WPKH(script) || isP2SHScript(script)) {
throw new Error('P2WPKH or P2SH can not be contained within P2WSH');
}
}
17 changes: 17 additions & 0 deletions src/cjs/psbt/internal/scriptType.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export type ScriptType = 'witnesspubkeyhash' | 'pubkeyhash' | 'multisig' | 'pubkey' | 'nonstandard';
export interface ScriptTypeDeps {
isP2WPKH(script: Uint8Array): boolean;
isP2PKH(script: Uint8Array): boolean;
isP2MS(script: Uint8Array): boolean;
isP2PK(script: Uint8Array): boolean;
isP2SHScript(script: Uint8Array): boolean;
isP2WSHScript(script: Uint8Array): boolean;
checkRedeemScript(index: number, script: Uint8Array, redeemScript: Uint8Array, ioType: 'input' | 'output'): void;
checkWitnessScript(index: number, script: Uint8Array, witnessScript: Uint8Array, ioType: 'input' | 'output'): void;
}
export declare function classifyScript(script: Uint8Array, deps: Pick<ScriptTypeDeps, 'isP2WPKH' | 'isP2PKH' | 'isP2MS' | 'isP2PK'>): ScriptType;
export declare function getMeaningfulScript(script: Uint8Array, index: number, ioType: 'input' | 'output', redeemScript: Uint8Array | undefined, witnessScript: Uint8Array | undefined, deps: Pick<ScriptTypeDeps, 'isP2SHScript' | 'isP2WSHScript' | 'isP2WPKH' | 'checkRedeemScript' | 'checkWitnessScript'>): {
meaningfulScript: Uint8Array;
type: 'p2sh' | 'p2wsh' | 'p2sh-p2wsh' | 'raw';
};
export declare function checkInvalidP2WSH(script: Uint8Array, isP2WPKH: (script: Uint8Array) => boolean, isP2SHScript: (script: Uint8Array) => boolean): void;
70 changes: 70 additions & 0 deletions src/cjs/psbt/internal/witness.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
'use strict';
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.scriptWitnessToWitnessStack = scriptWitnessToWitnessStack;
const varuint = __importStar(require('varuint-bitcoin'));
function scriptWitnessToWitnessStack(buffer) {
let offset = 0;
function readSlice(n) {
offset += n;
return buffer.slice(offset - n, offset);
}
function readVarInt() {
const vi = varuint.decode(buffer, offset);
offset += varuint.encodingLength(vi.bigintValue);
return vi.numberValue;
}
function readVarSlice() {
return readSlice(readVarInt());
}
function readVector() {
const count = readVarInt();
const vector = [];
for (let i = 0; i < count; i++) vector.push(readVarSlice());
return vector;
}
return readVector();
}
1 change: 1 addition & 0 deletions src/cjs/psbt/internal/witness.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export declare function scriptWitnessToWitnessStack(buffer: Uint8Array): Uint8Array[];
16 changes: 8 additions & 8 deletions src/cjs/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as v from 'valibot';
export declare const NBufferSchemaFactory: (size: number) => v.SchemaWithPipe<[v.InstanceSchema<Uint8ArrayConstructor, undefined>, v.LengthAction<Uint8Array, number, undefined>]>;
export declare const NBufferSchemaFactory: (size: number) => v.SchemaWithPipe<readonly [v.InstanceSchema<Uint8ArrayConstructor, undefined>, v.LengthAction<Uint8Array, number, undefined>]>;
/**
* Checks if two arrays of Buffers are equal.
* @param a - The first array of Buffers.
Expand Down Expand Up @@ -34,12 +34,12 @@ export interface TinySecp256k1Interface {
isXOnlyPoint(p: Uint8Array): boolean;
xOnlyPointAddTweak(p: Uint8Array, tweak: Uint8Array): XOnlyPointAddTweakResult | null;
}
export declare const Buffer256bitSchema: v.SchemaWithPipe<[v.InstanceSchema<Uint8ArrayConstructor, undefined>, v.LengthAction<Uint8Array, number, undefined>]>;
export declare const Hash160bitSchema: v.SchemaWithPipe<[v.InstanceSchema<Uint8ArrayConstructor, undefined>, v.LengthAction<Uint8Array, number, undefined>]>;
export declare const Hash256bitSchema: v.SchemaWithPipe<[v.InstanceSchema<Uint8ArrayConstructor, undefined>, v.LengthAction<Uint8Array, number, undefined>]>;
export declare const Buffer256bitSchema: v.SchemaWithPipe<readonly [v.InstanceSchema<Uint8ArrayConstructor, undefined>, v.LengthAction<Uint8Array, number, undefined>]>;
export declare const Hash160bitSchema: v.SchemaWithPipe<readonly [v.InstanceSchema<Uint8ArrayConstructor, undefined>, v.LengthAction<Uint8Array, number, undefined>]>;
export declare const Hash256bitSchema: v.SchemaWithPipe<readonly [v.InstanceSchema<Uint8ArrayConstructor, undefined>, v.LengthAction<Uint8Array, number, undefined>]>;
export declare const BufferSchema: v.InstanceSchema<Uint8ArrayConstructor, undefined>;
export declare const HexSchema: v.SchemaWithPipe<[v.StringSchema<undefined>, v.RegexAction<string, undefined>]>;
export declare const UInt8Schema: v.SchemaWithPipe<[v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>, v.MaxValueAction<number, 255, undefined>]>;
export declare const UInt32Schema: v.SchemaWithPipe<[v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>, v.MaxValueAction<number, 4294967295, undefined>]>;
export declare const SatoshiSchema: v.SchemaWithPipe<[v.BigintSchema<undefined>, v.MinValueAction<bigint, 0n, undefined>, v.MaxValueAction<bigint, 9223372036854775807n, undefined>]>;
export declare const HexSchema: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, undefined>]>;
export declare const UInt8Schema: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>, v.MaxValueAction<number, 255, undefined>]>;
export declare const UInt32Schema: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>, v.MaxValueAction<number, 4294967295, undefined>]>;
export declare const SatoshiSchema: v.SchemaWithPipe<readonly [v.BigintSchema<undefined>, v.MinValueAction<bigint, 0n, undefined>, v.MaxValueAction<bigint, 9223372036854775807n, undefined>]>;
export declare const NullablePartial: (a: Record<string, any>) => v.ObjectSchema<{}, undefined>;
Loading
Loading