moved process_block to planetmint

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
This commit is contained in:
Lorenz Herzberger 2022-09-29 18:38:46 +02:00
parent 11083c725f
commit 137025147f
No known key found for this signature in database
GPG Key ID: FA5EE906EB55316A
7 changed files with 90 additions and 95 deletions

View File

@ -209,7 +209,7 @@ class App(BaseApplication):
else:
self.block_txn_hash = block["app_hash"]
validator_update = Election.process_block(self.planetmint_node, self.new_height, self.block_transactions)
validator_update = self.planetmint_node.process_block(self.new_height, self.block_transactions)
return ResponseEndBlock(validator_updates=validator_update)

View File

@ -9,7 +9,7 @@ MongoDB.
"""
import logging
import json
from collections import namedtuple
from collections import namedtuple, OrderedDict
from uuid import uuid4
import rapidjson
@ -812,4 +812,71 @@ class Planetmint(object):
txns = list(backend.query.get_asset_tokens_for_public_key(self.connection, transaction.id, election_pk))
return self.count_votes(election_pk, txns, dict.get)
def _get_initiated_elections(self, height, txns): # TODO: move somewhere else
elections = []
for tx in txns:
if not isinstance(tx, Election):
continue
elections.append({"election_id": tx.id, "height": height, "is_concluded": False})
return elections
def _get_votes(self, txns): # TODO: move somewhere else
elections = OrderedDict()
for tx in txns:
if not isinstance(tx, Vote):
continue
election_id = tx.asset["id"]
if election_id not in elections:
elections[election_id] = []
elections[election_id].append(tx)
return elections
def process_block(self, new_height, txns): # TODO: move somewhere else
"""Looks for election and vote transactions inside the block, records
and processes elections.
Every election is recorded in the database.
Every vote has a chance to conclude the corresponding election. When
an election is concluded, the corresponding database record is
marked as such.
Elections and votes are processed in the order in which they
appear in the block. Elections are concluded in the order of
appearance of their first votes in the block.
For every election concluded in the block, calls its `on_approval`
method. The returned value of the last `on_approval`, if any,
is a validator set update to be applied in one of the following blocks.
`on_approval` methods are implemented by elections of particular type.
The method may contain side effects but should be idempotent. To account
for other concluded elections, if it requires so, the method should
rely on the database state.
"""
# elections initiated in this block
initiated_elections = self._get_initiated_elections(new_height, txns)
if initiated_elections:
self.store_elections(initiated_elections)
# elections voted for in this block and their votes
elections = self._get_votes(txns)
validator_update = None
for election_id, votes in elections.items():
election = self.get_transaction(election_id)
if election is None:
continue
if not election.has_concluded(self, votes):
continue
validator_update = election.on_approval(self, new_height)
self.store_election(election.id, new_height, is_concluded=True)
return [validator_update] if validator_update else []
Block = namedtuple("Block", ("app_hash", "height", "transactions"))

View File

@ -4,11 +4,9 @@
# Code is Apache-2.0 and docs are CC-BY-4.0
from collections import OrderedDict
import base58
from uuid import uuid4
from typing import Optional
from planetmint import backend
from planetmint.transactions.types.elections.vote import Vote
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.schema import _validate_schema, TX_SCHEMA_COMMON
@ -95,76 +93,6 @@ class Election(Transaction):
return False
@classmethod
def _get_initiated_elections(cls, height, txns): # TODO: move somewhere else
elections = []
for tx in txns:
if not isinstance(tx, Election):
continue
elections.append({"election_id": tx.id, "height": height, "is_concluded": False})
return elections
@classmethod
def _get_votes(cls, txns): # TODO: move somewhere else
elections = OrderedDict()
for tx in txns:
if not isinstance(tx, Vote):
continue
election_id = tx.asset["id"]
if election_id not in elections:
elections[election_id] = []
elections[election_id].append(tx)
return elections
@classmethod
def process_block(cls, planet, new_height, txns): # TODO: move somewhere else
"""Looks for election and vote transactions inside the block, records
and processes elections.
Every election is recorded in the database.
Every vote has a chance to conclude the corresponding election. When
an election is concluded, the corresponding database record is
marked as such.
Elections and votes are processed in the order in which they
appear in the block. Elections are concluded in the order of
appearance of their first votes in the block.
For every election concluded in the block, calls its `on_approval`
method. The returned value of the last `on_approval`, if any,
is a validator set update to be applied in one of the following blocks.
`on_approval` methods are implemented by elections of particular type.
The method may contain side effects but should be idempotent. To account
for other concluded elections, if it requires so, the method should
rely on the database state.
"""
# elections initiated in this block
initiated_elections = cls._get_initiated_elections(new_height, txns)
if initiated_elections:
planet.store_elections(initiated_elections)
# elections voted for in this block and their votes
elections = cls._get_votes(txns)
validator_update = None
for election_id, votes in elections.items():
election = planet.get_transaction(election_id)
if election is None:
continue
if not election.has_concluded(planet, votes):
continue
validator_update = election.on_approval(planet, new_height)
planet.store_election(election.id, new_height, is_concluded=True)
return [validator_update] if validator_update else []
@classmethod
def rollback(cls, planet, new_height, txn_ids): # TODO: move somewhere else
"""Looks for election and vote transactions inside the block and
@ -179,7 +107,7 @@ class Election(Transaction):
txns = [planet.get_transaction(tx_id) for tx_id in txn_ids]
elections = cls._get_votes(txns)
elections = planet._get_votes(txns)
for election_id in elections:
election = planet.get_transaction(election_id)
election.on_rollback(planet, new_height)

View File

@ -524,7 +524,7 @@ def test_chain_migration_election_show_shows_inconclusive(b):
assert not run_election_show(Namespace(election_id=election.id), b)
Election.process_block(b, 1, [election])
b.process_block(1, [election])
b.store_bulk_transactions([election])
assert run_election_show(Namespace(election_id=election.id), b) == "status=ongoing"
@ -554,13 +554,13 @@ def test_chain_migration_election_show_shows_concluded(b):
assert not run_election_show(Namespace(election_id=election.id), b)
b.store_bulk_transactions([election])
Election.process_block(b, 1, [election])
b.process_block(1, [election])
assert run_election_show(Namespace(election_id=election.id), b) == "status=ongoing"
b.store_abci_chain(1, "chain-X")
b.store_block(Block(height=1, transactions=[v.id for v in votes], app_hash="last_app_hash")._asdict())
Election.process_block(b, 2, votes)
b.process_block(2, votes)
assert (
run_election_show(Namespace(election_id=election.id), b)

View File

@ -31,11 +31,11 @@ def test_process_block_concludes_all_elections(b):
total_votes += votes
b.store_abci_chain(1, "chain-X")
Election.process_block(b, 1, txs)
b.process_block(1, txs)
b.store_block(Block(height=1, transactions=[tx.id for tx in txs], app_hash="")._asdict())
b.store_bulk_transactions(txs)
Election.process_block(b, 2, total_votes)
b.process_block(2, total_votes)
validators = b.get_validators()
assert len(validators) == 5
@ -78,11 +78,11 @@ def test_process_block_approves_only_one_validator_update(b):
txs += [election]
total_votes += votes
Election.process_block(b, 1, txs)
b.process_block(1, txs)
b.store_block(Block(height=1, transactions=[tx.id for tx in txs], app_hash="")._asdict())
b.store_bulk_transactions(txs)
Election.process_block(b, 2, total_votes)
b.process_block(2, total_votes)
validators = b.get_validators()
assert len(validators) == 5
@ -124,11 +124,11 @@ def test_process_block_approves_after_pending_validator_update(b):
total_votes += votes
b.store_abci_chain(1, "chain-X")
Election.process_block(b, 1, txs)
b.process_block(1, txs)
b.store_block(Block(height=1, transactions=[tx.id for tx in txs], app_hash="")._asdict())
b.store_bulk_transactions(txs)
Election.process_block(b, 2, total_votes)
b.process_block(2, total_votes)
validators = b.get_validators()
assert len(validators) == 5
@ -160,19 +160,19 @@ def test_process_block_does_not_approve_after_validator_update(b):
total_votes = votes
b.store_block(Block(height=1, transactions=[tx.id for tx in txs], app_hash="")._asdict())
Election.process_block(b, 1, txs)
b.process_block(1, txs)
b.store_bulk_transactions(txs)
second_election, second_votes = generate_election(
b, ChainMigrationElection, public_key, private_key, {}, voter_keys
)
Election.process_block(b, 2, total_votes + [second_election])
b.process_block(2, total_votes + [second_election])
b.store_block(Block(height=2, transactions=[v.id for v in total_votes + [second_election]], app_hash="")._asdict())
b.store_abci_chain(1, "chain-X")
Election.process_block(b, 3, second_votes)
b.process_block(3, second_votes)
assert not b.get_election(second_election.id)["is_concluded"]
assert b.get_latest_abci_chain() == {"height": 1, "chain_id": "chain-X", "is_synced": True}
@ -197,11 +197,11 @@ def test_process_block_applies_only_one_migration(b):
total_votes += votes
b.store_abci_chain(1, "chain-X")
Election.process_block(b, 1, txs)
b.process_block(1, txs)
b.store_block(Block(height=1, transactions=[tx.id for tx in txs], app_hash="")._asdict())
b.store_bulk_transactions(txs)
Election.process_block(b, 1, total_votes)
b.process_block(1, total_votes)
chain = b.get_latest_abci_chain()
assert chain
assert chain == {
@ -215,4 +215,4 @@ def test_process_block_applies_only_one_migration(b):
def test_process_block_gracefully_handles_empty_block(b):
Election.process_block(b, 1, [])
b.process_block(1, [])

View File

@ -341,7 +341,7 @@ def test_end_block_return_validator_updates(b, init_chain_request):
)
b.store_block(Block(height=1, transactions=[election.id], app_hash="")._asdict())
b.store_bulk_transactions([election])
Election.process_block(b, 1, [election])
b.process_block(1, [election])
app.block_transactions = votes

View File

@ -288,10 +288,10 @@ def test_get_validator_update(b, node_keys, node_key, ed25519_node_keys):
assert not election.has_concluded(b, [tx_vote0, tx_vote1])
assert election.has_concluded(b, [tx_vote0, tx_vote1, tx_vote2])
assert Election.process_block(b, 4, [tx_vote0]) == []
assert Election.process_block(b, 4, [tx_vote0, tx_vote1]) == []
assert b.process_block(4, [tx_vote0]) == []
assert b.process_block(4, [tx_vote0, tx_vote1]) == []
update = Election.process_block(b, 4, [tx_vote0, tx_vote1, tx_vote2])
update = b.process_block(4, [tx_vote0, tx_vote1, tx_vote2])
assert len(update) == 1
update_public_key = codecs.encode(update[0].pub_key.ed25519, "base64").decode().rstrip("\n")
assert update_public_key == public_key64
@ -314,7 +314,7 @@ def test_get_validator_update(b, node_keys, node_key, ed25519_node_keys):
b.store_bulk_transactions([tx_vote0, tx_vote1])
update = Election.process_block(b, 9, [tx_vote2])
update = b.process_block(9, [tx_vote2])
assert len(update) == 1
update_public_key = codecs.encode(update[0].pub_key.ed25519, "base64").decode().rstrip("\n")
assert update_public_key == public_key64