2024-05-21 21:03:33 +00:00
|
|
|
import
|
2024-10-25 20:59:02 +00:00
|
|
|
std/[strformat, sysrand, random, strutils, sequtils],
|
2024-05-21 21:03:33 +00:00
|
|
|
system/ansi_c,
|
|
|
|
chronicles,
|
|
|
|
chronos,
|
2024-08-21 12:54:18 +00:00
|
|
|
chronos/timer as chtimer,
|
2024-05-21 21:03:33 +00:00
|
|
|
stew/byteutils,
|
2024-07-09 11:14:28 +00:00
|
|
|
results,
|
2024-05-21 21:03:33 +00:00
|
|
|
json_serialization as js
|
|
|
|
import
|
2024-08-21 12:54:18 +00:00
|
|
|
waku/[
|
|
|
|
common/logging,
|
|
|
|
waku_node,
|
|
|
|
node/peer_manager,
|
|
|
|
waku_core,
|
|
|
|
waku_lightpush/client,
|
2024-10-25 20:59:02 +00:00
|
|
|
waku_lightpush/common,
|
2024-08-21 12:54:18 +00:00
|
|
|
common/utils/parse_size_units,
|
|
|
|
],
|
2024-05-21 21:03:33 +00:00
|
|
|
./tester_config,
|
2024-09-04 06:35:51 +00:00
|
|
|
./tester_message,
|
2024-10-25 20:59:02 +00:00
|
|
|
./lpt_metrics,
|
|
|
|
./diagnose_connections,
|
|
|
|
./service_peer_management
|
2024-05-21 21:03:33 +00:00
|
|
|
|
2024-08-21 12:54:18 +00:00
|
|
|
randomize()
|
|
|
|
|
|
|
|
type SizeRange* = tuple[min: uint64, max: uint64]
|
|
|
|
|
|
|
|
var RANDOM_PALYLOAD {.threadvar.}: seq[byte]
|
|
|
|
RANDOM_PALYLOAD = urandom(1024 * 1024)
|
|
|
|
# 1MiB of random payload to be used to extend message
|
|
|
|
|
2024-05-21 21:03:33 +00:00
|
|
|
proc prepareMessage(
|
|
|
|
sender: string,
|
|
|
|
messageIndex, numMessages: uint32,
|
|
|
|
startedAt: TimeStamp,
|
|
|
|
prevMessageAt: var Timestamp,
|
|
|
|
contentTopic: ContentTopic,
|
2024-08-21 12:54:18 +00:00
|
|
|
size: SizeRange,
|
|
|
|
): (WakuMessage, uint64) =
|
|
|
|
var renderSize = rand(size.min .. size.max)
|
2024-05-21 21:03:33 +00:00
|
|
|
let current = getNowInNanosecondTime()
|
|
|
|
let payload = ProtocolTesterMessage(
|
|
|
|
sender: sender,
|
|
|
|
index: messageIndex,
|
|
|
|
count: numMessages,
|
|
|
|
startedAt: startedAt,
|
|
|
|
sinceStart: current - startedAt,
|
|
|
|
sincePrev: current - prevMessageAt,
|
2024-08-21 12:54:18 +00:00
|
|
|
size: renderSize,
|
2024-05-21 21:03:33 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
prevMessageAt = current
|
|
|
|
|
|
|
|
let text = js.Json.encode(payload)
|
2024-08-21 12:54:18 +00:00
|
|
|
let contentPayload = toBytes(text & " \0")
|
|
|
|
|
|
|
|
if renderSize < len(contentPayload).uint64:
|
|
|
|
renderSize = len(contentPayload).uint64
|
|
|
|
|
|
|
|
let finalPayload = concat(
|
|
|
|
contentPayload, RANDOM_PALYLOAD[0 .. renderSize - len(contentPayload).uint64]
|
|
|
|
)
|
2024-05-21 21:03:33 +00:00
|
|
|
let message = WakuMessage(
|
2024-08-21 12:54:18 +00:00
|
|
|
payload: finalPayload, # content of the message
|
2024-05-21 21:03:33 +00:00
|
|
|
contentTopic: contentTopic, # content topic to publish to
|
|
|
|
ephemeral: true, # tell store nodes to not store it
|
|
|
|
timestamp: current, # current timestamp
|
|
|
|
)
|
|
|
|
|
2024-08-21 12:54:18 +00:00
|
|
|
return (message, renderSize)
|
|
|
|
|
|
|
|
var sentMessages {.threadvar.}: OrderedTable[uint32, tuple[hash: string, relayed: bool]]
|
|
|
|
var failedToSendCause {.threadvar.}: Table[string, uint32]
|
|
|
|
var failedToSendCount {.threadvar.}: uint32
|
|
|
|
var numMessagesToSend {.threadvar.}: uint32
|
|
|
|
var messagesSent {.threadvar.}: uint32
|
2024-10-25 20:59:02 +00:00
|
|
|
var noOfServicePeerSwitches {.threadvar.}: uint32
|
2024-08-21 12:54:18 +00:00
|
|
|
|
2024-10-25 20:59:02 +00:00
|
|
|
proc reportSentMessages() =
|
|
|
|
let report = catch:
|
|
|
|
"""*----------------------------------------*
|
|
|
|
| Service Peer Switches: {noOfServicePeerSwitches:>15} |
|
|
|
|
*----------------------------------------*
|
2024-08-21 12:54:18 +00:00
|
|
|
| Expected | Sent | Failed |
|
|
|
|
|{numMessagesToSend+failedToSendCount:>11} |{messagesSent:>11} |{failedToSendCount:>11} |
|
|
|
|
*----------------------------------------*""".fmt()
|
|
|
|
|
2024-10-25 20:59:02 +00:00
|
|
|
if report.isErr:
|
|
|
|
echo "Error while printing statistics"
|
|
|
|
else:
|
|
|
|
echo report.get()
|
|
|
|
|
|
|
|
echo "*--------------------------------------------------------------------------------------------------*"
|
|
|
|
echo "| Failure cause | count |"
|
|
|
|
for (cause, count) in failedToSendCause.pairs:
|
|
|
|
echo fmt"|{cause:<87}|{count:>10}|"
|
|
|
|
echo "*--------------------------------------------------------------------------------------------------*"
|
|
|
|
|
|
|
|
echo "*--------------------------------------------------------------------------------------------------*"
|
|
|
|
echo "| Index | Relayed | Hash |"
|
|
|
|
for (index, info) in sentMessages.pairs:
|
|
|
|
echo fmt"|{index+1:>10}|{info.relayed:<9}| {info.hash:<76}|"
|
|
|
|
echo "*--------------------------------------------------------------------------------------------------*"
|
|
|
|
# evere sent message hash should logged once
|
|
|
|
sentMessages.clear()
|
2024-05-21 21:03:33 +00:00
|
|
|
|
|
|
|
proc publishMessages(
|
|
|
|
wakuNode: WakuNode,
|
2024-10-25 20:59:02 +00:00
|
|
|
servicePeer: RemotePeerInfo,
|
2024-05-21 21:03:33 +00:00
|
|
|
lightpushPubsubTopic: PubsubTopic,
|
|
|
|
lightpushContentTopic: ContentTopic,
|
|
|
|
numMessages: uint32,
|
2024-08-21 12:54:18 +00:00
|
|
|
messageSizeRange: SizeRange,
|
2024-11-26 19:42:27 +00:00
|
|
|
messageInterval: Duration,
|
|
|
|
preventPeerSwitch: bool,
|
2024-05-21 21:03:33 +00:00
|
|
|
) {.async.} =
|
2024-10-25 20:59:02 +00:00
|
|
|
var actualServicePeer = servicePeer
|
2024-05-21 21:03:33 +00:00
|
|
|
let startedAt = getNowInNanosecondTime()
|
|
|
|
var prevMessageAt = startedAt
|
2024-08-21 12:54:18 +00:00
|
|
|
var renderMsgSize = messageSizeRange
|
|
|
|
# sets some default of min max message size to avoid conflict with meaningful payload size
|
|
|
|
renderMsgSize.min = max(1024.uint64, renderMsgSize.min) # do not use less than 1KB
|
|
|
|
renderMsgSize.max = max(2048.uint64, renderMsgSize.max) # minimum of max is 2KB
|
|
|
|
renderMsgSize.min = min(renderMsgSize.min, renderMsgSize.max)
|
|
|
|
renderMsgSize.max = max(renderMsgSize.min, renderMsgSize.max)
|
2024-05-21 21:03:33 +00:00
|
|
|
|
2024-10-25 20:59:02 +00:00
|
|
|
const maxFailedPush = 3
|
|
|
|
var noFailedPush = 0
|
|
|
|
var noFailedServiceNodeSwitches = 0
|
|
|
|
|
2024-05-21 21:03:33 +00:00
|
|
|
let selfPeerId = $wakuNode.switch.peerInfo.peerId
|
2024-08-21 12:54:18 +00:00
|
|
|
failedToSendCount = 0
|
|
|
|
numMessagesToSend = if numMessages == 0: uint32.high else: numMessages
|
2024-10-25 20:59:02 +00:00
|
|
|
messagesSent = 0
|
2024-08-21 12:54:18 +00:00
|
|
|
|
2024-10-25 20:59:02 +00:00
|
|
|
while messagesSent < numMessagesToSend:
|
2024-08-21 12:54:18 +00:00
|
|
|
let (message, msgSize) = prepareMessage(
|
2024-10-25 20:59:02 +00:00
|
|
|
selfPeerId,
|
|
|
|
messagesSent + 1,
|
|
|
|
numMessagesToSend,
|
|
|
|
startedAt,
|
|
|
|
prevMessageAt,
|
|
|
|
lightpushContentTopic,
|
|
|
|
renderMsgSize,
|
|
|
|
)
|
|
|
|
let wlpRes = await wakuNode.lightpushPublish(
|
|
|
|
some(lightpushPubsubTopic), message, actualServicePeer
|
2024-05-21 21:03:33 +00:00
|
|
|
)
|
|
|
|
|
2024-08-21 12:54:18 +00:00
|
|
|
let msgHash = computeMessageHash(lightpushPubsubTopic, message).to0xHex
|
|
|
|
|
2024-05-21 21:03:33 +00:00
|
|
|
if wlpRes.isOk():
|
2024-08-21 12:54:18 +00:00
|
|
|
sentMessages[messagesSent] = (hash: msgHash, relayed: true)
|
|
|
|
notice "published message using lightpush",
|
2024-10-25 20:59:02 +00:00
|
|
|
index = messagesSent + 1,
|
2024-08-21 12:54:18 +00:00
|
|
|
count = numMessagesToSend,
|
|
|
|
size = msgSize,
|
|
|
|
pubsubTopic = lightpushPubsubTopic,
|
|
|
|
hash = msgHash
|
|
|
|
inc(messagesSent)
|
2024-09-04 06:35:51 +00:00
|
|
|
lpt_publisher_sent_messages_count.inc()
|
|
|
|
lpt_publisher_sent_bytes.inc(amount = msgSize.int64)
|
2024-10-25 20:59:02 +00:00
|
|
|
if noFailedPush > 0:
|
|
|
|
noFailedPush -= 1
|
2024-05-21 21:03:33 +00:00
|
|
|
else:
|
2024-08-21 12:54:18 +00:00
|
|
|
sentMessages[messagesSent] = (hash: msgHash, relayed: false)
|
|
|
|
failedToSendCause.mgetOrPut(wlpRes.error, 1).inc()
|
|
|
|
error "failed to publish message using lightpush",
|
|
|
|
err = wlpRes.error, hash = msgHash
|
2024-05-21 21:03:33 +00:00
|
|
|
inc(failedToSendCount)
|
2024-09-04 06:35:51 +00:00
|
|
|
lpt_publisher_failed_messages_count.inc(labelValues = [wlpRes.error])
|
2024-10-25 20:59:02 +00:00
|
|
|
if not wlpRes.error.toLower().contains("dial"):
|
|
|
|
# retry sending after shorter wait
|
|
|
|
await sleepAsync(2.seconds)
|
|
|
|
continue
|
|
|
|
else:
|
|
|
|
noFailedPush += 1
|
2024-12-07 00:22:50 +00:00
|
|
|
lpt_service_peer_failure_count.inc(
|
|
|
|
labelValues = ["publisher", actualServicePeer.getAgent()]
|
|
|
|
)
|
2024-11-26 19:42:27 +00:00
|
|
|
if not preventPeerSwitch and noFailedPush > maxFailedPush:
|
2024-10-25 20:59:02 +00:00
|
|
|
info "Max push failure limit reached, Try switching peer."
|
|
|
|
let peerOpt = selectRandomServicePeer(
|
|
|
|
wakuNode.peerManager, some(actualServicePeer), WakuLightPushCodec
|
|
|
|
)
|
|
|
|
if peerOpt.isOk():
|
|
|
|
actualServicePeer = peerOpt.get()
|
2024-05-21 21:03:33 +00:00
|
|
|
|
2024-10-25 20:59:02 +00:00
|
|
|
info "New service peer in use",
|
|
|
|
codec = lightpushPubsubTopic,
|
|
|
|
peer = constructMultiaddrStr(actualServicePeer)
|
2024-05-21 21:03:33 +00:00
|
|
|
|
2024-10-25 20:59:02 +00:00
|
|
|
noFailedPush = 0
|
|
|
|
noOfServicePeerSwitches += 1
|
|
|
|
lpt_change_service_peer_count.inc(labelValues = ["publisher"])
|
|
|
|
continue # try again with new peer without delay
|
|
|
|
else:
|
|
|
|
error "Failed to find new service peer. Exiting."
|
|
|
|
noFailedServiceNodeSwitches += 1
|
|
|
|
break
|
2024-05-21 21:03:33 +00:00
|
|
|
|
2024-11-26 19:42:27 +00:00
|
|
|
await sleepAsync(messageInterval)
|
2024-05-21 21:03:33 +00:00
|
|
|
|
2024-10-25 20:59:02 +00:00
|
|
|
proc setupAndPublish*(
|
|
|
|
wakuNode: WakuNode, conf: LiteProtocolTesterConf, servicePeer: RemotePeerInfo
|
|
|
|
) =
|
2024-05-21 21:03:33 +00:00
|
|
|
if isNil(wakuNode.wakuLightpushClient):
|
2024-10-25 20:59:02 +00:00
|
|
|
# if we have not yet initialized lightpush client, then do it as the only way we can get here is
|
|
|
|
# by having a service peer discovered.
|
|
|
|
wakuNode.mountLightPushClient()
|
2024-05-21 21:03:33 +00:00
|
|
|
|
|
|
|
# give some time to receiver side to set up
|
2024-08-21 12:54:18 +00:00
|
|
|
let waitTillStartTesting = conf.startPublishingAfter.seconds
|
|
|
|
|
|
|
|
let parsedMinMsgSize = parseMsgSize(conf.minTestMessageSize).valueOr:
|
|
|
|
error "failed to parse 'min-test-msg-size' param: ", error = error
|
|
|
|
return
|
|
|
|
|
|
|
|
let parsedMaxMsgSize = parseMsgSize(conf.maxTestMessageSize).valueOr:
|
|
|
|
error "failed to parse 'max-test-msg-size' param: ", error = error
|
|
|
|
return
|
2024-05-21 21:03:33 +00:00
|
|
|
|
|
|
|
info "Sending test messages in", wait = waitTillStartTesting
|
|
|
|
waitFor sleepAsync(waitTillStartTesting)
|
|
|
|
|
|
|
|
info "Start sending messages to service node using lightpush"
|
|
|
|
|
2024-08-21 12:54:18 +00:00
|
|
|
sentMessages.sort(system.cmp)
|
2024-10-25 20:59:02 +00:00
|
|
|
|
|
|
|
let interval = secs(60)
|
|
|
|
var printStats: CallbackFunc
|
|
|
|
|
|
|
|
printStats = CallbackFunc(
|
|
|
|
proc(udata: pointer) {.gcsafe.} =
|
|
|
|
reportSentMessages()
|
|
|
|
|
|
|
|
if messagesSent >= numMessagesToSend:
|
|
|
|
info "All messages are sent. Exiting."
|
|
|
|
|
|
|
|
## for gracefull shutdown through signal hooks
|
|
|
|
discard c_raise(ansi_c.SIGTERM)
|
|
|
|
else:
|
|
|
|
discard setTimer(Moment.fromNow(interval), printStats)
|
|
|
|
)
|
|
|
|
|
|
|
|
discard setTimer(Moment.fromNow(interval), printStats)
|
|
|
|
|
2024-05-21 21:03:33 +00:00
|
|
|
# Start maintaining subscription
|
|
|
|
asyncSpawn publishMessages(
|
|
|
|
wakuNode,
|
2024-10-25 20:59:02 +00:00
|
|
|
servicePeer,
|
2024-05-21 21:03:33 +00:00
|
|
|
conf.pubsubTopics[0],
|
|
|
|
conf.contentTopics[0],
|
|
|
|
conf.numMessages,
|
2024-08-21 12:54:18 +00:00
|
|
|
(min: parsedMinMsgSize, max: parsedMaxMsgSize),
|
2024-11-26 19:42:27 +00:00
|
|
|
conf.messageInterval.milliseconds,
|
|
|
|
conf.fixedServicePeer,
|
2024-05-21 21:03:33 +00:00
|
|
|
)
|