Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ControlledAccessCrowdsale contract #812

Closed
wants to merge 5 commits into from

Conversation

PhABC
Copy link
Contributor

@PhABC PhABC commented Mar 13, 2018

🚀 Description

This contract provides a modifier that only allow users possessing an "access message", which has to be provided by the owner of the contract. This is effectively an off-chain whitelist with on-chain verification. More information on this approach can be found on a blog post I wrote on the topic.

I added this contract in the crowdsale/validation directory and added the Crowdsale function calls, but I think access control contract (e.g. whitelists, off-chain messages, etc.) should be separated from crowdsale contracts, since they could be used in contracts other than crowdsale. A WhitelistedCrowdsale could easily use a Whitelist contract, just as with other type of smart contracts (e.g. contracts with access only for small group of users). An independent issue will be made for this latter point (edit : see #813).

Note that the fallback function and buyTokens function from the Crowdsale.sol contract have been overwritten/overloaded since a signed message is required. msg.data is used when using the fallback function where msg.data is the signed message.

  • 📘 I've reviewed the OpenZeppelin Contributor Guidelines
  • ✅ I've added tests where applicable to test my new functionality.
  • 📖 I've made sure that my contracts are well-documented.
  • 🎨 I've run the JS/Solidity linters and fixed any issues (npm run lint:all:fix).

@shrugs
Copy link
Contributor

shrugs commented Mar 13, 2018

Very into this technique 👍 will do an actual review in the near future

@frangio frangio requested a review from fiiiu March 21, 2018 14:46
* @param _beneficiary Address performing the token purchase
* @param _sig Access message to provide with purchase.
*/
function buyTokens(address _beneficiary, bytes _sig) public payable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

giving this contract extends from Crowdsale we will still have the inherited buyTokens public function which can be called to bypass the signature check, am I right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notice that there is no need to override this function, in fact there is comment mentioning no to do that 😛
I think you just need to override the _preValidatePurchase function to use your modifier with msg.data, am I right?

Copy link
Contributor Author

@PhABC PhABC Apr 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, the problem with not overwriting buyTokens(_beneficiary) is that you can't call buyTokens(_beneficiary) with additionnal msg.data. Data will either be ignored or the function will throw for invalid number of arguments (I believe).

What this means is that it would only be possible to buy tokens via the fallback function, where buyer would be msg.sender. With this, msg.data can be the signature and allow the existing fallback and buyTokens functions to be used.

Or, in other words, the following is not possible :

await this.crowdsale.buyTokens(buyer,{ from: delegateBuyer, value: amount, data: signature });

Where, in this case, msg.data is 0xFUNCTION000000...BUYERADDRESS and signature is omitted.

Copy link
Contributor

@shrugs shrugs Apr 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does the evm handle a data field like

0x + buyTokens function sig + _beneficiary addresss + signature data

? will it call buyTokens with _beneficiary like normal and ignore the signature data part? if so, you could, in the pre-validate, pull the signature from msg.data and validate from there?

* @param _sig Valid signature from owner
* @return Validity of access message for a given address.
*/
function isValidAccessMessage(address _add, bytes _sig)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about _sender or _address instead of _add?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use ECRecovery here?

*/
function () external payable {
buyTokens(msg.sender, msg.data);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

following what I mentioned above, I guess there is no need to override the fallback function

await this.crowdsale.buyTokens(authorized, sig2, { value: value, from: authorized }).should.be.rejected;
});
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we may want to extract the access_message and signature calculations to a function or move them to the beforeEach block

@fiiiu
Copy link
Contributor

fiiiu commented Mar 22, 2018

@PhABC thanks for your PR, this looks like a very nice thing to have! Besides the points raised by @facuspagnuolo, and following your own suggestion: would you consider splitting this in two separate contracts, one SignedWhitelist in ownership, and the actual SignedWhitelistCrowdsale using this? (The naming is up for discussion ;) )

@PhABC
Copy link
Contributor Author

PhABC commented Apr 3, 2018

Sorry about the delay, will make the changes requested!

import "../crowdsale/validation/ControlledAccessCrowdsale.sol";


contract ControlledAccessCrowdsaleImpl is ControlledAccessCrowdsale {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like we might not need the mock, since it doesn't do anything beyond ControlledAccessCrowdsale?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied the logic from WhitelistedCrowdsaleImpl.sol. I believe the idea would be that users would create a contract such as contract MyTokenCrowdsale is ControlledAccessCrowdsale { ... } which would specify the parameters fed in the Crowdsale.sol constructor.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what a user would do, for sure, but since this mock doesn't do anything beyond inherit CrontrolledAccessCrowdsale, we can remove it for simplicity. Usually we'd need a mock if we needed to make the constructor payable or something to make testing easier.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps you could combine it with a MintableCrowdsale to test the inheritance while we're at it?

@shrugs
Copy link
Contributor

shrugs commented Apr 6, 2018

I'm using this technique for a project and have separated the valid message logic from the crowdsale into a Bouncer contract.

First, it uses RBACWithOwner.sol which will eventually be added to OZ (It's Ownable, but with the RBAC library)

You can see what the contracts look like in #883

@shrugs shrugs mentioned this pull request Apr 7, 2018
4 tasks
@shrugs
Copy link
Contributor

shrugs commented Apr 17, 2018

@PhABC #883 has been merged, so you can now use the SignatureBouncer contract to abstract away that logic for the controlled access crowdsale

@frangio
Copy link
Contributor

frangio commented Apr 17, 2018

Let me know when that's done so I can review this PR!

@shrugs
Copy link
Contributor

shrugs commented Sep 4, 2018

closing in favor of tracking via #1272

@shrugs shrugs closed this Sep 4, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants