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

App <-> bridge #98

Merged
merged 8 commits into from
Jul 24, 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 .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 PRE Application
run: ape run scripts/deploy_pre_application.py --network ethereum:local
- name: Deploy TACo Application
run: ape run scripts/deploy_taco_application.py --network ethereum:local

- name: Deploy Staking Escrow
run: ape run scripts/deploy_staking_escrow.py --network ethereum:local
Expand Down
40 changes: 13 additions & 27 deletions contracts/contracts/Adjudicator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import "./lib/ReEncryptionValidator.sol";
import "./lib/SignatureVerifier.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "./TACoApplication.sol";


/**
* @title Adjudicator
* @notice Supervises operators' behavior and punishes when something's wrong.
* @dev |v3.1.1|
*/
abstract contract Adjudicator {
contract Adjudicator {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems Adjudicator is not upgradeable anymore, right? In that case, the uint256[50] private reservedSlots; in L46 should be removed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, Adjudicator needs reincarnation but I guess it's not time. So I'll remove those fields but ideally make it upgradeable

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we have a setAdjudicator() method in the app, do we really need it to be upgradeable? We can just deploy a new version and set it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjudicator has some state that ideally to preserve. For example, history to not slash twice for the same misbehavior. That's main reason to make it upreadable with the current design

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracking issue here: #100


using UmbralDeserializer for bytes;
using SafeCast for uint256;
Expand All @@ -37,6 +38,7 @@ abstract contract Adjudicator {
uint256 public immutable basePenalty;
uint256 public immutable penaltyHistoryCoefficient;
uint256 public immutable percentagePenaltyCoefficient;
TACoApplication public immutable application;

mapping (address => uint256) public penaltyHistory;
mapping (bytes32 => bool) public evaluatedCFrags;
Expand All @@ -50,16 +52,22 @@ abstract contract Adjudicator {
* @param _percentagePenaltyCoefficient Coefficient for calculating the percentage penalty
*/
constructor(
TACoApplication _application,
SignatureVerifier.HashAlgorithm _hashAlgorithm,
uint256 _basePenalty,
uint256 _penaltyHistoryCoefficient,
uint256 _percentagePenaltyCoefficient
) {
require(_percentagePenaltyCoefficient != 0, "Wrong input parameters");
require(
_percentagePenaltyCoefficient != 0 &&
address(_application.token()) != address(0),
"Wrong input parameters"
);
hashAlgorithm = _hashAlgorithm;
basePenalty = _basePenalty;
percentagePenaltyCoefficient = _percentagePenaltyCoefficient;
penaltyHistoryCoefficient = _penaltyHistoryCoefficient;
application = _application;
}

/**
Expand Down Expand Up @@ -146,17 +154,17 @@ abstract contract Adjudicator {
address operator = SignatureVerifier.recover(
SignatureVerifier.hashEIP191(stamp, bytes1(0x45)), // Currently, we use version E (0x45) of EIP191 signatures
_operatorIdentityEvidence);
address stakingProvider = stakingProviderFromOperator(operator);
address stakingProvider = application.stakingProviderFromOperator(operator);
require(stakingProvider != address(0), "Operator must be related to a provider");

// 5. Check that staking provider can be slashed
uint96 stakingProviderValue = authorizedStake(stakingProvider);
uint96 stakingProviderValue = application.authorizedStake(stakingProvider);
require(stakingProviderValue > 0, "Provider has no tokens");

// 6. If CFrag was incorrect, slash staking provider
if (!cFragIsCorrect) {
uint96 penalty = calculatePenalty(stakingProvider, stakingProviderValue);
slash(stakingProvider, penalty, msg.sender);
application.slash(stakingProvider, penalty, msg.sender);
emit IncorrectCFragVerdict(evaluationHash, operator, stakingProvider);
}
}
Expand All @@ -176,26 +184,4 @@ abstract contract Adjudicator {
return penalty.toUint96();
}

/**
* @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;

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol";
import "@threshold/contracts/staking/IApplication.sol";
import "@threshold/contracts/staking/IStaking.sol";
import "./Adjudicator.sol";
import "./coordination/IUpdatableStakeInfo.sol";


/**
* @title PRE Application
* @title TACo Application
* @notice Contract distributes rewards for participating in app and slashes for violating rules
*/
contract PREApplication is IApplication, Adjudicator, OwnableUpgradeable {
contract TACoApplication is IApplication, OwnableUpgradeable {

using SafeERC20 for IERC20;
using SafeCast for uint256;
Expand Down Expand Up @@ -132,6 +132,9 @@ contract PREApplication is IApplication, Adjudicator, OwnableUpgradeable {

IStaking public immutable tStaking;
IERC20 public immutable token;

IUpdatableStakeInfo public updatableStakeInfo;
address public adjudicator;

mapping (address => StakingProviderInfo) public stakingProviderInfo;
address[] public stakingProviders;
Expand All @@ -148,10 +151,6 @@ contract PREApplication is IApplication, Adjudicator, OwnableUpgradeable {
* @notice Constructor sets address of token contract and parameters for staking
* @param _token T token contract
* @param _tStaking T token staking 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 _minimumAuthorization Amount of minimum allowable authorization
* @param _minOperatorSeconds Min amount of seconds while an operator can't be changed
* @param _rewardDuration Duration of one reward cycle
Expand All @@ -160,22 +159,11 @@ contract PREApplication is IApplication, Adjudicator, OwnableUpgradeable {
constructor(
IERC20 _token,
IStaking _tStaking,
SignatureVerifier.HashAlgorithm _hashAlgorithm,
uint256 _basePenalty,
uint256 _penaltyHistoryCoefficient,
uint256 _percentagePenaltyCoefficient,
uint96 _minimumAuthorization,
uint256 _minOperatorSeconds,
uint256 _rewardDuration,
uint256 _deauthorizationDuration
)
Adjudicator(
_hashAlgorithm,
_basePenalty,
_penaltyHistoryCoefficient,
_percentagePenaltyCoefficient
)
{
) {
require(
_rewardDuration != 0 &&
_tStaking.authorizedStake(address(this), address(this)) == 0 &&
Expand Down Expand Up @@ -228,6 +216,26 @@ contract PREApplication is IApplication, Adjudicator, OwnableUpgradeable {
__Ownable_init();
}

/**
* @notice Set contract for multi-chain interactions
*/
function setUpdatableStakeInfo(IUpdatableStakeInfo _updatableStakeInfo) external onlyOwner {
require(address(_updatableStakeInfo) != address(updatableStakeInfo), "New address must not be equal to the current one");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this check? What's the worst can happen?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

considering that is method for DAO - I prefer to more checks than less in any case

if (address(_updatableStakeInfo) != address(0)) {
// trying to call contract to be sure that is correct address
_updatableStakeInfo.updateOperator(address(0), address(0));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is to implicitly check the source in PolygonRoot, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and to check that contract is correct one in general

}
updatableStakeInfo = _updatableStakeInfo;
}

/**
* @notice Set Set adjudicator contract. If zero then slashing is disabled
*/
function setAdjudicator(address _adjudicator) external onlyOwner {
require(address(_adjudicator) != address(adjudicator), "New address must not be equal to the current one");
adjudicator = _adjudicator;
}

//------------------------Reward------------------------------

/**
Expand Down Expand Up @@ -341,8 +349,8 @@ contract PREApplication is IApplication, Adjudicator, OwnableUpgradeable {
/**
* @notice Recalculate reward and save increased authorization. Can be called only by staking contract
* @param _stakingProvider Address of staking provider
* @param _fromAmount Amount of previously authorized tokens to PRE application by staking provider
* @param _toAmount Amount of authorized tokens to PRE application by staking provider
* @param _fromAmount Amount of previously authorized tokens to TACo application by staking provider
* @param _toAmount Amount of authorized tokens to TACo application by staking provider
*/
function authorizationIncreased(
address _stakingProvider,
Expand All @@ -368,6 +376,7 @@ contract PREApplication is IApplication, Adjudicator, OwnableUpgradeable {

info.authorized = _toAmount;
emit AuthorizationIncreased(_stakingProvider, _fromAmount, _toAmount);
_updateAuthorization(_stakingProvider, info);
}

/**
Expand Down Expand Up @@ -398,8 +407,9 @@ contract PREApplication is IApplication, Adjudicator, OwnableUpgradeable {
if (info.authorized == 0) {
_stakingProviderFromOperator[info.operator] = address(0);
info.operator = address(0);
info.operatorConfirmed == false;
_releaseOperator(_stakingProvider);
}
_updateAuthorization(_stakingProvider, info);
}

/**
Expand Down Expand Up @@ -429,6 +439,7 @@ contract PREApplication is IApplication, Adjudicator, OwnableUpgradeable {
info.deauthorizing = _fromAmount - _toAmount;
info.endDeauthorization = uint64(block.timestamp + deauthorizationDuration);
emit AuthorizationDecreaseRequested(_stakingProvider, _fromAmount, _toAmount);
_updateAuthorization(_stakingProvider, info);
}

/**
Expand All @@ -454,8 +465,9 @@ contract PREApplication is IApplication, Adjudicator, OwnableUpgradeable {
if (info.authorized == 0) {
_stakingProviderFromOperator[info.operator] = address(0);
info.operator = address(0);
info.operatorConfirmed == false;
_releaseOperator(_stakingProvider);
}
_updateAuthorization(_stakingProvider, info);
}

/**
Expand All @@ -480,15 +492,16 @@ contract PREApplication is IApplication, Adjudicator, OwnableUpgradeable {
if (info.authorized == 0) {
_stakingProviderFromOperator[info.operator] = address(0);
info.operator = address(0);
info.operatorConfirmed == false;
_releaseOperator(_stakingProvider);
}
_updateAuthorization(_stakingProvider, info);
}

//-------------------------Main-------------------------
/**
* @notice Returns staking provider for specified operator
*/
function stakingProviderFromOperator(address _operator) public view override returns (address) {
function stakingProviderFromOperator(address _operator) public view returns (address) {
return _stakingProviderFromOperator[_operator];
}

Expand All @@ -502,10 +515,17 @@ contract PREApplication is IApplication, Adjudicator, OwnableUpgradeable {
/**
* @notice Get all tokens delegated to the staking provider
*/
function authorizedStake(address _stakingProvider) public view override returns (uint96) {
function authorizedStake(address _stakingProvider) public view returns (uint96) {
return stakingProviderInfo[_stakingProvider].authorized;
}

/**
* @notice Get all tokens delegated to the staking provider
*/
function getEligibleAmount(StakingProviderInfo storage _info) internal view returns (uint96) {
return _info.authorized - _info.deauthorizing;
}

/**
* @notice Get the value of authorized tokens for active providers as well as providers and their authorized tokens
* @param _startIndex Start index for looking in providers array
Expand All @@ -531,7 +551,7 @@ contract PREApplication is IApplication, Adjudicator, OwnableUpgradeable {
for (uint256 i = _startIndex; i < endIndex; i++) {
address stakingProvider = stakingProviders[i];
StakingProviderInfo storage info = stakingProviderInfo[stakingProvider];
uint256 eligibleAmount = info.authorized - info.deauthorizing;
uint256 eligibleAmount = getEligibleAmount(info);
if (eligibleAmount < minimumAuthorization || !info.operatorConfirmed) {
continue;
}
Expand Down Expand Up @@ -618,8 +638,8 @@ contract PREApplication is IApplication, Adjudicator, OwnableUpgradeable {
// Bond new operator (or unbond if _operator == address(0))
info.operator = _operator;
info.operatorStartTimestamp = uint64(block.timestamp);
info.operatorConfirmed = false;
emit OperatorBonded(_stakingProvider, _operator, previousOperator, block.timestamp);
_releaseOperator(_stakingProvider);
}

/**
Expand All @@ -636,6 +656,33 @@ contract PREApplication is IApplication, Adjudicator, OwnableUpgradeable {
info.operatorConfirmed = true;
authorizedOverall += info.authorized;
emit OperatorConfirmed(stakingProvider, msg.sender);

if (address(updatableStakeInfo) != address(0)) {
updatableStakeInfo.updateOperator(stakingProvider, msg.sender);
}
}

//-------------------------XChain-------------------------

/**
* @notice Resets operator confirmation
*/
function _releaseOperator(address _stakingProvider) internal {
stakingProviderInfo[_stakingProvider].operatorConfirmed = false;
if (address(updatableStakeInfo) != address(0)) {
updatableStakeInfo.updateOperator(_stakingProvider, address(0));
}
}

/**
* @notice Send updated authorized amount to xchain contract
*/
function _updateAuthorization(address _stakingProvider, StakingProviderInfo storage _info) internal {
if (address(updatableStakeInfo) != address(0)) {
// TODO send both authorized and eligible amounts in case of slashing from StakeInfo
uint96 eligibleAmount = getEligibleAmount(_info);
updatableStakeInfo.updateAmount(_stakingProvider, eligibleAmount);
}
}

//-------------------------Slashing-------------------------
Expand All @@ -650,8 +697,9 @@ contract PREApplication is IApplication, Adjudicator, OwnableUpgradeable {
uint96 _penalty,
address _investigator
)
internal override
external
{
require(msg.sender == adjudicator, "Only adjudicator allowed to slash");
address[] memory stakingProviderWrapper = new address[](1);
stakingProviderWrapper[0] = _stakingProvider;
tStaking.seize(_penalty, 100, _investigator, stakingProviderWrapper);
Expand Down
6 changes: 3 additions & 3 deletions contracts/contracts/TestnetThresholdStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ contract TestnetThresholdStaking is Ownable {
uint96 nuInTStake;
}

IApplication public preApplication;
IApplication public application;

mapping (address => StakingProviderInfo) public stakingProviderInfo;

function setApplication(IApplication _preApplication) external onlyOwner {
preApplication = _preApplication;
function setApplication(IApplication _application) external onlyOwner {
application = _application;
}

function stakedNu(address) external view returns (uint256) {
Expand Down
2 changes: 1 addition & 1 deletion contracts/contracts/coordination/IUpdatableStakeInfo.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pragma solidity ^0.8.0;
* @title StakeInfo
* @notice StakeInfo
*/
interface IUpdatableStakes {
interface IUpdatableStakeInfo {

event UpdatedStakeOperator(address indexed stakingProvider, address indexed operator);
event UpdatedStakeAmount(address indexed stakingProvider, uint96 amount);
Expand Down
Loading
Loading