fix: require expiry from api (#629)
Co-authored-by: markspanbroek <mark@spanbroek.net>
This commit is contained in:
parent
8681a40ee7
commit
79fce39dbf
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -135,6 +135,7 @@ components:
|
||||||
- duration
|
- duration
|
||||||
- proofProbability
|
- proofProbability
|
||||||
- collateral
|
- collateral
|
||||||
|
- expiry
|
||||||
properties:
|
properties:
|
||||||
duration:
|
duration:
|
||||||
$ref: "#/components/schemas/Duration"
|
$ref: "#/components/schemas/Duration"
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue