Closed nix1g closed 3 years ago
@nix1g thanks for the review, sorry that it took me a while to answer. Still work in progress but I just pushed most of the changes here
L143: verify that the dispute exists as well.
In some cases this might be important, but I'll rather delegate that responsibility to the user. Also, AFAIK we don't check that in none of our contracts.
require
: I cut error messages down to 32 bytes and merged some of them into one. I'm not sure about removing them entirely.Here you can see the gas difference of using the old and the new binary library (with the escrow mock). Look at batch withdrawals, that's crazy! The extra gas needed in rule() goes away if I use a mapping instead of an array for rounds.
Extra: I changed all the constants from public to private. I don't think we need getters for those.
BinaryArbitrable.sol and MultiOutcome.sol are close, it would be great if they were consistent (make a diff).
What do you mean with "make a diff"?
Lastly, there is no test file for this library.
I left some comments on the commit (also you can reference an issue in a commit message), everything else is OK.
totalFeesPaid > 0
and paidFees > 0
are false only if round.feeRewards == 0
, in which case there is nothing to withdraw anyway so a div-by-zero exception here is harmless. I would remove the if
/?:
altogether.
IMO the ideal alternative is to bypass solidity's div-by-zero check to get the same behavior (since the DIV
opcode returns 0
when the divisor is 0
) but that requires (very little) assembly so I did not propose.NON_PAYABLE_AMOUNT
my badUsing a mapping instead of an array for DisputeData.rounds
is a good idea as in most cases there is only one round, so storing the last index instead of the length saves gas in this case.
Extra: for an internal library there must be no public
or external
so yeah good to remove.
BinaryArbitrable.sol and MultiOutcome.sol are close, it would be great if they were consistent (make a diff).
What do you mean with "make a diff"?
To check the differences between two files, you can compare them using diff
. That way you can easily spot discrepancies and reviewers using this tool have a clear understanding of the difference between files. Check out the wiki, the man or this online tool.
Lastly, there is no test file for this library.
I must be blind :see_no_evil:
The condition
totalFeesPaid > 0
andpaidFees > 0
are false only ifround.feeRewards == 0
, in which case there is nothing to withdraw anyway so a div-by-zero exception here is harmless
The problem with this is that it affects batch withdrawing as well and the exception wouldn't be ok there. I couldn't think of a way to make this simpler.
To check the differences between two files, you can compare them using diff. That way you can easily spot discrepancies and reviewers using this tool have a clear understanding of the difference between files. Check out the wiki, the man or this online tool.
Ah I see now. I'll give it a try.
Pre-Review @ a403fa5
General comments
This library will likely be used by many smart contracts, so gas optimization is particularly important.
By making this a regular inheritable smart contract rather than a library, some functions could be exposed by default, such assubmitEvidence()
,withdrawFeesAndRewards()
, getters... while still being overwritable.There is little practical difference between internal libraries and inheritable contracts. I think libraries should be reserved for extending existing functionalities (likeCappedMath
) and inheritable contracts should be used for adding new features (like a new struct with its own functionalities in this case).BinaryArbitrable.sol
I did not find any vulnerability.
Bug (harmless)
L143: verify that the dispute exists as well.
Gas optimization
Storage
The
DisputeData
structure does not needstatus
as this can be inferred by other means. I tested this (see this patch) and got an overall reduction in gas consumption, especially increateDispute
(20000 gas).First,
Status.None
is simplyrounds.length == 0
. Then, to differentiate betweenStatus.Disputed
andStatus.Resolved
we need to change the defintion ofruling
:Status.Disputed
orStatus.None
Status.Resolved
Status.Resolved
Status.Resolved
require
Messages in
require
s should be (strictly) less than 32 bytes long. If they can be removed, that's even better.See L145, L162, L204.
Using local variables
Solidity's optimizer is bad at reducing access to storage when using deep structures. In consequence:
round
) consumes gas (negligeable amount though).The following variables should thus be cached on the stack:
L156:
dispute.rounds.length - 1
,round.rulingFunded
,round.paidFees[_ruling]
L300:dispute.ruling
L315:round.feeRewards
L322:round.paidFees[dispute.ruling]
Miscellaneous
_ruling <= AMOUNT_OF_CHOICES
, so it can be removed from therequire
subCap
asround.paidFees[_ruling] >= totalCost >= appealCost
_getWithdrawableAmount()
that takesdispute
as a storage parameter rather than its ID to save 250 gaspaidFees
can only be 0 ifappealCost
is 0, in which casefeeRewards
is also 0if
byelse if
(saves 853 gas)Other
subCap
has an effectnotblock the contract"MultiOutcomeArbitrable.sol
-1
rather than-2
(this was discussed for the realitio proxy already, there are 2^256 voting options including invalid, so that's 2^256-1 valid options the arbitrable needs to declare)v0.8
, the preferred syntax is nowtype(uint256).max - 2
README.md
GitHub has support for Solidity in Markdown, you can tag code blocks with
```solidity
rather than```js
.