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

integration: diva (#352) #13

Merged
merged 1 commit into from
May 3, 2023
Merged
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
4 changes: 2 additions & 2 deletions external/examples/oracle/NFTAirdrop.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ pragma solidity ^0.8.16;

import "forge-std/Test.sol";
import "forge-std/console.sol";
import {NFTAirdrop} from "examples/oracle/NFTAirdrop.sol";
import {NFTAirdrop} from "external/examples/oracle/NFTAirdrop.sol";
import {MockTelepathy} from "src/amb/mocks/MockTelepathy.sol";
import {TelepathyOracle, RequestData} from "src/oracle/TelepathyOracle.sol";
import {TelepathyOracleFulfiller} from "src/oracle/TelepathyOracleFulfiller.sol";
Expand Down Expand Up @@ -175,4 +175,4 @@ contract NFTAirdropTest is Test {
vm.prank(address(oracle));
nftAirdrop.handleOracleResponse(1, responseData2, responseSuccess2);
}
}
}
200 changes: 200 additions & 0 deletions external/integrations/diva/DivaBeaconOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
pragma solidity 0.8.16;

import {ILightClient} from "src/lightclient/interfaces/ILightClient.sol";
import {SSZ} from "src/libraries/SimpleSerialize.sol";
import {BeaconOracleHelper} from "external/integrations/libraries/BeaconOracleHelper.sol";

contract DivaBeaconOracle {
ILightClient lightclient;

event BeaconOracleUpdate(uint256 validatorIndex);

error InvalidBeaconStateRootProof();
error InvalidValidatorProof(uint256 validatorIndex);
error InvalidDepositProof(bytes32 validatorPubkeyHash);
error InvalidBalanceProof(uint256 validatorIndex);
error InvalidLightClientAddress();
error InvalidValidatorFieldProof(BeaconOracleHelper.ValidatorField field, uint256 validatorIndex);

constructor(address _lightClient) {
if (_lightClient == address(0)) {
revert InvalidLightClientAddress();
}
lightclient = ILightClient(_lightClient);
}

/// @notice Mapping from SHA-256 hash of padded pubkey to the slot of the latest proved deposit
mapping(bytes32 => uint256) public depositedSlots;
/// @notice Mapping from validator index to latest proved balance
mapping(uint256 => uint256) public validatorBalances;
/// @notice Mapping from validator index to validator struct
mapping(uint256 => BeaconOracleHelper.Validator) public validatorState;

/// @notice Prove pubkey against deposit in beacon block
function proveDeposit(
uint256 _slot,
bytes32 _pubkeyHash,
// Index of deposit in deposit tree (MAX_LENGTH = 16)
uint256 _depositIndex,
bytes32[] calldata _depositedPubkeyProof
) external {
bytes32 blockHeaderRoot = ILightClient(lightclient).headers(_slot);

if (
!BeaconOracleHelper._verifyValidatorDeposited(
_depositIndex, _pubkeyHash, _depositedPubkeyProof, blockHeaderRoot
)
) {
revert InvalidDepositProof(_pubkeyHash);
}

depositedSlots[_pubkeyHash] = _slot;
}

/// @notice Prove pubkey, withdrawal credentials
function proveValidatorField(
BeaconOracleHelper.BeaconStateRootProofInfo calldata _beaconStateRootProofInfo,
BeaconOracleHelper.ValidatorProofInfo calldata _validatorProofInfo,
// Prove fields that are bytes32
bytes32 _validatorFieldLeaf,
bytes32[] calldata _validatorFieldProof,
BeaconOracleHelper.ValidatorField _field
) external {
_verifyValidator(_beaconStateRootProofInfo, _validatorProofInfo);
if (
!BeaconOracleHelper._verifyValidatorField(
_validatorProofInfo.validatorRoot,
_validatorFieldLeaf,
_validatorFieldProof,
_field)
) {
revert InvalidValidatorFieldProof(_field, _validatorProofInfo.validatorIndex);
}
BeaconOracleHelper.Validator storage validator = validatorState[_validatorProofInfo.validatorIndex];
if (_field == BeaconOracleHelper.ValidatorField.Pubkey) {
validator.pubkey = _validatorFieldLeaf;
} else if (_field == BeaconOracleHelper.ValidatorField.WithdrawalCredentials) {
validator.withdrawalCredentials = _validatorFieldLeaf;
}
}

/// @notice Prove slashed & status epochs
function proveValidatorField(
BeaconOracleHelper.BeaconStateRootProofInfo calldata _beaconStateRootProofInfo,
BeaconOracleHelper.ValidatorProofInfo calldata _validatorProofInfo,
// Prove fields that are uint256 or bool
uint256 _validatorFieldLeaf,
bytes32[] calldata _validatorFieldProof,
BeaconOracleHelper.ValidatorField _field
) external {
_verifyValidator(_beaconStateRootProofInfo, _validatorProofInfo);
if (
!BeaconOracleHelper._verifyValidatorField(
_validatorProofInfo.validatorRoot,
SSZ.toLittleEndian(_validatorFieldLeaf),
_validatorFieldProof,
_field)
) {
revert InvalidValidatorFieldProof(_field, _validatorProofInfo.validatorIndex);
}

BeaconOracleHelper.Validator memory validator = validatorState[_validatorProofInfo.validatorIndex];
if (_field == BeaconOracleHelper.ValidatorField.Slashed) {
validator.slashed = _validatorFieldLeaf == 1;
} else if (_field == BeaconOracleHelper.ValidatorField.ActivationEligibilityEpoch) {
validator.activationEligibilityEpoch = _validatorFieldLeaf;
} else if (_field == BeaconOracleHelper.ValidatorField.ActivationEpoch) {
validator.activationEpoch = _validatorFieldLeaf;
} else if (_field == BeaconOracleHelper.ValidatorField.ExitEpoch) {
validator.exitEpoch = _validatorFieldLeaf;
} else if (_field == BeaconOracleHelper.ValidatorField.WithdrawableEpoch) {
validator.withdrawableEpoch = _validatorFieldLeaf;
}
validatorState[_validatorProofInfo.validatorIndex] = validator;
}

/// @notice Proves the balance of a validator against balances array
function proveValidatorBalance(
BeaconOracleHelper.BeaconStateRootProofInfo calldata _beaconStateRootProofInfo,
BeaconOracleHelper.ValidatorProofInfo calldata _validatorProofInfo,
// Combined balances of 4 validators packed into same gindex
bytes32 _combinedBalance,
bytes32[] calldata _balanceProof
) external {
_verifyValidator(_beaconStateRootProofInfo, _validatorProofInfo);
if (
!BeaconOracleHelper._verifyValidatorBalance(
_balanceProof,
_validatorProofInfo.validatorIndex,
_combinedBalance,
_beaconStateRootProofInfo.beaconStateRoot
)
) {
revert InvalidBalanceProof(_validatorProofInfo.validatorIndex);
}

validatorBalances[_validatorProofInfo.validatorIndex] = BeaconOracleHelper
._getBalanceFromCombinedBalance(_validatorProofInfo.validatorIndex, _combinedBalance);

emit BeaconOracleUpdate(_validatorProofInfo.validatorIndex);
}

function getWithdrawalCredentials(uint256 _validatorIndex) external view returns (bytes32) {
return validatorState[_validatorIndex].withdrawalCredentials;
}

function getPubkey(uint256 _validatorIndex) external view returns (bytes32) {
return validatorState[_validatorIndex].pubkey;
}

function getSlashed(uint256 _validatorIndex) external view returns (bool) {
return validatorState[_validatorIndex].slashed;
}

function getActivationEligibilityEpoch(uint256 _validatorIndex)
external
view
returns (uint256)
{
return validatorState[_validatorIndex].activationEligibilityEpoch;
}

function getActivationEpoch(uint256 _validatorIndex) external view returns (uint256) {
return validatorState[_validatorIndex].activationEpoch;
}

function getExitEpoch(uint256 _validatorIndex) external view returns (uint256) {
return validatorState[_validatorIndex].exitEpoch;
}

function getWithdrawableEpoch(uint256 _validatorIndex) external view returns (uint256) {
return validatorState[_validatorIndex].withdrawableEpoch;
}

function getBalance(uint256 _validatorIndex) external view returns (uint256) {
return validatorBalances[_validatorIndex];
}

function getDepositStatus(bytes32 _validatorPubkeyHash) external view returns (uint256) {
return depositedSlots[_validatorPubkeyHash];
}

function _verifyValidator(
BeaconOracleHelper.BeaconStateRootProofInfo calldata _beaconStateRootProofInfo,
BeaconOracleHelper.ValidatorProofInfo calldata _validatorProofInfo
) internal view {
bytes32 blockHeaderRoot = ILightClient(lightclient).headers(_beaconStateRootProofInfo.slot);

if (!BeaconOracleHelper._verifyBeaconStateRoot(_beaconStateRootProofInfo, blockHeaderRoot)) {
revert InvalidBeaconStateRootProof();
}

if (
!BeaconOracleHelper._verifyValidatorRoot(
_validatorProofInfo, _beaconStateRootProofInfo.beaconStateRoot
)
) {
revert InvalidValidatorProof(_validatorProofInfo.validatorIndex);
}
}
}
50 changes: 13 additions & 37 deletions external/integrations/eigenlayer/EigenLayerBeaconOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ pragma solidity 0.8.16;
import {ILightClient} from "src/lightclient/interfaces/ILightClient.sol";
import {SSZ} from "src/libraries/SimpleSerialize.sol";
import {ILightClientUpdater} from "external/integrations/eigenlayer/ILightClientUpdater.sol";
import {BeaconOracleHelper} from "external/integrations/libraries/BeaconOracleHelper.sol";
import {EigenLayerBeaconOracleStorage} from
"external/integrations/eigenlayer/EigenLayerBeaconOracleStorage.sol";
import {ReentrancyGuardUpgradeable} from
"openzeppelin-contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";

contract EigenLayerBeaconOracle is ILightClientUpdater, EigenLayerBeaconOracleStorage {
uint256 internal constant EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX = 3222;
uint256 internal constant BEACON_STATE_ROOT_INDEX = 11;

event BeaconStateOracleUpdate(uint256 slot, uint256 blockNumber, bytes32 stateRoot);

error InvalidBlockNumberProof();
Expand All @@ -31,57 +29,35 @@ contract EigenLayerBeaconOracle is ILightClientUpdater, EigenLayerBeaconOracleSt
}

function fulfillRequest(
uint256 _slot,
BeaconOracleHelper.BeaconStateRootProofInfo calldata _beaconStateRootProofInfo,
uint256 _blockNumber,
bytes32[] calldata _blockNumberProof,
bytes32 _beaconStateRoot,
bytes32[] calldata _beaconStateRootProof
bytes32[] calldata _blockNumberProof
) external onlyWhitelistedUpdater {
if (_slot <= head) {
if (_beaconStateRootProofInfo.slot <= head) {
revert SlotNumberTooLow();
}

bytes32 blockHeaderRoot = ILightClient(lightclient).headers(_slot);
bytes32 blockHeaderRoot = ILightClient(lightclient).headers(_beaconStateRootProofInfo.slot);

// Verify block number against block header root
if (!verifyBlockNumber(_blockNumber, _blockNumberProof, blockHeaderRoot)) {
if (!BeaconOracleHelper._verifyBlockNumber(_blockNumber, _blockNumberProof, blockHeaderRoot))
{
revert InvalidBlockNumberProof();
}

// Verify beacon state root against block header root
if (!verifyBeaconStateRoot(_beaconStateRoot, _beaconStateRootProof, blockHeaderRoot)) {
if (!BeaconOracleHelper._verifyBeaconStateRoot(_beaconStateRootProofInfo, blockHeaderRoot)) {
revert InvalidBeaconStateRootProof();
}

// Store the header root
blockNumberToStateRoot[_blockNumber] = _beaconStateRoot;
blockNumberToStateRoot[_blockNumber] = _beaconStateRootProofInfo.beaconStateRoot;

// Require that the slot number is greater than the previous slot number
head = _slot;

emit BeaconStateOracleUpdate(_slot, _blockNumber, _beaconStateRoot);
}

function verifyBlockNumber(
uint256 _blockNumber,
bytes32[] memory _blockNumberProof,
bytes32 _blockHeaderRoot
) internal pure returns (bool) {
return SSZ.isValidMerkleBranch(
SSZ.toLittleEndian(_blockNumber),
EXECUTION_PAYLOAD_BLOCK_NUMBER_INDEX,
_blockNumberProof,
_blockHeaderRoot
);
}
head = _beaconStateRootProofInfo.slot;

function verifyBeaconStateRoot(
bytes32 _beaconStateRoot,
bytes32[] memory _beaconStateRootProof,
bytes32 _blockHeaderRoot
) internal pure returns (bool) {
return SSZ.isValidMerkleBranch(
_beaconStateRoot, BEACON_STATE_ROOT_INDEX, _beaconStateRootProof, _blockHeaderRoot
emit BeaconStateOracleUpdate(
_beaconStateRootProofInfo.slot, _blockNumber, _beaconStateRootProofInfo.beaconStateRoot
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,4 @@ contract EigenLayerBeaconOracleProxy is

/// @notice Authorizes an upgrade for the implementation contract.
function _authorizeUpgrade(address newImplementation) internal override onlyTimelock {}
}
}
4 changes: 2 additions & 2 deletions external/integrations/gnosis/TelepathyValidator.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {MockTelepathy} from "src/amb/mocks/MockTelepathy.sol";
import {TelepathyPubSub} from "src/pubsub/TelepathyPubSub.sol";
import {Subscription} from "src/pubsub/interfaces/IPubSub.sol";
import {TelepathyHandler} from "src/amb/interfaces/TelepathyHandler.sol";
import {TelepathyValidator} from "examples/pubsub/gnosis/TelepathyValidator.sol";
import {TelepathyValidator} from "external/integrations/gnosis/TelepathyValidator.sol";
import {UUPSProxy} from "src/libraries/Proxy.sol";

interface IForeignAMB {}
Expand Down Expand Up @@ -165,4 +165,4 @@ contract TelepathyValidatorTest is Test {
telepathyValidator.toggleExecuteAffirmations();
assertEq(telepathyValidator.executeAffirmationsEnabled(), false);
}
}
}
Loading