33import { bcs , fromBase64 , fromHex , toBase64 , toHex } from '@mysten/bcs' ;
44import { bls12_381 } from '@noble/curves/bls12-381' ;
55
6- import { KeyServerMove , KeyServerMoveV1 } from './bcs.js' ;
7- import { InvalidKeyServerError , InvalidKeyServerVersionError , SealAPIError } from './error.js' ;
6+ import { KeyServerMove , KeyServerMoveV1 , KeyServerMoveV2 } from './bcs.js' ;
7+ import {
8+ InvalidClientOptionsError ,
9+ InvalidKeyServerError ,
10+ InvalidKeyServerVersionError ,
11+ SealAPIError ,
12+ } from './error.js' ;
813import { DST_POP } from './ibe.js' ;
914import { PACKAGE_VERSION } from './version.js' ;
10- import type { SealCompatibleClient } from './types.js' ;
15+ import type { KeyServerConfig , SealCompatibleClient } from './types.js' ;
1116import type { G1Element } from './bls12381.js' ;
1217import { flatten , Version } from './utils.js' ;
1318import { elgamalDecrypt } from './elgamal.js' ;
1419import type { Certificate } from './session-key.js' ;
1520
16- const EXPECTED_SERVER_VERSION = 1 ;
21+ const SUPPORTED_SERVER_VERSIONS = [ 2 , 1 ] ; // Must be configured in descending order.
22+
23+ export type ServerType = 'Independent' | 'Committee' ;
1724
1825export type KeyServer = {
1926 objectId : string ;
2027 name : string ;
2128 url : string ;
22- keyType : KeyServerType ;
29+ keyType : KeyType ;
2330 pk : Uint8Array < ArrayBuffer > ;
31+ serverType : ServerType ;
2432} ;
2533
26- export enum KeyServerType {
34+ export enum KeyType {
2735 BonehFranklinBLS12381 = 0 ,
2836}
2937
@@ -33,16 +41,22 @@ export const SERVER_VERSION_REQUIREMENT = new Version('0.4.1');
3341 * Given a list of key server object IDs, returns a list of SealKeyServer
3442 * from onchain state containing name, objectId, URL and pk.
3543 *
44+ * Supports both V1 (independent servers) and V2 (independent + committee servers).
45+ * For V2 committee servers, returns the aggregator URL from the config.
46+ *
3647 * @param objectIds - The key server object IDs.
3748 * @param client - The SuiClient to use.
49+ * @param configs - The key server configurations containing aggregator URLs.
3850 * @returns - An array of SealKeyServer.
3951 */
4052export async function retrieveKeyServers ( {
4153 objectIds,
4254 client,
55+ configs,
4356} : {
4457 objectIds : string [ ] ;
4558 client : SealCompatibleClient ;
59+ configs : Map < string , KeyServerConfig > ;
4660} ) : Promise < KeyServer [ ] > {
4761 return await Promise . all (
4862 objectIds . map ( async ( objectId ) => {
@@ -51,39 +65,102 @@ export async function retrieveKeyServers({
5165 objectId,
5266 } ) ;
5367 const ks = KeyServerMove . parse ( await res . object . content ) ;
54- if (
55- EXPECTED_SERVER_VERSION < Number ( ks . firstVersion ) ||
56- EXPECTED_SERVER_VERSION > Number ( ks . lastVersion )
57- ) {
68+
69+ // Find the highest supported version.
70+ const firstVersion = Number ( ks . firstVersion ) ;
71+ const lastVersion = Number ( ks . lastVersion ) ;
72+ const version = SUPPORTED_SERVER_VERSIONS . find ( ( v ) => v >= firstVersion && v <= lastVersion ) ;
73+
74+ if ( version === undefined ) {
5875 throw new InvalidKeyServerVersionError (
59- `Key server ${ objectId } supports versions between ${ ks . firstVersion } and ${ ks . lastVersion } (inclusive), but SDK expects version ${ EXPECTED_SERVER_VERSION } ` ,
76+ `Key server ${ objectId } supports versions between ${ ks . firstVersion } and ${ ks . lastVersion } (inclusive), but SDK expects one of ${ SUPPORTED_SERVER_VERSIONS . join ( ', ' ) } ` ,
6077 ) ;
6178 }
6279
63- // Then fetch the expected versioned object and parse it .
64- const resVersionedKs = await client . core . getDynamicField ( {
80+ // Fetch the versioned object.
81+ const versionedKeyServer = await client . core . getDynamicField ( {
6582 parentId : objectId ,
6683 name : {
6784 type : 'u64' ,
68- bcs : bcs . u64 ( ) . serialize ( EXPECTED_SERVER_VERSION ) . toBytes ( ) ,
85+ bcs : bcs . u64 ( ) . serialize ( version ) . toBytes ( ) ,
6986 } ,
7087 } ) ;
7188
72- const ksVersioned = KeyServerMoveV1 . parse ( resVersionedKs . dynamicField . value . bcs ) ;
89+ // Parse based on version.
90+ switch ( version ) {
91+ case 2 : {
92+ const ksV2 = KeyServerMoveV2 . parse ( versionedKeyServer . dynamicField . value . bcs ) ;
93+ if ( ksV2 . keyType !== KeyType . BonehFranklinBLS12381 ) {
94+ throw new InvalidKeyServerError (
95+ `Server ${ objectId } has invalid key type: ${ ksV2 . keyType } ` ,
96+ ) ;
97+ }
7398
74- if ( ksVersioned . keyType !== KeyServerType . BonehFranklinBLS12381 ) {
75- throw new InvalidKeyServerError (
76- `Server ${ objectId } has invalid key type: ${ ksVersioned . keyType } ` ,
77- ) ;
78- }
99+ // Return based on server type.
100+ switch ( ksV2 . serverType . $kind ) {
101+ case 'Independent' : {
102+ if ( configs . get ( objectId ) ?. aggregatorUrl ) {
103+ throw new InvalidClientOptionsError (
104+ `Independent server ${ objectId } should not have aggregatorUrl in config` ,
105+ ) ;
106+ }
107+ return {
108+ objectId,
109+ name : ksV2 . name ,
110+ url : ksV2 . serverType . Independent . url ,
111+ keyType : ksV2 . keyType ,
112+ pk : new Uint8Array ( ksV2 . pk ) ,
113+ serverType : 'Independent' ,
114+ } ;
115+ }
116+ case 'Committee' : {
117+ // For committee mode, get aggregator URL from config
118+ const config = configs . get ( objectId ) ;
119+ if ( ! config ?. aggregatorUrl ) {
120+ throw new InvalidClientOptionsError (
121+ `Committee server ${ objectId } requires aggregatorUrl in config` ,
122+ ) ;
123+ }
124+ return {
125+ objectId,
126+ name : ksV2 . name ,
127+ url : config . aggregatorUrl ,
128+ keyType : ksV2 . keyType ,
129+ pk : new Uint8Array ( ksV2 . pk ) ,
130+ serverType : 'Committee' ,
131+ } ;
132+ }
133+ default :
134+ throw new InvalidKeyServerError ( `Unknown server type for ${ objectId } ` ) ;
135+ }
136+ }
137+ case 1 : {
138+ const ksV1 = KeyServerMoveV1 . parse ( versionedKeyServer . dynamicField . value . bcs ) ;
139+ if ( ksV1 . keyType !== KeyType . BonehFranklinBLS12381 ) {
140+ throw new InvalidKeyServerError (
141+ `Server ${ objectId } has invalid key type: ${ ksV1 . keyType } ` ,
142+ ) ;
143+ }
79144
80- return {
81- objectId,
82- name : ksVersioned . name ,
83- url : ksVersioned . url ,
84- keyType : ksVersioned . keyType ,
85- pk : new Uint8Array ( ksVersioned . pk ) ,
86- } ;
145+ // V1 servers are always Independent and should not have aggregatorUrl
146+ if ( configs . get ( objectId ) ?. aggregatorUrl ) {
147+ throw new InvalidClientOptionsError (
148+ `V1 server ${ objectId } is always Independent and should not have aggregatorUrl in config` ,
149+ ) ;
150+ }
151+
152+ return {
153+ objectId,
154+ name : ksV1 . name ,
155+ url : ksV1 . url ,
156+ keyType : ksV1 . keyType ,
157+ pk : new Uint8Array ( ksV1 . pk ) ,
158+ serverType : 'Independent' ,
159+ } ;
160+ }
161+ default :
162+ throw new InvalidKeyServerVersionError ( `Unsupported key server version: ${ version } ` ) ;
163+ }
87164 } ) ,
88165 ) ;
89166}
0 commit comments