Skip to content

Commit

Permalink
Merge pull request #800 from ethereum/ci
Browse files Browse the repository at this point in the history
ci tests
  • Loading branch information
djrtwo authored Mar 19, 2019
2 parents aeb5bb9 + fbd0bb9 commit ab197d9
Show file tree
Hide file tree
Showing 20 changed files with 1,243 additions and 0 deletions.
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

0 comments on commit ab197d9

Please sign in to comment.