Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(jellyfish-api-core): add getBlockTemplate RPC #1993

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
47 changes: 47 additions & 0 deletions docs/node/CATEGORIES/02-mining.md
Original file line number Diff line number Diff line change
@@ -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<BlockTemplate>
}

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
}

```
Original file line number Diff line number Diff line change
@@ -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'
}
})
})
})
50 changes: 50 additions & 0 deletions packages/jellyfish-api-core/src/category/mining.ts
Original file line number Diff line number Diff line change
@@ -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<SmartFeeEstimation> {
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<BlockTemplate>}
*/
async getBlockTemplate (templateRequest: TemplateRequest): Promise<BlockTemplate> {
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
}