diff --git a/packages/jellyfish-api-core/__tests__/category/masternode/resignMasternode.test.ts b/packages/jellyfish-api-core/__tests__/category/masternode/resignMasternode.test.ts new file mode 100644 index 0000000000..9f9f69b3d8 --- /dev/null +++ b/packages/jellyfish-api-core/__tests__/category/masternode/resignMasternode.test.ts @@ -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) { + expect(masternode.state).toStrictEqual(MasternodeState.PRE_RESIGNED) + expect(masternode.resignTx).toStrictEqual(hex) + } + }) + + it('should resignMasternode with utxos', async () => { + 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) { + 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 }]) + + 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') + }) +}) diff --git a/packages/jellyfish-api-core/src/category/masternode.ts b/packages/jellyfish-api-core/src/category/masternode.ts index 38c72146b1..33a3189d90 100644 --- a/packages/jellyfish-api-core/src/category/masternode.ts +++ b/packages/jellyfish-api-core/src/category/masternode.ts @@ -105,6 +105,19 @@ export class Masternode { async getMasternode (masternodeId: string): Promise> { 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} Resignation Transaction. + */ + async resignMasternode (masternodeId: string, utxos: UTXO[] = []): Promise { + return await this.client.call('resignmasternode', [masternodeId, utxos], 'number') + } } export interface UTXO { diff --git a/website/docs/jellyfish/api/masternode.md b/website/docs/jellyfish/api/masternode.md index 03afce47f9..3192e7f86e 100644 --- a/website/docs/jellyfish/api/masternode.md +++ b/website/docs/jellyfish/api/masternode.md @@ -128,3 +128,18 @@ interface MasternodeResult { [id: string]: T } ``` + +## resignMasternode + +Creates a transaction resigning a masternode. + +```ts title="client.masternode.resignMasternode()" +interface masternode { + resignMasternode (masternodeId: string, utxos: UTXO[] = []): Promise +} + +interface UTXO { + txid: string + vout: number +} +```