Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add reorgProtectionEnabled feature flag in registry 2.2 #11862

Merged
merged 18 commits into from
Jan 25, 2024
Merged

Large diffs are not rendered by default.

189 changes: 189 additions & 0 deletions contracts/src/v0.8/automation/dev/test/AutomationRegistry2_2.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

import {AutomationForwarderLogic} from "../../AutomationForwarderLogic.sol";
import {BaseTest} from "./BaseTest.t.sol";
import {AutomationRegistry2_2} from "../v2_2/AutomationRegistry2_2.sol";
import {AutomationRegistryBase2_2} from "../v2_2/AutomationRegistryBase2_2.sol";
import {AutomationRegistryLogicA2_2} from "../v2_2/AutomationRegistryLogicA2_2.sol";
import {AutomationRegistryLogicB2_2} from "../v2_2/AutomationRegistryLogicB2_2.sol";
import {IAutomationRegistryMaster} from "../interfaces/v2_2/IAutomationRegistryMaster.sol";

contract AutomationRegistry2_2_SetUp is BaseTest {
address internal constant LINK_ETH_FEED = 0x1111111111111111111111111111111111111110;
address internal constant FAST_GAS_FEED = 0x1111111111111111111111111111111111111112;
address internal constant LINK_TOKEN = 0x1111111111111111111111111111111111111113;

// Signer private keys used for these test
uint256 internal constant PRIVATE0 = 0x7b2e97fe057e6de99d6872a2ef2abf52c9b4469bc848c2465ac3fcd8d336e81d;
uint256 internal constant PRIVATE1 = 0xab56160806b05ef1796789248e1d7f34a6465c5280899159d645218cd216cee6;
uint256 internal constant PRIVATE2 = 0x6ec7caa8406a49b76736602810e0a2871959fbbb675e23a8590839e4717f1f7f;
uint256 internal constant PRIVATE3 = 0x80f14b11da94ae7f29d9a7713ea13dc838e31960a5c0f2baf45ed458947b730a;

uint64 internal constant OFFCHAIN_CONFIG_VERSION = 30; // 2 for OCR2
uint8 internal constant F = 1;

address[] internal s_valid_signers;
address[] internal s_valid_transmitters;
address[] internal s_registrars;

function setUp() public override {
s_valid_transmitters = new address[](4);
for (uint160 i = 0; i < 4; ++i) {
s_valid_transmitters[i] = address(4 + i);
}

s_valid_signers = new address[](4);
s_valid_signers[0] = vm.addr(PRIVATE0); //0xc110458BE52CaA6bB68E66969C3218A4D9Db0211
s_valid_signers[1] = vm.addr(PRIVATE1); //0xc110a19c08f1da7F5FfB281dc93630923F8E3719
s_valid_signers[2] = vm.addr(PRIVATE2); //0xc110fdF6e8fD679C7Cc11602d1cd829211A18e9b
s_valid_signers[3] = vm.addr(PRIVATE3); //0xc11028017c9b445B6bF8aE7da951B5cC28B326C0

s_registrars = new address[](1);
s_registrars[0] = 0x3a0eDE26aa188BFE00b9A0C9A431A1a0CA5f7966;
}

function deployRegistry2_2(AutomationRegistryBase2_2.Mode mode) public returns (IAutomationRegistryMaster) {
AutomationForwarderLogic forwarderLogic = new AutomationForwarderLogic();
AutomationRegistryLogicB2_2 logicB2_2 = new AutomationRegistryLogicB2_2(
mode,
LINK_TOKEN,
LINK_ETH_FEED,
FAST_GAS_FEED,
address(forwarderLogic)
);
AutomationRegistryLogicA2_2 logicA2_2 = new AutomationRegistryLogicA2_2(logicB2_2);
IAutomationRegistryMaster registry2_2 = IAutomationRegistryMaster(
address(new AutomationRegistry2_2(AutomationRegistryLogicB2_2(address(logicA2_2))))
);
return registry2_2;
}
}

contract AutomationRegistry2_2_LatestConfigDetails is AutomationRegistry2_2_SetUp {
function testGet() public {
IAutomationRegistryMaster registry = IAutomationRegistryMaster(
address(deployRegistry2_2(AutomationRegistryBase2_2.Mode(0)))
);
(uint32 configCount, uint32 blockNumber, bytes32 configDigest) = registry.latestConfigDetails();
assertEq(configCount, 0);
assertEq(blockNumber, 0);
assertEq(configDigest, "");
}
}

contract AutomationRegistry2_2_SetConfig is AutomationRegistry2_2_SetUp {
Comment on lines +63 to +75
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can keep all of the tests in a single AutomationRegistry2_2_Test contract, yeah? I don't see the need to separate tests into their own contracts

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

according to CCIP's foundry tests, each function to be tested will have a separate contract like this.
i am thinking we can follow their style guide.

event ConfigSet(
uint32 previousConfigBlockNumber,
bytes32 configDigest,
uint64 configCount,
address[] signers,
address[] transmitters,
uint8 f,
bytes onchainConfig,
uint64 offchainConfigVersion,
bytes offchainConfig
);

function testSetConfigSuccess() public {
IAutomationRegistryMaster registry = IAutomationRegistryMaster(
address(deployRegistry2_2(AutomationRegistryBase2_2.Mode(0)))
);
(uint32 configCount, , ) = registry.latestConfigDetails();
assertEq(configCount, 0);

AutomationRegistryBase2_2.OnchainConfig memory cfg = AutomationRegistryBase2_2.OnchainConfig({
paymentPremiumPPB: 10_000,
flatFeeMicroLink: 40_000,
checkGasLimit: 5_000_000,
stalenessSeconds: 90_000,
gasCeilingMultiplier: 0,
minUpkeepSpend: 0,
maxPerformGas: 10_000_000,
maxCheckDataSize: 5_000,
maxPerformDataSize: 5_000,
maxRevertDataSize: 5_000,
fallbackGasPrice: 20_000_000_000,
fallbackLinkPrice: 200_000_000_000,
transcoder: 0xB1e66855FD67f6e85F0f0fA38cd6fBABdf00923c,
registrars: s_registrars,
upkeepPrivilegeManager: 0xD9c855F08A7e460691F41bBDDe6eC310bc0593D8,
reorgProtectionEnabled: true
});
bytes memory onchainConfigBytes = abi.encode(cfg);

uint256 a = 1234;
address b = address(0);
bytes memory offchainConfigBytes = abi.encode(a, b);
bytes32 configDigest = _configDigestFromConfigData(
block.chainid,
address(registry),
++configCount,
s_valid_signers,
s_valid_transmitters,
F,
onchainConfigBytes,
OFFCHAIN_CONFIG_VERSION,
offchainConfigBytes
);

vm.expectEmit();
emit ConfigSet(
0,
configDigest,
configCount,
s_valid_signers,
s_valid_transmitters,
F,
onchainConfigBytes,
OFFCHAIN_CONFIG_VERSION,
offchainConfigBytes
);

registry.setConfig(
s_valid_signers,
s_valid_transmitters,
F,
onchainConfigBytes,
OFFCHAIN_CONFIG_VERSION,
offchainConfigBytes
);

(, , address[] memory signers, address[] memory transmitters, uint8 f) = registry.getState();

assertEq(signers, s_valid_signers);
assertEq(transmitters, s_valid_transmitters);
assertEq(f, F);
}

function _configDigestFromConfigData(
uint256 chainId,
address contractAddress,
uint64 configCount,
address[] memory signers,
address[] memory transmitters,
uint8 f,
bytes memory onchainConfig,
uint64 offchainConfigVersion,
bytes memory offchainConfig
) internal pure returns (bytes32) {
uint256 h = uint256(
keccak256(
abi.encode(
chainId,
contractAddress,
configCount,
signers,
transmitters,
f,
onchainConfig,
offchainConfigVersion,
offchainConfig
)
)
);
uint256 prefixMask = type(uint256).max << (256 - 16); // 0xFFFF00..00
uint256 prefix = 0x0001 << (256 - 16); // 0x000100..00
return bytes32((prefix & prefixMask) | (h & ~prefixMask));
}
}
13 changes: 13 additions & 0 deletions contracts/src/v0.8/automation/dev/test/BaseTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

import "forge-std/Test.sol";

contract BaseTest is Test {
address internal OWNER = 0x00007e64E1fB0C487F25dd6D3601ff6aF8d32e4e;

function setUp() public virtual {
vm.startPrank(OWNER);
deal(OWNER, 1e20);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ contract AutomationRegistry2_2 is AutomationRegistryBase2_2, OCR2Abstract, Chain
(upkeepTransmitInfo[i].earlyChecksPassed, upkeepTransmitInfo[i].dedupID) = _prePerformChecks(
report.upkeepIds[i],
report.triggers[i],
upkeepTransmitInfo[i]
upkeepTransmitInfo[i],
hotVars
);

if (upkeepTransmitInfo[i].earlyChecksPassed) {
Expand Down Expand Up @@ -308,7 +309,8 @@ contract AutomationRegistry2_2 is AutomationRegistryBase2_2, OCR2Abstract, Chain
paused: s_hotVars.paused,
reentrancyGuard: s_hotVars.reentrancyGuard,
totalPremium: totalPremium,
latestEpoch: 0 // DON restarts epoch
latestEpoch: 0, // DON restarts epoch
reorgProtectionEnabled: onchainConfig.reorgProtectionEnabled
});

s_storage = Storage({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPreventi
* @member transcoder address of the transcoder contract
* @member registrars addresses of the registrar contracts
* @member upkeepPrivilegeManager address which can set privilege for upkeeps
* @member reorgProtectionEnabled if this registry will enable re-org protection checks
*/
struct OnchainConfig {
uint32 paymentPremiumPPB;
Expand All @@ -225,6 +226,7 @@ abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPreventi
address transcoder;
address[] registrars;
address upkeepPrivilegeManager;
bool reorgProtectionEnabled;
}

/**
Expand Down Expand Up @@ -307,16 +309,16 @@ abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPreventi

/// @dev Config + State storage struct which is on hot transmit path
struct HotVars {
uint8 f; // maximum number of faulty oracles
uint32 paymentPremiumPPB; // premium percentage charged to user over tx cost
uint32 flatFeeMicroLink; // flat fee charged to user for every perform
uint24 stalenessSeconds; // Staleness tolerance for feeds
uint16 gasCeilingMultiplier; // multiplier on top of fast gas feed for upper bound
bool paused; // pause switch for all upkeeps in the registry
bool reentrancyGuard; // guard against reentrancy
uint96 totalPremium; // total historical payment to oracles for premium
uint32 latestEpoch; // latest epoch for which a report was transmitted
// 1 EVM word full
uint96 totalPremium; // ─────────╮ total historical payment to oracles for premium
uint32 paymentPremiumPPB; // premium percentage charged to user over tx cost
uint32 flatFeeMicroLink; // flat fee charged to user for every perform
uint32 latestEpoch; // │ latest epoch for which a report was transmitted
uint24 stalenessSeconds; // │ Staleness tolerance for feeds
uint16 gasCeilingMultiplier; // │ multiplier on top of fast gas feed for upper bound
uint8 f; // │ maximum number of faulty oracles
bool paused; // │ pause switch for all upkeeps in the registry
bool reentrancyGuard; // ────────╯ guard against reentrancy
bool reorgProtectionEnabled; // if this registry should enable re-org protection mechanism
}

/// @dev Config + State storage struct which is not on hot transmit path
Expand Down Expand Up @@ -733,14 +735,15 @@ abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPreventi
function _prePerformChecks(
uint256 upkeepId,
bytes memory rawTrigger,
UpkeepTransmitInfo memory transmitInfo
UpkeepTransmitInfo memory transmitInfo,
HotVars memory hotVars
) internal returns (bool, bytes32) {
bytes32 dedupID;
if (transmitInfo.triggerType == Trigger.CONDITION) {
if (!_validateConditionalTrigger(upkeepId, rawTrigger, transmitInfo)) return (false, dedupID);
if (!_validateConditionalTrigger(upkeepId, rawTrigger, transmitInfo, hotVars)) return (false, dedupID);
} else if (transmitInfo.triggerType == Trigger.LOG) {
bool valid;
(valid, dedupID) = _validateLogTrigger(upkeepId, rawTrigger);
(valid, dedupID) = _validateLogTrigger(upkeepId, rawTrigger, hotVars);
if (!valid) return (false, dedupID);
} else {
revert InvalidTriggerType();
Expand All @@ -765,7 +768,8 @@ abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPreventi
function _validateConditionalTrigger(
uint256 upkeepId,
bytes memory rawTrigger,
UpkeepTransmitInfo memory transmitInfo
UpkeepTransmitInfo memory transmitInfo,
HotVars memory hotVars
) internal returns (bool) {
ConditionalTrigger memory trigger = abi.decode(rawTrigger, (ConditionalTrigger));
if (trigger.blockNum < transmitInfo.upkeep.lastPerformedBlockNumber) {
Expand All @@ -774,7 +778,8 @@ abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPreventi
return false;
}
if (
(trigger.blockHash != bytes32("") && _blockHash(trigger.blockNum) != trigger.blockHash) ||
(hotVars.reorgProtectionEnabled &&
(trigger.blockHash != bytes32("") && _blockHash(trigger.blockNum) != trigger.blockHash)) ||
trigger.blockNum >= _blockNum()
) {
// There are two cases of reorged report
Expand All @@ -789,11 +794,16 @@ abstract contract AutomationRegistryBase2_2 is ConfirmedOwner, ExecutionPreventi
return true;
}

function _validateLogTrigger(uint256 upkeepId, bytes memory rawTrigger) internal returns (bool, bytes32) {
function _validateLogTrigger(
uint256 upkeepId,
bytes memory rawTrigger,
HotVars memory hotVars
) internal returns (bool, bytes32) {
LogTrigger memory trigger = abi.decode(rawTrigger, (LogTrigger));
bytes32 dedupID = keccak256(abi.encodePacked(upkeepId, trigger.logBlockHash, trigger.txHash, trigger.logIndex));
if (
(trigger.blockHash != bytes32("") && _blockHash(trigger.blockNum) != trigger.blockHash) ||
(hotVars.reorgProtectionEnabled &&
(trigger.blockHash != bytes32("") && _blockHash(trigger.blockNum) != trigger.blockHash)) ||
trigger.blockNum >= _blockNum()
) {
// Reorg protection is same as conditional trigger upkeeps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,8 @@ contract AutomationRegistryLogicB2_2 is AutomationRegistryBase2_2 {
fallbackLinkPrice: s_fallbackLinkPrice,
transcoder: s_storage.transcoder,
registrars: s_registrars.values(),
upkeepPrivilegeManager: s_storage.upkeepPrivilegeManager
upkeepPrivilegeManager: s_storage.upkeepPrivilegeManager,
reorgProtectionEnabled: s_hotVars.reorgProtectionEnabled
});

return (state, config, s_signersList, s_transmittersList, s_hotVars.f);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,7 @@ describe('AutomationRegistry2_2', () => {
transcoder: transcoder.address,
registrars: [],
upkeepPrivilegeManager: upkeepManager,
reorgProtectionEnabled: true,
}),
offchainVersion,
offchainBytes,
Expand Down Expand Up @@ -880,6 +881,7 @@ describe('AutomationRegistry2_2', () => {
transcoder: transcoder.address,
registrars: [],
upkeepPrivilegeManager: upkeepManager,
reorgProtectionEnabled: true,
}

baseConfig = [
Expand Down Expand Up @@ -3544,6 +3546,7 @@ describe('AutomationRegistry2_2', () => {
transcoder: newTranscoder,
registrars: newRegistrars,
upkeepPrivilegeManager: upkeepManager,
reorgProtectionEnabled: true,
}

it('reverts when called by anyone but the proposed owner', async () => {
Expand Down Expand Up @@ -4565,6 +4568,7 @@ describe('AutomationRegistry2_2', () => {
transcoder: transcoder.address,
registrars: [],
upkeepPrivilegeManager: upkeepManager,
reorgProtectionEnabled: true,
},
offchainVersion,
offchainBytes,
Expand Down Expand Up @@ -5209,6 +5213,7 @@ describe('AutomationRegistry2_2', () => {
transcoder: transcoder.address,
registrars: [],
upkeepPrivilegeManager: upkeepManager,
reorgProtectionEnabled: true,
},
offchainVersion,
offchainBytes,
Expand Down Expand Up @@ -5262,6 +5267,7 @@ describe('AutomationRegistry2_2', () => {
transcoder: transcoder.address,
registrars: [],
upkeepPrivilegeManager: upkeepManager,
reorgProtectionEnabled: true,
},
offchainVersion,
offchainBytes,
Expand Down Expand Up @@ -5310,6 +5316,7 @@ describe('AutomationRegistry2_2', () => {
transcoder: transcoder.address,
registrars: [],
upkeepPrivilegeManager: upkeepManager,
reorgProtectionEnabled: true,
},
offchainVersion,
offchainBytes,
Expand Down
Loading
Loading