Skip to content

Commit

Permalink
feat(jellyfish-api-core): add blockchain getMempoolDescendants RPC (#…
Browse files Browse the repository at this point in the history
…1796)

What this PR does / why we need it:
/kind feature

Which issue(s) does this PR fixes?:
Fixes part of #48

- Implements `getmempooldescendants` type for RPC.
  • Loading branch information
helloscoopa authored Jan 10, 2023
1 parent e2dc4ed commit bd28ca9
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 0 deletions.
43 changes: 43 additions & 0 deletions docs/node/CATEGORIES/01-blockchain.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,49 @@ interface MempoolTx {
}
```
## getMempoolDescendants
Get all in-mempool descendants if a transaction id is in mempool as string[] if verbose is false else as json object
```ts title="client.blockchain.getMempoolDescendants()"
interface blockchain {
getMempoolDescendants (txId: string, verbose?: false): Promise<string[]>
getMempoolDescendants (txId: string, verbose?: true): Promise<MempoolTx>
getMempoolDescendants (txId: string, verbose?: boolean: Promise<string[] | MempoolTx>
}

interface MempoolTx {
[key: string]: {
vsize: BigNumber
/**
* @deprecated same as vsize. Only returned if defid is started with -deprecatedrpc=size
*/
size: BigNumber
weight: BigNumber
fee: BigNumber
modifiedfee: BigNumber
time: BigNumber
height: BigNumber
descendantcount: BigNumber
descendantsize: BigNumber
descendantfees: BigNumber
ancestorcount: BigNumber
ancestorsize: BigNumber
ancestorfees: BigNumber
wtxid: string
fees: {
base: BigNumber
modified: BigNumber
ancestor: BigNumber
descendant: BigNumber
}
depends: string[]
spentby: string[]
'bip125-replaceable': boolean
}
}
```
## getMempoolEntry
Get transaction details in the memory pool using a transaction ID.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { MasterNodeRegTestContainer } from '@defichain/testcontainers'
import { Testing } from '@defichain/jellyfish-testing'
import BigNumber from 'bignumber.js'

describe('Transactions without descendants', () => {
const container = new MasterNodeRegTestContainer()
const testing = Testing.create(container)

beforeAll(async () => {
await testing.container.start()
await testing.container.waitForWalletCoinbaseMaturity()
})

afterAll(async () => {
await testing.container.stop()
})

it('should return empty array for transaction id without descendants', async () => {
const txId = await testing.rpc.wallet.sendToAddress('mwsZw8nF7pKxWH8eoKL9tPxTpaFkz7QeLU', 0.003)
const mempoolDescendants = await testing.rpc.blockchain.getMempoolDescendants(txId)

expect(mempoolDescendants.length).toStrictEqual(0)
})

it('should return error if transaction is not in mempool', async () => {
const nonExistingTxId = 'a'.repeat(64)
const promise = testing.rpc.blockchain.getMempoolDescendants(nonExistingTxId)

await expect(promise)
.rejects
.toThrowError('RpcApiError: \'Transaction not in mempool\', code: -5, method: getmempooldescendants')
})

it('should return error if transaction id has an invalid length', async () => {
const invalidLengthTxid = 'a'.repeat(5)
const promise = testing.rpc.blockchain.getMempoolDescendants(invalidLengthTxid)

await expect(promise)
.rejects
.toThrow(`RpcApiError: 'parameter 1 must be of length 64 (not ${invalidLengthTxid.length}, for '${invalidLengthTxid}')', code: -8, method: getmempooldescendants`)
})
})

describe('Transactions with descendants', () => {
const container = new MasterNodeRegTestContainer()
const testing = Testing.create(container)

beforeAll(async () => {
await testing.container.start()
await testing.container.waitForWalletCoinbaseMaturity()
})

async function getTxIdWithDescendants (): Promise<string> {
const txId = await testing.rpc.wallet.sendToAddress('mwsZw8nF7pKxWH8eoKL9tPxTpaFkz7QeLU', 0.003)
for (let i = 0; i < 10; i++) {
await testing.rpc.wallet.sendToAddress('mwsZw8nF7pKxWH8eoKL9tPxTpaFkz7QeLU', 0.003)
}
return txId
}

afterAll(async () => {
await testing.container.stop()
})

it('should return JSON object if verbose is true', async () => {
const txIdWithDescendants = await getTxIdWithDescendants()
const mempoolDescendants = await testing.rpc.blockchain.getMempoolDescendants(txIdWithDescendants, true)

const keys = Object.keys(mempoolDescendants)
expect(keys.length).toBeGreaterThan(0)
for (const key of keys) {
expect(mempoolDescendants[key]).toStrictEqual({
fees: expect.any(Object),
vsize: expect.any(BigNumber),
weight: expect.any(BigNumber),
fee: expect.any(BigNumber),
modifiedfee: expect.any(BigNumber),
time: expect.any(BigNumber),
height: expect.any(BigNumber),
descendantcount: expect.any(BigNumber),
descendantsize: expect.any(BigNumber),
descendantfees: expect.any(BigNumber),
ancestorcount: expect.any(BigNumber),
ancestorsize: expect.any(BigNumber),
ancestorfees: expect.any(BigNumber),
wtxid: expect.any(String),
depends: expect.any(Array),
spentby: expect.any(Array),
'bip125-replaceable': expect.any(Boolean)
})
}
})

it('should return array of transaction ids if verbose is false', async () => {
const txIdWithDescendants = await getTxIdWithDescendants()
const mempoolDescendants = await testing.rpc.blockchain.getMempoolDescendants(txIdWithDescendants, false)
expect(mempoolDescendants.length).toBeGreaterThan(0)
for (const descendantId of mempoolDescendants) {
expect(descendantId).toStrictEqual(expect.stringMatching(/^[0-9a-f]{64}$/))
}
})

it('should return array of transaction ids if verbose is undefined', async () => {
const txIdWithDescendants = await getTxIdWithDescendants()
const mempoolDescendants = await testing.rpc.blockchain.getMempoolDescendants(txIdWithDescendants)
expect(mempoolDescendants.length).toBeGreaterThan(0)
for (const descendantId of mempoolDescendants) {
expect(descendantId).toStrictEqual(expect.stringMatching(/^[0-9a-f]{64}$/))
}
})
})
30 changes: 30 additions & 0 deletions packages/jellyfish-api-core/src/category/blockchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,36 @@ export class Blockchain {
return await this.client.call('getmempoolancestors', [txId, verbose], 'bignumber')
}

/**
* Get all in-mempool descendants for a given transaction as string[]
*
* @param {string} txId the transaction id
* @param {boolean} verbose false
* @return {Promise<string[]>}
*/
getMempoolDescendants (txId: string, verbose?: false): Promise<string[]>

/**
* Get all in-mempool descendants for a given transaction as json object
*
* @param {string} txId the transaction id
* @param {boolean} verbose true
* @return {Promise<MempoolTx>}
*/
getMempoolDescendants (txId: string, verbose?: true): Promise<MempoolTx>

/**
* Get all in-mempool descendants if txId is in mempool as string[] if verbose is false
* else as json object
*
* @param {string} txId the transaction id
* @param {boolean} verbose default = false, true for json object, false for array of transaction ids
* @return {Promise<string[] | MempoolTx>}
*/
async getMempoolDescendants (txId: string, verbose: boolean = false): Promise<string[] | MempoolTx> {
return await this.client.call('getmempooldescendants', [txId, verbose], 'bignumber')
}

/**
* Get mempool data for the given transaction
* @param {string} txId the transaction id
Expand Down

0 comments on commit bd28ca9

Please sign in to comment.