Пример #1
0
def confirm_staged_grant(emitter, grant_request: Dict, federated: bool, seconds_per_period=None) -> None:

    pretty_request = grant_request.copy()  # WARNING: Do not mutate

    if federated:  # Boring
        table = [[field.capitalize(), value] for field, value in pretty_request.items()]
        emitter.echo(tabulate(table, tablefmt="simple"))
        return

    period_rate = Web3.fromWei(pretty_request['n'] * pretty_request['rate'], 'gwei')
    pretty_request['rate'] = f"{pretty_request['rate']} wei/period * {pretty_request['n']} nodes"

    expiration = pretty_request['expiration']
    periods = calculate_period_duration(future_time=MayaDT.from_datetime(expiration),
                                        seconds_per_period=seconds_per_period)
    periods += 1  # current period is always included
    pretty_request['expiration'] = f"{pretty_request['expiration']} ({periods} periods)"

    # M of N
    pretty_request['Threshold Shares'] = f"{pretty_request['m']} of {pretty_request['n']}"
    del pretty_request['m']
    del pretty_request['n']

    def prettify_field(field):
        field_words = [word.capitalize() for word in field.split('_')]
        field = ' '.join(field_words)
        return field

    table = [[prettify_field(field), value] for field, value in pretty_request.items()]
    table.append(['Period Rate', f'{period_rate} gwei'])
    table.append(['Policy Value', f'{period_rate * periods} gwei'])

    emitter.echo("\nSuccessfully staged grant, Please review the details:\n", color='green')
    emitter.echo(tabulate(table, tablefmt="simple"))
    click.confirm('\nGrant access and sign transaction?', abort=True)
Пример #2
0
    def __init__(self, alice: PolicyAuthor, ursula: Ursula, value: int,
                 expiration: maya.MayaDT, *args, **kwargs) -> None:

        super().__init__(alice=alice,
                         ursula=ursula,
                         expiration=expiration,
                         value=value,
                         *args,
                         **kwargs)

        if not value > 0:
            raise self.InvalidArrangement("Value must be greater than 0.")

        # The relationship exists between two addresses
        self.author = alice  # type: PolicyAuthor
        self.policy_agent = alice.policy_agent  # type: PolicyManagerAgent

        self.staker = ursula  # type: Staker

        # Arrangement value, rate, and duration
        lock_periods = calculate_period_duration(future_time=expiration)

        rate = value // lock_periods  # type: int
        self._rate = rate  # type: int

        self.value = value  # type: int
        self.lock_periods = lock_periods  # type: int # TODO: <datetime> -> lock_periods

        self.is_published = False  # type: bool
        self.publish_transaction = None

        self.is_revoked = False  # type: bool
        self.revoke_transaction = None
Пример #3
0
    def make_arrangements(self,
                          network_middleware: RestMiddleware,
                          deposit: int,
                          expiration: maya.MayaDT,
                          handpicked_ursulas: Set[Ursula] = None
                          ) -> None:
        """
        Create and consider n Arrangements from sampled miners, a list of Ursulas, or a combination of both.
        """

        ADDITIONAL_URSULAS = 1.5        # TODO: Make constant

        handpicked_ursulas = handpicked_ursulas or set()           # type: set
        target_sample_quantity = self.n - len(handpicked_ursulas)

        selected_addresses = set()      # type: set
        actual_sample_quantity = math.ceil(target_sample_quantity * ADDITIONAL_URSULAS)
        duration = int(calculate_period_duration(expiration))

        try:  # Sample by reading from the Blockchain
            sampled_addresses = self.alice.recruit(quantity=actual_sample_quantity, duration=duration)
        except MinerAgent.NotEnoughMiners as e:
            error = "Cannot create policy with {} arrangements: {}".format(target_sample_quantity, e)
            raise self.NotEnoughBlockchainUrsulas(error)

        selected_addresses.update(sampled_addresses)
        found_ursulas = self.__find_ursulas(sampled_addresses, target_sample_quantity)

        candidates = handpicked_ursulas
        candidates.update(found_ursulas)

        #
        # Consider Arrangements
        #

        # Attempt 1
        accepted, rejected = self._consider_arrangements(network_middleware=network_middleware,
                                                         candidate_ursulas=candidates,
                                                         deposit=deposit,
                                                         expiration=expiration)
        # After all is said and done...
        if len(accepted) < self.n:

            # Attempt 2:  Find more ursulas from the spare pile
            remaining_quantity = self.n - len(accepted)

            # TODO: Handle spare Ursulas and try to claw back up to n.
            found_spare_ursulas, remaining_spare_addresses = self.__find_ursulas(spare_addresses, remaining_quantity)
            accepted_spares, rejected_spares = self._consider_arrangements(network_middleware,
                                                                           candidate_ursulas=found_spare_ursulas,
                                                                           deposit=deposit,
                                                                           expiration=expiration)
            accepted.update(accepted_spares)
            rejected.update(rejected_spares)

            if len(accepted) < self.n:
                raise Exception("Selected Ursulas rejected too many arrangements")  # TODO: Better exception
Пример #4
0
    def initialize_stake(self,
                         amount: NU,
                         lock_periods: int = None,
                         expiration: maya.MayaDT = None,
                         entire_balance: bool = False) -> Stake:
        """Create a new stake."""

        #
        # Duration
        #

        if lock_periods and expiration:
            raise ValueError(
                "Pass the number of lock periods or an expiration MayaDT; not both."
            )
        if expiration:
            lock_periods = calculate_period_duration(future_time=expiration)

        #
        # Value
        #

        if entire_balance and amount:
            raise ValueError("Specify an amount or entire balance, not both")
        if entire_balance:
            amount = self.token_balance
        if not self.token_balance >= amount:
            raise self.MinerError(
                f"Insufficient token balance ({self.token_agent}) for new stake initialization of {amount}"
            )

        # Ensure the new stake will not exceed the staking limit
        if (self.current_stake +
                amount) > self.economics.maximum_allowed_locked:
            raise Stake.StakingError(
                f"Cannot divide stake - Maximum stake value exceeded with a target value of {amount}."
            )

        #
        # Stake
        #

        # Write to blockchain
        new_stake = Stake.initialize_stake(miner=self,
                                           amount=amount,
                                           lock_periods=lock_periods)
        self.__read_stakes()  # Update local staking cache
        return new_stake
Пример #5
0
    def generate_policy_parameters(
        self,
        number_of_ursulas: int = None,
        duration_periods: int = None,
        expiration: maya.MayaDT = None,
        value: int = None,
        rate: int = None,
        first_period_reward: int = None,
    ) -> dict:
        """
        Construct policy creation from parameters or overrides.
        """

        if not duration_periods and not expiration:
            raise ValueError(
                "Policy end time must be specified as 'expiration' or 'duration_periods', got neither."
            )

        # Merge injected and default params.
        first_period_reward = first_period_reward or self.first_period_reward
        rate = rate or self.rate
        duration_periods = duration_periods or self.duration_periods

        # Calculate duration in periods and expiration datetime
        if expiration:
            duration_periods = calculate_period_duration(
                future_time=expiration,
                seconds_per_period=self.economics.seconds_per_period)
        else:
            duration_periods = duration_periods or self.duration_periods
            expiration = datetime_at_period(
                self.staking_agent.get_current_period() + duration_periods,
                seconds_per_period=self.economics.seconds_per_period)

        from nucypher.policy.policies import BlockchainPolicy
        blockchain_payload = BlockchainPolicy.generate_policy_parameters(
            n=number_of_ursulas,
            duration_periods=duration_periods,
            first_period_reward=first_period_reward,
            value=value,
            rate=rate)

        # These values may have been recalculated in this block.
        policy_end_time = dict(duration_periods=duration_periods,
                               expiration=expiration)
        payload = {**blockchain_payload, **policy_end_time}
        return payload
Пример #6
0
    def initialize_stake(self,
                         amount: int,
                         lock_periods: int = None,
                         expiration: maya.MayaDT = None,
                         entire_balance: bool = False) -> dict:
        """
        High level staking method for Miners.

        :param amount: Amount of tokens to stake denominated in the smallest unit.
        :param lock_periods: Duration of stake in periods.
        :param expiration: A MayaDT object representing the time the stake expires; used to calculate lock_periods.
        :param entire_balance: If True, stake the entire balance of this node, or the maximum possible.

        """

        if lock_periods and expiration:
            raise ValueError(
                "Pass the number of lock periods or an expiration MayaDT; not both."
            )
        if entire_balance and amount:
            raise self.MinerError(
                "Specify an amount or entire balance, not both")

        if expiration:
            lock_periods = calculate_period_duration(future_time=expiration)
        if entire_balance is True:
            amount = self.token_balance

        staking_transactions = OrderedDict(
        )  # type: OrderedDict # Time series of txhases

        # Validate
        assert self.__validate_stake(amount=amount, lock_periods=lock_periods)

        # Transact
        approve_txhash, initial_deposit_txhash = self.deposit(
            amount=amount, lock_periods=lock_periods)
        self._transaction_cache.append(
            (datetime.utcnow(), initial_deposit_txhash))

        self.log.info(
            "{} Initialized new stake: {} tokens for {} periods".format(
                self.checksum_public_address, amount, lock_periods))
        return staking_transactions
Пример #7
0
    def initialize_stake(self,
                         amount: NU,
                         lock_periods: int = None,
                         expiration: maya.MayaDT = None,
                         entire_balance: bool = False) -> Stake:
        """Create a new stake."""

        # Duration
        if lock_periods and expiration:
            raise ValueError(
                "Pass the number of lock periods or an expiration MayaDT; not both."
            )
        if expiration:
            lock_periods = calculate_period_duration(
                future_time=expiration,
                seconds_per_period=self.economics.seconds_per_period)

        # Value
        if entire_balance and amount:
            raise ValueError("Specify an amount or entire balance, not both")
        if entire_balance:
            amount = self.token_balance
        if not self.token_balance >= amount:
            raise self.InsufficientTokens(
                f"Insufficient token balance ({self.token_agent}) "
                f"for new stake initialization of {amount}")

        # Ensure the new stake will not exceed the staking limit
        if (self.current_stake +
                amount) > self.economics.maximum_allowed_locked:
            raise Stake.StakingError(
                f"Cannot initialize stake - "
                f"Maximum stake value exceeded for {self.checksum_address} "
                f"with a target value of {amount}.")

        # Write to blockchain
        new_stake = Stake.initialize_stake(staker=self,
                                           amount=amount,
                                           lock_periods=lock_periods)

        # Update staking cache element
        self.stakes.refresh()

        return new_stake
Пример #8
0
    def __init__(self,
                 alice: PolicyAuthor,
                 value: int,
                 expiration: maya.MayaDT,
                 handpicked_ursulas: set = None,
                 initial_reward: int = 0,  # TODO: move somewhere else?
                 *args, **kwargs):

        self.initial_reward = initial_reward
        self.lock_periods = int(calculate_period_duration(expiration))
        self.handpicked_ursulas = handpicked_ursulas or UNKNOWN_ARRANGEMENTS
        self.expiration = expiration
        self.value = value
        self.author = alice

        # Initial State
        self.publish_transaction = None
        self.is_published = False

        super().__init__(alice=alice, *args, **kwargs)