Skip to content

Commit bdfb2ee

Browse files
committed
feat(gateway-contracts): implement coprocessor contexts
1 parent 29b4ec8 commit bdfb2ee

File tree

68 files changed

+40045
-8890
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+40045
-8890
lines changed

.github/workflows/gateway-contracts-upgrade-tests.yml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,7 @@ jobs:
3333
sc-upgrade:
3434
name: gateway-contracts-upgrade-tests/sc-upgrade (bpr)
3535
needs: check-changes
36-
# if: ${{ needs.check-changes.outputs.changes-gw-contracts == 'true' }}
37-
# TODO: Re-enable the upgrade tests once fhevm 0.9 is released
38-
# See https://github.com/zama-ai/fhevm-internal/issues/439
39-
if: false
36+
if: ${{ needs.check-changes.outputs.changes-gw-contracts == 'true' }}
4037
permissions:
4138
contents: 'read' # Required to checkout repository code
4239
checks: 'write' # Required to create GitHub checks for test results
@@ -48,7 +45,7 @@ jobs:
4845
with:
4946
# This version should be updated whenever we release new contract versions or
5047
# touch a contract upgrade path.
51-
ref: v0.8.0-6
48+
ref: v0.9.0
5249
path: previous-fhevm
5350
persist-credentials: 'false'
5451

gateway-contracts/.env.example

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,24 +55,30 @@ KMS_SIGNER_ADDRESS_3="0xDb216ECeC4cEd51CdfD9609b6Ce7653aB04f6cAd" # accounts[10]
5555
KMS_NODE_IP_ADDRESS_3="127.0.0.4" # (string)
5656
KMS_NODE_STORAGE_URL_3="s3://kms-bucket-4" # (string)
5757

58+
# Coprocessor feature set
59+
COPROCESSORS_FEATURE_SET="1" # (uint256)
60+
5861
# Coprocessors
5962
# The number of coprocessors must be lower or equal to the number of coprocessors' metadata defined below
6063
NUM_COPROCESSORS="3" # (number)
6164

6265
# Coprocessor 1
66+
COPROCESSOR_NAME_0="Coprocessor 1" # (string)
6367
COPROCESSOR_TX_SENDER_ADDRESS_0="0x6518D50aDc9036Df37119eA465a8159E34417E2E" # accounts[11] (address)
6468
COPROCESSOR_SIGNER_ADDRESS_0="0xa5eE8292dA52d8234248709F3E217ffEBA5E8312" # accounts[12] (address)
65-
COPROCESSOR_S3_BUCKET_URL_0="s3://coprocessor-bucket-1" # (string)
69+
COPROCESSOR_STORAGE_URL_0="s3://coprocessor-bucket-1" # (string)
6670

6771
# Coprocessor 2
72+
COPROCESSOR_NAME_1="Coprocessor 2" # (string)
6873
COPROCESSOR_TX_SENDER_ADDRESS_1="0xCFbF539CB91c92ace0343c5B0487149Ad0b82078" # accounts[13] (address)
6974
COPROCESSOR_SIGNER_ADDRESS_1="0xA951F315d5FD35Cac111dFB5250DF231FB8eF905" # accounts[14] (address)
70-
COPROCESSOR_S3_BUCKET_URL_1="s3://coprocessor-bucket-2" # (string)
75+
COPROCESSOR_STORAGE_URL_1="s3://coprocessor-bucket-2" # (string)
7176

7277
# Coprocessor 3
78+
COPROCESSOR_NAME_2="Coprocessor 3" # (string)
7379
COPROCESSOR_TX_SENDER_ADDRESS_2="0x3C0033584da3A0f61AA5C7bde50eAF3642875a21" # accounts[15] (address)
7480
COPROCESSOR_SIGNER_ADDRESS_2="0x420AF5A5BBfAd922aE5a501d9a8Bf70a55F52E03" # accounts[16] (address)
75-
COPROCESSOR_S3_BUCKET_URL_2="s3://coprocessor-bucket-3" # (string)
81+
COPROCESSOR_STORAGE_URL_2="s3://coprocessor-bucket-3" # (string)
7682

7783
# Custodians
7884
# The number of custodians must be lower or equal to the number of custodians' metadata defined below

gateway-contracts/contracts/CiphertextCommits.sol

Lines changed: 116 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,33 @@
11
// SPDX-License-Identifier: BSD-3-Clause-Clear
22
pragma solidity ^0.8.24;
3-
import { gatewayConfigAddress, kmsGenerationAddress } from "../addresses/GatewayAddresses.sol";
3+
import {
4+
gatewayConfigAddress,
5+
kmsGenerationAddress,
6+
coprocessorContextsAddress
7+
} from "../addresses/GatewayAddresses.sol";
48
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
59
import { ICiphertextCommits } from "./interfaces/ICiphertextCommits.sol";
610
import { IGatewayConfig } from "./interfaces/IGatewayConfig.sol";
711
import { IKMSGeneration } from "./interfaces/IKMSGeneration.sol";
12+
import { ICoprocessorContexts } from "./interfaces/ICoprocessorContexts.sol";
813
import { UUPSUpgradeableEmptyProxy } from "./shared/UUPSUpgradeableEmptyProxy.sol";
914
import { GatewayConfigChecks } from "./shared/GatewayConfigChecks.sol";
1015
import { GatewayOwnable } from "./shared/GatewayOwnable.sol";
1116
import { CiphertextMaterial, SnsCiphertextMaterial } from "./shared/Structs.sol";
17+
import { ContextStatus } from "./shared/Enums.sol";
18+
import { ContextChecks } from "./shared/ContextChecks.sol";
1219

1320
/**
1421
* @title CiphertextCommits smart contract
1522
* @notice See {ICiphertextCommits}.
1623
*/
17-
contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, GatewayOwnable, GatewayConfigChecks {
24+
contract CiphertextCommits is
25+
ICiphertextCommits,
26+
UUPSUpgradeableEmptyProxy,
27+
GatewayOwnable,
28+
GatewayConfigChecks,
29+
ContextChecks
30+
{
1831
/**
1932
* @notice The address of the GatewayConfig contract, used for fetching information about coprocessors.
2033
*/
@@ -25,14 +38,17 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat
2538
*/
2639
IKMSGeneration private constant KMS_GENERATION = IKMSGeneration(kmsGenerationAddress);
2740

41+
/// @notice The address of the CoprocessorContexts contract, used for fetching information about coprocessors.
42+
ICoprocessorContexts private constant COPROCESSOR_CONTEXTS = ICoprocessorContexts(coprocessorContextsAddress);
43+
2844
/**
2945
* @dev The following constants are used for versioning the contract. They are made private
3046
* in order to force derived contracts to consider a different version. Note that
3147
* they can still define their own private constants with the same name.
3248
*/
3349
string private constant CONTRACT_NAME = "CiphertextCommits";
3450
uint256 private constant MAJOR_VERSION = 0;
35-
uint256 private constant MINOR_VERSION = 1;
51+
uint256 private constant MINOR_VERSION = 2;
3652
uint256 private constant PATCH_VERSION = 0;
3753

3854
/**
@@ -41,7 +57,7 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat
4157
* This constant does not represent the number of time a specific contract have been upgraded,
4258
* as a contract deployed from version VX will have a REINITIALIZER_VERSION > 2.
4359
*/
44-
uint64 private constant REINITIALIZER_VERSION = 2;
60+
uint64 private constant REINITIALIZER_VERSION = 3;
4561

4662
/**
4763
* @notice The contract's variable storage struct (@dev see ERC-7201)
@@ -75,6 +91,11 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat
7591
alreadyAddedCoprocessorTxSenders;
7692
/// @notice The coprocessor transaction senders involved in a consensus for a ciphertext material addition.
7793
mapping(bytes32 addCiphertextHash => address[] coprocessorTxSenderAddresses) coprocessorTxSenderAddresses;
94+
// ----------------------------------------------------------------------------------------------
95+
// Coprocessor context state variables:
96+
// ----------------------------------------------------------------------------------------------
97+
/// @notice The coprocessor context ID associated to the add ciphertext
98+
mapping(bytes32 addCiphertextHash => uint256 contextId) addCiphertextContextId;
7899
}
79100

80101
/**
@@ -99,34 +120,54 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat
99120

100121
/**
101122
* @notice Re-initializes the contract from V1.
102-
* @dev Define a `reinitializeVX` function once the contract needs to be upgraded.
103123
*/
104124
/// @custom:oz-upgrades-unsafe-allow missing-initializer-call
105125
/// @custom:oz-upgrades-validate-as-initializer
106-
// function reinitializeV2() public virtual reinitializer(REINITIALIZER_VERSION) {}
126+
function reinitializeV2() public virtual reinitializer(REINITIALIZER_VERSION) {}
107127

108-
/**
109-
* @notice See {ICiphertextCommits-addCiphertextMaterial}.
110-
*/
128+
/// @notice See {ICiphertextCommits-addCiphertextMaterial}.
111129
function addCiphertextMaterial(
112130
bytes32 ctHandle,
113131
uint256 keyId,
114132
bytes32 ciphertextDigest,
115133
bytes32 snsCiphertextDigest
116-
) external virtual onlyCoprocessorTxSender onlyHandleFromRegisteredHostChain(ctHandle) {
134+
) external virtual onlyHandleFromRegisteredHostChain(ctHandle) refreshCoprocessorContextStatuses {
135+
// The addCiphertextHash is the hash of all received input arguments which means that multiple
136+
// Coprocessors can only have a consensus on a ciphertext material with the same information.
137+
// This hash is used to differentiate different calls to the function, in particular when
138+
// tracking the consensus on the received ciphertext material.
139+
// Note that chainId is not included in the hash because it is already contained in the ctHandle.
140+
bytes32 addCiphertextHash = keccak256(abi.encode(ctHandle, keyId, ciphertextDigest, snsCiphertextDigest));
141+
117142
CiphertextCommitsStorage storage $ = _getCiphertextCommitsStorage();
118143

144+
// Get the context ID from the input verification context ID mapping
145+
// This ID may be 0 (invalid) if this is the first addCiphertextMaterial call for this
146+
// addCiphertextHash (see right below)
147+
uint256 contextId = $.addCiphertextContextId[addCiphertextHash];
148+
149+
// If the context ID is null, get the active coprocessor context's ID and associate it to
150+
// this ciphertext material addition
151+
if (contextId == 0) {
152+
contextId = COPROCESSOR_CONTEXTS.getActiveCoprocessorContextId();
153+
$.addCiphertextContextId[addCiphertextHash] = contextId;
154+
155+
// Else, that means a coprocessor already started to add the ciphertext material
156+
// and we need to check that the context is active or suspended
157+
// If it is not, that means the context is no longer valid for this operation and we revert
158+
} else if (!COPROCESSOR_CONTEXTS.isCoprocessorContextActiveOrSuspended(contextId)) {
159+
ContextStatus contextStatus = COPROCESSOR_CONTEXTS.getCoprocessorContextStatus(contextId);
160+
revert InvalidCoprocessorContextAddCiphertext(ctHandle, contextId, contextStatus);
161+
}
162+
163+
// Only accept coprocessor transaction senders from the same context
164+
_checkIsCoprocessorTxSenderFromContext(contextId, msg.sender);
165+
119166
// Check if the coprocessor transaction sender has already added the ciphertext handle.
120167
if ($.alreadyAddedCoprocessorTxSenders[ctHandle][msg.sender]) {
121168
revert CoprocessorAlreadyAdded(ctHandle, msg.sender);
122169
}
123170

124-
// The addCiphertextHash is the hash of all received input arguments which means that multiple
125-
// Coprocessors can only have a consensus on a ciphertext material with the same information.
126-
// This hash is used to differentiate different calls to the function, in particular when
127-
// tracking the consensus on the received ciphertext material.
128-
// Note that chainId is not included in the hash because it is already contained in the ctHandle.
129-
bytes32 addCiphertextHash = keccak256(abi.encode(ctHandle, keyId, ciphertextDigest, snsCiphertextDigest));
130171
$.addCiphertextHashCounters[addCiphertextHash]++;
131172

132173
// It is ok to only the handle can be considered here as a handle should only be added once
@@ -143,9 +184,10 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat
143184

144185
// Send the event if and only if the consensus is reached in the current response call.
145186
// This means a "late" response will not be reverted, just ignored and no event will be emitted
187+
// Besides, consensus only considers the coprocessors of the same context
146188
if (
147189
!$.isCiphertextMaterialAdded[ctHandle] &&
148-
_isConsensusReached($.addCiphertextHashCounters[addCiphertextHash])
190+
_isConsensusReached(contextId, $.addCiphertextHashCounters[addCiphertextHash])
149191
) {
150192
$.ciphertextDigests[ctHandle] = ciphertextDigest;
151193
$.snsCiphertextDigests[ctHandle] = snsCiphertextDigest;
@@ -159,12 +201,7 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat
159201
// by only knowing the handle, since a consensus can only happen once per handle
160202
$.ctHandleConsensusHash[ctHandle] = addCiphertextHash;
161203

162-
emit AddCiphertextMaterial(
163-
ctHandle,
164-
ciphertextDigest,
165-
snsCiphertextDigest,
166-
$.coprocessorTxSenderAddresses[addCiphertextHash]
167-
);
204+
emit AddCiphertextMaterial(ctHandle, ciphertextDigest, snsCiphertextDigest, contextId);
168205
}
169206
}
170207

@@ -191,16 +228,10 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat
191228
revert CiphertextMaterialNotFound(ctHandles[i]);
192229
}
193230

194-
// Get the unique hash associated to the handle and use it to get the list of coprocessor
195-
// transaction sender address that were involved in the consensus
196-
bytes32 addCiphertextHash = $.ctHandleConsensusHash[ctHandles[i]];
197-
address[] memory coprocessorTxSenderAddresses = $.coprocessorTxSenderAddresses[addCiphertextHash];
198-
199231
ctMaterials[i] = CiphertextMaterial(
200232
ctHandles[i],
201233
$.keyIds[ctHandles[i]],
202-
$.ciphertextDigests[ctHandles[i]],
203-
coprocessorTxSenderAddresses
234+
$.ciphertextDigests[ctHandles[i]]
204235
);
205236
}
206237

@@ -222,29 +253,21 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat
222253
revert CiphertextMaterialNotFound(ctHandles[i]);
223254
}
224255

225-
// Get the unique hash associated to the handle and use it to get the list of coprocessor
226-
// transaction sender address that were involved in the consensus
227-
bytes32 addCiphertextHash = $.ctHandleConsensusHash[ctHandles[i]];
228-
address[] memory coprocessorTxSenderAddresses = $.coprocessorTxSenderAddresses[addCiphertextHash];
229-
230256
snsCtMaterials[i] = SnsCiphertextMaterial(
231257
ctHandles[i],
232258
$.keyIds[ctHandles[i]],
233-
$.snsCiphertextDigests[ctHandles[i]],
234-
coprocessorTxSenderAddresses
259+
$.snsCiphertextDigests[ctHandles[i]]
235260
);
236261
}
237262

238263
return snsCtMaterials;
239264
}
240265

241266
/**
242-
* @notice See {ICiphertextCommits-getAddCiphertextMaterialConsensusTxSenders}.
267+
* @notice See {ICiphertextCommits-getConsensusTxSenders}.
243268
* The list remains empty until the consensus is reached.
244269
*/
245-
function getAddCiphertextMaterialConsensusTxSenders(
246-
bytes32 ctHandle
247-
) external view virtual returns (address[] memory) {
270+
function getConsensusTxSenders(bytes32 ctHandle) external view virtual returns (address[] memory) {
248271
CiphertextCommitsStorage storage $ = _getCiphertextCommitsStorage();
249272

250273
// Get the unique hash associated to the handle in order to retrieve the list of transaction
@@ -255,6 +278,54 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat
255278
return $.coprocessorTxSenderAddresses[addCiphertextHash];
256279
}
257280

281+
/**
282+
* @notice See {ICiphertextCommits-getConsensusStorageUrls}.
283+
*/
284+
function getConsensusStorageUrls(bytes32[] calldata ctHandles) external view virtual returns (string[][] memory) {
285+
CiphertextCommitsStorage storage $ = _getCiphertextCommitsStorage();
286+
string[][] memory consensusStorageUrls = new string[][](ctHandles.length);
287+
288+
for (uint256 i = 0; i < ctHandles.length; i++) {
289+
// Check that the consensus has been reached
290+
if (!isCiphertextMaterialAdded(ctHandles[i])) {
291+
revert CiphertextMaterialNotFound(ctHandles[i]);
292+
}
293+
294+
// Get the unique hash associated to the handle in order to retrieve the list of transaction
295+
// sender address that participated in the consensus
296+
// This digest is null (0x0) for version V1.
297+
bytes32 addCiphertextHash = $.ctHandleConsensusHash[ctHandles[i]];
298+
299+
// Get the transaction sender addresses and the context ID associated to the consensus
300+
// If the consensus has been reached but the hash is 0x0, it means that the handle has been
301+
// added in V1: the handle was used to retrieve the list of transaction sender addresses
302+
// instead of the hash, under the first context (`contextId=1`).
303+
// We therefore consider this in order to be backward compatible.
304+
// DEPRECATED: to remove in next state reset
305+
// See https://github.com/zama-ai/fhevm-internal/issues/471
306+
address[] memory coprocessorTxSenderAddresses;
307+
uint256 contextId;
308+
if (addCiphertextHash == bytes32(0)) {
309+
coprocessorTxSenderAddresses = $.coprocessorTxSenderAddresses[ctHandles[i]];
310+
contextId = 1;
311+
} else {
312+
coprocessorTxSenderAddresses = $.coprocessorTxSenderAddresses[addCiphertextHash];
313+
contextId = $.addCiphertextContextId[addCiphertextHash];
314+
}
315+
316+
// Get the list of storage URLs associated to the transaction sender addresses
317+
string[] memory coprocessorStorageUrls = new string[](coprocessorTxSenderAddresses.length);
318+
for (uint256 j = 0; j < coprocessorTxSenderAddresses.length; j++) {
319+
coprocessorStorageUrls[j] = COPROCESSOR_CONTEXTS
320+
.getCoprocessorFromContext(contextId, coprocessorTxSenderAddresses[j])
321+
.storageUrl;
322+
}
323+
consensusStorageUrls[i] = coprocessorStorageUrls;
324+
}
325+
326+
return consensusStorageUrls;
327+
}
328+
258329
/**
259330
* @notice See {ICiphertextCommits-getVersion}.
260331
*/
@@ -280,12 +351,13 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat
280351
function _authorizeUpgrade(address _newImplementation) internal virtual override onlyGatewayOwner {}
281352

282353
/**
283-
* @notice Checks if the consensus is reached among the Coprocessors.
354+
* @notice Checks if the consensus is reached among the coprocessors from the same context.
355+
* @param contextId The coprocessor context ID
284356
* @param coprocessorCounter The number of coprocessors that agreed
285357
* @return Whether the consensus is reached
286358
*/
287-
function _isConsensusReached(uint256 coprocessorCounter) internal view virtual returns (bool) {
288-
uint256 consensusThreshold = GATEWAY_CONFIG.getCoprocessorMajorityThreshold();
359+
function _isConsensusReached(uint256 contextId, uint256 coprocessorCounter) internal view virtual returns (bool) {
360+
uint256 consensusThreshold = COPROCESSOR_CONTEXTS.getCoprocessorMajorityThresholdFromContext(contextId);
289361
return coprocessorCounter >= consensusThreshold;
290362
}
291363

0 commit comments

Comments
 (0)