Skip to content

Commit

Permalink
Blue/green deployments for LLO; code cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
samsondav committed Oct 15, 2024
1 parent 37f3132 commit e7262f8
Show file tree
Hide file tree
Showing 75 changed files with 5,564 additions and 493 deletions.
2 changes: 1 addition & 1 deletion ccip/config/evm/Simulated.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
ChainID = '1337'
FinalityDepth = 10
FinalityDepth = 1
MinIncomingConfirmations = 1
MinContractPayment = '100'
NoNewHeadsThreshold = '0s'
Expand Down
1 change: 1 addition & 0 deletions contracts/scripts/native_solc_compile_all_llo-feeds
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ compileContract llo-feeds/v0.5.0/configuration/Configurator.sol
# Test | Mocks
compileContract llo-feeds/v0.3.0/test/mocks/ErroredVerifier.sol
compileContract llo-feeds/v0.3.0/test/mocks/ExposedVerifier.sol
compileContract llo-feeds/v0.5.0/configuration/test/mocks/ExposedConfigurator.sol
28 changes: 0 additions & 28 deletions contracts/src/v0.8/llo-feeds/interfaces/IConfigurator.sol

This file was deleted.

198 changes: 164 additions & 34 deletions contracts/src/v0.8/llo-feeds/v0.5.0/configuration/Configurator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ pragma solidity 0.8.19;
import {ConfirmedOwner} from "../../../shared/access/ConfirmedOwner.sol";
import {TypeAndVersionInterface} from "../../../interfaces/TypeAndVersionInterface.sol";
import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol";
import {IConfigurator} from "../../interfaces/IConfigurator.sol";
import {IConfigurator} from "./interfaces/IConfigurator.sol";

// OCR2 standard
uint256 constant MAX_NUM_ORACLES = 31;

uint256 constant SUPPORTED_ONCHAIN_CONFIG_VERSION = 1;

/**
* @title Configurator
* @author samsondav
Expand All @@ -33,44 +35,152 @@ contract Configurator is IConfigurator, ConfirmedOwner, TypeAndVersionInterface,
error InsufficientSigners(uint256 numSigners, uint256 minSigners);

struct ConfigurationState {
// The number of times a new configuration
// has been set
// The number of times a configuration (either staging or production) has
// been set for this configID
uint64 configCount;
// The block number of the block the last time
/// the configuration was updated.
// the configuration was updated.
uint32 latestConfigBlockNumber;
// isGreenProduction is a bit flip that indicates whether blue is production
// exactly one of blue/green must be production at all times.
// 0 -> blue is production
// 1 -> green is production
//
// So, to clarify, if isGreenProduction is false (initial state) then:
// [0](blue) is production and [1](green) is staging/retired
//
// and if isGreenProduction is true then:
// [0](blue) is staging/retired and [1](green) is production
// State is swapped every time a staging config is promoted to production.
bool isGreenProduction;
// The digest of the current configurations (0 is always blue, 1 is always green)
bytes32[2] configDigest;
}

constructor() ConfirmedOwner(msg.sender) {}

/// @notice Configuration states keyed on DON ID
/// @notice Configuration states keyed on config ID
/// @dev The first element is the blue configuration state
/// and the second element is the green configuration state
mapping(bytes32 => ConfigurationState) internal s_configurationStates;

/// @inheritdoc IConfigurator
function setConfig(
bytes32 donId,
address[] memory signers,
function setProductionConfig(
bytes32 configId,
bytes[] memory signers,
bytes32[] memory offchainTransmitters,
uint8 f,
bytes memory onchainConfig,
uint64 offchainConfigVersion,
bytes memory offchainConfig
) external override checkConfigValid(signers.length, f) onlyOwner {
require(onchainConfig.length == 64, "Invalid onchainConfig length");

// Ensure that predecessorConfigDigest is unset and version is correct
uint256 version;
bytes32 predecessorConfigDigest;
assembly {
version := mload(add(onchainConfig, 32))
predecessorConfigDigest := mload(add(onchainConfig, 64))
}
require(version == SUPPORTED_ONCHAIN_CONFIG_VERSION, "Unsupported onchainConfig version");
require(predecessorConfigDigest == 0, "predecessorConfigDigest must be unset for production config");

_setConfig(
donId,
configId,
block.chainid,
address(this),
signers,
offchainTransmitters,
f,
onchainConfig,
offchainConfigVersion,
offchainConfig
offchainConfig,
true
);
}

/// @inheritdoc IConfigurator
function setStagingConfig(
bytes32 configId,
bytes[] memory signers,
bytes32[] memory offchainTransmitters,
uint8 f,
bytes memory onchainConfig,
uint64 offchainConfigVersion,
bytes memory offchainConfig
) external override checkConfigValid(signers.length, f) onlyOwner {
require(onchainConfig.length == 64, "Invalid onchainConfig length");

// Ensure that predecessorConfigDigest is set and corresponds to an
// existing production instance
uint256 version;
bytes32 predecessorConfigDigest;
assembly {
version := mload(add(onchainConfig, 32))
predecessorConfigDigest := mload(add(onchainConfig, 64))
}
require(version == SUPPORTED_ONCHAIN_CONFIG_VERSION, "Unsupported onchainConfig version");

ConfigurationState memory configurationState = s_configurationStates[configId];
uint256 idx = configurationState.isGreenProduction ? 1 : 0;
require(
predecessorConfigDigest == s_configurationStates[configId].configDigest[idx],
"Invalid predecessorConfigDigest"
);

_setConfig(
configId,
block.chainid,
address(this),
signers,
offchainTransmitters,
f,
onchainConfig,
offchainConfigVersion,
offchainConfig,
false
);
}

/// @inheritdoc IConfigurator
// This will trigger the following:
// - Offchain ShouldRetireCache will start returning true for the old (production)
// protocol instance
// - Once the old production instance retires it will generate a handover
// retirement report
// - The staging instance will become the new production instance once
// any honest oracle that is on both instances forward the retirement
// report from the old instance to the new instance via the
// PredecessorRetirementReportCache
//
// Note: the promotion flow only works if the previous production instance
// is working correctly & generating reports. If that's not the case, the
// owner is expected to "setProductionConfig" directly instead. This will
// cause "gaps" to be created, but that seems unavoidable in such a scenario.
function promoteStagingConfig(bytes32 configId, bool isGreenProduction) external onlyOwner {
ConfigurationState storage configurationState = s_configurationStates[configId];
require(
isGreenProduction == configurationState.isGreenProduction,
"PromoteStagingConfig: isGreenProduction must match contract state"
);
require(configurationState.configCount > 0, "PromoteStagingConfig: Config has never been set for this config ID");
require(
configurationState.configDigest[isGreenProduction ? 0 : 1] != bytes32(0),
"PromoteStagingConfig: Config digest must be set for the staging config"
);
bytes32 retiredConfigDigest = configurationState.configDigest[isGreenProduction ? 1 : 0];
require(
retiredConfigDigest != bytes32(0),
"PromoteStagingConfig: Config digest must be set for the retiring production config"
);

configurationState.isGreenProduction = !isGreenProduction; // flip blue<->green
emit PromoteStagingConfig(configId, retiredConfigDigest, !isGreenProduction);
}

/// @notice Sets config based on the given arguments
/// @param donId DON ID to set config for
/// @param configId config ID to set config for
/// @param sourceChainId Chain ID of source config
/// @param sourceAddress Address of source config Verifier
/// @param signers addresses with which oracles sign the reports
Expand All @@ -80,22 +190,23 @@ contract Configurator is IConfigurator, ConfirmedOwner, TypeAndVersionInterface,
/// @param offchainConfigVersion version number for offchainEncoding schema
/// @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract
function _setConfig(
bytes32 donId,
bytes32 configId,
uint256 sourceChainId,
address sourceAddress,
address[] memory signers,
bytes[] memory signers,
bytes32[] memory offchainTransmitters,
uint8 f,
bytes memory onchainConfig,
uint64 offchainConfigVersion,
bytes memory offchainConfig
bytes memory offchainConfig,
bool isProduction
) internal {
ConfigurationState storage configurationState = s_configurationStates[donId];
ConfigurationState storage configurationState = s_configurationStates[configId];

uint64 newConfigCount = ++configurationState.configCount;

bytes32 configDigest = _configDigestFromConfigData(
donId,
configId,
sourceChainId,
sourceAddress,
newConfigCount,
Expand All @@ -107,24 +218,43 @@ contract Configurator is IConfigurator, ConfirmedOwner, TypeAndVersionInterface,
offchainConfig
);

emit ConfigSet(
donId,
configurationState.latestConfigBlockNumber,
configDigest,
newConfigCount,
signers,
offchainTransmitters,
f,
onchainConfig,
offchainConfigVersion,
offchainConfig
);
if (isProduction) {
emit ProductionConfigSet(
configId,
configurationState.latestConfigBlockNumber,
configDigest,
newConfigCount,
signers,
offchainTransmitters,
f,
onchainConfig,
offchainConfigVersion,
offchainConfig,
configurationState.isGreenProduction
);
s_configurationStates[configId].configDigest[configurationState.isGreenProduction ? 1 : 0] = configDigest;
} else {
emit StagingConfigSet(
configId,
configurationState.latestConfigBlockNumber,
configDigest,
newConfigCount,
signers,
offchainTransmitters,
f,
onchainConfig,
offchainConfigVersion,
offchainConfig,
configurationState.isGreenProduction
);
s_configurationStates[configId].configDigest[configurationState.isGreenProduction ? 0 : 1] = configDigest;
}

configurationState.latestConfigBlockNumber = uint32(block.number);
}

/// @notice Generates the config digest from config data
/// @param donId DON ID to set config for
/// @param configId config ID to set config for
/// @param sourceChainId Chain ID of configurator contract
/// @param sourceAddress Address of configurator contract
/// @param configCount ordinal number of this config setting among all config settings over the life of this contract
Expand All @@ -136,11 +266,11 @@ contract Configurator is IConfigurator, ConfirmedOwner, TypeAndVersionInterface,
/// @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract
/// @dev This function is a modified version of the method from OCR2Abstract
function _configDigestFromConfigData(
bytes32 donId,
bytes32 configId,
uint256 sourceChainId,
address sourceAddress,
uint64 configCount,
address[] memory signers,
bytes[] memory signers,
bytes32[] memory offchainTransmitters,
uint8 f,
bytes memory onchainConfig,
Expand All @@ -150,7 +280,7 @@ contract Configurator is IConfigurator, ConfirmedOwner, TypeAndVersionInterface,
uint256 h = uint256(
keccak256(
abi.encode(
donId,
configId,
sourceChainId,
sourceAddress,
configCount,
Expand All @@ -164,7 +294,7 @@ contract Configurator is IConfigurator, ConfirmedOwner, TypeAndVersionInterface,
)
);
uint256 prefixMask = type(uint256).max << (256 - 16); // 0xFFFF00..00
// 0x0006 corresponds to ConfigDigestPrefixLLO in libocr
// 0x0009 corresponds to ConfigDigestPrefixLLO in libocr
uint256 prefix = 0x0009 << (256 - 16); // 0x000900..00
return bytes32((prefix & prefixMask) | (h & ~prefixMask));
}
Expand All @@ -176,7 +306,7 @@ contract Configurator is IConfigurator, ConfirmedOwner, TypeAndVersionInterface,

/// @inheritdoc TypeAndVersionInterface
function typeAndVersion() external pure override returns (string memory) {
return "Configurator 0.4.0";
return "Configurator 0.5.0";
}

modifier checkConfigValid(uint256 numSigners, uint256 f) {
Expand Down
Loading

0 comments on commit e7262f8

Please sign in to comment.