Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fullnode related changes from wallet protocol and unharded derivation #8522

Merged
merged 6 commits into from
Sep 18, 2021

Conversation

Yostra
Copy link
Contributor

@Yostra Yostra commented Sep 18, 2021

No description provided.

Copy link
Contributor

@wjblanke wjblanke left a comment

Choose a reason for hiding this comment

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

aok

@wjblanke wjblanke merged commit 5766a8d into main Sep 18, 2021
@@ -312,16 +317,18 @@ async def _reconsider_peak(
tx_removals, tx_additions = [], []
if block.is_transaction_block():
assert block.foliage_transaction_block is not None
await self.coin_store.new_block(
added, _ = await self.coin_store.new_block(
Copy link
Contributor

Choose a reason for hiding this comment

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

@Yostra This looks like a bug. new_block() returns (removed, added). here you're binding the removed records to a variable called added.

There need to be a test that exercises this and fail as this code is written now. Otherwise we should remove this code to not return these lists at all (since clearly they aren't being used)

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree. Changing the blockchain logic is pretty risky as well, and this can be done externally. We can read the coinstore from outside of this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Those lists are used only to notify wallet of the changes, in this case (that was broken) change happens when we add a genesis block and in practice this wouldn't happen as no wallet can connect before blockchain is initialized. I'll fix a typo.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think, instead of returning added and remived CoinRecords, we should just return "changed coin IDs". and anything the wallet is subscribed to, we can lookup via the CoinStore. Presumably most coins are not subscribed to, and it would be a net improvement. That way, we also wouldn't need new_block() to return anything, since we already have the coin IDs added and removed at the call site.

Copy link
Contributor

Choose a reason for hiding this comment

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

(I'd be happy to propose that patch when I'm done with what I'm working on right now)

"""
When a new block is added, this is called, to check if the new block is the new peak of the chain.
This also handles reorgs by reverting blocks which are not in the heaviest chain.
It returns the height of the fork between the previous chain and the new chain, or returns
None if there was no update to the heaviest chain.
"""
peak = self.get_peak()
lastest_coin_state: Dict[bytes32, CoinRecord] = {}
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 probably be renamed to last_coin_state.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

that was supposed to be latest but last works just as well.

@@ -87,7 +87,7 @@ async def check_spend_bundle_validity(
)
newest_block = additional_blocks[-1]

received_block_result, err, fork_height = await blockchain.receive_block(newest_block)
received_block_result, err, fork_height, coin_changes = await blockchain.receive_block(newest_block)
Copy link
Contributor

Choose a reason for hiding this comment

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

this test doesn't seem to verify that coin_changes is correct

Copy link
Contributor Author

Choose a reason for hiding this comment

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

receive_block() is called from many places in the tests. I didn't add verification to those, but had to add , coin_changes or it would fail to unpack the return value.

@@ -825,13 +858,41 @@ def get_peers_with_peak(self, peak_hash: bytes32) -> List:
peers_with_peak: List = [c for c in self.server.all_connections.values() if c.peer_node_id in peer_ids]
return peers_with_peak

async def update_wallets(
self, height: uint32, fork_height: uint32, peak_hash: bytes32, state_update: List[CoinRecord]
Copy link
Contributor

Choose a reason for hiding this comment

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

I really think state_update should be a list of coin names, rather than full CoinRecords. Right now we're doing database lookups for every coin being removed (twice, once to validate it and then once in CoinStore) just to be able to pass it in here. In this function we really only care about a few coins (most likely a small subset). If we would defer looking up the coin records, we would presumably save a lot of database I/O.

Copy link
Contributor

Choose a reason for hiding this comment

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

an even more efficient way of doing it would be to have the protocol indicate coins as being spent (and maybe at what height) and just pass on the coin IDs to the wallet. Presumably the wallet will be able to look up its coins by ID and set the spent-bit itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think I added second lookup, it was there when _set_spent is called, I just returned the already looked up value.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But I agree that can probably be optimized so there is only one lookup

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the object that wallet receives

@streamable
class CoinState(Streamable):
    coin: Coin
    spent_height: Optional[uint32]
    created_height: Optional[uint32]

Copy link
Contributor

Choose a reason for hiding this comment

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

I have a PR where I'm eliminating that lookup, that's why I'm pointing it out. I think it would make sense to make this lookup only for the coins there are subscriptions on.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Subscriptions can be for both puzzle_hash and coin_id. If we return just a coin id from this we'll miss spend updates for puzzle_hashes.

@@ -65,7 +65,7 @@ def shutdown(self):
return
self.queue.put(("shutdown",))
log.info("UPnP, shutting down thread")
self.thread.join()
self.thread.join(5)
Copy link
Contributor

Choose a reason for hiding this comment

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

this seems like an unrelated change. Is there a good reason to do this? I would expect a comment in the code to explain why we need to detach the thread after 5 seconds

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, it's unrelated. I noticed on shutdown node was forever hanging on thread.join() there.

Copy link
Contributor

Choose a reason for hiding this comment

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

we should probably fix the issue causing the UPnP thread to hang instead. This will just hide it

@@ -24,3 +26,13 @@ class CoinRecord(Streamable):
@property
def name(self) -> bytes32:
return self.coin.name()

@property
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's risky to make this a property. It makes it hard to tell that this call has a cost. Since it's a new member function, there shouldn't be any existing code that requires this to be a property.

archiviz pushed a commit to Tranzact-Network/tranzact-blockchain that referenced this pull request Oct 6, 2021
…Chia-Network#8522)

* fullnode related changes from wallet protocol and unharded derivation

* limit total subscriptions per peer

* reset counter on disconnect

* dict not a set

* check membership

* remove unused tests, lint
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