diff --git a/docs/node/CATEGORIES/02-mining.md b/docs/node/CATEGORIES/02-mining.md index 4a17d75793..d84828ef2a 100644 --- a/docs/node/CATEGORIES/02-mining.md +++ b/docs/node/CATEGORIES/02-mining.md @@ -83,3 +83,50 @@ enum EstimateMode { CONSERVATIVE = 'CONSERVATIVE' } ``` + +## getBlockTemplate + +If the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'. +It returns data needed to construct a block to work on. +For full specification, see BIPs 22, 23, 9, and 145: + https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki + https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki + https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes + https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki + +```ts title="client.mining.getBlockTemplate()" +interface mining { + getBlockTemplate (templateRequest: TemplateRequest): Promise +} + +interface TemplateRequest { + mode?: string + capabilities?: string[] + rules: string[] +} + +interface BlockTemplate { + capabilities: string[] + version: number + rules: string[] + vbavailable: any + vbrequired: number + previousblockhash: string + transactions: Transaction[] + coinbaseaux: any + coinbasevalue: number + longpollid: string + target: string + mintime: number + mutable: string[] + noncerange: string + sigoplimit: number + sizelimit: number + weightlimit: number + curtime: number + bits: string + height: number + default_witness_commitment: string +} + +``` diff --git a/packages/jellyfish-api-core/__tests__/category/mining/getBlockTemplate.test.ts b/packages/jellyfish-api-core/__tests__/category/mining/getBlockTemplate.test.ts new file mode 100644 index 0000000000..f36f4d4461 --- /dev/null +++ b/packages/jellyfish-api-core/__tests__/category/mining/getBlockTemplate.test.ts @@ -0,0 +1,68 @@ +import { MasterNodeRegTestContainer } from '@defichain/testcontainers/dist/index' +import { ContainerAdapterClient } from '../../container_adapter_client' +import { Testing } from '@defichain/jellyfish-testing' + +describe('get block template', () => { + const container = new MasterNodeRegTestContainer() + const client = new ContainerAdapterClient(container) + const testing = Testing.create(container) + + beforeAll(async () => { + await container.start() + await container.waitForWalletCoinbaseMaturity() + await testing.container.addNode('127.0.0.1') + }) + + afterAll(async () => { + await container.stop() + }) + + it('should get a block template', async () => { + const promise = await client.mining.getBlockTemplate({ rules: ['segwit'] }) + expect(promise).toStrictEqual({ + capabilities: ['proposal'], + version: 536870912, + rules: [], + vbavailable: {}, + vbrequired: 0, + previousblockhash: expect.any(String), + transactions: [], + coinbaseaux: { flags: '' }, + coinbasevalue: 3333000000, + longpollid: expect.any(String), + target: '7fffff0000000000000000000000000000000000000000000000000000000000', + mintime: expect.any(Number), + mutable: ['time', 'transactions', 'prevblock'], + noncerange: '00000000ffffffff', + sigoplimit: 1280000, + sizelimit: 64000000, + weightlimit: 64000000, + curtime: expect.any(Number), + bits: '207fffff', + height: 102, + default_witness_commitment: '6a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf9' + }) + }) + + it('should throw an error if there is invalid mode', async () => { + const promise = client.mining.getBlockTemplate({ mode: 'mode', rules: ['segwit'] }) + await expect(promise).rejects.toMatchObject({ + payload: { + code: -8, + message: 'Invalid mode', + method: 'getblocktemplate' + } + }) + }) + + it('should throw an error if there is no rules', async () => { + const promise = client.mining.getBlockTemplate({ rules: [''] }) + await expect(promise).rejects.toMatchObject({ + payload: { + code: -8, + message: 'getblocktemplate must be called with the segwit rule set (call with {"rules": ["segwit"]})', + method: 'getblocktemplate' + } + }) + }) +}) diff --git a/packages/jellyfish-api-core/src/category/mining.ts b/packages/jellyfish-api-core/src/category/mining.ts index bfc1d79ce8..eda6447b93 100644 --- a/packages/jellyfish-api-core/src/category/mining.ts +++ b/packages/jellyfish-api-core/src/category/mining.ts @@ -1,3 +1,4 @@ +import { Transaction } from '@defichain/jellyfish-transaction/dist/tx' import { ApiClient } from '../.' export enum EstimateMode { @@ -44,6 +45,25 @@ export class Mining { async estimateSmartFee (confirmationTarget: number, estimateMode: EstimateMode = EstimateMode.CONSERVATIVE): Promise { return await this.client.call('estimatesmartfee', [confirmationTarget, estimateMode], 'number') } + + /** + * If the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'. + * It returns data needed to construct a block to work on. + * For full specification, see BIPs 22, 23, 9, and 145: + * https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki + * https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki + * https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes + * https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki + * + * @param {TemplateRequest} templateRequest A json object in the following spec + * @param {string} mode This must be set to 'template', 'proposal' (see BIP 23), or omitted + * @param {string[]} capabilities client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid' + * @param {string[]} rules A list of strings + * @returns {Promise} + */ + async getBlockTemplate (templateRequest: TemplateRequest): Promise { + return await this.client.call('getblocktemplate', [templateRequest], 'number') + } } /** @@ -100,3 +120,33 @@ export interface SmartFeeEstimation { errors?: string[] blocks: number } + +export interface TemplateRequest { + mode?: string + capabilities?: string[] + rules: string[] +} + +export interface BlockTemplate { + capabilities: string[] + version: number + rules: string[] + vbavailable: any + vbrequired: number + previousblockhash: string + transactions: Transaction[] + coinbaseaux: any + coinbasevalue: number + longpollid: string + target: string + mintime: number + mutable: string[] + noncerange: string + sigoplimit: number + sizelimit: number + weightlimit: number + curtime: number + bits: string + height: number + default_witness_commitment: string +}