Skip to content
Open
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
37b2612
Add encodeParam to ACLHelpers
leftab Nov 5, 2018
85dda17
Move Op and Param structs from ACL to ACLHelpers
leftab Nov 6, 2018
e3b49ea
Add encoreParams and encodeParam for Param structs
leftab Nov 6, 2018
bc0e525
Add TestACLHelpers
leftab Nov 6, 2018
a97c86d
Remove encodeParam with uint params
leftab Nov 8, 2018
fee7fc1
Add AclHelpers js launch test
leftab Nov 8, 2018
ecf2f5c
Add encodeParams test
leftab Nov 8, 2018
8542235
Add brackets around for loop
leftab Nov 8, 2018
a85c5aa
Remove ACL import and inheritance
leftab Nov 8, 2018
b3ba05d
Move Op and Param to ACLParams contract
leftab Nov 20, 2018
79100ce
Add encodeOperator and encodeIfElse tests
leftab Nov 20, 2018
9b4f8f3
Compact params
leftab Nov 20, 2018
b056828
Expose ACLHelpers functions
leftab Nov 20, 2018
0aa8d3c
Fix linter issues
leftab Nov 20, 2018
e96f7c0
chore: update license text (#467)
sohkai Dec 12, 2018
1fa2b95
Merge branch 'master' into dev
sohkai Dec 13, 2018
da862e2
KernelProxy: emit SetApp event on construction (#466)
sohkai Dec 14, 2018
480b2f6
Add encodeParam to ACLHelpers
leftab Nov 5, 2018
bdeee78
Move Op and Param structs from ACL to ACLHelpers
leftab Nov 6, 2018
04c2048
Add encoreParams and encodeParam for Param structs
leftab Nov 6, 2018
2530b15
Add TestACLHelpers
leftab Nov 6, 2018
45727e3
Remove encodeParam with uint params
leftab Nov 8, 2018
97327f3
Add AclHelpers js launch test
leftab Nov 8, 2018
c44a025
Add encodeParams test
leftab Nov 8, 2018
b647866
Add brackets around for loop
leftab Nov 8, 2018
aa08e27
Remove ACL import and inheritance
leftab Nov 8, 2018
b05c010
Move Op and Param to ACLParams contract
leftab Nov 20, 2018
9c84dc3
Add encodeOperator and encodeIfElse tests
leftab Nov 20, 2018
dbce97e
Compact params
leftab Nov 20, 2018
dd2f8f2
Expose ACLHelpers functions
leftab Nov 20, 2018
22ec764
Fix linter issues
leftab Nov 20, 2018
93e5ba1
Fix spaces and array size
leftab Feb 3, 2019
ad1b7f7
Use loop for testEncodeParams
leftab Feb 3, 2019
3a8fb64
Merge branch 'dev' of https://github.com/leftab/aragonOS into dev
leftab Feb 3, 2019
c4e0fb1
chore: pin ganache-cli to 6.2.3 (#472)
sohkai Feb 7, 2019
a026e22
feat: Add SafeERC20 (#469)
sohkai Feb 11, 2019
5c61d10
tests: Coverage improvements (#474)
sohkai Feb 11, 2019
c37d4fd
4.1.0-rc.1
sohkai Feb 11, 2019
3d8c54f
chore: upgrade solium (#476)
sohkai Feb 12, 2019
9734f9c
Merge with dev
leftab Feb 13, 2019
8cf6039
Convert back ACLHelpers functions to internal
leftab Feb 13, 2019
3c909ef
Expose encodeOperator and encodeIfElse in ACLHelpers
leftab Feb 14, 2019
584f4bc
Add MIT license to ACLParams
leftab Feb 18, 2019
05d3d26
Rename ACLHelper to ACLOracleHelper
leftab Feb 18, 2019
d2268c8
VaultRecoverable: emit event on successful recovery (#480)
sohkai Feb 22, 2019
722e25e
test: remove accidentally placed .only() in tests (#483)
sohkai Feb 22, 2019
dbb0e06
feat: Add radspec strings to ENSSubdomainRegistrar
usetech-llc Mar 7, 2019
db9342f
fix: update radspec strings (#489)
sohkai Mar 7, 2019
b18c1a5
Create CONTRIBUTING.md
a33bcn Mar 7, 2019
37f8eff
chore: update readme.md
sohkai Mar 7, 2019
9ff0bda
Merge branch 'dev' into leftab-dev
sohkai Mar 7, 2019
032f577
fix: move ACLHelpers to its own file
sohkai Mar 7, 2019
7366642
feat: move ACL param IDs to ACLParams
sohkai Mar 7, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .solcover.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const skipFiles = [
'test',
'acl/ACLSyntaxSugar.sol',
'common/DepositableStorage.sol', // Used in tests that send ETH
'common/SafeERC20.sol', // solidity-coverage fails on assembly if (https://github.com/sc-forks/solidity-coverage/issues/287)
'common/UnstructuredStorage.sol' // Used in tests that send ETH
]

Expand Down
10 changes: 0 additions & 10 deletions contracts/acl/ACL.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,6 @@ contract ACL is IACL, TimeHelpers, AragonApp, ACLHelpers {
*/
bytes32 public constant CREATE_PERMISSIONS_ROLE = 0x0b719b33c83b8e5d300c521cb8b54ae9bd933996a14bef8c2f4e0285d2d2400a;

enum Op { NONE, EQ, NEQ, GT, LT, GTE, LTE, RET, NOT, AND, OR, XOR, IF_ELSE } // op types

struct Param {
uint8 id;
uint8 op;
uint240 value; // even though value is an uint240 it can store addresses
// in the case of 32 byte hashes losing 2 bytes precision isn't a huge deal
// op and id take less than 1 byte each so it can be kept in 1 sstore
}

uint8 internal constant BLOCK_NUMBER_PARAM_ID = 200;
uint8 internal constant TIMESTAMP_PARAM_ID = 201;
// 202 is unused
Expand Down
19 changes: 19 additions & 0 deletions contracts/acl/ACLParams.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* SPDX-License-Identitifer: MIT
*/

pragma solidity ^0.4.24;
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we add the MIT header here (e.g. see https://github.com/aragon/aragonOS/blob/dev/contracts/acl/IACL.sol#L1)?

Copy link
Author

Choose a reason for hiding this comment

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

Done! ;) Is there a rule of thumb on which file an MIT header should be added?

Copy link
Contributor

Choose a reason for hiding this comment

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

I need to write more documentation on this, but on all the unpinned contracts (since they're the only ones meant to be used by apps, which frees them up to use any licensing model they want).



contract ACLParams {

enum Op { NONE, EQ, NEQ, GT, LT, GTE, LTE, RET, NOT, AND, OR, XOR, IF_ELSE } // op types

struct Param {
uint8 id;
uint8 op;
uint240 value; // even though value is an uint240 it can store addresses
// in the case of 32 byte hashes losing 2 bytes precision isn't a huge deal
// op and id take less than 1 byte each so it can be kept in 1 sstore
}
}
30 changes: 28 additions & 2 deletions contracts/acl/ACLSyntaxSugar.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@

pragma solidity ^0.4.24;

import "./ACLParams.sol";


contract ACLSyntaxSugar {
function arr() internal pure returns (uint256[]) {}
function arr() internal pure returns (uint256[]) {
// solium-disable-previous-line no-empty-blocks
}

function arr(bytes32 _a) internal pure returns (uint256[] r) {
return arr(uint256(_a));
Expand Down Expand Up @@ -85,7 +89,7 @@ contract ACLSyntaxSugar {
}


contract ACLHelpers {
contract ACLHelpers is ACLParams {
function decodeParamOp(uint256 _x) internal pure returns (uint8 b) {
return uint8(_x >> (8 * 30));
}
Expand All @@ -99,4 +103,26 @@ contract ACLHelpers {
b = uint32(_x >> (8 * 4));
c = uint32(_x >> (8 * 8));
}

function encodeParams(Param[] params) internal pure returns (uint256[]) {
uint256[] memory encodedParams = new uint256[](params.length);

for (uint i = 0; i < params.length; i++) {
encodedParams[i] = encodeParam(params[i]);
}

return encodedParams;
}

function encodeParam(Param param) internal pure returns (uint256) {
return uint256(param.id) << 248 | uint256(param.op) << 240 | param.value;
}

function encodeOperator(uint256 param1, uint256 param2) internal pure returns (uint240) {
return uint240(param1 + (param2 << 32) + (0 << 64));
}

function encodeIfElse(uint256 condition, uint256 successParam, uint256 failureParam) internal pure returns (uint240) {
return uint240(condition + (successParam << 32) + (failureParam << 64));
}
}
2 changes: 1 addition & 1 deletion contracts/apps/AppProxyUpgradeable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ contract AppProxyUpgradeable is AppProxyBase {
AppProxyBase(_kernel, _appId, _initializePayload)
public // solium-disable-line visibility-first
{

// solium-disable-previous-line no-empty-blocks
}

/**
Expand Down
156 changes: 156 additions & 0 deletions contracts/common/SafeERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Inspired by AdEx (https://github.com/AdExNetwork/adex-protocol-eth/blob/b9df617829661a7518ee10f4cb6c4108659dd6d5/contracts/libs/SafeERC20.sol)
// and 0x (https://github.com/0xProject/0x-monorepo/blob/737d1dc54d72872e24abce5a1dbe1b66d35fa21a/contracts/protocol/contracts/protocol/AssetProxy/ERC20Proxy.sol#L143)

pragma solidity ^0.4.24;

import "../lib/token/ERC20.sol";


library SafeERC20 {
// Before 0.5, solidity has a mismatch between `address.transfer()` and `token.transfer()`:
// https://github.com/ethereum/solidity/issues/3544
bytes4 private constant TRANSFER_SELECTOR = 0xa9059cbb;

string private constant ERROR_TOKEN_BALANCE_REVERTED = "SAFE_ERC_20_BALANCE_REVERTED";
string private constant ERROR_TOKEN_ALLOWANCE_REVERTED = "SAFE_ERC_20_ALLOWANCE_REVERTED";

function invokeAndCheckSuccess(address _addr, bytes memory _calldata)
private
returns (bool)
{
bool ret;
assembly {
let ptr := mload(0x40) // free memory pointer

let success := call(
gas, // forward all gas
_addr, // address
0, // no value
add(_calldata, 0x20), // calldata start
mload(_calldata), // calldata length
ptr, // write output over free memory
0x20 // uint256 return
)

if gt(success, 0) {
// Check number of bytes returned from last function call
switch returndatasize

// No bytes returned: assume success
case 0 {
ret := 1
}

// 32 bytes returned: check if non-zero
case 0x20 {
// Only return success if returned data was true
// Already have output in ptr
ret := eq(mload(ptr), 1)
}

// Not sure what was returned: don't mark as success
default { }
}
}
return ret;
}

function staticInvoke(address _addr, bytes memory _calldata)
private
view
returns (bool, uint256)
{
bool success;
uint256 ret;
assembly {
let ptr := mload(0x40) // free memory pointer

success := staticcall(
gas, // forward all gas
_addr, // address
add(_calldata, 0x20), // calldata start
mload(_calldata), // calldata length
ptr, // write output over free memory
0x20 // uint256 return
)

if gt(success, 0) {
ret := mload(ptr)
}
}
return (success, ret);
}

/**
* @dev Same as a standards-compliant ERC20.transfer() that never reverts (returns false).
* Note that this makes an external call to the token.
*/
function safeTransfer(ERC20 _token, address _to, uint256 _amount) internal returns (bool) {
bytes memory transferCallData = abi.encodeWithSelector(
TRANSFER_SELECTOR,
_to,
_amount
);
return invokeAndCheckSuccess(_token, transferCallData);
}

/**
* @dev Same as a standards-compliant ERC20.transferFrom() that never reverts (returns false).
* Note that this makes an external call to the token.
*/
function safeTransferFrom(ERC20 _token, address _from, address _to, uint256 _amount) internal returns (bool) {
bytes memory transferFromCallData = abi.encodeWithSelector(
_token.transferFrom.selector,
_from,
_to,
_amount
);
return invokeAndCheckSuccess(_token, transferFromCallData);
}

/**
* @dev Same as a standards-compliant ERC20.approve() that never reverts (returns false).
* Note that this makes an external call to the token.
*/
function safeApprove(ERC20 _token, address _spender, uint256 _amount) internal returns (bool) {
bytes memory approveCallData = abi.encodeWithSelector(
_token.approve.selector,
_spender,
_amount
);
return invokeAndCheckSuccess(_token, approveCallData);
}

/**
* @dev Static call into ERC20.balanceOf().
* Reverts if the call fails for some reason (should never fail).
*/
function staticBalanceOf(ERC20 _token, address _owner) internal view returns (uint256) {
bytes memory balanceOfCallData = abi.encodeWithSelector(
_token.balanceOf.selector,
_owner
);

(bool success, uint256 tokenBalance) = staticInvoke(_token, balanceOfCallData);
require(success, ERROR_TOKEN_BALANCE_REVERTED);

return tokenBalance;
}

/**
* @dev Static call into ERC20.allowance().
* Reverts if the call fails for some reason (should never fail).
*/
function staticAllowance(ERC20 _token, address _owner, address _spender) internal view returns (uint256) {
bytes memory allowanceCallData = abi.encodeWithSelector(
_token.allowance.selector,
_owner,
_spender
);

(bool success, uint256 allowance) = staticInvoke(_token, allowanceCallData);
require(success, ERROR_TOKEN_ALLOWANCE_REVERTED);

return allowance;
}
}
9 changes: 7 additions & 2 deletions contracts/common/VaultRecoverable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ import "../lib/token/ERC20.sol";
import "./EtherTokenConstant.sol";
import "./IsContract.sol";
import "./IVaultRecoverable.sol";
import "./SafeERC20.sol";


contract VaultRecoverable is IVaultRecoverable, EtherTokenConstant, IsContract {
using SafeERC20 for ERC20;

string private constant ERROR_DISALLOWED = "RECOVER_DISALLOWED";
string private constant ERROR_VAULT_NOT_CONTRACT = "RECOVER_VAULT_NOT_CONTRACT";
string private constant ERROR_TOKEN_TRANSFER_FAILED = "RECOVER_TOKEN_TRANSFER_FAILED";

/**
* @notice Send funds to recovery Vault. This contract should never receive funds,
Expand All @@ -27,8 +31,9 @@ contract VaultRecoverable is IVaultRecoverable, EtherTokenConstant, IsContract {
if (_token == ETH) {
vault.transfer(address(this).balance);
} else {
uint256 amount = ERC20(_token).balanceOf(this);
ERC20(_token).transfer(vault, amount);
ERC20 token = ERC20(_token);
uint256 amount = token.staticBalanceOf(this);
require(token.safeTransfer(vault, amount), ERROR_TOKEN_TRANSFER_FAILED);
}
}

Expand Down
7 changes: 5 additions & 2 deletions contracts/kernel/IKernel.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ import "../acl/IACL.sol";
import "../common/IVaultRecoverable.sol";


// This should be an interface, but interfaces can't inherit yet :(
contract IKernel is IVaultRecoverable {
interface IKernelEvents {
event SetApp(bytes32 indexed namespace, bytes32 indexed appId, address app);
}


// This should be an interface, but interfaces can't inherit yet :(
contract IKernel is IKernelEvents, IVaultRecoverable {
function acl() public view returns (IACL);
function hasPermission(address who, address where, bytes32 what, bytes how) public view returns (bool);

Expand Down
8 changes: 7 additions & 1 deletion contracts/kernel/KernelProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import "../common/DepositableDelegateProxy.sol";
import "../common/IsContract.sol";


contract KernelProxy is KernelStorage, KernelAppIds, KernelNamespaceConstants, IsContract, DepositableDelegateProxy {
contract KernelProxy is IKernelEvents, KernelStorage, KernelAppIds, KernelNamespaceConstants, IsContract, DepositableDelegateProxy {
/**
* @dev KernelProxy is a proxy contract to a kernel implementation. The implementation
* can update the reference, which effectively upgrades the contract
Expand All @@ -16,6 +16,12 @@ contract KernelProxy is KernelStorage, KernelAppIds, KernelNamespaceConstants, I
constructor(IKernel _kernelImpl) public {
require(isContract(address(_kernelImpl)));
apps[KERNEL_CORE_NAMESPACE][KERNEL_CORE_APP_ID] = _kernelImpl;

// Note that emitting this event is important for verifying that a KernelProxy instance
// was never upgraded to a malicious Kernel logic contract over its lifespan.
// This starts the "chain of trust", that can be followed through later SetApp() events
// emitted during kernel upgrades.
emit SetApp(KERNEL_CORE_NAMESPACE, KERNEL_CORE_APP_ID, _kernelImpl);
}

/**
Expand Down
42 changes: 42 additions & 0 deletions contracts/test/TestACLHelpers.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
pragma solidity 0.4.24;

import "./helpers/Assert.sol";
import "../acl/ACLSyntaxSugar.sol";
import "../acl/ACL.sol";


contract TestACLHelpers is ACL {

function testEncodeParam() public {
Param memory param = Param(2, uint8(Op.EQ), 5294967297);

uint256 encodedParam = encodeParam(param);

(uint32 id, uint32 op, uint32 value) = decodeParamsList(encodedParam);

Assert.equal(uint256(param.id), uint256(id), "Encoded id is not equal");
Assert.equal(uint256(param.op), uint256(op), "Encoded op is not equal");
Assert.equal(uint256(param.value), uint256(value), "Encoded value is not equal");
}

function testEncodeParams() public {
Param[] memory params = new Param[](4);

params[0] = Param(LOGIC_OP_PARAM_ID, uint8(Op.IF_ELSE), encodeIfElse(1, 2, 3));
params[1] = Param(LOGIC_OP_PARAM_ID, uint8(Op.AND), encodeOperator(2, 3));
params[2] = Param(2, uint8(Op.EQ), 1);
params[3] = Param(3, uint8(Op.NEQ), 2);

uint256[] memory encodedParam = encodeParams(params);

for (uint256 i = 0; i < 4; i++) {
(uint32 id, uint32 op, uint32 value) = decodeParamsList(encodedParam[i]);

Assert.equal(uint256(params[i].id), uint256(id), "Encoded id is not equal");
Assert.equal(uint256(params[i].op), uint256(op), "Encoded op is not equal");
Assert.equal(uint256(params[i].value), uint256(value), "Encoded value is not equal");
}

}

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's test encodeOperator and encodeIfElse as well :)

Copy link
Author

Choose a reason for hiding this comment

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

Done :)

Copy link
Author

Choose a reason for hiding this comment

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

I noticed that the encodeOperator and encodeIfElse functions are in a separate file in the test folder. Do you think it would be a good idea if I move them to the ACLHelpers contract?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I think it's a good idea, but let's wait for @sohkai opinion, maybe there is a reason I don't know for them to be there.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah I didn't notice they were only in the tests.

I think moving them could be helpful; AFAIK they're not simply because we only used them in test functionality. We should either add comments to explain how they work or make encodeOperator() more explicit (it's a bit confusing at first why it builds off of encodeIfElse()).

}
4 changes: 2 additions & 2 deletions contracts/test/TestACLInterpreter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ pragma solidity 0.4.24;

import "../acl/ACL.sol";
import "./helpers/Assert.sol";
import "./helpers/ACLHelper.sol";
import "./helpers/ACLOracleHelper.sol";


contract TestACLInterpreter is ACL, ACLHelper {
contract TestACLInterpreter is ACL {
function testEqualityUint() public {
// Assert param 0 is equal to 10, given that params are [10, 11]
assertEval(arr(uint256(10), 11), 0, Op.EQ, 10, true);
Expand Down
Loading