Open sherlock-admin3 opened 5 months ago
1 comment(s) were left on this issue during the judging contest.
infect3d commented:
this is exactly the
_firstFinishDrawTargetFraction
goal
The suggested mitigation is flawed since it would return the timestamp that the RNG was generated on the Witnet network, not the timestamp that it was relayed to the requesting network.
The auction should be set with an appropriate _firstFinishDrawTargetFraction
_auctionTargetTime
value such that the estimated auction value is reached after the 5-10 min expected relay time. In addition, the maxRewards
parameter on the DrawManager
contract helps to ensure a reasonable max limit to the total auction payout in the case of an unexpected outage.
@berndartmueller Could you take a look at the above comment? Does an appropriately admin set _firstFinishDrawTargetFraction
mitigate this issue?
Hey @nevillehuang!
The suggested mitigation is flawed since it would return the timestamp that the RNG was generated on the Witnet network, not the timestamp that it was relayed to the requesting network.
This statement is mostly true, but, the timestamp can also be the block.timestamp
at the time when the RNG request was relayed to the requesting network. Specifically, when reporters use the reportResult
function, the timestamp is set to block.timestamp
.
If reporters use one of the two other reporting methods, the timestamp is set to the time when the RNG was generated on the Witnet network (although it's not verified that reporters set this value exactly, the timestamp could also be set to block.timestamp
). To be fair, on-chain Optimism transactions to the Witnet contract show that the reportResultBatch
function is predominantly used.
However, reporters are free to use whatever relaying function they use, so it's possible that the timestamp is the time when the request was made available to the requesting network. Either way, IMO, this makes it a better approximation of when the random number is available to PoolTogether's finishDraw
, than using the time when startDraw
was called and the RNG requested (which neglects the time it needs for Witnet actors to relay the request to the Witnet chain, generate the random number and relay it back to the requesting chain)
_The auction should be set with an appropriate firstFinishDrawTargetFraction value such that the estimated auction value is reached after the 5-10 min expected relay time. In addition, the maxRewards parameter on the DrawManager contract helps to ensure a reasonable max limit to the total auction payout in the case of an unexpected outage.
_firstFinishDrawTargetFraction
is provided once in the constructor
and stored in lastFinishDrawFraction
. And lastFinishDrawFraction
is updated in every finishDraw
call. While it's true that this value is used to better estimate the expected rewards and the relay time, it's also not perfect. A single occurrence of a longer than usual RNG relay time is enough to set lastFinishDrawFraction
to an outlier value (i.e., inflated value, but upper-capped by maxRewards
).
Long story short, using the recommendation in this submission, in conjunction with providing a reasonable _firstFinishDrawTargetFraction
value by the admin, further improves the accuracy of the rewards calculation (i.e., prevents overpaying).
Removed the "disputed" tag since this seems like a valid issue for the auctions, but may not need additional mitigation.
I realized I quoted the wrong parameter in my statement above:
The auction should be set with an appropriate _firstFinishDrawTargetFraction value such that the estimated auction value is reached after the 5-10 min expected relay time.
I meant to say _auctionTargetTime
here instead of _firstFinishDrawTargetFraction
.
berndartmueller
medium
The RNG finish draw auction rewards are overpaid due to missing to account for the time it takes to fulfill the Witnet randomness request
Summary
The rewards for finishing the draw and submitting the previously requested randomness result are slightly overpaid due to the incorrect calculation of the elapsed auction time, wrongly including the time it takes to fulfill the Witnet randomness request.
Vulnerability Detail
The
DrawManager.finishDraw
function calculates the rewards for finishing the draw, i.e., providing the previously requested randomness result, via the_computeFinishDrawReward
function in lines338-342
. The calculation is based on the difference between the timestamp when the start draw auction was closed (startDrawAuction.closedAt
) and the current block timestamp. Specifically, a parabolic fractional dutch auction (PFDA) is used to incentivize shorter auction durations, resulting in faster randomness submissions.However, the requested randomness result at block X is not immediately available to be used in the
finishDraw
function. According to the Witnet documentation, the randomness result is available after 5-10 minutes. This time delay is currently included when determining the elapsed auction time becausestartDrawAuction.closedAt
marks the time when the randomness request was made, not when the result was available to be submitted.Consequently, the protocol always overpays rewards for the finish draw. Over the course of many draws, this can lead to a significant overpayment of rewards.
It should be noted that the PoolTogether documentation about the draw auction timing clearly outlines the timeline for the randomness auction and states that finish draw auction price will rise once the random number is available:
Impact
The protocol overpays rewards for the finish draw.
Code Snippet
pt-v5-draw-manager/src/DrawManager.sol#L339
Tool used
Manual Review
Recommendation
Consider determining the auction start for the finish draw reward calculation based on the timestamp when the randomness result was made available. This timestamp is accessible by using the
WitnetRandomnessV2.fetchRandomnessAfterProof
function (instead offetchRandomnessAfter
). The_witnetResultTimestamp
return value can be used to better approximate the auction start, hence, paying out rewards more accurately.