Giuliano Mega 1d1242e07a
feat: run DHT queries over Mix (#1452)
Signed-off-by: Chrysostomos Nanakos <chris@include.gr>
Co-authored-by: Chrysostomos Nanakos <chris@include.gr>
2026-06-17 18:41:32 +00:00

134 lines
3.4 KiB
Nim

## Logos Storage
## Copyright (c) 2026 Status Research & Development GmbH
## Licensed under either of
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
## at your option.
## This file may not be copied, modified, or distributed except according to
## those terms.
{.push raises: [].}
import pkg/libp2p/protobuf/minprotobuf
import pkg/libp2p_mix
import pkg/libp2p/routing_record
import ../logutils
const DhtProxyCodec* = "/storage/dht-proxy/1.0.0"
const DefaultMaxInFlightLookups* = 100
let MaxLookupRequestBytes* = getMaxMessageSizeForCodec(DhtProxyCodec, 1).expect(
"DhtProxyCodec framing leaves no room for a Sphinx forward payload"
)
let MaxLookupResponseBytes* = getMaxMessageSizeForCodec(DhtProxyCodec, 0).expect(
"DhtProxyCodec framing leaves no room for a Sphinx reply payload"
)
type
QueryType* {.pure.} = enum
FindProviders = 0
ResponseStatus* {.pure.} = enum
Ok = 0
NotFound = 1
Error = 2
ErrorKind* {.pure.} = enum
DecodeFailed = 0
InvalidCid = 1
Internal = 2
ResponseTooLarge = 3
TooBusy = 4
LookupRequest* = object
queryType*: QueryType
queryBytes*: seq[byte]
LookupResponse* = object
status*: ResponseStatus
errorKind*: ErrorKind
providers*: seq[seq[byte]]
proc encode*(req: LookupRequest): seq[byte] =
var pb = initProtoBuffer()
pb.write(1, req.queryType.uint32)
pb.write(2, req.queryBytes)
pb.finish()
pb.buffer
proc encode*(resp: LookupResponse): seq[byte] =
var pb = initProtoBuffer()
pb.write(1, resp.status.uint32)
if resp.status == ResponseStatus.Error:
pb.write(2, resp.errorKind.uint32)
for spr in resp.providers:
pb.write(3, spr)
pb.finish()
pb.buffer
proc decode*(_: type LookupRequest, data: openArray[byte]): ProtoResult[LookupRequest] =
let pb = initProtoBuffer(data)
var
req = LookupRequest()
qt: uint32
if ?pb.getField(1, qt):
if qt > QueryType.high.uint32:
return err(ProtoError.IncorrectBlob)
req.queryType = QueryType(qt)
discard ?pb.getField(2, req.queryBytes)
ok(req)
proc decode*(
_: type LookupResponse, data: openArray[byte]
): ProtoResult[LookupResponse] =
let pb = initProtoBuffer(data)
var
resp = LookupResponse()
status: uint32
if ?pb.getField(1, status):
if status > ResponseStatus.high.uint32:
return err(ProtoError.IncorrectBlob)
resp.status = ResponseStatus(status)
if resp.status == ResponseStatus.Error:
var ek: uint32
if ?pb.getField(2, ek):
if ek > ErrorKind.high.uint32:
return err(ProtoError.IncorrectBlob)
resp.errorKind = ErrorKind(ek)
discard ?pb.getRepeatedField(3, resp.providers)
ok(resp)
proc packProviders*(
providers: seq[seq[byte]], budget_bytes: int
): Result[seq[seq[byte]], ErrorKind] =
if providers.len == 0:
error "packProviders called with no providers"
return err(ErrorKind.Internal)
let single = LookupResponse(status: ResponseStatus.Ok, providers: providers[0 ..< 1])
if single.encode().len > budget_bytes:
return err(ErrorKind.ResponseTooLarge)
var
lo = 1
hi = providers.len
while lo < hi:
let
mid = (lo + hi + 1) div 2
test = LookupResponse(status: ResponseStatus.Ok, providers: providers[0 ..< mid])
if test.encode().len <= budget_bytes:
lo = mid
else:
hi = mid - 1
ok(providers[0 ..< lo])