@@ -3,8 +3,10 @@ pragma solidity 0.8.11;
33
44import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol " ;
55import "@openzeppelin/contracts/utils/Context.sol " ;
6+ import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol " ;
67import "./utils/Pausable.sol " ;
78
9+
810import "./interfaces/IDepositExecute.sol " ;
911import "./interfaces/IERCHandler.sol " ;
1012import "./interfaces/IGenericHandler.sol " ;
@@ -15,9 +17,15 @@ import "./interfaces/IAccessControlSegregator.sol";
1517 @title Facilitates deposits and creation of deposit proposals, and deposit executions.
1618 @author ChainSafe Systems.
1719 */
18- contract Bridge is Pausable , Context {
20+ contract Bridge is Pausable , Context , EIP712 {
1921 using ECDSA for bytes32 ;
2022
23+ bytes32 private constant _PROPOSALS_TYPEHASH =
24+ keccak256 ("Proposals(Proposal[] proposals)Proposal(uint8 originDomainID,uint64 depositNonce,bytes32 resourceID,bytes data) " );
25+ bytes32 private constant _PROPOSAL_TYPEHASH =
26+ keccak256 ("Proposal(uint8 originDomainID,uint64 depositNonce,bytes32 resourceID,bytes data) " );
27+
28+
2129 uint8 public immutable _domainID;
2230 address public _MPCAddress;
2331
@@ -96,7 +104,7 @@ contract Bridge is Pausable, Context {
96104 @param domainID ID of chain the Bridge contract exists on.
97105 @param accessControl Address of access control contract.
98106 */
99- constructor (uint8 domainID , address accessControl ) public {
107+ constructor (uint8 domainID , address accessControl ) EIP712 ( " Bridge " , " 3.1.0 " ) public {
100108 _domainID = domainID;
101109 _accessControl = IAccessControlSegregator (accessControl);
102110
@@ -271,37 +279,28 @@ contract Bridge is Pausable, Context {
271279
272280 /**
273281 @notice Executes a deposit proposal using a specified handler contract (only if signature is signed by MPC).
274- @param originDomainID ID of chain deposit originated from.
275- @param resourceID ResourceID to be used when making deposits.
276- @param depositNonce ID of deposit generated by origin Bridge contract.
277- @param data Data originally provided when deposit was made.
282+ @notice Failed executeProposal from handler don't revert, emits {FailedHandlerExecution} event.
283+ @param proposal Proposal which consists of:
284+ - originDomainID ID of chain deposit originated from.
285+ - resourceID ResourceID to be used when making deposits.
286+ - depositNonce ID of deposit generated by origin Bridge contract.
287+ - data Data originally provided when deposit was made.
278288 @param signature bytes memory signature composed of MPC key shares
279289 @notice Emits {ProposalExecution} event.
280290 @notice Behaviour of this function is different for {GenericHandler} and other specific ERC handlers.
281291 In the case of ERC handler, when execution fails, the handler will terminate the function with revert.
282292 In the case of {GenericHandler}, when execution fails, the handler will emit a failure event and terminate the function normally.
283293 */
284- function executeProposal (uint8 originDomainID , uint64 depositNonce , bytes calldata data , bytes32 resourceID , bytes calldata signature ) public whenNotPaused {
285- require (isProposalExecuted (originDomainID, depositNonce) != true , "Deposit with provided nonce already executed " );
286-
287- address signer = keccak256 (abi.encode (originDomainID, _domainID, depositNonce, data, resourceID)).recover (signature);
288- require (signer == _MPCAddress, "Invalid message signer " );
289-
290- address handler = _resourceIDToHandlerAddress[resourceID];
291- bytes32 dataHash = keccak256 (abi.encodePacked (handler, data));
294+ function executeProposal (Proposal memory proposal , bytes calldata signature ) public {
295+ Proposal[] memory proposalArray = new Proposal [](1 );
296+ proposalArray[0 ] = proposal;
292297
293- IDepositExecute depositHandler = IDepositExecute (handler);
294-
295- usedNonces[originDomainID][depositNonce / 256 ] |= 1 << (depositNonce % 256 );
296-
297- // Reverts for every handler except GenericHandler
298- depositHandler.executeProposal (resourceID, data);
299-
300- emit ProposalExecution (originDomainID, depositNonce, dataHash);
298+ executeProposals (proposalArray, signature);
301299 }
302300
303301 /**
304302 @notice Executes a batch of deposit proposals using a specified handler contract for each proposal (only if signature is signed by MPC).
303+ @notice If executeProposals fails it doesn't revert, emits {FailedHandlerExecution} event.
305304 @param proposals Array of Proposal which consists of:
306305 - originDomainID ID of chain deposit originated from.
307306 - resourceID ResourceID to be used when making deposits.
@@ -313,11 +312,9 @@ contract Bridge is Pausable, Context {
313312 In the case of ERC handler, when execution fails, the handler will terminate the function with revert.
314313 In the case of {GenericHandler}, when execution fails, the handler will emit a failure event and terminate the function normally.
315314 */
316- function executeProposals (Proposal[] memory proposals , bytes memory signature ) public whenNotPaused {
315+ function executeProposals (Proposal[] memory proposals , bytes calldata signature ) public whenNotPaused {
317316 require (proposals.length > 0 , "Proposals can't be an empty array " );
318-
319- address signer = keccak256 (abi.encode (proposals, _domainID)).recover (signature);
320- require (signer == _MPCAddress, "Invalid message signer " );
317+ require (verify (proposals, signature), "Invalid proposal signer " );
321318
322319 for (uint256 i = 0 ; i < proposals.length ; i++ ) {
323320 if (isProposalExecuted (proposals[i].originDomainID, proposals[i].depositNonce)) {
@@ -399,4 +396,31 @@ contract Bridge is Pausable, Context {
399396 function isProposalExecuted (uint8 domainID , uint256 depositNonce ) public view returns (bool ) {
400397 return usedNonces[domainID][depositNonce / 256 ] & (1 << (depositNonce % 256 )) != 0 ;
401398 }
399+
400+ /**
401+ @notice Verifies that proposal data is signed by MPC address.
402+ @param proposals array of Proposals.
403+ @param signature signature bytes memory signature composed of MPC key shares.
404+ @return Boolean value depending if signer is vaild or not.
405+ */
406+ function verify (Proposal[] memory proposals , bytes calldata signature ) public view returns (bool ) {
407+ bytes32 [] memory keccakData = new bytes32 [](proposals.length );
408+ for (uint256 i = 0 ; i < proposals.length ; i++ ) {
409+ keccakData[i] = keccak256 (
410+ abi.encode (
411+ _PROPOSAL_TYPEHASH,
412+ proposals[i].originDomainID,
413+ proposals[i].depositNonce,
414+ proposals[i].resourceID,
415+ keccak256 (proposals[i].data)
416+ )
417+ );
418+ }
419+
420+ address signer = _hashTypedDataV4 (
421+ keccak256 (abi.encode (
422+ _PROPOSALS_TYPEHASH, keccak256 (abi.encodePacked (keccakData))))
423+ ).recover (signature);
424+ return signer == _MPCAddress;
425+ }
402426}
0 commit comments