Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
658007a
commit 40326f7
Showing
18 changed files
with
269 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,20 @@ | ||
env* | ||
# Bytecode | ||
**/__pycache__/ | ||
|
||
# Development | ||
env*/ | ||
.vscode/ | ||
.DS_Store | ||
|
||
# Distibution | ||
dist/ | ||
*.egg-info/ | ||
|
||
# Testing | ||
.cache/ | ||
.pytest_cache/ | ||
contract_data/ | ||
|
||
# Artifacts | ||
*.pyc | ||
build/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
class EthereumAccount(object): | ||
|
||
def __init__(self, address, key): | ||
self.address = address | ||
self.key = key |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
NULL_BYTE = b'\x00' | ||
NULL_ADDRESS = NULL_BYTE * 20 | ||
NULL_HASH = NULL_BYTE * 32 |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
def address_to_hex(address): | ||
return '0x' + address.hex() | ||
|
||
|
||
def address_to_bytes(address): | ||
return bytes.fromhex(address[2:]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
import os | ||
import json | ||
from solc import compile_standard | ||
from web3 import Web3, HTTPProvider | ||
from web3.contract import ConciseContract | ||
|
||
|
||
OUTPUT_DIR = 'contract_data' | ||
|
||
|
||
class Deployer(object): | ||
|
||
def __init__(self, contracts_dir, w3=Web3(HTTPProvider('http://localhost:8545'))): | ||
self.contracts_dir = contracts_dir | ||
self.w3 = w3 | ||
|
||
def get_solc_input(self): | ||
"""Walks the contract directory and returns a Solidity input dict | ||
Learn more about Solidity input JSON here: https://goo.gl/7zKBvj | ||
Returns: | ||
dict: A Solidity input JSON object as a dict | ||
""" | ||
|
||
solc_input = { | ||
'language': 'Solidity', | ||
'sources': { | ||
file_name: { | ||
'urls': [os.path.realpath(os.path.join(r, file_name))] | ||
} for r, d, f in os.walk(self.contracts_dir) for file_name in f | ||
}, | ||
'settings': { | ||
'outputSelection': { | ||
"*": { | ||
"": [ | ||
"legacyAST", | ||
"ast" | ||
], | ||
"*": [ | ||
"abi", | ||
"evm.bytecode.object", | ||
"evm.bytecode.sourceMap", | ||
"evm.deployedBytecode.object", | ||
"evm.deployedBytecode.sourceMap" | ||
] | ||
} | ||
} | ||
} | ||
} | ||
|
||
return solc_input | ||
|
||
def compile_all(self): | ||
"""Compiles all of the contracts in the /contracts directory | ||
Creates {contract name}.json files in /build that contain | ||
the build output for each contract. | ||
""" | ||
|
||
# Solidity input JSON | ||
solc_input = self.get_solc_input() | ||
|
||
# Compile the contracts | ||
compilation_result = compile_standard(solc_input, allow_paths=self.contracts_dir) | ||
|
||
# Create the output folder if it doesn't already exist | ||
os.makedirs(OUTPUT_DIR, exist_ok=True) | ||
|
||
# Write the contract ABI to output files | ||
compiled_contracts = compilation_result['contracts'] | ||
for contract_file in compiled_contracts: | ||
for contract in compiled_contracts[contract_file]: | ||
contract_name = contract.split('.')[0] | ||
contract_data = compiled_contracts[contract_file][contract_name] | ||
|
||
contract_data_path = OUTPUT_DIR + '/{0}.json'.format(contract_name) | ||
with open(contract_data_path, "w+") as contract_data_file: | ||
json.dump(contract_data, contract_data_file) | ||
|
||
@staticmethod | ||
def get_contract_data(contract_name): | ||
"""Returns the contract data for a given contract | ||
Args: | ||
contract_name (str): Name of the contract to return. | ||
Returns: | ||
str, str: ABI and bytecode of the contract | ||
""" | ||
|
||
contract_data_path = OUTPUT_DIR + '/{0}.json'.format(contract_name) | ||
with open(contract_data_path, 'r') as contract_data_file: | ||
contract_data = json.load(contract_data_file) | ||
|
||
abi = contract_data['abi'] | ||
bytecode = contract_data['evm']['bytecode']['object'] | ||
|
||
return abi, bytecode | ||
|
||
def deploy_contract(self, contract_name, gas=5000000, args=(), concise=True): | ||
"""Deploys a contract to the given Ethereum network using Web3 | ||
Args: | ||
contract_name (str): Name of the contract to deploy. Must already be compiled. | ||
provider (HTTPProvider): The Web3 provider to deploy with. | ||
gas (int): Amount of gas to use when creating the contract. | ||
args (obj): Any additional arguments to include with the contract creation. | ||
concise (bool): Whether to return a Contract or ConciseContract instance. | ||
Returns: | ||
Contract: A Web3 contract instance. | ||
""" | ||
|
||
abi, bytecode = self.get_contract_data(contract_name) | ||
|
||
contract = self.w3.eth.contract(abi=abi, bytecode=bytecode) | ||
|
||
# Get transaction hash from deployed contract | ||
tx_hash = contract.deploy(transaction={ | ||
'from': self.w3.eth.accounts[0], | ||
'gas': gas | ||
}, args=args) | ||
|
||
# Get tx receipt to get contract address | ||
tx_receipt = self.w3.eth.getTransactionReceipt(tx_hash) | ||
contract_address = tx_receipt['contractAddress'] | ||
|
||
contract_instance = self.w3.eth.contract(address=contract_address, abi=abi) | ||
|
||
return ConciseContract(contract_instance) if concise else contract_instance | ||
|
||
def get_contract_at_address(self, contract_name, address, concise=True): | ||
"""Returns a Web3 instance of the given contract at the given address | ||
Args: | ||
contract_name (str): Name of the contract. Must already be compiled. | ||
address (str): Address of the contract. | ||
concise (bool): Whether to return a Contract or ConciseContract instance. | ||
Returns: | ||
Contract: A Web3 contract instance. | ||
""" | ||
|
||
abi, _ = self.get_contract_data(contract_name) | ||
|
||
contract_instance = self.w3.eth.contract(abi=abi, address=address) | ||
|
||
return ConciseContract(contract_instance) if concise else contract_instance |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ | |
install_requires=[ | ||
'ethereum==2.3.0', | ||
'rlp==0.6.0', | ||
'py-solc==3.1.0' | ||
'py-solc==3.1.0', | ||
'web3==4.4.1' | ||
] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import os | ||
import pytest | ||
from ethereum import utils | ||
from ethereum.abi import ContractTranslator | ||
from ethereum.tools import tester | ||
from ethereum.config import config_metropolis | ||
from plasma_core.account import EthereumAccount | ||
from plasma_core.utils.deployer import Deployer | ||
from plasma_core.utils.address import address_to_hex | ||
|
||
|
||
GAS_LIMIT = 8000000 | ||
START_GAS = GAS_LIMIT - 1000000 | ||
config_metropolis['BLOCK_GAS_LIMIT'] = GAS_LIMIT | ||
|
||
|
||
OWN_DIR = os.path.dirname(os.path.realpath(__file__)) | ||
CONTRACTS_DIR = os.path.abspath(os.path.realpath(os.path.join(OWN_DIR, '../plasma/contracts'))) | ||
deployer = Deployer(CONTRACTS_DIR) | ||
deployer.compile_all() | ||
|
||
|
||
@pytest.fixture | ||
def ethutils(): | ||
return utils | ||
|
||
|
||
@pytest.fixture | ||
def ethtester(): | ||
tester.chain = tester.Chain() | ||
tester.accounts = [] | ||
for i in range(10): | ||
address = getattr(tester, 'a{0}'.format(i)) | ||
key = getattr(tester, 'k{0}'.format(i)) | ||
tester.accounts.append(EthereumAccount(address_to_hex(address), key)) | ||
return tester | ||
|
||
|
||
@pytest.fixture | ||
def get_contract(ethtester, ethutils): | ||
def create_contract(path, args=(), sender=ethtester.k0): | ||
abi, hexcode = deployer.get_contract_data(path) | ||
bytecode = ethutils.decode_hex(hexcode) | ||
encoded_args = (ContractTranslator(abi).encode_constructor_arguments(args) if args else b'') | ||
code = bytecode + encoded_args | ||
address = ethtester.chain.tx(sender=sender, to=b'', startgas=START_GAS, data=code) | ||
return ethtester.ABIContract(ethtester.chain, abi, address) | ||
return create_contract | ||
|
||
|
||
@pytest.fixture | ||
def root_chain(ethtester, get_contract): | ||
contract = get_contract('RootChain') | ||
ethtester.chain.mine() | ||
return contract |
27 changes: 27 additions & 0 deletions
27
tests/contracts/root_chain/test_commit_plasma_block_root.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import pytest | ||
from ethereum.tools.tester import TransactionFailed | ||
from plasma_core.constants import NULL_HASH | ||
|
||
|
||
def test_commit_plasma_block_root_should_succeed(root_chain, ethtester, ethutils): | ||
random_hash = ethutils.sha3('abc123') | ||
operator = ethtester.accounts[0] | ||
root_chain.commitPlasmaBlockRoot(random_hash, sender=operator.key) | ||
|
||
plasma_block_root = root_chain.plasmaBlockRoots(0) | ||
assert plasma_block_root[0] == random_hash | ||
assert plasma_block_root[1] == ethtester.chain.head_state.timestamp | ||
assert root_chain.currentPlasmaBlockNumber() == 1 | ||
|
||
|
||
def test_commit_plasma_block_root_not_operator_should_fail(root_chain, ethtester, ethutils): | ||
random_hash = ethutils.sha3('abc123') | ||
non_operator = ethtester.accounts[1] | ||
|
||
with pytest.raises(TransactionFailed): | ||
root_chain.commitPlasmaBlockRoot(random_hash, sender=non_operator.key) | ||
|
||
plasma_block_root = root_chain.plasmaBlockRoots(0) | ||
assert plasma_block_root[0] == NULL_HASH | ||
assert plasma_block_root[1] == 0 | ||
assert root_chain.currentPlasmaBlockNumber() == 0 |