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

Add genesis header & val total dep size tests #11

Open
wants to merge 13 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ pyethereum/monkeypatch.py
.eggs
.cache
.env
.python-version
.idea
.vscode
1 change: 0 additions & 1 deletion .python-version

This file was deleted.

2 changes: 1 addition & 1 deletion casper
5 changes: 5 additions & 0 deletions ethereum/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@
SERENITY_HEADER_POST_FINALIZER=utils.int_to_addr(254),
SERENITY_GETTER_CODE=decode_hex(
'60ff331436604014161560155760203560003555005b6000355460205260206020f3'),
# Casper FFG
EPOCH_LENGTH=10,
WITHDRAWAL_DELAY=100,
BASE_INTEREST_FACTOR=0.02,
BASE_PENALTY_FACTOR=0.002,
# Custom specials
CUSTOM_SPECIALS={},
)
Expand Down
18 changes: 10 additions & 8 deletions ethereum/hybrid_casper/casper_utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import os
import sys
import copy

from ethereum import utils, abi, genesis_helpers, config
from ethereum import utils, abi, genesis_helpers
from ethereum.hybrid_casper.casper_initiating_transactions import mk_initializers, purity_checker_address, purity_checker_abi
from ethereum.hybrid_casper import consensus
from ethereum.hybrid_casper.config import config
Expand All @@ -21,18 +20,21 @@
purity_translator = abi.ContractTranslator(purity_checker_abi)

# Get a genesis state which is primed for Casper
def make_casper_genesis(alloc, epoch_length, withdrawal_delay, base_interest_factor, base_penalty_factor):
def make_casper_genesis(env, header=None, **kwargs):
assert isinstance(env, config.Env)

# The Casper-specific dynamic config declaration
config.casper_config['EPOCH_LENGTH'] = epoch_length
config.casper_config['WITHDRAWAL_DELAY'] = withdrawal_delay
config.casper_config['OWNER'] = a0
config.casper_config['BASE_INTEREST_FACTOR'] = base_interest_factor
config.casper_config['BASE_PENALTY_FACTOR'] = base_penalty_factor
config.casper_config['EPOCH_LENGTH'] = kwargs.get('epoch_length', env.config['EPOCH_LENGTH'])
config.casper_config['WITHDRAWAL_DELAY'] = kwargs.get('withdrawal_delay', env.config['WITHDRAWAL_DELAY'])
config.casper_config['BASE_INTEREST_FACTOR'] = kwargs.get('base_interest_factor', env.config['BASE_INTEREST_FACTOR'])
config.casper_config['BASE_PENALTY_FACTOR'] = kwargs.get('base_penalty_factor', env.config['BASE_PENALTY_FACTOR'])
alloc = kwargs.get('alloc', env.config['GENESIS_INITIAL_ALLOC'])
# Get initialization txs
init_txs, casper_address = mk_initializers(config.casper_config, config.casper_config['NULL_SENDER'])
config.casper_config['CASPER_ADDRESS'] = casper_address
# Create state and apply required state_transitions for initializing Casper
state = genesis_helpers.mk_basic_state(alloc, None, env=config.Env(config=config.casper_config))
state = genesis_helpers.mk_basic_state(alloc, header=header, env=config.Env(config=config.casper_config))
state.gas_limit = 10**8
for tx in init_txs:
state.set_balance(utils.privtoaddr(config.casper_config['NULL_SENDER']), 15**18)
Expand Down
2 changes: 1 addition & 1 deletion ethereum/hybrid_casper/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

casper_config = dict(
# The Casper-specific config declaration
HOMESTEAD_FORK_BLKNUM=0,
METROPOLIS_FORK_BLKNUM=0,
ANTI_DOS_FORK_BLKNUM=0,
CLEARING_FORK_BLKNUM=0,
CONSENSUS_STRATEGY='hybrid_casper',
Expand Down
16 changes: 11 additions & 5 deletions ethereum/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,16 @@ def apply_message(state, msg=None, **kwargs):
return bytearray_to_bytestr(data) if result else None


def apply_casper_vote_transaction(state, tx):
def apply_transaction(state, tx):
if tx.to == state.config['CASPER_ADDRESS'] and tx.data[0:4] == b'\xe9\xdc\x06\x14':
log_tx.debug("Applying CASPER VOTE transaction: {}".format(tx))
return _apply_casper_vote_transaction(state, tx)
else:
log_tx.debug("Applying transaction (non-CASPER VOTE): {}".format(tx))
return _apply_transaction(state, tx)


def _apply_casper_vote_transaction(state, tx):
if tx.sender == state.config['NULL_SENDER'] or not tx.to == state.config['CASPER_ADDRESS']:
raise InvalidTransaction("Sender must be not be null sender and to must be the Casper contract address")
state.logs = []
Expand Down Expand Up @@ -241,8 +250,6 @@ def apply_casper_vote_transaction(state, tx):
output = bytearray_to_bytestr(data)
success = 1

state.gas_used += gas_used

# Pre-Metropolis: commit state after every tx
if not state.is_METROPOLIS() and not SKIP_MEDSTATES:
state.commit()
Expand All @@ -258,8 +265,7 @@ def apply_casper_vote_transaction(state, tx):
return success, output



def apply_transaction(state, tx):
def _apply_transaction(state, tx):
state.logs = []
state.suicides = []
state.refunds = 0
Expand Down
8 changes: 2 additions & 6 deletions ethereum/meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
set_execution_results, add_transactions, post_finalize, \
validate_casper_vote_transaction_precedence
from ethereum.consensus_strategy import get_consensus_strategy
from ethereum.messages import apply_transaction, apply_casper_vote_transaction
from ethereum.messages import apply_transaction
from ethereum.state import State
from ethereum.utils import sha3, encode_hex
import rlp
Expand All @@ -29,11 +29,7 @@ def apply_block(state, block):
assert validate_casper_vote_transaction_precedence(state, block)
# Process transactions
for tx in block.transactions:
# Handles casper vote transactions
if tx.to == state.config['CASPER_ADDRESS'] and tx.data[0:4] == b'\xe9\xdc\x06\x14':
apply_casper_vote_transaction(state, tx)
else:
apply_transaction(state, tx)
apply_transaction(state, tx)
# Finalize (incl paying block rewards)
cs.finalize(state, block)
# Verify state root, tx list root, receipt root
Expand Down
56 changes: 50 additions & 6 deletions ethereum/tests/hybrid_casper/test_chain.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest
from ethereum.utils import privtoaddr
from ethereum.tools import tester
from ethereum.config import Env
from ethereum.tests.utils import new_db
from ethereum.db import EphemDB
from ethereum.hybrid_casper import casper_utils
Expand Down Expand Up @@ -28,7 +28,13 @@ def db():
alt_db = db

def init_chain_and_casper():
genesis = casper_utils.make_casper_genesis(ALLOC, EPOCH_LENGTH, 100, 0.02, 0.002)
genesis = casper_utils.make_casper_genesis(
env=Env(),
alloc=ALLOC,
epoch_length=EPOCH_LENGTH,
withdrawal_delay=100,
base_interest_factor=0.02,
base_penalty_factor=0.002)
t = tester.Chain(genesis=genesis)
casper = tester.ABIContract(t, casper_utils.casper_abi, t.chain.config['CASPER_ADDRESS'])
return t, casper
Expand Down Expand Up @@ -57,10 +63,11 @@ def test_mining_block_rewards(db):
blk3 = t.mine(coinbase=a1)
blk4 = t.mine(coinbase=a1)
t.mine(coinbase=a1)
assert t.chain.state.get_balance(a1) == t.chain.env.config['BLOCK_REWARD'] + t.chain.mk_poststate_of_blockhash(blk4.hash).get_balance(a1)
assert t.chain.state.get_balance(a1) == t.chain.env.config['BLOCK_REWARD'] * 2 + t.chain.mk_poststate_of_blockhash(blk3.hash).get_balance(a1)
assert t.chain.state.get_balance(a1) == t.chain.env.config['BLOCK_REWARD'] * 3 + t.chain.mk_poststate_of_blockhash(blk2.hash).get_balance(a1)
assert t.chain.state.get_balance(a1) == t.chain.env.config['BLOCK_REWARD'] * 4 + t.chain.mk_poststate_of_blockhash(genesis.hash).get_balance(a1)
balance = t.chain.state.get_balance(a1)
assert balance == t.chain.env.config['BYZANTIUM_BLOCK_REWARD'] + t.chain.mk_poststate_of_blockhash(blk4.hash).get_balance(a1)
assert balance == t.chain.env.config['BYZANTIUM_BLOCK_REWARD'] * 2 + t.chain.mk_poststate_of_blockhash(blk3.hash).get_balance(a1)
assert balance == t.chain.env.config['BYZANTIUM_BLOCK_REWARD'] * 3 + t.chain.mk_poststate_of_blockhash(blk2.hash).get_balance(a1)
assert balance == t.chain.env.config['BYZANTIUM_BLOCK_REWARD'] * 4 + t.chain.mk_poststate_of_blockhash(genesis.hash).get_balance(a1)
assert blk2.prevhash == genesis.hash


Expand Down Expand Up @@ -109,10 +116,13 @@ def test_no_gas_cost_for_successful_casper_vote(db):
test = TestLangHybrid(15, 100, 0.02, 0.002)
test.parse(test_string)
pre_balance = test.t.head_state.get_balance(sender)
pre_block_gas_used = test.t.head_state.gas_used
test_string = 'V0'
test.parse(test_string)
post_balance = test.t.head_state.get_balance(sender)
post_block_gas_used = test.t.head_state.gas_used
assert pre_balance == post_balance
assert pre_block_gas_used == post_block_gas_used


def test_costs_gas_for_failed_casper_vote(db):
Expand Down Expand Up @@ -167,3 +177,37 @@ def test_vote_surround_slash(db):
test_string = 'B J0 J1 J2 J3 B B S0 V0 V1 V2 V3 B V0 V1 V2 V3 B V0 V1 V2 V3 R0 B B B B B B B V0 B1'
test = TestLangHybrid(15, 100, 0.02, 0.002)
test.parse(test_string)

def test_rewards_when_validation_is_finalizing(db):
""" Test that the validation rewards are positive when validators are finalizing epochs """
number_of_epochs = 5
vote_string = 'B1'
for i in range(number_of_epochs):
vote_string += ' B1 V0 V1 V2 V3 B'
test_string = 'B J0 J1 J2 J3 B B'
test = TestLangHybrid(15, 100, 0.02, 0.002, 5000 * 18**10)
test.parse(test_string)
casper = tester.ABIContract(test.t, casper_utils.casper_abi, test.t.chain.casper_address)
deposits_in_first_epoch = casper.get_total_curdyn_deposits()
test.parse(vote_string)
deposits_after_votes = casper.get_total_curdyn_deposits()
print('Total deposits in first dynasty: {}'.format(deposits_in_first_epoch))
print('Total deposits after {} rounds of voting: {}'.format(number_of_epochs, deposits_after_votes))
assert deposits_in_first_epoch < deposits_after_votes

def test_rewards_when_validation_is_not_finalizing(db):
""" Test that the validation rewards are positive when validators are NOT finalizing epochs """
number_of_epochs = 5
vote_string = 'B1'
for i in range(number_of_epochs):
vote_string += ' B1 V0 B'
test_string = 'B J0 J1 J2 J3 B B'
test = TestLangHybrid(15, 100, 0.02, 0.002, 5000 * 18**10)
test.parse(test_string)
casper = tester.ABIContract(test.t, casper_utils.casper_abi, test.t.chain.casper_address)
deposits_in_first_epoch = casper.get_total_curdyn_deposits()
test.parse(vote_string)
deposits_after_votes = casper.get_total_curdyn_deposits()
print('Total deposits in first dynasty: {}'.format(deposits_in_first_epoch))
print('Total deposits after {} rounds of voting: {}'.format(number_of_epochs, deposits_after_votes))
assert deposits_in_first_epoch > deposits_after_votes
16 changes: 12 additions & 4 deletions ethereum/tests/hybrid_casper/testing_lang.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from ethereum.config import Env
from ethereum.tools import tester
from ethereum.utils import encode_hex, privtoaddr
from ethereum.hybrid_casper import casper_utils
import re

ALLOC = {a: {'balance': 500*10**19} for a in tester.accounts[:10]}
ALLOC = {a: {'balance': 500*10**20} for a in tester.accounts[:10]}

class Validator(object):
def __init__(self, withdrawal_addr, key):
Expand Down Expand Up @@ -60,8 +61,15 @@ def get_validator_index(self, casper):

class TestLangHybrid(object):
# For a custom Casper parser, overload generic parser and construct your chain
def __init__(self, epoch_length, withdrawal_delay, base_interest_factor, base_penalty_factor):
self.genesis = casper_utils.make_casper_genesis(ALLOC, epoch_length, withdrawal_delay, base_interest_factor, base_penalty_factor)
def __init__(self, epoch_length, withdrawal_delay, base_interest_factor, base_penalty_factor, validator_deposit_size=5000 * 10**18):
self.genesis = casper_utils.make_casper_genesis(
env=Env(),
alloc=ALLOC,
epoch_length=epoch_length,
withdrawal_delay=withdrawal_delay,
base_interest_factor=base_interest_factor,
base_penalty_factor=base_penalty_factor)
self.validator_deposit_size = validator_deposit_size
self.t = tester.Chain(genesis=self.genesis)
self.casper = tester.ABIContract(self.t, casper_utils.casper_abi, self.t.chain.env.config['CASPER_ADDRESS'])
self.saved_blocks = dict()
Expand All @@ -88,7 +96,7 @@ def mine_blocks(self, number):

def join(self, number):
withdrawal_addr = privtoaddr(tester.keys[number])
casper_utils.induct_validator(self.t, self.casper, tester.keys[number], 200 * 10**18)
casper_utils.induct_validator(self.t, self.casper, tester.keys[number], self.validator_deposit_size)
self.validators[number] = Validator(withdrawal_addr, tester.keys[number])

def vote(self, validator_index):
Expand Down
7 changes: 2 additions & 5 deletions ethereum/tools/tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from ethereum.consensus_strategy import get_consensus_strategy
from ethereum.config import config_homestead, config_tangerine, config_spurious, config_metropolis, default_config, Env
from ethereum.pow.ethpow import Miner
from ethereum.messages import apply_transaction, apply_message, apply_casper_vote_transaction
from ethereum.messages import apply_transaction, apply_message
from ethereum.common import verify_execution_results, mk_block_from_prevstate, set_execution_results
from ethereum.meta import make_head_candidate
from ethereum.abi import ContractTranslator
Expand Down Expand Up @@ -179,10 +179,7 @@ def direct_tx(self, transaction):
if self.last_sender is not None and privtoaddr(
self.last_sender) != transaction.sender:
self.last_sender = None
if transaction.to == self.head_state.env.config['CASPER_ADDRESS'] and transaction.data[0:4] == b'\xe9\xdc\x06\x14':
success, output = apply_casper_vote_transaction(self.head_state, transaction)
else:
success, output = apply_transaction(self.head_state, transaction)
success, output = apply_transaction(self.head_state, transaction)
self.block.transactions.append(transaction)
if not success:
raise TransactionFailed()
Expand Down