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

Masternode RPC (createmasternode, listmasternodes, getmasternode) #372

Merged
merged 34 commits into from
Jun 23, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ae3f84f
Masternode RPC (createmasternode, listmasternodes, getmasternode)
siradji Jun 11, 2021
459bfee
updated sidebar.js
siradji Jun 11, 2021
df6fd4b
Update packages/jellyfish-api-core/src/category/masternode.ts
siradji Jun 11, 2021
13abb5d
Update packages/jellyfish-api-core/src/category/masternode.ts
siradji Jun 11, 2021
cbc9760
Update packages/jellyfish-api-core/src/category/masternode.ts
siradji Jun 11, 2021
bc5c327
Minor fixes
siradji Jun 11, 2021
6f908d1
Minor fixes
siradji Jun 11, 2021
e751f6c
Implementation of suggestion from code review.
siradji Jun 11, 2021
8306bc9
Additional testings for createMasternode
siradji Jun 11, 2021
481fd48
Suggesstions from code review
siradji Jun 14, 2021
700fcfc
Clean up
siradji Jun 15, 2021
f3256d4
fixing CI
siradji Jun 15, 2021
4217386
fixing issue with CI
siradji Jun 15, 2021
9845c20
Fixed issue with CI
siradji Jun 15, 2021
eea316c
Fixed issue with CI
siradji Jun 16, 2021
a854ada
Minor fixes
siradji Jun 16, 2021
056d7a0
Minor fixes
siradji Jun 17, 2021
374ac99
Minor fixes
siradji Jun 17, 2021
fceae28
Minor fixes
siradji Jun 18, 2021
ce443ea
Minor fixes
siradji Jun 19, 2021
a42c763
Minor fixes
siradji Jun 20, 2021
f05451e
Update packages/jellyfish-api-core/src/category/masternode.ts
siradji Jun 21, 2021
782c2d3
Update packages/jellyfish-api-core/src/category/masternode.ts
siradji Jun 21, 2021
37844d9
Minor fix
siradji Jun 21, 2021
29c0852
Update website/docs/jellyfish/api/masternode.md
fuxingloh Jun 22, 2021
53a0a23
Minor fixes
siradji Jun 22, 2021
b188f91
Update packages/jellyfish-api-core/src/category/masternode.ts
siradji Jun 23, 2021
a89f236
Update packages/jellyfish-api-core/src/category/masternode.ts
siradji Jun 23, 2021
f08a735
Update packages/jellyfish-api-core/src/category/masternode.ts
siradji Jun 23, 2021
814c567
Update packages/jellyfish-api-core/src/category/masternode.ts
siradji Jun 23, 2021
1216088
Update packages/jellyfish-api-core/src/category/masternode.ts
siradji Jun 23, 2021
6469a39
Update packages/jellyfish-api-core/src/category/masternode.ts
siradji Jun 23, 2021
eceaa11
Minor fixes
siradji Jun 23, 2021
03a93f8
Update website/docs/jellyfish/api/masternode.md
fuxingloh Jun 23, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { MasterNodeRegTestContainer } from '@defichain/testcontainers'
import { ContainerAdapterClient } from '../../container_adapter_client'
import { MasternodeState } from '../../../src/category/masternode'

describe('Masternode', () => {
const container = new MasterNodeRegTestContainer()
const client = new ContainerAdapterClient(container)

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

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

it('should create a masternode transaction', async () => {
const masternodesBefore = await client.masternode.listMasternodes()
siradji marked this conversation as resolved.
Show resolved Hide resolved
const masternodesLengthBefore = Object.keys(masternodesBefore).length

const address = await client.wallet.getNewAddress()
const masternode = await client.masternode.createMasternode(address)

await container.generate(1)

const masternodesAfter = await client.masternode.listMasternodes()
siradji marked this conversation as resolved.
Show resolved Hide resolved
const masternodesLengthAfter = Object.keys(masternodesAfter).length

expect(masternodesLengthAfter).toStrictEqual(masternodesLengthBefore + 1)
expect(typeof masternode).toStrictEqual('string')
siradji marked this conversation as resolved.
Show resolved Hide resolved

for (const masternode in masternodesAfter) {
const createdMasternode = masternodesAfter[masternode]
if (createdMasternode.ownerAuthAddress === address) {
siradji marked this conversation as resolved.
Show resolved Hide resolved
expect(typeof createdMasternode.ownerAuthAddress).toStrictEqual('string')
expect(typeof createdMasternode.operatorAuthAddress).toStrictEqual('string')
expect(typeof createdMasternode.creationHeight).toStrictEqual('number')
expect(typeof createdMasternode.resignHeight).toStrictEqual('number')
expect(typeof createdMasternode.resignTx).toStrictEqual('string')
expect(typeof createdMasternode.banHeight).toStrictEqual('number')
expect(typeof createdMasternode.banTx).toStrictEqual('string')
expect(createdMasternode.state).toStrictEqual(MasternodeState.PRE_ENABLED)
expect(typeof createdMasternode.state).toStrictEqual('string')
expect(typeof createdMasternode.mintedBlocks).toStrictEqual('number')
expect(typeof createdMasternode.targetMultiplier).toStrictEqual('number')
}
}
})

it('should create masternode transaction with specified UTXOS to spend', async () => {
const utxos = await client.wallet.listUnspent()
const inputs = utxos.map((utxo: { txid: string, vout: number }) => ({ txid: utxo.txid, vout: utxo.vout }))
const masternodeTransaction = await client.masternode.createMasternode(await client.wallet.getNewAddress(), undefined, { inputs })
siradji marked this conversation as resolved.
Show resolved Hide resolved

expect(typeof masternodeTransaction).toStrictEqual('string')
siradji marked this conversation as resolved.
Show resolved Hide resolved
})

it('should throw an error with invalid owner address', async () => {
const invalidAddress = 'invalidAddress'
await expect(client.masternode.createMasternode(invalidAddress)).rejects.toThrow('operatorAddress (invalidAddress) does not refer to a P2PKH or P2WPKH address')
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { MasterNodeRegTestContainer } from '@defichain/testcontainers'
import { ContainerAdapterClient } from '../../container_adapter_client'
import { MasternodeState } from '../../../src/category/masternode'

describe('Masternode', () => {
const container = new MasterNodeRegTestContainer()
const client = new ContainerAdapterClient(container)

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

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

it('should get a masternode', async () => {
let id: string = ''

const address = await client.wallet.getNewAddress()
await client.masternode.createMasternode(address)

await container.generate(1)

const masterNodes = await client.masternode.listMasternodes()
for (const mnId in masterNodes) {
if (masterNodes[mnId].ownerAuthAddress === address) {
id = mnId
}
}

const masternode = await client.masternode.getMasternode(id)

for (const masternodeKey in masternode) {
siradji marked this conversation as resolved.
Show resolved Hide resolved
const data = masternode[masternodeKey]
expect(typeof data.operatorAuthAddress).toStrictEqual('string')
expect(typeof data.ownerAuthAddress).toStrictEqual('string')
expect(typeof data.creationHeight).toStrictEqual('number')
expect(typeof data.resignHeight).toStrictEqual('number')
expect(typeof data.resignTx).toStrictEqual('string')
expect(typeof data.banHeight).toStrictEqual('number')
expect(typeof data.banTx).toStrictEqual('string')
expect(data.state).toStrictEqual(MasternodeState.PRE_ENABLED)
expect(typeof data.state).toStrictEqual('string')
expect(typeof data.mintedBlocks).toStrictEqual('number')
expect(typeof data.targetMultiplier).toStrictEqual('number')
}
})

it('should fail and throw an error with invalid masternode id', async () => {
const invalidId = '8d4d987dee688e400a0cdc899386f243250d3656d802231755ab4d28178c9816'
const promise = client.masternode.getMasternode(invalidId)

await expect(promise).rejects.toThrow('Masternode not found')
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { MasterNodeRegTestContainer } from '@defichain/testcontainers'
import { ContainerAdapterClient } from '../../container_adapter_client'
import { MasternodePagination } from '../../../src/category/masternode'

describe('Masternode', () => {
const container = new MasterNodeRegTestContainer()
const client = new ContainerAdapterClient(container)

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

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

it('should list masternodes', async () => {
const masternodes = await client.masternode.listMasternodes()

for (const masternode in masternodes) {
siradji marked this conversation as resolved.
Show resolved Hide resolved
const createdMasternode = masternodes[masternode]
expect(typeof createdMasternode.ownerAuthAddress).toStrictEqual('string')
expect(typeof createdMasternode.operatorAuthAddress).toStrictEqual('string')
expect(typeof createdMasternode.creationHeight).toStrictEqual('number')
expect(typeof createdMasternode.resignHeight).toStrictEqual('number')
expect(typeof createdMasternode.resignTx).toStrictEqual('string')
expect(typeof createdMasternode.banHeight).toStrictEqual('number')
expect(typeof createdMasternode.banTx).toStrictEqual('string')
siradji marked this conversation as resolved.
Show resolved Hide resolved
expect(typeof createdMasternode.state).toStrictEqual('string')
siradji marked this conversation as resolved.
Show resolved Hide resolved
expect(typeof createdMasternode.mintedBlocks).toStrictEqual('number')
expect(typeof createdMasternode.targetMultiplier).toStrictEqual('number')
}
})

it('should list masternodes with verbose set to false to get just the ids', async () => {
const masternodes = await client.masternode.listMasternodes({}, false)

for (const value of Object.values(masternodes)) {
expect(typeof value).toStrictEqual('string')
}
})

it('should list masternodes with limit. Limited to 3 and should return 3', async () => {
const options: MasternodePagination = { limit: 3 }
const masternodes = await client.masternode.listMasternodes(options)

expect(Object.keys(masternodes).length).toStrictEqual(3)
})
})
99 changes: 99 additions & 0 deletions packages/jellyfish-api-core/src/category/masternode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { ApiClient } from '../.'

export enum MasternodeState {
PRE_ENABLED = 'PRE_ENABLED',
ENABLED = 'ENABLED',
PRE_RESIGNED = 'PRE_RESIGNED',
RESIGNED = 'RESIGNED',
PRE_BANNED = 'PRE_BANNED',
BANNED = 'BANNED'
}
siradji marked this conversation as resolved.
Show resolved Hide resolved

export class Masternode {
siradji marked this conversation as resolved.
Show resolved Hide resolved
private readonly client: ApiClient

constructor (client: ApiClient) {
this.client = client
}

/**
* Creates a masternode creation transaction with given owner and operator addresses.
*
* @param {string} ownerAddress Any valid address for keeping collateral amount
* @param {string} operatorAddress Masternode operator auth address (P2PKH only, unique). If empty, owner address will be used.
siradji marked this conversation as resolved.
Show resolved Hide resolved
siradji marked this conversation as resolved.
Show resolved Hide resolved
* @param {CreateMasternodeOptions} options
siradji marked this conversation as resolved.
Show resolved Hide resolved
* @param {UTXO[]} [options.inputs = []]
siradji marked this conversation as resolved.
Show resolved Hide resolved
* @param {string} [options.inputs.txid] The transaction id
* @param {string} [options.inputs.vout] The output number
siradji marked this conversation as resolved.
Show resolved Hide resolved
* @return {Promise<string>}
*/
async createMasternode (
ownerAddress: string,
operatorAddress?: string,
options: CreateMasternodeOptions = { inputs: [] }
siradji marked this conversation as resolved.
Show resolved Hide resolved
): Promise<string> {
operatorAddress = operatorAddress ?? ownerAddress
return await this.client.call('createmasternode', [ownerAddress, operatorAddress, options.inputs], 'number')
}

/**
* Returns information about multiple masternodes.
*
* @param {MasternodePagination} pagination
* @param {string} [pagination.start]
* @param {boolean} [pagination.including_star = true] Include starting position.
siradji marked this conversation as resolved.
Show resolved Hide resolved
* @param {string} [pagination.limit = 10000] Maximum number of orders to return.
* @param {boolean} [verbose = true] Flag for verbose list. Only ids are returned when false.
*/
async listMasternodes (
pagination: MasternodePagination = {
including_start: true,
siradji marked this conversation as resolved.
Show resolved Hide resolved
limit: 10000
siradji marked this conversation as resolved.
Show resolved Hide resolved
},
verbose: boolean = true
): Promise<MasternodeResult> {
return await this.client.call('listmasternodes', [pagination, verbose], 'number')
}

/**
* Returns information about a single masternode
*
* @param {string} masternodeId The masternode's id.
* @return {Promise<MasternodeResult>}
*/
async getMasternode (masternodeId: string): Promise<MasternodeResult> {
siradji marked this conversation as resolved.
Show resolved Hide resolved
return await this.client.call('getmasternode', [masternodeId], 'number')
}
}

export interface UTXO {
txid?: string
vout?: number
siradji marked this conversation as resolved.
Show resolved Hide resolved
}

export interface CreateMasternodeOptions {
inputs?: UTXO[]
siradji marked this conversation as resolved.
Show resolved Hide resolved
}

export interface MasternodePagination {
start?: string
including_start?: boolean
limit?: number
}

export interface MasternodeInfo {
ownerAuthAddress: string
operatorAuthAddress: string
creationHeight: number
resignHeight: number
resignTx: string
banHeight: number
banTx: string
state: MasternodeState
mintedBlocks: number
targetMultiplier: number
}
siradji marked this conversation as resolved.
Show resolved Hide resolved

export interface MasternodeResult {
[id: string]: MasternodeInfo
}
3 changes: 3 additions & 0 deletions packages/jellyfish-api-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { PoolPair } from './category/poolpair'
import { Token } from './category/token'
import { Oracle } from './category/oracle'
import { Server } from './category/server'
import { Masternode } from './category/masternode'

export * from '@defichain/jellyfish-json'

Expand All @@ -22,6 +23,7 @@ export * as token from './category/token'
export * as account from './category/account'
export * as oracle from './category/oracle'
export * as server from './category/server'
export * as masternode from './category/masternode'

/**
* A protocol agnostic DeFiChain node client, RPC calls are separated into their category.
Expand All @@ -37,6 +39,7 @@ export abstract class ApiClient {
public readonly token = new Token(this)
public readonly oracle = new Oracle(this)
public readonly server = new Server(this)
public readonly masternode = new Masternode(this)

/**
* A promise based procedure call handling
Expand Down
Loading