Skip to content

Commit 0740117

Browse files
authored
Merge pull request #7175 from BitGo/COIN-5917
feat(sdk-coin-canton): added wallet initialization builder class
2 parents af32d86 + 5a75d7a commit 0740117

File tree

12 files changed

+529
-53
lines changed

12 files changed

+529
-53
lines changed

modules/sdk-coin-canton/src/lib/constant.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ export const SigningKeySpec = {
99
export const SigningAlgorithmSpec = {
1010
ED25519: 1,
1111
};
12+
13+
export const PUBLIC_KEY_FORMAT = 'CRYPTO_KEY_FORMAT_RAW';
14+
export const PUBLIC_KEY_SPEC = 'SIGNING_KEY_SPEC_EC_CURVE25519';

modules/sdk-coin-canton/src/lib/iface.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ export interface PreparedTxnParsedInfo {
1717
amount: string;
1818
}
1919

20-
export interface WalletInitializationDataTxData {
20+
export interface WalletInitTxData {
2121
id: string;
2222
type: TransactionType;
23+
preparedParty: PreparedParty;
2324
}
2425

2526
export interface CantonPrepareCommandResponse {
@@ -30,14 +31,29 @@ export interface CantonPrepareCommandResponse {
3031
}
3132

3233
export interface PreparedParty {
33-
partyTransactions: Uint8Array<ArrayBufferLike>[];
34-
combinedHash: string;
35-
txHashes: Buffer<ArrayBuffer>[];
36-
namespace: string;
3734
partyId: string;
35+
publicKeyFingerprint: string;
36+
topologyTransactions: string[];
37+
multiHash: string;
3838
}
3939

4040
export interface PreparedTransaction {
4141
transaction?: DamlTransaction;
4242
metadata?: Metadata;
4343
}
44+
45+
export interface IPublicKey {
46+
format: string;
47+
keyData: string;
48+
keySpec: string;
49+
}
50+
51+
export interface WalletInitRequest {
52+
synchronizer: string;
53+
partyHint: string;
54+
publicKey: IPublicKey;
55+
localParticipantObservationOnly: boolean;
56+
otherConfirmingParticipantUids: string[];
57+
confirmationThreshold: number;
58+
observingParticipantUids: string[];
59+
}

modules/sdk-coin-canton/src/lib/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as Utils from './utils';
22
import * as Interface from './iface';
33

44
export { KeyPair } from './keyPair';
5-
export { Transaction } from './transaction';
5+
export { Transaction } from './transaction/transaction';
66
export { TransactionBuilder } from './transactionBuilder';
77
export { TransactionBuilderFactory } from './transactionBuilderFactory';
88

modules/sdk-coin-canton/src/lib/transaction.ts renamed to modules/sdk-coin-canton/src/lib/transaction/transaction.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { BaseKey, BaseTransaction, InvalidTransactionError, TransactionType } from '@bitgo/sdk-core';
22
import { BaseCoin as CoinConfig } from '@bitgo/statics';
3-
import { CantonPrepareCommandResponse, PreparedTxnParsedInfo, TxData } from './iface';
4-
import utils from './utils';
3+
import { CantonPrepareCommandResponse, PreparedTxnParsedInfo, TxData } from '../iface';
4+
import utils from '../utils';
55

66
export class Transaction extends BaseTransaction {
77
private _transaction: CantonPrepareCommandResponse;

modules/sdk-coin-canton/src/lib/transactionBuilder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
TransactionType,
1010
} from '@bitgo/sdk-core';
1111
import BigNumber from 'bignumber.js';
12-
import { Transaction } from './transaction';
12+
import { Transaction } from './transaction/transaction';
1313
import utils from './utils';
1414

1515
export abstract class TransactionBuilder extends BaseTransactionBuilder {

modules/sdk-coin-canton/src/lib/utils.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

189255
const utils = new Utils();

0 commit comments

Comments
 (0)