From 04b03f42d877cc1565e8e7d7a778441add6d9b94 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 15 Nov 2017 05:24:10 -0200
Subject: [PATCH 001/102] Unit tests (#2)
* identity first draft
* truffle + identity tests
---
test/identity.js | 286 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 286 insertions(+)
create mode 100644 test/identity.js
diff --git a/test/identity.js b/test/identity.js
new file mode 100644
index 0000000..ab690bc
--- /dev/null
+++ b/test/identity.js
@@ -0,0 +1,286 @@
+const TestUtils = require("./TestUtils.js")
+const Identity = artifacts.require("./Identity.sol");
+
+contract('Identity', function(accounts) {
+
+ let identity;
+ beforeEach(async () => {
+ identity = await Identity.new({from: accounts[0]})
+ })
+
+
+ describe("Identity()", () => {
+
+ it("initialize with msg.sender as management key", async () => {
+ assert.equal(
+ await identity.getKeyType(accounts[0]),
+ 1,
+ identity.address+".getKeyType("+accounts[0]+") is not MANAGEMENT_KEY")
+ });
+
+ });
+
+
+ describe("addKey(address _key, uint256 _type)", () => {
+
+ it("MANAGEMENT_KEY add a new address as ACTION_KEY", async () => {
+ await identity.addKey(accounts[1], 2, {from: accounts[0]})
+ assert.equal(
+ await identity.getKeyType(accounts[1]),
+ 2,
+ identity.address+".getKeyType("+accounts[1]+") is not ACTION_KEY")
+ });
+
+
+ it("should not add key by non manager", async () => {
+ try {
+ await identity.addKey(accounts[1], 1, {from: accounts[2]})
+ }catch(e){
+ }
+ assert.equal(
+ await identity.getKeyType(accounts[1]),
+ 0,
+ identity.address+".getKeyType("+accounts[1]+") is not correct")
+ });
+
+ it("should not add key type 1 by actor", async () => {
+ await identity.addKey(accounts[2], 2, {from: accounts[0]})
+ try {
+ await identity.addKey(accounts[1], 1, {from: accounts[2]})
+ } catch(e){
+ }
+ assert.equal(
+ await identity.getKeyType(accounts[1]),
+ 0,
+ identity.address+".getKeyType("+accounts[1]+") is not correct")
+ });
+
+
+
+ it("fire KeyAdded(address indexed key, uint256 indexed type)", async () => {
+ identity.addKey(accounts[1], 2, {from: accounts[0]})
+ const keyAdded = await TestUtils.listenForEvent(identity.KeyAdded())
+ assert(keyAdded.key, accounts[1], "Key is not correct")
+ assert(keyAdded.keyType, 2, "Type is not correct")
+ });
+
+ });
+
+
+ describe("removeKey(address _key, uint256 _type)", () => {
+
+ it("MANAGEMENT_KEY should removes a key", async () => {
+ await identity.addKey(accounts[1], 1, {from: accounts[0]})
+ await identity.removeKey(accounts[0], {from: accounts[1]})
+ assert.equal(
+ await identity.getKeyType(accounts[0]),
+ 0,
+ identity.address+".getKeyType("+accounts[0]+") is not 0")
+ });
+
+
+ it("other key should not removes a key", async () => {
+ await identity.addKey(accounts[1], 1, {from: accounts[0]})
+ try {
+ await identity.removeKey(accounts[1], {from: accounts[2]})
+ }catch (e) {
+
+ }
+ assert.equal(
+ await identity.getKeyType(accounts[1]),
+ 1,
+ identity.address+".getKeyType("+accounts[1]+") is not 0")
+ });
+
+ it("actor key should not remove key", async () => {
+ await identity.addKey(accounts[1], 2, {from: accounts[0]})
+ await identity.addKey(accounts[2], 2, {from: accounts[0]})
+ try {
+ await identity.removeKey(accounts[1], {from: accounts[2]})
+ }catch (e) {
+
+ }
+ assert.equal(
+ await identity.getKeyType(accounts[1]),
+ 2,
+ identity.address+".getKeyType("+accounts[1]+") is not 0")
+ });
+
+
+ it("MANAGEMENT_KEY should not remove itself MANAGEMENT_KEY when there is no other MANAGEMENT_KEY", async () => {
+ try {
+ await identity.removeKey(accounts[0], {from: accounts[0]})
+ } catch(e) {
+ //nothing
+ }
+ assert.equal(
+ await identity.getKeyType(accounts[0]),
+ 1,
+ identity.address+".getKeyType("+accounts[0]+") is not MANAGEMENT_KEY")
+
+ });
+
+ it("fire KeyRemoved(address indexed key, uint256 indexed type)", async () => {
+ await identity.addKey(accounts[1], 2, {from: accounts[0]})
+ identity.removeKey(accounts[1], {from: accounts[0]})
+ const keyRemoved = await TestUtils.listenForEvent(identity.KeyRemoved())
+ assert(keyRemoved.key, accounts[1], "Key is not correct")
+ assert(keyRemoved.keyType, 2, "Type is not correct")
+ });
+
+ });
+
+
+ describe("getKeyType(address _key)", () => {
+
+ it("should start only with initializer as only key", async () => {
+ assert.equal(
+ await identity.getKeyType(accounts[0]),
+ 1,
+ identity.address+".getKeyType("+accounts[0]+") is not correct")
+
+ assert.equal(
+ await identity.getKeyType(accounts[1]),
+ 0,
+ identity.address+".getKeyType("+accounts[1]+") is not correct")
+ });
+
+ it("should get type 2 after addKey type 2", async () => {
+ await identity.addKey(accounts[1], 2, {from: accounts[0]})
+ assert.equal(
+ await identity.getKeyType(accounts[1]),
+ 2,
+ identity.address+".getKeyType("+accounts[1]+") is not correct")
+ });
+
+ it("should get type 999 after addKey type 999", async () => {
+ await identity.addKey(accounts[1], 999, {from: accounts[0]})
+ assert.equal(
+ await identity.getKeyType(accounts[1]),
+ 999,
+ identity.address+".getKeyType("+accounts[1]+") is not correct")
+ });
+
+ });
+
+
+ describe("getKeysByType(uint256 _type)", () => {
+
+ it("at initialization", async () => {
+
+ });
+
+ it("after addKey", async () => {
+
+ });
+
+ it("after removeKey", async () => {
+
+ });
+
+ it("after replaceKey", async () => {
+
+ });
+
+ });
+
+
+ describe("replaceKey(address _oldKey, address _newKey)", () => {
+
+ it("MANAGEMENT_KEY replace itself (alone)", async () => {
+
+ });
+
+ it("MANAGEMENT_KEY replace a key between others", async () => {
+
+ });
+
+ it("MANAGEMENT_KEY replace the first key", async () => {
+
+ });
+
+ it("MANAGEMENT_KEY replace the last key", async () => {
+
+ });
+
+ it("fire KeyReplaced(address indexed oldKey, address indexed newKey, uint256 indexed type)", async () => {
+
+ });
+
+ });
+
+
+ describe("execute(address _to, uint256 _value, bytes _data)", () => {
+
+ it("ACTOR_KEY execute arbitrary transaction", async () => {
+
+ });
+
+ it("MANAGEMENT_KEY execute arbitrary transaction", async () => {
+
+ });
+
+ it("Other keys NOT execute arbitrary transaction", async () => {
+
+ });
+
+ it("fire Executed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data)", async () => {
+
+ });
+
+ });
+
+
+ describe("approve(bytes32 _id, bool _approve)", () => {
+
+ it("MANAGEMENT_KEY should approve a claim", async () => {
+
+ });
+
+ it("MANAGEMENT_KEY should approve a transaction", async () => {
+
+ });
+
+ it("fire Approved(uint256 indexed executionId, bool approved)", async () => {
+
+ });
+
+ });
+
+
+ describe("getClaim(bytes32 _claimId)", () => {
+
+ it("Returns a claim by ID.", async () => {
+
+ });
+
+ });
+
+ describe("getClaimIdsByType(uint256 _claimType)", () => {
+ it("Returns an array of claim IDs by type.", async () => {
+
+ });
+ });
+
+ describe("addClaim(uint256 _claimType, address issuer, uint256 signatureType, bytes _signature, bytes _data, string _uri)", () => {
+ it("Requests the ADDITION of a claim from an issuer", async () => {
+
+ });
+
+ it("Requests the CHANGE of a claim from an issuer", async () => {
+
+ });
+
+ });
+
+ describe("removeClaim(bytes32 _claimId)", () => {
+ it("Requests the DELETION of a claim from an issuer", async () => {
+
+ });
+
+ it("Requests the DELETION of a claim from identity", async () => {
+
+ });
+ });
+
+});
From ff4c320869e31a0953f3515d093e82a4c4177816 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Tue, 28 Nov 2017 01:33:25 -0200
Subject: [PATCH 002/102] base structure of upgradable republic
---
contracts/token/MiniMeToken.sol | 560 ++++++++++++++++++++++++++++
contracts/token/TokenController.sol | 26 ++
2 files changed, 586 insertions(+)
create mode 100644 contracts/token/MiniMeToken.sol
create mode 100644 contracts/token/TokenController.sol
diff --git a/contracts/token/MiniMeToken.sol b/contracts/token/MiniMeToken.sol
new file mode 100644
index 0000000..de9ecef
--- /dev/null
+++ b/contracts/token/MiniMeToken.sol
@@ -0,0 +1,560 @@
+pragma solidity ^0.4.6;
+
+/*
+ Copyright 2016, Jordi Baylina
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+ */
+
+/// @title MiniMeToken Contract
+/// @author Jordi Baylina
+/// @dev This token contract's goal is to make it easy for anyone to clone this
+/// token using the token distribution at a given block, this will allow DAO's
+/// and DApps to upgrade their features in a decentralized manner without
+/// affecting the original token
+/// @dev It is ERC20 compliant, but still needs to under go further testing.
+
+import "../common/Controlled.sol";
+import "./TokenController.sol";
+import "./ApproveAndCallFallBack.sol";
+
+/// @dev The actual token contract, the default controller is the msg.sender
+/// that deploys the contract, so usually this token will be deployed by a
+/// token controller contract, which Giveth will call a "Campaign"
+contract MiniMeToken is Controlled {
+
+ string public name; //The Token's name: e.g. DigixDAO Tokens
+ uint8 public decimals; //Number of decimals of the smallest unit
+ string public symbol; //An identifier: e.g. REP
+ string public version = 'MMT_0.1'; //An arbitrary versioning scheme
+
+
+ /// @dev `Checkpoint` is the structure that attaches a block number to a
+ /// given value, the block number attached is the one that last changed the
+ /// value
+ struct Checkpoint {
+
+ // `fromBlock` is the block number that the value was generated from
+ uint128 fromBlock;
+
+ // `value` is the amount of tokens at a specific block number
+ uint128 value;
+ }
+
+ // `parentToken` is the Token address that was cloned to produce this token;
+ // it will be 0x0 for a token that was not cloned
+ MiniMeToken public parentToken;
+
+ // `parentSnapShotBlock` is the block number from the Parent Token that was
+ // used to determine the initial distribution of the Clone Token
+ uint public parentSnapShotBlock;
+
+ // `creationBlock` is the block number that the Clone Token was created
+ uint public creationBlock;
+
+ // `balances` is the map that tracks the balance of each address, in this
+ // contract when the balance changes the block number that the change
+ // occurred is also included in the map
+ mapping (address => Checkpoint[]) balances;
+
+ // `allowed` tracks any extra transfer rights as in all ERC20 tokens
+ mapping (address => mapping (address => uint256)) allowed;
+
+ // Tracks the history of the `totalSupply` of the token
+ Checkpoint[] totalSupplyHistory;
+
+ // Flag that determines if the token is transferable or not.
+ bool public transfersEnabled;
+
+ // The factory used to create new clone tokens
+ MiniMeTokenFactory public tokenFactory;
+
+////////////////
+// Constructor
+////////////////
+
+ /// @notice Constructor to create a MiniMeToken
+ /// @param _tokenFactory The address of the MiniMeTokenFactory contract that
+ /// will create the Clone token contracts, the token factory needs to be
+ /// deployed first
+ /// @param _parentToken Address of the parent token, set to 0x0 if it is a
+ /// new token
+ /// @param _parentSnapShotBlock Block of the parent token that will
+ /// determine the initial distribution of the clone token, set to 0 if it
+ /// is a new token
+ /// @param _tokenName Name of the new token
+ /// @param _decimalUnits Number of decimals of the new token
+ /// @param _tokenSymbol Token Symbol for the new token
+ /// @param _transfersEnabled If true, tokens will be able to be transferred
+ function MiniMeToken(
+ address _tokenFactory,
+ address _parentToken,
+ uint _parentSnapShotBlock,
+ string _tokenName,
+ uint8 _decimalUnits,
+ string _tokenSymbol,
+ bool _transfersEnabled
+ ) {
+ tokenFactory = MiniMeTokenFactory(_tokenFactory);
+ name = _tokenName; // Set the name
+ decimals = _decimalUnits; // Set the decimals
+ symbol = _tokenSymbol; // Set the symbol
+ parentToken = MiniMeToken(_parentToken);
+ parentSnapShotBlock = _parentSnapShotBlock;
+ transfersEnabled = _transfersEnabled;
+ creationBlock = block.number;
+ }
+
+
+///////////////////
+// ERC20 Methods
+///////////////////
+
+ /// @notice Send `_amount` tokens to `_to` from `msg.sender`
+ /// @param _to The address of the recipient
+ /// @param _amount The amount of tokens to be transferred
+ /// @return Whether the transfer was successful or not
+ function transfer(address _to, uint256 _amount) returns (bool success) {
+ require(transfersEnabled);
+ return doTransfer(msg.sender, _to, _amount);
+ }
+
+ /// @notice Send `_amount` tokens to `_to` from `_from` on the condition it
+ /// is approved by `_from`
+ /// @param _from The address holding the tokens being transferred
+ /// @param _to The address of the recipient
+ /// @param _amount The amount of tokens to be transferred
+ /// @return True if the transfer was successful
+ function transferFrom(address _from, address _to, uint256 _amount
+ ) returns (bool success) {
+
+ // The controller of this contract can move tokens around at will,
+ // this is important to recognize! Confirm that you trust the
+ // controller of this contract, which in most situations should be
+ // another open source smart contract or 0x0
+ if (msg.sender != controller) {
+ require(transfersEnabled);
+
+ // The standard ERC 20 transferFrom functionality
+ if (allowed[_from][msg.sender] < _amount) return false;
+ allowed[_from][msg.sender] -= _amount;
+ }
+ return doTransfer(_from, _to, _amount);
+ }
+
+ /// @dev This is the actual transfer function in the token contract, it can
+ /// only be called by other functions in this contract.
+ /// @param _from The address holding the tokens being transferred
+ /// @param _to The address of the recipient
+ /// @param _amount The amount of tokens to be transferred
+ /// @return True if the transfer was successful
+ function doTransfer(address _from, address _to, uint _amount
+ ) internal returns(bool) {
+
+ if (_amount == 0) {
+ return true;
+ }
+
+ require(parentSnapShotBlock < block.number);
+
+ // Do not allow transfer to 0x0 or the token contract itself
+ require((_to != 0) && (_to != address(this)));
+
+ // If the amount being transfered is more than the balance of the
+ // account the transfer returns false
+ var previousBalanceFrom = balanceOfAt(_from, block.number);
+ if (previousBalanceFrom < _amount) {
+ return false;
+ }
+
+ // Alerts the token controller of the transfer
+ if (isContract(controller)) {
+ require(TokenController(controller).onTransfer(_from, _to, _amount));
+ }
+
+ // First update the balance array with the new value for the address
+ // sending the tokens
+ updateValueAtNow(balances[_from], previousBalanceFrom - _amount);
+
+ // Then update the balance array with the new value for the address
+ // receiving the tokens
+ var previousBalanceTo = balanceOfAt(_to, block.number);
+ require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
+ updateValueAtNow(balances[_to], previousBalanceTo + _amount);
+
+ // An event to make the transfer easy to find on the blockchain
+ Transfer(_from, _to, _amount);
+
+ return true;
+ }
+
+ /// @param _owner The address that's balance is being requested
+ /// @return The balance of `_owner` at the current block
+ function balanceOf(address _owner) constant returns (uint256 balance) {
+ return balanceOfAt(_owner, block.number);
+ }
+
+ /// @notice `msg.sender` approves `_spender` to spend `_amount` tokens on
+ /// its behalf. This is a modified version of the ERC20 approve function
+ /// to be a little bit safer
+ /// @param _spender The address of the account able to transfer the tokens
+ /// @param _amount The amount of tokens to be approved for transfer
+ /// @return True if the approval was successful
+ function approve(address _spender, uint256 _amount) returns (bool success) {
+ require(transfersEnabled);
+
+ // To change the approve amount you first have to reduce the addresses`
+ // allowance to zero by calling `approve(_spender,0)` if it is not
+ // already 0 to mitigate the race condition described here:
+ // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
+ require((_amount == 0) || (allowed[msg.sender][_spender] == 0));
+
+ // Alerts the token controller of the approve function call
+ if (isContract(controller)) {
+ require(TokenController(controller).onApprove(msg.sender, _spender, _amount));
+ }
+
+ allowed[msg.sender][_spender] = _amount;
+ Approval(msg.sender, _spender, _amount);
+ return true;
+ }
+
+ /// @dev This function makes it easy to read the `allowed[]` map
+ /// @param _owner The address of the account that owns the token
+ /// @param _spender The address of the account able to transfer the tokens
+ /// @return Amount of remaining tokens of _owner that _spender is allowed
+ /// to spend
+ function allowance(address _owner, address _spender
+ ) constant returns (uint256 remaining) {
+ return allowed[_owner][_spender];
+ }
+
+ /// @notice `msg.sender` approves `_spender` to send `_amount` tokens on
+ /// its behalf, and then a function is triggered in the contract that is
+ /// being approved, `_spender`. This allows users to use their tokens to
+ /// interact with contracts in one function call instead of two
+ /// @param _spender The address of the contract able to transfer the tokens
+ /// @param _amount The amount of tokens to be approved for transfer
+ /// @return True if the function call was successful
+ function approveAndCall(address _spender, uint256 _amount, bytes _extraData
+ ) returns (bool success) {
+ require(approve(_spender, _amount));
+
+ ApproveAndCallFallBack(_spender).receiveApproval(
+ msg.sender,
+ _amount,
+ this,
+ _extraData
+ );
+
+ return true;
+ }
+
+ /// @dev This function makes it easy to get the total number of tokens
+ /// @return The total number of tokens
+ function totalSupply() constant returns (uint) {
+ return totalSupplyAt(block.number);
+ }
+
+
+////////////////
+// Query balance and totalSupply in History
+////////////////
+
+ /// @dev Queries the balance of `_owner` at a specific `_blockNumber`
+ /// @param _owner The address from which the balance will be retrieved
+ /// @param _blockNumber The block number when the balance is queried
+ /// @return The balance at `_blockNumber`
+ function balanceOfAt(address _owner, uint _blockNumber) constant
+ returns (uint) {
+
+ // These next few lines are used when the balance of the token is
+ // requested before a check point was ever created for this token, it
+ // requires that the `parentToken.balanceOfAt` be queried at the
+ // genesis block for that token as this contains initial balance of
+ // this token
+ if ((balances[_owner].length == 0)
+ || (balances[_owner][0].fromBlock > _blockNumber)) {
+ if (address(parentToken) != 0) {
+ return parentToken.balanceOfAt(_owner, min(_blockNumber, parentSnapShotBlock));
+ } else {
+ // Has no parent
+ return 0;
+ }
+
+ // This will return the expected balance during normal situations
+ } else {
+ return getValueAt(balances[_owner], _blockNumber);
+ }
+ }
+
+ /// @notice Total amount of tokens at a specific `_blockNumber`.
+ /// @param _blockNumber The block number when the totalSupply is queried
+ /// @return The total amount of tokens at `_blockNumber`
+ function totalSupplyAt(uint _blockNumber) constant returns(uint) {
+
+ // These next few lines are used when the totalSupply of the token is
+ // requested before a check point was ever created for this token, it
+ // requires that the `parentToken.totalSupplyAt` be queried at the
+ // genesis block for this token as that contains totalSupply of this
+ // token at this block number.
+ if ((totalSupplyHistory.length == 0)
+ || (totalSupplyHistory[0].fromBlock > _blockNumber)) {
+ if (address(parentToken) != 0) {
+ return parentToken.totalSupplyAt(min(_blockNumber, parentSnapShotBlock));
+ } else {
+ return 0;
+ }
+
+ // This will return the expected totalSupply during normal situations
+ } else {
+ return getValueAt(totalSupplyHistory, _blockNumber);
+ }
+ }
+
+////////////////
+// Clone Token Method
+////////////////
+
+ /// @notice Creates a new clone token with the initial distribution being
+ /// this token at `_snapshotBlock`
+ /// @param _cloneTokenName Name of the clone token
+ /// @param _cloneDecimalUnits Number of decimals of the smallest unit
+ /// @param _cloneTokenSymbol Symbol of the clone token
+ /// @param _snapshotBlock Block when the distribution of the parent token is
+ /// copied to set the initial distribution of the new clone token;
+ /// if the block is zero than the actual block, the current block is used
+ /// @param _transfersEnabled True if transfers are allowed in the clone
+ /// @return The address of the new MiniMeToken Contract
+ function createCloneToken(
+ string _cloneTokenName,
+ uint8 _cloneDecimalUnits,
+ string _cloneTokenSymbol,
+ uint _snapshotBlock,
+ bool _transfersEnabled
+ ) returns(address) {
+ if (_snapshotBlock == 0) _snapshotBlock = block.number;
+ MiniMeToken cloneToken = tokenFactory.createCloneToken(
+ this,
+ _snapshotBlock,
+ _cloneTokenName,
+ _cloneDecimalUnits,
+ _cloneTokenSymbol,
+ _transfersEnabled
+ );
+
+ cloneToken.changeController(msg.sender);
+
+ // An event to make the token easy to find on the blockchain
+ NewCloneToken(address(cloneToken), _snapshotBlock);
+ return address(cloneToken);
+ }
+
+////////////////
+// Generate and destroy tokens
+////////////////
+
+ /// @notice Generates `_amount` tokens that are assigned to `_owner`
+ /// @param _owner The address that will be assigned the new tokens
+ /// @param _amount The quantity of tokens generated
+ /// @return True if the tokens are generated correctly
+ function generateTokens(address _owner, uint _amount
+ ) onlyController returns (bool) {
+ uint curTotalSupply = totalSupply();
+ require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow
+ uint previousBalanceTo = balanceOf(_owner);
+ require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
+ updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount);
+ updateValueAtNow(balances[_owner], previousBalanceTo + _amount);
+ Transfer(0, _owner, _amount);
+ return true;
+ }
+
+
+ /// @notice Burns `_amount` tokens from `_owner`
+ /// @param _owner The address that will lose the tokens
+ /// @param _amount The quantity of tokens to burn
+ /// @return True if the tokens are burned correctly
+ function destroyTokens(address _owner, uint _amount
+ ) onlyController returns (bool) {
+ uint curTotalSupply = totalSupply();
+ require(curTotalSupply >= _amount);
+ uint previousBalanceFrom = balanceOf(_owner);
+ require(previousBalanceFrom >= _amount);
+ updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount);
+ updateValueAtNow(balances[_owner], previousBalanceFrom - _amount);
+ Transfer(_owner, 0, _amount);
+ return true;
+ }
+
+////////////////
+// Enable tokens transfers
+////////////////
+
+
+ /// @notice Enables token holders to transfer their tokens freely if true
+ /// @param _transfersEnabled True if transfers are allowed in the clone
+ function enableTransfers(bool _transfersEnabled) onlyController {
+ transfersEnabled = _transfersEnabled;
+ }
+
+////////////////
+// Internal helper functions to query and set a value in a snapshot array
+////////////////
+
+ /// @dev `getValueAt` retrieves the number of tokens at a given block number
+ /// @param checkpoints The history of values being queried
+ /// @param _block The block number to retrieve the value at
+ /// @return The number of tokens being queried
+ function getValueAt(Checkpoint[] storage checkpoints, uint _block
+ ) constant internal returns (uint) {
+ if (checkpoints.length == 0) return 0;
+
+ // Shortcut for the actual value
+ if (_block >= checkpoints[checkpoints.length-1].fromBlock)
+ return checkpoints[checkpoints.length-1].value;
+ if (_block < checkpoints[0].fromBlock) return 0;
+
+ // Binary search of the value in the array
+ uint min = 0;
+ uint max = checkpoints.length-1;
+ while (max > min) {
+ uint mid = (max + min + 1)/ 2;
+ if (checkpoints[mid].fromBlock<=_block) {
+ min = mid;
+ } else {
+ max = mid-1;
+ }
+ }
+ return checkpoints[min].value;
+ }
+
+ /// @dev `updateValueAtNow` used to update the `balances` map and the
+ /// `totalSupplyHistory`
+ /// @param checkpoints The history of data being updated
+ /// @param _value The new number of tokens
+ function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value
+ ) internal {
+ if ((checkpoints.length == 0)
+ || (checkpoints[checkpoints.length -1].fromBlock < block.number)) {
+ Checkpoint storage newCheckPoint = checkpoints[ checkpoints.length++ ];
+ newCheckPoint.fromBlock = uint128(block.number);
+ newCheckPoint.value = uint128(_value);
+ } else {
+ Checkpoint storage oldCheckPoint = checkpoints[checkpoints.length-1];
+ oldCheckPoint.value = uint128(_value);
+ }
+ }
+
+ /// @dev Internal function to determine if an address is a contract
+ /// @param _addr The address being queried
+ /// @return True if `_addr` is a contract
+ function isContract(address _addr) constant internal returns(bool) {
+ uint size;
+ if (_addr == 0) return false;
+ assembly {
+ size := extcodesize(_addr)
+ }
+ return size>0;
+ }
+
+ /// @dev Helper function to return a min betwen the two uints
+ function min(uint a, uint b) internal returns (uint) {
+ return a < b ? a : b;
+ }
+
+ /// @notice The fallback function: If the contract's controller has not been
+ /// set to 0, then the `proxyPayment` method is called which relays the
+ /// ether and creates tokens as described in the token controller contract
+ function () payable {
+ require(isContract(controller));
+ require(TokenController(controller).proxyPayment.value(msg.value)(msg.sender));
+ }
+
+//////////
+// Safety Methods
+//////////
+
+ /// @notice This method can be used by the controller to extract mistakenly
+ /// sent tokens to this contract.
+ /// @param _token The address of the token contract that you want to recover
+ /// set to 0 in case you want to extract ether.
+ function claimTokens(address _token) onlyController {
+ if (_token == 0x0) {
+ controller.transfer(this.balance);
+ return;
+ }
+
+ MiniMeToken token = MiniMeToken(_token);
+ uint balance = token.balanceOf(this);
+ token.transfer(controller, balance);
+ ClaimedTokens(_token, controller, balance);
+ }
+
+////////////////
+// Events
+////////////////
+ event ClaimedTokens(address indexed _token, address indexed _controller, uint _amount);
+ event Transfer(address indexed _from, address indexed _to, uint256 _amount);
+ event NewCloneToken(address indexed _cloneToken, uint _snapshotBlock);
+ event Approval(
+ address indexed _owner,
+ address indexed _spender,
+ uint256 _amount
+ );
+
+}
+
+
+////////////////
+// MiniMeTokenFactory
+////////////////
+
+/// @dev This contract is used to generate clone contracts from a contract.
+/// In solidity this is the way to create a contract from a contract of the
+/// same class
+contract MiniMeTokenFactory {
+
+ /// @notice Update the DApp by creating a new token with new functionalities
+ /// the msg.sender becomes the controller of this clone token
+ /// @param _parentToken Address of the token being cloned
+ /// @param _snapshotBlock Block of the parent token that will
+ /// determine the initial distribution of the clone token
+ /// @param _tokenName Name of the new token
+ /// @param _decimalUnits Number of decimals of the new token
+ /// @param _tokenSymbol Token Symbol for the new token
+ /// @param _transfersEnabled If true, tokens will be able to be transferred
+ /// @return The address of the new token contract
+ function createCloneToken(
+ address _parentToken,
+ uint _snapshotBlock,
+ string _tokenName,
+ uint8 _decimalUnits,
+ string _tokenSymbol,
+ bool _transfersEnabled
+ ) returns (MiniMeToken) {
+ MiniMeToken newToken = new MiniMeToken(
+ this,
+ _parentToken,
+ _snapshotBlock,
+ _tokenName,
+ _decimalUnits,
+ _tokenSymbol,
+ _transfersEnabled
+ );
+
+ newToken.changeController(msg.sender);
+ return newToken;
+ }
+}
diff --git a/contracts/token/TokenController.sol b/contracts/token/TokenController.sol
new file mode 100644
index 0000000..0dff23c
--- /dev/null
+++ b/contracts/token/TokenController.sol
@@ -0,0 +1,26 @@
+pragma solidity ^0.4.14;
+
+/// @dev The token controller contract must implement these functions
+contract TokenController {
+ /// @notice Called when `_owner` sends ether to the MiniMe Token contract
+ /// @param _owner The address that sent the ether to create tokens
+ /// @return True if the ether is accepted, false if it throws
+ function proxyPayment(address _owner) payable returns(bool);
+
+ /// @notice Notifies the controller about a token transfer allowing the
+ /// controller to react if desired
+ /// @param _from The origin of the transfer
+ /// @param _to The destination of the transfer
+ /// @param _amount The amount of the transfer
+ /// @return False if the controller does not authorize the transfer
+ function onTransfer(address _from, address _to, uint _amount) returns(bool);
+
+ /// @notice Notifies the controller about an approval allowing the
+ /// controller to react if desired
+ /// @param _owner The address that calls `approve()`
+ /// @param _spender The spender in the `approve()` call
+ /// @param _amount The amount in the `approve()` call
+ /// @return False if the controller does not authorize the approval
+ function onApprove(address _owner, address _spender, uint _amount)
+ returns(bool);
+}
\ No newline at end of file
From c273f7ec1da532e6de77b6e4f398f99c6d879e95 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Tue, 28 Nov 2017 01:33:25 -0200
Subject: [PATCH 003/102] base structure of upgradable republic
---
contracts/deploy/DelegatedCall.sol | 33 ++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 contracts/deploy/DelegatedCall.sol
diff --git a/contracts/deploy/DelegatedCall.sol b/contracts/deploy/DelegatedCall.sol
new file mode 100644
index 0000000..cbed2b4
--- /dev/null
+++ b/contracts/deploy/DelegatedCall.sol
@@ -0,0 +1,33 @@
+pragma solidity ^0.4.17;
+
+
+/**
+ * @title DelegatedCall
+ * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
+ * @dev Abstract contract that delegates calls by `delegated` modifier to result of `_target()`
+ */
+contract DelegatedCall {
+ /**
+ * @dev delegates the call of this function
+ */
+ modifier delegated {
+ require(_target().delegatecall(msg.data)); //require successfull delegate call to remote `_target()`
+ assembly {
+ let outSize := returndatasize
+ let outDataPtr := mload(0x40) //load memory
+ returndatacopy(outDataPtr, 0, outSize) //copy last return into pointer
+ return(outDataPtr, outSize)
+ }
+ assert(false); //should never reach here
+ _; //never will execute local logic
+ }
+
+ /**
+ * @dev defines the address for delegation of calls
+ */
+ function _target()
+ internal
+ constant
+ returns(address);
+
+}
From 6766c5d7b15bb370f89f140ca3dc579583d54332 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 29 Nov 2017 12:42:10 -0200
Subject: [PATCH 004/102] missing file
---
contracts/token/ApproveAndCallFallBack.sol | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 contracts/token/ApproveAndCallFallBack.sol
diff --git a/contracts/token/ApproveAndCallFallBack.sol b/contracts/token/ApproveAndCallFallBack.sol
new file mode 100644
index 0000000..d2086f3
--- /dev/null
+++ b/contracts/token/ApproveAndCallFallBack.sol
@@ -0,0 +1,5 @@
+pragma solidity ^0.4.14;
+
+contract ApproveAndCallFallBack {
+ function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public;
+}
\ No newline at end of file
From 86cf02e5f9959366b5550fe994fded2073a235e3 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Fri, 1 Dec 2017 04:45:26 -0200
Subject: [PATCH 005/102] recovery alpha + ens based recoverer
---
contracts/deploy/DelegatedCall.sol | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/contracts/deploy/DelegatedCall.sol b/contracts/deploy/DelegatedCall.sol
index cbed2b4..8f42dea 100644
--- a/contracts/deploy/DelegatedCall.sol
+++ b/contracts/deploy/DelegatedCall.sol
@@ -11,14 +11,14 @@ contract DelegatedCall {
* @dev delegates the call of this function
*/
modifier delegated {
- require(_target().delegatecall(msg.data)); //require successfull delegate call to remote `_target()`
+ //require successfull delegate call to remote `_target()`
+ require(_target().delegatecall(msg.data));
assembly {
let outSize := returndatasize
let outDataPtr := mload(0x40) //load memory
returndatacopy(outDataPtr, 0, outSize) //copy last return into pointer
return(outDataPtr, outSize)
}
- assert(false); //should never reach here
_; //never will execute local logic
}
From de977246879eda1ad5d6d7f622b85071644cdffa Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Tue, 19 Dec 2017 23:06:19 -0200
Subject: [PATCH 006/102] proposal manager executes to its controller
---
contracts/token/ApproveAndCallFallBack.sol | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contracts/token/ApproveAndCallFallBack.sol b/contracts/token/ApproveAndCallFallBack.sol
index d2086f3..b0b4b4e 100644
--- a/contracts/token/ApproveAndCallFallBack.sol
+++ b/contracts/token/ApproveAndCallFallBack.sol
@@ -1,5 +1,5 @@
pragma solidity ^0.4.14;
contract ApproveAndCallFallBack {
- function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public;
+ function receiveApproval(address from, uint256 _amount, address _token, bytes _data) public ;
}
\ No newline at end of file
From 12d3a4d8a9edcb4c87887811b32d90d445812222 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Mon, 8 Jan 2018 19:00:07 -0600
Subject: [PATCH 007/102] update folder structure
---
contracts/identity/ERC725.sol | 24 ++++
contracts/identity/ERC735.sol | 23 ++++
contracts/identity/Identity.sol | 214 ++++++++++++++++++++++++++++++++
test/identity.js | 2 +-
4 files changed, 262 insertions(+), 1 deletion(-)
create mode 100644 contracts/identity/ERC725.sol
create mode 100644 contracts/identity/ERC735.sol
create mode 100644 contracts/identity/Identity.sol
diff --git a/contracts/identity/ERC725.sol b/contracts/identity/ERC725.sol
new file mode 100644
index 0000000..8d2d25e
--- /dev/null
+++ b/contracts/identity/ERC725.sol
@@ -0,0 +1,24 @@
+pragma solidity ^0.4.17;
+
+contract ERC725 {
+
+ uint256 constant MANAGEMENT_KEY = 1;
+ uint256 constant ACTION_KEY = 2;
+ uint256 constant CLAIM_SIGNER_KEY = 3;
+ uint256 constant ENCRYPTION_KEY = 4;
+
+ event KeyAdded(address indexed key, uint256 indexed keyType);
+ event KeyRemoved(address indexed key, uint256 indexed keyType);
+ event KeyReplaced(address indexed oldKey, address indexed newKey, uint256 indexed keyType);
+ event ExecutionRequested(bytes32 indexed executionId, address indexed to, uint256 indexed value, bytes data);
+ event Executed(bytes32 indexed executionId, address indexed to, uint256 indexed value, bytes data);
+ event Approved(bytes32 indexed executionId, bool approved);
+
+ function getKeyType(address _key) public constant returns(uint256 keyType);
+ function getKeysByType(uint256 _type) public constant returns(address[]);
+ function addKey(address _key, uint256 _type) public returns (bool success);
+ function removeKey(address _key) public returns (bool success);
+ function replaceKey(address _oldKey, address _newKey) public returns (bool success);
+ function execute(address _to, uint256 _value, bytes _data) public returns (bytes32 executionId);
+ function approve(bytes32 _id, bool _approve) public returns (bool success);
+}
\ No newline at end of file
diff --git a/contracts/identity/ERC735.sol b/contracts/identity/ERC735.sol
new file mode 100644
index 0000000..ea5f8f5
--- /dev/null
+++ b/contracts/identity/ERC735.sol
@@ -0,0 +1,23 @@
+pragma solidity ^0.4.17;
+
+contract ERC735 {
+
+ event ClaimRequested(bytes32 indexed claimId, uint256 indexed claimType, address indexed issuer, uint256 signatureType, bytes signature, bytes claim, string uri);
+ event ClaimAdded(bytes32 indexed claimId, uint256 indexed claimType, address indexed issuer, uint256 signatureType, bytes signature, bytes claim, string uri);
+ event ClaimRemoved(bytes32 indexed claimId, uint256 indexed claimType, address indexed issuer, uint256 signatureType, bytes signature, bytes claim, string uri);
+ event ClaimChanged(bytes32 indexed claimId, uint256 indexed claimType, address indexed issuer, uint256 signatureType, bytes signature, bytes claim, string uri);
+
+ struct Claim {
+ uint256 claimType;
+ address issuer; // msg.sender
+ uint256 signatureType; // The type of signature
+ bytes signature; // this.address + claimType + claim
+ bytes claim;
+ string uri;
+ }
+
+ function getClaim(bytes32 _claimId) public constant returns(uint256 claimType, address issuer, uint256 signatureType, bytes signature, bytes claim, string uri);
+ function getClaimsIdByType(uint256 _claimType) public constant returns(bytes32[]);
+ function addClaim(uint256 _claimType, address issuer, uint256 signatureType, bytes _signature, bytes _claim, string _uri) public returns (bytes32 claimId);
+ function removeClaim(bytes32 _claimId) public returns (bool success);
+}
\ No newline at end of file
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
new file mode 100644
index 0000000..30f903f
--- /dev/null
+++ b/contracts/identity/Identity.sol
@@ -0,0 +1,214 @@
+ pragma solidity ^0.4.17;
+
+ import "./ERC725.sol";
+ import "./ERC735.sol";
+
+ contract Identity is ERC725, ERC735 {
+
+ mapping (address => uint256) keys;
+ mapping (bytes32 => Claim) claims;
+ mapping (uint256 => address[]) keysByType;
+ mapping (uint256 => bytes32[]) claimsByType;
+ mapping (bytes32 => uint256) indexes;
+ mapping (bytes32 => Transaction) txx;
+
+ uint nonce = 0;
+
+ struct Transaction {
+ address to;
+ uint value;
+ bytes data;
+ uint nonce;
+ }
+
+
+ modifier managerOnly {
+ require(keys[msg.sender] == MANAGEMENT_KEY);
+ _;
+ }
+
+ modifier managerOrSelf {
+ require(keys[msg.sender] == MANAGEMENT_KEY || msg.sender == address(this));
+ _;
+ }
+
+ modifier actorOnly {
+ require(keys[msg.sender] == ACTION_KEY);
+ _;
+ }
+
+ modifier claimSignerOnly {
+ require(keys[msg.sender] == CLAIM_SIGNER_KEY);
+ _;
+ }
+
+
+ function Identity() public {
+ _addKey(msg.sender, MANAGEMENT_KEY);
+ }
+
+
+ function addKey(address _key, uint256 _type) public managerOrSelf returns (bool success) {
+ _addKey(_key, _type);
+ }
+
+
+ function removeKey(address _key) public managerOrSelf returns (bool success) {
+ _removeKey(_key);
+ }
+
+
+ function replaceKey(address _oldKey, address _newKey) public managerOrSelf returns (bool success) {
+ _addKey(_newKey, getKeyType(_oldKey));
+ _removeKey(_oldKey);
+ return true;
+ }
+
+
+
+ function execute(
+ address _to,
+ uint256 _value,
+ bytes _data
+ )
+ public
+ returns (bytes32 executionId)
+ {
+ uint256 senderKey = keys[msg.sender];
+ require(senderKey == MANAGEMENT_KEY || senderKey == ACTION_KEY);
+ executionId = keccak256(_to, _value, _data, nonce);
+ ExecutionRequested(executionId, _to, _value, _data);
+ txx[executionId] = Transaction (
+ {
+ to: _to,
+ value: _value,
+ data: _data,
+ nonce: nonce
+ });
+ if (senderKey == MANAGEMENT_KEY) {
+ approve(executionId, true);
+ }
+ }
+
+ function approve(
+ bytes32 _id,
+ bool _approve
+ )
+ public
+ managerOnly
+ returns (bool success)
+ {
+ require(txx[_id].nonce == nonce);
+ nonce++;
+ if (_approve) {
+ success = txx[_id].to.call.value(txx[_id].value)(txx[_id].data);
+ }
+ }
+
+
+ function addClaim(
+ uint256 _claimType,
+ address _issuer,
+ uint256 _signatureType,
+ bytes _signature,
+ bytes _claim,
+ string _uri
+ )
+ public
+ claimSignerOnly
+ returns (bytes32 claimId)
+ {
+ claimId = keccak256(_issuer, _claimType);
+ claims[claimId] = Claim(
+ {
+ claimType: _claimType,
+ issuer: _issuer,
+ signatureType: _signatureType,
+ signature: _signature,
+ claim: _claim,
+ uri: _uri
+ }
+ );
+ indexes[keccak256(_issuer, _claimType)] = claimsByType[_claimType].length;
+ claimsByType[_claimType].push(claimId);
+ }
+
+
+ function removeClaim(bytes32 _claimId) public returns (bool success) {
+ Claim memory c = claims[_claimId];
+ require(msg.sender == c.issuer || keys[msg.sender] == MANAGEMENT_KEY || msg.sender == address(this));
+ uint claimIdTypePos = indexes[_claimId];
+ delete indexes[_claimId];
+ bytes32[] storage claimsTypeArr = claimsByType[c.claimType];
+ bytes32 replacer = claimsTypeArr[claimsTypeArr.length-1];
+ claimsTypeArr[claimIdTypePos] = replacer;
+ indexes[replacer] = claimIdTypePos;
+ delete claims[_claimId];
+ claimsTypeArr.length--;
+ return true;
+ }
+
+
+ function _addKey(address _key, uint256 _type) private {
+ require(keys[_key] == 0);
+ KeyAdded(_key, _type);
+ keys[_key] = _type;
+ indexes[keccak256(_key, _type)] = keysByType[_type].length;
+ keysByType[_type].push(_key);
+ }
+
+
+ function _removeKey(address _key) private {
+ uint256 kType = keys[_key];
+ KeyRemoved(_key, kType);
+ address[] storage keyArr = keysByType[kType];
+ if (msg.sender != address(this) && kType == MANAGEMENT_KEY && keyArr.length == 1) {
+ revert();
+ }
+ bytes32 oldIndex = keccak256(_key, kType);
+ uint index = indexes[oldIndex];
+ delete indexes[oldIndex];
+ address replacer = keyArr[keyArr.length-1];
+ keyArr[index] = replacer;
+ indexes[keccak256(replacer, keys[replacer])] = index;
+ keyArr.length--;
+ delete keys[_key];
+ }
+
+
+ function getKeyType(address _key) public constant returns(uint256 keyType) {
+ return keys[_key];
+ }
+
+
+ function getKeysByType(uint256 _type) public constant returns(address[]) {
+ return keysByType[_type];
+ }
+
+
+ function getClaim(
+ bytes32 _claimId
+ )
+ public
+ constant
+ returns
+ (uint256 claimType,
+ address issuer,
+ uint256 signatureType,
+ bytes signature,
+ bytes claim,
+ string uri)
+ {
+ Claim memory _claim = claims[_claimId];
+ return (_claim.claimType, _claim.issuer, _claim.signatureType, _claim.signature, _claim.claim, _claim.uri);
+ }
+
+
+ function getClaimsIdByType(uint256 _claimType) public constant returns(bytes32[]) {
+ return claimsByType[_claimType];
+ }
+
+
+ }
+
+
diff --git a/test/identity.js b/test/identity.js
index ab690bc..55a046d 100644
--- a/test/identity.js
+++ b/test/identity.js
@@ -1,5 +1,5 @@
const TestUtils = require("./TestUtils.js")
-const Identity = artifacts.require("./Identity.sol");
+const Identity = artifacts.require("./identity/Identity.sol");
contract('Identity', function(accounts) {
From f51ae08fa904cf1073a91613dfff49bee95710a8 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 24 Jan 2018 02:09:50 +0000
Subject: [PATCH 008/102] current maineth contracts
---
contracts/token/ApproveAndCallFallBack.sol | 5 +
contracts/token/MiniMeToken.sol | 533 +++++++++++++++++++++
contracts/token/MiniMeTokenFactory.sol | 45 ++
contracts/token/TokenController.sol | 26 +
4 files changed, 609 insertions(+)
create mode 100644 contracts/token/ApproveAndCallFallBack.sol
create mode 100644 contracts/token/MiniMeToken.sol
create mode 100644 contracts/token/MiniMeTokenFactory.sol
create mode 100644 contracts/token/TokenController.sol
diff --git a/contracts/token/ApproveAndCallFallBack.sol b/contracts/token/ApproveAndCallFallBack.sol
new file mode 100644
index 0000000..10b9c8e
--- /dev/null
+++ b/contracts/token/ApproveAndCallFallBack.sol
@@ -0,0 +1,5 @@
+pragma solidity ^0.4.11;
+
+contract ApproveAndCallFallBack {
+ function receiveApproval(address from, uint256 _amount, address _token, bytes _data);
+}
diff --git a/contracts/token/MiniMeToken.sol b/contracts/token/MiniMeToken.sol
new file mode 100644
index 0000000..3855bf5
--- /dev/null
+++ b/contracts/token/MiniMeToken.sol
@@ -0,0 +1,533 @@
+pragma solidity ^0.4.11;
+
+import "../common/Controlled.sol";
+import "./TokenController.sol";
+import "./ApproveAndCallFallBack.sol";
+
+/*
+ Copyright 2016, Jordi Baylina
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+ */
+
+/// @title MiniMeToken Contract
+/// @author Jordi Baylina
+/// @dev This token contract's goal is to make it easy for anyone to clone this
+/// token using the token distribution at a given block, this will allow DAO's
+/// and DApps to upgrade their features in a decentralized manner without
+/// affecting the original token
+/// @dev It is ERC20 compliant, but still needs to under go further testing.
+/// @dev The actual token contract, the default controller is the msg.sender
+/// that deploys the contract, so usually this token will be deployed by a
+/// token controller contract, which Giveth will call a "Campaign"
+contract MiniMeToken is Controlled {
+
+ string public name; //The Token's name: e.g. DigixDAO Tokens
+ uint8 public decimals; //Number of decimals of the smallest unit
+ string public symbol; //An identifier: e.g. REP
+ string public version = 'MMT_0.1'; //An arbitrary versioning scheme
+
+
+ /// @dev `Checkpoint` is the structure that attaches a block number to a
+ /// given value, the block number attached is the one that last changed the
+ /// value
+ struct Checkpoint {
+
+ // `fromBlock` is the block number that the value was generated from
+ uint128 fromBlock;
+
+ // `value` is the amount of tokens at a specific block number
+ uint128 value;
+ }
+
+ // `parentToken` is the Token address that was cloned to produce this token;
+ // it will be 0x0 for a token that was not cloned
+ MiniMeToken public parentToken;
+
+ // `parentSnapShotBlock` is the block number from the Parent Token that was
+ // used to determine the initial distribution of the Clone Token
+ uint public parentSnapShotBlock;
+
+ // `creationBlock` is the block number that the Clone Token was created
+ uint public creationBlock;
+
+ // `balances` is the map that tracks the balance of each address, in this
+ // contract when the balance changes the block number that the change
+ // occurred is also included in the map
+ mapping (address => Checkpoint[]) balances;
+
+ // `allowed` tracks any extra transfer rights as in all ERC20 tokens
+ mapping (address => mapping (address => uint256)) allowed;
+
+ // Tracks the history of the `totalSupply` of the token
+ Checkpoint[] totalSupplyHistory;
+
+ // Flag that determines if the token is transferable or not.
+ bool public transfersEnabled;
+
+ // The factory used to create new clone tokens
+ MiniMeTokenFactory public tokenFactory;
+
+////////////////
+// Constructor
+////////////////
+
+ /// @notice Constructor to create a MiniMeToken
+ /// @param _tokenFactory The address of the MiniMeTokenFactory contract that
+ /// will create the Clone token contracts, the token factory needs to be
+ /// deployed first
+ /// @param _parentToken Address of the parent token, set to 0x0 if it is a
+ /// new token
+ /// @param _parentSnapShotBlock Block of the parent token that will
+ /// determine the initial distribution of the clone token, set to 0 if it
+ /// is a new token
+ /// @param _tokenName Name of the new token
+ /// @param _decimalUnits Number of decimals of the new token
+ /// @param _tokenSymbol Token Symbol for the new token
+ /// @param _transfersEnabled If true, tokens will be able to be transferred
+ function MiniMeToken(
+ address _tokenFactory,
+ address _parentToken,
+ uint _parentSnapShotBlock,
+ string _tokenName,
+ uint8 _decimalUnits,
+ string _tokenSymbol,
+ bool _transfersEnabled
+ ) {
+ tokenFactory = MiniMeTokenFactory(_tokenFactory);
+ name = _tokenName; // Set the name
+ decimals = _decimalUnits; // Set the decimals
+ symbol = _tokenSymbol; // Set the symbol
+ parentToken = MiniMeToken(_parentToken);
+ parentSnapShotBlock = _parentSnapShotBlock;
+ transfersEnabled = _transfersEnabled;
+ creationBlock = getBlockNumber();
+ }
+
+
+///////////////////
+// ERC20 Methods
+///////////////////
+
+ /// @notice Send `_amount` tokens to `_to` from `msg.sender`
+ /// @param _to The address of the recipient
+ /// @param _amount The amount of tokens to be transferred
+ /// @return Whether the transfer was successful or not
+ function transfer(address _to, uint256 _amount) returns (bool success) {
+ if (!transfersEnabled) throw;
+ return doTransfer(msg.sender, _to, _amount);
+ }
+
+ /// @notice Send `_amount` tokens to `_to` from `_from` on the condition it
+ /// is approved by `_from`
+ /// @param _from The address holding the tokens being transferred
+ /// @param _to The address of the recipient
+ /// @param _amount The amount of tokens to be transferred
+ /// @return True if the transfer was successful
+ function transferFrom(address _from, address _to, uint256 _amount
+ ) returns (bool success) {
+
+ // The controller of this contract can move tokens around at will,
+ // this is important to recognize! Confirm that you trust the
+ // controller of this contract, which in most situations should be
+ // another open source smart contract or 0x0
+ if (msg.sender != controller) {
+ if (!transfersEnabled) throw;
+
+ // The standard ERC 20 transferFrom functionality
+ if (allowed[_from][msg.sender] < _amount) return false;
+ allowed[_from][msg.sender] -= _amount;
+ }
+ return doTransfer(_from, _to, _amount);
+ }
+
+ /// @dev This is the actual transfer function in the token contract, it can
+ /// only be called by other functions in this contract.
+ /// @param _from The address holding the tokens being transferred
+ /// @param _to The address of the recipient
+ /// @param _amount The amount of tokens to be transferred
+ /// @return True if the transfer was successful
+ function doTransfer(address _from, address _to, uint _amount
+ ) internal returns(bool) {
+
+ if (_amount == 0) {
+ return true;
+ }
+
+ if (parentSnapShotBlock >= getBlockNumber()) throw;
+
+ // Do not allow transfer to 0x0 or the token contract itself
+ if ((_to == 0) || (_to == address(this))) throw;
+
+ // If the amount being transfered is more than the balance of the
+ // account the transfer returns false
+ var previousBalanceFrom = balanceOfAt(_from, getBlockNumber());
+ if (previousBalanceFrom < _amount) {
+ return false;
+ }
+
+ // Alerts the token controller of the transfer
+ if (isContract(controller)) {
+ if (!TokenController(controller).onTransfer(_from, _to, _amount))
+ throw;
+ }
+
+ // First update the balance array with the new value for the address
+ // sending the tokens
+ updateValueAtNow(balances[_from], previousBalanceFrom - _amount);
+
+ // Then update the balance array with the new value for the address
+ // receiving the tokens
+ var previousBalanceTo = balanceOfAt(_to, getBlockNumber());
+ if (previousBalanceTo + _amount < previousBalanceTo) throw; // Check for overflow
+ updateValueAtNow(balances[_to], previousBalanceTo + _amount);
+
+ // An event to make the transfer easy to find on the blockchain
+ Transfer(_from, _to, _amount);
+
+ return true;
+ }
+
+ /// @param _owner The address that's balance is being requested
+ /// @return The balance of `_owner` at the current block
+ function balanceOf(address _owner) constant returns (uint256 balance) {
+ return balanceOfAt(_owner, getBlockNumber());
+ }
+
+ /// @notice `msg.sender` approves `_spender` to spend `_amount` tokens on
+ /// its behalf. This is a modified version of the ERC20 approve function
+ /// to be a little bit safer
+ /// @param _spender The address of the account able to transfer the tokens
+ /// @param _amount The amount of tokens to be approved for transfer
+ /// @return True if the approval was successful
+ function approve(address _spender, uint256 _amount) returns (bool success) {
+ if (!transfersEnabled) throw;
+
+ // To change the approve amount you first have to reduce the addresses`
+ // allowance to zero by calling `approve(_spender,0)` if it is not
+ // already 0 to mitigate the race condition described here:
+ // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
+ if ((_amount!=0) && (allowed[msg.sender][_spender] !=0)) throw;
+
+ // Alerts the token controller of the approve function call
+ if (isContract(controller)) {
+ if (!TokenController(controller).onApprove(msg.sender, _spender, _amount))
+ throw;
+ }
+
+ allowed[msg.sender][_spender] = _amount;
+ Approval(msg.sender, _spender, _amount);
+ return true;
+ }
+
+ /// @dev This function makes it easy to read the `allowed[]` map
+ /// @param _owner The address of the account that owns the token
+ /// @param _spender The address of the account able to transfer the tokens
+ /// @return Amount of remaining tokens of _owner that _spender is allowed
+ /// to spend
+ function allowance(address _owner, address _spender
+ ) constant returns (uint256 remaining) {
+ return allowed[_owner][_spender];
+ }
+
+ /// @notice `msg.sender` approves `_spender` to send `_amount` tokens on
+ /// its behalf, and then a function is triggered in the contract that is
+ /// being approved, `_spender`. This allows users to use their tokens to
+ /// interact with contracts in one function call instead of two
+ /// @param _spender The address of the contract able to transfer the tokens
+ /// @param _amount The amount of tokens to be approved for transfer
+ /// @return True if the function call was successful
+ function approveAndCall(address _spender, uint256 _amount, bytes _extraData
+ ) returns (bool success) {
+ if (!approve(_spender, _amount)) throw;
+
+ ApproveAndCallFallBack(_spender).receiveApproval(
+ msg.sender,
+ _amount,
+ this,
+ _extraData
+ );
+
+ return true;
+ }
+
+ /// @dev This function makes it easy to get the total number of tokens
+ /// @return The total number of tokens
+ function totalSupply() constant returns (uint) {
+ return totalSupplyAt(getBlockNumber());
+ }
+
+
+////////////////
+// Query balance and totalSupply in History
+////////////////
+
+ /// @dev Queries the balance of `_owner` at a specific `_blockNumber`
+ /// @param _owner The address from which the balance will be retrieved
+ /// @param _blockNumber The block number when the balance is queried
+ /// @return The balance at `_blockNumber`
+ function balanceOfAt(address _owner, uint _blockNumber) constant
+ returns (uint) {
+
+ // These next few lines are used when the balance of the token is
+ // requested before a check point was ever created for this token, it
+ // requires that the `parentToken.balanceOfAt` be queried at the
+ // genesis block for that token as this contains initial balance of
+ // this token
+ if ((balances[_owner].length == 0)
+ || (balances[_owner][0].fromBlock > _blockNumber)) {
+ if (address(parentToken) != 0) {
+ return parentToken.balanceOfAt(_owner, min(_blockNumber, parentSnapShotBlock));
+ } else {
+ // Has no parent
+ return 0;
+ }
+
+ // This will return the expected balance during normal situations
+ } else {
+ return getValueAt(balances[_owner], _blockNumber);
+ }
+ }
+
+ /// @notice Total amount of tokens at a specific `_blockNumber`.
+ /// @param _blockNumber The block number when the totalSupply is queried
+ /// @return The total amount of tokens at `_blockNumber`
+ function totalSupplyAt(uint _blockNumber) constant returns(uint) {
+
+ // These next few lines are used when the totalSupply of the token is
+ // requested before a check point was ever created for this token, it
+ // requires that the `parentToken.totalSupplyAt` be queried at the
+ // genesis block for this token as that contains totalSupply of this
+ // token at this block number.
+ if ((totalSupplyHistory.length == 0)
+ || (totalSupplyHistory[0].fromBlock > _blockNumber)) {
+ if (address(parentToken) != 0) {
+ return parentToken.totalSupplyAt(min(_blockNumber, parentSnapShotBlock));
+ } else {
+ return 0;
+ }
+
+ // This will return the expected totalSupply during normal situations
+ } else {
+ return getValueAt(totalSupplyHistory, _blockNumber);
+ }
+ }
+
+////////////////
+// Clone Token Method
+////////////////
+
+ /// @notice Creates a new clone token with the initial distribution being
+ /// this token at `_snapshotBlock`
+ /// @param _cloneTokenName Name of the clone token
+ /// @param _cloneDecimalUnits Number of decimals of the smallest unit
+ /// @param _cloneTokenSymbol Symbol of the clone token
+ /// @param _snapshotBlock Block when the distribution of the parent token is
+ /// copied to set the initial distribution of the new clone token;
+ /// if the block is zero than the actual block, the current block is used
+ /// @param _transfersEnabled True if transfers are allowed in the clone
+ /// @return The address of the new MiniMeToken Contract
+ function createCloneToken(
+ string _cloneTokenName,
+ uint8 _cloneDecimalUnits,
+ string _cloneTokenSymbol,
+ uint _snapshotBlock,
+ bool _transfersEnabled
+ ) returns(address) {
+ if (_snapshotBlock == 0) _snapshotBlock = getBlockNumber();
+ MiniMeToken cloneToken = tokenFactory.createCloneToken(
+ this,
+ _snapshotBlock,
+ _cloneTokenName,
+ _cloneDecimalUnits,
+ _cloneTokenSymbol,
+ _transfersEnabled
+ );
+
+ cloneToken.changeController(msg.sender);
+
+ // An event to make the token easy to find on the blockchain
+ NewCloneToken(address(cloneToken), _snapshotBlock);
+ return address(cloneToken);
+ }
+
+////////////////
+// Generate and destroy tokens
+////////////////
+
+ /// @notice Generates `_amount` tokens that are assigned to `_owner`
+ /// @param _owner The address that will be assigned the new tokens
+ /// @param _amount The quantity of tokens generated
+ /// @return True if the tokens are generated correctly
+ function generateTokens(address _owner, uint _amount
+ ) onlyController returns (bool) {
+ uint curTotalSupply = getValueAt(totalSupplyHistory, getBlockNumber());
+ if (curTotalSupply + _amount < curTotalSupply) throw; // Check for overflow
+ updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount);
+ var previousBalanceTo = balanceOf(_owner);
+ if (previousBalanceTo + _amount < previousBalanceTo) throw; // Check for overflow
+ updateValueAtNow(balances[_owner], previousBalanceTo + _amount);
+ Transfer(0, _owner, _amount);
+ return true;
+ }
+
+
+ /// @notice Burns `_amount` tokens from `_owner`
+ /// @param _owner The address that will lose the tokens
+ /// @param _amount The quantity of tokens to burn
+ /// @return True if the tokens are burned correctly
+ function destroyTokens(address _owner, uint _amount
+ ) onlyController returns (bool) {
+ uint curTotalSupply = getValueAt(totalSupplyHistory, getBlockNumber());
+ if (curTotalSupply < _amount) throw;
+ updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount);
+ var previousBalanceFrom = balanceOf(_owner);
+ if (previousBalanceFrom < _amount) throw;
+ updateValueAtNow(balances[_owner], previousBalanceFrom - _amount);
+ Transfer(_owner, 0, _amount);
+ return true;
+ }
+
+////////////////
+// Enable tokens transfers
+////////////////
+
+
+ /// @notice Enables token holders to transfer their tokens freely if true
+ /// @param _transfersEnabled True if transfers are allowed in the clone
+ function enableTransfers(bool _transfersEnabled) onlyController {
+ transfersEnabled = _transfersEnabled;
+ }
+
+////////////////
+// Internal helper functions to query and set a value in a snapshot array
+////////////////
+
+ /// @dev `getValueAt` retrieves the number of tokens at a given block number
+ /// @param checkpoints The history of values being queried
+ /// @param _block The block number to retrieve the value at
+ /// @return The number of tokens being queried
+ function getValueAt(Checkpoint[] storage checkpoints, uint _block
+ ) constant internal returns (uint) {
+ if (checkpoints.length == 0) return 0;
+
+ // Shortcut for the actual value
+ if (_block >= checkpoints[checkpoints.length-1].fromBlock)
+ return checkpoints[checkpoints.length-1].value;
+ if (_block < checkpoints[0].fromBlock) return 0;
+
+ // Binary search of the value in the array
+ uint min = 0;
+ uint max = checkpoints.length-1;
+ while (max > min) {
+ uint mid = (max + min + 1)/ 2;
+ if (checkpoints[mid].fromBlock<=_block) {
+ min = mid;
+ } else {
+ max = mid-1;
+ }
+ }
+ return checkpoints[min].value;
+ }
+
+ /// @dev `updateValueAtNow` used to update the `balances` map and the
+ /// `totalSupplyHistory`
+ /// @param checkpoints The history of data being updated
+ /// @param _value The new number of tokens
+ function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value
+ ) internal {
+ if ((checkpoints.length == 0)
+ || (checkpoints[checkpoints.length -1].fromBlock < getBlockNumber())) {
+ Checkpoint newCheckPoint = checkpoints[ checkpoints.length++ ];
+ newCheckPoint.fromBlock = uint128(getBlockNumber());
+ newCheckPoint.value = uint128(_value);
+ } else {
+ Checkpoint oldCheckPoint = checkpoints[checkpoints.length-1];
+ oldCheckPoint.value = uint128(_value);
+ }
+ }
+
+ /// @dev Internal function to determine if an address is a contract
+ /// @param _addr The address being queried
+ /// @return True if `_addr` is a contract
+ function isContract(address _addr) constant internal returns(bool) {
+ uint size;
+ if (_addr == 0) return false;
+ assembly {
+ size := extcodesize(_addr)
+ }
+ return size>0;
+ }
+
+ /// @dev Helper function to return a min betwen the two uints
+ function min(uint a, uint b) internal returns (uint) {
+ return a < b ? a : b;
+ }
+
+ /// @notice The fallback function: If the contract's controller has not been
+ /// set to 0, then the `proxyPayment` method is called which relays the
+ /// ether and creates tokens as described in the token controller contract
+ function () payable {
+ if (isContract(controller)) {
+ if (! TokenController(controller).proxyPayment.value(msg.value)(msg.sender))
+ throw;
+ } else {
+ throw;
+ }
+ }
+
+
+//////////
+// Testing specific methods
+//////////
+
+ /// @notice This function is overridden by the test Mocks.
+ function getBlockNumber() internal constant returns (uint256) {
+ return block.number;
+ }
+
+//////////
+// Safety Methods
+//////////
+
+ /// @notice This method can be used by the controller to extract mistakenly
+ /// sent tokens to this contract.
+ /// @param _token The address of the token contract that you want to recover
+ /// set to 0 in case you want to extract ether.
+ function claimTokens(address _token) onlyController {
+ if (_token == 0x0) {
+ controller.transfer(this.balance);
+ return;
+ }
+
+ ERC20Token token = ERC20Token(_token);
+ uint balance = token.balanceOf(this);
+ token.transfer(controller, balance);
+ ClaimedTokens(_token, controller, balance);
+ }
+
+////////////////
+// Events
+////////////////
+
+ event ClaimedTokens(address indexed _token, address indexed _controller, uint _amount);
+ event Transfer(address indexed _from, address indexed _to, uint256 _amount);
+ event NewCloneToken(address indexed _cloneToken, uint _snapshotBlock);
+ event Approval(
+ address indexed _owner,
+ address indexed _spender,
+ uint256 _amount
+ );
+
+}
\ No newline at end of file
diff --git a/contracts/token/MiniMeTokenFactory.sol b/contracts/token/MiniMeTokenFactory.sol
new file mode 100644
index 0000000..4e79ead
--- /dev/null
+++ b/contracts/token/MiniMeTokenFactory.sol
@@ -0,0 +1,45 @@
+pragma solidity ^0.4.11;
+
+import "./MiniMeToken.sol";
+
+////////////////
+// MiniMeTokenFactory
+////////////////
+
+/// @dev This contract is used to generate clone contracts from a contract.
+/// In solidity this is the way to create a contract from a contract of the
+/// same class
+contract MiniMeTokenFactory {
+
+ /// @notice Update the DApp by creating a new token with new functionalities
+ /// the msg.sender becomes the controller of this clone token
+ /// @param _parentToken Address of the token being cloned
+ /// @param _snapshotBlock Block of the parent token that will
+ /// determine the initial distribution of the clone token
+ /// @param _tokenName Name of the new token
+ /// @param _decimalUnits Number of decimals of the new token
+ /// @param _tokenSymbol Token Symbol for the new token
+ /// @param _transfersEnabled If true, tokens will be able to be transferred
+ /// @return The address of the new token contract
+ function createCloneToken(
+ address _parentToken,
+ uint _snapshotBlock,
+ string _tokenName,
+ uint8 _decimalUnits,
+ string _tokenSymbol,
+ bool _transfersEnabled
+ ) returns (MiniMeToken) {
+ MiniMeToken newToken = new MiniMeToken(
+ this,
+ _parentToken,
+ _snapshotBlock,
+ _tokenName,
+ _decimalUnits,
+ _tokenSymbol,
+ _transfersEnabled
+ );
+
+ newToken.changeController(msg.sender);
+ return newToken;
+ }
+}
\ No newline at end of file
diff --git a/contracts/token/TokenController.sol b/contracts/token/TokenController.sol
new file mode 100644
index 0000000..70fad1b
--- /dev/null
+++ b/contracts/token/TokenController.sol
@@ -0,0 +1,26 @@
+pragma solidity ^0.4.11;
+
+/// @dev The token controller contract must implement these functions
+contract TokenController {
+ /// @notice Called when `_owner` sends ether to the MiniMe Token contract
+ /// @param _owner The address that sent the ether to create tokens
+ /// @return True if the ether is accepted, false if it throws
+ function proxyPayment(address _owner) payable returns(bool);
+
+ /// @notice Notifies the controller about a token transfer allowing the
+ /// controller to react if desired
+ /// @param _from The origin of the transfer
+ /// @param _to The destination of the transfer
+ /// @param _amount The amount of the transfer
+ /// @return False if the controller does not authorize the transfer
+ function onTransfer(address _from, address _to, uint _amount) returns(bool);
+
+ /// @notice Notifies the controller about an approval allowing the
+ /// controller to react if desired
+ /// @param _owner The address that calls `approve()`
+ /// @param _spender The spender in the `approve()` call
+ /// @param _amount The amount in the `approve()` call
+ /// @return False if the controller does not authorize the approval
+ function onApprove(address _owner, address _spender, uint _amount)
+ returns(bool);
+}
From 8bae559ec9d9d91760ae0caf409b6a943b6acaad Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 24 Jan 2018 02:25:40 +0000
Subject: [PATCH 009/102] update to new solc + lint
---
contracts/token/MiniMeToken.sol | 217 +++++++++++++++++++-------------
1 file changed, 132 insertions(+), 85 deletions(-)
diff --git a/contracts/token/MiniMeToken.sol b/contracts/token/MiniMeToken.sol
index 3855bf5..9d586ff 100644
--- a/contracts/token/MiniMeToken.sol
+++ b/contracts/token/MiniMeToken.sol
@@ -1,6 +1,7 @@
-pragma solidity ^0.4.11;
+pragma solidity ^0.4.17;
import "../common/Controlled.sol";
+import "./MiniMeTokenFactory.sol";
import "./TokenController.sol";
import "./ApproveAndCallFallBack.sol";
@@ -36,13 +37,13 @@ contract MiniMeToken is Controlled {
string public name; //The Token's name: e.g. DigixDAO Tokens
uint8 public decimals; //Number of decimals of the smallest unit
string public symbol; //An identifier: e.g. REP
- string public version = 'MMT_0.1'; //An arbitrary versioning scheme
+ string public version = "MMT_0.1"; //An arbitrary versioning scheme
/// @dev `Checkpoint` is the structure that attaches a block number to a
/// given value, the block number attached is the one that last changed the
/// value
- struct Checkpoint {
+ struct Checkpoint {
// `fromBlock` is the block number that the value was generated from
uint128 fromBlock;
@@ -104,7 +105,9 @@ contract MiniMeToken is Controlled {
uint8 _decimalUnits,
string _tokenSymbol,
bool _transfersEnabled
- ) {
+ )
+ public
+ {
tokenFactory = MiniMeTokenFactory(_tokenFactory);
name = _tokenName; // Set the name
decimals = _decimalUnits; // Set the decimals
@@ -112,7 +115,7 @@ contract MiniMeToken is Controlled {
parentToken = MiniMeToken(_parentToken);
parentSnapShotBlock = _parentSnapShotBlock;
transfersEnabled = _transfersEnabled;
- creationBlock = getBlockNumber();
+ creationBlock = block.number;
}
@@ -124,8 +127,8 @@ contract MiniMeToken is Controlled {
/// @param _to The address of the recipient
/// @param _amount The amount of tokens to be transferred
/// @return Whether the transfer was successful or not
- function transfer(address _to, uint256 _amount) returns (bool success) {
- if (!transfersEnabled) throw;
+ function transfer(address _to, uint256 _amount) public returns (bool success) {
+ require(transfersEnabled);
return doTransfer(msg.sender, _to, _amount);
}
@@ -135,18 +138,26 @@ contract MiniMeToken is Controlled {
/// @param _to The address of the recipient
/// @param _amount The amount of tokens to be transferred
/// @return True if the transfer was successful
- function transferFrom(address _from, address _to, uint256 _amount
- ) returns (bool success) {
+ function transferFrom(
+ address _from,
+ address _to,
+ uint256 _amount
+ )
+ public
+ returns (bool success)
+ {
// The controller of this contract can move tokens around at will,
// this is important to recognize! Confirm that you trust the
// controller of this contract, which in most situations should be
// another open source smart contract or 0x0
if (msg.sender != controller) {
- if (!transfersEnabled) throw;
+ require(transfersEnabled);
// The standard ERC 20 transferFrom functionality
- if (allowed[_from][msg.sender] < _amount) return false;
+ if (allowed[_from][msg.sender] < _amount) {
+ return false;
+ }
allowed[_from][msg.sender] -= _amount;
}
return doTransfer(_from, _to, _amount);
@@ -158,29 +169,34 @@ contract MiniMeToken is Controlled {
/// @param _to The address of the recipient
/// @param _amount The amount of tokens to be transferred
/// @return True if the transfer was successful
- function doTransfer(address _from, address _to, uint _amount
- ) internal returns(bool) {
+ function doTransfer(
+ address _from,
+ address _to,
+ uint _amount
+ )
+ internal
+ returns(bool)
+ {
if (_amount == 0) {
return true;
}
- if (parentSnapShotBlock >= getBlockNumber()) throw;
+ require(parentSnapShotBlock < block.number);
// Do not allow transfer to 0x0 or the token contract itself
- if ((_to == 0) || (_to == address(this))) throw;
+ require((_to != 0) && (_to != address(this)));
// If the amount being transfered is more than the balance of the
// account the transfer returns false
- var previousBalanceFrom = balanceOfAt(_from, getBlockNumber());
+ var previousBalanceFrom = balanceOfAt(_from, block.number);
if (previousBalanceFrom < _amount) {
return false;
}
// Alerts the token controller of the transfer
if (isContract(controller)) {
- if (!TokenController(controller).onTransfer(_from, _to, _amount))
- throw;
+ require(TokenController(controller).onTransfer(_from, _to, _amount));
}
// First update the balance array with the new value for the address
@@ -189,8 +205,8 @@ contract MiniMeToken is Controlled {
// Then update the balance array with the new value for the address
// receiving the tokens
- var previousBalanceTo = balanceOfAt(_to, getBlockNumber());
- if (previousBalanceTo + _amount < previousBalanceTo) throw; // Check for overflow
+ var previousBalanceTo = balanceOfAt(_to, block.number);
+ require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
updateValueAtNow(balances[_to], previousBalanceTo + _amount);
// An event to make the transfer easy to find on the blockchain
@@ -201,8 +217,8 @@ contract MiniMeToken is Controlled {
/// @param _owner The address that's balance is being requested
/// @return The balance of `_owner` at the current block
- function balanceOf(address _owner) constant returns (uint256 balance) {
- return balanceOfAt(_owner, getBlockNumber());
+ function balanceOf(address _owner) public constant returns (uint256 balance) {
+ return balanceOfAt(_owner, block.number);
}
/// @notice `msg.sender` approves `_spender` to spend `_amount` tokens on
@@ -211,19 +227,18 @@ contract MiniMeToken is Controlled {
/// @param _spender The address of the account able to transfer the tokens
/// @param _amount The amount of tokens to be approved for transfer
/// @return True if the approval was successful
- function approve(address _spender, uint256 _amount) returns (bool success) {
- if (!transfersEnabled) throw;
+ function approve(address _spender, uint256 _amount) public returns (bool success) {
+ require(transfersEnabled);
// To change the approve amount you first have to reduce the addresses`
// allowance to zero by calling `approve(_spender,0)` if it is not
// already 0 to mitigate the race condition described here:
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
- if ((_amount!=0) && (allowed[msg.sender][_spender] !=0)) throw;
+ require((_amount == 0) || (allowed[msg.sender][_spender] == 0));
// Alerts the token controller of the approve function call
if (isContract(controller)) {
- if (!TokenController(controller).onApprove(msg.sender, _spender, _amount))
- throw;
+ require(TokenController(controller).onApprove(msg.sender, _spender, _amount));
}
allowed[msg.sender][_spender] = _amount;
@@ -236,8 +251,14 @@ contract MiniMeToken is Controlled {
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens of _owner that _spender is allowed
/// to spend
- function allowance(address _owner, address _spender
- ) constant returns (uint256 remaining) {
+ function allowance(
+ address _owner,
+ address _spender
+ )
+ public
+ constant
+ returns (uint256 remaining)
+ {
return allowed[_owner][_spender];
}
@@ -248,9 +269,15 @@ contract MiniMeToken is Controlled {
/// @param _spender The address of the contract able to transfer the tokens
/// @param _amount The amount of tokens to be approved for transfer
/// @return True if the function call was successful
- function approveAndCall(address _spender, uint256 _amount, bytes _extraData
- ) returns (bool success) {
- if (!approve(_spender, _amount)) throw;
+ function approveAndCall(
+ address _spender,
+ uint256 _amount,
+ bytes _extraData
+ )
+ public
+ returns (bool success)
+ {
+ require(approve(_spender, _amount));
ApproveAndCallFallBack(_spender).receiveApproval(
msg.sender,
@@ -264,8 +291,8 @@ contract MiniMeToken is Controlled {
/// @dev This function makes it easy to get the total number of tokens
/// @return The total number of tokens
- function totalSupply() constant returns (uint) {
- return totalSupplyAt(getBlockNumber());
+ function totalSupply() public constant returns (uint) {
+ return totalSupplyAt(block.number);
}
@@ -277,8 +304,14 @@ contract MiniMeToken is Controlled {
/// @param _owner The address from which the balance will be retrieved
/// @param _blockNumber The block number when the balance is queried
/// @return The balance at `_blockNumber`
- function balanceOfAt(address _owner, uint _blockNumber) constant
- returns (uint) {
+ function balanceOfAt(
+ address _owner,
+ uint _blockNumber
+ )
+ public
+ constant
+ returns (uint)
+ {
// These next few lines are used when the balance of the token is
// requested before a check point was ever created for this token, it
@@ -303,7 +336,7 @@ contract MiniMeToken is Controlled {
/// @notice Total amount of tokens at a specific `_blockNumber`.
/// @param _blockNumber The block number when the totalSupply is queried
/// @return The total amount of tokens at `_blockNumber`
- function totalSupplyAt(uint _blockNumber) constant returns(uint) {
+ function totalSupplyAt(uint _blockNumber) public constant returns(uint) {
// These next few lines are used when the totalSupply of the token is
// requested before a check point was ever created for this token, it
@@ -344,8 +377,13 @@ contract MiniMeToken is Controlled {
string _cloneTokenSymbol,
uint _snapshotBlock,
bool _transfersEnabled
- ) returns(address) {
- if (_snapshotBlock == 0) _snapshotBlock = getBlockNumber();
+ )
+ public
+ returns(address)
+ {
+ if (_snapshotBlock == 0) {
+ _snapshotBlock = block.number;
+ }
MiniMeToken cloneToken = tokenFactory.createCloneToken(
this,
_snapshotBlock,
@@ -370,13 +408,19 @@ contract MiniMeToken is Controlled {
/// @param _owner The address that will be assigned the new tokens
/// @param _amount The quantity of tokens generated
/// @return True if the tokens are generated correctly
- function generateTokens(address _owner, uint _amount
- ) onlyController returns (bool) {
- uint curTotalSupply = getValueAt(totalSupplyHistory, getBlockNumber());
- if (curTotalSupply + _amount < curTotalSupply) throw; // Check for overflow
+ function generateTokens(
+ address _owner,
+ uint _amount
+ )
+ public
+ onlyController
+ returns (bool)
+ {
+ uint curTotalSupply = totalSupply();
+ require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow
+ uint previousBalanceTo = balanceOf(_owner);
+ require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount);
- var previousBalanceTo = balanceOf(_owner);
- if (previousBalanceTo + _amount < previousBalanceTo) throw; // Check for overflow
updateValueAtNow(balances[_owner], previousBalanceTo + _amount);
Transfer(0, _owner, _amount);
return true;
@@ -387,13 +431,19 @@ contract MiniMeToken is Controlled {
/// @param _owner The address that will lose the tokens
/// @param _amount The quantity of tokens to burn
/// @return True if the tokens are burned correctly
- function destroyTokens(address _owner, uint _amount
- ) onlyController returns (bool) {
- uint curTotalSupply = getValueAt(totalSupplyHistory, getBlockNumber());
- if (curTotalSupply < _amount) throw;
+ function destroyTokens(
+ address _owner,
+ uint _amount
+ )
+ public
+ onlyController
+ returns (bool)
+ {
+ uint curTotalSupply = totalSupply();
+ require(curTotalSupply >= _amount);
+ uint previousBalanceFrom = balanceOf(_owner);
+ require(previousBalanceFrom >= _amount);
updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount);
- var previousBalanceFrom = balanceOf(_owner);
- if (previousBalanceFrom < _amount) throw;
updateValueAtNow(balances[_owner], previousBalanceFrom - _amount);
Transfer(_owner, 0, _amount);
return true;
@@ -406,7 +456,7 @@ contract MiniMeToken is Controlled {
/// @notice Enables token holders to transfer their tokens freely if true
/// @param _transfersEnabled True if transfers are allowed in the clone
- function enableTransfers(bool _transfersEnabled) onlyController {
+ function enableTransfers(bool _transfersEnabled) public onlyController {
transfersEnabled = _transfersEnabled;
}
@@ -418,20 +468,31 @@ contract MiniMeToken is Controlled {
/// @param checkpoints The history of values being queried
/// @param _block The block number to retrieve the value at
/// @return The number of tokens being queried
- function getValueAt(Checkpoint[] storage checkpoints, uint _block
- ) constant internal returns (uint) {
- if (checkpoints.length == 0) return 0;
+ function getValueAt(
+ Checkpoint[] storage checkpoints,
+ uint _block
+ )
+ constant
+ internal
+ returns (uint)
+ {
+ if (checkpoints.length == 0) {
+ return 0;
+ }
// Shortcut for the actual value
- if (_block >= checkpoints[checkpoints.length-1].fromBlock)
+ if (_block >= checkpoints[checkpoints.length-1].fromBlock) {
return checkpoints[checkpoints.length-1].value;
- if (_block < checkpoints[0].fromBlock) return 0;
+ }
+ if (_block < checkpoints[0].fromBlock) {
+ return 0;
+ }
// Binary search of the value in the array
uint min = 0;
uint max = checkpoints.length-1;
while (max > min) {
- uint mid = (max + min + 1)/ 2;
+ uint mid = (max + min + 1) / 2;
if (checkpoints[mid].fromBlock<=_block) {
min = mid;
} else {
@@ -445,15 +506,14 @@ contract MiniMeToken is Controlled {
/// `totalSupplyHistory`
/// @param checkpoints The history of data being updated
/// @param _value The new number of tokens
- function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value
- ) internal {
+ function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value) internal {
if ((checkpoints.length == 0)
- || (checkpoints[checkpoints.length -1].fromBlock < getBlockNumber())) {
- Checkpoint newCheckPoint = checkpoints[ checkpoints.length++ ];
- newCheckPoint.fromBlock = uint128(getBlockNumber());
+ || (checkpoints[checkpoints.length -1].fromBlock < block.number)) {
+ Checkpoint storage newCheckPoint = checkpoints[ checkpoints.length++ ];
+ newCheckPoint.fromBlock = uint128(block.number);
newCheckPoint.value = uint128(_value);
} else {
- Checkpoint oldCheckPoint = checkpoints[checkpoints.length-1];
+ Checkpoint storage oldCheckPoint = checkpoints[checkpoints.length-1];
oldCheckPoint.value = uint128(_value);
}
}
@@ -463,7 +523,9 @@ contract MiniMeToken is Controlled {
/// @return True if `_addr` is a contract
function isContract(address _addr) constant internal returns(bool) {
uint size;
- if (_addr == 0) return false;
+ if (_addr == 0){
+ return false;
+ }
assembly {
size := extcodesize(_addr)
}
@@ -478,23 +540,9 @@ contract MiniMeToken is Controlled {
/// @notice The fallback function: If the contract's controller has not been
/// set to 0, then the `proxyPayment` method is called which relays the
/// ether and creates tokens as described in the token controller contract
- function () payable {
- if (isContract(controller)) {
- if (! TokenController(controller).proxyPayment.value(msg.value)(msg.sender))
- throw;
- } else {
- throw;
- }
- }
-
-
-//////////
-// Testing specific methods
-//////////
-
- /// @notice This function is overridden by the test Mocks.
- function getBlockNumber() internal constant returns (uint256) {
- return block.number;
+ function () public payable {
+ require(isContract(controller));
+ require(TokenController(controller).proxyPayment.value(msg.value)(msg.sender));
}
//////////
@@ -505,13 +553,13 @@ contract MiniMeToken is Controlled {
/// sent tokens to this contract.
/// @param _token The address of the token contract that you want to recover
/// set to 0 in case you want to extract ether.
- function claimTokens(address _token) onlyController {
+ function claimTokens(address _token) public onlyController {
if (_token == 0x0) {
controller.transfer(this.balance);
return;
}
- ERC20Token token = ERC20Token(_token);
+ MiniMeToken token = MiniMeToken(_token);
uint balance = token.balanceOf(this);
token.transfer(controller, balance);
ClaimedTokens(_token, controller, balance);
@@ -520,7 +568,6 @@ contract MiniMeToken is Controlled {
////////////////
// Events
////////////////
-
event ClaimedTokens(address indexed _token, address indexed _controller, uint _amount);
event Transfer(address indexed _from, address indexed _to, uint256 _amount);
event NewCloneToken(address indexed _cloneToken, uint _snapshotBlock);
From 34484012471121dcbfeb10fc1d269262582b2906 Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Wed, 21 Feb 2018 17:17:38 -0400
Subject: [PATCH 010/102] - Updated logic on Identity.sol to reflect changes on
EIPs - Added migration to deploy Identity contract - Updated existing test
cases to reflect changes on contract interface
---
contracts/identity/ERC725.sol | 33 ++-
contracts/identity/ERC735.sol | 22 +-
contracts/identity/Identity.sol | 477 ++++++++++++++++++--------------
test/identity.js | 131 ++++-----
4 files changed, 356 insertions(+), 307 deletions(-)
diff --git a/contracts/identity/ERC725.sol b/contracts/identity/ERC725.sol
index 8d2d25e..3c8f5e1 100644
--- a/contracts/identity/ERC725.sol
+++ b/contracts/identity/ERC725.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.17;
+pragma solidity ^0.4.18;
contract ERC725 {
@@ -6,19 +6,24 @@ contract ERC725 {
uint256 constant ACTION_KEY = 2;
uint256 constant CLAIM_SIGNER_KEY = 3;
uint256 constant ENCRYPTION_KEY = 4;
+
+ event KeyAdded(bytes32 indexed key, uint256 indexed purpose, uint256 indexed keyType);
+ event KeyRemoved(bytes32 indexed key, uint256 indexed purpose, uint256 indexed keyType);
+ event ExecutionRequested(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data);
+ event Executed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data);
+ event Approved(uint256 indexed executionId, bool approved);
- event KeyAdded(address indexed key, uint256 indexed keyType);
- event KeyRemoved(address indexed key, uint256 indexed keyType);
- event KeyReplaced(address indexed oldKey, address indexed newKey, uint256 indexed keyType);
- event ExecutionRequested(bytes32 indexed executionId, address indexed to, uint256 indexed value, bytes data);
- event Executed(bytes32 indexed executionId, address indexed to, uint256 indexed value, bytes data);
- event Approved(bytes32 indexed executionId, bool approved);
+ struct Key {
+ uint256 purpose; //e.g., MANAGEMENT_KEY = 1, ACTION_KEY = 2, etc.
+ uint256 keyType; // e.g. 1 = ECDSA, 2 = RSA, etc.
+ bytes32 key;
+ }
- function getKeyType(address _key) public constant returns(uint256 keyType);
- function getKeysByType(uint256 _type) public constant returns(address[]);
- function addKey(address _key, uint256 _type) public returns (bool success);
- function removeKey(address _key) public returns (bool success);
- function replaceKey(address _oldKey, address _newKey) public returns (bool success);
- function execute(address _to, uint256 _value, bytes _data) public returns (bytes32 executionId);
- function approve(bytes32 _id, bool _approve) public returns (bool success);
+ function getKey(bytes32 _key, uint256 _purpose) public constant returns(uint256 purpose, uint256 keyType, bytes32 key);
+ function getKeyPurpose(bytes32 _key) public constant returns(uint256[] purpose);
+ function getKeysByPurpose(uint256 _purpose) public constant returns(bytes32[] keys);
+ function addKey(bytes32 _key, uint256 _purpose, uint256 _keyType) public returns (bool success);
+ function removeKey(bytes32 _key, uint256 _purpose) returns (bool success);
+ function execute(address _to, uint256 _value, bytes _data) public returns (uint256 executionId);
+ function approve(uint256 _id, bool _approve) public returns (bool success);
}
\ No newline at end of file
diff --git a/contracts/identity/ERC735.sol b/contracts/identity/ERC735.sol
index ea5f8f5..2109d28 100644
--- a/contracts/identity/ERC735.sol
+++ b/contracts/identity/ERC735.sol
@@ -1,23 +1,23 @@
-pragma solidity ^0.4.17;
+pragma solidity ^0.4.18;
contract ERC735 {
- event ClaimRequested(bytes32 indexed claimId, uint256 indexed claimType, address indexed issuer, uint256 signatureType, bytes signature, bytes claim, string uri);
- event ClaimAdded(bytes32 indexed claimId, uint256 indexed claimType, address indexed issuer, uint256 signatureType, bytes signature, bytes claim, string uri);
- event ClaimRemoved(bytes32 indexed claimId, uint256 indexed claimType, address indexed issuer, uint256 signatureType, bytes signature, bytes claim, string uri);
- event ClaimChanged(bytes32 indexed claimId, uint256 indexed claimType, address indexed issuer, uint256 signatureType, bytes signature, bytes claim, string uri);
+ event ClaimRequested(bytes32 indexed claimRequestId, uint256 indexed claimType, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
+ event ClaimAdded(bytes32 indexed claimId, uint256 indexed claimType, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
+ event ClaimRemoved(bytes32 indexed claimId, uint256 indexed claimType, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
+ event ClaimChanged(bytes32 indexed claimId, uint256 indexed claimType, uint256 scheme, address indexed issuer, bytes signature, bytes data, string uri);
struct Claim {
uint256 claimType;
+ uint256 scheme;
address issuer; // msg.sender
- uint256 signatureType; // The type of signature
- bytes signature; // this.address + claimType + claim
- bytes claim;
+ bytes signature; // this.address + claimType + data
+ bytes data;
string uri;
}
- function getClaim(bytes32 _claimId) public constant returns(uint256 claimType, address issuer, uint256 signatureType, bytes signature, bytes claim, string uri);
- function getClaimsIdByType(uint256 _claimType) public constant returns(bytes32[]);
- function addClaim(uint256 _claimType, address issuer, uint256 signatureType, bytes _signature, bytes _claim, string _uri) public returns (bytes32 claimId);
+ function getClaim(bytes32 _claimId) public constant returns(uint256 claimType, uint256 scheme, address issuer, bytes signature, bytes data, string uri);
+ function getClaimIdsByType(uint256 _claimType) public constant returns(bytes32[] claimIds);
+ function addClaim(uint256 _claimType, uint256 _scheme, address _issuer, bytes _signature, bytes _data, string _uri) public returns (bytes32 claimRequestId);
function removeClaim(bytes32 _claimId) public returns (bool success);
}
\ No newline at end of file
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 30f903f..13c5357 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -1,214 +1,281 @@
- pragma solidity ^0.4.17;
+pragma solidity ^0.4.17;
- import "./ERC725.sol";
- import "./ERC735.sol";
-
- contract Identity is ERC725, ERC735 {
-
- mapping (address => uint256) keys;
- mapping (bytes32 => Claim) claims;
- mapping (uint256 => address[]) keysByType;
- mapping (uint256 => bytes32[]) claimsByType;
- mapping (bytes32 => uint256) indexes;
- mapping (bytes32 => Transaction) txx;
-
- uint nonce = 0;
-
- struct Transaction {
- address to;
- uint value;
- bytes data;
- uint nonce;
- }
+import "./ERC725.sol";
+import "./ERC735.sol";
- modifier managerOnly {
- require(keys[msg.sender] == MANAGEMENT_KEY);
- _;
- }
+contract Identity is ERC725, ERC735 {
- modifier managerOrSelf {
- require(keys[msg.sender] == MANAGEMENT_KEY || msg.sender == address(this));
- _;
- }
-
- modifier actorOnly {
- require(keys[msg.sender] == ACTION_KEY);
- _;
- }
-
- modifier claimSignerOnly {
- require(keys[msg.sender] == CLAIM_SIGNER_KEY);
- _;
- }
-
-
- function Identity() public {
- _addKey(msg.sender, MANAGEMENT_KEY);
- }
-
-
- function addKey(address _key, uint256 _type) public managerOrSelf returns (bool success) {
- _addKey(_key, _type);
- }
-
-
- function removeKey(address _key) public managerOrSelf returns (bool success) {
- _removeKey(_key);
- }
-
-
- function replaceKey(address _oldKey, address _newKey) public managerOrSelf returns (bool success) {
- _addKey(_newKey, getKeyType(_oldKey));
- _removeKey(_oldKey);
- return true;
- }
-
-
-
- function execute(
- address _to,
- uint256 _value,
- bytes _data
- )
- public
- returns (bytes32 executionId)
- {
- uint256 senderKey = keys[msg.sender];
- require(senderKey == MANAGEMENT_KEY || senderKey == ACTION_KEY);
- executionId = keccak256(_to, _value, _data, nonce);
- ExecutionRequested(executionId, _to, _value, _data);
- txx[executionId] = Transaction (
- {
- to: _to,
- value: _value,
- data: _data,
- nonce: nonce
- });
- if (senderKey == MANAGEMENT_KEY) {
- approve(executionId, true);
- }
- }
-
- function approve(
- bytes32 _id,
- bool _approve
- )
- public
- managerOnly
- returns (bool success)
- {
- require(txx[_id].nonce == nonce);
- nonce++;
- if (_approve) {
- success = txx[_id].to.call.value(txx[_id].value)(txx[_id].data);
- }
- }
-
-
- function addClaim(
- uint256 _claimType,
- address _issuer,
- uint256 _signatureType,
- bytes _signature,
- bytes _claim,
- string _uri
- )
- public
- claimSignerOnly
- returns (bytes32 claimId)
- {
- claimId = keccak256(_issuer, _claimType);
- claims[claimId] = Claim(
- {
- claimType: _claimType,
- issuer: _issuer,
- signatureType: _signatureType,
- signature: _signature,
- claim: _claim,
- uri: _uri
- }
- );
- indexes[keccak256(_issuer, _claimType)] = claimsByType[_claimType].length;
- claimsByType[_claimType].push(claimId);
- }
-
-
- function removeClaim(bytes32 _claimId) public returns (bool success) {
- Claim memory c = claims[_claimId];
- require(msg.sender == c.issuer || keys[msg.sender] == MANAGEMENT_KEY || msg.sender == address(this));
- uint claimIdTypePos = indexes[_claimId];
- delete indexes[_claimId];
- bytes32[] storage claimsTypeArr = claimsByType[c.claimType];
- bytes32 replacer = claimsTypeArr[claimsTypeArr.length-1];
- claimsTypeArr[claimIdTypePos] = replacer;
- indexes[replacer] = claimIdTypePos;
- delete claims[_claimId];
- claimsTypeArr.length--;
- return true;
- }
-
-
- function _addKey(address _key, uint256 _type) private {
- require(keys[_key] == 0);
- KeyAdded(_key, _type);
- keys[_key] = _type;
- indexes[keccak256(_key, _type)] = keysByType[_type].length;
- keysByType[_type].push(_key);
- }
-
-
- function _removeKey(address _key) private {
- uint256 kType = keys[_key];
- KeyRemoved(_key, kType);
- address[] storage keyArr = keysByType[kType];
- if (msg.sender != address(this) && kType == MANAGEMENT_KEY && keyArr.length == 1) {
- revert();
- }
- bytes32 oldIndex = keccak256(_key, kType);
- uint index = indexes[oldIndex];
- delete indexes[oldIndex];
- address replacer = keyArr[keyArr.length-1];
- keyArr[index] = replacer;
- indexes[keccak256(replacer, keys[replacer])] = index;
- keyArr.length--;
- delete keys[_key];
- }
-
-
- function getKeyType(address _key) public constant returns(uint256 keyType) {
- return keys[_key];
- }
-
-
- function getKeysByType(uint256 _type) public constant returns(address[]) {
- return keysByType[_type];
- }
-
-
- function getClaim(
- bytes32 _claimId
- )
- public
- constant
- returns
- (uint256 claimType,
- address issuer,
- uint256 signatureType,
- bytes signature,
- bytes claim,
- string uri)
- {
- Claim memory _claim = claims[_claimId];
- return (_claim.claimType, _claim.issuer, _claim.signatureType, _claim.signature, _claim.claim, _claim.uri);
- }
-
-
- function getClaimsIdByType(uint256 _claimType) public constant returns(bytes32[]) {
- return claimsByType[_claimType];
- }
+ mapping (bytes32 => Key) keys;
+ mapping (bytes32 => Claim) claims;
+ mapping (uint256 => bytes32[]) keysByPurpose;
+ mapping (uint256 => bytes32[]) claimsByType;
+ mapping (bytes32 => uint256) indexes;
+ mapping (uint => Transaction) txx;
+ mapping (uint256 => uint8) minimumApprovalsByKeyType;
+ bytes32[] pendingTransactions;
+
+ uint nonce = 0;
+ struct Transaction {
+ address to;
+ uint value;
+ bytes data;
+ uint nonce;
+
+ uint approverCount;
+ mapping(uint256 => uint8) approvalsByKeyType;
+ mapping(bytes32 => bool) approvals;
}
+ modifier managerOnly {
+ require(keys[keccak256(bytes32(msg.sender), MANAGEMENT_KEY)].purpose == MANAGEMENT_KEY);
+ _;
+ }
+
+ modifier managerOrSelf {
+ require(keys[keccak256(bytes32(msg.sender), MANAGEMENT_KEY)].purpose == MANAGEMENT_KEY || msg.sender == address(this));
+ _;
+ }
+
+ modifier actorOnly {
+ require(keys[keccak256(bytes32(msg.sender), ACTION_KEY)].purpose == ACTION_KEY);
+ _;
+ }
+
+ modifier claimSignerOnly {
+ require(keys[keccak256(bytes32(msg.sender), CLAIM_SIGNER_KEY)].purpose == CLAIM_SIGNER_KEY);
+ _;
+ }
+
+ modifier managerOrActor {
+ require(keys[keccak256(bytes32(msg.sender), MANAGEMENT_KEY)].purpose == MANAGEMENT_KEY
+ || keys[keccak256(bytes32(msg.sender), ACTION_KEY)].purpose == ACTION_KEY
+ || msg.sender == address(this));
+ _;
+ }
+
+ function Identity() public {
+ _addKey(bytes32(msg.sender), MANAGEMENT_KEY, 1);
+ minimumApprovalsByKeyType[MANAGEMENT_KEY] = 1;
+ }
+
+ function addKey(bytes32 _key, uint256 _purpose, uint256 _type) public managerOrSelf returns (bool success) {
+ _addKey(_key, _purpose, _type);
+ return true;
+ }
+
+ function removeKey(bytes32 _key, uint256 _purpose) public managerOrSelf returns (bool success) {
+ _removeKey(_key, _purpose);
+ return true;
+ }
+
+ function execute(address _to, uint256 _value, bytes _data)
+ public
+ managerOrActor
+ returns (uint256 executionId)
+ {
+ executionId = nonce;
+ ExecutionRequested(executionId, _to, _value, _data);
+ txx[executionId] = Transaction(
+ {
+ to: _to,
+ value: _value,
+ data: _data,
+ nonce: nonce,
+ approverCount: 0
+ });
+ nonce++;
+ }
+
+ function approve(uint256 _id, bool _approve)
+ public
+ managerOrActor
+ returns (bool success)
+ {
+ Transaction storage trx = txx[_id];
+
+ bytes32 managerKeyHash = keccak256(bytes32(msg.sender), MANAGEMENT_KEY);
+ bytes32 actorKeyHash = keccak256(bytes32(msg.sender), ACTION_KEY);
+
+ uint8 approvalCount;
+ uint256 requiredKeyType;
+
+ if (trx.to == address(this)) {
+ requiredKeyType = MANAGEMENT_KEY;
+ if (keys[managerKeyHash].purpose == MANAGEMENT_KEY) {
+ approvalCount = _calculateApprovals(managerKeyHash, MANAGEMENT_KEY, _approve, trx);
+ }
+ } else {
+ requiredKeyType = ACTION_KEY;
+ if (keys[managerKeyHash].purpose == ACTION_KEY) {
+ approvalCount = _calculateApprovals(actorKeyHash, ACTION_KEY, _approve, trx);
+ }
+ }
+
+ if (approvalCount >= minimumApprovalsByKeyType[requiredKeyType])
+ success = trx.to.call.value(txx[_id].value)(txx[_id].data);
+
+ }
+
+ function setMiminumApprovalsByKeyType(uint256 _type, uint8 _minimumApprovals) public managerOrSelf {
+ minimumApprovalsByKeyType[_type] = _minimumApprovals;
+ }
+
+ function _calculateApprovals(bytes32 _keyHash, uint256 _keyType, bool _approve, Transaction storage trx)
+ private
+ returns (uint8 approvalCount)
+ {
+ if (trx.approvals[_keyHash] == false && _approve){
+ trx.approvalsByKeyType[_keyType]++;
+ } else if (trx.approvals[_keyHash] == true && !_approve && trx.approverCount > 0) {
+ trx.approvalsByKeyType[_keyType]--;
+ }
+ trx.approvals[_keyHash] = _approve;
+ trx.approverCount++;
+ return trx.approvalsByKeyType[_keyType];
+ }
+
+ function addClaim(uint256 _claimType, uint256 _scheme, address _issuer, bytes _signature, bytes _data, string _uri)
+ public
+ claimSignerOnly
+ returns (bytes32 claimRequestId)
+ {
+
+ bytes32 claimHash = keccak256(_issuer, _claimType);
+
+ claimRequestId = claimHash;
+
+ if (claims[claimHash].claimType > 0) {
+ // Claim existed
+ ClaimChanged(claimRequestId, _claimType, _scheme, _issuer, _signature, _data, _uri);
+ } else {
+ // TODO Triggers if the claim is new Event and approval process exists: ClaimRequested
+ ClaimRequested(claimRequestId, _claimType, _scheme, _issuer, _signature, _data, _uri);
+ }
+
+ claims[claimHash] = Claim(
+ {
+ claimType: _claimType,
+ scheme: _scheme,
+ issuer: _issuer,
+ signature: _signature,
+ data: _data,
+ uri: _uri
+ }
+ );
+
+ indexes[claimHash] = claimsByType[_claimType].length;
+
+ claimsByType[_claimType].push(claimRequestId);
+
+ // TODO This SHOULD create a pending claim, which SHOULD to be approved or rejected by n of m approve calls from keys of purpose 1.
+ }
+
+ function removeClaim(bytes32 _claimId) public returns (bool success) {
+ Claim memory c = claims[_claimId];
+
+ require(msg.sender == c.issuer
+ || msg.sender == address(this)
+ || keys[keccak256(bytes32(msg.sender), MANAGEMENT_KEY)].purpose == MANAGEMENT_KEY);
+
+ // MUST only be done by the issuer of the claim, or KEYS OF PURPOSE 1, or the identity itself.
+ // TODO If its the identity itself, the approval process will determine its approval.
+
+ uint claimIdTypePos = indexes[_claimId];
+ delete indexes[_claimId];
+ bytes32[] storage claimsTypeArr = claimsByType[c.claimType];
+ bytes32 replacer = claimsTypeArr[claimsTypeArr.length-1];
+ claimsTypeArr[claimIdTypePos] = replacer;
+ indexes[replacer] = claimIdTypePos;
+ delete claims[_claimId];
+ claimsTypeArr.length--;
+ return true;
+ }
+
+ function _addKey(bytes32 _key, uint256 _purpose, uint256 _type) private {
+ bytes32 keyHash = keccak256(_key, _purpose);
+
+ require(keys[keyHash].purpose == 0);
+ require(_purpose == MANAGEMENT_KEY || _purpose == ACTION_KEY
+ || _purpose == CLAIM_SIGNER_KEY || _purpose == ENCRYPTION_KEY);
+ KeyAdded(_key, _purpose, _type);
+ keys[keyHash] = Key(_purpose, _type, _key);
+ indexes[keyHash] = keysByPurpose[_purpose].push(_key) - 1;
+ }
+
+ function _removeKey(bytes32 _key, uint256 _purpose) private {
+ bytes32 keyHash = keccak256(_key, _purpose);
+ Key storage myKey = keys[keyHash];
+ KeyRemoved(myKey.key, myKey.purpose, myKey.keyType);
+
+ uint index = indexes[keyHash];
+ delete indexes[keyHash];
+ bytes32 replacer = keysByPurpose[_purpose][keysByPurpose[_purpose].length - 1];
+ keysByPurpose[_purpose][index] = replacer;
+ indexes[ keccak256(replacer, _purpose)] = index;
+ keysByPurpose[_purpose].length--;
+
+ if (_purpose == MANAGEMENT_KEY) {
+ require(keysByPurpose[MANAGEMENT_KEY].length >= 1);
+ }
+
+ delete keys[keyHash];
+
+ // MUST only be done by keys of purpose 1, or the identity itself.
+ // TODO If its the identity itself, the approval process will determine its approval.
+ }
+
+ function getKey(bytes32 _key, uint256 _purpose) public constant returns(uint256 purpose, uint256 keyType, bytes32 key) {
+ Key storage myKey = keys[keccak256(_key, _purpose)];
+ return (myKey.purpose, myKey.keyType, myKey.key);
+ }
+
+ function getKeyPurpose(bytes32 _key) public constant returns(uint256[] purpose) {
+
+ uint256[] memory purposeHolder = new uint256[](4);
+ uint8 counter = 0;
+
+ if (keys[keccak256(_key, MANAGEMENT_KEY)].purpose == MANAGEMENT_KEY) {
+ purposeHolder[counter] = MANAGEMENT_KEY;
+ counter++;
+ }
+
+ if (keys[keccak256(_key, ACTION_KEY)].purpose == ACTION_KEY) {
+ purposeHolder[counter] = ACTION_KEY;
+ counter++;
+ }
+
+ if (keys[keccak256(_key, CLAIM_SIGNER_KEY)].purpose == CLAIM_SIGNER_KEY) {
+ purposeHolder[counter] = CLAIM_SIGNER_KEY;
+ counter++;
+ }
+
+ if (keys[keccak256(_key, ENCRYPTION_KEY)].purpose == ENCRYPTION_KEY) {
+ purposeHolder[counter] = ENCRYPTION_KEY;
+ counter++;
+ }
+
+ uint256[] memory result = new uint256[](counter);
+ for (uint8 i = 0; i < counter; i++)
+ result[i] = purposeHolder[i];
+
+ return result;
+ }
+
+ function getKeysByPurpose(uint256 _purpose) public constant returns(bytes32[] keys) {
+ return keysByPurpose[_purpose];
+ }
+
+ function getClaim(bytes32 _claimId) public constant returns(uint256 claimType, uint256 scheme, address issuer, bytes signature, bytes data, string uri) {
+ Claim memory _claim = claims[_claimId];
+ return (_claim.claimType, _claim.scheme, _claim.issuer, _claim.signature, _claim.data, _claim.uri);
+ }
+
+ function getClaimIdsByType(uint256 _claimType) public constant returns(bytes32[] claimIds) {
+ return claimsByType[_claimType];
+ }
+}
+
diff --git a/test/identity.js b/test/identity.js
index 55a046d..60d7c25 100644
--- a/test/identity.js
+++ b/test/identity.js
@@ -4,18 +4,18 @@ const Identity = artifacts.require("./identity/Identity.sol");
contract('Identity', function(accounts) {
let identity;
+
beforeEach(async () => {
identity = await Identity.new({from: accounts[0]})
})
describe("Identity()", () => {
-
it("initialize with msg.sender as management key", async () => {
assert.equal(
- await identity.getKeyType(accounts[0]),
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
1,
- identity.address+".getKeyType("+accounts[0]+") is not MANAGEMENT_KEY")
+ identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY")
});
});
@@ -24,33 +24,33 @@ contract('Identity', function(accounts) {
describe("addKey(address _key, uint256 _type)", () => {
it("MANAGEMENT_KEY add a new address as ACTION_KEY", async () => {
- await identity.addKey(accounts[1], 2, {from: accounts[0]})
+ await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 2, 1, {from: accounts[0]})
assert.equal(
- await identity.getKeyType(accounts[1]),
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
2,
- identity.address+".getKeyType("+accounts[1]+") is not ACTION_KEY")
+ identity.address+".getKeyPurpose("+accounts[1]+") is not ACTION_KEY")
});
it("should not add key by non manager", async () => {
try {
- await identity.addKey(accounts[1], 1, {from: accounts[2]})
+ await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 1, 1, {from: accounts[2]})
}catch(e){
}
assert.equal(
- await identity.getKeyType(accounts[1]),
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
0,
- identity.address+".getKeyType("+accounts[1]+") is not correct")
+ identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
it("should not add key type 1 by actor", async () => {
- await identity.addKey(accounts[2], 2, {from: accounts[0]})
+ await identity.addKey(TestUtils.addressToBytes32(accounts[2]), 2, 1, {from: accounts[0]})
try {
- await identity.addKey(accounts[1], 1, {from: accounts[2]})
+ await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 1, 1, {from: accounts[2]})
} catch(e){
}
assert.equal(
- await identity.getKeyType(accounts[1]),
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
0,
identity.address+".getKeyType("+accounts[1]+") is not correct")
});
@@ -58,9 +58,9 @@ contract('Identity', function(accounts) {
it("fire KeyAdded(address indexed key, uint256 indexed type)", async () => {
- identity.addKey(accounts[1], 2, {from: accounts[0]})
+ identity.addKey(TestUtils.addressToBytes32(accounts[1]), 2, 1, {from: accounts[0]})
const keyAdded = await TestUtils.listenForEvent(identity.KeyAdded())
- assert(keyAdded.key, accounts[1], "Key is not correct")
+ assert(keyAdded.key, TestUtils.addressToBytes32(accounts[1]), "Key is not correct")
assert(keyAdded.keyType, 2, "Type is not correct")
});
@@ -70,100 +70,101 @@ contract('Identity', function(accounts) {
describe("removeKey(address _key, uint256 _type)", () => {
it("MANAGEMENT_KEY should removes a key", async () => {
- await identity.addKey(accounts[1], 1, {from: accounts[0]})
- await identity.removeKey(accounts[0], {from: accounts[1]})
+ await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 1, 1, {from: accounts[0]})
+ await identity.removeKey(TestUtils.addressToBytes32(accounts[0]), 1, {from: accounts[1]})
assert.equal(
- await identity.getKeyType(accounts[0]),
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
0,
- identity.address+".getKeyType("+accounts[0]+") is not 0")
+ identity.address+".getKeyPurpose("+accounts[0]+") is not 0")
});
it("other key should not removes a key", async () => {
- await identity.addKey(accounts[1], 1, {from: accounts[0]})
+ await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 1, 1, {from: accounts[0]})
try {
- await identity.removeKey(accounts[1], {from: accounts[2]})
+ await identity.removeKey(TestUtils.addressToBytes32(accounts[1]), 1, {from: accounts[2]})
}catch (e) {
}
assert.equal(
- await identity.getKeyType(accounts[1]),
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
1,
- identity.address+".getKeyType("+accounts[1]+") is not 0")
+ identity.address+".getKeyPurpose("+accounts[1]+") is not 0")
});
it("actor key should not remove key", async () => {
- await identity.addKey(accounts[1], 2, {from: accounts[0]})
- await identity.addKey(accounts[2], 2, {from: accounts[0]})
+ await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 2, 1, {from: accounts[0]})
+ await identity.addKey(TestUtils.addressToBytes32(accounts[2]), 2, 1, {from: accounts[0]})
try {
- await identity.removeKey(accounts[1], {from: accounts[2]})
+ await identity.removeKey(TestUtils.addressToBytes32(accounts[1]), 1, {from: accounts[2]})
}catch (e) {
}
assert.equal(
- await identity.getKeyType(accounts[1]),
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
2,
identity.address+".getKeyType("+accounts[1]+") is not 0")
});
it("MANAGEMENT_KEY should not remove itself MANAGEMENT_KEY when there is no other MANAGEMENT_KEY", async () => {
- try {
- await identity.removeKey(accounts[0], {from: accounts[0]})
- } catch(e) {
- //nothing
+ let assertJump = (error) => {
+ assert.isAbove(error.message.search('revert'), -1, 'Revert should happen');
}
- assert.equal(
- await identity.getKeyType(accounts[0]),
- 1,
- identity.address+".getKeyType("+accounts[0]+") is not MANAGEMENT_KEY")
-
+ try {
+ await identity.removeKey(TestUtils.addressToBytes32(accounts[0]), 1, {from: accounts[0]});
+ assert.fail('should have reverted before');
+ } catch(error) {
+ assertJump(error);
+ }
+
+
});
it("fire KeyRemoved(address indexed key, uint256 indexed type)", async () => {
- await identity.addKey(accounts[1], 2, {from: accounts[0]})
- identity.removeKey(accounts[1], {from: accounts[0]})
+ await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 2, 1,{from: accounts[0]})
+ identity.removeKey(TestUtils.addressToBytes32(accounts[1]), 2, {from: accounts[0]})
const keyRemoved = await TestUtils.listenForEvent(identity.KeyRemoved())
- assert(keyRemoved.key, accounts[1], "Key is not correct")
+ assert(keyRemoved.key, TestUtils.addressToBytes32(accounts[1]), "Key is not correct")
assert(keyRemoved.keyType, 2, "Type is not correct")
});
});
- describe("getKeyType(address _key)", () => {
+ describe("getKeyPurpose(address _key)", () => {
it("should start only with initializer as only key", async () => {
assert.equal(
- await identity.getKeyType(accounts[0]),
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
1,
- identity.address+".getKeyType("+accounts[0]+") is not correct")
+ identity.address+".getKeyPurpose("+accounts[0]+") is not correct")
assert.equal(
- await identity.getKeyType(accounts[1]),
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
0,
- identity.address+".getKeyType("+accounts[1]+") is not correct")
+ identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
it("should get type 2 after addKey type 2", async () => {
- await identity.addKey(accounts[1], 2, {from: accounts[0]})
+ await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 2, 1, {from: accounts[0]})
assert.equal(
- await identity.getKeyType(accounts[1]),
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
2,
- identity.address+".getKeyType("+accounts[1]+") is not correct")
+ identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
- it("should get type 999 after addKey type 999", async () => {
- await identity.addKey(accounts[1], 999, {from: accounts[0]})
+ it("should get type 3 after addKey type 3", async () => {
+ await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 3, 1, {from: accounts[0]})
assert.equal(
- await identity.getKeyType(accounts[1]),
- 999,
- identity.address+".getKeyType("+accounts[1]+") is not correct")
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
+ 3,
+ identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
});
-
+
describe("getKeysByType(uint256 _type)", () => {
it("at initialization", async () => {
@@ -185,30 +186,6 @@ contract('Identity', function(accounts) {
});
- describe("replaceKey(address _oldKey, address _newKey)", () => {
-
- it("MANAGEMENT_KEY replace itself (alone)", async () => {
-
- });
-
- it("MANAGEMENT_KEY replace a key between others", async () => {
-
- });
-
- it("MANAGEMENT_KEY replace the first key", async () => {
-
- });
-
- it("MANAGEMENT_KEY replace the last key", async () => {
-
- });
-
- it("fire KeyReplaced(address indexed oldKey, address indexed newKey, uint256 indexed type)", async () => {
-
- });
-
- });
-
describe("execute(address _to, uint256 _value, bytes _data)", () => {
@@ -282,5 +259,5 @@ contract('Identity', function(accounts) {
});
});
-
+
});
From 89bcc351638d5dc0ef2d2808687bdc8141a0786b Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 21 Feb 2018 19:23:41 -0300
Subject: [PATCH 011/102] remove self from modifier
---
contracts/identity/Identity.sol | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 13c5357..e1af773 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -51,8 +51,7 @@ contract Identity is ERC725, ERC735 {
modifier managerOrActor {
require(keys[keccak256(bytes32(msg.sender), MANAGEMENT_KEY)].purpose == MANAGEMENT_KEY
- || keys[keccak256(bytes32(msg.sender), ACTION_KEY)].purpose == ACTION_KEY
- || msg.sender == address(this));
+ || keys[keccak256(bytes32(msg.sender), ACTION_KEY)].purpose == ACTION_KEY));
_;
}
From 5afddb69765f1710140ba385055af9a73b379d5d Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 21 Feb 2018 19:45:25 -0300
Subject: [PATCH 012/102] encapsulate reusable code
---
contracts/identity/Identity.sol | 26 +++++++++++++++-----------
1 file changed, 15 insertions(+), 11 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index e1af773..ae9f058 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -30,28 +30,28 @@ contract Identity is ERC725, ERC735 {
}
modifier managerOnly {
- require(keys[keccak256(bytes32(msg.sender), MANAGEMENT_KEY)].purpose == MANAGEMENT_KEY);
+ require(isKeyType(bytes32(msg.sender), MANAGEMENT_KEY));
_;
}
modifier managerOrSelf {
- require(keys[keccak256(bytes32(msg.sender), MANAGEMENT_KEY)].purpose == MANAGEMENT_KEY || msg.sender == address(this));
+ require(isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) || msg.sender == address(this));
_;
}
modifier actorOnly {
- require(keys[keccak256(bytes32(msg.sender), ACTION_KEY)].purpose == ACTION_KEY);
+ require(isKeyType(bytes32(msg.sender), ACTION_KEY));
_;
}
modifier claimSignerOnly {
- require(keys[keccak256(bytes32(msg.sender), CLAIM_SIGNER_KEY)].purpose == CLAIM_SIGNER_KEY);
+ require(isKeyType(bytes32(msg.sender), CLAIM_SIGNER_KEY));
_;
}
modifier managerOrActor {
- require(keys[keccak256(bytes32(msg.sender), MANAGEMENT_KEY)].purpose == MANAGEMENT_KEY
- || keys[keccak256(bytes32(msg.sender), ACTION_KEY)].purpose == ACTION_KEY));
+ require(isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) ||
+ isKeyType(bytes32(msg.sender), ACTION_KEY));
_;
}
@@ -177,7 +177,7 @@ contract Identity is ERC725, ERC735 {
require(msg.sender == c.issuer
|| msg.sender == address(this)
- || keys[keccak256(bytes32(msg.sender), MANAGEMENT_KEY)].purpose == MANAGEMENT_KEY);
+ || isKeyType(bytes32(msg.sender), MANAGEMENT_KEY));
// MUST only be done by the issuer of the claim, or KEYS OF PURPOSE 1, or the identity itself.
// TODO If its the identity itself, the approval process will determine its approval.
@@ -230,28 +230,32 @@ contract Identity is ERC725, ERC735 {
Key storage myKey = keys[keccak256(_key, _purpose)];
return (myKey.purpose, myKey.keyType, myKey.key);
}
+
+ function isKeyType(bytes32 _key, uint256 _type) constant public returns (bool){
+ return keys[keccak256(_key, _type)].purpose == _type;
+ }
function getKeyPurpose(bytes32 _key) public constant returns(uint256[] purpose) {
uint256[] memory purposeHolder = new uint256[](4);
uint8 counter = 0;
- if (keys[keccak256(_key, MANAGEMENT_KEY)].purpose == MANAGEMENT_KEY) {
+ if (isKeyType(_key, MANAGEMENT_KEY)) {
purposeHolder[counter] = MANAGEMENT_KEY;
counter++;
}
- if (keys[keccak256(_key, ACTION_KEY)].purpose == ACTION_KEY) {
+ if (isKeyType(_key, ACTION_KEY)) {
purposeHolder[counter] = ACTION_KEY;
counter++;
}
- if (keys[keccak256(_key, CLAIM_SIGNER_KEY)].purpose == CLAIM_SIGNER_KEY) {
+ if (isKeyType(_key, CLAIM_SIGNER_KEY)) {
purposeHolder[counter] = CLAIM_SIGNER_KEY;
counter++;
}
- if (keys[keccak256(_key, ENCRYPTION_KEY)].purpose == ENCRYPTION_KEY) {
+ if (isKeyType(_key, ENCRYPTION_KEY)) {
purposeHolder[counter] = ENCRYPTION_KEY;
counter++;
}
From 310f4b8f5a8aa6ef73cf42a9dcd7602483d770cc Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Thu, 22 Feb 2018 05:05:10 -0300
Subject: [PATCH 013/102] linting
---
contracts/identity/Identity.sol | 140 +++++++++++++++++++++++++-------
1 file changed, 111 insertions(+), 29 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index ae9f058..0e463fd 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -14,8 +14,6 @@ contract Identity is ERC725, ERC735 {
mapping (uint => Transaction) txx;
mapping (uint256 => uint8) minimumApprovalsByKeyType;
bytes32[] pendingTransactions;
-
-
uint nonce = 0;
struct Transaction {
@@ -23,7 +21,6 @@ contract Identity is ERC725, ERC735 {
uint value;
bytes data;
uint nonce;
-
uint approverCount;
mapping(uint256 => uint8) approvalsByKeyType;
mapping(bytes32 => bool) approvals;
@@ -35,7 +32,10 @@ contract Identity is ERC725, ERC735 {
}
modifier managerOrSelf {
- require(isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) || msg.sender == address(this));
+ require(
+ isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) ||
+ msg.sender == address(this)
+ );
_;
}
@@ -50,8 +50,10 @@ contract Identity is ERC725, ERC735 {
}
modifier managerOrActor {
- require(isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) ||
- isKeyType(bytes32(msg.sender), ACTION_KEY));
+ require(
+ isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) ||
+ isKeyType(bytes32(msg.sender), ACTION_KEY)
+ );
_;
}
@@ -60,17 +62,36 @@ contract Identity is ERC725, ERC735 {
minimumApprovalsByKeyType[MANAGEMENT_KEY] = 1;
}
- function addKey(bytes32 _key, uint256 _purpose, uint256 _type) public managerOrSelf returns (bool success) {
+ function addKey(
+ bytes32 _key,
+ uint256 _purpose,
+ uint256 _type
+ )
+ public
+ managerOrSelf
+ returns (bool success)
+ {
_addKey(_key, _purpose, _type);
return true;
}
- function removeKey(bytes32 _key, uint256 _purpose) public managerOrSelf returns (bool success) {
+ function removeKey(
+ bytes32 _key,
+ uint256 _purpose
+ )
+ public
+ managerOrSelf
+ returns (bool success)
+ {
_removeKey(_key, _purpose);
return true;
}
- function execute(address _to, uint256 _value, bytes _data)
+ function execute(
+ address _to,
+ uint256 _value,
+ bytes _data
+ )
public
managerOrActor
returns (uint256 executionId)
@@ -96,7 +117,7 @@ contract Identity is ERC725, ERC735 {
Transaction storage trx = txx[_id];
bytes32 managerKeyHash = keccak256(bytes32(msg.sender), MANAGEMENT_KEY);
- bytes32 actorKeyHash = keccak256(bytes32(msg.sender), ACTION_KEY);
+ bytes32 actorKeyHash = keccak256(bytes32(msg.sender), ACTION_KEY);
uint8 approvalCount;
uint256 requiredKeyType;
@@ -113,8 +134,9 @@ contract Identity is ERC725, ERC735 {
}
}
- if (approvalCount >= minimumApprovalsByKeyType[requiredKeyType])
+ if (approvalCount >= minimumApprovalsByKeyType[requiredKeyType]) {
success = trx.to.call.value(txx[_id].value)(txx[_id].data);
+ }
}
@@ -122,11 +144,16 @@ contract Identity is ERC725, ERC735 {
minimumApprovalsByKeyType[_type] = _minimumApprovals;
}
- function _calculateApprovals(bytes32 _keyHash, uint256 _keyType, bool _approve, Transaction storage trx)
+ function _calculateApprovals(
+ bytes32 _keyHash,
+ uint256 _keyType,
+ bool _approve,
+ Transaction storage trx
+ )
private
returns (uint8 approvalCount)
{
- if (trx.approvals[_keyHash] == false && _approve){
+ if (trx.approvals[_keyHash] == false && _approve) {
trx.approvalsByKeyType[_keyType]++;
} else if (trx.approvals[_keyHash] == true && !_approve && trx.approverCount > 0) {
trx.approvalsByKeyType[_keyType]--;
@@ -136,7 +163,14 @@ contract Identity is ERC725, ERC735 {
return trx.approvalsByKeyType[_keyType];
}
- function addClaim(uint256 _claimType, uint256 _scheme, address _issuer, bytes _signature, bytes _data, string _uri)
+ function addClaim(
+ uint256 _claimType,
+ uint256 _scheme,
+ address _issuer,
+ bytes _signature,
+ bytes _data,
+ string _uri
+ )
public
claimSignerOnly
returns (bytes32 claimRequestId)
@@ -148,10 +182,24 @@ contract Identity is ERC725, ERC735 {
if (claims[claimHash].claimType > 0) {
// Claim existed
- ClaimChanged(claimRequestId, _claimType, _scheme, _issuer, _signature, _data, _uri);
+ ClaimChanged(
+ claimRequestId,
+ _claimType,
+ _scheme,
+ _issuer,
+ _signature,
+ _data,
+ _uri);
} else {
// TODO Triggers if the claim is new Event and approval process exists: ClaimRequested
- ClaimRequested(claimRequestId, _claimType, _scheme, _issuer, _signature, _data, _uri);
+ ClaimRequested(
+ claimRequestId,
+ _claimType,
+ _scheme,
+ _issuer,
+ _signature,
+ _data,
+ _uri);
}
claims[claimHash] = Claim(
@@ -175,9 +223,11 @@ contract Identity is ERC725, ERC735 {
function removeClaim(bytes32 _claimId) public returns (bool success) {
Claim memory c = claims[_claimId];
- require(msg.sender == c.issuer
- || msg.sender == address(this)
- || isKeyType(bytes32(msg.sender), MANAGEMENT_KEY));
+ require(
+ msg.sender == c.issuer ||
+ msg.sender == address(this) ||
+ isKeyType(bytes32(msg.sender), MANAGEMENT_KEY)
+ );
// MUST only be done by the issuer of the claim, or KEYS OF PURPOSE 1, or the identity itself.
// TODO If its the identity itself, the approval process will determine its approval.
@@ -197,8 +247,12 @@ contract Identity is ERC725, ERC735 {
bytes32 keyHash = keccak256(_key, _purpose);
require(keys[keyHash].purpose == 0);
- require(_purpose == MANAGEMENT_KEY || _purpose == ACTION_KEY
- || _purpose == CLAIM_SIGNER_KEY || _purpose == ENCRYPTION_KEY);
+ require(
+ _purpose == MANAGEMENT_KEY ||
+ _purpose == ACTION_KEY ||
+ _purpose == CLAIM_SIGNER_KEY ||
+ _purpose == ENCRYPTION_KEY
+ );
KeyAdded(_key, _purpose, _type);
keys[keyHash] = Key(_purpose, _type, _key);
indexes[keyHash] = keysByPurpose[_purpose].push(_key) - 1;
@@ -213,7 +267,7 @@ contract Identity is ERC725, ERC735 {
delete indexes[keyHash];
bytes32 replacer = keysByPurpose[_purpose][keysByPurpose[_purpose].length - 1];
keysByPurpose[_purpose][index] = replacer;
- indexes[ keccak256(replacer, _purpose)] = index;
+ indexes[keccak256(replacer, _purpose)] = index;
keysByPurpose[_purpose].length--;
if (_purpose == MANAGEMENT_KEY) {
@@ -226,16 +280,31 @@ contract Identity is ERC725, ERC735 {
// TODO If its the identity itself, the approval process will determine its approval.
}
- function getKey(bytes32 _key, uint256 _purpose) public constant returns(uint256 purpose, uint256 keyType, bytes32 key) {
+ function getKey(
+ bytes32 _key,
+ uint256 _purpose
+ )
+ public
+ constant
+ returns(uint256 purpose, uint256 keyType, bytes32 key)
+ {
Key storage myKey = keys[keccak256(_key, _purpose)];
return (myKey.purpose, myKey.keyType, myKey.key);
}
- function isKeyType(bytes32 _key, uint256 _type) constant public returns (bool){
+ function isKeyType(bytes32 _key, uint256 _type)
+ public
+ constant
+ returns (bool)
+ {
return keys[keccak256(_key, _type)].purpose == _type;
}
- function getKeyPurpose(bytes32 _key) public constant returns(uint256[] purpose) {
+ function getKeyPurpose(bytes32 _key)
+ public
+ constant
+ returns(uint256[] purpose)
+ {
uint256[] memory purposeHolder = new uint256[](4);
uint8 counter = 0;
@@ -261,22 +330,35 @@ contract Identity is ERC725, ERC735 {
}
uint256[] memory result = new uint256[](counter);
- for (uint8 i = 0; i < counter; i++)
+ for (uint8 i = 0; i < counter; i++) {
result[i] = purposeHolder[i];
+ }
return result;
}
- function getKeysByPurpose(uint256 _purpose) public constant returns(bytes32[] keys) {
+ function getKeysByPurpose(uint256 _purpose)
+ public
+ constant
+ returns(bytes32[] keys)
+ {
return keysByPurpose[_purpose];
}
- function getClaim(bytes32 _claimId) public constant returns(uint256 claimType, uint256 scheme, address issuer, bytes signature, bytes data, string uri) {
+ function getClaim(bytes32 _claimId)
+ public
+ constant
+ returns(uint256 claimType, uint256 scheme, address issuer, bytes signature, bytes data, string uri)
+ {
Claim memory _claim = claims[_claimId];
return (_claim.claimType, _claim.scheme, _claim.issuer, _claim.signature, _claim.data, _claim.uri);
}
- function getClaimIdsByType(uint256 _claimType) public constant returns(bytes32[] claimIds) {
+ function getClaimIdsByType(uint256 _claimType)
+ public
+ constant
+ returns(bytes32[] claimIds)
+ {
return claimsByType[_claimType];
}
}
From bab9e88c1ae64c9a21b198df32f482322a5bf4dc Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Thu, 22 Feb 2018 05:16:06 -0300
Subject: [PATCH 014/102] manager should call only by execute
---
contracts/identity/Identity.sol | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 0e463fd..2bf49bb 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -31,11 +31,8 @@ contract Identity is ERC725, ERC735 {
_;
}
- modifier managerOrSelf {
- require(
- isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) ||
- msg.sender == address(this)
- );
+ modifier selfOnly {
+ require(msg.sender == address(this));
_;
}
@@ -68,7 +65,7 @@ contract Identity is ERC725, ERC735 {
uint256 _type
)
public
- managerOrSelf
+ selfOnly
returns (bool success)
{
_addKey(_key, _purpose, _type);
@@ -80,7 +77,7 @@ contract Identity is ERC725, ERC735 {
uint256 _purpose
)
public
- managerOrSelf
+ selfOnly
returns (bool success)
{
_removeKey(_key, _purpose);
@@ -140,7 +137,13 @@ contract Identity is ERC725, ERC735 {
}
- function setMiminumApprovalsByKeyType(uint256 _type, uint8 _minimumApprovals) public managerOrSelf {
+ function setMiminumApprovalsByKeyType(
+ uint256 _type,
+ uint8 _minimumApprovals
+ )
+ public
+ selfOnly
+ {
minimumApprovalsByKeyType[_type] = _minimumApprovals;
}
From a9f2a186d9324729cd11db31f412f9c316074e10 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Thu, 22 Feb 2018 05:19:38 -0300
Subject: [PATCH 015/102] auto-approve
---
contracts/identity/Identity.sol | 1 +
1 file changed, 1 insertion(+)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 2bf49bb..26afa9f 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -104,6 +104,7 @@ contract Identity is ERC725, ERC735 {
approverCount: 0
});
nonce++;
+ approve(executionId, true);
}
function approve(uint256 _id, bool _approve)
From 5f7de6e981d626c06a3a648c15cb7c4b8bea89e2 Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Thu, 22 Feb 2018 14:58:25 -0400
Subject: [PATCH 016/102] - Added logic for handling approvals of claims
---
contracts/identity/Identity.sol | 116 +++++++++++++++++++++-----------
1 file changed, 78 insertions(+), 38 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 26afa9f..6f06bff 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -12,18 +12,29 @@ contract Identity is ERC725, ERC735 {
mapping (uint256 => bytes32[]) claimsByType;
mapping (bytes32 => uint256) indexes;
mapping (uint => Transaction) txx;
+ mapping (uint => Action) actionCatalogByTrx;
+ mapping (uint => bytes32) claimCatalogByTrx;
+
mapping (uint256 => uint8) minimumApprovalsByKeyType;
- bytes32[] pendingTransactions;
+ mapping (bytes32 => Claim) pendingClaims;
uint nonce = 0;
- struct Transaction {
+ struct Action {
address to;
uint value;
bytes data;
+ }
+
+ uint8 constant ACTION_TRANSACTION = 1;
+ uint8 constant CLAIM_TRANSACTION = 2;
+
+ struct Transaction {
+ uint8 trxType;
uint nonce;
uint approverCount;
mapping(uint256 => uint8) approvalsByKeyType;
mapping(bytes32 => bool) approvals;
+ bool executed;
}
modifier managerOnly {
@@ -93,17 +104,10 @@ contract Identity is ERC725, ERC735 {
managerOrActor
returns (uint256 executionId)
{
- executionId = nonce;
+ executionId = createTransaction(ACTION_TRANSACTION);
ExecutionRequested(executionId, _to, _value, _data);
- txx[executionId] = Transaction(
- {
- to: _to,
- value: _value,
- data: _data,
- nonce: nonce,
- approverCount: 0
- });
- nonce++;
+
+ actionCatalogByTrx[nonce] = Action({to: _to, value: _value, data: _data});
approve(executionId, true);
}
@@ -113,29 +117,55 @@ contract Identity is ERC725, ERC735 {
returns (bool success)
{
Transaction storage trx = txx[_id];
+ require(trx.executed == false);
bytes32 managerKeyHash = keccak256(bytes32(msg.sender), MANAGEMENT_KEY);
bytes32 actorKeyHash = keccak256(bytes32(msg.sender), ACTION_KEY);
uint8 approvalCount;
uint256 requiredKeyType;
-
- if (trx.to == address(this)) {
- requiredKeyType = MANAGEMENT_KEY;
- if (keys[managerKeyHash].purpose == MANAGEMENT_KEY) {
- approvalCount = _calculateApprovals(managerKeyHash, MANAGEMENT_KEY, _approve, trx);
+ if (trx.trxType == ACTION_KEY) {
+ Action memory action = actionCatalogByTrx[_id];
+ if (action.to == address(this)) {
+ requiredKeyType = MANAGEMENT_KEY;
+ if (keys[managerKeyHash].purpose == MANAGEMENT_KEY)
+ approvalCount = _calculateApprovals(managerKeyHash, MANAGEMENT_KEY, _approve, trx);
+ } else {
+ requiredKeyType = ACTION_KEY;
+ if (keys[managerKeyHash].purpose == ACTION_KEY)
+ approvalCount = _calculateApprovals(actorKeyHash, ACTION_KEY, _approve, trx);
+ }
+
+ if (approvalCount >= minimumApprovalsByKeyType[requiredKeyType]) {
+ trx.executed = true;
+ Executed(_id, action.to, action.value, action.data);
+ success = action.to.call.value(action.value)(action.data);
}
} else {
- requiredKeyType = ACTION_KEY;
- if (keys[managerKeyHash].purpose == ACTION_KEY) {
- approvalCount = _calculateApprovals(actorKeyHash, ACTION_KEY, _approve, trx);
+ // It is a claim
+ bytes32 claimHash = claimCatalogByTrx[_id];
+ if (keys[managerKeyHash].purpose == MANAGEMENT_KEY)
+ approvalCount = _calculateApprovals(managerKeyHash, MANAGEMENT_KEY, _approve, trx);
+
+ if (approvalCount >= minimumApprovalsByKeyType[MANAGEMENT_KEY]) {
+ Claim memory c = pendingClaims[claimHash];
+ claims[claimHash] = Claim(
+ {
+ claimType: c.claimType,
+ scheme: c.scheme,
+ issuer: c.issuer,
+ signature: c.signature,
+ data: c.data,
+ uri: c.uri
+ });
+
+ indexes[claimHash] = claimsByType[c.claimType].length;
+ claimsByType[c.claimType].push(claimHash);
+ ClaimAdded(claimHash, c.claimType, c.scheme, c.issuer, c.signature, c.data, c.uri);
+ trx.executed = true;
}
+
}
-
- if (approvalCount >= minimumApprovalsByKeyType[requiredKeyType]) {
- success = trx.to.call.value(txx[_id].value)(txx[_id].data);
- }
-
}
function setMiminumApprovalsByKeyType(
@@ -166,6 +196,22 @@ contract Identity is ERC725, ERC735 {
trx.approverCount++;
return trx.approvalsByKeyType[_keyType];
}
+
+ function createTransaction(uint8 _trxType) private returns (uint256 id){
+ id = nonce;
+ txx[nonce] = Transaction(
+ {
+ nonce: nonce,
+ trxType: _trxType,
+ approverCount: 0,
+ executed: false
+ });
+ nonce++;
+ }
+
+ function getTransactionIdByClaim(bytes32 _claimHash) public view returns (uint256 id) {
+ return indexes[_claimHash];
+ }
function addClaim(
uint256 _claimType,
@@ -184,8 +230,12 @@ contract Identity is ERC725, ERC735 {
claimRequestId = claimHash;
+ uint256 executionId = createTransaction(CLAIM_TRANSACTION);
+ claimCatalogByTrx[executionId] = claimHash;
+ indexes[claimHash] = executionId;
+
if (claims[claimHash].claimType > 0) {
- // Claim existed
+ // Claim existed, needs approval
ClaimChanged(
claimRequestId,
_claimType,
@@ -195,7 +245,6 @@ contract Identity is ERC725, ERC735 {
_data,
_uri);
} else {
- // TODO Triggers if the claim is new Event and approval process exists: ClaimRequested
ClaimRequested(
claimRequestId,
_claimType,
@@ -206,7 +255,7 @@ contract Identity is ERC725, ERC735 {
_uri);
}
- claims[claimHash] = Claim(
+ pendingClaims[claimHash] = Claim(
{
claimType: _claimType,
scheme: _scheme,
@@ -215,13 +264,7 @@ contract Identity is ERC725, ERC735 {
data: _data,
uri: _uri
}
- );
-
- indexes[claimHash] = claimsByType[_claimType].length;
-
- claimsByType[_claimType].push(claimRequestId);
-
- // TODO This SHOULD create a pending claim, which SHOULD to be approved or rejected by n of m approve calls from keys of purpose 1.
+ );
}
function removeClaim(bytes32 _claimId) public returns (bool success) {
@@ -233,9 +276,6 @@ contract Identity is ERC725, ERC735 {
isKeyType(bytes32(msg.sender), MANAGEMENT_KEY)
);
- // MUST only be done by the issuer of the claim, or KEYS OF PURPOSE 1, or the identity itself.
- // TODO If its the identity itself, the approval process will determine its approval.
-
uint claimIdTypePos = indexes[_claimId];
delete indexes[_claimId];
bytes32[] storage claimsTypeArr = claimsByType[c.claimType];
From c3c2b8e4644912e368cd5db346d4be88b95cc30f Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Fri, 23 Feb 2018 14:02:53 -0300
Subject: [PATCH 017/102] other option of claim approval
---
contracts/identity/Identity.sol | 165 +++++++++++++++++++++-----------
1 file changed, 110 insertions(+), 55 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 26afa9f..b96f2ab 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -93,17 +93,7 @@ contract Identity is ERC725, ERC735 {
managerOrActor
returns (uint256 executionId)
{
- executionId = nonce;
- ExecutionRequested(executionId, _to, _value, _data);
- txx[executionId] = Transaction(
- {
- to: _to,
- value: _value,
- data: _data,
- nonce: nonce,
- approverCount: 0
- });
- nonce++;
+ executionId = _execute(_to, _value, _data);
approve(executionId, true);
}
@@ -176,52 +166,29 @@ contract Identity is ERC725, ERC735 {
string _uri
)
public
- claimSignerOnly
- returns (bytes32 claimRequestId)
+ returns (bytes32 claimHash)
{
-
- bytes32 claimHash = keccak256(_issuer, _claimType);
-
- claimRequestId = claimHash;
-
- if (claims[claimHash].claimType > 0) {
- // Claim existed
- ClaimChanged(
- claimRequestId,
- _claimType,
- _scheme,
- _issuer,
- _signature,
- _data,
- _uri);
- } else {
- // TODO Triggers if the claim is new Event and approval process exists: ClaimRequested
- ClaimRequested(
- claimRequestId,
- _claimType,
- _scheme,
- _issuer,
- _signature,
- _data,
- _uri);
- }
-
- claims[claimHash] = Claim(
- {
- claimType: _claimType,
- scheme: _scheme,
- issuer: _issuer,
- signature: _signature,
- data: _data,
- uri: _uri
+ claimHash = keccak256(_issuer, _claimType);
+ if (msg.sender == address(this)) {
+ if (claims[claimHash].claimType > 0) {
+ _modifyClaim(claimHash, _claimType, _scheme, _issuer, _signature, _data, _uri);
+ } else {
+ _includeClaim(claimHash, _claimType, _scheme, _issuer, _signature, _data, _uri);
}
- );
-
- indexes[claimHash] = claimsByType[_claimType].length;
-
- claimsByType[_claimType].push(claimRequestId);
-
- // TODO This SHOULD create a pending claim, which SHOULD to be approved or rejected by n of m approve calls from keys of purpose 1.
+ } else {
+ require(_issuer == msg.sender);
+ require(isKeyType(bytes32(msg.sender), CLAIM_SIGNER_KEY));
+ _execute(address(this), 0, msg.data);
+ ClaimRequested(
+ claimHash,
+ _claimType,
+ _scheme,
+ _issuer,
+ _signature,
+ _data,
+ _uri
+ );
+ }
}
function removeClaim(bytes32 _claimId) public returns (bool success) {
@@ -247,6 +214,94 @@ contract Identity is ERC725, ERC735 {
return true;
}
+ function _execute(
+ address _to,
+ uint256 _value,
+ bytes _data
+ )
+ private
+ returns (uint256 executionId)
+ {
+ executionId = nonce;
+ ExecutionRequested(executionId, _to, _value, _data);
+ txx[executionId] = Transaction(
+ {
+ to: _to,
+ value: _value,
+ data: _data,
+ nonce: nonce,
+ approverCount: 0
+ });
+ nonce++;
+ }
+
+ function _includeClaim(
+ bytes32 _claimHash,
+ uint256 _claimType,
+ uint256 _scheme,
+ address _issuer,
+ bytes _signature,
+ bytes _data,
+ string _uri
+ )
+ private
+ {
+ claims[_claimHash] = Claim(
+ {
+ claimType: _claimType,
+ scheme: _scheme,
+ issuer: _issuer,
+ signature: _signature,
+ data: _data,
+ uri: _uri
+ }
+ );
+ indexes[_claimHash] = claimsByType[_claimType].length;
+ claimsByType[_claimType].push(_claimHash);
+ ClaimAdded(
+ _claimHash,
+ _claimType,
+ _scheme,
+ _issuer,
+ _signature,
+ _data,
+ _uri
+ );
+ }
+
+ function _modifyClaim(
+ bytes32 _claimHash,
+ uint256 _claimType,
+ uint256 _scheme,
+ address _issuer,
+ bytes _signature,
+ bytes _data,
+ string _uri
+ )
+ private
+ {
+ require(msg.sender == _issuer);
+ ClaimChanged(
+ _claimHash,
+ _claimType,
+ _scheme,
+ _issuer,
+ _signature,
+ _data,
+ _uri
+ );
+ claims[_claimHash] = Claim(
+ {
+ claimType: _claimType,
+ scheme: _scheme,
+ issuer: _issuer,
+ signature: _signature,
+ data: _data,
+ uri: _uri
+ }
+ );
+ }
+
function _addKey(bytes32 _key, uint256 _purpose, uint256 _type) private {
bytes32 keyHash = keccak256(_key, _purpose);
From 0b9626a5f85643fc1bd37bf97935554eb295a342 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Fri, 23 Feb 2018 14:14:59 -0300
Subject: [PATCH 018/102] remove leftover code
---
contracts/identity/Identity.sol | 5 -----
1 file changed, 5 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index b96f2ab..6202706 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -40,11 +40,6 @@ contract Identity is ERC725, ERC735 {
require(isKeyType(bytes32(msg.sender), ACTION_KEY));
_;
}
-
- modifier claimSignerOnly {
- require(isKeyType(bytes32(msg.sender), CLAIM_SIGNER_KEY));
- _;
- }
modifier managerOrActor {
require(
From 73850a463f78980a3c24dd31790aaddc57ceea05 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Fri, 23 Feb 2018 14:17:57 -0300
Subject: [PATCH 019/102] manager should use execute method
---
contracts/identity/Identity.sol | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 6202706..4ff0058 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -191,8 +191,7 @@ contract Identity is ERC725, ERC735 {
require(
msg.sender == c.issuer ||
- msg.sender == address(this) ||
- isKeyType(bytes32(msg.sender), MANAGEMENT_KEY)
+ msg.sender == address(this)
);
// MUST only be done by the issuer of the claim, or KEYS OF PURPOSE 1, or the identity itself.
From 0a253aa9b344cfbb2c8a44d56c3bbb947ab70c16 Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Fri, 23 Feb 2018 15:01:35 -0400
Subject: [PATCH 020/102] Initial version of possible recovery contract (hasn't
been tested yet)
---
contracts/identity/Identity.sol | 36 ++++++++++++++++++++++++++++++++-
1 file changed, 35 insertions(+), 1 deletion(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 26afa9f..1aefc7b 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -2,7 +2,7 @@ pragma solidity ^0.4.17;
import "./ERC725.sol";
import "./ERC735.sol";
-
+import "./Recovery.sol";
contract Identity is ERC725, ERC735 {
@@ -15,6 +15,8 @@ contract Identity is ERC725, ERC735 {
mapping (uint256 => uint8) minimumApprovalsByKeyType;
bytes32[] pendingTransactions;
uint nonce = 0;
+ bool recoverySet;
+ Recovery recoveryContract;
struct Transaction {
address to;
@@ -365,6 +367,38 @@ contract Identity is ERC725, ERC735 {
{
return claimsByType[_claimType];
}
+
+// ---- Recovery Specific Functions
+ event RecoverySetUp(address sender, bytes32[] recoveryHashes);
+ event RecoveryCompleted(bytes32 newManagementKey);
+
+ function setupRecovery(bytes32[] _recoveryHashes)
+ public
+ managerOnly
+ {
+ require(recoverySet == false);
+ RecoverySetUp(msg.sender, _recoveryHashes);
+ recoveryContract = new Recovery(this, _recoveryHashes);
+ recoverySet = true;
+ }
+
+ function completeRecovery() public {
+ require(recoverySet == true);
+ address newManager = recoveryContract.getNewManager();
+ require(newManager != address(0x0));
+
+ RecoveryCompleted(bytes32(newManager));
+
+ bytes32[] memory managementKeys = getKeysByPurpose(MANAGEMENT_KEY);
+ for (uint256 i = 0; i < managementKeys.length; i++) {
+ _removeKey(managementKeys[i], MANAGEMENT_KEY);
+ }
+ addKey(bytes32(newManager), MANAGEMENT_KEY, 1);
+ setMiminumApprovalsByKeyType(MANAGEMENT_KEY, 1);
+ }
+
+
+
}
From 0e2015bf3f1bf19c158bb8c6041f3973e540cad4 Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Mon, 26 Feb 2018 17:26:14 -0400
Subject: [PATCH 021/102] Added execute and approve function for ECDSA keys.
---
contracts/identity/Identity.sol | 85 ++++++++++++++++++++++++++++++---
test/identityExtended.js | 64 +++++++++++++++++++++++++
2 files changed, 143 insertions(+), 6 deletions(-)
create mode 100644 test/identityExtended.js
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 4ff0058..5363a3d 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -13,6 +13,7 @@ contract Identity is ERC725, ERC735 {
mapping (bytes32 => uint256) indexes;
mapping (uint => Transaction) txx;
mapping (uint256 => uint8) minimumApprovalsByKeyType;
+ mapping (bytes32 => bytes) publicKeys;
bytes32[] pendingTransactions;
uint nonce = 0;
@@ -32,7 +33,7 @@ contract Identity is ERC725, ERC735 {
}
modifier selfOnly {
- require(msg.sender == address(this));
+ // require(msg.sender == address(this));
_;
}
@@ -50,7 +51,7 @@ contract Identity is ERC725, ERC735 {
}
function Identity() public {
- _addKey(bytes32(msg.sender), MANAGEMENT_KEY, 1);
+ _addKey(bytes32(msg.sender), MANAGEMENT_KEY, 0);
minimumApprovalsByKeyType[MANAGEMENT_KEY] = 1;
}
@@ -67,6 +68,17 @@ contract Identity is ERC725, ERC735 {
return true;
}
+ function addPublicKey(bytes32 _key, bytes _publicKey)
+ public
+ selfOnly
+ {
+ publicKeys[_key] = _publicKey;
+ }
+
+ function getPublicKey(bytes32 _key) public constant returns (bytes _publicKey) {
+ return publicKeys[_key];
+ }
+
function removeKey(
bytes32 _key,
uint256 _purpose
@@ -97,6 +109,12 @@ contract Identity is ERC725, ERC735 {
managerOrActor
returns (bool success)
{
+
+ approveExecution(_id, _approve);
+
+ }
+
+ function approveExecution(uint256 _id, bool _approve) internal returns(bool success) {
Transaction storage trx = txx[_id];
bytes32 managerKeyHash = keccak256(bytes32(msg.sender), MANAGEMENT_KEY);
@@ -120,7 +138,6 @@ contract Identity is ERC725, ERC735 {
if (approvalCount >= minimumApprovalsByKeyType[requiredKeyType]) {
success = trx.to.call.value(txx[_id].value)(txx[_id].data);
}
-
}
function setMiminumApprovalsByKeyType(
@@ -296,7 +313,7 @@ contract Identity is ERC725, ERC735 {
);
}
- function _addKey(bytes32 _key, uint256 _purpose, uint256 _type) private {
+ function _addKey(bytes32 _key, uint256 _purpose, uint256 _type) internal {
bytes32 keyHash = keccak256(_key, _purpose);
require(keys[keyHash].purpose == 0);
@@ -311,7 +328,7 @@ contract Identity is ERC725, ERC735 {
indexes[keyHash] = keysByPurpose[_purpose].push(_key) - 1;
}
- function _removeKey(bytes32 _key, uint256 _purpose) private {
+ function _removeKey(bytes32 _key, uint256 _purpose) internal {
bytes32 keyHash = keccak256(_key, _purpose);
Key storage myKey = keys[keyHash];
KeyRemoved(myKey.key, myKey.purpose, myKey.keyType);
@@ -328,7 +345,8 @@ contract Identity is ERC725, ERC735 {
}
delete keys[keyHash];
-
+ delete publicKeys[keyHash];
+
// MUST only be done by keys of purpose 1, or the identity itself.
// TODO If its the identity itself, the approval process will determine its approval.
}
@@ -414,6 +432,61 @@ contract Identity is ERC725, ERC735 {
{
return claimsByType[_claimType];
}
+
+
+
+ modifier validECDSAKey(
+ bytes32 _key,
+ bytes32 signHash,
+ uint8 v, bytes32 r, bytes32 s) {
+ require(
+ uint(keccak256(publicKeys[_key]) & 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
+ == uint(ecrecover(signHash, v, r, s)));
+ _;
+ }
+
+
+ function approveECDSA(uint256 _id, bool _approve,
+ bytes32 _key,
+ bytes32 signHash,
+ uint8 v,
+ bytes32 r,
+ bytes32 s)
+ public
+ validECDSAKey(_key, signHash, v, r, s)
+ returns (bool success)
+ {
+
+ approveExecution(_id, _approve);
+
+ }
+
+ function executeECDSA(
+ address _to,
+ uint256 _value,
+ bytes _data,
+ bytes32 _key,
+ bytes32 signHash,
+ uint8 v,
+ bytes32 r,
+ bytes32 s
+ )
+ public
+ validECDSAKey(_key, signHash, v, r, s)
+ returns (uint256 executionId)
+ {
+
+ require(
+ isKeyType(_key, MANAGEMENT_KEY) ||
+ isKeyType(_key, ACTION_KEY)
+ );
+
+
+ executionId = _execute(_to, _value, _data);
+ approve(executionId, true);
+ }
+
+
}
diff --git a/test/identityExtended.js b/test/identityExtended.js
new file mode 100644
index 0000000..e881362
--- /dev/null
+++ b/test/identityExtended.js
@@ -0,0 +1,64 @@
+const TestUtils = require("./TestUtils.js")
+var ethUtils = require('ethereumjs-util')
+
+const Identity = artifacts.require("./identity/Identity.sol");
+
+contract('Identity', function(accounts) {
+
+ let identity;
+
+ beforeEach(async () => {
+ identity = await Identity.new({from: accounts[0]});
+ })
+
+
+ describe("Identity()", () => {
+
+ let privateKey = new Buffer('61bffea9215f65164ad18b45aff1436c0c165d0d5dd2087ef61b4232ba6d2c1a', 'hex')
+ let publicKey = ethUtils.privateToPublic(privateKey);
+ let pkSha = web3.sha3(publicKey.toString('hex'), {encoding: 'hex'});
+
+ it("Add ECDSA Management Key", async () => {
+
+ await identity.addKey(pkSha, 2, 1, {from: accounts[0]})
+
+ await identity.addPublicKey(pkSha, '0x' + publicKey.toString('hex'), {from: accounts[0]});
+
+ assert.equal(
+ await identity.getPublicKey(pkSha, {from: accounts[0]}),
+ '0x' + publicKey.toString('hex'),
+ identity.address+".getPublicKey("+pkSha+") is not correct");
+
+ });
+
+
+ it("Test Execution", async () => {
+
+ let to = accounts[1];
+ let value = 100;
+ let data = '';
+
+ let message = ethUtils.toBuffer("SignedMessage");
+ let msgHash = ethUtils.hashPersonalMessage(message);
+ let sig = ethUtils.ecsign(msgHash, privateKey);
+
+ let r = '0x' + sig.r.toString('hex');
+ let s = '0x' + sig.s.toString('hex');
+ let v = sig.v;
+
+
+ await identity.addKey(pkSha, 2, 1, {from: accounts[0]})
+
+ await identity.addPublicKey(pkSha, '0x' + publicKey.toString('hex'), {from: accounts[0]});
+
+ let tx = await identity.executeECDSA(to, value, data, pkSha, '0x' + msgHash.toString('hex'), v, r, s, {from: accounts[0]});
+
+ // TODO Assert ExecutionRequested Event
+ console.log(tx)
+
+ });
+ });
+
+
+
+});
From ebbfbeb1a067c7a9d2a16bab4fa74623c62984fa Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Mon, 26 Feb 2018 19:22:04 -0300
Subject: [PATCH 022/102] message signing verifications, use same storage,
abstracted modifiers
---
contracts/identity/Identity.sol | 69 +++++++++++----------------------
1 file changed, 23 insertions(+), 46 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 5363a3d..27e523f 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -13,7 +13,6 @@ contract Identity is ERC725, ERC735 {
mapping (bytes32 => uint256) indexes;
mapping (uint => Transaction) txx;
mapping (uint256 => uint8) minimumApprovalsByKeyType;
- mapping (bytes32 => bytes) publicKeys;
bytes32[] pendingTransactions;
uint nonce = 0;
@@ -27,25 +26,25 @@ contract Identity is ERC725, ERC735 {
mapping(bytes32 => bool) approvals;
}
- modifier managerOnly {
- require(isKeyType(bytes32(msg.sender), MANAGEMENT_KEY));
+ modifier managerOnly(bytes32 _key) {
+ require(isKeyType(_key, MANAGEMENT_KEY));
_;
}
modifier selfOnly {
- // require(msg.sender == address(this));
+ require(msg.sender == address(this));
_;
}
- modifier actorOnly {
- require(isKeyType(bytes32(msg.sender), ACTION_KEY));
+ modifier actorOnly(bytes32 _key) {
+ require(isKeyType(_key, ACTION_KEY));
_;
}
- modifier managerOrActor {
+ modifier managerOrActor(bytes32 _key) {
require(
- isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) ||
- isKeyType(bytes32(msg.sender), ACTION_KEY)
+ isKeyType(_key, MANAGEMENT_KEY) ||
+ isKeyType(_key, ACTION_KEY)
);
_;
}
@@ -68,17 +67,6 @@ contract Identity is ERC725, ERC735 {
return true;
}
- function addPublicKey(bytes32 _key, bytes _publicKey)
- public
- selfOnly
- {
- publicKeys[_key] = _publicKey;
- }
-
- function getPublicKey(bytes32 _key) public constant returns (bytes _publicKey) {
- return publicKeys[_key];
- }
-
function removeKey(
bytes32 _key,
uint256 _purpose
@@ -97,7 +85,7 @@ contract Identity is ERC725, ERC735 {
bytes _data
)
public
- managerOrActor
+ managerOrActor(bytes32(msg.sender))
returns (uint256 executionId)
{
executionId = _execute(_to, _value, _data);
@@ -106,19 +94,18 @@ contract Identity is ERC725, ERC735 {
function approve(uint256 _id, bool _approve)
public
- managerOrActor
+ managerOrActor(bytes32(msg.sender))
returns (bool success)
{
- approveExecution(_id, _approve);
-
+ approveExecution(bytes32(msg.sender), _id, _approve);
}
- function approveExecution(uint256 _id, bool _approve) internal returns(bool success) {
+ function approveExecution(bytes32 _key, uint256 _id, bool _approve) internal returns(bool success) {
Transaction storage trx = txx[_id];
- bytes32 managerKeyHash = keccak256(bytes32(msg.sender), MANAGEMENT_KEY);
- bytes32 actorKeyHash = keccak256(bytes32(msg.sender), ACTION_KEY);
+ bytes32 managerKeyHash = keccak256(_key, MANAGEMENT_KEY);
+ bytes32 actorKeyHash = keccak256(_key, ACTION_KEY);
uint8 approvalCount;
uint256 requiredKeyType;
@@ -345,7 +332,6 @@ contract Identity is ERC725, ERC735 {
}
delete keys[keyHash];
- delete publicKeys[keyHash];
// MUST only be done by keys of purpose 1, or the identity itself.
// TODO If its the identity itself, the approval process will determine its approval.
@@ -439,51 +425,42 @@ contract Identity is ERC725, ERC735 {
bytes32 _key,
bytes32 signHash,
uint8 v, bytes32 r, bytes32 s) {
- require(
- uint(keccak256(publicKeys[_key]) & 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
- == uint(ecrecover(signHash, v, r, s)));
+ require(address(_key) == ecrecover(keccak256("\x19Ethereum Signed Message:\n32", signHash), v, r, s));
+ require(keys[_key].purpose != 0);
_;
}
function approveECDSA(uint256 _id, bool _approve,
bytes32 _key,
- bytes32 signHash,
uint8 v,
bytes32 r,
bytes32 s)
public
- validECDSAKey(_key, signHash, v, r, s)
+ validECDSAKey(_key, keccak256(address(this), bytes4(keccak256("approve(uint256,bool)")), _id, _approve), v, r, s)
+ managerOrActor(_key)
returns (bool success)
{
-
- approveExecution(_id, _approve);
-
+ return approveExecution(_key, _id, _approve);
}
function executeECDSA(
address _to,
uint256 _value,
bytes _data,
+ uint _nonce,
bytes32 _key,
- bytes32 signHash,
uint8 v,
bytes32 r,
bytes32 s
)
public
- validECDSAKey(_key, signHash, v, r, s)
+ validECDSAKey(_key, keccak256(address(this), bytes4(keccak256("execute(address,uint256,bytes)")), _to, _value, _data, _nonce), v, r, s)
+ managerOrActor(_key)
returns (uint256 executionId)
{
-
- require(
- isKeyType(_key, MANAGEMENT_KEY) ||
- isKeyType(_key, ACTION_KEY)
- );
-
-
executionId = _execute(_to, _value, _data);
- approve(executionId, true);
+ approveExecution(_key, executionId, true);
}
From 6f413373b0fb2c7c18fa09ae78eb78d02c696a8f Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Tue, 27 Feb 2018 13:23:00 -0400
Subject: [PATCH 023/102] - Modified Identity to setup recovery contract
address - Added initial version of test unit for recovery contract
---
contracts/identity/Identity.sol | 45 +++++++++++----------------------
1 file changed, 15 insertions(+), 30 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 1aefc7b..af82fc6 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -2,7 +2,6 @@ pragma solidity ^0.4.17;
import "./ERC725.sol";
import "./ERC735.sol";
-import "./Recovery.sol";
contract Identity is ERC725, ERC735 {
@@ -16,7 +15,7 @@ contract Identity is ERC725, ERC735 {
bytes32[] pendingTransactions;
uint nonce = 0;
bool recoverySet;
- Recovery recoveryContract;
+ address recoveryContract;
struct Transaction {
address to;
@@ -29,12 +28,18 @@ contract Identity is ERC725, ERC735 {
}
modifier managerOnly {
- require(isKeyType(bytes32(msg.sender), MANAGEMENT_KEY));
+ require(
+ isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) ||
+ (recoverySet && msg.sender == address(recoveryContract))
+ );
_;
}
modifier selfOnly {
- require(msg.sender == address(this));
+ require(
+ msg.sender == address(this) ||
+ (recoverySet && msg.sender == address(recoveryContract))
+ );
_;
}
@@ -51,7 +56,8 @@ contract Identity is ERC725, ERC735 {
modifier managerOrActor {
require(
isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) ||
- isKeyType(bytes32(msg.sender), ACTION_KEY)
+ isKeyType(bytes32(msg.sender), ACTION_KEY) ||
+ (recoverySet && msg.sender == address(recoveryContract))
);
_;
}
@@ -114,6 +120,7 @@ contract Identity is ERC725, ERC735 {
managerOrActor
returns (bool success)
{
+
Transaction storage trx = txx[_id];
bytes32 managerKeyHash = keccak256(bytes32(msg.sender), MANAGEMENT_KEY);
@@ -368,37 +375,15 @@ contract Identity is ERC725, ERC735 {
return claimsByType[_claimType];
}
-// ---- Recovery Specific Functions
- event RecoverySetUp(address sender, bytes32[] recoveryHashes);
- event RecoveryCompleted(bytes32 newManagementKey);
-
- function setupRecovery(bytes32[] _recoveryHashes)
+ function setupRecovery(address _recoveryContract)
public
- managerOnly
+ selfOnly
{
require(recoverySet == false);
- RecoverySetUp(msg.sender, _recoveryHashes);
- recoveryContract = new Recovery(this, _recoveryHashes);
+ recoveryContract = _recoveryContract;
recoverySet = true;
}
- function completeRecovery() public {
- require(recoverySet == true);
- address newManager = recoveryContract.getNewManager();
- require(newManager != address(0x0));
-
- RecoveryCompleted(bytes32(newManager));
-
- bytes32[] memory managementKeys = getKeysByPurpose(MANAGEMENT_KEY);
- for (uint256 i = 0; i < managementKeys.length; i++) {
- _removeKey(managementKeys[i], MANAGEMENT_KEY);
- }
- addKey(bytes32(newManager), MANAGEMENT_KEY, 1);
- setMiminumApprovalsByKeyType(MANAGEMENT_KEY, 1);
- }
-
-
-
}
From 5a22b71313af1bc66d2b406f14b0e2378f915d70 Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Tue, 27 Feb 2018 17:11:22 -0400
Subject: [PATCH 024/102] - Test case to illustrate how the recovery process
works
---
contracts/identity/Identity.sol | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index af82fc6..468d83c 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -63,10 +63,10 @@ contract Identity is ERC725, ERC735 {
}
function Identity() public {
- _addKey(bytes32(msg.sender), MANAGEMENT_KEY, 1);
+ _addKey(bytes32(msg.sender), MANAGEMENT_KEY, 0);
minimumApprovalsByKeyType[MANAGEMENT_KEY] = 1;
}
-
+
function addKey(
bytes32 _key,
uint256 _purpose,
@@ -75,7 +75,7 @@ contract Identity is ERC725, ERC735 {
public
selfOnly
returns (bool success)
- {
+ {
_addKey(_key, _purpose, _type);
return true;
}
From 5c9070f7dc02dd08de9fe030a901274bb740d72e Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 28 Feb 2018 01:36:48 -0300
Subject: [PATCH 025/102] add upgradable instance factory and needed
architecture
---
contracts/identity/Identity.sol | 1 +
contracts/identity/IdentityFactory.sol | 32 ++++++++++++++++++++++++++
contracts/identity/IdentityKernel.sol | 13 +++++++++++
3 files changed, 46 insertions(+)
create mode 100644 contracts/identity/IdentityFactory.sol
create mode 100644 contracts/identity/IdentityKernel.sol
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 27e523f..de89f6d 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -134,6 +134,7 @@ contract Identity is ERC725, ERC735 {
public
selfOnly
{
+ require(_minimumApprovals > 0);
minimumApprovalsByKeyType[_type] = _minimumApprovals;
}
diff --git a/contracts/identity/IdentityFactory.sol b/contracts/identity/IdentityFactory.sol
new file mode 100644
index 0000000..6da2bf8
--- /dev/null
+++ b/contracts/identity/IdentityFactory.sol
@@ -0,0 +1,32 @@
+pragma solidity ^0.4.17;
+
+import "../deploy/Factory.sol";
+import "../deploy/UpdatableInstance.sol";
+import "./IdentityKernel.sol";
+
+contract IdentityFactory is Factory {
+
+ event IdentityCreated(address instance);
+
+ function IdentityFactory(bytes _infohash)
+ Factory(new IdentityKernel(), _infohash)
+ public
+ {
+
+ }
+
+ function createIdentity()
+ external
+ {
+ createIdentity(msg.sender);
+ }
+
+ function createIdentity(address _idOwner)
+ public
+ {
+ IdentityKernel instance = IdentityKernel(new UpdatableInstance(address(latestKernel)));
+ instance.initIdentity(_idOwner);
+ IdentityCreated(address(instance));
+ }
+
+}
diff --git a/contracts/identity/IdentityKernel.sol b/contracts/identity/IdentityKernel.sol
new file mode 100644
index 0000000..7966b8b
--- /dev/null
+++ b/contracts/identity/IdentityKernel.sol
@@ -0,0 +1,13 @@
+pragma solidity ^0.4.17;
+
+import "../deploy/InstanceStorage.sol";
+import "./Identity.sol";
+
+contract IdentityKernel is InstanceStorage, Identity {
+
+ function initIdentity(address _caller) external {
+ require(minimumApprovalsByKeyType[MANAGEMENT_KEY] == 0);
+ _addKey(bytes32(_caller), MANAGEMENT_KEY, 0);
+ minimumApprovalsByKeyType[MANAGEMENT_KEY] = 1;
+ }
+}
From b3025a5d80e179c242c11486d392bc0fe30caa87 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 28 Feb 2018 01:36:48 -0300
Subject: [PATCH 026/102] add upgradable instance factory and needed
architecture
---
contracts/deploy/DelegatedCall.sol | 35 ++++++++++++++++++++
contracts/deploy/Factory.sol | 45 ++++++++++++++++++++++++++
contracts/deploy/Instance.sol | 38 ++++++++++++++++++++++
contracts/deploy/InstanceStorage.sol | 15 +++++++++
contracts/deploy/UpdatableInstance.sol | 25 ++++++++++++++
5 files changed, 158 insertions(+)
create mode 100644 contracts/deploy/DelegatedCall.sol
create mode 100644 contracts/deploy/Factory.sol
create mode 100644 contracts/deploy/Instance.sol
create mode 100644 contracts/deploy/InstanceStorage.sol
create mode 100644 contracts/deploy/UpdatableInstance.sol
diff --git a/contracts/deploy/DelegatedCall.sol b/contracts/deploy/DelegatedCall.sol
new file mode 100644
index 0000000..0526c76
--- /dev/null
+++ b/contracts/deploy/DelegatedCall.sol
@@ -0,0 +1,35 @@
+pragma solidity ^0.4.17;
+
+
+/**
+ * @title DelegatedCall
+ * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
+ * @dev Abstract contract that delegates calls by `delegated` modifier to result of `targetDelegatedCall()`
+ * Important to avoid overwriting wrong storage pointers is that never define storage to this contract
+ */
+contract DelegatedCall {
+ /**
+ * @dev delegates the call of this function
+ */
+ modifier delegated {
+ //require successfull delegate call to remote `_target()`
+ require(targetDelegatedCall().delegatecall(msg.data));
+ assembly {
+ let outSize := returndatasize
+ let outDataPtr := mload(0x40) //load memory
+ returndatacopy(outDataPtr, 0, outSize) //copy last return into pointer
+ return(outDataPtr, outSize)
+ }
+ assert(false); //should never reach here
+ _; //never will execute local logic
+ }
+
+ /**
+ * @dev defines the address for delegation of calls
+ */
+ function targetDelegatedCall()
+ internal
+ constant
+ returns(address);
+
+}
diff --git a/contracts/deploy/Factory.sol b/contracts/deploy/Factory.sol
new file mode 100644
index 0000000..19975d5
--- /dev/null
+++ b/contracts/deploy/Factory.sol
@@ -0,0 +1,45 @@
+pragma solidity ^0.4.17;
+
+import "../common/Controlled.sol";
+
+contract Factory is Controlled {
+
+ event NewKernel(address newKernel, bytes infohash);
+
+ struct Version {
+ uint256 blockNumber;
+ uint256 timestamp;
+ address kernel;
+ bytes infohash;
+ }
+ mapping (address => uint256) versionMap;
+
+ Version[] versionLog;
+ uint256 latestUpdate;
+ address latestKernel;
+
+ function Factory(address _kernel, bytes _infohash)
+ public
+ {
+ _setKernel(_kernel, _infohash);
+ }
+
+ function setKernel(address _kernel, bytes _infohash)
+ external
+ onlyController
+ {
+ _setKernel(_kernel, _infohash);
+ }
+
+ function _setKernel(address _kernel, bytes _infohash)
+ internal
+ {
+ require(_kernel != latestKernel);
+ versionMap[_kernel] = versionLog.length;
+ versionLog.push(Version({blockNumber: block.number, timestamp: block.timestamp, kernel: _kernel, infohash: _infohash}));
+ latestUpdate = block.timestamp;
+ latestKernel = _kernel;
+ NewKernel(_kernel, _infohash);
+ }
+
+}
\ No newline at end of file
diff --git a/contracts/deploy/Instance.sol b/contracts/deploy/Instance.sol
new file mode 100644
index 0000000..96c18d8
--- /dev/null
+++ b/contracts/deploy/Instance.sol
@@ -0,0 +1,38 @@
+pragma solidity ^0.4.17;
+
+import "./InstanceStorage.sol";
+import "./DelegatedCall.sol";
+
+/**
+ * @title Instance
+ * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
+ * @dev Contract that forward everything through delegatecall to defined kernel
+ */
+contract Instance is InstanceStorage, DelegatedCall {
+
+ function Instance(address _kernel) public {
+ kernel = _kernel;
+ }
+
+ /**
+ * @dev delegatecall everything (but declared functions) to `_target()`
+ * @notice Verify `kernel()` code to predict behavior
+ */
+ function () external delegated {
+ //all goes to kernel
+ }
+
+ /**
+ * @dev returns kernel if kernel that is configured
+ * @return kernel address
+ */
+ function targetDelegatedCall()
+ internal
+ constant
+ returns(address)
+ {
+ return kernel;
+ }
+
+
+}
\ No newline at end of file
diff --git a/contracts/deploy/InstanceStorage.sol b/contracts/deploy/InstanceStorage.sol
new file mode 100644
index 0000000..ba84f64
--- /dev/null
+++ b/contracts/deploy/InstanceStorage.sol
@@ -0,0 +1,15 @@
+pragma solidity ^0.4.17;
+
+
+/**
+ * @title InstanceStorage
+ * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
+ * @dev Defines kernel vars that Kernel contract share with Instance.
+ * Important to avoid overwriting wrong storage pointers is that
+ * InstanceStorage should be always the first contract at heritance.
+ */
+contract InstanceStorage {
+ // protected zone start (InstanceStorage vars)
+ address public kernel;
+ // protected zone end
+}
\ No newline at end of file
diff --git a/contracts/deploy/UpdatableInstance.sol b/contracts/deploy/UpdatableInstance.sol
new file mode 100644
index 0000000..550df8d
--- /dev/null
+++ b/contracts/deploy/UpdatableInstance.sol
@@ -0,0 +1,25 @@
+pragma solidity ^0.4.17;
+
+import "./Instance.sol";
+
+
+/**
+ * @title UpdatableInstance
+ * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
+ * @dev Contract that can be updated by a call from itself.
+ */
+contract UpdatableInstance is Instance {
+
+ function UpdatableInstance(address _kernel)
+ Instance(_kernel)
+ public
+ {
+
+ }
+
+ function updateUpdatableInstance(address _kernel) external {
+ require(msg.sender == address(this));
+ kernel = _kernel;
+ }
+
+}
\ No newline at end of file
From 3a79af82383cdc688f4431d15ad18cb122be68b0 Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Wed, 28 Feb 2018 10:18:00 -0400
Subject: [PATCH 027/102] Placeholder for IdentityFactory test
---
test/factory.js | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
create mode 100644 test/factory.js
diff --git a/test/factory.js b/test/factory.js
new file mode 100644
index 0000000..2072256
--- /dev/null
+++ b/test/factory.js
@@ -0,0 +1,21 @@
+const TestUtils = require("./TestUtils.js")
+const Identity = artifacts.require("./identity/Identity.sol");
+const IdentityFactory = artifacts.require("./identity/IdentityFactory.sol");
+
+contract('IdentityFactory', function(accounts) {
+
+ let identityFactory;
+
+ beforeEach(async () => {
+ identityFactory = await IdentityFactory.new("0x0", {from: accounts[0]});
+ })
+
+ describe("IdentityFactory()", () => {
+
+ it("test1", async () => {
+
+ });
+
+ });
+
+});
\ No newline at end of file
From e8cc00be53b7d413f16d163333aac75baa0c04cf Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Wed, 28 Feb 2018 11:47:59 -0400
Subject: [PATCH 028/102] - Added event to inform of an identity instance
upgrade - Added test units for Identity factory and its kernel upgrading
---
contracts/tests/UpdatedIdentityKernel.sol | 20 +++++++++
test/factory.js | 53 +++++++++++++++++++++--
2 files changed, 69 insertions(+), 4 deletions(-)
create mode 100644 contracts/tests/UpdatedIdentityKernel.sol
diff --git a/contracts/tests/UpdatedIdentityKernel.sol b/contracts/tests/UpdatedIdentityKernel.sol
new file mode 100644
index 0000000..fb9be92
--- /dev/null
+++ b/contracts/tests/UpdatedIdentityKernel.sol
@@ -0,0 +1,20 @@
+pragma solidity ^0.4.17;
+
+import "../identity/IdentityKernel.sol";
+
+contract UpdatedIdentityKernel is IdentityKernel {
+
+ function initIdentity(address _caller) external {
+ require(minimumApprovalsByKeyType[MANAGEMENT_KEY] == 0);
+ _addKey(bytes32(_caller), MANAGEMENT_KEY, 0);
+ minimumApprovalsByKeyType[MANAGEMENT_KEY] = 1;
+ }
+
+ event TestFunctionExecuted();
+
+ function test() public {
+ TestFunctionExecuted();
+ }
+
+
+}
diff --git a/test/factory.js b/test/factory.js
index 2072256..8b12c39 100644
--- a/test/factory.js
+++ b/test/factory.js
@@ -1,21 +1,66 @@
const TestUtils = require("./TestUtils.js")
const Identity = artifacts.require("./identity/Identity.sol");
const IdentityFactory = artifacts.require("./identity/IdentityFactory.sol");
+const UpdatableInstance = artifacts.require('./deploy/UpdatableInstance.sol');
+const UpdatedIdentityKernel = artifacts.require("./tests/UpdatedIdentityKernel.sol");
contract('IdentityFactory', function(accounts) {
let identityFactory;
+ let identity;
+ let updatedIdentity;
+ let updatedIdentityKernel;
- beforeEach(async () => {
- identityFactory = await IdentityFactory.new("0x0", {from: accounts[0]});
+ before(async () => {
+ identityFactory = await IdentityFactory.new("0xaaa", {from: accounts[0]});
})
describe("IdentityFactory()", () => {
+ it("Creates a new identity", async () => {
+ let tx = await identityFactory.createIdentity({from: accounts[0]});
+ const logEntry = tx.logs[0];
+ assert.strictEqual(logEntry.event, "IdentityCreated");
- it("test1", async () => {
-
+ identity = await Identity.at(logEntry.args.instance, {from: accounts[0]})
+
+ assert.equal(
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
+ 1,
+ identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY")
});
+
+ it("Registers a updated identity contract", async() => {
+ updatedIdentityKernel = await UpdatedIdentityKernel.new({from: accounts[0]});
+ let tx = await identityFactory.setKernel(updatedIdentityKernel.address, "0xbbb");
+ assert.strictEqual(tx.logs[0].event, "NewKernel");
+ });
+
+
+ it("Creates a new identity using latest version", async() => {
+ let tx = await identityFactory.createIdentity({from: accounts[0]});
+ const logEntry = tx.logs[0];
+ assert.strictEqual(logEntry.event, "IdentityCreated");
+
+ updatedIdentity = await UpdatedIdentityKernel.at(logEntry.args.instance, {from: accounts[0]})
+ tx = await updatedIdentity.test({from: accounts[0]});
+ assert.strictEqual(tx.logs[0].event, "TestFunctionExecuted");
+
+ // Test if it still executes identity functions as expected
+ let baseIdentity = await Identity.at(updatedIdentity.address, {from: accounts[0]})
+ assert.equal(
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
+ 1,
+ identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY")
+ });
+
+
+ it("Updates an identity to the latest version", async() => {
+
+ })
+
+
+
});
});
\ No newline at end of file
From d4b98d1186ff50f7d8bfff7612d795285a365f6d Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Wed, 28 Feb 2018 11:47:59 -0400
Subject: [PATCH 029/102] - Added event to inform of an identity instance
upgrade - Added test units for Identity factory and its kernel upgrading
---
contracts/deploy/UpdatableInstance.sol | 3 +++
1 file changed, 3 insertions(+)
diff --git a/contracts/deploy/UpdatableInstance.sol b/contracts/deploy/UpdatableInstance.sol
index 550df8d..60f6d0a 100644
--- a/contracts/deploy/UpdatableInstance.sol
+++ b/contracts/deploy/UpdatableInstance.sol
@@ -10,6 +10,8 @@ import "./Instance.sol";
*/
contract UpdatableInstance is Instance {
+ event InstanceUpdated(address oldKernel, address newKernel);
+
function UpdatableInstance(address _kernel)
Instance(_kernel)
public
@@ -19,6 +21,7 @@ contract UpdatableInstance is Instance {
function updateUpdatableInstance(address _kernel) external {
require(msg.sender == address(this));
+ InstanceUpdated(kernel, _kernel);
kernel = _kernel;
}
From 47a04341185d4e8968b2c58b56858d266fb0e42c Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Wed, 28 Feb 2018 14:22:06 -0400
Subject: [PATCH 030/102] Added Approved and Executed events to Identity
contract
---
contracts/identity/Identity.sol | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 2e86f21..836eb02 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -117,6 +117,8 @@ contract Identity is ERC725, ERC735 {
uint8 approvalCount;
uint256 requiredKeyType;
+ Approved(_id, _approve);
+
if (trx.to == address(this)) {
requiredKeyType = MANAGEMENT_KEY;
if (keys[managerKeyHash].purpose == MANAGEMENT_KEY) {
@@ -128,9 +130,10 @@ contract Identity is ERC725, ERC735 {
approvalCount = _calculateApprovals(actorKeyHash, ACTION_KEY, _approve, trx);
}
}
-
+
if (approvalCount >= minimumApprovalsByKeyType[requiredKeyType]) {
- success = trx.to.call.value(txx[_id].value)(txx[_id].data);
+ Executed(_id, trx.to, trx.value, trx.data);
+ success = trx.to.call.value(trx.value)(trx.data);
}
}
From b096936f9869233f16f181cb84f6d3be8fed6c33 Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Wed, 28 Feb 2018 14:35:15 -0400
Subject: [PATCH 031/102] Added unit test to verify identity upgrades
---
contracts/tests/UpdatedIdentityKernel.sol | 4 ++--
test/factory.js | 26 ++++++++++++++++++++---
2 files changed, 25 insertions(+), 5 deletions(-)
diff --git a/contracts/tests/UpdatedIdentityKernel.sol b/contracts/tests/UpdatedIdentityKernel.sol
index fb9be92..35f3a79 100644
--- a/contracts/tests/UpdatedIdentityKernel.sol
+++ b/contracts/tests/UpdatedIdentityKernel.sol
@@ -10,10 +10,10 @@ contract UpdatedIdentityKernel is IdentityKernel {
minimumApprovalsByKeyType[MANAGEMENT_KEY] = 1;
}
- event TestFunctionExecuted();
+ event TestFunctionExecuted(uint8 minApprovalsByManagementKeys);
function test() public {
- TestFunctionExecuted();
+ TestFunctionExecuted(minimumApprovalsByKeyType[MANAGEMENT_KEY]);
}
diff --git a/test/factory.js b/test/factory.js
index 8b12c39..ac5c402 100644
--- a/test/factory.js
+++ b/test/factory.js
@@ -1,4 +1,6 @@
const TestUtils = require("./TestUtils.js")
+const web3EthAbi = require("web3-eth-abi");
+
const Identity = artifacts.require("./identity/Identity.sol");
const IdentityFactory = artifacts.require("./identity/IdentityFactory.sol");
const UpdatableInstance = artifacts.require('./deploy/UpdatableInstance.sol');
@@ -56,11 +58,29 @@ contract('IdentityFactory', function(accounts) {
it("Updates an identity to the latest version", async() => {
+ let functionPayload = web3EthAbi.encodeFunctionCall({
+ name: 'updateUpdatableInstance',
+ type: 'function',
+ inputs: [{
+ type: 'address',
+ name: '_kernel'
+ }]
+ }, [updatedIdentityKernel.address]);
+
+ let tx1 = await identity.execute(identity.address, 0, functionPayload, {from: accounts[0]} );
+ assert.strictEqual(tx1.logs[tx1.logs.length - 1].event, "Executed");
+
+ // Calling function available in updated identity kernel
+ let updatedIdentity1 = await UpdatedIdentityKernel.at(identity.address, {from: accounts[0]})
+ let tx2 = await updatedIdentity1.test({from: accounts[0]});
+
+ assert.strictEqual(tx2.logs[tx2.logs.length - 1].event, "TestFunctionExecuted");
+ assert.equal(
+ tx2.logs[tx2.logs.length - 1].args.minApprovalsByManagementKeys.toString(10),
+ 1,
+ identity.address + " wasn't updated to last version");
})
-
-
-
});
});
\ No newline at end of file
From e9b99631539acb3c8a58b7a08671f5950ae03d9e Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Wed, 28 Feb 2018 14:39:22 -0400
Subject: [PATCH 032/102] Removing unused function
---
contracts/tests/UpdatedIdentityKernel.sol | 6 ------
1 file changed, 6 deletions(-)
diff --git a/contracts/tests/UpdatedIdentityKernel.sol b/contracts/tests/UpdatedIdentityKernel.sol
index 35f3a79..c102d2c 100644
--- a/contracts/tests/UpdatedIdentityKernel.sol
+++ b/contracts/tests/UpdatedIdentityKernel.sol
@@ -4,12 +4,6 @@ import "../identity/IdentityKernel.sol";
contract UpdatedIdentityKernel is IdentityKernel {
- function initIdentity(address _caller) external {
- require(minimumApprovalsByKeyType[MANAGEMENT_KEY] == 0);
- _addKey(bytes32(_caller), MANAGEMENT_KEY, 0);
- minimumApprovalsByKeyType[MANAGEMENT_KEY] = 1;
- }
-
event TestFunctionExecuted(uint8 minApprovalsByManagementKeys);
function test() public {
From 3c4f2ab7d1a5219b8f36e7659c6b8b22166132d8 Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Wed, 28 Feb 2018 14:52:32 -0400
Subject: [PATCH 033/102] Linting to reduce problems in IDE
---
contracts/identity/Identity.sol | 50 ++++++++++++-----------
contracts/identity/IdentityFactory.sol | 4 +-
contracts/tests/UpdatedIdentityKernel.sol | 7 ++--
3 files changed, 31 insertions(+), 30 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 836eb02..66c4a02 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -3,6 +3,7 @@ pragma solidity ^0.4.17;
import "./ERC725.sol";
import "./ERC735.sol";
+
contract Identity is ERC725, ERC735 {
mapping (bytes32 => Key) keys;
@@ -105,7 +106,7 @@ contract Identity is ERC725, ERC735 {
managerOrActor(bytes32(msg.sender))
returns (bool success)
{
- approveExecution(bytes32(msg.sender), _id, _approve);
+ return approveExecution(bytes32(msg.sender), _id, _approve);
}
function approveExecution(bytes32 _key, uint256 _id, bool _approve) internal returns(bool success) {
@@ -137,7 +138,7 @@ contract Identity is ERC725, ERC735 {
}
}
- function setMiminumApprovalsByKeyType(
+ function setMininumApprovalsByKeyType(
uint256 _type,
uint8 _minimumApprovals
)
@@ -290,25 +291,23 @@ contract Identity is ERC725, ERC735 {
private
{
require(msg.sender == _issuer);
- ClaimChanged(
- _claimHash,
- _claimType,
- _scheme,
- _issuer,
- _signature,
- _data,
- _uri
- );
- claims[_claimHash] = Claim(
- {
- claimType: _claimType,
- scheme: _scheme,
- issuer: _issuer,
- signature: _signature,
- data: _data,
- uri: _uri
- }
- );
+ ClaimChanged(
+ _claimHash,
+ _claimType,
+ _scheme,
+ _issuer,
+ _signature,
+ _data,
+ _uri
+ );
+ claims[_claimHash] = Claim({
+ claimType: _claimType,
+ scheme: _scheme,
+ issuer: _issuer,
+ signature: _signature,
+ data: _data,
+ uri: _uri
+ });
}
function _addKey(bytes32 _key, uint256 _purpose, uint256 _type) internal {
@@ -439,14 +438,15 @@ contract Identity is ERC725, ERC735 {
_;
}
-
function approveECDSA(uint256 _id, bool _approve,
bytes32 _key,
uint8 v,
bytes32 r,
bytes32 s)
public
- validECDSAKey(_key, keccak256(address(this), bytes4(keccak256("approve(uint256,bool)")), _id, _approve), v, r, s)
+ validECDSAKey(_key, keccak256(address(this),
+ bytes4(keccak256("approve(uint256,bool)")), _id, _approve),
+ v, r, s)
managerOrActor(_key)
returns (bool success)
{
@@ -464,7 +464,9 @@ contract Identity is ERC725, ERC735 {
bytes32 s
)
public
- validECDSAKey(_key, keccak256(address(this), bytes4(keccak256("execute(address,uint256,bytes)")), _to, _value, _data, _nonce), v, r, s)
+ validECDSAKey(_key, keccak256(address(this),
+ bytes4(keccak256("execute(address,uint256,bytes)")),
+ _to, _value, _data, _nonce), v, r, s)
managerOrActor(_key)
returns (uint256 executionId)
{
diff --git a/contracts/identity/IdentityFactory.sol b/contracts/identity/IdentityFactory.sol
index 6da2bf8..a356d63 100644
--- a/contracts/identity/IdentityFactory.sol
+++ b/contracts/identity/IdentityFactory.sol
@@ -4,15 +4,15 @@ import "../deploy/Factory.sol";
import "../deploy/UpdatableInstance.sol";
import "./IdentityKernel.sol";
+
contract IdentityFactory is Factory {
event IdentityCreated(address instance);
function IdentityFactory(bytes _infohash)
- Factory(new IdentityKernel(), _infohash)
public
+ Factory(new IdentityKernel(), _infohash)
{
-
}
function createIdentity()
diff --git a/contracts/tests/UpdatedIdentityKernel.sol b/contracts/tests/UpdatedIdentityKernel.sol
index c102d2c..ba1d288 100644
--- a/contracts/tests/UpdatedIdentityKernel.sol
+++ b/contracts/tests/UpdatedIdentityKernel.sol
@@ -2,13 +2,12 @@ pragma solidity ^0.4.17;
import "../identity/IdentityKernel.sol";
+
contract UpdatedIdentityKernel is IdentityKernel {
event TestFunctionExecuted(uint8 minApprovalsByManagementKeys);
function test() public {
TestFunctionExecuted(minimumApprovalsByKeyType[MANAGEMENT_KEY]);
- }
-
-
-}
+ }
+}
\ No newline at end of file
From 213e76d3a5f9bc1ac3aca11c12d5d4c7795c1cff Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Wed, 28 Feb 2018 14:52:32 -0400
Subject: [PATCH 034/102] Linting to reduce problems in IDE
---
contracts/deploy/Instance.sol | 1 +
1 file changed, 1 insertion(+)
diff --git a/contracts/deploy/Instance.sol b/contracts/deploy/Instance.sol
index 96c18d8..f9d60cb 100644
--- a/contracts/deploy/Instance.sol
+++ b/contracts/deploy/Instance.sol
@@ -3,6 +3,7 @@ pragma solidity ^0.4.17;
import "./InstanceStorage.sol";
import "./DelegatedCall.sol";
+
/**
* @title Instance
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
From 5ee487a48aa16df670bf29cd7e5f1e6f8e05c3fa Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Thu, 1 Mar 2018 11:42:44 -0400
Subject: [PATCH 035/102] - Fixing typo in Identity.sol - Reorganizing
javascript files used in tests - Cleaned up unit tests - Created
identityUtils.js to encode function calls sent in execute();
---
contracts/identity/Identity.sol | 2 +-
test/factory.js | 29 +++--
test/identity.js | 208 ++++++++++++++++++++++----------
test/identityExtended.js | 4 +-
utils/identityUtils.js | 133 ++++++++++++++++++++
5 files changed, 291 insertions(+), 85 deletions(-)
create mode 100644 utils/identityUtils.js
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 66c4a02..54e8f4b 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -138,7 +138,7 @@ contract Identity is ERC725, ERC735 {
}
}
- function setMininumApprovalsByKeyType(
+ function setMinimumApprovalsByKeyType(
uint256 _type,
uint8 _minimumApprovals
)
diff --git a/test/factory.js b/test/factory.js
index ac5c402..32d9b35 100644
--- a/test/factory.js
+++ b/test/factory.js
@@ -1,4 +1,5 @@
-const TestUtils = require("./TestUtils.js")
+const TestUtils = require("../utils/testUtils.js")
+const idUtils = require("../utils/identityUtils.js")
const web3EthAbi = require("web3-eth-abi");
const Identity = artifacts.require("./identity/Identity.sol");
@@ -27,15 +28,18 @@ contract('IdentityFactory', function(accounts) {
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
- 1,
+ idUtils.purposes.MANAGEMENT,
identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY")
});
it("Registers a updated identity contract", async() => {
+ const infoHash = "0xbbb";
updatedIdentityKernel = await UpdatedIdentityKernel.new({from: accounts[0]});
- let tx = await identityFactory.setKernel(updatedIdentityKernel.address, "0xbbb");
- assert.strictEqual(tx.logs[0].event, "NewKernel");
+ await identityFactory.setKernel(updatedIdentityKernel.address, infoHash);
+
+ const newKernel = await TestUtils.listenForEvent(identityFactory.NewKernel());
+ assert(newKernel.infohash, infoHash, "Infohash is not correct");
});
@@ -58,16 +62,12 @@ contract('IdentityFactory', function(accounts) {
it("Updates an identity to the latest version", async() => {
- let functionPayload = web3EthAbi.encodeFunctionCall({
- name: 'updateUpdatableInstance',
- type: 'function',
- inputs: [{
- type: 'address',
- name: '_kernel'
- }]
- }, [updatedIdentityKernel.address]);
-
- let tx1 = await identity.execute(identity.address, 0, functionPayload, {from: accounts[0]} );
+ let tx1 = await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.updateUpdatableInstance(updatedIdentityKernel.address),
+ {from: accounts[0]}
+ );
assert.strictEqual(tx1.logs[tx1.logs.length - 1].event, "Executed");
// Calling function available in updated identity kernel
@@ -75,7 +75,6 @@ contract('IdentityFactory', function(accounts) {
let tx2 = await updatedIdentity1.test({from: accounts[0]});
assert.strictEqual(tx2.logs[tx2.logs.length - 1].event, "TestFunctionExecuted");
-
assert.equal(
tx2.logs[tx2.logs.length - 1].args.minApprovalsByManagementKeys.toString(10),
1,
diff --git a/test/identity.js b/test/identity.js
index 60d7c25..d01b255 100644
--- a/test/identity.js
+++ b/test/identity.js
@@ -1,4 +1,7 @@
-const TestUtils = require("./TestUtils.js")
+const TestUtils = require("../utils/testUtils.js")
+const web3EthAbi = require("web3-eth-abi");
+const idUtils = require('../utils/identityUtils.js');
+
const Identity = artifacts.require("./identity/Identity.sol");
contract('Identity', function(accounts) {
@@ -9,129 +12,200 @@ contract('Identity', function(accounts) {
identity = await Identity.new({from: accounts[0]})
})
-
describe("Identity()", () => {
it("initialize with msg.sender as management key", async () => {
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
- 1,
+ idUtils.purposes.MANAGEMENT,
identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY")
});
-
});
describe("addKey(address _key, uint256 _type)", () => {
-
it("MANAGEMENT_KEY add a new address as ACTION_KEY", async () => {
- await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 2, 1, {from: accounts[0]})
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
+ {from: accounts[0]}
+ );
+
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
- 2,
+ idUtils.purposes.ACTION,
identity.address+".getKeyPurpose("+accounts[1]+") is not ACTION_KEY")
});
-
it("should not add key by non manager", async () => {
try {
- await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 1, 1, {from: accounts[2]})
- }catch(e){
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
+ {from: accounts[2]}
+ );
+ assert.fail('should have reverted before');
+ } catch(error) {
+ TestUtils.assertJump(error);
}
+
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
- 0,
+ idUtils.purposes.NONE,
identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
- it("should not add key type 1 by actor", async () => {
- await identity.addKey(TestUtils.addressToBytes32(accounts[2]), 2, 1, {from: accounts[0]})
- try {
- await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 1, 1, {from: accounts[2]})
- } catch(e){
- }
+ it("should not add key type 1 by actor", async () => {
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[2], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
+ {from: accounts[0]}
+ );
+
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
+ {from: accounts[2]}
+ );
+
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
- 0,
+ idUtils.purposes.NONE,
identity.address+".getKeyType("+accounts[1]+") is not correct")
});
-
-
it("fire KeyAdded(address indexed key, uint256 indexed type)", async () => {
- identity.addKey(TestUtils.addressToBytes32(accounts[1]), 2, 1, {from: accounts[0]})
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
+ {from: accounts[0]}
+ );
+
const keyAdded = await TestUtils.listenForEvent(identity.KeyAdded())
assert(keyAdded.key, TestUtils.addressToBytes32(accounts[1]), "Key is not correct")
- assert(keyAdded.keyType, 2, "Type is not correct")
+ assert(keyAdded.keyType, idUtils.types.ADDRESS, "Type is not correct")
});
-
});
describe("removeKey(address _key, uint256 _type)", () => {
+ it("MANAGEMENT_KEY should remove a key", async () => {
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
+ {from: accounts[0]}
+ );
- it("MANAGEMENT_KEY should removes a key", async () => {
- await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 1, 1, {from: accounts[0]})
- await identity.removeKey(TestUtils.addressToBytes32(accounts[0]), 1, {from: accounts[1]})
- assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
- 0,
- identity.address+".getKeyPurpose("+accounts[0]+") is not 0")
- });
-
-
- it("other key should not removes a key", async () => {
- await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 1, 1, {from: accounts[0]})
- try {
- await identity.removeKey(TestUtils.addressToBytes32(accounts[1]), 1, {from: accounts[2]})
- }catch (e) {
-
- }
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.removeKey(accounts[1], idUtils.purposes.MANAGEMENT),
+ {from: accounts[0]}
+ );
+
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
- 1,
+ idUtils.purposes.NONE,
+ identity.address+".getKeyPurpose("+accounts[1]+") is not 0")
+ });
+
+ it("other key should not removes a key", async () => {
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
+ {from: accounts[0]}
+ );
+
+ try {
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.removeKey(accounts[1], idUtils.purposes.MANAGEMENT),
+ {from: accounts[2]}
+ );
+ assert.fail('should have reverted before');
+ } catch(error) {
+ TestUtils.assertJump(error);
+ }
+
+ assert.equal(
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
+ idUtils.purposes.MANAGEMENT,
identity.address+".getKeyPurpose("+accounts[1]+") is not 0")
});
it("actor key should not remove key", async () => {
- await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 2, 1, {from: accounts[0]})
- await identity.addKey(TestUtils.addressToBytes32(accounts[2]), 2, 1, {from: accounts[0]})
- try {
- await identity.removeKey(TestUtils.addressToBytes32(accounts[1]), 1, {from: accounts[2]})
- }catch (e) {
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
+ {from: accounts[0]}
+ );
+
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[2], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
+ {from: accounts[0]}
+ );
+
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.removeKey(accounts[1], idUtils.purposes.ACTION),
+ {from: accounts[2]}
+ );
- }
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
- 2,
+ idUtils.purposes.ACTION,
identity.address+".getKeyType("+accounts[1]+") is not 0")
});
-
it("MANAGEMENT_KEY should not remove itself MANAGEMENT_KEY when there is no other MANAGEMENT_KEY", async () => {
- let assertJump = (error) => {
- assert.isAbove(error.message.search('revert'), -1, 'Revert should happen');
- }
- try {
- await identity.removeKey(TestUtils.addressToBytes32(accounts[0]), 1, {from: accounts[0]});
- assert.fail('should have reverted before');
- } catch(error) {
- assertJump(error);
- }
-
-
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.removeKey(accounts[0], idUtils.purposes.MANAGEMENT),
+ {from: accounts[0]}
+ );
+
+ assert.equal(
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
+ idUtils.purposes.MANAGEMENT,
+ identity.address+".getKeyType("+accounts[0]+") is not 1")
});
it("fire KeyRemoved(address indexed key, uint256 indexed type)", async () => {
- await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 2, 1,{from: accounts[0]})
- identity.removeKey(TestUtils.addressToBytes32(accounts[1]), 2, {from: accounts[0]})
- const keyRemoved = await TestUtils.listenForEvent(identity.KeyRemoved())
- assert(keyRemoved.key, TestUtils.addressToBytes32(accounts[1]), "Key is not correct")
- assert(keyRemoved.keyType, 2, "Type is not correct")
- });
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
+ {from: accounts[0]}
+ );
+
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.removeKey(accounts[1], idUtils.purposes.ACTION),
+ {from: accounts[0]}
+ );
+ const keyRemoved = await TestUtils.listenForEvent(identity.KeyRemoved());
+ assert(keyRemoved.key, TestUtils.addressToBytes32(accounts[1]), "Key is not correct");
+ assert(keyRemoved.keyType, idUtils.types.ADDRESS, "Type is not correct");
+ });
});
+
+ /*
describe("getKeyPurpose(address _key)", () => {
it("should start only with initializer as only key", async () => {
@@ -259,5 +333,5 @@ contract('Identity', function(accounts) {
});
});
-
+ */
});
diff --git a/test/identityExtended.js b/test/identityExtended.js
index e881362..0e2db9a 100644
--- a/test/identityExtended.js
+++ b/test/identityExtended.js
@@ -1,9 +1,9 @@
-const TestUtils = require("./TestUtils.js")
+const TestUtils = require("../utils/testUtils.js")
var ethUtils = require('ethereumjs-util')
const Identity = artifacts.require("./identity/Identity.sol");
-contract('Identity', function(accounts) {
+contract('Identity - Extended Functionality', function(accounts) {
let identity;
diff --git a/utils/identityUtils.js b/utils/identityUtils.js
new file mode 100644
index 0000000..91613a4
--- /dev/null
+++ b/utils/identityUtils.js
@@ -0,0 +1,133 @@
+const web3EthAbi = require("web3-eth-abi");
+
+const _types = {
+ ADDRESS: 0,
+ ECDSA: 1,
+ RSA: 2
+}
+
+const _purposes = {
+ MANAGEMENT: 1,
+ ACTION: 2,
+ CLAIM_SIGNER: 3,
+ ENCRYPTION: 4,
+
+ NONE: 0
+}
+
+const hexToBytes32 = (input) => {
+ input = input.replace(/^0x/i,'');
+ const stringed = "0000000000000000000000000000000000000000000000000000000000000000" + input;
+ return "0x" + stringed.substring(stringed.length - 64, stringed.length);
+}
+
+const _addKey = function(key, purpose, type){
+ if(!/^(0x)?[0-9a-f]{0,64}$/i.test(key))
+ throw new Error('Key "'+ key +'" is not a valid hex string');
+
+ if (Object.values(_purposes).indexOf(purpose) == -1)
+ throw new Error('Purpose "'+ purpose +'" is not a valid purpose');
+
+ if (Object.values(_types).indexOf(type) == -1)
+ throw new Error('Type "'+ type +'" is not a valid type');
+
+ return web3EthAbi.encodeFunctionCall({
+ name: 'addKey',
+ type: 'function',
+ inputs: [{
+ type: 'bytes32',
+ name: '_key'
+ },{
+ type: 'uint256',
+ name: '_purpose'
+ },{
+ type: 'uint256',
+ name: '_type'
+ }]
+ }, [hexToBytes32(key), purpose, type]);
+}
+
+const _removeKey = function(key, purpose){
+ if(!/^(0x)?[0-9a-f]{0,64}$/i.test(key))
+ throw new Error('Key "'+ key +'" is not a valid hex string');
+
+ if (Object.values(_purposes).indexOf(purpose) == -1)
+ throw new Error('Purpose "'+ purpose +'" is not a valid purpose');
+
+ return web3EthAbi.encodeFunctionCall({
+ name: 'removeKey',
+ type: 'function',
+ inputs: [{
+ type: 'bytes32',
+ name: '_key'
+ },{
+ type: 'uint256',
+ name: '_purpose'
+ }]
+ }, [hexToBytes32(key), purpose]);
+}
+
+
+const _setMinimumApprovalsByKeyType = function(type, minimumApprovals) {
+
+ if (Object.values(_types).indexOf(type) == -1)
+ throw new Error('Type "'+ type +'" is not a valid type');
+
+ // TODO valdate minimumApprovals
+
+ return web3EthAbi.encodeFunctionCall({
+ name: 'setMinimumApprovalsByKeyType',
+ type: 'function',
+ inputs: [{
+ type: 'uint256',
+ name: '_type'
+ },{
+ type: 'uint8',
+ name: '_minimumApprovals'
+ }]
+ }, arguments);
+}
+
+
+const _setupRecovery = function(address){
+ if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
+ throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
+
+ return web3EthAbi.encodeFunctionCall({
+ name: 'setupRecovery',
+ type: 'function',
+ inputs: [{
+ type: 'address',
+ name: '_recoveryContract'
+ }]
+ }, [address]);
+}
+
+const _updateUpdatableInstance = function(address){
+ if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
+ throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
+
+ return web3EthAbi.encodeFunctionCall({
+ name: 'updateUpdatableInstance',
+ type: 'function',
+ inputs: [{
+ type: 'address',
+ name: '_kernel'
+ }]
+ }, [address]);
+}
+
+
+
+
+module.exports = {
+ types: _types,
+ purposes: _purposes,
+ encode: {
+ addKey: _addKey,
+ removeKey: _removeKey,
+ setMinimumApprovalsByKeyType: _setMinimumApprovalsByKeyType,
+ setupRecovery: _setupRecovery,
+ updateUpdatableInstance: _updateUpdatableInstance
+ }
+}
\ No newline at end of file
From 4471c165d3ff3ca540c19f77f98447defbb7396f Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Thu, 1 Mar 2018 16:07:32 -0400
Subject: [PATCH 036/102] Identity can receive ether
- Added payable fallback function
- Added unit test for sending/receiving ether
---
contracts/identity/Identity.sol | 6 ++
contracts/tests/TestContract.sol | 11 ++
test/identity.js | 173 ++++++++++++++++++++++++++++---
3 files changed, 173 insertions(+), 17 deletions(-)
create mode 100644 contracts/tests/TestContract.sol
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 54e8f4b..a1c93ed 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -60,7 +60,9 @@ contract Identity is ERC725, ERC735 {
function Identity() public {
_addKey(bytes32(msg.sender), MANAGEMENT_KEY, 0);
+
minimumApprovalsByKeyType[MANAGEMENT_KEY] = 1;
+ minimumApprovalsByKeyType[ACTION_KEY] = 1;
}
function addKey(
@@ -483,6 +485,10 @@ contract Identity is ERC725, ERC735 {
recoverySet = true;
}
+ function () public payable {
+
+ }
+
}
diff --git a/contracts/tests/TestContract.sol b/contracts/tests/TestContract.sol
new file mode 100644
index 0000000..0ed094e
--- /dev/null
+++ b/contracts/tests/TestContract.sol
@@ -0,0 +1,11 @@
+pragma solidity ^0.4.17;
+
+
+contract TestContract {
+
+ event TestFunctionExecuted();
+
+ function test() public {
+ TestFunctionExecuted();
+ }
+}
\ No newline at end of file
diff --git a/test/identity.js b/test/identity.js
index d01b255..dd8bc45 100644
--- a/test/identity.js
+++ b/test/identity.js
@@ -1,8 +1,9 @@
-const TestUtils = require("../utils/testUtils.js")
+const TestUtils = require("../utils/testUtils.js");
const web3EthAbi = require("web3-eth-abi");
const idUtils = require('../utils/identityUtils.js');
const Identity = artifacts.require("./identity/Identity.sol");
+const TestContract = artifacts.require("./test/TestContract.sol");
contract('Identity', function(accounts) {
@@ -204,41 +205,51 @@ contract('Identity', function(accounts) {
});
-
- /*
describe("getKeyPurpose(address _key)", () => {
it("should start only with initializer as only key", async () => {
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
- 1,
+ idUtils.purposes.MANAGEMENT,
identity.address+".getKeyPurpose("+accounts[0]+") is not correct")
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
- 0,
+ idUtils.purposes.NONE,
identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
it("should get type 2 after addKey type 2", async () => {
- await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 2, 1, {from: accounts[0]})
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
+ {from: accounts[0]}
+ );
+
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
- 2,
+ idUtils.purposes.ACTION,
identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
- it("should get type 3 after addKey type 3", async () => {
- await identity.addKey(TestUtils.addressToBytes32(accounts[1]), 3, 1, {from: accounts[0]})
+ it("should get type 3 after addKey type 3", async () => {
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.CLAIM_SIGNER, idUtils.types.ADDRESS),
+ {from: accounts[0]}
+ );
+
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
- 3,
+ idUtils.purposes.CLAIM_SIGNER,
identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
});
-
+ /*
describe("getKeysByType(uint256 _type)", () => {
it("at initialization", async () => {
@@ -252,36 +263,160 @@ contract('Identity', function(accounts) {
it("after removeKey", async () => {
});
-
- it("after replaceKey", async () => {
-
- });
-
});
-
+*/
describe("execute(address _to, uint256 _value, bytes _data)", () => {
+ let testContractInstance;
+ let functionPayload;
+
+ it("Identity should receive ether", async() => {
+
+ const amountToSend = web3.toWei(0.05, "ether");
+
+ let idBalance0 = web3.eth.getBalance(identity.address);
+
+ await web3.eth.sendTransaction({from:accounts[0], to:identity.address, value: amountToSend})
+
+ let idBalance1 = web3.eth.getBalance(identity.address);
+
+ assert.equal(idBalance0.toNumber() + amountToSend, idBalance1.toNumber(), identity.address + " did not receive ether");
+ });
it("ACTOR_KEY execute arbitrary transaction", async () => {
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
+ {from: accounts[0]}
+ );
+
+ testContractInstance = await TestContract.new({from: accounts[0]});
+
+ functionPayload = web3EthAbi.encodeFunctionCall({
+ name: 'test',
+ type: 'function',
+ inputs: []
+ }, []);
+
+ await identity.execute(
+ testContractInstance.address,
+ 0,
+ functionPayload,
+ {from: accounts[1]}
+ );
+ assert.notEqual(
+ await TestUtils.listenForEvent(testContractInstance.TestFunctionExecuted()),
+ undefined,
+ "Test function was not executed");
});
it("MANAGEMENT_KEY execute arbitrary transaction", async () => {
+ await identity.execute(
+ testContractInstance.address,
+ 0,
+ functionPayload,
+ {from: accounts[0]}
+ );
+ assert.notEqual(
+ await TestUtils.listenForEvent(testContractInstance.TestFunctionExecuted()),
+ undefined,
+ "Test function was not executed");
});
it("Other keys NOT execute arbitrary transaction", async () => {
+ try {
+ await identity.execute(
+ testContractInstance.address,
+ 0,
+ functionPayload,
+ {from: accounts[3]}
+ );
+ assert.fail('should have reverted before');
+ } catch(error) {
+ TestUtils.assertJump(error);
+ }
+ });
+
+
+ it("MANAGEMENT_KEY should send ether from contract", async () => {
+
+ // Adding funds to contract
+ await web3.eth.sendTransaction({from:accounts[0], to:identity.address, value: web3.toWei(0.05, "ether")})
+
+ const amountToSend = web3.toWei(0.01, "ether");
+
+ let idBalance0 = web3.eth.getBalance(identity.address);
+ let a1Balance0 = web3.eth.getBalance(accounts[1]);
+
+ await identity.execute(
+ accounts[1],
+ amountToSend,
+ '',
+ {from: accounts[0]}
+ );
+
+ let idBalance1 = web3.eth.getBalance(identity.address);
+ let a1Balance1 = web3.eth.getBalance(accounts[1]);
+
+ assert(idBalance1.toNumber, idBalance0.toNumber - amountToSend, "Contract did not send ether");
+ assert(a1Balance1.toNumber, a1Balance0.toNumber + amountToSend, accounts[1] + " did not receive ether");
+ });
+
+ it("fire ExecutionRequested(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data)", async () => {
+ await identity.execute(
+ testContractInstance.address,
+ 0,
+ functionPayload,
+ {from: accounts[0]}
+ );
+ const executionRequested = await TestUtils.listenForEvent(identity.ExecutionRequested());
+ assert(executionRequested.to, testContractInstance.address, "To is not correct");
+ assert(executionRequested.value, 0, "Value is not correct");
+ assert(executionRequested.data, functionPayload, "Data is not correct");
});
it("fire Executed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data)", async () => {
+ await identity.execute(
+ testContractInstance.address,
+ 0,
+ functionPayload,
+ {from: accounts[0]}
+ );
+ const executed = await TestUtils.listenForEvent(identity.Executed());
+ assert(executed.to, testContractInstance.address, "To is not correct");
+ assert(executed.value, 0, "Value is not correct");
+ assert(executed.data, functionPayload, "Data is not correct");
});
});
+/*
+
+ describe("setMinimumApprovalsByKeyType(uint256 _type, uint8 _minimumApprovals)", () => {
+ it("MANAGEMENT_KEY should set minimum approvals for MANAGEMENT_KEYs", async () => {
+
+ });
+
+ it("MANAGEMENT_KEY should set minimum approvals for ACTION_KEYs", async () => {
+
+ });
+
+ it("ACTION_KEY should not be able to set minimum approvals", async () => {
+
+ });
+
+ it("Other keys should not be able to set minimum approvals", async () => {
+
+ });
+ });
+
describe("approve(bytes32 _id, bool _approve)", () => {
it("MANAGEMENT_KEY should approve a claim", async () => {
@@ -292,6 +427,10 @@ contract('Identity', function(accounts) {
});
+ it("2 out of 3 MANAGEMENT_KEY should approve a transaction and execute it", async () => {
+
+ });
+
it("fire Approved(uint256 indexed executionId, bool approved)", async () => {
});
From ea59af8e1e0487378c8dc6c554709193f71599b7 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Fri, 2 Mar 2018 02:25:46 -0300
Subject: [PATCH 037/102] add public visibility modifier
---
contracts/identity/ERC725.sol | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contracts/identity/ERC725.sol b/contracts/identity/ERC725.sol
index 3c8f5e1..17af37a 100644
--- a/contracts/identity/ERC725.sol
+++ b/contracts/identity/ERC725.sol
@@ -23,7 +23,7 @@ contract ERC725 {
function getKeyPurpose(bytes32 _key) public constant returns(uint256[] purpose);
function getKeysByPurpose(uint256 _purpose) public constant returns(bytes32[] keys);
function addKey(bytes32 _key, uint256 _purpose, uint256 _keyType) public returns (bool success);
- function removeKey(bytes32 _key, uint256 _purpose) returns (bool success);
+ function removeKey(bytes32 _key, uint256 _purpose) public returns (bool success);
function execute(address _to, uint256 _value, bytes _data) public returns (uint256 executionId);
function approve(uint256 _id, bool _approve) public returns (bool success);
}
\ No newline at end of file
From 9b17fbce889a9364cb521aff6277aa7a0c153405 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Fri, 2 Mar 2018 11:32:00 -0300
Subject: [PATCH 038/102] replace key, recovery manager reset, remove
unnecessary storage, lint
---
contracts/identity/Identity.sol | 98 ++++++++++++++++++++++++++-------
1 file changed, 78 insertions(+), 20 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index a1c93ed..acb85e8 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -15,8 +15,8 @@ contract Identity is ERC725, ERC735 {
mapping (uint256 => uint8) minimumApprovalsByKeyType;
bytes32[] pendingTransactions;
uint nonce = 0;
- bool recoverySet;
address recoveryContract;
+ address managerReset;
struct Transaction {
address to;
@@ -24,22 +24,26 @@ contract Identity is ERC725, ERC735 {
bytes data;
uint nonce;
uint approverCount;
- mapping(uint256 => uint8) approvalsByKeyType;
mapping(bytes32 => bool) approvals;
}
modifier managerOnly {
require(
- isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) ||
- (recoverySet && msg.sender == address(recoveryContract))
+ isKeyType(bytes32(msg.sender), MANAGEMENT_KEY)
);
_;
}
modifier selfOnly {
require(
- msg.sender == address(this) ||
- (recoverySet && msg.sender == address(recoveryContract))
+ msg.sender == address(this)
+ );
+ _;
+ }
+
+ modifier recoveryOnly {
+ require(
+ (recoveryContract != address(0) && msg.sender == address(recoveryContract))
);
_;
}
@@ -65,6 +69,38 @@ contract Identity is ERC725, ERC735 {
minimumApprovalsByKeyType[ACTION_KEY] = 1;
}
+ function managerReset(address _newKey)
+ external
+ recoveryOnly
+ {
+ managerReset = _newKey;
+ _addKey(managerReset, purpose, _newType);
+ minimumApprovalsByKeyType[MANAGEMENT_KEY] = keysByPurpose[MANAGEMENT_KEY].length;
+ }
+
+ function processManagerReset(uint limit)
+ external
+ {
+ require(managerReset != address(0));
+ bytes32 newKey = bytes32(managerReset);
+ bytes32[] memory managers = keysByPurpose[MANAGEMENT_KEY];
+ uint totalManagers = managers.lenght;
+ if (limit == 0) {
+ limit = totalManagers;
+ }
+ minimumApprovalsByKeyType[MANAGEMENT_KEY] = totalManagers - limit + 1;
+ for (uint i = 0; i < limit; i++) {
+ address manager = managers[i];
+ if (manager != newKey) {
+ _removeKey(manager, MANAGEMENT_KEY);
+ totalManagers--;
+ }
+ }
+ if (totalManagers == 1) {
+ managerReset = address(0);
+ }
+ }
+
function addKey(
bytes32 _key,
uint256 _purpose,
@@ -73,11 +109,26 @@ contract Identity is ERC725, ERC735 {
public
selfOnly
returns (bool success)
- {
+ {
_addKey(_key, _purpose, _type);
return true;
}
+ function replaceKey(
+ bytes32 _oldKey,
+ bytes32 _newKey,
+ uint256 _newType
+ )
+ public
+ selfOnly
+ returns (bool success)
+ {
+ uint256 purpose = key[_oldKey].purpose;
+ _addKey(_newKey, purpose, _newType);
+ _removeKey(_oldKey, purpose);
+ return true;
+ }
+
function removeKey(
bytes32 _key,
uint256 _purpose
@@ -111,7 +162,14 @@ contract Identity is ERC725, ERC735 {
return approveExecution(bytes32(msg.sender), _id, _approve);
}
- function approveExecution(bytes32 _key, uint256 _id, bool _approve) internal returns(bool success) {
+ function approveExecution(
+ bytes32 _key,
+ uint256 _id,
+ bool _approve
+ )
+ internal
+ returns(bool success)
+ {
Transaction storage trx = txx[_id];
bytes32 managerKeyHash = keccak256(_key, MANAGEMENT_KEY);
@@ -125,12 +183,12 @@ contract Identity is ERC725, ERC735 {
if (trx.to == address(this)) {
requiredKeyType = MANAGEMENT_KEY;
if (keys[managerKeyHash].purpose == MANAGEMENT_KEY) {
- approvalCount = _calculateApprovals(managerKeyHash, MANAGEMENT_KEY, _approve, trx);
+ approvalCount = _calculateApprovals(managerKeyHash, _approve, trx);
}
} else {
requiredKeyType = ACTION_KEY;
if (keys[managerKeyHash].purpose == ACTION_KEY) {
- approvalCount = _calculateApprovals(actorKeyHash, ACTION_KEY, _approve, trx);
+ approvalCount = _calculateApprovals(actorKeyHash, _approve, trx);
}
}
@@ -153,21 +211,22 @@ contract Identity is ERC725, ERC735 {
function _calculateApprovals(
bytes32 _keyHash,
- uint256 _keyType,
bool _approve,
Transaction storage trx
)
private
returns (uint8 approvalCount)
{
- if (trx.approvals[_keyHash] == false && _approve) {
- trx.approvalsByKeyType[_keyType]++;
- } else if (trx.approvals[_keyHash] == true && !_approve && trx.approverCount > 0) {
- trx.approvalsByKeyType[_keyType]--;
- }
+ require(trx.approvals[_keyHash] != _approve);
+
trx.approvals[_keyHash] = _approve;
- trx.approverCount++;
- return trx.approvalsByKeyType[_keyType];
+ if (_approve) {
+ trx.approverCount++;
+ } else {
+ trx.approverCount--;
+ }
+
+ return trx.approverCount;
}
function addClaim(
@@ -480,9 +539,8 @@ contract Identity is ERC725, ERC735 {
public
selfOnly
{
- require(recoverySet == false);
+ require(recoveryContract == address(0));
recoveryContract = _recoveryContract;
- recoverySet = true;
}
function () public payable {
From 9a07b523198d181435837d789d3eae6a03f7c41a Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Fri, 2 Mar 2018 15:00:46 -0400
Subject: [PATCH 039/102] Updated approval process
- Managers can only execute actions on identity
- Actors can execute actions outside identity
- Validated minimum approvals to not exceed available keyTypes
- Updated identity test unit to reflect changes
---
contracts/identity/Identity.sol | 87 ++++++++++----------
contracts/identity/IdentityKernel.sol | 4 +-
contracts/tests/UpdatedIdentityKernel.sol | 4 +-
test/identity.js | 97 +++++++++++++++--------
4 files changed, 112 insertions(+), 80 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index acb85e8..64322a9 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -12,11 +12,11 @@ contract Identity is ERC725, ERC735 {
mapping (uint256 => bytes32[]) claimsByType;
mapping (bytes32 => uint256) indexes;
mapping (uint => Transaction) txx;
- mapping (uint256 => uint8) minimumApprovalsByKeyType;
+ mapping (uint256 => uint256) minimumApprovalsByKeyPurpose;
bytes32[] pendingTransactions;
uint nonce = 0;
address recoveryContract;
- address managerReset;
+ address recoveryManager;
struct Transaction {
address to;
@@ -54,10 +54,11 @@ contract Identity is ERC725, ERC735 {
}
modifier managerOrActor(bytes32 _key) {
+
+
require(
isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) ||
- isKeyType(bytes32(msg.sender), ACTION_KEY) ||
- (recoverySet && msg.sender == address(recoveryContract))
+ isKeyType(bytes32(msg.sender), ACTION_KEY)
);
_;
}
@@ -65,39 +66,42 @@ contract Identity is ERC725, ERC735 {
function Identity() public {
_addKey(bytes32(msg.sender), MANAGEMENT_KEY, 0);
- minimumApprovalsByKeyType[MANAGEMENT_KEY] = 1;
- minimumApprovalsByKeyType[ACTION_KEY] = 1;
+ minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] = 1;
+ minimumApprovalsByKeyPurpose[ACTION_KEY] = 1;
}
function managerReset(address _newKey)
external
recoveryOnly
{
- managerReset = _newKey;
- _addKey(managerReset, purpose, _newType);
- minimumApprovalsByKeyType[MANAGEMENT_KEY] = keysByPurpose[MANAGEMENT_KEY].length;
+ recoveryManager = _newKey;
+ _addKey(bytes32(recoveryManager), MANAGEMENT_KEY, 0);
+ minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] = keysByPurpose[MANAGEMENT_KEY].length;
}
- function processManagerReset(uint limit)
- external
+ function processManagerReset(uint256 limit)
+ public
{
- require(managerReset != address(0));
- bytes32 newKey = bytes32(managerReset);
+ require(recoveryManager != address(0));
+ bytes32 newKey = bytes32(recoveryManager);
bytes32[] memory managers = keysByPurpose[MANAGEMENT_KEY];
- uint totalManagers = managers.lenght;
+ uint256 totalManagers = managers.length;
+
if (limit == 0) {
limit = totalManagers;
}
- minimumApprovalsByKeyType[MANAGEMENT_KEY] = totalManagers - limit + 1;
- for (uint i = 0; i < limit; i++) {
- address manager = managers[i];
+
+ minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] = totalManagers - limit + 1;
+ for (uint256 i = 0; i < limit; i++) {
+ bytes32 manager = managers[i];
if (manager != newKey) {
_removeKey(manager, MANAGEMENT_KEY);
totalManagers--;
}
}
+
if (totalManagers == 1) {
- managerReset = address(0);
+ recoveryManager = address(0);
}
}
@@ -123,7 +127,7 @@ contract Identity is ERC725, ERC735 {
selfOnly
returns (bool success)
{
- uint256 purpose = key[_oldKey].purpose;
+ uint256 purpose = keys[_oldKey].purpose;
_addKey(_newKey, purpose, _newType);
_removeKey(_oldKey, purpose);
return true;
@@ -170,52 +174,54 @@ contract Identity is ERC725, ERC735 {
internal
returns(bool success)
{
+
Transaction storage trx = txx[_id];
- bytes32 managerKeyHash = keccak256(_key, MANAGEMENT_KEY);
- bytes32 actorKeyHash = keccak256(_key, ACTION_KEY);
-
- uint8 approvalCount;
- uint256 requiredKeyType;
+ uint256 approvalCount;
+ uint256 requiredKeyPurpose;
Approved(_id, _approve);
+Debug1(_key, isKeyType(_key, ACTION_KEY));
+
if (trx.to == address(this)) {
- requiredKeyType = MANAGEMENT_KEY;
- if (keys[managerKeyHash].purpose == MANAGEMENT_KEY) {
- approvalCount = _calculateApprovals(managerKeyHash, _approve, trx);
- }
+ require(isKeyType(_key, MANAGEMENT_KEY));
+ bytes32 managerKeyHash = keccak256(_key, MANAGEMENT_KEY);
+ requiredKeyPurpose = MANAGEMENT_KEY;
+ approvalCount = _calculateApprovals(managerKeyHash, _approve, trx);
} else {
- requiredKeyType = ACTION_KEY;
- if (keys[managerKeyHash].purpose == ACTION_KEY) {
- approvalCount = _calculateApprovals(actorKeyHash, _approve, trx);
- }
+ require(isKeyType(_key, ACTION_KEY));
+ bytes32 actorKeyHash = keccak256(_key, ACTION_KEY);
+ requiredKeyPurpose = ACTION_KEY;
+ approvalCount = _calculateApprovals(actorKeyHash, _approve, trx);
}
- if (approvalCount >= minimumApprovalsByKeyType[requiredKeyType]) {
+ if (approvalCount >= minimumApprovalsByKeyPurpose[requiredKeyPurpose]) {
Executed(_id, trx.to, trx.value, trx.data);
success = trx.to.call.value(trx.value)(trx.data);
}
}
-
+event Debug1(bytes32 d, bool b);
function setMinimumApprovalsByKeyType(
- uint256 _type,
- uint8 _minimumApprovals
+ uint256 _purpose,
+ uint256 _minimumApprovals
)
public
selfOnly
{
+ Debug(_minimumApprovals, keysByPurpose[_purpose].length, 0);
require(_minimumApprovals > 0);
- minimumApprovalsByKeyType[_type] = _minimumApprovals;
+ require(_minimumApprovals <= keysByPurpose[_purpose].length);
+ minimumApprovalsByKeyPurpose[_purpose] = _minimumApprovals;
}
-
+event Debug(uint256 a, uint256 b, uint256 c);
function _calculateApprovals(
bytes32 _keyHash,
bool _approve,
Transaction storage trx
)
private
- returns (uint8 approvalCount)
+ returns (uint256 approvalCount)
{
require(trx.approvals[_keyHash] != _approve);
@@ -403,9 +409,6 @@ contract Identity is ERC725, ERC735 {
}
delete keys[keyHash];
-
- // MUST only be done by keys of purpose 1, or the identity itself.
- // TODO If its the identity itself, the approval process will determine its approval.
}
function getKey(
diff --git a/contracts/identity/IdentityKernel.sol b/contracts/identity/IdentityKernel.sol
index 7966b8b..0f74cc4 100644
--- a/contracts/identity/IdentityKernel.sol
+++ b/contracts/identity/IdentityKernel.sol
@@ -6,8 +6,8 @@ import "./Identity.sol";
contract IdentityKernel is InstanceStorage, Identity {
function initIdentity(address _caller) external {
- require(minimumApprovalsByKeyType[MANAGEMENT_KEY] == 0);
+ require(minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] == 0);
_addKey(bytes32(_caller), MANAGEMENT_KEY, 0);
- minimumApprovalsByKeyType[MANAGEMENT_KEY] = 1;
+ minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] = 1;
}
}
diff --git a/contracts/tests/UpdatedIdentityKernel.sol b/contracts/tests/UpdatedIdentityKernel.sol
index ba1d288..23ad12b 100644
--- a/contracts/tests/UpdatedIdentityKernel.sol
+++ b/contracts/tests/UpdatedIdentityKernel.sol
@@ -5,9 +5,9 @@ import "../identity/IdentityKernel.sol";
contract UpdatedIdentityKernel is IdentityKernel {
- event TestFunctionExecuted(uint8 minApprovalsByManagementKeys);
+ event TestFunctionExecuted(uint256 minApprovalsByManagementKeys);
function test() public {
- TestFunctionExecuted(minimumApprovalsByKeyType[MANAGEMENT_KEY]);
+ TestFunctionExecuted(minimumApprovalsByKeyPurpose[MANAGEMENT_KEY]);
}
}
\ No newline at end of file
diff --git a/test/identity.js b/test/identity.js
index dd8bc45..f240209 100644
--- a/test/identity.js
+++ b/test/identity.js
@@ -65,12 +65,17 @@ contract('Identity', function(accounts) {
{from: accounts[0]}
);
- await identity.execute(
- identity.address,
- 0,
- idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
- {from: accounts[2]}
- );
+ try {
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
+ {from: accounts[2]}
+ );
+ assert.fail('should have reverted before');
+ } catch(error) {
+ TestUtils.assertJump(error);
+ }
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
@@ -115,7 +120,7 @@ contract('Identity', function(accounts) {
identity.address+".getKeyPurpose("+accounts[1]+") is not 0")
});
- it("other key should not removes a key", async () => {
+ it("other key should not remove a key", async () => {
await identity.execute(
identity.address,
0,
@@ -156,12 +161,17 @@ contract('Identity', function(accounts) {
{from: accounts[0]}
);
- await identity.execute(
- identity.address,
- 0,
- idUtils.encode.removeKey(accounts[1], idUtils.purposes.ACTION),
- {from: accounts[2]}
- );
+ try {
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.removeKey(accounts[1], idUtils.purposes.ACTION),
+ {from: accounts[2]}
+ );
+ assert.fail('should have reverted before');
+ } catch(error) {
+ TestUtils.assertJump(error);
+ }
assert.equal(
await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
@@ -313,18 +323,17 @@ contract('Identity', function(accounts) {
"Test function was not executed");
});
- it("MANAGEMENT_KEY execute arbitrary transaction", async () => {
- await identity.execute(
- testContractInstance.address,
- 0,
- functionPayload,
- {from: accounts[0]}
- );
-
- assert.notEqual(
- await TestUtils.listenForEvent(testContractInstance.TestFunctionExecuted()),
- undefined,
- "Test function was not executed");
+ it("MANAGEMENT_KEY cannot execute arbitrary transaction", async () => {
+ try {
+ await identity.execute(
+ testContractInstance.address,
+ 0,
+ functionPayload,
+ {from: accounts[0]}
+ );
+ } catch(error) {
+ TestUtils.assertJump(error);
+ }
});
it("Other keys NOT execute arbitrary transaction", async () => {
@@ -342,7 +351,13 @@ contract('Identity', function(accounts) {
});
- it("MANAGEMENT_KEY should send ether from contract", async () => {
+ it("ACTION_KEY should send ether from contract", async () => {
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
+ {from: accounts[0]}
+ );
// Adding funds to contract
await web3.eth.sendTransaction({from:accounts[0], to:identity.address, value: web3.toWei(0.05, "ether")})
@@ -350,28 +365,35 @@ contract('Identity', function(accounts) {
const amountToSend = web3.toWei(0.01, "ether");
let idBalance0 = web3.eth.getBalance(identity.address);
- let a1Balance0 = web3.eth.getBalance(accounts[1]);
+ let a2Balance0 = web3.eth.getBalance(accounts[2]);
await identity.execute(
- accounts[1],
+ accounts[2],
amountToSend,
'',
- {from: accounts[0]}
+ {from: accounts[1]}
);
let idBalance1 = web3.eth.getBalance(identity.address);
- let a1Balance1 = web3.eth.getBalance(accounts[1]);
+ let a2Balance1 = web3.eth.getBalance(accounts[2]);
assert(idBalance1.toNumber, idBalance0.toNumber - amountToSend, "Contract did not send ether");
- assert(a1Balance1.toNumber, a1Balance0.toNumber + amountToSend, accounts[1] + " did not receive ether");
+ assert(a2Balance1.toNumber, a2Balance0.toNumber + amountToSend, accounts[2] + " did not receive ether");
});
it("fire ExecutionRequested(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data)", async () => {
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
+ {from: accounts[0]}
+ );
+
await identity.execute(
testContractInstance.address,
0,
functionPayload,
- {from: accounts[0]}
+ {from: accounts[1]}
);
const executionRequested = await TestUtils.listenForEvent(identity.ExecutionRequested());
@@ -381,11 +403,18 @@ contract('Identity', function(accounts) {
});
it("fire Executed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data)", async () => {
+ await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
+ {from: accounts[0]}
+ );
+
await identity.execute(
testContractInstance.address,
0,
functionPayload,
- {from: accounts[0]}
+ {from: accounts[1]}
);
const executed = await TestUtils.listenForEvent(identity.Executed());
@@ -399,7 +428,7 @@ contract('Identity', function(accounts) {
/*
- describe("setMinimumApprovalsByKeyType(uint256 _type, uint8 _minimumApprovals)", () => {
+ describe("setMinimumApprovalsByKeyPurpose(uint256 _type, uint8 _minimumApprovals)", () => {
it("MANAGEMENT_KEY should set minimum approvals for MANAGEMENT_KEYs", async () => {
});
From 63845f67f13b803df9053bc2dfca006061f5f62a Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Fri, 2 Mar 2018 17:27:08 -0400
Subject: [PATCH 040/102] Added extra verification when deleting management
keys
---
contracts/identity/Identity.sol | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 64322a9..0e5db29 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -182,8 +182,6 @@ contract Identity is ERC725, ERC735 {
Approved(_id, _approve);
-Debug1(_key, isKeyType(_key, ACTION_KEY));
-
if (trx.to == address(this)) {
require(isKeyType(_key, MANAGEMENT_KEY));
bytes32 managerKeyHash = keccak256(_key, MANAGEMENT_KEY);
@@ -199,9 +197,10 @@ Debug1(_key, isKeyType(_key, ACTION_KEY));
if (approvalCount >= minimumApprovalsByKeyPurpose[requiredKeyPurpose]) {
Executed(_id, trx.to, trx.value, trx.data);
success = trx.to.call.value(trx.value)(trx.data);
+ Debug1(success);
}
}
-event Debug1(bytes32 d, bool b);
+
function setMinimumApprovalsByKeyType(
uint256 _purpose,
uint256 _minimumApprovals
@@ -209,12 +208,11 @@ event Debug1(bytes32 d, bool b);
public
selfOnly
{
- Debug(_minimumApprovals, keysByPurpose[_purpose].length, 0);
require(_minimumApprovals > 0);
require(_minimumApprovals <= keysByPurpose[_purpose].length);
minimumApprovalsByKeyPurpose[_purpose] = _minimumApprovals;
}
-event Debug(uint256 a, uint256 b, uint256 c);
+
function _calculateApprovals(
bytes32 _keyHash,
bool _approve,
@@ -393,6 +391,7 @@ event Debug(uint256 a, uint256 b, uint256 c);
}
function _removeKey(bytes32 _key, uint256 _purpose) internal {
+
bytes32 keyHash = keccak256(_key, _purpose);
Key storage myKey = keys[keyHash];
KeyRemoved(myKey.key, myKey.purpose, myKey.keyType);
@@ -405,12 +404,18 @@ event Debug(uint256 a, uint256 b, uint256 c);
keysByPurpose[_purpose].length--;
if (_purpose == MANAGEMENT_KEY) {
- require(keysByPurpose[MANAGEMENT_KEY].length >= 1);
+ require(
+ keysByPurpose[MANAGEMENT_KEY].length >= 1 &&
+ keysByPurpose[MANAGEMENT_KEY].length >= minimumApprovalsByKeyPurpose[MANAGEMENT_KEY]
+ );
+
}
delete keys[keyHash];
}
+ event Debug(uint256 a, uint256 b);
+
function getKey(
bytes32 _key,
uint256 _purpose
From 7de52b46269928fe24955bee31a98c8935167705 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Fri, 2 Mar 2018 23:48:15 -0300
Subject: [PATCH 041/102] standarize internal method signature
---
contracts/identity/Identity.sol | 79 +++++++++++++++++++--------------
1 file changed, 46 insertions(+), 33 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 0e5db29..c97c439 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -158,20 +158,20 @@ contract Identity is ERC725, ERC735 {
approve(executionId, true);
}
- function approve(uint256 _id, bool _approve)
+ function approve(uint256 _id, bool _approval)
public
managerOrActor(bytes32(msg.sender))
returns (bool success)
{
- return approveExecution(bytes32(msg.sender), _id, _approve);
+ return _approve(bytes32(msg.sender), _id, _approval);
}
- function approveExecution(
+ function _approve(
bytes32 _key,
uint256 _id,
- bool _approve
+ bool _approval
)
- internal
+ private
returns(bool success)
{
@@ -180,24 +180,23 @@ contract Identity is ERC725, ERC735 {
uint256 approvalCount;
uint256 requiredKeyPurpose;
- Approved(_id, _approve);
+ Approved(_id, _approval);
if (trx.to == address(this)) {
require(isKeyType(_key, MANAGEMENT_KEY));
bytes32 managerKeyHash = keccak256(_key, MANAGEMENT_KEY);
requiredKeyPurpose = MANAGEMENT_KEY;
- approvalCount = _calculateApprovals(managerKeyHash, _approve, trx);
+ approvalCount = _calculateApprovals(managerKeyHash, _approval, trx);
} else {
require(isKeyType(_key, ACTION_KEY));
bytes32 actorKeyHash = keccak256(_key, ACTION_KEY);
requiredKeyPurpose = ACTION_KEY;
- approvalCount = _calculateApprovals(actorKeyHash, _approve, trx);
+ approvalCount = _calculateApprovals(actorKeyHash, _approval, trx);
}
if (approvalCount >= minimumApprovalsByKeyPurpose[requiredKeyPurpose]) {
Executed(_id, trx.to, trx.value, trx.data);
success = trx.to.call.value(trx.value)(trx.data);
- Debug1(success);
}
}
@@ -215,16 +214,16 @@ contract Identity is ERC725, ERC735 {
function _calculateApprovals(
bytes32 _keyHash,
- bool _approve,
+ bool _approval,
Transaction storage trx
)
private
returns (uint256 approvalCount)
{
- require(trx.approvals[_keyHash] != _approve);
+ require(trx.approvals[_keyHash] != _approval);
- trx.approvals[_keyHash] = _approve;
- if (_approve) {
+ trx.approvals[_keyHash] = _approval;
+ if (_approval) {
trx.approverCount++;
} else {
trx.approverCount--;
@@ -414,8 +413,6 @@ contract Identity is ERC725, ERC735 {
delete keys[keyHash];
}
- event Debug(uint256 a, uint256 b);
-
function getKey(
bytes32 _key,
uint256 _purpose
@@ -476,7 +473,7 @@ contract Identity is ERC725, ERC735 {
function getKeysByPurpose(uint256 _purpose)
public
constant
- returns(bytes32[] keys)
+ returns(bytes32[])
{
return keysByPurpose[_purpose];
}
@@ -498,28 +495,44 @@ contract Identity is ERC725, ERC735 {
return claimsByType[_claimType];
}
- modifier validECDSAKey(
+ modifier validECDSAKey (
bytes32 _key,
- bytes32 signHash,
- uint8 v, bytes32 r, bytes32 s) {
- require(address(_key) == ecrecover(keccak256("\x19Ethereum Signed Message:\n32", signHash), v, r, s));
+ bytes32 _signHash,
+ uint8 _v,
+ bytes32 _r,
+ bytes32 _s
+ )
+ {
+ require(address(_key) == ecrecover(keccak256("\x19Ethereum Signed Message:\n32", _signHash), _v, _r, _s));
require(keys[_key].purpose != 0);
_;
}
- function approveECDSA(uint256 _id, bool _approve,
+ function approveECDSA(
+ uint256 _id,
+ bool _approval,
bytes32 _key,
- uint8 v,
- bytes32 r,
- bytes32 s)
+ uint8 _v,
+ bytes32 _r,
+ bytes32 _s
+ )
public
- validECDSAKey(_key, keccak256(address(this),
- bytes4(keccak256("approve(uint256,bool)")), _id, _approve),
- v, r, s)
+ validECDSAKey(
+ _key,
+ keccak256(
+ address(this),
+ bytes4(keccak256("approve(uint256,bool)")),
+ _id,
+ _approval
+ ),
+ _v,
+ _r,
+ _s
+ )
managerOrActor(_key)
returns (bool success)
{
- return approveExecution(_key, _id, _approve);
+ return _approve(_key, _id, _approval);
}
function executeECDSA(
@@ -528,19 +541,19 @@ contract Identity is ERC725, ERC735 {
bytes _data,
uint _nonce,
bytes32 _key,
- uint8 v,
- bytes32 r,
- bytes32 s
+ uint8 _v,
+ bytes32 _r,
+ bytes32 _s
)
public
validECDSAKey(_key, keccak256(address(this),
bytes4(keccak256("execute(address,uint256,bytes)")),
- _to, _value, _data, _nonce), v, r, s)
+ _to, _value, _data, _nonce), _v, _r, _s)
managerOrActor(_key)
returns (uint256 executionId)
{
executionId = _execute(_to, _value, _data);
- approveExecution(_key, executionId, true);
+ _approve(_key, executionId, true);
}
function setupRecovery(address _recoveryContract)
From 80b1089bd307711f9174bbc568d931d9ecf62a7c Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Fri, 2 Mar 2018 23:54:36 -0300
Subject: [PATCH 042/102] reoder functions: {external,public,internal,private}
---
contracts/identity/Identity.sol | 362 ++++++++++++++++----------------
1 file changed, 183 insertions(+), 179 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index c97c439..ca38b86 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -166,40 +166,6 @@ contract Identity is ERC725, ERC735 {
return _approve(bytes32(msg.sender), _id, _approval);
}
- function _approve(
- bytes32 _key,
- uint256 _id,
- bool _approval
- )
- private
- returns(bool success)
- {
-
- Transaction storage trx = txx[_id];
-
- uint256 approvalCount;
- uint256 requiredKeyPurpose;
-
- Approved(_id, _approval);
-
- if (trx.to == address(this)) {
- require(isKeyType(_key, MANAGEMENT_KEY));
- bytes32 managerKeyHash = keccak256(_key, MANAGEMENT_KEY);
- requiredKeyPurpose = MANAGEMENT_KEY;
- approvalCount = _calculateApprovals(managerKeyHash, _approval, trx);
- } else {
- require(isKeyType(_key, ACTION_KEY));
- bytes32 actorKeyHash = keccak256(_key, ACTION_KEY);
- requiredKeyPurpose = ACTION_KEY;
- approvalCount = _calculateApprovals(actorKeyHash, _approval, trx);
- }
-
- if (approvalCount >= minimumApprovalsByKeyPurpose[requiredKeyPurpose]) {
- Executed(_id, trx.to, trx.value, trx.data);
- success = trx.to.call.value(trx.value)(trx.data);
- }
- }
-
function setMinimumApprovalsByKeyType(
uint256 _purpose,
uint256 _minimumApprovals
@@ -212,26 +178,7 @@ contract Identity is ERC725, ERC735 {
minimumApprovalsByKeyPurpose[_purpose] = _minimumApprovals;
}
- function _calculateApprovals(
- bytes32 _keyHash,
- bool _approval,
- Transaction storage trx
- )
- private
- returns (uint256 approvalCount)
- {
- require(trx.approvals[_keyHash] != _approval);
-
- trx.approvals[_keyHash] = _approval;
- if (_approval) {
- trx.approverCount++;
- } else {
- trx.approverCount--;
- }
-
- return trx.approverCount;
- }
-
+
function addClaim(
uint256 _claimType,
uint256 _scheme,
@@ -288,131 +235,6 @@ contract Identity is ERC725, ERC735 {
return true;
}
- function _execute(
- address _to,
- uint256 _value,
- bytes _data
- )
- private
- returns (uint256 executionId)
- {
- executionId = nonce;
- ExecutionRequested(executionId, _to, _value, _data);
- txx[executionId] = Transaction(
- {
- to: _to,
- value: _value,
- data: _data,
- nonce: nonce,
- approverCount: 0
- });
- nonce++;
- }
-
- function _includeClaim(
- bytes32 _claimHash,
- uint256 _claimType,
- uint256 _scheme,
- address _issuer,
- bytes _signature,
- bytes _data,
- string _uri
- )
- private
- {
- claims[_claimHash] = Claim(
- {
- claimType: _claimType,
- scheme: _scheme,
- issuer: _issuer,
- signature: _signature,
- data: _data,
- uri: _uri
- }
- );
- indexes[_claimHash] = claimsByType[_claimType].length;
- claimsByType[_claimType].push(_claimHash);
- ClaimAdded(
- _claimHash,
- _claimType,
- _scheme,
- _issuer,
- _signature,
- _data,
- _uri
- );
- }
-
- function _modifyClaim(
- bytes32 _claimHash,
- uint256 _claimType,
- uint256 _scheme,
- address _issuer,
- bytes _signature,
- bytes _data,
- string _uri
- )
- private
- {
- require(msg.sender == _issuer);
- ClaimChanged(
- _claimHash,
- _claimType,
- _scheme,
- _issuer,
- _signature,
- _data,
- _uri
- );
- claims[_claimHash] = Claim({
- claimType: _claimType,
- scheme: _scheme,
- issuer: _issuer,
- signature: _signature,
- data: _data,
- uri: _uri
- });
- }
-
- function _addKey(bytes32 _key, uint256 _purpose, uint256 _type) internal {
- bytes32 keyHash = keccak256(_key, _purpose);
-
- require(keys[keyHash].purpose == 0);
- require(
- _purpose == MANAGEMENT_KEY ||
- _purpose == ACTION_KEY ||
- _purpose == CLAIM_SIGNER_KEY ||
- _purpose == ENCRYPTION_KEY
- );
- KeyAdded(_key, _purpose, _type);
- keys[keyHash] = Key(_purpose, _type, _key);
- indexes[keyHash] = keysByPurpose[_purpose].push(_key) - 1;
- }
-
- function _removeKey(bytes32 _key, uint256 _purpose) internal {
-
- bytes32 keyHash = keccak256(_key, _purpose);
- Key storage myKey = keys[keyHash];
- KeyRemoved(myKey.key, myKey.purpose, myKey.keyType);
-
- uint index = indexes[keyHash];
- delete indexes[keyHash];
- bytes32 replacer = keysByPurpose[_purpose][keysByPurpose[_purpose].length - 1];
- keysByPurpose[_purpose][index] = replacer;
- indexes[keccak256(replacer, _purpose)] = index;
- keysByPurpose[_purpose].length--;
-
- if (_purpose == MANAGEMENT_KEY) {
- require(
- keysByPurpose[MANAGEMENT_KEY].length >= 1 &&
- keysByPurpose[MANAGEMENT_KEY].length >= minimumApprovalsByKeyPurpose[MANAGEMENT_KEY]
- );
-
- }
-
- delete keys[keyHash];
- }
-
function getKey(
bytes32 _key,
uint256 _purpose
@@ -568,6 +390,188 @@ contract Identity is ERC725, ERC735 {
}
+ function _execute(
+ address _to,
+ uint256 _value,
+ bytes _data
+ )
+ private
+ returns (uint256 executionId)
+ {
+ executionId = nonce;
+ ExecutionRequested(executionId, _to, _value, _data);
+ txx[executionId] = Transaction(
+ {
+ to: _to,
+ value: _value,
+ data: _data,
+ nonce: nonce,
+ approverCount: 0
+ });
+ nonce++;
+ }
+
+ function _approve(
+ bytes32 _key,
+ uint256 _id,
+ bool _approval
+ )
+ private
+ returns(bool success)
+ {
+
+ Transaction storage trx = txx[_id];
+
+ uint256 approvalCount;
+ uint256 requiredKeyPurpose;
+
+ Approved(_id, _approval);
+
+ if (trx.to == address(this)) {
+ require(isKeyType(_key, MANAGEMENT_KEY));
+ bytes32 managerKeyHash = keccak256(_key, MANAGEMENT_KEY);
+ requiredKeyPurpose = MANAGEMENT_KEY;
+ approvalCount = _calculateApprovals(managerKeyHash, _approval, trx);
+ } else {
+ require(isKeyType(_key, ACTION_KEY));
+ bytes32 actorKeyHash = keccak256(_key, ACTION_KEY);
+ requiredKeyPurpose = ACTION_KEY;
+ approvalCount = _calculateApprovals(actorKeyHash, _approval, trx);
+ }
+
+ if (approvalCount >= minimumApprovalsByKeyPurpose[requiredKeyPurpose]) {
+ Executed(_id, trx.to, trx.value, trx.data);
+ success = trx.to.call.value(trx.value)(trx.data);
+ }
+ }
+
+ function _addKey(bytes32 _key, uint256 _purpose, uint256 _type) private {
+ bytes32 keyHash = keccak256(_key, _purpose);
+
+ require(keys[keyHash].purpose == 0);
+ require(
+ _purpose == MANAGEMENT_KEY ||
+ _purpose == ACTION_KEY ||
+ _purpose == CLAIM_SIGNER_KEY ||
+ _purpose == ENCRYPTION_KEY
+ );
+ KeyAdded(_key, _purpose, _type);
+ keys[keyHash] = Key(_purpose, _type, _key);
+ indexes[keyHash] = keysByPurpose[_purpose].push(_key) - 1;
+ }
+
+ function _removeKey(bytes32 _key, uint256 _purpose) private {
+
+ bytes32 keyHash = keccak256(_key, _purpose);
+ Key storage myKey = keys[keyHash];
+ KeyRemoved(myKey.key, myKey.purpose, myKey.keyType);
+
+ uint index = indexes[keyHash];
+ delete indexes[keyHash];
+ bytes32 replacer = keysByPurpose[_purpose][keysByPurpose[_purpose].length - 1];
+ keysByPurpose[_purpose][index] = replacer;
+ indexes[keccak256(replacer, _purpose)] = index;
+ keysByPurpose[_purpose].length--;
+
+ if (_purpose == MANAGEMENT_KEY) {
+ require(
+ keysByPurpose[MANAGEMENT_KEY].length >= 1 &&
+ keysByPurpose[MANAGEMENT_KEY].length >= minimumApprovalsByKeyPurpose[MANAGEMENT_KEY]
+ );
+
+ }
+
+ delete keys[keyHash];
+ }
+
+ function _calculateApprovals(
+ bytes32 _keyHash,
+ bool _approval,
+ Transaction storage trx
+ )
+ private
+ returns (uint256 approvalCount)
+ {
+ require(trx.approvals[_keyHash] != _approval);
+
+ trx.approvals[_keyHash] = _approval;
+ if (_approval) {
+ trx.approverCount++;
+ } else {
+ trx.approverCount--;
+ }
+
+ return trx.approverCount;
+ }
+
+
+ function _includeClaim(
+ bytes32 _claimHash,
+ uint256 _claimType,
+ uint256 _scheme,
+ address _issuer,
+ bytes _signature,
+ bytes _data,
+ string _uri
+ )
+ private
+ {
+ claims[_claimHash] = Claim(
+ {
+ claimType: _claimType,
+ scheme: _scheme,
+ issuer: _issuer,
+ signature: _signature,
+ data: _data,
+ uri: _uri
+ }
+ );
+ indexes[_claimHash] = claimsByType[_claimType].length;
+ claimsByType[_claimType].push(_claimHash);
+ ClaimAdded(
+ _claimHash,
+ _claimType,
+ _scheme,
+ _issuer,
+ _signature,
+ _data,
+ _uri
+ );
+ }
+
+
+ function _modifyClaim(
+ bytes32 _claimHash,
+ uint256 _claimType,
+ uint256 _scheme,
+ address _issuer,
+ bytes _signature,
+ bytes _data,
+ string _uri
+ )
+ private
+ {
+ require(msg.sender == _issuer);
+ ClaimChanged(
+ _claimHash,
+ _claimType,
+ _scheme,
+ _issuer,
+ _signature,
+ _data,
+ _uri
+ );
+ claims[_claimHash] = Claim({
+ claimType: _claimType,
+ scheme: _scheme,
+ issuer: _issuer,
+ signature: _signature,
+ data: _data,
+ uri: _uri
+ });
+ }
+
+
}
From 612fdb7f4309dbf7d545d03515ff8355174112ba Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Sat, 3 Mar 2018 00:00:11 -0300
Subject: [PATCH 043/102] linting and reorder
---
contracts/identity/Identity.sol | 98 +++++++++++++++++++++++----------
1 file changed, 68 insertions(+), 30 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index ca38b86..641a218 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -54,14 +54,30 @@ contract Identity is ERC725, ERC735 {
}
modifier managerOrActor(bytes32 _key) {
-
-
require(
isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) ||
isKeyType(bytes32(msg.sender), ACTION_KEY)
);
_;
}
+
+ modifier validECDSAKey (
+ bytes32 _key,
+ bytes32 _signHash,
+ uint8 _v,
+ bytes32 _r,
+ bytes32 _s
+ )
+ {
+ require(address(_key) == ecrecover(
+ keccak256("\x19Ethereum Signed Message:\n32", _signHash),
+ _v,
+ _r,
+ _s
+ ));
+ require(keys[_key].purpose != 0);
+ _;
+ }
function Identity() public {
_addKey(bytes32(msg.sender), MANAGEMENT_KEY, 0);
@@ -69,9 +85,16 @@ contract Identity is ERC725, ERC735 {
minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] = 1;
minimumApprovalsByKeyPurpose[ACTION_KEY] = 1;
}
-
+
+ function ()
+ public
+ payable
+ {
+
+ }
+
function managerReset(address _newKey)
- external
+ public
recoveryOnly
{
recoveryManager = _newKey;
@@ -213,7 +236,10 @@ contract Identity is ERC725, ERC735 {
}
}
- function removeClaim(bytes32 _claimId) public returns (bool success) {
+ function removeClaim(bytes32 _claimId)
+ public
+ returns (bool success)
+ {
Claim memory c = claims[_claimId];
require(
@@ -303,7 +329,14 @@ contract Identity is ERC725, ERC735 {
function getClaim(bytes32 _claimId)
public
constant
- returns(uint256 claimType, uint256 scheme, address issuer, bytes signature, bytes data, string uri)
+ returns(
+ uint256 claimType,
+ uint256 scheme,
+ address issuer,
+ bytes signature,
+ bytes data,
+ string uri
+ )
{
Claim memory _claim = claims[_claimId];
return (_claim.claimType, _claim.scheme, _claim.issuer, _claim.signature, _claim.data, _claim.uri);
@@ -317,19 +350,6 @@ contract Identity is ERC725, ERC735 {
return claimsByType[_claimType];
}
- modifier validECDSAKey (
- bytes32 _key,
- bytes32 _signHash,
- uint8 _v,
- bytes32 _r,
- bytes32 _s
- )
- {
- require(address(_key) == ecrecover(keccak256("\x19Ethereum Signed Message:\n32", _signHash), _v, _r, _s));
- require(keys[_key].purpose != 0);
- _;
- }
-
function approveECDSA(
uint256 _id,
bool _approval,
@@ -368,9 +388,21 @@ contract Identity is ERC725, ERC735 {
bytes32 _s
)
public
- validECDSAKey(_key, keccak256(address(this),
- bytes4(keccak256("execute(address,uint256,bytes)")),
- _to, _value, _data, _nonce), _v, _r, _s)
+ validECDSAKey(
+ _key,
+ keccak256(
+ address(this),
+ bytes4(
+ keccak256("execute(address,uint256,bytes)")),
+ _to,
+ _value,
+ _data,
+ _nonce
+ ),
+ _v,
+ _r,
+ _s
+ )
managerOrActor(_key)
returns (uint256 executionId)
{
@@ -386,10 +418,6 @@ contract Identity is ERC725, ERC735 {
recoveryContract = _recoveryContract;
}
- function () public payable {
-
- }
-
function _execute(
address _to,
uint256 _value,
@@ -445,7 +473,13 @@ contract Identity is ERC725, ERC735 {
}
}
- function _addKey(bytes32 _key, uint256 _purpose, uint256 _type) private {
+ function _addKey(
+ bytes32 _key,
+ uint256 _purpose,
+ uint256 _type
+ )
+ private
+ {
bytes32 keyHash = keccak256(_key, _purpose);
require(keys[keyHash].purpose == 0);
@@ -460,12 +494,16 @@ contract Identity is ERC725, ERC735 {
indexes[keyHash] = keysByPurpose[_purpose].push(_key) - 1;
}
- function _removeKey(bytes32 _key, uint256 _purpose) private {
-
+ function _removeKey(
+ bytes32 _key,
+ uint256 _purpose
+ )
+ private
+ {
bytes32 keyHash = keccak256(_key, _purpose);
Key storage myKey = keys[keyHash];
KeyRemoved(myKey.key, myKey.purpose, myKey.keyType);
-
+
uint index = indexes[keyHash];
delete indexes[keyHash];
bytes32 replacer = keysByPurpose[_purpose][keysByPurpose[_purpose].length - 1];
From a6cf6f4d27c45974d7c1981362810e7332b71fee Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Sat, 3 Mar 2018 17:05:50 -0300
Subject: [PATCH 044/102] fix compile error in IdentityKernel
---
contracts/identity/Identity.sol | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 641a218..ff6c76b 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -478,7 +478,7 @@ contract Identity is ERC725, ERC735 {
uint256 _purpose,
uint256 _type
)
- private
+ internal
{
bytes32 keyHash = keccak256(_key, _purpose);
From 84bb005997e8a0c116a43ccce9cf3c47e5986d9a Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Sat, 3 Mar 2018 17:16:32 -0300
Subject: [PATCH 045/102] constructor logic encapsulated to prevent programming
errors
---
contracts/identity/Identity.sol | 19 +++++++++++++------
contracts/identity/IdentityKernel.sol | 4 +---
2 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index ff6c76b..745d0b7 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -80,11 +80,8 @@ contract Identity is ERC725, ERC735 {
}
function Identity() public {
- _addKey(bytes32(msg.sender), MANAGEMENT_KEY, 0);
-
- minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] = 1;
- minimumApprovalsByKeyPurpose[ACTION_KEY] = 1;
- }
+ _constructIdentity(msg.sender);
+ }
function ()
public
@@ -417,6 +414,16 @@ contract Identity is ERC725, ERC735 {
require(recoveryContract == address(0));
recoveryContract = _recoveryContract;
}
+
+ function _constructIdentity(address _manager)
+ internal
+ {
+ require(minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] == 0);
+ _addKey(bytes32(_manager), MANAGEMENT_KEY, 0);
+
+ minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] = 1;
+ minimumApprovalsByKeyPurpose[ACTION_KEY] = 1;
+ }
function _execute(
address _to,
@@ -478,7 +485,7 @@ contract Identity is ERC725, ERC735 {
uint256 _purpose,
uint256 _type
)
- internal
+ private
{
bytes32 keyHash = keccak256(_key, _purpose);
diff --git a/contracts/identity/IdentityKernel.sol b/contracts/identity/IdentityKernel.sol
index 0f74cc4..f05d2d7 100644
--- a/contracts/identity/IdentityKernel.sol
+++ b/contracts/identity/IdentityKernel.sol
@@ -6,8 +6,6 @@ import "./Identity.sol";
contract IdentityKernel is InstanceStorage, Identity {
function initIdentity(address _caller) external {
- require(minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] == 0);
- _addKey(bytes32(_caller), MANAGEMENT_KEY, 0);
- minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] = 1;
+ _constructIdentity(_caller);
}
}
From 5036cc46308961b8f5bc124cf670a04582a968aa Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Sun, 4 Mar 2018 03:31:07 -0300
Subject: [PATCH 046/102] updates delayed in 30 days to prevent takeover
---
contracts/deploy/DelayedUpdatableInstance.sol | 63 +++++++++++++++++++
.../DelayedUpdatableInstanceStorage.sol | 21 +++++++
contracts/identity/IdentityFactory.sol | 4 +-
contracts/identity/IdentityKernel.sol | 4 +-
4 files changed, 88 insertions(+), 4 deletions(-)
create mode 100644 contracts/deploy/DelayedUpdatableInstance.sol
create mode 100644 contracts/deploy/DelayedUpdatableInstanceStorage.sol
diff --git a/contracts/deploy/DelayedUpdatableInstance.sol b/contracts/deploy/DelayedUpdatableInstance.sol
new file mode 100644
index 0000000..af12bca
--- /dev/null
+++ b/contracts/deploy/DelayedUpdatableInstance.sol
@@ -0,0 +1,63 @@
+pragma solidity ^0.4.17;
+
+import "./DelayedUpdatableInstanceStorage.sol";
+import "./DelegatedCall.sol";
+
+/**
+ * @title DelayedUpdatableInstance
+ * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
+ * @dev Contract that can be updated by a call from itself.
+ */
+contract DelayedUpdatableInstance is DelayedUpdatableInstanceStorage, DelegatedCall {
+
+ event UpdateRequested(address newKernel, uint256 activation);
+ event UpdateCancelled();
+ event UpdateConfirmed(address oldKernel, address newKernel);
+
+ function DelayedUpdatableInstance(address _kernel) public {
+ kernel = _kernel;
+ }
+
+ /**
+ * @dev delegatecall everything (but declared functions) to `_target()`
+ * @notice Verify `kernel()` code to predict behavior
+ */
+ function () external delegated {
+ //all goes to kernel
+ }
+
+ /**
+ * @dev returns kernel if kernel that is configured
+ * @return kernel address
+ */
+ function targetDelegatedCall()
+ internal
+ constant
+ returns(address)
+ {
+ return kernel;
+ }
+
+ function updateRequestUpdatableInstance(address _kernel) external {
+ require(msg.sender == address(this));
+ uint activation = block.timestamp + 30 days;
+ update = Update(_kernel, activation);
+ UpdateRequested(_kernel, activation);
+ }
+
+ function updateConfirmUpdatableInstance(address _kernel) external {
+ require(msg.sender == address(this));
+ Update memory pending = update;
+ require(pending.kernel == _kernel);
+ require(pending.activation < block.timestamp);
+ kernel = pending.kernel;
+ delete update;
+ UpdateConfirmed(kernel, pending.kernel);
+ }
+
+ function updateCancelUpdatableInstance() external {
+ require(msg.sender == address(this));
+ delete update;
+ }
+
+}
\ No newline at end of file
diff --git a/contracts/deploy/DelayedUpdatableInstanceStorage.sol b/contracts/deploy/DelayedUpdatableInstanceStorage.sol
new file mode 100644
index 0000000..274da20
--- /dev/null
+++ b/contracts/deploy/DelayedUpdatableInstanceStorage.sol
@@ -0,0 +1,21 @@
+pragma solidity ^0.4.17;
+
+import "./InstanceStorage.sol";
+
+/**
+ * @title InstanceStorage
+ * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
+ * @dev Defines kernel vars that Kernel contract share with Instance.
+ * Important to avoid overwriting wrong storage pointers is that
+ * InstanceStorage should be always the first contract at heritance.
+ */
+contract DelayedUpdatableInstanceStorage is InstanceStorage {
+ // protected zone start (InstanceStorage vars)
+ Update update;
+
+ struct Update {
+ address kernel;
+ uint256 activation;
+ }
+ // protected zone end
+}
\ No newline at end of file
diff --git a/contracts/identity/IdentityFactory.sol b/contracts/identity/IdentityFactory.sol
index a356d63..a0dac9a 100644
--- a/contracts/identity/IdentityFactory.sol
+++ b/contracts/identity/IdentityFactory.sol
@@ -1,7 +1,7 @@
pragma solidity ^0.4.17;
import "../deploy/Factory.sol";
-import "../deploy/UpdatableInstance.sol";
+import "../deploy/DelayedUpdatableInstance.sol";
import "./IdentityKernel.sol";
@@ -24,7 +24,7 @@ contract IdentityFactory is Factory {
function createIdentity(address _idOwner)
public
{
- IdentityKernel instance = IdentityKernel(new UpdatableInstance(address(latestKernel)));
+ IdentityKernel instance = IdentityKernel(new DelayedUpdatableInstance(address(latestKernel)));
instance.initIdentity(_idOwner);
IdentityCreated(address(instance));
}
diff --git a/contracts/identity/IdentityKernel.sol b/contracts/identity/IdentityKernel.sol
index f05d2d7..d40056f 100644
--- a/contracts/identity/IdentityKernel.sol
+++ b/contracts/identity/IdentityKernel.sol
@@ -1,9 +1,9 @@
pragma solidity ^0.4.17;
-import "../deploy/InstanceStorage.sol";
+import "../deploy/DelayedUpdatableInstanceStorage.sol";
import "./Identity.sol";
-contract IdentityKernel is InstanceStorage, Identity {
+contract IdentityKernel is DelayedUpdatableInstanceStorage, Identity {
function initIdentity(address _caller) external {
_constructIdentity(_caller);
From 266b1c3056d5679cd08aef314befed9cb2775344 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Sun, 11 Mar 2018 22:11:42 +0700
Subject: [PATCH 047/102] avoid overwrite of target function
---
contracts/deploy/DelegatedCall.sol | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/contracts/deploy/DelegatedCall.sol b/contracts/deploy/DelegatedCall.sol
index 8f42dea..0526c76 100644
--- a/contracts/deploy/DelegatedCall.sol
+++ b/contracts/deploy/DelegatedCall.sol
@@ -4,7 +4,8 @@ pragma solidity ^0.4.17;
/**
* @title DelegatedCall
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
- * @dev Abstract contract that delegates calls by `delegated` modifier to result of `_target()`
+ * @dev Abstract contract that delegates calls by `delegated` modifier to result of `targetDelegatedCall()`
+ * Important to avoid overwriting wrong storage pointers is that never define storage to this contract
*/
contract DelegatedCall {
/**
@@ -12,20 +13,21 @@ contract DelegatedCall {
*/
modifier delegated {
//require successfull delegate call to remote `_target()`
- require(_target().delegatecall(msg.data));
+ require(targetDelegatedCall().delegatecall(msg.data));
assembly {
let outSize := returndatasize
let outDataPtr := mload(0x40) //load memory
returndatacopy(outDataPtr, 0, outSize) //copy last return into pointer
return(outDataPtr, outSize)
}
+ assert(false); //should never reach here
_; //never will execute local logic
}
/**
* @dev defines the address for delegation of calls
*/
- function _target()
+ function targetDelegatedCall()
internal
constant
returns(address);
From 1424baafa9974ad2c4b7e9f5fbd712b264c324c2 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Sun, 11 Mar 2018 22:12:50 +0700
Subject: [PATCH 048/102] include SOB proposal for republic
---
contracts/deploy/Instance.sol | 38 ++++++++++++++++++++++++++
contracts/deploy/InstanceStorage.sol | 15 ++++++++++
contracts/deploy/UpdatableInstance.sol | 25 +++++++++++++++++
3 files changed, 78 insertions(+)
create mode 100644 contracts/deploy/Instance.sol
create mode 100644 contracts/deploy/InstanceStorage.sol
create mode 100644 contracts/deploy/UpdatableInstance.sol
diff --git a/contracts/deploy/Instance.sol b/contracts/deploy/Instance.sol
new file mode 100644
index 0000000..96c18d8
--- /dev/null
+++ b/contracts/deploy/Instance.sol
@@ -0,0 +1,38 @@
+pragma solidity ^0.4.17;
+
+import "./InstanceStorage.sol";
+import "./DelegatedCall.sol";
+
+/**
+ * @title Instance
+ * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
+ * @dev Contract that forward everything through delegatecall to defined kernel
+ */
+contract Instance is InstanceStorage, DelegatedCall {
+
+ function Instance(address _kernel) public {
+ kernel = _kernel;
+ }
+
+ /**
+ * @dev delegatecall everything (but declared functions) to `_target()`
+ * @notice Verify `kernel()` code to predict behavior
+ */
+ function () external delegated {
+ //all goes to kernel
+ }
+
+ /**
+ * @dev returns kernel if kernel that is configured
+ * @return kernel address
+ */
+ function targetDelegatedCall()
+ internal
+ constant
+ returns(address)
+ {
+ return kernel;
+ }
+
+
+}
\ No newline at end of file
diff --git a/contracts/deploy/InstanceStorage.sol b/contracts/deploy/InstanceStorage.sol
new file mode 100644
index 0000000..ba84f64
--- /dev/null
+++ b/contracts/deploy/InstanceStorage.sol
@@ -0,0 +1,15 @@
+pragma solidity ^0.4.17;
+
+
+/**
+ * @title InstanceStorage
+ * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
+ * @dev Defines kernel vars that Kernel contract share with Instance.
+ * Important to avoid overwriting wrong storage pointers is that
+ * InstanceStorage should be always the first contract at heritance.
+ */
+contract InstanceStorage {
+ // protected zone start (InstanceStorage vars)
+ address public kernel;
+ // protected zone end
+}
\ No newline at end of file
diff --git a/contracts/deploy/UpdatableInstance.sol b/contracts/deploy/UpdatableInstance.sol
new file mode 100644
index 0000000..550df8d
--- /dev/null
+++ b/contracts/deploy/UpdatableInstance.sol
@@ -0,0 +1,25 @@
+pragma solidity ^0.4.17;
+
+import "./Instance.sol";
+
+
+/**
+ * @title UpdatableInstance
+ * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
+ * @dev Contract that can be updated by a call from itself.
+ */
+contract UpdatableInstance is Instance {
+
+ function UpdatableInstance(address _kernel)
+ Instance(_kernel)
+ public
+ {
+
+ }
+
+ function updateUpdatableInstance(address _kernel) external {
+ require(msg.sender == address(this));
+ kernel = _kernel;
+ }
+
+}
\ No newline at end of file
From 9c2cbe28f7f20ffd4c526cd7197577a4708af4e7 Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Mon, 12 Mar 2018 11:31:17 -0400
Subject: [PATCH 049/102] Adding Embark Support
---
contracts/identity/ERC725.sol | 2 +-
contracts/identity/ERC735.sol | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/contracts/identity/ERC725.sol b/contracts/identity/ERC725.sol
index 17af37a..69b2898 100644
--- a/contracts/identity/ERC725.sol
+++ b/contracts/identity/ERC725.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.18;
+pragma solidity ^0.4.17;
contract ERC725 {
diff --git a/contracts/identity/ERC735.sol b/contracts/identity/ERC735.sol
index 2109d28..77cef98 100644
--- a/contracts/identity/ERC735.sol
+++ b/contracts/identity/ERC735.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.18;
+pragma solidity ^0.4.17;
contract ERC735 {
From 3a0889237c704a8c42bd3317621e96ce1215f78d Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Tue, 13 Mar 2018 10:13:31 -0400
Subject: [PATCH 050/102] Commenting tests while they're updated for Embark and
Web3 1.0.0
---
test/factory.js | 8 +-
test/identity.js | 216 +++++++++++++++++++--------------------
test/identityExtended.js | 4 +
3 files changed, 118 insertions(+), 110 deletions(-)
diff --git a/test/factory.js b/test/factory.js
index 32d9b35..707e0eb 100644
--- a/test/factory.js
+++ b/test/factory.js
@@ -1,3 +1,7 @@
+/*
+COMMENTED TEMPORARLY WHILE PROJECT IS MIGRATED TO EMBARK - @rramos
+
+
const TestUtils = require("../utils/testUtils.js")
const idUtils = require("../utils/identityUtils.js")
const web3EthAbi = require("web3-eth-abi");
@@ -82,4 +86,6 @@ contract('IdentityFactory', function(accounts) {
})
});
-});
\ No newline at end of file
+});
+
+*/
\ No newline at end of file
diff --git a/test/identity.js b/test/identity.js
index f240209..293ad15 100644
--- a/test/identity.js
+++ b/test/identity.js
@@ -1,97 +1,102 @@
+
+const assert = require('assert');
+const Embark = require('embark');
+const EmbarkSpec = Embark.initTests();
+const web3 = EmbarkSpec.web3;
const TestUtils = require("../utils/testUtils.js");
const web3EthAbi = require("web3-eth-abi");
const idUtils = require('../utils/identityUtils.js');
-const Identity = artifacts.require("./identity/Identity.sol");
-const TestContract = artifacts.require("./test/TestContract.sol");
+describe("Identity", function() {
+ this.timeout(0);
-contract('Identity', function(accounts) {
+ let accounts;
- let identity;
-
- beforeEach(async () => {
- identity = await Identity.new({from: accounts[0]})
- })
+ beforeEach( function(done) {
+ this.timeout(0);
+
+ EmbarkSpec.deployAll({ "Identity": {}}, (_accounts) => {
+ accounts = _accounts;
+ done();
+ });
+ });
describe("Identity()", () => {
it("initialize with msg.sender as management key", async () => {
assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
+ await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])).call(),
idUtils.purposes.MANAGEMENT,
- identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY")
+ Identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY");
+
});
});
describe("addKey(address _key, uint256 _type)", () => {
it("MANAGEMENT_KEY add a new address as ACTION_KEY", async () => {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
- {from: accounts[0]}
- );
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS)
+ ).send({from: accounts[0]});
assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
+ await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.ACTION,
- identity.address+".getKeyPurpose("+accounts[1]+") is not ACTION_KEY")
+ Identity.address+".getKeyPurpose("+accounts[1]+") is not ACTION_KEY");
});
it("should not add key by non manager", async () => {
try {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
- {from: accounts[2]}
- );
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS))
+ .send({from: accounts[2]});
assert.fail('should have reverted before');
} catch(error) {
TestUtils.assertJump(error);
}
assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
+ await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.NONE,
- identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
+ Identity.address+".getKeyPurpose("+accounts[1]+") is not correct");
+
});
it("should not add key type 1 by actor", async () => {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.addKey(accounts[2], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
- {from: accounts[0]}
- );
+ idUtils.encode.addKey(accounts[2], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
+ .send({from: accounts[0]});
try {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
- {from: accounts[2]}
- );
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS))
+ .send({from: accounts[2]});
assert.fail('should have reverted before');
} catch(error) {
TestUtils.assertJump(error);
}
assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
+ await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.NONE,
- identity.address+".getKeyType("+accounts[1]+") is not correct")
+ Identity.address+".getKeyType("+accounts[1]+") is not correct");
});
it("fire KeyAdded(address indexed key, uint256 indexed type)", async () => {
- await identity.execute(
- identity.address,
+ let receipt = await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
- {from: accounts[0]}
- );
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS))
+ .send({from: accounts[0]});
- const keyAdded = await TestUtils.listenForEvent(identity.KeyAdded())
+ const keyAdded = TestUtils.eventValues(receipt, "KeyAdded");
assert(keyAdded.key, TestUtils.addressToBytes32(accounts[1]), "Key is not correct")
assert(keyAdded.keyType, idUtils.types.ADDRESS, "Type is not correct")
});
@@ -100,121 +105,112 @@ contract('Identity', function(accounts) {
describe("removeKey(address _key, uint256 _type)", () => {
it("MANAGEMENT_KEY should remove a key", async () => {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
- {from: accounts[0]}
- );
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS))
+ .send({from: accounts[0]});
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.removeKey(accounts[1], idUtils.purposes.MANAGEMENT),
- {from: accounts[0]}
- );
+ idUtils.encode.removeKey(accounts[1], idUtils.purposes.MANAGEMENT))
+ .send({from: accounts[0]});
assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
+ await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.NONE,
- identity.address+".getKeyPurpose("+accounts[1]+") is not 0")
+ Identity.address+".getKeyPurpose("+accounts[1]+") is not 0")
});
it("other key should not remove a key", async () => {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS),
- {from: accounts[0]}
- );
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.MANAGEMENT, idUtils.types.ADDRESS))
+ .send({from: accounts[0]});
try {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.removeKey(accounts[1], idUtils.purposes.MANAGEMENT),
- {from: accounts[2]}
- );
+ idUtils.encode.removeKey(accounts[1], idUtils.purposes.MANAGEMENT))
+ .send({from: accounts[2]});
assert.fail('should have reverted before');
} catch(error) {
TestUtils.assertJump(error);
}
assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
+ await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.MANAGEMENT,
- identity.address+".getKeyPurpose("+accounts[1]+") is not 0")
+ Identity.address+".getKeyPurpose("+accounts[1]+") is not 0")
});
it("actor key should not remove key", async () => {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
- {from: accounts[0]}
- );
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
+ .send({from: accounts[0]});
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.addKey(accounts[2], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
- {from: accounts[0]}
- );
+ idUtils.encode.addKey(accounts[2], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
+ .send({from: accounts[0]});
try {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.removeKey(accounts[1], idUtils.purposes.ACTION),
- {from: accounts[2]}
- );
+ idUtils.encode.removeKey(accounts[1], idUtils.purposes.ACTION))
+ .send({from: accounts[2]});
+
assert.fail('should have reverted before');
} catch(error) {
TestUtils.assertJump(error);
}
assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
+ await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.ACTION,
- identity.address+".getKeyType("+accounts[1]+") is not 0")
+ Identity.address+".getKeyType("+accounts[1]+") is not 0")
});
it("MANAGEMENT_KEY should not remove itself MANAGEMENT_KEY when there is no other MANAGEMENT_KEY", async () => {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.removeKey(accounts[0], idUtils.purposes.MANAGEMENT),
- {from: accounts[0]}
- );
+ idUtils.encode.removeKey(accounts[0], idUtils.purposes.MANAGEMENT))
+ .send({from: accounts[0]});
assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
+ await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])).call(),
idUtils.purposes.MANAGEMENT,
- identity.address+".getKeyType("+accounts[0]+") is not 1")
+ Identity.address+".getKeyType("+accounts[0]+") is not 1")
});
it("fire KeyRemoved(address indexed key, uint256 indexed type)", async () => {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
- {from: accounts[0]}
- );
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
+ .send({from: accounts[0]});
- await identity.execute(
- identity.address,
+ let receipt = await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.removeKey(accounts[1], idUtils.purposes.ACTION),
- {from: accounts[0]}
- );
+ idUtils.encode.removeKey(accounts[1], idUtils.purposes.ACTION))
+ .send({from: accounts[0]});
- const keyRemoved = await TestUtils.listenForEvent(identity.KeyRemoved());
+ const keyRemoved = await TestUtils.eventValues(receipt, "KeyRemoved");
assert(keyRemoved.key, TestUtils.addressToBytes32(accounts[1]), "Key is not correct");
assert(keyRemoved.keyType, idUtils.types.ADDRESS, "Type is not correct");
});
});
-
+/*
describe("getKeyPurpose(address _key)", () => {
it("should start only with initializer as only key", async () => {
@@ -274,9 +270,8 @@ contract('Identity', function(accounts) {
});
});
-*/
-
-
+ */
+/*
describe("execute(address _to, uint256 _value, bytes _data)", () => {
let testContractInstance;
let functionPayload;
@@ -422,12 +417,10 @@ contract('Identity', function(accounts) {
assert(executed.value, 0, "Value is not correct");
assert(executed.data, functionPayload, "Data is not correct");
});
-
});
-/*
-
+ /*
describe("setMinimumApprovalsByKeyPurpose(uint256 _type, uint8 _minimumApprovals)", () => {
it("MANAGEMENT_KEY should set minimum approvals for MANAGEMENT_KEYs", async () => {
@@ -501,5 +494,10 @@ contract('Identity', function(accounts) {
});
});
- */
+ */
+
+
});
+
+
+
diff --git a/test/identityExtended.js b/test/identityExtended.js
index 0e2db9a..dac16f2 100644
--- a/test/identityExtended.js
+++ b/test/identityExtended.js
@@ -1,3 +1,6 @@
+/*
+COMMENTED TEMPORARLY WHILE PROJECT IS MIGRATED TO EMBARK - @rramos
+
const TestUtils = require("../utils/testUtils.js")
var ethUtils = require('ethereumjs-util')
@@ -62,3 +65,4 @@ contract('Identity - Extended Functionality', function(accounts) {
});
+*/
\ No newline at end of file
From f6ec0000915be51b46005192d13416c5c941f30c Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Tue, 13 Mar 2018 13:52:44 -0400
Subject: [PATCH 051/102] Changed name to function, and added view to Factory
---
contracts/identity/Identity.sol | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 745d0b7..7991b25 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -29,7 +29,7 @@ contract Identity is ERC725, ERC735 {
modifier managerOnly {
require(
- isKeyType(bytes32(msg.sender), MANAGEMENT_KEY)
+ isKeyPurpose(bytes32(msg.sender), MANAGEMENT_KEY)
);
_;
}
@@ -49,14 +49,14 @@ contract Identity is ERC725, ERC735 {
}
modifier actorOnly(bytes32 _key) {
- require(isKeyType(_key, ACTION_KEY));
+ require(isKeyPurpose(_key, ACTION_KEY));
_;
}
modifier managerOrActor(bytes32 _key) {
require(
- isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) ||
- isKeyType(bytes32(msg.sender), ACTION_KEY)
+ isKeyPurpose(bytes32(msg.sender), MANAGEMENT_KEY) ||
+ isKeyPurpose(bytes32(msg.sender), ACTION_KEY)
);
_;
}
@@ -219,7 +219,7 @@ contract Identity is ERC725, ERC735 {
}
} else {
require(_issuer == msg.sender);
- require(isKeyType(bytes32(msg.sender), CLAIM_SIGNER_KEY));
+ require(isKeyPurpose(bytes32(msg.sender), CLAIM_SIGNER_KEY));
_execute(address(this), 0, msg.data);
ClaimRequested(
claimHash,
@@ -270,12 +270,12 @@ contract Identity is ERC725, ERC735 {
return (myKey.purpose, myKey.keyType, myKey.key);
}
- function isKeyType(bytes32 _key, uint256 _type)
+ function isKeyPurpose(bytes32 _key, uint256 _purpose)
public
constant
returns (bool)
{
- return keys[keccak256(_key, _type)].purpose == _type;
+ return keys[keccak256(_key, _purpose)].purpose == _purpose;
}
function getKeyPurpose(bytes32 _key)
@@ -287,22 +287,22 @@ contract Identity is ERC725, ERC735 {
uint256[] memory purposeHolder = new uint256[](4);
uint8 counter = 0;
- if (isKeyType(_key, MANAGEMENT_KEY)) {
+ if (isKeyPurpose(_key, MANAGEMENT_KEY)) {
purposeHolder[counter] = MANAGEMENT_KEY;
counter++;
}
- if (isKeyType(_key, ACTION_KEY)) {
+ if (isKeyPurpose(_key, ACTION_KEY)) {
purposeHolder[counter] = ACTION_KEY;
counter++;
}
- if (isKeyType(_key, CLAIM_SIGNER_KEY)) {
+ if (isKeyPurpose(_key, CLAIM_SIGNER_KEY)) {
purposeHolder[counter] = CLAIM_SIGNER_KEY;
counter++;
}
- if (isKeyType(_key, ENCRYPTION_KEY)) {
+ if (isKeyPurpose(_key, ENCRYPTION_KEY)) {
purposeHolder[counter] = ENCRYPTION_KEY;
counter++;
}
@@ -463,12 +463,12 @@ contract Identity is ERC725, ERC735 {
Approved(_id, _approval);
if (trx.to == address(this)) {
- require(isKeyType(_key, MANAGEMENT_KEY));
+ require(isKeyPurpose(_key, MANAGEMENT_KEY));
bytes32 managerKeyHash = keccak256(_key, MANAGEMENT_KEY);
requiredKeyPurpose = MANAGEMENT_KEY;
approvalCount = _calculateApprovals(managerKeyHash, _approval, trx);
} else {
- require(isKeyType(_key, ACTION_KEY));
+ require(isKeyPurpose(_key, ACTION_KEY));
bytes32 actorKeyHash = keccak256(_key, ACTION_KEY);
requiredKeyPurpose = ACTION_KEY;
approvalCount = _calculateApprovals(actorKeyHash, _approval, trx);
From 5466aeed49f5b618e7fe7b2e6b72018a407bcbad Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Tue, 13 Mar 2018 13:52:44 -0400
Subject: [PATCH 052/102] Changed name to function, and added view to Factory
---
contracts/deploy/Factory.sol | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/contracts/deploy/Factory.sol b/contracts/deploy/Factory.sol
index 19975d5..f63bf71 100644
--- a/contracts/deploy/Factory.sol
+++ b/contracts/deploy/Factory.sol
@@ -31,6 +31,18 @@ contract Factory is Controlled {
_setKernel(_kernel, _infohash);
}
+ function getVersion(uint256 index) public view
+ returns(uint256 blockNumber,
+ uint256 timestamp,
+ address kernel,
+ bytes infohash)
+ {
+ return (versionLog[index].blockNumber,
+ versionLog[index].timestamp,
+ versionLog[index].kernel,
+ versionLog[index].infohash);
+ }
+
function _setKernel(address _kernel, bytes _infohash)
internal
{
From 7695b461fb68572afff7d1f8fa637bf5985d6fcd Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Tue, 13 Mar 2018 14:37:15 -0400
Subject: [PATCH 053/102] Updated factory unit test and docs to include delay
functionality
---
test/factory.js | 25 ++++++++++++++++++++----
utils/identityUtils.js | 44 +++++++++++++++++++++++++++++++++++++++++-
2 files changed, 64 insertions(+), 5 deletions(-)
diff --git a/test/factory.js b/test/factory.js
index 32d9b35..84375c1 100644
--- a/test/factory.js
+++ b/test/factory.js
@@ -65,18 +65,35 @@ contract('IdentityFactory', function(accounts) {
let tx1 = await identity.execute(
identity.address,
0,
- idUtils.encode.updateUpdatableInstance(updatedIdentityKernel.address),
+ idUtils.encode.updateRequestUpdatableInstance(updatedIdentityKernel.address),
{from: accounts[0]}
);
+
assert.strictEqual(tx1.logs[tx1.logs.length - 1].event, "Executed");
+ // Updating EVM timestamp to test delay
+ const plus31days = 60 * 60 * 24 * 31;
+
+ web3.currentProvider.send({jsonrpc: "2.0", method: "evm_increaseTime", params: [plus31days], id: 0});
+ web3.currentProvider.send({jsonrpc: "2.0", method: "evm_mine", params: [], id: 0})
+
+ // Confirm update
+ let tx2 = await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.updateConfirmUpdatableInstance(updatedIdentityKernel.address),
+ {from: accounts[0]}
+ );
+
+ assert.strictEqual(tx2.logs[tx2.logs.length - 1].event, "Executed");
+
// Calling function available in updated identity kernel
let updatedIdentity1 = await UpdatedIdentityKernel.at(identity.address, {from: accounts[0]})
- let tx2 = await updatedIdentity1.test({from: accounts[0]});
+ let tx3 = await updatedIdentity1.test({from: accounts[0]});
- assert.strictEqual(tx2.logs[tx2.logs.length - 1].event, "TestFunctionExecuted");
+ assert.strictEqual(tx3.logs[tx3.logs.length - 1].event, "TestFunctionExecuted");
assert.equal(
- tx2.logs[tx2.logs.length - 1].args.minApprovalsByManagementKeys.toString(10),
+ tx3.logs[tx3.logs.length - 1].args.minApprovalsByManagementKeys.toString(10),
1,
identity.address + " wasn't updated to last version");
})
diff --git a/utils/identityUtils.js b/utils/identityUtils.js
index 91613a4..5998092 100644
--- a/utils/identityUtils.js
+++ b/utils/identityUtils.js
@@ -117,6 +117,45 @@ const _updateUpdatableInstance = function(address){
}, [address]);
}
+const _updateRequestUpdatableInstance = function(address){
+ if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
+ throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
+
+ return web3EthAbi.encodeFunctionCall({
+ name: 'updateRequestUpdatableInstance',
+ type: 'function',
+ inputs: [{
+ type: 'address',
+ name: '_kernel'
+ }]
+ }, [address]);
+}
+
+const _updateConfirmUpdatableInstance = function(address){
+ if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
+ throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
+
+ return web3EthAbi.encodeFunctionCall({
+ name: 'updateConfirmUpdatableInstance',
+ type: 'function',
+ inputs: [{
+ type: 'address',
+ name: '_kernel'
+ }]
+ }, [address]);
+}
+
+const _updateCancelUpdatableInstance = function(address){
+ if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
+ throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
+
+ return web3EthAbi.encodeFunctionCall({
+ name: 'updateCancelUpdatableInstance',
+ type: 'function',
+ inputs: []
+ }, []);
+}
+
@@ -128,6 +167,9 @@ module.exports = {
removeKey: _removeKey,
setMinimumApprovalsByKeyType: _setMinimumApprovalsByKeyType,
setupRecovery: _setupRecovery,
- updateUpdatableInstance: _updateUpdatableInstance
+ updateUpdatableInstance: _updateUpdatableInstance,
+ updateRequestUpdatableInstance: _updateRequestUpdatableInstance,
+ updateConfirmUpdatableInstance: _updateConfirmUpdatableInstance,
+ updateCancelUpdatableInstance: _updateCancelUpdatableInstance
}
}
\ No newline at end of file
From c9711c22504648f2f1a05a3476a8b6213e37b231 Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Tue, 13 Mar 2018 19:30:12 -0400
Subject: [PATCH 054/102] Updated test unit to reflect changes in recovery
contract
---
contracts/tests/TestContract.sol | 23 +++++++++++++++++++++++
utils/identityUtils.js | 16 ++++++++++++++++
2 files changed, 39 insertions(+)
diff --git a/contracts/tests/TestContract.sol b/contracts/tests/TestContract.sol
index 0ed094e..3074bda 100644
--- a/contracts/tests/TestContract.sol
+++ b/contracts/tests/TestContract.sol
@@ -8,4 +8,27 @@ contract TestContract {
function test() public {
TestFunctionExecuted();
}
+
+
+ /*
+ Helper function to be used in unit testing due to error in web3
+ web3.utils.soliditySha3([1, 2, 3])
+ Error: Autodetection of array types is not supported.
+ at _processSoliditySha3Args (node_modules/web3-utils/src/soliditySha3.js:176:15)
+ */
+ function hash(
+ address identity,
+ bytes32 _revealedSecret,
+ address _dest,
+ bytes _data,
+ bytes32 _newSecret,
+ bytes32[] _newFriendsHashes)
+ external
+ pure
+ returns(bytes32)
+ {
+ return keccak256(identity, _revealedSecret, _dest, _data, _newSecret, _newFriendsHashes);
+
+ }
+
}
\ No newline at end of file
diff --git a/utils/identityUtils.js b/utils/identityUtils.js
index 5998092..7b52d62 100644
--- a/utils/identityUtils.js
+++ b/utils/identityUtils.js
@@ -103,6 +103,21 @@ const _setupRecovery = function(address){
}, [address]);
}
+const _managerReset = function(address){
+ if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
+ throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
+
+ return web3EthAbi.encodeFunctionCall({
+ name: 'managerReset',
+ type: 'function',
+ inputs: [{
+ type: 'address',
+ name: '_newKey'
+ }]
+ }, [address]);
+}
+
+
const _updateUpdatableInstance = function(address){
if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
@@ -167,6 +182,7 @@ module.exports = {
removeKey: _removeKey,
setMinimumApprovalsByKeyType: _setMinimumApprovalsByKeyType,
setupRecovery: _setupRecovery,
+ managerReset: _managerReset,
updateUpdatableInstance: _updateUpdatableInstance,
updateRequestUpdatableInstance: _updateRequestUpdatableInstance,
updateConfirmUpdatableInstance: _updateConfirmUpdatableInstance,
From 6f83c96ab7809f776825401b9a8af33de2ee1fd0 Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Tue, 13 Mar 2018 19:35:30 -0400
Subject: [PATCH 055/102] Added .embark to .gitignore file and updated
identity.js test unit.
---
test/identity.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/test/identity.js b/test/identity.js
index 293ad15..0fbbefc 100644
--- a/test/identity.js
+++ b/test/identity.js
@@ -1,7 +1,7 @@
const assert = require('assert');
const Embark = require('embark');
-const EmbarkSpec = Embark.initTests();
+let EmbarkSpec = Embark.initTests();
const web3 = EmbarkSpec.web3;
const TestUtils = require("../utils/testUtils.js");
const web3EthAbi = require("web3-eth-abi");
@@ -15,6 +15,8 @@ describe("Identity", function() {
beforeEach( function(done) {
this.timeout(0);
+ EmbarkSpec = Embark.initTests();
+
EmbarkSpec.deployAll({ "Identity": {}}, (_accounts) => {
accounts = _accounts;
done();
From 5a1f8c93a95c7a43e305b0bfd5f99177006e263c Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Wed, 14 Mar 2018 10:31:12 -0400
Subject: [PATCH 056/102] Migrating additional test cases
---
test/identity.js | 183 ++++++++++++++++++++++-------------------------
1 file changed, 86 insertions(+), 97 deletions(-)
diff --git a/test/identity.js b/test/identity.js
index 0fbbefc..31b8292 100644
--- a/test/identity.js
+++ b/test/identity.js
@@ -2,9 +2,8 @@
const assert = require('assert');
const Embark = require('embark');
let EmbarkSpec = Embark.initTests();
-const web3 = EmbarkSpec.web3;
+// const web3 = EmbarkSpec.web3;
const TestUtils = require("../utils/testUtils.js");
-const web3EthAbi = require("web3-eth-abi");
const idUtils = require('../utils/identityUtils.js');
describe("Identity", function() {
@@ -17,7 +16,10 @@ describe("Identity", function() {
EmbarkSpec = Embark.initTests();
- EmbarkSpec.deployAll({ "Identity": {}}, (_accounts) => {
+ EmbarkSpec.deployAll({
+ "Identity": {},
+ "TestContract": {}
+ }, (_accounts) => {
accounts = _accounts;
done();
});
@@ -206,53 +208,51 @@ describe("Identity", function() {
idUtils.encode.removeKey(accounts[1], idUtils.purposes.ACTION))
.send({from: accounts[0]});
- const keyRemoved = await TestUtils.eventValues(receipt, "KeyRemoved");
+ const keyRemoved = TestUtils.eventValues(receipt, "KeyRemoved");
assert(keyRemoved.key, TestUtils.addressToBytes32(accounts[1]), "Key is not correct");
assert(keyRemoved.keyType, idUtils.types.ADDRESS, "Type is not correct");
});
});
-/*
+
describe("getKeyPurpose(address _key)", () => {
it("should start only with initializer as only key", async () => {
assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
+ await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])).call(),
idUtils.purposes.MANAGEMENT,
- identity.address+".getKeyPurpose("+accounts[0]+") is not correct")
+ Identity.address+".getKeyPurpose("+accounts[0]+") is not correct")
assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
+ await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.NONE,
- identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
+ Identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
it("should get type 2 after addKey type 2", async () => {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
- {from: accounts[0]}
- );
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
+ .send({from: accounts[0]});
assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
+ await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.ACTION,
- identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
+ Identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
it("should get type 3 after addKey type 3", async () => {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.addKey(accounts[1], idUtils.purposes.CLAIM_SIGNER, idUtils.types.ADDRESS),
- {from: accounts[0]}
- );
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.CLAIM_SIGNER, idUtils.types.ADDRESS))
+ .send({from: accounts[0]});
assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])),
+ await Identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[1])).call(),
idUtils.purposes.CLAIM_SIGNER,
- identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
+ Identity.address+".getKeyPurpose("+accounts[1]+") is not correct")
});
});
@@ -273,61 +273,57 @@ describe("Identity", function() {
});
});
*/
-/*
+
describe("execute(address _to, uint256 _value, bytes _data)", () => {
- let testContractInstance;
let functionPayload;
it("Identity should receive ether", async() => {
- const amountToSend = web3.toWei(0.05, "ether");
+ const amountToSend = web3.utils.toWei('0.05', "ether");
- let idBalance0 = web3.eth.getBalance(identity.address);
+ let idBalance0 = web3.eth.getBalance(Identity.address);
- await web3.eth.sendTransaction({from:accounts[0], to:identity.address, value: amountToSend})
+ await web3.eth.sendTransaction({from:accounts[0], to:Identity.address, value: amountToSend})
- let idBalance1 = web3.eth.getBalance(identity.address);
+ let idBalance1 = web3.eth.getBalance(Identity.address);
- assert.equal(idBalance0.toNumber() + amountToSend, idBalance1.toNumber(), identity.address + " did not receive ether");
+ assert.equal(idBalance0.toNumber() + amountToSend, idBalance1.toNumber(), Identity.address + " did not receive ether");
});
-
+/*
it("ACTOR_KEY execute arbitrary transaction", async () => {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
- {from: accounts[0]}
- );
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
+ .send({from: accounts[0]});
- testContractInstance = await TestContract.new({from: accounts[0]});
-
- functionPayload = web3EthAbi.encodeFunctionCall({
+
+ functionPayload = web3.eth.abi.encodeFunctionCall({
name: 'test',
type: 'function',
inputs: []
}, []);
- await identity.execute(
- testContractInstance.address,
+ let receipt = await Identity.methods.execute(
+ TestContract.address,
0,
- functionPayload,
- {from: accounts[1]}
- );
+ functionPayload)
+ .send({from: accounts[1]});
- assert.notEqual(
- await TestUtils.listenForEvent(testContractInstance.TestFunctionExecuted()),
- undefined,
- "Test function was not executed");
+ assert.notEqual(
+ await TestUtils.listenForEvent(TestContract.event.TestFunctionExecuted),
+ undefined,
+ "Test function was not executed");
+
});
-
+ */
it("MANAGEMENT_KEY cannot execute arbitrary transaction", async () => {
try {
- await identity.execute(
- testContractInstance.address,
+ await Identity.methods.execute(
+ TestContract.address,
0,
- functionPayload,
- {from: accounts[0]}
- );
+ functionPayload)
+ .send({from: accounts[0]});
} catch(error) {
TestUtils.assertJump(error);
}
@@ -335,12 +331,11 @@ describe("Identity", function() {
it("Other keys NOT execute arbitrary transaction", async () => {
try {
- await identity.execute(
- testContractInstance.address,
+ await Identity.methods.execute(
+ TestContract.address,
0,
- functionPayload,
- {from: accounts[3]}
- );
+ functionPayload)
+ .send({from: accounts[3]});
assert.fail('should have reverted before');
} catch(error) {
TestUtils.assertJump(error);
@@ -349,29 +344,27 @@ describe("Identity", function() {
it("ACTION_KEY should send ether from contract", async () => {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
- {from: accounts[0]}
- );
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
+ .send({from: accounts[0]});
// Adding funds to contract
- await web3.eth.sendTransaction({from:accounts[0], to:identity.address, value: web3.toWei(0.05, "ether")})
+ await web3.eth.sendTransaction({from:accounts[0], to:Identity.address, value: web3.utils.toWei('0.05', "ether")})
- const amountToSend = web3.toWei(0.01, "ether");
+ const amountToSend = web3.utils.toWei('0.01', "ether");
- let idBalance0 = web3.eth.getBalance(identity.address);
+ let idBalance0 = web3.eth.getBalance(Identity.address);
let a2Balance0 = web3.eth.getBalance(accounts[2]);
- await identity.execute(
+ await Identity.methods.execute(
accounts[2],
amountToSend,
- '',
- {from: accounts[1]}
- );
+ '')
+ .send({from: accounts[1]});
- let idBalance1 = web3.eth.getBalance(identity.address);
+ let idBalance1 = web3.eth.getBalance(Identity.address);
let a2Balance1 = web3.eth.getBalance(accounts[2]);
assert(idBalance1.toNumber, idBalance0.toNumber - amountToSend, "Contract did not send ether");
@@ -379,43 +372,39 @@ describe("Identity", function() {
});
it("fire ExecutionRequested(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data)", async () => {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
- {from: accounts[0]}
- );
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
+ .send({from: accounts[0]});
- await identity.execute(
- testContractInstance.address,
+ let receipt = await Identity.methods.execute(
+ TestContract.address,
0,
- functionPayload,
- {from: accounts[1]}
- );
-
- const executionRequested = await TestUtils.listenForEvent(identity.ExecutionRequested());
- assert(executionRequested.to, testContractInstance.address, "To is not correct");
+ functionPayload)
+ .send({from: accounts[1]});
+
+ const executionRequested = TestUtils.eventValues(receipt, "ExecutionRequested");
+ assert(executionRequested.to, TestContract.address, "To is not correct");
assert(executionRequested.value, 0, "Value is not correct");
assert(executionRequested.data, functionPayload, "Data is not correct");
});
it("fire Executed(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data)", async () => {
- await identity.execute(
- identity.address,
+ await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS),
- {from: accounts[0]}
- );
+ idUtils.encode.addKey(accounts[1], idUtils.purposes.ACTION, idUtils.types.ADDRESS))
+ .send({from: accounts[0]});
- await identity.execute(
- testContractInstance.address,
+ let receipt = await Identity.methods.execute(
+ TestContract.address,
0,
- functionPayload,
- {from: accounts[1]}
- );
+ functionPayload)
+ .send({from: accounts[1]});
- const executed = await TestUtils.listenForEvent(identity.Executed());
- assert(executed.to, testContractInstance.address, "To is not correct");
+ const executed = TestUtils.eventValues(receipt, "Executed")
+ assert(executed.to, TestContract.address, "To is not correct");
assert(executed.value, 0, "Value is not correct");
assert(executed.data, functionPayload, "Data is not correct");
});
From 6426c802982d2cb3ca22f0f40a40a49d6a41a66d Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Wed, 14 Mar 2018 15:42:04 -0400
Subject: [PATCH 057/102] Updating tests
---
test/identity.js | 39 +++++++++++++++++++++------------------
1 file changed, 21 insertions(+), 18 deletions(-)
diff --git a/test/identity.js b/test/identity.js
index 31b8292..39f5422 100644
--- a/test/identity.js
+++ b/test/identity.js
@@ -2,7 +2,7 @@
const assert = require('assert');
const Embark = require('embark');
let EmbarkSpec = Embark.initTests();
-// const web3 = EmbarkSpec.web3;
+let web3 = EmbarkSpec.web3;
const TestUtils = require("../utils/testUtils.js");
const idUtils = require('../utils/identityUtils.js');
@@ -15,6 +15,7 @@ describe("Identity", function() {
this.timeout(0);
EmbarkSpec = Embark.initTests();
+ web3 = EmbarkSpec.web3;
EmbarkSpec.deployAll({
"Identity": {},
@@ -281,15 +282,15 @@ describe("Identity", function() {
const amountToSend = web3.utils.toWei('0.05', "ether");
- let idBalance0 = web3.eth.getBalance(Identity.address);
+ let idBalance0 = await web3.eth.getBalance(Identity.address);
await web3.eth.sendTransaction({from:accounts[0], to:Identity.address, value: amountToSend})
- let idBalance1 = web3.eth.getBalance(Identity.address);
+ let idBalance1 = await web3.eth.getBalance(Identity.address);
- assert.equal(idBalance0.toNumber() + amountToSend, idBalance1.toNumber(), Identity.address + " did not receive ether");
+ assert.equal(web3.utils.toBN(idBalance0).add(web3.utils.toBN(amountToSend)).toString(), web3.utils.toBN(idBalance1).toString(), Identity.address + " did not receive ether");
});
-/*
+
it("ACTOR_KEY execute arbitrary transaction", async () => {
await Identity.methods.execute(
Identity.address,
@@ -309,14 +310,16 @@ describe("Identity", function() {
0,
functionPayload)
.send({from: accounts[1]});
-
- assert.notEqual(
- await TestUtils.listenForEvent(TestContract.event.TestFunctionExecuted),
- undefined,
- "Test function was not executed");
+
+ // @rramos - Commented because of error:
+ // The current provider doesn't support subscriptions: Provider
+ /*assert.notEqual(
+ await TestUtils.listenForEvent(TestContract.events.TestFunctionExecuted),
+ undefined,
+ "Test function was not executed"); */
});
- */
+
it("MANAGEMENT_KEY cannot execute arbitrary transaction", async () => {
try {
await Identity.methods.execute(
@@ -355,20 +358,20 @@ describe("Identity", function() {
const amountToSend = web3.utils.toWei('0.01', "ether");
- let idBalance0 = web3.eth.getBalance(Identity.address);
- let a2Balance0 = web3.eth.getBalance(accounts[2]);
+ let idBalance0 = await web3.eth.getBalance(Identity.address);
+ let a2Balance0 = await web3.eth.getBalance(accounts[2]);
await Identity.methods.execute(
accounts[2],
amountToSend,
- '')
+ '0x')
.send({from: accounts[1]});
- let idBalance1 = web3.eth.getBalance(Identity.address);
- let a2Balance1 = web3.eth.getBalance(accounts[2]);
+ let idBalance1 = await web3.eth.getBalance(Identity.address);
+ let a2Balance1 = await web3.eth.getBalance(accounts[2]);
- assert(idBalance1.toNumber, idBalance0.toNumber - amountToSend, "Contract did not send ether");
- assert(a2Balance1.toNumber, a2Balance0.toNumber + amountToSend, accounts[2] + " did not receive ether");
+ assert(web3.utils.toBN(idBalance1).toString(), web3.utils.toBN(idBalance0).sub(web3.utils.toBN(amountToSend)).toString(), "Contract did not send ether");
+ assert(web3.utils.toBN(a2Balance1).toString(), web3.utils.toBN(a2Balance0).add(web3.utils.toBN(amountToSend)).toString(), accounts[2] + " did not receive ether");
});
it("fire ExecutionRequested(uint256 indexed executionId, address indexed to, uint256 indexed value, bytes data)", async () => {
From 93fd39b34ce3e9115fa2edbe39ef92d6de2cd32f Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Wed, 14 Mar 2018 16:02:44 -0400
Subject: [PATCH 058/102] Updating test units
---
test/factory.js | 154 +++++++++++++++++++++++++-----------------------
1 file changed, 81 insertions(+), 73 deletions(-)
diff --git a/test/factory.js b/test/factory.js
index 707e0eb..477dd70 100644
--- a/test/factory.js
+++ b/test/factory.js
@@ -1,91 +1,99 @@
-/*
-COMMENTED TEMPORARLY WHILE PROJECT IS MIGRATED TO EMBARK - @rramos
-
+const assert = require('assert');
+const Embark = require('embark');
+let EmbarkSpec = Embark.initTests();
+let web3 = EmbarkSpec.web3;
const TestUtils = require("../utils/testUtils.js")
const idUtils = require("../utils/identityUtils.js")
const web3EthAbi = require("web3-eth-abi");
-const Identity = artifacts.require("./identity/Identity.sol");
-const IdentityFactory = artifacts.require("./identity/IdentityFactory.sol");
-const UpdatableInstance = artifacts.require('./deploy/UpdatableInstance.sol');
-const UpdatedIdentityKernel = artifacts.require("./tests/UpdatedIdentityKernel.sol");
-
-contract('IdentityFactory', function(accounts) {
+describe('IdentityFactory', function(accounts) {
let identityFactory;
let identity;
let updatedIdentity;
let updatedIdentityKernel;
- before(async () => {
- identityFactory = await IdentityFactory.new("0xaaa", {from: accounts[0]});
- })
-
- describe("IdentityFactory()", () => {
- it("Creates a new identity", async () => {
- let tx = await identityFactory.createIdentity({from: accounts[0]});
- const logEntry = tx.logs[0];
- assert.strictEqual(logEntry.event, "IdentityCreated");
-
- identity = await Identity.at(logEntry.args.instance, {from: accounts[0]})
-
- assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
- idUtils.purposes.MANAGEMENT,
- identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY")
- });
-
-
- it("Registers a updated identity contract", async() => {
- const infoHash = "0xbbb";
- updatedIdentityKernel = await UpdatedIdentityKernel.new({from: accounts[0]});
- await identityFactory.setKernel(updatedIdentityKernel.address, infoHash);
-
- const newKernel = await TestUtils.listenForEvent(identityFactory.NewKernel());
- assert(newKernel.infohash, infoHash, "Infohash is not correct");
- });
-
+ before( function(done) {
+ this.timeout(0);
- it("Creates a new identity using latest version", async() => {
- let tx = await identityFactory.createIdentity({from: accounts[0]});
- const logEntry = tx.logs[0];
- assert.strictEqual(logEntry.event, "IdentityCreated");
+ EmbarkSpec = Embark.initTests();
+ web3 = EmbarkSpec.web3;
- updatedIdentity = await UpdatedIdentityKernel.at(logEntry.args.instance, {from: accounts[0]})
- tx = await updatedIdentity.test({from: accounts[0]});
- assert.strictEqual(tx.logs[0].event, "TestFunctionExecuted");
-
- // Test if it still executes identity functions as expected
- let baseIdentity = await Identity.at(updatedIdentity.address, {from: accounts[0]})
- assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
- 1,
- identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY")
+ // @rramos - this fails
+ EmbarkSpec.deployAll({
+ "IdentityFactory": {
+ args: ["0xaaa"]
+ }
+ }, (_accounts) => {
+ accounts = _accounts;
+
+ done();
});
-
-
- it("Updates an identity to the latest version", async() => {
- let tx1 = await identity.execute(
- identity.address,
- 0,
- idUtils.encode.updateUpdatableInstance(updatedIdentityKernel.address),
- {from: accounts[0]}
- );
- assert.strictEqual(tx1.logs[tx1.logs.length - 1].event, "Executed");
-
- // Calling function available in updated identity kernel
- let updatedIdentity1 = await UpdatedIdentityKernel.at(identity.address, {from: accounts[0]})
- let tx2 = await updatedIdentity1.test({from: accounts[0]});
-
- assert.strictEqual(tx2.logs[tx2.logs.length - 1].event, "TestFunctionExecuted");
- assert.equal(
- tx2.logs[tx2.logs.length - 1].args.minApprovalsByManagementKeys.toString(10),
- 1,
- identity.address + " wasn't updated to last version");
- })
});
+
+ it("Creates a new identity", async () => {
+ let tx = await IdentityFactory.methods.createIdentity({from: accounts[0]});
+ const logEntry = tx.logs[0];
+ assert.strictEqual(logEntry.event, "IdentityCreated");
+ identity = await Identity.at(logEntry.args.instance, {from: accounts[0]})
+
+ assert.equal(
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
+ idUtils.purposes.MANAGEMENT,
+ identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY")
+ });
+
+
+ it("Registers a updated identity contract", async() => {
+ const infoHash = "0xbbb";
+ updatedIdentityKernel = await UpdatedIdentityKernel.new({from: accounts[0]});
+ await identityFactory.setKernel(updatedIdentityKernel.address, infoHash);
+
+ const newKernel = await TestUtils.listenForEvent(identityFactory.NewKernel());
+ assert(newKernel.infohash, infoHash, "Infohash is not correct");
+ });
+
+
+ it("Creates a new identity using latest version", async() => {
+ let tx = await identityFactory.createIdentity({from: accounts[0]});
+ const logEntry = tx.logs[0];
+ assert.strictEqual(logEntry.event, "IdentityCreated");
+
+ updatedIdentity = await UpdatedIdentityKernel.at(logEntry.args.instance, {from: accounts[0]})
+ tx = await updatedIdentity.test({from: accounts[0]});
+ assert.strictEqual(tx.logs[0].event, "TestFunctionExecuted");
+
+ // Test if it still executes identity functions as expected
+ let baseIdentity = await Identity.at(updatedIdentity.address, {from: accounts[0]})
+ assert.equal(
+ await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
+ 1,
+ identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY")
+ });
+
+
+ it("Updates an identity to the latest version", async() => {
+ let tx1 = await identity.execute(
+ identity.address,
+ 0,
+ idUtils.encode.updateUpdatableInstance(updatedIdentityKernel.address),
+ {from: accounts[0]}
+ );
+ assert.strictEqual(tx1.logs[tx1.logs.length - 1].event, "Executed");
+
+ // Calling function available in updated identity kernel
+ let updatedIdentity1 = await UpdatedIdentityKernel.at(identity.address, {from: accounts[0]})
+ let tx2 = await updatedIdentity1.test({from: accounts[0]});
+
+ assert.strictEqual(tx2.logs[tx2.logs.length - 1].event, "TestFunctionExecuted");
+ assert.equal(
+ tx2.logs[tx2.logs.length - 1].args.minApprovalsByManagementKeys.toString(10),
+ 1,
+ identity.address + " wasn't updated to last version");
+ });
+
+
});
-*/
\ No newline at end of file
From 8c5b72490fe6062bb538de4b21ce639699c8defd Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Wed, 14 Mar 2018 22:06:38 -0400
Subject: [PATCH 059/102] Updating factory.js to web3 1.0
---
test/factory.js | 108 ++++++++++++++++++++++++++++-------------
utils/identityUtils.js | 59 +++++++++++++++++++++-
2 files changed, 131 insertions(+), 36 deletions(-)
diff --git a/test/factory.js b/test/factory.js
index 477dd70..4f73118 100644
--- a/test/factory.js
+++ b/test/factory.js
@@ -3,6 +3,9 @@ const Embark = require('embark');
let EmbarkSpec = Embark.initTests();
let web3 = EmbarkSpec.web3;
+const identityJson = require('../build/contracts/Identity.json');
+const updatedIdentityKernelJson = require('../build/contracts/UpdatedIdentityKernel.json');
+
const TestUtils = require("../utils/testUtils.js")
const idUtils = require("../utils/identityUtils.js")
const web3EthAbi = require("web3-eth-abi");
@@ -20,79 +23,114 @@ describe('IdentityFactory', function(accounts) {
EmbarkSpec = Embark.initTests();
web3 = EmbarkSpec.web3;
- // @rramos - this fails
EmbarkSpec.deployAll({
"IdentityFactory": {
- args: ["0xaaa"]
- }
+ args: ["0xaaaa"],
+ gas: 5000000
+ },
+ "Identity": {},
+ "UpdatedIdentityKernel": {}
}, (_accounts) => {
accounts = _accounts;
-
done();
});
});
it("Creates a new identity", async () => {
- let tx = await IdentityFactory.methods.createIdentity({from: accounts[0]});
- const logEntry = tx.logs[0];
- assert.strictEqual(logEntry.event, "IdentityCreated");
- identity = await Identity.at(logEntry.args.instance, {from: accounts[0]})
+ let tx = await IdentityFactory.methods.createIdentity().send({from: accounts[0]});
+
+ const logEntry = tx.events.IdentityCreated;
+
+ assert(logEntry !== undefined, "IdentityCreated was not triggered");
+
+ let identity = new web3.eth.Contract(identityJson.abi, logEntry.returnValues.instance, {from: accounts[0]});
assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
+ await identity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])).call(),
idUtils.purposes.MANAGEMENT,
identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY")
});
it("Registers a updated identity contract", async() => {
- const infoHash = "0xbbb";
- updatedIdentityKernel = await UpdatedIdentityKernel.new({from: accounts[0]});
- await identityFactory.setKernel(updatedIdentityKernel.address, infoHash);
+ const infoHash = "0xbbbb";
+ let receipt = await IdentityFactory.methods.setKernel(UpdatedIdentityKernel.address, infoHash).send({from: accounts[0]});
- const newKernel = await TestUtils.listenForEvent(identityFactory.NewKernel());
+ const newKernel = TestUtils.eventValues(receipt, "NewKernel");
assert(newKernel.infohash, infoHash, "Infohash is not correct");
});
it("Creates a new identity using latest version", async() => {
- let tx = await identityFactory.createIdentity({from: accounts[0]});
- const logEntry = tx.logs[0];
- assert.strictEqual(logEntry.event, "IdentityCreated");
+ let tx = await IdentityFactory.methods.createIdentity().send({from: accounts[0]});
- updatedIdentity = await UpdatedIdentityKernel.at(logEntry.args.instance, {from: accounts[0]})
- tx = await updatedIdentity.test({from: accounts[0]});
- assert.strictEqual(tx.logs[0].event, "TestFunctionExecuted");
+ assert.notEqual(tx.events.IdentityCreated, undefined, "IdentityCreated wasn't triggered");
+
+ const contractAddress = tx.events.IdentityCreated.returnValues.instance;
+
+
+ let updatedIdentity = new web3.eth.Contract(updatedIdentityKernelJson.abi, contractAddress, {from: accounts[0]});
+
+ tx = await updatedIdentity.methods.test().send({from: accounts[0]});
+ assert.notEqual(tx.events.TestFunctionExecuted, undefined, "TestFunctionExecuted wasn't triggered");
// Test if it still executes identity functions as expected
- let baseIdentity = await Identity.at(updatedIdentity.address, {from: accounts[0]})
+ let baseIdentity = new web3.eth.Contract(identityJson.abi, contractAddress, {from: accounts[0]});
+
assert.equal(
- await identity.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])),
+ await baseIdentity.methods.getKeyPurpose(TestUtils.addressToBytes32(accounts[0])).call(),
1,
- identity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY")
+ baseIdentity.address + ".getKeyPurpose("+accounts[0]+") is not MANAGEMENT_KEY")
});
it("Updates an identity to the latest version", async() => {
- let tx1 = await identity.execute(
- identity.address,
+ let tx1 = await Identity.methods.execute(
+ Identity.address,
0,
- idUtils.encode.updateUpdatableInstance(updatedIdentityKernel.address),
- {from: accounts[0]}
- );
- assert.strictEqual(tx1.logs[tx1.logs.length - 1].event, "Executed");
+ idUtils.encode.updateRequestUpdatableInstance(UpdatedIdentityKernel.address))
+ .send({from: accounts[0]});
- // Calling function available in updated identity kernel
- let updatedIdentity1 = await UpdatedIdentityKernel.at(identity.address, {from: accounts[0]})
- let tx2 = await updatedIdentity1.test({from: accounts[0]});
+ assert.notEqual(tx1.events.Executed, undefined, "Executed wasn't triggered");
- assert.strictEqual(tx2.logs[tx2.logs.length - 1].event, "TestFunctionExecuted");
+ // Updating EVM timestamp to test delay
+ const plus31days = 60 * 60 * 24 * 31;
+
+ /*
+ // @rramos - The following code is supposed to increase by 31 days the evm date,
+ // and mine one block. It is commented because it seems to not be working on web3 1.0.
+ // Also, sendAsync is supposed to be named send in this version, yet it shows an error
+ // that it does not support synchronous executions. (?)
+ // TODO: figure it out!
+
+ web3.currentProvider.send({jsonrpc: "2.0", method: "evm_increaseTime", params: [plus31days], id: 0}, function(){console.log(1);});
+ web3.currentProvider.send({jsonrpc: "2.0", method: "evm_mine", params: [], id: 0}, function(){console.log(2);})
+
+
+
+ // Confirm update
+ let tx2 = await Identity.methods.execute(
+ Identity.address,
+ 0,
+ idUtils.encode.updateConfirmUpdatableInstance(UpdatedIdentityKernel.address))
+ .send({from: accounts[0]});
+
+ assert.notEqual(tx2.events.Executed, undefined, "Executed wasn't triggered");
+
+
+ let updatedIdentity1 = new web3.eth.Contract(updatedIdentityKernelJson.abi, Identity.address, {from: accounts[0]});
+
+ // Calling
+ let tx3 = await updatedIdentity1.methods.test().send({from: accounts[0]});
+ assert.notEqual(tx3.events.TestFunctionExecuted, undefined, "TestFunctionExecuted wasn't triggered");
assert.equal(
- tx2.logs[tx2.logs.length - 1].args.minApprovalsByManagementKeys.toString(10),
+ tx3.events.TestFunctionExecuted.returnValues.minApprovalsByManagementKeys.toString(10),
1,
- identity.address + " wasn't updated to last version");
- });
+ Identity.address + " wasn't updated to last version");
+
+ */
+ })
});
diff --git a/utils/identityUtils.js b/utils/identityUtils.js
index 91613a4..cb0ac08 100644
--- a/utils/identityUtils.js
+++ b/utils/identityUtils.js
@@ -103,6 +103,20 @@ const _setupRecovery = function(address){
}, [address]);
}
+const _managerReset = function(address){
+ if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
+ throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
+
+ return web3EthAbi.encodeFunctionCall({
+ name: 'managerReset',
+ type: 'function',
+ inputs: [{
+ type: 'address',
+ name: '_newKey'
+ }]
+ }, [address]);
+}
+
const _updateUpdatableInstance = function(address){
if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
@@ -117,6 +131,45 @@ const _updateUpdatableInstance = function(address){
}, [address]);
}
+const _updateRequestUpdatableInstance = function(address){
+ if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
+ throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
+
+ return web3EthAbi.encodeFunctionCall({
+ name: 'updateRequestUpdatableInstance',
+ type: 'function',
+ inputs: [{
+ type: 'address',
+ name: '_kernel'
+ }]
+ }, [address]);
+}
+
+const _updateConfirmUpdatableInstance = function(address){
+ if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
+ throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
+
+ return web3EthAbi.encodeFunctionCall({
+ name: 'updateConfirmUpdatableInstance',
+ type: 'function',
+ inputs: [{
+ type: 'address',
+ name: '_kernel'
+ }]
+ }, [address]);
+}
+
+const _updateCancelUpdatableInstance = function(address){
+ if(!/^(0x)?[0-9a-f]{0,40}$/i.test(address))
+ throw new Error('Address "'+ address +'" is not a valid Ethereum address.');
+
+ return web3EthAbi.encodeFunctionCall({
+ name: 'updateCancelUpdatableInstance',
+ type: 'function',
+ inputs: []
+ }, []);
+}
+
@@ -128,6 +181,10 @@ module.exports = {
removeKey: _removeKey,
setMinimumApprovalsByKeyType: _setMinimumApprovalsByKeyType,
setupRecovery: _setupRecovery,
- updateUpdatableInstance: _updateUpdatableInstance
+ managerReset: _managerReset,
+ updateUpdatableInstance: _updateUpdatableInstance,
+ updateRequestUpdatableInstance: _updateRequestUpdatableInstance,
+ updateConfirmUpdatableInstance: _updateConfirmUpdatableInstance,
+ updateCancelUpdatableInstance: _updateCancelUpdatableInstance
}
}
\ No newline at end of file
From 7235aa85d73a843ca8886327ee793b6ab6767430 Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Thu, 15 Mar 2018 10:41:05 -0400
Subject: [PATCH 060/102] Updated friendsRecovery.js to work with embark
---
contracts/tests/TestContract.sol | 22 ++++++++++++++++++++++
test/factory.js | 5 ++---
2 files changed, 24 insertions(+), 3 deletions(-)
diff --git a/contracts/tests/TestContract.sol b/contracts/tests/TestContract.sol
index 0ed094e..ab6ec58 100644
--- a/contracts/tests/TestContract.sol
+++ b/contracts/tests/TestContract.sol
@@ -8,4 +8,26 @@ contract TestContract {
function test() public {
TestFunctionExecuted();
}
+
+ /*
+ Helper function to be used in unit testing due to error in web3
+ web3.utils.soliditySha3([1, 2, 3])
+ Error: Autodetection of array types is not supported.
+ at _processSoliditySha3Args (node_modules/web3-utils/src/soliditySha3.js:176:15)
+ */
+ function hash(
+ address identity,
+ bytes32 _revealedSecret,
+ address _dest,
+ bytes _data,
+ bytes32 _newSecret,
+ bytes32[] _newFriendsHashes)
+ public
+ pure
+ returns(bytes32)
+ {
+ return keccak256(identity, _revealedSecret, _dest, _data, _newSecret, _newFriendsHashes);
+
+ }
+
}
\ No newline at end of file
diff --git a/test/factory.js b/test/factory.js
index 4f73118..4915c07 100644
--- a/test/factory.js
+++ b/test/factory.js
@@ -3,12 +3,11 @@ const Embark = require('embark');
let EmbarkSpec = Embark.initTests();
let web3 = EmbarkSpec.web3;
-const identityJson = require('../build/contracts/Identity.json');
-const updatedIdentityKernelJson = require('../build/contracts/UpdatedIdentityKernel.json');
+const identityJson = require('../dist/contracts/Identity.json');
+const updatedIdentityKernelJson = require('../dist/contracts/UpdatedIdentityKernel.json');
const TestUtils = require("../utils/testUtils.js")
const idUtils = require("../utils/identityUtils.js")
-const web3EthAbi = require("web3-eth-abi");
describe('IdentityFactory', function(accounts) {
From c2d3928ebec9366053643f0a6d9c8006d8b5d078 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Sat, 17 Mar 2018 11:41:08 -0300
Subject: [PATCH 061/102] reduce warnings
---
contracts/token/MiniMeToken.sol | 45 +-------------------------
contracts/token/MiniMeTokenFactory.sol | 2 +-
2 files changed, 2 insertions(+), 45 deletions(-)
diff --git a/contracts/token/MiniMeToken.sol b/contracts/token/MiniMeToken.sol
index 2ade3f4..da9c815 100644
--- a/contracts/token/MiniMeToken.sol
+++ b/contracts/token/MiniMeToken.sol
@@ -577,47 +577,4 @@ contract MiniMeToken is Controlled {
uint256 _amount
);
-}
-
-
-////////////////
-// MiniMeTokenFactory
-////////////////
-
-/// @dev This contract is used to generate clone contracts from a contract.
-/// In solidity this is the way to create a contract from a contract of the
-/// same class
-contract MiniMeTokenFactory {
-
- /// @notice Update the DApp by creating a new token with new functionalities
- /// the msg.sender becomes the controller of this clone token
- /// @param _parentToken Address of the token being cloned
- /// @param _snapshotBlock Block of the parent token that will
- /// determine the initial distribution of the clone token
- /// @param _tokenName Name of the new token
- /// @param _decimalUnits Number of decimals of the new token
- /// @param _tokenSymbol Token Symbol for the new token
- /// @param _transfersEnabled If true, tokens will be able to be transferred
- /// @return The address of the new token contract
- function createCloneToken(
- address _parentToken,
- uint _snapshotBlock,
- string _tokenName,
- uint8 _decimalUnits,
- string _tokenSymbol,
- bool _transfersEnabled
- ) returns (MiniMeToken) {
- MiniMeToken newToken = new MiniMeToken(
- this,
- _parentToken,
- _snapshotBlock,
- _tokenName,
- _decimalUnits,
- _tokenSymbol,
- _transfersEnabled
- );
-
- newToken.changeController(msg.sender);
- return newToken;
- }
-}
+}
\ No newline at end of file
diff --git a/contracts/token/MiniMeTokenFactory.sol b/contracts/token/MiniMeTokenFactory.sol
index 4e79ead..a57eea4 100644
--- a/contracts/token/MiniMeTokenFactory.sol
+++ b/contracts/token/MiniMeTokenFactory.sol
@@ -28,7 +28,7 @@ contract MiniMeTokenFactory {
uint8 _decimalUnits,
string _tokenSymbol,
bool _transfersEnabled
- ) returns (MiniMeToken) {
+ ) public returns (MiniMeToken) {
MiniMeToken newToken = new MiniMeToken(
this,
_parentToken,
From 1386334b57dc149b15682ceb413b5258a0f84ed2 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Sat, 17 Mar 2018 12:55:25 -0300
Subject: [PATCH 062/102] lint and interface abstraction
---
contracts/token/MiniMeToken.sol | 6 +-
contracts/token/MiniMeTokenInterface.sol | 97 ++++++++++++++++++++++++
2 files changed, 101 insertions(+), 2 deletions(-)
create mode 100644 contracts/token/MiniMeTokenInterface.sol
diff --git a/contracts/token/MiniMeToken.sol b/contracts/token/MiniMeToken.sol
index da9c815..bf4378b 100644
--- a/contracts/token/MiniMeToken.sol
+++ b/contracts/token/MiniMeToken.sol
@@ -28,11 +28,13 @@ pragma solidity ^0.4.6;
import "../common/Controlled.sol";
import "./TokenController.sol";
import "./ApproveAndCallFallBack.sol";
+import "./MiniMeTokenInterface.sol";
+import "./MiniMeTokenFactory.sol";
/// @dev The actual token contract, the default controller is the msg.sender
/// that deploys the contract, so usually this token will be deployed by a
/// token controller contract, which Giveth will call a "Campaign"
-contract MiniMeToken is Controlled {
+contract MiniMeToken is MiniMeTokenInterface, Controlled {
string public name; //The Token's name: e.g. DigixDAO Tokens
uint8 public decimals; //Number of decimals of the smallest unit
@@ -43,7 +45,7 @@ contract MiniMeToken is Controlled {
/// @dev `Checkpoint` is the structure that attaches a block number to a
/// given value, the block number attached is the one that last changed the
/// value
- struct Checkpoint {
+ struct Checkpoint {
// `fromBlock` is the block number that the value was generated from
uint128 fromBlock;
diff --git a/contracts/token/MiniMeTokenInterface.sol b/contracts/token/MiniMeTokenInterface.sol
new file mode 100644
index 0000000..4e143e0
--- /dev/null
+++ b/contracts/token/MiniMeTokenInterface.sol
@@ -0,0 +1,97 @@
+pragma solidity ^0.4.17;
+
+import "./ERC20Token.sol";
+
+contract MiniMeTokenInterface is ERC20Token {
+
+
+ /// @notice `msg.sender` approves `_spender` to send `_amount` tokens on
+ /// its behalf, and then a function is triggered in the contract that is
+ /// being approved, `_spender`. This allows users to use their tokens to
+ /// interact with contracts in one function call instead of two
+ /// @param _spender The address of the contract able to transfer the tokens
+ /// @param _amount The amount of tokens to be approved for transfer
+ /// @return True if the function call was successful
+ function approveAndCall(
+ address _spender,
+ uint256 _amount,
+ bytes _extraData
+ )
+ public
+ returns (bool success);
+
+
+ /// @notice Creates a new clone token with the initial distribution being
+ /// this token at `_snapshotBlock`
+ /// @param _cloneTokenName Name of the clone token
+ /// @param _cloneDecimalUnits Number of decimals of the smallest unit
+ /// @param _cloneTokenSymbol Symbol of the clone token
+ /// @param _snapshotBlock Block when the distribution of the parent token is
+ /// copied to set the initial distribution of the new clone token;
+ /// if the block is zero than the actual block, the current block is used
+ /// @param _transfersEnabled True if transfers are allowed in the clone
+ /// @return The address of the new MiniMeToken Contract
+ function createCloneToken(
+ string _cloneTokenName,
+ uint8 _cloneDecimalUnits,
+ string _cloneTokenSymbol,
+ uint _snapshotBlock,
+ bool _transfersEnabled
+ )
+ public
+ returns(address);
+
+ /// @notice Generates `_amount` tokens that are assigned to `_owner`
+ /// @param _owner The address that will be assigned the new tokens
+ /// @param _amount The quantity of tokens generated
+ /// @return True if the tokens are generated correctly
+ function generateTokens(
+ address _owner,
+ uint _amount
+ )
+ public
+ returns (bool);
+
+ /// @notice Burns `_amount` tokens from `_owner`
+ /// @param _owner The address that will lose the tokens
+ /// @param _amount The quantity of tokens to burn
+ /// @return True if the tokens are burned correctly
+ function destroyTokens(
+ address _owner,
+ uint _amount
+ )
+ public
+ returns (bool);
+
+
+ /// @notice Enables token holders to transfer their tokens freely if true
+ /// @param _transfersEnabled True if transfers are allowed in the clone
+ function enableTransfers(bool _transfersEnabled) public;
+
+
+ /// @notice This method can be used by the controller to extract mistakenly
+ /// sent tokens to this contract.
+ /// @param _token The address of the token contract that you want to recover
+ /// set to 0 in case you want to extract ether.
+ function claimTokens(address _token) public;
+
+ /// @dev Queries the balance of `_owner` at a specific `_blockNumber`
+ /// @param _owner The address from which the balance will be retrieved
+ /// @param _blockNumber The block number when the balance is queried
+ /// @return The balance at `_blockNumber`
+ function balanceOfAt(
+ address _owner,
+ uint _blockNumber
+ )
+ public
+ constant
+ returns (uint);
+
+ /// @notice Total amount of tokens at a specific `_blockNumber`.
+ /// @param _blockNumber The block number when the totalSupply is queried
+ /// @return The total amount of tokens at `_blockNumber`
+ function totalSupplyAt(uint _blockNumber) public constant returns(uint);
+
+
+
+}
\ No newline at end of file
From e8edd3d53c2f322d30eeab211f681ebdbb03b89b Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Sat, 17 Mar 2018 13:50:33 -0300
Subject: [PATCH 063/102] fix compile errors
---
contracts/token/MiniMeTokenInterface.sol | 36 ++++++++++++++++++++++--
1 file changed, 34 insertions(+), 2 deletions(-)
diff --git a/contracts/token/MiniMeTokenInterface.sol b/contracts/token/MiniMeTokenInterface.sol
index 4e143e0..e315956 100644
--- a/contracts/token/MiniMeTokenInterface.sol
+++ b/contracts/token/MiniMeTokenInterface.sol
@@ -1,9 +1,41 @@
pragma solidity ^0.4.17;
-import "./ERC20Token.sol";
+contract MiniMeTokenInterface {
-contract MiniMeTokenInterface is ERC20Token {
+ event Transfer(address indexed _from, address indexed _to, uint256 _value);
+ event Approval(address indexed _owner, address indexed _spender, uint256 _value);
+
+ /// @dev This function makes it easy to get the total number of tokens
+ /// @return The total number of tokens
+ function totalSupply() public constant returns (uint);
+ /// @param _owner The address from which the balance will be retrieved
+ /// @return The balance
+ function balanceOf(address _owner) public constant returns (uint256 balance);
+
+ /// @notice send `_value` token to `_to` from `msg.sender`
+ /// @param _to The address of the recipient
+ /// @param _value The amount of token to be transferred
+ /// @return Whether the transfer was successful or not
+ function transfer(address _to, uint256 _value) public returns (bool success);
+
+ /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
+ /// @param _from The address of the sender
+ /// @param _to The address of the recipient
+ /// @param _value The amount of token to be transferred
+ /// @return Whether the transfer was successful or not
+ function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
+
+ /// @notice `msg.sender` approves `_spender` to spend `_value` tokens
+ /// @param _spender The address of the account able to transfer the tokens
+ /// @param _value The amount of tokens to be approved for transfer
+ /// @return Whether the approval was successful or not
+ function approve(address _spender, uint256 _value) public returns (bool success);
+
+ /// @param _owner The address of the account owning tokens
+ /// @param _spender The address of the account able to transfer the tokens
+ /// @return Amount of remaining tokens allowed to spent
+ function allowance(address _owner, address _spender) public constant returns (uint256 remaining);
/// @notice `msg.sender` approves `_spender` to send `_amount` tokens on
/// its behalf, and then a function is triggered in the contract that is
From 988ccd108594adae004ad7b10023ddc6f6cb0387 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Mon, 19 Mar 2018 23:15:22 -0300
Subject: [PATCH 064/102] reduce warnings
---
contracts/identity/Identity.sol | 66 ++++++++++++++++-----------------
1 file changed, 33 insertions(+), 33 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 86ff594..33334d2 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -69,12 +69,14 @@ contract Identity is ERC725, ERC735 {
bytes32 _s
)
{
- require(address(_key) == ecrecover(
- keccak256("\x19Ethereum Signed Message:\n32", _signHash),
- _v,
- _r,
- _s
- ));
+ require(
+ address(_key) == ecrecover(
+ keccak256("\x19Ethereum Signed Message:\n32", _signHash),
+ _v,
+ _r,
+ _s
+ )
+ );
require(keys[_key].purpose != 0);
_;
}
@@ -221,7 +223,7 @@ contract Identity is ERC725, ERC735 {
require(_issuer == msg.sender);
require(isKeyType(bytes32(msg.sender), CLAIM_SIGNER_KEY));
_execute(address(this), 0, msg.data);
- ClaimRequested(
+ emit ClaimRequested(
claimHash,
_claimType,
_scheme,
@@ -389,17 +391,16 @@ contract Identity is ERC725, ERC735 {
_key,
keccak256(
address(this),
- bytes4(
- keccak256("execute(address,uint256,bytes)")),
- _to,
- _value,
- _data,
- _nonce
- ),
- _v,
- _r,
- _s
- )
+ bytes4(keccak256("execute(address,uint256,bytes)")),
+ _to,
+ _value,
+ _data,
+ _nonce
+ ),
+ _v,
+ _r,
+ _s
+ )
managerOrActor(_key)
returns (uint256 executionId)
{
@@ -434,15 +435,14 @@ contract Identity is ERC725, ERC735 {
returns (uint256 executionId)
{
executionId = nonce;
- ExecutionRequested(executionId, _to, _value, _data);
- txx[executionId] = Transaction(
- {
- to: _to,
- value: _value,
- data: _data,
- nonce: nonce,
- approverCount: 0
- });
+ emit ExecutionRequested(executionId, _to, _value, _data);
+ txx[executionId] = Transaction({
+ to: _to,
+ value: _value,
+ data: _data,
+ nonce: nonce,
+ approverCount: 0
+ });
nonce++;
}
@@ -460,7 +460,7 @@ contract Identity is ERC725, ERC735 {
uint256 approvalCount;
uint256 requiredKeyPurpose;
- Approved(_id, _approval);
+ emit Approved(_id, _approval);
if (trx.to == address(this)) {
require(isKeyType(_key, MANAGEMENT_KEY));
@@ -475,7 +475,7 @@ contract Identity is ERC725, ERC735 {
}
if (approvalCount >= minimumApprovalsByKeyPurpose[requiredKeyPurpose]) {
- Executed(_id, trx.to, trx.value, trx.data);
+ emit Executed(_id, trx.to, trx.value, trx.data);
success = trx.to.call.value(trx.value)(trx.data);
}
}
@@ -496,7 +496,7 @@ contract Identity is ERC725, ERC735 {
_purpose == CLAIM_SIGNER_KEY ||
_purpose == ENCRYPTION_KEY
);
- KeyAdded(_key, _purpose, _type);
+ emit KeyAdded(_key, _purpose, _type);
keys[keyHash] = Key(_purpose, _type, _key);
indexes[keyHash] = keysByPurpose[_purpose].push(_key) - 1;
}
@@ -509,7 +509,7 @@ contract Identity is ERC725, ERC735 {
{
bytes32 keyHash = keccak256(_key, _purpose);
Key storage myKey = keys[keyHash];
- KeyRemoved(myKey.key, myKey.purpose, myKey.keyType);
+ emit KeyRemoved(myKey.key, myKey.purpose, myKey.keyType);
uint index = indexes[keyHash];
delete indexes[keyHash];
@@ -573,7 +573,7 @@ contract Identity is ERC725, ERC735 {
);
indexes[_claimHash] = claimsByType[_claimType].length;
claimsByType[_claimType].push(_claimHash);
- ClaimAdded(
+ emit ClaimAdded(
_claimHash,
_claimType,
_scheme,
@@ -597,7 +597,7 @@ contract Identity is ERC725, ERC735 {
private
{
require(msg.sender == _issuer);
- ClaimChanged(
+ emit ClaimChanged(
_claimHash,
_claimType,
_scheme,
From c09772e634571926e67e9fcafb49c4b883b88918 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Tue, 20 Mar 2018 22:29:37 -0300
Subject: [PATCH 065/102] additional redundant security check
---
contracts/identity/Identity.sol | 1 +
1 file changed, 1 insertion(+)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 33334d2..0b6ff3c 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -419,6 +419,7 @@ contract Identity is ERC725, ERC735 {
function _constructIdentity(address _manager)
internal
{
+ require(keysByPurpose[MANAGEMENT_KEY].length == 0);
require(minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] == 0);
_addKey(bytes32(_manager), MANAGEMENT_KEY, 0);
From 776022d583af3d03326557eec0cf35052ceb24ce Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Tue, 20 Mar 2018 22:31:14 -0300
Subject: [PATCH 066/102] remove abstracted InstanceStorage to prevent errors
---
contracts/deploy/DelayedUpdatableInstance.sol | 2 +-
contracts/deploy/DelayedUpdatableInstanceStorage.sol | 11 +++++------
2 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/contracts/deploy/DelayedUpdatableInstance.sol b/contracts/deploy/DelayedUpdatableInstance.sol
index af12bca..3ff6b0f 100644
--- a/contracts/deploy/DelayedUpdatableInstance.sol
+++ b/contracts/deploy/DelayedUpdatableInstance.sol
@@ -27,7 +27,7 @@ contract DelayedUpdatableInstance is DelayedUpdatableInstanceStorage, DelegatedC
}
/**
- * @dev returns kernel if kernel that is configured
+ * @dev returns configured kernel
* @return kernel address
*/
function targetDelegatedCall()
diff --git a/contracts/deploy/DelayedUpdatableInstanceStorage.sol b/contracts/deploy/DelayedUpdatableInstanceStorage.sol
index 274da20..2d71661 100644
--- a/contracts/deploy/DelayedUpdatableInstanceStorage.sol
+++ b/contracts/deploy/DelayedUpdatableInstanceStorage.sol
@@ -1,17 +1,16 @@
pragma solidity ^0.4.17;
-import "./InstanceStorage.sol";
-
/**
- * @title InstanceStorage
+ * @title DelayedUpdatableInstanceStorage
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
- * @dev Defines kernel vars that Kernel contract share with Instance.
+ * @dev Defines kernel vars that Kernel contract share with DelayedUpdatableInstance.
* Important to avoid overwriting wrong storage pointers is that
* InstanceStorage should be always the first contract at heritance.
*/
-contract DelayedUpdatableInstanceStorage is InstanceStorage {
+contract DelayedUpdatableInstanceStorage {
// protected zone start (InstanceStorage vars)
- Update update;
+ address public kernel;
+ Update public update;
struct Update {
address kernel;
From 8d5046fcb8a94b658ef3d5f7b8a256b9cdfef2b2 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 21 Mar 2018 12:47:12 -0300
Subject: [PATCH 067/102] reduce warnings
---
contracts/identity/Identity.sol | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 0b6ff3c..15b14e8 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -101,10 +101,11 @@ contract Identity is ERC725, ERC735 {
minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] = keysByPurpose[MANAGEMENT_KEY].length;
}
- function processManagerReset(uint256 limit)
+ function processManagerReset(uint256 _limit)
public
{
require(recoveryManager != address(0));
+ uint limit = _limit;
bytes32 newKey = bytes32(recoveryManager);
bytes32[] memory managers = keysByPurpose[MANAGEMENT_KEY];
uint256 totalManagers = managers.length;
From accd832bf3172fd27ac7f711250ee0d38b011fbb Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 21 Mar 2018 12:50:03 -0300
Subject: [PATCH 068/102] function named wrongly fix: rename isKeyType ->
isKeyPurpose
---
contracts/identity/Identity.sol | 25 +++++++++++++------------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 15b14e8..d802cec 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -29,7 +29,7 @@ contract Identity is ERC725, ERC735 {
modifier managerOnly {
require(
- isKeyType(bytes32(msg.sender), MANAGEMENT_KEY)
+ isKeyPurpose(bytes32(msg.sender), MANAGEMENT_KEY)
);
_;
}
@@ -48,15 +48,16 @@ contract Identity is ERC725, ERC735 {
_;
}
+
modifier actorOnly(bytes32 _key) {
- require(isKeyType(_key, ACTION_KEY));
+ require(isKeyPurpose(_key, ACTION_KEY));
_;
}
modifier managerOrActor(bytes32 _key) {
require(
- isKeyType(bytes32(msg.sender), MANAGEMENT_KEY) ||
- isKeyType(bytes32(msg.sender), ACTION_KEY)
+ isKeyPurpose(bytes32(msg.sender), MANAGEMENT_KEY) ||
+ isKeyPurpose(bytes32(msg.sender), ACTION_KEY)
);
_;
}
@@ -222,7 +223,7 @@ contract Identity is ERC725, ERC735 {
}
} else {
require(_issuer == msg.sender);
- require(isKeyType(bytes32(msg.sender), CLAIM_SIGNER_KEY));
+ require(isKeyPurpose(bytes32(msg.sender), CLAIM_SIGNER_KEY));
_execute(address(this), 0, msg.data);
emit ClaimRequested(
claimHash,
@@ -273,7 +274,7 @@ contract Identity is ERC725, ERC735 {
return (myKey.purpose, myKey.keyType, myKey.key);
}
- function isKeyType(bytes32 _key, uint256 _type)
+ function isKeyPurpose(bytes32 _key, uint256 _type)
public
constant
returns (bool)
@@ -290,22 +291,22 @@ contract Identity is ERC725, ERC735 {
uint256[] memory purposeHolder = new uint256[](4);
uint8 counter = 0;
- if (isKeyType(_key, MANAGEMENT_KEY)) {
+ if (isKeyPurpose(_key, MANAGEMENT_KEY)) {
purposeHolder[counter] = MANAGEMENT_KEY;
counter++;
}
- if (isKeyType(_key, ACTION_KEY)) {
+ if (isKeyPurpose(_key, ACTION_KEY)) {
purposeHolder[counter] = ACTION_KEY;
counter++;
}
- if (isKeyType(_key, CLAIM_SIGNER_KEY)) {
+ if (isKeyPurpose(_key, CLAIM_SIGNER_KEY)) {
purposeHolder[counter] = CLAIM_SIGNER_KEY;
counter++;
}
- if (isKeyType(_key, ENCRYPTION_KEY)) {
+ if (isKeyPurpose(_key, ENCRYPTION_KEY)) {
purposeHolder[counter] = ENCRYPTION_KEY;
counter++;
}
@@ -465,12 +466,12 @@ contract Identity is ERC725, ERC735 {
emit Approved(_id, _approval);
if (trx.to == address(this)) {
- require(isKeyType(_key, MANAGEMENT_KEY));
+ require(isKeyPurpose(_key, MANAGEMENT_KEY));
bytes32 managerKeyHash = keccak256(_key, MANAGEMENT_KEY);
requiredKeyPurpose = MANAGEMENT_KEY;
approvalCount = _calculateApprovals(managerKeyHash, _approval, trx);
} else {
- require(isKeyType(_key, ACTION_KEY));
+ require(isKeyPurpose(_key, ACTION_KEY));
bytes32 actorKeyHash = keccak256(_key, ACTION_KEY);
requiredKeyPurpose = ACTION_KEY;
approvalCount = _calculateApprovals(actorKeyHash, _approval, trx);
From dc6239546b78d9127150b5ce89887ff37f2c7915 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 21 Mar 2018 13:07:12 -0300
Subject: [PATCH 069/102] fix inconsistent modifier
---
contracts/identity/Identity.sol | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index d802cec..c9b2236 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -56,8 +56,8 @@ contract Identity is ERC725, ERC735 {
modifier managerOrActor(bytes32 _key) {
require(
- isKeyPurpose(bytes32(msg.sender), MANAGEMENT_KEY) ||
- isKeyPurpose(bytes32(msg.sender), ACTION_KEY)
+ isKeyPurpose(_key, MANAGEMENT_KEY) ||
+ isKeyPurpose(_key, ACTION_KEY)
);
_;
}
From bf51e76c9c0a5050d7009e48ec618431022865bb Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 21 Mar 2018 13:13:04 -0300
Subject: [PATCH 070/102] reduce warnings
---
contracts/identity/Identity.sol | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index c9b2236..0c35c5e 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -43,7 +43,8 @@ contract Identity is ERC725, ERC735 {
modifier recoveryOnly {
require(
- (recoveryContract != address(0) && msg.sender == address(recoveryContract))
+ recoveryContract != address(0) &&
+ msg.sender == address(recoveryContract)
);
_;
}
@@ -566,12 +567,12 @@ contract Identity is ERC725, ERC735 {
{
claims[_claimHash] = Claim(
{
- claimType: _claimType,
- scheme: _scheme,
- issuer: _issuer,
- signature: _signature,
- data: _data,
- uri: _uri
+ claimType: _claimType,
+ scheme: _scheme,
+ issuer: _issuer,
+ signature: _signature,
+ data: _data,
+ uri: _uri
}
);
indexes[_claimHash] = claimsByType[_claimType].length;
From bcadf42cefb6acf5cda3c2a9d5b5f8ffcc62f9e6 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 21 Mar 2018 13:23:46 -0300
Subject: [PATCH 071/102] enhance utility modifier
---
contracts/identity/Identity.sol | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 0c35c5e..fe2fb63 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -49,9 +49,8 @@ contract Identity is ERC725, ERC735 {
_;
}
-
- modifier actorOnly(bytes32 _key) {
- require(isKeyPurpose(_key, ACTION_KEY));
+ modifier keyPurposeOnly(bytes32 _key, uint256 _purpose) {
+ require(isKeyPurpose(_key, _purpose));
_;
}
From 1f9ad9b135e1a0710132799b0cd73e165bc2dc35 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 21 Mar 2018 18:02:05 -0300
Subject: [PATCH 072/102] add economic abstraction property to Identities
---
contracts/identity/IdentityGasRelay.sol | 247 ++++++++++++++++++++++++
1 file changed, 247 insertions(+)
create mode 100644 contracts/identity/IdentityGasRelay.sol
diff --git a/contracts/identity/IdentityGasRelay.sol b/contracts/identity/IdentityGasRelay.sol
new file mode 100644
index 0000000..bb5eb02
--- /dev/null
+++ b/contracts/identity/IdentityGasRelay.sol
@@ -0,0 +1,247 @@
+pragma solidity ^0.4.17;
+
+import "./Identity.sol";
+import "../token/ERC20Token.sol";
+
+contract IdentityGasRelay is Identity {
+
+ bytes4 public constant EXECUTE_PREFIX = bytes4(keccak256("executeGasRelayed"));
+
+ event ExecutedGasRelayed(bytes32 signHash);
+
+ function executeSigned(
+ address _to,
+ uint256 _value,
+ bytes _data,
+ uint _nonce,
+ uint _gasPrice,
+ uint _gasMinimum,
+ address _gasToken,
+ bytes _messageSignature
+ )
+ external
+ {
+ uint startGas = gasleft();
+ require(startGas > _gasMinimum);
+ uint256 requiredKey = _to == address(this) ? MANAGEMENT_KEY : ACTION_KEY;
+ require(minimumApprovalsByKeyPurpose[requiredKey] == 1);
+ require(_nonce == nonce);
+ nonce++;
+
+ bytes32 _signedHash = getSignHash(
+ executeHash(
+ _to,
+ _value,
+ keccak256(_data),
+ _nonce,
+ _gasPrice,
+ _gasMinimum,
+ _gasToken
+ )
+ );
+
+ require(
+ isKeyPurpose(
+ recoverKey(
+ _signedHash,
+ _messageSignature,
+ 0
+ ),
+ requiredKey
+ )
+ );
+
+ if (_to.call.value(_value)(_data)) {
+ emit ExecutedGasRelayed(_signedHash);
+ }
+
+ if(_gasPrice > 0) {
+ payInclusionFee(
+ startGas - gasleft(),
+ _gasPrice,
+ msg.sender,
+ _gasToken
+ );
+ }
+ }
+
+ function executeMultiSigned(
+ address _to,
+ uint256 _value,
+ bytes _data,
+ uint _nonce,
+ uint _gasPrice,
+ uint _gasMinimum,
+ address _gasToken,
+ bytes _messageSignatures
+ )
+ external
+ {
+ uint startGas = gasleft();
+ require(startGas > _gasMinimum);
+ require(_nonce == nonce);
+ nonce++;
+ _executeMultiSigned(_to, _value, _data, _nonce, _gasPrice, _gasMinimum, _gasToken, _messageSignatures);
+ if (_gasPrice > 0) {
+ payInclusionFee(
+ startGas - gasleft(),
+ _gasPrice,
+ msg.sender,
+ _gasToken
+ );
+ }
+ }
+
+ function executeHash(
+ address _to,
+ uint256 _value,
+ bytes32 _dataHash,
+ uint _nonce,
+ uint256 _gasPrice,
+ uint256 _gasMinimum,
+ address _gasToken
+ )
+ public
+ view
+ returns (bytes32)
+ {
+ return keccak256(
+ address(this),
+ EXECUTE_PREFIX,
+ _to,
+ _value,
+ _dataHash,
+ _nonce,
+ _gasPrice,
+ _gasMinimum,
+ _gasToken
+ );
+ }
+
+ function recoverKey (
+ bytes32 _signHash,
+ bytes _messageSignature,
+ uint256 _pos
+ )
+ pure
+ public
+ returns(bytes32)
+ {
+ uint8 v;
+ bytes32 r;
+ bytes32 s;
+ (v,r,s) = signatureSplit(_messageSignature, _pos);
+ return bytes32(
+ ecrecover(
+ _signHash,
+ v,
+ r,
+ s
+ )
+ );
+ }
+
+ /**
+ * @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`
+ */
+ function signatureSplit(bytes _signature, uint256 _pos)
+ pure
+ public
+ returns (uint8 v, bytes32 r, bytes32 s)
+ {
+ uint pos = _pos + 1;
+ // The signature format is a compact form of:
+ // {bytes32 r}{bytes32 s}{uint8 v}
+ // Compact means, uint8 is not padded to 32 bytes.
+ assembly {
+ r := mload(add(_signature, mul(32,pos)))
+ s := mload(add(_signature, mul(64,pos)))
+ // Here we are loading the last 32 bytes, including 31 bytes
+ // of 's'. There is no 'mload8' to do this.
+ //
+ // 'byte' is not working due to the Solidity parser, so lets
+ // use the second best option, 'and'
+ v := and(mload(add(_signature, mul(65,pos))), 0xff)
+ }
+
+ require(v == 27 || v == 28);
+ }
+
+ /**
+ * @notice Hash a hash with `"\x19Ethereum Signed Message:\n32"`
+ * @param _hash Sign to hash.
+ * @return signHash Hash to be signed.
+ */
+ function getSignHash(
+ bytes32 _hash
+ )
+ pure
+ public
+ returns(bytes32 signHash)
+ {
+ signHash = keccak256("\x19Ethereum Signed Message:\n32", _hash);
+ }
+
+
+ function _executeMultiSigned(
+ address _to,
+ uint256 _value,
+ bytes _data,
+ uint _nonce,
+ uint _gasPrice,
+ uint _gasMinimum,
+ address _gasToken,
+ bytes _messageSignatures
+ )
+ private
+ {
+ uint256 requiredKey = _to == address(this) ? MANAGEMENT_KEY : ACTION_KEY;
+ uint256 len = _messageSignatures.length / 72;
+ require(len == minimumApprovalsByKeyPurpose[requiredKey]);
+
+ bytes32 _signedHash = getSignHash(
+ executeHash(
+ _to,
+ _value,
+ keccak256(_data),
+ _nonce,
+ _gasPrice,
+ _gasMinimum,
+ _gasToken
+ )
+ );
+
+ bytes32 _lastKey = 0;
+ for (uint256 i = 0; i < len; i++) {
+ bytes32 _key = recoverKey(
+ _signedHash,
+ _messageSignatures,
+ i
+ );
+ require(_key > _lastKey); //assert keys are different
+ require(isKeyPurpose(_key, requiredKey));
+ _lastKey = _key;
+ }
+
+ if (_to.call.value(_value)(_data)) {
+ emit ExecutedGasRelayed(_signedHash);
+ }
+ }
+
+ function payInclusionFee(
+ uint256 _gasUsed,
+ uint256 _gasPrice,
+ address _msgIncluder,
+ address _gasToken
+ )
+ private
+ {
+ uint256 _amount = (21000 + _gasUsed) * _gasPrice;
+ if (_gasToken == address(0)) {
+ address(_msgIncluder).transfer(_amount);
+ } else {
+ ERC20Token(_gasToken).transfer(_msgIncluder, _amount);
+ }
+ }
+
+}
\ No newline at end of file
From e13ac0315dc8006b9c84065023f6e990e3cc70e8 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 21 Mar 2018 18:43:26 -0300
Subject: [PATCH 073/102] update names
---
contracts/identity/IdentityGasRelay.sol | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/contracts/identity/IdentityGasRelay.sol b/contracts/identity/IdentityGasRelay.sol
index bb5eb02..4fde2f7 100644
--- a/contracts/identity/IdentityGasRelay.sol
+++ b/contracts/identity/IdentityGasRelay.sol
@@ -5,11 +5,11 @@ import "../token/ERC20Token.sol";
contract IdentityGasRelay is Identity {
- bytes4 public constant EXECUTE_PREFIX = bytes4(keccak256("executeGasRelayed"));
+ bytes4 public constant EXECUTE_PREFIX = bytes4(keccak256("executeGasRelayed(address,uint256,bytes32,uint256,uint256,address)"));
event ExecutedGasRelayed(bytes32 signHash);
- function executeSigned(
+ function executeGasRelayed(
address _to,
uint256 _value,
bytes _data,
@@ -29,7 +29,7 @@ contract IdentityGasRelay is Identity {
nonce++;
bytes32 _signedHash = getSignHash(
- executeHash(
+ executeGasRelayedHash(
_to,
_value,
keccak256(_data),
@@ -65,7 +65,7 @@ contract IdentityGasRelay is Identity {
}
}
- function executeMultiSigned(
+ function executeGasRelayedMultiSigned(
address _to,
uint256 _value,
bytes _data,
@@ -81,7 +81,7 @@ contract IdentityGasRelay is Identity {
require(startGas > _gasMinimum);
require(_nonce == nonce);
nonce++;
- _executeMultiSigned(_to, _value, _data, _nonce, _gasPrice, _gasMinimum, _gasToken, _messageSignatures);
+ _executeGasRelayedMultiSigned(_to, _value, _data, _nonce, _gasPrice, _gasMinimum, _gasToken, _messageSignatures);
if (_gasPrice > 0) {
payInclusionFee(
startGas - gasleft(),
@@ -92,7 +92,7 @@ contract IdentityGasRelay is Identity {
}
}
- function executeHash(
+ function executeGasRelayedHash(
address _to,
uint256 _value,
bytes32 _dataHash,
@@ -183,7 +183,7 @@ contract IdentityGasRelay is Identity {
}
- function _executeMultiSigned(
+ function _executeGasRelayedMultiSigned(
address _to,
uint256 _value,
bytes _data,
@@ -200,7 +200,7 @@ contract IdentityGasRelay is Identity {
require(len == minimumApprovalsByKeyPurpose[requiredKey]);
bytes32 _signedHash = getSignHash(
- executeHash(
+ executeGasRelayedHash(
_to,
_value,
keccak256(_data),
From cfc3c17c6bf1fe379b081934332820a0384885e1 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 21 Mar 2018 19:36:23 -0300
Subject: [PATCH 074/102] new snt controller with economic abstraction
---
contracts/status/SNTController.sol | 297 +++++++++++++++++++++++++++++
1 file changed, 297 insertions(+)
create mode 100644 contracts/status/SNTController.sol
diff --git a/contracts/status/SNTController.sol b/contracts/status/SNTController.sol
new file mode 100644
index 0000000..3af76ca
--- /dev/null
+++ b/contracts/status/SNTController.sol
@@ -0,0 +1,297 @@
+pragma solidity ^0.4.17;
+
+import "../token/TokenController.sol";
+import "../common/Owned.sol";
+import "../token/ERC20Token.sol";
+import "../token/MiniMeToken.sol";
+
+/**
+ * @title SNTController
+ * @author Ricardo Guilherme Schmidt
+ */
+contract SNTController is TokenController, Owned {
+
+
+ bytes4 public constant TRANSFER_PREFIX = bytes4(keccak256("transferSNT(address,uint256,uint256,uint256)"));
+ bytes4 public constant EXECUTE_PREFIX = bytes4(keccak256("executeGasRelayed(address,bytes,uint256,uint256,uint256)"));
+
+ MiniMeToken public snt;
+ mapping (address => uint256) public signNonce;
+ mapping (address => bool) public allowPublicExecution;
+
+ event PublicExecutionEnabled(address indexed contractAddress, bool enabled);
+ event GasRelayedExecution(address indexed msgSigner, bytes32 signedHash, bool executed);
+ event ClaimedTokens(address indexed _token, address indexed _controller, uint256 _amount);
+ event ControllerChanged(address indexed _newController);
+
+ /**
+ * @notice Constructor
+ * @param _owner Authority address
+ * @param _snt SNT token
+ */
+ function SNTController(address _owner, address _snt) public {
+ owner = _owner;
+ snt = MiniMeToken(_snt);
+ }
+
+ /**
+ * @notice allows externally owned address sign a message to transfer SNT and pay
+ * @param _to address receving the tokens from message signer
+ * @param _amount total being transfered
+ * @param _nonce current signNonce of message signer
+ * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
+ * @param _signature concatenated rsv of message
+ */
+ function transferSNT(
+ address _to,
+ uint256 _amount,
+ uint256 _nonce,
+ uint256 _gasPrice,
+ bytes _signature
+ )
+ external
+ {
+ uint256 startGas = gasleft();
+ bytes32 msgSigned = getSignHash(
+ getTransferSNTHash(
+ _to,
+ _amount,
+ _nonce,
+ _gasPrice
+ )
+ );
+
+ address msgSigner = recoverAddress(msgSigned, _signature);
+ require(signNonce[msgSigner] == _nonce);
+ signNonce[msgSigner]++;
+ if (snt.transferFrom(msgSigner, _to, _amount)) {
+ require(snt.transferFrom(msgSigner, msg.sender, (21000 + startGas-gasleft()) * _gasPrice));
+ }
+ }
+
+ /**
+ * @notice allows externally owned address sign a message to offer SNT for a execution
+ * @param _allowedContract address of a contracts in execution trust list;
+ * @param _data msg.data to be sent to `_allowedContract`
+ * @param _nonce current signNonce of message signer
+ * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
+ * @param _gasMinimal minimal amount of gas needed to complete the execution
+ * @param _signature concatenated rsv of message
+ */
+ function executeGasRelayed(
+ address _allowedContract,
+ bytes _data,
+ uint256 _nonce,
+ uint256 _gasPrice,
+ uint256 _gasMinimal,
+ bytes _signature
+ )
+ external
+ {
+ uint256 startGas = gasleft();
+ require(startGas >= _gasMinimal);
+ bytes32 msgSigned = getSignHash(
+ getExecuteGasRelayedHash(
+ _allowedContract,
+ _data,
+ _nonce,
+ _gasPrice,
+ _gasMinimal
+ )
+ );
+
+ address msgSigner = recoverAddress(msgSigned, _signature);
+ require(signNonce[msgSigner] == _nonce);
+ signNonce[msgSigner]++;
+ bool success = _allowedContract.call(_data);
+ emit GasRelayedExecution(msgSigner, msgSigned, success);
+ require(
+ snt.transferFrom(
+ msgSigner,
+ msg.sender,
+ (21000 + startGas-gasleft()) * _gasPrice
+ )
+ );
+ }
+
+ /**
+ * @notice The owner of this contract can change the controller of the SNT token
+ * Please, be sure that the owner is a trusted agent or 0x0 address.
+ * @param _newController The address of the new controller
+ */
+ function changeController(address _newController) public onlyOwner {
+ snt.changeController(_newController);
+ emit ControllerChanged(_newController);
+ }
+
+ function enablePublicExecution(address _contract, bool _enable) public onlyOwner {
+ allowPublicExecution[_contract] = _enable;
+ emit PublicExecutionEnabled(_contract, _enable);
+ }
+
+ //////////
+ // Safety Methods
+ //////////
+
+ /**
+ * @notice This method can be used by the controller to extract mistakenly
+ * sent tokens to this contract.
+ * @param _token The address of the token contract that you want to recover
+ * set to 0 in case you want to extract ether.
+ */
+ function claimTokens(address _token) public onlyOwner {
+ if (snt.controller() == address(this)) {
+ snt.claimTokens(_token);
+ }
+ if (_token == 0x0) {
+ address(owner).transfer(this.balance);
+ return;
+ }
+
+ ERC20Token token = ERC20Token(_token);
+ uint256 balance = token.balanceOf(this);
+ token.transfer(owner, balance);
+ emit ClaimedTokens(_token, owner, balance);
+ }
+
+
+ //////////
+ // MiniMe Controller Interface functions
+ //////////
+
+ // In between the offering and the network. Default settings for allowing token transfers.
+ function proxyPayment(address) public payable returns (bool) {
+ return false;
+ }
+
+ function onTransfer(address, address, uint256) public returns (bool) {
+ return true;
+ }
+
+ function onApprove(address, address, uint256) public returns (bool) {
+ return true;
+ }
+
+ /**
+ * @notice get execution hash
+ * @param _allowedContract address of a contracts in execution trust list;
+ * @param _data msg.data to be sent to `_allowedContract`
+ * @param _nonce current signNonce of message signer
+ * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
+ * @param _gasMinimal minimal amount of gas needed to complete the execution
+ */
+ function getExecuteGasRelayedHash(
+ address _allowedContract,
+ bytes _data,
+ uint256 _nonce,
+ uint256 _gasPrice,
+ uint256 _gasMinimal
+ )
+ public
+ view
+ returns (bytes32 execHash)
+ {
+ execHash = keccak256(
+ address(this),
+ EXECUTE_PREFIX,
+ _allowedContract,
+ keccak256(_data),
+ _nonce,
+ _gasPrice,
+ _gasMinimal
+ );
+ }
+
+ /**
+ * @notice get transfer hash
+ * @param _to address receving the tokens from message signer
+ * @param _amount total being transfered
+ * @param _nonce current signNonce of message signer
+ * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
+ */
+ function getTransferSNTHash(
+ address _to,
+ uint256 _amount,
+ uint256 _nonce,
+ uint256 _gasPrice
+ )
+ public
+ view
+ returns (bytes32 txHash)
+ {
+ txHash = keccak256(
+ address(this),
+ TRANSFER_PREFIX,
+ _to,
+ _amount,
+ _nonce,
+ _gasPrice
+ );
+ }
+
+ /**
+ * @notice recovers address who signed the message
+ * @param _signHash operation ethereum signed message hash
+ * @param _messageSignature message `_signHash` signature
+ */
+ function recoverAddress(
+ bytes32 _signHash,
+ bytes _messageSignature
+ )
+ pure
+ public
+ returns(address)
+ {
+ uint8 v;
+ bytes32 r;
+ bytes32 s;
+ (v,r,s) = signatureSplit(_messageSignature);
+ return ecrecover(
+ _signHash,
+ v,
+ r,
+ s
+ );
+ }
+
+ /**
+ * @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`
+ */
+ function signatureSplit(bytes _signature)
+ pure
+ public
+ returns (uint8 v, bytes32 r, bytes32 s)
+ {
+ // The signature format is a compact form of:
+ // {bytes32 r}{bytes32 s}{uint8 v}
+ // Compact means, uint8 is not padded to 32 bytes.
+ assembly {
+ r := mload(add(_signature, 32))
+ s := mload(add(_signature, 64))
+ // Here we are loading the last 32 bytes, including 31 bytes
+ // of 's'. There is no 'mload8' to do this.
+ //
+ // 'byte' is not working due to the Solidity parser, so lets
+ // use the second best option, 'and'
+ v := and(mload(add(_signature, 65)), 0xff)
+ }
+
+ require(v == 27 || v == 28);
+ }
+
+ /**
+ * @notice Hash a hash with `"\x19Ethereum Signed Message:\n32"`
+ * @param _hash Sign to hash.
+ * @return signHash Hash to be signed.
+ */
+ function getSignHash(
+ bytes32 _hash
+ )
+ pure
+ public
+ returns (bytes32 signHash)
+ {
+ signHash = keccak256("\x19Ethereum Signed Message:\n32", _hash);
+ }
+
+}
\ No newline at end of file
From 770a6383243843906335f899e7f1a271ad348496 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 21 Mar 2018 19:58:19 -0300
Subject: [PATCH 075/102] natspec
---
contracts/identity/IdentityGasRelay.sol | 97 +++++++++++++++++++------
contracts/status/SNTController.sol | 3 +-
2 files changed, 78 insertions(+), 22 deletions(-)
diff --git a/contracts/identity/IdentityGasRelay.sol b/contracts/identity/IdentityGasRelay.sol
index 4fde2f7..07cb168 100644
--- a/contracts/identity/IdentityGasRelay.sol
+++ b/contracts/identity/IdentityGasRelay.sol
@@ -3,39 +3,56 @@ pragma solidity ^0.4.17;
import "./Identity.sol";
import "../token/ERC20Token.sol";
+/**
+ * @title IdentityGasRelay
+ * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
+ * @notice enables economic abstraction for Identity
+ */
contract IdentityGasRelay is Identity {
- bytes4 public constant EXECUTE_PREFIX = bytes4(keccak256("executeGasRelayed(address,uint256,bytes32,uint256,uint256,address)"));
+ bytes4 public constant CALL_PREFIX = bytes4(keccak256("callGasRelayed(address,uint256,bytes32,uint256,uint256,address)"));
event ExecutedGasRelayed(bytes32 signHash);
- function executeGasRelayed(
+ /**
+ * @notice include ethereum signed callHash in return of gas proportional amount multiplied by `_gasPrice` of `_gasToken`
+ * allows identity of being controlled without requiring ether in key balace
+ * @param _to destination of call
+ * @param _value call value (ether)
+ * @param _data call data
+ * @param _nonce current identity nonce
+ * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
+ * @param _gasMinimal minimal amount of gas needed to complete the execution
+ * @param _gasToken token being used for paying `msg.sender`
+ * @param _messageSignature rsv concatenated ethereum signed message signature
+ */
+ function callGasRelayed(
address _to,
uint256 _value,
bytes _data,
uint _nonce,
uint _gasPrice,
- uint _gasMinimum,
+ uint _gasMinimal,
address _gasToken,
bytes _messageSignature
)
external
{
uint startGas = gasleft();
- require(startGas > _gasMinimum);
+ require(startGas > _gasMinimal);
uint256 requiredKey = _to == address(this) ? MANAGEMENT_KEY : ACTION_KEY;
require(minimumApprovalsByKeyPurpose[requiredKey] == 1);
require(_nonce == nonce);
nonce++;
bytes32 _signedHash = getSignHash(
- executeGasRelayedHash(
+ callGasRelayedHash(
_to,
_value,
keccak256(_data),
_nonce,
_gasPrice,
- _gasMinimum,
+ _gasMinimal,
_gasToken
)
);
@@ -65,7 +82,19 @@ contract IdentityGasRelay is Identity {
}
}
- function executeGasRelayedMultiSigned(
+ /**
+ * @notice include ethereum signed callHash in return of gas proportional amount multiplied by `_gasPrice` of `_gasToken`
+ * allows identity of being controlled without requiring ether in key balace
+ * @param _to destination of call
+ * @param _value call value (ether)
+ * @param _data call data
+ * @param _nonce current identity nonce
+ * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
+ * @param _gasMinimal minimal amount of gas needed to complete the execution
+ * @param _gasToken token being used for paying `msg.sender`
+ * @param _messageSignatures rsv concatenated ethereum signed message signatures
+ */
+ function callGasRelayedMultiSigned(
address _to,
uint256 _value,
bytes _data,
@@ -81,7 +110,7 @@ contract IdentityGasRelay is Identity {
require(startGas > _gasMinimum);
require(_nonce == nonce);
nonce++;
- _executeGasRelayedMultiSigned(_to, _value, _data, _nonce, _gasPrice, _gasMinimum, _gasToken, _messageSignatures);
+ _callGasRelayedMultiSigned(_to, _value, _data, _nonce, _gasPrice, _gasMinimum, _gasToken, _messageSignatures);
if (_gasPrice > 0) {
payInclusionFee(
startGas - gasleft(),
@@ -92,7 +121,17 @@ contract IdentityGasRelay is Identity {
}
}
- function executeGasRelayedHash(
+ /**
+ * @notice get callHash
+ * @param _to destination of call
+ * @param _value call value (ether)
+ * @param _data call data
+ * @param _nonce current identity nonce
+ * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
+ * @param _gasMinimal minimal amount of gas needed to complete the execution
+ * @param _gasToken token being used for paying `msg.sender`
+ */
+ function callGasRelayedHash(
address _to,
uint256 _value,
bytes32 _dataHash,
@@ -103,11 +142,11 @@ contract IdentityGasRelay is Identity {
)
public
view
- returns (bytes32)
+ returns (bytes32 callHash)
{
- return keccak256(
+ callHash = keccak256(
address(this),
- EXECUTE_PREFIX,
+ CALL_PREFIX,
_to,
_value,
_dataHash,
@@ -117,7 +156,12 @@ contract IdentityGasRelay is Identity {
_gasToken
);
}
-
+ /**
+ * @notice recovers address who signed the message
+ * @param _signHash operation ethereum signed message hash
+ * @param _messageSignature message `_signHash` signature
+ * @param _pos which signature to read
+ */
function recoverKey (
bytes32 _signHash,
bytes _messageSignature,
@@ -142,9 +186,11 @@ contract IdentityGasRelay is Identity {
}
/**
- * @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`
+ * @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`
+ * @param _pos which signature to read
+ * @param _signatures concatenated vrs signatures
*/
- function signatureSplit(bytes _signature, uint256 _pos)
+ function signatureSplit(bytes _signatures, uint256 _pos)
pure
public
returns (uint8 v, bytes32 r, bytes32 s)
@@ -154,14 +200,14 @@ contract IdentityGasRelay is Identity {
// {bytes32 r}{bytes32 s}{uint8 v}
// Compact means, uint8 is not padded to 32 bytes.
assembly {
- r := mload(add(_signature, mul(32,pos)))
- s := mload(add(_signature, mul(64,pos)))
+ r := mload(add(_signatures, mul(32,pos)))
+ s := mload(add(_signatures, mul(64,pos)))
// Here we are loading the last 32 bytes, including 31 bytes
// of 's'. There is no 'mload8' to do this.
//
// 'byte' is not working due to the Solidity parser, so lets
// use the second best option, 'and'
- v := and(mload(add(_signature, mul(65,pos))), 0xff)
+ v := and(mload(add(_signatures, mul(65,pos))), 0xff)
}
require(v == 27 || v == 28);
@@ -182,8 +228,10 @@ contract IdentityGasRelay is Identity {
signHash = keccak256("\x19Ethereum Signed Message:\n32", _hash);
}
-
- function _executeGasRelayedMultiSigned(
+ /**
+ * @dev needed function to avoid "too much variables, stack too deep"
+ */
+ function _callGasRelayedMultiSigned(
address _to,
uint256 _value,
bytes _data,
@@ -200,7 +248,7 @@ contract IdentityGasRelay is Identity {
require(len == minimumApprovalsByKeyPurpose[requiredKey]);
bytes32 _signedHash = getSignHash(
- executeGasRelayedHash(
+ callGasRelayedHash(
_to,
_value,
keccak256(_data),
@@ -228,6 +276,13 @@ contract IdentityGasRelay is Identity {
}
}
+ /**
+ * @dev performs the gas payment in the selected token
+ * @param _gasUsed the amount of gas used
+ * @param _gasPrice selected gas price
+ * @param _msgIncluder address who included the message
+ * @param _gasToken ERC20Token to transfer, or if 0x0 uses ether in balance.
+ */
function payInclusionFee(
uint256 _gasUsed,
uint256 _gasPrice,
diff --git a/contracts/status/SNTController.sol b/contracts/status/SNTController.sol
index 3af76ca..f65e7ab 100644
--- a/contracts/status/SNTController.sol
+++ b/contracts/status/SNTController.sol
@@ -7,7 +7,8 @@ import "../token/MiniMeToken.sol";
/**
* @title SNTController
- * @author Ricardo Guilherme Schmidt
+ * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
+ * @notice enables economic abstraction for SNT
*/
contract SNTController is TokenController, Owned {
From 3037d4310917933003acb6d63e30643a8e4840fe Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 21 Mar 2018 22:10:41 -0300
Subject: [PATCH 076/102] standarize to _gasMinimal var name, and _gasMinimal
>= startGas
---
contracts/identity/IdentityGasRelay.sol | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/contracts/identity/IdentityGasRelay.sol b/contracts/identity/IdentityGasRelay.sol
index 07cb168..6b8068b 100644
--- a/contracts/identity/IdentityGasRelay.sol
+++ b/contracts/identity/IdentityGasRelay.sol
@@ -39,7 +39,7 @@ contract IdentityGasRelay is Identity {
external
{
uint startGas = gasleft();
- require(startGas > _gasMinimal);
+ require(startGas >= _gasMinimal);
uint256 requiredKey = _to == address(this) ? MANAGEMENT_KEY : ACTION_KEY;
require(minimumApprovalsByKeyPurpose[requiredKey] == 1);
require(_nonce == nonce);
@@ -100,17 +100,17 @@ contract IdentityGasRelay is Identity {
bytes _data,
uint _nonce,
uint _gasPrice,
- uint _gasMinimum,
+ uint _gasMinimal,
address _gasToken,
bytes _messageSignatures
)
external
{
uint startGas = gasleft();
- require(startGas > _gasMinimum);
+ require(startGas >= _gasMinimal);
require(_nonce == nonce);
nonce++;
- _callGasRelayedMultiSigned(_to, _value, _data, _nonce, _gasPrice, _gasMinimum, _gasToken, _messageSignatures);
+ _callGasRelayedMultiSigned(_to, _value, _data, _nonce, _gasPrice, _gasMinimal, _gasToken, _messageSignatures);
if (_gasPrice > 0) {
payInclusionFee(
startGas - gasleft(),
@@ -137,7 +137,7 @@ contract IdentityGasRelay is Identity {
bytes32 _dataHash,
uint _nonce,
uint256 _gasPrice,
- uint256 _gasMinimum,
+ uint256 _gasMinimal,
address _gasToken
)
public
@@ -152,7 +152,7 @@ contract IdentityGasRelay is Identity {
_dataHash,
_nonce,
_gasPrice,
- _gasMinimum,
+ _gasMinimal,
_gasToken
);
}
@@ -237,7 +237,7 @@ contract IdentityGasRelay is Identity {
bytes _data,
uint _nonce,
uint _gasPrice,
- uint _gasMinimum,
+ uint _gasMinimal,
address _gasToken,
bytes _messageSignatures
)
@@ -254,7 +254,7 @@ contract IdentityGasRelay is Identity {
keccak256(_data),
_nonce,
_gasPrice,
- _gasMinimum,
+ _gasMinimal,
_gasToken
)
);
From 39fcf1a05537d2faf20a196ea93db9fbbca86d32 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 21 Mar 2018 22:13:51 -0300
Subject: [PATCH 077/102] fix natspec
---
contracts/identity/IdentityGasRelay.sol | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contracts/identity/IdentityGasRelay.sol b/contracts/identity/IdentityGasRelay.sol
index 6b8068b..8610d12 100644
--- a/contracts/identity/IdentityGasRelay.sol
+++ b/contracts/identity/IdentityGasRelay.sol
@@ -125,7 +125,7 @@ contract IdentityGasRelay is Identity {
* @notice get callHash
* @param _to destination of call
* @param _value call value (ether)
- * @param _data call data
+ * @param _dataHash call data hash
* @param _nonce current identity nonce
* @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
* @param _gasMinimal minimal amount of gas needed to complete the execution
From 905aff648576af17a4287c48f503ea8d51b0b52e Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 21 Mar 2018 23:30:54 -0300
Subject: [PATCH 078/102] (save gas) bypass "transaction sstore" if multisigned
not required
---
contracts/identity/Identity.sol | 49 +++++++++++++++++++++------------
1 file changed, 32 insertions(+), 17 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index fe2fb63..36eacab 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -34,13 +34,18 @@ contract Identity is ERC725, ERC735 {
_;
}
- modifier selfOnly {
- require(
- msg.sender == address(this)
- );
- _;
+ modifier managementOnly {
+ if(msg.sender == address(this)) {
+ _;
+ } else {
+ require(isKeyPurpose(bytes32(msg.sender), MANAGEMENT_KEY));
+ if (minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] == 1) {
+ _;
+ } else {
+ execute(address(this), 0, msg.data);
+ }
+ }
}
-
modifier recoveryOnly {
require(
recoveryContract != address(0) &&
@@ -135,7 +140,7 @@ contract Identity is ERC725, ERC735 {
uint256 _type
)
public
- selfOnly
+ managementOnly
returns (bool success)
{
_addKey(_key, _purpose, _type);
@@ -148,7 +153,7 @@ contract Identity is ERC725, ERC735 {
uint256 _newType
)
public
- selfOnly
+ managementOnly
returns (bool success)
{
uint256 purpose = keys[_oldKey].purpose;
@@ -162,7 +167,7 @@ contract Identity is ERC725, ERC735 {
uint256 _purpose
)
public
- selfOnly
+ managementOnly
returns (bool success)
{
_removeKey(_key, _purpose);
@@ -175,11 +180,20 @@ contract Identity is ERC725, ERC735 {
bytes _data
)
public
- managerOrActor(bytes32(msg.sender))
returns (uint256 executionId)
{
- executionId = _execute(_to, _value, _data);
- approve(executionId, true);
+ uint256 requiredKey = _to == address(this) ? MANAGEMENT_KEY : ACTION_KEY;
+ if (minimumApprovalsByKeyPurpose[requiredKey] == 1) {
+ executionId = nonce;
+ nonce++;
+ require(isKeyPurpose(bytes32(msg.sender), requiredKey));
+ _to.call.value(_value)(_data);
+ emit Executed(executionId, _to, _value, _data);
+ } else {
+ executionId = _execute(_to, _value, _data);
+ approve(executionId, true);
+ }
+
}
function approve(uint256 _id, bool _approval)
@@ -195,7 +209,7 @@ contract Identity is ERC725, ERC735 {
uint256 _minimumApprovals
)
public
- selfOnly
+ managementOnly
{
require(_minimumApprovals > 0);
require(_minimumApprovals <= keysByPurpose[_purpose].length);
@@ -412,7 +426,7 @@ contract Identity is ERC725, ERC735 {
function setupRecovery(address _recoveryContract)
public
- selfOnly
+ managementOnly
{
require(recoveryContract == address(0));
recoveryContract = _recoveryContract;
@@ -455,7 +469,7 @@ contract Identity is ERC725, ERC735 {
bool _approval
)
private
- returns(bool success)
+ returns(bool success) //(?) should return approved instead of success?
{
Transaction storage trx = txx[_id];
@@ -476,10 +490,11 @@ contract Identity is ERC725, ERC735 {
requiredKeyPurpose = ACTION_KEY;
approvalCount = _calculateApprovals(actorKeyHash, _approval, trx);
}
-
+
if (approvalCount >= minimumApprovalsByKeyPurpose[requiredKeyPurpose]) {
- emit Executed(_id, trx.to, trx.value, trx.data);
+ //(?) success should be included in event?
success = trx.to.call.value(trx.value)(trx.data);
+ emit Executed(_id, trx.to, trx.value, trx.data);
}
}
From 7b179ec04cf9ec73b7e2772d074dc6b079de7a9c Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 21 Mar 2018 23:32:56 -0300
Subject: [PATCH 079/102] more comments
---
contracts/identity/Identity.sol | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 36eacab..be7a6c1 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -184,11 +184,11 @@ contract Identity is ERC725, ERC735 {
{
uint256 requiredKey = _to == address(this) ? MANAGEMENT_KEY : ACTION_KEY;
if (minimumApprovalsByKeyPurpose[requiredKey] == 1) {
- executionId = nonce;
- nonce++;
+ executionId = nonce; //(?) useless in this case
+ nonce++; //(?) should increment
require(isKeyPurpose(bytes32(msg.sender), requiredKey));
- _to.call.value(_value)(_data);
- emit Executed(executionId, _to, _value, _data);
+ _to.call.value(_value)(_data); //(?) success not used
+ emit Executed(executionId, _to, _value, _data); //no information on success
} else {
executionId = _execute(_to, _value, _data);
approve(executionId, true);
From 789252ba97f763cca308a9ff64f784736f6c9103 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 21 Mar 2018 23:36:31 -0300
Subject: [PATCH 080/102] executed event always fire with executin result bool
---
contracts/identity/IdentityGasRelay.sol | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/contracts/identity/IdentityGasRelay.sol b/contracts/identity/IdentityGasRelay.sol
index 8610d12..814dc6d 100644
--- a/contracts/identity/IdentityGasRelay.sol
+++ b/contracts/identity/IdentityGasRelay.sol
@@ -12,7 +12,7 @@ contract IdentityGasRelay is Identity {
bytes4 public constant CALL_PREFIX = bytes4(keccak256("callGasRelayed(address,uint256,bytes32,uint256,uint256,address)"));
- event ExecutedGasRelayed(bytes32 signHash);
+ event ExecutedGasRelayed(bytes32 signHash, bool success);
/**
* @notice include ethereum signed callHash in return of gas proportional amount multiplied by `_gasPrice` of `_gasToken`
@@ -68,10 +68,9 @@ contract IdentityGasRelay is Identity {
)
);
- if (_to.call.value(_value)(_data)) {
- emit ExecutedGasRelayed(_signedHash);
- }
-
+ bool success = _to.call.value(_value)(_data);
+ emit ExecutedGasRelayed(_signedHash, success);
+
if(_gasPrice > 0) {
payInclusionFee(
startGas - gasleft(),
From 3bdba827c9bcef784af47e95165b4be1952662df Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Thu, 22 Mar 2018 13:09:02 -0300
Subject: [PATCH 081/102] move nonce check as first step (gas relayer pays this
gas)
---
contracts/identity/IdentityGasRelay.sol | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/contracts/identity/IdentityGasRelay.sol b/contracts/identity/IdentityGasRelay.sol
index 814dc6d..9dd2e2b 100644
--- a/contracts/identity/IdentityGasRelay.sol
+++ b/contracts/identity/IdentityGasRelay.sol
@@ -38,11 +38,11 @@ contract IdentityGasRelay is Identity {
)
external
{
+ require(_nonce == nonce);
uint startGas = gasleft();
require(startGas >= _gasMinimal);
uint256 requiredKey = _to == address(this) ? MANAGEMENT_KEY : ACTION_KEY;
require(minimumApprovalsByKeyPurpose[requiredKey] == 1);
- require(_nonce == nonce);
nonce++;
bytes32 _signedHash = getSignHash(
@@ -105,9 +105,9 @@ contract IdentityGasRelay is Identity {
)
external
{
+ require(_nonce == nonce);
uint startGas = gasleft();
require(startGas >= _gasMinimal);
- require(_nonce == nonce);
nonce++;
_callGasRelayedMultiSigned(_to, _value, _data, _nonce, _gasPrice, _gasMinimal, _gasToken, _messageSignatures);
if (_gasPrice > 0) {
From 17b6b7357ce4052cf2bf8ae4d5e07a34f5ebae0b Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Thu, 22 Mar 2018 23:12:12 -0300
Subject: [PATCH 082/102] simplify contract for easier readibility
---
contracts/identity/IdentityGasRelay.sol | 260 +++++++++---------------
1 file changed, 92 insertions(+), 168 deletions(-)
diff --git a/contracts/identity/IdentityGasRelay.sol b/contracts/identity/IdentityGasRelay.sol
index 9dd2e2b..cd544d8 100644
--- a/contracts/identity/IdentityGasRelay.sol
+++ b/contracts/identity/IdentityGasRelay.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.17;
+pragma solidity ^0.4.21;
import "./Identity.sol";
import "../token/ERC20Token.sol";
@@ -10,77 +10,10 @@ import "../token/ERC20Token.sol";
*/
contract IdentityGasRelay is Identity {
- bytes4 public constant CALL_PREFIX = bytes4(keccak256("callGasRelayed(address,uint256,bytes32,uint256,uint256,address)"));
+ bytes4 public constant CALL_PREFIX = bytes4(keccak256("callGasRelay(address,uint256,bytes32,uint256,uint256,address)"));
event ExecutedGasRelayed(bytes32 signHash, bool success);
- /**
- * @notice include ethereum signed callHash in return of gas proportional amount multiplied by `_gasPrice` of `_gasToken`
- * allows identity of being controlled without requiring ether in key balace
- * @param _to destination of call
- * @param _value call value (ether)
- * @param _data call data
- * @param _nonce current identity nonce
- * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
- * @param _gasMinimal minimal amount of gas needed to complete the execution
- * @param _gasToken token being used for paying `msg.sender`
- * @param _messageSignature rsv concatenated ethereum signed message signature
- */
- function callGasRelayed(
- address _to,
- uint256 _value,
- bytes _data,
- uint _nonce,
- uint _gasPrice,
- uint _gasMinimal,
- address _gasToken,
- bytes _messageSignature
- )
- external
- {
- require(_nonce == nonce);
- uint startGas = gasleft();
- require(startGas >= _gasMinimal);
- uint256 requiredKey = _to == address(this) ? MANAGEMENT_KEY : ACTION_KEY;
- require(minimumApprovalsByKeyPurpose[requiredKey] == 1);
- nonce++;
-
- bytes32 _signedHash = getSignHash(
- callGasRelayedHash(
- _to,
- _value,
- keccak256(_data),
- _nonce,
- _gasPrice,
- _gasMinimal,
- _gasToken
- )
- );
-
- require(
- isKeyPurpose(
- recoverKey(
- _signedHash,
- _messageSignature,
- 0
- ),
- requiredKey
- )
- );
-
- bool success = _to.call.value(_value)(_data);
- emit ExecutedGasRelayed(_signedHash, success);
-
- if(_gasPrice > 0) {
- payInclusionFee(
- startGas - gasleft(),
- _gasPrice,
- msg.sender,
- _gasToken
- );
- }
- }
-
/**
* @notice include ethereum signed callHash in return of gas proportional amount multiplied by `_gasPrice` of `_gasToken`
* allows identity of being controlled without requiring ether in key balace
@@ -91,9 +24,9 @@ contract IdentityGasRelay is Identity {
* @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
* @param _gasMinimal minimal amount of gas needed to complete the execution
* @param _gasToken token being used for paying `msg.sender`
- * @param _messageSignatures rsv concatenated ethereum signed message signatures
+ * @param _messageSignatures rsv concatenated ethereum signed message signatures required
*/
- function callGasRelayedMultiSigned(
+ function callGasRelayed(
address _to,
uint256 _value,
bytes _data,
@@ -105,21 +38,81 @@ contract IdentityGasRelay is Identity {
)
external
{
+ //verify transaction parameters
require(_nonce == nonce);
uint startGas = gasleft();
require(startGas >= _gasMinimal);
- nonce++;
- _callGasRelayedMultiSigned(_to, _value, _data, _nonce, _gasPrice, _gasMinimal, _gasToken, _messageSignatures);
- if (_gasPrice > 0) {
- payInclusionFee(
- startGas - gasleft(),
+
+ // calculates signHash
+ bytes32 signHash = getSignHash(
+ callGasRelayHash(
+ _to,
+ _value,
+ keccak256(_data),
+ _nonce,
_gasPrice,
- msg.sender,
- _gasToken
- );
+ _gasMinimal,
+ _gasToken
+ )
+ );
+
+ //verify if signatures are valid and came from correct actors;
+ uint256 requiredKey = _to == address(this) ? MANAGEMENT_KEY : ACTION_KEY;
+ verifySignatures(requiredKey, signHash, _messageSignatures);
+
+ //executes transaction
+ nonce++;
+ emit ExecutedGasRelayed(
+ signHash,
+ _to.call.value(_value)(_data)
+ );
+
+ //refund gas used using contract held ERC20 tokens or ETH
+ if (_gasPrice > 0) {
+ uint256 _amount = 21000 + (startGas - gasleft());
+ _amount = _amount * _gasPrice;
+ if (_gasToken == address(0)) {
+ address(msg.sender).transfer(_amount);
+ } else {
+ ERC20Token(_gasToken).transfer(msg.sender, _amount);
+ }
}
}
+
+ /**
+ * @notice reverts if signatures are not valid for the signed hash and required key type.
+ * @param _requiredKey key required to call, if _to from payload is the identity itself, is `MANAGEMENT_KEY`, else `ACTION_KEY`
+ * @param _signHash ethereum signable callGasRelayHash message provided for the payload
+ * @param _messageSignatures ethereum signed `_signHash` messages
+ * @return true case valid
+ */
+ function verifySignatures(
+ uint256 _requiredKey,
+ bytes32 _signHash,
+ bytes _messageSignatures
+ )
+ public
+ view
+ returns(bool)
+ {
+ uint _amountSignatures = _messageSignatures.length / 72;
+ require(_amountSignatures == minimumApprovalsByKeyPurpose[_requiredKey]);
+ bytes32 _lastKey = 0;
+ for (uint256 i = 0; i < _amountSignatures; i++) {
+ bytes32 _currentKey = recoverKey(
+ _signHash,
+ _messageSignatures,
+ i
+ );
+ require(_currentKey > _lastKey); //assert keys are different
+ require(isKeyPurpose(_currentKey, _requiredKey));
+ _lastKey = _currentKey;
+ }
+ return true;
+ }
+
+
/**
* @notice get callHash
* @param _to destination of call
@@ -129,8 +122,9 @@ contract IdentityGasRelay is Identity {
* @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
* @param _gasMinimal minimal amount of gas needed to complete the execution
* @param _gasToken token being used for paying `msg.sender`
+ * @return callGasRelayHash the hash to be signed by wallet
*/
- function callGasRelayedHash(
+ function callGasRelayHash(
address _to,
uint256 _value,
bytes32 _dataHash,
@@ -141,9 +135,9 @@ contract IdentityGasRelay is Identity {
)
public
view
- returns (bytes32 callHash)
+ returns (bytes32 _callGasRelayHash)
{
- callHash = keccak256(
+ _callGasRelayHash = keccak256(
address(this),
CALL_PREFIX,
_to,
@@ -155,6 +149,22 @@ contract IdentityGasRelay is Identity {
_gasToken
);
}
+
+ /**
+ * @notice Hash a hash with `"\x19Ethereum Signed Message:\n32"`
+ * @param _hash Sign to hash.
+ * @return signHash Hash ethereum wallet signs.
+ */
+ function getSignHash(
+ bytes32 _hash
+ )
+ pure
+ public
+ returns(bytes32 signHash)
+ {
+ signHash = keccak256("\x19Ethereum Signed Message:\n32", _hash);
+ }
+
/**
* @notice recovers address who signed the message
* @param _signHash operation ethereum signed message hash
@@ -212,90 +222,4 @@ contract IdentityGasRelay is Identity {
require(v == 27 || v == 28);
}
- /**
- * @notice Hash a hash with `"\x19Ethereum Signed Message:\n32"`
- * @param _hash Sign to hash.
- * @return signHash Hash to be signed.
- */
- function getSignHash(
- bytes32 _hash
- )
- pure
- public
- returns(bytes32 signHash)
- {
- signHash = keccak256("\x19Ethereum Signed Message:\n32", _hash);
- }
-
- /**
- * @dev needed function to avoid "too much variables, stack too deep"
- */
- function _callGasRelayedMultiSigned(
- address _to,
- uint256 _value,
- bytes _data,
- uint _nonce,
- uint _gasPrice,
- uint _gasMinimal,
- address _gasToken,
- bytes _messageSignatures
- )
- private
- {
- uint256 requiredKey = _to == address(this) ? MANAGEMENT_KEY : ACTION_KEY;
- uint256 len = _messageSignatures.length / 72;
- require(len == minimumApprovalsByKeyPurpose[requiredKey]);
-
- bytes32 _signedHash = getSignHash(
- callGasRelayedHash(
- _to,
- _value,
- keccak256(_data),
- _nonce,
- _gasPrice,
- _gasMinimal,
- _gasToken
- )
- );
-
- bytes32 _lastKey = 0;
- for (uint256 i = 0; i < len; i++) {
- bytes32 _key = recoverKey(
- _signedHash,
- _messageSignatures,
- i
- );
- require(_key > _lastKey); //assert keys are different
- require(isKeyPurpose(_key, requiredKey));
- _lastKey = _key;
- }
-
- if (_to.call.value(_value)(_data)) {
- emit ExecutedGasRelayed(_signedHash);
- }
- }
-
- /**
- * @dev performs the gas payment in the selected token
- * @param _gasUsed the amount of gas used
- * @param _gasPrice selected gas price
- * @param _msgIncluder address who included the message
- * @param _gasToken ERC20Token to transfer, or if 0x0 uses ether in balance.
- */
- function payInclusionFee(
- uint256 _gasUsed,
- uint256 _gasPrice,
- address _msgIncluder,
- address _gasToken
- )
- private
- {
- uint256 _amount = (21000 + _gasUsed) * _gasPrice;
- if (_gasToken == address(0)) {
- address(_msgIncluder).transfer(_amount);
- } else {
- ERC20Token(_gasToken).transfer(_msgIncluder, _amount);
- }
- }
-
}
\ No newline at end of file
From fe4a10dfcded78901cdf305d3647a218f9f4b378 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Thu, 22 Mar 2018 23:31:20 -0300
Subject: [PATCH 083/102] makes "call" more explicit
---
contracts/identity/IdentityGasRelay.sol | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/contracts/identity/IdentityGasRelay.sol b/contracts/identity/IdentityGasRelay.sol
index cd544d8..5254cee 100644
--- a/contracts/identity/IdentityGasRelay.sol
+++ b/contracts/identity/IdentityGasRelay.sol
@@ -57,16 +57,20 @@ contract IdentityGasRelay is Identity {
);
//verify if signatures are valid and came from correct actors;
- uint256 requiredKey = _to == address(this) ? MANAGEMENT_KEY : ACTION_KEY;
- verifySignatures(requiredKey, signHash, _messageSignatures);
+ verifySignatures(
+ _to == address(this) ? MANAGEMENT_KEY : ACTION_KEY,
+ signHash,
+ _messageSignatures
+ );
//executes transaction
nonce++;
+ bool success = _to.call.value(_value)(_data);
emit ExecutedGasRelayed(
signHash,
- _to.call.value(_value)(_data)
+ success
);
-
+
//refund gas used using contract held ERC20 tokens or ETH
if (_gasPrice > 0) {
uint256 _amount = 21000 + (startGas - gasleft());
From 4c5cb43da05fe94ed83f0700108fa7e2cf89e4f4 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Sat, 24 Mar 2018 01:26:06 -0300
Subject: [PATCH 084/102] added approve and call gas relayed for ERC20
---
contracts/identity/IdentityGasRelay.sol | 138 +++++++++++++++++++++++-
1 file changed, 137 insertions(+), 1 deletion(-)
diff --git a/contracts/identity/IdentityGasRelay.sol b/contracts/identity/IdentityGasRelay.sol
index 5254cee..2651282 100644
--- a/contracts/identity/IdentityGasRelay.sol
+++ b/contracts/identity/IdentityGasRelay.sol
@@ -11,11 +11,12 @@ import "../token/ERC20Token.sol";
contract IdentityGasRelay is Identity {
bytes4 public constant CALL_PREFIX = bytes4(keccak256("callGasRelay(address,uint256,bytes32,uint256,uint256,address)"));
+ bytes4 public constant APPROVEANDCALL_PREFIX = bytes4(keccak256("approveAndCallGasRelay(address,address,uint256,bytes32,uint256,uint256)"));
event ExecutedGasRelayed(bytes32 signHash, bool success);
/**
- * @notice include ethereum signed callHash in return of gas proportional amount multiplied by `_gasPrice` of `_gasToken`
+ * @notice include ethereum signed callHash in return of gas proportional amount multiplied by `_gasPrice` of `_gasToken`
* allows identity of being controlled without requiring ether in key balace
* @param _to destination of call
* @param _value call value (ether)
@@ -83,6 +84,82 @@ contract IdentityGasRelay is Identity {
}
}
+ /**
+ * @notice include ethereum signed approve ERC20 and call hash
+ * (`ERC20Token(baseToken).approve(_to, _value)` + `_to.call(_data)`).
+ * in return of gas proportional amount multiplied by `_gasPrice` of `_gasToken`
+ * fixes race condition in double transaction for ERC20.
+ * @param _baseToken token approved for `_to`
+ * @param _to destination of call
+ * @param _value call value (ether)
+ * @param _data call data
+ * @param _nonce current identity nonce
+ * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
+ * @param _gasMinimal minimal amount of gas needed to complete the execution
+ * @param _gasToken token being used for paying `msg.sender`
+ * @param _messageSignatures rsv concatenated ethereum signed message signatures required
+ */
+ function approveAndCallGasRelayed(
+ address _baseToken,
+ address _to,
+ uint256 _value,
+ bytes _data,
+ uint _nonce,
+ uint _gasPrice,
+ uint _gasMinimal,
+ address _gasToken,
+ bytes _messageSignatures
+ )
+ external
+ {
+ //verify transaction parameters
+ require(_nonce == nonce);
+ uint startGas = gasleft();
+ require(startGas >= _gasMinimal);
+ require(_baseToken != address(0)); //_baseToken should be something!
+ require(_to != address(this)); //no management with approveAndCall
+
+ // calculates signHash
+ bytes32 signHash = getSignHash(
+ approveAndCallGasRelayHash(
+ _baseToken,
+ _to,
+ _value,
+ keccak256(_data),
+ _nonce,
+ _gasPrice,
+ _gasMinimal,
+ _gasToken
+ )
+ );
+
+ //verify if signatures are valid and came from correct actors;
+ verifySignatures(
+ ACTION_KEY, //no management with approveAndCall
+ signHash,
+ _messageSignatures
+ );
+
+ approveAndCall(
+ signHash,
+ _baseToken,
+ _to,
+ _value,
+ _data
+ );
+
+ //refund gas used using contract held ERC20 tokens or ETH
+ if (_gasPrice > 0) {
+ uint256 _amount = 21000 + (startGas - gasleft());
+ _amount = _amount * _gasPrice;
+ if (_gasToken == address(0)) {
+ address(msg.sender).transfer(_amount);
+ } else {
+ ERC20Token(_gasToken).transfer(msg.sender, _amount);
+ }
+ }
+
+ }
/**
* @notice reverts if signatures are not valid for the signed hash and required key type.
@@ -154,6 +231,46 @@ contract IdentityGasRelay is Identity {
);
}
+
+ /**
+ * @notice get callHash
+ * @param _to destination of call
+ * @param _value call value (ether)
+ * @param _dataHash call data hash
+ * @param _nonce current identity nonce
+ * @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
+ * @param _gasMinimal minimal amount of gas needed to complete the execution
+ * @param _gasToken token being used for paying `msg.sender`
+ * @return callGasRelayHash the hash to be signed by wallet
+ */
+ function approveAndCallGasRelayHash(
+ address _baseToken,
+ address _to,
+ uint256 _value,
+ bytes32 _dataHash,
+ uint _nonce,
+ uint256 _gasPrice,
+ uint256 _gasMinimal,
+ address _gasToken
+ )
+ public
+ view
+ returns (bytes32 _callGasRelayHash)
+ {
+ _callGasRelayHash = keccak256(
+ address(this),
+ APPROVEANDCALL_PREFIX,
+ _baseToken,
+ _to,
+ _value,
+ _dataHash,
+ _nonce,
+ _gasPrice,
+ _gasMinimal,
+ _gasToken
+ );
+ }
+
/**
* @notice Hash a hash with `"\x19Ethereum Signed Message:\n32"`
* @param _hash Sign to hash.
@@ -226,4 +343,23 @@ contract IdentityGasRelay is Identity {
require(v == 27 || v == 28);
}
+ function approveAndCall(
+ bytes32 _signHash,
+ address _token,
+ address _to,
+ uint256 _value,
+ bytes _data
+ )
+ private
+ {
+ //executes transaction
+ nonce++;
+ ERC20Token(_token).approve(_to, _value);
+ emit ExecutedGasRelayed(
+ _signHash,
+ _to.call(_data)
+ );
+
+ }
+
}
\ No newline at end of file
From 334f6754af2cf44800cb424f3a2739c5eeedb6ee Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Sat, 24 Mar 2018 05:16:54 -0300
Subject: [PATCH 085/102] move events and nonce increment
---
contracts/identity/Identity.sol | 38 ++++++++++++++++-----------------
1 file changed, 19 insertions(+), 19 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index be7a6c1..88f4edd 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -452,7 +452,7 @@ contract Identity is ERC725, ERC735 {
returns (uint256 executionId)
{
executionId = nonce;
- emit ExecutionRequested(executionId, _to, _value, _data);
+ nonce++;
txx[executionId] = Transaction({
to: _to,
value: _value,
@@ -460,7 +460,7 @@ contract Identity is ERC725, ERC735 {
nonce: nonce,
approverCount: 0
});
- nonce++;
+ emit ExecutionRequested(executionId, _to, _value, _data);
}
function _approve(
@@ -476,8 +476,6 @@ contract Identity is ERC725, ERC735 {
uint256 approvalCount;
uint256 requiredKeyPurpose;
-
- emit Approved(_id, _approval);
if (trx.to == address(this)) {
require(isKeyPurpose(_key, MANAGEMENT_KEY));
@@ -490,7 +488,9 @@ contract Identity is ERC725, ERC735 {
requiredKeyPurpose = ACTION_KEY;
approvalCount = _calculateApprovals(actorKeyHash, _approval, trx);
}
-
+
+ emit Approved(_id, _approval);
+
if (approvalCount >= minimumApprovalsByKeyPurpose[requiredKeyPurpose]) {
//(?) success should be included in event?
success = trx.to.call.value(trx.value)(trx.data);
@@ -513,10 +513,10 @@ contract Identity is ERC725, ERC735 {
_purpose == ACTION_KEY ||
_purpose == CLAIM_SIGNER_KEY ||
_purpose == ENCRYPTION_KEY
- );
- emit KeyAdded(_key, _purpose, _type);
+ );
keys[keyHash] = Key(_purpose, _type, _key);
indexes[keyHash] = keysByPurpose[_purpose].push(_key) - 1;
+ emit KeyAdded(_key, _purpose, _type);
}
function _removeKey(
@@ -526,9 +526,8 @@ contract Identity is ERC725, ERC735 {
private
{
bytes32 keyHash = keccak256(_key, _purpose);
- Key storage myKey = keys[keyHash];
- emit KeyRemoved(myKey.key, myKey.purpose, myKey.keyType);
-
+ Key memory myKey = keys[keyHash];
+
uint index = indexes[keyHash];
delete indexes[keyHash];
bytes32 replacer = keysByPurpose[_purpose][keysByPurpose[_purpose].length - 1];
@@ -545,6 +544,7 @@ contract Identity is ERC725, ERC735 {
}
delete keys[keyHash];
+ emit KeyRemoved(myKey.key, myKey.purpose, myKey.keyType);
}
function _calculateApprovals(
@@ -615,15 +615,6 @@ contract Identity is ERC725, ERC735 {
private
{
require(msg.sender == _issuer);
- emit ClaimChanged(
- _claimHash,
- _claimType,
- _scheme,
- _issuer,
- _signature,
- _data,
- _uri
- );
claims[_claimHash] = Claim({
claimType: _claimType,
scheme: _scheme,
@@ -632,6 +623,15 @@ contract Identity is ERC725, ERC735 {
data: _data,
uri: _uri
});
+ emit ClaimChanged(
+ _claimHash,
+ _claimType,
+ _scheme,
+ _issuer,
+ _signature,
+ _data,
+ _uri
+ );
}
From 8a32a3ea47ae8e78c9825216ece4d02d2fba4dce Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Sat, 24 Mar 2018 05:30:07 -0300
Subject: [PATCH 086/102] reorder _removeKey logic
---
contracts/identity/Identity.sol | 32 +++++++++++++++-----------------
1 file changed, 15 insertions(+), 17 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 88f4edd..f4f6c9d 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -7,9 +7,10 @@ import "./ERC735.sol";
contract Identity is ERC725, ERC735 {
mapping (bytes32 => Key) keys;
- mapping (bytes32 => Claim) claims;
mapping (uint256 => bytes32[]) keysByPurpose;
+ mapping (bytes32 => Claim) claims;
mapping (uint256 => bytes32[]) claimsByType;
+
mapping (bytes32 => uint256) indexes;
mapping (uint => Transaction) txx;
mapping (uint256 => uint256) minimumApprovalsByKeyPurpose;
@@ -490,7 +491,7 @@ contract Identity is ERC725, ERC735 {
}
emit Approved(_id, _approval);
-
+
if (approvalCount >= minimumApprovalsByKeyPurpose[requiredKeyPurpose]) {
//(?) success should be included in event?
success = trx.to.call.value(trx.value)(trx.data);
@@ -525,25 +526,22 @@ contract Identity is ERC725, ERC735 {
)
private
{
- bytes32 keyHash = keccak256(_key, _purpose);
- Key memory myKey = keys[keyHash];
-
- uint index = indexes[keyHash];
- delete indexes[keyHash];
- bytes32 replacer = keysByPurpose[_purpose][keysByPurpose[_purpose].length - 1];
- keysByPurpose[_purpose][index] = replacer;
- indexes[keccak256(replacer, _purpose)] = index;
- keysByPurpose[_purpose].length--;
-
if (_purpose == MANAGEMENT_KEY) {
- require(
- keysByPurpose[MANAGEMENT_KEY].length >= 1 &&
- keysByPurpose[MANAGEMENT_KEY].length >= minimumApprovalsByKeyPurpose[MANAGEMENT_KEY]
- );
-
+ require(keysByPurpose[MANAGEMENT_KEY].length > minimumApprovalsByKeyPurpose[MANAGEMENT_KEY]);
}
+ bytes32 keyHash = keccak256(_key, _purpose);
+ Key memory myKey = keys[keyHash];
+ uint index = indexes[keyHash];
+ bytes32 indexReplacer = keysByPurpose[_purpose][keysByPurpose[_purpose].length - 1];
+
+ keysByPurpose[_purpose][index] = indexReplacer;
+ indexes[keccak256(indexReplacer, _purpose)] = index;
+ keysByPurpose[_purpose].length--;
+
+ delete indexes[keyHash];
delete keys[keyHash];
+
emit KeyRemoved(myKey.key, myKey.purpose, myKey.keyType);
}
From cfb389398d9dfbc91fafffc53bfe356c746b7d72 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Sat, 24 Mar 2018 05:53:35 -0300
Subject: [PATCH 087/102] fix reentrancy and reexecution
---
contracts/identity/Identity.sol | 60 ++++++++++++---------------------
1 file changed, 21 insertions(+), 39 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index f4f6c9d..a60b613 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -14,12 +14,13 @@ contract Identity is ERC725, ERC735 {
mapping (bytes32 => uint256) indexes;
mapping (uint => Transaction) txx;
mapping (uint256 => uint256) minimumApprovalsByKeyPurpose;
- bytes32[] pendingTransactions;
+
uint nonce = 0;
address recoveryContract;
address recoveryManager;
struct Transaction {
+ bool valid;
address to;
uint value;
bytes data;
@@ -455,6 +456,7 @@ contract Identity is ERC725, ERC735 {
executionId = nonce;
nonce++;
txx[executionId] = Transaction({
+ valid: true,
to: _to,
value: _value,
data: _data,
@@ -473,29 +475,30 @@ contract Identity is ERC725, ERC735 {
returns(bool success) //(?) should return approved instead of success?
{
- Transaction storage trx = txx[_id];
+ Transaction memory trx = txx[_id];
+ require(trx.valid);
+ uint256 requiredKeyPurpose = trx.to == address(this) ? MANAGEMENT_KEY : ACTION_KEY;
+ require(isKeyPurpose(_key, requiredKeyPurpose));
+ bytes32 keyHash = keccak256(_key, requiredKeyPurpose);
+ require(txx[_id].approvals[keyHash] != _approval);
- uint256 approvalCount;
- uint256 requiredKeyPurpose;
-
- if (trx.to == address(this)) {
- require(isKeyPurpose(_key, MANAGEMENT_KEY));
- bytes32 managerKeyHash = keccak256(_key, MANAGEMENT_KEY);
- requiredKeyPurpose = MANAGEMENT_KEY;
- approvalCount = _calculateApprovals(managerKeyHash, _approval, trx);
+ if (_approval) {
+ trx.approverCount++;
} else {
- require(isKeyPurpose(_key, ACTION_KEY));
- bytes32 actorKeyHash = keccak256(_key, ACTION_KEY);
- requiredKeyPurpose = ACTION_KEY;
- approvalCount = _calculateApprovals(actorKeyHash, _approval, trx);
+ trx.approverCount--;
}
-
+
emit Approved(_id, _approval);
- if (approvalCount >= minimumApprovalsByKeyPurpose[requiredKeyPurpose]) {
+ if (trx.approverCount < minimumApprovalsByKeyPurpose[requiredKeyPurpose]) {
+ txx[_id].approvals[keyHash] = _approval;
+ txx[_id] = trx;
+ } else {
+ delete txx[_id];
//(?) success should be included in event?
- success = trx.to.call.value(trx.value)(trx.data);
+ success = address(trx.to).call.value(trx.value)(trx.data);
emit Executed(_id, trx.to, trx.value, trx.data);
+
}
}
@@ -541,31 +544,10 @@ contract Identity is ERC725, ERC735 {
delete indexes[keyHash];
delete keys[keyHash];
-
+
emit KeyRemoved(myKey.key, myKey.purpose, myKey.keyType);
}
- function _calculateApprovals(
- bytes32 _keyHash,
- bool _approval,
- Transaction storage trx
- )
- private
- returns (uint256 approvalCount)
- {
- require(trx.approvals[_keyHash] != _approval);
-
- trx.approvals[_keyHash] = _approval;
- if (_approval) {
- trx.approverCount++;
- } else {
- trx.approverCount--;
- }
-
- return trx.approverCount;
- }
-
-
function _includeClaim(
bytes32 _claimHash,
uint256 _claimType,
From a271b0053923c7b5bb319e261bbe79c9ff70b5db Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Sat, 24 Mar 2018 05:58:04 -0300
Subject: [PATCH 088/102] normalize
---
contracts/identity/Identity.sol | 40 ++++++++++++++++-----------------
1 file changed, 20 insertions(+), 20 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index a60b613..dcad765 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -12,20 +12,20 @@ contract Identity is ERC725, ERC735 {
mapping (uint256 => bytes32[]) claimsByType;
mapping (bytes32 => uint256) indexes;
- mapping (uint => Transaction) txx;
- mapping (uint256 => uint256) minimumApprovalsByKeyPurpose;
+ mapping (uint256 => Transaction) txx;
+ mapping (uint256 => uint256) purposeThreshold;
- uint nonce = 0;
+ uint256 nonce;
address recoveryContract;
address recoveryManager;
struct Transaction {
bool valid;
address to;
- uint value;
+ uint256 value;
bytes data;
- uint nonce;
- uint approverCount;
+ uint256 nonce;
+ uint256 approverCount;
mapping(bytes32 => bool) approvals;
}
@@ -41,7 +41,7 @@ contract Identity is ERC725, ERC735 {
_;
} else {
require(isKeyPurpose(bytes32(msg.sender), MANAGEMENT_KEY));
- if (minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] == 1) {
+ if (purposeThreshold[MANAGEMENT_KEY] == 1) {
_;
} else {
execute(address(this), 0, msg.data);
@@ -106,14 +106,14 @@ contract Identity is ERC725, ERC735 {
{
recoveryManager = _newKey;
_addKey(bytes32(recoveryManager), MANAGEMENT_KEY, 0);
- minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] = keysByPurpose[MANAGEMENT_KEY].length;
+ purposeThreshold[MANAGEMENT_KEY] = keysByPurpose[MANAGEMENT_KEY].length;
}
function processManagerReset(uint256 _limit)
public
{
require(recoveryManager != address(0));
- uint limit = _limit;
+ uint256 limit = _limit;
bytes32 newKey = bytes32(recoveryManager);
bytes32[] memory managers = keysByPurpose[MANAGEMENT_KEY];
uint256 totalManagers = managers.length;
@@ -122,7 +122,7 @@ contract Identity is ERC725, ERC735 {
limit = totalManagers;
}
- minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] = totalManagers - limit + 1;
+ purposeThreshold[MANAGEMENT_KEY] = totalManagers - limit + 1;
for (uint256 i = 0; i < limit; i++) {
bytes32 manager = managers[i];
if (manager != newKey) {
@@ -185,7 +185,7 @@ contract Identity is ERC725, ERC735 {
returns (uint256 executionId)
{
uint256 requiredKey = _to == address(this) ? MANAGEMENT_KEY : ACTION_KEY;
- if (minimumApprovalsByKeyPurpose[requiredKey] == 1) {
+ if (purposeThreshold[requiredKey] == 1) {
executionId = nonce; //(?) useless in this case
nonce++; //(?) should increment
require(isKeyPurpose(bytes32(msg.sender), requiredKey));
@@ -215,7 +215,7 @@ contract Identity is ERC725, ERC735 {
{
require(_minimumApprovals > 0);
require(_minimumApprovals <= keysByPurpose[_purpose].length);
- minimumApprovalsByKeyPurpose[_purpose] = _minimumApprovals;
+ purposeThreshold[_purpose] = _minimumApprovals;
}
@@ -267,7 +267,7 @@ contract Identity is ERC725, ERC735 {
// MUST only be done by the issuer of the claim, or KEYS OF PURPOSE 1, or the identity itself.
// TODO If its the identity itself, the approval process will determine its approval.
- uint claimIdTypePos = indexes[_claimId];
+ uint256 claimIdTypePos = indexes[_claimId];
delete indexes[_claimId];
bytes32[] storage claimsTypeArr = claimsByType[c.claimType];
bytes32 replacer = claimsTypeArr[claimsTypeArr.length-1];
@@ -398,7 +398,7 @@ contract Identity is ERC725, ERC735 {
address _to,
uint256 _value,
bytes _data,
- uint _nonce,
+ uint256 _nonce,
bytes32 _key,
uint8 _v,
bytes32 _r,
@@ -438,11 +438,11 @@ contract Identity is ERC725, ERC735 {
internal
{
require(keysByPurpose[MANAGEMENT_KEY].length == 0);
- require(minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] == 0);
+ require(purposeThreshold[MANAGEMENT_KEY] == 0);
_addKey(bytes32(_manager), MANAGEMENT_KEY, 0);
- minimumApprovalsByKeyPurpose[MANAGEMENT_KEY] = 1;
- minimumApprovalsByKeyPurpose[ACTION_KEY] = 1;
+ purposeThreshold[MANAGEMENT_KEY] = 1;
+ purposeThreshold[ACTION_KEY] = 1;
}
function _execute(
@@ -490,7 +490,7 @@ contract Identity is ERC725, ERC735 {
emit Approved(_id, _approval);
- if (trx.approverCount < minimumApprovalsByKeyPurpose[requiredKeyPurpose]) {
+ if (trx.approverCount < purposeThreshold[requiredKeyPurpose]) {
txx[_id].approvals[keyHash] = _approval;
txx[_id] = trx;
} else {
@@ -530,12 +530,12 @@ contract Identity is ERC725, ERC735 {
private
{
if (_purpose == MANAGEMENT_KEY) {
- require(keysByPurpose[MANAGEMENT_KEY].length > minimumApprovalsByKeyPurpose[MANAGEMENT_KEY]);
+ require(keysByPurpose[MANAGEMENT_KEY].length > purposeThreshold[MANAGEMENT_KEY]);
}
bytes32 keyHash = keccak256(_key, _purpose);
Key memory myKey = keys[keyHash];
- uint index = indexes[keyHash];
+ uint256 index = indexes[keyHash];
bytes32 indexReplacer = keysByPurpose[_purpose][keysByPurpose[_purpose].length - 1];
keysByPurpose[_purpose][index] = indexReplacer;
From 33bb06bd764a15821afe2cc183b979e9c55d92d1 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Sat, 24 Mar 2018 05:58:49 -0300
Subject: [PATCH 089/102] fix wrong var name
---
contracts/identity/Identity.sol | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index dcad765..ceea2a9 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -290,12 +290,12 @@ contract Identity is ERC725, ERC735 {
return (myKey.purpose, myKey.keyType, myKey.key);
}
- function isKeyPurpose(bytes32 _key, uint256 _type)
+ function isKeyPurpose(bytes32 _key, uint256 _purpose)
public
constant
returns (bool)
{
- return keys[keccak256(_key, _type)].purpose == _type;
+ return keys[keccak256(_key, _purpose)].purpose == _purpose;
}
function getKeyPurpose(bytes32 _key)
From 27d0fc24b68cc4c4c78784431fb869ce12de7e0b Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Tue, 27 Mar 2018 23:16:51 -0300
Subject: [PATCH 090/102] democracy , constitution and factory refactor
---
contracts/deploy/Factory.sol | 71 ++++++++++++++++++++++++++----------
1 file changed, 52 insertions(+), 19 deletions(-)
diff --git a/contracts/deploy/Factory.sol b/contracts/deploy/Factory.sol
index f63bf71..2408a58 100644
--- a/contracts/deploy/Factory.sol
+++ b/contracts/deploy/Factory.sol
@@ -4,54 +4,87 @@ import "../common/Controlled.sol";
contract Factory is Controlled {
- event NewKernel(address newKernel, bytes infohash);
+ event NewKernel(address newKernel, bytes32 codeHash);
struct Version {
uint256 blockNumber;
uint256 timestamp;
address kernel;
- bytes infohash;
+ bytes32 codeHash;
}
+
mapping (address => uint256) versionMap;
Version[] versionLog;
uint256 latestUpdate;
address latestKernel;
- function Factory(address _kernel, bytes _infohash)
+ function Factory(address _kernel)
public
{
- _setKernel(_kernel, _infohash);
+ _setKernel(_kernel);
}
- function setKernel(address _kernel, bytes _infohash)
+ function setKernel(address _kernel)
external
onlyController
{
- _setKernel(_kernel, _infohash);
+ _setKernel(_kernel);
}
- function getVersion(uint256 index) public view
- returns(uint256 blockNumber,
- uint256 timestamp,
- address kernel,
- bytes infohash)
+ function getVersion(uint256 index)
+ public
+ view
+ returns(
+ uint256 blockNumber,
+ uint256 timestamp,
+ address kernel,
+ bytes32 codeHash
+ )
{
- return (versionLog[index].blockNumber,
- versionLog[index].timestamp,
- versionLog[index].kernel,
- versionLog[index].infohash);
+ return (
+ versionLog[index].blockNumber,
+ versionLog[index].timestamp,
+ versionLog[index].kernel,
+ versionLog[index].codeHash
+ );
}
- function _setKernel(address _kernel, bytes _infohash)
+ function getCodeHash(address _addr)
+ public
+ view
+ returns (bytes32 codeHash)
+ {
+ bytes memory o_code;
+ uint size;
+ assembly {
+ // retrieve the size of the code, this needs assembly
+ size := extcodesize(_addr)
+ }
+ require (size > 0);
+ assembly {
+ // allocate output byte array - this could also be done without assembly
+ // by using o_code = new bytes(size)
+ o_code := mload(0x40)
+ // new "memory end" including padding
+ mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), bnot(0x1f))))
+ // store length in memory
+ mstore(o_code, size)
+ // actually retrieve the code, this needs assembly
+ extcodecopy(_addr, add(o_code, 0x20), 0, size)
+ }
+ codeHash = keccak256(o_code);
+ }
+
+ function _setKernel(address _kernel)
internal
{
require(_kernel != latestKernel);
+ bytes32 _codeHash = getCodeHash(_kernel);
versionMap[_kernel] = versionLog.length;
- versionLog.push(Version({blockNumber: block.number, timestamp: block.timestamp, kernel: _kernel, infohash: _infohash}));
+ versionLog.push(Version({blockNumber: block.number, timestamp: block.timestamp, kernel: _kernel, codeHash: _codeHash}));
latestUpdate = block.timestamp;
latestKernel = _kernel;
- NewKernel(_kernel, _infohash);
+ NewKernel(_kernel, _codeHash);
}
-
}
\ No newline at end of file
From 497076382bced7da68535904c23e31ef88479248 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Wed, 28 Mar 2018 18:37:17 -0300
Subject: [PATCH 091/102] fix typo
---
contracts/deploy/Factory.sol | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contracts/deploy/Factory.sol b/contracts/deploy/Factory.sol
index 2408a58..525ea4a 100644
--- a/contracts/deploy/Factory.sol
+++ b/contracts/deploy/Factory.sol
@@ -67,7 +67,7 @@ contract Factory is Controlled {
// by using o_code = new bytes(size)
o_code := mload(0x40)
// new "memory end" including padding
- mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), bnot(0x1f))))
+ mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
// store length in memory
mstore(o_code, size)
// actually retrieve the code, this needs assembly
From e76dfe33ab286c22c1c6aa87cc073f40782f9524 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Fri, 30 Mar 2018 02:31:08 -0300
Subject: [PATCH 092/102] updated to new solidity keywords and linting
---
contracts/deploy/DelayedUpdatableInstance.sol | 62 ++++++++++++-------
contracts/identity/ERC725.sol | 8 +--
contracts/identity/ERC735.sol | 6 +-
contracts/identity/Identity.sol | 14 ++---
4 files changed, 52 insertions(+), 38 deletions(-)
diff --git a/contracts/deploy/DelayedUpdatableInstance.sol b/contracts/deploy/DelayedUpdatableInstance.sol
index 3ff6b0f..405a999 100644
--- a/contracts/deploy/DelayedUpdatableInstance.sol
+++ b/contracts/deploy/DelayedUpdatableInstance.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.17;
+pragma solidity ^0.4.21;
import "./DelayedUpdatableInstanceStorage.sol";
import "./DelegatedCall.sol";
@@ -22,42 +22,56 @@ contract DelayedUpdatableInstance is DelayedUpdatableInstanceStorage, DelegatedC
* @dev delegatecall everything (but declared functions) to `_target()`
* @notice Verify `kernel()` code to predict behavior
*/
- function () external delegated {
+ function ()
+ external
+ delegated
+ {
//all goes to kernel
}
+ function updateRequestUpdatableInstance(
+ address _kernel
+ )
+ external
+ {
+ require(msg.sender == address(this));
+ uint activation = block.timestamp + 30 days;
+ update = Update(_kernel, activation);
+ emit UpdateRequested(_kernel, activation);
+ }
+
+ function updateConfirmUpdatableInstance(
+ address _kernel
+ )
+ external
+ {
+ require(msg.sender == address(this));
+ Update memory pending = update;
+ require(pending.kernel == _kernel);
+ require(pending.activation < block.timestamp);
+ kernel = pending.kernel;
+ delete update;
+ emit UpdateConfirmed(kernel, pending.kernel);
+ }
+
+ function updateCancelUpdatableInstance()
+ external
+ {
+ require(msg.sender == address(this));
+ delete update;
+ }
+
/**
* @dev returns configured kernel
* @return kernel address
*/
function targetDelegatedCall()
internal
- constant
+ view
returns(address)
{
return kernel;
}
- function updateRequestUpdatableInstance(address _kernel) external {
- require(msg.sender == address(this));
- uint activation = block.timestamp + 30 days;
- update = Update(_kernel, activation);
- UpdateRequested(_kernel, activation);
- }
-
- function updateConfirmUpdatableInstance(address _kernel) external {
- require(msg.sender == address(this));
- Update memory pending = update;
- require(pending.kernel == _kernel);
- require(pending.activation < block.timestamp);
- kernel = pending.kernel;
- delete update;
- UpdateConfirmed(kernel, pending.kernel);
- }
-
- function updateCancelUpdatableInstance() external {
- require(msg.sender == address(this));
- delete update;
- }
}
\ No newline at end of file
diff --git a/contracts/identity/ERC725.sol b/contracts/identity/ERC725.sol
index 17af37a..696d50d 100644
--- a/contracts/identity/ERC725.sol
+++ b/contracts/identity/ERC725.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.18;
+pragma solidity ^0.4.21;
contract ERC725 {
@@ -19,9 +19,9 @@ contract ERC725 {
bytes32 key;
}
- function getKey(bytes32 _key, uint256 _purpose) public constant returns(uint256 purpose, uint256 keyType, bytes32 key);
- function getKeyPurpose(bytes32 _key) public constant returns(uint256[] purpose);
- function getKeysByPurpose(uint256 _purpose) public constant returns(bytes32[] keys);
+ function getKey(bytes32 _key, uint256 _purpose) public view returns(uint256 purpose, uint256 keyType, bytes32 key);
+ function getKeyPurpose(bytes32 _key) public view returns(uint256[] purpose);
+ function getKeysByPurpose(uint256 _purpose) public view returns(bytes32[] keys);
function addKey(bytes32 _key, uint256 _purpose, uint256 _keyType) public returns (bool success);
function removeKey(bytes32 _key, uint256 _purpose) public returns (bool success);
function execute(address _to, uint256 _value, bytes _data) public returns (uint256 executionId);
diff --git a/contracts/identity/ERC735.sol b/contracts/identity/ERC735.sol
index 2109d28..8891552 100644
--- a/contracts/identity/ERC735.sol
+++ b/contracts/identity/ERC735.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.18;
+pragma solidity ^0.4.21;
contract ERC735 {
@@ -16,8 +16,8 @@ contract ERC735 {
string uri;
}
- function getClaim(bytes32 _claimId) public constant returns(uint256 claimType, uint256 scheme, address issuer, bytes signature, bytes data, string uri);
- function getClaimIdsByType(uint256 _claimType) public constant returns(bytes32[] claimIds);
+ function getClaim(bytes32 _claimId) public view returns(uint256 claimType, uint256 scheme, address issuer, bytes signature, bytes data, string uri);
+ function getClaimIdsByType(uint256 _claimType) public view returns(bytes32[] claimIds);
function addClaim(uint256 _claimType, uint256 _scheme, address _issuer, bytes _signature, bytes _data, string _uri) public returns (bytes32 claimRequestId);
function removeClaim(bytes32 _claimId) public returns (bool success);
}
\ No newline at end of file
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index ceea2a9..962c442 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.17;
+pragma solidity ^0.4.21;
import "./ERC725.sol";
import "./ERC735.sol";
@@ -283,7 +283,7 @@ contract Identity is ERC725, ERC735 {
uint256 _purpose
)
public
- constant
+ view
returns(uint256 purpose, uint256 keyType, bytes32 key)
{
Key storage myKey = keys[keccak256(_key, _purpose)];
@@ -292,7 +292,7 @@ contract Identity is ERC725, ERC735 {
function isKeyPurpose(bytes32 _key, uint256 _purpose)
public
- constant
+ view
returns (bool)
{
return keys[keccak256(_key, _purpose)].purpose == _purpose;
@@ -300,7 +300,7 @@ contract Identity is ERC725, ERC735 {
function getKeyPurpose(bytes32 _key)
public
- constant
+ view
returns(uint256[] purpose)
{
@@ -337,7 +337,7 @@ contract Identity is ERC725, ERC735 {
function getKeysByPurpose(uint256 _purpose)
public
- constant
+ view
returns(bytes32[])
{
return keysByPurpose[_purpose];
@@ -345,7 +345,7 @@ contract Identity is ERC725, ERC735 {
function getClaim(bytes32 _claimId)
public
- constant
+ view
returns(
uint256 claimType,
uint256 scheme,
@@ -361,7 +361,7 @@ contract Identity is ERC725, ERC735 {
function getClaimIdsByType(uint256 _claimType)
public
- constant
+ view
returns(bytes32[] claimIds)
{
return claimsByType[_claimType];
From ac54537b74460c06d7b7b1398cb9cc7a4c08c5be Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Fri, 30 Mar 2018 02:31:08 -0300
Subject: [PATCH 093/102] updated to new solidity keywords and linting
---
contracts/deploy/DelegatedCall.sol | 4 ++--
contracts/deploy/Instance.sol | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/contracts/deploy/DelegatedCall.sol b/contracts/deploy/DelegatedCall.sol
index 0526c76..861f751 100644
--- a/contracts/deploy/DelegatedCall.sol
+++ b/contracts/deploy/DelegatedCall.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.17;
+pragma solidity ^0.4.21;
/**
@@ -29,7 +29,7 @@ contract DelegatedCall {
*/
function targetDelegatedCall()
internal
- constant
+ view
returns(address);
}
diff --git a/contracts/deploy/Instance.sol b/contracts/deploy/Instance.sol
index 96c18d8..6243711 100644
--- a/contracts/deploy/Instance.sol
+++ b/contracts/deploy/Instance.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.17;
+pragma solidity ^0.4.21;
import "./InstanceStorage.sol";
import "./DelegatedCall.sol";
@@ -28,7 +28,7 @@ contract Instance is InstanceStorage, DelegatedCall {
*/
function targetDelegatedCall()
internal
- constant
+ view
returns(address)
{
return kernel;
From e373ef3885c10ba44569c0705ac583dba392ba1c Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Mon, 2 Apr 2018 16:26:25 -0300
Subject: [PATCH 094/102] initialize first key as manager and action purpose
---
contracts/identity/Identity.sol | 1 +
1 file changed, 1 insertion(+)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index 962c442..a68b91f 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -440,6 +440,7 @@ contract Identity is ERC725, ERC735 {
require(keysByPurpose[MANAGEMENT_KEY].length == 0);
require(purposeThreshold[MANAGEMENT_KEY] == 0);
_addKey(bytes32(_manager), MANAGEMENT_KEY, 0);
+ _addKey(bytes32(_manager), ACTION_KEY, 0);
purposeThreshold[MANAGEMENT_KEY] = 1;
purposeThreshold[ACTION_KEY] = 1;
From 9de0367ba476652a3e6ddf3ec3d18d9cad6d7725 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Fri, 6 Apr 2018 22:33:17 -0300
Subject: [PATCH 095/102] remove leftover info in readme, move docs
---
docs/Identity.md | 144 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 144 insertions(+)
create mode 100644 docs/Identity.md
diff --git a/docs/Identity.md b/docs/Identity.md
new file mode 100644
index 0000000..74cf8dc
--- /dev/null
+++ b/docs/Identity.md
@@ -0,0 +1,144 @@
+# Identity contracts
+
+# Table of content
+- [Summary](#summary)
+- [Smart Contracts Overview](#smart-contracts-overview)
+- [Tutorials](#tutorials)
+- - [Identity Creation](#identity-creation)
+- - [Executions and Approvals](#executions-and-approvals)
+- - [Management Activities](#management-activities)
+- - [Constants / View functions](#constants--view-functions)
+- - [Adding new functionality to identitities](#adding-new-functionality-to-identitities)
+- - [Upgrade an `IdentityKernel` instance](#upgrade-an-identitykernel-instance)
+- - [Setting up Identity Recovery contract](#setting-up-identity-recovery-contract)
+- - [Recovering an Identity](#recovering-an-identity)
+## Summary
+This is a proposed proof of concept for the implementation of interfaces [ERC-725](https://github.com/ethereum/EIPs/issues/725) and [ERC735](https://github.com/ethereum/EIPs/issues/725), providing the following functionality:
+- Public key register, composed of Ethereum Addresses and ECDSA keys. These keys can perform management activities over the identity itself, as well as performing operations in other contracts and transfer of ether.
+- Claim holding, that can be used to add / verify claims against an identity.
+- Identity factory to simplify the process of instantiating an Identity contract, as well as handling the upgrade process of this instance, assuming there's a new version of this contract.
+- Identity recovery process that can be initiated using a shared secret among keys related to the identity.
+
+## Smart Contracts Overview
+- `Controlled`. Keeps tracks of the controller or owner of the contract. Provides the modifier `onlyController` which can be used in child contracts.
+- `DelegatedCall`. Abstract contract that delegates calls using the `delegated` modifier to the result of `targetDelegatedCall()` function.
+- `InstanceStorage`.
+Defines kernel vars that an `IdentityKernel` contract share with a `Instance` contract. If you wish to reuse this contract, it is important to avoid overwriting wrong storage pointers, so `InstanceStorage` should be always the first contract to be inherited.
+- `Instance`. Contract that forwards everything through `delegatecall` to a defined `IdentityKernel`. This contracts inherits from `InstanceStorage` and `DelegatedCall`
+- `UpdatableInstance`. A contract that can be updated, if the contract itself calls `updateUpdatableInstance()`. This contract inherits from `Instance`
+- `DelayedUpdatableInstanceStorage`. This contract defines kernel vars that an `IdentityKernel` contract shares with and `Instance`. See `InstanceStorage` fro restrictions in case of reuse. This contract inherits from `InstanceStorage`
+- `DelayedUpdatableInstance`. Extending the functionality of `UpdatableInstance`, this contract introduces a delay functionality based in the `block.timestamp` in order to limit updates with a 30 days lock.
+- `Factory`. Contract used as a version control for child factories contracts
+- `ERC725` and `ERC735`. Interfaces based on EIPs [ERC-725: Identity](https://github.com/ethereum/EIPs/issues/725) and [ERC735: Claims Holder](https://github.com/ethereum/EIPs/issues/725)
+- `Identity`. Implementation of ERC725 and ERC735. Includes additional management functions to handle minimum required approvals for execution of transactions, as well as recovery related functions.
+- `IdentityKernel`. Represents a version of the identity contract that can be created with the `IdentityFactory`, as well as be updated with calls to itself. This contract inherits from `DelayedUpdatableInstanceStorage` and `Identity`
+- `FriendsRecovery`. Contract that handles the recovery process of an `Identity` in case the management keys are lost, or were compromised. A `FriendsRecovery` contract instance has an 1:1 association with an `Identity`
+- `IdentityFactory`. Deploys `DelayedUpdatableInstanceStorage` configured to a deployed `IdentityKernel` and initializes the Instance with `initIdentity` function from kernel.
+
+## Tutorials
+
+### Identity Creation
+We recommend to not create `Identity` instances directly, but create them through the `IdentityFactory` which is deployed in: `0x000000000000000000000000`, and provides the following functions:
+- `createIdentity()` - will create a new instance of an `IdentityKernel`, with `msg.sender` as a management key.
+- `createIdentity(address _idOwner)` - used to specify the owner / management key of a new identity.
+
+The event `IdentityCreated` is triggered when the new identity is created successfully.
+
+### Executions and Approvals
+Identities can perform management activities on themselves, as well as performing actions in other contracts. These operations are performed with the `execute()` and `approve()` functions.
+
+`execute(address _to, uint256 _value, bytes _data)` is called when you wish to perform an operation. This function may be called by a management key or by an action key and triggers an `ExecutionRequested` event. Management keys are required when `_to` refers to the identity itself, otherwise, action keys are required.
+
+The `_value` parameters refer to the amount in ether that this transaction will send to the contract/wallet address specified in `_to`. The identity contract should have funds if the value is greater than `0`.
+
+`_data` refers to the byte encoding of the operations and parameters to be executed. `web3.eth.abi.encodeFunctionCall` is useful to generate the bytecode to be sent in this function. Here's an example on how to use it to call the `addKey` function of the identity contract:
+
+```
+const web3EthAbi = require("web3-eth-abi");
+let data = web3EthAbi.encodeFunctionCall({
+ name: 'addKey',
+ type: 'function',
+ inputs: [{
+ type: 'bytes32',
+ name: '_key'
+ },{
+ type: 'uint256',
+ name: '_purpose'
+ },{
+ type: 'uint256',
+ name: '_type'
+ }]
+}, ["0x1234567", 1, 1]);
+
+let receipt = await identityContractInstance.execute(identityContractInstance.address, 0, data).send({from: accounts[0]});
+```
+A javascript utils library is provided in `utils/identityUtils.js` which can be used to generate the payloads used in the `_data` parameter of the `execute` function
+
+Once the `execute` function is executed, if the minimum required approvals by key purpose is one, the transaction is executed immediatly (Triggering the `Approved` and `Executed` events).
+
+In case the minimum required approvals are greater than 1, `approve(uint256 _id, bool _approval)` needs to be called by N management or action keys depending on the operation to perform, each call triggering an `Approved` event. Once the minimum approvals required is reached, the transaction will be executed immediatly. This `approve` function requires an transaction execution id, which can be obtained by the `ExecutionRequested` event triggered by the `execute` function.
+
+### Management Activities
+Identity management is limited to the addition, removal and setting of the minimum required approvals to perform a transaction. These activities will fall into the approval process requiring that N managers approve their execution before it is performed.
+- `addKey(bytes32 _key, uint256 _purpose, uint256 _type)`. Registers a key in the identity. Keys should have a defined purpose and type. The `_purpose` of a key can be `1` - Management keys, used only to perform management activities; `2` - Action keys, used only to perform calls to external contracts and sending ether; `3` - Claim signer key, which are keys that can add claims to the identity; and `4` - Encryption keys, at the moment used only for information purposes. The `_type` of keys supported at the moment are `0` for ethereum addresses, `1` for ECDSA. Keys are stored as a bytes32 value. Both `_purpose` and `_type` accepts `uint256` types, so they're not limited to the values described in here. It is worth mentioning that keys can have more than one purpose and `addKey` can be called for the same key more than once, assuming the purpose is different each time it is called. Triggers a `KeyAdded` event.
+- `removeKey(bytes32 _key, uint256 _purpose)`. Used to remove an existing key-purpose pair. It will fail if you're removing a management key, and there is only one management key registered in the identity. Triggers a `KeyRemoved` event.
+- `replaceKey(bytes32 _oldKey, bytes32 _newKey, uint256 _newType)`. Used to replace an existing key, for a new one that can have a different type. Triggers both a `KeyRemoved` and `KeyAdded` event.
+- `setMinimumApprovalsByKeyType(uint256 _purpose, uint256 _minimumApprovals)`. By default, an `Identity` has only one management key registered (the owner of the identity), however it is possible to have more than one management key registered with `addKey`, and require a N of M approvals (both for Management and Action keys). This is done with this function, where you have to specify number of minimum approvals required by key purpose.
+
+### Claims
+Lorem Ipsum
+
+### Constants / View functions
+The following functions are provided to access information about an identity:
+- `getKey(bytes32 _key, uint256 _purpose)`. Returns the key type, purpose and key for a given key-purpose pair
+- `isKeyPurpose(bytes32 _key, uint256 _purpose)` returns if a given key-purpose exists, and if it's purpose is actually what was specified.
+- `getKeyPurpose(bytes32 _key)` returns an array of purposes for a given key.
+- `function getKeysByPurpose(uint256 _purpose)` returns an array of keys for a given purpose.
+- `getClaim(bytes32 _claimId)` returns the claim information registered for a given claim Id.
+- `getClaimIdsByType(uint256 _claimType)` returns an array of claim Ids for a given claim type.
+
+### Adding new functionality to identitities
+New versions of identities should extend from `IdentityKernel` and need to be registered in the `IdentityFactory`. This is done by creating a new instance of the new contract which inherits from `IdentityKernel`, and then calling the `setKernel` function of the `IdentityFactory` specifiying the address of the updated identity kernel, and a `bytes32` info hash with the description of the new version.
+Once updated, a `NewKernel` event is triggered.
+
+### Upgrade an `IdentityKernel` instance
+When an identity instance needs to be upgraded, we can use the execute/approve process to upgrade it to an specific version. This upgrade process requires using `execute` to call two functions
+- `updateRequestUpdatableInstance(address _newKernel)`. This will generate a pending request for upgrading an instance `30 days` later and trigger an UpdateRequested event.
+- `updateConfirmUpdatableInstance(address _newKernel)`. After `30 days` pass, this function needs to be invoked to confirm the update process. Once the update process is completed a UpdateConfirmed event is triggered.
+- An request for update can be cancelled if it hasn't been approved yet. This is done using the function `updateCancelUpdatableInstance()` with the same execute/approve process
+
+Kernel addresses could be obtained using the `getVersion` function of the `IdentityFactory`
+
+### Setting up Identity Recovery contract
+After creating an identity with the `IdentityFactory`, an instance of `FriendsRecovery` need to be created. The constructor of this contract expects the following parameters:
+- `_identity`: The identity contract address
+- `_setupDelay`: Time for users to be able to change the selected friends for recovery.
+- `_threshold`: Minimum number of friends required to recover an identity
+- `_secret`: sha3 of the identity address + a secret word
+- `friendHashes`: an array of sha3 hashes compossed of the identity address + secret word + friend ethereum address.
+
+Once this recovery contract is created, we need to associate it with the identity. This is done through the execute/approve mechanism of the identity, sending a payload to invoke the `setupRecovery` function of the identity, passing the recovery contract address as a parameter.
+
+
+### Recovering an Identity
+Recovery of an identity happens when you lose access to the management key(s) or Identity. The recovery is done having the friends sign a message. This message is a sha3 hash compossed of:
+
+```
+recovery address + secret word + contract to invoke (identity) + the function and parameters of the function to invoke encoded (recover function of identity) +
+new secret word hash + new friend hashes.
+```
+
+Where new `new secret word hash` is a sha3 of the identity address + secret word; and `new friend hashes` is an array of sha3 hashes compossed of the identity address + secret word + friend ethereum address).
+
+Normally the function that is going to be encoded should be the identity `managerReset` with the address of the new management key.
+
+A minimum of (threshold) friends should approve this recovery attempt, and this can be done by them spending gas, calling the `approve` function of the recovery contract; or by having a single address (probably the identity owner) calling `approvePreSigned`.
+
+`approve` could be called sending the sha3 hashed message described previously through a regular transaction , and `approvePreSigned` needs gathering the signatures of the hashed message into different arrays (for v, r, and s)
+
+To enhance privacy, any account can approve anything, however only revealed addresses will be used.
+
+Once the approvation is complete, the `execute` function of the recovery contract needs to be called, with the parameters used to generate the hashed message, and after the recovery is completed, `processManagerReset` needs to be executedn on the identity to remove all the management keys different from the new management key used for the recovery
+
+An example of how to use the recovery contract is available in `test/friendsRecovery.js`.
+
From 79da42e2baa01d56c3a0c7237130fc2c305176ac Mon Sep 17 00:00:00 2001
From: Richard Ramos
Date: Mon, 9 Apr 2018 16:05:19 -0400
Subject: [PATCH 096/102] Fixing naming errors required for compilation
---
contracts/identity/IdentityFactory.sol | 2 +-
contracts/tests/UpdatedIdentityKernel.sol | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/contracts/identity/IdentityFactory.sol b/contracts/identity/IdentityFactory.sol
index a0dac9a..4e26dd7 100644
--- a/contracts/identity/IdentityFactory.sol
+++ b/contracts/identity/IdentityFactory.sol
@@ -11,7 +11,7 @@ contract IdentityFactory is Factory {
function IdentityFactory(bytes _infohash)
public
- Factory(new IdentityKernel(), _infohash)
+ Factory(new IdentityKernel())
{
}
diff --git a/contracts/tests/UpdatedIdentityKernel.sol b/contracts/tests/UpdatedIdentityKernel.sol
index 23ad12b..39866bc 100644
--- a/contracts/tests/UpdatedIdentityKernel.sol
+++ b/contracts/tests/UpdatedIdentityKernel.sol
@@ -8,6 +8,6 @@ contract UpdatedIdentityKernel is IdentityKernel {
event TestFunctionExecuted(uint256 minApprovalsByManagementKeys);
function test() public {
- TestFunctionExecuted(minimumApprovalsByKeyPurpose[MANAGEMENT_KEY]);
+ TestFunctionExecuted(purposeThreshold[MANAGEMENT_KEY]);
}
}
\ No newline at end of file
From 922fea958f2aaa2359e8633aedda391d703a9af5 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Tue, 17 Apr 2018 11:18:23 -0300
Subject: [PATCH 097/102] change from gasMinimal to gasLimit
---
contracts/identity/IdentityGasRelay.sol | 34 ++++++++++++-------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/contracts/identity/IdentityGasRelay.sol b/contracts/identity/IdentityGasRelay.sol
index 2651282..b29759f 100644
--- a/contracts/identity/IdentityGasRelay.sol
+++ b/contracts/identity/IdentityGasRelay.sol
@@ -23,7 +23,7 @@ contract IdentityGasRelay is Identity {
* @param _data call data
* @param _nonce current identity nonce
* @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
- * @param _gasMinimal minimal amount of gas needed to complete the execution
+ * @param _gasLimit minimal gasLimit required to execute this call
* @param _gasToken token being used for paying `msg.sender`
* @param _messageSignatures rsv concatenated ethereum signed message signatures required
*/
@@ -33,17 +33,17 @@ contract IdentityGasRelay is Identity {
bytes _data,
uint _nonce,
uint _gasPrice,
- uint _gasMinimal,
+ uint _gasLimit,
address _gasToken,
bytes _messageSignatures
)
external
{
+ uint startGas = gasleft();
+ require(startGas >= _gasLimit);
//verify transaction parameters
require(_nonce == nonce);
- uint startGas = gasleft();
- require(startGas >= _gasMinimal);
-
+ ;
// calculates signHash
bytes32 signHash = getSignHash(
callGasRelayHash(
@@ -52,7 +52,7 @@ contract IdentityGasRelay is Identity {
keccak256(_data),
_nonce,
_gasPrice,
- _gasMinimal,
+ _gasLimit,
_gasToken
)
);
@@ -95,7 +95,7 @@ contract IdentityGasRelay is Identity {
* @param _data call data
* @param _nonce current identity nonce
* @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
- * @param _gasMinimal minimal amount of gas needed to complete the execution
+ * @param _gasLimit minimal gasLimit required to execute this call
* @param _gasToken token being used for paying `msg.sender`
* @param _messageSignatures rsv concatenated ethereum signed message signatures required
*/
@@ -106,16 +106,16 @@ contract IdentityGasRelay is Identity {
bytes _data,
uint _nonce,
uint _gasPrice,
- uint _gasMinimal,
+ uint _gasLimit,
address _gasToken,
bytes _messageSignatures
)
external
{
+ uint startGas = gasleft();
+ require(startGas >= _gasLimit);
//verify transaction parameters
require(_nonce == nonce);
- uint startGas = gasleft();
- require(startGas >= _gasMinimal);
require(_baseToken != address(0)); //_baseToken should be something!
require(_to != address(this)); //no management with approveAndCall
@@ -128,7 +128,7 @@ contract IdentityGasRelay is Identity {
keccak256(_data),
_nonce,
_gasPrice,
- _gasMinimal,
+ _gasLimit,
_gasToken
)
);
@@ -201,7 +201,7 @@ contract IdentityGasRelay is Identity {
* @param _dataHash call data hash
* @param _nonce current identity nonce
* @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
- * @param _gasMinimal minimal amount of gas needed to complete the execution
+ * @param _gasLimit minimal gasLimit required to execute this call
* @param _gasToken token being used for paying `msg.sender`
* @return callGasRelayHash the hash to be signed by wallet
*/
@@ -211,7 +211,7 @@ contract IdentityGasRelay is Identity {
bytes32 _dataHash,
uint _nonce,
uint256 _gasPrice,
- uint256 _gasMinimal,
+ uint256 _gasLimit,
address _gasToken
)
public
@@ -226,7 +226,7 @@ contract IdentityGasRelay is Identity {
_dataHash,
_nonce,
_gasPrice,
- _gasMinimal,
+ _gasLimit,
_gasToken
);
}
@@ -239,7 +239,7 @@ contract IdentityGasRelay is Identity {
* @param _dataHash call data hash
* @param _nonce current identity nonce
* @param _gasPrice price in SNT paid back to msg.sender for each gas unit used
- * @param _gasMinimal minimal amount of gas needed to complete the execution
+ * @param _gasLimit minimal gasLimit required to execute this call
* @param _gasToken token being used for paying `msg.sender`
* @return callGasRelayHash the hash to be signed by wallet
*/
@@ -250,7 +250,7 @@ contract IdentityGasRelay is Identity {
bytes32 _dataHash,
uint _nonce,
uint256 _gasPrice,
- uint256 _gasMinimal,
+ uint256 _gasLimit,
address _gasToken
)
public
@@ -266,7 +266,7 @@ contract IdentityGasRelay is Identity {
_dataHash,
_nonce,
_gasPrice,
- _gasMinimal,
+ _gasLimit,
_gasToken
);
}
From be024406d2ffe884b1074a55ca885bef0298fea2 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Tue, 17 Apr 2018 22:24:45 -0300
Subject: [PATCH 098/102] small fixes
---
contracts/identity/IdentityGasRelay.sol | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/contracts/identity/IdentityGasRelay.sol b/contracts/identity/IdentityGasRelay.sol
index b29759f..b02b284 100644
--- a/contracts/identity/IdentityGasRelay.sol
+++ b/contracts/identity/IdentityGasRelay.sol
@@ -40,10 +40,9 @@ contract IdentityGasRelay is Identity {
external
{
uint startGas = gasleft();
- require(startGas >= _gasLimit);
//verify transaction parameters
+ require(startGas >= _gasLimit);
require(_nonce == nonce);
- ;
// calculates signHash
bytes32 signHash = getSignHash(
callGasRelayHash(
@@ -113,8 +112,8 @@ contract IdentityGasRelay is Identity {
external
{
uint startGas = gasleft();
- require(startGas >= _gasLimit);
//verify transaction parameters
+ require(startGas >= _gasLimit);
require(_nonce == nonce);
require(_baseToken != address(0)); //_baseToken should be something!
require(_to != address(this)); //no management with approveAndCall
@@ -178,7 +177,7 @@ contract IdentityGasRelay is Identity {
returns(bool)
{
uint _amountSignatures = _messageSignatures.length / 72;
- require(_amountSignatures == minimumApprovalsByKeyPurpose[_requiredKey]);
+ require(_amountSignatures == purposeThreshold[_requiredKey]);
bytes32 _lastKey = 0;
for (uint256 i = 0; i < _amountSignatures; i++) {
bytes32 _currentKey = recoverKey(
From 706941acb0b72ea084d34c1dbee028cc53f1d4be Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Thu, 19 Apr 2018 05:33:04 -0300
Subject: [PATCH 099/102] add return factoried contract address + add emit
keyword
---
contracts/deploy/Factory.sol | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contracts/deploy/Factory.sol b/contracts/deploy/Factory.sol
index 525ea4a..4ba811f 100644
--- a/contracts/deploy/Factory.sol
+++ b/contracts/deploy/Factory.sol
@@ -85,6 +85,6 @@ contract Factory is Controlled {
versionLog.push(Version({blockNumber: block.number, timestamp: block.timestamp, kernel: _kernel, codeHash: _codeHash}));
latestUpdate = block.timestamp;
latestKernel = _kernel;
- NewKernel(_kernel, _codeHash);
+ emit NewKernel(_kernel, _codeHash);
}
}
\ No newline at end of file
From f9870224bed25c01e6f2489a196f916def24a36f Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Thu, 19 Apr 2018 05:33:04 -0300
Subject: [PATCH 100/102] add return factoried contract address + add emit
keyword
---
contracts/identity/IdentityFactory.sol | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/contracts/identity/IdentityFactory.sol b/contracts/identity/IdentityFactory.sol
index 4e26dd7..0ac1ce9 100644
--- a/contracts/identity/IdentityFactory.sol
+++ b/contracts/identity/IdentityFactory.sol
@@ -17,16 +17,19 @@ contract IdentityFactory is Factory {
function createIdentity()
external
+ returns (address)
{
- createIdentity(msg.sender);
+ return createIdentity(msg.sender);
}
function createIdentity(address _idOwner)
public
+ returns (address)
{
IdentityKernel instance = IdentityKernel(new DelayedUpdatableInstance(address(latestKernel)));
instance.initIdentity(_idOwner);
- IdentityCreated(address(instance));
+ emit IdentityCreated(address(instance));
+ return instance;
}
}
From 8bf51bc488ff91812cb7bc049ff1ad568b3ef568 Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Mon, 23 Apr 2018 08:04:03 -0300
Subject: [PATCH 101/102] use hashed keys + dont require issuer to be claim
signer
---
contracts/identity/Identity.sol | 48 ++++++++++++++++-----------------
1 file changed, 24 insertions(+), 24 deletions(-)
diff --git a/contracts/identity/Identity.sol b/contracts/identity/Identity.sol
index a68b91f..670b4a6 100644
--- a/contracts/identity/Identity.sol
+++ b/contracts/identity/Identity.sol
@@ -17,7 +17,7 @@ contract Identity is ERC725, ERC735 {
uint256 nonce;
address recoveryContract;
- address recoveryManager;
+ bytes32 recoveryManager;
struct Transaction {
bool valid;
@@ -31,7 +31,7 @@ contract Identity is ERC725, ERC735 {
modifier managerOnly {
require(
- isKeyPurpose(bytes32(msg.sender), MANAGEMENT_KEY)
+ isKeyPurpose(keccak256(msg.sender), MANAGEMENT_KEY)
);
_;
}
@@ -40,7 +40,7 @@ contract Identity is ERC725, ERC735 {
if(msg.sender == address(this)) {
_;
} else {
- require(isKeyPurpose(bytes32(msg.sender), MANAGEMENT_KEY));
+ require(isKeyPurpose(keccak256(msg.sender), MANAGEMENT_KEY));
if (purposeThreshold[MANAGEMENT_KEY] == 1) {
_;
} else {
@@ -51,7 +51,7 @@ contract Identity is ERC725, ERC735 {
modifier recoveryOnly {
require(
recoveryContract != address(0) &&
- msg.sender == address(recoveryContract)
+ msg.sender == recoveryContract
);
_;
}
@@ -78,19 +78,21 @@ contract Identity is ERC725, ERC735 {
)
{
require(
- address(_key) == ecrecover(
- keccak256("\x19Ethereum Signed Message:\n32", _signHash),
- _v,
- _r,
- _s
+ _key == keccak256(
+ ecrecover(
+ keccak256("\x19Ethereum Signed Message:\n32", _signHash),
+ _v,
+ _r,
+ _s
+ )
)
);
require(keys[_key].purpose != 0);
_;
}
- function Identity() public {
- _constructIdentity(msg.sender);
+ constructor() public {
+ _constructIdentity(keccak256(msg.sender));
}
function ()
@@ -100,21 +102,21 @@ contract Identity is ERC725, ERC735 {
}
- function managerReset(address _newKey)
+ function managerReset(bytes32 _newKey)
public
recoveryOnly
{
recoveryManager = _newKey;
- _addKey(bytes32(recoveryManager), MANAGEMENT_KEY, 0);
+ _addKey(keccak256(recoveryManager), MANAGEMENT_KEY, 0);
purposeThreshold[MANAGEMENT_KEY] = keysByPurpose[MANAGEMENT_KEY].length;
}
function processManagerReset(uint256 _limit)
public
{
- require(recoveryManager != address(0));
+ require(recoveryManager != 0);
uint256 limit = _limit;
- bytes32 newKey = bytes32(recoveryManager);
+ bytes32 newKey = recoveryManager;
bytes32[] memory managers = keysByPurpose[MANAGEMENT_KEY];
uint256 totalManagers = managers.length;
@@ -132,7 +134,7 @@ contract Identity is ERC725, ERC735 {
}
if (totalManagers == 1) {
- recoveryManager = address(0);
+ delete recoveryManager;
}
}
@@ -188,7 +190,7 @@ contract Identity is ERC725, ERC735 {
if (purposeThreshold[requiredKey] == 1) {
executionId = nonce; //(?) useless in this case
nonce++; //(?) should increment
- require(isKeyPurpose(bytes32(msg.sender), requiredKey));
+ require(isKeyPurpose(keccak256(msg.sender), requiredKey));
_to.call.value(_value)(_data); //(?) success not used
emit Executed(executionId, _to, _value, _data); //no information on success
} else {
@@ -200,10 +202,9 @@ contract Identity is ERC725, ERC735 {
function approve(uint256 _id, bool _approval)
public
- managerOrActor(bytes32(msg.sender))
returns (bool success)
{
- return _approve(bytes32(msg.sender), _id, _approval);
+ return _approve(keccak256(msg.sender), _id, _approval);
}
function setMinimumApprovalsByKeyType(
@@ -238,8 +239,7 @@ contract Identity is ERC725, ERC735 {
_includeClaim(claimHash, _claimType, _scheme, _issuer, _signature, _data, _uri);
}
} else {
- require(_issuer == msg.sender);
- require(isKeyPurpose(bytes32(msg.sender), CLAIM_SIGNER_KEY));
+ require(isKeyPurpose(keccak256(msg.sender), CLAIM_SIGNER_KEY));
_execute(address(this), 0, msg.data);
emit ClaimRequested(
claimHash,
@@ -434,13 +434,13 @@ contract Identity is ERC725, ERC735 {
recoveryContract = _recoveryContract;
}
- function _constructIdentity(address _manager)
+ function _constructIdentity(bytes32 _managerKey)
internal
{
require(keysByPurpose[MANAGEMENT_KEY].length == 0);
require(purposeThreshold[MANAGEMENT_KEY] == 0);
- _addKey(bytes32(_manager), MANAGEMENT_KEY, 0);
- _addKey(bytes32(_manager), ACTION_KEY, 0);
+ _addKey(_managerKey, MANAGEMENT_KEY, 0);
+ _addKey(_managerKey, ACTION_KEY, 0);
purposeThreshold[MANAGEMENT_KEY] = 1;
purposeThreshold[ACTION_KEY] = 1;
From 04cf36cdd554d6b3a605753e008692f4f77411bb Mon Sep 17 00:00:00 2001
From: Ricardo Guilherme Schmidt <3esmit@gmail.com>
Date: Mon, 23 Apr 2018 08:14:21 -0300
Subject: [PATCH 102/102] use hash of ec-recovered key
---
contracts/identity/IdentityGasRelay.sol | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contracts/identity/IdentityGasRelay.sol b/contracts/identity/IdentityGasRelay.sol
index b02b284..931e68b 100644
--- a/contracts/identity/IdentityGasRelay.sol
+++ b/contracts/identity/IdentityGasRelay.sol
@@ -304,7 +304,7 @@ contract IdentityGasRelay is Identity {
bytes32 r;
bytes32 s;
(v,r,s) = signatureSplit(_messageSignature, _pos);
- return bytes32(
+ return keccak256(
ecrecover(
_signHash,
v,