Skip to content

Commit 5181e30

Browse files
authored
Merge pull request #306 from hyperledger-labs/audit-202409
Audit-202409 Signed-off-by: Jun Kimura <jun.kimura@datachain.jp>
2 parents d0bb1cd + 3788b23 commit 5181e30

37 files changed

+960
-349
lines changed

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
FOUNDRY_PROFILE=default
22
FORGE=FOUNDRY_PROFILE=$(FOUNDRY_PROFILE) forge
33
SOLC_VERSION=0.8.24
4-
EVM_VERSION=paris
4+
EVM_VERSION=cancun
55
DOCKER=docker
66
ABIGEN="$(DOCKER) run -v .:/workspace -w /workspace -it ethereum/client-go:alltools-v1.11.6 abigen"
77
SOLHINT=npx solhint
@@ -16,7 +16,7 @@ TEST_UPGRADEABLE=false
1616

1717
.PHONY: build
1818
build:
19-
$(FORGE) build --sizes --skip test --use solc:$(SOLC_VERSION)
19+
$(FORGE) build --sizes --skip test --use solc:$(SOLC_VERSION) --evm-version $(EVM_VERSION)
2020

2121
.PHONY: clean
2222
clean:
@@ -33,7 +33,7 @@ lint:
3333

3434
.PHONY: test
3535
test:
36-
TEST_UPGRADEABLE=$(TEST_UPGRADEABLE) $(FORGE) test -vvvv --gas-report --isolate --use solc:$(SOLC_VERSION)
36+
TEST_UPGRADEABLE=$(TEST_UPGRADEABLE) $(FORGE) test -vvvv --gas-report --isolate --use solc:$(SOLC_VERSION) --evm-version $(EVM_VERSION)
3737

3838
.PHONY: snapshot
3939
snapshot:

chains/ibft2/chain0/ibftConfigFile.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
"genesis": {
33
"config": {
44
"chainId": 2018,
5-
"muirglacierblock": 0,
5+
"cancunTime": 0,
6+
"zeroBaseFee": true,
67
"ibft2": {
78
"blockperiodseconds": 1,
89
"epochlength": 30000,

chains/ibft2/chain1/ibftConfigFile.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
"genesis": {
33
"config": {
44
"chainId": 3018,
5-
"muirglacierblock": 0,
5+
"cancunTime": 0,
6+
"zeroBaseFee": true,
67
"ibft2": {
78
"blockperiodseconds": 1,
89
"epochlength": 30000,

chains/qbft/chain0/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM hyperledger/besu:24.3.0
1+
FROM hyperledger/besu:24.10.0
22

33
USER root
44

chains/qbft/chain0/qbftConfigFile.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
"genesis": {
33
"config": {
44
"chainId": 2018,
5-
"muirglacierblock": 0,
5+
"cancunTime": 0,
6+
"zeroBaseFee": true,
67
"qbft": {
78
"blockperiodseconds": 1,
89
"epochlength": 30000,

chains/qbft/chain1/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM hyperledger/besu:24.3.0
1+
FROM hyperledger/besu:24.10.0
22

33
USER root
44

chains/qbft/chain1/qbftConfigFile.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
"genesis": {
33
"config": {
44
"chainId": 3018,
5-
"muirglacierblock": 0,
5+
"cancunTime": 0,
6+
"zeroBaseFee": true,
67
"qbft": {
78
"blockperiodseconds": 1,
89
"epochlength": 30000,

contracts/core/02-client/IBCClient.sol

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {Height} from "../../proto/Client.sol";
77
import {IBCHost} from "../24-host/IBCHost.sol";
88
import {IBCCommitment} from "../24-host/IBCCommitment.sol";
99
import {IIBCClient} from "../02-client/IIBCClient.sol";
10+
import {IBCClientLib} from "../02-client/IBCClientLib.sol";
1011
import {IIBCClientErrors} from "../02-client/IIBCClientErrors.sol";
1112

1213
/**
@@ -41,6 +42,8 @@ contract IBCClient is IBCHost, IIBCClient, IIBCClientErrors {
4142
*/
4243
function updateClient(MsgUpdateClient calldata msg_) external override {
4344
(address lc, bytes4 selector, bytes memory args) = routeUpdateClient(msg_);
45+
// NOTE: We assume that the client contract was correctly validated by the authority at registration via `registerClient` function.
46+
// For details, see the `registerClient` function in the IBCHostConfigurator.
4447
(bool success, bytes memory returndata) = lc.call(abi.encodePacked(selector, args));
4548
if (!success) {
4649
if (returndata.length > 0) {
@@ -61,6 +64,9 @@ contract IBCClient is IBCHost, IIBCClient, IIBCClientErrors {
6164
/**
6265
* @dev routeUpdateClient returns the LC contract address and the calldata to the receiving function of the client message.
6366
* Light client contract may encode a client message as other encoding scheme(e.g. ethereum ABI)
67+
* WARNING: If the caller is an EOA like a relayer, the caller must validate the return values with the allow list of the contract functions before calling the LC contract with the data.
68+
* This validation is always required because even if the caller trusts the IBC contract, a malicious RPC provider can return arbitrary data to the caller.
69+
* Check ADR-001 for details.
6470
*/
6571
function routeUpdateClient(MsgUpdateClient calldata msg_)
6672
public
@@ -69,6 +75,7 @@ contract IBCClient is IBCHost, IIBCClient, IIBCClientErrors {
6975
returns (address, bytes4, bytes memory)
7076
{
7177
ILightClient lc = checkAndGetClient(msg_.clientId);
78+
// NOTE: The `lc.routeUpdateClient` function must be validated by the authority at registration via `registerClient` function.
7279
(bytes4 functionId, bytes memory args) = lc.routeUpdateClient(msg_.clientId, msg_.protoClientMessage);
7380
return (address(lc), functionId, args);
7481
}
@@ -95,10 +102,17 @@ contract IBCClient is IBCHost, IIBCClient, IIBCClientErrors {
95102
bytes32 key = IBCCommitment.consensusStateCommitmentKey(
96103
clientId, heights[i].revision_number, heights[i].revision_height
97104
);
98-
if (commitments[key] != bytes32(0)) {
99-
continue;
105+
bytes32 commitment = keccak256(consensusState);
106+
bytes32 prev = commitments[key];
107+
if (prev != bytes32(0) && commitment != prev) {
108+
// Revert if the new commitment is inconsistent with the previous one.
109+
// This case may indicate misbehavior of either the LightClient or the target chain.
110+
// Since the definition and specification of misbehavior are defined for each LightClient,
111+
// if a relayer detects this error, it is recommended to submit an evidence of misbehaviour to the LightClient accordingly.
112+
// (e.g., via the updateClient function).
113+
revert IBCClientInconsistentConsensusStateCommitment(key, commitment, prev);
100114
}
101-
commitments[key] = keccak256(consensusState);
115+
commitments[key] = commitment;
102116
}
103117
}
104118

@@ -109,6 +123,9 @@ contract IBCClient is IBCHost, IIBCClient, IIBCClientErrors {
109123
HostStorage storage hostStorage = getHostStorage();
110124
string memory identifier =
111125
string(abi.encodePacked(clientType, "-", Strings.toString(hostStorage.nextClientSequence)));
126+
if (!IBCClientLib.validateClientId(bytes(identifier))) {
127+
revert IBCClientInvalidClientId(identifier);
128+
}
112129
hostStorage.nextClientSequence++;
113130
return identifier;
114131
}

contracts/core/02-client/IBCClientLib.sol

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,29 @@ pragma solidity ^0.8.20;
44
library IBCClientLib {
55
/**
66
* @dev validateClientType validates the client type
7-
* - clientType must be non-empty
8-
* - clientType must be in the form of `^[a-z][a-z0-9-]*[a-z0-9]$`
7+
* - clientType must be non-empty
8+
* - clientType must be between 7 and 62 characters long
9+
* - This is because the length of the client ID is 9-64 characters long in the ICS-24, and the client ID is composed of the client type and the counter suffix (minimum 2 characters long).
10+
* - clientType must be in the form of `^[a-z][a-z0-9-]*[a-z0-9]$`
911
*/
1012
function validateClientType(bytes memory clientTypeBytes) internal pure returns (bool) {
11-
uint256 byteLength = clientTypeBytes.length;
12-
if (byteLength == 0) {
13+
uint256 bytesLength = clientTypeBytes.length;
14+
if (bytesLength == 0) {
1315
return false;
1416
}
15-
for (uint256 i = 0; i < byteLength; i++) {
17+
if (bytesLength < 7 || bytesLength > 62) {
18+
return false;
19+
}
20+
for (uint256 i = 0; i < bytesLength; i++) {
1621
uint256 c = uint256(uint8(clientTypeBytes[i]));
1722
if (0x61 <= c && c <= 0x7a) {
1823
// a-z
1924
continue;
2025
} else if (c == 0x2d) {
2126
// hyphen cannot be the first or last character
2227
unchecked {
23-
// SAFETY: `byteLength` is greater than 0
24-
if (i == 0 || i == byteLength - 1) {
28+
// SAFETY: `bytesLength` is greater than 0
29+
if (i == 0 || i == bytesLength - 1) {
2530
return false;
2631
}
2732
}
@@ -35,4 +40,17 @@ library IBCClientLib {
3540
}
3641
return true;
3742
}
43+
44+
/**
45+
* @dev validateClientId validates the client ID
46+
* NOTE: The client ID must be composed of the client type is validated by `validateClientType` and the counter suffix.
47+
* - clientId must be between 9 and 64 characters long
48+
*/
49+
function validateClientId(bytes memory clientIdBytes) internal pure returns (bool) {
50+
uint256 bytesLength = clientIdBytes.length;
51+
if (bytesLength < 9 || bytesLength > 64) {
52+
return false;
53+
}
54+
return true;
55+
}
3856
}

contracts/core/02-client/IIBCClientErrors.sol

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ pragma solidity ^0.8.20;
44
import {Height} from "../../proto/Client.sol";
55

66
interface IIBCClientErrors {
7+
/// @param clientId the client identifier
8+
error IBCClientInvalidClientId(string clientId);
9+
710
/// @param clientType the client type
811
error IBCClientUnregisteredClientType(string clientType);
912

@@ -20,4 +23,9 @@ interface IIBCClientErrors {
2023
/// @param selector the function selector
2124
/// @param args the calldata
2225
error IBCClientFailedUpdateClient(bytes4 selector, bytes args);
26+
27+
/// @param commitmentKey the commitment key
28+
/// @param commitment the commitment
29+
/// @param prev the previous commitment
30+
error IBCClientInconsistentConsensusStateCommitment(bytes32 commitmentKey, bytes32 commitment, bytes32 prev);
2331
}

0 commit comments

Comments
 (0)