Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ea91b31
review comments.
raulk Nov 15, 2024
25e6495
update implementation
Nov 15, 2024
4ea50b0
wip
Nov 18, 2024
74d43af
update review and fendermint
Nov 18, 2024
1d3b7a2
remove call state
Nov 18, 2024
3da9018
fix tests
Nov 19, 2024
fed289b
fix ci
Nov 19, 2024
bf4d28d
Merge branch 'main' of protocol-github:consensus-shipyard/ipc into ra…
Nov 19, 2024
ef8740a
wip: refactors. see below.
raulk Nov 19, 2024
6a9b088
Merge branch 'raulk/rewards-review' of protocol-github:consensus-ship…
Nov 20, 2024
2607714
update implementation
Nov 20, 2024
8c298d2
fix solc to 0.8.23
Nov 20, 2024
8b15adc
fix solhint
Nov 20, 2024
ecf5cd1
rename ActivityBundle to ActivityRollup.
raulk Nov 20, 2024
a4885b4
refactor bottom up checkpoint consensus report
Nov 20, 2024
572a62b
unit tests
Nov 21, 2024
28b0483
add doc
Nov 21, 2024
d565897
fmt
Nov 21, 2024
3b44110
activity-tracker actor: refactor API.
raulk Nov 20, 2024
4937dab
rename activities to activity.
raulk Nov 20, 2024
654f977
wip: continue refactoring.
raulk Nov 21, 2024
90fcdf7
rename activities to activity_rollup.
raulk Nov 21, 2024
7552c9d
fix Cargo dep.
raulk Nov 21, 2024
9119cfa
workaround for ethers dep on getrandom + drop crypto feature from fvm…
raulk Nov 21, 2024
6807f89
fully qualify const expr for trait bound.
raulk Nov 21, 2024
cb3114e
fix activity tracker clippy
Nov 21, 2024
e4a78a4
revert usage of ..Default::default().
raulk Nov 21, 2024
9d228b4
clippy
Nov 21, 2024
8186188
fix eth rpc error
Nov 22, 2024
ec908e3
Merge branch 'main' into raulk/rewards-review
raulk Nov 22, 2024
024be35
feat(node): remove ethers in fvm actor (#1213)
cryptoAtwill Nov 22, 2024
88213bd
Merge branch 'integration/rewards' into raulk/rewards-review
raulk Nov 22, 2024
431b2e0
fix cargo lock
Nov 25, 2024
fbe966e
fix tests
Nov 25, 2024
ce6dc44
enable crypto for required crates
Nov 25, 2024
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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion contracts/binding/Cargo.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

92 changes: 50 additions & 42 deletions contracts/contracts/activities/Activity.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,63 @@ pragma solidity ^0.8.23;

import {SubnetID} from "../structs/Subnet.sol";

event ActivityReportCreated(uint64 checkpointHeight, ActivityReport report);
// Event to be emitted within the subnet when a new activity summary has been recorded.
event ActivityRollupRecorded(uint64 checkpointHeight, FullActivityRollup rollup);

/// The full validator activities report
struct ActivityReport {
ValidatorActivityReport[] validators;
// Carries a set of reports summarising various aspects of the activity that took place in the subnet between the
// previous checkpoint and the checkpoint this summary is committed into. If this is the first checkpoint, the summary
// contains information about the subnet's activity since genesis.
// In the future we'll be having more kinds of activity reports here.
struct FullActivityRollup {
/// A report of consensus-level activity that took place in the subnet between the previous checkpoint
/// and the checkpoint this summary is committed into.
/// @dev If there is a configuration change applied at this checkpoint, this carries information
/// about the _old_ validator set.
Consensus.FullSummary consensus;
}

struct ValidatorActivityReport {
/// @dev The validator whose activity we're reporting about.
address validator;
/// @dev The number of blocks committed by each validator in the position they appear in the validators array.
/// If there is a configuration change applied at this checkpoint, this carries information about the _old_ validator set.
uint64 blocksCommitted;
/// @dev Other metadata
bytes metadata;
// Compressed representation of the activity summary that can be embedded in checkpoints to propagate up the hierarchy.
struct CompressedActivityRollup {
Consensus.CompressedSummary consensus;
}

/// The summary for the child subnet activities that should be submitted to the parent subnet
/// together with a bottom up checkpoint
struct ActivitySummary {
/// The total number of distintive validators that have mined
uint64 totalActiveValidators;
/// The activity commitment for validators
bytes32 commitment;
/// Namespace for consensus-level activity summaries.
library Consensus {
type MerkleHash is bytes32;

// TODO: add relayed rewarder commitment
}
// Aggregated stats for consensus-level activity.
struct AggregatedStats {
/// The total number of unique validators that have mined within this period.
uint64 totalActiveValidators;
/// The total number of blocks committed by all validators during this period.
uint64 totalNumBlocksCommitted;
}

/// The summary for a single validator
struct ValidatorSummary {
/// @dev The child subnet checkpoint height associated with this summary
uint64 checkpointHeight;
/// @dev The validator whose activity we're reporting about.
address validator;
/// @dev The number of blocks committed by each validator in the position they appear in the validators array.
/// If there is a configuration change applied at this checkpoint, this carries information about the _old_ validator set.
uint64 blocksCommitted;
/// @dev Other metadata
bytes metadata;
}
// The full activity summary for consensus-level activity.
struct FullSummary {
AggregatedStats stats;
/// The breakdown of activity per validator.
ValidatorData[] data;
}

/// The proof required for validators to claim rewards
struct ValidatorClaimProof {
ValidatorSummary summary;
bytes32[] proof;
}
// The compresed representation of the activity summary for consensus-level activity suitable for embedding in a checkpoint.
struct CompressedSummary {
AggregatedStats stats;
/// The commitment for the validator details, so that we don't have to transmit them in full.
MerkleHash dataRootCommitment;
}

struct ValidatorData {
/// @dev The validator whose activity we're reporting about, identified by the Ethereum address corresponding
/// to its secp256k1 pubkey.
address validator;
/// @dev The number of blocks committed by this validator during the summarised period.
uint64 blocksCommitted;
}

/// The proofs to batch claim validator rewards in a specific subnet
struct BatchClaimProofs {
SubnetID subnetId;
ValidatorClaimProof[] proofs;
/// The payload for validators to claim rewards
struct ValidatorClaim {
ValidatorData data;
MerkleHash[] proof;
}
}
4 changes: 2 additions & 2 deletions contracts/contracts/activities/IValidatorRewarder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.23;

import {SubnetID} from "../structs/Subnet.sol";
import {ValidatorSummary} from "./Activity.sol";
import {Consensus} from "./Activity.sol";

/// @title ValidatorRewarder interface.
///
Expand All @@ -14,7 +14,7 @@ interface IValidatorRewarder {
/// @notice Called by the subnet manager contract to instruct the rewarder to process the subnet summary and
/// disburse any relevant rewards.
/// @dev This method should revert if the summary is invalid; this will cause the
function disburseRewards(SubnetID calldata id, ValidatorSummary calldata summary) external;
function disburseRewards(SubnetID calldata id, Consensus.ValidatorData calldata detail) external;
}

/// @title Validator reward setup interface
Expand Down
21 changes: 10 additions & 11 deletions contracts/contracts/activities/LibActivityMerkleVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,24 @@ pragma solidity ^0.8.23;

import {SubnetID} from "../structs/Subnet.sol";
import {InvalidProof} from "../errors/IPCErrors.sol";
import {ValidatorSummary} from "./Activity.sol";
import {Consensus} from "./Activity.sol";
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

/// Verifies the proof to the commitment in subnet activity summary
library LibActivityMerkleVerifier {
function ensureValidProof(
bytes32 commitment,
ValidatorSummary calldata summary,
bytes32[] calldata proof
Consensus.ValidatorData calldata detail,
Consensus.MerkleHash[] calldata proof
) internal pure {
// Constructing leaf: https://github.com/OpenZeppelin/merkle-tree#leaf-hash
bytes32 leaf = keccak256(
bytes.concat(
keccak256(
abi.encode(summary.validator, summary.blocksCommitted, summary.metadata)
)
)
);
bool valid = MerkleProof.verify({proof: proof, root: commitment, leaf: leaf});
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(detail.validator, detail.blocksCommitted))));
// converting proof to bytes32[]
bytes32[] memory proofBytes = new bytes32[](proof.length);
for (uint256 i = 0; i < proof.length; i++) {
proofBytes[i] = Consensus.MerkleHash.unwrap(proof[i]);
}
bool valid = MerkleProof.verify({proof: proofBytes, root: commitment, leaf: leaf});
if (!valid) {
revert InvalidProof();
}
Expand Down
89 changes: 43 additions & 46 deletions contracts/contracts/activities/ValidatorRewardFacet.sol
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.23;

import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
import {Consensus} from "./Activity.sol";

import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {IValidatorRewarder, IValidatorRewardSetup} from "./IValidatorRewarder.sol";
import {LibActivityMerkleVerifier} from "./LibActivityMerkleVerifier.sol";
import {LibDiamond} from "../lib/LibDiamond.sol";
import {NotValidator, SubnetNoTargetCommitment, CommitmentAlreadyInitialized, ValidatorAlreadyClaimed, NotGateway, NotOwner} from "../errors/IPCErrors.sol";
import {Pausable} from "../lib/LibPausable.sol";
import {ReentrancyGuard} from "../lib/LibReentrancyGuard.sol";
import {NotValidator, SubnetNoTargetCommitment, CommitmentAlreadyInitialized, ValidatorAlreadyClaimed, NotGateway, NotOwner} from "../errors/IPCErrors.sol";
import {ValidatorSummary, BatchClaimProofs} from "./Activity.sol";
import {IValidatorRewarder, IValidatorRewardSetup} from "./IValidatorRewarder.sol";
import {SubnetIDHelper} from "../lib/SubnetIDHelper.sol";
import {SubnetID} from "../structs/Subnet.sol";
import {LibActivityMerkleVerifier} from "./LibActivityMerkleVerifier.sol";
import {LibDiamond} from "../lib/LibDiamond.sol";

/// The validator reward facet for the parent subnet, i.e. for validators in the child subnet
/// to claim their reward in the parent subnet, which should be the current subnet this facet
/// is deployed.
contract ValidatorRewardFacet is ReentrancyGuard, Pausable {
function batchClaim(BatchClaimProofs[] calldata payload) external nonReentrant whenNotPaused {
uint256 len = payload.length;
function batchSubnetClaim(
SubnetID calldata subnet,
uint64[] calldata checkpointHeights,
Consensus.ValidatorClaim[] calldata claims
) external nonReentrant whenNotPaused {
require(checkpointHeights.length == claims.length, "length mismatch");
uint256 len = claims.length;
for (uint256 i = 0; i < len; ) {
_batchClaimInSubnet(payload[i]);
_claim(subnet, checkpointHeights[i], claims[i].data, claims[i].proof);
unchecked {
i++;
}
Expand All @@ -30,51 +35,41 @@ contract ValidatorRewardFacet is ReentrancyGuard, Pausable {

/// Validators claim their reward for doing work in the child subnet
function claim(
SubnetID calldata subnetId,
ValidatorSummary calldata summary,
bytes32[] calldata proof
SubnetID calldata subnet,
uint64 checkpointHeight,
Consensus.ValidatorData calldata data,
Consensus.MerkleHash[] calldata proof
) external nonReentrant whenNotPaused {
ValidatorRewardStorage storage s = LibValidatorReward.facetStorage();
_claim(s, subnetId, summary, proof);
_claim(subnet, checkpointHeight, data, proof);
}

// ======== Internal functions ===========

function handleRelay() internal pure {
// no opt for now
// no-op for now
return;
}

function _batchClaimInSubnet(BatchClaimProofs calldata payload) internal {
uint256 len = payload.proofs.length;
ValidatorRewardStorage storage s = LibValidatorReward.facetStorage();

for (uint256 i = 0; i < len; ) {
_claim(s, payload.subnetId, payload.proofs[i].summary, payload.proofs[i].proof);
unchecked {
i++;
}
}
}

function _claim(
ValidatorRewardStorage storage s,
SubnetID calldata subnetId,
ValidatorSummary calldata summary,
bytes32[] calldata proof
uint64 checkpointHeight,
Consensus.ValidatorData calldata detail,
Consensus.MerkleHash[] calldata proof
) internal {
ValidatorRewardStorage storage s = LibValidatorReward.facetStorage();

// note: No need to check if the subnet is active. If the subnet is not active, the checkpointHeight
// note: will never exist.

if (msg.sender != summary.validator) {
if (msg.sender != detail.validator) {
revert NotValidator(msg.sender);
}

if (s.validatorRewarder == address(0)) {
return handleRelay();
}

LibValidatorReward.handleDistribution(s, subnetId, summary, proof);
LibValidatorReward.handleDistribution(subnetId, checkpointHeight, detail, proof);
}
}

Expand Down Expand Up @@ -102,7 +97,7 @@ struct ValidatorRewardStorage {
}

/// The payload for list commitments query
struct ListCommimentDetail {
struct ListCommitmentDetail {
/// The child subnet checkpoint height
uint64 checkpointHeight;
/// The actual commiment of the activities
Expand All @@ -121,7 +116,7 @@ library LibValidatorReward {
function initNewDistribution(
SubnetID calldata subnetId,
uint64 checkpointHeight,
bytes32 commitment,
Consensus.MerkleHash commitment,
uint64 totalActiveValidators
) internal {
ValidatorRewardStorage storage ds = facetStorage();
Expand All @@ -132,24 +127,24 @@ library LibValidatorReward {
revert CommitmentAlreadyInitialized();
}

ds.commitments[subnetKey].set(bytes32(uint256(checkpointHeight)), commitment);
ds.commitments[subnetKey].set(bytes32(uint256(checkpointHeight)), Consensus.MerkleHash.unwrap(commitment));
ds.distributions[subnetKey][checkpointHeight].totalValidators = totalActiveValidators;
}

function listCommitments(
SubnetID calldata subnetId
) internal view returns (ListCommimentDetail[] memory listDetails) {
) internal view returns (ListCommitmentDetail[] memory listDetails) {
ValidatorRewardStorage storage ds = facetStorage();

bytes32 subnetKey = subnetId.toHash();

uint256 size = ds.commitments[subnetKey].length();
listDetails = new ListCommimentDetail[](size);
listDetails = new ListCommitmentDetail[](size);

for (uint256 i = 0; i < size; ) {
(bytes32 heightBytes32, bytes32 commitment) = ds.commitments[subnetKey].at(i);

listDetails[i] = ListCommimentDetail({
listDetails[i] = ListCommitmentDetail({
checkpointHeight: uint64(uint256(heightBytes32)),
commitment: commitment
});
Expand Down Expand Up @@ -178,18 +173,20 @@ library LibValidatorReward {
}

function handleDistribution(
ValidatorRewardStorage storage s,
SubnetID calldata subnetId,
ValidatorSummary calldata summary,
bytes32[] calldata proof
uint64 checkpointHeight,
Consensus.ValidatorData calldata detail,
Consensus.MerkleHash[] calldata proof
) internal {
ValidatorRewardStorage storage s = LibValidatorReward.facetStorage();

bytes32 subnetKey = subnetId.toHash();

bytes32 commitment = ensureValidCommitment(s, subnetKey, summary.checkpointHeight);
LibActivityMerkleVerifier.ensureValidProof(commitment, summary, proof);
bytes32 commitment = ensureValidCommitment(s, subnetKey, checkpointHeight);
LibActivityMerkleVerifier.ensureValidProof(commitment, detail, proof);

validatorTryClaim(s, subnetKey, summary.checkpointHeight, summary.validator);
IValidatorRewarder(s.validatorRewarder).disburseRewards(subnetId, summary);
validatorTryClaim(s, subnetKey, checkpointHeight, detail.validator);
IValidatorRewarder(s.validatorRewarder).disburseRewards(subnetId, detail);
}

function ensureValidCommitment(
Expand Down
Loading