@opcat-labs/bip174 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +109 -0
- package/package.json +66 -0
- package/src/cjs/lib/combiner/index.js +67 -0
- package/src/cjs/lib/converter/global/globalXpub.js +83 -0
- package/src/cjs/lib/converter/global/unsignedTx.js +10 -0
- package/src/cjs/lib/converter/index.js +72 -0
- package/src/cjs/lib/converter/input/finalScriptSig.js +36 -0
- package/src/cjs/lib/converter/input/finalScriptWitness.js +36 -0
- package/src/cjs/lib/converter/input/nonWitnessUtxo.js +35 -0
- package/src/cjs/lib/converter/input/opcatUtxo.js +74 -0
- package/src/cjs/lib/converter/input/partialSig.js +74 -0
- package/src/cjs/lib/converter/input/porCommitment.js +36 -0
- package/src/cjs/lib/converter/input/sighashType.js +38 -0
- package/src/cjs/lib/converter/input/tapKeySig.js +36 -0
- package/src/cjs/lib/converter/input/tapLeafScript.js +56 -0
- package/src/cjs/lib/converter/input/tapMerkleRoot.js +36 -0
- package/src/cjs/lib/converter/input/tapScriptSig.js +59 -0
- package/src/cjs/lib/converter/input/witnessUtxo.js +53 -0
- package/src/cjs/lib/converter/output/tapTree.js +60 -0
- package/src/cjs/lib/converter/shared/bip32Derivation.js +84 -0
- package/src/cjs/lib/converter/shared/checkPubkey.js +25 -0
- package/src/cjs/lib/converter/shared/redeemScript.js +41 -0
- package/src/cjs/lib/converter/shared/tapBip32Derivation.js +55 -0
- package/src/cjs/lib/converter/shared/tapInternalKey.js +41 -0
- package/src/cjs/lib/converter/shared/witnessScript.js +41 -0
- package/src/cjs/lib/converter/tools.js +45 -0
- package/src/cjs/lib/interfaces.js +2 -0
- package/src/cjs/lib/parser/fromBuffer.js +325 -0
- package/src/cjs/lib/parser/index.js +7 -0
- package/src/cjs/lib/parser/toBuffer.js +69 -0
- package/src/cjs/lib/psbt.js +145 -0
- package/src/cjs/lib/typeFields.js +64 -0
- package/src/cjs/lib/utils.js +141 -0
- package/src/esm/lib/combiner/index.d.ts +2 -0
- package/src/esm/lib/combiner/index.js +57 -0
- package/src/esm/lib/converter/global/globalXpub.d.ts +6 -0
- package/src/esm/lib/converter/global/globalXpub.js +70 -0
- package/src/esm/lib/converter/global/unsignedTx.d.ts +2 -0
- package/src/esm/lib/converter/global/unsignedTx.js +7 -0
- package/src/esm/lib/converter/index.d.ts +110 -0
- package/src/esm/lib/converter/index.js +61 -0
- package/src/esm/lib/converter/input/finalScriptSig.d.ts +6 -0
- package/src/esm/lib/converter/input/finalScriptSig.js +23 -0
- package/src/esm/lib/converter/input/finalScriptWitness.d.ts +6 -0
- package/src/esm/lib/converter/input/finalScriptWitness.js +23 -0
- package/src/esm/lib/converter/input/nonWitnessUtxo.d.ts +6 -0
- package/src/esm/lib/converter/input/nonWitnessUtxo.js +22 -0
- package/src/esm/lib/converter/input/opcatUtxo.d.ts +6 -0
- package/src/esm/lib/converter/input/opcatUtxo.js +61 -0
- package/src/esm/lib/converter/input/partialSig.d.ts +6 -0
- package/src/esm/lib/converter/input/partialSig.js +61 -0
- package/src/esm/lib/converter/input/porCommitment.d.ts +6 -0
- package/src/esm/lib/converter/input/porCommitment.js +23 -0
- package/src/esm/lib/converter/input/sighashType.d.ts +6 -0
- package/src/esm/lib/converter/input/sighashType.js +25 -0
- package/src/esm/lib/converter/input/tapKeySig.d.ts +6 -0
- package/src/esm/lib/converter/input/tapKeySig.js +23 -0
- package/src/esm/lib/converter/input/tapLeafScript.d.ts +6 -0
- package/src/esm/lib/converter/input/tapLeafScript.js +43 -0
- package/src/esm/lib/converter/input/tapMerkleRoot.d.ts +6 -0
- package/src/esm/lib/converter/input/tapMerkleRoot.js +23 -0
- package/src/esm/lib/converter/input/tapScriptSig.d.ts +6 -0
- package/src/esm/lib/converter/input/tapScriptSig.js +46 -0
- package/src/esm/lib/converter/input/witnessUtxo.d.ts +6 -0
- package/src/esm/lib/converter/input/witnessUtxo.js +40 -0
- package/src/esm/lib/converter/output/tapTree.d.ts +6 -0
- package/src/esm/lib/converter/output/tapTree.js +47 -0
- package/src/esm/lib/converter/shared/bip32Derivation.d.ts +8 -0
- package/src/esm/lib/converter/shared/bip32Derivation.js +74 -0
- package/src/esm/lib/converter/shared/checkPubkey.d.ts +2 -0
- package/src/esm/lib/converter/shared/checkPubkey.js +15 -0
- package/src/esm/lib/converter/shared/redeemScript.d.ts +8 -0
- package/src/esm/lib/converter/shared/redeemScript.js +31 -0
- package/src/esm/lib/converter/shared/tapBip32Derivation.d.ts +8 -0
- package/src/esm/lib/converter/shared/tapBip32Derivation.js +45 -0
- package/src/esm/lib/converter/shared/tapInternalKey.d.ts +8 -0
- package/src/esm/lib/converter/shared/tapInternalKey.js +31 -0
- package/src/esm/lib/converter/shared/witnessScript.d.ts +8 -0
- package/src/esm/lib/converter/shared/witnessScript.js +31 -0
- package/src/esm/lib/converter/tools.d.ts +5 -0
- package/src/esm/lib/converter/tools.js +33 -0
- package/src/esm/lib/interfaces.d.ts +120 -0
- package/src/esm/lib/interfaces.js +0 -0
- package/src/esm/lib/parser/fromBuffer.d.ts +11 -0
- package/src/esm/lib/parser/fromBuffer.js +313 -0
- package/src/esm/lib/parser/index.d.ts +8 -0
- package/src/esm/lib/parser/index.js +2 -0
- package/src/esm/lib/parser/toBuffer.d.ts +8 -0
- package/src/esm/lib/parser/toBuffer.js +58 -0
- package/src/esm/lib/psbt.d.ts +26 -0
- package/src/esm/lib/psbt.js +133 -0
- package/src/esm/lib/typeFields.d.ts +34 -0
- package/src/esm/lib/typeFields.js +62 -0
- package/src/esm/lib/utils.d.ts +13 -0
- package/src/esm/lib/utils.js +123 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
import * as varuint from 'varuint-bitcoin';
|
2
|
+
import * as tools from 'uint8array-tools';
|
3
|
+
import * as bip32Derivation from './bip32Derivation.js';
|
4
|
+
const isValidBIP340Key = (pubkey) => pubkey.length === 32;
|
5
|
+
export function makeConverter(TYPE_BYTE) {
|
6
|
+
const parent = bip32Derivation.makeConverter(TYPE_BYTE, isValidBIP340Key);
|
7
|
+
function decode(keyVal) {
|
8
|
+
const { numberValue: nHashes, bytes: nHashesLen } = varuint.decode(keyVal.value);
|
9
|
+
const base = parent.decode({
|
10
|
+
key: keyVal.key,
|
11
|
+
value: keyVal.value.slice(nHashesLen + Number(nHashes) * 32),
|
12
|
+
});
|
13
|
+
const leafHashes = new Array(Number(nHashes));
|
14
|
+
for (let i = 0, _offset = nHashesLen; i < nHashes; i++, _offset += 32) {
|
15
|
+
leafHashes[i] = keyVal.value.slice(_offset, _offset + 32);
|
16
|
+
}
|
17
|
+
return { ...base, leafHashes };
|
18
|
+
}
|
19
|
+
function encode(data) {
|
20
|
+
const base = parent.encode(data);
|
21
|
+
const nHashesLen = varuint.encodingLength(data.leafHashes.length);
|
22
|
+
const nHashesBuf = new Uint8Array(nHashesLen);
|
23
|
+
varuint.encode(data.leafHashes.length, nHashesBuf);
|
24
|
+
const value = tools.concat([nHashesBuf, ...data.leafHashes, base.value]);
|
25
|
+
return { ...base, value };
|
26
|
+
}
|
27
|
+
const expected = '{ ' +
|
28
|
+
'masterFingerprint: Uint8Array; ' +
|
29
|
+
'pubkey: Uint8Array; ' +
|
30
|
+
'path: string; ' +
|
31
|
+
'leafHashes: Uint8Array[]; ' +
|
32
|
+
'}';
|
33
|
+
function check(data) {
|
34
|
+
return (Array.isArray(data.leafHashes) &&
|
35
|
+
data.leafHashes.every((leafHash) => leafHash instanceof Uint8Array && leafHash.length === 32) &&
|
36
|
+
parent.check(data));
|
37
|
+
}
|
38
|
+
return {
|
39
|
+
decode,
|
40
|
+
encode,
|
41
|
+
check,
|
42
|
+
expected,
|
43
|
+
canAddToArray: parent.canAddToArray,
|
44
|
+
};
|
45
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import { KeyValue, TapInternalKey } from '../../interfaces';
|
2
|
+
export declare function makeConverter(TYPE_BYTE: number): {
|
3
|
+
decode: (keyVal: KeyValue) => TapInternalKey;
|
4
|
+
encode: (data: TapInternalKey) => KeyValue;
|
5
|
+
check: (data: any) => data is TapInternalKey;
|
6
|
+
expected: string;
|
7
|
+
canAdd: (currentData: any, newData: any) => boolean;
|
8
|
+
};
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import * as tools from 'uint8array-tools';
|
2
|
+
export function makeConverter(TYPE_BYTE) {
|
3
|
+
function decode(keyVal) {
|
4
|
+
if (keyVal.key[0] !== TYPE_BYTE || keyVal.key.length !== 1) {
|
5
|
+
throw new Error('Decode Error: could not decode tapInternalKey with key 0x' +
|
6
|
+
tools.toHex(keyVal.key));
|
7
|
+
}
|
8
|
+
if (keyVal.value.length !== 32) {
|
9
|
+
throw new Error('Decode Error: tapInternalKey not a 32-byte x-only pubkey');
|
10
|
+
}
|
11
|
+
return keyVal.value;
|
12
|
+
}
|
13
|
+
function encode(value) {
|
14
|
+
const key = Uint8Array.from([TYPE_BYTE]);
|
15
|
+
return { key, value };
|
16
|
+
}
|
17
|
+
const expected = 'Uint8Array';
|
18
|
+
function check(data) {
|
19
|
+
return data instanceof Uint8Array && data.length === 32;
|
20
|
+
}
|
21
|
+
function canAdd(currentData, newData) {
|
22
|
+
return (!!currentData && !!newData && currentData.tapInternalKey === undefined);
|
23
|
+
}
|
24
|
+
return {
|
25
|
+
decode,
|
26
|
+
encode,
|
27
|
+
check,
|
28
|
+
expected,
|
29
|
+
canAdd,
|
30
|
+
};
|
31
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import { KeyValue, WitnessScript } from '../../interfaces';
|
2
|
+
export declare function makeConverter(TYPE_BYTE: number): {
|
3
|
+
decode: (keyVal: KeyValue) => WitnessScript;
|
4
|
+
encode: (data: WitnessScript) => KeyValue;
|
5
|
+
check: (data: any) => data is WitnessScript;
|
6
|
+
expected: string;
|
7
|
+
canAdd: (currentData: any, newData: any) => boolean;
|
8
|
+
};
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import * as tools from 'uint8array-tools';
|
2
|
+
export function makeConverter(TYPE_BYTE) {
|
3
|
+
function decode(keyVal) {
|
4
|
+
if (keyVal.key[0] !== TYPE_BYTE) {
|
5
|
+
throw new Error('Decode Error: could not decode witnessScript with key 0x' +
|
6
|
+
tools.toHex(keyVal.key));
|
7
|
+
}
|
8
|
+
return keyVal.value;
|
9
|
+
}
|
10
|
+
function encode(data) {
|
11
|
+
const key = Uint8Array.from([TYPE_BYTE]);
|
12
|
+
return {
|
13
|
+
key,
|
14
|
+
value: data,
|
15
|
+
};
|
16
|
+
}
|
17
|
+
const expected = 'Uint8Array';
|
18
|
+
function check(data) {
|
19
|
+
return data instanceof Uint8Array;
|
20
|
+
}
|
21
|
+
function canAdd(currentData, newData) {
|
22
|
+
return (!!currentData && !!newData && currentData.witnessScript === undefined);
|
23
|
+
}
|
24
|
+
return {
|
25
|
+
decode,
|
26
|
+
encode,
|
27
|
+
check,
|
28
|
+
expected,
|
29
|
+
canAdd,
|
30
|
+
};
|
31
|
+
}
|
@@ -0,0 +1,5 @@
|
|
1
|
+
import { KeyValue } from '../interfaces';
|
2
|
+
export declare const range: (n: number) => number[];
|
3
|
+
export declare function reverseBuffer(buffer: Uint8Array): Uint8Array;
|
4
|
+
export declare function keyValsToBuffer(keyVals: KeyValue[]): Uint8Array;
|
5
|
+
export declare function keyValToBuffer(keyVal: KeyValue): Uint8Array;
|
@@ -0,0 +1,33 @@
|
|
1
|
+
import * as varuint from 'varuint-bitcoin';
|
2
|
+
import * as tools from 'uint8array-tools';
|
3
|
+
export const range = (n) => [...Array(n).keys()];
|
4
|
+
export function reverseBuffer(buffer) {
|
5
|
+
if (buffer.length < 1)
|
6
|
+
return buffer;
|
7
|
+
let j = buffer.length - 1;
|
8
|
+
let tmp = 0;
|
9
|
+
for (let i = 0; i < buffer.length / 2; i++) {
|
10
|
+
tmp = buffer[i];
|
11
|
+
buffer[i] = buffer[j];
|
12
|
+
buffer[j] = tmp;
|
13
|
+
j--;
|
14
|
+
}
|
15
|
+
return buffer;
|
16
|
+
}
|
17
|
+
export function keyValsToBuffer(keyVals) {
|
18
|
+
const buffers = keyVals.map(keyValToBuffer);
|
19
|
+
buffers.push(Uint8Array.from([0]));
|
20
|
+
return tools.concat(buffers);
|
21
|
+
}
|
22
|
+
export function keyValToBuffer(keyVal) {
|
23
|
+
const keyLen = keyVal.key.length;
|
24
|
+
const valLen = keyVal.value.length;
|
25
|
+
const keyVarIntLen = varuint.encodingLength(keyLen);
|
26
|
+
const valVarIntLen = varuint.encodingLength(valLen);
|
27
|
+
const buffer = new Uint8Array(keyVarIntLen + keyLen + valVarIntLen + valLen);
|
28
|
+
varuint.encode(keyLen, buffer, 0);
|
29
|
+
buffer.set(keyVal.key, keyVarIntLen);
|
30
|
+
varuint.encode(valLen, buffer, keyVarIntLen + keyLen);
|
31
|
+
buffer.set(keyVal.value, keyVarIntLen + keyLen + valVarIntLen);
|
32
|
+
return buffer;
|
33
|
+
}
|
@@ -0,0 +1,120 @@
|
|
1
|
+
export declare type TransactionFromBuffer = (buffer: Uint8Array) => Transaction;
|
2
|
+
export interface Transaction {
|
3
|
+
getInputOutputCounts(): {
|
4
|
+
inputCount: number;
|
5
|
+
outputCount: number;
|
6
|
+
};
|
7
|
+
addInput(objectArg: any): void;
|
8
|
+
addOutput(objectArg: any): void;
|
9
|
+
toBuffer(): Uint8Array;
|
10
|
+
}
|
11
|
+
export interface KeyValue {
|
12
|
+
key: Uint8Array;
|
13
|
+
value: Uint8Array;
|
14
|
+
}
|
15
|
+
export interface PsbtGlobal extends PsbtGlobalUpdate {
|
16
|
+
unsignedTx: Transaction;
|
17
|
+
unknownKeyVals?: KeyValue[];
|
18
|
+
}
|
19
|
+
export interface PsbtGlobalUpdate {
|
20
|
+
globalXpub?: GlobalXpub[];
|
21
|
+
}
|
22
|
+
export interface PsbtInput extends PsbtInputUpdate {
|
23
|
+
unknownKeyVals?: KeyValue[];
|
24
|
+
}
|
25
|
+
export interface PsbtInputUpdate {
|
26
|
+
partialSig?: PartialSig[];
|
27
|
+
nonWitnessUtxo?: NonWitnessUtxo;
|
28
|
+
witnessUtxo?: WitnessUtxo;
|
29
|
+
sighashType?: SighashType;
|
30
|
+
redeemScript?: RedeemScript;
|
31
|
+
witnessScript?: WitnessScript;
|
32
|
+
bip32Derivation?: Bip32Derivation[];
|
33
|
+
finalScriptSig?: FinalScriptSig;
|
34
|
+
finalScriptWitness?: FinalScriptWitness;
|
35
|
+
porCommitment?: PorCommitment;
|
36
|
+
tapKeySig?: TapKeySig;
|
37
|
+
tapScriptSig?: TapScriptSig[];
|
38
|
+
tapLeafScript?: TapLeafScript[];
|
39
|
+
tapBip32Derivation?: TapBip32Derivation[];
|
40
|
+
tapInternalKey?: TapInternalKey;
|
41
|
+
tapMerkleRoot?: TapMerkleRoot;
|
42
|
+
opcatUtxo?: OpcatUtxo;
|
43
|
+
}
|
44
|
+
export interface PsbtInputExtended extends PsbtInput {
|
45
|
+
[index: string]: any;
|
46
|
+
}
|
47
|
+
export interface PsbtOutput extends PsbtOutputUpdate {
|
48
|
+
unknownKeyVals?: KeyValue[];
|
49
|
+
}
|
50
|
+
export interface PsbtOutputUpdate {
|
51
|
+
redeemScript?: RedeemScript;
|
52
|
+
witnessScript?: WitnessScript;
|
53
|
+
bip32Derivation?: Bip32Derivation[];
|
54
|
+
tapBip32Derivation?: TapBip32Derivation[];
|
55
|
+
tapTree?: TapTree;
|
56
|
+
tapInternalKey?: TapInternalKey;
|
57
|
+
}
|
58
|
+
export interface PsbtOutputExtended extends PsbtOutput {
|
59
|
+
[index: string]: any;
|
60
|
+
}
|
61
|
+
export interface GlobalXpub {
|
62
|
+
extendedPubkey: Uint8Array;
|
63
|
+
masterFingerprint: Uint8Array;
|
64
|
+
path: string;
|
65
|
+
}
|
66
|
+
export interface PartialSig {
|
67
|
+
pubkey: Uint8Array;
|
68
|
+
signature: Uint8Array;
|
69
|
+
}
|
70
|
+
export interface Bip32Derivation {
|
71
|
+
masterFingerprint: Uint8Array;
|
72
|
+
pubkey: Uint8Array;
|
73
|
+
path: string;
|
74
|
+
}
|
75
|
+
export interface WitnessUtxo {
|
76
|
+
script: Uint8Array;
|
77
|
+
value: bigint;
|
78
|
+
}
|
79
|
+
export declare type NonWitnessUtxo = Uint8Array;
|
80
|
+
export declare type SighashType = number;
|
81
|
+
export declare type RedeemScript = Uint8Array;
|
82
|
+
export declare type WitnessScript = Uint8Array;
|
83
|
+
export declare type FinalScriptSig = Uint8Array;
|
84
|
+
export declare type FinalScriptWitness = Uint8Array;
|
85
|
+
export declare type PorCommitment = string;
|
86
|
+
export declare type TapKeySig = Uint8Array;
|
87
|
+
export interface TapScriptSig extends PartialSig {
|
88
|
+
leafHash: Uint8Array;
|
89
|
+
}
|
90
|
+
interface TapScript {
|
91
|
+
leafVersion: number;
|
92
|
+
script: Uint8Array;
|
93
|
+
}
|
94
|
+
export declare type ControlBlock = Uint8Array;
|
95
|
+
export interface TapLeafScript extends TapScript {
|
96
|
+
controlBlock: ControlBlock;
|
97
|
+
}
|
98
|
+
export interface TapBip32Derivation extends Bip32Derivation {
|
99
|
+
leafHashes: Uint8Array[];
|
100
|
+
}
|
101
|
+
export declare type TapInternalKey = Uint8Array;
|
102
|
+
export declare type TapMerkleRoot = Uint8Array;
|
103
|
+
export interface TapLeaf extends TapScript {
|
104
|
+
depth: number;
|
105
|
+
}
|
106
|
+
export interface TapTree {
|
107
|
+
leaves: TapLeaf[];
|
108
|
+
}
|
109
|
+
export declare type TransactionIOCountGetter = (txBuffer: Uint8Array) => {
|
110
|
+
inputCount: number;
|
111
|
+
outputCount: number;
|
112
|
+
};
|
113
|
+
export declare type TransactionVersionSetter = (version: number, txBuffer: Uint8Array) => Uint8Array;
|
114
|
+
export declare type TransactionLocktimeSetter = (locktime: number, txBuffer: Uint8Array) => Uint8Array;
|
115
|
+
export interface OpcatUtxo {
|
116
|
+
script: Uint8Array;
|
117
|
+
data: Uint8Array;
|
118
|
+
value: bigint;
|
119
|
+
}
|
120
|
+
export {};
|
File without changes
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import { KeyValue, Transaction, TransactionFromBuffer } from '../interfaces';
|
2
|
+
import { PsbtAttributes } from './index.js';
|
3
|
+
export declare function psbtFromBuffer(buffer: Uint8Array, txGetter: TransactionFromBuffer): PsbtAttributes;
|
4
|
+
interface PsbtFromKeyValsArg {
|
5
|
+
globalMapKeyVals: KeyValue[];
|
6
|
+
inputKeyVals: KeyValue[][];
|
7
|
+
outputKeyVals: KeyValue[][];
|
8
|
+
}
|
9
|
+
export declare function checkKeyBuffer(type: string, keyBuf: Uint8Array, keyNum: number): void;
|
10
|
+
export declare function psbtFromKeyVals(unsignedTx: Transaction, { globalMapKeyVals, inputKeyVals, outputKeyVals }: PsbtFromKeyValsArg): PsbtAttributes;
|
11
|
+
export {};
|
@@ -0,0 +1,313 @@
|
|
1
|
+
import * as convert from '../converter/index.js';
|
2
|
+
import { range } from '../converter/tools.js';
|
3
|
+
import * as varuint from 'varuint-bitcoin';
|
4
|
+
import * as tools from 'uint8array-tools';
|
5
|
+
import { GlobalTypes, InputTypes, OutputTypes } from '../typeFields.js';
|
6
|
+
export function psbtFromBuffer(buffer, txGetter) {
|
7
|
+
let offset = 0;
|
8
|
+
function varSlice() {
|
9
|
+
const { numberValue: keyLen, bytes } = varuint.decode(buffer, offset);
|
10
|
+
offset += bytes;
|
11
|
+
const key = buffer.slice(offset, offset + Number(keyLen));
|
12
|
+
offset += Number(keyLen);
|
13
|
+
return key;
|
14
|
+
}
|
15
|
+
function readUInt32BE() {
|
16
|
+
const num = tools.readUInt32(buffer, offset, 'BE');
|
17
|
+
offset += 4;
|
18
|
+
return num;
|
19
|
+
}
|
20
|
+
function readUInt8() {
|
21
|
+
const num = tools.readUInt8(buffer, offset);
|
22
|
+
offset += 1;
|
23
|
+
return num;
|
24
|
+
}
|
25
|
+
function getKeyValue() {
|
26
|
+
const key = varSlice();
|
27
|
+
const value = varSlice();
|
28
|
+
return {
|
29
|
+
key,
|
30
|
+
value,
|
31
|
+
};
|
32
|
+
}
|
33
|
+
function checkEndOfKeyValPairs() {
|
34
|
+
if (offset >= buffer.length) {
|
35
|
+
throw new Error('Format Error: Unexpected End of PSBT');
|
36
|
+
}
|
37
|
+
const isEnd = tools.readUInt8(buffer, offset) === 0;
|
38
|
+
if (isEnd) {
|
39
|
+
offset++;
|
40
|
+
}
|
41
|
+
return isEnd;
|
42
|
+
}
|
43
|
+
if (readUInt32BE() !== 0x70736274) {
|
44
|
+
throw new Error('Format Error: Invalid Magic Number');
|
45
|
+
}
|
46
|
+
if (readUInt8() !== 0xff) {
|
47
|
+
throw new Error('Format Error: Magic Number must be followed by 0xff separator');
|
48
|
+
}
|
49
|
+
const globalMapKeyVals = [];
|
50
|
+
const globalKeyIndex = {};
|
51
|
+
while (!checkEndOfKeyValPairs()) {
|
52
|
+
const keyVal = getKeyValue();
|
53
|
+
const hexKey = tools.toHex(keyVal.key);
|
54
|
+
if (globalKeyIndex[hexKey]) {
|
55
|
+
throw new Error('Format Error: Keys must be unique for global keymap: key ' + hexKey);
|
56
|
+
}
|
57
|
+
globalKeyIndex[hexKey] = 1;
|
58
|
+
globalMapKeyVals.push(keyVal);
|
59
|
+
}
|
60
|
+
const unsignedTxMaps = globalMapKeyVals.filter(keyVal => keyVal.key[0] === GlobalTypes.UNSIGNED_TX);
|
61
|
+
if (unsignedTxMaps.length !== 1) {
|
62
|
+
throw new Error('Format Error: Only one UNSIGNED_TX allowed');
|
63
|
+
}
|
64
|
+
const unsignedTx = txGetter(unsignedTxMaps[0].value);
|
65
|
+
// Get input and output counts to loop the respective fields
|
66
|
+
const { inputCount, outputCount } = unsignedTx.getInputOutputCounts();
|
67
|
+
const inputKeyVals = [];
|
68
|
+
const outputKeyVals = [];
|
69
|
+
// Get input fields
|
70
|
+
for (const index of range(inputCount)) {
|
71
|
+
const inputKeyIndex = {};
|
72
|
+
const input = [];
|
73
|
+
while (!checkEndOfKeyValPairs()) {
|
74
|
+
const keyVal = getKeyValue();
|
75
|
+
const hexKey = tools.toHex(keyVal.key);
|
76
|
+
if (inputKeyIndex[hexKey]) {
|
77
|
+
throw new Error('Format Error: Keys must be unique for each input: ' +
|
78
|
+
'input index ' +
|
79
|
+
index +
|
80
|
+
' key ' +
|
81
|
+
hexKey);
|
82
|
+
}
|
83
|
+
inputKeyIndex[hexKey] = 1;
|
84
|
+
input.push(keyVal);
|
85
|
+
}
|
86
|
+
inputKeyVals.push(input);
|
87
|
+
}
|
88
|
+
for (const index of range(outputCount)) {
|
89
|
+
const outputKeyIndex = {};
|
90
|
+
const output = [];
|
91
|
+
while (!checkEndOfKeyValPairs()) {
|
92
|
+
const keyVal = getKeyValue();
|
93
|
+
const hexKey = tools.toHex(keyVal.key);
|
94
|
+
if (outputKeyIndex[hexKey]) {
|
95
|
+
throw new Error('Format Error: Keys must be unique for each output: ' +
|
96
|
+
'output index ' +
|
97
|
+
index +
|
98
|
+
' key ' +
|
99
|
+
hexKey);
|
100
|
+
}
|
101
|
+
outputKeyIndex[hexKey] = 1;
|
102
|
+
output.push(keyVal);
|
103
|
+
}
|
104
|
+
outputKeyVals.push(output);
|
105
|
+
}
|
106
|
+
return psbtFromKeyVals(unsignedTx, {
|
107
|
+
globalMapKeyVals,
|
108
|
+
inputKeyVals,
|
109
|
+
outputKeyVals,
|
110
|
+
});
|
111
|
+
}
|
112
|
+
export function checkKeyBuffer(type, keyBuf, keyNum) {
|
113
|
+
if (tools.compare(keyBuf, Uint8Array.from([keyNum]))) {
|
114
|
+
throw new Error(
|
115
|
+
// `Format Error: Invalid ${type} key: ${keyBuf.toString('hex')}`,
|
116
|
+
`Format Error: Invalid ${type} key: ${tools.toHex(keyBuf)}`);
|
117
|
+
}
|
118
|
+
}
|
119
|
+
export function psbtFromKeyVals(unsignedTx, { globalMapKeyVals, inputKeyVals, outputKeyVals }) {
|
120
|
+
// That was easy :-)
|
121
|
+
const globalMap = {
|
122
|
+
unsignedTx,
|
123
|
+
};
|
124
|
+
let txCount = 0;
|
125
|
+
for (const keyVal of globalMapKeyVals) {
|
126
|
+
// If a globalMap item needs pubkey, uncomment
|
127
|
+
// const pubkey = convert.globals.checkPubkey(keyVal);
|
128
|
+
switch (keyVal.key[0]) {
|
129
|
+
case GlobalTypes.UNSIGNED_TX:
|
130
|
+
checkKeyBuffer('global', keyVal.key, GlobalTypes.UNSIGNED_TX);
|
131
|
+
if (txCount > 0) {
|
132
|
+
throw new Error('Format Error: GlobalMap has multiple UNSIGNED_TX');
|
133
|
+
}
|
134
|
+
txCount++;
|
135
|
+
break;
|
136
|
+
case GlobalTypes.GLOBAL_XPUB:
|
137
|
+
if (globalMap.globalXpub === undefined) {
|
138
|
+
globalMap.globalXpub = [];
|
139
|
+
}
|
140
|
+
globalMap.globalXpub.push(convert.globals.globalXpub.decode(keyVal));
|
141
|
+
break;
|
142
|
+
default:
|
143
|
+
// This will allow inclusion during serialization.
|
144
|
+
if (!globalMap.unknownKeyVals)
|
145
|
+
globalMap.unknownKeyVals = [];
|
146
|
+
globalMap.unknownKeyVals.push(keyVal);
|
147
|
+
}
|
148
|
+
}
|
149
|
+
// Get input and output counts to loop the respective fields
|
150
|
+
const inputCount = inputKeyVals.length;
|
151
|
+
const outputCount = outputKeyVals.length;
|
152
|
+
const inputs = [];
|
153
|
+
const outputs = [];
|
154
|
+
// Get input fields
|
155
|
+
for (const index of range(inputCount)) {
|
156
|
+
const input = {};
|
157
|
+
for (const keyVal of inputKeyVals[index]) {
|
158
|
+
convert.inputs.checkPubkey(keyVal);
|
159
|
+
switch (keyVal.key[0]) {
|
160
|
+
case InputTypes.NON_WITNESS_UTXO:
|
161
|
+
checkKeyBuffer('input', keyVal.key, InputTypes.NON_WITNESS_UTXO);
|
162
|
+
if (input.nonWitnessUtxo !== undefined) {
|
163
|
+
throw new Error('Format Error: Input has multiple NON_WITNESS_UTXO');
|
164
|
+
}
|
165
|
+
input.nonWitnessUtxo = convert.inputs.nonWitnessUtxo.decode(keyVal);
|
166
|
+
break;
|
167
|
+
case InputTypes.WITNESS_UTXO:
|
168
|
+
checkKeyBuffer('input', keyVal.key, InputTypes.WITNESS_UTXO);
|
169
|
+
if (input.witnessUtxo !== undefined) {
|
170
|
+
throw new Error('Format Error: Input has multiple WITNESS_UTXO');
|
171
|
+
}
|
172
|
+
input.witnessUtxo = convert.inputs.witnessUtxo.decode(keyVal);
|
173
|
+
break;
|
174
|
+
case InputTypes.PARTIAL_SIG:
|
175
|
+
if (input.partialSig === undefined) {
|
176
|
+
input.partialSig = [];
|
177
|
+
}
|
178
|
+
input.partialSig.push(convert.inputs.partialSig.decode(keyVal));
|
179
|
+
break;
|
180
|
+
case InputTypes.SIGHASH_TYPE:
|
181
|
+
checkKeyBuffer('input', keyVal.key, InputTypes.SIGHASH_TYPE);
|
182
|
+
if (input.sighashType !== undefined) {
|
183
|
+
throw new Error('Format Error: Input has multiple SIGHASH_TYPE');
|
184
|
+
}
|
185
|
+
input.sighashType = convert.inputs.sighashType.decode(keyVal);
|
186
|
+
break;
|
187
|
+
case InputTypes.REDEEM_SCRIPT:
|
188
|
+
checkKeyBuffer('input', keyVal.key, InputTypes.REDEEM_SCRIPT);
|
189
|
+
if (input.redeemScript !== undefined) {
|
190
|
+
throw new Error('Format Error: Input has multiple REDEEM_SCRIPT');
|
191
|
+
}
|
192
|
+
input.redeemScript = convert.inputs.redeemScript.decode(keyVal);
|
193
|
+
break;
|
194
|
+
case InputTypes.WITNESS_SCRIPT:
|
195
|
+
checkKeyBuffer('input', keyVal.key, InputTypes.WITNESS_SCRIPT);
|
196
|
+
if (input.witnessScript !== undefined) {
|
197
|
+
throw new Error('Format Error: Input has multiple WITNESS_SCRIPT');
|
198
|
+
}
|
199
|
+
input.witnessScript = convert.inputs.witnessScript.decode(keyVal);
|
200
|
+
break;
|
201
|
+
case InputTypes.BIP32_DERIVATION:
|
202
|
+
if (input.bip32Derivation === undefined) {
|
203
|
+
input.bip32Derivation = [];
|
204
|
+
}
|
205
|
+
input.bip32Derivation.push(convert.inputs.bip32Derivation.decode(keyVal));
|
206
|
+
break;
|
207
|
+
case InputTypes.FINAL_SCRIPTSIG:
|
208
|
+
checkKeyBuffer('input', keyVal.key, InputTypes.FINAL_SCRIPTSIG);
|
209
|
+
input.finalScriptSig = convert.inputs.finalScriptSig.decode(keyVal);
|
210
|
+
break;
|
211
|
+
case InputTypes.FINAL_SCRIPTWITNESS:
|
212
|
+
checkKeyBuffer('input', keyVal.key, InputTypes.FINAL_SCRIPTWITNESS);
|
213
|
+
input.finalScriptWitness = convert.inputs.finalScriptWitness.decode(keyVal);
|
214
|
+
break;
|
215
|
+
case InputTypes.POR_COMMITMENT:
|
216
|
+
checkKeyBuffer('input', keyVal.key, InputTypes.POR_COMMITMENT);
|
217
|
+
input.porCommitment = convert.inputs.porCommitment.decode(keyVal);
|
218
|
+
break;
|
219
|
+
case InputTypes.TAP_KEY_SIG:
|
220
|
+
checkKeyBuffer('input', keyVal.key, InputTypes.TAP_KEY_SIG);
|
221
|
+
input.tapKeySig = convert.inputs.tapKeySig.decode(keyVal);
|
222
|
+
break;
|
223
|
+
case InputTypes.TAP_SCRIPT_SIG:
|
224
|
+
if (input.tapScriptSig === undefined) {
|
225
|
+
input.tapScriptSig = [];
|
226
|
+
}
|
227
|
+
input.tapScriptSig.push(convert.inputs.tapScriptSig.decode(keyVal));
|
228
|
+
break;
|
229
|
+
case InputTypes.TAP_LEAF_SCRIPT:
|
230
|
+
if (input.tapLeafScript === undefined) {
|
231
|
+
input.tapLeafScript = [];
|
232
|
+
}
|
233
|
+
input.tapLeafScript.push(convert.inputs.tapLeafScript.decode(keyVal));
|
234
|
+
break;
|
235
|
+
case InputTypes.TAP_BIP32_DERIVATION:
|
236
|
+
if (input.tapBip32Derivation === undefined) {
|
237
|
+
input.tapBip32Derivation = [];
|
238
|
+
}
|
239
|
+
input.tapBip32Derivation.push(convert.inputs.tapBip32Derivation.decode(keyVal));
|
240
|
+
break;
|
241
|
+
case InputTypes.TAP_INTERNAL_KEY:
|
242
|
+
checkKeyBuffer('input', keyVal.key, InputTypes.TAP_INTERNAL_KEY);
|
243
|
+
input.tapInternalKey = convert.inputs.tapInternalKey.decode(keyVal);
|
244
|
+
break;
|
245
|
+
case InputTypes.TAP_MERKLE_ROOT:
|
246
|
+
checkKeyBuffer('input', keyVal.key, InputTypes.TAP_MERKLE_ROOT);
|
247
|
+
input.tapMerkleRoot = convert.inputs.tapMerkleRoot.decode(keyVal);
|
248
|
+
break;
|
249
|
+
case InputTypes.OPCAT_UTXO:
|
250
|
+
checkKeyBuffer('input', keyVal.key, InputTypes.OPCAT_UTXO);
|
251
|
+
if (input.opcatUtxo !== undefined) {
|
252
|
+
throw new Error('Format Error: Input has multiple OPCAT_UTXO');
|
253
|
+
}
|
254
|
+
input.opcatUtxo = convert.inputs.opcatUtxo.decode(keyVal);
|
255
|
+
break;
|
256
|
+
default:
|
257
|
+
// This will allow inclusion during serialization.
|
258
|
+
if (!input.unknownKeyVals)
|
259
|
+
input.unknownKeyVals = [];
|
260
|
+
input.unknownKeyVals.push(keyVal);
|
261
|
+
}
|
262
|
+
}
|
263
|
+
inputs.push(input);
|
264
|
+
}
|
265
|
+
for (const index of range(outputCount)) {
|
266
|
+
const output = {};
|
267
|
+
for (const keyVal of outputKeyVals[index]) {
|
268
|
+
convert.outputs.checkPubkey(keyVal);
|
269
|
+
switch (keyVal.key[0]) {
|
270
|
+
case OutputTypes.REDEEM_SCRIPT:
|
271
|
+
checkKeyBuffer('output', keyVal.key, OutputTypes.REDEEM_SCRIPT);
|
272
|
+
if (output.redeemScript !== undefined) {
|
273
|
+
throw new Error('Format Error: Output has multiple REDEEM_SCRIPT');
|
274
|
+
}
|
275
|
+
output.redeemScript = convert.outputs.redeemScript.decode(keyVal);
|
276
|
+
break;
|
277
|
+
case OutputTypes.WITNESS_SCRIPT:
|
278
|
+
checkKeyBuffer('output', keyVal.key, OutputTypes.WITNESS_SCRIPT);
|
279
|
+
if (output.witnessScript !== undefined) {
|
280
|
+
throw new Error('Format Error: Output has multiple WITNESS_SCRIPT');
|
281
|
+
}
|
282
|
+
output.witnessScript = convert.outputs.witnessScript.decode(keyVal);
|
283
|
+
break;
|
284
|
+
case OutputTypes.BIP32_DERIVATION:
|
285
|
+
if (output.bip32Derivation === undefined) {
|
286
|
+
output.bip32Derivation = [];
|
287
|
+
}
|
288
|
+
output.bip32Derivation.push(convert.outputs.bip32Derivation.decode(keyVal));
|
289
|
+
break;
|
290
|
+
case OutputTypes.TAP_INTERNAL_KEY:
|
291
|
+
checkKeyBuffer('output', keyVal.key, OutputTypes.TAP_INTERNAL_KEY);
|
292
|
+
output.tapInternalKey = convert.outputs.tapInternalKey.decode(keyVal);
|
293
|
+
break;
|
294
|
+
case OutputTypes.TAP_TREE:
|
295
|
+
checkKeyBuffer('output', keyVal.key, OutputTypes.TAP_TREE);
|
296
|
+
output.tapTree = convert.outputs.tapTree.decode(keyVal);
|
297
|
+
break;
|
298
|
+
case OutputTypes.TAP_BIP32_DERIVATION:
|
299
|
+
if (output.tapBip32Derivation === undefined) {
|
300
|
+
output.tapBip32Derivation = [];
|
301
|
+
}
|
302
|
+
output.tapBip32Derivation.push(convert.outputs.tapBip32Derivation.decode(keyVal));
|
303
|
+
break;
|
304
|
+
default:
|
305
|
+
if (!output.unknownKeyVals)
|
306
|
+
output.unknownKeyVals = [];
|
307
|
+
output.unknownKeyVals.push(keyVal);
|
308
|
+
}
|
309
|
+
}
|
310
|
+
outputs.push(output);
|
311
|
+
}
|
312
|
+
return { globalMap, inputs, outputs };
|
313
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import { KeyValue } from '../interfaces';
|
2
|
+
import { PsbtAttributes } from './index.js';
|
3
|
+
export declare function psbtToBuffer({ globalMap, inputs, outputs, }: PsbtAttributes): Uint8Array;
|
4
|
+
export declare function psbtToKeyVals({ globalMap, inputs, outputs, }: PsbtAttributes): {
|
5
|
+
globalKeyVals: KeyValue[];
|
6
|
+
inputKeyVals: KeyValue[][];
|
7
|
+
outputKeyVals: KeyValue[][];
|
8
|
+
};
|