pass eligibilityEnabled as config parameter

This commit is contained in:
Sergei Tikhomirov 2025-06-22 15:31:01 +02:00
parent 5d3af6d3c3
commit 1e1d89ec93
7 changed files with 179 additions and 36 deletions

View File

@ -46,6 +46,10 @@ type
#Rate limit configs for non-relay req-resp protocols
rateLimitSettings: Option[seq[string]]
# Eligibility enabled
# FIXME: is this the right way to propagate eligibility-related config items?
eligibilityEnabled: bool
WakuNodeBuilderResult* = Result[void, string]
## Init
@ -113,6 +117,7 @@ proc withPeerManagerConfig*(
maxConnections: int,
relayServiceRatio: string,
shardAware = false,
eligibilityEnabled = false,
) =
let (relayRatio, serviceRatio) = parseRelayServiceRatio(relayServiceRatio).get()
var relayPeers = int(ceil(float(maxConnections) * relayRatio))
@ -121,6 +126,7 @@ proc withPeerManagerConfig*(
builder.maxServicePeers = servicePeers
builder.maxRelayPeers = relayPeers
builder.shardAware = shardAware
builder.eligibilityEnabled = eligibilityEnabled
proc withColocationLimit*(builder: var WakuNodeBuilder, colocationLimit: int) =
builder.colocationLimit = colocationLimit
@ -206,7 +212,8 @@ proc build*(builder: WakuNodeBuilder): Result[WakuNode, string] =
colocationLimit = builder.colocationLimit,
shardedPeerManagement = builder.shardAware,
dnsNameServers = netConfig.dnsNameServers,
eligibilityEnabled = true, # FIXME: i13n: read config or something instead
# FIXME: should eligibilityEnabled be part of some eligibilityConfig within WakuNodeBuilder here?
eligibilityEnabled = builder.eligibilityEnabled
#reputationEnabled = true, # FIXME: i13n: read config or something instead
)

View File

@ -0,0 +1,60 @@
import chronicles, std/options, results
import ../waku_conf
import eth/common as eth_common
logScope:
topics = "waku conf builder eligibility"
type EligibilityConfBuilder* = object
enabled*: Option[bool]
receiverAddress*: Option[string]
paymentAmountWei*: Option[uint32]
ethClientUrls*: Option[seq[string]]
proc init*(T: type EligibilityConfBuilder): EligibilityConfBuilder =
EligibilityConfBuilder()
proc withEnabled*(b: var EligibilityConfBuilder, enabled: bool) =
b.enabled = some(enabled)
proc withReceiverAddress*(b: var EligibilityConfBuilder, receiverAddress: string) =
b.receiverAddress = some(receiverAddress)
proc withPaymentAmountWei*(b: var EligibilityConfBuilder, amount: uint32) =
b.paymentAmountWei = some(amount)
proc withEthClientUrls*(b: var EligibilityConfBuilder, urls: seq[string]) =
b.ethClientUrls = some(urls)
proc build*(b: EligibilityConfBuilder): Result[Option[EligibilityConf], string] =
if not b.enabled.get(false):
debug "eligibility: EligibilityConf not enabled"
return ok(none(EligibilityConf))
# Validation
if b.receiverAddress.isNone() or b.paymentAmountWei.isNone():
debug "eligibility: EligibilityConf validation failed - missing address or amount"
return err("Eligibility: receiver address and payment amount must be specified")
# FIXME: add validation check that receiver address is validly formed
if b.paymentAmountWei.get() == 0:
debug "eligibility: EligibilityConf validation failed - payment amount is zero"
return err("Eligibility: payment amount must be above zero")
let urls = b.ethClientUrls.get(@[])
if urls.len == 0:
debug "eligibility: EligibilityConf validation failed - no eth rpc urls"
return err("Eligibility: eligibility-eth-client-address is not specified")
debug "eligibility: EligibilityConf created"
return ok(
some(
EligibilityConf(
enabled: true,
receiverAddress: b.receiverAddress.get(),
paymentAmountWei: b.paymentAmountWei.get(),
ethClientUrls: urls,
)
)
)

View File

@ -22,7 +22,8 @@ import
./discv5_conf_builder,
./web_socket_conf_builder,
./metrics_server_conf_builder,
./rln_relay_conf_builder
./rln_relay_conf_builder,
./eligibility_conf_builder
logScope:
topics = "waku conf builder"
@ -72,6 +73,7 @@ type WakuConfBuilder* = object
rlnRelayConf*: RlnRelayConfBuilder
storeServiceConf*: StoreServiceConfBuilder
webSocketConf*: WebSocketConfBuilder
eligibilityConf*: EligibilityConfBuilder
# End conf builders
relay: Option[bool]
lightPush: Option[bool]
@ -135,6 +137,7 @@ proc init*(T: type WakuConfBuilder): WakuConfBuilder =
rlnRelayConf: RlnRelayConfBuilder.init(),
storeServiceConf: StoreServiceConfBuilder.init(),
webSocketConf: WebSocketConfBuilder.init(),
eligibilityConf: EligibilityConfBuilder.init(),
)
proc withClusterConf*(b: var WakuConfBuilder, clusterConf: ClusterConf) =
@ -473,6 +476,9 @@ proc build*(
let webSocketConf = builder.webSocketConf.build().valueOr:
return err("WebSocket Conf building failed: " & $error)
let eligibilityConf = builder.eligibilityConf.build().valueOr:
return err("Eligibility Conf building failed: " & $error)
# End - Build sub-configs
let logLevel =
@ -596,6 +602,7 @@ proc build*(
metricsServerConf: metricsServerConf,
restServerConf: restServerConf,
dnsDiscoveryConf: dnsDiscoveryConf,
eligibilityConf: eligibilityConf,
# end confs
nodeKey: nodeKey,
clusterId: clusterId,

View File

@ -27,7 +27,8 @@ import
../node/peer_manager,
../waku_core/topics/pubsub_topic,
../../tools/rln_keystore_generator/rln_keystore_generator,
../../tools/rln_db_inspector/rln_db_inspector
../../tools/rln_db_inspector/rln_db_inspector,
./conf_builder/eligibility_conf_builder
include ../waku_core/message/default_values
@ -266,8 +267,8 @@ type WakuNodeConf* = object
isRelayClient* {.
desc:
"""Set the node as a relay-client.
Set it to true for nodes that run behind a NAT or firewall and
hence would have reachability issues.""",
Set it to true for nodes that run behind a NAT or firewall and
hence would have reachability issues.""",
defaultValue: false,
name: "relay-client"
.}: bool
@ -489,12 +490,30 @@ hence would have reachability issues.""",
## Eligibility config
## Is eligibility check enabled or not
eligibilityEnabled* {.
desc:
"Enable server-side eligibility (proof-of-payment) check for light protocols: true|false",
desc: "Enable server-side eligibility (proof-of-payment) check for light protocols: true|false",
defaultValue: false,
name: "eligibility"
name: "eligibility-enabled"
.}: bool
## The expected blockchain address receiving payments for eligibility
eligibilityReceiverAddress* {.
desc: "Blockchain address receiving payment for eligibility from light protocol clients",
defaultValue: "",
name: "eligibility-receiver-address"
.}: string
## The expected amount for eligibility payments
eligibilityPaymentAmountWei* {.
desc: "The expected payment amount from light protocol clients",
defaultValue: 0,
name: "eligibility-payment-amount-wei"
.}: uint32
eligibilityEthRpcUrl* {.
desc: "HTTP address of an Ethereum client for eligibility checks. Argument may be repeated.",
defaultValue: @[EthRpcUrl("http://localhost:8540/")],
name: "eligibility-eth-client-address"
.}: seq[EthRpcUrl]
## Reliability config
reliabilityEnabled* {.
@ -1064,4 +1083,13 @@ proc toWakuConf*(n: WakuNodeConf): ConfResult[WakuConf] =
b.withRateLimits(n.rateLimits)
# Setup eligibility configuration
b.eligibilityConf.withEnabled(n.eligibilityEnabled)
if n.eligibilityReceiverAddress != "":
b.eligibilityConf.withReceiverAddress(n.eligibilityReceiverAddress)
if n.eligibilityPaymentAmountWei != 0:
b.eligibilityConf.withPaymentAmountWei(n.eligibilityPaymentAmountWei)
if n.eligibilityEthRpcUrl.len > 0:
b.eligibilityConf.withEthClientUrls(n.eligibilityEthRpcUrl.mapIt(string(it)))
return b.build()

View File

@ -109,6 +109,12 @@ proc initNode(
)
builder.withColocationLimit(conf.colocationLimit)
let eligibilityEnabled =
if conf.eligibilityConf.isSome():
conf.eligibilityConf.get().enabled
else:
false
if conf.maxRelayPeers.isSome():
let
maxRelayPeers = conf.maxRelayPeers.get()
@ -117,10 +123,12 @@ proc initNode(
relayRatio = (maxRelayPeers.float / maxConnections.float) * 100
serviceRatio = 100 - relayRatio
# FIXME: DRY
builder.withPeerManagerConfig(
maxConnections = conf.maxConnections,
relayServiceRatio = $relayRatio & ":" & $serviceRatio,
shardAware = conf.relayShardedPeerManagement,
eligibilityEnabled = eligibilityEnabled,
)
error "maxRelayPeers is deprecated. It is recommended to use relayServiceRatio instead. If relayServiceRatio is not set, it will be automatically calculated based on maxConnections and maxRelayPeers."
else:
@ -128,6 +136,7 @@ proc initNode(
maxConnections = conf.maxConnections,
relayServiceRatio = conf.relayServiceRatio,
shardAware = conf.relayShardedPeerManagement,
eligibilityEnabled = eligibilityEnabled,
)
builder.withRateLimit(conf.rateLimits)
builder.withCircuitRelay(relay)

View File

@ -58,6 +58,12 @@ type NetworkConfig* = object # TODO: make enum
extMultiAddrs*: seq[MultiAddress]
extMultiAddrsOnly*: bool
type EligibilityConf* = object
enabled*: bool
receiverAddress*: string
paymentAmountWei*: uint32
ethClientUrls*: seq[string]
## `WakuConf` is a valid configuration for a Waku node
## All information needed by a waku node should be contained
## In this object. A convenient `validate` method enables doing
@ -93,6 +99,7 @@ type WakuConf* {.requiresInit.} = ref object
restServerConf*: Option[RestServerConf]
metricsServerConf*: Option[MetricsServerConf]
webSocketConf*: Option[WebSocketConf]
eligibilityConf*: Option[EligibilityConf]
portsShift*: uint16
dnsAddrs*: bool
@ -133,35 +140,39 @@ type WakuConf* {.requiresInit.} = ref object
p2pReliability*: bool
proc logConf*(conf: WakuConf) =
proc logConf*(wakuConf: WakuConf) =
info "Configuration: Enabled protocols",
relay = conf.relay,
rlnRelay = conf.rlnRelayConf.isSome(),
store = conf.storeServiceConf.isSome(),
filter = conf.filterServiceConf.isSome(),
lightPush = conf.lightPush,
peerExchange = conf.peerExchange
relay = wakuConf.relay,
rlnRelay = wakuConf.rlnRelayConf.isSome(),
store = wakuConf.storeServiceConf.isSome(),
filter = wakuConf.filterServiceConf.isSome(),
lightPush = wakuConf.lightPush,
peerExchange = wakuConf.peerExchange
info "Configuration. Network", cluster = conf.clusterId
info "Configuration. Network", cluster = wakuConf.clusterId
for shard in conf.shards:
for shard in wakuConf.shards:
info "Configuration. Shards", shard = shard
if conf.discv5Conf.isSome():
for i in conf.discv5Conf.get().bootstrapNodes:
if wakuConf.discv5Conf.isSome():
for i in wakuConf.discv5Conf.get().bootstrapNodes:
info "Configuration. Bootstrap nodes", node = i.string
if conf.rlnRelayConf.isSome():
var rlnRelayConf = conf.rlnRelayConf.get()
if wakuConf.rlnRelayConf.isSome():
var rlnRelayConf = wakuConf.rlnRelayConf.get()
if rlnRelayConf.dynamic:
info "Configuration. Validation",
mechanism = "onchain rln",
contract = rlnRelayConf.ethContractAddress.string,
maxMessageSize = conf.maxMessageSizeBytes,
maxMessageSize = wakuConf.maxMessageSizeBytes,
rlnEpochSizeSec = rlnRelayConf.epochSizeSec,
rlnRelayUserMessageLimit = rlnRelayConf.userMessageLimit,
ethClientUrls = rlnRelayConf.ethClientUrls
if wakuConf.eligibilityConf.isSome():
let ec = wakuConf.eligibilityConf.get()
debug "eligibility: EligibilityConf created", enabled = ec.enabled, receiverAddress = $ec.receiverAddress, paymentAmountWei = ec.paymentAmountWei, ethClientUrls = ec.ethClientUrls
proc validateNodeKey(wakuConf: WakuConf): Result[void, string] =
wakuConf.nodeKey.getPublicKey().isOkOr:
return err("nodekey param is invalid")
@ -240,4 +251,17 @@ proc validate*(wakuConf: WakuConf): Result[void, string] =
?wakuConf.validateNodeKey()
?wakuConf.validateShards()
?wakuConf.validateNoEmptyStrings()
if wakuConf.eligibilityConf.isSome():
let ec = wakuConf.eligibilityConf.get()
debug "eligibility: EligibilityConf validation start"
if ec.enabled:
if not wakuConf.rlnRelayConf.isSome():
debug "eligibility: EligibilityConf validation failed - RLN relay not enabled"
return err("eligibility: RLN relay must be enabled if eligibility is enabled")
if not wakuConf.lightPush:
debug "eligibility: EligibilityConf validation failed - Lightpush not enabled"
return err("eligibility: Lightpush must be enabled if eligibility is enabled")
debug "eligibility: EligibilityConf validation successful"
return ok()

View File

@ -1097,7 +1097,7 @@ proc legacyLightpushPublish*(
except CatchableError:
return err(getCurrentExceptionMsg())
# TODO: Move to application module (e.g., wakunode2.nim)
# TODO: Move to application module (e.g, wakunode2.nim)
proc legacyLightpushPublish*(
node: WakuNode, pubsubTopic: Option[PubsubTopic], message: WakuMessage
): Future[legacy_lightpush_protocol.WakuLightPushResult[string]] {.
@ -1223,21 +1223,29 @@ proc lightpushPublish*(
debug "eligibilityManager is enabled"
var em = node.peerManager.eligibilityManager.get()
# FIXME: where should I init eligibilityManager?
# FIXME: extract values from... config parameters (which config?)
# FIXME: how to reuse EthClient from CLI arguments?
debug "initializing eligibilityManager..."
let ethClient = "https://sepolia.infura.io/v3/470c2e9a16f24057aee6660081729fb9"
em = await EligibilityManager.init(ethClient)
debug "checking eligibilityProof..."
let txNonExistent = TxHash.fromHex("0x0000000000000000000000000000000000000000000000000000000000000000")
let expectedToAddress = Address.fromHex("0xe8284Af9A5F3b0CD1334DBFaf512F09BeDA805a3")
let expectedValueWei = 30000000000000.u256
let isEligible = await em.isEligibleTxId(
eligibilityProof.get(), expectedToAddress, expectedValueWei
)
if isEligible.isErr():
let msg = "Eligibility check failed!"
#debug msg
try:
let ethClient = "https://sepolia.infura.io/v3/470c2e9a16f24057aee6660081729fb9"
em = await EligibilityManager.init(ethClient)
debug "checking eligibilityProof..."
# Check if eligibility proof is provided before accessing it
if eligibilityProof.isNone():
let msg = "Eligibility proof is required"
return lighpushErrorResult(PAYMENT_REQUIRED, msg)
#let txNonExistent = TxHash.fromHex("0x0000000000000000000000000000000000000000000000000000000000000000")
let expectedToAddress = Address.fromHex("0xe8284Af9A5F3b0CD1334DBFaf512F09BeDA805a3")
let expectedValueWei = 30000000000000.u256
let isEligible = await em.isEligibleTxId(
eligibilityProof.get(), expectedToAddress, expectedValueWei
)
except CatchableError:
let msg = "Eligibility check threw exception: " & getCurrentExceptionMsg()
return lighpushErrorResult(PAYMENT_REQUIRED, msg)
debug "Eligibility check passed!"