-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Creates a Diva Beacon Oracle contract that can prove: Any of the fields of a validator. The balance of a validator using the balances container in BeaconState. A deposit from a BLSPubkey using the deposits container in BeaconBlock. Note: Lodestar currently has a gindex issue for fields in the Validator container. As such, we need to calculate the gindices of Validator fields manually. GitOrigin-RevId: 26db3160591809821b4785bce105ac980c0db6bd
- Loading branch information
1 parent
0d324c8
commit 67dbcc5
Showing
13 changed files
with
1,021 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.