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

Added resignMasternode RPC #419

Merged
merged 17 commits into from
Jun 28, 2021
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { MasterNodeRegTestContainer } from '@defichain/testcontainers'
import { ContainerAdapterClient } from '../../container_adapter_client'
import { MasternodeState } from '../../../src/category/masternode'
import { RpcApiError } from '../../../src'

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 resignMasternode', async () => {
const ownerAddress = await container.getNewAddress()
const masternodeId = await client.masternode.createMasternode(ownerAddress)

await container.generate(1)

const hex = await client.masternode.resignMasternode(masternodeId)
expect(typeof hex).toStrictEqual('string')
expect(hex.length).toStrictEqual(64)

await container.generate(1)

const resignedMasternode = Object.values(await client.masternode.listMasternodes()).filter(mn => mn.ownerAuthAddress === ownerAddress)

expect(resignedMasternode.length).toStrictEqual(1)
for (const masternode of resignedMasternode) {
siradji marked this conversation as resolved.
Show resolved Hide resolved
expect(masternode.state).toStrictEqual(MasternodeState.PRE_RESIGNED)
expect(masternode.resignTx).toStrictEqual(hex)
}
})

it('should resignMasternode with utxos', async () => {
siradji marked this conversation as resolved.
Show resolved Hide resolved
const ownerAddress = await container.getNewAddress()
const masternodeId = await client.masternode.createMasternode(ownerAddress)
const { txid } = await container.fundAddress(ownerAddress, 10)

await container.generate(1)

const utxos = (await container.call('listunspent'))
.filter((utxo: any) => utxo.txid === txid)
.map((utxo: any) => {
return {
txid: utxo.txid,
vout: utxo.vout
}
})

const hex = await client.masternode.resignMasternode(masternodeId, utxos)
expect(typeof hex).toStrictEqual('string')
expect(hex.length).toStrictEqual(64)

await container.generate(1)

const resignedMasternode = Object.values(await client.masternode.listMasternodes()).filter(mn => mn.ownerAuthAddress === ownerAddress)

expect(resignedMasternode.length).toStrictEqual(1)
for (const masternode of resignedMasternode) {
siradji marked this conversation as resolved.
Show resolved Hide resolved
expect(masternode.state).toStrictEqual(MasternodeState.PRE_RESIGNED)
expect(masternode.resignTx).toStrictEqual(hex)
}
})

it('should throw an error with invalid masternode id', async () => {
const invalidMasternodeId = 'b3efcc1bf6cb77c465d7f5686a55f967e73b1a048a3716fdbffa523e22b66frb'
const promise = client.masternode.resignMasternode(invalidMasternodeId)

await expect(promise).rejects.toThrow(RpcApiError)
await expect(promise).rejects.toThrow(`The masternode ${invalidMasternodeId} does not exist`)
})

it('should not resignMasternode with arbitrary utxos', async () => {
const ownerAddress = await container.getNewAddress()
const masternodeId = await client.masternode.createMasternode(ownerAddress)
const { txid, vout } = await container.fundAddress(await container.getNewAddress(), 10)

await container.generate(1)

const promise = client.masternode.resignMasternode(masternodeId, [{ txid, vout }])

siradji marked this conversation as resolved.
Show resolved Hide resolved
await expect(promise).rejects.toThrow(RpcApiError)
await expect(promise).rejects.toThrow('RpcApiError: \'Test ResignMasternodeTx execution failed:\n' + 'tx must have at least one input from the owner\', code: -32600, method: resignmasternode')
})
})
13 changes: 13 additions & 0 deletions packages/jellyfish-api-core/src/category/masternode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,19 @@ export class Masternode {
async getMasternode (masternodeId: string): Promise<MasternodeResult<MasternodeInfo>> {
return await this.client.call('getmasternode', [masternodeId], 'number')
}

/**
* Creates a transaction resigning a masternode.
*
* @param {string} masternodeId The masternode's id.
* @param {UTXO[]} [utxos = []] Array of specified utxos to spend.
* @param {string} [utxos.txid] The transaction id.
* @param {number} [utxos.vout] The output number.
* @return {Promise<string>} Resignation Transaction.
*/
async resignMasternode (masternodeId: string, utxos: UTXO[] = []): Promise<string> {
return await this.client.call('resignmasternode', [masternodeId, utxos], 'number')
}
}

export interface UTXO {
Expand Down
15 changes: 15 additions & 0 deletions website/docs/jellyfish/api/masternode.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,18 @@ interface MasternodeResult<T> {
[id: string]: T
}
```

## resignMasternode

Creates a transaction resigning a masternode.

```ts title="client.masternode.resignMasternode()"
interface masternode {
resignMasternode (masternodeId: string, utxos: UTXO[] = []): Promise<string>
}

interface UTXO {
txid: string
vout: number
}
```