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.
@ -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.
- Run the tests in laconic-sdk repo:
- Run the tests in registry-sdk repo:
```bash
yarn test

View File

@ -1,7 +1,9 @@
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);
const { fee: laconic2Fee } = getLaconic2Config();
const { chainId, restEndpoint, gqlEndpoint, privateKey, fee } = getConfig();
@ -23,15 +25,15 @@ const auctionTests = (numBidders = 3) => {
for (let i = 0; i < numBidders; i++) {
const mnenonic = Account.generateMnemonic();
const account = await Account.generateFromMnemonic(mnenonic);
const bidderAddress = account.formattedCosmosAddress;
await registry.sendCoins({ denom: 'aphoton', amount: '1000000000', destinationAddress: bidderAddress }, privateKey, fee);
accounts.push({ address: bidderAddress, privateKey: account.privateKey.toString('hex') });
await account.init();
await registry.sendCoins({ denom: DENOM, amount: '100000', destinationAddress: account.address }, privateKey, laconic2Fee);
accounts.push({ address: account.address, privateKey: account.privateKey.toString('hex') });
}
});
test('Reserve authority.', async () => {
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 () => {

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

View File

@ -54,6 +54,7 @@ import {
} from './messages/auction';
import { LaconicClient } from './laconic-client';
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';
@ -199,15 +200,23 @@ export class Registry {
/**
* Send coins.
*/
async sendCoins (params: MessageSendParams, privateKey: string, fee: Fee) {
let result;
async sendCoins ({ amount, denom, destinationAddress }: MessageSendParams, privateKey: string, fee: StdFee) {
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);
result = await this._submitTx(msg, privateKey, sender);
const response: DeliverTxResponse = await laconicClient.sendTokens(
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.
*/
async reserveAuthority (params: { name: string, owner?: string }, privateKey: string, fee: Fee) {
let result;
async reserveAuthority ({ name, owner }: { name: string, owner?: string }, privateKey: string, fee: StdFee) {
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 = {
name: params.name,
owner: params.owner || sender.accountAddress
};
const msg = createTxMsgReserveAuthority(this._chain, sender, fee, '', msgParams);
result = await this._submitTx(msg, privateKey, sender);
return parseTxResponse(result);
// TODO: Parse error response
return laconicClient.registry.decode(response.msgResponses[0]);
}
/**
* Set authority bond.
*/
async setAuthorityBond (params: MessageMsgSetAuthorityBond, privateKey: string, fee: Fee) {
let result;
async setAuthorityBond ({ bondId, name }: MessageMsgSetAuthorityBond, privateKey: string, fee: StdFee) {
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);
result = await this._submitTx(msg, privateKey, sender);
return parseTxResponse(result);
return laconicClient.registry.decode(response.msgResponses[0]);
}
/**

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 { Coin } from './proto2/cosmos/base/v1beta1/coin';
import { MsgReserveAuthorityEncodeObject, MsgSetAuthorityBondEncodeObject, registryTypes, typeUrlMsgReserveAuthority, typeUrlMsgSetAuthorityBond } from './types/cerc/registry/message';
export const laconicDefaultRegistryTypes: ReadonlyArray<[string, GeneratedType]> = [
...defaultRegistryTypes,
...bondTypes
...bondTypes,
...registryTypes
];
function createDefaultRegistry (): Registry {
@ -123,4 +125,42 @@ export class LaconicClient extends SigningStargateClient {
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 { 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 { fee: laconic2Fee } = getLaconic2Config();
jest.setTimeout(5 * 60 * 1000);
@ -23,28 +25,28 @@ const namingTests = () => {
// Create bond.
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.
watcher = await ensureUpdatedConfig(WATCHER_YML_PATH);
const result = await registry.setRecord(
{
privateKey,
bondId,
record: watcher.record
},
privateKey,
fee
);
// watcher = await ensureUpdatedConfig(WATCHER_YML_PATH);
// const result = await registry.setRecord(
// {
// privateKey,
// bondId,
// record: watcher.record
// },
// privateKey,
// fee
// );
watcherId = result.data.id;
// watcherId = result.data.id;
});
describe('Authority tests', () => {
test('Reserve authority.', async () => {
const authorityName = `laconic-${Date.now()}`;
await registry.reserveAuthority({ name: authorityName }, privateKey, fee);
await registry.reserveAuthority({ name: authorityName }, privateKey, laconic2Fee);
});
describe('With authority reserved', () => {
@ -55,7 +57,7 @@ const namingTests = () => {
authorityName = `laconic-${Date.now()}`;
crn = `crn://${authorityName}/app/test`;
await registry.reserveAuthority({ name: authorityName }, privateKey, fee);
await registry.reserveAuthority({ name: authorityName }, privateKey, laconic2Fee);
});
test('Lookup authority.', async () => {
@ -75,14 +77,15 @@ const namingTests = () => {
expect(Number(record.height)).toBe(0);
});
test('Reserve already reserved authority', async () => {
await expect(registry.reserveAuthority({ name: authorityName }, privateKey, fee))
// TODO: Implement parse error response
xtest('Reserve already reserved authority', async () => {
await expect(registry.reserveAuthority({ name: authorityName }, privateKey, laconic2Fee))
.rejects.toThrow('Name already reserved.');
});
test('Reserve sub-authority.', async () => {
const subAuthority = `echo.${authorityName}`;
await registry.reserveAuthority({ name: subAuthority }, privateKey, fee);
await registry.reserveAuthority({ name: subAuthority }, privateKey, laconic2Fee);
const [record] = await registry.lookupAuthorities([subAuthority]);
expect(record).toBeDefined();
@ -95,36 +98,40 @@ const namingTests = () => {
// Create another account, send tx to set public key on the account.
const mnenonic1 = Account.generateMnemonic();
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 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}`;
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]);
expect(record).toBeDefined();
expect(record.ownerAddress).toBeDefined();
expect(record.ownerAddress).toBe(otherAccount1.getCosmosAddress());
expect(record.ownerAddress).toBe(otherAccount1.address);
expect(record.ownerPublicKey).toBeDefined();
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);
await expect(registry.setName({ crn, cid: watcherId }, privateKey, fee))
.rejects.toThrow('Authority bond not found.');
});
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 otherAuthorityName: string;
let otherPrivateKey: string;
@ -132,14 +139,13 @@ const namingTests = () => {
beforeAll(async () => {
authorityName = `laconic-${Date.now()}`;
await registry.reserveAuthority({ name: authorityName }, privateKey, fee);
await registry.setAuthorityBond({ name: authorityName, bondId }, privateKey, fee);
await registry.reserveAuthority({ name: authorityName }, privateKey, laconic2Fee);
await registry.setAuthorityBond({ name: authorityName, bondId }, privateKey, laconic2Fee);
// Create another account.
const mnenonic = Account.generateMnemonic();
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()}`;
otherPrivateKey = otherAccount.privateKey.toString('hex');
@ -271,10 +277,10 @@ const namingTests = () => {
});
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.
await registry.reserveAuthority({ name: otherAuthorityName }, otherPrivateKey, fee);
await registry.reserveAuthority({ name: otherAuthorityName }, otherPrivateKey, laconic2Fee);
// Try setting name under other authority.
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 () => {
const otherBondId = await registry.getNextBondId(otherPrivateKey);
await registry.createBond({ denom: 'aphoton', amount: '10000' }, otherPrivateKey, fee);
await registry.setAuthorityBond({ name: otherAuthorityName, bondId: otherBondId }, otherPrivateKey, fee);
await registry.createBond({ denom: 'aphoton', amount: '10000' }, otherPrivateKey, laconic2Fee);
await registry.setAuthorityBond({ name: otherAuthorityName, bondId: otherBondId }, otherPrivateKey, laconic2Fee);
await registry.setName({ crn: `crn://${otherAuthorityName}/app/test`, cid: watcherId }, otherPrivateKey, fee);
// 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>;
}