fix: require expiry from api (#629)

Co-authored-by: markspanbroek <mark@spanbroek.net>
This commit is contained in:
Adam Uhlíř 2023-11-22 12:35:26 +01:00 committed by GitHub
parent 8681a40ee7
commit 79fce39dbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 84 additions and 21 deletions

View File

@ -34,6 +34,7 @@ import ./utils/fileutils
import ./erasure import ./erasure
import ./discovery import ./discovery
import ./contracts import ./contracts
import ./systemclock
import ./contracts/clock import ./contracts/clock
import ./contracts/deployment import ./contracts/deployment
import ./utils/addrutils import ./utils/addrutils
@ -55,12 +56,15 @@ type
EthWallet = ethers.Wallet EthWallet = ethers.Wallet
proc bootstrapInteractions( proc bootstrapInteractions(
config: CodexConf, s: CodexServer
repo: RepoStore ): Future[void] {.async.} =
): Future[Contracts] {.async.} =
## bootstrap interactions and return contracts ## bootstrap interactions and return contracts
## using clients, hosts, validators pairings ## using clients, hosts, validators pairings
## ##
let
config = s.config
repo = s.repoStore
if not config.persistence and not config.validator: if not config.persistence and not config.validator:
if config.ethAccount.isSome or config.ethPrivateKey.isSome: if config.ethAccount.isSome or config.ethPrivateKey.isSome:
@ -106,6 +110,11 @@ proc bootstrapInteractions(
var host: ?HostInteractions var host: ?HostInteractions
var validator: ?ValidatorInteractions var validator: ?ValidatorInteractions
if config.validator or config.persistence:
s.codexNode.clock = clock
else:
s.codexNode.clock = SystemClock()
if config.persistence: if config.persistence:
# This is used for simulation purposes. Normal nodes won't be compiled with this flag # This is used for simulation purposes. Normal nodes won't be compiled with this flag
# and hence the proof failure will always be 0. # and hence the proof failure will always be 0.
@ -126,7 +135,7 @@ proc bootstrapInteractions(
let validation = Validation.new(clock, market, config.validatorMaxSlots) let validation = Validation.new(clock, market, config.validatorMaxSlots)
validator = some ValidatorInteractions.new(clock, validation) validator = some ValidatorInteractions.new(clock, validation)
return (client, host, validator) s.codexNode.contracts = (client, host, validator)
proc start*(s: CodexServer) {.async.} = proc start*(s: CodexServer) {.async.} =
trace "Starting codex node", config = $s.config trace "Starting codex node", config = $s.config
@ -161,7 +170,7 @@ proc start*(s: CodexServer) {.async.} =
s.codexNode.discovery.updateAnnounceRecord(announceAddrs) s.codexNode.discovery.updateAnnounceRecord(announceAddrs)
s.codexNode.discovery.updateDhtRecord(s.config.nat, s.config.discoveryPort) s.codexNode.discovery.updateDhtRecord(s.config.nat, s.config.discoveryPort)
s.codexNode.contracts = await bootstrapInteractions(s.config, s.repoStore) await s.bootstrapInteractions()
await s.codexNode.start() await s.codexNode.start()
s.restServer.start() s.restServer.start()

View File

@ -10,7 +10,7 @@ type
clock*: Clock clock*: Clock
method start*(self: ContractInteractions) {.async, base.} = method start*(self: ContractInteractions) {.async, base.} =
await self.clock.start() discard
method stop*(self: ContractInteractions) {.async, base.} = method stop*(self: ContractInteractions) {.async, base.} =
await self.clock.stop() discard

View File

@ -26,6 +26,7 @@ import pkg/libp2p/routing_record
import pkg/libp2p/signed_envelope import pkg/libp2p/signed_envelope
import ./chunker import ./chunker
import ./clock
import ./blocktype as bt import ./blocktype as bt
import ./manifest import ./manifest
import ./merkletree import ./merkletree
@ -62,6 +63,7 @@ type
erasure*: Erasure erasure*: Erasure
discovery*: Discovery discovery*: Discovery
contracts*: Contracts contracts*: Contracts
clock*: Clock
OnManifest* = proc(cid: Cid, manifest: Manifest): void {.gcsafe, closure.} OnManifest* = proc(cid: Cid, manifest: Manifest): void {.gcsafe, closure.}
@ -320,7 +322,7 @@ proc requestStorage*(
tolerance: uint, tolerance: uint,
reward: UInt256, reward: UInt256,
collateral: UInt256, collateral: UInt256,
expiry = UInt256.none): Future[?!PurchaseId] {.async.} = expiry: UInt256): Future[?!PurchaseId] {.async.} =
## Initiate a request for storage sequence, this might ## Initiate a request for storage sequence, this might
## be a multistep procedure. ## be a multistep procedure.
## ##
@ -384,7 +386,7 @@ proc requestStorage*(
name: @[] # TODO: PoR setup name: @[] # TODO: PoR setup
) )
), ),
expiry: expiry |? 0.u256 expiry: expiry
) )
let purchase = await contracts.purchasing.purchase(request) let purchase = await contracts.purchasing.purchase(request)
@ -418,6 +420,9 @@ proc start*(node: CodexNodeRef) {.async.} =
if not node.discovery.isNil: if not node.discovery.isNil:
await node.discovery.start() await node.discovery.start()
if not node.clock.isNil:
await node.clock.start()
if hostContracts =? node.contracts.host: if hostContracts =? node.contracts.host:
# TODO: remove Sales callbacks, pass BlockStore and StorageProofs instead # TODO: remove Sales callbacks, pass BlockStore and StorageProofs instead
hostContracts.sales.onStore = proc(request: StorageRequest, hostContracts.sales.onStore = proc(request: StorageRequest,
@ -499,6 +504,9 @@ proc stop*(node: CodexNodeRef) {.async.} =
if not node.discovery.isNil: if not node.discovery.isNil:
await node.discovery.stop() await node.discovery.stop()
if not node.clock.isNil:
await node.clock.stop()
if clientContracts =? node.contracts.client: if clientContracts =? node.contracts.client:
await clientContracts.stop() await clientContracts.stop()

View File

@ -270,6 +270,9 @@ proc initPurchasingApi(node: CodexNodeRef, router: var RestRouter) =
## colateral - requested collateral from hosts when they fill slot ## colateral - requested collateral from hosts when they fill slot
try: try:
without contracts =? node.contracts.client:
return RestApiResponse.error(Http503, "Purchasing unavailable")
without cid =? cid.tryGet.catch, error: without cid =? cid.tryGet.catch, error:
return RestApiResponse.error(Http400, error.msg) return RestApiResponse.error(Http400, error.msg)
@ -281,6 +284,15 @@ proc initPurchasingApi(node: CodexNodeRef, router: var RestRouter) =
let nodes = params.nodes |? 1 let nodes = params.nodes |? 1
let tolerance = params.tolerance |? 0 let tolerance = params.tolerance |? 0
without expiry =? params.expiry:
return RestApiResponse.error(Http400, "Expiry required")
if node.clock.isNil:
return RestApiResponse.error(Http500)
if expiry <= node.clock.now.u256:
return RestApiResponse.error(Http400, "Expiry needs to be in future")
without purchaseId =? await node.requestStorage( without purchaseId =? await node.requestStorage(
cid, cid,
params.duration, params.duration,
@ -289,7 +301,7 @@ proc initPurchasingApi(node: CodexNodeRef, router: var RestRouter) =
tolerance, tolerance,
params.reward, params.reward,
params.collateral, params.collateral,
params.expiry), error: expiry), error:
return RestApiResponse.error(Http500, error.msg) return RestApiResponse.error(Http500, error.msg)

View File

@ -135,6 +135,7 @@ components:
- duration - duration
- proofProbability - proofProbability
- collateral - collateral
- expiry
properties: properties:
duration: duration:
$ref: "#/components/schemas/Duration" $ref: "#/components/schemas/Duration"

View File

@ -54,6 +54,35 @@ proc list*(client: CodexClient): ?!seq[RestContent] =
let json = ? parseJson(response.body).catch let json = ? parseJson(response.body).catch
seq[RestContent].fromJson(json) seq[RestContent].fromJson(json)
proc requestStorageRaw*(
client: CodexClient,
cid: Cid,
duration: UInt256,
reward: UInt256,
proofProbability: UInt256,
collateral: UInt256,
expiry: UInt256 = 0.u256,
nodes: uint = 1,
tolerance: uint = 0
): Response =
## Call request storage REST endpoint
##
let url = client.baseurl & "/storage/request/" & $cid
let json = %*{
"duration": duration,
"reward": reward,
"proofProbability": proofProbability,
"collateral": collateral,
"nodes": nodes,
"tolerance": tolerance
}
if expiry != 0:
json["expiry"] = %expiry
return client.http.post(url, $json)
proc requestStorage*( proc requestStorage*(
client: CodexClient, client: CodexClient,
cid: Cid, cid: Cid,
@ -67,17 +96,7 @@ proc requestStorage*(
): ?!PurchaseId = ): ?!PurchaseId =
## Call request storage REST endpoint ## Call request storage REST endpoint
## ##
let url = client.baseurl & "/storage/request/" & $cid let response = client.requestStorageRaw(cid, duration, reward, proofProbability, collateral, expiry, nodes, tolerance)
let json = %*{
"duration": duration,
"reward": reward,
"proofProbability": proofProbability,
"expiry": expiry,
"collateral": collateral,
"nodes": nodes,
"tolerance": tolerance
}
let response = client.http.post(url, $json)
assert response.status == "200 OK" assert response.status == "200 OK"
PurchaseId.fromHex(response.body).catch PurchaseId.fromHex(response.body).catch

View File

@ -1,5 +1,6 @@
import std/options import std/options
import std/sequtils import std/sequtils
import std/httpclient
from pkg/libp2p import `==` from pkg/libp2p import `==`
import pkg/chronos import pkg/chronos
import pkg/stint import pkg/stint
@ -216,6 +217,19 @@ twonodessuite "Integration tests", debug1 = false, debug2 = false:
check eventually (await token.balanceOf(account2)) - startBalance == duration*reward check eventually (await token.balanceOf(account2)) - startBalance == duration*reward
test "node requires expiry and its value to be in future":
let currentTime = await provider.currentTime()
let cid = client1.upload("some file contents").get
let responseMissing = client1.requestStorageRaw(cid, duration=1.u256, reward=2.u256, proofProbability=3.u256, collateral=200.u256)
check responseMissing.status == "400 Bad Request"
check responseMissing.body == "Expiry required"
let responsePast = client1.requestStorageRaw(cid, duration=1.u256, reward=2.u256, proofProbability=3.u256, collateral=200.u256, expiry=currentTime-10)
check responsePast.status == "400 Bad Request"
check responsePast.body == "Expiry needs to be in future"
test "expired request partially pays out for stored time": test "expired request partially pays out for stored time":
let marketplace = Marketplace.new(Marketplace.address, provider.getSigner()) let marketplace = Marketplace.new(Marketplace.address, provider.getSigner())
let tokenAddress = await marketplace.token() let tokenAddress = await marketplace.token()