Skip to content

Commit

Permalink
Add SPV listAnchors, listAnchorsPending, listAnchorAuths and `s…
Browse files Browse the repository at this point in the history
…etLastHeight` rpc (#618)

* added listanchors

update desc

* added listanchorspending

* added listanchorauths

typo

* added setlastheight

* pr changes

fix

* jellyfish-testing supports multinodes (#621)

* jellyfish supports multinode

added jellyfish-testing misc

* missing param on doc

* refactor testing in group

apply new TestingGroup to test

fix syntax err

* pr changes

* refine the optional input mnkeys logic

* added configurable container

* refactor testing.ts with default and used exec

Co-authored-by: Fuxing Loh <[email protected]>

* refactor to use tgroup

* fix path

* added generateanchorauth

* refine setlastheight test

* refactor: testinganchor to testinggroupanchor

* add desc for generateanchorauths

* fix

fix

* temp ignore test

* remove redundent default params

* remove extra br

* fix

Co-authored-by: Fuxing Loh <[email protected]>
  • Loading branch information
canonbrother and fuxingloh authored Sep 9, 2021
1 parent 9f1ac6b commit ee7767c
Show file tree
Hide file tree
Showing 12 changed files with 878 additions and 157 deletions.
Original file line number Diff line number Diff line change
@@ -1,69 +1,55 @@
import { RpcApiError } from '@defichain/jellyfish-api-core'
import { MasterNodeRegTestContainer, ContainerGroup, GenesisKeys } from '@defichain/testcontainers'
import { ContainerAdapterClient } from '../../container_adapter_client'
import { TestingGroup, Testing } from '@defichain/jellyfish-testing'
import { GenesisKeys, MasterNodeRegTestContainer } from '@defichain/testcontainers'
import BigNumber from 'bignumber.js'
import { Testing } from '@defichain/jellyfish-testing'

describe('Spv', () => {
const group = new ContainerGroup([
new MasterNodeRegTestContainer(GenesisKeys[0]),
new MasterNodeRegTestContainer(GenesisKeys[1]),
new MasterNodeRegTestContainer(GenesisKeys[2])
])

const clients = [
new ContainerAdapterClient(group.get(0)),
new ContainerAdapterClient(group.get(1)),
new ContainerAdapterClient(group.get(2))
]
const tGroup = TestingGroup.create(3)

beforeAll(async () => {
await group.start()

await tGroup.start()
await setup()
})

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

async function setMockTime (
clients: ContainerAdapterClient[], pastHour: number, futureHour = 0
): Promise<void> {
const offset = Date.now() - (pastHour * 60 * 60 * 1000) + (futureHour * 60 * 60 * 1000)
for (let i = 0; i < clients.length; i += 1) {
await clients[i].misc.setMockTime(offset)
}
async function setMockTime (offsetHour: number): Promise<void> {
await tGroup.exec(async testing => {
await testing.misc.offsetTimeHourly(offsetHour)
})
}

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

// time travel back 13 hours ago
await setMockTime(clients, 13)
// time travel back 12 hours ago
const initOffsetHour = -12
await setMockTime(initOffsetHour)

const anchorFreq = 15
for (let i = 0; i < anchorFreq; i += 1) {
const container = group.get(i % clients.length)
// 15 as anchor frequency
for (let i = 0; i < 15; i += 1) {
const { container } = tGroup.get(i % tGroup.length())
await container.generate(1)
await group.waitForSync()
await tGroup.waitForSync()
}

{
const count = await group.get(0).getBlockCount()
const count = await tGroup.get(0).container.getBlockCount()
expect(count).toStrictEqual(15)
}

// check the auth and confirm anchor mn teams at current height 15
await group.get(0).waitForAnchorTeams(clients.length)
await tGroup.get(0).container.waitForAnchorTeams(tGroup.length())

// assertion for team
for (let i = 0; i < clients.length; i += 1) {
const container = group.get(i % clients.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(clients.length)
expect(team.confirm.length).toStrictEqual(clients.length)
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))
Expand All @@ -74,41 +60,31 @@ describe('Spv', () => {

{
// anchor auths length should be still zero
const auths = await group.get(0).call('spv_listanchorauths')
const auths = await tGroup.get(0).container.call('spv_listanchorauths')
expect(auths.length).toStrictEqual(0)
}

// forward 3 hours from 12 hours ago and generate 15 blocks per hour
// set 3 hours because block height and hash chosen is then 3 hours
// then every 15 blocks will be matched again
for (let i = 1; i < 3 + 1; i += 1) {
await setMockTime(clients, 12, i)
await group.get(0).generate(15)
await group.waitForSync()
}

{
const count = await group.get(0).getBlockCount()
expect(count).toStrictEqual(60)
}
// generate 2 anchor auths
await tGroup.anchor.generateAnchorAuths(2, initOffsetHour)

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

// check each container should be quorum ready
for (let i = 0; i < clients.length; i += 1) {
const container = group.get(i % clients.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(1)
expect(auths[0].signers).toStrictEqual(clients.length)
expect(auths[0].blockHeight).toStrictEqual(15)
expect(auths[0].creationHeight).toStrictEqual(60)
expect(auths.length).toStrictEqual(2)
expect(auths[0].signers).toStrictEqual(tGroup.length())
}
}

it('should be failed as invalid txid', async () => {
const rewardAddress = await clients[0].spv.getNewAddress()
const rewardAddress = await tGroup.get(0).rpc.spv.getNewAddress()

const promise = clients[0].spv.createAnchor([{
const promise = tGroup.get(0).rpc.spv.createAnchor([{
txid: 'INVALID',
vout: 2,
amount: 15800,
Expand All @@ -120,9 +96,9 @@ describe('Spv', () => {
})

it('should be failed as not enough money to create anchor', async () => {
const rewardAddress = await clients[0].spv.getNewAddress()
const rewardAddress = await tGroup.get(0).rpc.spv.getNewAddress()

const promise = clients[0].spv.createAnchor([{
const promise = tGroup.get(0).rpc.spv.createAnchor([{
txid: '11a276bb25585f6973a4dd68373cffff41dbcaddf12bbc1c2b489d1dc84564ee',
vout: 2,
amount: 1,
Expand All @@ -134,9 +110,9 @@ describe('Spv', () => {
})

it('should be failed as invalid privkey', async () => {
const rewardAddress = await clients[0].spv.getNewAddress()
const rewardAddress = await tGroup.get(0).rpc.spv.getNewAddress()

const promise = clients[0].spv.createAnchor([{
const promise = tGroup.get(0).rpc.spv.createAnchor([{
txid: '11a276bb25585f6973a4dd68373cffff41dbcaddf12bbc1c2b489d1dc84564ee',
vout: 2,
amount: 15800,
Expand All @@ -148,7 +124,7 @@ describe('Spv', () => {
})

it('should be failed as invalid address', async () => {
const promise = clients[0].spv.createAnchor([{
const promise = tGroup.get(0).rpc.spv.createAnchor([{
txid: '11a276bb25585f6973a4dd68373cffff41dbcaddf12bbc1c2b489d1dc84564ee',
vout: 2,
amount: 15800,
Expand All @@ -160,9 +136,9 @@ describe('Spv', () => {
})

it('should createAnchor', async () => {
const rewardAddress = await clients[0].spv.getNewAddress()
const rewardAddress = await tGroup.get(0).rpc.spv.getNewAddress()

const anchor = await clients[0].spv.createAnchor([{
const anchor = await tGroup.get(0).rpc.spv.createAnchor([{
txid: '11a276bb25585f6973a4dd68373cffff41dbcaddf12bbc1c2b489d1dc84564ee',
vout: 2,
amount: 15800,
Expand All @@ -171,56 +147,56 @@ describe('Spv', () => {
expect(typeof anchor.txHex).toStrictEqual('string')
expect(typeof anchor.txHash).toStrictEqual('string')
expect(typeof anchor.defiHash).toStrictEqual('string')
expect(anchor.defiHeight).toStrictEqual(15)
expect(typeof anchor.defiHeight).toStrictEqual('number')
expect(anchor.estimatedReward).toStrictEqual(new BigNumber(0))
expect(anchor.cost).toStrictEqual(new BigNumber(3556))
expect(anchor.sendResult).toStrictEqual(0)
expect(anchor.sendMessage).toStrictEqual('Success')

// pending anchor list is updated
{
const pending = await group.get(0).call('spv_listanchorspending')
const pending = await tGroup.get(0).container.call('spv_listanchorspending')
expect(pending.length).toStrictEqual(1)
}

// generate the anchor block
await group.get(0).generate(1)
await tGroup.get(0).generate(1)

// pending is cleared
{
const pending = await group.get(0).call('spv_listanchorspending')
const pending = await tGroup.get(0).container.call('spv_listanchorspending')
expect(pending.length).toStrictEqual(0)
}

// should be not active yet
const anchors = await group.get(0).call('spv_listanchors')
const anchors = await tGroup.get(0).container.call('spv_listanchors')
expect(anchors.length).toStrictEqual(1)
expect(anchors[0].btcBlockHeight).toStrictEqual(0)
expect(typeof anchors[0].btcBlockHash).toStrictEqual('string')
expect(typeof anchors[0].btcTxHash).toStrictEqual('string')
expect(typeof anchors[0].previousAnchor).toStrictEqual('string')
expect(anchors[0].defiBlockHeight).toStrictEqual(15)
expect(typeof anchors[0].defiBlockHeight).toStrictEqual('number')
expect(typeof anchors[0].defiBlockHash).toStrictEqual('string')
expect(anchors[0].rewardAddress).toStrictEqual(rewardAddress)
expect(anchors[0].confirmations).toBeLessThan(6)
expect(anchors[0].signatures).toStrictEqual(2)
expect(anchors[0].anchorCreationHeight).toStrictEqual(60)
expect(typeof anchors[0].anchorCreationHeight).toStrictEqual('number')
expect(anchors[0].active).toStrictEqual(false)

// to activate anchor at min 6 conf
await group.get(0).call('spv_setlastheight', [6])
await tGroup.get(0).container.call('spv_setlastheight', [6])

{
// should be active now
const anchors = await group.get(0).call('spv_listanchors')
const anchors = await tGroup.get(0).container.call('spv_listanchors')
expect(anchors[0].confirmations).toBeGreaterThanOrEqual(6)
expect(anchors[0].active).toStrictEqual(true)
}

{
// auths back to zero after the anchor active
const auths = await group.get(0).call('spv_listanchorauths')
expect(auths.length).toStrictEqual(0)
// auths reduce from 2 to 1 after the anchor active
const auths = await tGroup.get(0).container.call('spv_listanchorauths')
expect(auths.length).toStrictEqual(1)
}
})
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { TestingGroup } from '@defichain/jellyfish-testing'
import { GenesisKeys } from '@defichain/testcontainers'

describe('Spv', () => {
const tGroup = TestingGroup.create(3)

beforeAll(async () => {
await tGroup.start()
await setup()
})

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

async function setMockTime (offsetHour: number): Promise<void> {
await tGroup.exec(async testing => {
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)

// time travel back 12 hours ago
const initOffsetHour = -12
await setMockTime(initOffsetHour)

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

const blockCount = await tGroup.get(0).container.getBlockCount()
expect(blockCount).toStrictEqual(15)

// check the auth and confirm anchor mn teams
await tGroup.get(0).container.waitForAnchorTeams(tGroup.length())

// assertion for team
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))
}

// generate 2 anchor auths
await tGroup.anchor.generateAnchorAuths(2, initOffsetHour)

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

it('should listAnchorAuths', async () => {
for (let i = 0; i < 2; i += 1) {
const auths = await tGroup.get(0).rpc.spv.listAnchorAuths()
expect(auths.length).toStrictEqual(2)
expect(typeof auths[i].previousAnchor).toStrictEqual('string')
expect(typeof auths[i].blockHeight).toStrictEqual('number')
expect(typeof auths[i].blockHash).toStrictEqual('string')
expect(typeof auths[i].creationHeight).toStrictEqual('number')
expect(typeof auths[i].signers).toStrictEqual('number')
expect(auths[i].signers).toStrictEqual(tGroup.length())
expect(auths[i].signees?.length).toStrictEqual(tGroup.length())
expect(auths[i].signees?.includes(GenesisKeys[0].operator.address))
expect(auths[i].signees?.includes(GenesisKeys[1].operator.address))
expect(auths[i].signees?.includes(GenesisKeys[2].operator.address))
}
})
})
Loading

0 comments on commit ee7767c

Please sign in to comment.