Organization
- @impossiblecloud
Engagement Type
Cantina Reviews
Period
-
Repositories
Researchers
Findings
High Risk
1 findings
1 fixed
0 acknowledged
Medium Risk
2 findings
2 fixed
0 acknowledged
Low Risk
3 findings
3 fixed
0 acknowledged
Informational
3 findings
1 fixed
2 acknowledged
Gas Optimizations
1 findings
1 fixed
0 acknowledged
High Risk1 finding
ScalerNode can be removed before its utilized capacity is reset
State
- Fixed
PR #275
Severity
- Severity: High
Submitted by
Joran Honig
Description
Users can create bookings for resources on ScalerNodes. The
BookingManager
is responsible for updating the utilized capacity trackers for the region, cluster and hardware provider for the node that's being booked/re-booked. As a booking expires the updates to these trackers are not updated immediately, instead a call toexpireCapacity()
needs to be made which will perform the desired operations.Currently a node can be removed by a call to
removeScalerNode()
onICNRegistry
which does not check whether the capacity for the node is still marked as utilized. After removing the node it will be impossible to revert the utilized capacity updates that were made in creating the last booking for the removed node.As a result various computations around protocol rewards will be inaccurate since they'll operate with inaccurate utilized capacity numbers.
Recommendation
Adjust the implementation of
removeScalerNode()
with a check that ensures a node can only be removed if there is no booking that still needs to be expired.
Medium Risk2 findings
Users are not guarded against price increase in the extendBooking path
Description
Users are guarded against unexpected price increases in the
bookCapacity
function by specifying a maxBookingPrice. But this guard is not present in theextendBooking
function where similar similar issue can occurRecommendation
Add a similar guard in
extendBooking
Link token ids must be smaller than the max uint32 value
State
- Fixed
PR #80
Severity
- Severity: Medium
Submitted by
Joran Honig
Description
The modules supporting link staking and reward claiming assume that link token ids have a value lower than the max uint32 value.
Currently the ICN link contract supports minting tokens that do not satisfy this constraint, as a result governance might accidentally mint tokens that are incompatible with the modules.
Recommendation
Introduce a require check that ensures that _mint only succeeds for tokens that have valid token ids.
Low Risk3 findings
Checkpoints may be increased even when reward amount is 0 due to rounding
Description
The
batchInitiateDelegationRewardsClaim
function only checks that the sum of_unclaimedRewards
has to be non-zero. This allows individual claims to have 0 as the reward amount due to rounding in which case the checkpoints are still incremented. This can cause users to lose dust amount of rewards continuouslyRecommendation
Revert in case a single
_claimAmount
is 0Incorrect index is emitted in DelegationCreated event
Description
The
DelegationCreated
event is supposed to emit the index of the locked delegation rather than the node delegation's index. But the implementation currently emits the index of the nodeDelegation/// @notice Emitted when a delegation is created. /// @param nodeId The ID of the node /// @param delegator The address of the delegator /// @param lockedDelegationIndex The index of the locked delegation /// @param apyScalingFactor The APY scaling factor for the delegation /// @param unlockTimestamp The timestamp when the collateral can be unlocked /// @param amount The amount of collateral delegated event DelegationCreated( uint256 indexed nodeId, address indexed delegator, uint256 indexed lockedDelegationIndex, uint256 apyScalingFactor, uint256 unlockTimestamp, uint256 amount );
Recommendation
Emit the UserDelegation index
The store32 library function should ensure input validity
State
- Fixed
PR #273
Severity
- Severity: Low
Submitted by
Joran Honig
Description
The
store32()
function is used to efficiently store an array of integers in storage.There is a notable difference between the input type and output type. Namely the storage array is for
uint32
and the input array containsuint256
. It functions correctly if all numbers in the input array are smaller thantype(uint32).max
. However, the functions' behavior becomes undefined if the input has larger integers.Since the parameter types allow
uint256
the function should function under all potential values of that type.Recommendation
Introducing a require ensuring the input values are lower than the maximum
uint32
value or changing the input type to an array ofuint32
would ensure the function has no undefined or inaccurate behavior.
Informational3 findings
Limited domain isolation between modules
State
- Acknowledged
Severity
- Severity: Informational
Submitted by
Joran Honig
Description
The architecture of ICN is aimed at having a single upgradable proxy contract that has multiple modules responsible for parts of the protocol. As expected there are some dependencies between modules. For example, the
BookingManager
needs to know if a node is bookable and will depend on theICNRegistry
module for that.Each module is accompanied by a storage contract which provides an interface to the storage domain for that module. Currently most cross-module operations will perform direct look-ups and mutations to the storage of other modules.
This creates a situation where the responsibilities and dependencies between modules can be more opaque. For example, the
BookingManager
is responsible for recording the utilized capacity of resources otherwise managed by theICNRegistry
. In bug #1 we see that this leads to a potential misaccounting of capacities sinceICNRegistry
does not account for utilized capacity.Recommendation
It is worth exploring the introduction of internal functions to storage contracts that wrap external reads and writes to make cross-module dependencies explicit. This will not prevent bugs but can help with code understanding.
Inconsistency in the reward formula b/w implementation and documentation
Description
According to the documentation rewards should be distributed as
where
- $x_{n_i}(t)$ is the total capacity of the node $n_i$ at the time $t$.
But the implementation uses
targetCapacity
in the denominator rather than ∑$x_{n_i}(t)$Recommendation
Correct the documentation
Introduce Fuzz Testing and Testing Enhancements
State
- Acknowledged
Severity
- Severity: Informational
Submitted by
Joran Honig
Description
The ICN codebase has several aspects that are quite amenable to fuzz testing and (bounded) formal verification.
The following are some example areas:
- bitmap implementation used for efficient batch staking and initial rewards computation
- complex arithmetic operations
- library implementation for array copies
testability
To make introducing fuzz testing easier it can help to isolate (more) pure logic in libraries. This also aids in building a regular test suite and scaling the codebase as it grows in complexity.
For example, the logic that structures the link initial reward bitmap and the link staking bitmap implements is roughly equivalent and generalizable. Though the operations are relatively straightforward, it is nice to be able to test such low level operations directly. Implementing a bitmap library that efficiently supports single & batch operations can greatly simplify the link modules while simultaneously allowing for extensive unit tests and techniques such as fuzzing.
fuzzing invariants
The areas of interest noted above can be tested in a stateless fashion, this is generally easier both on the developer and the fuzzing tool.
The following are example invariants that you might consider introducing fuzz tests for:
- given two differing id's in the bitmap domain (e.g. link ids):
- marking the first will only work if it is not marked.
- marking only the first will not set the second as marked.
- given any list of integers the
store32
function will populate a list in storage where for every index the original array and storage array will have the same value.
stateful fuzzing
There is also an opportunity for stateful fuzzing.
For example bug #1 might have been discovered by the following invariant:
The utilized capacity for a region/hp/cluster is equal to the sum of recorded utilized capacity of those nodes that belong to the region/hp/cluster.
Similar invariants might be formulated for other aspects of cross module accounting.
Note that stateful fuzzing requires a more complex setup, so we recommend focussing on the invariants above first.
Gas Optimizations1 finding
Node removal always swaps nodes
State
- Fixed
PR #269
Severity
- Severity: Gas optimization
Submitted by
Joran Honig
Description
Swapping the final ScalerNode with the to-be-deleted node should only be necessary when the to be deleted node is not the last node in the array.