Open hats-bug-reporter[bot] opened 1 year ago
Hey
Nice find
However I'd like to point out that the proposed mitigation is missing an important step as totalBondsToConfiscate
is not being reset to 0
after the executeResolution
transfer the funds to the expert committee.
Please find below the revised mitigation:
function executeResolution(
IHATClaimsManager _vault,
bytes32 _claimId
)
...
_vault.approveClaim(
_claimId,
resolution.bountyPercentage,
resolution.beneficiary
);
+ token.safeTransfer(expertCommittee, totalBondsToConfiscate);
+ totalBondsToConfiscate = 0;
emit ResolutionExecuted(_vault, _claimId);
}
Hey, thank you
I had put the reset to zero inside _confiscateDisputers()
but that is weird and I prefer you suggestion. In that case, one needs also to reset the value to 0 inside dismissResolution()
.
function dismissResolution(
IHATClaimsManager _vault,
bytes32 _claimId
)
external
onlyChallengedActiveClaim(_vault, _claimId)
onlyResolvedDispute(_vault, _claimId)
{
...
_vault.dismissClaim(_claimId);
+ token.safeTransfer(dismissedResolutionBondsReceiver, totalBondsOnClaim[_vault][_claimId]);
+ totalBondsToConfiscate = 0;
emit ResolutionDismissed(_vault, _claimId);
}
Now that I look again at the recommendation, I notice that totalBondsToConfiscate
could mix up between vaults.
In the recommendation all totalBondsToConfiscate
should be replaced by a mapping totalBondsToConfiscate[_vault]
.
Mapping by _vault
should be enough since vaults only treat one _claimId
at a time, but totalBondsToConfiscate[_vault][_claimId]
can be used as well if preferred.
So this is a very interesting discussion. The current implementation was by design, so I would not categorize this as a bug in the code. But you are making a good point about the validity fo the design. We'll discuss further internally
So although we see your point, we decided to stick to the design we have now.
We have several reasons
Github username: @bahurum Submission hash (on-chain): 0x1398bad727b98575f754097c690fc0bd1e63666ab475956b31251410b77bb25f Severity: medium
Description: Description\ When a resolution is dismissed by the court, bonds are sent to the expert committee and refunded to some disputers even if they shouldn't be since expert committee and disputers were proven wrong by the court ruling.
Attack Scenario
acceptDispute()
with Alice's address in_disputersToRefund
and Bob's address in_disputersToConfiscate
. Bob's bonds go to the expert committeedismissResolution()
.reclaimBond()
to get the bonds for her dispute refunded despite being wrong in disputing the claimIn this scenario both Alice and the Expert committee are wrong (final court judgement is against them) and yet Alice gets her bonds refunded and the Expert committee gains Bob's bonds.
Recommendation\ The correct behavior would be to send the bonds for the dispute to a third party (for example the Hats governance) in the case the court dismisses the expert committee's resolution.
To do this,
_confiscateDisputers()
should not send the confiscated tokens to the expert committee inacceptDispute()
. DefinetotalBondsToConfiscate
as a state variable to be able to confiscate the tokens later in case the court executes the expert committee's resolution.Refunds should wait until the final decision of the court to be available.
If the expert committee's resolution is executed by the court, send the
totalBondsToConfiscate
amount stored to the expert committee.If the resolution is dismissed, the bonds should be sent to the chosen third party
dismissedResolutionBondsReceiver