Manuel Polzhofer
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
      • No invitee
    • Publish Note

      Publish Note

      Everyone on the web can find and read all notes of this public team.
      Once published, notes can be searched and viewed by anyone online.
      See published notes
      Please check the box to agree to the Community Guidelines.
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
No invitee
Publish Note

Publish Note

Everyone on the web can find and read all notes of this public team.
Once published, notes can be searched and viewed by anyone online.
See published notes
Please check the box to agree to the Community Guidelines.
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
# Internal Security Review: Drips Contract v2.0 Author: `Manuel Polzhofer` Date: `November 2022` ### Scope The following security review focuses on potential bugs and security issues in the new features and changes to the **Drips Solidity Contracts** since July 2022. In addition, the review aims to identify features that have introduced complexity and discuss their design and underlying requirements. The Drips SDK library is not within the scope of this document. ### Drips Contracts Repository Repository: https://github.com/radicle-dev/drips-contracts Commit Hash: [835656a99015e3cc28ee1003924654e5071f3d00](https://github.com/radicle-dev/drips-contracts/commit/835656a99015e3cc28ee1003924654e5071f3d00) ## Minor ### M.1. Caller batchTransaction doesn't verify msg.value sum The caller contract allows to batch multiple different transaction together. Each of them can include a `msg.value` to send ETH to a contract. ```solidity returnData[i] = _call(sender, call.to, call.data, call.value); ``` The ETH value of a indiviual batch call is passed as a parameter with `call.value`. However, the sum of all `call.value` is not verfied against the initial `msg.value` sent to the contract itself. In case the caller contract holds any ETH a user could send less ETH to the contract and use the ETH locked in the caller contract for a transaction. #### Recommendation Verify that the sum of the individual `call.value` equals `msg.value`. For a clean implementation fo the batch transaction pattern. As an alternative we could check the pre-state eth balance of the contract and compare it with the post-state ETH balance. ### M.2. Caller callAs authorize pattern gives permission to add other authorized addresses A user can give another user the permission to act in behalf of them. However, this user also gets the permission to authorize other user again. ## Informal ### I.1. _squeezedAmt receiver binary search doesn't need be performed on-chain In the squeezing algorithm as sender configuration is passed to drips contract in `calldata`. A sender can stream to multiple different userId's and one or multiple of them stream to receiver which should get squeezed. Currently to find the squeeze-receiver a binary search is applied on the sorted sender-configuration. ```solidity= // binary search in squeezed DripsReceiver[] memory receivers = dripsHistory.receivers; // Binary search for the `idx` of the first occurrence of `userId` uint256 idx = 0; for (uint256 idxCap = receivers.length; idx < idxCap;) { uint256 idxMid = (idx + idxCap) / 2; if (receivers[idxMid].userId < userId) { idx = idxMid + 1; } else { idxCap = idxMid; } } ``` #### Recommendation The `indexes` of the receiver in the sender array could be passed as a parameter to safe gas. ### I.2. Simplification of squeezeDrips and squeezeDripsResult The function `squeezeDripsResult` performs the squeezing algorithm without modifying the state. This function is used by `squeezeDrips` which modifies the state afterwards. However, it results that an array with the `squeezedIdxs` needs to be generated and passed arround. The length of the array can be more than needed. #### Recommendation One function which peforms the `squeezeDrips` algorithm and modifies the state to avoid passing an array around. A seperate `view` function not used by `squeezeDrips` could be added. This would reduce the gas cost and reduce the complexity of the code which acutally modifies the state. ### I.3. ERC20 Rebase-Token break Drips logic It is important to point out that the current Drips implementation doesn't support any rebase-token. A rebase token is a token in which the balance of a user can increase or decrease over time. **Example** 1. A user deposits a 100 tokens into Drips 2. The token balance increases to actual 120 tokens 3. The user can only withdraw 100 tokens instead of 120 For most common rebase token a wrapped version with a fixed balance of tokens exists. The fixed balance represents a share in the underlying rebase token. **Recommendation** Add rebase limitation to documentation and code comments. Like in description of the `erc20` parameter addresses. ### I.4. Unused imports in the source code In `ImmutableDrips` ```solidity import { ERC721, ERC721Burnable } from "openzeppelin-contracts/token/ERC721/extensions/ERC721Burnable.sol"; ``` ### I.5. Remove Upgradeablity from ImmutableSplitsDriver The ImmutableSplitsDriver only has some immutable variables to point to other drips contracts and no additional state. The upgradablity feature could be removed and the contract could be just redeployed in case the address of the drips contract changes. It would also add more trust to the `immutable` feature, since an upgrade could potentially allow the change the splits configurations again. ### Gas Improvements #### G.1. use custom error types instead of revert messages Usage of custom Solidity error types instead of string revert message can reduce gas costs. #### G.2. allow passing lower and upper hint block.timestamps for binary search to find `maxEnd` Currently, finding the `maxEnd` needs to be performed on-chain because the block.timestamp of the transaction is unkown in advance. However, it would be possible to pass a lower and upper value for the search, which could be the exact `maxEnd` in some cases. In other cases a mint time assumption could be made. Only if the `maxEnd` is not inbetween lower and upper limit. #### G.3. nextSqueezing using a mapping instead of an array in Drips.sol A common pattern in Solidity is to use a mapping instead of a storage array. An array over the entire storage space is also a potential `security risk`. If the user could choose the `currCycleConfigs` as a parameter we would give them full storage write access to write a block.timestamp. Currently, it is not such a problem because `currCycleConfigs` is autoincrement and the next interesting storage slot should be too far away. The gas-cost are too high to perform such an attack with different cycleConfigs. However, with a mapping which results in a hashing it is too computation intensive to find a index for an interesting slot. **Currently** ```solidity mapping(uint256 => uint32[2 ** 32]) nextSqueezed; ``` **Recommentation** ```solidity mapping(uint256 => mapping(uint256 => uint32)) nextSqueezed; ``` **Array** [PASS] testSqueezeDrips() (gas: 257887) **Mapping** [PASS] testSqueezeDrips() (gas: 257810) I tested the change and all tests are passing. ### Architecture Discussion #### 1. Caller Pattern with self-calling trustedForwarder For enabling meta-transactions in `drips` a ERC712 scheme has been implemented in a `Caller` contract. **Meta Transaction** This feature allows any address to act in behalf of any user if a signed message by the user is provided. After a signature verification for a concrete action the call will be forwarded. Each interaction can only happens once afterwards the signature for a specific action is invalid. A sender signs a message with indicates which function should be called in behalf of them. The `caller` contracts verifies the signature and if correct attaches the message signer address in `calldata`. Other contracts like the `AddressDriver` or `NFTDriver` use the `Caller` contract as so called `TrustedForwarder`. The address passed by the `Caller` will be treated like a `msg.sender` from other calls. **callAs Pattern** However, the `Caller` contract itself treates itself as a `TrustedForwarder` for recursive self-calls. This happens because the `Caller` contract manages some additional state for so called `callAs` pattern. In the `callAs` pattern a user can give the permission to an Ethereum address to act in behalf of him. For the `callAs` pattern user gives the full power to one specific address. An user give permission by calling the `Caller` directly or by using the meta transaction feature. **Batch Transaction** TODO **Complex Scenario** The `callAs`, `MetaTransaction` and `BatchTransaction` used together allow to generate complex secenarios. Consider the following: Alice gave Bob a `callAs` permission to act in behalf of her. Bob wants to make a batch transaction as meta transaction. Therefore he signs a message to **callAs** which should call `callBatched** **Call-Stack** 1. **Charly** calls **Caller.callSigned** (`_msgSender() == Charly`) 2. **Caller.callSigned** calls **Caller.calledAs** (`_msgSender()==Bob`) 3. **Caller.calledAs** calls **Caller.callBatched** (`_msgSender()==Alice`) 4. **Caller.callBatched** calls **AddressDriver.setDrips**(`_msgSender() == Alice`) **Discussion** - Considering if the `calledAs` feature is really needed or it can be removed to reduce the complexity. - A `callAs` pattern could be always later implemented on top by other parties if really needed. - In many cases a multi-sig with one signature approval might used instead. #### 2. Squeezing Complexity / Drips Complexity The supported features in Drips allow an immense varity of different cases in the contracts. I want to provide some examples to ilustrate complexity of and permutation of different scenarios of squeezing the contractsneed to handle accordingly. - squeeze a drips which **started in the the current cycle** - squeeze a drips which **starts in the future** - squeeze a drips which **ended in the the current cycle** - squeeze a drips which **will end in a future cycle** - multiple **different sender configs in one cycle** - multiple **different squeeze combinations of one cycle** - regular drip **ends before squeeze happens** - **overlapping drip from previous cycle** - drips end because **maxEnd reached** (duration=0 case) - drip started with **current block.timestamp** (start=0) - **maxEnd** in curr Cycle **before squeezing** - **maxEnd** in currCycle **after squeezing** - **not using all drips from dripsHistory** in squeezing - **holes in squeezing** (drips end in curr cycle with maxEnd and normal end) and start a new one after some time in same cycle - **multiple squeezes** after more time has passed - scheduled**drips in the future** and remove it again - calling squeeze in edgeCases like **beginning of a cycle** - calling squeezing with a sender which **doesn't include the receiver** - **same receiver multiple times** in the sender config (with all combinations, start=0, duration=0, etc) # Unstructed Notes ### Questions Why do we allow multiple entries of the same stream in one receiver config? - What is the point of it, is it not additional code complexity? -`_verifyDripsHistory` iterates the drips once to verify and then again for the squeeze algorithm - squeeze algorithm starts from the end to pass the previous updateTime. It could start from the beginning and look one element ahead to get the updateTime - squeezeDripsResult view function, can it be simplified. Removal of passing around the array with the squeezeIdx's. - What are good on-chain invariant which could be added as requires in the contracts to enfore correctness? - Ideas: `history.length < senderDripsConfigs + 1` => previous configs which have no impact on the current cycle - inconsistence with return values in different function.

Import from clipboard

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template is not available.
Upgrade
All
  • All
  • Team
No template found.

Create custom template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

How to use Slide mode

API Docs

Edit in VSCode

Install browser extension

Get in Touch

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Upgrade to Prime Plan

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

No updates to save
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Upgrade

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Upgrade

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully