Module project.simulator

This module simulates an ecosystem in which we can test delegations. It generates new delegations, applies rewards, penalties, and slashings, and processes withdrawals.

Classes

class Simulator (beacon_chain_accounting)
Expand source code
class Simulator: 
    """
    The Simulator operates in discrete 'ticks' over a defined number of iterations. 
    During each tick, it generates new delegations, applies rewards, penalties, and slashings, 
    and processes withdrawals.
    """
    beacon_chain_accounting: BeaconChainAccounting
    
    def __init__(self, beacon_chain_accounting):
        self.beacon_chain_accounting = beacon_chain_accounting

   
    def initialize_required_data(self):
        """
        This method generates the initial set of validators and delegators.
        """
        # generate the validators
        num_validators_to_generate = random.randint(constants.min_validators, constants.max_validators)
        
        for _ in range(num_validators_to_generate):
            validator_initial_balance = random.randint(constants.min_validator_initial_balance, constants.max_validator_initial_balance)
            self.beacon_chain_accounting.validators_registry.validators_balances.append(validator_initial_balance)

            validator = Validator()
            validator.pubkey = secrets.token_hex(32)
            validator.effective_balance = validator_initial_balance # + histeresys in the future
            validator.slashed = False
            validator.delegated = False
            validator.fee_percentage = constants.validators_withdrawal_fee_percentage
            self.beacon_chain_accounting.validators_registry.validators.append(validator)

        # generate the delegators    
        num_delegators_to_generate = random.randint(constants.min_delegators, constants.max_delegators)
        for _ in range(num_delegators_to_generate):
            self.beacon_chain_accounting.deposit_to_delegator_balance(
                secrets.token_hex(32), 
                random.randint(
                    constants.min_delegator_deposit, 
                    constants.max_delegator_deposit
                    )
                )
    
    def tick_delegation(self):
        """
        This method creates a random number of delegations with a random amount of delegated GWEI.
        """
        num_delegations = random.randint(constants.min_delegations_per_tick, constants.max_delegations_per_tick)
        
        for _ in range(num_delegations):
            delegated_amount = random.randint(constants.min_delegated_amount, constants.max_delegated_amount)
            
            delegator_index = random.randint(0, len(self.beacon_chain_accounting.delegators_registry.delegators)-1)
           
            validator_index = random.randint(0, len(self.beacon_chain_accounting.validators_registry.validators)-1)
            validator = self.beacon_chain_accounting.validators_registry.validators[validator_index]
            validator_key = validator.pubkey
            
            self.beacon_chain_accounting.delegate_to_validator(delegator_index, validator_key, delegated_amount)

    def tick_withdrawals(self):
        """
        This method creates a random number of withdrawals with a random amount of withdrawed GWEI.
        """
        num_withdrawals = random.randint(constants.min_withdrawals_per_tick, constants.max_withdrawals_per_tick)
        
        for _ in range(num_withdrawals):
            delegated_validator_index = random.randrange(0, len(self.beacon_chain_accounting.delegated_validators_registry.delegated_validators))

            delegated_validator = self.beacon_chain_accounting.delegated_validators_registry.delegated_validators[delegated_validator_index]
            
            matching_indices = [i for i, delegator_balance in enumerate(delegated_validator.delegated_balances) if delegator_balance > 0]

            if len(matching_indices) > 0:

                index = random.randrange(0, len(matching_indices))

                delegator_index = matching_indices[index]
                
                withdrawed_amount = random.randint(0, math.floor(delegated_validator.delegated_balances[delegator_index]))

                self.beacon_chain_accounting.withdraw_from_validator(delegator_index, delegated_validator.delegated_validator.pubkey, withdrawed_amount)
      
    def process_rewards_penalties(self):
        """
        This method generates the rewards, penalties and slashings to be applied to the validator balances.
        """    
        delegated_validators = self.beacon_chain_accounting.delegated_validators_registry.delegated_validators

        for delegated_validator in delegated_validators:
            weight = delegated_validator.validator_balance / constants.MIN_ACTIVATION_BALANCE
            reward = constants.B * weight 
            penalties  = constants.B * weight 
            slash  = random.randint(constants.min_slash, constants.max_slash) * weight
  
            probability = random.randint(0,100)
            if(probability >= 99):
                delegated_validator.penalties += slash
            if(probability >= 70):
                delegated_validator.penalties += penalties
            elif(probability >= 30):
                delegated_validator.rewards += reward
        
        self.beacon_chain_accounting.delegated_validators_registry.process_rewards_penalties()

The Simulator operates in discrete 'ticks' over a defined number of iterations. During each tick, it generates new delegations, applies rewards, penalties, and slashings, and processes withdrawals.

Class variables

var beacon_chain_accounting : eods.beacon_chain_accounting.BeaconChainAccounting

Methods

def initialize_required_data(self)
Expand source code
def initialize_required_data(self):
    """
    This method generates the initial set of validators and delegators.
    """
    # generate the validators
    num_validators_to_generate = random.randint(constants.min_validators, constants.max_validators)
    
    for _ in range(num_validators_to_generate):
        validator_initial_balance = random.randint(constants.min_validator_initial_balance, constants.max_validator_initial_balance)
        self.beacon_chain_accounting.validators_registry.validators_balances.append(validator_initial_balance)

        validator = Validator()
        validator.pubkey = secrets.token_hex(32)
        validator.effective_balance = validator_initial_balance # + histeresys in the future
        validator.slashed = False
        validator.delegated = False
        validator.fee_percentage = constants.validators_withdrawal_fee_percentage
        self.beacon_chain_accounting.validators_registry.validators.append(validator)

    # generate the delegators    
    num_delegators_to_generate = random.randint(constants.min_delegators, constants.max_delegators)
    for _ in range(num_delegators_to_generate):
        self.beacon_chain_accounting.deposit_to_delegator_balance(
            secrets.token_hex(32), 
            random.randint(
                constants.min_delegator_deposit, 
                constants.max_delegator_deposit
                )
            )

This method generates the initial set of validators and delegators.

def process_rewards_penalties(self)
Expand source code
def process_rewards_penalties(self):
    """
    This method generates the rewards, penalties and slashings to be applied to the validator balances.
    """    
    delegated_validators = self.beacon_chain_accounting.delegated_validators_registry.delegated_validators

    for delegated_validator in delegated_validators:
        weight = delegated_validator.validator_balance / constants.MIN_ACTIVATION_BALANCE
        reward = constants.B * weight 
        penalties  = constants.B * weight 
        slash  = random.randint(constants.min_slash, constants.max_slash) * weight

        probability = random.randint(0,100)
        if(probability >= 99):
            delegated_validator.penalties += slash
        if(probability >= 70):
            delegated_validator.penalties += penalties
        elif(probability >= 30):
            delegated_validator.rewards += reward
    
    self.beacon_chain_accounting.delegated_validators_registry.process_rewards_penalties()

This method generates the rewards, penalties and slashings to be applied to the validator balances.

def tick_delegation(self)
Expand source code
def tick_delegation(self):
    """
    This method creates a random number of delegations with a random amount of delegated GWEI.
    """
    num_delegations = random.randint(constants.min_delegations_per_tick, constants.max_delegations_per_tick)
    
    for _ in range(num_delegations):
        delegated_amount = random.randint(constants.min_delegated_amount, constants.max_delegated_amount)
        
        delegator_index = random.randint(0, len(self.beacon_chain_accounting.delegators_registry.delegators)-1)
       
        validator_index = random.randint(0, len(self.beacon_chain_accounting.validators_registry.validators)-1)
        validator = self.beacon_chain_accounting.validators_registry.validators[validator_index]
        validator_key = validator.pubkey
        
        self.beacon_chain_accounting.delegate_to_validator(delegator_index, validator_key, delegated_amount)

This method creates a random number of delegations with a random amount of delegated GWEI.

def tick_withdrawals(self)
Expand source code
def tick_withdrawals(self):
    """
    This method creates a random number of withdrawals with a random amount of withdrawed GWEI.
    """
    num_withdrawals = random.randint(constants.min_withdrawals_per_tick, constants.max_withdrawals_per_tick)
    
    for _ in range(num_withdrawals):
        delegated_validator_index = random.randrange(0, len(self.beacon_chain_accounting.delegated_validators_registry.delegated_validators))

        delegated_validator = self.beacon_chain_accounting.delegated_validators_registry.delegated_validators[delegated_validator_index]
        
        matching_indices = [i for i, delegator_balance in enumerate(delegated_validator.delegated_balances) if delegator_balance > 0]

        if len(matching_indices) > 0:

            index = random.randrange(0, len(matching_indices))

            delegator_index = matching_indices[index]
            
            withdrawed_amount = random.randint(0, math.floor(delegated_validator.delegated_balances[delegator_index]))

            self.beacon_chain_accounting.withdraw_from_validator(delegator_index, delegated_validator.delegated_validator.pubkey, withdrawed_amount)

This method creates a random number of withdrawals with a random amount of withdrawed GWEI.