|
1 | 1 | 'use strict'; |
2 | 2 | Object.defineProperty(exports, '__esModule', { value: true }); |
3 | | -exports.tweakInternalPubKey = exports.checkTaprootInputFields = exports.isTaprootInput = exports.serializeTaprootSignature = exports.tapScriptFinalizer = exports.toXOnly = void 0; |
| 3 | +exports.tapTreeFromList = exports.tapTreeToList = exports.tweakInternalPubKey = exports.checkTaprootInputFields = exports.isTaprootInput = exports.serializeTaprootSignature = exports.tapScriptFinalizer = exports.toXOnly = void 0; |
| 4 | +const types_1 = require('../types'); |
4 | 5 | const psbtutils_1 = require('./psbtutils'); |
5 | 6 | const taprootutils_1 = require('../payments/taprootutils'); |
6 | 7 | const toXOnly = pubKey => (pubKey.length === 32 ? pubKey : pubKey.slice(1, 33)); |
@@ -69,6 +70,76 @@ function tweakInternalPubKey(inputIndex, input) { |
69 | 70 | return outputKey.x; |
70 | 71 | } |
71 | 72 | exports.tweakInternalPubKey = tweakInternalPubKey; |
| 73 | +/** |
| 74 | + * Convert a binary tree to a BIP371 type list. Each element of the list is (according to BIP371): |
| 75 | + * One or more tuples representing the depth, leaf version, and script for a leaf in the Taproot tree, |
| 76 | + * allowing the entire tree to be reconstructed. The tuples must be in depth first search order so that |
| 77 | + * the tree is correctly reconstructed. |
| 78 | + * @param tree the binary tap tree |
| 79 | + * @returns a list of BIP 371 tapleaves |
| 80 | + */ |
| 81 | +function tapTreeToList(tree) { |
| 82 | + return _tapTreeToList(tree); |
| 83 | +} |
| 84 | +exports.tapTreeToList = tapTreeToList; |
| 85 | +/** |
| 86 | + * Convert a BIP371 TapLeaf list to a TapTree (binary). |
| 87 | + * @param leaves a list of tapleaves where each element of the list is (according to BIP371): |
| 88 | + * One or more tuples representing the depth, leaf version, and script for a leaf in the Taproot tree, |
| 89 | + * allowing the entire tree to be reconstructed. The tuples must be in depth first search order so that |
| 90 | + * the tree is correctly reconstructed. |
| 91 | + * @returns the corresponding taptree, or throws an exception if the tree cannot be reconstructed |
| 92 | + */ |
| 93 | +function tapTreeFromList(leaves = []) { |
| 94 | + if (leaves.length === 1 && leaves[0].depth === 0) |
| 95 | + return { |
| 96 | + output: leaves[0].script, |
| 97 | + version: leaves[0].leafVersion, |
| 98 | + }; |
| 99 | + return instertLeavesInTree(leaves); |
| 100 | +} |
| 101 | +exports.tapTreeFromList = tapTreeFromList; |
| 102 | +function _tapTreeToList(tree, leaves = [], depth = 0) { |
| 103 | + if (depth > taprootutils_1.MAX_TAPTREE_DEPTH) |
| 104 | + throw new Error('Max taptree depth exceeded.'); |
| 105 | + if (!tree) return []; |
| 106 | + if ((0, types_1.isTapleaf)(tree)) { |
| 107 | + leaves.push({ |
| 108 | + depth, |
| 109 | + leafVersion: tree.version || taprootutils_1.LEAF_VERSION_TAPSCRIPT, |
| 110 | + script: tree.output, |
| 111 | + }); |
| 112 | + return leaves; |
| 113 | + } |
| 114 | + if (tree[0]) _tapTreeToList(tree[0], leaves, depth + 1); |
| 115 | + if (tree[1]) _tapTreeToList(tree[1], leaves, depth + 1); |
| 116 | + return leaves; |
| 117 | +} |
| 118 | +function instertLeavesInTree(leaves) { |
| 119 | + let tree; |
| 120 | + for (const leaf of leaves) { |
| 121 | + tree = instertLeafInTree(leaf, tree); |
| 122 | + if (!tree) throw new Error(`No room left to insert tapleaf in tree`); |
| 123 | + } |
| 124 | + return tree; |
| 125 | +} |
| 126 | +function instertLeafInTree(leaf, tree, depth = 0) { |
| 127 | + if (depth > taprootutils_1.MAX_TAPTREE_DEPTH) |
| 128 | + throw new Error('Max taptree depth exceeded.'); |
| 129 | + if (leaf.depth === depth) { |
| 130 | + if (!tree) |
| 131 | + return { |
| 132 | + output: leaf.script, |
| 133 | + version: leaf.leafVersion, |
| 134 | + }; |
| 135 | + return; |
| 136 | + } |
| 137 | + if ((0, types_1.isTapleaf)(tree)) return; |
| 138 | + const leftSide = instertLeafInTree(leaf, tree && tree[0], depth + 1); |
| 139 | + if (leftSide) return [leftSide, tree && tree[1]]; |
| 140 | + const rightSide = instertLeafInTree(leaf, tree && tree[1], depth + 1); |
| 141 | + if (rightSide) return [tree && tree[0], rightSide]; |
| 142 | +} |
72 | 143 | function checkMixedTaprootAndNonTaprootFields(inputData, newInputData, action) { |
73 | 144 | const isBadTaprootUpdate = |
74 | 145 | isTaprootInput(inputData) && hasNonTaprootInputFields(newInputData); |
|
0 commit comments