@@ -112,6 +112,35 @@ export class Utils implements BaseUtils {
112112 } ;
113113 }
114114
115+ /**
116+ * Computes the topology hash from the API response of the 'create party' endpoint.
117+ *
118+ * @param topologyTransactions - List of base64-encoded topology transactions from the Canton API.
119+ * @returns The final base64-encoded topology transaction hash.
120+ */
121+ computeHashFromCreatePartyResponse ( topologyTransactions : string [ ] ) : string {
122+ const txBuffers = topologyTransactions . map ( ( tx ) => Buffer . from ( tx , 'base64' ) ) ;
123+ return this . computeHashFromTopologyTransaction ( txBuffers ) ;
124+ }
125+
126+ /**
127+ * Computes the final topology transaction hash for a list of prepared Canton transactions.
128+ *
129+ * Each transaction is first hashed with purpose `11`, then all hashes are combined and
130+ * hashed again with purpose `55`, following the Canton topology hash rules.
131+ *
132+ * The resulting hash is encoded as a base64 string.
133+ *
134+ * @param {Buffer[] } preparedTransactions - An array of Canton transaction buffers.
135+ * @returns {string } The final topology hash, base64-encoded.
136+ */
137+ private computeHashFromTopologyTransaction ( preparedTransactions : Buffer [ ] ) : string {
138+ const rawHashes = preparedTransactions . map ( ( tx ) => this . computeSha256CantonHash ( 11 , tx ) ) ;
139+ const combinedHashes = this . computeMultiHashForTopology ( rawHashes ) ;
140+ const computedHash = this . computeSha256CantonHash ( 55 , combinedHashes ) ;
141+ return Buffer . from ( computedHash , 'hex' ) . toString ( 'base64' ) ;
142+ }
143+
115144 /**
116145 * Converts a base64-encoded Ed25519 public key string into a structured signing public key object.
117146 * @param {String } publicKey The base64-encoded Ed25519 public key
@@ -184,6 +213,43 @@ export class Utils implements BaseUtils {
184213 const bytes = this . fromBase64 ( base64 ) ;
185214 return PreparedTransaction . fromBinary ( bytes ) ;
186215 }
216+
217+ /**
218+ * Computes a deterministic combined hash from an array of individual Canton-style SHA-256 hashes
219+ *
220+ * Each hash is decoded from hex, sorted lexicographically (by hex), and prefixed with its length
221+ * The final buffer includes the number of hashes followed by each (length-prefixed) hash
222+ *
223+ * @param {string[] } hashes - An array of Canton-prefixed SHA-256 hashes in hexadecimal string format
224+ * @returns {Buffer } A binary buffer representing the combined hash input
225+ */
226+ private computeMultiHashForTopology ( hashes : string [ ] ) : Buffer {
227+ const sortedHashes = hashes
228+ . map ( ( hex ) => Buffer . from ( hex , 'hex' ) )
229+ . sort ( ( a , b ) => a . toString ( 'hex' ) . localeCompare ( b . toString ( 'hex' ) ) ) ;
230+
231+ const numHashesBytes = this . encodeInt32 ( sortedHashes . length ) ;
232+ const parts : Buffer [ ] = [ numHashesBytes ] ;
233+
234+ for ( const h of sortedHashes ) {
235+ const lengthBytes = this . encodeInt32 ( h . length ) ;
236+ parts . push ( lengthBytes , h ) ;
237+ }
238+
239+ return Buffer . concat ( parts ) ;
240+ }
241+
242+ /**
243+ * Encodes a 32-bit signed integer into a 4-byte big-endian Buffer
244+ *
245+ * @param {number } value - The integer to encode
246+ * @returns {Buffer } A 4-byte buffer representing the integer in big-endian format
247+ */
248+ private encodeInt32 ( value : number ) : Buffer {
249+ const buf = Buffer . alloc ( 4 ) ;
250+ buf . writeInt32BE ( value , 0 ) ;
251+ return buf ;
252+ }
187253}
188254
189255const utils = new Utils ( ) ;
0 commit comments