Skip to content

Commit

Permalink
added a check to verify that ProtocolControl calls are delegated. Cle…
Browse files Browse the repository at this point in the history
…aned up some naming / variable schemes
  • Loading branch information
thogard785 committed Aug 2, 2023
1 parent 67b153f commit 85a0cd6
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 45 deletions.
2 changes: 1 addition & 1 deletion src/contracts/atlas/ExecutionEnvironment.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ contract ExecutionEnvironment is Test {
require(msg.sender == atlas && userCall.from == _user(), "ERR-CE00 InvalidSenderStaging");

bytes memory stagingData = abi.encodeWithSelector(
IProtocolControl.stageCall.selector, userCall.to, userCall.from, bytes4(userCall.data), userCall.data[4:]
IProtocolControl.stagingCall.selector, userCall.to, userCall.from, bytes4(userCall.data), userCall.data[4:]
);

stagingData = abi.encodePacked(
Expand Down
58 changes: 29 additions & 29 deletions src/contracts/atlas/TokenTransfers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,33 @@ import {ProtocolCall} from "../types/CallTypes.sol";
abstract contract TokenTransfers {
using SafeTransferLib for ERC20;

uint16 internal constant _EXECUTION_PHASE_OFFSET = uint16(type(BaseLock).max);

// NOTE: No user transfers allowed during UserRefund or HandlingPayments
uint16 internal constant _SAFE_USER_TRANSFER = uint16(
1 << (_EXECUTION_PHASE_OFFSET + uint16(ExecutionPhase.Staging)) |
1 << (_EXECUTION_PHASE_OFFSET + uint16(ExecutionPhase.UserCall)) |
1 << (_EXECUTION_PHASE_OFFSET + uint16(ExecutionPhase.Verification))
);

// NOTE: No protocol transfers allowed during UserCall
uint16 internal constant _SAFE_PROTOCOL_TRANSFER = uint16(
1 << (_EXECUTION_PHASE_OFFSET + uint16(ExecutionPhase.Staging)) |
1 << (_EXECUTION_PHASE_OFFSET + uint16(ExecutionPhase.HandlingPayments)) |
1 << (_EXECUTION_PHASE_OFFSET + uint16(ExecutionPhase.UserRefund)) |
1 << (_EXECUTION_PHASE_OFFSET + uint16(ExecutionPhase.Verification))
);

// Virtual Functions defined by other Atlas modules
function _getExecutionEnvironmentCustom(address user, bytes32 controlCodeHash, address protocolControl, uint16 callConfig)
internal
view
virtual
returns (address environment);

function _getLockState() internal view virtual returns (EscrowKey memory);

// Transfer functions
function transferUserERC20(
address token,
address destination,
Expand All @@ -18,9 +45,10 @@ abstract contract TokenTransfers {
uint16 callConfig
) external {
// Verify that the caller is legitimate
// NOTE: Use the *current* protocolControl's codehash to help mitigate social engineering bamboozles.
require(msg.sender == _getExecutionEnvironmentCustom(user, protocolControl.codehash, protocolControl, callConfig), "ERR-T001 ProtocolTransfer");

// Verify that the protocol is in control of the ExecutionEnvironment
// Verify that the user is in control (or approved the protocol's control) of the ExecutionEnvironment
require(_getLockState().lockState & _SAFE_USER_TRANSFER != 0, "ERR-T002 ProtocolTransfer");

// Transfer token
Expand All @@ -44,32 +72,4 @@ abstract contract TokenTransfers {
// Transfer token
ERC20(token).safeTransferFrom(protocolControl, destination, amount);
}

uint16 internal constant _EXECUTION_PHASE_OFFSET = uint16(type(BaseLock).max);
uint16 internal constant _SAFETY_LEVEL_OFFSET = uint16(type(BaseLock).max) + uint16(type(ExecutionPhase).max);

uint16 internal constant _SAFE_USER_TRANSFER = uint16(
1 << (_EXECUTION_PHASE_OFFSET + uint16(ExecutionPhase.Staging)) |
1 << (_EXECUTION_PHASE_OFFSET + uint16(ExecutionPhase.UserCall)) |
1 << (_EXECUTION_PHASE_OFFSET + uint16(ExecutionPhase.Verification))
);

uint16 internal constant _SAFE_PROTOCOL_TRANSFER = uint16(
1 << (_EXECUTION_PHASE_OFFSET + uint16(ExecutionPhase.Staging)) |
1 << (_EXECUTION_PHASE_OFFSET + uint16(ExecutionPhase.HandlingPayments)) |
1 << (_EXECUTION_PHASE_OFFSET + uint16(ExecutionPhase.UserRefund)) |
1 << (_EXECUTION_PHASE_OFFSET + uint16(ExecutionPhase.Verification))
);

function _isSafeUserTransfer() internal view returns (bool) {
return _getLockState().lockState & _SAFE_USER_TRANSFER != 0;
}

function _getExecutionEnvironmentCustom(address user, bytes32 controlCodeHash, address protocolControl, uint16 callConfig)
internal
view
virtual
returns (address environment);

function _getLockState() internal view virtual returns (EscrowKey memory);
}
2 changes: 1 addition & 1 deletion src/contracts/interfaces/IProtocolControl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.16;
import "../types/CallTypes.sol";

interface IProtocolControl {
function stageCall(address to, address from, bytes4 userSelector, bytes calldata userData)
function stagingCall(address to, address from, bytes4 userSelector, bytes calldata userData)
external
returns (bytes memory);

Expand Down
4 changes: 2 additions & 2 deletions src/contracts/libraries/CallVerification.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ library CallVerification {
bytes32(0), // initial hash = null
protocolCall.to,
abi.encodeWithSelector(
IProtocolControl.stageCall.selector,
IProtocolControl.stagingCall.selector,
userCall.to,
userCall.from,
bytes4(userCall.data),
Expand Down Expand Up @@ -93,7 +93,7 @@ library CallVerification {
bytes32(0), // initial hash = null
protocolCall.to,
abi.encodeWithSelector(
IProtocolControl.stageCall.selector,
IProtocolControl.stagingCall.selector,
userCall.to,
userCall.from,
bytes4(userCall.data),
Expand Down
2 changes: 2 additions & 0 deletions src/contracts/protocol/ExecutionBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ contract ExecutionBase {
// via delegatecall, but can be added to ProtocolControl as funcs that
// can be used during ProtocolControl's delegated funcs

// Returns the address(ProtocolControl).codehash for the calling
// ExecutionEnvironment's ProtocolControl
function _controlCodeHash() internal pure returns (bytes32 controlCodeHash) {
assembly {
controlCodeHash := calldataload(sub(calldatasize(), 32))
Expand Down
11 changes: 9 additions & 2 deletions src/contracts/protocol/GovernanceControl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ import {ISafetyLocks} from "../interfaces/ISafetyLocks.sol";
import "../types/CallTypes.sol";

abstract contract GovernanceControl {

address internal immutable _executionBase;

constructor () {
_executionBase = address(this);
}

string internal constant _NOT_IMPLEMENTED = "NOT IMPLEMENTED";
// Virtual functions to be overridden by participating protocol governance
// (not FastLane) prior to deploying contract. Note that protocol governance
Expand All @@ -21,7 +28,7 @@ abstract contract GovernanceControl {
// bytes calldata userCallData
//

// _stageCall
// _stagingCall
// Details:
// staging/delegate =
// Inputs: User's calldata
Expand All @@ -31,7 +38,7 @@ abstract contract GovernanceControl {
//
// Protocol exposure: Trustless
// User exposure: Trustless
function _stageCall(address to, address from, bytes4 userSelector, bytes calldata userData)
function _stagingCall(address to, address from, bytes4 userSelector, bytes calldata userData)
internal
virtual
returns (bytes memory stagingData);
Expand Down
45 changes: 37 additions & 8 deletions src/contracts/protocol/ProtocolControl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,9 @@ abstract contract ProtocolControl is Test, GovernanceControl, ExecutionBase {
bool shouldDelegateVerification,
bool allowRecycledStorage
) {
control = address(this);

sequenced = shouldRequireSequencedNonces;


if (shouldDelegateStaging) {
require(shouldRequireStaging, "ERR-GC04 InvalidStaging");
}
Expand All @@ -61,6 +59,7 @@ abstract contract ProtocolControl is Test, GovernanceControl, ExecutionBase {
// TODO: Consider allowing
}

control = address(this);
escrow = escrowAddress;
governance = governanceAddress;

Expand All @@ -76,6 +75,21 @@ abstract contract ProtocolControl is Test, GovernanceControl, ExecutionBase {

// Safety and support functions and modifiers that make the relationship between protocol
// and FastLane's backend trustless.
modifier mustBeDelegated() {
require(address(this) != control, "ERR-EB001 MustBeDelegated");
_;
}

modifier onlyApprovedDelegateCaller() {
require(msg.sender == escrow, "ERR-PC060 InvalidCaller");
_;
}

modifier onlyApprovedStandardCaller() {
require(msg.sender == ISafetyLocks(escrow).approvedCaller(), "ERR-PC061 InvalidCaller");
_;
}

modifier onlyApprovedCaller(bool isDelegated) {
if (isDelegated) {
require(msg.sender == escrow, "ERR-PC060 InvalidCaller");
Expand All @@ -85,23 +99,38 @@ abstract contract ProtocolControl is Test, GovernanceControl, ExecutionBase {
_;
}

function stageCall(address to, address from, bytes4 userSelector, bytes calldata userData)
function stagingCall(address to, address from, bytes4 userSelector, bytes calldata userData)
external
onlyApprovedCaller(delegateStaging)
mustBeDelegated
onlyApprovedDelegateCaller
returns (bytes memory)
{
return _stageCall(to, from, userSelector, userData);
return _stagingCall(to, from, userSelector, userData);
}

function userLocalCall(bytes calldata data) external onlyApprovedCaller(delegateUser) returns (bytes memory) {
function userLocalCall(bytes calldata data)
external
mustBeDelegated
onlyApprovedDelegateCaller
returns (bytes memory)
{
return delegateUser ? _userLocalDelegateCall(data) : _userLocalStandardCall(data);
}

function allocatingCall(bytes calldata data) external onlyApprovedCaller(true) {
function allocatingCall(bytes calldata data)
external
mustBeDelegated
onlyApprovedDelegateCaller
{
return _allocatingCall(data);
}

function verificationCall(bytes calldata data) external onlyApprovedCaller(delegateVerification) returns (bool) {
function verificationCall(bytes calldata data)
external
mustBeDelegated
onlyApprovedDelegateCaller
returns (bool)
{
return _verificationCall(data);
}

Expand Down
2 changes: 1 addition & 1 deletion src/contracts/v2-example/V2ProtocolControl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ contract V2ProtocolControl is ProtocolControl {
)
*/

function _stageCall(address to, address, bytes4 userSelector, bytes calldata userData)
function _stagingCall(address to, address, bytes4 userSelector, bytes calldata userData)
internal
override
returns (bytes memory)
Expand Down
2 changes: 1 addition & 1 deletion src/contracts/v4-example/V4ProtocolControl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ contract V4ProtocolControl is ProtocolControl {
/////////////////////////////////////////////////////////

/////////////// DELEGATED CALLS //////////////////
function _stageCall(address to, address from, bytes4 userSelector, bytes calldata userData)
function _stagingCall(address to, address from, bytes4 userSelector, bytes calldata userData)
internal
override
returns (bytes memory stagingData)
Expand Down

0 comments on commit 85a0cd6

Please sign in to comment.