mirror of https://github.com/nucypher/nucypher.git
Merge pull request #2860 from cygnusv/sm
Adapt "Hello Operator" to new interface of SubscriptionManagerpull/2869/head
commit
966d6c08c4
|
@ -17,6 +17,9 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
from typing import Tuple, Optional
|
||||
|
||||
from web3 import Web3
|
||||
from web3.types import Wei
|
||||
|
||||
from nucypher.blockchain.eth.agents import (
|
||||
ContractAgency,
|
||||
PREApplicationAgent
|
||||
|
@ -29,6 +32,7 @@ class Economics:
|
|||
|
||||
_default_min_authorization = TToken(40_000, 'T').to_units()
|
||||
_default_min_operator_seconds = 60 * 60 * 24 # one day in seconds
|
||||
_default_fee_rate = Wei(Web3.toWei(1, 'gwei'))
|
||||
|
||||
# TODO: Reintroduce Adjudicator
|
||||
# Slashing parameters
|
||||
|
@ -44,6 +48,7 @@ class Economics:
|
|||
def __init__(self,
|
||||
min_operator_seconds: int = _default_min_operator_seconds,
|
||||
min_authorization: int = _default_min_authorization,
|
||||
fee_rate: Wei = _default_fee_rate,
|
||||
|
||||
# Adjudicator
|
||||
# hash_algorithm: int = _default_hash_algorithm,
|
||||
|
@ -73,6 +78,7 @@ class Economics:
|
|||
|
||||
self.min_operator_seconds = min_operator_seconds
|
||||
self.min_authorization = min_authorization
|
||||
self.fee_rate = fee_rate
|
||||
|
||||
@property
|
||||
def pre_application_deployment_parameters(self) -> Tuple[int, ...]:
|
||||
|
|
|
@ -825,8 +825,8 @@ class SubscriptionManagerAgent(EthereumContractAgent):
|
|||
#
|
||||
|
||||
@contract_api(CONTRACT_CALL)
|
||||
def rate_per_second(self) -> Wei:
|
||||
result = self.contract.functions.RATE_PER_SECOND().call()
|
||||
def fee_rate(self) -> Wei:
|
||||
result = self.contract.functions.feeRate().call()
|
||||
return Wei(result)
|
||||
|
||||
@contract_api(CONTRACT_CALL)
|
||||
|
@ -839,10 +839,11 @@ class SubscriptionManagerAgent(EthereumContractAgent):
|
|||
record = self.contract.functions.policies(policy_id).call()
|
||||
policy_info = self.PolicyInfo(
|
||||
sponsor=record[0],
|
||||
start_timestamp=record[1],
|
||||
end_timestamp=record[2],
|
||||
size=record[3],
|
||||
# If the policyOwner addr is null, we return the sponsor addr instead of the owner.
|
||||
owner=record[0] if record[1] == NULL_ADDRESS else record[1],
|
||||
start_timestamp=record[2],
|
||||
end_timestamp=record[3]
|
||||
owner=record[0] if record[4] == NULL_ADDRESS else record[4]
|
||||
)
|
||||
return policy_info
|
||||
|
||||
|
@ -854,6 +855,7 @@ class SubscriptionManagerAgent(EthereumContractAgent):
|
|||
def create_policy(self,
|
||||
policy_id: bytes,
|
||||
transacting_power: TransactingPower,
|
||||
size: int,
|
||||
start_timestamp: Timestamp,
|
||||
end_timestamp: Timestamp,
|
||||
value: Wei,
|
||||
|
@ -863,6 +865,7 @@ class SubscriptionManagerAgent(EthereumContractAgent):
|
|||
contract_function: ContractFunction = self.contract.functions.createPolicy(
|
||||
policy_id,
|
||||
owner_address,
|
||||
size,
|
||||
start_timestamp,
|
||||
end_timestamp
|
||||
)
|
||||
|
@ -873,13 +876,6 @@ class SubscriptionManagerAgent(EthereumContractAgent):
|
|||
)
|
||||
return receipt
|
||||
|
||||
@contract_api(TRANSACTION)
|
||||
def sweep(self, recipient: ChecksumAddress, transacting_power: TransactingPower) -> TxReceipt:
|
||||
"""Collect fees (ETH) earned since last withdrawal"""
|
||||
contract_function: ContractFunction = self.contract.functions.sweep(recipient)
|
||||
receipt = self.blockchain.send_transaction(contract_function=contract_function, transacting_power=transacting_power)
|
||||
return receipt
|
||||
|
||||
|
||||
class AdjudicatorAgent(EthereumContractAgent):
|
||||
|
||||
|
|
|
@ -721,7 +721,7 @@ class SubscriptionManagerDeployer(BaseContractDeployer, OwnableContractMixin):
|
|||
|
||||
agency = SubscriptionManagerAgent
|
||||
contract_name = agency.contract_name
|
||||
deployment_steps = ('contract_deployment',)
|
||||
deployment_steps = ('contract_deployment', 'initialize')
|
||||
_upgradeable = False
|
||||
_ownable = True
|
||||
|
||||
|
@ -744,8 +744,19 @@ class SubscriptionManagerDeployer(BaseContractDeployer, OwnableContractMixin):
|
|||
gas_limit=gas_limit,
|
||||
confirmations=confirmations,
|
||||
**constructor_kwargs)
|
||||
|
||||
self._contract = contract
|
||||
return {self.deployment_steps[0]: deployment_receipt}
|
||||
|
||||
tx_args = {}
|
||||
if gas_limit:
|
||||
tx_args.update({'gas': gas_limit}) # TODO: Gas management - 842
|
||||
initialize_function = contract.functions.initialize(self.economics.fee_rate)
|
||||
initialize_receipt = self.blockchain.send_transaction(contract_function=initialize_function,
|
||||
transacting_power=transacting_power,
|
||||
payload=tx_args,
|
||||
confirmations=confirmations)
|
||||
return {self.deployment_steps[0]: deployment_receipt,
|
||||
self.deployment_steps[1]: initialize_receipt}
|
||||
|
||||
|
||||
class AdjudicatorDeployer(BaseContractDeployer, UpgradeableContractMixin, OwnableContractMixin):
|
||||
|
|
|
@ -2,38 +2,47 @@
|
|||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract SubscriptionManager {
|
||||
import "../zeppelin/proxy/Initializable.sol";
|
||||
|
||||
uint256 private constant RATE_PER_DAY = 50 gwei;
|
||||
uint256 public constant RATE_PER_SECOND = RATE_PER_DAY / 1 days;
|
||||
contract SubscriptionManager is Initializable {
|
||||
|
||||
struct Policy { // TODO: Optimize struct layout
|
||||
// The layout of policy struct is optimized, so sponsor, timestamps and size
|
||||
// fit in a single 256-word.
|
||||
struct Policy {
|
||||
address payable sponsor;
|
||||
uint32 startTimestamp;
|
||||
uint32 endTimestamp;
|
||||
uint16 size; // also known as `N`
|
||||
address owner;
|
||||
uint64 startTimestamp;
|
||||
uint64 endTimestamp;
|
||||
}
|
||||
|
||||
event PolicyCreated(
|
||||
bytes16 indexed policyId,
|
||||
address indexed sponsor,
|
||||
address indexed owner,
|
||||
uint64 startTimestamp,
|
||||
uint64 endTimestamp
|
||||
uint16 size,
|
||||
uint32 startTimestamp,
|
||||
uint32 endTimestamp
|
||||
);
|
||||
|
||||
address payable public owner;
|
||||
mapping (bytes16 => Policy) public policies;
|
||||
|
||||
constructor(){
|
||||
owner = payable(msg.sender);
|
||||
event FeeRateUpdated(uint256 oldFeeRate, uint256 newFeeRate);
|
||||
|
||||
// Per-second service fee rate
|
||||
uint256 public feeRate;
|
||||
|
||||
// Mapping that stores policy structs, keyed by policy ID
|
||||
mapping (bytes16 => Policy) internal _policies;
|
||||
|
||||
function initialize(uint256 _feeRate) public initializer {
|
||||
_setFeeRate(_feeRate);
|
||||
}
|
||||
|
||||
function createPolicy(
|
||||
bytes16 _policyId,
|
||||
address _policyOwner,
|
||||
uint64 _startTimestamp,
|
||||
uint64 _endTimestamp
|
||||
uint16 _size,
|
||||
uint32 _startTimestamp,
|
||||
uint32 _endTimestamp
|
||||
)
|
||||
external payable
|
||||
{
|
||||
|
@ -42,31 +51,33 @@ contract SubscriptionManager {
|
|||
block.timestamp < _endTimestamp,
|
||||
"Invalid timestamps"
|
||||
);
|
||||
uint64 duration = _endTimestamp - _startTimestamp;
|
||||
uint32 duration = _endTimestamp - _startTimestamp;
|
||||
require(
|
||||
duration > 0 &&
|
||||
msg.value == RATE_PER_SECOND * uint64(duration)
|
||||
duration > 0 && _size > 0 &&
|
||||
msg.value == feeRate * _size * uint32(duration)
|
||||
);
|
||||
//Policy storage policy =
|
||||
_createPolicy(_policyId, _policyOwner, _startTimestamp, _endTimestamp);
|
||||
|
||||
_createPolicy(_policyId, _policyOwner, _size, _startTimestamp, _endTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Create policy
|
||||
* @param _policyId Policy id
|
||||
* @param _policyOwner Policy owner. Zero address means sender is owner
|
||||
* @param _size Number of nodes involved in the policy
|
||||
* @param _startTimestamp Start timestamp of the policy in seconds
|
||||
* @param _endTimestamp End timestamp of the policy in seconds
|
||||
*/
|
||||
function _createPolicy(
|
||||
bytes16 _policyId,
|
||||
address _policyOwner,
|
||||
uint64 _startTimestamp,
|
||||
uint64 _endTimestamp
|
||||
uint16 _size,
|
||||
uint32 _startTimestamp,
|
||||
uint32 _endTimestamp
|
||||
)
|
||||
internal returns (Policy storage policy)
|
||||
{
|
||||
policy = policies[_policyId];
|
||||
policy = _policies[_policyId];
|
||||
require(
|
||||
policy.endTimestamp < block.timestamp,
|
||||
"Policy is currently active"
|
||||
|
@ -75,6 +86,7 @@ contract SubscriptionManager {
|
|||
policy.sponsor = payable(msg.sender);
|
||||
policy.startTimestamp = _startTimestamp;
|
||||
policy.endTimestamp = _endTimestamp;
|
||||
policy.size = _size;
|
||||
|
||||
if (_policyOwner != msg.sender && _policyOwner != address(0)) {
|
||||
policy.owner = _policyOwner;
|
||||
|
@ -84,20 +96,34 @@ contract SubscriptionManager {
|
|||
_policyId,
|
||||
msg.sender,
|
||||
_policyOwner == address(0) ? msg.sender : _policyOwner,
|
||||
_size,
|
||||
_startTimestamp,
|
||||
_endTimestamp
|
||||
);
|
||||
}
|
||||
|
||||
function getPolicy(bytes16 _policyID) public view returns(Policy memory){
|
||||
return _policies[_policyID];
|
||||
}
|
||||
|
||||
function isPolicyActive(bytes16 _policyID) public view returns(bool){
|
||||
return policies[_policyID].endTimestamp > block.timestamp;
|
||||
return _policies[_policyID].endTimestamp > block.timestamp;
|
||||
}
|
||||
|
||||
function _setFeeRate(uint256 newFee) internal {
|
||||
uint256 oldFee = feeRate;
|
||||
feeRate = newFee;
|
||||
emit FeeRateUpdated(oldFee, newFee);
|
||||
}
|
||||
|
||||
function setFeeRate(uint256 _rate_per_second) external {
|
||||
_setFeeRate(_rate_per_second);
|
||||
}
|
||||
|
||||
function sweep(address payable recipient) external {
|
||||
require(msg.sender == owner);
|
||||
uint256 balance = address(this).balance;
|
||||
(bool sent, ) = recipient.call{value: balance}("");
|
||||
require(sent, "Failed transfer");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -48,6 +48,7 @@ class PaymentMethod(ReencryptionPrerequisite, ABC):
|
|||
commencement: int # epoch
|
||||
expiration: int # epoch
|
||||
duration: int # seconds or periods
|
||||
shares: int
|
||||
|
||||
@abstractmethod
|
||||
def pay(self, policy: Policy) -> Dict:
|
||||
|
@ -125,6 +126,7 @@ class FreeReencryptions(PaymentMethod):
|
|||
return 0
|
||||
|
||||
def quote(self,
|
||||
shares: int,
|
||||
commencement: Optional[Timestamp] = None,
|
||||
expiration: Optional[Timestamp] = None,
|
||||
duration: Optional[int] = None,
|
||||
|
@ -133,6 +135,7 @@ class FreeReencryptions(PaymentMethod):
|
|||
return self.Quote(
|
||||
value=0,
|
||||
rate=0,
|
||||
shares=shares,
|
||||
duration=duration,
|
||||
commencement=commencement,
|
||||
expiration=expiration
|
||||
|
@ -158,6 +161,7 @@ class SubscriptionManagerPayment(ContractPayment):
|
|||
receipt = self.agent.create_policy(
|
||||
value=policy.value, # wei
|
||||
policy_id=bytes(policy.hrac), # bytes16 _policyID
|
||||
size=len(policy.kfrags), # uint16
|
||||
start_timestamp=policy.commencement, # uint16
|
||||
end_timestamp=policy.expiration, # uint16
|
||||
transacting_power=policy.publisher.transacting_power
|
||||
|
@ -166,10 +170,11 @@ class SubscriptionManagerPayment(ContractPayment):
|
|||
|
||||
@property
|
||||
def rate(self) -> Wei:
|
||||
fixed_rate = self.agent.rate_per_second()
|
||||
fixed_rate = self.agent.fee_rate()
|
||||
return Wei(fixed_rate)
|
||||
|
||||
def quote(self,
|
||||
shares: int,
|
||||
commencement: Optional[Timestamp] = None,
|
||||
expiration: Optional[Timestamp] = None,
|
||||
duration: Optional[int] = None,
|
||||
|
@ -202,19 +207,18 @@ class SubscriptionManagerPayment(ContractPayment):
|
|||
|
||||
q = self.Quote(
|
||||
rate=Wei(self.rate),
|
||||
value=Wei(self.rate * duration),
|
||||
value=Wei(self.rate * duration * shares),
|
||||
shares=shares,
|
||||
commencement=Timestamp(commencement),
|
||||
expiration=Timestamp(expiration),
|
||||
duration=duration
|
||||
)
|
||||
return q
|
||||
|
||||
def validate_price(self, value: Wei, duration: Wei, *args, **kwargs) -> bool:
|
||||
if value and duration:
|
||||
if duration != value // self.rate:
|
||||
raise ValueError(f"Invalid duration ({duration}) for value ({value}).")
|
||||
if value != duration * self.rate:
|
||||
raise ValueError(f"Invalid value ({value}) for duration ({duration}).")
|
||||
def validate_price(self, value: Wei, duration: Wei, shares: int, *args, **kwargs) -> bool:
|
||||
expected_price = Wei(shares * duration * self.rate)
|
||||
if value != expected_price:
|
||||
raise ValueError(f"Policy value ({value}) doesn't match expected value ({expected_price})")
|
||||
return True
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue