@bitgo/wasm-utxo 1.7.0 → 1.9.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.
Files changed (56) hide show
  1. package/dist/cjs/js/fixedScriptWallet/BitGoPsbt.d.ts +99 -0
  2. package/dist/cjs/js/fixedScriptWallet/BitGoPsbt.js +122 -0
  3. package/dist/cjs/js/wasm/wasm_utxo.d.ts +86 -0
  4. package/dist/cjs/js/wasm/wasm_utxo.js +276 -24
  5. package/dist/cjs/js/wasm/wasm_utxo_bg.wasm +0 -0
  6. package/dist/cjs/js/wasm/wasm_utxo_bg.wasm.d.ts +21 -17
  7. package/dist/esm/js/fixedScriptWallet/BitGoPsbt.d.ts +99 -0
  8. package/dist/esm/js/fixedScriptWallet/BitGoPsbt.js +122 -0
  9. package/dist/esm/js/wasm/wasm_utxo.d.ts +86 -0
  10. package/dist/esm/js/wasm/wasm_utxo_bg.js +276 -24
  11. package/dist/esm/js/wasm/wasm_utxo_bg.wasm +0 -0
  12. package/dist/esm/js/wasm/wasm_utxo_bg.wasm.d.ts +21 -17
  13. package/package.json +3 -3
  14. package/dist/cjs/package.json +0 -1
  15. package/dist/cjs/tsconfig.cjs.tsbuildinfo +0 -1
  16. package/dist/esm/test/address/utxolibCompat.d.ts +0 -1
  17. package/dist/esm/test/address/utxolibCompat.js +0 -107
  18. package/dist/esm/test/ast/formatNode.d.ts +0 -1
  19. package/dist/esm/test/ast/formatNode.js +0 -15
  20. package/dist/esm/test/bip32.d.ts +0 -1
  21. package/dist/esm/test/bip32.js +0 -242
  22. package/dist/esm/test/descriptorFixtures.d.ts +0 -25
  23. package/dist/esm/test/descriptorFixtures.js +0 -605
  24. package/dist/esm/test/descriptorUtil.d.ts +0 -13
  25. package/dist/esm/test/descriptorUtil.js +0 -52
  26. package/dist/esm/test/ecpair.d.ts +0 -1
  27. package/dist/esm/test/ecpair.js +0 -137
  28. package/dist/esm/test/fixedScript/address.d.ts +0 -1
  29. package/dist/esm/test/fixedScript/address.js +0 -66
  30. package/dist/esm/test/fixedScript/finalizeExtract.d.ts +0 -1
  31. package/dist/esm/test/fixedScript/finalizeExtract.js +0 -66
  32. package/dist/esm/test/fixedScript/fixtureUtil.d.ts +0 -95
  33. package/dist/esm/test/fixedScript/fixtureUtil.js +0 -55
  34. package/dist/esm/test/fixedScript/parseTransactionWithWalletKeys.d.ts +0 -1
  35. package/dist/esm/test/fixedScript/parseTransactionWithWalletKeys.js +0 -168
  36. package/dist/esm/test/fixedScript/verifySignature.d.ts +0 -1
  37. package/dist/esm/test/fixedScript/verifySignature.js +0 -187
  38. package/dist/esm/test/fixedScriptToDescriptor.d.ts +0 -1
  39. package/dist/esm/test/fixedScriptToDescriptor.js +0 -93
  40. package/dist/esm/test/fixtures.d.ts +0 -1
  41. package/dist/esm/test/fixtures.js +0 -16
  42. package/dist/esm/test/opdrop.d.ts +0 -1
  43. package/dist/esm/test/opdrop.js +0 -85
  44. package/dist/esm/test/psbt.util.d.ts +0 -8
  45. package/dist/esm/test/psbt.util.js +0 -113
  46. package/dist/esm/test/psbtFixedScriptCompat.d.ts +0 -1
  47. package/dist/esm/test/psbtFixedScriptCompat.js +0 -116
  48. package/dist/esm/test/psbtFixedScriptCompatFixtures.d.ts +0 -10
  49. package/dist/esm/test/psbtFixedScriptCompatFixtures.js +0 -53
  50. package/dist/esm/test/psbtFromDescriptor.d.ts +0 -1
  51. package/dist/esm/test/psbtFromDescriptor.js +0 -104
  52. package/dist/esm/test/psbtFromDescriptor.util.d.ts +0 -63
  53. package/dist/esm/test/psbtFromDescriptor.util.js +0 -101
  54. package/dist/esm/test/test.d.ts +0 -1
  55. package/dist/esm/test/test.js +0 -123
  56. package/dist/esm/tsconfig.tsbuildinfo +0 -1
@@ -1,52 +0,0 @@
1
- import * as assert from "node:assert";
2
- import * as fs from "fs/promises";
3
- import * as utxolib from "@bitgo/utxo-lib";
4
- import { formatNode } from "../js/ast/index.js";
5
- async function assertEqualJSON(path, value) {
6
- try {
7
- const data = JSON.parse(await fs.readFile(path, "utf8"));
8
- assert.deepStrictEqual(data, value);
9
- }
10
- catch (e) {
11
- if (typeof e === "object" && e !== null && "code" in e && e.code === "ENOENT") {
12
- await fs.writeFile(path, JSON.stringify(value, null, 2));
13
- throw new Error("Expected file not found, wrote it instead");
14
- }
15
- throw e;
16
- }
17
- }
18
- export async function assertEqualFixture(path, content) {
19
- await assertEqualJSON(path, content);
20
- }
21
- /** Expand a template with the given root wallet keys and chain code */
22
- function expand(rootWalletKeys, keyIndex, chainCode) {
23
- if (keyIndex !== 0 && keyIndex !== 1 && keyIndex !== 2) {
24
- throw new Error("Invalid key index");
25
- }
26
- const xpub = rootWalletKeys.triple[keyIndex].neutered().toBase58();
27
- const prefix = rootWalletKeys.derivationPrefixes[keyIndex];
28
- return xpub + "/" + prefix + "/" + chainCode + "/*";
29
- }
30
- /**
31
- * Get a standard output descriptor that corresponds to the proprietary HD wallet setup
32
- * used in BitGo wallets.
33
- * Only supports a subset of script types.
34
- */
35
- export function getDescriptorForScriptType(rootWalletKeys, scriptType, scope) {
36
- const chain = scope === "external"
37
- ? utxolib.bitgo.getExternalChainCode(scriptType)
38
- : utxolib.bitgo.getInternalChainCode(scriptType);
39
- const multi = {
40
- multi: [2, ...rootWalletKeys.triple.map((_, i) => expand(rootWalletKeys, i, chain))],
41
- };
42
- switch (scriptType) {
43
- case "p2sh":
44
- return formatNode({ sh: multi });
45
- case "p2shP2wsh":
46
- return formatNode({ sh: { wsh: multi } });
47
- case "p2wsh":
48
- return formatNode({ wsh: multi });
49
- default:
50
- throw new Error(`Unsupported script type ${scriptType}`);
51
- }
52
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,137 +0,0 @@
1
- import * as assert from "assert";
2
- import { ECPair } from "../js/ecpair.js";
3
- describe("WasmECPair", () => {
4
- const testPrivateKey = Buffer.from("1111111111111111111111111111111111111111111111111111111111111111", "hex");
5
- const testWifMainnet = "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn";
6
- const testWifTestnet = "cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87JcbXMTcA";
7
- it("should create from private key", () => {
8
- const key = ECPair.fromPrivateKey(testPrivateKey);
9
- assert.ok(key.privateKey instanceof Uint8Array);
10
- assert.ok(key.publicKey instanceof Uint8Array);
11
- assert.strictEqual(key.privateKey.length, 32);
12
- assert.strictEqual(key.publicKey.length, 33); // Always compressed
13
- });
14
- it("should create from public key", () => {
15
- const tempKey = ECPair.fromPrivateKey(testPrivateKey);
16
- const publicKey = tempKey.publicKey;
17
- const key = ECPair.fromPublicKey(publicKey);
18
- assert.strictEqual(key.privateKey, undefined);
19
- assert.ok(key.publicKey instanceof Uint8Array);
20
- assert.strictEqual(key.publicKey.length, 33);
21
- });
22
- it("should create from mainnet WIF", () => {
23
- const key = ECPair.fromWIF(testWifMainnet);
24
- assert.ok(key.privateKey instanceof Uint8Array);
25
- assert.ok(key.publicKey instanceof Uint8Array);
26
- assert.strictEqual(key.privateKey.length, 32);
27
- });
28
- it("should create from testnet WIF", () => {
29
- const key = ECPair.fromWIF(testWifTestnet);
30
- assert.ok(key.privateKey instanceof Uint8Array);
31
- assert.ok(key.publicKey instanceof Uint8Array);
32
- assert.strictEqual(key.privateKey.length, 32);
33
- });
34
- it("should create from mainnet WIF using fromWIFMainnet", () => {
35
- const key = ECPair.fromWIFMainnet(testWifMainnet);
36
- assert.ok(key.privateKey instanceof Uint8Array);
37
- assert.ok(key.publicKey instanceof Uint8Array);
38
- });
39
- it("should create from testnet WIF using fromWIFTestnet", () => {
40
- const key = ECPair.fromWIFTestnet(testWifTestnet);
41
- assert.ok(key.privateKey instanceof Uint8Array);
42
- assert.ok(key.publicKey instanceof Uint8Array);
43
- });
44
- it("should fail when using wrong network WIF method", () => {
45
- assert.throws(() => {
46
- ECPair.fromWIFMainnet(testWifTestnet);
47
- });
48
- assert.throws(() => {
49
- ECPair.fromWIFTestnet(testWifMainnet);
50
- });
51
- });
52
- it("should export to WIF mainnet", () => {
53
- const key = ECPair.fromPrivateKey(testPrivateKey);
54
- const wif = key.toWIF();
55
- assert.ok(typeof wif === "string");
56
- assert.ok(wif.length > 0);
57
- assert.ok(wif.startsWith("K") || wif.startsWith("L")); // Mainnet compressed
58
- });
59
- it("should export to WIF testnet", () => {
60
- const key = ECPair.fromPrivateKey(testPrivateKey);
61
- const wif = key.toWIFTestnet();
62
- assert.ok(typeof wif === "string");
63
- assert.ok(wif.length > 0);
64
- assert.ok(wif.startsWith("c")); // Testnet compressed
65
- });
66
- it("should roundtrip WIF mainnet", () => {
67
- const key1 = ECPair.fromPrivateKey(testPrivateKey);
68
- const wif = key1.toWIF();
69
- const key2 = ECPair.fromWIF(wif);
70
- assert.deepStrictEqual(key1.privateKey, key2.privateKey);
71
- assert.deepStrictEqual(key1.publicKey, key2.publicKey);
72
- });
73
- it("should roundtrip WIF testnet", () => {
74
- const key1 = ECPair.fromPrivateKey(testPrivateKey);
75
- const wif = key1.toWIFTestnet();
76
- const key2 = ECPair.fromWIF(wif);
77
- assert.deepStrictEqual(key1.privateKey, key2.privateKey);
78
- assert.deepStrictEqual(key1.publicKey, key2.publicKey);
79
- });
80
- it("should fail to export WIF from public key", () => {
81
- const tempKey = ECPair.fromPrivateKey(testPrivateKey);
82
- const publicKey = tempKey.publicKey;
83
- const key = ECPair.fromPublicKey(publicKey);
84
- assert.throws(() => {
85
- key.toWIF();
86
- });
87
- assert.throws(() => {
88
- key.toWIFMainnet();
89
- });
90
- assert.throws(() => {
91
- key.toWIFTestnet();
92
- });
93
- });
94
- it("should reject invalid private keys", () => {
95
- // All zeros
96
- assert.throws(() => {
97
- ECPair.fromPrivateKey(new Uint8Array(32));
98
- });
99
- // Wrong length
100
- assert.throws(() => {
101
- ECPair.fromPrivateKey(new Uint8Array(31));
102
- });
103
- assert.throws(() => {
104
- ECPair.fromPrivateKey(new Uint8Array(33));
105
- });
106
- });
107
- it("should reject invalid public keys", () => {
108
- // Wrong length
109
- assert.throws(() => {
110
- ECPair.fromPublicKey(new Uint8Array(32));
111
- });
112
- assert.throws(() => {
113
- ECPair.fromPublicKey(new Uint8Array(34));
114
- });
115
- // Invalid format
116
- assert.throws(() => {
117
- const invalidPubkey = new Uint8Array(33);
118
- invalidPubkey[0] = 0x01; // Invalid prefix
119
- ECPair.fromPublicKey(invalidPubkey);
120
- });
121
- });
122
- it("should always produce compressed public keys", () => {
123
- const key1 = ECPair.fromPrivateKey(testPrivateKey);
124
- const key2 = ECPair.fromWIF(testWifMainnet);
125
- // All public keys should be 33 bytes (compressed)
126
- assert.strictEqual(key1.publicKey.length, 33);
127
- assert.strictEqual(key2.publicKey.length, 33);
128
- // All should start with 0x02 or 0x03 (compressed format)
129
- assert.ok(key1.publicKey[0] === 0x02 || key1.publicKey[0] === 0x03);
130
- assert.ok(key2.publicKey[0] === 0x02 || key2.publicKey[0] === 0x03);
131
- });
132
- it("should derive same public key from same private key", () => {
133
- const key1 = ECPair.fromPrivateKey(testPrivateKey);
134
- const key2 = ECPair.fromPrivateKey(testPrivateKey);
135
- assert.deepStrictEqual(key1.publicKey, key2.publicKey);
136
- });
137
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,66 +0,0 @@
1
- import assert from "node:assert";
2
- import * as utxolib from "@bitgo/utxo-lib";
3
- import { fixedScriptWallet } from "../../js/index.js";
4
- function getAddressUtxoLib(keys, chain, index, network, addressFormat) {
5
- if (!utxolib.bitgo.isChainCode(chain)) {
6
- throw new Error(`Invalid chain code: ${chain}`);
7
- }
8
- const derived = keys.deriveForChainAndIndex(chain, index);
9
- const script = utxolib.bitgo.outputScripts.createOutputScript2of3(derived.publicKeys, utxolib.bitgo.outputScripts.scriptTypeForChain(chain));
10
- const address = utxolib.addressFormat.fromOutputScriptWithFormat(script.scriptPubKey, addressFormat, network);
11
- return address;
12
- }
13
- function runTest(network, { derivationPrefixes, addressFormat, } = {}) {
14
- describe(`address for network ${utxolib.getNetworkName(network)}, derivationPrefixes=${Boolean(derivationPrefixes)}`, function () {
15
- const keyTriple = utxolib.testutil.getKeyTriple("wasm");
16
- const rootWalletKeys = new utxolib.bitgo.RootWalletKeys(keyTriple.map((k) => k.neutered()), derivationPrefixes);
17
- const supportedChainCodes = utxolib.bitgo.chainCodes.filter((chainCode) => {
18
- const scriptType = utxolib.bitgo.outputScripts.scriptTypeForChain(chainCode);
19
- return utxolib.bitgo.outputScripts.isSupportedScriptType(network, scriptType);
20
- });
21
- it(`can recreate address from wallet keys for chain codes ${supportedChainCodes.join(", ")}`, function () {
22
- for (const chainCode of supportedChainCodes) {
23
- for (let index = 0; index < 2; index++) {
24
- const utxolibAddress = getAddressUtxoLib(rootWalletKeys, chainCode, index, network, addressFormat ?? "default");
25
- const wasmAddress = fixedScriptWallet.address(rootWalletKeys, chainCode, index, network, addressFormat);
26
- assert.strictEqual(utxolibAddress, wasmAddress);
27
- }
28
- }
29
- });
30
- const unsupportedChainCodes = utxolib.bitgo.chainCodes.filter((chainCode) => {
31
- const scriptType = utxolib.bitgo.outputScripts.scriptTypeForChain(chainCode);
32
- return !utxolib.bitgo.outputScripts.isSupportedScriptType(network, scriptType);
33
- });
34
- if (unsupportedChainCodes.length > 0) {
35
- it(`throws error for unsupported chain codes ${unsupportedChainCodes.join(", ")}`, function () {
36
- for (const chainCode of unsupportedChainCodes) {
37
- const scriptType = utxolib.bitgo.outputScripts.scriptTypeForChain(chainCode);
38
- assert.throws(() => {
39
- fixedScriptWallet.address(rootWalletKeys, chainCode, 0, network, addressFormat);
40
- }, (error) => {
41
- const errorMessage = error.message.toLowerCase();
42
- const isSegwitError = scriptType === "p2shP2wsh" || scriptType === "p2wsh";
43
- const isTaprootError = scriptType === "p2tr" || scriptType === "p2trMusig2";
44
- if (isSegwitError) {
45
- return errorMessage.includes("does not support segwit");
46
- }
47
- else if (isTaprootError) {
48
- return errorMessage.includes("does not support taproot");
49
- }
50
- return false;
51
- }, `Expected error for unsupported script type ${scriptType} on network ${utxolib.getNetworkName(network)}`);
52
- }
53
- });
54
- }
55
- });
56
- }
57
- describe("address for networks", function () {
58
- utxolib.getNetworkList().forEach((network) => {
59
- runTest(network);
60
- runTest(network, { derivationPrefixes: ["m/1/2", "m/0/0", "m/0/0"] });
61
- if (utxolib.getMainnet(network) === utxolib.networks.bitcoincash ||
62
- utxolib.getMainnet(network) === utxolib.networks.ecash) {
63
- runTest(network, { addressFormat: "cashaddr" });
64
- }
65
- });
66
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,66 +0,0 @@
1
- import assert from "node:assert";
2
- import * as utxolib from "@bitgo/utxo-lib";
3
- import { fixedScriptWallet } from "../../js/index.js";
4
- import { loadPsbtFixture, getPsbtBuffer, getExtractedTransactionHex, } from "./fixtureUtil.js";
5
- describe("finalize and extract transaction", function () {
6
- const supportedNetworks = utxolib.getNetworkList().filter((network) => {
7
- return (utxolib.isMainnet(network) &&
8
- network !== utxolib.networks.bitcoincash &&
9
- network !== utxolib.networks.bitcoingold &&
10
- network !== utxolib.networks.bitcoinsv &&
11
- network !== utxolib.networks.ecash &&
12
- network !== utxolib.networks.zcash);
13
- });
14
- supportedNetworks.forEach((network) => {
15
- const networkName = utxolib.getNetworkName(network);
16
- describe(`network: ${networkName}`, function () {
17
- let fullsignedFixture;
18
- let fullsignedPsbtBuffer;
19
- let fullsignedBitgoPsbt;
20
- before(function () {
21
- fullsignedFixture = loadPsbtFixture(networkName, "fullsigned");
22
- fullsignedPsbtBuffer = getPsbtBuffer(fullsignedFixture);
23
- fullsignedBitgoPsbt = fixedScriptWallet.BitGoPsbt.fromBytes(fullsignedPsbtBuffer, networkName);
24
- });
25
- it("should serialize and deserialize PSBT (round-trip)", function () {
26
- const serialized = fullsignedBitgoPsbt.serialize();
27
- // Verify we can deserialize what we serialized (functional round-trip)
28
- const deserialized = fixedScriptWallet.BitGoPsbt.fromBytes(serialized, networkName);
29
- // Verify the deserialized PSBT has the same unsigned txid
30
- assert.strictEqual(deserialized.unsignedTxid(), fullsignedBitgoPsbt.unsignedTxid(), "Deserialized PSBT should have same unsigned txid after round-trip");
31
- // Verify the re-deserialized PSBT can be serialized back to bytes
32
- const reserialized = deserialized.serialize();
33
- // Verify functional equivalence by deserializing again and checking txid
34
- const redeserialized = fixedScriptWallet.BitGoPsbt.fromBytes(reserialized, networkName);
35
- assert.strictEqual(redeserialized.unsignedTxid(), fullsignedBitgoPsbt.unsignedTxid(), "PSBT should maintain consistency through multiple serialize/deserialize cycles");
36
- });
37
- it("should finalize all inputs and be extractable", function () {
38
- // Create a fresh instance for finalization
39
- const psbt = fixedScriptWallet.BitGoPsbt.fromBytes(fullsignedPsbtBuffer, networkName);
40
- // Finalize all inputs
41
- psbt.finalizeAllInputs();
42
- // Serialize the finalized PSBT
43
- const serialized = psbt.serialize();
44
- // Verify we can deserialize the finalized PSBT
45
- const deserialized = fixedScriptWallet.BitGoPsbt.fromBytes(serialized, networkName);
46
- // Verify it can be extracted (which confirms finalization worked)
47
- const extractedTx = deserialized.extractTransaction();
48
- const extractedTxHex = Buffer.from(extractedTx).toString("hex");
49
- const expectedTxHex = getExtractedTransactionHex(fullsignedFixture);
50
- assert.strictEqual(extractedTxHex, expectedTxHex, "Extracted transaction from finalized PSBT should match expected transaction");
51
- });
52
- it("should extract transaction from finalized PSBT", function () {
53
- // Create a fresh instance for extraction
54
- const psbt = fixedScriptWallet.BitGoPsbt.fromBytes(fullsignedPsbtBuffer, networkName);
55
- // Finalize all inputs
56
- psbt.finalizeAllInputs();
57
- // Extract transaction
58
- const extractedTx = psbt.extractTransaction();
59
- const extractedTxHex = Buffer.from(extractedTx).toString("hex");
60
- // Get expected transaction hex from fixture
61
- const expectedTxHex = getExtractedTransactionHex(fullsignedFixture);
62
- assert.strictEqual(extractedTxHex, expectedTxHex, "Extracted transaction should match expected transaction");
63
- });
64
- });
65
- });
66
- });
@@ -1,95 +0,0 @@
1
- import { RootWalletKeys } from "../../js/fixedScriptWallet/RootWalletKeys.js";
2
- import { ECPair } from "../../js/ecpair.js";
3
- export type SignatureState = "unsigned" | "halfsigned" | "fullsigned";
4
- export type Triple<T> = [T, T, T];
5
- export type Bip32Derivation = {
6
- masterFingerprint: string;
7
- pubkey: string;
8
- path: string;
9
- };
10
- export type TapBip32Derivation = Bip32Derivation & {
11
- leafHashes: string[];
12
- };
13
- export type WitnessUtxo = {
14
- value: string;
15
- script: string;
16
- };
17
- export type TapLeafScript = {
18
- controlBlock: string;
19
- script: string;
20
- leafVersion: number;
21
- };
22
- export type PsbtInput = {
23
- type: string;
24
- sighashType: number;
25
- redeemScript?: string;
26
- witnessScript?: string;
27
- bip32Derivation?: Bip32Derivation[];
28
- tapBip32Derivation?: TapBip32Derivation[];
29
- witnessUtxo?: WitnessUtxo;
30
- tapLeafScript?: TapLeafScript[];
31
- tapInternalKey?: string;
32
- tapMerkleRoot?: string;
33
- musig2Participants?: {
34
- tapOutputKey: string;
35
- tapInternalKey: string;
36
- participantPubKeys: string[];
37
- };
38
- unknownKeyVals?: Array<{
39
- key: string;
40
- value: string;
41
- }>;
42
- };
43
- export type Input = {
44
- hash: string;
45
- index: number;
46
- sequence: number;
47
- };
48
- export type Output = {
49
- script: string;
50
- value: string;
51
- address?: string;
52
- };
53
- export type TapTreeLeaf = {
54
- depth: number;
55
- leafVersion: number;
56
- script: string;
57
- };
58
- export type PsbtOutput = {
59
- redeemScript?: string;
60
- witnessScript?: string;
61
- bip32Derivation?: Bip32Derivation[];
62
- tapBip32Derivation?: TapBip32Derivation[];
63
- tapInternalKey?: string;
64
- tapTree?: {
65
- leaves: TapTreeLeaf[];
66
- };
67
- };
68
- export type Fixture = {
69
- walletKeys: [string, string, string];
70
- psbtBase64: string;
71
- psbtBase64Finalized: string | null;
72
- inputs: Input[];
73
- psbtInputs: PsbtInput[];
74
- psbtInputsFinalized: PsbtInput[] | null;
75
- outputs: Output[];
76
- psbtOutputs: PsbtOutput[];
77
- extractedTransaction: string | null;
78
- };
79
- /**
80
- * Get PSBT buffer from a fixture
81
- */
82
- export declare function getPsbtBuffer(fixture: Fixture): Buffer;
83
- /**
84
- * Load a PSBT fixture from JSON file
85
- */
86
- export declare function loadPsbtFixture(network: string, signatureState: string): Fixture;
87
- /**
88
- * Load wallet keys from fixture
89
- */
90
- export declare function loadWalletKeysFromFixture(fixture: Fixture): RootWalletKeys;
91
- export declare function loadReplayProtectionKeyFromFixture(fixture: Fixture): ECPair;
92
- /**
93
- * Get extracted transaction hex from fixture
94
- */
95
- export declare function getExtractedTransactionHex(fixture: Fixture): string;
@@ -1,55 +0,0 @@
1
- import assert from "node:assert";
2
- import * as fs from "node:fs";
3
- import * as path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { dirname } from "node:path";
6
- import { BIP32 } from "../../js/bip32.js";
7
- import { RootWalletKeys } from "../../js/fixedScriptWallet/RootWalletKeys.js";
8
- import { ECPair } from "../../js/ecpair.js";
9
- const __filename = fileURLToPath(import.meta.url);
10
- const __dirname = dirname(__filename);
11
- /**
12
- * Get PSBT buffer from a fixture
13
- */
14
- export function getPsbtBuffer(fixture) {
15
- return Buffer.from(fixture.psbtBase64, "base64");
16
- }
17
- /**
18
- * Load a PSBT fixture from JSON file
19
- */
20
- export function loadPsbtFixture(network, signatureState) {
21
- const fixturePath = path.join(__dirname, "..", "fixtures", "fixed-script", `psbt-lite.${network}.${signatureState}.json`);
22
- const fixtureContent = fs.readFileSync(fixturePath, "utf-8");
23
- return JSON.parse(fixtureContent);
24
- }
25
- /**
26
- * Load wallet keys from fixture
27
- */
28
- export function loadWalletKeysFromFixture(fixture) {
29
- // Parse xprvs and convert to xpubs
30
- const xpubs = fixture.walletKeys.map((xprv) => {
31
- const key = BIP32.fromBase58(xprv);
32
- return key.neutered();
33
- });
34
- const walletKeysLike = {
35
- triple: xpubs,
36
- derivationPrefixes: ["0/0", "0/0", "0/0"],
37
- };
38
- return RootWalletKeys.from(walletKeysLike);
39
- }
40
- export function loadReplayProtectionKeyFromFixture(fixture) {
41
- // underived user key
42
- const userBip32 = BIP32.fromBase58(fixture.walletKeys[0]);
43
- assert(userBip32.privateKey);
44
- const userECPair = ECPair.fromPrivateKey(Buffer.from(userBip32.privateKey));
45
- return userECPair;
46
- }
47
- /**
48
- * Get extracted transaction hex from fixture
49
- */
50
- export function getExtractedTransactionHex(fixture) {
51
- if (fixture.extractedTransaction === null) {
52
- throw new Error("Fixture does not have an extracted transaction");
53
- }
54
- return fixture.extractedTransaction;
55
- }
@@ -1,168 +0,0 @@
1
- import assert from "node:assert";
2
- import * as utxolib from "@bitgo/utxo-lib";
3
- import { fixedScriptWallet } from "../../js/index.js";
4
- import { loadPsbtFixture, loadWalletKeysFromFixture, getPsbtBuffer, loadReplayProtectionKeyFromFixture, } from "./fixtureUtil.js";
5
- function getExpectedInputScriptType(fixtureScriptType) {
6
- // Map fixture types to InputScriptType values
7
- // Based on the Rust mapping in src/fixed_script_wallet/test_utils/fixtures.rs
8
- switch (fixtureScriptType) {
9
- case "p2shP2pk":
10
- case "p2sh":
11
- case "p2shP2wsh":
12
- case "p2wsh":
13
- return fixtureScriptType;
14
- case "p2tr":
15
- return "p2trLegacy";
16
- case "p2trMusig2":
17
- return "p2trMusig2ScriptPath";
18
- case "taprootKeyPathSpend":
19
- return "p2trMusig2KeyPath";
20
- default:
21
- throw new Error(`Unknown fixture script type: ${fixtureScriptType}`);
22
- }
23
- }
24
- function getOtherWalletKeys() {
25
- const otherWalletKeys = utxolib.testutil.getKeyTriple("too many secrets");
26
- return new utxolib.bitgo.RootWalletKeys(otherWalletKeys);
27
- }
28
- describe("parseTransactionWithWalletKeys", function () {
29
- const supportedNetworks = utxolib.getNetworkList().filter((network) => {
30
- return (utxolib.isMainnet(network) &&
31
- network !== utxolib.networks.bitcoincash &&
32
- network !== utxolib.networks.bitcoingold &&
33
- network !== utxolib.networks.bitcoinsv &&
34
- network !== utxolib.networks.ecash &&
35
- network !== utxolib.networks.zcash);
36
- });
37
- supportedNetworks.forEach((network) => {
38
- const networkName = utxolib.getNetworkName(network);
39
- describe(`network: ${networkName}`, function () {
40
- let fullsignedPsbtBytes;
41
- let bitgoPsbt;
42
- let rootWalletKeys;
43
- let replayProtectionKey;
44
- let fixture;
45
- before(function () {
46
- fixture = loadPsbtFixture(networkName, "fullsigned");
47
- fullsignedPsbtBytes = getPsbtBuffer(fixture);
48
- bitgoPsbt = fixedScriptWallet.BitGoPsbt.fromBytes(fullsignedPsbtBytes, networkName);
49
- rootWalletKeys = loadWalletKeysFromFixture(fixture);
50
- replayProtectionKey = loadReplayProtectionKeyFromFixture(fixture);
51
- });
52
- it("should have matching unsigned transaction ID", function () {
53
- const unsignedTxid = bitgoPsbt.unsignedTxid();
54
- const expectedUnsignedTxid = utxolib.bitgo
55
- .createPsbtFromBuffer(fullsignedPsbtBytes, network)
56
- .getUnsignedTx()
57
- .getId();
58
- assert.strictEqual(unsignedTxid, expectedUnsignedTxid);
59
- });
60
- it("should parse transaction and identify internal/external outputs", function () {
61
- const parsed = bitgoPsbt.parseTransactionWithWalletKeys(rootWalletKeys, {
62
- publicKeys: [replayProtectionKey],
63
- });
64
- // Verify all inputs have addresses and values
65
- parsed.inputs.forEach((input, i) => {
66
- assert.ok(input.address, `Input ${i} should have an address`);
67
- assert.ok(typeof input.value === "bigint", `Input ${i} value should be bigint`);
68
- assert.ok(input.value > 0n, `Input ${i} value should be > 0`);
69
- });
70
- // Validate outputs
71
- assert.ok(parsed.outputs.length > 0, "Should have at least one output");
72
- // Count internal outputs (scriptId is defined and not null)
73
- const internalOutputs = parsed.outputs.filter((o) => o.scriptId);
74
- // Count external outputs (scriptId is null or undefined)
75
- const externalOutputs = parsed.outputs.filter((o) => o.scriptId === null);
76
- assert.ok(externalOutputs.every((o) => o.address || o.script));
77
- const nonAddressOutputs = externalOutputs.filter((o) => o.address === null);
78
- assert.strictEqual(nonAddressOutputs.length, 1);
79
- const [opReturnOutput] = nonAddressOutputs;
80
- const expectedOpReturn = utxolib.payments.embed({
81
- data: [Buffer.from("setec astronomy")],
82
- }).output;
83
- assert.strictEqual(Buffer.from(opReturnOutput.script).toString("hex"), expectedOpReturn.toString("hex"));
84
- // Fixtures now have 3 external outputs
85
- assert.ok(internalOutputs.length > 0, "Should have internal outputs (have scriptId)");
86
- assert.strictEqual(externalOutputs.length, 3, "Should have 3 external outputs in test fixture");
87
- // Verify all outputs have proper structure
88
- parsed.outputs.forEach((output, i) => {
89
- assert.ok(output.script instanceof Uint8Array, `Output ${i} script should be Uint8Array`);
90
- assert.ok(typeof output.value === "bigint", `Output ${i} value should be bigint`);
91
- assert.ok(output.value > 0n, `Output ${i} value should be > 0`);
92
- // Address is optional for non-standard scripts
93
- });
94
- // Verify spend amount (should be > 0 since there are external outputs)
95
- assert.strictEqual(parsed.spendAmount, 900n * 3n);
96
- // Verify miner fee calculation
97
- const totalInputValue = parsed.inputs.reduce((sum, i) => sum + i.value, 0n);
98
- const totalOutputValue = parsed.outputs.reduce((sum, o) => sum + o.value, 0n);
99
- assert.strictEqual(parsed.minerFee, totalInputValue - totalOutputValue, "Miner fee should equal inputs minus outputs");
100
- assert.ok(parsed.minerFee > 0n, "Miner fee should be > 0");
101
- // Verify virtual size
102
- assert.ok(typeof parsed.virtualSize === "number", "Virtual size should be a number");
103
- assert.ok(parsed.virtualSize > 0, "Virtual size should be > 0");
104
- });
105
- it("should parse inputs with correct scriptType", function () {
106
- const parsed = bitgoPsbt.parseTransactionWithWalletKeys(rootWalletKeys, {
107
- publicKeys: [replayProtectionKey],
108
- });
109
- // Verify all inputs have scriptType matching fixture
110
- parsed.inputs.forEach((input, i) => {
111
- const fixtureInput = fixture.psbtInputs[i];
112
- const expectedScriptType = getExpectedInputScriptType(fixtureInput.type);
113
- assert.strictEqual(input.scriptType, expectedScriptType, `Input ${i} scriptType should be ${expectedScriptType}, got ${input.scriptType}`);
114
- });
115
- });
116
- it("should fail to parse with other wallet keys", function () {
117
- assert.throws(() => {
118
- bitgoPsbt.parseTransactionWithWalletKeys(getOtherWalletKeys(), {
119
- publicKeys: [replayProtectionKey],
120
- });
121
- }, (error) => {
122
- return error.message.includes("Failed to parse transaction: Input 0: wallet validation failed");
123
- });
124
- });
125
- it("should recognize output for other wallet keys", function () {
126
- const parsedOutputs = bitgoPsbt.parseOutputsWithWalletKeys(getOtherWalletKeys());
127
- // Should return an array of parsed outputs
128
- assert.ok(Array.isArray(parsedOutputs), "Should return an array");
129
- assert.ok(parsedOutputs.length > 0, "Should have at least one output");
130
- // Verify all outputs have proper structure
131
- parsedOutputs.forEach((output, i) => {
132
- assert.ok(output.script instanceof Uint8Array, `Output ${i} script should be Uint8Array`);
133
- assert.ok(typeof output.value === "bigint", `Output ${i} value should be bigint`);
134
- assert.ok(output.value > 0n, `Output ${i} value should be > 0`);
135
- // Address can be null for non-standard scripts
136
- assert.ok(typeof output.address === "string" || output.address === null, `Output ${i} address should be string or null`);
137
- // scriptId can be null for external outputs
138
- assert.ok(output.scriptId === null ||
139
- (typeof output.scriptId === "object" &&
140
- typeof output.scriptId.chain === "number" &&
141
- typeof output.scriptId.index === "number"), `Output ${i} scriptId should be null or an object with chain and index`);
142
- });
143
- // Compare with the original wallet keys to verify we get different results
144
- const originalParsedOutputs = bitgoPsbt.parseOutputsWithWalletKeys(rootWalletKeys);
145
- // Should have the same number of outputs
146
- assert.strictEqual(parsedOutputs.length, originalParsedOutputs.length, "Should parse the same number of outputs");
147
- // Find outputs that belong to the other wallet keys (scriptId !== null)
148
- const otherWalletOutputs = parsedOutputs.filter((o) => o.scriptId !== null);
149
- // Should have exactly one output for the other wallet keys
150
- assert.strictEqual(otherWalletOutputs.length, 1, "Should have exactly one output belonging to the other wallet keys");
151
- // Verify that this output is marked as external (scriptId === null) under regular wallet keys
152
- const otherWalletOutputIndex = parsedOutputs.findIndex((o) => o.scriptId !== null);
153
- const sameOutputWithRegularKeys = originalParsedOutputs[otherWalletOutputIndex];
154
- assert.strictEqual(sameOutputWithRegularKeys.scriptId, null, "The output belonging to other wallet keys should be marked as external (scriptId === null) when parsed with regular wallet keys");
155
- });
156
- });
157
- });
158
- describe("error handling", function () {
159
- it("should throw error for invalid PSBT bytes", function () {
160
- const invalidBytes = new Uint8Array([0x00, 0x01, 0x02]);
161
- assert.throws(() => {
162
- fixedScriptWallet.BitGoPsbt.fromBytes(invalidBytes, "bitcoin");
163
- }, (error) => {
164
- return error.message.includes("Failed to deserialize PSBT");
165
- }, "Should throw error for invalid PSBT bytes");
166
- });
167
- });
168
- });
@@ -1 +0,0 @@
1
- export {};