diff --git a/package.json b/package.json index e229ce6..990f097 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@a-block/a-blockjs", - "version": "2.0.0", + "version": "2.0.1", "description": "API wrapper to access the ABlock network", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/src/interfaces/error.interfaces.ts b/src/interfaces/error.interfaces.ts index f1fa7b9..5abaabc 100644 --- a/src/interfaces/error.interfaces.ts +++ b/src/interfaces/error.interfaces.ts @@ -92,6 +92,7 @@ export enum IErrorInternal { UnableToFindNonEmptyAddresses = 'Unable to find addresses that contain assets', InvalidNetworkResponse = 'Invalid network response', UnableToSignMessage = 'Unable to sign message', + UnableToVerifyMessage = 'Unable to verify message', NoHostsProvided = 'No hosts provided', NoKeypairsProvided = 'No key-pairs provided', NoPassPhraseProvided = 'No passphrase provided', diff --git a/src/interfaces/success.interfaces.ts b/src/interfaces/success.interfaces.ts index b65839f..da3ea5b 100644 --- a/src/interfaces/success.interfaces.ts +++ b/src/interfaces/success.interfaces.ts @@ -30,7 +30,8 @@ export enum ISuccessNotary { export enum ISuccessInternal { ClientInitialized = 'Client initialized', - MessageSigned = 'Message successfully signed', + MessageSigned = 'Successfully signed message', + MessageVirified = 'Successfully verified message', RbPaymentProcessing = 'Receipt-based payment processing', PendingRbPaymentsFetched = 'Succesfully fetched pending receipt-based transactions', AddressesReconstructed = 'Addresses have successfully been reconstructed', diff --git a/src/services/ablock.service.ts b/src/services/ablock.service.ts index 6c379fd..c6bbce4 100644 --- a/src/services/ablock.service.ts +++ b/src/services/ablock.service.ts @@ -107,10 +107,9 @@ export class ABlockWallet { /** * Initialize an instance of the client with a provided master key * - * @param {IClientConfig} config - Additional configuration parameters * @param {IMasterKeyEncrypted} masterKey - Master key + * @param {IClientConfig} config - Additional configuration parameters * @param initOffline - Optionally initialize the client without initializing network settings - * * @return {*} {IClientResponse} * @memberof ABlockWallet */ @@ -144,11 +143,10 @@ export class ABlockWallet { /** * Initialize an instance of the wallet with a provided seed phrase * - * @param {IClientConfig} config - Additional configuration parameters * @param {string} seedPhrase - Seed phrase + * @param {IClientConfig} config - Additional configuration parameters + * @param initOffline - Optionally initialize the client without initializing network settings * @return {*} {IClientResponse} - * @param initOffline - Optionally initialize the client without initializing network settings - * * @memberof ABlockWallet */ public async fromSeed( @@ -157,7 +155,7 @@ export class ABlockWallet { initOffline = false, ): Promise { this.keyMgmt = new mgmtClient(); - const initIResult = this.keyMgmt.fromSeed(config.passphrase, seedPhrase); + const initIResult = this.keyMgmt.fromSeed(seedPhrase, config.passphrase); if (!initOffline) { const initNetworkIResult = await this.initNetwork(config); if (initNetworkIResult.status === 'error') { @@ -184,7 +182,7 @@ export class ABlockWallet { * Common network initialization (retrieval of PoW list for compute and storage) * * @param {IClientConfig} config - Configuration parameters - * + * @return {*} {IClientResponse} * @memberof ABlockWallet */ public async initNetwork(config: IClientConfig): Promise { @@ -239,6 +237,7 @@ export class ABlockWallet { * * @private * @param {IClientConfig} config - Additional configuration parameters + * @return {*} {IClientResponse} * @memberof ABlockWallet */ private async initNetworkForHost( @@ -601,10 +600,10 @@ export class ABlockWallet { * @return {*} {Promise} * @memberof ABlockWallet */ - async signMessage( + signMessage( keyPairsToSignWith: IKeypairEncrypted[], message: string, - ): Promise { + ): IClientResponse { try { if (this.keyMgmt === undefined) throw new Error(IErrorInternal.ClientNotInitialized); const keyPairs = throwIfErr(this.keyMgmt.decryptKeypairs(keyPairsToSignWith)); @@ -624,6 +623,28 @@ export class ABlockWallet { } } + verifyMessage( + message: string, + signatures: IGenericKeyPair, + keyPairs: IKeypairEncrypted[], + ): IClientResponse { + try { + if (this.keyMgmt === undefined) throw new Error(IErrorInternal.ClientNotInitialized); + const keyPairsUnencrypted = throwIfErr(this.keyMgmt.decryptKeypairs(keyPairs)); + throwIfErr(this.keyMgmt.verifyMessage(message, signatures, keyPairsUnencrypted)); + return { + status: 'success', + reason: ISuccessInternal.MessageVirified, + } as IClientResponse; + } catch (error) { + return { + status: 'error', + reason: `${error}`, + } as IClientResponse; + } + } + + /** * Make a payment of a specified token amount to a payment address * diff --git a/src/services/mgmt.service.ts b/src/services/mgmt.service.ts index 44bf608..0a73842 100644 --- a/src/services/mgmt.service.ts +++ b/src/services/mgmt.service.ts @@ -30,7 +30,7 @@ import { TEMP_ADDRESS_VERSION, } from '../mgmt'; import { concatTypedArrays, getBytesString, getStringBytes, truncateByBytesUTF8 } from '../utils'; -import { getBytesHexString } from '../utils/general.utils'; +import { getBytesHexString, getHexStringBytes } from '../utils/general.utils'; export class mgmtClient { private passphraseKey: Uint8Array; @@ -80,8 +80,8 @@ export class mgmtClient { /** * Init the client with a provided master key * - * @param {string} passphraseKey * @param {IMasterKeyEncrypted} masterKey + * @param {string} passphraseKey * @return {*} {IResult} * @memberof mgmtClient */ @@ -99,8 +99,8 @@ export class mgmtClient { /** * Init the client with a provided seed phrase * - * @param {string} passphraseKey * @param {string} seedPhrase + * @param {string} passphraseKey * @return {*} {IResult} * @memberof mgmtClient */ @@ -481,16 +481,27 @@ export class mgmtClient { * @memberof mgmtClient */ public signMessage(keypairs: IKeypair[], message: string): IResult> { - try { - const signatures: IGenericKeyPair = {}; - for (const keypair of keypairs) { - const signature = nacl.sign.detached(Buffer.from(message), keypair.secretKey); - signatures[getBytesHexString(keypair.publicKey)] = getBytesHexString(signature); - } - return ok(signatures); - } catch { - return err(IErrorInternal.UnableToSignMessage); + if (keypairs.length < 1) + return err(IErrorInternal.InvalidInputs); + const signatures: IGenericKeyPair = {}; + for (const keypair of keypairs) { + const signature = nacl.sign.detached(Buffer.from(message), keypair.secretKey); + signatures[getBytesHexString(keypair.publicKey)] = getBytesHexString(signature); + } + if (Object.keys(signatures).length < 1) return err(IErrorInternal.UnableToSignMessage); + return ok(signatures); + } + + public verifyMessage(message: string, signatures: IGenericKeyPair, keypairs: IKeypair[]): IResult { + console.log(keypairs.length, Object.keys(signatures).length) + if (keypairs.length < 1 || Object.keys(signatures).length != keypairs.length) + return err(IErrorInternal.InvalidInputs); + for (const keypair of keypairs) { + const sig = signatures[getBytesHexString(keypair.publicKey)] + if (!sig || !nacl.sign.detached.verify(Buffer.from(message), getHexStringBytes(sig), keypair.publicKey)) + return err(IErrorInternal.UnableToVerifyMessage); } + return ok(true); } /** diff --git a/src/tests/__tests__/ablock.service.test.ts b/src/tests/__tests__/ablock.service.test.ts index f899b83..1bacdf7 100644 --- a/src/tests/__tests__/ablock.service.test.ts +++ b/src/tests/__tests__/ablock.service.test.ts @@ -33,7 +33,6 @@ test('init wallet without optional config fields', async () => { test('init wallet locally and then connect', async () => { await ablockInstance.initNew({ passphrase: '' }, true).then((res) => { - console.log(res) expect(res.status).toBe('success'); }); @@ -44,7 +43,6 @@ test('init wallet locally and then connect', async () => { }; await ablockInstance.initNetwork(config).then((res) => { - console.log(res) expect(res.status).toBe('success'); }); }); @@ -79,3 +77,61 @@ test('handles key-pair re-generation from wallet seed phrase', async () => { ), ).toEqual(utxoAddressList); }); + +test('sign message with given keypairs', async () => { + const config = { + mempoolHost: 'http://49.12.234.10:3003', + passphrase: '', + }; + const MSG = 'hello, world'; + + await ablockInstance.initNew(config).then((res) => { + expect(res.status).toBe('success'); + }); + + const kp = ablockInstance.getNewKeypair([]).content?.newKeypairResponse; + const kpAddr = kp?.address; + + expect(kp).toBeDefined(); + expect(kpAddr).toBeDefined(); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const kp1 = ablockInstance.getNewKeypair([kpAddr!]).content?.newKeypairResponse; + + expect(kp1).toBeDefined(); + + const keypairs = [kp!, kp1!]; + + expect(keypairs).toBeDefined(); + + const signatures = ablockInstance.signMessage(keypairs, MSG).content?.signMessageResponse; + + expect(signatures).toBeDefined(); + + const result = ablockInstance.verifyMessage(MSG, signatures!, keypairs) + + expect(result.status).toBe('success'); + + const kp2 = ablockInstance.getNewKeypair([kpAddr!, kp1!.address]).content?.newKeypairResponse; + + expect(kp2).toBeDefined(); + + const keypairs1 = [kp!, kp2!]; + + const result1 = ablockInstance.verifyMessage(MSG, signatures!, keypairs1) + console.log('RESULT 1: ', result1) + expect(result1.status).toBe('error'); + + // if (kp.content?.newKeypairResponse && kp1?.content?.newKeypairResponse) + // keypairs = [kp.content.newKeypairResponse, kp1.content.newKeypairResponse]; + + // if (keypairs) { + // const signatures = await ablockInstance.signMessage(keypairs, MSG); + // ยจ const result = await ablockInstance.verifyMessage(MSG, signatures.content?.signMessageResponse, keypairs); + // console.log(result) + // } + + // const kp = ablockInstance.getNewKeypair([]); + + // await ablockInstance.signMessage(,) +});