From f2e1a1bd4a97ebfc21f5921ad15e5134eb25fcbc Mon Sep 17 00:00:00 2001 From: Isha Venikar <145848618+IshaVenikar@users.noreply.github.com> Date: Wed, 3 Jul 2024 19:43:52 +0530 Subject: [PATCH 1/9] Incorporate onboarding module and export required types (#5) * Add proto bindings and generated ts files * Add onboard participant message * Add onboard participant method in client * Add test for onboarding participant * Regenerate proto bindings * Remove hard-coded values from onboarding-test * Export required types for onboarding app * Export types from message files --------- Co-authored-by: Adw8 --- proto/cerc/onboarding/module/v1/module.proto | 15 ++ proto/cerc/onboarding/v1/genesis.proto | 16 ++ proto/cerc/onboarding/v1/onboarding.proto | 31 +++ proto/cerc/onboarding/v1/query.proto | 5 + proto/cerc/onboarding/v1/tx.proto | 35 +++ src/index.ts | 34 ++- src/laconic-client.ts | 28 +- src/onboarding.test.ts | 40 +++ src/proto/cerc/onboarding/module/v1/module.ts | 102 ++++++++ src/proto/cerc/onboarding/v1/genesis.ts | 129 ++++++++++ src/proto/cerc/onboarding/v1/onboarding.ts | 228 +++++++++++++++++ src/proto/cerc/onboarding/v1/query.ts | 2 + src/proto/cerc/onboarding/v1/tx.ts | 240 ++++++++++++++++++ src/testing/data/watcher.yml | 2 +- src/types/cerc/onboarding/message.ts | 27 ++ 15 files changed, 926 insertions(+), 8 deletions(-) create mode 100644 proto/cerc/onboarding/module/v1/module.proto create mode 100644 proto/cerc/onboarding/v1/genesis.proto create mode 100644 proto/cerc/onboarding/v1/onboarding.proto create mode 100644 proto/cerc/onboarding/v1/query.proto create mode 100644 proto/cerc/onboarding/v1/tx.proto create mode 100644 src/onboarding.test.ts create mode 100644 src/proto/cerc/onboarding/module/v1/module.ts create mode 100644 src/proto/cerc/onboarding/v1/genesis.ts create mode 100644 src/proto/cerc/onboarding/v1/onboarding.ts create mode 100644 src/proto/cerc/onboarding/v1/query.ts create mode 100644 src/proto/cerc/onboarding/v1/tx.ts create mode 100644 src/types/cerc/onboarding/message.ts diff --git a/proto/cerc/onboarding/module/v1/module.proto b/proto/cerc/onboarding/module/v1/module.proto new file mode 100644 index 0000000..e921a6c --- /dev/null +++ b/proto/cerc/onboarding/module/v1/module.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package cerc.onboarding.module.v1; + +import "cosmos/app/v1alpha1/module.proto"; + +// Module is the app config object of the module. +// Learn more: https://docs.cosmos.network/main/building-modules/depinject +message Module { + option (cosmos.app.v1alpha1.module) = { + go_import : "git.vdb.to/cerc-io/laconicd/x/onboarding" + }; + + string authority = 1; +} diff --git a/proto/cerc/onboarding/v1/genesis.proto b/proto/cerc/onboarding/v1/genesis.proto new file mode 100644 index 0000000..5bfac13 --- /dev/null +++ b/proto/cerc/onboarding/v1/genesis.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package cerc.onboarding.v1; + +import "gogoproto/gogo.proto"; +import "cerc/onboarding/v1/onboarding.proto"; + +option go_package = "git.vdb.to/cerc-io/laconicd/x/onboarding"; + +// GenesisState defines the onboarding module's genesis state. +message GenesisState { + // params defines all the parameters of the module. + Params params = 1 [ (gogoproto.nullable) = false ]; + // participants defines all the participants + repeated Participant participants = 2 [ (gogoproto.nullable) = false ]; +} diff --git a/proto/cerc/onboarding/v1/onboarding.proto b/proto/cerc/onboarding/v1/onboarding.proto new file mode 100644 index 0000000..371de6e --- /dev/null +++ b/proto/cerc/onboarding/v1/onboarding.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; + +package cerc.onboarding.v1; + +import "gogoproto/gogo.proto"; + +option go_package = "git.vdb.to/cerc-io/laconicd/x/onboarding"; + +// Params defines the parameters of the onboarding module. +message Params {} + +// Participant defines the data that will be stored for each enrolled participant +message Participant { + string cosmos_address = 1 [ + (gogoproto.moretags) = "json:\"cosmos_address\" yaml:\"cosmos_address\"" + ]; + + string ethereum_address = 2 [ + (gogoproto.moretags) = "json:\"ethereum_address\" yaml:\"ethereum_address\"" + ]; +} +// EthPayload defines the payload that is signed by the ethereum private key +message EthPayload { + string address = 1 [ + (gogoproto.moretags) = "json:\"address\" yaml:\"address\"" + ]; + + string msg = 2 [ + (gogoproto.moretags) = "json:\"msg\" yaml:\"msg\"" + ]; +} diff --git a/proto/cerc/onboarding/v1/query.proto b/proto/cerc/onboarding/v1/query.proto new file mode 100644 index 0000000..e3801fe --- /dev/null +++ b/proto/cerc/onboarding/v1/query.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +package cerc.onboarding.v1; + +option go_package = "git.vdb.to/cerc-io/laconicd/x/onboarding"; diff --git a/proto/cerc/onboarding/v1/tx.proto b/proto/cerc/onboarding/v1/tx.proto new file mode 100644 index 0000000..18e87cd --- /dev/null +++ b/proto/cerc/onboarding/v1/tx.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; + +package cerc.onboarding.v1; + +import "cosmos/msg/v1/msg.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "cerc/onboarding/v1/onboarding.proto"; + +option go_package = "git.vdb.to/cerc-io/laconicd/x/onboarding"; + +// Msg defines the onboarding Msg service. +service Msg { + option (cosmos.msg.v1.service) = true; + + // OnboardParticipant defines a method for enrolling a new validator. + rpc OnboardParticipant(MsgOnboardParticipant) + returns (MsgOnboardParticipantResponse) { + option (google.api.http).post = "/cerc/onboarding/v1/onboard_participant"; + }; +} + +// MsgOnboardParticipant defines a SDK message for enrolling a new validator. +message MsgOnboardParticipant { + option (cosmos.msg.v1.signer) = "participant"; + + // Participant is the msg sender + string participant = 1; + EthPayload eth_payload = 2 [ (gogoproto.nullable) = false ]; + string eth_signature = 3; + string message = 4; +} + +// MsgOnboardParticipantResponse defines the Msg/OnboardParticipant response type. +message MsgOnboardParticipantResponse {} diff --git a/src/index.ts b/src/index.ts index 1bb4239..0f3014f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,11 +24,13 @@ import { MessageMsgCommitBid, MessageMsgRevealBid } from './types/cerc/auction/message'; -import { LaconicClient } from './laconic-client'; -import { MsgCancelBondResponse, MsgCreateBondResponse, MsgRefillBondResponse, MsgWithdrawBondResponse } from './proto/cerc/bond/v1/tx'; -import { Coin } from './proto/cosmos/base/v1beta1/coin'; -import { MsgSendResponse } from './proto/cosmos/bank/v1beta1/tx'; import { MessageMsgSendCoins } from './types/cosmos/bank/message'; +import { MessageMsgOnboardParticipant } from './types/cerc/onboarding/message'; +import { LaconicClient } from './laconic-client'; +import { Coin } from './proto/cosmos/base/v1beta1/coin'; +import { MsgCancelBondResponse, MsgCreateBondResponse, MsgRefillBondResponse, MsgWithdrawBondResponse } from './proto/cerc/bond/v1/tx'; +import { MsgOnboardParticipantResponse } from './proto/cerc/onboarding/v1/tx'; +import { MsgSendResponse } from './proto/cosmos/bank/v1beta1/tx'; export const DEFAULT_CHAIN_ID = 'laconic_9000-1'; @@ -59,7 +61,7 @@ export const createBid = async (chainId: string, auctionId: string, bidderAddres }; export class Registry { - _endpoints: {[key: string]: string}; + _endpoints: { [key: string]: string }; _chainID: string; _client: RegistryClient; @@ -105,7 +107,7 @@ export class Registry { /** * Get records by attributes. */ - async queryRecords (attributes: {[key: string]: any}, all = false, refs = false) { + async queryRecords (attributes: { [key: string]: any }, all = false, refs = false) { return this._client.queryRecords(attributes, all, refs); } @@ -434,6 +436,26 @@ export class Registry { async getLaconicClient (account: Account) { return LaconicClient.connectWithSigner(this._endpoints.rpc, account.wallet); } + + /** + * Onboard participant. + */ + async onboardParticipant ({ ethPayload, ethSignature, message }: MessageMsgOnboardParticipant, privateKey: string, fee: StdFee): Promise { + const account = new Account(Buffer.from(privateKey, 'hex')); + await account.init(); + const laconicClient = await this.getLaconicClient(account); + + return laconicClient.onboardParticipant( + account.address, + ethPayload, + ethSignature, + message, + fee + ); + } } export { Account }; +export { LaconicClient }; +export * from './types/cerc/bond/message'; +export * from './types/cerc/onboarding/message'; diff --git a/src/laconic-client.ts b/src/laconic-client.ts index 1434f26..3afa6d4 100644 --- a/src/laconic-client.ts +++ b/src/laconic-client.ts @@ -13,13 +13,16 @@ import { MsgCancelBondEncodeObject, MsgCreateBondEncodeObject, MsgRefillBondEnco import { Coin } from './proto/cosmos/base/v1beta1/coin'; import { MsgAssociateBondEncodeObject, MsgDeleteNameEncodeObject, MsgDissociateBondEncodeObject, MsgDissociateRecordsEncodeObject, MsgReassociateRecordsEncodeObject, MsgReserveAuthorityEncodeObject, MsgSetAuthorityBondEncodeObject, MsgSetNameEncodeObject, MsgSetRecordEncodeObject, registryTypes, typeUrlMsgAssociateBond, typeUrlMsgDeleteName, typeUrlMsgDissociateBond, typeUrlMsgDissociateRecords, typeUrlMsgReassociateRecords, typeUrlMsgReserveAuthority, typeUrlMsgSetAuthorityBond, typeUrlMsgSetName, typeUrlMsgSetRecord, NAMESERVICE_ERRORS } from './types/cerc/registry/message'; import { MsgCommitBidEncodeObject, MsgRevealBidEncodeObject, auctionTypes, typeUrlMsgCommitBid, typeUrlMsgRevealBid } from './types/cerc/auction/message'; +import { MsgOnboardParticipantEncodeObject, onboardingTypes, typeUrlMsgOnboardParticipant } from './types/cerc/onboarding/message'; import { MsgAssociateBondResponse, MsgDeleteNameResponse, MsgDissociateBondResponse, MsgDissociateRecordsResponse, MsgReassociateRecordsResponse, MsgReserveAuthorityResponse, MsgSetAuthorityBondResponse, MsgSetNameResponse, MsgSetRecordResponse, Payload } from './proto/cerc/registry/v1/tx'; import { Record, Signature } from './proto/cerc/registry/v1/registry'; import { Account } from './account'; import { Util } from './util'; import { MsgCommitBidResponse, MsgRevealBidResponse } from './proto/cerc/auction/v1/tx'; import { MsgCancelBondResponse, MsgCreateBondResponse, MsgRefillBondResponse, MsgWithdrawBondResponse } from './proto/cerc/bond/v1/tx'; +import { MsgOnboardParticipantResponse } from './proto/cerc/onboarding/v1/tx'; import { bankTypes } from './types/cosmos/bank/message'; +import { EthPayload } from './proto/cerc/onboarding/v1/onboarding'; const DEFAULT_WRITE_ERROR = 'Unable to write to laconic2d.'; @@ -28,7 +31,8 @@ export const laconicDefaultRegistryTypes: ReadonlyArray<[string, GeneratedType]> ...bondTypes, ...registryTypes, ...auctionTypes, - ...bankTypes + ...bankTypes, + ...onboardingTypes ]; function createDefaultRegistry (): Registry { @@ -385,4 +389,26 @@ export class LaconicClient extends SigningStargateClient { return `${errorMessage || DEFAULT_WRITE_ERROR}: ${error}`; } + + public async onboardParticipant ( + signer: string, + ethPayload: EthPayload, + ethSignature: string, + message: string, + fee: StdFee | 'auto' | number, + memo = '' + ) { + const onboardParticipantMsg: MsgOnboardParticipantEncodeObject = { + typeUrl: typeUrlMsgOnboardParticipant, + value: { + participant: signer, + ethPayload, + ethSignature, + message + } + }; + + const response = await this.signAndBroadcast(signer, [onboardParticipantMsg], fee, memo); + return this.parseResponse(response); + } } diff --git a/src/onboarding.test.ts b/src/onboarding.test.ts new file mode 100644 index 0000000..f299ec2 --- /dev/null +++ b/src/onboarding.test.ts @@ -0,0 +1,40 @@ +import { Wallet } from 'ethers'; + +import { Registry, Account } from './index'; +import { getConfig } from './testing/helper'; + +const { chainId, rpcEndpoint, gqlEndpoint, privateKey, fee } = getConfig(); + +jest.setTimeout(90 * 1000); + +const onboardingTests = () => { + let registry: Registry; + + beforeAll(async () => { + registry = new Registry(gqlEndpoint, rpcEndpoint, chainId); + }); + + test('Onboard participant.', async () => { + const mnenonic = Account.generateMnemonic(); + let wallet = Wallet.fromMnemonic(mnenonic); + + const ethPayload = { + address: wallet.address, + msg: 'Message signed by ethereum private key' + }; + + const message = JSON.stringify(ethPayload); + + const ethSignature = await wallet.signMessage(message); + + await registry.onboardParticipant({ + ethPayload, + ethSignature, + message: 'Message signed by cosmos private key' + }, privateKey, fee); + + // TODO: Verify participant getting stored in state + }); +}; + +describe('Onboarding', onboardingTests); diff --git a/src/proto/cerc/onboarding/module/v1/module.ts b/src/proto/cerc/onboarding/module/v1/module.ts new file mode 100644 index 0000000..e76c41b --- /dev/null +++ b/src/proto/cerc/onboarding/module/v1/module.ts @@ -0,0 +1,102 @@ +/* eslint-disable */ +import Long from "long"; +import _m0 from "protobufjs/minimal"; + +export const protobufPackage = "cerc.onboarding.module.v1"; + +/** + * Module is the app config object of the module. + * Learn more: https://docs.cosmos.network/main/building-modules/depinject + */ +export interface Module { + authority: string; +} + +function createBaseModule(): Module { + return { authority: "" }; +} + +export const Module = { + encode( + message: Module, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.authority !== "") { + writer.uint32(10).string(message.authority); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Module { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseModule(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.authority = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): Module { + return { + authority: isSet(object.authority) ? String(object.authority) : "", + }; + }, + + toJSON(message: Module): unknown { + const obj: any = {}; + message.authority !== undefined && (obj.authority = message.authority); + return obj; + }, + + fromPartial, I>>(object: I): Module { + const message = createBaseModule(); + message.authority = object.authority ?? ""; + return message; + }, +}; + +type Builtin = + | Date + | Function + | Uint8Array + | string + | number + | boolean + | undefined; + +export type DeepPartial = T extends Builtin + ? T + : T extends Long + ? string | number | Long + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin + ? P + : P & { [K in keyof P]: Exact } & { + [K in Exclude>]: never; + }; + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/proto/cerc/onboarding/v1/genesis.ts b/src/proto/cerc/onboarding/v1/genesis.ts new file mode 100644 index 0000000..10a4057 --- /dev/null +++ b/src/proto/cerc/onboarding/v1/genesis.ts @@ -0,0 +1,129 @@ +/* eslint-disable */ +import { Params, Participant } from "./onboarding"; +import Long from "long"; +import _m0 from "protobufjs/minimal"; + +export const protobufPackage = "cerc.onboarding.v1"; + +/** GenesisState defines the onboarding module's genesis state. */ +export interface GenesisState { + /** params defines all the parameters of the module. */ + params?: Params; + /** participants defines all the participants */ + participants: Participant[]; +} + +function createBaseGenesisState(): GenesisState { + return { params: undefined, participants: [] }; +} + +export const GenesisState = { + encode( + message: GenesisState, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.params !== undefined) { + Params.encode(message.params, writer.uint32(10).fork()).ldelim(); + } + for (const v of message.participants) { + Participant.encode(v!, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GenesisState { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGenesisState(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.params = Params.decode(reader, reader.uint32()); + break; + case 2: + message.participants.push( + Participant.decode(reader, reader.uint32()) + ); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): GenesisState { + return { + params: isSet(object.params) ? Params.fromJSON(object.params) : undefined, + participants: Array.isArray(object?.participants) + ? object.participants.map((e: any) => Participant.fromJSON(e)) + : [], + }; + }, + + toJSON(message: GenesisState): unknown { + const obj: any = {}; + message.params !== undefined && + (obj.params = message.params ? Params.toJSON(message.params) : undefined); + if (message.participants) { + obj.participants = message.participants.map((e) => + e ? Participant.toJSON(e) : undefined + ); + } else { + obj.participants = []; + } + return obj; + }, + + fromPartial, I>>( + object: I + ): GenesisState { + const message = createBaseGenesisState(); + message.params = + object.params !== undefined && object.params !== null + ? Params.fromPartial(object.params) + : undefined; + message.participants = + object.participants?.map((e) => Participant.fromPartial(e)) || []; + return message; + }, +}; + +type Builtin = + | Date + | Function + | Uint8Array + | string + | number + | boolean + | undefined; + +export type DeepPartial = T extends Builtin + ? T + : T extends Long + ? string | number | Long + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin + ? P + : P & { [K in keyof P]: Exact } & { + [K in Exclude>]: never; + }; + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/proto/cerc/onboarding/v1/onboarding.ts b/src/proto/cerc/onboarding/v1/onboarding.ts new file mode 100644 index 0000000..fba3b5f --- /dev/null +++ b/src/proto/cerc/onboarding/v1/onboarding.ts @@ -0,0 +1,228 @@ +/* eslint-disable */ +import Long from "long"; +import _m0 from "protobufjs/minimal"; + +export const protobufPackage = "cerc.onboarding.v1"; + +/** Params defines the parameters of the onboarding module. */ +export interface Params {} + +/** Participant defines the data that will be stored for each enrolled participant */ +export interface Participant { + cosmosAddress: string; + ethereumAddress: string; +} + +/** EthPayload defines the payload that is signed by the ethereum private key */ +export interface EthPayload { + address: string; + msg: string; +} + +function createBaseParams(): Params { + return {}; +} + +export const Params = { + encode(_: Params, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Params { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseParams(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(_: any): Params { + return {}; + }, + + toJSON(_: Params): unknown { + const obj: any = {}; + return obj; + }, + + fromPartial, I>>(_: I): Params { + const message = createBaseParams(); + return message; + }, +}; + +function createBaseParticipant(): Participant { + return { cosmosAddress: "", ethereumAddress: "" }; +} + +export const Participant = { + encode( + message: Participant, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.cosmosAddress !== "") { + writer.uint32(10).string(message.cosmosAddress); + } + if (message.ethereumAddress !== "") { + writer.uint32(18).string(message.ethereumAddress); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Participant { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseParticipant(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.cosmosAddress = reader.string(); + break; + case 2: + message.ethereumAddress = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): Participant { + return { + cosmosAddress: isSet(object.cosmosAddress) + ? String(object.cosmosAddress) + : "", + ethereumAddress: isSet(object.ethereumAddress) + ? String(object.ethereumAddress) + : "", + }; + }, + + toJSON(message: Participant): unknown { + const obj: any = {}; + message.cosmosAddress !== undefined && + (obj.cosmosAddress = message.cosmosAddress); + message.ethereumAddress !== undefined && + (obj.ethereumAddress = message.ethereumAddress); + return obj; + }, + + fromPartial, I>>( + object: I + ): Participant { + const message = createBaseParticipant(); + message.cosmosAddress = object.cosmosAddress ?? ""; + message.ethereumAddress = object.ethereumAddress ?? ""; + return message; + }, +}; + +function createBaseEthPayload(): EthPayload { + return { address: "", msg: "" }; +} + +export const EthPayload = { + encode( + message: EthPayload, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.address !== "") { + writer.uint32(10).string(message.address); + } + if (message.msg !== "") { + writer.uint32(18).string(message.msg); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): EthPayload { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEthPayload(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.address = reader.string(); + break; + case 2: + message.msg = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): EthPayload { + return { + address: isSet(object.address) ? String(object.address) : "", + msg: isSet(object.msg) ? String(object.msg) : "", + }; + }, + + toJSON(message: EthPayload): unknown { + const obj: any = {}; + message.address !== undefined && (obj.address = message.address); + message.msg !== undefined && (obj.msg = message.msg); + return obj; + }, + + fromPartial, I>>( + object: I + ): EthPayload { + const message = createBaseEthPayload(); + message.address = object.address ?? ""; + message.msg = object.msg ?? ""; + return message; + }, +}; + +type Builtin = + | Date + | Function + | Uint8Array + | string + | number + | boolean + | undefined; + +export type DeepPartial = T extends Builtin + ? T + : T extends Long + ? string | number | Long + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin + ? P + : P & { [K in keyof P]: Exact } & { + [K in Exclude>]: never; + }; + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/proto/cerc/onboarding/v1/query.ts b/src/proto/cerc/onboarding/v1/query.ts new file mode 100644 index 0000000..8cf9ffe --- /dev/null +++ b/src/proto/cerc/onboarding/v1/query.ts @@ -0,0 +1,2 @@ +/* eslint-disable */ +export const protobufPackage = "cerc.onboarding.v1"; diff --git a/src/proto/cerc/onboarding/v1/tx.ts b/src/proto/cerc/onboarding/v1/tx.ts new file mode 100644 index 0000000..977498f --- /dev/null +++ b/src/proto/cerc/onboarding/v1/tx.ts @@ -0,0 +1,240 @@ +/* eslint-disable */ +import { EthPayload } from "./onboarding"; +import Long from "long"; +import _m0 from "protobufjs/minimal"; + +export const protobufPackage = "cerc.onboarding.v1"; + +/** MsgOnboardParticipant defines a SDK message for enrolling a new validator. */ +export interface MsgOnboardParticipant { + /** Participant is the msg sender */ + participant: string; + ethPayload?: EthPayload; + ethSignature: string; + message: string; +} + +/** MsgOnboardParticipantResponse defines the Msg/OnboardParticipant response type. */ +export interface MsgOnboardParticipantResponse {} + +function createBaseMsgOnboardParticipant(): MsgOnboardParticipant { + return { + participant: "", + ethPayload: undefined, + ethSignature: "", + message: "", + }; +} + +export const MsgOnboardParticipant = { + encode( + message: MsgOnboardParticipant, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.participant !== "") { + writer.uint32(10).string(message.participant); + } + if (message.ethPayload !== undefined) { + EthPayload.encode(message.ethPayload, writer.uint32(18).fork()).ldelim(); + } + if (message.ethSignature !== "") { + writer.uint32(26).string(message.ethSignature); + } + if (message.message !== "") { + writer.uint32(34).string(message.message); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number + ): MsgOnboardParticipant { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgOnboardParticipant(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.participant = reader.string(); + break; + case 2: + message.ethPayload = EthPayload.decode(reader, reader.uint32()); + break; + case 3: + message.ethSignature = reader.string(); + break; + case 4: + message.message = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): MsgOnboardParticipant { + return { + participant: isSet(object.participant) ? String(object.participant) : "", + ethPayload: isSet(object.ethPayload) + ? EthPayload.fromJSON(object.ethPayload) + : undefined, + ethSignature: isSet(object.ethSignature) + ? String(object.ethSignature) + : "", + message: isSet(object.message) ? String(object.message) : "", + }; + }, + + toJSON(message: MsgOnboardParticipant): unknown { + const obj: any = {}; + message.participant !== undefined && + (obj.participant = message.participant); + message.ethPayload !== undefined && + (obj.ethPayload = message.ethPayload + ? EthPayload.toJSON(message.ethPayload) + : undefined); + message.ethSignature !== undefined && + (obj.ethSignature = message.ethSignature); + message.message !== undefined && (obj.message = message.message); + return obj; + }, + + fromPartial, I>>( + object: I + ): MsgOnboardParticipant { + const message = createBaseMsgOnboardParticipant(); + message.participant = object.participant ?? ""; + message.ethPayload = + object.ethPayload !== undefined && object.ethPayload !== null + ? EthPayload.fromPartial(object.ethPayload) + : undefined; + message.ethSignature = object.ethSignature ?? ""; + message.message = object.message ?? ""; + return message; + }, +}; + +function createBaseMsgOnboardParticipantResponse(): MsgOnboardParticipantResponse { + return {}; +} + +export const MsgOnboardParticipantResponse = { + encode( + _: MsgOnboardParticipantResponse, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number + ): MsgOnboardParticipantResponse { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgOnboardParticipantResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(_: any): MsgOnboardParticipantResponse { + return {}; + }, + + toJSON(_: MsgOnboardParticipantResponse): unknown { + const obj: any = {}; + return obj; + }, + + fromPartial, I>>( + _: I + ): MsgOnboardParticipantResponse { + const message = createBaseMsgOnboardParticipantResponse(); + return message; + }, +}; + +/** Msg defines the onboarding Msg service. */ +export interface Msg { + /** OnboardParticipant defines a method for enrolling a new validator. */ + OnboardParticipant( + request: MsgOnboardParticipant + ): Promise; +} + +export class MsgClientImpl implements Msg { + private readonly rpc: Rpc; + constructor(rpc: Rpc) { + this.rpc = rpc; + this.OnboardParticipant = this.OnboardParticipant.bind(this); + } + OnboardParticipant( + request: MsgOnboardParticipant + ): Promise { + const data = MsgOnboardParticipant.encode(request).finish(); + const promise = this.rpc.request( + "cerc.onboarding.v1.Msg", + "OnboardParticipant", + data + ); + return promise.then((data) => + MsgOnboardParticipantResponse.decode(new _m0.Reader(data)) + ); + } +} + +interface Rpc { + request( + service: string, + method: string, + data: Uint8Array + ): Promise; +} + +type Builtin = + | Date + | Function + | Uint8Array + | string + | number + | boolean + | undefined; + +export type DeepPartial = T extends Builtin + ? T + : T extends Long + ? string | number | Long + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin + ? P + : P & { [K in keyof P]: Exact } & { + [K in Exclude>]: never; + }; + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/testing/data/watcher.yml b/src/testing/data/watcher.yml index d9deb11..dddddab 100644 --- a/src/testing/data/watcher.yml +++ b/src/testing/data/watcher.yml @@ -7,4 +7,4 @@ record: /: QmP8jTG1m9GSDJLCbeWhVSVgEzCPPwXRdCRuJtQ5Tz9Kc9 tls_cert_cid: /: QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR - version: 1.0.23 + version: 1.0.28 diff --git a/src/types/cerc/onboarding/message.ts b/src/types/cerc/onboarding/message.ts new file mode 100644 index 0000000..567da37 --- /dev/null +++ b/src/types/cerc/onboarding/message.ts @@ -0,0 +1,27 @@ +import { EncodeObject, GeneratedType } from '@cosmjs/proto-signing'; + +import { MsgOnboardParticipantResponse, MsgOnboardParticipant } from '../../../proto/cerc/onboarding/v1/tx'; + +export const typeUrlMsgOnboardParticipant = '/cerc.onboarding.v1.MsgOnboardParticipant'; +export const typeUrlMsgOnboardParticipantResponse = '/cerc.onboarding.v1.MsgOnboardParticipantResponse'; + +export const onboardingTypes: ReadonlyArray<[string, GeneratedType]> = [ + [typeUrlMsgOnboardParticipant, MsgOnboardParticipant], + [typeUrlMsgOnboardParticipantResponse, MsgOnboardParticipantResponse] +]; + +export interface MsgOnboardParticipantEncodeObject extends EncodeObject { + readonly typeUrl: '/cerc.onboarding.v1.MsgOnboardParticipant'; + readonly value: Partial; +} + +interface ethPayload { + address: string + msg: string +} + +export interface MessageMsgOnboardParticipant { + ethPayload: ethPayload + ethSignature: string + message: string +} -- 2.45.2 From 51fed6aa6212c93f97bdf104f9143a270491eeea Mon Sep 17 00:00:00 2001 From: Isha Venikar <145848618+IshaVenikar@users.noreply.github.com> Date: Thu, 4 Jul 2024 19:33:10 +0530 Subject: [PATCH 2/9] Add a method to get registered participants (#6) * Add getParticipant query method for getting participants list * Add check to verify the list of registered participants in onboarding-test * Modify test messages * Add updated proto files and regenerate ts bindings * Update proto files and regenerate ts bindings * Fix imports --- proto/cerc/onboarding/v1/onboarding.proto | 6 +- proto/cerc/onboarding/v1/query.proto | 28 +++ proto/cerc/onboarding/v1/tx.proto | 3 +- src/index.ts | 7 + src/onboarding.test.ts | 29 ++- src/proto/cerc/onboarding/v1/onboarding.ts | 32 ++- src/proto/cerc/onboarding/v1/query.ts | 248 +++++++++++++++++++++ src/proto/cerc/onboarding/v1/tx.ts | 17 +- src/registry-client.ts | 20 +- src/testing/data/watcher.yml | 2 +- 10 files changed, 357 insertions(+), 35 deletions(-) diff --git a/proto/cerc/onboarding/v1/onboarding.proto b/proto/cerc/onboarding/v1/onboarding.proto index 371de6e..876cc7e 100644 --- a/proto/cerc/onboarding/v1/onboarding.proto +++ b/proto/cerc/onboarding/v1/onboarding.proto @@ -7,7 +7,11 @@ import "gogoproto/gogo.proto"; option go_package = "git.vdb.to/cerc-io/laconicd/x/onboarding"; // Params defines the parameters of the onboarding module. -message Params {} +message Params { + bool onboarding_enabled = 1 [ + (gogoproto.moretags) = "json:\"onboarding_enabled\" yaml:\"onboarding_enabled\"" + ]; +} // Participant defines the data that will be stored for each enrolled participant message Participant { diff --git a/proto/cerc/onboarding/v1/query.proto b/proto/cerc/onboarding/v1/query.proto index e3801fe..1345bb8 100644 --- a/proto/cerc/onboarding/v1/query.proto +++ b/proto/cerc/onboarding/v1/query.proto @@ -2,4 +2,32 @@ syntax = "proto3"; package cerc.onboarding.v1; +import "gogoproto/gogo.proto"; +import "cosmos/base/query/v1beta1/pagination.proto"; +import "cerc/onboarding/v1/onboarding.proto"; +import "google/api/annotations.proto"; + option go_package = "git.vdb.to/cerc-io/laconicd/x/onboarding"; + +// Query defines the gRPC querier service for onboarding module +service Query { + // Participants queries Participants list + rpc Participants(QueryParticipantsRequest) returns (QueryParticipantsResponse) { + option (google.api.http).get = "/cerc/onboarding/v1/participants"; + } +} + +// QueryParticipantsRequest queries participants +message QueryParticipantsRequest { + // pagination defines an optional pagination for the request. + cosmos.base.query.v1beta1.PageRequest pagination = 1; +} + +// QueryParticipantsResponse is response type for get the participants +message QueryParticipantsResponse { + repeated Participant participants = 1 + [ (gogoproto.moretags) = "json:\"participants\" yaml:\"participants\"" ]; + + // pagination defines the pagination in the response. + cosmos.base.query.v1beta1.PageResponse pagination = 2; +} diff --git a/proto/cerc/onboarding/v1/tx.proto b/proto/cerc/onboarding/v1/tx.proto index 18e87cd..bf07de5 100644 --- a/proto/cerc/onboarding/v1/tx.proto +++ b/proto/cerc/onboarding/v1/tx.proto @@ -3,8 +3,8 @@ syntax = "proto3"; package cerc.onboarding.v1; import "cosmos/msg/v1/msg.proto"; -import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; +import "gogoproto/gogo.proto"; import "cerc/onboarding/v1/onboarding.proto"; option go_package = "git.vdb.to/cerc-io/laconicd/x/onboarding"; @@ -28,7 +28,6 @@ message MsgOnboardParticipant { string participant = 1; EthPayload eth_payload = 2 [ (gogoproto.nullable) = false ]; string eth_signature = 3; - string message = 4; } // MsgOnboardParticipantResponse defines the Msg/OnboardParticipant response type. diff --git a/src/index.ts b/src/index.ts index 0f3014f..e94de8a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -453,6 +453,13 @@ export class Registry { fee ); } + + /** + * Query participants. + */ + async getParticipants () { + return this._client.getParticipants(); + } } export { Account }; diff --git a/src/onboarding.test.ts b/src/onboarding.test.ts index f299ec2..ea8f1f1 100644 --- a/src/onboarding.test.ts +++ b/src/onboarding.test.ts @@ -1,5 +1,7 @@ import { Wallet } from 'ethers'; +import { DirectSecp256k1Wallet } from '@cosmjs/proto-signing'; + import { Registry, Account } from './index'; import { getConfig } from './testing/helper'; @@ -9,31 +11,46 @@ jest.setTimeout(90 * 1000); const onboardingTests = () => { let registry: Registry; + let ethWallet: Wallet; beforeAll(async () => { registry = new Registry(gqlEndpoint, rpcEndpoint, chainId); }); test('Onboard participant.', async () => { - const mnenonic = Account.generateMnemonic(); - let wallet = Wallet.fromMnemonic(mnenonic); + const mnemonic = Account.generateMnemonic(); + ethWallet = Wallet.fromMnemonic(mnemonic); const ethPayload = { - address: wallet.address, + address: ethWallet.address, msg: 'Message signed by ethereum private key' }; const message = JSON.stringify(ethPayload); - - const ethSignature = await wallet.signMessage(message); + const ethSignature = await ethWallet.signMessage(message); await registry.onboardParticipant({ ethPayload, ethSignature, message: 'Message signed by cosmos private key' }, privateKey, fee); + }); - // TODO: Verify participant getting stored in state + describe('With participants enrolled', () => { + test('Query participants.', async () => { + const account = new Account(Buffer.from(privateKey, 'hex')); + const cosmosAccount = await DirectSecp256k1Wallet.fromKey(account._privateKey, 'laconic'); + const [cosmosWallet] = await cosmosAccount.getAccounts(); + + const expectedParticipants = [ + { + cosmos_address: cosmosWallet.address, + ethereum_address: ethWallet.address + } + ]; + const participants = await registry.getParticipants(); + expect(participants).toEqual(expectedParticipants); + }); }); }; diff --git a/src/proto/cerc/onboarding/v1/onboarding.ts b/src/proto/cerc/onboarding/v1/onboarding.ts index fba3b5f..9c514e6 100644 --- a/src/proto/cerc/onboarding/v1/onboarding.ts +++ b/src/proto/cerc/onboarding/v1/onboarding.ts @@ -5,7 +5,9 @@ import _m0 from "protobufjs/minimal"; export const protobufPackage = "cerc.onboarding.v1"; /** Params defines the parameters of the onboarding module. */ -export interface Params {} +export interface Params { + onboardingEnabled: boolean; +} /** Participant defines the data that will be stored for each enrolled participant */ export interface Participant { @@ -20,11 +22,17 @@ export interface EthPayload { } function createBaseParams(): Params { - return {}; + return { onboardingEnabled: false }; } export const Params = { - encode(_: Params, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + encode( + message: Params, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.onboardingEnabled === true) { + writer.uint32(8).bool(message.onboardingEnabled); + } return writer; }, @@ -35,6 +43,9 @@ export const Params = { while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { + case 1: + message.onboardingEnabled = reader.bool(); + break; default: reader.skipType(tag & 7); break; @@ -43,17 +54,24 @@ export const Params = { return message; }, - fromJSON(_: any): Params { - return {}; + fromJSON(object: any): Params { + return { + onboardingEnabled: isSet(object.onboardingEnabled) + ? Boolean(object.onboardingEnabled) + : false, + }; }, - toJSON(_: Params): unknown { + toJSON(message: Params): unknown { const obj: any = {}; + message.onboardingEnabled !== undefined && + (obj.onboardingEnabled = message.onboardingEnabled); return obj; }, - fromPartial, I>>(_: I): Params { + fromPartial, I>>(object: I): Params { const message = createBaseParams(); + message.onboardingEnabled = object.onboardingEnabled ?? false; return message; }, }; diff --git a/src/proto/cerc/onboarding/v1/query.ts b/src/proto/cerc/onboarding/v1/query.ts index 8cf9ffe..7c0d0e0 100644 --- a/src/proto/cerc/onboarding/v1/query.ts +++ b/src/proto/cerc/onboarding/v1/query.ts @@ -1,2 +1,250 @@ /* eslint-disable */ +import { + PageRequest, + PageResponse, +} from "../../../cosmos/base/query/v1beta1/pagination"; +import Long from "long"; +import { Participant } from "./onboarding"; +import _m0 from "protobufjs/minimal"; + export const protobufPackage = "cerc.onboarding.v1"; + +/** QueryParticipantsRequest queries participants */ +export interface QueryParticipantsRequest { + /** pagination defines an optional pagination for the request. */ + pagination?: PageRequest; +} + +/** QueryParticipantsResponse is response type for get the participants */ +export interface QueryParticipantsResponse { + participants: Participant[]; + /** pagination defines the pagination in the response. */ + pagination?: PageResponse; +} + +function createBaseQueryParticipantsRequest(): QueryParticipantsRequest { + return { pagination: undefined }; +} + +export const QueryParticipantsRequest = { + encode( + message: QueryParticipantsRequest, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.pagination !== undefined) { + PageRequest.encode(message.pagination, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number + ): QueryParticipantsRequest { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseQueryParticipantsRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.pagination = PageRequest.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): QueryParticipantsRequest { + return { + pagination: isSet(object.pagination) + ? PageRequest.fromJSON(object.pagination) + : undefined, + }; + }, + + toJSON(message: QueryParticipantsRequest): unknown { + const obj: any = {}; + message.pagination !== undefined && + (obj.pagination = message.pagination + ? PageRequest.toJSON(message.pagination) + : undefined); + return obj; + }, + + fromPartial, I>>( + object: I + ): QueryParticipantsRequest { + const message = createBaseQueryParticipantsRequest(); + message.pagination = + object.pagination !== undefined && object.pagination !== null + ? PageRequest.fromPartial(object.pagination) + : undefined; + return message; + }, +}; + +function createBaseQueryParticipantsResponse(): QueryParticipantsResponse { + return { participants: [], pagination: undefined }; +} + +export const QueryParticipantsResponse = { + encode( + message: QueryParticipantsResponse, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + for (const v of message.participants) { + Participant.encode(v!, writer.uint32(10).fork()).ldelim(); + } + if (message.pagination !== undefined) { + PageResponse.encode( + message.pagination, + writer.uint32(18).fork() + ).ldelim(); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number + ): QueryParticipantsResponse { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseQueryParticipantsResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.participants.push( + Participant.decode(reader, reader.uint32()) + ); + break; + case 2: + message.pagination = PageResponse.decode(reader, reader.uint32()); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): QueryParticipantsResponse { + return { + participants: Array.isArray(object?.participants) + ? object.participants.map((e: any) => Participant.fromJSON(e)) + : [], + pagination: isSet(object.pagination) + ? PageResponse.fromJSON(object.pagination) + : undefined, + }; + }, + + toJSON(message: QueryParticipantsResponse): unknown { + const obj: any = {}; + if (message.participants) { + obj.participants = message.participants.map((e) => + e ? Participant.toJSON(e) : undefined + ); + } else { + obj.participants = []; + } + message.pagination !== undefined && + (obj.pagination = message.pagination + ? PageResponse.toJSON(message.pagination) + : undefined); + return obj; + }, + + fromPartial, I>>( + object: I + ): QueryParticipantsResponse { + const message = createBaseQueryParticipantsResponse(); + message.participants = + object.participants?.map((e) => Participant.fromPartial(e)) || []; + message.pagination = + object.pagination !== undefined && object.pagination !== null + ? PageResponse.fromPartial(object.pagination) + : undefined; + return message; + }, +}; + +/** Query defines the gRPC querier service for onboarding module */ +export interface Query { + /** Participants queries Participants list */ + Participants( + request: QueryParticipantsRequest + ): Promise; +} + +export class QueryClientImpl implements Query { + private readonly rpc: Rpc; + constructor(rpc: Rpc) { + this.rpc = rpc; + this.Participants = this.Participants.bind(this); + } + Participants( + request: QueryParticipantsRequest + ): Promise { + const data = QueryParticipantsRequest.encode(request).finish(); + const promise = this.rpc.request( + "cerc.onboarding.v1.Query", + "Participants", + data + ); + return promise.then((data) => + QueryParticipantsResponse.decode(new _m0.Reader(data)) + ); + } +} + +interface Rpc { + request( + service: string, + method: string, + data: Uint8Array + ): Promise; +} + +type Builtin = + | Date + | Function + | Uint8Array + | string + | number + | boolean + | undefined; + +export type DeepPartial = T extends Builtin + ? T + : T extends Long + ? string | number | Long + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin + ? P + : P & { [K in keyof P]: Exact } & { + [K in Exclude>]: never; + }; + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/proto/cerc/onboarding/v1/tx.ts b/src/proto/cerc/onboarding/v1/tx.ts index 977498f..3897ffd 100644 --- a/src/proto/cerc/onboarding/v1/tx.ts +++ b/src/proto/cerc/onboarding/v1/tx.ts @@ -11,19 +11,13 @@ export interface MsgOnboardParticipant { participant: string; ethPayload?: EthPayload; ethSignature: string; - message: string; } /** MsgOnboardParticipantResponse defines the Msg/OnboardParticipant response type. */ export interface MsgOnboardParticipantResponse {} function createBaseMsgOnboardParticipant(): MsgOnboardParticipant { - return { - participant: "", - ethPayload: undefined, - ethSignature: "", - message: "", - }; + return { participant: "", ethPayload: undefined, ethSignature: "" }; } export const MsgOnboardParticipant = { @@ -40,9 +34,6 @@ export const MsgOnboardParticipant = { if (message.ethSignature !== "") { writer.uint32(26).string(message.ethSignature); } - if (message.message !== "") { - writer.uint32(34).string(message.message); - } return writer; }, @@ -65,9 +56,6 @@ export const MsgOnboardParticipant = { case 3: message.ethSignature = reader.string(); break; - case 4: - message.message = reader.string(); - break; default: reader.skipType(tag & 7); break; @@ -85,7 +73,6 @@ export const MsgOnboardParticipant = { ethSignature: isSet(object.ethSignature) ? String(object.ethSignature) : "", - message: isSet(object.message) ? String(object.message) : "", }; }, @@ -99,7 +86,6 @@ export const MsgOnboardParticipant = { : undefined); message.ethSignature !== undefined && (obj.ethSignature = message.ethSignature); - message.message !== undefined && (obj.message = message.message); return obj; }, @@ -113,7 +99,6 @@ export const MsgOnboardParticipant = { ? EthPayload.fromPartial(object.ethPayload) : undefined; message.ethSignature = object.ethSignature ?? ""; - message.message = object.message ?? ""; return message; }, }; diff --git a/src/registry-client.ts b/src/registry-client.ts index 69d9ab5..4d2caef 100644 --- a/src/registry-client.ts +++ b/src/registry-client.ts @@ -241,7 +241,7 @@ export class RegistryClient { /** * Get records by attributes. */ - async queryRecords (attributes: {[key: string]: any}, all = false, refs = false) { + async queryRecords (attributes: { [key: string]: any }, all = false, refs = false) { if (!attributes) { attributes = {}; } @@ -397,7 +397,7 @@ export class RegistryClient { } /** - * Get records by attributes. + * Get bonds by attributes. */ async queryBonds (attributes = {}) { const query = `query ($attributes: [KeyValueInput!]) { @@ -417,4 +417,20 @@ export class RegistryClient { return RegistryClient.getResult(this._graph(query)(variables), 'queryBonds'); } + + /** + * Get participants. + */ + async getParticipants () { + const query = `query { + getParticipants { + cosmos_address + ethereum_address + } + }`; + + const variables = {}; + + return RegistryClient.getResult(this._graph(query)(variables), 'getParticipants'); + } } diff --git a/src/testing/data/watcher.yml b/src/testing/data/watcher.yml index dddddab..23356e5 100644 --- a/src/testing/data/watcher.yml +++ b/src/testing/data/watcher.yml @@ -7,4 +7,4 @@ record: /: QmP8jTG1m9GSDJLCbeWhVSVgEzCPPwXRdCRuJtQ5Tz9Kc9 tls_cert_cid: /: QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR - version: 1.0.28 + version: 1.0.30 -- 2.45.2 From f51b31b954b8a164be7b97eadc835ff56f5787eb Mon Sep 17 00:00:00 2001 From: Isha Venikar <145848618+IshaVenikar@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:25:33 +0530 Subject: [PATCH 3/9] Remove message field from onboardParticipant message and add build workflow (#7) * Remove message field from onboardParticipant message * Add a workflow to run lint and build --------- Co-authored-by: Prathamesh Musale --- .gitea/workflows/on-pr.yaml | 23 +++++++++++++++++++++++ .github/workflows/on-pr.yaml | 23 +++++++++++++++++++++++ src/index.ts | 3 +-- src/laconic-client.ts | 4 +--- src/onboarding.test.ts | 3 +-- src/types/cerc/onboarding/message.ts | 1 - 6 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 .gitea/workflows/on-pr.yaml create mode 100644 .github/workflows/on-pr.yaml diff --git a/.gitea/workflows/on-pr.yaml b/.gitea/workflows/on-pr.yaml new file mode 100644 index 0000000..d9f3736 --- /dev/null +++ b/.gitea/workflows/on-pr.yaml @@ -0,0 +1,23 @@ +name: Lint & Build + +on: + pull_request: + +jobs: + lint_and_build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [20.x] + steps: + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + registry-url: 'https://npm.pkg.github.com' + - run: yarn + - name: Run lint + run: yarn lint + - name: Run build + run: yarn build diff --git a/.github/workflows/on-pr.yaml b/.github/workflows/on-pr.yaml new file mode 100644 index 0000000..d9f3736 --- /dev/null +++ b/.github/workflows/on-pr.yaml @@ -0,0 +1,23 @@ +name: Lint & Build + +on: + pull_request: + +jobs: + lint_and_build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [20.x] + steps: + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + registry-url: 'https://npm.pkg.github.com' + - run: yarn + - name: Run lint + run: yarn lint + - name: Run build + run: yarn build diff --git a/src/index.ts b/src/index.ts index e94de8a..b3b128e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -440,7 +440,7 @@ export class Registry { /** * Onboard participant. */ - async onboardParticipant ({ ethPayload, ethSignature, message }: MessageMsgOnboardParticipant, privateKey: string, fee: StdFee): Promise { + async onboardParticipant ({ ethPayload, ethSignature }: MessageMsgOnboardParticipant, privateKey: string, fee: StdFee): Promise { const account = new Account(Buffer.from(privateKey, 'hex')); await account.init(); const laconicClient = await this.getLaconicClient(account); @@ -449,7 +449,6 @@ export class Registry { account.address, ethPayload, ethSignature, - message, fee ); } diff --git a/src/laconic-client.ts b/src/laconic-client.ts index 3afa6d4..ec57184 100644 --- a/src/laconic-client.ts +++ b/src/laconic-client.ts @@ -394,7 +394,6 @@ export class LaconicClient extends SigningStargateClient { signer: string, ethPayload: EthPayload, ethSignature: string, - message: string, fee: StdFee | 'auto' | number, memo = '' ) { @@ -403,8 +402,7 @@ export class LaconicClient extends SigningStargateClient { value: { participant: signer, ethPayload, - ethSignature, - message + ethSignature } }; diff --git a/src/onboarding.test.ts b/src/onboarding.test.ts index ea8f1f1..4876112 100644 --- a/src/onboarding.test.ts +++ b/src/onboarding.test.ts @@ -31,8 +31,7 @@ const onboardingTests = () => { await registry.onboardParticipant({ ethPayload, - ethSignature, - message: 'Message signed by cosmos private key' + ethSignature }, privateKey, fee); }); diff --git a/src/types/cerc/onboarding/message.ts b/src/types/cerc/onboarding/message.ts index 567da37..edbeccf 100644 --- a/src/types/cerc/onboarding/message.ts +++ b/src/types/cerc/onboarding/message.ts @@ -23,5 +23,4 @@ interface ethPayload { export interface MessageMsgOnboardParticipant { ethPayload: ethPayload ethSignature: string - message: string } -- 2.45.2 From a871d0c035fbf96bcf7af760e7228452c62b4ffc Mon Sep 17 00:00:00 2001 From: Nabarun Gogoi Date: Fri, 5 Jul 2024 18:38:46 +0530 Subject: [PATCH 4/9] Update CI for using testnet-onboarding laconic2d (#8) * Update CI for using testnet-onboarding laconic2d * Run onboarding test in CI --- .github/workflows/test.yml | 14 ++++++++++++-- package.json | 1 + src/onboarding.test.ts | 15 ++++++++++++++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2b77c8e..234c36c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,9 +15,10 @@ jobs: uses: actions/checkout@v3 with: path: "./laconicd/" - repository: cerc-io/laconicd + repository: deep-stack/laconic2d + token: ${{ secrets.GH_PAT }} # PAT for accessing private laconic2d fetch-depth: 0 - ref: main + ref: testnet-onboarding - name: Environment run: ls -tlh && env @@ -51,3 +52,12 @@ jobs: - name: Run nameservice expiry tests working-directory: laconicd/tests/sdk_tests run: ./run-tests.sh test:nameservice-expiry + + - name: Start containers (onboarding enabled) + working-directory: laconicd/tests/sdk_tests + env: + ONBOARDING_ENABLED: true + run: docker compose up -d + - name: Run nameservice expiry tests + working-directory: laconicd/tests/sdk_tests + run: ./run-tests.sh test:onboarding diff --git a/package.json b/package.json index 17d7150..199abd6 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "test": "jest --runInBand --verbose --testPathPattern=src", "test:auctions": "TEST_AUCTIONS_ENABLED=1 jest --runInBand --verbose src/auction.test.ts", "test:nameservice-expiry": "TEST_NAMESERVICE_EXPIRY=1 jest --runInBand --verbose src/nameservice-expiry.test.ts", + "test:onboarding": "ONBOARDING_ENABLED=1 jest --runInBand --verbose src/onboarding.test.ts", "build": "tsc", "lint": "eslint .", "prepare": "husky" diff --git a/src/onboarding.test.ts b/src/onboarding.test.ts index 4876112..a6c86e6 100644 --- a/src/onboarding.test.ts +++ b/src/onboarding.test.ts @@ -53,4 +53,17 @@ const onboardingTests = () => { }); }; -describe('Onboarding', onboardingTests); +if (!process.env.ONBOARDING_ENABLED) { + // Required as jest complains if file has no tests. + test('skipping onboarding tests', () => {}); +} else { + /** + In laconic2d repo run: + TEST_REGISTRY_EXPIRY=true ./init.sh + + Run tests: + yarn test:onboarding + */ + + describe('Onboarding', onboardingTests); +} -- 2.45.2 From ed5aa06890061e75cb40195cbb3d9b8a3666b772 Mon Sep 17 00:00:00 2001 From: Isha Venikar <145848618+IshaVenikar@users.noreply.github.com> Date: Wed, 10 Jul 2024 18:43:52 +0530 Subject: [PATCH 5/9] Add onboarding disabled test (#9) * Add test for onboarding disabled flag * Add onboardingDisabled test * Modify test message * Add test for onboarding disabled to query participants * Modify test messages * Use participants array type * Remove unnecessary describe block * Remove describe statement --- src/onboarding.test.ts | 85 ++++++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 24 deletions(-) diff --git a/src/onboarding.test.ts b/src/onboarding.test.ts index a6c86e6..772f81b 100644 --- a/src/onboarding.test.ts +++ b/src/onboarding.test.ts @@ -4,12 +4,13 @@ import { DirectSecp256k1Wallet } from '@cosmjs/proto-signing'; import { Registry, Account } from './index'; import { getConfig } from './testing/helper'; +import { Participant } from './proto/cerc/onboarding/v1/onboarding'; const { chainId, rpcEndpoint, gqlEndpoint, privateKey, fee } = getConfig(); jest.setTimeout(90 * 1000); -const onboardingTests = () => { +const onboardingEnabledTests = () => { let registry: Registry; let ethWallet: Wallet; @@ -35,35 +36,71 @@ const onboardingTests = () => { }, privateKey, fee); }); - describe('With participants enrolled', () => { - test('Query participants.', async () => { - const account = new Account(Buffer.from(privateKey, 'hex')); - const cosmosAccount = await DirectSecp256k1Wallet.fromKey(account._privateKey, 'laconic'); - const [cosmosWallet] = await cosmosAccount.getAccounts(); + test('Query participants.', async () => { + const account = new Account(Buffer.from(privateKey, 'hex')); + const cosmosAccount = await DirectSecp256k1Wallet.fromKey(account._privateKey, 'laconic'); + const [cosmosWallet] = await cosmosAccount.getAccounts(); - const expectedParticipants = [ - { - cosmos_address: cosmosWallet.address, - ethereum_address: ethWallet.address - } - ]; - const participants = await registry.getParticipants(); - expect(participants).toEqual(expectedParticipants); - }); + const expectedParticipants = [ + { + cosmos_address: cosmosWallet.address, + ethereum_address: ethWallet.address + } + ]; + const participants = await registry.getParticipants(); + expect(participants).toEqual(expectedParticipants); }); }; -if (!process.env.ONBOARDING_ENABLED) { - // Required as jest complains if file has no tests. - test('skipping onboarding tests', () => {}); +const onboardingDisabledTests = () => { + let registry: Registry; + let ethWallet: Wallet; + + beforeAll(async () => { + registry = new Registry(gqlEndpoint, rpcEndpoint, chainId); + }); + + test('Error on onboarding attempt.', async () => { + const errorMsg = 'Validator onboarding is disabled: invalid request'; + const mnemonic = Account.generateMnemonic(); + ethWallet = Wallet.fromMnemonic(mnemonic); + + const ethPayload = { + address: ethWallet.address, + msg: 'Message signed by ethereum private key' + }; + + const message = JSON.stringify(ethPayload); + const ethSignature = await ethWallet.signMessage(message); + + try { + await registry.onboardParticipant({ + ethPayload, + ethSignature + }, privateKey, fee); + } catch (error: any) { + expect(error.toString()).toContain(errorMsg); + } + }); + + test('No participants onboarded.', async () => { + const expectedParticipants: Participant[] = []; + const participants = await registry.getParticipants(); + expect(participants).toMatchObject(expectedParticipants); + }); +}; + +if (process.env.ONBOARDING_ENABLED !== '1') { + describe('Onboarding disabled', onboardingDisabledTests); } else { /** - In laconic2d repo run: - TEST_REGISTRY_EXPIRY=true ./init.sh + Running this test requires participants onboarding enabled. In laconic2d repo run: + + ONBOARDING_ENABLED=true ./init.sh + + Run test: - Run tests: yarn test:onboarding - */ - - describe('Onboarding', onboardingTests); + */ + describe('Onboarding enabled', onboardingEnabledTests); } -- 2.45.2 From b44cc00b7b83a54f7c074a5155170d6343f6272c Mon Sep 17 00:00:00 2001 From: Isha Venikar <145848618+IshaVenikar@users.noreply.github.com> Date: Mon, 15 Jul 2024 17:14:38 +0530 Subject: [PATCH 6/9] Rename `ethereum_address` to `nitro_address` in onboarding module (#10) * Rename ethereum_address to nitro_address * Use camel case for variables in query fields * Replace snake case with camel case --- proto/cerc/onboarding/v1/onboarding.proto | 4 ++-- src/onboarding.test.ts | 4 ++-- src/proto/cerc/onboarding/v1/onboarding.ts | 20 ++++++++--------- src/registry-client.ts | 26 +++++++++++----------- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/proto/cerc/onboarding/v1/onboarding.proto b/proto/cerc/onboarding/v1/onboarding.proto index 876cc7e..cda954a 100644 --- a/proto/cerc/onboarding/v1/onboarding.proto +++ b/proto/cerc/onboarding/v1/onboarding.proto @@ -19,8 +19,8 @@ message Participant { (gogoproto.moretags) = "json:\"cosmos_address\" yaml:\"cosmos_address\"" ]; - string ethereum_address = 2 [ - (gogoproto.moretags) = "json:\"ethereum_address\" yaml:\"ethereum_address\"" + string nitro_address = 2 [ + (gogoproto.moretags) = "json:\"nitro_address\" yaml:\"nitro_address\"" ]; } // EthPayload defines the payload that is signed by the ethereum private key diff --git a/src/onboarding.test.ts b/src/onboarding.test.ts index 772f81b..81144e5 100644 --- a/src/onboarding.test.ts +++ b/src/onboarding.test.ts @@ -43,8 +43,8 @@ const onboardingEnabledTests = () => { const expectedParticipants = [ { - cosmos_address: cosmosWallet.address, - ethereum_address: ethWallet.address + cosmosAddress: cosmosWallet.address, + nitroAddress: ethWallet.address } ]; const participants = await registry.getParticipants(); diff --git a/src/proto/cerc/onboarding/v1/onboarding.ts b/src/proto/cerc/onboarding/v1/onboarding.ts index 9c514e6..445b4cf 100644 --- a/src/proto/cerc/onboarding/v1/onboarding.ts +++ b/src/proto/cerc/onboarding/v1/onboarding.ts @@ -12,7 +12,7 @@ export interface Params { /** Participant defines the data that will be stored for each enrolled participant */ export interface Participant { cosmosAddress: string; - ethereumAddress: string; + nitroAddress: string; } /** EthPayload defines the payload that is signed by the ethereum private key */ @@ -77,7 +77,7 @@ export const Params = { }; function createBaseParticipant(): Participant { - return { cosmosAddress: "", ethereumAddress: "" }; + return { cosmosAddress: "", nitroAddress: "" }; } export const Participant = { @@ -88,8 +88,8 @@ export const Participant = { if (message.cosmosAddress !== "") { writer.uint32(10).string(message.cosmosAddress); } - if (message.ethereumAddress !== "") { - writer.uint32(18).string(message.ethereumAddress); + if (message.nitroAddress !== "") { + writer.uint32(18).string(message.nitroAddress); } return writer; }, @@ -105,7 +105,7 @@ export const Participant = { message.cosmosAddress = reader.string(); break; case 2: - message.ethereumAddress = reader.string(); + message.nitroAddress = reader.string(); break; default: reader.skipType(tag & 7); @@ -120,8 +120,8 @@ export const Participant = { cosmosAddress: isSet(object.cosmosAddress) ? String(object.cosmosAddress) : "", - ethereumAddress: isSet(object.ethereumAddress) - ? String(object.ethereumAddress) + nitroAddress: isSet(object.nitroAddress) + ? String(object.nitroAddress) : "", }; }, @@ -130,8 +130,8 @@ export const Participant = { const obj: any = {}; message.cosmosAddress !== undefined && (obj.cosmosAddress = message.cosmosAddress); - message.ethereumAddress !== undefined && - (obj.ethereumAddress = message.ethereumAddress); + message.nitroAddress !== undefined && + (obj.nitroAddress = message.nitroAddress); return obj; }, @@ -140,7 +140,7 @@ export const Participant = { ): Participant { const message = createBaseParticipant(); message.cosmosAddress = object.cosmosAddress ?? ""; - message.ethereumAddress = object.ethereumAddress ?? ""; + message.nitroAddress = object.nitroAddress ?? ""; return message; }, }; diff --git a/src/registry-client.ts b/src/registry-client.ts index 4d2caef..bf28a75 100644 --- a/src/registry-client.ts +++ b/src/registry-client.ts @@ -151,31 +151,31 @@ export class RegistryClient { moniker } sync { - latest_block_hash - latest_block_height - latest_block_time - catching_up + latestBlockHash + latestBlockHeight + latestBlockTime + catchingUp } validator { address - voting_power + votingPower } validators { address - voting_power - proposer_priority + votingPower + proposerPriority } - num_peers + numPeers peers { node { id network moniker } - is_outbound - remote_ip + isOutbound + remoteIp } - disk_usage + diskUsage } }`; @@ -424,8 +424,8 @@ export class RegistryClient { async getParticipants () { const query = `query { getParticipants { - cosmos_address - ethereum_address + cosmosAddress + nitroAddress } }`; -- 2.45.2 From fa239cf400f356338009079731a1b7b7b071c8c1 Mon Sep 17 00:00:00 2001 From: Nabarun Date: Tue, 16 Jul 2024 11:02:54 +0530 Subject: [PATCH 7/9] Add onboarding test in gitea CI --- .gitea/workflows/test.yml | 11 ++++++++++- .github/workflows/test.yml | 7 +++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index e39c7ca..f3f836e 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -22,7 +22,7 @@ jobs: path: "./laconicd/" repository: cerc-io/laconic2d # TODO: Update to laconicd on repo renaming fetch-depth: 0 - ref: main + ref: testnet-onboarding - name: Environment run: ls -tlh && env @@ -61,3 +61,12 @@ jobs: - name: Run nameservice expiry tests working-directory: laconicd/tests/sdk_tests run: ./run-tests.sh test:nameservice-expiry + + - name: Start containers (onboarding enabled) + working-directory: laconicd/tests/sdk_tests + env: + ONBOARDING_ENABLED: true + run: docker compose up -d + - name: Run participants onboarding tests + working-directory: laconicd/tests/sdk_tests + run: ./run-tests.sh test:onboarding diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 234c36c..a45b8e6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,10 +15,9 @@ jobs: uses: actions/checkout@v3 with: path: "./laconicd/" - repository: deep-stack/laconic2d - token: ${{ secrets.GH_PAT }} # PAT for accessing private laconic2d + repository: cerc-io/laconic2d fetch-depth: 0 - ref: testnet-onboarding + ref: main - name: Environment run: ls -tlh && env @@ -58,6 +57,6 @@ jobs: env: ONBOARDING_ENABLED: true run: docker compose up -d - - name: Run nameservice expiry tests + - name: Run participants onboarding tests working-directory: laconicd/tests/sdk_tests run: ./run-tests.sh test:onboarding -- 2.45.2 From 6251fb6c8219af2cae40ae084fdbf7d92cc393e3 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Tue, 16 Jul 2024 12:12:42 +0530 Subject: [PATCH 8/9] Install yarn in the on-pr workflow --- .gitea/workflows/on-pr.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitea/workflows/on-pr.yaml b/.gitea/workflows/on-pr.yaml index d9f3736..b20b248 100644 --- a/.gitea/workflows/on-pr.yaml +++ b/.gitea/workflows/on-pr.yaml @@ -11,6 +11,10 @@ jobs: node-version: [20.x] steps: - uses: actions/checkout@v3 + - name: Download yarn + run: | + curl -fsSL -o /usr/local/bin/yarn https://github.com/yarnpkg/yarn/releases/download/v1.22.21/yarn-1.22.21.js + chmod +x /usr/local/bin/yarn - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: -- 2.45.2 From 4ac1cba093b5743ff2b0e20a00144c76e3492f7c Mon Sep 17 00:00:00 2001 From: Nabarun Date: Tue, 16 Jul 2024 13:03:31 +0530 Subject: [PATCH 9/9] Update laconic2d branch to main in CI --- .gitea/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index f3f836e..561430e 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -22,7 +22,7 @@ jobs: path: "./laconicd/" repository: cerc-io/laconic2d # TODO: Update to laconicd on repo renaming fetch-depth: 0 - ref: testnet-onboarding + ref: main - name: Environment run: ls -tlh && env -- 2.45.2