-
Notifications
You must be signed in to change notification settings - Fork 302
Expand file tree
/
Copy pathvalidatePolicy.ts
More file actions
118 lines (103 loc) · 3.88 KB
/
validatePolicy.ts
File metadata and controls
118 lines (103 loc) · 3.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import { EnvironmentName, Triple } from '@bitgo/sdk-core';
import { bip32, descriptorWallet } from '@bitgo/wasm-utxo';
import type { DescriptorMap } from '../wasmUtil.js';
import { parseDescriptor } from './builder/index.js';
import {
hasValidSignature,
NamedDescriptor,
NamedDescriptorNative,
toNamedDescriptorNative,
} from './NamedDescriptor.js';
export type KeyTriple = Triple<bip32.BIP32Interface>;
export interface DescriptorValidationPolicy {
name: string;
validate(arr: NamedDescriptorNative[], walletKeys: KeyTriple): boolean;
}
export const policyAllowAll: DescriptorValidationPolicy = {
name: 'allowAll',
validate: () => true,
};
export function getValidatorDescriptorTemplate(name: string): DescriptorValidationPolicy {
return {
name: 'descriptorTemplate(' + name + ')',
validate(arr: NamedDescriptorNative[], walletKeys: KeyTriple): boolean {
return arr.every((d) => {
const parsed = parseDescriptor(d.value);
return (
parsed.name === name &&
parsed.keys.length === walletKeys.length &&
parsed.keys.every((k, i) => k.toBase58() === walletKeys[i].neutered().toBase58())
);
});
},
};
}
export function getValidatorEvery(validators: DescriptorValidationPolicy[]): DescriptorValidationPolicy {
return {
name: 'every(' + validators.map((v) => v.name).join(',') + ')',
validate(arr: NamedDescriptorNative[], walletKeys: KeyTriple): boolean {
return validators.every((v) => v.validate(arr, walletKeys));
},
};
}
export function getValidatorSome(validators: DescriptorValidationPolicy[]): DescriptorValidationPolicy {
return {
name: 'some(' + validators.map((v) => v.name).join(',') + ')',
validate(arr: NamedDescriptorNative[], walletKeys: KeyTriple): boolean {
return validators.some((v) => v.validate(arr, walletKeys));
},
};
}
export function getValidatorOneOfTemplates(names: string[]): DescriptorValidationPolicy {
return getValidatorSome(names.map(getValidatorDescriptorTemplate));
}
export function getValidatorSignedByUserKey(): DescriptorValidationPolicy {
return {
name: 'signedByUser',
validate(arr: NamedDescriptorNative[], walletKeys: KeyTriple): boolean {
// the first key is the user key, by convention
return arr.every((d) => hasValidSignature(d.value, walletKeys[0], d.signatures ?? []));
},
};
}
export class DescriptorPolicyValidationError extends Error {
constructor(ds: NamedDescriptorNative[], policy: DescriptorValidationPolicy) {
super(`Descriptors ${ds.map((d) => d.value.toString())} does not match policy ${policy.name}`);
}
}
export function assertDescriptorPolicy(
descriptors: NamedDescriptorNative[],
policy: DescriptorValidationPolicy,
walletKeys: KeyTriple
): void {
if (!policy.validate(descriptors, walletKeys)) {
throw new DescriptorPolicyValidationError(descriptors, policy);
}
}
export function toDescriptorMapValidate(
descriptors: NamedDescriptor[],
walletKeys: KeyTriple,
policy: DescriptorValidationPolicy
): DescriptorMap {
const namedDescriptorsNative: NamedDescriptorNative[] = descriptors.map((v) =>
toNamedDescriptorNative(v, 'derivable')
);
assertDescriptorPolicy(namedDescriptorsNative, policy, walletKeys);
return descriptorWallet.toDescriptorMap(namedDescriptorsNative);
}
export function getPolicyForEnv(env: EnvironmentName): DescriptorValidationPolicy {
switch (env) {
case 'adminProd':
case 'prod':
return getValidatorSome([
// allow 2-of-3-ish descriptor groups where the keys match the wallet keys
getValidatorDescriptorTemplate('Wsh2Of3'),
// allow descriptor groups where all keys match the wallet keys plus OP_DROP (coredao staking)
getValidatorDescriptorTemplate('Wsh2Of3CltvDrop'),
// allow all descriptors signed by the user key
getValidatorSignedByUserKey(),
]);
default:
return policyAllowAll;
}
}