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

View File

@ -10,7 +10,7 @@ type
clock*: Clock
method start*(self: ContractInteractions) {.async, base.} =
await self.clock.start()
discard
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 ./chunker
import ./clock
import ./blocktype as bt
import ./manifest
import ./merkletree
@ -62,6 +63,7 @@ type
erasure*: Erasure
discovery*: Discovery
contracts*: Contracts
clock*: Clock
OnManifest* = proc(cid: Cid, manifest: Manifest): void {.gcsafe, closure.}
@ -320,7 +322,7 @@ proc requestStorage*(
tolerance: uint,
reward: UInt256,
collateral: UInt256,
expiry = UInt256.none): Future[?!PurchaseId] {.async.} =
expiry: UInt256): Future[?!PurchaseId] {.async.} =
## Initiate a request for storage sequence, this might
## be a multistep procedure.
##
@ -384,7 +386,7 @@ proc requestStorage*(
name: @[] # TODO: PoR setup
)
),
expiry: expiry |? 0.u256
expiry: expiry
)
let purchase = await contracts.purchasing.purchase(request)
@ -418,6 +420,9 @@ proc start*(node: CodexNodeRef) {.async.} =
if not node.discovery.isNil:
await node.discovery.start()
if not node.clock.isNil:
await node.clock.start()
if hostContracts =? node.contracts.host:
# TODO: remove Sales callbacks, pass BlockStore and StorageProofs instead
hostContracts.sales.onStore = proc(request: StorageRequest,
@ -499,6 +504,9 @@ proc stop*(node: CodexNodeRef) {.async.} =
if not node.discovery.isNil:
await node.discovery.stop()
if not node.clock.isNil:
await node.clock.stop()
if clientContracts =? node.contracts.client:
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
try:
without contracts =? node.contracts.client:
return RestApiResponse.error(Http503, "Purchasing unavailable")
without cid =? cid.tryGet.catch, error:
return RestApiResponse.error(Http400, error.msg)
@ -281,6 +284,15 @@ proc initPurchasingApi(node: CodexNodeRef, router: var RestRouter) =
let nodes = params.nodes |? 1
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(
cid,
params.duration,
@ -289,7 +301,7 @@ proc initPurchasingApi(node: CodexNodeRef, router: var RestRouter) =
tolerance,
params.reward,
params.collateral,
params.expiry), error:
expiry), error:
return RestApiResponse.error(Http500, error.msg)

View File

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

View File

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

View File

@ -1,5 +1,6 @@
import std/options
import std/sequtils
import std/httpclient
from pkg/libp2p import `==`
import pkg/chronos
import pkg/stint
@ -216,6 +217,19 @@ twonodessuite "Integration tests", debug1 = false, debug2 = false:
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":
let marketplace = Marketplace.new(Marketplace.address, provider.getSigner())
let tokenAddress = await marketplace.token()