Skip to content

Commit

Permalink
feat: SwapTreeSerializer support for covenants
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 committed Feb 15, 2024
1 parent d01c88d commit abd5679
Show file tree
Hide file tree
Showing 13 changed files with 575 additions and 220 deletions.
2 changes: 2 additions & 0 deletions lib/consts/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,5 @@ export type SwapTree = {
claimLeaf: Tapleaf;
refundLeaf: Tapleaf;
};

export type LiquidSwapTree = SwapTree & { covenantClaimLeaf?: Tapleaf };
4 changes: 1 addition & 3 deletions lib/liquid/consts/Types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { BIP32Interface } from 'bip32';
import { ECPairInterface } from 'ecpair';
import { Transaction, TxOutput } from 'liquidjs-lib';
import { RefundDetails, SwapTree, Tapleaf } from '../../consts/Types';

export type LiquidSwapTree = SwapTree & { covenantClaimLeaf?: Tapleaf };
import { LiquidSwapTree, RefundDetails } from '../../consts/Types';

export type LiquidRefundDetails = Omit<RefundDetails, 'value' | 'swapTree'> &
TxOutput & {
Expand Down
5 changes: 2 additions & 3 deletions lib/liquid/swap/ReverseSwapTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ import ops from '@boltz/bitcoin-ops';
import { crypto, script } from 'bitcoinjs-lib';
import { reverseBuffer } from 'liquidjs-lib/src/bufferutils';
import { getHexBuffer } from '../../Utils';
import { Tapleaf } from '../../consts/Types';
import { LiquidSwapTree, Tapleaf } from '../../consts/Types';
import bitcoinReverseSwapTree from '../../swap/ReverseSwapTree';
import { leafVersionLiquid } from '../../swap/TaprootUtils';
import { assignTreeProbabilities, sortTree } from '../TreeSort';
import { assignTreeProbabilities, sortTree } from '../../swap/TreeSort';
import { getScriptIntrospectionValues } from '../Utils';
import liquidOps from '../consts/Ops';
import { LiquidSwapTree } from '../consts/Types';

enum Feature {
ClaimCovenant,
Expand Down
31 changes: 16 additions & 15 deletions lib/liquid/swap/TaprootUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ import { Taptree, isTapleaf } from 'bitcoinjs-lib/src/types';
import { Transaction, TxOutput } from 'liquidjs-lib';
import {
HashTree,
TaprootLeaf,
findScriptPath as liquidFindScriptPath,
tapLeafHash as liquidTapLeafHash,
toHashTree as liquidToHashTree,
} from 'liquidjs-lib/src/bip341';
import { taggedHash } from 'liquidjs-lib/src/crypto';
import { Network } from 'liquidjs-lib/src/networks';
Expand Down Expand Up @@ -44,24 +42,27 @@ export const hashForWitnessV1 = (
export const tapLeafHash = (leaf: Tapleaf) =>
liquidTapLeafHash(convertLeaf(leaf));

export const tapBranchHash = (a: Buffer, b: Buffer) =>
taggedHash('TapBranch/elements', Buffer.concat([a, b]));

export const tapTweakHash = (publicKey: Buffer, tweak: Buffer) =>
taggedHash('TapTweak/elements', Buffer.concat([toXOnly(publicKey), tweak]));

export const toHashTree = (tree: Taptree): HashTree => {
const leafs: TaprootLeaf[] = [];
export function toHashTree(scriptTree: Taptree): HashTree {
if (isTapleaf(scriptTree)) {
return { hash: tapLeafHash(scriptTree as Tapleaf) };
}

const convertToLeafs = (tree: Taptree) => {
if (isTapleaf(tree)) {
leafs.push(convertLeaf(tree as Tapleaf));
} else {
convertToLeafs(tree[0]);
convertToLeafs(tree[1]);
}
};
convertToLeafs(tree);
const hashes = [toHashTree(scriptTree[0]), toHashTree(scriptTree[1])];
hashes.sort((a, b) => a.hash.compare(b.hash));
const [left, right] = hashes;

return liquidToHashTree(leafs);
};
return {
hash: tapBranchHash(left.hash, right.hash),
left,
right,
};
}

export const tweakMusig = (musig: Musig, tree: Taptree): Buffer =>
toXOnly(
Expand Down
38 changes: 28 additions & 10 deletions lib/swap/SwapTreeSerializer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getHexBuffer, getHexString } from '../Utils';
import { SwapTree, Tapleaf } from '../consts/Types';
import { swapLeafsToTree } from './TaprootUtils';
import { LiquidSwapTree, SwapTree, Tapleaf } from '../consts/Types';
import { assignTreeProbabilities, sortTree } from './TreeSort';

type SerializedLeaf = {
version: number;
Expand All @@ -12,6 +12,10 @@ type SerializedTree = {
refundLeaf: SerializedLeaf;
};

type SerializedLiquidSwapTree = SerializedTree & {
covenantClaimLeaf?: SerializedLeaf;
};

const serializeLeaf = (leaf: Tapleaf): SerializedLeaf => ({
version: leaf.version,
output: getHexString(leaf.output),
Expand All @@ -22,24 +26,38 @@ const deserializeLeaf = (leaf: SerializedLeaf): Tapleaf => ({
output: getHexBuffer(leaf.output),
});

export const serializeSwapTree = (tree: SwapTree): SerializedTree => ({
claimLeaf: serializeLeaf(tree.claimLeaf),
refundLeaf: serializeLeaf(tree.refundLeaf),
});
export const serializeSwapTree = (
tree: LiquidSwapTree,
): SerializedTree | SerializedLiquidSwapTree => {
const res: SerializedLiquidSwapTree = {
claimLeaf: serializeLeaf(tree.claimLeaf),
refundLeaf: serializeLeaf(tree.refundLeaf),
};

if (tree.covenantClaimLeaf !== undefined) {
res.covenantClaimLeaf = serializeLeaf(tree.covenantClaimLeaf);
}

return res;
};

export const deserializeSwapTree = (
tree: string | SerializedTree,
): SwapTree => {
tree: string | SerializedTree | LiquidSwapTree,
): SwapTree | LiquidSwapTree => {
const parsed = typeof tree === 'string' ? JSON.parse(tree) : tree;

const res = {
const res: Omit<LiquidSwapTree, 'tree'> = {
claimLeaf: deserializeLeaf(parsed.claimLeaf),
refundLeaf: deserializeLeaf(parsed.refundLeaf),
covenantClaimLeaf:
parsed.covenantClaimLeaf !== undefined
? deserializeLeaf(parsed.covenantClaimLeaf)
: undefined,
};

return {
...res,
tree: swapLeafsToTree(res.claimLeaf, res.refundLeaf),
tree: sortTree(assignTreeProbabilities(res)),
};
};

Expand Down
18 changes: 13 additions & 5 deletions lib/liquid/TreeSort.ts → lib/swap/TreeSort.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Tapleaf } from '../consts/Types';
import { LiquidSwapTree } from './consts/Types';
import { LiquidSwapTree } from '../consts/Types';

type ProbabilityNode<T> = { probability: number; value: T };

Expand Down Expand Up @@ -29,9 +28,18 @@ const subSortTree = <T>(nodes: ProbabilityNode<T>[]): TreeNode<T> => {
export const sortTree = <T>(nodes: ProbabilityNode<T>[]): TreeNode<T> =>
subSortTree(nodes.sort((a, b) => b.probability - a.probability));

export const assignTreeProbabilities = (
tree: Omit<LiquidSwapTree, 'tree'>,
): ProbabilityNode<Tapleaf>[] => {
export const assignTreeProbabilities = <T>(
tree: Omit<
Record<keyof Omit<LiquidSwapTree, 'covenantClaimLeaf'>, T | undefined> & {
covenantClaimLeaf?: T;
},
'tree'
>,
): ProbabilityNode<T>[] => {
if (tree.claimLeaf === undefined || tree.refundLeaf === undefined) {
throw 'invalid tree';
}

if (tree.covenantClaimLeaf) {
return [
{
Expand Down
Loading

0 comments on commit abd5679

Please sign in to comment.