mirror of
https://github.com/logos-storage/logos-storage-nim.git
synced 2026-02-07 15:13:08 +00:00
WIP getting integration tests to work with marketplacesuite
This commit is contained in:
parent
eda4a85c3e
commit
93f9dbba08
@ -5,7 +5,6 @@ import pkg/ethers/erc20
|
||||
import ./contracts/requests
|
||||
import ./clock
|
||||
import ./periods
|
||||
import ./utils/exceptions
|
||||
|
||||
export chronos
|
||||
export questionable
|
||||
|
||||
@ -2,7 +2,6 @@ import pkg/metrics
|
||||
import pkg/chronicles
|
||||
import ../statemachine
|
||||
import ./errorhandling
|
||||
import ./error
|
||||
|
||||
declareCounter(codex_purchases_cancelled, "codex purchases cancelled")
|
||||
|
||||
|
||||
@ -4,7 +4,6 @@ when codex_enable_proof_failures:
|
||||
import pkg/chronicles
|
||||
import pkg/stint
|
||||
import pkg/ethers
|
||||
import pkg/ethers/testing
|
||||
|
||||
import ../../contracts/requests
|
||||
import ../../market
|
||||
|
||||
@ -11,6 +11,8 @@ import pkg/codex/purchasing
|
||||
import pkg/codex/errors
|
||||
import pkg/codex/sales/reservations
|
||||
|
||||
export purchasing
|
||||
|
||||
type CodexClient* = ref object
|
||||
http: HttpClient
|
||||
baseurl: string
|
||||
@ -142,5 +144,8 @@ proc restart*(client: CodexClient) =
|
||||
client.http.close()
|
||||
client.http = newHttpClient()
|
||||
|
||||
proc purchaseStateIs(client: CodexClient, id: PurchaseId, state: string): bool =
|
||||
proc purchaseStateIs*(client: CodexClient, id: PurchaseId, state: string): bool =
|
||||
client.getPurchase(id).option.?state == some state
|
||||
|
||||
proc requestId*(client: CodexClient, id: PurchaseId): ?RequestId =
|
||||
return client.getPurchase(id).option.?requestId
|
||||
|
||||
@ -70,7 +70,7 @@ proc start(node: HardhatProcess) =
|
||||
for arg in node.arguments:
|
||||
if arg.contains "--log-file=":
|
||||
let logFilePath = arg.split("=")[1]
|
||||
node.captureOutput(logFilePath).track(node)
|
||||
discard node.captureOutput(logFilePath).track(node)
|
||||
break
|
||||
|
||||
proc waitUntilOutput*(node: HardhatProcess, output: string) =
|
||||
|
||||
99
tests/integration/marketplacesuite.nim
Normal file
99
tests/integration/marketplacesuite.nim
Normal file
@ -0,0 +1,99 @@
|
||||
import std/times
|
||||
import pkg/chronos
|
||||
import pkg/codex/contracts/marketplace as mp
|
||||
import pkg/codex/periods
|
||||
import ./multinodes
|
||||
|
||||
export mp
|
||||
export multinodes
|
||||
|
||||
template marketplacesuite*(name: string, startNodes: Nodes, body: untyped) =
|
||||
|
||||
multinodesuite name, startNodes:
|
||||
|
||||
var marketplace {.inject, used.}: Marketplace
|
||||
var period: uint64
|
||||
var token {.inject, used.}: Erc20Token
|
||||
|
||||
proc advanceToNextPeriod() {.async.} =
|
||||
let periodicity = Periodicity(seconds: period.u256)
|
||||
let currentPeriod = periodicity.periodOf(ethProvider.currentTime())
|
||||
let nextPeriod = periodicity.periodEnd(currentPeriod)
|
||||
echo "advancing to next period start at ", nextPeriod + 1
|
||||
await ethProvider.advanceTimeTo(nextPeriod + 1)
|
||||
|
||||
proc periods(p: int): uint64 =
|
||||
p.uint64 * period
|
||||
|
||||
template eventuallyP(condition: untyped, totalProofs: int, sleep: int): bool =
|
||||
proc e: Future[bool] {.async.} =
|
||||
for i in 0..<totalProofs.int:
|
||||
if condition:
|
||||
echo "condition is true, returning, ", i, " out of ", totalProofs.int
|
||||
return true
|
||||
else:
|
||||
echo $(getTime().toUnix) & " advancing to the next period... ", i, " out of ", totalProofs.int
|
||||
await advanceToNextPeriod()
|
||||
await sleepAsync(chronos.seconds(sleep))
|
||||
|
||||
return false
|
||||
|
||||
let r = await e()
|
||||
echo "returning result of eventuallyP: ", r
|
||||
r
|
||||
|
||||
proc createAvailabilities(datasetSize: int, duration: uint64) =
|
||||
# post availability to each provider
|
||||
for i in 0..<providers().len:
|
||||
let provider = providers()[i].node.client
|
||||
|
||||
discard provider.postAvailability(
|
||||
size=datasetSize.u256, # should match 1 slot only
|
||||
duration=duration.u256,
|
||||
minPrice=300.u256,
|
||||
maxCollateral=200.u256
|
||||
)
|
||||
|
||||
proc requestStorage(client: CodexClient,
|
||||
cid: Cid,
|
||||
# provider: JsonRpcProvider,
|
||||
proofProbability: uint64 = 1,
|
||||
duration: uint64 = 12.periods,
|
||||
expiry: uint64 = 4.periods,
|
||||
nodes = providers().len,
|
||||
tolerance = 0): Future[PurchaseId] {.async.} =
|
||||
|
||||
# let cid = client.upload(byteutils.toHex(data)).get
|
||||
let expiry = (await ethProvider.currentTime()) + expiry.u256
|
||||
|
||||
# avoid timing issues by filling the slot at the start of the next period
|
||||
await advanceToNextPeriod()
|
||||
|
||||
let id = client.requestStorage(
|
||||
cid,
|
||||
expiry=expiry,
|
||||
duration=duration.u256,
|
||||
proofProbability=proofProbability.u256,
|
||||
collateral=100.u256,
|
||||
reward=400.u256,
|
||||
nodes=nodes.uint,
|
||||
tolerance=tolerance.uint
|
||||
).get
|
||||
|
||||
return id
|
||||
|
||||
setup:
|
||||
marketplace = Marketplace.new(Marketplace.address, ethProvider.getSigner())
|
||||
let tokenAddress = await marketplace.token()
|
||||
token = Erc20Token.new(tokenAddress, ethProvider.getSigner())
|
||||
let config = await mp.config(marketplace)
|
||||
period = config.proofs.period.truncate(uint64)
|
||||
|
||||
|
||||
|
||||
# Our Hardhat configuration does use automine, which means that time tracked by `provider.currentTime()` is not
|
||||
# advanced until blocks are mined and that happens only when transaction is submitted.
|
||||
# As we use in tests provider.currentTime() which uses block timestamp this can lead to synchronization issues.
|
||||
await ethProvider.advanceTime(1.u256)
|
||||
|
||||
body
|
||||
@ -135,12 +135,11 @@ proc withLogFile*(
|
||||
startConfig.logFile = logToFile
|
||||
return startConfig
|
||||
|
||||
template multinodesuite*(name: string,
|
||||
startNodes: Nodes, body: untyped) =
|
||||
template multinodesuite*(name: string, startNodes: Nodes, body: untyped) =
|
||||
|
||||
asyncchecksuite name:
|
||||
|
||||
var provider {.inject, used.}: JsonRpcProvider
|
||||
var ethProvider {.inject, used.}: JsonRpcProvider
|
||||
var accounts {.inject, used.}: seq[Address]
|
||||
|
||||
var running: seq[RunningNode]
|
||||
@ -262,8 +261,8 @@ template multinodesuite*(name: string,
|
||||
running.add RunningNode(role: Role.Hardhat, node: node)
|
||||
|
||||
echo "Connecting to hardhat on ws://localhost:8545..."
|
||||
provider = JsonRpcProvider.new("ws://localhost:8545")
|
||||
accounts = await provider.listAccounts()
|
||||
ethProvider = JsonRpcProvider.new("ws://localhost:8545")
|
||||
accounts = await ethProvider.listAccounts()
|
||||
|
||||
for i in 0..<startNodes.clients.numNodes:
|
||||
let node = startClientNode()
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import std/sequtils
|
||||
import std/os
|
||||
from std/times import getTime, toUnix
|
||||
# import std/sequtils
|
||||
# import std/os
|
||||
# from std/times import getTime, toUnix
|
||||
import pkg/chronicles
|
||||
import pkg/stew/byteutils
|
||||
import pkg/codex/contracts
|
||||
import pkg/codex/periods
|
||||
# import pkg/codex/contracts
|
||||
# import pkg/codex/periods
|
||||
import ../contracts/time
|
||||
import ../contracts/deployment
|
||||
import ../codex/helpers
|
||||
import ../examples
|
||||
import ./twonodes
|
||||
import ./multinodes
|
||||
# import ./twonodes
|
||||
import ./marketplacesuite
|
||||
|
||||
export chronicles
|
||||
|
||||
@ -239,7 +239,74 @@ logScope:
|
||||
|
||||
# await subscription.unsubscribe()
|
||||
|
||||
multinodesuite "Simulate invalid proofs - 1 provider node",
|
||||
|
||||
|
||||
|
||||
marketplacesuite "Simulate invalid proofs - 1 provider node",
|
||||
Nodes(
|
||||
# hardhat: HardhatConfig()
|
||||
# .withLogFile(),
|
||||
|
||||
clients: NodeConfig()
|
||||
.nodes(1)
|
||||
.debug()
|
||||
.withLogFile()
|
||||
.withLogTopics("node"),
|
||||
|
||||
providers: NodeConfig()
|
||||
.nodes(1)
|
||||
.simulateProofFailuresFor(providerIdx=0, failEveryNProofs=1)
|
||||
.debug()
|
||||
.withLogFile()
|
||||
.withLogTopics(
|
||||
"marketplace",
|
||||
"sales",
|
||||
"reservations",
|
||||
"node",
|
||||
"JSONRPC-HTTP-CLIENT",
|
||||
"JSONRPC-WS-CLIENT",
|
||||
"ethers",
|
||||
"restapi",
|
||||
"clock"
|
||||
),
|
||||
|
||||
validators: NodeConfig()
|
||||
.nodes(1)
|
||||
.withLogFile()
|
||||
.withLogTopics("validator", "initial-proving", "proving", "clock")
|
||||
):
|
||||
test "slot is freed after too many invalid proofs submitted":
|
||||
let client0 = clients()[0].node.client
|
||||
let totalProofs = 50
|
||||
|
||||
let data = byteutils.toHex(await exampleData())
|
||||
createAvailabilities(data.len, totalProofs.periods)
|
||||
|
||||
let cid = client0.upload(data).get
|
||||
|
||||
let purchaseId = await client0.requestStorage(cid, duration=totalProofs.periods)
|
||||
let requestId = client0.requestId(purchaseId).get
|
||||
|
||||
check eventually client0.purchaseStateIs(purchaseId, "started")
|
||||
|
||||
var slotWasFreed = false
|
||||
proc onSlotFreed(event: SlotFreed) {.gcsafe, upraises:[].} =
|
||||
if event.requestId == requestId and
|
||||
event.slotIndex == 0.u256: # assume only one slot, so index 0
|
||||
slotWasFreed = true
|
||||
|
||||
let subscription = await marketplace.subscribe(SlotFreed, onSlotFreed)
|
||||
|
||||
echo "waiting for proofs ", totalProofs
|
||||
# check eventuallyP(slotWasFreed, totalProofs, 1.periods.int)
|
||||
check eventually(slotWasFreed, totalProofs.periods.int * 1000)
|
||||
echo "got to the end... freed: ", slotWasFreed
|
||||
|
||||
echo "unsubscribing from slotFreed"
|
||||
await subscription.unsubscribe()
|
||||
echo "unsubscribed from slotFreed"
|
||||
|
||||
marketplacesuite "Simulate invalid proofs - 1 provider node",
|
||||
Nodes(
|
||||
hardhat: HardhatConfig()
|
||||
.withLogFile(),
|
||||
@ -252,7 +319,7 @@ multinodesuite "Simulate invalid proofs - 1 provider node",
|
||||
|
||||
providers: NodeConfig()
|
||||
.nodes(1)
|
||||
.simulateProofFailuresFor(providerIdx=0, failEveryNProofs=2)
|
||||
.simulateProofFailuresFor(providerIdx=0, failEveryNProofs=3)
|
||||
.debug()
|
||||
.withLogFile()
|
||||
.withLogTopics(
|
||||
@ -263,153 +330,129 @@ multinodesuite "Simulate invalid proofs - 1 provider node",
|
||||
"JSONRPC-HTTP-CLIENT",
|
||||
"JSONRPC-WS-CLIENT",
|
||||
"ethers",
|
||||
"restapi"
|
||||
)
|
||||
):
|
||||
test "1=1":
|
||||
check 1 == 1
|
||||
|
||||
multinodesuite "Simulate invalid proofs",
|
||||
Nodes(
|
||||
hardhat: HardhatConfig()
|
||||
.withLogFile(),
|
||||
|
||||
clients: NodeConfig()
|
||||
.nodes(1)
|
||||
.debug()
|
||||
.withLogFile()
|
||||
.withLogTopics("node"),
|
||||
|
||||
providers: NodeConfig()
|
||||
.nodes(5)
|
||||
.simulateProofFailuresFor(providerIdx=0, failEveryNProofs=2)
|
||||
.debug()
|
||||
.withLogFile()
|
||||
.withLogTopics(
|
||||
"marketplace",
|
||||
"sales",
|
||||
"reservations",
|
||||
"node",
|
||||
"JSONRPC-HTTP-CLIENT",
|
||||
"JSONRPC-WS-CLIENT",
|
||||
"ethers",
|
||||
"restapi"
|
||||
"restapi",
|
||||
"clock"
|
||||
),
|
||||
|
||||
validators: NodeConfig()
|
||||
.nodes(1)
|
||||
.withLogFile()
|
||||
.withLogTopics("validator", "initial-proving", "proving")
|
||||
.withLogTopics("validator", "initial-proving", "proving", "clock")
|
||||
):
|
||||
test "slot is not freed when not enough invalid proofs submitted":
|
||||
let client0 = clients()[0].node.client
|
||||
let totalProofs = 25
|
||||
|
||||
var marketplace: Marketplace
|
||||
var period: uint64
|
||||
var token: Erc20Token
|
||||
let data = byteutils.toHex(await exampleData())
|
||||
createAvailabilities(data.len, totalProofs.periods)
|
||||
|
||||
setup:
|
||||
marketplace = Marketplace.new(Marketplace.address, provider.getSigner())
|
||||
let tokenAddress = await marketplace.token()
|
||||
token = Erc20Token.new(tokenAddress, provider.getSigner())
|
||||
let config = await marketplace.config()
|
||||
period = config.proofs.period.truncate(uint64)
|
||||
let cid = client0.upload(data).get
|
||||
|
||||
# Our Hardhat configuration does use automine, which means that time tracked by `provider.currentTime()` is not
|
||||
# advanced until blocks are mined and that happens only when transaction is submitted.
|
||||
# As we use in tests provider.currentTime() which uses block timestamp this can lead to synchronization issues.
|
||||
await provider.advanceTime(1.u256)
|
||||
let purchaseId = await client0.requestStorage(cid, duration=totalProofs.periods)
|
||||
let requestId = client0.requestId(purchaseId).get
|
||||
|
||||
proc periods(p: int): uint64 =
|
||||
p.uint64 * period
|
||||
check eventually client0.purchaseStateIs(purchaseId, "started")
|
||||
|
||||
proc advanceToNextPeriod {.async.} =
|
||||
let periodicity = Periodicity(seconds: period.u256)
|
||||
let currentPeriod = periodicity.periodOf(await provider.currentTime())
|
||||
let endOfPeriod = periodicity.periodEnd(currentPeriod)
|
||||
await provider.advanceTimeTo(endOfPeriod + 1)
|
||||
var slotWasFreed = false
|
||||
proc onSlotFreed(event: SlotFreed) {.gcsafe, upraises:[].} =
|
||||
if event.requestId == requestId and
|
||||
event.slotIndex == 0.u256: # assume only one slot, so index 0
|
||||
slotWasFreed = true
|
||||
|
||||
proc createAvailabilities(datasetSize: int, duration: uint64) =
|
||||
# post availability to each provider
|
||||
for i in 0..<providers().len:
|
||||
let provider = providers()[i].node.client
|
||||
let subscription = await marketplace.subscribe(SlotFreed, onSlotFreed)
|
||||
|
||||
discard provider.postAvailability(
|
||||
size=datasetSize.u256, # should match 1 slot only
|
||||
duration=duration.u256,
|
||||
minPrice=300.u256,
|
||||
maxCollateral=200.u256
|
||||
)
|
||||
echo "waiting for proofs: ", totalProofs
|
||||
let freed = eventuallyP(slotWasFreed, totalProofs, 1.periods.int)
|
||||
echo "got to the end... freed: ", freed
|
||||
check not freed
|
||||
# check not eventually(slotWasFreed, totalProofs.periods.int * 1000)
|
||||
|
||||
proc requestStorage(data: seq[byte],
|
||||
proofProbability: uint64 = 1,
|
||||
duration: uint64 = 12.periods,
|
||||
expiry: uint64 = 4.periods): Future[PurchaseId] {.async.} =
|
||||
echo "unsubscribing from slotFreed"
|
||||
await subscription.unsubscribe()
|
||||
echo "unsubscribed from slotFreed"
|
||||
|
||||
if clients().len < 1 or providers().len < 2:
|
||||
raiseAssert("must start at least one client and two providers")
|
||||
# marketplacesuite "Simulate invalid proofs",
|
||||
# Nodes(
|
||||
# hardhat: HardhatConfig()
|
||||
# .withLogFile(),
|
||||
|
||||
let client0 = clients()[0].node.client
|
||||
let cid = client0.upload(byteutils.toHex(data)).get
|
||||
let expiry = (await provider.currentTime()) + expiry.u256
|
||||
# clients: NodeConfig()
|
||||
# .nodes(1)
|
||||
# .debug()
|
||||
# .withLogFile()
|
||||
# .withLogTopics("node", "erasure"),
|
||||
|
||||
# avoid timing issues by filling the slot at the start of the next period
|
||||
await advanceToNextPeriod()
|
||||
# providers: NodeConfig()
|
||||
# .nodes(2)
|
||||
# .simulateProofFailuresFor(providerIdx=0, failEveryNProofs=2)
|
||||
# .debug()
|
||||
# .withLogFile()
|
||||
# .withLogTopics(
|
||||
# "marketplace",
|
||||
# "sales",
|
||||
# "reservations",
|
||||
# "node",
|
||||
# "JSONRPC-HTTP-CLIENT",
|
||||
# "JSONRPC-WS-CLIENT",
|
||||
# "ethers",
|
||||
# "restapi"
|
||||
# ),
|
||||
|
||||
let id = client0.requestStorage(
|
||||
cid,
|
||||
expiry=expiry,
|
||||
duration=duration.u256,
|
||||
proofProbability=proofProbability.u256,
|
||||
collateral=100.u256,
|
||||
reward=400.u256,
|
||||
nodes=2'u
|
||||
).get
|
||||
# validators: NodeConfig()
|
||||
# .nodes(1)
|
||||
# .withLogFile()
|
||||
# .withLogTopics("validator", "initial-proving", "proving")
|
||||
# ):
|
||||
|
||||
return id
|
||||
# # TODO: these are very loose tests in that they are not testing EXACTLY how
|
||||
# # proofs were marked as missed by the validator. These tests should be
|
||||
# # tightened so that they are showing, as an integration test, that specific
|
||||
# # proofs are being marked as missed by the validator.
|
||||
|
||||
# TODO: these are very loose tests in that they are not testing EXACTLY how
|
||||
# proofs were marked as missed by the validator. These tests should be
|
||||
# tightened so that they are showing, as an integration test, that specific
|
||||
# proofs are being marked as missed by the validator.
|
||||
# test "provider that submits invalid proofs is paid out less":
|
||||
# let client0 = clients()[0].node.client
|
||||
# let provider0 = providers()[0]
|
||||
# let provider1 = providers()[1]
|
||||
# let totalProofs = 25
|
||||
|
||||
test "provider that submits invalid proofs is paid out less":
|
||||
let clientRestClient = clients()[0].node.client
|
||||
let provider0 = providers()[0]
|
||||
let provider1 = providers()[1]
|
||||
let totalProofs = 25
|
||||
# let data = byteutils.toHex(await exampleData())
|
||||
# # createAvailabilities(data.len, totalProofs.periods)
|
||||
|
||||
let data = await exampleData()
|
||||
createAvailabilities(byteutils.toHex(data).len, totalProofs.periods)
|
||||
let purchaseId = await requestStorage(data, duration=totalProofs.periods)
|
||||
check eventually clientRestClient.purchaseStateIs(purchaseId, "started")
|
||||
# discard provider0.node.client.postAvailability(
|
||||
# size=data.len.u256, # should match 1 slot only
|
||||
# duration=totalProofs.periods.u256,
|
||||
# minPrice=300.u256,
|
||||
# maxCollateral=200.u256
|
||||
# )
|
||||
|
||||
for _ in 0..<totalProofs.periods.int:
|
||||
if clientRestClient.purchaseStateIs(purchaseId, "finished"):
|
||||
break
|
||||
else:
|
||||
await advanceToNextPeriod()
|
||||
await sleepAsync(1.seconds)
|
||||
# let cid = client0.upload(data).get
|
||||
|
||||
check eventually (
|
||||
(await token.balanceOf(!provider1.address)) >
|
||||
(await token.balanceOf(!provider0.address))
|
||||
)
|
||||
echo "provider1 balance: ", (await token.balanceOf(!provider1.address))
|
||||
echo "provider0 balance: ", (await token.balanceOf(!provider0.address))
|
||||
# let purchaseId = await client0.requestStorage(
|
||||
# cid,
|
||||
# duration=totalProofs.periods,
|
||||
# # tolerance=1
|
||||
# )
|
||||
|
||||
# await sleepAsync(1.seconds) # allow time for provider0 to fill a slot
|
||||
|
||||
# var slotWasFreed = false
|
||||
# proc onSlotFreed(event: SlotFreed) =
|
||||
# if slotId(event.requestId, event.slotIndex) == slotId:
|
||||
# slotWasFreed = true
|
||||
# let subscription = await marketplace.subscribe(SlotFreed, onSlotFreed)
|
||||
# # now add availability for provider1, which should allow provider1 to put
|
||||
# # the remaining slot in its queue
|
||||
# discard provider1.node.client.postAvailability(
|
||||
# size=data.len.u256, # should match 1 slot only
|
||||
# duration=totalProofs.periods.u256,
|
||||
# minPrice=300.u256,
|
||||
# maxCollateral=200.u256
|
||||
# )
|
||||
|
||||
# for _ in 0..<totalProofs:
|
||||
# if slotWasFreed:
|
||||
# break
|
||||
# else:
|
||||
# await advanceToNextPeriod()
|
||||
# await sleepAsync(1.seconds)
|
||||
# check eventually client0.purchaseStateIs(purchaseId, "started")
|
||||
|
||||
# check slotWasFreed
|
||||
# check eventuallyP(
|
||||
# client0.purchaseStateIs(purchaseId, "finished"),
|
||||
# totalProofs)
|
||||
|
||||
# await subscription.unsubscribe()
|
||||
# check eventually (
|
||||
# (await token.balanceOf(!provider1.address)) >
|
||||
# (await token.balanceOf(!provider0.address))
|
||||
# )
|
||||
# echo "provider1 balance: ", (await token.balanceOf(!provider1.address))
|
||||
# echo "provider0 balance: ", (await token.balanceOf(!provider0.address))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user