From 8208415cffeb3c4039095556abc60a07f6a46998 Mon Sep 17 00:00:00 2001 From: Alexandru Matei Date: Tue, 30 Aug 2022 15:44:45 +0300 Subject: [PATCH 1/2] feat: update chain methods group --- src/providers/Types.ts | 10 ++ src/providers/method-groups/chain.ts | 200 ++++++++++++++++++++------- tests/src/test-chain.ts | 68 +++++++++ 3 files changed, 225 insertions(+), 53 deletions(-) diff --git a/src/providers/Types.ts b/src/providers/Types.ts index 1415502..fb94ed9 100644 --- a/src/providers/Types.ts +++ b/src/providers/Types.ts @@ -1203,6 +1203,16 @@ export class DataCIDSize { PieceCID!: Cid; } +export type IpldObject = { + Cid: Cid; + Obj: any; +} + +export type PruneOpts = { + MovingGC: boolean + RetainState: number +} + /** * Interface to be implemented by all providers. * diff --git a/src/providers/method-groups/chain.ts b/src/providers/method-groups/chain.ts index c5b3099..60269e2 100644 --- a/src/providers/method-groups/chain.ts +++ b/src/providers/method-groups/chain.ts @@ -4,9 +4,11 @@ import { BlockMessages, ChainEpoch, Cid, HeadChange, + IpldObject, Message, MessageReceipt, ObjStat, + PruneOpts, TipSet, TipSetKey, WrappedMessage, } from '../Types'; @@ -24,41 +26,6 @@ export class JsonRpcChainMethodGroup { this.conn = conn; } - /** - * reads ipld nodes referenced by the specified CID from chain blockstore and returns raw bytes. - * @param cid - */ - public async readObj(cid: Cid): Promise { - const ret = await this.conn.request({ method: 'Filecoin.ChainReadObj', params: [cid] }); - return ret as string; - } - - /** - * deletes node referenced by the given CID - * @param cid - */ - public async deleteObj(cid: Cid): Promise { - const error = await this.conn.request({ method: 'Filecoin.ChainDeleteObj', params: [cid] }); - return error as string; - } - - /** - * returns messages stored in the specified block. - * @param blockCid - */ - public async getBlockMessages(blockCid: Cid): Promise { - const ret = await this.conn.request({ method: 'Filecoin.ChainGetBlockMessages', params: [blockCid] }); - return ret as BlockMessages; - } - - /** - * returns the current head of the chain - */ - public async getHead(): Promise { - const ret = await this.conn.request({ method: 'Filecoin.ChainHead' }); - return ret as TipSet; - } - /** * call back on chain head updates. * @param cb @@ -81,12 +48,12 @@ export class JsonRpcChainMethodGroup { } } - public stopChainNotify(id?: any) { - if (this.conn instanceof HttpJsonRpcConnector && id) { - clearInterval(id); - } else if (this.conn instanceof WsJsonRpcConnector) { - this.conn.closeSubscription(id); - } + /** + * returns the current head of the chain + */ + public async getHead(): Promise { + const ret = await this.conn.request({ method: 'Filecoin.ChainHead' }); + return ret as TipSet; } /** @@ -99,16 +66,37 @@ export class JsonRpcChainMethodGroup { } /** - * reads a message referenced by the specified CID from the chain blockstore - * @param messageCid + * Returns the tipset specified by the given TipSetKey. + * @param tipSetKey */ - public async getMessage(messageCid: Cid): Promise { - const ret = await this.conn.request({ method: 'Filecoin.ChainGetMessage', params: [messageCid] }); - return ret as Message; + public async getTipSet(tipSetKey: TipSetKey): Promise { + const ret: TipSet = await this.conn.request({ method: 'Filecoin.ChainGetTipSet', params: [tipSetKey] }); + return ret; } /** - * returns receipts for messages in parent tipset of the specified block + * returns messages stored in the specified block. + * @param blockCid + * + * Note: If there are multiple blocks in a tipset, it's likely that some + * messages will be duplicated. It's also possible for blocks in a tipset to have + * different messages from the same sender at the same nonce. When that happens, + * only the first message (in a block with lowest ticket) will be considered + * for execution + * + * NOTE: THIS METHOD SHOULD ONLY BE USED FOR GETTING MESSAGES IN A SPECIFIC BLOCK + * + * DO NOT USE THIS METHOD TO GET MESSAGES INCLUDED IN A TIPSET + * Use ChainGetParentMessages, which will perform correct message deduplication + */ + public async getBlockMessages(blockCid: Cid): Promise { + const ret = await this.conn.request({ method: 'Filecoin.ChainGetBlockMessages', params: [blockCid] }); + return ret as BlockMessages; + } + + /** + * returns receipts for messages in parent tipset of the specified block. The receipts in the list returned are one-to-one with the + * messages returned by a call to ChainGetParentMessages with the same blockCid. * @param blockCid */ public async getParentReceipts(blockCid: Cid): Promise { @@ -125,6 +113,53 @@ export class JsonRpcChainMethodGroup { return ret as WrappedMessage[]; } + + /** + * returns message stores in current tipset + * @param tipSetKey + */ + public async getMessagesInTipset(tipSetKey: TipSetKey): Promise { + const ret = await this.conn.request({ method: 'Filecoin.ChainGetMessagesInTipset', params: [tipSetKey] }); + return ret as WrappedMessage[]; + } + + /** + * looks back for a tipset at the specified epoch. If there are no blocks at the specified epoch, a tipset at an earlier epoch will be returned. + * @param epochNumber + */ + public async getTipSetByHeight(epochNumber: number): Promise { + const ret: TipSet = await this.conn.request({ method: 'Filecoin.ChainGetTipSetByHeight', params: [epochNumber, []] }); + return ret; + } + + /** + * looks back for a tipset at the specified epoch. If there are no blocks at the specified epoch, the first non-nil tipset at a later epoch + * will be returned. + * @param epochNumber + */ + public async getTipSetAfterHeight(epochNumber: number, tipSetKey?: TipSetKey): Promise { + const ret: TipSet = await this.conn.request({ method: 'Filecoin.ChainGetTipSetAfterHeight', params: [epochNumber, tipSetKey] }); + return ret; + } + + /** + * reads ipld nodes referenced by the specified CID from chain blockstore and returns raw bytes. + * @param cid + */ + public async readObj(cid: Cid): Promise { + const ret = await this.conn.request({ method: 'Filecoin.ChainReadObj', params: [cid] }); + return ret as string; + } + + /** + * deletes node referenced by the given CID + * @param cid + */ + public async deleteObj(cid: Cid): Promise { + const error = await this.conn.request({ method: 'Filecoin.ChainDeleteObj', params: [cid] }); + return error as string; + } + /** * checks if a given CID exists in the chain blockstore * @param cid @@ -173,16 +208,25 @@ export class JsonRpcChainMethodGroup { } /** - * looks back for a tipset at the specified epoch. - * @param epochNumber + * @param p */ - public async getTipSetByHeight(epochNumber: number): Promise { - const ret: TipSet = await this.conn.request({ method: 'Filecoin.ChainGetTipSetByHeight', params: [epochNumber, []] }); - return ret; + public async getNode(p: string): Promise { + const node: IpldObject = await this.conn.request({ method: 'Filecoin.ChainGetNode', params: [p] }); + return node; } /** - * Returns a set of revert/apply operations needed to get from + * reads a message referenced by the specified CID from the chain blockstore + * @param messageCid + */ + public async getMessage(messageCid: Cid): Promise { + const ret = await this.conn.request({ method: 'Filecoin.ChainGetMessage', params: [messageCid] }); + return ret as Message; + } + + /** + * Returns a set of revert/apply operations needed to get from one tipset to another, for example: + * * @param from * @param to */ @@ -203,4 +247,54 @@ export class JsonRpcChainMethodGroup { const path: HeadChange[] = await this.conn.request({ method: 'Filecoin.ChainExport', params: [nroots, oldmsgskip, tipSetKey] }); return path; } + + /** + * Prunes the stored chain state and garbage collects; only supported if you are using the splitstore + * + * @param opts + */ + public async prune(opts: PruneOpts) { + return this.conn.request({ method: 'Filecoin.ChainPrune', params: [opts] }); + } + + /** + * Performs an (asynchronous) health check on the chain/state blockstore if supported by the underlying implementation. + */ + public async checkBlockstore() { + return this.conn.request({ method: 'Filecoin.ChainCheckBlockstore', params: [] }); + } + + /** + * Returns some basic information about the blockstore + */ + public async blockstoreInfo(): Promise { + return this.conn.request({ method: 'Filecoin.ChainBlockstoreInfo', params: [] }); + } + + /** + * Estimates gas fee cap + * @param message + * @param n + * @param tipSetKey + */ + public async gasEstimateFeeCap(message: Message, n: number, tipSetKey?: TipSetKey): Promise { + return this.conn.request({ method: 'Filecoin.GasEstimateFeeCap', params: [message, n, tipSetKey] }); + } + + /** + * Estimates gas used by the message and returns it. It fails if message fails to execute. + * @param message + * @param tipSetKey + */ + public async gasEstimateGasLimit(message: Message, tipSetKey?: TipSetKey): Promise { + return this.conn.request({ method: 'Filecoin.GasEstimateGasLimit', params: [message, tipSetKey] }); + } + + public stopChainNotify(id?: any) { + if (this.conn instanceof HttpJsonRpcConnector && id) { + clearInterval(id); + } else if (this.conn instanceof WsJsonRpcConnector) { + this.conn.closeSubscription(id); + } + } } diff --git a/tests/src/test-chain.ts b/tests/src/test-chain.ts index 21a5ce9..fc88f38 100644 --- a/tests/src/test-chain.ts +++ b/tests/src/test-chain.ts @@ -137,6 +137,74 @@ describe("Chain methods", function() { await provider.release(); }); + it("should get tipset [http]", async function() { + const provider = new LotusClient(httpConnector); + const tipSetKey = await provider.chain.getTipSetByHeight(10); + await provider.chain.getTipSet([tipSetKey.Cids[0]]); + }); + + it("should get tipset [ws]", async function() { + const provider = new LotusClient(wsConnector); + const tipSetKey = await provider.chain.getTipSetByHeight(10); + await provider.chain.getTipSet([tipSetKey.Cids[0]]); + await provider.release(); + }); + + it("should check tipset after height [http]", async function() { + const con = new LotusClient(httpConnector); + const tipSet = await con.chain.getTipSetAfterHeight(1); + assert.strictEqual(typeof tipSet.Height === "number", true, "invalid tipset after height"); + }); + + it("should check tipset after height [ws]", async function() { + const provider = new LotusClient(wsConnector); + const tipSet = await provider.chain.getTipSetAfterHeight(1); + assert.strictEqual(typeof tipSet.Height === "number", true, "invalid tipset after height"); + await provider.release(); + }); + + it("should get gas fee cap [http]", async function() { + const con = new LotusClient(httpConnector); + const messages = await con.state.listMessages({ + To: 't01000' + }); + const message = await con.chain.getMessage(messages[0]); + const cap = await con.chain.gasEstimateFeeCap(message, 0); + assert.strictEqual(typeof cap === "string", true, "invalid gas fee cap"); + }); + + it("should get gas fee cap [ws]", async function() { + const provider = new LotusClient(wsConnector); + const messages = await provider.state.listMessages({ + To: 't01000' + }); + const message = await provider.chain.getMessage(messages[0]); + const cap = await provider.chain.gasEstimateFeeCap(message, 0); + assert.strictEqual(typeof cap === "string", true, "invalid gas fee cap"); + await provider.release(); + }); + + it("should get estimate gas limit [http]", async function() { + const con = new LotusClient(httpConnector); + const messages = await con.state.listMessages({ + To: 't01000' + }); + const message = await con.chain.getMessage(messages[0]); + const cap = await con.chain.gasEstimateGasLimit(message); + assert.strictEqual(typeof cap === "number", true, "invalid estimate gas limit"); + }); + + it("should get estimate gas limit [ws]", async function() { + const provider = new LotusClient(wsConnector); + const messages = await provider.state.listMessages({ + To: 't01000' + }); + const message = await provider.chain.getMessage(messages[0]); + const cap = await provider.chain.gasEstimateGasLimit(message); + assert.strictEqual(typeof cap === "number", true, "invalid estimate gas limit"); + await provider.release(); + }); + // This test fails in the current testnet setup with the following error: RPC Error: method 'Filecoin.ChainExport' not supported in this mode (no out channel support) // it("export chain", async () => { // const provider = new LotusClient(httpConnector); From 258f6eeeae010bd4a74caa8415fc3b0a2cb5c175 Mon Sep 17 00:00:00 2001 From: Alexandru Matei Date: Mon, 19 Sep 2022 10:48:32 +0300 Subject: [PATCH 2/2] feat: update mpool method group --- src/providers/Types.ts | 9 +++++++++ src/providers/method-groups/mpool.ts | 30 +++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/providers/Types.ts b/src/providers/Types.ts index fb94ed9..541764f 100644 --- a/src/providers/Types.ts +++ b/src/providers/Types.ts @@ -1191,6 +1191,15 @@ export class MsigVesting { export type NetworkVersion = number; +export class MessagePrototype { + Message!: Message; + ValidNonce!: boolean; +} + +export class MessageCheckStatus { + Cid!: Cid; + CheckStatus!: any; +} export class MessageMatch { To?: Address; diff --git a/src/providers/method-groups/mpool.ts b/src/providers/method-groups/mpool.ts index ceb7ca6..3b7ff97 100644 --- a/src/providers/method-groups/mpool.ts +++ b/src/providers/method-groups/mpool.ts @@ -1,5 +1,5 @@ import { Connector } from '../../connectors/Connector'; -import { Cid, Message, MpoolConfig, MpoolUpdate, SignedMessage, TipSetKey } from '../Types'; +import { Address, Cid, Message, MessageCheckStatus, MessagePrototype, MpoolConfig, MpoolUpdate, SignedMessage, TipSetKey } from '../Types'; import { WsJsonRpcConnector } from '../../index'; /** @@ -139,4 +139,32 @@ export class JsonRpcMPoolMethodGroup { const ret = await this.conn.request({ method: 'Filecoin.MpoolBatchPushMessage', params: [messages] }); return ret; } + + /** + * Performs logical checks on a batch of messages + * @param msgPrototype + */ + public async checkMessages(msgPrototype: MessagePrototype[]): Promise { + const ret = await this.conn.request({ method: 'Filecoin.MpoolCheckMessages', params: [msgPrototype] }); + return ret; + } + + + /** + * Performs logical checks for all pending messages from a given address + * @param address + */ + public async checkPendingMessages(address: Address[]): Promise { + const ret = await this.conn.request({ method: 'Filecoin.MpoolCheckPendingMessages', params: [address] }); + return ret; + } + + /** + * Performs logical checks on pending messages with replacement + * @param msg + */ + public async checkReplaceMessages(msg: Message[]): Promise { + const ret = await this.conn.request({ method: 'Filecoin.MpoolCheckReplaceMessages', params: [msg] }); + return ret; + } }