feat(mix): accept inline relay pool JSON via --mix-pool-json

Part of https://github.com/logos-storage/logos-storage-pm/issues/13

Signed-off-by: Chrysostomos Nanakos <chris@include.gr>
This commit is contained in:
Chrysostomos Nanakos 2026-06-16 12:52:20 +03:00
parent fac507bda2
commit 80b083768e
No known key found for this signature in database
4 changed files with 108 additions and 21 deletions

View File

@ -208,16 +208,21 @@ type
mixEnabled* {.
desc:
"Route DHT provider lookups through the Mix protocol via the " &
"dht-mix-proxy. Hides the requester's identity from the proxy.",
"dht-mix-proxy. Hides the requester's identity from the proxy",
defaultValue: false,
name: "mix-enabled"
.}: bool
mixPool* {.
desc: "Path to the Mix relay pool JSON file", defaultValue: "", name: "mix-pool"
.}: string
mixPoolJson* {.
desc:
"Path to the Mix relay pool JSON file " & "(produced by the `mix_pool` tool).",
"Inline JSON content of the Mix relay pool." &
"Takes precedence over --mix-pool when non-empty",
defaultValue: "",
name: "mix-pool"
name: "mix-pool-json"
.}: string
maxPeers* {.

View File

@ -94,7 +94,12 @@ proc start*(s: StorageServer) {.async.} =
mixPub, mixPriv, switch.peerInfo.peerId, mixAddr, switch.peerInfo.privateKey
).valueOr:
raise newException(StorageError, "Failed to build Mix node info: " & error.msg)
relayPool = loadRelayPubInfoTable(s.config.mixPool).valueOr:
relayPool = (
if s.config.mixPoolJson.len > 0:
loadRelayPubInfoTableFromJson(s.config.mixPoolJson)
else:
loadRelayPubInfoTableFromFile(s.config.mixPool)
).valueOr:
raise newException(StorageError, "Failed to load Mix relay pool: " & error.msg)
mixProto = MixProtocol.new(mixNodeInfo, switch)

View File

@ -155,37 +155,25 @@ proc pubInfoFromJson(node: JsonNode): ?!MixPubInfo =
success MixPubInfo.init(peerId, multiAddr, mixPubKey, libp2pPubKey)
proc loadRelayPubInfoTable*(poolPath: string): ?!Table[PeerId, MixPubInfo] =
proc loadRelayPubInfoTableFromJson*(poolJson: string): ?!Table[PeerId, MixPubInfo] =
## Expected format:
## { "version": 1, "relays": [ { peerId, multiAddr, mixPubKey, libp2pPubKey }, ... ] }
if poolPath.len == 0:
if poolJson.len == 0:
return success initTable[PeerId, MixPubInfo]()
if not fileExists(poolPath):
return failure("Mix pool file does not exist: " & poolPath)
let raw =
try:
readFile(poolPath)
except IOError as exc:
return failure("Failed to read pool " & poolPath & ": " & exc.msg)
let parsed =
try:
parseJson(raw)
parseJson(poolJson)
except CatchableError as exc:
return failure("Failed to parse pool JSON: " & exc.msg)
let versionNode = parsed.getOrDefault("version")
if versionNode.isNil or versionNode.getInt() != PoolFormatVersion:
return failure(
"Unsupported pool version (expected " & $PoolFormatVersion & " in " & poolPath &
")"
)
return failure("Unsupported pool version (expected " & $PoolFormatVersion & ")")
let relaysNode = parsed.getOrDefault("relays")
if relaysNode.isNil or relaysNode.kind != JArray:
return failure("Pool file missing 'relays' array: " & poolPath)
return failure("Pool JSON missing 'relays' array")
var t = initTable[PeerId, MixPubInfo]()
for entry in relaysNode:
@ -194,4 +182,19 @@ proc loadRelayPubInfoTable*(poolPath: string): ?!Table[PeerId, MixPubInfo] =
success t
proc loadRelayPubInfoTableFromFile*(poolPath: string): ?!Table[PeerId, MixPubInfo] =
if poolPath.len == 0:
return success initTable[PeerId, MixPubInfo]()
if not fileExists(poolPath):
return failure("Mix pool file does not exist: " & poolPath)
let poolJson =
try:
readFile(poolPath)
except IOError as exc:
return failure("Failed to read pool " & poolPath & ": " & exc.msg)
loadRelayPubInfoTableFromJson(poolJson)
{.pop.}

View File

@ -0,0 +1,74 @@
import std/tables
import pkg/unittest2
import pkg/libp2p/peerid
import pkg/storage/utils/mixidentity {.all.}
const SamplePoolJson = """
{
"version": 1,
"relays": [
{
"peerId": "16Uiu2HAmNNzXL3wnW64pPFJDwrSJnNaX4CNLeWPbzdPcVJhRTGwP",
"multiAddr": "/ip4/127.0.0.1/tcp/4242",
"mixPubKey": "8a6571e8665fb1c894215f97d6a244591b655b1f5fd5ff7f928ef8b74aa66c5f",
"libp2pPubKey": "03907bc5a41bec7c5ba11f8dfe6c7f779328d2d5bb48c9a978a11e09f3fbf61b3e"
},
{
"peerId": "16Uiu2HAmM6CDJa9HJQ76cRubcpAmrHfMcUCvYncA9M4BfFFEszQn",
"multiAddr": "/ip4/127.0.0.1/tcp/4243",
"mixPubKey": "f268d04a1a0903ecf63a3441b986eae414579aa47ff22b071370e6fcd9d3b45c",
"libp2pPubKey": "037d526dab2572c2336f721813964011899ba7d11a3ebebed1d22d1dea2b74e547"
}
]
}
"""
suite "mixidentity / loadRelayPubInfoTableFromJson":
test "empty string yields an empty table":
let res = loadRelayPubInfoTableFromJson("")
check res.isOk
check res.get.len == 0
test "parses a well-formed pool":
let res = loadRelayPubInfoTableFromJson(SamplePoolJson)
check res.isOk
let t = res.get
check t.len == 2
let
p0 = PeerId.init("16Uiu2HAmNNzXL3wnW64pPFJDwrSJnNaX4CNLeWPbzdPcVJhRTGwP").get
p1 = PeerId.init("16Uiu2HAmM6CDJa9HJQ76cRubcpAmrHfMcUCvYncA9M4BfFFEszQn").get
check p0 in t
check p1 in t
test "rejects malformed JSON":
let res = loadRelayPubInfoTableFromJson("{not json")
check res.isErr
test "rejects unsupported version":
let
json = """{"version": 99, "relays": []}"""
res = loadRelayPubInfoTableFromJson(json)
check res.isErr
test "rejects missing relays array":
let
json = """{"version": 1}"""
res = loadRelayPubInfoTableFromJson(json)
check res.isErr
test "rejects entry missing required field":
let json = """
{
"version": 1,
"relays": [
{
"peerId": "16Uiu2HAmNNzXL3wnW64pPFJDwrSJnNaX4CNLeWPbzdPcVJhRTGwP",
"multiAddr": "/ip4/127.0.0.1/tcp/4242",
"mixPubKey": "8a6571e8665fb1c894215f97d6a244591b655b1f5fd5ff7f928ef8b74aa66c5f"
}
]
}
"""
let res = loadRelayPubInfoTableFromJson(json)
check res.isErr