Skip to content
Open
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
54 changes: 21 additions & 33 deletions spec_v2/randomness.spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,11 @@ enum ConfigVariant {
}

/// @notice V2 configuration data with DKG thresholds
/// @dev All thresholds are fixed-point values (value / 2^64)
/// @dev All thresholds are fixed-point values (value / 2^64), stored as uint128
struct ConfigV2Data {
uint64 secrecyThreshold; // Min stake ratio to keep secret
uint64 reconstructionThreshold; // Min stake ratio to reveal
uint64 fastPathSecrecyThreshold; // Threshold for fast path
uint128 secrecyThreshold; // Min stake ratio to keep secret
uint128 reconstructionThreshold; // Min stake ratio to reveal
uint128 fastPathSecrecyThreshold; // Threshold for fast path
}

/// @notice Complete randomness configuration
Expand Down Expand Up @@ -168,7 +168,7 @@ interface IRandomnessConfig {
function newOff() external pure returns (RandomnessConfigData memory);

/// @notice Create V2 config
function newV2(uint64 secrecy, uint64 reconstruction, uint64 fastPath)
function newV2(uint128 secrecy, uint128 reconstruction, uint128 fastPath)
external pure returns (RandomnessConfigData memory);
}
```
Expand Down Expand Up @@ -217,28 +217,23 @@ event PendingRandomnessConfigCleared();
### Types

```solidity
/// @notice Essential DKG session info stored on-chain
/// @dev Full metadata including validator sets is emitted in events only
struct DKGSessionInfo {
uint64 dealerEpoch; // Epoch of dealers
ConfigVariant configVariant; // Config variant used
uint64 dealerCount; // Number of dealers
uint64 targetCount; // Number of targets
uint64 startTimeUs; // Start timestamp (microseconds)
bytes transcript; // DKG output (set on completion)
}

/// @notice Full DKG session metadata (for events only)
/// @dev Not stored on-chain to avoid dynamic array storage issues
/// @notice DKG session metadata (shared with genesis contract)
struct DKGSessionMetadata {
uint64 dealerEpoch; // Epoch of dealers
RandomnessConfigData randomnessConfig; // Config for session
ValidatorConsensusInfo[] dealerValidatorSet; // Current validators
ValidatorConsensusInfo[] targetValidatorSet; // Next epoch validators
}

/// @notice Essential DKG session info stored on-chain
struct DKGSessionInfo {
DKGSessionMetadata metadata; // Session metadata
uint64 startTimeUs; // Start timestamp (microseconds)
bytes transcript; // DKG output (set on completion)
}
```

**Design Note**: Full validator arrays are emitted in events only (via `DKGSessionMetadata`) and not stored in contract state. This avoids Solidity storage limitations with dynamic arrays containing nested structs. The consensus engine receives full metadata from events, while the contract tracks essential info (`DKGSessionInfo`).
**Design Note**: `DKGSessionInfo` contains a nested `DKGSessionMetadata` to keep the structure consistent with the genesis contract. The full validator arrays are stored on-chain within `metadata`.

### State Variables

Expand All @@ -254,20 +249,12 @@ bool public hasInProgress;

/// @notice Whether a last completed session exists
bool public hasLastCompleted;

/// @notice Whether the contract has been initialized
bool private _initialized;
```

### Interface

```solidity
interface IDKG {
// ========== Initialization ==========

/// @notice Initialize the DKG contract (genesis only)
function initialize() external;

// ========== Session Management (RECONFIGURATION only) ==========

/// @notice Start a new DKG session
Expand Down Expand Up @@ -305,8 +292,13 @@ interface IDKG {
/// @notice Get dealer epoch from session info
function sessionDealerEpoch(DKGSessionInfo calldata info) external pure returns (uint64);

/// @notice Check if initialized
function isInitialized() external view returns (bool);
/// @notice Get complete DKG state for debugging
function getDKGState() external view returns (
DKGSessionInfo memory lastCompleted,
bool hasLastCompleted,
DKGSessionInfo memory inProgress,
bool hasInProgress
);
}
```

Expand All @@ -333,7 +325,6 @@ event DKGSessionCleared(uint64 indexed dealerEpoch);

| Function | Caller | Rationale |
|----------|--------|-----------|
| `initialize()` | GENESIS | One-time initialization |
| `start()` | RECONFIGURATION | Start transition |
| `finish()` | RECONFIGURATION | Complete transition |
| `tryClearIncompleteSession()` | RECONFIGURATION | Cleanup stale sessions |
Expand Down Expand Up @@ -413,9 +404,6 @@ error DKGInProgress();

/// @notice No DKG session is in progress
error DKGNotInProgress();

/// @notice DKG contract has not been initialized
error DKGNotInitialized();
```

---
Expand Down
107 changes: 56 additions & 51 deletions src/runtime/DKG.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

import { SystemAddresses } from "../foundation/SystemAddresses.sol";
import { requireAllowed } from "../foundation/SystemAccessControl.sol";
import { Errors } from "../foundation/Errors.sol";
import { ValidatorConsensusInfo } from "../foundation/Types.sol";
import { RandomnessConfig } from "./RandomnessConfig.sol";
import { IDKG } from "./IDKG.sol";
import { ITimestamp } from "./ITimestamp.sol";
import {SystemAddresses} from "../foundation/SystemAddresses.sol";
import {requireAllowed} from "../foundation/SystemAccessControl.sol";
import {Errors} from "../foundation/Errors.sol";
import {ValidatorConsensusInfo} from "../foundation/Types.sol";
import {RandomnessConfig} from "./RandomnessConfig.sol";
import {IDKG} from "./IDKG.sol";
import {ITimestamp} from "./ITimestamp.sol";

/// @title DKG
/// @author Gravity Team
Expand All @@ -18,23 +18,6 @@ import { ITimestamp } from "./ITimestamp.sol";
/// Note: Full validator arrays are emitted in events only (not stored in contract state)
/// to avoid storage limitations with dynamic arrays.
contract DKG is IDKG {
// ========================================================================
// TYPES
// ========================================================================

/// @notice Full DKG session metadata for events
/// @dev Emitted in DKGStartEvent for consensus engine
struct DKGSessionMetadata {
/// @notice Epoch number of the dealers (current validators)
uint64 dealerEpoch;
/// @notice Randomness configuration for this session
RandomnessConfig.RandomnessConfigData randomnessConfig;
/// @notice Current validators who will run DKG (dealers)
ValidatorConsensusInfo[] dealerValidatorSet;
/// @notice Next epoch validators who will receive keys (targets)
ValidatorConsensusInfo[] targetValidatorSet;
}

// ========================================================================
// STATE
// ========================================================================
Expand All @@ -61,7 +44,11 @@ contract DKG is IDKG {
/// @param dealerEpoch Epoch of the dealer validators
/// @param startTimeUs When the session started (microseconds)
/// @param metadata Full session metadata for consensus engine
event DKGStartEvent(uint64 indexed dealerEpoch, uint64 startTimeUs, DKGSessionMetadata metadata);
event DKGStartEvent(
uint64 indexed dealerEpoch,
uint64 startTimeUs,
IDKG.DKGSessionMetadata metadata
);

/// @notice Emitted when a DKG session completes
/// @param dealerEpoch Epoch of the dealer validators
Expand Down Expand Up @@ -99,43 +86,37 @@ contract DKG is IDKG {
// Get current timestamp from Timestamp contract
uint64 startTimeUs = _getCurrentTimeMicros();

// Store essential session info on-chain
// Store full session info on-chain
// TODO(lightman): validator's voting power needs to be uint64 on the consensus engine.
_inProgress = IDKG.DKGSessionInfo({
dealerEpoch: dealerEpoch,
configVariant: randomnessConfig.variant,
dealerCount: uint64(dealerValidatorSet.length),
targetCount: uint64(targetValidatorSet.length),
startTimeUs: startTimeUs,
transcript: ""
});
_inProgress.metadata.dealerEpoch = dealerEpoch;
_inProgress.metadata.randomnessConfig = randomnessConfig;
_inProgress.startTimeUs = startTimeUs;
_inProgress.transcript = "";
// Copy validator arrays
delete _inProgress.metadata.dealerValidatorSet;
for (uint256 i = 0; i < dealerValidatorSet.length; i++) {
_inProgress.metadata.dealerValidatorSet.push(dealerValidatorSet[i]);
}
delete _inProgress.metadata.targetValidatorSet;
for (uint256 i = 0; i < targetValidatorSet.length; i++) {
_inProgress.metadata.targetValidatorSet.push(targetValidatorSet[i]);
}
hasInProgress = true;

// Emit full metadata in event for consensus engine
// Note: We create the memory struct here to include the calldata arrays
DKGSessionMetadata memory metadata = DKGSessionMetadata({
dealerEpoch: dealerEpoch,
randomnessConfig: randomnessConfig,
dealerValidatorSet: dealerValidatorSet,
targetValidatorSet: targetValidatorSet
});

emit DKGStartEvent(dealerEpoch, startTimeUs, metadata);
emit DKGStartEvent(dealerEpoch, startTimeUs, _inProgress.metadata);
}

/// @notice Complete a DKG session with the generated transcript
/// @dev Called by RECONFIGURATION after DKG completes off-chain
/// @param transcript The DKG transcript from consensus engine
function finish(
bytes calldata transcript
) external override {
function finish(bytes calldata transcript) external override {
requireAllowed(SystemAddresses.RECONFIGURATION);

if (!hasInProgress) {
revert Errors.DKGNotInProgress();
}

uint64 dealerEpoch = _inProgress.dealerEpoch;
uint64 dealerEpoch = _inProgress.metadata.dealerEpoch;

// Store transcript and move to completed
_inProgress.transcript = transcript;
Expand All @@ -159,7 +140,7 @@ contract DKG is IDKG {
return;
}

uint64 dealerEpoch = _inProgress.dealerEpoch;
uint64 dealerEpoch = _inProgress.metadata.dealerEpoch;
_clearInProgress();

emit DKGSessionCleared(dealerEpoch);
Expand All @@ -178,7 +159,12 @@ contract DKG is IDKG {
/// @notice Get the incomplete session info if any
/// @return hasSession Whether an in-progress session exists
/// @return info Session info (only valid if hasSession is true)
function getIncompleteSession() external view override returns (bool hasSession, IDKG.DKGSessionInfo memory info) {
function getIncompleteSession()
external
view
override
returns (bool hasSession, IDKG.DKGSessionInfo memory info)
{
if (hasInProgress) {
return (true, _inProgress);
}
Expand Down Expand Up @@ -206,7 +192,26 @@ contract DKG is IDKG {
function sessionDealerEpoch(
IDKG.DKGSessionInfo calldata info
) external pure override returns (uint64) {
return info.dealerEpoch;
return info.metadata.dealerEpoch;
}

/// @notice Get complete DKG state for debugging
/// @return lastCompleted Last completed session
/// @return hasLastCompleted_ Whether a completed session exists
/// @return inProgress_ In-progress session
/// @return hasInProgress_ Whether an in-progress session exists
function getDKGState()
external
view
override
returns (
IDKG.DKGSessionInfo memory lastCompleted,
bool hasLastCompleted_,
IDKG.DKGSessionInfo memory inProgress_,
bool hasInProgress_
)
{
return (_lastCompleted, hasLastCompleted, _inProgress, hasInProgress);
}

// ========================================================================
Expand Down
51 changes: 36 additions & 15 deletions src/runtime/IDKG.sol
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

import { RandomnessConfig } from "./RandomnessConfig.sol";
import { ValidatorConsensusInfo } from "../foundation/Types.sol";
import {RandomnessConfig} from "./RandomnessConfig.sol";
import {ValidatorConsensusInfo} from "../foundation/Types.sol";

/// @title IDKG
/// @author Gravity Team
/// @notice Interface for the DKG contract
interface IDKG {
/// @notice Essential DKG session info stored on-chain
struct DKGSessionInfo {
/// @notice DKG session metadata (shared with genesis contract)
struct DKGSessionMetadata {
/// @notice Epoch number of the dealers (current validators)
uint64 dealerEpoch;
/// @notice Randomness configuration variant
RandomnessConfig.ConfigVariant configVariant;
/// @notice Number of dealers
uint64 dealerCount;
/// @notice Number of targets
uint64 targetCount;
/// @notice Randomness configuration for this session
RandomnessConfig.RandomnessConfigData randomnessConfig;
/// @notice Current validators who will run DKG (dealers)
ValidatorConsensusInfo[] dealerValidatorSet;
/// @notice Next epoch validators who will receive keys (targets)
ValidatorConsensusInfo[] targetValidatorSet;
}

/// @notice Essential DKG session info stored on-chain
struct DKGSessionInfo {
/// @notice Session metadata
DKGSessionMetadata metadata;
/// @notice When the session started (microseconds)
uint64 startTimeUs;
/// @notice DKG transcript (output, set on completion)
Expand All @@ -36,9 +42,7 @@ interface IDKG {
) external;

/// @notice Complete DKG session with transcript
function finish(
bytes calldata transcript
) external;
function finish(bytes calldata transcript) external;

/// @notice Clear incomplete session (no-op if none)
function tryClearIncompleteSession() external;
Expand All @@ -49,13 +53,30 @@ interface IDKG {
function isInProgress() external view returns (bool);

/// @notice Get incomplete session info if any
function getIncompleteSession() external view returns (bool hasSession, DKGSessionInfo memory info);
function getIncompleteSession()
external
view
returns (bool hasSession, DKGSessionInfo memory info);

/// @notice Get last completed session info if any
function getLastCompletedSession() external view returns (bool hasSession, DKGSessionInfo memory info);
function getLastCompletedSession()
external
view
returns (bool hasSession, DKGSessionInfo memory info);

/// @notice Get dealer epoch from session info
function sessionDealerEpoch(
DKGSessionInfo calldata info
) external pure returns (uint64);

/// @notice Get complete DKG state for debugging
function getDKGState()
external
view
returns (
DKGSessionInfo memory lastCompleted,
bool hasLastCompleted,
DKGSessionInfo memory inProgress,
bool hasInProgress
);
}
Loading
Loading