Update authority method for registry module to use cosmjs (#4)

* Update methods to send coins and reserve authority

* Make owner as mandatory param of reserve authority method

* Update method to set authority bond

* Add todo for skipping tests

* Decode message response

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
Nabarun Gogoi 2024-03-07 16:06:52 +05:30 committed by GitHub
parent de0ac597a1
commit 2589aacdb4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 155 additions and 69 deletions

View File

@ -1,4 +1,4 @@
# laconic-sdk # registry-sdk
Client library used by TS/JS applications to communicate with laconicd. Client library used by TS/JS applications to communicate with laconicd.
@ -26,7 +26,7 @@ Follow these steps to run the tests:
- Copy the private key and assign it to variable `PRIVATE_KEY` in the `.env` file. - Copy the private key and assign it to variable `PRIVATE_KEY` in the `.env` file.
- Run the tests in laconic-sdk repo: - Run the tests in registry-sdk repo:
```bash ```bash
yarn test yarn test

View File

@ -1,7 +1,9 @@
import { Registry, Account, createBid } from './index'; import { Registry, Account, createBid } from './index';
import { getConfig } from './testing/helper'; import { getConfig, getLaconic2Config } from './testing/helper';
import { DENOM } from './constants';
jest.setTimeout(30 * 60 * 1000); jest.setTimeout(30 * 60 * 1000);
const { fee: laconic2Fee } = getLaconic2Config();
const { chainId, restEndpoint, gqlEndpoint, privateKey, fee } = getConfig(); const { chainId, restEndpoint, gqlEndpoint, privateKey, fee } = getConfig();
@ -23,15 +25,15 @@ const auctionTests = (numBidders = 3) => {
for (let i = 0; i < numBidders; i++) { for (let i = 0; i < numBidders; i++) {
const mnenonic = Account.generateMnemonic(); const mnenonic = Account.generateMnemonic();
const account = await Account.generateFromMnemonic(mnenonic); const account = await Account.generateFromMnemonic(mnenonic);
const bidderAddress = account.formattedCosmosAddress; await account.init();
await registry.sendCoins({ denom: 'aphoton', amount: '1000000000', destinationAddress: bidderAddress }, privateKey, fee); await registry.sendCoins({ denom: DENOM, amount: '100000', destinationAddress: account.address }, privateKey, laconic2Fee);
accounts.push({ address: bidderAddress, privateKey: account.privateKey.toString('hex') }); accounts.push({ address: account.address, privateKey: account.privateKey.toString('hex') });
} }
}); });
test('Reserve authority.', async () => { test('Reserve authority.', async () => {
authorityName = `laconic-${Date.now()}`; authorityName = `laconic-${Date.now()}`;
await registry.reserveAuthority({ name: authorityName }, accounts[0].privateKey, fee); await registry.reserveAuthority({ name: authorityName }, accounts[0].privateKey, laconic2Fee);
}); });
test('Authority should be under auction.', async () => { test('Authority should be under auction.', async () => {

View File

@ -95,7 +95,8 @@ const bondTests = () => {
}); });
}); });
test('Associate/Dissociate bond.', async () => { // TODO: Implement set record
xtest('Associate/Dissociate bond.', async () => {
let bondId1: string; let bondId1: string;
bondId1 = await registry.getNextBondId(privateKey); bondId1 = await registry.getNextBondId(privateKey);
@ -119,7 +120,8 @@ const bondTests = () => {
expect(record1.bondId).toBe(bondId1); expect(record1.bondId).toBe(bondId1);
}); });
test('Reassociate/Dissociate records.', async () => { // TODO: Implement set record
xtest('Reassociate/Dissociate records.', async () => {
let bondId1: string; let bondId1: string;
let bondId2: string; let bondId2: string;

View File

@ -54,6 +54,7 @@ import {
} from './messages/auction'; } from './messages/auction';
import { LaconicClient } from './laconic-client'; import { LaconicClient } from './laconic-client';
import { MsgCancelBondResponse, MsgCreateBondResponse, MsgRefillBondResponse, MsgWithdrawBondResponse } from './proto2/cerc/bond/v1/tx'; import { MsgCancelBondResponse, MsgCreateBondResponse, MsgRefillBondResponse, MsgWithdrawBondResponse } from './proto2/cerc/bond/v1/tx';
import { Coin } from './proto2/cosmos/base/v1beta1/coin';
export const DEFAULT_CHAIN_ID = 'laconic_9000-1'; export const DEFAULT_CHAIN_ID = 'laconic_9000-1';
@ -199,15 +200,23 @@ export class Registry {
/** /**
* Send coins. * Send coins.
*/ */
async sendCoins (params: MessageSendParams, privateKey: string, fee: Fee) { async sendCoins ({ amount, denom, destinationAddress }: MessageSendParams, privateKey: string, fee: StdFee) {
let result;
const account = new Account(Buffer.from(privateKey, 'hex')); const account = new Account(Buffer.from(privateKey, 'hex'));
const sender = await this._getSender(account); await account.init();
const laconicClient = await this.getLaconicClient(account);
const msg = createMessageSend(this._chain, sender, fee, '', params); const response: DeliverTxResponse = await laconicClient.sendTokens(
result = await this._submitTx(msg, privateKey, sender); account.address,
destinationAddress,
[
Coin.fromPartial({
denom,
amount
})
],
fee);
return parseTxResponse(result); return laconicClient.registry.decode(response.msgResponses[0]);
} }
/** /**
@ -375,34 +384,36 @@ export class Registry {
/** /**
* Reserve authority. * Reserve authority.
*/ */
async reserveAuthority (params: { name: string, owner?: string }, privateKey: string, fee: Fee) { async reserveAuthority ({ name, owner }: { name: string, owner?: string }, privateKey: string, fee: StdFee) {
let result;
const account = new Account(Buffer.from(privateKey, 'hex')); const account = new Account(Buffer.from(privateKey, 'hex'));
const sender = await this._getSender(account); await account.init();
const laconicClient = await this.getLaconicClient(account);
const response: DeliverTxResponse = await laconicClient.reserveAuthority(
account.address,
name,
owner || account.address,
fee
);
const msgParams = { // TODO: Parse error response
name: params.name, return laconicClient.registry.decode(response.msgResponses[0]);
owner: params.owner || sender.accountAddress
};
const msg = createTxMsgReserveAuthority(this._chain, sender, fee, '', msgParams);
result = await this._submitTx(msg, privateKey, sender);
return parseTxResponse(result);
} }
/** /**
* Set authority bond. * Set authority bond.
*/ */
async setAuthorityBond (params: MessageMsgSetAuthorityBond, privateKey: string, fee: Fee) { async setAuthorityBond ({ bondId, name }: MessageMsgSetAuthorityBond, privateKey: string, fee: StdFee) {
let result;
const account = new Account(Buffer.from(privateKey, 'hex')); const account = new Account(Buffer.from(privateKey, 'hex'));
const sender = await this._getSender(account); await account.init();
const laconicClient = await this.getLaconicClient(account);
const response: DeliverTxResponse = await laconicClient.setAuthorityBond(
account.address,
bondId,
name,
fee
);
const msg = createTxMsgSetAuthorityBond(this._chain, sender, fee, '', params); return laconicClient.registry.decode(response.msgResponses[0]);
result = await this._submitTx(msg, privateKey, sender);
return parseTxResponse(result);
} }
/** /**

View File

@ -11,10 +11,12 @@ import { Comet38Client } from '@cosmjs/tendermint-rpc';
import { MsgCancelBondEncodeObject, MsgCreateBondEncodeObject, MsgRefillBondEncodeObject, MsgWithdrawBondEncodeObject, bondTypes, typeUrlMsgCancelBond, typeUrlMsgCreateBond, typeUrlMsgRefillBond, typeUrlMsgWithdrawBond } from './types/cerc/bond/message'; import { MsgCancelBondEncodeObject, MsgCreateBondEncodeObject, MsgRefillBondEncodeObject, MsgWithdrawBondEncodeObject, bondTypes, typeUrlMsgCancelBond, typeUrlMsgCreateBond, typeUrlMsgRefillBond, typeUrlMsgWithdrawBond } from './types/cerc/bond/message';
import { Coin } from './proto2/cosmos/base/v1beta1/coin'; import { Coin } from './proto2/cosmos/base/v1beta1/coin';
import { MsgReserveAuthorityEncodeObject, MsgSetAuthorityBondEncodeObject, registryTypes, typeUrlMsgReserveAuthority, typeUrlMsgSetAuthorityBond } from './types/cerc/registry/message';
export const laconicDefaultRegistryTypes: ReadonlyArray<[string, GeneratedType]> = [ export const laconicDefaultRegistryTypes: ReadonlyArray<[string, GeneratedType]> = [
...defaultRegistryTypes, ...defaultRegistryTypes,
...bondTypes ...bondTypes,
...registryTypes
]; ];
function createDefaultRegistry (): Registry { function createDefaultRegistry (): Registry {
@ -123,4 +125,42 @@ export class LaconicClient extends SigningStargateClient {
return this.signAndBroadcast(signer, [createMsg], fee, memo); return this.signAndBroadcast(signer, [createMsg], fee, memo);
} }
public async reserveAuthority (
signer: string,
name: string,
owner: string,
fee: StdFee | 'auto' | number,
memo = ''
): Promise<DeliverTxResponse> {
const createMsg: MsgReserveAuthorityEncodeObject = {
typeUrl: typeUrlMsgReserveAuthority,
value: {
name,
signer,
owner
}
};
return this.signAndBroadcast(signer, [createMsg], fee, memo);
}
public async setAuthorityBond (
signer: string,
bondId: string,
name: string,
fee: StdFee | 'auto' | number,
memo = ''
): Promise<DeliverTxResponse> {
const createMsg: MsgSetAuthorityBondEncodeObject = {
typeUrl: typeUrlMsgSetAuthorityBond,
value: {
signer,
bondId,
name
}
};
return this.signAndBroadcast(signer, [createMsg], fee, memo);
}
} }

View File

@ -3,9 +3,11 @@ import path from 'path';
import { Account } from './account'; import { Account } from './account';
import { Registry } from './index'; import { Registry } from './index';
import { ensureUpdatedConfig, getConfig } from './testing/helper'; import { ensureUpdatedConfig, getConfig, getLaconic2Config } from './testing/helper';
import { DENOM } from './constants';
const WATCHER_YML_PATH = path.join(__dirname, './testing/data/watcher.yml'); const WATCHER_YML_PATH = path.join(__dirname, './testing/data/watcher.yml');
const { fee: laconic2Fee } = getLaconic2Config();
jest.setTimeout(5 * 60 * 1000); jest.setTimeout(5 * 60 * 1000);
@ -23,28 +25,28 @@ const namingTests = () => {
// Create bond. // Create bond.
bondId = await registry.getNextBondId(privateKey); bondId = await registry.getNextBondId(privateKey);
await registry.createBond({ denom: 'aphoton', amount: '2000000000' }, privateKey, fee); await registry.createBond({ denom: DENOM, amount: '20000' }, privateKey, laconic2Fee);
// TODO: Implement set record
// Create watcher. // Create watcher.
watcher = await ensureUpdatedConfig(WATCHER_YML_PATH); // watcher = await ensureUpdatedConfig(WATCHER_YML_PATH);
const result = await registry.setRecord( // const result = await registry.setRecord(
{ // {
privateKey, // privateKey,
bondId, // bondId,
record: watcher.record // record: watcher.record
}, // },
privateKey, // privateKey,
fee // fee
); // );
watcherId = result.data.id; // watcherId = result.data.id;
}); });
describe('Authority tests', () => { describe('Authority tests', () => {
test('Reserve authority.', async () => { test('Reserve authority.', async () => {
const authorityName = `laconic-${Date.now()}`; const authorityName = `laconic-${Date.now()}`;
await registry.reserveAuthority({ name: authorityName }, privateKey, laconic2Fee);
await registry.reserveAuthority({ name: authorityName }, privateKey, fee);
}); });
describe('With authority reserved', () => { describe('With authority reserved', () => {
@ -55,7 +57,7 @@ const namingTests = () => {
authorityName = `laconic-${Date.now()}`; authorityName = `laconic-${Date.now()}`;
crn = `crn://${authorityName}/app/test`; crn = `crn://${authorityName}/app/test`;
await registry.reserveAuthority({ name: authorityName }, privateKey, fee); await registry.reserveAuthority({ name: authorityName }, privateKey, laconic2Fee);
}); });
test('Lookup authority.', async () => { test('Lookup authority.', async () => {
@ -75,14 +77,15 @@ const namingTests = () => {
expect(Number(record.height)).toBe(0); expect(Number(record.height)).toBe(0);
}); });
test('Reserve already reserved authority', async () => { // TODO: Implement parse error response
await expect(registry.reserveAuthority({ name: authorityName }, privateKey, fee)) xtest('Reserve already reserved authority', async () => {
await expect(registry.reserveAuthority({ name: authorityName }, privateKey, laconic2Fee))
.rejects.toThrow('Name already reserved.'); .rejects.toThrow('Name already reserved.');
}); });
test('Reserve sub-authority.', async () => { test('Reserve sub-authority.', async () => {
const subAuthority = `echo.${authorityName}`; const subAuthority = `echo.${authorityName}`;
await registry.reserveAuthority({ name: subAuthority }, privateKey, fee); await registry.reserveAuthority({ name: subAuthority }, privateKey, laconic2Fee);
const [record] = await registry.lookupAuthorities([subAuthority]); const [record] = await registry.lookupAuthorities([subAuthority]);
expect(record).toBeDefined(); expect(record).toBeDefined();
@ -95,36 +98,40 @@ const namingTests = () => {
// Create another account, send tx to set public key on the account. // Create another account, send tx to set public key on the account.
const mnenonic1 = Account.generateMnemonic(); const mnenonic1 = Account.generateMnemonic();
const otherAccount1 = await Account.generateFromMnemonic(mnenonic1); const otherAccount1 = await Account.generateFromMnemonic(mnenonic1);
await registry.sendCoins({ denom: 'aphoton', amount: '1000000000', destinationAddress: otherAccount1.formattedCosmosAddress }, privateKey, fee); await otherAccount1.init();
await registry.sendCoins({ denom: DENOM, amount: '10000', destinationAddress: otherAccount1.address }, privateKey, laconic2Fee);
const mnenonic2 = Account.generateMnemonic(); const mnenonic2 = Account.generateMnemonic();
const otherAccount2 = await Account.generateFromMnemonic(mnenonic2); const otherAccount2 = await Account.generateFromMnemonic(mnenonic2);
await registry.sendCoins({ denom: 'aphoton', amount: '10', destinationAddress: otherAccount2.formattedCosmosAddress }, otherAccount1.getPrivateKey(), fee); await otherAccount2.init();
await registry.sendCoins({ denom: DENOM, amount: '10000', destinationAddress: otherAccount2.address }, otherAccount1.getPrivateKey(), laconic2Fee);
const subAuthority = `halo.${authorityName}`; const subAuthority = `halo.${authorityName}`;
await registry.reserveAuthority({ name: subAuthority, owner: otherAccount1.formattedCosmosAddress }, privateKey, fee); await registry.reserveAuthority({ name: subAuthority, owner: otherAccount1.address }, privateKey, laconic2Fee);
const [record] = await registry.lookupAuthorities([subAuthority]); const [record] = await registry.lookupAuthorities([subAuthority]);
expect(record).toBeDefined(); expect(record).toBeDefined();
expect(record.ownerAddress).toBeDefined(); expect(record.ownerAddress).toBeDefined();
expect(record.ownerAddress).toBe(otherAccount1.getCosmosAddress()); expect(record.ownerAddress).toBe(otherAccount1.address);
expect(record.ownerPublicKey).toBeDefined(); expect(record.ownerPublicKey).toBeDefined();
expect(Number(record.height)).toBeGreaterThan(0); expect(Number(record.height)).toBeGreaterThan(0);
}); });
test('Set name for unbonded authority', async () => { // TODO: Implement set record
xtest('Set name for unbonded authority', async () => {
assert(watcherId); assert(watcherId);
await expect(registry.setName({ crn, cid: watcherId }, privateKey, fee)) await expect(registry.setName({ crn, cid: watcherId }, privateKey, fee))
.rejects.toThrow('Authority bond not found.'); .rejects.toThrow('Authority bond not found.');
}); });
test('Set authority bond', async () => { test('Set authority bond', async () => {
await registry.setAuthorityBond({ name: authorityName, bondId }, privateKey, fee); await registry.setAuthorityBond({ name: authorityName, bondId }, privateKey, laconic2Fee);
}); });
}); });
}); });
describe('Naming tests', () => { // TODO: Implement set record
xdescribe('Naming tests', () => {
let authorityName: string; let authorityName: string;
let otherAuthorityName: string; let otherAuthorityName: string;
let otherPrivateKey: string; let otherPrivateKey: string;
@ -132,14 +139,13 @@ const namingTests = () => {
beforeAll(async () => { beforeAll(async () => {
authorityName = `laconic-${Date.now()}`; authorityName = `laconic-${Date.now()}`;
await registry.reserveAuthority({ name: authorityName }, privateKey, laconic2Fee);
await registry.reserveAuthority({ name: authorityName }, privateKey, fee); await registry.setAuthorityBond({ name: authorityName, bondId }, privateKey, laconic2Fee);
await registry.setAuthorityBond({ name: authorityName, bondId }, privateKey, fee);
// Create another account. // Create another account.
const mnenonic = Account.generateMnemonic(); const mnenonic = Account.generateMnemonic();
otherAccount = await Account.generateFromMnemonic(mnenonic); otherAccount = await Account.generateFromMnemonic(mnenonic);
await registry.sendCoins({ denom: 'aphoton', amount: '1000000000', destinationAddress: otherAccount.formattedCosmosAddress }, privateKey, fee); await registry.sendCoins({ denom: 'aphoton', amount: '1000000000', destinationAddress: otherAccount.formattedCosmosAddress }, privateKey, laconic2Fee);
otherAuthorityName = `other-${Date.now()}`; otherAuthorityName = `other-${Date.now()}`;
otherPrivateKey = otherAccount.privateKey.toString('hex'); otherPrivateKey = otherAccount.privateKey.toString('hex');
@ -271,10 +277,10 @@ const namingTests = () => {
}); });
test('Set name for non-owned authority', async () => { test('Set name for non-owned authority', async () => {
await registry.sendCoins({ denom: 'aphoton', amount: '1000000000', destinationAddress: otherAccount.formattedCosmosAddress }, privateKey, fee); await registry.sendCoins({ denom: 'aphoton', amount: '1000000000', destinationAddress: otherAccount.formattedCosmosAddress }, privateKey, laconic2Fee);
// Other account reserves an authority. // Other account reserves an authority.
await registry.reserveAuthority({ name: otherAuthorityName }, otherPrivateKey, fee); await registry.reserveAuthority({ name: otherAuthorityName }, otherPrivateKey, laconic2Fee);
// Try setting name under other authority. // Try setting name under other authority.
await expect(registry.setName({ crn: `crn://${otherAuthorityName}/app/test`, cid: watcherId }, privateKey, fee)).rejects.toThrow('Access denied.'); await expect(registry.setName({ crn: `crn://${otherAuthorityName}/app/test`, cid: watcherId }, privateKey, fee)).rejects.toThrow('Access denied.');
@ -282,8 +288,8 @@ const namingTests = () => {
test('Delete name for non-owned authority.', async () => { test('Delete name for non-owned authority.', async () => {
const otherBondId = await registry.getNextBondId(otherPrivateKey); const otherBondId = await registry.getNextBondId(otherPrivateKey);
await registry.createBond({ denom: 'aphoton', amount: '10000' }, otherPrivateKey, fee); await registry.createBond({ denom: 'aphoton', amount: '10000' }, otherPrivateKey, laconic2Fee);
await registry.setAuthorityBond({ name: otherAuthorityName, bondId: otherBondId }, otherPrivateKey, fee); await registry.setAuthorityBond({ name: otherAuthorityName, bondId: otherBondId }, otherPrivateKey, laconic2Fee);
await registry.setName({ crn: `crn://${otherAuthorityName}/app/test`, cid: watcherId }, otherPrivateKey, fee); await registry.setName({ crn: `crn://${otherAuthorityName}/app/test`, cid: watcherId }, otherPrivateKey, fee);
// Try deleting name under other authority. // Try deleting name under other authority.

View File

@ -0,0 +1,25 @@
import { EncodeObject, GeneratedType } from '@cosmjs/proto-signing';
import { MsgReserveAuthority, MsgReserveAuthorityResponse, MsgSetAuthorityBond, MsgSetAuthorityBondResponse } from '../../../proto2/cerc/registry/v1/tx';
export const typeUrlMsgReserveAuthority = '/cerc.registry.v1.MsgReserveAuthority';
export const typeUrlMsgSetAuthorityBond = '/cerc.registry.v1.MsgSetAuthorityBond';
export const typeUrlMsgReserveAuthorityResponse = '/cerc.registry.v1.MsgReserveAuthorityResponse';
export const typeUrlMsgSetAuthorityBondResponse = '/cerc.registry.v1.MsgSetAuthorityBondResponse';
export const registryTypes: ReadonlyArray<[string, GeneratedType]> = [
[typeUrlMsgReserveAuthority, MsgReserveAuthority],
[typeUrlMsgReserveAuthorityResponse, MsgReserveAuthorityResponse],
[typeUrlMsgSetAuthorityBond, MsgSetAuthorityBond],
[typeUrlMsgSetAuthorityBondResponse, MsgSetAuthorityBondResponse]
];
export interface MsgReserveAuthorityEncodeObject extends EncodeObject {
readonly typeUrl: '/cerc.registry.v1.MsgReserveAuthority';
readonly value: Partial<MsgReserveAuthority>;
}
export interface MsgSetAuthorityBondEncodeObject extends EncodeObject {
readonly typeUrl: '/cerc.registry.v1.MsgSetAuthorityBond';
readonly value: Partial<MsgSetAuthorityBond>;
}