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

ACL: optimize checkOracle() #500

Open
wants to merge 3 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
50 changes: 29 additions & 21 deletions contracts/acl/ACL.sol
Original file line number Diff line number Diff line change
Expand Up @@ -419,31 +419,39 @@ contract ACL is IACL, TimeHelpers, AragonApp, ACLHelpers {
function checkOracle(IACLOracle _oracleAddr, address _who, address _where, bytes32 _what, uint256[] _how) internal view returns (bool) {
bytes4 sig = _oracleAddr.canPerform.selector;

// a raw call is required so we can return false if the call reverts, rather than reverting
bytes memory checkCalldata = abi.encodeWithSelector(sig, _who, _where, _what, _how);
uint256 oracleCheckGas = ORACLE_CHECK_GAS;

bool ok;
assembly {
ok := staticcall(oracleCheckGas, _oracleAddr, add(checkCalldata, 0x20), mload(checkCalldata), 0, 0)
}

if (!ok) {
return false;
}

uint256 size;
assembly { size := returndatasize }
if (size != 32) {
return false;
}

bool result;

assembly {
let ptr := mload(0x40) // get next free memory ptr
returndatacopy(ptr, 0, size) // copy return from above `staticcall`
result := mload(ptr) // read data at ptr and set it to result
mstore(ptr, 0) // set pointer memory to 0 so it still is the next free ptr
let ptr := mload(0x40) // free memory pointer

// A raw staticcall is required so we can return false if the call reverts, rather than reverting
result := staticcall(
oracleCheckGas, // gas forwarded
_oracleAddr, // address
add(checkCalldata, 0x20), // calldata start
mload(checkCalldata), // calldata length
ptr, // write output over free memory
0x20 // uint256 return
)

// solidity-coverage fails on assembly `if`
Copy link
Contributor

Choose a reason for hiding this comment

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

🤷‍♂️

switch result case 0 { }
default {
// Check number of bytes returned from last external call
switch returndatasize

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

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

return result;
Expand Down
4 changes: 2 additions & 2 deletions contracts/common/SafeERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ library SafeERC20 {
add(_calldata, 0x20), // calldata start
mload(_calldata), // calldata length
ptr, // write output over free memory
0x20 // uint256 return
0x20 // write 32 bytes
)

if gt(success, 0) {
Expand All @@ -41,7 +41,7 @@ library SafeERC20 {
ret := 1
}

// 32 bytes returned: check if non-zero
// 32 bytes returned: check if true
case 0x20 {
// Only return success if returned data was true
// Already have output in ptr
Expand Down
1 change: 1 addition & 0 deletions contracts/test/TestACLInterpreter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ contract TestACLInterpreter is ACL, ACLHelper {
assertEval(arr(), ORACLE_PARAM_ID, Op.EQ, uint256(new StateModifyingOracle()), false);
// if returned data size is not correct, returns false
assertEval(arr(), ORACLE_PARAM_ID, Op.EQ, uint256(new EmptyDataReturnOracle()), false);
assertEval(arr(), ORACLE_PARAM_ID, Op.EQ, uint256(new LargeDataReturnOracle()), false);

// conditional oracle returns true if first param > 0
ConditionalOracle conditionalOracle = new ConditionalOracle();
Expand Down
12 changes: 12 additions & 0 deletions contracts/test/helpers/ACLHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ contract EmptyDataReturnOracle is IACLOracle {
}
}

contract LargeDataReturnOracle is IACLOracle {
function canPerform(address, address, bytes32, uint256[]) external view returns (bool) {
uint256[] memory largeData = new uint256[](2);
largeData[0] = 1;
largeData[1] = 2;
assembly {
// Return two uint256s
return(largeData, 0x40)
}
}
}

contract ConditionalOracle is IACLOracle {
function canPerform(address, address, bytes32, uint256[] how) external view returns (bool) {
return how[0] > 0;
Expand Down