ethereum: QueryDemo updates

* Use custom errors in place of require() to match the error handling
style overall of QueryResponse.
This commit is contained in:
Jeff Schroeder 2023-10-17 21:21:41 +00:00 committed by Evan Gray
parent 1eb481b2c9
commit e613486b92
1 changed files with 60 additions and 15 deletions

View File

@ -7,6 +7,18 @@ import "../libraries/external/BytesLib.sol";
import "../interfaces/IWormhole.sol";
import "./QueryResponse.sol";
error InvalidOwner();
// @dev for the onlyOwner modifier
error InvalidCaller();
error InvalidContractAddress();
error InvalidWormholeAddress();
error InvalidForeignChainID();
error ObsoleteUpdate();
error StaleUpdate();
error UnexpectedCallData();
error UnexpectedResultLength();
error UnexpectedResultMismatch();
/// @dev QueryDemo is a library that implements the parsing and verification of Cross Chain Query (CCQ) responses.
contract QueryDemo is QueryResponse {
using BytesLib for bytes;
@ -28,9 +40,14 @@ contract QueryDemo is QueryResponse {
bytes4 GetMyCounter = bytes4(hex"916d5743");
constructor(address _owner, address _wormhole, uint16 _myChainID) {
require(_owner != address(0), "Invalid owner");
if (_owner == address(0)) {
revert InvalidOwner();
}
owner = _owner;
require(_wormhole != address(0), "Invalid wormhole address");
if (_wormhole == address(0)) {
revert InvalidWormholeAddress();
}
wormhole = _wormhole;
myChainID = _myChainID;
counters[_myChainID] = ChainEntry(_myChainID, address(this), 0, 0, 0);
@ -71,16 +88,35 @@ contract QueryDemo is QueryResponse {
function updateCounters(bytes memory response, IWormhole.Signature[] memory signatures) public {
uint256 adjustedBlockTime;
ParsedQueryResponse memory r = parseAndVerifyQueryResponse(address(wormhole), response, signatures);
require(r.responses.length == foreignChainIDs.length, "unexpected number of results");
for (uint idx=0; idx<r.responses.length; idx++) {
require(counters[r.responses[idx].chainId].chainID == foreignChainIDs[idx], "unexpected foreign chain ID");
EthCallQueryResponse memory eqr = parseEthCallQueryResponse(r.responses[idx]);
require(eqr.blockNum > counters[r.responses[idx].chainId].blockNum, "update is obsolete");
if (r.responses.length != foreignChainIDs.length) {
revert UnexpectedResultLength();
}
for (uint i=0; i < r.responses.length;) {
// Create a storage pointer for frequently read and updated data stored on the blockchain
ChainEntry storage chainEntry = counters[r.responses[i].chainId];
if (chainEntry.chainID != foreignChainIDs[i]) {
revert InvalidForeignChainID();
}
EthCallQueryResponse memory eqr = parseEthCallQueryResponse(r.responses[i]);
if (eqr.blockNum <= chainEntry.blockNum) {
revert ObsoleteUpdate();
}
// wormhole time is in microseconds, timestamp is in seconds
adjustedBlockTime = eqr.blockTime / 1_000_000;
require(adjustedBlockTime > block.timestamp - 300, "update is stale");
require(eqr.result.length == 1, "result mismatch");
require(eqr.result[0].contractAddress == counters[r.responses[idx].chainId].contractAddress, "contract address is wrong");
if (adjustedBlockTime <= block.timestamp - 300) {
revert StaleUpdate();
}
if (eqr.result.length != 1) {
revert UnexpectedResultMismatch();
}
if (eqr.result[0].contractAddress != chainEntry.contractAddress) {
revert InvalidContractAddress();
}
// TODO: Is there an easier way to verify that the call data is correct!
bytes memory callData = eqr.result[0].callData;
@ -88,12 +124,19 @@ contract QueryDemo is QueryResponse {
assembly {
result := mload(add(callData, 32))
}
require(result == GetMyCounter, "unexpected callData");
if (result != GetMyCounter) {
revert UnexpectedCallData();
}
require(eqr.result[0].result.length == 32, "result is not a uint256");
counters[r.responses[idx].chainId].blockNum = eqr.blockNum;
counters[r.responses[idx].chainId].blockTime = adjustedBlockTime;
counters[r.responses[idx].chainId].counter = abi.decode(eqr.result[0].result, (uint256));
chainEntry.blockNum = eqr.blockNum;
chainEntry.blockTime = adjustedBlockTime;
chainEntry.counter = abi.decode(eqr.result[0].result, (uint256));
unchecked {
++i;
}
}
counters[myChainID].blockNum = block.number;
@ -102,7 +145,9 @@ contract QueryDemo is QueryResponse {
}
modifier onlyOwner() {
require(owner == msg.sender, "caller is not the owner");
if (owner != msg.sender) {
revert InvalidOwner();
}
_;
}
}