Skip to content

Introduce Stakeholder#1064

Merged
KPrasch merged 13 commits intonucypher:sekanjabinfrom
KPrasch:stakeholder
Jul 16, 2019
Merged

Introduce Stakeholder#1064
KPrasch merged 13 commits intonucypher:sekanjabinfrom
KPrasch:stakeholder

Conversation

@KPrasch
Copy link
Member

@KPrasch KPrasch commented Jun 8, 2019

PR goes into epic. Introduces new Issue #1119

@KPrasch KPrasch changed the base branch from master to alhambra-verde June 9, 2019 19:26
@cygnusv cygnusv force-pushed the alhambra-verde branch 4 times, most recently from ffdb6f6 to e2a3ec9 Compare June 10, 2019 10:02
@cygnusv cygnusv force-pushed the alhambra-verde branch 2 times, most recently from 4eca13d to b1572a7 Compare June 11, 2019 08:05
@codecov
Copy link

codecov bot commented Jun 11, 2019

Codecov Report

Merging #1064 into sekanjabin will decrease coverage by 1.47%.
The diff coverage is 36.1%.

Impacted file tree graph

@@              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
Impacted Files Coverage Δ
nucypher/blockchain/eth/deployers.py 91.24% <100%> (ø) ⬆️
nucypher/crypto/powers.py 95.09% <100%> (+0.06%) ⬆️
nucypher/config/node.py 90.16% <100%> (+0.44%) ⬆️
nucypher/blockchain/eth/actors.py 58.63% <26.69%> (-22.43%) ⬇️
nucypher/blockchain/eth/clients.py 57.89% <41.17%> (-0.84%) ⬇️
nucypher/blockchain/eth/agents.py 89.85% <50%> (-2.25%) ⬇️
nucypher/blockchain/eth/interfaces.py 80.6% <73.68%> (-1.29%) ⬇️
nucypher/config/base.py 96.62% <75%> (-2.16%) ⬇️
nucypher/blockchain/eth/token.py 87.62% <87.5%> (+0.8%) ⬆️
nucypher/network/nodes.py 82.3% <0%> (+0.61%) ⬆️
... and 1 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 65f6fcd...34abab8. Read the comment docs.

@KPrasch KPrasch changed the title [Concept-Only] Stakeholder Stakeholder Jun 11, 2019
@KPrasch KPrasch added the API label Jun 13, 2019
@KPrasch KPrasch force-pushed the stakeholder branch 3 times, most recently from 3c0cf99 to 3a1d3cc Compare June 15, 2019 05:51
@cygnusv cygnusv force-pushed the alhambra-verde branch 2 times, most recently from 0dc5f37 to ddf1d08 Compare June 20, 2019 13:58
@KPrasch KPrasch changed the base branch from alhambra-verde to sekanjabin June 27, 2019 19:58
@KPrasch KPrasch changed the title Stakeholder Introduce Stakeholder Jun 27, 2019
KPrasch added a commit to KPrasch/nucypher that referenced this pull request Jul 15, 2019
self.__worker_address = worker_address
if self.__worker_address is BlockchainInterface.NULL_ADDRESS:
return NO_WORKER_ASSIGNED
return self.__worker_address
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two issues here:

  1. If there's no worker set and you call worker_address twice in a row, the first time you will obtain NO_WORKER_ASSIGNED, and the second time you will get NULL_ADDRESS.
  2. You're using is again to test string equality!

Copy link
Member Author

@KPrasch KPrasch Jul 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️ (right?)

# Agency
self.staking_agent = None
self.token_agent = NucypherTokenAgent()
self.token_agent = NucypherTokenAgent() # TODO: Use Agency
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this staker be added to self.__stakers too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also fixed in 68426a32076f60be232a5b50b458f5da56d06f31

Copy link
Contributor

@vepkenez vepkenez Jul 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh - Just generally prefer statelessness?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member Author

@KPrasch KPrasch Jul 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😍

self.funding_power.activate()

# Send new staker account ETH
tx = {'to': staker.checksum_address, 'from': self.funding_account, 'value': self.eth_funding}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it have to send a default amount of ETH?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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... 🤢 )

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweep is deprecated (for now) in 68426a32076f60be232a5b50b458f5da56d06f31

Copy link
Member

@cygnusv cygnusv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍
Just the minor details I described above.

blockchain.transacting_power.activate()
self.log = Logger("Deployment-Actor")

# TODO: Does this class want to be a Character implementing PowerUp consumption?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would would it mean to make this change? Moving this class into either chaotic.py or lawful.py (and deciding which one)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be a constant/cli arg/envvar right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well that's all the more reason to make it modifiable without editing the code eh?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. I want to bring this forth for discussion before implementing it. If there's not already an open Issue, lets make one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loosely Related to #842

return payload

@classmethod
def from_configuration_file(cls, filepath: str = None, **overrides) -> 'StakeHolder':
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we already have this method in node.py don't we? I guess this is how we know if it should be a Character?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

self.__accounts.extend(accounts)

def set_funding_account(self, address: str):
self.__funding_account = address
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these getters and setters are a little... why have them instead of
self.funding_account = <whatever>

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()
Copy link
Contributor

@vepkenez vepkenez Jul 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't we already activate this in the __init__?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:
Copy link
Contributor

@vepkenez vepkenez Jul 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Member Author

@KPrasch KPrasch Jul 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is an "mcs"?

Copy link
Member Author

@KPrasch KPrasch Jul 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any tests for a lot of the new methods in this file

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which new methods? I do see one: new_account.

Copy link
Contributor

@vepkenez vepkenez Jul 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

@vepkenez vepkenez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I say merge it after a test for new_account.

@KPrasch KPrasch requested a review from vepkenez July 16, 2019 23:46
@KPrasch KPrasch merged commit 04122fe into nucypher:sekanjabin Jul 16, 2019
KPrasch added a commit to KPrasch/nucypher that referenced this pull request Jul 25, 2019
@KPrasch KPrasch deleted the stakeholder branch March 22, 2021 18:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants