mirror of
https://github.com/planetmint/planetmint.git
synced 2025-11-24 22:45:44 +00:00
moved election validation to planetmint
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
This commit is contained in:
parent
9ba1423a16
commit
e0d09198d9
@ -121,7 +121,7 @@ def create_new_election(sk, planet, election_class, data):
|
|||||||
key = load_node_key(sk)
|
key = load_node_key(sk)
|
||||||
voters = election_class.recipients(planet)
|
voters = election_class.recipients(planet)
|
||||||
election = election_class.generate([key.public_key], voters, data, None).sign([key.private_key])
|
election = election_class.generate([key.public_key], voters, data, None).sign([key.private_key])
|
||||||
election.validate(planet)
|
planet.validate_election(election)
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
return False
|
return False
|
||||||
|
|||||||
@ -20,14 +20,29 @@ import requests
|
|||||||
import planetmint
|
import planetmint
|
||||||
from planetmint.config import Config
|
from planetmint.config import Config
|
||||||
from planetmint import backend, config_utils, fastquery
|
from planetmint import backend, config_utils, fastquery
|
||||||
from planetmint.transactions.common.transaction import Transaction
|
from planetmint.transactions.common.transaction import VALIDATOR_ELECTION, Transaction
|
||||||
from planetmint.transactions.common.exceptions import DuplicateTransaction, InvalidSignature, SchemaValidationError, ValidationError, DoubleSpend, AmountError, InputDoesNotExist, AssetIdMismatch
|
from planetmint.transactions.common.exceptions import (
|
||||||
|
DuplicateTransaction,
|
||||||
|
InvalidSignature,
|
||||||
|
SchemaValidationError,
|
||||||
|
ValidationError,
|
||||||
|
DoubleSpend,
|
||||||
|
AmountError,
|
||||||
|
InputDoesNotExist,
|
||||||
|
AssetIdMismatch,
|
||||||
|
InvalidProposer,
|
||||||
|
UnequalValidatorSet,
|
||||||
|
DuplicateTransaction,
|
||||||
|
MultipleInputsError,
|
||||||
|
InvalidPowerChange
|
||||||
|
)
|
||||||
|
from planetmint.transactions.common.crypto import public_key_from_ed25519_key
|
||||||
from planetmint.transactions.common.transaction_mode_types import (
|
from planetmint.transactions.common.transaction_mode_types import (
|
||||||
BROADCAST_TX_COMMIT,
|
BROADCAST_TX_COMMIT,
|
||||||
BROADCAST_TX_ASYNC,
|
BROADCAST_TX_ASYNC,
|
||||||
BROADCAST_TX_SYNC,
|
BROADCAST_TX_SYNC,
|
||||||
)
|
)
|
||||||
from planetmint.tendermint_utils import encode_transaction, merkleroot
|
from planetmint.tendermint_utils import encode_transaction, merkleroot, key_from_base64
|
||||||
from planetmint import exceptions as core_exceptions
|
from planetmint import exceptions as core_exceptions
|
||||||
from planetmint.validation import BaseValidationRules
|
from planetmint.validation import BaseValidationRules
|
||||||
|
|
||||||
@ -600,6 +615,79 @@ class Planetmint(object):
|
|||||||
tx = list(tx_map.values())[0]
|
tx = list(tx_map.values())[0]
|
||||||
return Transaction.from_dict(tx)
|
return Transaction.from_dict(tx)
|
||||||
|
|
||||||
|
# NOTE: moved here from Election needs to be placed somewhere else
|
||||||
|
def get_validators_dict(self, height=None):
|
||||||
|
"""Return a dictionary of validators with key as `public_key` and
|
||||||
|
value as the `voting_power`
|
||||||
|
"""
|
||||||
|
validators = {}
|
||||||
|
for validator in self.get_validators(height):
|
||||||
|
# NOTE: we assume that Tendermint encodes public key in base64
|
||||||
|
public_key = public_key_from_ed25519_key(key_from_base64(validator["public_key"]["value"]))
|
||||||
|
validators[public_key] = validator["voting_power"]
|
||||||
|
|
||||||
|
return validators
|
||||||
|
|
||||||
|
def validate_election(self, transaction, current_transactions=[]): # TODO: move somewhere else
|
||||||
|
"""Validate election transaction
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
* A valid election is initiated by an existing validator.
|
||||||
|
|
||||||
|
* A valid election is one where voters are validators and votes are
|
||||||
|
allocated according to the voting power of each validator node.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
:param planet: (Planetmint) an instantiated planetmint.lib.Planetmint object.
|
||||||
|
:param current_transactions: (list) A list of transactions to be validated along with the election
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Election: a Election object or an object of the derived Election subclass.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValidationError: If the election is invalid
|
||||||
|
"""
|
||||||
|
input_conditions = []
|
||||||
|
|
||||||
|
duplicates = any(txn for txn in current_transactions if txn.id == transaction.id)
|
||||||
|
if self.is_committed(transaction.id) or duplicates:
|
||||||
|
raise DuplicateTransaction("transaction `{}` already exists".format(transaction.id))
|
||||||
|
|
||||||
|
if not transaction.inputs_valid(input_conditions):
|
||||||
|
raise InvalidSignature("Transaction signature is invalid.")
|
||||||
|
|
||||||
|
current_validators = self.get_validators_dict()
|
||||||
|
|
||||||
|
# NOTE: Proposer should be a single node
|
||||||
|
if len(transaction.inputs) != 1 or len(transaction.inputs[0].owners_before) != 1:
|
||||||
|
raise MultipleInputsError("`tx_signers` must be a list instance of length one")
|
||||||
|
|
||||||
|
# NOTE: Check if the proposer is a validator.
|
||||||
|
[election_initiator_node_pub_key] = transaction.inputs[0].owners_before
|
||||||
|
if election_initiator_node_pub_key not in current_validators.keys():
|
||||||
|
raise InvalidProposer("Public key is not a part of the validator set")
|
||||||
|
|
||||||
|
# NOTE: Check if all validators have been assigned votes equal to their voting power
|
||||||
|
if not transaction.is_same_topology(current_validators, transaction.outputs):
|
||||||
|
raise UnequalValidatorSet("Validator set much be exactly same to the outputs of election")
|
||||||
|
|
||||||
|
if transaction.operation == VALIDATOR_ELECTION:
|
||||||
|
self.validate_validator_election(transaction, current_transactions)
|
||||||
|
|
||||||
|
return transaction
|
||||||
|
|
||||||
|
|
||||||
|
def validate_validator_election(self, transaction, current_transactions=[]): # TODO: move somewhere else
|
||||||
|
"""For more details refer BEP-21: https://github.com/planetmint/BEPs/tree/master/21"""
|
||||||
|
|
||||||
|
current_validators = self.get_validators_dict()
|
||||||
|
|
||||||
|
# super(ValidatorElection, self).validate(planet, current_transactions=current_transactions)
|
||||||
|
|
||||||
|
# NOTE: change more than 1/3 of the current power is not allowed
|
||||||
|
if transaction.asset["data"]["power"] >= (1 / 3) * sum(current_validators.values()):
|
||||||
|
raise InvalidPowerChange("`power` change must be less than 1/3 of total power")
|
||||||
|
|
||||||
|
return transaction
|
||||||
|
|
||||||
Block = namedtuple("Block", ("app_hash", "height", "transactions"))
|
Block = namedtuple("Block", ("app_hash", "height", "transactions"))
|
||||||
|
|||||||
@ -12,7 +12,7 @@ class ChainMigrationElection(Election):
|
|||||||
ALLOWED_OPERATIONS = (OPERATION,)
|
ALLOWED_OPERATIONS = (OPERATION,)
|
||||||
TX_SCHEMA_CUSTOM = TX_SCHEMA_CHAIN_MIGRATION_ELECTION
|
TX_SCHEMA_CUSTOM = TX_SCHEMA_CHAIN_MIGRATION_ELECTION
|
||||||
|
|
||||||
def has_concluded(self, planetmint, *args, **kwargs):
|
def has_concluded(self, planetmint, *args, **kwargs): # TODO: move somewhere else
|
||||||
chain = planetmint.get_latest_abci_chain()
|
chain = planetmint.get_latest_abci_chain()
|
||||||
if chain is not None and not chain["is_synced"]:
|
if chain is not None and not chain["is_synced"]:
|
||||||
# do not conclude the migration election if
|
# do not conclude the migration election if
|
||||||
@ -21,10 +21,10 @@ class ChainMigrationElection(Election):
|
|||||||
|
|
||||||
return super().has_concluded(planetmint, *args, **kwargs)
|
return super().has_concluded(planetmint, *args, **kwargs)
|
||||||
|
|
||||||
def on_approval(self, planet, *args, **kwargs):
|
def on_approval(self, planet, *args, **kwargs): # TODO: move somewhere else
|
||||||
planet.migrate_abci_chain()
|
planet.migrate_abci_chain()
|
||||||
|
|
||||||
def show_election(self, planet):
|
def show_election(self, planet): # TODO: move somewhere else
|
||||||
output = super().show_election(planet)
|
output = super().show_election(planet)
|
||||||
chain = planet.get_latest_abci_chain()
|
chain = planet.get_latest_abci_chain()
|
||||||
if chain is None or chain["is_synced"]:
|
if chain is None or chain["is_synced"]:
|
||||||
@ -46,5 +46,5 @@ class ChainMigrationElection(Election):
|
|||||||
output += f"\nvalidators={json.dumps(validators, indent=4)}"
|
output += f"\nvalidators={json.dumps(validators, indent=4)}"
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def on_rollback(self, planet, new_height):
|
def on_rollback(self, planet, new_height): # TODO: move somewhere else
|
||||||
planet.delete_abci_chain(new_height)
|
planet.delete_abci_chain(new_height)
|
||||||
|
|||||||
@ -9,8 +9,6 @@ from uuid import uuid4
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from planetmint import backend
|
from planetmint import backend
|
||||||
from planetmint.transactions.types.assets.create import Create
|
|
||||||
from planetmint.transactions.types.assets.transfer import Transfer
|
|
||||||
from planetmint.transactions.types.elections.vote import Vote
|
from planetmint.transactions.types.elections.vote import Vote
|
||||||
from planetmint.transactions.common.exceptions import (
|
from planetmint.transactions.common.exceptions import (
|
||||||
InvalidSignature,
|
InvalidSignature,
|
||||||
@ -44,7 +42,7 @@ class Election(Transaction):
|
|||||||
ELECTION_THRESHOLD = 2 / 3
|
ELECTION_THRESHOLD = 2 / 3
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_validator_change(cls, planet):
|
def get_validator_change(cls, planet): # TODO: move somewhere else
|
||||||
"""Return the validator set from the most recent approved block
|
"""Return the validator set from the most recent approved block
|
||||||
|
|
||||||
:return: {
|
:return: {
|
||||||
@ -58,7 +56,7 @@ class Election(Transaction):
|
|||||||
return planet.get_validator_change(latest_block["height"])
|
return planet.get_validator_change(latest_block["height"])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_validators(cls, planet, height=None):
|
def get_validators(cls, planet, height=None): # TODO: move somewhere else
|
||||||
"""Return a dictionary of validators with key as `public_key` and
|
"""Return a dictionary of validators with key as `public_key` and
|
||||||
value as the `voting_power`
|
value as the `voting_power`
|
||||||
"""
|
"""
|
||||||
@ -71,7 +69,7 @@ class Election(Transaction):
|
|||||||
return validators
|
return validators
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def recipients(cls, planet):
|
def recipients(cls, planet): # TODO: move somewhere else
|
||||||
"""Convert validator dictionary to a recipient list for `Transaction`"""
|
"""Convert validator dictionary to a recipient list for `Transaction`"""
|
||||||
|
|
||||||
recipients = []
|
recipients = []
|
||||||
@ -81,7 +79,7 @@ class Election(Transaction):
|
|||||||
return recipients
|
return recipients
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_same_topology(cls, current_topology, election_topology):
|
def is_same_topology(cls, current_topology, election_topology): # TODO: move somewhere else
|
||||||
voters = {}
|
voters = {}
|
||||||
for voter in election_topology:
|
for voter in election_topology:
|
||||||
if len(voter.public_keys) > 1:
|
if len(voter.public_keys) > 1:
|
||||||
@ -114,51 +112,6 @@ class Election(Transaction):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def validate(self, planet, current_transactions=[]):
|
|
||||||
"""Validate election transaction
|
|
||||||
|
|
||||||
NOTE:
|
|
||||||
* A valid election is initiated by an existing validator.
|
|
||||||
|
|
||||||
* A valid election is one where voters are validators and votes are
|
|
||||||
allocated according to the voting power of each validator node.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
:param planet: (Planetmint) an instantiated planetmint.lib.Planetmint object.
|
|
||||||
:param current_transactions: (list) A list of transactions to be validated along with the election
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Election: a Election object or an object of the derived Election subclass.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValidationError: If the election is invalid
|
|
||||||
"""
|
|
||||||
input_conditions = []
|
|
||||||
|
|
||||||
duplicates = any(txn for txn in current_transactions if txn.id == self.id)
|
|
||||||
if planet.is_committed(self.id) or duplicates:
|
|
||||||
raise DuplicateTransaction("transaction `{}` already exists".format(self.id))
|
|
||||||
|
|
||||||
if not self.inputs_valid(input_conditions):
|
|
||||||
raise InvalidSignature("Transaction signature is invalid.")
|
|
||||||
|
|
||||||
current_validators = self.get_validators(planet)
|
|
||||||
|
|
||||||
# NOTE: Proposer should be a single node
|
|
||||||
if len(self.inputs) != 1 or len(self.inputs[0].owners_before) != 1:
|
|
||||||
raise MultipleInputsError("`tx_signers` must be a list instance of length one")
|
|
||||||
|
|
||||||
# NOTE: Check if the proposer is a validator.
|
|
||||||
[election_initiator_node_pub_key] = self.inputs[0].owners_before
|
|
||||||
if election_initiator_node_pub_key not in current_validators.keys():
|
|
||||||
raise InvalidProposer("Public key is not a part of the validator set")
|
|
||||||
|
|
||||||
# NOTE: Check if all validators have been assigned votes equal to their voting power
|
|
||||||
if not self.is_same_topology(current_validators, self.outputs):
|
|
||||||
raise UnequalValidatorSet("Validator set much be exactly same to the outputs of election")
|
|
||||||
|
|
||||||
return self
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generate(cls, initiator, voters, election_data, metadata=None):
|
def generate(cls, initiator, voters, election_data, metadata=None):
|
||||||
# Break symmetry in case we need to call an election with the same properties twice
|
# Break symmetry in case we need to call an election with the same properties twice
|
||||||
@ -181,19 +134,11 @@ class Election(Transaction):
|
|||||||
_validate_schema(cls.TX_SCHEMA_CUSTOM, tx)
|
_validate_schema(cls.TX_SCHEMA_CUSTOM, tx)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, tx_signers, recipients, metadata=None, asset=None):
|
def to_public_key(cls, election_id): # TODO: move somewhere else
|
||||||
Create.generate(tx_signers, recipients, metadata=None, asset=None)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def transfer(cls, tx_signers, recipients, metadata=None, asset=None):
|
|
||||||
Transfer.generate(tx_signers, recipients, metadata=None, asset=None)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def to_public_key(cls, election_id):
|
|
||||||
return base58.b58encode(bytes.fromhex(election_id)).decode()
|
return base58.b58encode(bytes.fromhex(election_id)).decode()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def count_votes(cls, election_pk, transactions, getter=getattr):
|
def count_votes(cls, election_pk, transactions, getter=getattr): # TODO: move somewhere else
|
||||||
votes = 0
|
votes = 0
|
||||||
for txn in transactions:
|
for txn in transactions:
|
||||||
if getter(txn, "operation") == Vote.OPERATION:
|
if getter(txn, "operation") == Vote.OPERATION:
|
||||||
@ -205,13 +150,13 @@ class Election(Transaction):
|
|||||||
votes = votes + int(getter(output, "amount"))
|
votes = votes + int(getter(output, "amount"))
|
||||||
return votes
|
return votes
|
||||||
|
|
||||||
def get_commited_votes(self, planet, election_pk=None):
|
def get_commited_votes(self, planet, election_pk=None): # TODO: move somewhere else
|
||||||
if election_pk is None:
|
if election_pk is None:
|
||||||
election_pk = self.to_public_key(self.id)
|
election_pk = self.to_public_key(self.id)
|
||||||
txns = list(backend.query.get_asset_tokens_for_public_key(planet.connection, self.id, election_pk))
|
txns = list(backend.query.get_asset_tokens_for_public_key(planet.connection, self.id, election_pk))
|
||||||
return self.count_votes(election_pk, txns, dict.get)
|
return self.count_votes(election_pk, txns, dict.get)
|
||||||
|
|
||||||
def has_concluded(self, planet, current_votes=[]):
|
def has_concluded(self, planet, current_votes=[]): # TODO: move somewhere else
|
||||||
"""Check if the election can be concluded or not.
|
"""Check if the election can be concluded or not.
|
||||||
|
|
||||||
* Elections can only be concluded if the validator set has not changed
|
* Elections can only be concluded if the validator set has not changed
|
||||||
@ -233,14 +178,14 @@ class Election(Transaction):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_status(self, planet):
|
def get_status(self, planet): # TODO: move somewhere else
|
||||||
election = self.get_election(self.id, planet)
|
election = self.get_election(self.id, planet)
|
||||||
if election and election["is_concluded"]:
|
if election and election["is_concluded"]:
|
||||||
return self.CONCLUDED
|
return self.CONCLUDED
|
||||||
|
|
||||||
return self.INCONCLUSIVE if self.has_validator_set_changed(planet) else self.ONGOING
|
return self.INCONCLUSIVE if self.has_validator_set_changed(planet) else self.ONGOING
|
||||||
|
|
||||||
def has_validator_set_changed(self, planet):
|
def has_validator_set_changed(self, planet): # TODO: move somewhere else
|
||||||
latest_change = self.get_validator_change(planet)
|
latest_change = self.get_validator_change(planet)
|
||||||
if latest_change is None:
|
if latest_change is None:
|
||||||
return False
|
return False
|
||||||
@ -251,13 +196,13 @@ class Election(Transaction):
|
|||||||
|
|
||||||
return latest_change_height > election["height"]
|
return latest_change_height > election["height"]
|
||||||
|
|
||||||
def get_election(self, election_id, planet):
|
def get_election(self, election_id, planet): # TODO: move somewhere else
|
||||||
return planet.get_election(election_id)
|
return planet.get_election(election_id)
|
||||||
|
|
||||||
def store(self, planet, height, is_concluded):
|
def store(self, planet, height, is_concluded): # TODO: move somewhere else
|
||||||
planet.store_election(self.id, height, is_concluded)
|
planet.store_election(self.id, height, is_concluded)
|
||||||
|
|
||||||
def show_election(self, planet):
|
def show_election(self, planet): # TODO: move somewhere else
|
||||||
data = self.asset["data"]
|
data = self.asset["data"]
|
||||||
if "public_key" in data.keys():
|
if "public_key" in data.keys():
|
||||||
data["public_key"] = public_key_to_base64(data["public_key"]["value"])
|
data["public_key"] = public_key_to_base64(data["public_key"]["value"])
|
||||||
@ -270,7 +215,7 @@ class Election(Transaction):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_initiated_elections(cls, height, txns):
|
def _get_initiated_elections(cls, height, txns): # TODO: move somewhere else
|
||||||
elections = []
|
elections = []
|
||||||
for tx in txns:
|
for tx in txns:
|
||||||
if not isinstance(tx, Election):
|
if not isinstance(tx, Election):
|
||||||
@ -280,7 +225,7 @@ class Election(Transaction):
|
|||||||
return elections
|
return elections
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_votes(cls, txns):
|
def _get_votes(cls, txns): # TODO: move somewhere else
|
||||||
elections = OrderedDict()
|
elections = OrderedDict()
|
||||||
for tx in txns:
|
for tx in txns:
|
||||||
if not isinstance(tx, Vote):
|
if not isinstance(tx, Vote):
|
||||||
@ -293,7 +238,7 @@ class Election(Transaction):
|
|||||||
return elections
|
return elections
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def process_block(cls, planet, new_height, txns):
|
def process_block(cls, planet, new_height, txns): # TODO: move somewhere else
|
||||||
"""Looks for election and vote transactions inside the block, records
|
"""Looks for election and vote transactions inside the block, records
|
||||||
and processes elections.
|
and processes elections.
|
||||||
|
|
||||||
@ -340,7 +285,7 @@ class Election(Transaction):
|
|||||||
return [validator_update] if validator_update else []
|
return [validator_update] if validator_update else []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def rollback(cls, planet, new_height, txn_ids):
|
def rollback(cls, planet, new_height, txn_ids): # TODO: move somewhere else
|
||||||
"""Looks for election and vote transactions inside the block and
|
"""Looks for election and vote transactions inside the block and
|
||||||
cleans up the database artifacts possibly created in `process_blocks`.
|
cleans up the database artifacts possibly created in `process_blocks`.
|
||||||
|
|
||||||
|
|||||||
@ -19,25 +19,12 @@ class ValidatorElection(Election):
|
|||||||
ALLOWED_OPERATIONS = (OPERATION,)
|
ALLOWED_OPERATIONS = (OPERATION,)
|
||||||
TX_SCHEMA_CUSTOM = TX_SCHEMA_VALIDATOR_ELECTION
|
TX_SCHEMA_CUSTOM = TX_SCHEMA_VALIDATOR_ELECTION
|
||||||
|
|
||||||
def validate(self, planet, current_transactions=[]):
|
|
||||||
"""For more details refer BEP-21: https://github.com/planetmint/BEPs/tree/master/21"""
|
|
||||||
|
|
||||||
current_validators = self.get_validators(planet)
|
|
||||||
|
|
||||||
super(ValidatorElection, self).validate(planet, current_transactions=current_transactions)
|
|
||||||
|
|
||||||
# NOTE: change more than 1/3 of the current power is not allowed
|
|
||||||
if self.asset["data"]["power"] >= (1 / 3) * sum(current_validators.values()):
|
|
||||||
raise InvalidPowerChange("`power` change must be less than 1/3 of total power")
|
|
||||||
|
|
||||||
return self
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_schema(cls, tx):
|
def validate_schema(cls, tx):
|
||||||
super(ValidatorElection, cls).validate_schema(tx)
|
super(ValidatorElection, cls).validate_schema(tx)
|
||||||
validate_asset_public_key(tx["asset"]["data"]["public_key"])
|
validate_asset_public_key(tx["asset"]["data"]["public_key"])
|
||||||
|
|
||||||
def has_concluded(self, planet, *args, **kwargs):
|
def has_concluded(self, planet, *args, **kwargs): # TODO: move somewhere else
|
||||||
latest_block = planet.get_latest_block()
|
latest_block = planet.get_latest_block()
|
||||||
if latest_block is not None:
|
if latest_block is not None:
|
||||||
latest_block_height = latest_block["height"]
|
latest_block_height = latest_block["height"]
|
||||||
@ -50,7 +37,7 @@ class ValidatorElection(Election):
|
|||||||
|
|
||||||
return super().has_concluded(planet, *args, **kwargs)
|
return super().has_concluded(planet, *args, **kwargs)
|
||||||
|
|
||||||
def on_approval(self, planet, new_height):
|
def on_approval(self, planet, new_height): # TODO: move somewhere else
|
||||||
validator_updates = [self.asset["data"]]
|
validator_updates = [self.asset["data"]]
|
||||||
curr_validator_set = planet.get_validators(new_height)
|
curr_validator_set = planet.get_validators(new_height)
|
||||||
updated_validator_set = new_validator_set(curr_validator_set, validator_updates)
|
updated_validator_set = new_validator_set(curr_validator_set, validator_updates)
|
||||||
@ -61,6 +48,6 @@ class ValidatorElection(Election):
|
|||||||
planet.store_validator_set(new_height + 1, updated_validator_set)
|
planet.store_validator_set(new_height + 1, updated_validator_set)
|
||||||
return encode_validator(self.asset["data"])
|
return encode_validator(self.asset["data"])
|
||||||
|
|
||||||
def on_rollback(self, planetmint, new_height):
|
def on_rollback(self, planetmint, new_height): # TODO: move somewhere else
|
||||||
# TODO change to `new_height + 2` when upgrading to Tendermint 0.24.0.
|
# TODO change to `new_height + 2` when upgrading to Tendermint 0.24.0.
|
||||||
planetmint.delete_validator_set(new_height + 1)
|
planetmint.delete_validator_set(new_height + 1)
|
||||||
|
|||||||
@ -4,4 +4,4 @@ from planetmint.transactions.types.elections.chain_migration_election import Cha
|
|||||||
def test_valid_migration_election(b_mock, node_key):
|
def test_valid_migration_election(b_mock, node_key):
|
||||||
voters = ChainMigrationElection.recipients(b_mock)
|
voters = ChainMigrationElection.recipients(b_mock)
|
||||||
election = ChainMigrationElection.generate([node_key.public_key], voters, {}, None).sign([node_key.private_key])
|
election = ChainMigrationElection.generate([node_key.public_key], voters, {}, None).sign([node_key.private_key])
|
||||||
assert election.validate(b_mock)
|
assert b_mock.validate_election(election)
|
||||||
|
|||||||
@ -25,7 +25,7 @@ def test_upsert_validator_valid_election(b_mock, new_validator, node_key):
|
|||||||
election = ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign(
|
election = ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign(
|
||||||
[node_key.private_key]
|
[node_key.private_key]
|
||||||
)
|
)
|
||||||
assert election.validate(b_mock)
|
assert b_mock.validate_election(election)
|
||||||
|
|
||||||
|
|
||||||
def test_upsert_validator_invalid_election_public_key(b_mock, new_validator, node_key):
|
def test_upsert_validator_invalid_election_public_key(b_mock, new_validator, node_key):
|
||||||
@ -47,7 +47,7 @@ def test_upsert_validator_invalid_power_election(b_mock, new_validator, node_key
|
|||||||
[node_key.private_key]
|
[node_key.private_key]
|
||||||
)
|
)
|
||||||
with pytest.raises(InvalidPowerChange):
|
with pytest.raises(InvalidPowerChange):
|
||||||
election.validate(b_mock)
|
b_mock.validate_election(election)
|
||||||
|
|
||||||
|
|
||||||
def test_upsert_validator_invalid_proposed_election(b_mock, new_validator, node_key):
|
def test_upsert_validator_invalid_proposed_election(b_mock, new_validator, node_key):
|
||||||
@ -57,7 +57,7 @@ def test_upsert_validator_invalid_proposed_election(b_mock, new_validator, node_
|
|||||||
voters = ValidatorElection.recipients(b_mock)
|
voters = ValidatorElection.recipients(b_mock)
|
||||||
election = ValidatorElection.generate([alice.public_key], voters, new_validator, None).sign([alice.private_key])
|
election = ValidatorElection.generate([alice.public_key], voters, new_validator, None).sign([alice.private_key])
|
||||||
with pytest.raises(InvalidProposer):
|
with pytest.raises(InvalidProposer):
|
||||||
election.validate(b_mock)
|
b_mock.validate_election(election)
|
||||||
|
|
||||||
|
|
||||||
def test_upsert_validator_invalid_inputs_election(b_mock, new_validator, node_key):
|
def test_upsert_validator_invalid_inputs_election(b_mock, new_validator, node_key):
|
||||||
@ -69,7 +69,7 @@ def test_upsert_validator_invalid_inputs_election(b_mock, new_validator, node_ke
|
|||||||
[node_key.private_key, alice.private_key]
|
[node_key.private_key, alice.private_key]
|
||||||
)
|
)
|
||||||
with pytest.raises(MultipleInputsError):
|
with pytest.raises(MultipleInputsError):
|
||||||
election.validate(b_mock)
|
b_mock.validate_election(election)
|
||||||
|
|
||||||
|
|
||||||
@patch("planetmint.transactions.types.elections.election.uuid4", lambda: "mock_uuid4")
|
@patch("planetmint.transactions.types.elections.election.uuid4", lambda: "mock_uuid4")
|
||||||
@ -80,12 +80,12 @@ def test_upsert_validator_invalid_election(b_mock, new_validator, node_key, fixe
|
|||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(DuplicateTransaction):
|
with pytest.raises(DuplicateTransaction):
|
||||||
fixed_seed_election.validate(b_mock, [duplicate_election])
|
b_mock.validate_election(fixed_seed_election, [duplicate_election])
|
||||||
|
|
||||||
b_mock.store_bulk_transactions([fixed_seed_election])
|
b_mock.store_bulk_transactions([fixed_seed_election])
|
||||||
|
|
||||||
with pytest.raises(DuplicateTransaction):
|
with pytest.raises(DuplicateTransaction):
|
||||||
duplicate_election.validate(b_mock)
|
b_mock.validate_election(duplicate_election)
|
||||||
|
|
||||||
# Try creating an election with incomplete voter set
|
# Try creating an election with incomplete voter set
|
||||||
invalid_election = ValidatorElection.generate([node_key.public_key], voters[1:], new_validator, None).sign(
|
invalid_election = ValidatorElection.generate([node_key.public_key], voters[1:], new_validator, None).sign(
|
||||||
@ -93,7 +93,7 @@ def test_upsert_validator_invalid_election(b_mock, new_validator, node_key, fixe
|
|||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(UnequalValidatorSet):
|
with pytest.raises(UnequalValidatorSet):
|
||||||
invalid_election.validate(b_mock)
|
b_mock.validate_election(invalid_election)
|
||||||
|
|
||||||
recipients = ValidatorElection.recipients(b_mock)
|
recipients = ValidatorElection.recipients(b_mock)
|
||||||
altered_recipients = []
|
altered_recipients = []
|
||||||
@ -107,7 +107,7 @@ def test_upsert_validator_invalid_election(b_mock, new_validator, node_key, fixe
|
|||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(UnequalValidatorSet):
|
with pytest.raises(UnequalValidatorSet):
|
||||||
tx_election.validate(b_mock)
|
b_mock.validate_election(tx_election)
|
||||||
|
|
||||||
|
|
||||||
def test_get_status_ongoing(b, ongoing_validator_election, new_validator):
|
def test_get_status_ongoing(b, ongoing_validator_election, new_validator):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user