From a0d7b0dcd66f60000fc608fc5ff0d56e3fe3c697 Mon Sep 17 00:00:00 2001 From: Xueying Wang Date: Mon, 20 Jan 2025 10:45:11 +0100 Subject: [PATCH] fix interlay dmp receive --- .../services/agents/xcm/ops/common.spec.ts | 385 +++++++++++------- .../src/services/agents/xcm/ops/common.ts | 97 ++++- .../src/services/agents/xcm/ops/xcmp.spec.ts | 87 +--- .../src/services/agents/xcm/ops/xcmp.ts | 74 +--- .../src/services/agents/xcm/tracking.spec.ts | 22 + .../src/services/agents/xcm/tracking.ts | 21 +- .../__data__/blocks/interlay/7025155.cbor | Bin 0 -> 31148 bytes packages/server/src/testing/tools/download.ts | 1 + 8 files changed, 379 insertions(+), 308 deletions(-) create mode 100644 packages/server/src/testing/__data__/blocks/interlay/7025155.cbor diff --git a/packages/server/src/services/agents/xcm/ops/common.spec.ts b/packages/server/src/services/agents/xcm/ops/common.spec.ts index 7c0a0f2e..ad4540b1 100644 --- a/packages/server/src/services/agents/xcm/ops/common.spec.ts +++ b/packages/server/src/services/agents/xcm/ops/common.spec.ts @@ -1,117 +1,240 @@ import { from, of } from 'rxjs' -import { apiContext } from '@/testing/xcm.js' +import { extractEvents } from '@/common/index.js' +import { testBlocksFrom } from '@/testing/blocks.js' +import { apiContext, xcmpReceive } from '@/testing/xcm.js' import { GenericXcmSentWithContext } from '../types.js' -import { mapXcmSent } from './common.js' +import { extractParachainReceive, mapXcmSent } from './common.js' import { getMessageId } from './util.js' import { asVersionedXcm, fromXcmpFormat } from './xcm-format.js' -describe('extract waypoints operator', () => { - describe('mapXcmSent', () => { - it('should extract stops for a V2 XCM message without hops', async () => { - const calls = vi.fn() - const moon5531424 = - '0002100004000000001700004b3471bb156b050a13000000001700004b3471bb156b05010300286bee0d010004000101001e08eb75720cb63fbfcbe7237c6d9b7cf6b4953518da6b38731d5bc65b9ffa32021000040000000017206d278c7e297945030a130000000017206d278c7e29794503010300286bee0d010004000101000257fd81d0a71b094c2c8d3e6c93a9b01a31a43d38408bb2c4c2b49a4c58eb01' - const buf = new Uint8Array(Buffer.from(moon5531424, 'hex')) - const xcms = fromXcmpFormat(buf, apiContext) - const test$ = mapXcmSent( - apiContext, - 'urn:ocn:local:2004', - )( - from( - xcms.map( - (x) => - new GenericXcmSentWithContext({ - event: {}, - sender: { signer: { id: 'xyz', publicKey: '0x01' }, extraSigners: [] }, - blockHash: '0x01', - blockNumber: '32', - extrinsicPosition: 4, - recipient: 'urn:ocn:local:2104', - messageDataBuffer: buf, - messageHash: x.hash, - messageId: getMessageId(x), - instructions: { - bytes: x.data, - json: x.instructions, - }, - }), +describe('common xcm operators', () => { + describe('extract waypoints operator', () => { + describe('mapXcmSent', () => { + it('should extract stops for a V2 XCM message without hops', async () => { + const calls = vi.fn() + const moon5531424 = + '0002100004000000001700004b3471bb156b050a13000000001700004b3471bb156b05010300286bee0d010004000101001e08eb75720cb63fbfcbe7237c6d9b7cf6b4953518da6b38731d5bc65b9ffa32021000040000000017206d278c7e297945030a130000000017206d278c7e29794503010300286bee0d010004000101000257fd81d0a71b094c2c8d3e6c93a9b01a31a43d38408bb2c4c2b49a4c58eb01' + const buf = new Uint8Array(Buffer.from(moon5531424, 'hex')) + const xcms = fromXcmpFormat(buf, apiContext) + const test$ = mapXcmSent( + apiContext, + 'urn:ocn:local:2004', + )( + from( + xcms.map( + (x) => + new GenericXcmSentWithContext({ + event: {}, + sender: { signer: { id: 'xyz', publicKey: '0x01' }, extraSigners: [] }, + blockHash: '0x01', + blockNumber: '32', + extrinsicPosition: 4, + recipient: 'urn:ocn:local:2104', + messageDataBuffer: buf, + messageHash: x.hash, + messageId: getMessageId(x), + instructions: { + bytes: x.data, + json: x.instructions, + }, + }), + ), + ), + ) + + await new Promise((resolve) => { + test$.subscribe({ + next: (msg) => { + expect(msg).toBeDefined() + expect(msg.waypoint.chainId).toBe('urn:ocn:local:2004') + expect(msg.legs.length).toBe(1) + expect(msg.legs[0]).toEqual({ + from: 'urn:ocn:local:2004', + to: 'urn:ocn:local:2104', + relay: 'urn:ocn:local:0', + type: 'hrmp', + }) + expect(msg.destination.chainId).toBe('urn:ocn:local:2104') + calls() + }, + complete: () => { + expect(calls).toHaveBeenCalledTimes(2) + resolve() + }, + }) + }) + }) + + it('should extract stops for a XCM message hopping with InitiateReserveWithdraw', async () => { + const calls = vi.fn() + const polka19505060 = + '0310000400010300a10f043205011f000700f2052a011300010300a10f043205011f000700f2052a010010010204010100a10f0813000002043205011f0002093d00000d0102040001010081bd2c1d40052682633fb3e67eff151b535284d1d1a9633613af14006656f42b2c8e75728b841da22d8337ff5fadd1264f13addcdee755b01ce1a3afb9ef629b9a' + const buf = new Uint8Array(Buffer.from(polka19505060, 'hex')) + const xcm = asVersionedXcm(buf, apiContext) + const test$ = mapXcmSent( + apiContext, + 'urn:ocn:local:0', + )( + of( + new GenericXcmSentWithContext({ + event: {}, + sender: { signer: { id: 'xyz', publicKey: '0x01' }, extraSigners: [] }, + blockHash: '0x01', + blockNumber: '32', + extrinsicPosition: 4, + recipient: 'urn:ocn:local:2034', + messageDataBuffer: buf, + messageHash: xcm.hash, + messageId: getMessageId(xcm), + instructions: { + bytes: xcm.data, + json: xcm.instructions, + }, + }), ), - ), - ) + ) + + await new Promise((resolve) => { + test$.subscribe({ + next: (msg) => { + expect(msg).toBeDefined() + expect(msg.waypoint.chainId).toBe('urn:ocn:local:0') + expect(msg.legs.length).toBe(2) + expect(msg.legs[0]).toEqual({ + from: 'urn:ocn:local:0', + to: 'urn:ocn:local:2034', + type: 'hop', + }) + expect(msg.legs[1]).toEqual({ + from: 'urn:ocn:local:2034', + to: 'urn:ocn:local:1000', + relay: 'urn:ocn:local:0', + partialMessage: + '0x030813000002043205011f0002093d00000d0102040001010081bd2c1d40052682633fb3e67eff151b535284d1d1a9633613af14006656f42b', + type: 'hrmp', + }) + expect(msg.destination.chainId).toBe('urn:ocn:local:1000') + calls() + }, + complete: () => { + expect(calls).toHaveBeenCalledTimes(1) + resolve() + }, + }) + }) + }) + + it('should extract stops for a XCM message hopping with DepositReserveAsset', async () => { + const calls = vi.fn() + const heiko5389341 = + '0003100004000000000f251850c822be030a13000000000f120c286411df01000e010204010100411f081300010100511f000f120c286411df01000d01020400010100842745b99b8042d28a7c677d9469332bfc24aa5266c7ec57c43c7af125a0c16c' + const buf = new Uint8Array(Buffer.from(heiko5389341, 'hex')) + const xcms = fromXcmpFormat(buf, apiContext) + const test$ = mapXcmSent( + apiContext, + 'urn:ocn:local:2085', + )( + from( + xcms.map( + (x) => + new GenericXcmSentWithContext({ + event: {}, + sender: { signer: { id: 'xyz', publicKey: '0x01' }, extraSigners: [] }, + blockHash: '0x01', + blockNumber: '32', + extrinsicPosition: 4, + recipient: 'urn:ocn:local:2004', + messageDataBuffer: buf, + messageHash: x.hash, + messageId: getMessageId(x), + instructions: { + bytes: x.data, + json: x.instructions, + }, + }), + ), + ), + ) + + await new Promise((resolve) => { + test$.subscribe({ + next: (msg) => { + expect(msg).toBeDefined() + expect(msg.waypoint.chainId).toBe('urn:ocn:local:2085') + + expect(msg.legs.length).toBe(2) + expect(msg.legs[0]).toEqual({ + from: 'urn:ocn:local:2085', + to: 'urn:ocn:local:2004', + relay: 'urn:ocn:local:0', + type: 'hop', + }) + expect(msg.legs[1]).toEqual({ + from: 'urn:ocn:local:2004', + to: 'urn:ocn:local:2000', + relay: 'urn:ocn:local:0', + partialMessage: + '0x03081300010100511f000f120c286411df01000d01020400010100842745b99b8042d28a7c677d9469332bfc24aa5266c7ec57c43c7af125a0c16c', + type: 'hrmp', + }) + + expect(msg.destination.chainId).toBe('urn:ocn:local:2000') + calls() + }, + complete: () => { + expect(calls).toHaveBeenCalledTimes(1) + resolve() + }, + }) + }) + }) + }) + }) + + describe('extractParachainReceive', () => { + it('should extract XCMP receive with outcome success', async () => { + const { successBlocks } = xcmpReceive + const calls = vi.fn() + const test$ = extractParachainReceive()(successBlocks.pipe(extractEvents())) await new Promise((resolve) => { test$.subscribe({ next: (msg) => { expect(msg).toBeDefined() - expect(msg.waypoint.chainId).toBe('urn:ocn:local:2004') - expect(msg.legs.length).toBe(1) - expect(msg.legs[0]).toEqual({ - from: 'urn:ocn:local:2004', - to: 'urn:ocn:local:2104', - relay: 'urn:ocn:local:0', - type: 'hrmp', - }) - expect(msg.destination.chainId).toBe('urn:ocn:local:2104') + expect(msg.blockNumber).toBeDefined() + expect(msg.blockHash).toBeDefined() + expect(msg.event).toBeDefined() + expect(msg.messageHash).toBeDefined() + expect(msg.timestamp).toBeDefined() + expect(msg.outcome).toBeDefined() + expect(msg.outcome).toBe('Success') calls() }, complete: () => { - expect(calls).toHaveBeenCalledTimes(2) + expect(calls).toHaveBeenCalledTimes(1) resolve() }, }) }) }) - it('should extract stops for a XCM message hopping with InitiateReserveWithdraw', async () => { + it('should extract failed XCMP received message with error', async () => { + const { failBlocks } = xcmpReceive const calls = vi.fn() - const polka19505060 = - '0310000400010300a10f043205011f000700f2052a011300010300a10f043205011f000700f2052a010010010204010100a10f0813000002043205011f0002093d00000d0102040001010081bd2c1d40052682633fb3e67eff151b535284d1d1a9633613af14006656f42b2c8e75728b841da22d8337ff5fadd1264f13addcdee755b01ce1a3afb9ef629b9a' - const buf = new Uint8Array(Buffer.from(polka19505060, 'hex')) - const xcm = asVersionedXcm(buf, apiContext) - const test$ = mapXcmSent( - apiContext, - 'urn:ocn:local:0', - )( - of( - new GenericXcmSentWithContext({ - event: {}, - sender: { signer: { id: 'xyz', publicKey: '0x01' }, extraSigners: [] }, - blockHash: '0x01', - blockNumber: '32', - extrinsicPosition: 4, - recipient: 'urn:ocn:local:2034', - messageDataBuffer: buf, - messageHash: xcm.hash, - messageId: getMessageId(xcm), - instructions: { - bytes: xcm.data, - json: xcm.instructions, - }, - }), - ), - ) + const test$ = extractParachainReceive()(failBlocks.pipe(extractEvents())) await new Promise((resolve) => { test$.subscribe({ next: (msg) => { expect(msg).toBeDefined() - expect(msg.waypoint.chainId).toBe('urn:ocn:local:0') - expect(msg.legs.length).toBe(2) - expect(msg.legs[0]).toEqual({ - from: 'urn:ocn:local:0', - to: 'urn:ocn:local:2034', - type: 'hop', - }) - expect(msg.legs[1]).toEqual({ - from: 'urn:ocn:local:2034', - to: 'urn:ocn:local:1000', - relay: 'urn:ocn:local:0', - partialMessage: - '0x030813000002043205011f0002093d00000d0102040001010081bd2c1d40052682633fb3e67eff151b535284d1d1a9633613af14006656f42b', - type: 'hrmp', - }) - expect(msg.destination.chainId).toBe('urn:ocn:local:1000') + expect(msg.blockNumber).toBeDefined() + expect(msg.blockHash).toBeDefined() + expect(msg.event).toBeDefined() + expect(msg.messageHash).toBeDefined() + expect(msg.outcome).toBeDefined() + expect(msg.outcome).toBe('Fail') + expect(msg.timestamp).toBeDefined() calls() }, complete: () => { @@ -122,61 +245,24 @@ describe('extract waypoints operator', () => { }) }) - it('should extract stops for a XCM message hopping with DepositReserveAsset', async () => { + it('should extract assets trapped info on XCMP received message for V4 assets', async () => { + const { trappedBlocks } = xcmpReceive const calls = vi.fn() - const heiko5389341 = - '0003100004000000000f251850c822be030a13000000000f120c286411df01000e010204010100411f081300010100511f000f120c286411df01000d01020400010100842745b99b8042d28a7c677d9469332bfc24aa5266c7ec57c43c7af125a0c16c' - const buf = new Uint8Array(Buffer.from(heiko5389341, 'hex')) - const xcms = fromXcmpFormat(buf, apiContext) - const test$ = mapXcmSent( - apiContext, - 'urn:ocn:local:2085', - )( - from( - xcms.map( - (x) => - new GenericXcmSentWithContext({ - event: {}, - sender: { signer: { id: 'xyz', publicKey: '0x01' }, extraSigners: [] }, - blockHash: '0x01', - blockNumber: '32', - extrinsicPosition: 4, - recipient: 'urn:ocn:local:2004', - messageDataBuffer: buf, - messageHash: x.hash, - messageId: getMessageId(x), - instructions: { - bytes: x.data, - json: x.instructions, - }, - }), - ), - ), - ) + const test$ = extractParachainReceive()(trappedBlocks.pipe(extractEvents())) await new Promise((resolve) => { test$.subscribe({ next: (msg) => { expect(msg).toBeDefined() - expect(msg.waypoint.chainId).toBe('urn:ocn:local:2085') - - expect(msg.legs.length).toBe(2) - expect(msg.legs[0]).toEqual({ - from: 'urn:ocn:local:2085', - to: 'urn:ocn:local:2004', - relay: 'urn:ocn:local:0', - type: 'hop', - }) - expect(msg.legs[1]).toEqual({ - from: 'urn:ocn:local:2004', - to: 'urn:ocn:local:2000', - relay: 'urn:ocn:local:0', - partialMessage: - '0x03081300010100511f000f120c286411df01000d01020400010100842745b99b8042d28a7c677d9469332bfc24aa5266c7ec57c43c7af125a0c16c', - type: 'hrmp', - }) - - expect(msg.destination.chainId).toBe('urn:ocn:local:2000') + expect(msg.blockNumber).toBeDefined() + expect(msg.blockHash).toBeDefined() + expect(msg.event).toBeDefined() + expect(msg.messageHash).toBeDefined() + expect(msg.outcome).toBeDefined() + expect(msg.outcome).toBe('Fail') + expect(msg.assetsTrapped).toBeDefined() + expect(msg.assetsTrapped?.assets).toBeDefined() + expect(msg.timestamp).toBeDefined() calls() }, complete: () => { @@ -186,5 +272,32 @@ describe('extract waypoints operator', () => { }) }) }) + + it('should extract dmpQueue.ExecutedDownward events', async () => { + const blocks = from(testBlocksFrom('interlay/7025155.cbor')) + const calls = vi.fn() + const test$ = extractParachainReceive()(blocks.pipe(extractEvents())) + + await new Promise((resolve) => { + test$.subscribe({ + next: (msg) => { + calls() + expect(msg).toBeDefined() + expect(msg.blockNumber).toBeDefined() + expect(msg.blockHash).toBeDefined() + expect(msg.event).toBeDefined() + expect(msg.messageHash).toBeDefined() + expect(msg.outcome).toBeDefined() + expect(msg.outcome).toBe('Success') + expect(msg.assetsTrapped).toBeUndefined() + expect(msg.timestamp).toBeDefined() + }, + complete: () => { + resolve() + expect(calls).toHaveBeenCalledTimes(1) + }, + }) + }) + }) }) }) diff --git a/packages/server/src/services/agents/xcm/ops/common.ts b/packages/server/src/services/agents/xcm/ops/common.ts index d7c2c414..c1e88455 100644 --- a/packages/server/src/services/agents/xcm/ops/common.ts +++ b/packages/server/src/services/agents/xcm/ops/common.ts @@ -1,11 +1,13 @@ -import { Observable, map, mergeMap } from 'rxjs' +import { Observable, bufferCount, map, mergeMap } from 'rxjs' +import { filterNonNull } from '@/common/index.js' import { createNetworkId, getChainId, getConsensus, isOnSameConsensus } from '@/services/config.js' import { ApiContext, BlockEvent } from '@/services/networking/index.js' import { HexString } from '@/services/subscriptions/types.js' import { AnyJson, NetworkURN } from '@/services/types.js' import { toHex } from 'polkadot-api/utils' import { + GenericXcmInboundWithContext, GenericXcmSent, Leg, XcmInbound, @@ -13,8 +15,15 @@ import { XcmSent, XcmSentWithContext, } from '../types.js' -import { getParaIdFromJunctions, getSendersFromEvent, networkIdFromMultiLocation } from './util.js' +import { + getParaIdFromJunctions, + getSendersFromEvent, + mapAssetsTrapped, + matchEvent, + networkIdFromMultiLocation, +} from './util.js' import { raw, versionedXcmCodec } from './xcm-format.js' +import { METHODS_XCMP_QUEUE } from './xcmp.js' type Stop = { networkId: NetworkURN; message?: any[] } @@ -161,3 +170,87 @@ export function xcmMessagesSent() { ) } } + +/** + * Extract XCM receive events for both DMP and HRMP in parachains. + * Most parachains emit the same event, MessageQueue.Processed, for both DMP and HRMP. + * But some, like Interlay, emits a different event DmpQueue.ExecutedDownward for DMP. + */ +export function extractParachainReceive() { + return (source: Observable): Observable => { + return source.pipe( + bufferCount(2, 1), + // eslint-disable-next-line complexity + map(([maybeAssetTrapEvent, maybeXcmpEvent]) => { + if (maybeXcmpEvent === undefined) { + return null + } + + const assetTrapEvent = matchEvent(maybeAssetTrapEvent, ['XcmPallet', 'PolkadotXcm'], 'AssetsTrapped') + ? maybeAssetTrapEvent + : undefined + const assetsTrapped = mapAssetsTrapped(assetTrapEvent) + + if (matchEvent(maybeXcmpEvent, 'XcmpQueue', METHODS_XCMP_QUEUE)) { + const xcmpQueueData = maybeXcmpEvent.value + + return new GenericXcmInboundWithContext({ + event: maybeXcmpEvent, + extrinsicHash: maybeXcmpEvent.extrinsic?.hash as HexString, + blockHash: maybeXcmpEvent.blockHash as HexString, + blockNumber: maybeXcmpEvent.blockNumber, + timestamp: maybeXcmpEvent.timestamp, + extrinsicPosition: maybeXcmpEvent.extrinsicPosition, + messageHash: xcmpQueueData.message_hash, + messageId: xcmpQueueData.message_id, + outcome: maybeXcmpEvent.name === 'Success' ? 'Success' : 'Fail', + error: xcmpQueueData.error, + assetsTrapped, + }) + } else if (matchEvent(maybeXcmpEvent, 'MessageQueue', 'Processed')) { + const { id, success, error } = maybeXcmpEvent.value + // Received event only emits field `message_id`, + // which is actually the message hash in chains that do not yet support Topic ID. + const messageId = id + const messageHash = messageId + + return new GenericXcmInboundWithContext({ + event: maybeXcmpEvent, + extrinsicHash: maybeXcmpEvent.extrinsic?.hash as HexString, + blockHash: maybeXcmpEvent.blockHash as HexString, + blockNumber: maybeXcmpEvent.blockNumber, + timestamp: maybeXcmpEvent.timestamp, + messageHash, + messageId, + outcome: success ? 'Success' : 'Fail', + error, + assetsTrapped, + }) + } else if (matchEvent(maybeXcmpEvent, 'DmpQueue', 'ExecutedDownward')) { + const { message_id, outcome } = maybeXcmpEvent.value + + // Received event only emits field `message_id`, + // which is actually the message hash in chains that do not yet support Topic ID. + const messageId = message_id + const messageHash = messageId + + return new GenericXcmInboundWithContext({ + event: maybeXcmpEvent, + extrinsicHash: maybeXcmpEvent.extrinsic?.hash as HexString, + blockHash: maybeXcmpEvent.blockHash as HexString, + blockNumber: maybeXcmpEvent.blockNumber, + timestamp: maybeXcmpEvent.timestamp, + messageHash, + messageId, + outcome: outcome.type === 'Complete' ? 'Success' : 'Fail', + error: null, + assetsTrapped, + }) + } + + return null + }), + filterNonNull(), + ) + } +} diff --git a/packages/server/src/services/agents/xcm/ops/xcmp.spec.ts b/packages/server/src/services/agents/xcm/ops/xcmp.spec.ts index ba7ee9d4..78c6c512 100644 --- a/packages/server/src/services/agents/xcm/ops/xcmp.spec.ts +++ b/packages/server/src/services/agents/xcm/ops/xcmp.spec.ts @@ -1,7 +1,6 @@ import { extractEvents } from '@/common/index.js' -import { apiContext, xcmpReceive, xcmpSend } from '@/testing/xcm.js' - -import { extractXcmpReceive, extractXcmpSend } from './xcmp.js' +import { apiContext, xcmpSend } from '@/testing/xcm.js' +import { extractXcmpSend } from './xcmp.js' describe('xcmp operator', () => { describe('extractXcmpSend', () => { @@ -33,86 +32,4 @@ describe('xcmp operator', () => { }) }) }) - - describe('extractXcmpReceive', () => { - it('should extract XCMP receive with outcome success', async () => { - const { successBlocks } = xcmpReceive - const calls = vi.fn() - const test$ = extractXcmpReceive()(successBlocks.pipe(extractEvents())) - - await new Promise((resolve) => { - test$.subscribe({ - next: (msg) => { - expect(msg).toBeDefined() - expect(msg.blockNumber).toBeDefined() - expect(msg.blockHash).toBeDefined() - expect(msg.event).toBeDefined() - expect(msg.messageHash).toBeDefined() - expect(msg.timestamp).toBeDefined() - expect(msg.outcome).toBeDefined() - expect(msg.outcome).toBe('Success') - calls() - }, - complete: () => { - expect(calls).toHaveBeenCalledTimes(1) - resolve() - }, - }) - }) - }) - - it('should extract failed XCMP received message with error', async () => { - const { failBlocks } = xcmpReceive - const calls = vi.fn() - const test$ = extractXcmpReceive()(failBlocks.pipe(extractEvents())) - - await new Promise((resolve) => { - test$.subscribe({ - next: (msg) => { - expect(msg).toBeDefined() - expect(msg.blockNumber).toBeDefined() - expect(msg.blockHash).toBeDefined() - expect(msg.event).toBeDefined() - expect(msg.messageHash).toBeDefined() - expect(msg.outcome).toBeDefined() - expect(msg.outcome).toBe('Fail') - expect(msg.timestamp).toBeDefined() - calls() - }, - complete: () => { - expect(calls).toHaveBeenCalledTimes(1) - resolve() - }, - }) - }) - }) - - it('should extract assets trapped info on XCMP received message for V4 assets', async () => { - const { trappedBlocks } = xcmpReceive - const calls = vi.fn() - const test$ = extractXcmpReceive()(trappedBlocks.pipe(extractEvents())) - - await new Promise((resolve) => { - test$.subscribe({ - next: (msg) => { - expect(msg).toBeDefined() - expect(msg.blockNumber).toBeDefined() - expect(msg.blockHash).toBeDefined() - expect(msg.event).toBeDefined() - expect(msg.messageHash).toBeDefined() - expect(msg.outcome).toBeDefined() - expect(msg.outcome).toBe('Fail') - expect(msg.assetsTrapped).toBeDefined() - expect(msg.assetsTrapped?.assets).toBeDefined() - expect(msg.timestamp).toBeDefined() - calls() - }, - complete: () => { - expect(calls).toHaveBeenCalledTimes(1) - resolve() - }, - }) - }) - }) - }) }) diff --git a/packages/server/src/services/agents/xcm/ops/xcmp.ts b/packages/server/src/services/agents/xcm/ops/xcmp.ts index c75b1d03..877960d5 100644 --- a/packages/server/src/services/agents/xcm/ops/xcmp.ts +++ b/packages/server/src/services/agents/xcm/ops/xcmp.ts @@ -1,23 +1,16 @@ -import { Observable, bufferCount, filter, map, mergeMap } from 'rxjs' +import { Observable, filter, map, mergeMap } from 'rxjs' import { filterNonNull } from '@/common/index.js' -import { HexString } from '@/lib.js' import { createNetworkId } from '@/services/config.js' import { ApiContext, BlockEvent } from '@/services/networking/index.js' import { NetworkURN } from '@/services/types.js' -import { - GenericXcmInboundWithContext, - GenericXcmSentWithContext, - GetOutboundHrmpMessages, - XcmInboundWithContext, - XcmSentWithContext, -} from '../types.js' +import { GenericXcmSentWithContext, GetOutboundHrmpMessages, XcmSentWithContext } from '../types.js' import { xcmMessagesSent } from './common.js' -import { getMessageId, mapAssetsTrapped, matchEvent } from './util.js' +import { getMessageId, matchEvent } from './util.js' import { fromXcmpFormat } from './xcm-format.js' -const METHODS_XCMP_QUEUE = ['Success', 'Fail'] +export const METHODS_XCMP_QUEUE = ['Success', 'Fail'] function findOutboundHrmpMessage( origin: NetworkURN, @@ -78,62 +71,3 @@ export function extractXcmpSend( ) } } - -export function extractXcmpReceive() { - return (source: Observable): Observable => { - return source.pipe( - bufferCount(2, 1), - // eslint-disable-next-line complexity - map(([maybeAssetTrapEvent, maybeXcmpEvent]) => { - if (maybeXcmpEvent === undefined) { - return null - } - - const assetTrapEvent = matchEvent(maybeAssetTrapEvent, ['XcmPallet', 'PolkadotXcm'], 'AssetsTrapped') - ? maybeAssetTrapEvent - : undefined - const assetsTrapped = mapAssetsTrapped(assetTrapEvent) - - if (matchEvent(maybeXcmpEvent, 'XcmpQueue', METHODS_XCMP_QUEUE)) { - const xcmpQueueData = maybeXcmpEvent.value - - return new GenericXcmInboundWithContext({ - event: maybeXcmpEvent, - extrinsicHash: maybeXcmpEvent.extrinsic?.hash as HexString, - blockHash: maybeXcmpEvent.blockHash as HexString, - blockNumber: maybeXcmpEvent.blockNumber, - timestamp: maybeXcmpEvent.timestamp, - extrinsicPosition: maybeXcmpEvent.extrinsicPosition, - messageHash: xcmpQueueData.message_hash, - messageId: xcmpQueueData.message_id, - outcome: maybeXcmpEvent.name === 'Success' ? 'Success' : 'Fail', - error: xcmpQueueData.error, - assetsTrapped, - }) - } else if (matchEvent(maybeXcmpEvent, 'MessageQueue', 'Processed')) { - const { id, success, error } = maybeXcmpEvent.value - // Received event only emits field `message_id`, - // which is actually the message hash in chains that do not yet support Topic ID. - const messageId = id - const messageHash = messageId - - return new GenericXcmInboundWithContext({ - event: maybeXcmpEvent, - extrinsicHash: maybeXcmpEvent.extrinsic?.hash as HexString, - blockHash: maybeXcmpEvent.blockHash as HexString, - blockNumber: maybeXcmpEvent.blockNumber, - timestamp: maybeXcmpEvent.timestamp, - messageHash, - messageId, - outcome: success ? 'Success' : 'Fail', - error, - assetsTrapped, - }) - } - - return null - }), - filterNonNull(), - ) - } -} diff --git a/packages/server/src/services/agents/xcm/tracking.spec.ts b/packages/server/src/services/agents/xcm/tracking.spec.ts index 583c4fdf..81a875b2 100644 --- a/packages/server/src/services/agents/xcm/tracking.spec.ts +++ b/packages/server/src/services/agents/xcm/tracking.spec.ts @@ -68,4 +68,26 @@ describe('extractXcmMessageData', () => { }) }) }) + + it('should extract xcm messages from dmp with topic id that does not accept topic id', async () => { + const blocks = from(testBlocksFrom('interlay/7025155.cbor')) + const calls = vi.fn() + const test$ = extractXcmMessageData(apiContext)(blocks.pipe()) + + new Promise((resolve) => { + test$.subscribe({ + next: ({ hashData }) => { + console.log(hashData) + calls() + expect(hashData).toBeDefined() + expect(hashData[0].hash).toBeDefined() + expect(hashData[0].data).toBeDefined() + }, + complete: () => { + resolve() + expect(calls).toHaveBeenCalledTimes(1) + }, + }) + }) + }) }) diff --git a/packages/server/src/services/agents/xcm/tracking.ts b/packages/server/src/services/agents/xcm/tracking.ts index 01f2c829..0ea87062 100644 --- a/packages/server/src/services/agents/xcm/tracking.ts +++ b/packages/server/src/services/agents/xcm/tracking.ts @@ -13,13 +13,14 @@ import { SharedStreams } from '../base/shared.js' import { AgentRuntimeContext } from '../types.js' import { MatchingEngine } from './matching.js' import { mapXcmInbound, mapXcmSent } from './ops/common.js' +import { extractParachainReceive } from './ops/common.js' import { messageCriteria } from './ops/criteria.js' import { extractDmpSendByEvent } from './ops/dmp.js' import { extractRelayReceive } from './ops/relay.js' import { extractUmpReceive, extractUmpSend } from './ops/ump.js' import { getMessageId, matchExtrinsic } from './ops/util.js' import { fromXcmpFormat, raw } from './ops/xcm-format.js' -import { extractXcmpReceive, extractXcmpSend } from './ops/xcmp.js' +import { extractXcmpSend } from './ops/xcmp.js' import { TelemetryXcmEventEmitter } from './telemetry/events.js' import { xcmAgentMetrics, xcmMatchingEngineMetrics } from './telemetry/metrics.js' import { @@ -171,8 +172,8 @@ export class XcmTracker { .subscribe(inboundObserver), }) } else { - // VMP DMP - this.#log.info('[%s] %s subscribe inbound DMP', this.#id, chainId) + // VMP + HRMP + this.#log.info('[%s] %s subscribe inbound DMP + HRMP / XCMP', this.#id, chainId) const messageHashBlocks$ = this.#ingress.getContext(chainId).pipe( switchMap((context) => @@ -188,21 +189,11 @@ export class XcmTracker { ), ) - // DMP and HRMP receive matches the same event - // subs.push({ - // chainId, - // sub: messageHashBlocks$ - // .pipe(extractEvents(), extractDmpReceive(), mapXcmInbound(chainId)) - // .subscribe(inboundObserver), - // }) - - // Inbound HRMP / XCMP transport - this.#log.info('[%s] %s subscribe inbound HRMP', this.#id, chainId) - + // Extract both DMP and HRMP receive subs.push({ chainId, sub: messageHashBlocks$ - .pipe(extractEvents(), extractXcmpReceive(), mapXcmInbound(chainId)) + .pipe(extractEvents(), extractParachainReceive(), mapXcmInbound(chainId)) .subscribe(inboundObserver), }) } diff --git a/packages/server/src/testing/__data__/blocks/interlay/7025155.cbor b/packages/server/src/testing/__data__/blocks/interlay/7025155.cbor new file mode 100644 index 0000000000000000000000000000000000000000..4aef7a7faaa9b23b71593a0747a8cbab2e9a0c99 GIT binary patch literal 31148 zcmdtry|ZTNbrtZLkzAyNmIGqK^ZgOZ9)T=I!)P>?N|dYG&-t3}neM)=zPHu)PGlSr zKt@17`7a=#ibw<$5g|oDIV2z=a1nq5LVoMq`}WMpmXIvfU}%-P=YGBCoafnl?X}k4 z&wGFR)^BZ(%k|^i->q)P@fe!1K903sO4p9lT9tXI#<5$M>ZrH=X!g4A>w2iRec8%h z=5FblrX2M$EMt2-fAwtHUw%ARP4D}T7v<&t{3fqFjN|M(m$Ho2T&>m7R7>5hrLUK+ z-Rfg&+jSZ1X55Em-j04LW9{{ud1;&49euLj-n@MB{QAjy{U<+t>$i?)SKF(n`;(8J zJln5t%Ci^8v;F4r)wW*mH(Po6@%2yNdV76-_4-*owqrB3!!-77b6h|9`1!v5#hd4C z>v64yX=~PcU&ehLT(F*oYVJzabS~Y^O*^j5RBwCR9ZlQM^SiK9zVa6s}IV{ zvOX?Po`3)D`euLja6i}X=h08f(t_c9&pv2R1Sl&UTDFiuN9RAuVh{TQdx4AV5srHuXD z*Hu}kvQ~Aw*}taUwxRT8%T}({dd%(I)z#8ZrELdW)GgDvEdAE5{WzC>-`c9`>Z9%V zX=|s-YIRnk)K+%yd{oLjSL1dpM?3n#V;Q!oU#)4qEqyarLo>VgzMX43KMeKQkIhgw zRb|6VT}{>HPd5y{at!-4RU&FGVqxpbJT66a41G72d9@?mJ}-0KRr@e>UAI-`=!dZ^ zqOk4uY98v@u6fb^a;>WNdWq(JD*ZkWHr^%1e%sy9Ts)N@tGDK8ZO=M)$IuS-x@=Q5 zbc1bQr`~#W<5ti8(rnYxdF8fqBm1~i^|bX}v&~EKf1zt>j%hi{KJWZ&cCa!5*GgSg5&(m{fktW zpFO{JeqlYo@ADnsr*Y_}dOW}QSN!#hzrIfow+mBpgP*_d{l~)1XCEG5sH(NgH-6#I zP}TF)K0L+!=P%FVBR`QpKl@bumtUi*_xta=eE;Qw0`&gR!wtB-_RaTPu6g;lepvE1 zx7%J`_2EU1?%V<|vNZcpdGxhiXR-J2@x!s$s3=KqK;{beWpQ zO_kXT_s7)j5O{83Zy5le??0+zKenxvCCzlcN|TG9JG@EZTyX0i{#7gT@xI^x3+2y` zkM`kTIhU{BepKDUj$iXtJi)o)&3^yy?LVq++o4&Sdg$9)md;he^}J#|wN;TpGdyhe zqiwsSy$vqMSh@vz4V8kn^<~)h(lpX`*v57m%CybL)c>N_hPPYkr?v0ed0N(S?aQ*x zOYO?LIm4|6m3^UycLI_y>c8yarQ?H{@CwXM71n98*GU2g1t-5&1Vo{7_X z^DT>-tNPaM1U%QVs+$FZH%-57^E|CWV{iK!*q5#wt4VS-+i<(M!Uh6;$EL0++OYb| z*MP06WVa2=vUHne=}T|l>Fn15<~0bw*p`QD^usZi_E^if z&SK~O8kcymKI6VL-PYE9+Yhsnunp^0nzgL!Hh@kcRwD*=HMd6(_r|3kq1@EhLn-US zHHNZvS*~7HnlCYUxmI_y^**&*H?R9VsfwedU#k6Rj(%Q?<(ek|-gk39mP(A2U0IwC zb#z>m_=jtZxwEnC$jS71JU={-+!NW|*m#Os39RdFXsd2hS;{mW)3zOe#$6uuVD+ZC zURBF!*s5(k)aQq5l;&8fX{pDfRP~!&dANqUIq%(2IsvtcPb)EZc+JXnrR#TJ=iOS+npg#g7R2;Pb*vYxwGlr(LJoyYO{R#m~fBrat+n> z9zkZ=h6PXc5a!@T;EV*ep*FQ0OWF3J?YC)M>!wF`Yinlxrs9YCQa@bdsG8~6>t;Th zZogdPlJ9$Kdxd7WTJB;l{ZjV>_*mDnO)?lXEc>y~1?-QQJcZC~JEhgRLNUxNk(o!!SnnDz+wFef|t7KEY`XmP353Cx?bUwPLk_bZ~w5m?SBpvnN$XPfx>{KRM#|CqY!8Ztf3r7-P>m@0~KdpuAZHHAun<8$@m)?g%qciUz3EVFtt#2141nbgdRB z>RU4pZoeV!=<7vdw_EZ04NR+gt9*ZzJq2{0Hyj6Ybz=RwADgi=Ot)_j}aY3+T z@-(z%mrSt_O4EAPIYpN0!YpR@derj~rPQl?XvOV6t!_i&>Tb%Tc2sw@0=B(5L^P6C z?|bRd3#zc4+kj&>h)lKA4N+gnPAax5{hX?d-Bk$1AXOTlU+|V0s!kgf1^@B8so&lC zu)}{O0=dBZ1jGge$IIPJYCaA_)vXe@Yg*($4%O}~3`m-yNXPC8>|+B#$8DSH{gCF{ z2+{1UwV5!Kfqu2Z(_{t26)J;OV@_U?jvWT;wMWDDf>adwPy%mF)wRp<#k&!Jv-PIO zb`aHe9%-LyEOwhM&P&T*OKjq1BU!{2OtD zr+P{IGfJkWt9NMZ-iHoXX{cj?R=>F$JJm^-IpH|UYa7`gV|Hh&SU@6USvP99Z(9g% z4_mORdJGNR#C~8VM%Act4G1`R56iI=v+|6@r$6dVY;QFseFy`|?yL2%h-P4d-Ad;- zJ0-ZN7jDP4h&Fn+vITYB(%qxGw}Bf5<$)%!SZCXOXPnbs4~7 z%~O%H*xM}G)}1l1c>wVjOiB{S>pA?!b~cKqNIHZ9lVK6a9czlFj9!p4waNr_{%XB* zk(}o%pMT|h(UWMNrcOF>ja=KGzx=(ox0fsA8~8az+IyO*p`*IKuNG*5-Jto(xjbpJ zuoGL#>-NkSvCu2#D_2C=|GewM{!8_^Fw%!8tK$mM1LzPMB_E&?tA#GzR>orswMxA( zz7Ei!P@+aX2Tyfjz~qN<71fzkNiw1TAqd7VOAI@$`_>rj7 zPP{O4zIZqCg8Rn05 z0z%u*R6+@AXomdA;)4R7k+dnaOo2(Hae7q&xGB0sJj}Z1xgBotpv-^8z59=! z-`~OH6}jP;b@cTdxOE3jSr&HhBE_vm9n7PdJNLoT6#7C4J``9!upQh71Z5!>4wyoD z^v$x=W!?D~^Jp7AB!{ZqR<7u=_XWlkFACvNhs_IM1-7zj?JJd%TBYc(jhwQ88)$dw z8JFuUvJgf=o2WQUxZ56f-)*}MszJ$Dhogvu_SlGZum&jkkML(6VHz@^Kpb$slDRNT ziC=&qy3R%!7MtJB@MUaiJDICmDlIdpn#k_Zx-uR$AXAPIe7+};=Dm2iFW${MiqM^n z)AYR8!LnJv7>*pF)^nrKV8|VNap}T{l|f9S>abMoQ?;p7^EeYm5*1%Wxhy-^1L^|L z1^3yKW?n_D5;V+Zw7ptGI-9OEZ|(+`D{Ay+Drvn=iysn^J*-vvdj-3p;H`DsWCson4(2myF<3UWoc`Q|6+QrjW#Yr# z@X}-kLMZ&bpm1qzzq^MH!aWn_7?@qBEg8?To6G*njpt{3ZxAkZ%lMH;2nDio>U zp65XGSvV`2D2BO)!gaS)ON3Dk&21t#5mMc~{m;)tuu@8kN`$LdLBX(vk==-6R@gze{ ztGk>MNbbo`^50cVFoUwK4cuQBdQ>lLkW&=cs*M1W4qZ^=yWdjx?l@3f=j5PSEs3pN$ z8h~s9Dh=WK@6KI`a}$jbn`@|=(Su@&3l?9s&2E8Vy5`A(G)3`8b}eIRqP@3)4CBL9%Iy%lN3}m<;|wWLhUn_f`PWO zenWiNP@==@q^kjXr%tEM18|eoamGRnBbbAK9DCa?jE2)21J2kuu8LDn2>j`R^)83l zfMI%rT3$EAze0mMF-WkeQ;{^BDss6Fd75P}x{w-#lWMLU9KoZt;1f6s?~C`^i`#ag zrBOb~=n9Gxt>@koy;y9|I}0FbuZ)nNG7SA*j*$t=nqYJ9R$Hd%#Slt0Gt;Z$funTP zA*sMNGx#1kP^nNMSX^(TCQ6HaG13I!iQf$~fMe6lI3WSmR_lTf0e&@FL&{Am0LU(6 z5b8t~w}4aNy3$?@dI+yKtA|7_t|DNiTdgBj_g;~dSeU|Pc~C>?j7d3$m&$p-J$@T5 z)FslU7BVP&;$LueyItSg1JO_i(QCSmGT&^r-9}(KFRDz%JVbK3z<{tWO0&QPxI-_y zP!iaqb%!@&{YSCSQvU(t8#v@{r~tT_P9rI_B0Vn9u-gDxTkYGrK}QTpJ~h#>`hDyY z43WqZ=OUICuwFQ<%~nf7gj|*iMRQi`oV6!)yG;#7#Z#eI;1ms|9#AlPhMK8tA26N6Ju z>?CAbcYds41gfsEL>0x=P6eplrh;-5EHThXTMSmfN-cv;2&8N7=JY#-Ft?=Fy%a+_ zhNpml+~n}xJHMTDwiv?L6V)J@9H+AzDkn=ub)0K}#dIxV=-$|QE8jPDJ}wMVs^MF- zw^g2*3yJ%LkB<|Z)_*Jo4(uW>Hm`j~c@+}i%6Xxhcza55$BP`jg0d}XW7{rTMv92K zkZP~o_>W%Gb|bUlEx)@`XKW{03s@oDDwI;|fH=HSIM56If4544B1E*%rnD=sl5~pY z){QNHD0PM>K}zVds57P1RAUIS+iZZpJMzfrIz^%7q0y3#u`tmaj=SrN@{s0F3)CYd zKEZ{8W=Sa1iPP)pOe2VXcEr>I5SJ~Uv=?L;q?e=%RW26}OE^ z(wc|<5DY#vc>C!R8qt{Ig3JIa#Hq?mCI~AEk()sZrv7+1xiE>TX_jo6*Vr(JB1RG7 zJWzv>GGEt$8`HwUV}LvC%(W$LVJzN=zX*110FT^*2sA_?l=uW|oD8k!T8Ya*fGulc zSD*?sL(xp@U@Wz!$Rk>_#k2Rw05BJ*I#RZ!Ne5AXoYmS^%(@|Kj4SY}#f)d5i4V`o z>XwSAGI2(4+dPQCAGE0Iz9-iL@LK08+5x-dA;pS^vUIYk6{9-_*my7$Ih_$vF8@+1 z<}naN2c-5VO>_bf5L>^YfvfJ6JZA%LWT60t1i;}aLj9ZpsN5wl0$C?IQze+^!f}j= zjc7ny0(+404I1Q!HjGk)=pyks7>QP>lCfP9m9JqUMx{!Ni9>~+iWSNSb}7UQI2pzc zbx}nX0csD!?zg+IY30S7iZ-forF4U|%4k+KUSuwAQuzUI$N*Z3sVumQ^|7YzvG
`ON+AYXDbfXw@dL46 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 literal 0 HcmV?d00001 diff --git a/packages/server/src/testing/tools/download.ts b/packages/server/src/testing/tools/download.ts index 4896c78e..35744df5 100644 --- a/packages/server/src/testing/tools/download.ts +++ b/packages/server/src/testing/tools/download.ts @@ -17,6 +17,7 @@ export const networks = { moonbeam: 'wss://moonbeam.ibp.network', astar: 'wss://rpc.astar.network', bifrost: 'wss://bifrost-polkadot.ibp.network', + interlay: 'wss://interlay-rpc.dwellir.com', } as Record async function download([name, ws, height]: [string, string, string]) {