Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions .github/workflows/gateway-contracts-upgrade-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ jobs:
sc-upgrade:
name: gateway-contracts-upgrade-tests/sc-upgrade (bpr)
needs: check-changes
# if: ${{ needs.check-changes.outputs.changes-gw-contracts == 'true' }}
# TODO: Re-enable the upgrade tests once fhevm 0.9 is released
# See https://github.com/zama-ai/fhevm-internal/issues/439
if: false
if: ${{ needs.check-changes.outputs.changes-gw-contracts == 'true' }}
permissions:
contents: 'read' # Required to checkout repository code
checks: 'write' # Required to create GitHub checks for test results
Expand All @@ -48,7 +45,7 @@ jobs:
with:
# This version should be updated whenever we release new contract versions or
# touch a contract upgrade path.
ref: v0.8.0-6
ref: v0.9.1-1
path: previous-fhevm
persist-credentials: 'false'

Expand Down Expand Up @@ -209,15 +206,17 @@ jobs:
# See https://github.com/zama-ai/fhevm-internal/issues/379
- name: Upgrade KMSGeneration contract
working-directory: current-fhevm/gateway-contracts
# TODO: Re-enable the upgrade tests once the contract needs an upgrade
if: false
env:
DOTENV_CONFIG_PATH: .env
HARDHAT_NETWORK: staging
CHAIN_ID_GATEWAY: 54321
RPC_URL: http://localhost:8546
run: |
npx hardhat task:upgradeKmsGeneration \
--current-implementation previous-contracts/KmsGeneration.sol:KmsGeneration \
--new-implementation contracts/KmsGeneration.sol:KmsGeneration \
npx hardhat task:upgradeKMSGeneration \
--current-implementation previous-contracts/KMSGeneration.sol:KMSGeneration \
--new-implementation contracts/KMSGeneration.sol:KMSGeneration \
--use-internal-proxy-address true \
--verify-contract false

Expand Down
12 changes: 9 additions & 3 deletions gateway-contracts/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,30 @@ KMS_SIGNER_ADDRESS_3="0xDb216ECeC4cEd51CdfD9609b6Ce7653aB04f6cAd" # accounts[10]
KMS_NODE_IP_ADDRESS_3="127.0.0.4" # (string)
KMS_NODE_STORAGE_URL_3="s3://kms-bucket-4" # (string)

# Coprocessor blob
COPROCESSORS_BLOB="0x54c4e861e4036812a7d137acfbf134758045b3e8feec003d838082fbd4ae6f2192ea668d9bb73abedd159ffa9d09d3d5846d9c4ac19c40aa16e894ae491f3cc3" # (bytes)

# Coprocessors
# The number of coprocessors must be lower or equal to the number of coprocessors' metadata defined below
NUM_COPROCESSORS="3" # (number)

# Coprocessor 1
COPROCESSOR_NAME_0="Coprocessor 1" # (string)
COPROCESSOR_TX_SENDER_ADDRESS_0="0x6518D50aDc9036Df37119eA465a8159E34417E2E" # accounts[11] (address)
COPROCESSOR_SIGNER_ADDRESS_0="0xa5eE8292dA52d8234248709F3E217ffEBA5E8312" # accounts[12] (address)
COPROCESSOR_S3_BUCKET_URL_0="s3://coprocessor-bucket-1" # (string)
COPROCESSOR_STORAGE_URL_0="s3://coprocessor-bucket-1" # (string)

# Coprocessor 2
COPROCESSOR_NAME_1="Coprocessor 2" # (string)
COPROCESSOR_TX_SENDER_ADDRESS_1="0xCFbF539CB91c92ace0343c5B0487149Ad0b82078" # accounts[13] (address)
COPROCESSOR_SIGNER_ADDRESS_1="0xA951F315d5FD35Cac111dFB5250DF231FB8eF905" # accounts[14] (address)
COPROCESSOR_S3_BUCKET_URL_1="s3://coprocessor-bucket-2" # (string)
COPROCESSOR_STORAGE_URL_1="s3://coprocessor-bucket-2" # (string)

# Coprocessor 3
COPROCESSOR_NAME_2="Coprocessor 3" # (string)
COPROCESSOR_TX_SENDER_ADDRESS_2="0x3C0033584da3A0f61AA5C7bde50eAF3642875a21" # accounts[15] (address)
COPROCESSOR_SIGNER_ADDRESS_2="0x420AF5A5BBfAd922aE5a501d9a8Bf70a55F52E03" # accounts[16] (address)
COPROCESSOR_S3_BUCKET_URL_2="s3://coprocessor-bucket-3" # (string)
COPROCESSOR_STORAGE_URL_2="s3://coprocessor-bucket-3" # (string)

# Custodians
# The number of custodians must be lower or equal to the number of custodians' metadata defined below
Expand Down
12 changes: 12 additions & 0 deletions gateway-contracts/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,15 @@ prerelease:
# Get the size of the contracts
get-contracts-sized:
forge build --sizes

# Get the list of coprocessor signers from the active coprocessor context registered in the gateway
get-coprocessor-signers:
DOTENV_CONFIG_PATH=$(ENV_PATH) npx hardhat task:getCoprocessorSigners --use-internal-proxy-address true --network localGateway

# Get the list of KMS signers registered in the gateway
get-kms-signers:
DOTENV_CONFIG_PATH=$(ENV_PATH) npx hardhat task:getKmsSigners --use-internal-proxy-address true --network localGateway

# Get the list of host chains registered in the gateway
get-host-chains:
DOTENV_CONFIG_PATH=$(ENV_PATH) npx hardhat task:getHostChains --use-internal-proxy-address true --network localGateway
118 changes: 75 additions & 43 deletions gateway-contracts/contracts/CiphertextCommits.sol
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import { gatewayConfigAddress, kmsGenerationAddress } from "../addresses/GatewayAddresses.sol";
import {
gatewayConfigAddress,
kmsGenerationAddress,
coprocessorContextsAddress
} from "../addresses/GatewayAddresses.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { ICiphertextCommits } from "./interfaces/ICiphertextCommits.sol";
import { IGatewayConfig } from "./interfaces/IGatewayConfig.sol";
import { IKMSGeneration } from "./interfaces/IKMSGeneration.sol";
import { ICoprocessorContexts } from "./interfaces/ICoprocessorContexts.sol";
import { UUPSUpgradeableEmptyProxy } from "./shared/UUPSUpgradeableEmptyProxy.sol";
import { GatewayConfigChecks } from "./shared/GatewayConfigChecks.sol";
import { GatewayOwnable } from "./shared/GatewayOwnable.sol";
import { CiphertextMaterial, SnsCiphertextMaterial } from "./shared/Structs.sol";
import { ContextStatus } from "./shared/Enums.sol";
import { ContextChecks } from "./shared/ContextChecks.sol";

/**
* @title CiphertextCommits smart contract
* @notice See {ICiphertextCommits}.
*/
contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, GatewayOwnable, GatewayConfigChecks {
contract CiphertextCommits is
ICiphertextCommits,
UUPSUpgradeableEmptyProxy,
GatewayOwnable,
GatewayConfigChecks,
ContextChecks
{
/**
* @notice The address of the GatewayConfig contract, used for fetching information about coprocessors.
*/
Expand All @@ -25,14 +38,17 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat
*/
IKMSGeneration private constant KMS_GENERATION = IKMSGeneration(kmsGenerationAddress);

/// @notice The address of the CoprocessorContexts contract, used for fetching information about coprocessors.
ICoprocessorContexts private constant COPROCESSOR_CONTEXTS = ICoprocessorContexts(coprocessorContextsAddress);

/**
* @dev The following constants are used for versioning the contract. They are made private
* in order to force derived contracts to consider a different version. Note that
* they can still define their own private constants with the same name.
*/
string private constant CONTRACT_NAME = "CiphertextCommits";
uint256 private constant MAJOR_VERSION = 0;
uint256 private constant MINOR_VERSION = 1;
uint256 private constant MINOR_VERSION = 2;
uint256 private constant PATCH_VERSION = 0;

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

/**
* @notice The contract's variable storage struct (@dev see ERC-7201)
Expand Down Expand Up @@ -104,44 +120,57 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat

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

/**
* @notice See {ICiphertextCommits-addCiphertextMaterial}.
*/
/// @notice See {ICiphertextCommits-addCiphertextMaterial}.
function addCiphertextMaterial(
bytes32 ctHandle,
uint256 keyId,
bytes32 ciphertextDigest,
bytes32 snsCiphertextDigest
) external virtual onlyCoprocessorTxSender onlyHandleFromRegisteredHostChain(ctHandle) {
CiphertextCommitsStorage storage $ = _getCiphertextCommitsStorage();

// Check if the coprocessor transaction sender has already added the ciphertext handle.
if ($.alreadyAddedCoprocessorTxSenders[ctHandle][msg.sender]) {
revert CoprocessorAlreadyAdded(ctHandle, msg.sender);
}

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

// Associate the handle to coprocessor context ID 1 to anticipate their introduction in V2.
// Only set the context ID if it hasn't been set yet to avoid multiple identical SSTOREs.
if ($.addCiphertextContextId[addCiphertextHash] == 0) {
$.addCiphertextContextId[addCiphertextHash] = 1;
CiphertextCommitsStorage storage $ = _getCiphertextCommitsStorage();

// Get the context ID from the input verification context ID mapping
// This ID may be 0 (invalid) if this is the first addCiphertextMaterial call for this
// addCiphertextHash (see right below)
uint256 contextId = $.addCiphertextContextId[addCiphertextHash];

// If the context ID is null, get the active coprocessor context's ID and associate it to
// this ciphertext material addition
if (contextId == 0) {
contextId = COPROCESSOR_CONTEXTS.getActiveCoprocessorContextId();
$.addCiphertextContextId[addCiphertextHash] = contextId;

// Else, that means a coprocessor already started to add the ciphertext material
// and we need to check that the context is active or suspended
// If it is not, that means the context is no longer valid for this operation and we revert
} else if (!COPROCESSOR_CONTEXTS.isCoprocessorContextOperating(contextId)) {
ContextStatus contextStatus = COPROCESSOR_CONTEXTS.getCoprocessorContextStatus(contextId);
revert InvalidCoprocessorContextAddCiphertext(ctHandle, contextId, contextStatus);
}

// It is ok to only the handle can be considered here as a handle should only be added once
// in the contract anyway
// Only accept coprocessor transaction senders from the same context
_checkIsCoprocessorTxSender(contextId, msg.sender);

// Check if the coprocessor transaction sender has already added the ciphertext handle.
if ($.alreadyAddedCoprocessorTxSenders[ctHandle][msg.sender]) {
revert CoprocessorAlreadyAdded(ctHandle, msg.sender);
}

$.addCiphertextHashCounters[addCiphertextHash]++;

// It is ok to only consider the handle here as it is only added once in the contract anyway
$.alreadyAddedCoprocessorTxSenders[ctHandle][msg.sender] = true;

// Store the coprocessor transaction sender address for the ciphertext material addition
Expand All @@ -154,9 +183,10 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat

// Send the event if and only if the consensus is reached in the current response call.
// This means a "late" response will not be reverted, just ignored and no event will be emitted
// Besides, consensus only considers the coprocessors of the same context
if (
!$.isCiphertextMaterialAdded[ctHandle] &&
_isConsensusReached($.addCiphertextHashCounters[addCiphertextHash])
_isConsensusReached(contextId, $.addCiphertextHashCounters[addCiphertextHash])
) {
$.ciphertextDigests[ctHandle] = ciphertextDigest;
$.snsCiphertextDigests[ctHandle] = snsCiphertextDigest;
Expand All @@ -170,12 +200,7 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat
// by only knowing the handle, since a consensus can only happen once per handle
$.ctHandleConsensusHash[ctHandle] = addCiphertextHash;

emit AddCiphertextMaterial(
ctHandle,
ciphertextDigest,
snsCiphertextDigest,
$.coprocessorTxSenderAddresses[addCiphertextHash]
);
emit AddCiphertextMaterial(ctHandle, ciphertextDigest, snsCiphertextDigest, contextId);
}
}

Expand Down Expand Up @@ -205,13 +230,13 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat
// Get the unique hash associated to the handle and use it to get the list of coprocessor
// transaction sender address that were involved in the consensus
bytes32 addCiphertextHash = $.ctHandleConsensusHash[ctHandles[i]];
address[] memory coprocessorTxSenderAddresses = $.coprocessorTxSenderAddresses[addCiphertextHash];

ctMaterials[i] = CiphertextMaterial(
ctHandles[i],
$.keyIds[ctHandles[i]],
$.ciphertextDigests[ctHandles[i]],
coprocessorTxSenderAddresses
$.coprocessorTxSenderAddresses[addCiphertextHash],
$.addCiphertextContextId[addCiphertextHash]
);
}

Expand All @@ -236,34 +261,40 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat
// Get the unique hash associated to the handle and use it to get the list of coprocessor
// transaction sender address that were involved in the consensus
bytes32 addCiphertextHash = $.ctHandleConsensusHash[ctHandles[i]];
address[] memory coprocessorTxSenderAddresses = $.coprocessorTxSenderAddresses[addCiphertextHash];

snsCtMaterials[i] = SnsCiphertextMaterial(
ctHandles[i],
$.keyIds[ctHandles[i]],
$.snsCiphertextDigests[ctHandles[i]],
coprocessorTxSenderAddresses
$.coprocessorTxSenderAddresses[addCiphertextHash],
$.addCiphertextContextId[addCiphertextHash]
);
}

return snsCtMaterials;
}

/**
* @notice See {ICiphertextCommits-getAddCiphertextMaterialConsensusTxSenders}.
* The list remains empty until the consensus is reached.
* @notice See {ICiphertextCommits-getConsensusCoprocessorTxSenders}.
* The list remains empty and the contextId is 0 until the consensus is reached.
*/
function getAddCiphertextMaterialConsensusTxSenders(
function getConsensusCoprocessorTxSenders(
bytes32 ctHandle
) external view virtual returns (address[] memory) {
) external view virtual returns (address[] memory, uint256, bool) {
CiphertextCommitsStorage storage $ = _getCiphertextCommitsStorage();

bool ciphertextMaterialAdded = $.isCiphertextMaterialAdded[ctHandle];

// Get the unique hash associated to the handle in order to retrieve the list of transaction
// sender address that participated in the consensus
// This digest remains the default value (0x0) until the consensus is reached.
bytes32 addCiphertextHash = $.ctHandleConsensusHash[ctHandle];

return $.coprocessorTxSenderAddresses[addCiphertextHash];
return (
$.coprocessorTxSenderAddresses[addCiphertextHash],
$.addCiphertextContextId[addCiphertextHash],
ciphertextMaterialAdded
);
}

/**
Expand Down Expand Up @@ -291,12 +322,13 @@ contract CiphertextCommits is ICiphertextCommits, UUPSUpgradeableEmptyProxy, Gat
function _authorizeUpgrade(address _newImplementation) internal virtual override onlyGatewayOwner {}

/**
* @notice Checks if the consensus is reached among the Coprocessors.
* @notice Checks if the consensus is reached among the coprocessors from the same context.
* @param contextId The coprocessor context ID
* @param coprocessorCounter The number of coprocessors that agreed
* @return Whether the consensus is reached
*/
function _isConsensusReached(uint256 coprocessorCounter) internal view virtual returns (bool) {
uint256 consensusThreshold = GATEWAY_CONFIG.getCoprocessorMajorityThreshold();
function _isConsensusReached(uint256 contextId, uint256 coprocessorCounter) internal view virtual returns (bool) {
uint256 consensusThreshold = COPROCESSOR_CONTEXTS.getCoprocessorMajorityThreshold(contextId);
return coprocessorCounter >= consensusThreshold;
}

Expand Down
Loading
Loading