Relocates and adds miner contract API to ContractAgents, from whence they came.

pull/330/head
Kieran Prasch 2018-06-11 10:50:20 -07:00
parent 1896da5e05
commit 0528d14708
1 changed files with 113 additions and 9 deletions

View File

@ -1,13 +1,13 @@
import random
from abc import ABC
from enum import Enum
from typing import Set, Generator, List, Tuple, Union
from typing import Generator, List, Tuple, Union
from constant_sorrow import constants
from web3.contract import Contract
from nucypher.blockchain.eth import constants
from nucypher.blockchain.eth.chains import Blockchain
from constant_sorrow import constants
class EthereumContractAgent(ABC):
@ -54,10 +54,6 @@ class EthereumContractAgent(ABC):
def contract_name(self) -> str:
return self._principal_contract_name
@property
def origin(self) -> str:
return self.blockchain.interface.w3.eth.coinbase # TODO: make swappable
def get_balance(self, address: str=None) -> int:
"""Get the balance of a token address, or of this contract address"""
address = address if address is not None else self.contract_address
@ -67,6 +63,13 @@ class EthereumContractAgent(ABC):
class NucypherTokenAgent(EthereumContractAgent):
_principal_contract_name = "NuCypherToken"
def approve_transfer(self, amount: int, target_address: str, sender_address: str) -> str:
"""Approve the transfer of token from the sender address to the target address."""
txhash = self.contract.functions.approve(target_address, amount).transact({'from': sender_address})
self.blockchain.wait_for_receipt(txhash)
return txhash
class MinerAgent(EthereumContractAgent):
"""
@ -94,16 +97,117 @@ class MinerAgent(EthereumContractAgent):
super().__init__(blockchain=token_agent.blockchain, *args, **kwargs)
self.token_agent = token_agent
#
# Miner Network Status
#
def get_miner_population(self) -> int:
"""Returns the number of miners on the blockchain"""
return self.contract.functions.getMinersLength().call()
def get_current_period(self) -> int:
"""Returns the current period"""
return self.contract.functions.getCurrentPeriod().call()
def get_total_locked_tokens(self) -> int:
"""Returns the total amount of locked tokens on the blockchain."""
return self.contract.functions.getAllLockedTokens().call()
#
# MinersEscrow Contract API
#
def get_locked_tokens(self, node_address):
"""Returns the amount of tokens this miner has locked."""
return self.contract.functions.getLockedTokens(node_address).call()
def get_stake_info(self, miner_address: str, stake_index: int):
stake_info = self.contract.functions.getStakeInfo(miner_address, stake_index).call()
return stake_info
def deposit_tokens(self, amount: int, lock_periods: int, sender_address: str) -> str:
"""Send tokes to the escrow from the miner's address"""
deposit_txhash = self.contract.functions.deposit(amount, lock_periods).transact({'from': sender_address})
self.blockchain.wait_for_receipt(deposit_txhash)
return deposit_txhash
def divide_stake(self, miner_address, balance, end_period, target_value, periods):
tx = self.contract.functions.divideStake(balance, # uint256 _oldValue
end_period, # uint256 _lastPeriod,
target_value, # uint256 _newValue,
periods # uint256 _periods
).transact({'from': miner_address})
self.blockchain.wait_for_receipt(tx)
return tx
def confirm_activity(self, node_address: str) -> str:
"""Miner rewarded for every confirmed period"""
txhash = self.contract.functions.confirmActivity().transact({'from': node_address})
self.blockchain.wait_for_receipt(txhash)
return txhash
def mint(self, node_address) -> Tuple[str, str]:
"""Computes and transfers tokens to the miner's account"""
mint_txhash = self.contract.functions.mint().transact({'from': node_address})
self.blockchain.wait_for_receipt(mint_txhash)
return mint_txhash
def collect_staking_reward(self, collector_address) -> str:
"""Withdraw tokens rewarded for staking."""
token_amount = self.contract.functions.minerInfo(collector_address).call()[0]
staked_amount = max(self.contract.functions.getLockedTokens(collector_address).call(),
self.contract.functions.getLockedTokens(collector_address, 1).call())
collection_txhash = self.contract.functions.withdraw(token_amount - staked_amount).transact({'from': collector_address})
self.blockchain.wait_for_receipt(collection_txhash)
return collection_txhash
# Node Datastore #
def _publish_datastore(self, node_address: str, data) -> str:
"""Publish new data to the MinerEscrow contract as a public record associated with this miner."""
txhash = self.contract.functions.setMinerId(data).transact({'from': node_address})
self.blockchain.wait_for_receipt(txhash)
return txhash
def _get_datastore_entries(self, node_address: str) -> int:
count_bytes = self.contract.functions.getMinerIdsLength(node_address).call()
datastore_entries = self.blockchain.interface.w3.toInt(count_bytes)
return datastore_entries
def _fetch_node_datastore(self, node_address):
"""Cache a generator of all asosciated contract data for this miner."""
datastore_entries = self._get_datastore_entries(node_address=node_address)
def __node_datastore_reader():
for index in range(datastore_entries):
value = self.contract.functions.getMinerId(node_address, index).call()
yield value
return __node_datastore_reader()
#
# Contract Utilities
#
def swarm(self, fetch_data: bool=False) -> Union[Generator[str, None, None], Generator[Tuple[str, bytes], None, None]]:
"""
Returns an iterator of all miner addresses via cumulative sum, on-network.
if fetch_data is true, tuples containing the address and the miners stored data are yielded.
Miner addresses are returned in the order in which they registered with the MinersEscrow contract's ledger
"""
miner_population = self.contract.functions.getMinersLength().call()
for index in range(miner_population):
for index in range(self.get_miner_population()):
miner_address = self.contract.functions.miners(index).call()
validated_address = self.blockchain.interface.w3.toChecksumAddress(miner_address) # string address of next node
@ -139,7 +243,7 @@ class MinerAgent(EthereumContractAgent):
system_random = random.SystemRandom()
n_select = round(quantity*additional_ursulas) # Select more Ursulas
n_tokens = self.contract.functions.getAllLockedTokens().call()
n_tokens = self.get_total_locked_tokens()
if not n_tokens > 0:
raise self.NotEnoughMiners('There are no locked tokens.')