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

rbac mintable erc721 token with signature bouncer #950

Closed
12 changes: 12 additions & 0 deletions contracts/AutoIncrementing.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pragma solidity ^0.4.23;


contract AutoIncrementing {
uint256 internal nextId_ = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

We should make this private so that derived contracts cannot access/modify directly.


function nextId() internal returns (uint256) {
uint256 thisId = nextId_;
nextId_ = nextId_ + 1;
return thisId;
}
}
57 changes: 57 additions & 0 deletions contracts/access/AutoIncrementingERC721Minter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
pragma solidity ^0.4.23;

import "../token/ERC721/MintableERC721Token.sol";
import "./SignatureBouncer.sol";
import "../AutoIncrementing.sol";


/**
* @title AutoIncrementingERC721Minter
* @author Matt Condon (@shrugs)
* @dev An ERc721Minter that generates auto-incrementing `tokenId`s.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: lowercase c.

*/
contract AutoIncrementingERC721Minter is AutoIncrementing, SignatureBouncer {
MintableERC721Token public token;

constructor(MintableERC721Token _token)
public
{
token = _token;
}

function mint(bytes _sig, string _tokenURI)
public
returns (uint256)
{
require(
isValidMintSignature(
msg.sender,
_sig,
_tokenURI
)
);

uint256 _tokenId = nextId();
token.mint(msg.sender, _tokenId, _tokenURI);
return _tokenId;
}

function isValidMintSignature(
address _address,
bytes _sig,
string _tokenURI
)
internal
view
returns (bool)
{
return isValidDataHash(
keccak256(
address(this),
_address,
_tokenURI
),
_sig
);
}
}
64 changes: 64 additions & 0 deletions contracts/access/ERC721Minter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
pragma solidity ^0.4.23;

import "../token/ERC721/MintableERC721Token.sol";
import "./SignatureBouncer.sol";


/**
* @title ERC721Minter
* @author Matt Condon (@shrugs)
* @dev A SignatureBouncer that allows users to mint themselves a MintableERC721Token
* @dev iff they have a valid signature from a bouncer.
* @dev 1. Deploy a MintableERC721Token and ERC721Minter.
* @dev 2. Make ERC721Minter a minter of the token using `addMinter`.
* @dev 3. Make your server a bouncer of ERC721Minter using `addBouncer`.
* @dev 4. Generate a valid bouncer signature (address(this) + msg.sender + tokenId + tokenURI)
* @dev and submit it to the ERC721Minter using `mint`
*/
contract ERC721Minter is SignatureBouncer {
MintableERC721Token public token;

constructor(MintableERC721Token _token)
public
{
token = _token;
}

function mint(bytes _sig, uint256 _tokenId, string _tokenURI)
Copy link
Contributor

Choose a reason for hiding this comment

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

The _sig argument is found in too many different argument positions. It's the first argument in mint, the second one in isValidMintSignature, and the last one in isValidSignature. It would be nice to make it consistent.

What do you think about making it always the last argument?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Confirmed in #973 that it should always be last. Good catch.

Also this PR will be updated to use #973 when it's merged.

public
returns (uint256)
{
require(
isValidMintSignature(
msg.sender,
_sig,
_tokenId,
_tokenURI
)
);

token.mint(msg.sender, _tokenId, _tokenURI);
return _tokenId;
}

function isValidMintSignature(
address _address,
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd prefer a more relevant name here. I can only think of _beneficiary but there may be better ones...

bytes _sig,
uint256 _tokenId,
string _tokenURI
)
internal
view
returns (bool)
{
return isValidDataHash(
keccak256(
address(this),
_address,
_tokenId,
_tokenURI
),
_sig
);
}
}
54 changes: 54 additions & 0 deletions contracts/access/NonceTracker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
pragma solidity ^0.4.23;


/**
* @title NonceTracker
* @author Matt Condon (@shrugs)
* @dev A simple way to keep track of nonces and restrict access.
* @dev Use the `withAccess` modifier to restrict access by address.
* @dev Use the `withMaxAccess` modifier to restrict access by address up to a max amount
* @dev For example, withAccess(msg.sender, 1) will only allow once-per-address.
* @dev You can also accept nonces from users (as part of a hash you verify).
*/
contract NonceTracker {
mapping(address => uint256) private nonces;

modifier withAccess(address _address, uint256 _nonce)
{
access(_address, _nonce);
_;
}

/**
* @dev use withMaxAccess to restrict access per-address for a maxiumum
* @dev number of times
*/
modifier withMaxAccess(address _address, uint256 _maxNonce)
{
// require that we haven't accessed this resource too much
require(nonce(_address) < _maxNonce);
// access it once more by incrementing the nonce
access(_address, nonce(_address) + 1);
// allow access
_;
}

function nonce(address _address)
public
view
returns (uint256)
{
return nonces[_address];
}

/**
* @dev call this function when accepting a nonce
* @dev throws if nonce is not strictly greater than previous nonce
*/
function access(address _address, uint256 _nonce)
internal
{
require(_nonce > nonces[_address]);
nonces[_address] = _nonce;
}
}
4 changes: 2 additions & 2 deletions contracts/access/SignatureBouncer.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pragma solidity ^0.4.23;

import "../ownership/Ownable.sol";
import "../ownership/rbac/RBACOwnable.sol";
import "../ownership/rbac/RBAC.sol";
import "../ECRecovery.sol";

Expand All @@ -22,7 +22,7 @@ import "../ECRecovery.sol";
* @dev
* @dev See the tests Bouncer.test.js for specific usage examples.
*/
contract SignatureBouncer is Ownable, RBAC {
contract SignatureBouncer is RBACOwnable {
using ECRecovery for bytes32;

string public constant ROLE_BOUNCER = "bouncer";
Expand Down
16 changes: 16 additions & 0 deletions contracts/mocks/DefaultTokenURIMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
pragma solidity ^0.4.23;

import "../token/ERC721/MintableERC721Token.sol";
import "../token/ERC721/DefaultTokenURI.sol";


contract DefaultTokenURIMock is DefaultTokenURI, MintableERC721Token {

constructor(string _name, string _symbol, string _tokenURI)
MintableERC721Token(_name, _symbol)
DefaultTokenURI(_tokenURI)
public
{

}
}
42 changes: 42 additions & 0 deletions contracts/mocks/NonceTrackerImpl.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
pragma solidity ^0.4.23;

import "../access/NonceTracker.sol";


contract NonceTrackerImpl is NonceTracker {

modifier onlyValidInputs(uint256 _nonce)
{
require(true);
// ^ you'd implement this using something like SignatureBouncer
_;
}

function canDoThisOnce()
withMaxAccess(msg.sender, 1)
public
{

}

function canDoThisTwice()
withMaxAccess(msg.sender, 2)
public
{
}

function cantDoThisAtAll()
withMaxAccess(msg.sender, 0)
public
{
require(nonce(msg.sender) <= 0);
}

function withAcceptedNonce(uint256 _nonce)
onlyValidInputs(_nonce)
withAccess(msg.sender, _nonce)
public
{

}
}
20 changes: 20 additions & 0 deletions contracts/ownership/rbac/RBACMintable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
pragma solidity ^0.4.23;

import "./RBAC.sol";


/**
* @title RBACMintable
* @author Matt Condon (@shrugs)
* @dev Mintable logic using RBAC.
* @dev You must add any logic for addMinter/removeMinter yourself because
* @dev security concerns will vary.
*/
contract RBACMintable is RBAC {
string public constant ROLE_MINTER = "minter";

modifier onlyMinter() {
checkRole(msg.sender, ROLE_MINTER);
_;
}
}
41 changes: 41 additions & 0 deletions contracts/ownership/rbac/RBACOwnable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
pragma solidity ^0.4.23;

import "./RBAC.sol";


/**
* @title RBACOwnable
* @author Matt Condon (@shrugs)
* @dev Ownable logic using RBAC.
* @dev Use RBACOwnable if you could have many different owners and you're ok with
* @dev the security profile of any owner being able to add another owner.
* @dev Only difference from Ownable.sol is that the owners are not stored as public variables.
*/
contract RBACOwnable is RBAC {
string public constant ROLE_OWNER = "owner";

constructor()
public
{
addRole(msg.sender, ROLE_OWNER);
}

modifier onlyOwner() {
checkRole(msg.sender, ROLE_OWNER);
_;
}

function addOwner(address _owner)
onlyOwner
public
{
addRole(_owner, ROLE_OWNER);
}

function removeOwner(address _owner)
onlyOwner
public
{
removeRole(_owner, ROLE_OWNER);
}
}
9 changes: 2 additions & 7 deletions contracts/token/ERC20/RBACMintableToken.sol
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
pragma solidity ^0.4.23;

import "./MintableToken.sol";
import "../../ownership/rbac/RBAC.sol";
import "../../ownership/rbac/RBACMintable.sol";


/**
* @title RBACMintableToken
* @author Vittorio Minacori (@vittominacori)
* @dev Mintable Token, with RBAC minter permissions
*/
contract RBACMintableToken is MintableToken, RBAC {
/**
* A constant role name for indicating minters.
*/
string public constant ROLE_MINTER = "minter";

contract RBACMintableToken is MintableToken, RBACMintable {
/**
* @dev override the Mintable token modifier to add role based logic
*/
Expand Down
31 changes: 31 additions & 0 deletions contracts/token/ERC721/DefaultTokenURI.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
pragma solidity ^0.4.23;

import "./ERC721Token.sol";


contract DefaultTokenURI is ERC721Token {
string private tokenURI_ = "";

constructor(string _tokenURI)
public
{
require(bytes(_tokenURI).length > 0);
tokenURI_ = _tokenURI;
}

/**
* @dev Returns a default URI for every tokenId unless a specific URI is set
* @param _tokenId uint256 ID of the token to query
*/
function tokenURI(uint256 _tokenId)
public
view
returns (string)
{
if (bytes(tokenURIs[_tokenId]).length != 0) {
return super.tokenURI(_tokenId);
}

return tokenURI_;
}
}
Loading