diff --git a/src/account.ts b/src/account.ts index a88a426..1792870 100644 --- a/src/account.ts +++ b/src/account.ts @@ -13,7 +13,7 @@ import { ethToEthermint } from '@tharsis/address-converter'; import { encodeSecp256k1Pubkey } from '@cosmjs/amino'; import { DirectSecp256k1Wallet } from '@cosmjs/proto-signing'; -import { Payload, Signature } from './types'; +import { Payload, Record as RegistryRecord, Signature } from './types'; const AMINO_PREFIX = 'EB5AE98721'; const HDPATH = "m/44'/60'/0'/0"; @@ -42,6 +42,7 @@ export class Account { _ethAddress!: string; _wallet!: DirectSecp256k1Wallet; _address!: string; + _publicKeyLaconic2!: string; /** * Generate bip39 mnemonic. @@ -97,6 +98,10 @@ export class Account { return this._address; } + get publicKeyLaconic2 () { + return this._publicKeyLaconic2; + } + get wallet () { return this._wallet; } @@ -107,7 +112,9 @@ export class Account { ACCOUNT_PREFIX ); - this._address = (await this._wallet.getAccounts())[0].address; + const [account] = await this._wallet.getAccounts(); + this._address = account.address; + this._publicKeyLaconic2 = toHex(account.pubkey); // Generate public key. this._publicKey = secp256k1.publicKeyCreate(this._privateKey); @@ -166,7 +173,7 @@ export class Account { assert(payload); const { record } = payload; - const messageToSign = record.getMessageToSign(); + const messageToSign = RegistryRecord.getMessageToSign(record); const sig = await this.signRecord(messageToSign); assert(this.registryPublicKey); diff --git a/src/index.ts b/src/index.ts index e7399ed..ef79361 100644 --- a/src/index.ts +++ b/src/index.ts @@ -187,14 +187,20 @@ export class Registry { * @param transactionPrivateKey - private key in HEX to sign transaction. */ async setRecord ( - params: { privateKey: string, record: any, bondId: string }, + { privateKey, record, bondId }: { privateKey: string, record: any, bondId: string }, transactionPrivateKey: string, - fee: Fee + fee: StdFee ) { - let result; - result = await this._submitRecordTx(params, transactionPrivateKey, fee); + const account = new Account(Buffer.from(transactionPrivateKey, 'hex')); + await account.init(); + const laconicClient = await this.getLaconicClient(account); - return parseTxResponse(result, parseMsgSetRecordResponse); + const response: DeliverTxResponse = await laconicClient.setRecord({ privateKey, record, bondId }, + account.address, + fee + ); + + return response; } /** diff --git a/src/laconic-client.ts b/src/laconic-client.ts index 1f2a854..6995217 100644 --- a/src/laconic-client.ts +++ b/src/laconic-client.ts @@ -11,8 +11,12 @@ import { Comet38Client } from '@cosmjs/tendermint-rpc'; import { MsgCancelBondEncodeObject, MsgCreateBondEncodeObject, MsgRefillBondEncodeObject, MsgWithdrawBondEncodeObject, bondTypes, typeUrlMsgCancelBond, typeUrlMsgCreateBond, typeUrlMsgRefillBond, typeUrlMsgWithdrawBond } from './types/cerc/bond/message'; import { Coin } from './proto2/cosmos/base/v1beta1/coin'; -import { MsgReserveAuthorityEncodeObject, MsgSetAuthorityBondEncodeObject, registryTypes, typeUrlMsgReserveAuthority, typeUrlMsgSetAuthorityBond } from './types/cerc/registry/message'; +import { MsgReserveAuthorityEncodeObject, MsgSetAuthorityBondEncodeObject, MsgSetRecordEncodeObject, registryTypes, typeUrlMsgReserveAuthority, typeUrlMsgSetAuthorityBond, typeUrlMsgSetRecord } from './types/cerc/registry/message'; import { MsgCommitBidEncodeObject, MsgRevealBidEncodeObject, auctionTypes, typeUrlMsgCommitBid, typeUrlMsgRevealBid } from './types/cerc/auction/message'; +import { Payload } from './proto2/cerc/registry/v1/tx'; +import { Record, Signature } from './proto2/cerc/registry/v1/registry'; +import { Account } from './account'; +import { Record as RegistryRecord } from './types'; export const laconicDefaultRegistryTypes: ReadonlyArray<[string, GeneratedType]> = [ ...defaultRegistryTypes, @@ -185,6 +189,36 @@ export class LaconicClient extends SigningStargateClient { return this.signAndBroadcast(signer, [createMsg], fee, memo); } + public async setRecord ( + params: { privateKey: string, record: any, bondId: string }, + signer: string, + fee: StdFee | 'auto' | number, + memo = '' + ): Promise { + const registryRecord = Record.fromPartial({ attributes: Buffer.from(JSON.stringify(params.record), 'binary') }); + + // Sign record. + const recordSignerAccount = new Account(Buffer.from(params.privateKey, 'hex')); + await recordSignerAccount.init(); + const messageToSign = RegistryRecord.getMessageToSign(params.record); + const sig = await recordSignerAccount.signRecord(messageToSign); + + const signature = Signature.fromJSON({ sig: sig.toString('base64'), pubKey: recordSignerAccount.publicKeyLaconic2 }); + + const payload = Payload.fromJSON({ record: registryRecord, signatures: [signature] }); + + const createMsg: MsgSetRecordEncodeObject = { + typeUrl: typeUrlMsgSetRecord, + value: { + signer, + bondId: params.bondId, + payload + } + }; + + return this.signAndBroadcast(signer, [createMsg], fee, memo); + } + public async setAuthorityBond ( signer: string, bondId: string, diff --git a/src/sdk.test.ts b/src/sdk.test.ts index 59fab04..67dbfb3 100644 --- a/src/sdk.test.ts +++ b/src/sdk.test.ts @@ -23,13 +23,13 @@ describe('Querying', () => { await registry.createBond({ denom: DENOM, amount: '1000000000' }, privateKey, laconic2Fee); // TODO: Implement set record - // const publishNewWatcherVersion = async () => { - // watcher = await ensureUpdatedConfig(WATCHER_YML_PATH); - // await registry.setRecord({ privateKey, record: watcher.record, bondId }, privateKey, fee); - // return watcher.record.version; - // }; + const publishNewWatcherVersion = async () => { + watcher = await ensureUpdatedConfig(WATCHER_YML_PATH); + await registry.setRecord({ privateKey, record: watcher.record, bondId }, privateKey, laconic2Fee); + return watcher.record.version; + }; - // await publishNewWatcherVersion(); + await publishNewWatcherVersion(); }); test('Endpoint and chain ID.', async () => { @@ -45,7 +45,7 @@ describe('Querying', () => { expect(status.version).toBeDefined(); }); - xtest('List records.', async () => { + test('List records.', async () => { const records = await registry.queryRecords({}, true); expect(records.length).toBeGreaterThanOrEqual(1); }); diff --git a/src/types.ts b/src/types.ts index c2295d4..7f4bd25 100644 --- a/src/types.ts +++ b/src/types.ts @@ -48,8 +48,10 @@ export class Record { /** * Get message to calculate record signature. */ - getMessageToSign () { - return Util.sortJSON(this._record); + + // TODO: Replace any type for record + static getMessageToSign (record: any) { + return Util.sortJSON(record); } } diff --git a/src/types/cerc/registry/message.ts b/src/types/cerc/registry/message.ts index effdb74..6488ad7 100644 --- a/src/types/cerc/registry/message.ts +++ b/src/types/cerc/registry/message.ts @@ -1,15 +1,19 @@ import { EncodeObject, GeneratedType } from '@cosmjs/proto-signing'; -import { MsgReserveAuthority, MsgReserveAuthorityResponse, MsgSetAuthorityBond, MsgSetAuthorityBondResponse } from '../../../proto2/cerc/registry/v1/tx'; +import { MsgReserveAuthority, MsgReserveAuthorityResponse, MsgSetAuthorityBond, MsgSetAuthorityBondResponse, MsgSetRecord, MsgSetRecordResponse } from '../../../proto2/cerc/registry/v1/tx'; export const typeUrlMsgReserveAuthority = '/cerc.registry.v1.MsgReserveAuthority'; +export const typeUrlMsgSetRecord = '/cerc.registry.v1.MsgSetRecord'; export const typeUrlMsgSetAuthorityBond = '/cerc.registry.v1.MsgSetAuthorityBond'; export const typeUrlMsgReserveAuthorityResponse = '/cerc.registry.v1.MsgReserveAuthorityResponse'; +export const typeUrlMsgSetRecordResponse = '/cerc.registry.v1.MsgSetRecordResponse'; export const typeUrlMsgSetAuthorityBondResponse = '/cerc.registry.v1.MsgSetAuthorityBondResponse'; export const registryTypes: ReadonlyArray<[string, GeneratedType]> = [ [typeUrlMsgReserveAuthority, MsgReserveAuthority], [typeUrlMsgReserveAuthorityResponse, MsgReserveAuthorityResponse], + [typeUrlMsgSetRecord, MsgSetRecord], + [typeUrlMsgSetRecordResponse, MsgSetRecordResponse], [typeUrlMsgSetAuthorityBond, MsgSetAuthorityBond], [typeUrlMsgSetAuthorityBondResponse, MsgSetAuthorityBondResponse] ]; @@ -19,6 +23,11 @@ export interface MsgReserveAuthorityEncodeObject extends EncodeObject { readonly value: Partial; } +export interface MsgSetRecordEncodeObject extends EncodeObject { + readonly typeUrl: '/cerc.registry.v1.MsgSetRecord'; + readonly value: Partial; +} + export interface MsgSetAuthorityBondEncodeObject extends EncodeObject { readonly typeUrl: '/cerc.registry.v1.MsgSetAuthorityBond'; readonly value: Partial;