Explorar o código

Add Whitelist contract (#746)

* Add Whitelist contract
medvedev1088 %!s(int64=7) %!d(string=hai) anos
pai
achega
d1146e8c8b

+ 14 - 0
contracts/mocks/WhitelistMock.sol

@@ -0,0 +1,14 @@
+pragma solidity ^0.4.18;
+
+import "../ownership/Whitelist.sol";
+
+
+contract WhitelistMock is Whitelist {
+
+  function onlyWhitelistedCanDoThis()
+    onlyWhitelisted
+    view
+    external
+  {
+  }
+}

+ 81 - 0
contracts/ownership/Whitelist.sol

@@ -0,0 +1,81 @@
+pragma solidity ^0.4.18;
+
+
+import "./Ownable.sol";
+
+
+/**
+ * @title Whitelist
+ * @dev The Whitelist contract has a whitelist of addresses, and provides basic authorization control functions.
+ * @dev This simplifies the implementation of "user permissions".
+ */
+contract Whitelist is Ownable {
+  mapping(address => bool) public whitelist;
+  
+  event WhitelistedAddressAdded(address addr);
+  event WhitelistedAddressRemoved(address addr);
+
+  /**
+   * @dev Throws if called by any account that's not whitelisted.
+   */
+  modifier onlyWhitelisted() {
+    require(whitelist[msg.sender]);
+    _;
+  }
+
+  /**
+   * @dev add an address to the whitelist
+   * @param addr address
+   * @return true if the address was added to the whitelist, false if the address was already in the whitelist 
+   */
+  function addAddressToWhitelist(address addr) onlyOwner public returns(bool success) {
+    if (!whitelist[addr]) {
+      whitelist[addr] = true;
+      WhitelistedAddressAdded(addr);
+      success = true; 
+    }
+  }
+
+  /**
+   * @dev add addresses to the whitelist
+   * @param addrs addresses
+   * @return true if at least one address was added to the whitelist, 
+   * false if all addresses were already in the whitelist  
+   */
+  function addAddressesToWhitelist(address[] addrs) onlyOwner public returns(bool success) {
+    for (uint256 i = 0; i < addrs.length; i++) {
+      if (addAddressToWhitelist(addrs[i])) {
+        success = true;
+      }
+    }
+  }
+
+  /**
+   * @dev remove an address from the whitelist
+   * @param addr address
+   * @return true if the address was removed from the whitelist, 
+   * false if the address wasn't in the whitelist in the first place 
+   */
+  function removeAddressFromWhitelist(address addr) onlyOwner public returns(bool success) {
+    if (whitelist[addr]) {
+      whitelist[addr] = false;
+      WhitelistedAddressRemoved(addr);
+      success = true;
+    }
+  }
+
+  /**
+   * @dev remove addresses from the whitelist
+   * @param addrs addresses
+   * @return true if at least one address was removed from the whitelist, 
+   * false if all addresses weren't in the whitelist in the first place
+   */
+  function removeAddressesFromWhitelist(address[] addrs) onlyOwner public returns(bool success) {
+    for (uint256 i = 0; i < addrs.length; i++) {
+      if (removeAddressFromWhitelist(addrs[i])) {
+        success = true;
+      }
+    }
+  }
+
+}

+ 103 - 0
test/ownership/Whitelist.test.js

@@ -0,0 +1,103 @@
+import expectThrow from '../helpers/expectThrow';
+import expectEvent from '../helpers/expectEvent';
+
+const WhitelistMock = artifacts.require('WhitelistMock');
+
+require('chai')
+  .use(require('chai-as-promised'))
+  .should();
+
+contract('Whitelist', function (accounts) {
+  let mock;
+
+  const [
+    owner,
+    whitelistedAddress1,
+    whitelistedAddress2,
+    anyone,
+  ] = accounts;
+
+  const whitelistedAddresses = [whitelistedAddress1, whitelistedAddress2];
+
+  before(async function () {
+    mock = await WhitelistMock.new();
+  });
+
+  context('in normal conditions', () => {
+    it('should add address to the whitelist', async function () {
+      await expectEvent.inTransaction(
+        mock.addAddressToWhitelist(whitelistedAddress1, { from: owner }),
+        'WhitelistedAddressAdded'
+      );
+      const isWhitelisted = await mock.whitelist(whitelistedAddress1);
+      isWhitelisted.should.be.equal(true);
+    });
+
+    it('should add addresses to the whitelist', async function () {
+      await expectEvent.inTransaction(
+        mock.addAddressesToWhitelist(whitelistedAddresses, { from: owner }),
+        'WhitelistedAddressAdded'
+      );
+      for (let addr of whitelistedAddresses) {
+        const isWhitelisted = await mock.whitelist(addr);
+        isWhitelisted.should.be.equal(true);
+      }
+    });
+
+    it('should not announce WhitelistedAddressAdded event if address is already in the whitelist', async function () {
+      const { logs } = await mock.addAddressToWhitelist(whitelistedAddress1, { from: owner });
+      logs.should.be.empty;
+    });
+
+    it('should remove address from the whitelist', async function () {
+      await expectEvent.inTransaction(
+        mock.removeAddressFromWhitelist(whitelistedAddress1, { from: owner }),
+        'WhitelistedAddressRemoved'
+      );
+      let isWhitelisted = await mock.whitelist(whitelistedAddress1);
+      isWhitelisted.should.be.equal(false);
+    });
+
+    it('should remove addresses from the the whitelist', async function () {
+      await expectEvent.inTransaction(
+        mock.removeAddressesFromWhitelist(whitelistedAddresses, { from: owner }),
+        'WhitelistedAddressRemoved'
+      );
+      for (let addr of whitelistedAddresses) {
+        const isWhitelisted = await mock.whitelist(addr);
+        isWhitelisted.should.be.equal(false);
+      }
+    });
+
+    it('should not announce WhitelistedAddressRemoved event if address is not in the whitelist', async function () {
+      const { logs } = await mock.removeAddressFromWhitelist(whitelistedAddress1, { from: owner });
+      logs.should.be.empty;
+    });
+    
+    it('should allow whitelisted address to call #onlyWhitelistedCanDoThis', async () => {
+      await mock.addAddressToWhitelist(whitelistedAddress1, { from: owner });
+      await mock.onlyWhitelistedCanDoThis({ from: whitelistedAddress1 })
+        .should.be.fulfilled;
+    });
+  });
+
+  context('in adversarial conditions', () => {
+    it('should not allow "anyone" to add to the whitelist', async () => {
+      await expectThrow(
+        mock.addAddressToWhitelist(whitelistedAddress1, { from: anyone })
+      );
+    });
+    
+    it('should not allow "anyone" to remove from the whitelist', async () => {
+      await expectThrow(
+        mock.removeAddressFromWhitelist(whitelistedAddress1, { from: anyone })
+      );
+    });
+    
+    it('should not allow "anyone" to call #onlyWhitelistedCanDoThis', async () => {
+      await expectThrow(
+        mock.onlyWhitelistedCanDoThis({ from: anyone })
+      );
+    });
+  });
+});