Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/sdk-coin-canton/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules/
.idea/
dist/
.DS_Store
33 changes: 27 additions & 6 deletions modules/sdk-coin-canton/src/canton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import {
ParseTransactionOptions,
SignedTransaction,
SignTransactionOptions,
VerifyAddressOptions,
TransactionType,
TssVerifyAddressOptions,
VerifyTransactionOptions,
} from '@bitgo/sdk-core';
import { auditEddsaPrivateKey } from '@bitgo/sdk-lib-mpc';
import { BaseCoin as StaticsBaseCoin } from '@bitgo/statics';
import { BaseCoin as StaticsBaseCoin, coins } from '@bitgo/statics';
import { TransactionBuilderFactory } from './lib';
import { KeyPair as CantonKeyPair } from './lib/keyPair';
import utils from './lib/utils';

Expand Down Expand Up @@ -70,12 +72,29 @@ export class Canton extends BaseCoin {
}

/** @inheritDoc */
verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
throw new Error('Method not implemented.');
async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
const coinConfig = coins.get(this.getChain());
// extract `txParams` when verifying other transaction types
const { txPrebuild: txPrebuild } = params;
const rawTx = txPrebuild.txHex;
if (!rawTx) {
throw new Error('missing required tx prebuild property txHex');
}
const txBuilder = new TransactionBuilderFactory(coinConfig).from(rawTx);
const transaction = txBuilder.transaction;
switch (transaction.type) {
case TransactionType.WalletInitialization: {
// there is no input for this type of transaction, so always return true
return true;
}
default: {
throw new Error(`unknown transaction type, ${transaction.type}`);
}
}
}

/** @inheritDoc */
isWalletAddress(params: VerifyAddressOptions): Promise<boolean> {
isWalletAddress(params: TssVerifyAddressOptions): Promise<boolean> {
throw new Error('Method not implemented.');
}

Expand Down Expand Up @@ -104,7 +123,9 @@ export class Canton extends BaseCoin {

/** @inheritDoc */
isValidAddress(address: string): boolean {
throw new Error('Method not implemented.');
// canton addresses are of the form, partyHint::fingerprint
// where partyHint is of length 5 and fingerprint is 68 characters long
return utils.isValidAddress(address);
}

/** @inheritDoc */
Expand Down
2 changes: 1 addition & 1 deletion modules/sdk-coin-canton/src/lib/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder {
protected abstract get transactionType(): TransactionType;

/** @inheritdoc */
protected get transaction(): Transaction {
get transaction(): Transaction {
return this._transaction;
}

Expand Down
15 changes: 14 additions & 1 deletion modules/sdk-coin-canton/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import { RecordField } from './resourcesInterface';
export class Utils implements BaseUtils {
/** @inheritdoc */
isValidAddress(address: string): boolean {
throw new Error('Method not implemented.');
if (!address || address.trim() === '') return false;
const [partyHint, fingerprint] = address.trim().split('::');
if (!partyHint || !fingerprint) return false;
return partyHint.length === 5 && this.isValidCantonHex(fingerprint);
}

/** @inheritdoc */
Expand Down Expand Up @@ -40,6 +43,16 @@ export class Utils implements BaseUtils {
throw new Error('Method not implemented.');
}

/**
* Method to validate the input is a valid canton hex string
* @param {String} value the hex string value
* @returns {Boolean} true if valid
*/
isValidCantonHex(value: string): boolean {
const regex = /^[a-fA-F0-9]{68}$/;
return regex.test(value);
}

/**
* Method to create fingerprint (part of the canton partyId) from public key
* @param {String} publicKey the public key
Expand Down
10 changes: 10 additions & 0 deletions modules/sdk-coin-canton/test/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,13 @@ export const InvalidOneStepPreApprovalPrepareResponse = {
hashingSchemeVersion: 'HASHING_SCHEME_VERSION_V2',
hashingDetails: null,
};

export const CANTON_ADDRESSES = {
VALID_ADDRESS: '12205::12205b4e3537a95126d90604592344d8ad3c3ddccda4f79901954280ee19c576714d',
// party hint is not 5 characters
INVALID_PARTY_HINT: '123456::12205b4e3537a95126d90604592344d8ad3c3ddccda4f79901954280ee19c576714d',
// fingerprint is not a valid hex value
INVALID_FINGERPRINT: '12205::12205b4e3537a95126d9060459234gd8ad3c3ddccda4f79901954280ee19c576714d',
MISSING_PARTY_HINT: '::12205b4e3537a95126d9060459234gd8ad3c3ddccda4f79901954280ee19c576714d',
MISSING_FINGERPRINT: '12205::',
};
39 changes: 38 additions & 1 deletion modules/sdk-coin-canton/test/unit/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import assert from 'assert';
import should from 'should';
import utils from '../../src/lib/utils';
import { GenerateTopologyResponse, PreparedTransactionRawData, PrepareSubmissionResponse } from '../resources';
import {
CANTON_ADDRESSES,
GenerateTopologyResponse,
PreparedTransactionRawData,
PrepareSubmissionResponse,
} from '../resources';

describe('Canton Util', function () {
describe('Raw transaction parser', function () {
Expand Down Expand Up @@ -31,4 +36,36 @@ describe('Canton Util', function () {
assert.strictEqual(computedHash, PrepareSubmissionResponse.preparedTransactionHash);
});
});

describe('Check if the address is valid', function () {
it('should return true when the address is valid', function () {
const isValid = utils.isValidAddress(CANTON_ADDRESSES.VALID_ADDRESS);
should.exist(isValid);
assert.strictEqual(isValid, true);
});

it('should return false when party hint is invalid', function () {
const isValid = utils.isValidAddress(CANTON_ADDRESSES.INVALID_PARTY_HINT);
should.exist(isValid);
assert.strictEqual(isValid, false);
});

it('should return false when fingerprint is invalid', function () {
const isValid = utils.isValidAddress(CANTON_ADDRESSES.INVALID_FINGERPRINT);
should.exist(isValid);
assert.strictEqual(isValid, false);
});

it('should return false when party hint is missing', function () {
const isValid = utils.isValidAddress(CANTON_ADDRESSES.MISSING_PARTY_HINT);
should.exist(isValid);
assert.strictEqual(isValid, false);
});

it('should return false when fingerprint is missing', function () {
const isValid = utils.isValidAddress(CANTON_ADDRESSES.MISSING_FINGERPRINT);
should.exist(isValid);
assert.strictEqual(isValid, false);
});
});
});