Skip to content

Commit

Permalink
revert(protocol): revert AssignmentHook to production version with on…
Browse files Browse the repository at this point in the history
…ly an event removed (#17512)
  • Loading branch information
dantaik authored Jun 7, 2024
1 parent 1eb6439 commit a4a9b98
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 230 deletions.
167 changes: 162 additions & 5 deletions packages/protocol/contracts/L1/hooks/AssignmentHook.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,53 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import "../../common/EssentialContract.sol";
import "./AssignmentHookBase.sol";
import "../../common/LibStrings.sol";
import "../../libs/LibAddress.sol";
import "../ITaikoL1.sol";
import "./IHook.sol";

/// @title AssignmentHook
/// @notice A hook that handles prover assignment verification and fee processing.
/// @custom:security-contact [email protected]
contract AssignmentHook is EssentialContract, AssignmentHookBase, IHook {
contract AssignmentHook is EssentialContract, IHook {
using LibAddress for address;
using SignatureChecker for address;
using SafeERC20 for IERC20;

struct ProverAssignment {
address feeToken;
uint64 expiry;
uint64 maxBlockId;
uint64 maxProposedIn;
bytes32 metaHash;
bytes32 parentMetaHash;
TaikoData.TierFee[] tierFees;
bytes signature;
}

struct Input {
ProverAssignment assignment;
uint256 tip; // A tip to L1 block builder
}

event EtherPaymentFailed(address to, uint256 maxGas);

/// @notice Max gas paying the prover.
/// @dev This should be large enough to prevent the worst cases for the prover.
/// To assure a trustless relationship between the proposer and the prover it's
/// the prover's job to make sure it can get paid within this limit.
uint256 public constant MAX_GAS_PAYING_PROVER = 50_000;

uint256[50] private __gap;

error HOOK_ASSIGNMENT_EXPIRED();
error HOOK_ASSIGNMENT_INVALID_SIG();
error HOOK_TIER_NOT_FOUND();

/// @notice Initializes the contract.
/// @param _owner The owner of this contract. msg.sender will be used if this value is zero.
/// @param _addressManager The address of the {AddressManager} contract.
Expand All @@ -28,10 +66,129 @@ contract AssignmentHook is EssentialContract, AssignmentHookBase, IHook {
onlyFromNamed(LibStrings.B_TAIKO)
nonReentrant
{
_onBlockProposed(_blk, _meta, _data);
// Note that
// - 'msg.sender' is the TaikoL1 contract address
// - 'block.coinbase' is the L1 block builder
// - 'meta.coinbase' is the L2 block proposer (chosen by block's proposer)

Input memory input = abi.decode(_data, (Input));
ProverAssignment memory assignment = input.assignment;

// Check assignment validity
if (
block.timestamp > assignment.expiry
|| assignment.metaHash != 0 && _blk.metaHash != assignment.metaHash
|| assignment.parentMetaHash != 0 && _meta.parentMetaHash != assignment.parentMetaHash
|| assignment.maxBlockId != 0 && _meta.id > assignment.maxBlockId
|| assignment.maxProposedIn != 0 && block.number > assignment.maxProposedIn
) {
revert HOOK_ASSIGNMENT_EXPIRED();
}

// Hash the assignment with the blobHash, this hash will be signed by
// the prover, therefore, we add a string as a prefix.

// msg.sender is taikoL1Address
bytes32 hash = hashAssignment(
assignment, msg.sender, _meta.sender, _blk.assignedProver, _meta.blobHash
);

if (!_blk.assignedProver.isValidSignatureNow(hash, assignment.signature)) {
revert HOOK_ASSIGNMENT_INVALID_SIG();
}

// Send the liveness bond to the Taiko contract
IERC20 tko = IERC20(resolve(LibStrings.B_TAIKO_TOKEN, false));

// Note that we don't have to worry about
// https://github.com/crytic/slither/wiki/Detector-Documentation#arbitrary-from-in-transferfrom
// as `assignedProver` has provided a signature above to authorize this hook.
tko.safeTransferFrom(_blk.assignedProver, msg.sender, _blk.livenessBond);

// Find the prover fee using the minimal tier
uint256 proverFee = _getProverFee(assignment.tierFees, _meta.minTier);

// The proposer irrevocably pays a fee to the assigned prover, either in
// Ether or ERC20 tokens.
if (assignment.feeToken == address(0)) {
// Paying Ether even when proverFee is 0 to trigger a potential receive() function call.
// Note that this payment may fail if it cost more gas
bool success = _blk.assignedProver.sendEther(proverFee, MAX_GAS_PAYING_PROVER, "");
if (!success) emit EtherPaymentFailed(_blk.assignedProver, MAX_GAS_PAYING_PROVER);
} else if (proverFee != 0 && _meta.sender != _blk.assignedProver) {
// Paying ERC20 tokens
IERC20(assignment.feeToken).safeTransferFrom(
_meta.sender, _blk.assignedProver, proverFee
);
}

// block.coinbase can be address(0) in tests
if (input.tip != 0 && block.coinbase != address(0)) {
address(block.coinbase).sendEtherAndVerify(input.tip);
}

// Send all remaining Ether back to TaikoL1 contract
if (address(this).balance != 0) {
msg.sender.sendEtherAndVerify(address(this).balance);
}
}

/// @notice Hashes the prover assignment.
/// @param _assignment The prover assignment.
/// @param _taikoL1Address The address of the TaikoL1 contract.
/// @param _blockProposer The block proposer address.
/// @param _assignedProver The assigned prover address.
/// @param _blobHash The blob hash.
/// @return The hash of the prover assignment.
function hashAssignment(
ProverAssignment memory _assignment,
address _taikoL1Address,
address _blockProposer,
address _assignedProver,
bytes32 _blobHash
)
public
view
returns (bytes32)
{
// split up into two parts otherwise stack is too deep
bytes32 hash = keccak256(
abi.encode(
_assignment.metaHash,
_assignment.parentMetaHash,
_assignment.feeToken,
_assignment.expiry,
_assignment.maxBlockId,
_assignment.maxProposedIn,
_assignment.tierFees
)
);

return keccak256(
abi.encodePacked(
LibStrings.B_PROVER_ASSIGNMENT,
ITaikoL1(_taikoL1Address).getConfig().chainId,
_taikoL1Address,
_blockProposer,
_assignedProver,
_blobHash,
hash,
address(this)
)
);
}

function _getTaikoTokenAddress() internal view virtual override returns (address) {
return resolve(LibStrings.B_TAIKO_TOKEN, false);
function _getProverFee(
TaikoData.TierFee[] memory _tierFees,
uint16 _tierId
)
private
pure
returns (uint256)
{
for (uint256 i; i < _tierFees.length; ++i) {
if (_tierFees[i].tier == _tierId) return _tierFees[i].fee;
}
revert HOOK_TIER_NOT_FOUND();
}
}
187 changes: 0 additions & 187 deletions packages/protocol/contracts/L1/hooks/AssignmentHookBase.sol

This file was deleted.

Loading

0 comments on commit a4a9b98

Please sign in to comment.