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

View File

@ -24,19 +24,23 @@ class EthereumContractAgent(ABC):
self.blockchain = blockchain self.blockchain = blockchain
address = blockchain.provider.get_contract_address(contract_name=self._principal_contract_name)[-1] # TODO 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): def __repr__(self):
class_name = self.__class__.__name__ class_name = self.__class__.__name__
r = "{}(blockchain={}, contract={})" 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): def __eq__(self, other):
return bool(self.contract_address == other.contract_address) return bool(self.contract_address == other.contract_address)
@property
def contract(self):
return self.__contract
@property @property
def contract_address(self): def contract_address(self):
return self._contract.address return self.__contract.address
@property @property
def contract_name(self) -> str: def contract_name(self) -> str:
@ -46,28 +50,11 @@ class EthereumContractAgent(ABC):
def origin(self) -> str: def origin(self) -> str:
return self.blockchain.provider.w3.eth.coinbase # TODO: make swappable 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: def get_balance(self, address: str=None) -> int:
"""Get the balance of a token address, or of this contract address""" """Get the balance of a token address, or of this contract address"""
if address is None: if address is None:
address = self.contract_address address = self.contract_address
return self.read().balanceOf(address) return self.contract.functions.balanceOf(address).call()
class NucypherTokenAgent(EthereumContractAgent): 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 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): 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]: 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() system_random = random.SystemRandom()
n_select = round(quantity*additional_ursulas) # Select more Ursulas 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: if not n_tokens > 0:
raise self.NotEnoughUrsulas('There are no locked tokens.') 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 addrs, addr, index, shift = set(), self._deployer._null_addr, 0, 0
for delta in deltas: 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) addrs.add(addr)
if len(addrs) >= quantity: if len(addrs) >= quantity:
@ -173,7 +166,7 @@ class PolicyAgent(EthereumContractAgent):
_principal_contract_name = PolicyManagerDeployer._contract_name _principal_contract_name = PolicyManagerDeployer._contract_name
def fetch_arrangement_data(self, arrangement_id: bytes) -> list: 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 return blockchain_record
def revoke_arrangement(self, arrangement_id: bytes, author, gas_price: int): 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 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) self.blockchain.wait_for_receipt(txhash)
return txhash return txhash

View File

@ -257,7 +257,7 @@ class MinerEscrowDeployer(ContractDeployer, NuCypherMinerConfig):
the_escrow_contract = wrapped_escrow_contract the_escrow_contract = wrapped_escrow_contract
# 3 - Transfer tokens to the miner escrow # # 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) _reward_receipt = self.blockchain.wait_for_receipt(reward_txhash)
# 4 - Initialize the Miner Escrow contract # 4 - Initialize the Miner Escrow contract
@ -314,8 +314,8 @@ class PolicyManagerDeployer(ContractDeployer):
the_policy_manager_contract = wrapped_policy_manager_contract the_policy_manager_contract = wrapped_policy_manager_contract
# Configure the MinerEscrow by setting the PolicyManager # Configure the MinerEscrow by setting the PolicyManager
policy_setter_txhash = self.miner_agent.transact({'from': self.token_agent.origin}).\ policy_setter_txhash = self.miner_agent.contract.functions. \
setPolicyManager(the_policy_manager_contract.address) setPolicyManager(the_policy_manager_contract.address).transact({'from': self.token_agent.origin})
self.blockchain.wait_for_receipt(policy_setter_txhash) 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.") @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]) 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) miner.stake(amount=an_amount_of_tokens, locktime=mock_miner_agent._deployer._min_locked_periods)
# Verify that the escrow is allowed to receive tokens # 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 # Stake starts after one period
# assert miner.token_balance() == 0 assert miner.token_balance() == 0
# assert mock_miner_agent.read().getLockedTokens(miner.address) == 0 assert mock_miner_agent.contract.functions.getLockedTokens(miner.address).call() == 0
# Wait for it... # Wait for it...
chain.time_travel(mock_miner_agent._deployer._hours_per_period) 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.") @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 assert miner.locked_tokens == half_of_stake
# Ensure the MinerEscrow contract is allowed to receive tokens form Alice # 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 # 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... # Wait for it...
# chain.wait_time(2) # chain.wait_time(2)
@ -139,6 +141,9 @@ def test_publish_miner_datastore(chain, mock_miner_agent):
assert len(stored_miner_ids) == 2 assert len(stored_miner_ids) == 2
assert another_mock_miner_id == stored_miner_ids[1] 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 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 assert len(token_agent.contract_address) == 42
# Check that the token contract has tokens # 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 # assert token().totalSupply() == int(1e9) * _M # TODO
# Retrieve the token from the blockchain # Retrieve the token from the blockchain