@@ -3,12 +3,31 @@ import { PatternMatcher, Pattern } from '@bitgo/utxo-core/descriptor';
33
44import { getUnspendableKey } from './descriptor' ;
55
6- type ParsedStakingDescriptor = {
6+ export type ParsedStakingDescriptor = {
7+ stakerKey : Buffer ;
8+ finalityProviderKeys : Buffer [ ] ;
9+ covenantKeys : Buffer [ ] ;
10+ covenantThreshold : number ;
11+ stakingTimeLock : number ;
712 slashingMiniscriptNode : ast . MiniscriptNode ;
813 unbondingMiniscriptNode : ast . MiniscriptNode ;
914 timelockMiniscriptNode : ast . MiniscriptNode ;
1015} ;
1116
17+ function parseMulti ( multi : unknown ) : [ number , string [ ] ] {
18+ if ( ! Array . isArray ( multi ) || multi . length < 1 ) {
19+ throw new Error ( 'Invalid multi structure: not an array or empty' ) ;
20+ }
21+ const [ threshold , ...keys ] = multi ;
22+ if ( typeof threshold !== 'number' ) {
23+ throw new Error ( 'Invalid multi structure: threshold is not a number' ) ;
24+ }
25+ if ( ! keys . every ( ( k ) => typeof k === 'string' ) ) {
26+ throw new Error ( 'Invalid multi structure: not all keys are strings' ) ;
27+ }
28+ return [ threshold , keys ] ;
29+ }
30+
1231/**
1332 * @return parsed staking descriptor components or null if the descriptor does not match the expected staking pattern.
1433 */
@@ -59,7 +78,7 @@ export function parseStakingDescriptor(descriptor: Descriptor | ast.DescriptorNo
5978
6079 // Verify unbonding timelock node shape: and_v([pk, older])
6180 const timelockPattern : Pattern = {
62- and_v : [ { 'v:pk' : { $var : 'stakerKey3' } } , { older : { $var : 'unbondingTimeLockValue ' } } ] ,
81+ and_v : [ { 'v:pk' : { $var : 'stakerKey3' } } , { older : { $var : 'stakingTimeLock ' } } ] ,
6382 } ;
6483
6584 const timelockMatch = matcher . match ( timelockNode , timelockPattern ) ;
@@ -76,11 +95,36 @@ export function parseStakingDescriptor(descriptor: Descriptor | ast.DescriptorNo
7695 }
7796
7897 // Verify timelock value is a number
79- if ( typeof timelockMatch . unbondingTimeLockValue !== 'number' ) {
98+ if ( typeof timelockMatch . stakingTimeLock !== 'number' ) {
8099 throw new Error ( 'Unbonding timelock value must be a number' ) ;
81100 }
82101
102+ const stakerKey = Buffer . from ( slashingMatch . stakerKey1 as string , 'hex' ) ;
103+ const stakingTimeLock = timelockMatch . stakingTimeLock as number ;
104+
105+ const [ covenantThreshold , covenantKeyStrings ] = parseMulti ( slashingMatch . covenantMulti ) ;
106+ const covenantKeys = covenantKeyStrings . map ( ( k ) => Buffer . from ( k , 'hex' ) ) ;
107+
108+ let finalityProviderKeys : Buffer [ ] ;
109+ const fpKeyOrMulti = slashingMatch . finalityProviderKeyOrMulti as ast . MiniscriptNode ;
110+ if ( 'v:pk' in fpKeyOrMulti ) {
111+ finalityProviderKeys = [ Buffer . from ( fpKeyOrMulti [ 'v:pk' ] , 'hex' ) ] ;
112+ } else if ( 'v:multi_a' in fpKeyOrMulti ) {
113+ const [ threshold , keyStrings ] = parseMulti ( fpKeyOrMulti [ 'v:multi_a' ] ) ;
114+ if ( threshold !== 1 ) {
115+ throw new Error ( 'Finality provider multi threshold must be 1' ) ;
116+ }
117+ finalityProviderKeys = keyStrings . map ( ( k ) => Buffer . from ( k , 'hex' ) ) ;
118+ } else {
119+ throw new Error ( 'Invalid finality provider key structure' ) ;
120+ }
121+
83122 return {
123+ stakerKey,
124+ finalityProviderKeys,
125+ covenantKeys,
126+ covenantThreshold,
127+ stakingTimeLock,
84128 slashingMiniscriptNode : slashingNode ,
85129 unbondingMiniscriptNode : unbondingNode ,
86130 timelockMiniscriptNode : timelockNode ,
0 commit comments