Skip to content

Commit

Permalink
feat(jellyfish-api-core): add getCustomTx and decodeCustomTx RPCs (#1878
Browse files Browse the repository at this point in the history
)

<!--  Thanks for sending a pull request! -->

#### What this PR does / why we need it:

Adds getCustomTx and decodeCustomTx RPCs and associated tests.

#### Which issue(s) does this PR fixes?:
<!--
(Optional) Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Fixes part of #48
  • Loading branch information
shohamc1 authored Dec 2, 2022
1 parent d55ac9f commit 140c02e
Show file tree
Hide file tree
Showing 4 changed files with 274 additions and 0 deletions.
36 changes: 36 additions & 0 deletions docs/node/CATEGORIES/07-token.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,39 @@ interface UTXO {
vout: number
}
```

## getCustomTx

Get detailed information about any custom transaction.

```ts title="client.token.getCustomTx()"
interface token {
getCustomTx (txid: string, blockhash?: string): Promise<GetCustomTxResult | string>
}

interface GetCustomTxResult {
type: string
valid: boolean
results: object
block_height: string
blockhash: string
confirmations: number
}
```

## decodeCustomTx

Get detailed information about any custom transaction from the raw transaction.

```ts title="client.token.decodeCustomTx()"
interface token {
decodeCustomTx (hexstring: string, iswitness?: boolean): Promise<DecodeCustomTxResult | string>
}

interface DecodeCustomTxResult {
txid: string
type: string
valid: boolean
results: object
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { MasterNodeRegTestContainer } from '@defichain/testcontainers'
import { ContainerAdapterClient } from '../../container_adapter_client'
import { RpcApiError } from '@defichain/jellyfish-api-core'

describe('Get Custom TX', () => {
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 throw error if invalid tx', async () => {
const promise = client.token.decodeCustomTx('000')

await expect(promise).rejects.toThrow(RpcApiError)
await expect(promise).rejects.toMatchObject({
payload: {
code: -22,
message: 'TX decode failed',
method: 'decodecustomtx'
}
})
})

it('should throw error if witness flag is false on witness TX', async () => {
// raw data of 0010c59335d9dcca98531ddb809258829cdfbac88ac953afb8e4e620873b6b7f
const promise = client.token.decodeCustomTx('040000000001012abaf715b5fbed90406a4b40b96c0aa562265568b107d51560f96d0e73dfc9d80100000000fdffffff020000000000000000476a454466547853e8888f0acc092b8c4e60d895e1368e0f20596776f073e9ce4988b2aa0a5a3843160014ffbdc8c27db5abd43917b309e2884f3bf8ea832f0feac243a90300000000b620090000000000160014ffbdc8c27db5abd43917b309e2884f3bf8ea832f00024730440220776dbb86676f26e62e7624b879cc5d6cfba20a8f5aa600950a7da2eac6a600a902204495c5a9123333a75fad85882034cdcb6dd87b15e237a1b20686d320271820f401210275a8747349619132a3a1708a3db1c8d34ea6053d1758c90b4b25bb3f704579fc00000000', false)

await expect(promise).rejects.toThrow(RpcApiError)
await expect(promise).rejects.toMatchObject({
payload: {
code: -22,
message: 'TX decode failed',
method: 'decodecustomtx'
}
})
})

it('should fail if coinbase tx', async () => {
// raw data of b7db79537b12d9195cd066b3b753769fe81a40e380a9bb61e7c8405ca8f3649c
const res = await client.token.decodeCustomTx('040000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0503b0632500ffffffff036741bb6e010000001976a91413d4431d52eb3b0d69da2f6b9ff3ca00ff95a13888ac00e06106360000000017a914dd7730517e0e4969b4e43677ff5bee682e53420a87000000000000000000266a24aa21a9edbc2842d4049400181996ac667c3493be2da9e65c5355fd4db50fdb50a836bcd2000120000000000000000000000000000000000000000000000000000000000000000000000000')
expect(res).toStrictEqual('Coinbase transaction. Not a custom transaction.')
})

it('should fail if not custom tx', async () => {
// raw data of 0010c59335d9dcca98531ddb809258829cdfbac88ac953afb8e4e620873b6b7f
const res = await client.token.decodeCustomTx('04000000000102aaac80ae37b370600e2c125f897e45ca51a01bfa45b48f82e1ceb5305709ff4a0100000000ffffffffaaac80ae37b370600e2c125f897e45ca51a01bfa45b48f82e1ceb5305709ff4a0200000000ffffffff02e4f058e70a0000001600148074c1cbadd0c5cd3e328a4a85769296d4f88f9b00ae95980000000000160014b634b8cdb17e196551d5b27f8118f523aa0b07a40002473044022047081709493b10b3e2c92ea1342218d12ba1e6ca8112767f897d7b6687f3655202203639c1a3fa19670e4bbcf5a27f30869ea9528c4bd2fe2131e3d273295b1648430121030ae5092bc2415b1fb565d8ee1cb03afc3dee8ac1c0292106e94f69c60e12b6040247304402201bec5a01af13cdd6bad1631252dde40c7409d69a45e2842483b499b4c7d239a90220447100133f5b1e1b9e42cf499fa5b1316e917c6db0492bcb98b9ad70672e67510121030ae5092bc2415b1fb565d8ee1cb03afc3dee8ac1c0292106e94f69c60e12b60400000000')
expect(res).toStrictEqual('Not a custom transaction')
})

it('should return custom tx data', async () => {
// raw data of 407bee741668a7406b9b49c1560fe5b15148f86aad031781d0a7e4b6cb9763d0
const res = await client.token.decodeCustomTx('040000000001012abaf715b5fbed90406a4b40b96c0aa562265568b107d51560f96d0e73dfc9d80100000000fdffffff020000000000000000476a454466547853e8888f0acc092b8c4e60d895e1368e0f20596776f073e9ce4988b2aa0a5a3843160014ffbdc8c27db5abd43917b309e2884f3bf8ea832f0feac243a90300000000b620090000000000160014ffbdc8c27db5abd43917b309e2884f3bf8ea832f00024730440220776dbb86676f26e62e7624b879cc5d6cfba20a8f5aa600950a7da2eac6a600a902204495c5a9123333a75fad85882034cdcb6dd87b15e237a1b20686d320271820f401210275a8747349619132a3a1708a3db1c8d34ea6053d1758c90b4b25bb3f704579fc00000000')
expect(res).toMatchObject({
txid: '407bee741668a7406b9b49c1560fe5b15148f86aad031781d0a7e4b6cb9763d0',
type: 'DepositToVault',
valid: true,
results: {
vaultId: '43385a0aaab28849cee973f0766759200f8e36e195d8604e8c2b09cc0a8f88e8',
from: 'bcrt1ql77u3snakk4agwghkvy79zz080uw4qe0jqw2vh',
amount: '157.24692202@15'
}
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { MasterNodeRegTestContainer } from '@defichain/testcontainers'
import { ContainerAdapterClient } from '../../container_adapter_client'
import { RpcApiError } from '@defichain/jellyfish-api-core'
import BigNumber from 'bignumber.js'

describe('Get Custom TX', () => {
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 throw error if transaction is not in block', async () => {
const promise = client.token.getCustomTx('751e8020ed618d6a7d5ffeb1a4df6f0e4ccd344cdd72c5ab400bef63ad99df1a', await container.getBestBlockHash())

await expect(promise).rejects.toThrow(RpcApiError)
await expect(promise).rejects.toMatchObject({
payload: {
code: -5,
message: 'No such transaction found in the provided block.',
method: 'getcustomtx'
}
})
})

it('should throw error if transaction is not in mempool', async () => {
const promise = client.token.getCustomTx('751e8020ed618d6a7d5ffeb1a4df6f0e4ccd344cdd72c5ab400bef63ad99df1a')

await expect(promise).rejects.toThrow(RpcApiError)
await expect(promise).rejects.toMatchObject({
payload: {
code: -5,
message: 'No such mempool, wallet or blockchain transaction.',
method: 'getcustomtx'
}
})
})

it('should throw error if incorrect blockhash', async () => {
const promise = client.token.getCustomTx('751e8020ed618d6a7d5ffeb1a4df6f0e4ccd344cdd72c5ab400bef63ad99df1a', '751e8020ed618d6a7d5ffeb1a4df6f0e4ccd344cdd72c5ab400bef63ad99df1a')

await expect(promise).rejects.toThrow(RpcApiError)
await expect(promise).rejects.toMatchObject({
payload: {
code: -5,
message: 'Block hash not found',
method: 'getcustomtx'
}
})
})

it('should throw error if not custom tx', async () => {
const UTXO = (await client.wallet.listUnspent())[0]
const fromAddress = UTXO.address
const amount = UTXO.amount.toNumber()

const txid = await container.call('sendutxosfrom', [fromAddress, await container.getNewAddress(), amount])
const promise = await client.token.getCustomTx(txid, await container.getBestBlockHash())

expect(promise).toStrictEqual('Not a custom transaction')
})

it('should return custom transaction details - createToken', async () => {
const txid = await client.token.createToken({
symbol: 'TEST',
name: 'TEST',
isDAT: true,
mintable: true,
tradeable: true,
collateralAddress: await container.getNewAddress()
})
await container.generate(1)

const result = await client.token.getCustomTx(txid, await container.getBestBlockHash())

expect(result).toMatchObject({
type: 'CreateToken',
valid: true,
results: {
creationTx: txid,
name: 'TEST',
symbol: 'TEST',
isDAT: true,
mintable: true,
tradeable: true,
finalized: false
},
blockHeight: await client.blockchain.getBlockCount(),
blockhash: await client.blockchain.getBestBlockHash(),
confirmations: 1
})
})

it('should return custom transaction details - createLoanScheme', async () => {
const txid = await client.loan.createLoanScheme({
minColRatio: 105,
interestRate: new BigNumber(5),
id: 'LOAN105'
})
await container.generate(1)

const result = await client.token.getCustomTx(txid, await container.getBestBlockHash())

expect(result).toMatchObject({
type: 'LoanScheme',
valid: true,
results: {
id: 'LOAN105',
interestrate: 5,
mincolratio: 105,
updateHeight: 0
},
blockHeight: await client.blockchain.getBlockCount(),
blockhash: await client.blockchain.getBestBlockHash(),
confirmations: 1
})
})
})
41 changes: 41 additions & 0 deletions packages/jellyfish-api-core/src/category/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,31 @@ export class Token {
async burnTokens (amounts: string, from: string, context?: string, utxos: UTXO[] = []): Promise<string> {
return await this.client.call('burntokens', [{ amounts, from, context }, utxos], 'number')
}

/**
* Get detailed information about any custom transaction.
*
* @param {string} txid Transaction hash
* @param {string} [blockhash] (for confirmed transactions) Hash of the block of the transaction
* @return {Promise<GetCustomTxResult | string>} Inferred custom transaction data, or error message
*/
async getCustomTx (txid: string, blockhash?: string): Promise<GetCustomTxResult | string> {
return await this.client.call('getcustomtx', [txid, blockhash], 'number')
}

/**
* Get detailed information about any custom transaction from the raw transaction.
*
* @param {string} hexstring Serialised custom transaction data
* @param {boolean} [iswitness] is the transaction a serialised witness transaction
* @return {Promise<DecodeCustomTxResult | string>} Inferred custom transaction data, or error message
*/
async decodeCustomTx (hexstring: string, iswitness?: boolean): Promise<DecodeCustomTxResult | string> {
if (iswitness === undefined) {
return await this.client.call('decodecustomtx', [hexstring], 'number')
}
return await this.client.call('decodecustomtx', [hexstring, iswitness], 'number')
}
}

export interface TokenResult {
Expand Down Expand Up @@ -167,3 +192,19 @@ export interface UTXO {
txid: string
vout: number
}

export interface GetCustomTxResult {
type: string
valid: boolean
results: object
blockHeight: string
blockhash: string
confirmations: number
}

export interface DecodeCustomTxResult {
txid: string
type: string
valid: boolean
results: object
}

0 comments on commit 140c02e

Please sign in to comment.