mirror of
https://github.com/logos-storage/logos-storage-network-crawler.git
synced 2026-01-07 16:03:08 +00:00
Imports nim-ethers and required glue-code from nim-codex.
This commit is contained in:
parent
5bd35720b5
commit
56df7adfd8
@ -25,6 +25,7 @@ requires "questionable >= 0.10.15 & < 0.11.0"
|
||||
requires "https://github.com/codex-storage/nim-codex-dht#f6eef1ac95c70053b2518f1e3909c909ed8701a6"
|
||||
requires "docopt >= 0.7.1 & < 1.0.0"
|
||||
requires "nph >= 0.6.1 & < 1.0.0"
|
||||
requires "ethers >= 1.0.0 & < 2.0.0"
|
||||
|
||||
task format, "Formatting...":
|
||||
exec "nph ./"
|
||||
|
||||
6
codexcrawler/services/marketplace.nim
Normal file
6
codexcrawler/services/marketplace.nim
Normal file
@ -0,0 +1,6 @@
|
||||
import ./marketplace/marketplace
|
||||
|
||||
# todo
|
||||
|
||||
proc aaa*() =
|
||||
echo "aaa"
|
||||
103
codexcrawler/services/marketplace/config.nim
Normal file
103
codexcrawler/services/marketplace/config.nim
Normal file
@ -0,0 +1,103 @@
|
||||
import pkg/contractabi
|
||||
import pkg/ethers/fields
|
||||
import pkg/questionable/results
|
||||
|
||||
export contractabi
|
||||
|
||||
const DefaultRequestCacheSize* = 128.uint16
|
||||
|
||||
type
|
||||
MarketplaceConfig* = object
|
||||
collateral*: CollateralConfig
|
||||
proofs*: ProofConfig
|
||||
reservations*: SlotReservationsConfig
|
||||
requestDurationLimit*: uint64
|
||||
|
||||
CollateralConfig* = object
|
||||
repairRewardPercentage*: uint8
|
||||
# percentage of remaining collateral slot has after it has been freed
|
||||
maxNumberOfSlashes*: uint8 # frees slot when the number of slashes reaches this value
|
||||
slashPercentage*: uint8 # percentage of the collateral that is slashed
|
||||
validatorRewardPercentage*: uint8
|
||||
# percentage of the slashed amount going to the validators
|
||||
|
||||
ProofConfig* = object
|
||||
period*: uint64 # proofs requirements are calculated per period (in seconds)
|
||||
timeout*: uint64 # mark proofs as missing before the timeout (in seconds)
|
||||
downtime*: uint8 # ignore this much recent blocks for proof requirements
|
||||
downtimeProduct*: uint8
|
||||
zkeyHash*: string # hash of the zkey file which is linked to the verifier
|
||||
# Ensures the pointer does not remain in downtime for many consecutive
|
||||
# periods. For each period increase, move the pointer `pointerProduct`
|
||||
# blocks. Should be a prime number to ensure there are no cycles.
|
||||
|
||||
SlotReservationsConfig* = object
|
||||
maxReservations*: uint8
|
||||
|
||||
func fromTuple(_: type ProofConfig, tupl: tuple): ProofConfig =
|
||||
ProofConfig(
|
||||
period: tupl[0],
|
||||
timeout: tupl[1],
|
||||
downtime: tupl[2],
|
||||
downtimeProduct: tupl[3],
|
||||
zkeyHash: tupl[4],
|
||||
)
|
||||
|
||||
func fromTuple(_: type SlotReservationsConfig, tupl: tuple): SlotReservationsConfig =
|
||||
SlotReservationsConfig(maxReservations: tupl[0])
|
||||
|
||||
func fromTuple(_: type CollateralConfig, tupl: tuple): CollateralConfig =
|
||||
CollateralConfig(
|
||||
repairRewardPercentage: tupl[0],
|
||||
maxNumberOfSlashes: tupl[1],
|
||||
slashPercentage: tupl[2],
|
||||
validatorRewardPercentage: tupl[3],
|
||||
)
|
||||
|
||||
func fromTuple(_: type MarketplaceConfig, tupl: tuple): MarketplaceConfig =
|
||||
MarketplaceConfig(
|
||||
collateral: tupl[0],
|
||||
proofs: tupl[1],
|
||||
reservations: tupl[2],
|
||||
requestDurationLimit: tupl[3],
|
||||
)
|
||||
|
||||
func solidityType*(_: type SlotReservationsConfig): string =
|
||||
solidityType(SlotReservationsConfig.fieldTypes)
|
||||
|
||||
func solidityType*(_: type ProofConfig): string =
|
||||
solidityType(ProofConfig.fieldTypes)
|
||||
|
||||
func solidityType*(_: type CollateralConfig): string =
|
||||
solidityType(CollateralConfig.fieldTypes)
|
||||
|
||||
func solidityType*(_: type MarketplaceConfig): string =
|
||||
solidityType(MarketplaceConfig.fieldTypes)
|
||||
|
||||
func encode*(encoder: var AbiEncoder, slot: SlotReservationsConfig) =
|
||||
encoder.write(slot.fieldValues)
|
||||
|
||||
func encode*(encoder: var AbiEncoder, slot: ProofConfig) =
|
||||
encoder.write(slot.fieldValues)
|
||||
|
||||
func encode*(encoder: var AbiEncoder, slot: CollateralConfig) =
|
||||
encoder.write(slot.fieldValues)
|
||||
|
||||
func encode*(encoder: var AbiEncoder, slot: MarketplaceConfig) =
|
||||
encoder.write(slot.fieldValues)
|
||||
|
||||
func decode*(decoder: var AbiDecoder, T: type ProofConfig): ?!T =
|
||||
let tupl = ?decoder.read(ProofConfig.fieldTypes)
|
||||
success ProofConfig.fromTuple(tupl)
|
||||
|
||||
func decode*(decoder: var AbiDecoder, T: type SlotReservationsConfig): ?!T =
|
||||
let tupl = ?decoder.read(SlotReservationsConfig.fieldTypes)
|
||||
success SlotReservationsConfig.fromTuple(tupl)
|
||||
|
||||
func decode*(decoder: var AbiDecoder, T: type CollateralConfig): ?!T =
|
||||
let tupl = ?decoder.read(CollateralConfig.fieldTypes)
|
||||
success CollateralConfig.fromTuple(tupl)
|
||||
|
||||
func decode*(decoder: var AbiDecoder, T: type MarketplaceConfig): ?!T =
|
||||
let tupl = ?decoder.read(MarketplaceConfig.fieldTypes)
|
||||
success MarketplaceConfig.fromTuple(tupl)
|
||||
285
codexcrawler/services/marketplace/logutils.nim
Normal file
285
codexcrawler/services/marketplace/logutils.nim
Normal file
@ -0,0 +1,285 @@
|
||||
## logutils is a module that has several goals:
|
||||
## 1. Fix json logging output (run with `--log-format=json`) which was
|
||||
## effectively broken for many types using default Chronicles json
|
||||
## serialization.
|
||||
## 2. Ability to specify log output for textlines and json sinks together or
|
||||
## separately
|
||||
## - This is useful if consuming json in some kind of log parser and need
|
||||
## valid json with real values
|
||||
## - eg a shortened Cid is nice to see in a text log in stdout, but won't
|
||||
## provide a real Cid when parsed in json
|
||||
## 4. Remove usages of `nim-json-serialization` from the codebase
|
||||
## 5. Remove need to declare `writeValue` for new types
|
||||
## 6. Remove need to [avoid importing or exporting `toJson`, `%`, `%*` to prevent
|
||||
## conflicts](https://github.com/codex-storage/nim-codex/pull/645#issuecomment-1838834467)
|
||||
##
|
||||
## When declaring a new type, one should consider importing the `codex/logutils`
|
||||
## module, and specifying `formatIt`. If textlines log output and json log output
|
||||
## need to be different, overload `formatIt` and specify a `LogFormat`. If json
|
||||
## serialization is needed, it can be declared with a `%` proc. `logutils`
|
||||
## imports and exports `nim-serde` which handles the de/serialization, examples
|
||||
## below. **Only `codex/logutils` needs to be imported.**
|
||||
##
|
||||
## Using `logutils` in the Codex codebase:
|
||||
## - Instead of importing `pkg/chronicles`, import `pkg/codex/logutils`
|
||||
## - most of `chronicles` is exported by `logutils`
|
||||
## - Instead of importing `std/json`, import `pkg/serde/json`
|
||||
## - `std/json` is exported by `serde` which is exported by `logutils`
|
||||
## - Instead of importing `pkg/nim-json-serialization`, import
|
||||
## `pkg/serde/json` or use codex-specific overloads by importing `utils/json`
|
||||
## - one of the goals is to remove the use of `nim-json-serialization`
|
||||
##
|
||||
## ```nim
|
||||
## import pkg/codex/logutils
|
||||
##
|
||||
## type
|
||||
## BlockAddress* = object
|
||||
## case leaf*: bool
|
||||
## of true:
|
||||
## treeCid* {.serialize.}: Cid
|
||||
## index* {.serialize.}: Natural
|
||||
## else:
|
||||
## cid* {.serialize.}: Cid
|
||||
##
|
||||
## logutils.formatIt(LogFormat.textLines, BlockAddress):
|
||||
## if it.leaf:
|
||||
## "treeCid: " & shortLog($it.treeCid) & ", index: " & $it.index
|
||||
## else:
|
||||
## "cid: " & shortLog($it.cid)
|
||||
##
|
||||
## logutils.formatIt(LogFormat.json, BlockAddress): %it
|
||||
##
|
||||
## # chronicles textlines output
|
||||
## TRC test tid=14397405 ba="treeCid: zb2*fndjU1, index: 0"
|
||||
## # chronicles json output
|
||||
## {"lvl":"TRC","msg":"test","tid":14397405,"ba":{"treeCid":"zb2rhgsDE16rLtbwTFeNKbdSobtKiWdjJPvKEuPgrQAfndjU1","index":0}}
|
||||
## ```
|
||||
## In this case, `BlockAddress` is just an object, so `nim-serde` can handle
|
||||
## serializing it without issue (only fields annotated with `{.serialize.}` will
|
||||
## serialize (aka opt-in serialization)).
|
||||
##
|
||||
## If one so wished, another option for the textlines log output, would be to
|
||||
## simply `toString` the serialised json:
|
||||
## ```nim
|
||||
## logutils.formatIt(LogFormat.textLines, BlockAddress): $ %it
|
||||
## # or, more succinctly:
|
||||
## logutils.formatIt(LogFormat.textLines, BlockAddress): it.toJson
|
||||
## ```
|
||||
## In that case, both the textlines and json sinks would have the same output,
|
||||
## so we could reduce this even further by not specifying a `LogFormat`:
|
||||
## ```nim
|
||||
## type
|
||||
## BlockAddress* = object
|
||||
## case leaf*: bool
|
||||
## of true:
|
||||
## treeCid* {.serialize.}: Cid
|
||||
## index* {.serialize.}: Natural
|
||||
## else:
|
||||
## cid* {.serialize.}: Cid
|
||||
##
|
||||
## logutils.formatIt(BlockAddress): %it
|
||||
##
|
||||
## # chronicles textlines output
|
||||
## TRC test tid=14400673 ba="{\"treeCid\":\"zb2rhgsDE16rLtbwTFeNKbdSobtKiWdjJPvKEuPgrQAfndjU1\",\"index\":0}"
|
||||
## # chronicles json output
|
||||
## {"lvl":"TRC","msg":"test","tid":14400673,"ba":{"treeCid":"zb2rhgsDE16rLtbwTFeNKbdSobtKiWdjJPvKEuPgrQAfndjU1","index":0}}
|
||||
## ```
|
||||
|
||||
import std/options
|
||||
import std/sequtils
|
||||
import std/strutils
|
||||
import std/sugar
|
||||
import std/typetraits
|
||||
|
||||
import pkg/chronicles except toJson, `%`
|
||||
from pkg/libp2p import Cid, PeerId, SignedPeerRecord, MultiAddress, AddressInfo, init, `$`
|
||||
from pkg/ethers import Address
|
||||
import pkg/questionable
|
||||
import pkg/questionable/results
|
||||
import pkg/stew/byteutils
|
||||
import pkg/stint
|
||||
import pkg/serde/json
|
||||
import pkg/codexdht/discv5/node as dn
|
||||
import pkg/contractabi
|
||||
|
||||
export byteutils
|
||||
export chronicles except toJson, formatIt, `%`
|
||||
export questionable
|
||||
export sequtils
|
||||
export json except formatIt
|
||||
export strutils
|
||||
export sugar
|
||||
export results
|
||||
|
||||
proc fromJson*(_: type Cid, json: JsonNode): ?!Cid =
|
||||
expectJsonKind(Cid, JString, json)
|
||||
Cid.init(json.str).mapFailure
|
||||
|
||||
func `%`*(cid: Cid): JsonNode =
|
||||
% $cid
|
||||
|
||||
func `%`*(obj: PeerId): JsonNode =
|
||||
% $obj
|
||||
|
||||
func `%`*(obj: SignedPeerRecord): JsonNode =
|
||||
% $obj
|
||||
|
||||
func `%`*(obj: dn.Address): JsonNode =
|
||||
% $obj
|
||||
|
||||
func `%`*(obj: AddressInfo): JsonNode =
|
||||
% $obj.address
|
||||
|
||||
func `%`*(obj: MultiAddress): JsonNode =
|
||||
% $obj
|
||||
|
||||
func `%`*(address: ethers.Address): JsonNode =
|
||||
% $address
|
||||
|
||||
func shortLog*(long: string, ellipses = "*", start = 3, stop = 6): string =
|
||||
## Returns compact string representation of ``long``.
|
||||
var short = long
|
||||
let minLen = start + ellipses.len + stop
|
||||
if len(short) > minLen:
|
||||
short.insert(ellipses, start)
|
||||
|
||||
when (NimMajor, NimMinor) > (1, 4):
|
||||
short.delete(start + ellipses.len .. short.high - stop)
|
||||
else:
|
||||
short.delete(start + ellipses.len, short.high - stop)
|
||||
|
||||
short
|
||||
|
||||
func shortHexLog*(long: string): string =
|
||||
if long[0 .. 1] == "0x":
|
||||
result &= "0x"
|
||||
result &= long[2 .. long.high].shortLog("..", 4, 4)
|
||||
|
||||
func short0xHexLog*[N: static[int], T: array[N, byte]](v: T): string =
|
||||
v.to0xHex.shortHexLog
|
||||
|
||||
func short0xHexLog*[T: distinct](v: T): string =
|
||||
type BaseType = T.distinctBase
|
||||
BaseType(v).short0xHexLog
|
||||
|
||||
func short0xHexLog*[U: distinct, T: seq[U]](v: T): string =
|
||||
type BaseType = U.distinctBase
|
||||
"@[" & v.map(x => BaseType(x).short0xHexLog).join(",") & "]"
|
||||
|
||||
func to0xHexLog*[T: distinct](v: T): string =
|
||||
type BaseType = T.distinctBase
|
||||
BaseType(v).to0xHex
|
||||
|
||||
func to0xHexLog*[U: distinct, T: seq[U]](v: T): string =
|
||||
type BaseType = U.distinctBase
|
||||
"@[" & v.map(x => BaseType(x).to0xHex).join(",") & "]"
|
||||
|
||||
proc formatTextLineSeq*(val: seq[string]): string =
|
||||
"@[" & val.join(", ") & "]"
|
||||
|
||||
template formatIt*(format: LogFormat, T: typedesc, body: untyped) =
|
||||
# Provides formatters for logging with Chronicles for the given type and
|
||||
# `LogFormat`.
|
||||
# NOTE: `seq[T]`, `Option[T]`, and `seq[Option[T]]` are overridden
|
||||
# since the base `setProperty` is generic using `auto` and conflicts with
|
||||
# providing a generic `seq` and `Option` override.
|
||||
when format == LogFormat.json:
|
||||
proc formatJsonOption(val: ?T): JsonNode =
|
||||
if it =? val:
|
||||
json.`%`(body)
|
||||
else:
|
||||
newJNull()
|
||||
|
||||
proc formatJsonResult*(val: ?!T): JsonNode =
|
||||
without it =? val, error:
|
||||
let jObj = newJObject()
|
||||
jObj["error"] = newJString(error.msg)
|
||||
return jObj
|
||||
json.`%`(body)
|
||||
|
||||
proc setProperty*(r: var JsonRecord, key: string, res: ?!T) =
|
||||
var it {.inject, used.}: T
|
||||
setProperty(r, key, res.formatJsonResult)
|
||||
|
||||
proc setProperty*(r: var JsonRecord, key: string, opt: ?T) =
|
||||
var it {.inject, used.}: T
|
||||
let v = opt.formatJsonOption
|
||||
setProperty(r, key, v)
|
||||
|
||||
proc setProperty*(r: var JsonRecord, key: string, opts: seq[?T]) =
|
||||
var it {.inject, used.}: T
|
||||
let v = opts.map(opt => opt.formatJsonOption)
|
||||
setProperty(r, key, json.`%`(v))
|
||||
|
||||
proc setProperty*(
|
||||
r: var JsonRecord, key: string, val: seq[T]
|
||||
) {.raises: [ValueError, IOError].} =
|
||||
var it {.inject, used.}: T
|
||||
let v = val.map(it => body)
|
||||
setProperty(r, key, json.`%`(v))
|
||||
|
||||
proc setProperty*(
|
||||
r: var JsonRecord, key: string, val: T
|
||||
) {.raises: [ValueError, IOError].} =
|
||||
var it {.inject, used.}: T = val
|
||||
let v = body
|
||||
setProperty(r, key, json.`%`(v))
|
||||
|
||||
elif format == LogFormat.textLines:
|
||||
proc formatTextLineOption*(val: ?T): string =
|
||||
var v = "none(" & $T & ")"
|
||||
if it =? val:
|
||||
v = "some(" & $(body) & ")" # that I used to know :)
|
||||
v
|
||||
|
||||
proc formatTextLineResult*(val: ?!T): string =
|
||||
without it =? val, error:
|
||||
return "Error: " & error.msg
|
||||
$(body)
|
||||
|
||||
proc setProperty*(r: var TextLineRecord, key: string, res: ?!T) =
|
||||
var it {.inject, used.}: T
|
||||
setProperty(r, key, res.formatTextLineResult)
|
||||
|
||||
proc setProperty*(r: var TextLineRecord, key: string, opt: ?T) =
|
||||
var it {.inject, used.}: T
|
||||
let v = opt.formatTextLineOption
|
||||
setProperty(r, key, v)
|
||||
|
||||
proc setProperty*(r: var TextLineRecord, key: string, opts: seq[?T]) =
|
||||
var it {.inject, used.}: T
|
||||
let v = opts.map(opt => opt.formatTextLineOption)
|
||||
setProperty(r, key, v.formatTextLineSeq)
|
||||
|
||||
proc setProperty*(
|
||||
r: var TextLineRecord, key: string, val: seq[T]
|
||||
) {.raises: [ValueError, IOError].} =
|
||||
var it {.inject, used.}: T
|
||||
let v = val.map(it => body)
|
||||
setProperty(r, key, v.formatTextLineSeq)
|
||||
|
||||
proc setProperty*(
|
||||
r: var TextLineRecord, key: string, val: T
|
||||
) {.raises: [ValueError, IOError].} =
|
||||
var it {.inject, used.}: T = val
|
||||
let v = body
|
||||
setProperty(r, key, v)
|
||||
|
||||
template formatIt*(T: type, body: untyped) {.dirty.} =
|
||||
formatIt(LogFormat.textLines, T):
|
||||
body
|
||||
formatIt(LogFormat.json, T):
|
||||
body
|
||||
|
||||
formatIt(LogFormat.textLines, Cid):
|
||||
shortLog($it)
|
||||
formatIt(LogFormat.json, Cid):
|
||||
$it
|
||||
formatIt(UInt256):
|
||||
$it
|
||||
formatIt(MultiAddress):
|
||||
$it
|
||||
formatIt(LogFormat.textLines, array[32, byte]):
|
||||
it.short0xHexLog
|
||||
formatIt(LogFormat.json, array[32, byte]):
|
||||
it.to0xHex
|
||||
184
codexcrawler/services/marketplace/marketplace.nim
Normal file
184
codexcrawler/services/marketplace/marketplace.nim
Normal file
@ -0,0 +1,184 @@
|
||||
import pkg/ethers
|
||||
import pkg/ethers/erc20
|
||||
import pkg/json_rpc/rpcclient
|
||||
import pkg/stint
|
||||
import pkg/chronos
|
||||
import ./requests
|
||||
import ./proofs
|
||||
import ./config
|
||||
|
||||
export stint
|
||||
export ethers except `%`, `%*`, toJson
|
||||
export erc20 except `%`, `%*`, toJson
|
||||
export config
|
||||
export requests
|
||||
|
||||
type
|
||||
Marketplace* = ref object of Contract
|
||||
|
||||
Marketplace_RepairRewardPercentageTooHigh* = object of SolidityError
|
||||
Marketplace_SlashPercentageTooHigh* = object of SolidityError
|
||||
Marketplace_MaximumSlashingTooHigh* = object of SolidityError
|
||||
Marketplace_InvalidExpiry* = object of SolidityError
|
||||
Marketplace_InvalidMaxSlotLoss* = object of SolidityError
|
||||
Marketplace_InsufficientSlots* = object of SolidityError
|
||||
Marketplace_InvalidClientAddress* = object of SolidityError
|
||||
Marketplace_RequestAlreadyExists* = object of SolidityError
|
||||
Marketplace_InvalidSlot* = object of SolidityError
|
||||
Marketplace_SlotNotFree* = object of SolidityError
|
||||
Marketplace_InvalidSlotHost* = object of SolidityError
|
||||
Marketplace_AlreadyPaid* = object of SolidityError
|
||||
Marketplace_TransferFailed* = object of SolidityError
|
||||
Marketplace_UnknownRequest* = object of SolidityError
|
||||
Marketplace_InvalidState* = object of SolidityError
|
||||
Marketplace_StartNotBeforeExpiry* = object of SolidityError
|
||||
Marketplace_SlotNotAcceptingProofs* = object of SolidityError
|
||||
Marketplace_SlotIsFree* = object of SolidityError
|
||||
Marketplace_ReservationRequired* = object of SolidityError
|
||||
Marketplace_NothingToWithdraw* = object of SolidityError
|
||||
Marketplace_InsufficientDuration* = object of SolidityError
|
||||
Marketplace_InsufficientProofProbability* = object of SolidityError
|
||||
Marketplace_InsufficientCollateral* = object of SolidityError
|
||||
Marketplace_InsufficientReward* = object of SolidityError
|
||||
Marketplace_InvalidCid* = object of SolidityError
|
||||
Marketplace_DurationExceedsLimit* = object of SolidityError
|
||||
Proofs_InsufficientBlockHeight* = object of SolidityError
|
||||
Proofs_InvalidProof* = object of SolidityError
|
||||
Proofs_ProofAlreadySubmitted* = object of SolidityError
|
||||
Proofs_PeriodNotEnded* = object of SolidityError
|
||||
Proofs_ValidationTimedOut* = object of SolidityError
|
||||
Proofs_ProofNotMissing* = object of SolidityError
|
||||
Proofs_ProofNotRequired* = object of SolidityError
|
||||
Proofs_ProofAlreadyMarkedMissing* = object of SolidityError
|
||||
Proofs_InvalidProbability* = object of SolidityError
|
||||
Periods_InvalidSecondsPerPeriod* = object of SolidityError
|
||||
|
||||
proc configuration*(marketplace: Marketplace): MarketplaceConfig {.contract, view.}
|
||||
proc token*(marketplace: Marketplace): Address {.contract, view.}
|
||||
proc currentCollateral*(
|
||||
marketplace: Marketplace, id: SlotId
|
||||
): UInt256 {.contract, view.}
|
||||
|
||||
proc requestStorage*(
|
||||
marketplace: Marketplace, request: StorageRequest
|
||||
): Confirmable {.
|
||||
contract,
|
||||
errors: [
|
||||
Marketplace_InvalidClientAddress, Marketplace_RequestAlreadyExists,
|
||||
Marketplace_InvalidExpiry, Marketplace_InsufficientSlots,
|
||||
Marketplace_InvalidMaxSlotLoss,
|
||||
]
|
||||
.}
|
||||
|
||||
proc fillSlot*(
|
||||
marketplace: Marketplace, requestId: RequestId, slotIndex: uint64, proof: Groth16Proof
|
||||
): Confirmable {.
|
||||
contract,
|
||||
errors: [
|
||||
Marketplace_InvalidSlot, Marketplace_ReservationRequired, Marketplace_SlotNotFree,
|
||||
Marketplace_StartNotBeforeExpiry, Marketplace_UnknownRequest,
|
||||
]
|
||||
.}
|
||||
|
||||
proc withdrawFunds*(
|
||||
marketplace: Marketplace, requestId: RequestId
|
||||
): Confirmable {.
|
||||
contract,
|
||||
errors: [
|
||||
Marketplace_InvalidClientAddress, Marketplace_InvalidState,
|
||||
Marketplace_NothingToWithdraw, Marketplace_UnknownRequest,
|
||||
]
|
||||
.}
|
||||
|
||||
proc withdrawFunds*(
|
||||
marketplace: Marketplace, requestId: RequestId, withdrawAddress: Address
|
||||
): Confirmable {.
|
||||
contract,
|
||||
errors: [
|
||||
Marketplace_InvalidClientAddress, Marketplace_InvalidState,
|
||||
Marketplace_NothingToWithdraw, Marketplace_UnknownRequest,
|
||||
]
|
||||
.}
|
||||
|
||||
proc freeSlot*(
|
||||
marketplace: Marketplace, id: SlotId
|
||||
): Confirmable {.
|
||||
contract,
|
||||
errors: [
|
||||
Marketplace_InvalidSlotHost, Marketplace_AlreadyPaid,
|
||||
Marketplace_StartNotBeforeExpiry, Marketplace_UnknownRequest, Marketplace_SlotIsFree,
|
||||
]
|
||||
.}
|
||||
|
||||
proc freeSlot*(
|
||||
marketplace: Marketplace,
|
||||
id: SlotId,
|
||||
rewardRecipient: Address,
|
||||
collateralRecipient: Address,
|
||||
): Confirmable {.
|
||||
contract,
|
||||
errors: [
|
||||
Marketplace_InvalidSlotHost, Marketplace_AlreadyPaid,
|
||||
Marketplace_StartNotBeforeExpiry, Marketplace_UnknownRequest, Marketplace_SlotIsFree,
|
||||
]
|
||||
.}
|
||||
|
||||
proc getRequest*(
|
||||
marketplace: Marketplace, id: RequestId
|
||||
): StorageRequest {.contract, view, errors: [Marketplace_UnknownRequest].}
|
||||
|
||||
proc getHost*(marketplace: Marketplace, id: SlotId): Address {.contract, view.}
|
||||
proc getActiveSlot*(
|
||||
marketplace: Marketplace, id: SlotId
|
||||
): Slot {.contract, view, errors: [Marketplace_SlotIsFree].}
|
||||
|
||||
proc myRequests*(marketplace: Marketplace): seq[RequestId] {.contract, view.}
|
||||
proc mySlots*(marketplace: Marketplace): seq[SlotId] {.contract, view.}
|
||||
proc requestState*(
|
||||
marketplace: Marketplace, requestId: RequestId
|
||||
): RequestState {.contract, view, errors: [Marketplace_UnknownRequest].}
|
||||
|
||||
proc slotState*(marketplace: Marketplace, slotId: SlotId): SlotState {.contract, view.}
|
||||
proc requestEnd*(
|
||||
marketplace: Marketplace, requestId: RequestId
|
||||
): int64 {.contract, view.}
|
||||
|
||||
proc requestExpiry*(
|
||||
marketplace: Marketplace, requestId: RequestId
|
||||
): int64 {.contract, view.}
|
||||
|
||||
proc missingProofs*(marketplace: Marketplace, id: SlotId): UInt256 {.contract, view.}
|
||||
proc isProofRequired*(marketplace: Marketplace, id: SlotId): bool {.contract, view.}
|
||||
proc willProofBeRequired*(marketplace: Marketplace, id: SlotId): bool {.contract, view.}
|
||||
proc getChallenge*(
|
||||
marketplace: Marketplace, id: SlotId
|
||||
): array[32, byte] {.contract, view.}
|
||||
|
||||
proc getPointer*(marketplace: Marketplace, id: SlotId): uint8 {.contract, view.}
|
||||
|
||||
proc submitProof*(
|
||||
marketplace: Marketplace, id: SlotId, proof: Groth16Proof
|
||||
): Confirmable {.
|
||||
contract,
|
||||
errors:
|
||||
[Proofs_ProofAlreadySubmitted, Proofs_InvalidProof, Marketplace_UnknownRequest]
|
||||
.}
|
||||
|
||||
proc markProofAsMissing*(
|
||||
marketplace: Marketplace, id: SlotId, period: uint64
|
||||
): Confirmable {.
|
||||
contract,
|
||||
errors: [
|
||||
Marketplace_SlotNotAcceptingProofs, Marketplace_StartNotBeforeExpiry,
|
||||
Proofs_PeriodNotEnded, Proofs_ValidationTimedOut, Proofs_ProofNotMissing,
|
||||
Proofs_ProofNotRequired, Proofs_ProofAlreadyMarkedMissing,
|
||||
]
|
||||
.}
|
||||
|
||||
proc reserveSlot*(
|
||||
marketplace: Marketplace, requestId: RequestId, slotIndex: uint64
|
||||
): Confirmable {.contract.}
|
||||
|
||||
proc canReserveSlot*(
|
||||
marketplace: Marketplace, requestId: RequestId, slotIndex: uint64
|
||||
): bool {.contract, view.}
|
||||
46
codexcrawler/services/marketplace/proofs.nim
Normal file
46
codexcrawler/services/marketplace/proofs.nim
Normal file
@ -0,0 +1,46 @@
|
||||
import pkg/stint
|
||||
import pkg/contractabi
|
||||
import pkg/ethers/fields
|
||||
|
||||
type
|
||||
Groth16Proof* = object
|
||||
a*: G1Point
|
||||
b*: G2Point
|
||||
c*: G1Point
|
||||
|
||||
G1Point* = object
|
||||
x*: UInt256
|
||||
y*: UInt256
|
||||
|
||||
# A field element F_{p^2} encoded as `real + i * imag`
|
||||
Fp2Element* = object
|
||||
real*: UInt256
|
||||
imag*: UInt256
|
||||
|
||||
G2Point* = object
|
||||
x*: Fp2Element
|
||||
y*: Fp2Element
|
||||
|
||||
func solidityType*(_: type G1Point): string =
|
||||
solidityType(G1Point.fieldTypes)
|
||||
|
||||
func solidityType*(_: type Fp2Element): string =
|
||||
solidityType(Fp2Element.fieldTypes)
|
||||
|
||||
func solidityType*(_: type G2Point): string =
|
||||
solidityType(G2Point.fieldTypes)
|
||||
|
||||
func solidityType*(_: type Groth16Proof): string =
|
||||
solidityType(Groth16Proof.fieldTypes)
|
||||
|
||||
func encode*(encoder: var AbiEncoder, point: G1Point) =
|
||||
encoder.write(point.fieldValues)
|
||||
|
||||
func encode*(encoder: var AbiEncoder, element: Fp2Element) =
|
||||
encoder.write(element.fieldValues)
|
||||
|
||||
func encode*(encoder: var AbiEncoder, point: G2Point) =
|
||||
encoder.write(point.fieldValues)
|
||||
|
||||
func encode*(encoder: var AbiEncoder, proof: Groth16Proof) =
|
||||
encoder.write(proof.fieldValues)
|
||||
2
codexcrawler/services/marketplace/readme.md
Normal file
2
codexcrawler/services/marketplace/readme.md
Normal file
@ -0,0 +1,2 @@
|
||||
These are copied from nim-codex v0.2.0.
|
||||
There are plans to extract/refactor the contract interoperability code from nim-codex into its own submodule. But this isn't prioritized atm. So we're copying it here until that's been handled.
|
||||
220
codexcrawler/services/marketplace/requests.nim
Normal file
220
codexcrawler/services/marketplace/requests.nim
Normal file
@ -0,0 +1,220 @@
|
||||
import std/hashes
|
||||
import std/sequtils
|
||||
import std/typetraits
|
||||
import pkg/contractabi
|
||||
import pkg/nimcrypto
|
||||
import pkg/ethers/fields
|
||||
import pkg/results
|
||||
import pkg/questionable/results
|
||||
import pkg/stew/byteutils
|
||||
import pkg/libp2p/[cid, multicodec]
|
||||
import pkg/serde/json
|
||||
import ./logutils
|
||||
|
||||
export contractabi
|
||||
|
||||
type
|
||||
StorageRequest* = object
|
||||
client* {.serialize.}: Address
|
||||
ask* {.serialize.}: StorageAsk
|
||||
content* {.serialize.}: StorageContent
|
||||
expiry* {.serialize.}: uint64
|
||||
nonce*: Nonce
|
||||
|
||||
StorageAsk* = object
|
||||
proofProbability* {.serialize.}: UInt256
|
||||
pricePerBytePerSecond* {.serialize.}: UInt256
|
||||
collateralPerByte* {.serialize.}: UInt256
|
||||
slots* {.serialize.}: uint64
|
||||
slotSize* {.serialize.}: uint64
|
||||
duration* {.serialize.}: uint64
|
||||
maxSlotLoss* {.serialize.}: uint64
|
||||
|
||||
StorageContent* = object
|
||||
cid* {.serialize.}: Cid
|
||||
merkleRoot*: array[32, byte]
|
||||
|
||||
Slot* = object
|
||||
request* {.serialize.}: StorageRequest
|
||||
slotIndex* {.serialize.}: uint64
|
||||
|
||||
SlotId* = distinct array[32, byte]
|
||||
RequestId* = distinct array[32, byte]
|
||||
Nonce* = distinct array[32, byte]
|
||||
RequestState* {.pure.} = enum
|
||||
New
|
||||
Started
|
||||
Cancelled
|
||||
Finished
|
||||
Failed
|
||||
|
||||
SlotState* {.pure.} = enum
|
||||
Free
|
||||
Filled
|
||||
Finished
|
||||
Failed
|
||||
Paid
|
||||
Cancelled
|
||||
Repair
|
||||
|
||||
template mapFailure*[T, V, E](
|
||||
exp: Result[T, V], exc: typedesc[E]
|
||||
): Result[T, ref CatchableError] =
|
||||
## Convert `Result[T, E]` to `Result[E, ref CatchableError]`
|
||||
##
|
||||
|
||||
exp.mapErr(
|
||||
proc(e: V): ref CatchableError =
|
||||
(ref exc)(msg: $e)
|
||||
)
|
||||
|
||||
template mapFailure*[T, V](exp: Result[T, V]): Result[T, ref CatchableError] =
|
||||
mapFailure(exp, CatchableError)
|
||||
|
||||
proc `==`*(x, y: Nonce): bool {.borrow.}
|
||||
proc `==`*(x, y: RequestId): bool {.borrow.}
|
||||
proc `==`*(x, y: SlotId): bool {.borrow.}
|
||||
proc hash*(x: SlotId): Hash {.borrow.}
|
||||
proc hash*(x: Nonce): Hash {.borrow.}
|
||||
proc hash*(x: Address): Hash {.borrow.}
|
||||
|
||||
func toArray*(id: RequestId | SlotId | Nonce): array[32, byte] =
|
||||
array[32, byte](id)
|
||||
|
||||
proc `$`*(id: RequestId | SlotId | Nonce): string =
|
||||
id.toArray.toHex
|
||||
|
||||
proc fromHex*(T: type RequestId, hex: string): T =
|
||||
T array[32, byte].fromHex(hex)
|
||||
|
||||
proc fromHex*(T: type SlotId, hex: string): T =
|
||||
T array[32, byte].fromHex(hex)
|
||||
|
||||
proc fromHex*(T: type Nonce, hex: string): T =
|
||||
T array[32, byte].fromHex(hex)
|
||||
|
||||
proc fromHex*[T: distinct](_: type T, hex: string): T =
|
||||
type baseType = T.distinctBase
|
||||
T baseType.fromHex(hex)
|
||||
|
||||
proc toHex*[T: distinct](id: T): string =
|
||||
type baseType = T.distinctBase
|
||||
baseType(id).toHex
|
||||
|
||||
logutils.formatIt(LogFormat.textLines, Nonce):
|
||||
it.short0xHexLog
|
||||
logutils.formatIt(LogFormat.textLines, RequestId):
|
||||
it.short0xHexLog
|
||||
logutils.formatIt(LogFormat.textLines, SlotId):
|
||||
it.short0xHexLog
|
||||
logutils.formatIt(LogFormat.json, Nonce):
|
||||
it.to0xHexLog
|
||||
logutils.formatIt(LogFormat.json, RequestId):
|
||||
it.to0xHexLog
|
||||
logutils.formatIt(LogFormat.json, SlotId):
|
||||
it.to0xHexLog
|
||||
|
||||
func fromTuple(_: type StorageRequest, tupl: tuple): StorageRequest =
|
||||
StorageRequest(
|
||||
client: tupl[0], ask: tupl[1], content: tupl[2], expiry: tupl[3], nonce: tupl[4]
|
||||
)
|
||||
|
||||
func fromTuple(_: type Slot, tupl: tuple): Slot =
|
||||
Slot(request: tupl[0], slotIndex: tupl[1])
|
||||
|
||||
func fromTuple(_: type StorageAsk, tupl: tuple): StorageAsk =
|
||||
StorageAsk(
|
||||
proofProbability: tupl[0],
|
||||
pricePerBytePerSecond: tupl[1],
|
||||
collateralPerByte: tupl[2],
|
||||
slots: tupl[3],
|
||||
slotSize: tupl[4],
|
||||
duration: tupl[5],
|
||||
maxSlotLoss: tupl[6],
|
||||
)
|
||||
|
||||
func fromTuple(_: type StorageContent, tupl: tuple): StorageContent =
|
||||
StorageContent(cid: tupl[0], merkleRoot: tupl[1])
|
||||
|
||||
func solidityType*(_: type Cid): string =
|
||||
solidityType(seq[byte])
|
||||
|
||||
func solidityType*(_: type StorageContent): string =
|
||||
solidityType(StorageContent.fieldTypes)
|
||||
|
||||
func solidityType*(_: type StorageAsk): string =
|
||||
solidityType(StorageAsk.fieldTypes)
|
||||
|
||||
func solidityType*(_: type StorageRequest): string =
|
||||
solidityType(StorageRequest.fieldTypes)
|
||||
|
||||
# Note: it seems to be ok to ignore the vbuffer offset for now
|
||||
func encode*(encoder: var AbiEncoder, cid: Cid) =
|
||||
encoder.write(cid.data.buffer)
|
||||
|
||||
func encode*(encoder: var AbiEncoder, content: StorageContent) =
|
||||
encoder.write(content.fieldValues)
|
||||
|
||||
func encode*(encoder: var AbiEncoder, ask: StorageAsk) =
|
||||
encoder.write(ask.fieldValues)
|
||||
|
||||
func encode*(encoder: var AbiEncoder, id: RequestId | SlotId | Nonce) =
|
||||
encoder.write(id.toArray)
|
||||
|
||||
func encode*(encoder: var AbiEncoder, request: StorageRequest) =
|
||||
encoder.write(request.fieldValues)
|
||||
|
||||
func encode*(encoder: var AbiEncoder, slot: Slot) =
|
||||
encoder.write(slot.fieldValues)
|
||||
|
||||
func decode*(decoder: var AbiDecoder, T: type Cid): ?!T =
|
||||
let data = ?decoder.read(seq[byte])
|
||||
Cid.init(data).mapFailure
|
||||
|
||||
func decode*(decoder: var AbiDecoder, T: type StorageContent): ?!T =
|
||||
let tupl = ?decoder.read(StorageContent.fieldTypes)
|
||||
success StorageContent.fromTuple(tupl)
|
||||
|
||||
func decode*(decoder: var AbiDecoder, T: type StorageAsk): ?!T =
|
||||
let tupl = ?decoder.read(StorageAsk.fieldTypes)
|
||||
success StorageAsk.fromTuple(tupl)
|
||||
|
||||
func decode*(decoder: var AbiDecoder, T: type StorageRequest): ?!T =
|
||||
let tupl = ?decoder.read(StorageRequest.fieldTypes)
|
||||
success StorageRequest.fromTuple(tupl)
|
||||
|
||||
func decode*(decoder: var AbiDecoder, T: type Slot): ?!T =
|
||||
let tupl = ?decoder.read(Slot.fieldTypes)
|
||||
success Slot.fromTuple(tupl)
|
||||
|
||||
func id*(request: StorageRequest): RequestId =
|
||||
let encoding = AbiEncoder.encode((request,))
|
||||
RequestId(keccak256.digest(encoding).data)
|
||||
|
||||
func slotId*(requestId: RequestId, slotIndex: uint64): SlotId =
|
||||
let encoding = AbiEncoder.encode((requestId, slotIndex))
|
||||
SlotId(keccak256.digest(encoding).data)
|
||||
|
||||
func slotId*(request: StorageRequest, slotIndex: uint64): SlotId =
|
||||
slotId(request.id, slotIndex)
|
||||
|
||||
func id*(slot: Slot): SlotId =
|
||||
slotId(slot.request, slot.slotIndex)
|
||||
|
||||
func pricePerSlotPerSecond*(ask: StorageAsk): UInt256 =
|
||||
ask.pricePerBytePerSecond * ask.slotSize.u256
|
||||
|
||||
func pricePerSlot*(ask: StorageAsk): UInt256 =
|
||||
ask.duration.u256 * ask.pricePerSlotPerSecond
|
||||
|
||||
func totalPrice*(ask: StorageAsk): UInt256 =
|
||||
ask.slots.u256 * ask.pricePerSlot
|
||||
|
||||
func totalPrice*(request: StorageRequest): UInt256 =
|
||||
request.ask.totalPrice
|
||||
|
||||
func collateralPerSlot*(ask: StorageAsk): UInt256 =
|
||||
ask.collateralPerByte * ask.slotSize.u256
|
||||
|
||||
func size*(ask: StorageAsk): uint64 =
|
||||
ask.slots * ask.slotSize
|
||||
Loading…
x
Reference in New Issue
Block a user