Conversation
ffdb6f6 to
e2a3ec9
Compare
4eca13d to
b1572a7
Compare
Codecov Report
@@ Coverage Diff @@
## sekanjabin #1064 +/- ##
=============================================
- Coverage 83.08% 81.6% -1.48%
=============================================
Files 70 70
Lines 9252 9521 +269
=============================================
+ Hits 7687 7770 +83
- Misses 1565 1751 +186
Continue to review full report at Codecov.
|
3c0cf99 to
3a1d3cc
Compare
0dc5f37 to
ddf1d08
Compare
…, Geth tranction formatting and signging.
…e same address multiple times.
…hwill's funding account recipt fix; Touch Ups.
nucypher/blockchain/eth/actors.py
Outdated
| self.__worker_address = worker_address | ||
| if self.__worker_address is BlockchainInterface.NULL_ADDRESS: | ||
| return NO_WORKER_ASSIGNED | ||
| return self.__worker_address |
There was a problem hiding this comment.
Two issues here:
- If there's no worker set and you call
worker_addresstwice in a row, the first time you will obtainNO_WORKER_ASSIGNED, and the second time you will getNULL_ADDRESS. - You're using
isagain to test string equality!
| # Agency | ||
| self.staking_agent = None | ||
| self.token_agent = NucypherTokenAgent() | ||
| self.token_agent = NucypherTokenAgent() # TODO: Use Agency |
There was a problem hiding this comment.
Initially, I was OK with simply using the constructor of each agent, but I now agree with you about that API can be misleading, and I think centralizing everything in the Agency is good.
| stakes = list(self.staking_agent.get_all_stakes(staker_address=account)) | ||
| if stakes: | ||
| staker = Staker(is_me=True, checksum_address=account, blockchain=self.blockchain) | ||
| self.__stakers[staker] = staker.stakes |
There was a problem hiding this comment.
You're using Staker as dict key here, but as far as I can tell, this class is not Hashable. Can this be problematic somehow?
There was a problem hiding this comment.
Yes. Fixed here: 68426a32076f60be232a5b50b458f5da56d06f31
| raise self.ConfigurationError("No password supplied to create new staking account.") | ||
| new_account = self.blockchain.client.new_account(password=password) | ||
| self.__accounts.append(new_account) | ||
| staker = Staker(blockchain=self.blockchain, is_me=True, checksum_address=new_account) |
There was a problem hiding this comment.
Shouldn't this staker be added to self.__stakers too?
There was a problem hiding this comment.
Also fixed in 68426a32076f60be232a5b50b458f5da56d06f31
There was a problem hiding this comment.
to me, these little fixes are a reason not to do the whole
@property
def thing(self)
def set_thing(self, thing)
self.__thing = thing
def get_thing(self, thing)
return self.__thing
just another bug to have
There was a problem hiding this comment.
Heh - Just generally prefer statelessness?
There was a problem hiding this comment.
python objects already have a thing called __dict__ and methods called setattr and getattr that help manage state.
self.thing = new_value
print (self.thing)
that is state enough no?
I guess if we want to do some kind of validation on the attribute being set it makes sense, but we're not really.
Also, there is nothing stopping a developer from directly modifying these attributes anyway.
There was a problem hiding this comment.
Sometimes I want to perform some logic during object.attr = value. In python this can be implemented with the @property.getter and @property.setter methods, but I typically prefer to use named methods for this sort of operation. object.setter(value)
In any case, private attributes with public interfaces make it much harder to "shoot yourself in the foot" if executed well.
Another Item I want to check here is that the user does indeed have control of the new funding account. It's a bit awkward to set it outside of the configuration API.
| federated_only=False, | ||
| provider_uri=self.blockchain.provider_uri, | ||
| **configuration) | ||
| return worker_configuration |
| self.funding_power.activate() | ||
|
|
||
| # Send new staker account ETH | ||
| tx = {'to': staker.checksum_address, 'from': self.funding_account, 'value': self.eth_funding} |
There was a problem hiding this comment.
Why does it have to send a default amount of ETH?
There was a problem hiding this comment.
The new staker account needs to perform transactions, and pay for gas.
| receipts = dict() | ||
| for staker in self.stakers: | ||
| self.attach_transacting_power(checksum_address=staker.checksum_address) | ||
| receipt = self.collect_rewards(staker_address=staker.checksum_address) |
There was a problem hiding this comment.
No password is passed to self.attach_transacting_power() and to self.collect_rewards(), which means this assumes that the transacting power is already cached with a password; otherwise, this won't work. Perhaps you can pass a list of passwords as input to sweep()? (I know I know.....yuck... 🤢 )
There was a problem hiding this comment.
Sweep is deprecated (for now) in 68426a32076f60be232a5b50b458f5da56d06f31
cygnusv
left a comment
There was a problem hiding this comment.
👍
Just the minor details I described above.
…hwill's funding account recipt fix; Touch Ups.
| blockchain.transacting_power.activate() | ||
| self.log = Logger("Deployment-Actor") | ||
|
|
||
| # TODO: Does this class want to be a Character implementing PowerUp consumption? |
There was a problem hiding this comment.
Would would it mean to make this change? Moving this class into either chaotic.py or lawful.py (and deciding which one)?
There was a problem hiding this comment.
Yeah - I think into lawful.py - However, I'm not fully convinced.
| self.log = Logger(f"stakeholder") | ||
| self.blockchain = blockchain | ||
| self.offline_mode = offline_mode | ||
| self.eth_funding = Web3.toWei('0.1', 'ether') # TODO: How much ETH to use while funding new stakes. |
There was a problem hiding this comment.
this should be a constant/cli arg/envvar right?
There was a problem hiding this comment.
Yes to all. Not sure what makes sense as a default. Perhaps an amount based on the duration or another way to estimate future gas consumption?
There was a problem hiding this comment.
well that's all the more reason to make it modifiable without editing the code eh?
There was a problem hiding this comment.
Indeed. I want to bring this forth for discussion before implementing it. If there's not already an open Issue, lets make one.
| return payload | ||
|
|
||
| @classmethod | ||
| def from_configuration_file(cls, filepath: str = None, **overrides) -> 'StakeHolder': |
There was a problem hiding this comment.
we already have this method in node.py don't we? I guess this is how we know if it should be a Character?
There was a problem hiding this comment.
It's one way for sure, but not the most defining characteristic (no pun indented) is the usage of TransactingPower in the StakeHolder.__init__, and the analogous layers to that of Worker -> Ursula & Staker -> StakeHolder
| instance = cls(filepath=filepath, **payload) | ||
| return instance | ||
|
|
||
| @validate_checksum_address |
| self.__accounts.extend(accounts) | ||
|
|
||
| def set_funding_account(self, address: str): | ||
| self.__funding_account = address |
There was a problem hiding this comment.
these getters and setters are a little... why have them instead of
self.funding_account = <whatever>
There was a problem hiding this comment.
I want to perform checksum address validation on setter here.
| raise self.ConfigurationError(f"{self.funding_account} has no ETH") | ||
|
|
||
| # Transfer NU | ||
| self.funding_power.activate() |
There was a problem hiding this comment.
didn't we already activate this in the __init__?
There was a problem hiding this comment.
Yes, however, its possible through normal usage to switch TransactingPowers with one belonging to another account, like a Staker. We might be able to remove this with some careful inspection, but this is ensuing that we indeed have the funding power attached.
|
|
||
| @validate_checksum_address | ||
| def attach_transacting_power(self, checksum_address: str, password: str = None) -> None: | ||
| try: |
There was a problem hiding this comment.
I feel like the right way to do this is more like
if checksum_addresss in self.__transacting_powers: # or self.__transacting_powers.get(checksum_address)
transacting_power = self.__transacting_powers[checksum_address]
else:
transacting_power = TransactingPower(blockchain=self.blockchain, account=checksum_address)
self.__transacting_powers[checksum_address] = transacting_power
transacting_power.activate(password=password)
I guess it's an ongoing debate...
https://softwareengineering.stackexchange.com/questions/351110/are-exceptions-for-flow-control-best-practice-in-python
There was a problem hiding this comment.
Heh - Yeah I even say that I prefer the exception handling as flow control, from the old idiom "Don't look before you leap" / "Better to ask for forgiveness than permission"
|
|
||
| @classmethod | ||
| def get_agent(mcs, cls): | ||
| try: |
There was a problem hiding this comment.
"metaclass". it's a conventional first-arg name for a meta-classmethod.
| @@ -197,20 +197,20 @@ def coinbase(self) -> str: | |||
| return self.w3.eth.coinbase | |||
There was a problem hiding this comment.
I don't see any tests for a lot of the new methods in this file
There was a problem hiding this comment.
Which new methods? I do see one: new_account.
There was a problem hiding this comment.
Yes that one, but I am mode thinking that some of these will fail to work in some of the various clients which will necessitate another fix-ganache-issues branch.
vepkenez
left a comment
There was a problem hiding this comment.
I say merge it after a test for new_account.
…hwill's funding account recipt fix; Touch Ups.
PR goes into epic. Introduces new Issue #1119