deprecates agent.call() and .transact(); Upgrade to newer web3 API, with direct contract access

pull/270/head
Kieran Prasch 2018-04-26 19:00:33 -07:00 committed by Kieran R Prasch
parent f9eb10de60
commit 4ec9df051f
5 changed files with 69 additions and 53 deletions

View File

@ -67,7 +67,7 @@ class Miner(TokenActor):
def __update_locked_tokens(self) -> None:
"""Query the contract for the amount of locked tokens on this miner's eth address and cache it"""
self.__locked_tokens = self.miner_agent.read().getLockedTokens(self.address)
self.__locked_tokens = self.miner_agent.contract.functions.getLockedTokens(self.address).call()
@property
def is_staking(self):
@ -86,7 +86,7 @@ class Miner(TokenActor):
def _approve_escrow(self, amount: int) -> str:
"""Approve the transfer of token from the miner's address to the escrow contract."""
txhash = self.token_agent.transact({'from': self.address}).approve(self.miner_agent.contract_address, amount)
txhash = self.token_agent.contract.functions.approve(self.miner_agent.contract_address, amount).transact({'from': self.address})
self.blockchain.wait_for_receipt(txhash)
self._transactions.append((datetime.utcnow(), txhash))
@ -96,7 +96,7 @@ class Miner(TokenActor):
def _send_tokens_to_escrow(self, amount, locktime) -> str:
"""Send tokes to the escrow from the miner's address"""
deposit_txhash = self.miner_agent.transact({'from': self.address}).deposit(amount, locktime)
deposit_txhash = self.miner_agent.contract.functions.deposit(amount, locktime).transact({'from': self.address})
self.blockchain.wait_for_receipt(deposit_txhash)
self._transactions.append((datetime.utcnow(), deposit_txhash))
@ -111,11 +111,17 @@ class Miner(TokenActor):
return approve_txhash, deposit_txhash
# TODO add divide_stake method
def switch_lock(self):
lock_txhash = self.miner_agent.contract.functions.switchLock().transact({'from': self.address})
self.blockchain.wait_for_receipt(lock_txhash)
self._transactions.append((datetime.utcnow(), lock_txhash))
return lock_txhash
def _confirm_activity(self) -> str:
"""Miner rewarded for every confirmed period"""
txhash = self.miner_agent.transact({'from': self.address}).confirmActivity()
txhash = self.miner_agent.contract.functions.confirmActivity().transact({'from': self.address})
self.blockchain.wait_for_receipt(txhash)
self._transactions.append((datetime.utcnow(), txhash))
@ -125,8 +131,8 @@ class Miner(TokenActor):
def mint(self) -> Tuple[str, str]:
"""Computes and transfers tokens to the miner's account"""
confirm_txhash = self.miner_agent.transact({'from': self.address, 'gas_price': 0}).confirmActivity()
mint_txhash = self.miner_agent.transact({'from': self.address, 'gas_price': 0}).mint()
confirm_txhash = self.miner_agent.contract.functions.confirmActivity().transact({'from': self.address, 'gas_price': 0})
mint_txhash = self.miner_agent.contract.functions.mint().transact({'from': self.address, 'gas_price': 0})
self.blockchain.wait_for_receipt(mint_txhash)
self._transactions.append((datetime.utcnow(), mint_txhash))
@ -136,7 +142,7 @@ class Miner(TokenActor):
def collect_policy_reward(self, policy_manager):
"""Collect rewarded ETH"""
policy_reward_txhash = policy_manager.transact({'from': self.address}).withdraw()
policy_reward_txhash = policy_manager.contract.functions.withdraw().transact({'from': self.address})
self.blockchain.wait_for_receipt(policy_reward_txhash)
self._transactions.append((datetime.utcnow(), policy_reward_txhash))
@ -146,11 +152,14 @@ class Miner(TokenActor):
def collect_staking_reward(self) -> str:
"""Withdraw tokens rewarded for staking."""
token_amount = self.miner_agent.read().minerInfo(self.address)[self.miner_agent.MinerInfo.VALUE.value]
token_amount_bytes = self.miner_agent.contract.functions.getMinerInfo(self.miner_agent.MinerInfo.VALUE.value,
self.address, 0).call()
token_amount = self.blockchain._chain.web3.toInt(token_amount_bytes)
# reward_amount = TODO
reward_txhash = self.miner_agent.transact({'from': self.address}).withdraw(token_amount)
reward_txhash = self.miner_agent.contract.functions.withdraw(token_amount).transact({'from': self.address})
self.blockchain.wait_for_receipt(reward_txhash)
self._transactions.append((datetime.utcnow(), reward_txhash))
@ -172,7 +181,11 @@ class Miner(TokenActor):
raise self.StakingError('Locktime must be at least {}'.format(min_stake_time))
if entire_balance is True:
amount = self.miner_agent.read().minerInfo(self.address)[self.miner_agent.MinerInfo.VALUE.value]
balance_bytes = self.miner_agent.contract.functions.getMinerInfo(self.miner_agent.MinerInfo.VALUE.value,
self.address, 0).call()
amount = self.blockchain._chain.web3.toInt(balance_bytes)
else:
if not amount > 0:
raise self.StakingError('Staking amount must be greater than zero.')
@ -185,7 +198,7 @@ class Miner(TokenActor):
def publish_data(self, data) -> str:
"""Store new data"""
txhash = self.miner_agent.transact({'from': self.address}).setMinerId(data)
txhash = self.miner_agent.contract.functions.setMinerId(data).transact({'from': self.address})
self.blockchain.wait_for_receipt(txhash)
self._transactions.append((datetime.utcnow(), txhash))
@ -195,12 +208,17 @@ class Miner(TokenActor):
def fetch_data(self) -> tuple:
"""Retrieve all asosciated contract data for this miner."""
count = self.miner_agent.read().getMinerIdsLength(self.address)
count_bytes = self.miner_agent.contract.functions.getMinerInfo(self.miner_agent.MinerInfo.MINER_IDS_LENGTH.value,
self.address, 0).call()
count = self.blockchain._chain.web3.toInt(count_bytes)
miner_ids = list()
for index in range(count):
miner_id = self.miner_agent.read().getMinerId(self.address, index)
miner_ids.append(miner_id)
miner_id = self.miner_agent.contract.functions.getMinerInfo(self.miner_agent.MinerInfo.MINER_ID.value,
self.address, index).call()
encoded_miner_id = miner_id.encode('latin-1')
miner_ids.append(encoded_miner_id)
return tuple(miner_ids)

View File

@ -24,19 +24,23 @@ class EthereumContractAgent(ABC):
self.blockchain = blockchain
address = blockchain.provider.get_contract_address(contract_name=self._principal_contract_name)[-1] # TODO
self._contract = blockchain.provider.get_contract(address)
self.__contract = blockchain.provider.get_contract(address)
def __repr__(self):
class_name = self.__class__.__name__
r = "{}(blockchain={}, contract={})"
return r.format(class_name, self.blockchain, self._contract)
return r.format(class_name, self.blockchain, self.__contract)
def __eq__(self, other):
return bool(self.contract_address == other.contract_address)
@property
def contract(self):
return self.__contract
@property
def contract_address(self):
return self._contract.address
return self.__contract.address
@property
def contract_name(self) -> str:
@ -46,28 +50,11 @@ class EthereumContractAgent(ABC):
def origin(self) -> str:
return self.blockchain.provider.w3.eth.coinbase # TODO: make swappable
def read(self):
"""
Returns an object that exposes the contract instance functions.
This method is intended for use with method chaining,
results in zero state changes, and costs zero gas.
Useful as a dry-run before sending an actual transaction.
See more on interacting with contract instances in the Populus docs:
http://populus.readthedocs.io/en/latest/dev_cycle.part-07.html#call-an-instance-function
"""
return self._contract.call()
def transact(self, payload: dict):
"""Packs kwargs into payload dictionary and transmits an eth contract transaction"""
return self._contract.transact(payload)
def get_balance(self, address: str=None) -> int:
"""Get the balance of a token address, or of this contract address"""
if address is None:
address = self.contract_address
return self.read().balanceOf(address)
return self.contract.functions.balanceOf(address).call()
class NucypherTokenAgent(EthereumContractAgent):
@ -117,10 +104,16 @@ class MinerAgent(EthereumContractAgent):
Miner addresses will be returned in the order in which they were added to the MinersEscrow's ledger
"""
count = self.read().getMinersLength()
info_reader = partial(self.contract.functions.getMinerInfo,
self.MinerInfo.MINERS_LENGTH.value, self._deployer._null_addr
# ___,
)
count_bytes = info_reader(0).call()
count = self.blockchain.provider.w3.toInt(count_bytes)
for index in range(count):
yield self.read().miners(index)
addr = info_reader(index).call()
yield self.blockchain.provider.w3.toChecksumAddress(addr)
def sample(self, quantity: int=10, additional_ursulas: float=1.7, attempts: int=5, duration: int=10) -> List[str]:
"""
@ -147,7 +140,7 @@ class MinerAgent(EthereumContractAgent):
system_random = random.SystemRandom()
n_select = round(quantity*additional_ursulas) # Select more Ursulas
n_tokens = self.read().getAllLockedTokens()
n_tokens = self.contract.functions.getAllLockedTokens().call()
if not n_tokens > 0:
raise self.NotEnoughUrsulas('There are no locked tokens.')
@ -158,7 +151,7 @@ class MinerAgent(EthereumContractAgent):
addrs, addr, index, shift = set(), self._deployer._null_addr, 0, 0
for delta in deltas:
addr, index, shift = self.read().findCumSum(index, delta + shift, duration)
addr, index, shift = self.contract.functions.findCumSum(index, delta + shift, duration).call()
addrs.add(addr)
if len(addrs) >= quantity:
@ -173,7 +166,7 @@ class PolicyAgent(EthereumContractAgent):
_principal_contract_name = PolicyManagerDeployer._contract_name
def fetch_arrangement_data(self, arrangement_id: bytes) -> list:
blockchain_record = self.read().policies(arrangement_id)
blockchain_record = self.contract.functions.policies(arrangement_id).call()
return blockchain_record
def revoke_arrangement(self, arrangement_id: bytes, author, gas_price: int):
@ -181,6 +174,6 @@ class PolicyAgent(EthereumContractAgent):
Revoke by arrangement ID; Only the policy author can revoke the policy
"""
txhash = self.transact({'from': author.address, 'gas_price': gas_price}).revokePolicy(arrangement_id)
txhash = self.contract.functions.revokePolicy(arrangement_id).transact({'from': author.address, 'gas_price': gas_price})
self.blockchain.wait_for_receipt(txhash)
return txhash

View File

@ -257,7 +257,7 @@ class MinerEscrowDeployer(ContractDeployer, NuCypherMinerConfig):
the_escrow_contract = wrapped_escrow_contract
# 3 - Transfer tokens to the miner escrow #
reward_txhash = self.token_agent.transact(origin_args).transfer(the_escrow_contract.address, self.remaining_supply)
reward_txhash = self.token_agent.contract.functions.transfer(the_escrow_contract.address, self.remaining_supply).transact(origin_args)
_reward_receipt = self.blockchain.wait_for_receipt(reward_txhash)
# 4 - Initialize the Miner Escrow contract
@ -314,8 +314,8 @@ class PolicyManagerDeployer(ContractDeployer):
the_policy_manager_contract = wrapped_policy_manager_contract
# Configure the MinerEscrow by setting the PolicyManager
policy_setter_txhash = self.miner_agent.transact({'from': self.token_agent.origin}).\
setPolicyManager(the_policy_manager_contract.address)
policy_setter_txhash = self.miner_agent.contract.functions. \
setPolicyManager(the_policy_manager_contract.address).transact({'from': self.token_agent.origin})
self.blockchain.wait_for_receipt(policy_setter_txhash)

View File

@ -6,7 +6,9 @@ from nucypher.blockchain.eth.agents import MinerAgent
@pytest.mark.skip("Last 5 stubborn blockchain tests.")
def test_miner_locking_tokens(chain, mock_token_deployer, mock_miner_agent):
def test_miner_locking_tokens(chain, token_agent, mock_token_deployer, mock_miner_agent):
chain._token_airdrop(token_agent=token_agent, amount=10000)
miner = Miner(miner_agent=mock_miner_agent, address=chain.provider.w3.eth.accounts[1])
@ -14,16 +16,16 @@ def test_miner_locking_tokens(chain, mock_token_deployer, mock_miner_agent):
miner.stake(amount=an_amount_of_tokens, locktime=mock_miner_agent._deployer._min_locked_periods)
# Verify that the escrow is allowed to receive tokens
# assert mock_miner_agent.token_agent.read().allowance(miner.address, mock_miner_agent.contract_address) == 0
assert mock_miner_agent.token_agent.contract.functions.allowance(miner.address, mock_miner_agent.contract_address).call() == 0
# Stake starts after one period
# assert miner.token_balance() == 0
# assert mock_miner_agent.read().getLockedTokens(miner.address) == 0
assert miner.token_balance() == 0
assert mock_miner_agent.contract.functions.getLockedTokens(miner.address).call() == 0
# Wait for it...
chain.time_travel(mock_miner_agent._deployer._hours_per_period)
assert mock_miner_agent.read().getLockedTokens(miner.address) == an_amount_of_tokens
assert mock_miner_agent.contract.functions.getLockedTokens(miner.address).call() == an_amount_of_tokens
@pytest.mark.skip("Last 5 stubborn blockchain tests.")
@ -60,10 +62,10 @@ def test_mine_then_withdraw_tokens(chain, mock_token_deployer, token_agent, mock
assert miner.locked_tokens == half_of_stake
# Ensure the MinerEscrow contract is allowed to receive tokens form Alice
# assert miner.token_agent.read().allowance(miner.address, miner.miner_agent.contract_address) == half_of_stake
# assert miner.token_agent.contract.functions.allowance(miner.address, miner.miner_agent.contract_address).call() == half_of_stake
# Blockchain staking starts after one period
# assert mock_miner_agent.read().getAllLockedTokens() == 0
# assert mock_miner_agent.contract.functions.getAllLockedTokens().call() == 0
# Wait for it...
# chain.wait_time(2)
@ -139,6 +141,9 @@ def test_publish_miner_datastore(chain, mock_miner_agent):
assert len(stored_miner_ids) == 2
assert another_mock_miner_id == stored_miner_ids[1]
supposedly_the_same_miner_id = mock_miner_agent.read().getMinerId(miner_addr, 1)
supposedly_the_same_miner_id = mock_miner_agent.contract.functions \
.getMinerInfo(mock_miner_agent._deployer.MinerInfoField.MINER_ID.value,
miner_addr, 1).call()
assert another_mock_miner_id == supposedly_the_same_miner_id

View File

@ -31,7 +31,7 @@ def test_token_deployer_and_agent(chain):
assert len(token_agent.contract_address) == 42
# Check that the token contract has tokens
assert token_agent.read().totalSupply() != 0
assert token_agent.contract.functions.totalSupply().call() != 0
# assert token().totalSupply() == int(1e9) * _M # TODO
# Retrieve the token from the blockchain