mirror of
https://github.com/logos-messaging/logos-delivery.git
synced 2026-07-27 06:53:29 +00:00
Stop clearing eligibilityProof after JSON parse in logosdelivery_store_query so tag-30 proof bytes reach the remote Store handler (Step 17 paid E2E). Add waku_store test for E2E query JSON / N8 canonical parity.
169 lines
5.2 KiB
Nim
169 lines
5.2 KiB
Nim
import std/[json, options, strutils, tables, sugar, locks]
|
|
import chronos, results, stew/byteutils, ffi
|
|
import
|
|
logos_delivery/waku/factory/waku,
|
|
logos_delivery/waku/waku_core/peers,
|
|
logos_delivery/waku/waku_store/[
|
|
common, protocol, client, eligibility_canonical, eligibility_hooks
|
|
],
|
|
../declare_lib,
|
|
./store_query_json
|
|
|
|
const EligibilityOutProofHexMinLen = 4096
|
|
|
|
type
|
|
EligibilityProviderCb* = proc (
|
|
canonical_hex, provider_peer_id: cstring, out_proof_hex: cstring,
|
|
out_buf_len: csize_t, user_data: pointer
|
|
): cint {.cdecl, gcsafe, raises: [].}
|
|
|
|
StoreEligibilityState* = ref object
|
|
verifierCb: EligibilityVerifierCb
|
|
verifierUserData: pointer
|
|
providerCb: EligibilityProviderCb
|
|
providerUserData: pointer
|
|
innerHandler: StoreQueryRequestHandler
|
|
handlerWrapped: bool
|
|
|
|
export EligibilityVerifierCb
|
|
|
|
var
|
|
storeEligibilityByCtx = initTable[pointer, StoreEligibilityState]()
|
|
eligibilityHookLock: Lock
|
|
|
|
initLock(eligibilityHookLock)
|
|
|
|
proc getOrCreateStateUnlocked(key: pointer): StoreEligibilityState =
|
|
result = storeEligibilityByCtx.getOrDefault(key)
|
|
if result.isNil:
|
|
result = StoreEligibilityState.new()
|
|
storeEligibilityByCtx[key] = result
|
|
|
|
proc dropState*(ctx: ptr FFIContext[Waku]) =
|
|
eligibilityHookLock.acquire()
|
|
defer:
|
|
eligibilityHookLock.release()
|
|
storeEligibilityByCtx.del cast[pointer](ctx)
|
|
|
|
proc applyVerifierWrapper*(ctx: ptr FFIContext[Waku], state: StoreEligibilityState) =
|
|
let store = ctx.myLib[].node.wakuStore
|
|
if store.isNil:
|
|
return
|
|
if not state.handlerWrapped:
|
|
state.innerHandler = store.requestHandler
|
|
state.handlerWrapped = true
|
|
store.requestHandler = makeEligibilityWrappedHandler(
|
|
state.verifierCb, state.verifierUserData, state.innerHandler, store
|
|
)
|
|
|
|
proc clearVerifierWrapper*(ctx: ptr FFIContext[Waku], state: StoreEligibilityState) =
|
|
let store = ctx.myLib[].node.wakuStore
|
|
if store.isNil or not state.handlerWrapped:
|
|
return
|
|
store.requestHandler = state.innerHandler
|
|
state.handlerWrapped = false
|
|
|
|
proc logosdelivery_set_eligibility_verifier(
|
|
ctx: ptr FFIContext[Waku], cb: EligibilityVerifierCb, userData: pointer
|
|
): cint {.dynlib, exportc, cdecl.} =
|
|
initializeLibrary()
|
|
if isNil(ctx):
|
|
return RET_ERR
|
|
eligibilityHookLock.acquire()
|
|
defer:
|
|
eligibilityHookLock.release()
|
|
let state = getOrCreateStateUnlocked(cast[pointer](ctx))
|
|
state.verifierCb = cb
|
|
state.verifierUserData = userData
|
|
if cb.isNil:
|
|
clearVerifierWrapper(ctx, state)
|
|
else:
|
|
applyVerifierWrapper(ctx, state)
|
|
RET_OK
|
|
|
|
proc logosdelivery_set_eligibility_provider(
|
|
ctx: ptr FFIContext[Waku], cb: EligibilityProviderCb, userData: pointer
|
|
): cint {.dynlib, exportc, cdecl.} =
|
|
initializeLibrary()
|
|
if isNil(ctx):
|
|
return RET_ERR
|
|
eligibilityHookLock.acquire()
|
|
defer:
|
|
eligibilityHookLock.release()
|
|
let state = getOrCreateStateUnlocked(cast[pointer](ctx))
|
|
state.providerCb = cb
|
|
state.providerUserData = userData
|
|
RET_OK
|
|
|
|
proc snapshotProviderCb(ctx: ptr FFIContext[Waku]): (EligibilityProviderCb, pointer) {.
|
|
gcsafe
|
|
.} =
|
|
eligibilityHookLock.acquire()
|
|
defer:
|
|
eligibilityHookLock.release()
|
|
{.cast(gcsafe).}:
|
|
let state = storeEligibilityByCtx.getOrDefault(cast[pointer](ctx))
|
|
if state.isNil:
|
|
return (nil, nil)
|
|
return (state.providerCb, state.providerUserData)
|
|
|
|
proc logosdelivery_store_query(
|
|
ctx: ptr FFIContext[Waku],
|
|
callback: FFICallBack,
|
|
userData: pointer,
|
|
queryJson: cstring,
|
|
providerAddr: cstring,
|
|
) {.ffi.} =
|
|
requireInitializedNode(ctx, "STORE_QUERY"):
|
|
return err(errMsg)
|
|
|
|
let jsonContentRes = catch:
|
|
parseJson($queryJson)
|
|
|
|
if jsonContentRes.isErr():
|
|
return err("StoreRequest failed parsing store request: " & jsonContentRes.error.msg)
|
|
|
|
var storeQueryRequest = storeQueryRequestFromJson(jsonContentRes.get()).valueOr:
|
|
return err("StoreRequest invalid query: " & error)
|
|
|
|
let (providerCb, providerUserData) = snapshotProviderCb(ctx)
|
|
|
|
let peer = peers.parsePeerInfo(($providerAddr).split(",")).valueOr:
|
|
return err("StoreRequest failed to parse peer addr: " & $error)
|
|
|
|
if storeQueryRequest.eligibilityProof.isNone and not providerCb.isNil:
|
|
let canonicalHex = storeEligibilityCanonicalHex(storeQueryRequest)
|
|
var outProof = newString(EligibilityOutProofHexMinLen)
|
|
outProof.setLen(EligibilityOutProofHexMinLen)
|
|
let providerRc = providerCb(
|
|
cstring(canonicalHex),
|
|
cstring($peer.peerId),
|
|
cstring(outProof),
|
|
csize_t(EligibilityOutProofHexMinLen),
|
|
providerUserData,
|
|
)
|
|
if providerRc < 0:
|
|
return err("eligibility provider callback failed")
|
|
|
|
var proofHex = outProof.strip(chars = {'\0'})
|
|
if proofHex.startsWith("0x"):
|
|
proofHex = proofHex[2 .. ^1]
|
|
let proofBytes =
|
|
try:
|
|
proofHex.hexToSeqByte()
|
|
except ValueError:
|
|
return err("eligibility provider returned invalid proof hex")
|
|
storeQueryRequest.eligibilityProof = some(proofBytes)
|
|
|
|
let queryResponse = (
|
|
await ctx.myLib[].node.wakuStoreClient.query(storeQueryRequest, peer)
|
|
).valueOr:
|
|
return err("StoreRequest failed store query: " & $error)
|
|
|
|
let res = $(%*(queryResponse.toHex()))
|
|
return ok(res)
|
|
|
|
export
|
|
logosdelivery_set_eligibility_verifier, logosdelivery_set_eligibility_provider,
|
|
logosdelivery_store_query
|