Skip to content
This repository has been archived by the owner on Jun 3, 2022. It is now read-only.

/anchors endpoint #389

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ COPY LICENSE ./
COPY package.json ./
COPY package-lock.json ./

RUN npm ci
siradji marked this conversation as resolved.
Show resolved Hide resolved
RUN npm i && npm ci

COPY tsconfig.json ./
COPY tsconfig.build.json ./
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
},
"devDependencies": {
"@defichain/jellyfish-crypto": ">=0.43.0",
"@defichain/jellyfish-testing": ">=0.43.0",
"@defichain/testcontainers": ">=0.43.0",
"@defichain/testing": ">=0.43.0",
"@nestjs/cli": "^8.1.1",
Expand Down
35 changes: 35 additions & 0 deletions packages/whale-api-client/src/api/anchors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { WhaleApiClient } from '../whale.api.client'

/**
* DeFi whale endpoint for poolpair related services.
*/

export class Anchors {
constructor (private readonly client: WhaleApiClient) {}

/**
* List anchors
*/

fuxingloh marked this conversation as resolved.
Show resolved Hide resolved
async list (): Promise<AnchorData[]> {
siradji marked this conversation as resolved.
Show resolved Hide resolved
return await this.client.requestData('GET', 'anchors')
}
}

export interface AnchorData {
btcBlock: {
height: number
hash: string
txHash: string
}
defiBlock: {
height: number
hash: string
}
previousAnchor: string
rewardAddress: string
confirmations: number
signatures: number
active?: boolean
anchorCreationHeight?: number
}
1 change: 1 addition & 0 deletions packages/whale-api-client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export * as prices from './api/prices'
export * as stats from './api/stats'
export * as rawtx from './api/rawtx'
export * as fee from './api/fee'
export * as anchors from './api/anchors'

export * from './whale.api.client'
export * from './whale.api.response'
Expand Down
3 changes: 2 additions & 1 deletion packages/whale-api-client/src/whale.api.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Prices } from './api/prices'
import { Stats } from './api/stats'
import { Rawtx } from './api/rawtx'
import { Fee } from './api/fee'
import { Anchors } from './api/anchors'

/**
* WhaleApiClient Options
Expand Down Expand Up @@ -74,7 +75,7 @@ export class WhaleApiClient {
public readonly stats = new Stats(this)
public readonly rawtx = new Rawtx(this)
public readonly fee = new Fee(this)

public readonly anchors = new Anchors(this)
constructor (
protected readonly options: WhaleApiClientOptions
) {
Expand Down
4 changes: 3 additions & 1 deletion src/module.api/_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { PriceController } from '@src/module.api/price.controller'
import { StatsController } from '@src/module.api/stats.controller'
import { FeeController } from '@src/module.api/fee.controller'
import { RawtxController } from '@src/module.api/rawtx.controller'
import { AnchorsController } from '@src/module.api/anchors.controller'

/**
* Exposed ApiModule for public interfacing
Expand All @@ -41,7 +42,8 @@ import { RawtxController } from '@src/module.api/rawtx.controller'
PriceController,
StatsController,
FeeController,
RawtxController
RawtxController,
AnchorsController
],
providers: [
{ provide: APP_PIPE, useClass: ApiValidationPipe },
Expand Down
139 changes: 139 additions & 0 deletions src/module.api/anchors.controller.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { GenesisKeys, MasterNodeRegTestContainer } from '@defichain/testcontainers'
import { NestFastifyApplication } from '@nestjs/platform-fastify'
import { createTestingApp, stopTestingApp } from '@src/e2e.module'
import { AnchorsController } from '@src/module.api/anchors.controller'
import { TestingGroup } from '@defichain/jellyfish-testing'

let tGroup: TestingGroup
let container: MasterNodeRegTestContainer
let app: NestFastifyApplication
let controller: AnchorsController

beforeAll(async () => {
tGroup = TestingGroup.create(3)
container = tGroup.group.get(0)

await container.start()
await tGroup.start()

app = await createTestingApp(container)
controller = app.get(AnchorsController)

await setup()
})

afterAll(async () => {
await stopTestingApp(container, app)
await tGroup.stop()
siradji marked this conversation as resolved.
Show resolved Hide resolved
})

async function setMockTime (offsetHour: number): Promise<void> {
await tGroup.exec(async (testing: any) => {
await testing.misc.offsetTimeHourly(offsetHour)
})
}

async function setup (): Promise<void> {
{
const auths = await tGroup.get(0).container.call('spv_listanchorauths')
expect(auths.length).toStrictEqual(0)
}

const initOffsetHour = -12
await setMockTime(initOffsetHour)

for (let i = 0; i < 15; i += 1) {
const { container } = tGroup.get(i % tGroup.length())
await container.generate(1)
await tGroup.waitForSync()
}

await tGroup.get(0).container.waitForAnchorTeams(tGroup.length())

for (let i = 0; i < tGroup.length(); i += 1) {
const { container } = tGroup.get(i % tGroup.length())
const team = await container.call('getanchorteams')
expect(team.auth.length).toStrictEqual(tGroup.length())
expect(team.confirm.length).toStrictEqual(tGroup.length())
expect(team.auth.includes(GenesisKeys[0].operator.address))
expect(team.auth.includes(GenesisKeys[1].operator.address))
expect(team.auth.includes(GenesisKeys[2].operator.address))
expect(team.confirm.includes(GenesisKeys[0].operator.address))
expect(team.confirm.includes(GenesisKeys[1].operator.address))
expect(team.confirm.includes(GenesisKeys[2].operator.address))
}

await tGroup.anchor.generateAnchorAuths(2, initOffsetHour)

await tGroup.get(0).container.waitForAnchorAuths(tGroup.length())

for (let i = 0; i < tGroup.length(); i += 1) {
const { container } = tGroup.get(i % tGroup.length())
const auths = await container.call('spv_listanchorauths')
expect(auths.length).toStrictEqual(2)
expect(auths[0].signers).toStrictEqual(tGroup.length())
}

await tGroup.get(0).container.call('spv_setlastheight', [1])
const anchor1 = await createAnchor()
await tGroup.get(0).generate(1)
await tGroup.waitForSync()

await tGroup.get(0).container.call('spv_setlastheight', [2])
const anchor2 = await createAnchor()
await tGroup.get(0).generate(1)
await tGroup.waitForSync()

await tGroup.get(0).container.call('spv_setlastheight', [3])
const anchor3 = await createAnchor()
await tGroup.get(0).generate(1)
await tGroup.waitForSync()

await tGroup.get(0).container.call('spv_setlastheight', [4])
const anchor4 = await createAnchor()
await tGroup.get(0).generate(1)
await tGroup.waitForSync()

await tGroup.get(1).container.call('spv_sendrawtx', [anchor1.txHex])
await tGroup.get(1).container.call('spv_sendrawtx', [anchor2.txHex])
await tGroup.get(1).container.call('spv_sendrawtx', [anchor3.txHex])
await tGroup.get(1).container.call('spv_sendrawtx', [anchor4.txHex])
await tGroup.get(1).generate(1)
await tGroup.waitForSync()

await tGroup.get(0).container.call('spv_setlastheight', [6])
}

async function createAnchor (): Promise<any> {
const rewardAddress = await tGroup.get(0).rpc.spv.getNewAddress()
return await tGroup.get(0).rpc.spv.createAnchor([{
txid: '11a276bb25585f6973a4dd68373cffff41dbcaddf12bbc1c2b489d1dc84564ee',
vout: 2,
amount: 15800,
privkey: 'b0528d87cfdb09f72c9d10b7b3cc00727062d93537a3e8abcf1fde821d08b59d'
}], rewardAddress)
}

describe('list', () => {
it('should list anchors', async () => {
const response = await controller.list()
expect(response.length).toStrictEqual(4)
expect(response[0]).toStrictEqual({
btcBlock: {
height: 4,
hash: '0000000000000001000000000000000100000000000000010000000000000001',
txHash: expect.any(String)
},
defiBlock: {
height: 30,
hash: expect.any(String)
},
previousAnchor: '0000000000000000000000000000000000000000000000000000000000000000',
rewardAddress: expect.any(String),
confirmations: 3,
signatures: 2,
active: false,
anchorCreationHeight: 75
})
})
})
149 changes: 149 additions & 0 deletions src/module.api/anchors.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { Test, TestingModule } from '@nestjs/testing'
import { GenesisKeys, MasterNodeRegTestContainer } from '@defichain/testcontainers'
import { JsonRpcClient } from '@defichain/jellyfish-api-jsonrpc'
import { CacheModule } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { TestingGroup } from '@defichain/jellyfish-testing'
import { AnchorsController } from '@src/module.api/anchors.controller'

let tGroup: TestingGroup
let controller: AnchorsController
let container: MasterNodeRegTestContainer

beforeAll(async () => {
tGroup = TestingGroup.create(3)
container = tGroup.group.get(0)

await tGroup.start()
const client = new JsonRpcClient(await container.getCachedRpcUrl())

const app: TestingModule = await Test.createTestingModule({
imports: [
CacheModule.register()
],
controllers: [AnchorsController],
providers: [
{ provide: JsonRpcClient, useValue: client },
ConfigService
]
}).compile()

controller = app.get(AnchorsController)

await setup()
})

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

async function setMockTime (offsetHour: number): Promise<void> {
await tGroup.exec(async (testing: any) => {
await testing.misc.offsetTimeHourly(offsetHour)
})
}

async function setup (): Promise<void> {
{
const auths = await tGroup.get(0).container.call('spv_listanchorauths')
expect(auths.length).toStrictEqual(0)
}

const initOffsetHour = -12
await setMockTime(initOffsetHour)

for (let i = 0; i < 15; i += 1) {
const { container } = tGroup.get(i % tGroup.length())
await container.generate(1)
await tGroup.waitForSync()
}

await tGroup.get(0).container.waitForAnchorTeams(tGroup.length())

for (let i = 0; i < tGroup.length(); i += 1) {
const { container } = tGroup.get(i % tGroup.length())
const team = await container.call('getanchorteams')
expect(team.auth.length).toStrictEqual(tGroup.length())
expect(team.confirm.length).toStrictEqual(tGroup.length())
expect(team.auth.includes(GenesisKeys[0].operator.address))
expect(team.auth.includes(GenesisKeys[1].operator.address))
expect(team.auth.includes(GenesisKeys[2].operator.address))
expect(team.confirm.includes(GenesisKeys[0].operator.address))
expect(team.confirm.includes(GenesisKeys[1].operator.address))
expect(team.confirm.includes(GenesisKeys[2].operator.address))
}

await tGroup.anchor.generateAnchorAuths(2, initOffsetHour)

await tGroup.get(0).container.waitForAnchorAuths(tGroup.length())

for (let i = 0; i < tGroup.length(); i += 1) {
const { container } = tGroup.get(i % tGroup.length())
const auths = await container.call('spv_listanchorauths')
expect(auths.length).toStrictEqual(2)
expect(auths[0].signers).toStrictEqual(tGroup.length())
}

await tGroup.get(0).container.call('spv_setlastheight', [1])
const anchor1 = await createAnchor()
await tGroup.get(0).generate(1)
await tGroup.waitForSync()

await tGroup.get(0).container.call('spv_setlastheight', [2])
const anchor2 = await createAnchor()
await tGroup.get(0).generate(1)
await tGroup.waitForSync()

await tGroup.get(0).container.call('spv_setlastheight', [3])
const anchor3 = await createAnchor()
await tGroup.get(0).generate(1)
await tGroup.waitForSync()

await tGroup.get(0).container.call('spv_setlastheight', [4])
const anchor4 = await createAnchor()
await tGroup.get(0).generate(1)
await tGroup.waitForSync()

await tGroup.get(1).container.call('spv_sendrawtx', [anchor1.txHex])
await tGroup.get(1).container.call('spv_sendrawtx', [anchor2.txHex])
await tGroup.get(1).container.call('spv_sendrawtx', [anchor3.txHex])
await tGroup.get(1).container.call('spv_sendrawtx', [anchor4.txHex])
await tGroup.get(1).generate(1)
await tGroup.waitForSync()

await tGroup.get(0).container.call('spv_setlastheight', [6])
}

async function createAnchor (): Promise<any> {
const rewardAddress = await tGroup.get(0).rpc.spv.getNewAddress()
return await tGroup.get(0).rpc.spv.createAnchor([{
txid: '11a276bb25585f6973a4dd68373cffff41dbcaddf12bbc1c2b489d1dc84564ee',
vout: 2,
amount: 15800,
privkey: 'b0528d87cfdb09f72c9d10b7b3cc00727062d93537a3e8abcf1fde821d08b59d'
}], rewardAddress)
}

describe('list', () => {
it('should get list of anchors rewards of four anchors', async function () {
const response = await controller.list()
expect(response.length).toStrictEqual(4)
expect(response[0]).toStrictEqual({
btcBlock: {
height: 4,
hash: '0000000000000001000000000000000100000000000000010000000000000001',
txHash: expect.any(String)
},
defiBlock: {
height: 30,
hash: expect.any(String)
},
previousAnchor: '0000000000000000000000000000000000000000000000000000000000000000',
rewardAddress: expect.any(String),
confirmations: 3,
signatures: 2,
active: false,
anchorCreationHeight: 75
})
})
})
Loading