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 ] ; // Try V2 first, fallback to V1
1722
1823export type KeyServer = {
1924 objectId : string ;
2025 name : string ;
2126 url : string ;
2227 keyType : KeyServerType ;
2328 pk : Uint8Array < ArrayBuffer > ;
29+ serverType : 'Independent' | 'Committee' ;
2430} ;
2531
2632export enum KeyServerType {
@@ -33,16 +39,22 @@ export const SERVER_VERSION_REQUIREMENT = new Version('0.4.1');
3339 * Given a list of key server object IDs, returns a list of SealKeyServer
3440 * from onchain state containing name, objectId, URL and pk.
3541 *
42+ * Supports both V1 (independent servers) and V2 (independent + committee servers).
43+ * For V2 committee servers, returns the aggregator URL from the config.
44+ *
3645 * @param objectIds - The key server object IDs.
3746 * @param client - The SuiClient to use.
47+ * @param configs - The key server configurations containing aggregator URLs.
3848 * @returns - An array of SealKeyServer.
3949 */
4050export async function retrieveKeyServers ( {
4151 objectIds,
4252 client,
53+ configs,
4354} : {
4455 objectIds : string [ ] ;
4556 client : SealCompatibleClient ;
57+ configs : Map < string , KeyServerConfig > ;
4658} ) : Promise < KeyServer [ ] > {
4759 return await Promise . all (
4860 objectIds . map ( async ( objectId ) => {
@@ -51,39 +63,88 @@ export async function retrieveKeyServers({
5163 objectId,
5264 } ) ;
5365 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- ) {
66+
67+ // Find the highest supported version
68+ let version : number | null = null ;
69+ for ( const v of SUPPORTED_SERVER_VERSIONS ) {
70+ if ( Number ( ks . firstVersion ) <= v && Number ( ks . lastVersion ) >= v ) {
71+ version = v ;
72+ break ;
73+ }
74+ }
75+
76+ if ( version === null ) {
5877 throw new InvalidKeyServerVersionError (
59- `Key server ${ objectId } supports versions between ${ ks . firstVersion } and ${ ks . lastVersion } (inclusive), but SDK expects version ${ EXPECTED_SERVER_VERSION } ` ,
78+ `Key server ${ objectId } supports versions between ${ ks . firstVersion } and ${ ks . lastVersion } (inclusive), but SDK expects version 1 or 2 ` ,
6079 ) ;
6180 }
6281
63- // Then fetch the expected versioned object and parse it.
82+ // Fetch the versioned object
6483 const resVersionedKs = await client . core . getDynamicField ( {
6584 parentId : objectId ,
6685 name : {
6786 type : 'u64' ,
68- bcs : bcs . u64 ( ) . serialize ( EXPECTED_SERVER_VERSION ) . toBytes ( ) ,
87+ bcs : bcs . u64 ( ) . serialize ( version ) . toBytes ( ) ,
6988 } ,
7089 } ) ;
7190
72- const ksVersioned = KeyServerMoveV1 . parse ( resVersionedKs . dynamicField . value . bcs ) ;
91+ if ( version === 2 ) {
92+ // Parse V2 key server
93+ const ksV2 = KeyServerMoveV2 . parse ( resVersionedKs . dynamicField . value . bcs ) ;
94+ if ( ksV2 . keyType !== KeyServerType . BonehFranklinBLS12381 ) {
95+ throw new InvalidKeyServerError (
96+ `Server ${ objectId } has invalid key type: ${ ksV2 . keyType } ` ,
97+ ) ;
98+ }
7399
74- if ( ksVersioned . keyType !== KeyServerType . BonehFranklinBLS12381 ) {
75- throw new InvalidKeyServerError (
76- `Server ${ objectId } has invalid key type: ${ ksVersioned . keyType } ` ,
77- ) ;
78- }
100+ // Extract URL and server type
101+ let url : string ;
102+ let serverType : 'Independent' | 'Committee' ;
79103
80- return {
81- objectId,
82- name : ksVersioned . name ,
83- url : ksVersioned . url ,
84- keyType : ksVersioned . keyType ,
85- pk : new Uint8Array ( ksVersioned . pk ) ,
86- } ;
104+ if ( ksV2 . serverType . $kind === 'Independent' ) {
105+ url = ksV2 . serverType . Independent . url ;
106+ serverType = 'Independent' ;
107+ } else if ( ksV2 . serverType . $kind === 'Committee' ) {
108+ // For committee mode, get aggregator URL from config
109+ const config = configs . get ( objectId ) ;
110+ if ( ! config ?. aggregatorUrl ) {
111+ throw new InvalidClientOptionsError (
112+ `Committee server ${ objectId } requires aggregatorUrl in config` ,
113+ ) ;
114+ }
115+ url = config . aggregatorUrl ;
116+ serverType = 'Committee' ;
117+ } else {
118+ throw new InvalidKeyServerError ( `Unknown server type for ${ objectId } ` ) ;
119+ }
120+
121+ return {
122+ objectId,
123+ name : ksV2 . name ,
124+ url,
125+ keyType : ksV2 . keyType ,
126+ pk : new Uint8Array ( ksV2 . pk ) ,
127+ serverType,
128+ } ;
129+ } else {
130+ // Parse V1 key server
131+ const ksV1 = KeyServerMoveV1 . parse ( resVersionedKs . dynamicField . value . bcs ) ;
132+
133+ if ( ksV1 . keyType !== KeyServerType . BonehFranklinBLS12381 ) {
134+ throw new InvalidKeyServerError (
135+ `Server ${ objectId } has invalid key type: ${ ksV1 . keyType } ` ,
136+ ) ;
137+ }
138+
139+ return {
140+ objectId,
141+ name : ksV1 . name ,
142+ url : ksV1 . url ,
143+ keyType : ksV1 . keyType ,
144+ pk : new Uint8Array ( ksV1 . pk ) ,
145+ serverType : 'Independent' ,
146+ } ;
147+ }
87148 } ) ,
88149 ) ;
89150}
0 commit comments