Skip to content

Commit 8fb25c2

Browse files
committed
evm: EG01 add senderAddress
1 parent 78c2a79 commit 8fb25c2

File tree

3 files changed

+52
-22
lines changed

3 files changed

+52
-22
lines changed

design/02_On_Chain_Quotes.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,11 @@ On EVM, two new contracts will be introduced.
8282
1. `updateQuoterContract(bytes calldata gov)` allows a Quoter to set their `ExecutorQuoter` contract via signed governance (detailed below). This MUST
8383
1. Verify the chain ID matches the Executor’s `ourChain`.
8484
2. Verify the contract address is an EVM address.
85-
3. Verify the governance has not expired.
86-
4. Verify the signature `ecrecover`s to the quoter address on the governance.
87-
5. Assign the specified contract address to that quoter address.
88-
6. Emit a `QuoterContractUpdate` event.
85+
3. Verify the sender matches the sender on the governance.
86+
4. Verify the governance has not expired.
87+
5. Verify the signature `ecrecover`s to the quoter address on the governance.
88+
6. Assign the specified contract address to that quoter address.
89+
7. Emit a `QuoterContractUpdate` event.
8990
2. `quoteExecution` allows an integrator to quote the cost of an execution for a given quoter in place of a signed quote. This MUST call `requestQuote` from that Quoter’s registered contract.
9091
3. `requestExecution` allows an integrator to request execution via Executor providing a quoter address in place of a signed quote. This MUST
9192
1. Call `requestQuote` from that Quoter’s registered contract.
@@ -143,13 +144,16 @@ Relay Providers will need to change their verification for Executor requests. If
143144

144145
### Governance
145146

146-
This design introduces a new concept of a Quoter’s on-chain governance
147+
This design introduces a new concept of a Quoter’s on-chain governance.
148+
149+
The governance includes a sender address and expiry time in order to prevent replay attacks in lieu of a nonce and hash storage. The intention being that a short enough expiry time along with a pre-designated submitter mitigates the event where a quoter could be rolled back to a previous implementation by replaying their governance even when two governance messages are generated in short succession.
147150

148151
```solidity
149152
bytes4 prefix = "EG01"; // 4-byte prefix for this struct
150153
uint16 sourceChain; // Wormhole Chain ID
151154
address quoterAddress; // The public key of the quoter. Used to identify an execution provider.
152155
bytes32 contractAddress; // UniversalAddress the quote contract to assign.
156+
bytes32 senderAddress; // The public key of address expected to submit this governance.
153157
uint64 expiryTime; // The unix time, in seconds, after which this quote should no longer be considered valid for requesting an execution
154158
[65]byte signature // Quoter's signature of the previous bytes
155159
```

evm/src/ExecutorQuoterRouter.sol

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ contract ExecutorQuoterRouter is IExecutorQuoterRouter {
2828
/// @param refundAddr The refund address.
2929
error RefundFailed(address refundAddr);
3030
error ChainIdMismatch(uint16 govChain, uint16 ourChain);
31+
error InvalidSender();
3132
error InvalidSignature();
3233
error GovernanceExpired(uint64 expiryTime);
3334
error NotAnEvmAddress(bytes32);
@@ -43,6 +44,7 @@ contract ExecutorQuoterRouter is IExecutorQuoterRouter {
4344
uint160 quoter;
4445
address quoterAddr;
4546
bytes32 universalContractAddress;
47+
bytes32 universalSenderAddress;
4648
uint64 expiryTime;
4749
bytes32 r;
4850
bytes32 s;
@@ -52,10 +54,11 @@ contract ExecutorQuoterRouter is IExecutorQuoterRouter {
5254
chainId := shr(240, calldataload(add(gov.offset, 4)))
5355
quoter := shr(96, calldataload(add(gov.offset, 6)))
5456
universalContractAddress := calldataload(add(gov.offset, 26))
55-
expiryTime := shr(192, calldataload(add(gov.offset, 58)))
56-
r := calldataload(add(gov.offset, 66))
57-
s := calldataload(add(gov.offset, 98))
58-
v := shr(248, calldataload(add(gov.offset, 130)))
57+
universalSenderAddress := calldataload(add(gov.offset, 58))
58+
expiryTime := shr(192, calldataload(add(gov.offset, 90)))
59+
r := calldataload(add(gov.offset, 98))
60+
s := calldataload(add(gov.offset, 130))
61+
v := shr(248, calldataload(add(gov.offset, 162)))
5962
}
6063
if (chainId != OUR_CHAIN) {
6164
revert ChainIdMismatch(chainId, OUR_CHAIN);
@@ -64,11 +67,19 @@ contract ExecutorQuoterRouter is IExecutorQuoterRouter {
6467
if (uint256(universalContractAddress) >> 160 != 0) {
6568
revert NotAnEvmAddress(universalContractAddress);
6669
}
70+
// Check if the higher 96 bits (left-most 12 bytes) are non-zero
71+
if (uint256(universalSenderAddress) >> 160 != 0) {
72+
revert NotAnEvmAddress(universalSenderAddress);
73+
}
74+
address senderAddress = address(uint160(uint256(universalSenderAddress)));
75+
if (msg.sender != senderAddress) {
76+
revert InvalidSender();
77+
}
6778
if (expiryTime <= block.timestamp) {
6879
revert GovernanceExpired(expiryTime);
6980
}
7081
quoterAddr = address(quoter);
71-
bytes32 hash = keccak256(gov[0:66]);
82+
bytes32 hash = keccak256(gov[0:98]);
7283
address signer = ecrecover(hash, v, r, s);
7384
if (signer == address(0)) {
7485
revert InvalidSignature();

evm/test/ExecutorQuoterRouter.t.sol

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,24 @@ contract ExecutorQuoterRouterTest is Test {
1212
uint16 constant OUR_CHAIN = 10002;
1313
bytes32 constant UPDATE_IMPLEMENTATION = 0x000000000000000000000000aaa039ee238299b23cb4f9cd40775589efa962fd;
1414
bytes32 constant BAD_UPDATE_IMPLEMENTATION = 0x100000000000000000000000aaa039ee238299b23cb4f9cd40775589efa962fd;
15+
bytes32 constant SENDER_ADDRESS = 0x0000000000000000000000007FA9385bE102ac3EAc297483Dd6233D62b3e1496;
16+
bytes32 constant BAD_SENDER_ADDRESS = 0x0000000000000000000000007FA9385bE102ac3EAc297483Dd6233D62b3e1490;
1517
uint64 constant EXPIRY = 1762880900;
1618

1719
function setUp() public {
1820
executor = new Executor(OUR_CHAIN);
1921
executorQuoterRouter = new ExecutorQuoterRouter(address(executor));
2022
}
2123

22-
function makeAndSignGovernance(uint16 chainId, address quoterAddr, bytes32 updateImplementation, uint256 quoterPk)
23-
private
24-
pure
25-
returns (bytes memory)
26-
{
27-
bytes memory govBody = abi.encodePacked(hex"45473031", chainId, quoterAddr, updateImplementation, EXPIRY);
24+
function makeAndSignGovernance(
25+
uint16 chainId,
26+
address quoterAddr,
27+
bytes32 updateImplementation,
28+
bytes32 senderAddress,
29+
uint256 quoterPk
30+
) private pure returns (bytes memory) {
31+
bytes memory govBody =
32+
abi.encodePacked(hex"45473031", chainId, quoterAddr, updateImplementation, senderAddress, EXPIRY);
2833
bytes32 digest = keccak256(govBody);
2934
(uint8 v, bytes32 r, bytes32 s) = vm.sign(quoterPk, digest);
3035
return abi.encodePacked(govBody, r, s, v);
@@ -33,7 +38,7 @@ contract ExecutorQuoterRouterTest is Test {
3338
function test_updateQuoterContract() public {
3439
(address alice, uint256 alicePk) = makeAddrAndKey("alice");
3540
executorQuoterRouter.updateQuoterContract(
36-
makeAndSignGovernance(OUR_CHAIN, alice, UPDATE_IMPLEMENTATION, alicePk)
41+
makeAndSignGovernance(OUR_CHAIN, alice, UPDATE_IMPLEMENTATION, SENDER_ADDRESS, alicePk)
3742
);
3843
}
3944

@@ -42,7 +47,7 @@ contract ExecutorQuoterRouterTest is Test {
4247
uint16 badChain = OUR_CHAIN + 1;
4348
vm.expectRevert(abi.encodeWithSelector(ExecutorQuoterRouter.ChainIdMismatch.selector, badChain, OUR_CHAIN));
4449
executorQuoterRouter.updateQuoterContract(
45-
makeAndSignGovernance(badChain, alice, UPDATE_IMPLEMENTATION, alicePk)
50+
makeAndSignGovernance(badChain, alice, UPDATE_IMPLEMENTATION, SENDER_ADDRESS, alicePk)
4651
);
4752
}
4853

@@ -52,7 +57,15 @@ contract ExecutorQuoterRouterTest is Test {
5257
abi.encodeWithSelector(ExecutorQuoterRouter.NotAnEvmAddress.selector, BAD_UPDATE_IMPLEMENTATION)
5358
);
5459
executorQuoterRouter.updateQuoterContract(
55-
makeAndSignGovernance(OUR_CHAIN, alice, BAD_UPDATE_IMPLEMENTATION, alicePk)
60+
makeAndSignGovernance(OUR_CHAIN, alice, BAD_UPDATE_IMPLEMENTATION, SENDER_ADDRESS, alicePk)
61+
);
62+
}
63+
64+
function test_updateQuoterContractInvalidSender() public {
65+
vm.expectRevert(abi.encodeWithSelector(ExecutorQuoterRouter.InvalidSender.selector));
66+
(address alice, uint256 alicePk) = makeAddrAndKey("alice");
67+
executorQuoterRouter.updateQuoterContract(
68+
makeAndSignGovernance(OUR_CHAIN, alice, UPDATE_IMPLEMENTATION, BAD_SENDER_ADDRESS, alicePk)
5669
);
5770
}
5871

@@ -61,21 +74,23 @@ contract ExecutorQuoterRouterTest is Test {
6174
vm.expectRevert(abi.encodeWithSelector(ExecutorQuoterRouter.GovernanceExpired.selector, EXPIRY));
6275
(address alice, uint256 alicePk) = makeAddrAndKey("alice");
6376
executorQuoterRouter.updateQuoterContract(
64-
makeAndSignGovernance(OUR_CHAIN, alice, UPDATE_IMPLEMENTATION, alicePk)
77+
makeAndSignGovernance(OUR_CHAIN, alice, UPDATE_IMPLEMENTATION, SENDER_ADDRESS, alicePk)
6578
);
6679
}
6780

6881
function test_updateQuoterContractBadSignature() public {
6982
vm.expectRevert(abi.encodeWithSelector(ExecutorQuoterRouter.InvalidSignature.selector));
7083
executorQuoterRouter.updateQuoterContract(
71-
hex"4547303127125241c9276698439fef2780dbab76fec90b633fbd000000000000000000000000aaa039ee238299b23cb4f9cd40775589efa962fd00000000691248922111b9ac29b0d785d41e8f8c66980f4651c9a35c066e875cab67fd625e5e59c62fc65912c14a2c2ee99acdd809397f932bcf35ba7d269f02f96e8688588145701b"
84+
hex"4547303127125241c9276698439fef2780dbab76fec90b633fbd000000000000000000000000aaa039ee238299b23cb4f9cd40775589efa962fd0000000000000000000000007FA9385bE102ac3EAc297483Dd6233D62b3e149600000000691248922111b9ac29b0d785d41e8f8c66980f4651c9a35c066e875cab67fd625e5e59c62fc65912c14a2c2ee99acdd809397f932bcf35ba7d269f02f96e8688588145701b"
7285
);
7386
}
7487

7588
function test_updateQuoterContractQuoterMismatch() public {
7689
(address alice,) = makeAddrAndKey("alice");
7790
(, uint256 bobPk) = makeAddrAndKey("bob");
7891
vm.expectRevert(abi.encodeWithSelector(ExecutorQuoterRouter.InvalidSignature.selector));
79-
executorQuoterRouter.updateQuoterContract(makeAndSignGovernance(OUR_CHAIN, alice, UPDATE_IMPLEMENTATION, bobPk));
92+
executorQuoterRouter.updateQuoterContract(
93+
makeAndSignGovernance(OUR_CHAIN, alice, UPDATE_IMPLEMENTATION, SENDER_ADDRESS, bobPk)
94+
);
8095
}
8196
}

0 commit comments

Comments
 (0)