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

ci tests #800

Merged
merged 11 commits into from
Mar 19, 2019
Merged
Show file tree
Hide file tree
Changes from 9 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
41 changes: 41 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Python CircleCI 2.0 configuration file
version: 2
jobs:
build:
docker:
- image: circleci/python:3.6
working_directory: ~/repo

steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "requirements.txt" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-

- run:
name: install dependencies
command: |
python3 -m venv venv
. venv/bin/activate
pip install -r requirements.txt
- run:
name: build phase0 spec
command: make build/phase0

- save_cache:
paths:
- ./venv
key: v1-dependencies-{{ checksum "requirements.txt" }}

- run:
name: run tests
command: |
. venv/bin/activate
pytest tests

- store_artifacts:
path: test-reports
destination: test-reports
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.pyc
/__pycache__
/venv
/.pytest_cache

build/
29 changes: 29 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
SPEC_DIR = ./specs
SCRIPT_DIR = ./scripts
BUILD_DIR = ./build
UTILS_DIR = ./utils


.PHONY: clean all test


all: $(BUILD_DIR)/phase0


clean:
rm -rf $(BUILD_DIR)


# runs a limited set of tests against a minimal config
# run pytest with `-m` option to full suite
test:
pytest -m "sanity and minimal_config" tests/


$(BUILD_DIR)/phase0:
mkdir -p $@
python3 $(SCRIPT_DIR)/phase0/build_spec.py $(SPEC_DIR)/core/0_beacon-chain.md $@/spec.py
mkdir -p $@/utils
cp $(UTILS_DIR)/phase0/* $@/utils
cp $(UTILS_DIR)/phase0/state_transition.py $@
touch $@/__init__.py $@/utils/__init__.py
6 changes: 6 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
eth-utils>=1.3.0,<2
eth-typing>=2.1.0,<3.0.0
oyaml==0.7
pycryptodome==3.7.3
py_ecc>=1.6.0
pytest>=3.6,<3.7
Empty file added scripts/__init__.py
Empty file.
Empty file added scripts/phase0/__init__.py
Empty file.
78 changes: 78 additions & 0 deletions scripts/phase0/build_spec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import sys
import function_puller


def build_spec(sourcefile, outfile):
code_lines = []

code_lines.append("from build.phase0.utils.minimal_ssz import *")
code_lines.append("from build.phase0.utils.bls_stub import *")
for i in (1, 2, 3, 4, 8, 32, 48, 96):
code_lines.append("def int_to_bytes%d(x): return x.to_bytes(%d, 'little')" % (i, i))
code_lines.append("SLOTS_PER_EPOCH = 64") # stub, will get overwritten by real var
code_lines.append("def slot_to_epoch(x): return x // SLOTS_PER_EPOCH")

code_lines.append("""
from typing import (
Any,
Callable,
List,
NewType,
Tuple,
)


Slot = NewType('Slot', int) # uint64
Epoch = NewType('Epoch', int) # uint64
Shard = NewType('Shard', int) # uint64
ValidatorIndex = NewType('ValidatorIndex', int) # uint64
Gwei = NewType('Gwei', int) # uint64
Bytes32 = NewType('Bytes32', bytes) # bytes32
BLSPubkey = NewType('BLSPubkey', bytes) # bytes48
BLSSignature = NewType('BLSSignature', bytes) # bytes96
Any = None
Store = None
""")

code_lines += function_puller.get_lines(sourcefile)

code_lines.append("""
# Monkey patch validator shuffling cache
_get_shuffling = get_shuffling
shuffling_cache = {}
def get_shuffling(seed: Bytes32,
validators: List[Validator],
epoch: Epoch) -> List[List[ValidatorIndex]]:

param_hash = (seed, hash_tree_root(validators, [Validator]), epoch)

if param_hash in shuffling_cache:
# print("Cache hit, epoch={0}".format(epoch))
return shuffling_cache[param_hash]
else:
# print("Cache miss, epoch={0}".format(epoch))
ret = _get_shuffling(seed, validators, epoch)
shuffling_cache[param_hash] = ret
return ret


# Monkey patch hash cache
_hash = hash
hash_cache = {}
def hash(x):
if x in hash_cache:
return hash_cache[x]
else:
ret = _hash(x)
hash_cache[x] = ret
return ret
""")

with open(outfile, 'w') as out:
out.write("\n".join(code_lines))


if __name__ == '__main__':
if len(sys.argv) < 3:
print("Error: spec source and outfile must defined")
build_spec(sys.argv[1], sys.argv[2])
46 changes: 46 additions & 0 deletions scripts/phase0/function_puller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import sys


def get_lines(file_name):
code_lines = []
pulling_from = None
current_name = None
processing_typedef = False
for linenum, line in enumerate(open(sys.argv[1]).readlines()):
line = line.rstrip()
if pulling_from is None and len(line) > 0 and line[0] == '#' and line[-1] == '`':
current_name = line[line[:-1].rfind('`') + 1: -1]
if line[:9] == '```python':
assert pulling_from is None
pulling_from = linenum + 1
elif line[:3] == '```':
if pulling_from is None:
pulling_from = linenum
else:
if processing_typedef:
assert code_lines[-1] == '}'
code_lines[-1] = '})'
pulling_from = None
processing_typedef = False
else:
if pulling_from == linenum and line == '{':
code_lines.append('%s = SSZType({' % current_name)
processing_typedef = True
elif pulling_from is not None:
code_lines.append(line)
elif pulling_from is None and len(line) > 0 and line[0] == '|':
row = line[1:].split('|')
if len(row) >= 2:
for i in range(2):
row[i] = row[i].strip().strip('`')
if '`' in row[i]:
row[i] = row[i][:row[i].find('`')]
eligible = True
if row[0][0] not in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_':
eligible = False
for c in row[0]:
if c not in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789':
eligible = False
if eligible:
code_lines.append(row[0] + ' = ' + (row[1].replace('**TBD**', '0x1234567890123567890123456789012357890')))
return code_lines
Empty file added tests/__init__.py
Empty file.
Empty file added tests/conftest.py
Empty file.
82 changes: 82 additions & 0 deletions tests/phase0/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import pytest

from build.phase0 import spec

from tests.phase0.helpers import (
privkeys_list,
pubkeys_list,
create_genesis_state,
)


DEFAULT_CONFIG = {} # no change

MINIMAL_CONFIG = {
"SHARD_COUNT": 8,
"MIN_ATTESTATION_INCLUSION_DELAY": 2,
"TARGET_COMMITTEE_SIZE": 4,
"SLOTS_PER_EPOCH": 8,
"GENESIS_EPOCH": spec.GENESIS_SLOT // 8,
"SLOTS_PER_HISTORICAL_ROOT": 64,
"LATEST_RANDAO_MIXES_LENGTH": 64,
"LATEST_ACTIVE_INDEX_ROOTS_LENGTH": 64,
"LATEST_SLASHED_EXIT_LENGTH": 64,
}


@pytest.fixture
def privkeys():
return privkeys_list


@pytest.fixture
def pubkeys():
return pubkeys_list


def overwrite_spec_config(config):
for field in config:
setattr(spec, field, config[field])
if field == "LATEST_RANDAO_MIXES_LENGTH":
spec.BeaconState.fields['latest_randao_mixes'][1] = config[field]
elif field == "SHARD_COUNT":
spec.BeaconState.fields['latest_crosslinks'][1] = config[field]
elif field == "SLOTS_PER_HISTORICAL_ROOT":
spec.BeaconState.fields['latest_block_roots'][1] = config[field]
spec.BeaconState.fields['latest_state_roots'][1] = config[field]
spec.HistoricalBatch.fields['block_roots'][1] = config[field]
spec.HistoricalBatch.fields['state_roots'][1] = config[field]
elif field == "LATEST_ACTIVE_INDEX_ROOTS_LENGTH":
spec.BeaconState.fields['latest_active_index_roots'][1] = config[field]
elif field == "LATEST_SLASHED_EXIT_LENGTH":
spec.BeaconState.fields['latest_slashed_balances'][1] = config[field]


@pytest.fixture(
params=[
pytest.param(MINIMAL_CONFIG, marks=pytest.mark.minimal_config),
DEFAULT_CONFIG,
]
)
def config(request):
return request.param


@pytest.fixture(autouse=True)
def overwrite_config(config):
overwrite_spec_config(config)


@pytest.fixture
def num_validators():
return 100


@pytest.fixture
def deposit_data_leaves():
return list()


@pytest.fixture
def state(num_validators, deposit_data_leaves):
return create_genesis_state(num_validators, deposit_data_leaves)
Loading