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

TACo Encryption Allow Logic Interfaces #89

Merged
merged 19 commits into from
Aug 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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: 0 additions & 2 deletions ape-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ deployments:
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
Expand Down
47 changes: 35 additions & 12 deletions contracts/contracts/coordination/Coordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import "./IFeeModel.sol";
import "./IReimbursementPool.sol";
import "../lib/BLS12381.sol";
import "../../threshold/IAccessControlApplication.sol";
import "./IEncryptionAuthorizer.sol";

/**
* @title Coordinator
Expand Down Expand Up @@ -58,6 +59,7 @@ contract Coordinator is AccessControlDefaultAdminRules {
address authority;
uint16 dkgSize;
bool aggregationMismatch;
IEncryptionAuthorizer accessController;
BLS12381.G1Point publicKey;
bytes aggregatedTranscript;
Participant[] participant;
Expand Down Expand Up @@ -100,11 +102,15 @@ contract Coordinator is AccessControlDefaultAdminRules {
feeModel = IFeeModel(_feeModel);
}

function getRitualState(uint256 ritualId) external view returns (RitualState){
// TODO: restrict to ritualID < rituals.length?
function getRitualState(uint32 ritualId) external view returns (RitualState){
// TODO: restrict to ritualId < rituals.length?
return getRitualState(rituals[ritualId]);
}

function isRitualFinalized(uint32 ritualId) external view returns (bool){
return getRitualState(rituals[ritualId]) == RitualState.FINALIZED;
}
Copy link
Member

Choose a reason for hiding this comment

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

Not a comment for this PR, but something to consider I just realized: we may need to discern if a covenant is currently active or not, depending whether it's within its arranged duration or not. The fact that the ritual is FINALIZED is a prerequisite for that to happen, of course. I can address that in #107


function getRitualState(Ritual storage ritual) internal view returns (RitualState){
uint32 t0 = ritual.initTimestamp;
uint32 deadline = t0 + timeout;
Expand All @@ -125,6 +131,7 @@ contract Coordinator is AccessControlDefaultAdminRules {
// - No public key
// - All transcripts and all aggregations
// - Still within the deadline
revert("Invalid ritual state");
}
}

Expand Down Expand Up @@ -175,6 +182,13 @@ contract Coordinator is AccessControlDefaultAdminRules {
// TODO: Events
}

function setRitualAuthority(uint32 ritualId, address authority) external {
Ritual storage ritual = rituals[ritualId];
require(getRitualState(ritual) == RitualState.FINALIZED, "Ritual not finalized");
require(msg.sender == ritual.authority, "Sender not ritual authority");
ritual.authority = authority;
}

function numberOfRituals() external view returns (uint256) {
return rituals.length;
}
Expand All @@ -187,8 +201,12 @@ contract Coordinator is AccessControlDefaultAdminRules {
function initiateRitual(
address[] calldata providers,
address authority,
uint32 duration
uint32 duration,
IEncryptionAuthorizer accessController
) external returns (uint32) {

require(authority != address(0), "Invalid authority");

require(
isInitiationPublic || hasRole(INITIATOR_ROLE, msg.sender),
"Sender can't initiate ritual"
Expand All @@ -205,6 +223,7 @@ contract Coordinator is AccessControlDefaultAdminRules {
ritual.dkgSize = uint16(length);
ritual.initTimestamp = uint32(block.timestamp);
ritual.endTimestamp = ritual.initTimestamp + duration;
ritual.accessController = accessController;

address previous = address(0);
for (uint256 i = 0; i < length; i++) {
Expand Down Expand Up @@ -272,6 +291,10 @@ contract Coordinator is AccessControlDefaultAdminRules {
processReimbursement(initialGasLeft);
}

function getAuthority(uint32 ritualId) external view returns (address) {
return rituals[ritualId].authority;
}

function postAggregation(
uint32 ritualId,
bytes calldata aggregatedTranscript,
Expand Down Expand Up @@ -359,39 +382,39 @@ contract Coordinator is AccessControlDefaultAdminRules {
}

function getParticipantFromProvider(
uint256 ritualID,
uint32 ritualId,
address provider
) external view returns (Participant memory) {
return getParticipantFromProvider(rituals[ritualID], provider);
return getParticipantFromProvider(rituals[ritualId], provider);
}

function processRitualPayment(uint256 ritualID, address[] calldata providers, uint32 duration) internal {
function processRitualPayment(uint32 ritualId, address[] calldata providers, uint32 duration) internal {
uint256 ritualCost = feeModel.getRitualInitiationCost(providers, duration);
if (ritualCost > 0) {
totalPendingFees += ritualCost;
assert(pendingFees[ritualID] == 0); // TODO: This is an invariant, not sure if actually needed
pendingFees[ritualID] += ritualCost;
assert(pendingFees[ritualId] == 0); // TODO: This is an invariant, not sure if actually needed
pendingFees[ritualId] += ritualCost;
IERC20 currency = IERC20(feeModel.currency());
currency.safeTransferFrom(msg.sender, address(this), ritualCost);
// TODO: Define methods to manage these funds
}
}

function processPendingFee(uint256 ritualID) public {
Ritual storage ritual = rituals[ritualID];
function processPendingFee(uint32 ritualId) public {
Ritual storage ritual = rituals[ritualId];
RitualState state = getRitualState(ritual);
require(
state == RitualState.TIMEOUT ||
state == RitualState.INVALID ||
state == RitualState.FINALIZED,
"Ritual is not ended"
);
uint256 pending = pendingFees[ritualID];
uint256 pending = pendingFees[ritualId];
require(pending > 0, "No pending fees for this ritual");

// Finalize fees for this ritual
totalPendingFees -= pending;
delete pendingFees[ritualID];
delete pendingFees[ritualId];
// Transfer fees back to initiator if failed
if (state == RitualState.TIMEOUT || state == RitualState.INVALID) {
// Amount to refund depends on how much work nodes did for the ritual.
Expand Down
58 changes: 58 additions & 0 deletions contracts/contracts/coordination/GlobalAllowList.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControlDefaultAdminRules.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "./IEncryptionAuthorizer.sol";
import "./Coordinator.sol";


contract GlobalAllowList is AccessControlDefaultAdminRules, IEncryptionAuthorizer {
using ECDSA for bytes32;

Coordinator public coordinator;
vzotova marked this conversation as resolved.
Show resolved Hide resolved
mapping(uint256 => mapping(address => bool)) public authorizations;

constructor(
Coordinator _coordinator,
address _admin
) AccessControlDefaultAdminRules(0, _admin) {
require(address(_coordinator) != address(0), "Coordinator cannot be zero address");
require(_coordinator.numberOfRituals() >= 0, "Invalid coordinator");
coordinator = _coordinator;
vzotova marked this conversation as resolved.
Show resolved Hide resolved
}

modifier onlyAuthority(uint32 ritualId) {
require(coordinator.getAuthority(ritualId) == msg.sender,
"Only ritual authority is permitted");
_;
}

function setCoordinator(Coordinator _coordinator) public {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Only admin can set coordinator");
coordinator = _coordinator;
Copy link
Member

Choose a reason for hiding this comment

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

What's the use case for setting the Coordinator after deployment, if it was already set on construction time?

Copy link
Member Author

Choose a reason for hiding this comment

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

In the event the coordinator is redeployed to support a security patch or new feature.

}

function isAuthorized(
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the DKG client supposed to call this method at some point? Fore example, when picking nodes for the DKG cohort?

Copy link
Member

@derekpierre derekpierre Aug 15, 2023

Choose a reason for hiding this comment

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

Not when picking nodes for the cohort but rather when decryption is requested from the nodes. Each node that receives a decryption request will check to make sure that this encryptor is allowed to use the corresponding ritual/encrypting key and therefore worthy to be decrypted. This is showcased in WIP PR 3194 - see https://github.com/nucypher/nucypher/pull/3194/files#diff-8df83dacf71b2ff2ca3636cd470cba70eb6b141e3c8843fadfab72514aafa3f6R199 and https://github.com/nucypher/nucypher/pull/3194/files#diff-911a370c18d0d0e3ea62099fc5ff6daa16915da19cf5399f24e404b7d8d6d168R790

uint32 ritualId,
bytes memory evidence,
bytes32 digest
) public view override returns(bool) {
address recovered_address = digest.toEthSignedMessageHash().recover(evidence);
return authorizations[ritualId][recovered_address];
}

function authorize(uint32 ritualId, address[] calldata addresses) public onlyAuthority(ritualId) {
require(coordinator.isRitualFinalized(ritualId),
"Only active rituals can add authorizations");
for (uint256 i=0; i < addresses.length; i++) {
authorizations[ritualId][addresses[i]] = true;
}
}

function deauthorize(uint32 ritualId, address[] calldata addresses) public onlyAuthority(ritualId) {
require(coordinator.isRitualFinalized(ritualId),
"Only active rituals can add authorizations");
for (uint256 i=0; i < addresses.length; i++) {
authorizations[ritualId][addresses[i]] = false;
}
}
}
9 changes: 9 additions & 0 deletions contracts/contracts/coordination/IEncryptionAuthorizer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pragma solidity ^0.8.0;

interface IEncryptionAuthorizer {
function isAuthorized(
uint32 ritualID,
bytes memory evidence, // signature
bytes32 digest // signed message hash
) external view returns(bool);
}
Loading
Loading