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

Add missing escape hatch tests #450

Merged
merged 11 commits into from
Aug 3, 2023
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { MerkleProof, PositionLeaf } from '@explorer/state'
import { AssetId, PedersenHash, StarkKey } from '@explorer/types'
import { expect } from 'earl'

import { serializeMerkleProofForEscape } from './serializeMerkleProofForEscape'

describe(serializeMerkleProofForEscape.name, () => {
it('should serialize a merkle proof', () => {
const positionLeaf = new PositionLeaf(StarkKey.fake('beef'), 69n, [
{ assetId: AssetId('BTC-10'), balance: 3n, fundingIndex: 4n },
])

const perpetualMerkleProof: MerkleProof<PositionLeaf> = {
leaf: positionLeaf,
leafIndex: 7n,
leafPrefixLength: 3,
path: [
{
left: PedersenHash(
'0000000000000000000000000000000000000000000000000000000000000000'
),
right: PedersenHash(
'004254432d313000000000000000000080000000000000048000000000000003'
),
},
{
left: PedersenHash(
'0726c9603ac7bccf9523718f2f1b45fac7673780ae5089ecf2ab0e67f96f7dd0'
),
right: PedersenHash(
'0beef00000000000000000000000000000000000000000000000000000000000'
),
},
{
left: PedersenHash(
'0271eb38f2bf004c2407a88958620a5d2c25fcee84590e50f0d2e37858bfba62'
),
right: PedersenHash(
'0000000000000000000000000000000000000000000080000000000000450001'
),
},
{
left: PedersenHash(
'028109b4e56fad0455aa4b316045c93937b1e7e4e0fc663db375b9e67c80c620'
),
right: PedersenHash(
'040e52d372a32b20035f44f456e7beea936b35298c64f5b8d6a56604ff4b3a6d'
),
},
{
left: PedersenHash(
'037ff447129584d02735f8b24db6d39dfc7d1ccbd7459fca871b795bffbeddf2'
),
right: PedersenHash(
'00968c9e36fd542708ca7d03ce09b81835ee1da53d50faa4bba820b28da6f93e'
),
},
{
left: PedersenHash(
'07d54313f8a6085c0f072dab5a3bbb28132f74b4adc3a00238c465163d052ed4'
),
right: PedersenHash(
'0490d6399cb336c5b4f017c608cdf662dba1942fbbc9c744d7e1cdda5feedaf9'
),
},
],
perpetualAssetCount: 1,
root: PedersenHash(
'00c9c74a31d9247f04cc9dbef31686d072fec342810c56d53855fa81e7af4bfd'
),
}

expect(serializeMerkleProofForEscape(perpetualMerkleProof)).toEqual([
0n,
30001526795319849252324097071977050905586053462486420188706939170735295300352n,
51755519582484935180678720569846960714349028541078472964416237606474853375243n,
108074501258364212671951141525442839776135535004087860430382408120666966458368n,
17694445779186356052184739537155289443108441618591601491420520392058218456608n,
154742504910672535520018688n,
18121855379086738056127543246288844289035300259056337124023244527956344463876n,
6478720698574293975535197600228144301494206361683389185958982644949786979584n,
25328225043177823319220877681895856553959192982182631511906782068825060663072n,
68095377834997473110967502460299490343402840164279191569551851314365135928832n,
56687861124970118891962212466772367015533463385725521495813263122984365911364n,
65511553092019813532680860505595526153543086133745386269528099820127758514432n,
5704187325266392042567303450826654811460944722812564435329740863596318212048n,
14336n,
])
})
})
208 changes: 208 additions & 0 deletions packages/backend/src/core/FreezeCheckService.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import { EthereumAddress, Timestamp } from '@explorer/types'
import { Logger } from '@l2beat/backend-tools'
import { expect, mockFn, mockObject } from 'earl'
import { ethers } from 'ethers'

import { KeyValueStore } from '../peripherals/database/KeyValueStore'
import { UserTransactionRepository } from '../peripherals/database/transactions/UserTransactionRepository'
import { EthereumClient } from '../peripherals/ethereum/EthereumClient'
import { FreezeCheckService } from './FreezeCheckService'

describe(FreezeCheckService.name, () => {
describe(FreezeCheckService.prototype.updateFreezeStatus.name, () => {
it('sets to not-frozen when no blocks synced', async () => {
const kvStoreMock = mockObject<KeyValueStore>({
findByKey: mockFn().resolvesTo(undefined),
addOrUpdate: mockFn().resolvesTo(undefined),
})

const service = new FreezeCheckService(
EthereumAddress.ZERO,
mockObject<EthereumClient>(),
kvStoreMock,
mockObject<UserTransactionRepository>(),
Logger.SILENT
)

await service.updateFreezeStatus()

expect(kvStoreMock.addOrUpdate).toHaveBeenOnlyCalledWith({
key: 'freezeStatus',
value: 'not-frozen',
})
})
it('sets to frozen when isFrozen() returns true', async () => {
const kvStoreMock = mockObject<KeyValueStore>({
findByKey: mockFn().resolvesTo(1),
addOrUpdate: mockFn().resolvesTo(undefined),
})
const ethereumClientMock = mockObject<EthereumClient>({
getBlockTimestamp: mockFn().resolvesTo(123000000),
call: mockFn()
.given(
EthereumAddress.fake('fade'),
'isFrozen',
'function isFrozen() public view returns (bool)'
)
.resolvesToOnce([true, undefined])
.given(
EthereumAddress.fake('fade'),
'FREEZE_GRACE_PERIOD',
'function FREEZE_GRACE_PERIOD() view returns (uint256)'
)
.resolvesToOnce([ethers.BigNumber.from(360000), undefined]),
})
const userTransactionRepositoryMock =
mockObject<UserTransactionRepository>({
findOldestNotIncluded: mockFn().resolvesTo({
timestamp: Timestamp.fromSeconds(123000000),
}),
})

const service = new FreezeCheckService(
EthereumAddress.fake('fade'),
ethereumClientMock,
kvStoreMock,
userTransactionRepositoryMock,
Logger.SILENT
)

await service.updateFreezeStatus()

expect(kvStoreMock.addOrUpdate).toHaveBeenOnlyCalledWith({
key: 'freezeStatus',
value: 'frozen',
})
})

it('sets to not-frozen when no not-included transactions', async () => {
const kvStoreMock = mockObject<KeyValueStore>({
findByKey: mockFn().resolvesTo(1),
addOrUpdate: mockFn().resolvesTo(undefined),
})
const ethereumClientMock = mockObject<EthereumClient>({
getBlockTimestamp: mockFn().resolvesTo(123000000),
call: mockFn()
.given(
EthereumAddress.fake('fade'),
'isFrozen',
'function isFrozen() public view returns (bool)'
)
.resolvesToOnce([false, undefined])
.given(
EthereumAddress.fake('fade'),
'FREEZE_GRACE_PERIOD',
'function FREEZE_GRACE_PERIOD() view returns (uint256)'
)
.resolvesToOnce([ethers.BigNumber.from(360000), undefined]),
})
const userTransactionRepositoryMock =
mockObject<UserTransactionRepository>({
findOldestNotIncluded: mockFn().resolvesTo(undefined),
})

const service = new FreezeCheckService(
EthereumAddress.fake('fade'),
ethereumClientMock,
kvStoreMock,
userTransactionRepositoryMock,
Logger.SILENT
)

await service.updateFreezeStatus()

expect(kvStoreMock.addOrUpdate).toHaveBeenOnlyCalledWith({
key: 'freezeStatus',
value: 'not-frozen',
})
})

it("sets not-frozen when grace period hasn't passed", async () => {
const kvStoreMock = mockObject<KeyValueStore>({
findByKey: mockFn().resolvesTo(1),
addOrUpdate: mockFn().resolvesTo(undefined),
})
const ethereumClientMock = mockObject<EthereumClient>({
getBlockTimestamp: mockFn().resolvesTo(1200),
call: mockFn()
.given(
EthereumAddress.fake('fade'),
'isFrozen',
'function isFrozen() public view returns (bool)'
)
.resolvesToOnce([false, undefined])
.given(
EthereumAddress.fake('fade'),
'FREEZE_GRACE_PERIOD',
'function FREEZE_GRACE_PERIOD() view returns (uint256)'
)
.resolvesToOnce([ethers.BigNumber.from(300), undefined]),
})
const userTransactionRepositoryMock =
mockObject<UserTransactionRepository>({
findOldestNotIncluded: mockFn().resolvesTo({
timestamp: Timestamp.fromSeconds(1000),
}),
})

const service = new FreezeCheckService(
EthereumAddress.fake('fade'),
ethereumClientMock,
kvStoreMock,
userTransactionRepositoryMock,
Logger.SILENT
)

await service.updateFreezeStatus()

expect(kvStoreMock.addOrUpdate).toHaveBeenOnlyCalledWith({
key: 'freezeStatus',
value: 'not-frozen',
})
})

it('sets freezable when grace period has passed', async () => {
const kvStoreMock = mockObject<KeyValueStore>({
findByKey: mockFn().resolvesTo(1),
addOrUpdate: mockFn().resolvesTo(undefined),
})
const ethereumClientMock = mockObject<EthereumClient>({
getBlockTimestamp: mockFn().resolvesTo(1301),
call: mockFn()
.given(
EthereumAddress.fake('fade'),
'isFrozen',
'function isFrozen() public view returns (bool)'
)
.resolvesToOnce([false, undefined])
.given(
EthereumAddress.fake('fade'),
'FREEZE_GRACE_PERIOD',
'function FREEZE_GRACE_PERIOD() view returns (uint256)'
)
.resolvesToOnce([ethers.BigNumber.from(300), undefined]),
})
const userTransactionRepositoryMock =
mockObject<UserTransactionRepository>({
findOldestNotIncluded: mockFn().resolvesTo({
timestamp: Timestamp.fromSeconds(1000),
}),
})

const service = new FreezeCheckService(
EthereumAddress.fake('fade'),
ethereumClientMock,
kvStoreMock,
userTransactionRepositoryMock,
Logger.SILENT
)

await service.updateFreezeStatus()

expect(kvStoreMock.addOrUpdate).toHaveBeenOnlyCalledWith({
key: 'freezeStatus',
value: 'freezable',
})
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import range from 'lodash/range'
import { it } from 'mocha'

import { setupDatabaseTestSuite } from '../../test/database'
import { fakeInt, fakeTimestamp, fakeWithdrawal } from '../../test/fakes'
import {
decodedFakePerpetualState,
fakeInt,
fakeTimestamp,
fakeWithdrawal,
} from '../../test/fakes'
import { ForcedTransactionRepository } from './ForcedTransactionRepository'
import {
StateUpdateRecord,
Expand Down Expand Up @@ -64,6 +69,7 @@ describe(StateUpdateRepository.name, () => {
rootHash: PedersenHash.fake(),
stateTransitionHash: Hash256.fake(),
timestamp: Timestamp(0),
perpetualState: decodedFakePerpetualState,
}
await repository.add({
stateUpdate,
Expand All @@ -86,7 +92,7 @@ describe(StateUpdateRepository.name, () => {
rootHash: PedersenHash.fake(),
stateTransitionHash: Hash256.fake(),
timestamp: Timestamp(0),
perpetualState: undefined,
perpetualState: decodedFakePerpetualState,
}

const result = await repository.update(stateUpdateUpdateData)
Expand Down Expand Up @@ -148,7 +154,7 @@ describe(StateUpdateRepository.name, () => {
rootHash: PedersenHash.fake(),
stateTransitionHash: Hash256.fake(),
timestamp: Timestamp(0),
perpetualState: undefined,
perpetualState: decodedFakePerpetualState,
}

await repository.add({ stateUpdate, positions: [], prices: [] })
Expand Down Expand Up @@ -248,7 +254,7 @@ describe(StateUpdateRepository.name, () => {
rootHash: PedersenHash.fake(),
stateTransitionHash: Hash256.fake(),
timestamp: Timestamp(0),
perpetualState: undefined,
perpetualState: decodedFakePerpetualState,
}
await repository.add({ stateUpdate, positions: [], prices: [] })

Expand Down Expand Up @@ -307,7 +313,7 @@ describe(StateUpdateRepository.name, () => {
rootHash: PedersenHash.fake(),
stateTransitionHash: Hash256.fake(),
timestamp: Timestamp(0),
perpetualState: undefined,
perpetualState: decodedFakePerpetualState,
}
const stateUpdate2 = {
id: 30_002_000,
Expand Down Expand Up @@ -356,7 +362,7 @@ describe(StateUpdateRepository.name, () => {
rootHash: PedersenHash.fake(),
stateTransitionHash: Hash256.fake(),
timestamp: Timestamp(0),
perpetualState: undefined,
perpetualState: decodedFakePerpetualState,
}
const stateUpdate2 = {
id: 30_002_000,
Expand Down
Loading