Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Agent app: EVM #580

Merged
merged 45 commits into from
Feb 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
0d01aaf
Actor app: inherit Vault and test vault functionality
izqui Nov 15, 2018
1fa6386
Actor app: implement execute action
izqui Nov 16, 2018
7a4dd5b
Actor app: implement permissioned forwarding
izqui Nov 16, 2018
9830981
Actor app: improve tests
izqui Nov 16, 2018
7457eb3
Actor app: signatures
izqui Nov 19, 2018
af478c1
CI: travis wait on npm install
izqui Nov 19, 2018
ebb10aa
Actor app: lint and enable CI
izqui Nov 19, 2018
e132297
Add max gas for static calls
izqui Nov 26, 2018
3ad9920
chore: upgrade to web3-eth-abi@1.0.0-beta.38
sohkai Jan 26, 2019
ea9a5aa
Address review comments
izqui Jan 26, 2019
8e40798
Lint
izqui Feb 4, 2019
2aaf462
test: add comments, cosmetic changes, and more tests
sohkai Feb 7, 2019
c73d999
SignatureValidator: fix compile warnings
sohkai Feb 7, 2019
5f1291f
test: add context headers
sohkai Feb 7, 2019
f17a0f5
Metadata: add roles to arapp, remove webapp artifacts
izqui Feb 11, 2019
f6f66f7
Radspec strings
izqui Feb 11, 2019
eb95db8
Travis: allow actor coverage to fail, tracking fix in #658
izqui Feb 11, 2019
abf1c91
Prevent infinite loop by prohibiting setting itself as the designated…
izqui Feb 11, 2019
a10c62c
Radspec
izqui Feb 11, 2019
cc21b67
Actor -> Agent rename
izqui Feb 11, 2019
139ad8a
Add status to arapp and rename constants
izqui Feb 12, 2019
fe86092
Update ERC1271 implementation
izqui Feb 12, 2019
1849286
Add ERC1271 interfaceId
izqui Feb 12, 2019
1b7b735
Rename constants
izqui Feb 12, 2019
d960fee
Rename calldata
izqui Feb 12, 2019
39b6b7e
Refactor SignatureValidator.sol
izqui Feb 12, 2019
de9d4a6
Refactor signature validation tests and test EIP712 signatures
izqui Feb 12, 2019
474f0c1
Handle empty signatures and undefined signature modes
izqui Feb 12, 2019
345a9d8
Refactor SignatureValidator: handle ERC1271 checks in library
izqui Feb 13, 2019
499cdcb
Test ERC1271 signature wrapping
izqui Feb 13, 2019
1b76fb9
cosmetic: fix whitespace
sohkai Feb 13, 2019
98369d1
Merge branch 'next' into actor-app
sohkai Feb 13, 2019
a0bddef
Agent: always allow execute to transfer ETH (#651)
sohkai Feb 13, 2019
e8eb268
cosmetic: fix whitespace and add EIPs hash
sohkai Feb 13, 2019
2225408
chore: upgrade aragonOS and use test-helper's contracts
sohkai Feb 13, 2019
2ed4a2a
cosmetic: fix compile errors
sohkai Feb 13, 2019
c482981
cosmetic: fix linter
sohkai Feb 13, 2019
cd9614b
chore: fix travis allowed to fail matrix
sohkai Feb 13, 2019
970431a
test: ignore all test contracts from coverage
sohkai Feb 13, 2019
cff0689
Actor: reuse Vault tests (#668)
sohkai Feb 14, 2019
905c32d
Move agent from future-apps/ to apps/
izqui Feb 15, 2019
0d3e2ec
Refactor popFirstByte to copy to a new array
izqui Feb 15, 2019
5b7a5fe
Address last review comments
izqui Feb 15, 2019
2a293d1
Lint
izqui Feb 15, 2019
687e29d
Address very last review comments
izqui Feb 15, 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
7 changes: 7 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ cache:
- apps/vault/node_modules
- apps/voting/node_modules
- apps/voting/app/node_modules
- future-apps/agent/node_modules
- future-apps/payroll/node_modules
- shared/migrations/node_modules
- shared/minime/node_modules
- shared/test-helpers/node_modules
Expand All @@ -24,12 +26,14 @@ env:
- INSTALL_FRONTEND=false
matrix:
- TASK=lint
- TASK=test:agent
- TASK=test:finance
- TASK=test:survey
- TASK=test:token-manager
- TASK=test:token-manager:app INSTALL_FRONTEND=true
- TASK=test:vault
- TASK=test:voting
- TASK=coverage:agent
- TASK=coverage:finance
- TASK=coverage:survey
- TASK=coverage:token-manager
Expand All @@ -38,7 +42,10 @@ env:
- TASK=test:shared
matrix:
allow_failures:
- env: TASK=coverage:agent
- env: TASK=coverage:finance
install:
- travis_wait 60 npm install
before_script:
- npm prune
script:
Expand Down
7 changes: 7 additions & 0 deletions apps/agent/.solcover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
norpc: true,
copyPackages: ['@aragon/os', '@aragon/apps-vault'],
skipFiles: [
'test'
]
}
1 change: 1 addition & 0 deletions apps/agent/.soliumignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
contracts/test
23 changes: 23 additions & 0 deletions apps/agent/.soliumrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"extends": "solium:all",
"rules": {
"imports-on-top": ["error"],
"variable-declarations": ["error"],
"array-declarations": ["error"],
"operator-whitespace": ["error"],
"lbrace": ["error"],
"mixedcase": 0,
"camelcase": ["error"],
"uppercase": 0,
"no-empty-blocks": ["error"],
"no-unused-vars": ["error"],
"quotes": ["error"],
"indentation": 0,
"whitespace": ["error"],
"deprecated-suicide": ["error"],
"arg-overflow": ["error", 8],
"pragma-on-top": ["error"],
"security/enforce-explicit-visibility": ["error"],
"error-reason": 0
}
}
63 changes: 63 additions & 0 deletions apps/agent/arapp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"status": "experimental",
"environments": {
"default": {
"registry": "0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1",
"appName": "agent.aragonpm.eth",
"network": "rpc"
},
"rinkeby": {
"registry": "0x98Df287B6C145399Aaa709692c8D308357bC085D",
"appName": "agent.aragonpm.eth",
"network": "rinkeby"
},
"staging": {
"registry": "0xfe03625ea880a8cba336f9b5ad6e15b0a3b5a939",
"appName": "agent.aragonpm.eth",
"network": "rinkeby"
},
"mainnet": {
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
"appName": "agent.aragonpm.eth",
"network": "mainnet"
},
"rinkeby-old": {
"registry": "0xfbae32d1cde62858bc45f51efc8cc4fa1415447e",
"appName": "agent.aragonpm.eth",
"network": "rinkeby"
}
},
"roles": [
{
"name": "Execute actions",
"id": "EXECUTE_ROLE",
"params": [
"Target address",
"ETH value",
"Signature"
]
},
{
"name": "Designate signer",
"id": "DESIGNATE_SIGNER_ROLE",
"params": [
"Designated signer address"
]
},
{
"name": "Presign hash",
"id": "ADD_PRESIGNED_HASH_ROLE",
"params": [
"Hash"
]
},
{
"name": "Run EVM Script",
"id": "RUN_SCRIPT_ROLE",
"params": [
"EVM Script"
]
}
],
"path": "contracts/Agent.sol"
}
1 change: 1 addition & 0 deletions apps/agent/contracts/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/test
147 changes: 147 additions & 0 deletions apps/agent/contracts/Agent.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* SPDX-License-Identitifer: GPL-3.0-or-later
*/

pragma solidity 0.4.24;

import "./SignatureValidator.sol";
import "./standards/IERC165.sol";
import "./standards/ERC1271.sol";

import "@aragon/apps-vault/contracts/Vault.sol";

import "@aragon/os/contracts/common/IForwarder.sol";


contract Agent is IERC165, ERC1271Bytes, IForwarder, IsContract, Vault {
bytes32 public constant EXECUTE_ROLE = keccak256("EXECUTE_ROLE");
bytes32 public constant RUN_SCRIPT_ROLE = keccak256("RUN_SCRIPT_ROLE");
bytes32 public constant ADD_PRESIGNED_HASH_ROLE = keccak256("ADD_PRESIGNED_HASH_ROLE");
bytes32 public constant DESIGNATE_SIGNER_ROLE = keccak256("DESIGNATE_SIGNER_ROLE");

bytes4 private constant ERC165_INTERFACE_ID = 0x01ffc9a7;

string private constant ERROR_EXECUTE_ETH_NO_DATA = "AGENT_EXEC_ETH_NO_DATA";
string private constant ERROR_EXECUTE_TARGET_NOT_CONTRACT = "AGENT_EXEC_TARGET_NO_CONTRACT";
string private constant ERROR_DESIGNATED_TO_SELF = "AGENT_DESIGNATED_TO_SELF";

mapping (bytes32 => bool) public isPresigned;
address public designatedSigner;

event Execute(address indexed sender, address indexed target, uint256 ethValue, bytes data);
event PresignHash(address indexed sender, bytes32 indexed hash);
event SetDesignatedSigner(address indexed sender, address indexed oldSigner, address indexed newSigner);

/**
* @notice Execute '`@radspec(_target, _data)`' on `_target``_ethValue == 0 ? '' : ' (Sending' + @tokenAmount(_ethValue, 0x00) + ')'`
* @param _target Address where the action is being executed
* @param _ethValue Amount of ETH from the contract that is sent with the action
* @param _data Calldata for the action
* @return Exits call frame forwarding the return data of the executed call (either error or success data)
*/
function execute(address _target, uint256 _ethValue, bytes _data)
external // This function MUST always be external as the function performs a low level return, exiting the Agent app execution context
authP(EXECUTE_ROLE, arr(_target, _ethValue, uint256(getSig(_data)))) // TODO: Test that sig bytes are the least significant bytes
{
bool result = _target.call.value(_ethValue)(_data);

if (result) {
emit Execute(msg.sender, _target, _ethValue, _data);
}

assembly {
let size := returndatasize
let ptr := mload(0x40)
returndatacopy(ptr, 0, size)

// revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas.
// if the call returned error data, forward it
switch result case 0 { revert(ptr, size) }
default { return(ptr, size) }
}
}

/**
* @notice Set `_designatedSigner` as the designated signer of the app, which will be able to sign messages on behalf of the app
* @param _designatedSigner Address that will be able to sign messages on behalf of the app
*/
function setDesignatedSigner(address _designatedSigner)
external
authP(DESIGNATE_SIGNER_ROLE, arr(_designatedSigner))
{
// Prevent an infinite loop by setting the app itself as its designated signer.
// An undetectable loop can be created by setting a different contract as the
// designated signer which calls back into `isValidSignature`.
// Given that `isValidSignature` is always called with just 50k gas, the max
// damage of the loop is wasting 50k gas.
require(_designatedSigner != address(this), ERROR_DESIGNATED_TO_SELF);

address oldDesignatedSigner = designatedSigner;
designatedSigner = _designatedSigner;

emit SetDesignatedSigner(msg.sender, oldDesignatedSigner, _designatedSigner);
}

/**
* @notice Pre-sign hash `_hash`
* @param _hash Hash that will be considered signed regardless of the signature checked with 'isValidSignature()'
*/
function presignHash(bytes32 _hash)
external
authP(ADD_PRESIGNED_HASH_ROLE, arr(_hash))
{
isPresigned[_hash] = true;

emit PresignHash(msg.sender, _hash);
}

function isForwarder() external pure returns (bool) {
return true;
}

function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
return
interfaceId == ERC1271_INTERFACE_ID ||
interfaceId == ERC165_INTERFACE_ID;
}

/**
* @notice Execute the script as the Agent app
* @dev IForwarder interface conformance. Forwards any token holder action.
* @param _evmScript Script being executed
*/
function forward(bytes _evmScript)
public
authP(RUN_SCRIPT_ROLE, arr(getScriptACLParam(_evmScript)))
{
bytes memory input = ""; // no input
address[] memory blacklist = new address[](0); // no addr blacklist, can interact with anything
runScript(_evmScript, input, blacklist);
// We don't need to emit an event here as EVMScriptRunner will emit ScriptResult if successful
}

function isValidSignature(bytes32 hash, bytes signature) public view returns (bytes4) {
// Short-circuit in case the hash was presigned. Optimization as performing calls
// and ecrecover is more expensive than an SLOAD.
if (isPresigned[hash]) {
return returnIsValidSignatureMagicNumber(true);
}

bool isValid = SignatureValidator.isValidSignature(hash, designatedSigner, signature);
return returnIsValidSignatureMagicNumber(isValid);
}

function canForward(address sender, bytes evmScript) public view returns (bool) {
uint256[] memory params = new uint256[](1);
params[0] = getScriptACLParam(evmScript);
return canPerform(sender, RUN_SCRIPT_ROLE, params);
}

function getScriptACLParam(bytes evmScript) internal pure returns (uint256) {
return uint256(keccak256(abi.encodePacked(evmScript)));
}

function getSig(bytes data) internal pure returns (bytes4 sig) {
assembly { sig := add(data, 0x20) }
}
}
Loading