Skip to content

Commit

Permalink
Merge pull request #65 from vzotova/pre-app
Browse files Browse the repository at this point in the history
PRE application contract
  • Loading branch information
KPrasch authored Jun 26, 2023
2 parents da35b9d + d155613 commit 13c665f
Show file tree
Hide file tree
Showing 17 changed files with 2,270 additions and 502 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ jobs:
- name: Deploy NuCypher Token
run: ape run scripts/deploy_nucypher_token.py --network ethereum:local

- name: Deploy Simple PRE
run: ape run scripts/deploy_simple_pre.py --network ethereum:local
- name: Deploy PRE Application
run: ape run scripts/deploy_pre_application.py --network ethereum:local

- name: Deploy Staking Escrow
run: ape run scripts/deploy_staking_escrow.py --network ethereum:local
Expand Down
8 changes: 8 additions & 0 deletions ape-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ deployments:
- nu_token_supply: 1_000_000_000
pre_min_authorization: 40000000000000000000000
pre_min_operator_seconds: 86400 # one day in seconds
pre_hash_algorithm: 1
pre_base_penalty: 2
pre_penalty_history_coefficient: 0
pre_percentage_penalty_coefficient: 100000
pre_min_authorization: 40000000000000000000000
pre_min_operator_seconds: 86400 # one day in seconds
reward_duration: 604800 # one week in seconds
deauthorization_duration: 5184000 # 60 days in seconds
verify: False
rinkeby:
- nu_token: '0x78D591D90a4a768B9D2790deA465D472b6Fe0f18'
Expand Down
140 changes: 66 additions & 74 deletions contracts/contracts/Adjudicator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ pragma solidity ^0.8.0;

import "./lib/ReEncryptionValidator.sol";
import "./lib/SignatureVerifier.sol";
import "./IStakingEscrow.sol";
import "./proxy/Upgradeable.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";


/**
* @title Adjudicator
* @notice Supervises stakers' behavior and punishes when something's wrong.
* @dev |v2.1.2|
* @notice Supervises operators' behavior and punishes when something's wrong.
* @dev |v3.1.1|
*/
contract Adjudicator is Upgradeable {
abstract contract Adjudicator {

using UmbralDeserializer for bytes;
using SafeCast for uint256;

event CFragEvaluated(
bytes32 indexed evaluationHash,
Expand All @@ -25,62 +25,52 @@ contract Adjudicator is Upgradeable {
);
event IncorrectCFragVerdict(
bytes32 indexed evaluationHash,
address indexed worker,
address indexed staker
address indexed operator,
address indexed stakingProvider
);

// used only for upgrading
bytes32 constant RESERVED_CAPSULE_AND_CFRAG_BYTES = bytes32(0);
address constant RESERVED_ADDRESS = address(0);

IStakingEscrow public immutable escrow;
SignatureVerifier.HashAlgorithm public immutable hashAlgorithm;
uint256 public immutable basePenalty;
uint256 public immutable penaltyHistoryCoefficient;
uint256 public immutable percentagePenaltyCoefficient;
uint256 public immutable rewardCoefficient;

mapping (address => uint256) public penaltyHistory;
mapping (bytes32 => bool) public evaluatedCFrags;

uint256[50] private reservedSlots;

/**
* @param _escrow Escrow contract
* @param _hashAlgorithm Hashing algorithm
* @param _basePenalty Base for the penalty calculation
* @param _penaltyHistoryCoefficient Coefficient for calculating the penalty depending on the history
* @param _percentagePenaltyCoefficient Coefficient for calculating the percentage penalty
* @param _rewardCoefficient Coefficient for calculating the reward
*/
constructor(
IStakingEscrow _escrow,
SignatureVerifier.HashAlgorithm _hashAlgorithm,
uint256 _basePenalty,
uint256 _penaltyHistoryCoefficient,
uint256 _percentagePenaltyCoefficient,
uint256 _rewardCoefficient
uint256 _percentagePenaltyCoefficient
) {
// Sanity checks.
require(_escrow.secondsPerPeriod() > 0 && // This contract has an escrow, and it's not the null address.
// The reward and penalty coefficients are set.
_percentagePenaltyCoefficient != 0 &&
_rewardCoefficient != 0);
escrow = _escrow;
require(_percentagePenaltyCoefficient != 0, "Wrong input parameters");
hashAlgorithm = _hashAlgorithm;
basePenalty = _basePenalty;
percentagePenaltyCoefficient = _percentagePenaltyCoefficient;
penaltyHistoryCoefficient = _penaltyHistoryCoefficient;
rewardCoefficient = _rewardCoefficient;
}

/**
* @notice Submit proof that a worker created wrong CFrag
* @notice Submit proof that a operator created wrong CFrag
* @param _capsuleBytes Serialized capsule
* @param _cFragBytes Serialized CFrag
* @param _cFragSignature Signature of CFrag by worker
* @param _cFragSignature Signature of CFrag by operator
* @param _taskSignature Signature of task specification by Bob
* @param _requesterPublicKey Bob's signing public key, also known as "stamp"
* @param _workerPublicKey Worker's signing public key, also known as "stamp"
* @param _workerIdentityEvidence Signature of worker's public key by worker's eth-key
* @param _operatorPublicKey Operator's signing public key, also known as "stamp"
* @param _operatorIdentityEvidence Signature of operator's public key by operator's eth-key
* @param _preComputedData Additional pre-computed data for CFrag correctness verification
*/
function evaluateCFrag(
Expand All @@ -89,8 +79,8 @@ contract Adjudicator is Upgradeable {
bytes memory _cFragSignature,
bytes memory _taskSignature,
bytes memory _requesterPublicKey,
bytes memory _workerPublicKey,
bytes memory _workerIdentityEvidence,
bytes memory _operatorPublicKey,
bytes memory _operatorIdentityEvidence,
bytes memory _preComputedData
)
public
Expand All @@ -106,28 +96,28 @@ contract Adjudicator is Upgradeable {
emit CFragEvaluated(evaluationHash, msg.sender, cFragIsCorrect);

// 3. Verify associated public keys and signatures
require(ReEncryptionValidator.checkSerializedCoordinates(_workerPublicKey),
require(ReEncryptionValidator.checkSerializedCoordinates(_operatorPublicKey),
"Staker's public key is invalid");
require(ReEncryptionValidator.checkSerializedCoordinates(_requesterPublicKey),
"Requester's public key is invalid");

UmbralDeserializer.PreComputedData memory precomp = _preComputedData.toPreComputedData();

// Verify worker's signature of CFrag
// Verify operator's signature of CFrag
require(SignatureVerifier.verify(
_cFragBytes,
abi.encodePacked(_cFragSignature, precomp.lostBytes[1]),
_workerPublicKey,
_operatorPublicKey,
hashAlgorithm),
"CFrag signature is invalid"
);

// Verify worker's signature of taskSignature and that it corresponds to cfrag.proof.metadata
// Verify operator's signature of taskSignature and that it corresponds to cfrag.proof.metadata
UmbralDeserializer.CapsuleFrag memory cFrag = _cFragBytes.toCapsuleFrag();
require(SignatureVerifier.verify(
_taskSignature,
abi.encodePacked(cFrag.proof.metadata, precomp.lostBytes[2]),
_workerPublicKey,
_operatorPublicKey,
hashAlgorithm),
"Task signature is invalid"
);
Expand All @@ -136,14 +126,14 @@ contract Adjudicator is Upgradeable {
// A task specification is: capsule + ursula pubkey + alice address + blockhash
bytes32 stampXCoord;
assembly {
stampXCoord := mload(add(_workerPublicKey, 32))
stampXCoord := mload(add(_operatorPublicKey, 32))
}
bytes memory stamp = abi.encodePacked(precomp.lostBytes[4], stampXCoord);

require(SignatureVerifier.verify(
abi.encodePacked(_capsuleBytes,
stamp,
_workerIdentityEvidence,
_operatorIdentityEvidence,
precomp.alicesKeyAsAddress,
bytes32(0)),
abi.encodePacked(_taskSignature, precomp.lostBytes[3]),
Expand All @@ -152,58 +142,60 @@ contract Adjudicator is Upgradeable {
"Specification signature is invalid"
);

// 4. Extract worker address from stamp signature.
address worker = SignatureVerifier.recover(
// 4. Extract operator address from stamp signature.
address operator = SignatureVerifier.recover(
SignatureVerifier.hashEIP191(stamp, bytes1(0x45)), // Currently, we use version E (0x45) of EIP191 signatures
_workerIdentityEvidence);
address staker = escrow.stakerFromWorker(worker);
require(staker != address(0), "Worker must be related to a staker");
_operatorIdentityEvidence);
address stakingProvider = stakingProviderFromOperator(operator);
require(stakingProvider != address(0), "Operator must be related to a provider");

// 5. Check that staker can be slashed
uint256 stakerValue = escrow.getAllTokens(staker);
require(stakerValue > 0, "Staker has no tokens");
// 5. Check that staking provider can be slashed
uint96 stakingProviderValue = authorizedStake(stakingProvider);
require(stakingProviderValue > 0, "Provider has no tokens");

// 6. If CFrag was incorrect, slash staker
// 6. If CFrag was incorrect, slash staking provider
if (!cFragIsCorrect) {
(uint256 penalty, uint256 reward) = calculatePenaltyAndReward(staker, stakerValue);
escrow.slashStaker(staker, penalty, msg.sender, reward);
emit IncorrectCFragVerdict(evaluationHash, worker, staker);
uint96 penalty = calculatePenalty(stakingProvider, stakingProviderValue);
slash(stakingProvider, penalty, msg.sender);
emit IncorrectCFragVerdict(evaluationHash, operator, stakingProvider);
}
}

/**
* @notice Calculate penalty to the staker and reward to the investigator
* @param _staker Staker's address
* @param _stakerValue Amount of tokens that belong to the staker
* @notice Calculate penalty to the staking provider
* @param _stakingProvider Staking provider address
* @param _stakingProviderValue Amount of tokens that belong to the staking provider
*/
function calculatePenaltyAndReward(address _staker, uint256 _stakerValue)
internal returns (uint256 penalty, uint256 reward)
function calculatePenalty(address _stakingProvider, uint96 _stakingProviderValue)
internal returns (uint96)
{
penalty = basePenalty + penaltyHistoryCoefficient * penaltyHistory[_staker];
penalty = Math.min(penalty, _stakerValue / percentagePenaltyCoefficient);
reward = penalty / rewardCoefficient;
uint256 penalty = basePenalty + penaltyHistoryCoefficient * penaltyHistory[_stakingProvider];
penalty = Math.min(penalty, _stakingProviderValue / percentagePenaltyCoefficient);
// TODO add maximum condition or other overflow protection or other penalty condition (#305?)
penaltyHistory[_staker] = penaltyHistory[_staker] + 1;
penaltyHistory[_stakingProvider] = penaltyHistory[_stakingProvider] + 1;
return penalty.toUint96();
}

/// @dev the `onlyWhileUpgrading` modifier works through a call to the parent `verifyState`
function verifyState(address _testTarget) public override virtual {
super.verifyState(_testTarget);
bytes32 evaluationCFragHash = SignatureVerifier.hash(
abi.encodePacked(RESERVED_CAPSULE_AND_CFRAG_BYTES), SignatureVerifier.HashAlgorithm.SHA256);
require(delegateGet(_testTarget, this.evaluatedCFrags.selector, evaluationCFragHash) ==
(evaluatedCFrags[evaluationCFragHash] ? 1 : 0));
require(delegateGet(_testTarget, this.penaltyHistory.selector, bytes32(bytes20(RESERVED_ADDRESS))) ==
penaltyHistory[RESERVED_ADDRESS]);
}
/**
* @notice Get all tokens delegated to the staking provider
*/
function authorizedStake(address _stakingProvider) public view virtual returns (uint96);

/**
* @notice Get staking provider address bonded with specified operator address
*/
function stakingProviderFromOperator(address _operator) public view virtual returns (address);

/**
* @notice Slash the provider's stake and reward the investigator
* @param _stakingProvider Staking provider address
* @param _penalty Penalty
* @param _investigator Investigator
*/
function slash(
address _stakingProvider,
uint96 _penalty,
address _investigator
) internal virtual;

/// @dev the `onlyWhileUpgrading` modifier works through a call to the parent `finishUpgrade`
function finishUpgrade(address _target) public override virtual {
super.finishUpgrade(_target);
// preparation for the verifyState method
bytes32 evaluationCFragHash = SignatureVerifier.hash(
abi.encodePacked(RESERVED_CAPSULE_AND_CFRAG_BYTES), SignatureVerifier.HashAlgorithm.SHA256);
evaluatedCFrags[evaluationCFragHash] = true;
penaltyHistory[RESERVED_ADDRESS] = 123;
}
}
Loading

0 comments on commit 13c665f

Please sign in to comment.