Merge pull request #2860 from cygnusv/sm

Adapt "Hello Operator" to new interface of SubscriptionManager
pull/2869/head
KPrasch 2022-02-10 11:22:25 -08:00 committed by GitHub
commit 966d6c08c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 91 additions and 48 deletions

View File

@ -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, ...]:

View File

@ -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):

View File

@ -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):

View File

@ -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");
}
}
}

View File

@ -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