[KMS-ETH]- Fixtures for class based API, unit tests, and smart contract tests from @szotov, code reorg.

pull/195/head^2
Kieran R Prasch 2018-02-04 19:42:29 -08:00 committed by Kieran Prasch
parent 8eb301a598
commit 875814c614
16 changed files with 791 additions and 212 deletions

View File

@ -7,7 +7,7 @@ sudo pip3 install pipenv
pipenv --three
VENV="$(pipenv --venv)"
# Sol
# Sol - TODO: integrity
wget https://github.com/ethereum/solidity/releases/download/v0.4.19/solc-static-linux -O "${VENV}/bin/solc"
chmod +x "${VENV}/bin/solc"

View File

@ -6,6 +6,17 @@ from os.path import dirname, join, abspath
class Blockchain:
"""
http://populus.readthedocs.io/en/latest/config.html#chains
mainnet: Connects to the public ethereum mainnet via geth.
ropsten: Connects to the public ethereum ropsten testnet via geth.
tester: Uses an ephemeral in-memory chain backed by pyethereum.
testrpc: Uses an ephemeral in-memory chain backed by pyethereum.
temp: Local private chain whos data directory is removed when the chain is shutdown. Runs via geth.
"""
network = 'mainnetrpc'
project_name = 'nucypher-kms'
_project = threading.local()
@ -21,7 +32,7 @@ class Blockchain:
# Populus project config
project_dir = join(dirname(abspath(nkms_eth.__file__)), 'project')
project = populus.Project(project_dir)
project.config['chains.mainnetrpc']['contracts']['backends']['JSONFile']['settings']['file_path'] = self.registrar_path
project.config['chains.mainnetrpc.contracts.backends.JSONFile.settings.file_path'] = self.registrar_path
self.project_dir = project_dir
self._project.project = project

View File

@ -1,20 +1,16 @@
from nkms_eth.token import NuCypherKMSToken
from nkms_eth.blockchain import Blockchain
from nkms_eth.escrow import Escrow
class Miner:
"""Practically carrying a pickaxe"""
def __init__(self, blockchain, escrow=None):
def __init__(self, blockchain, token, escrow):
self.blockchain = blockchain
if not escrow:
escrow = Escrow.get(blockchain=blockchain)
self.escrow = escrow
self.token = token
def lock(self, amount: int, locktime: int, address: str=None):
"""
Deposit and lock coins for mining. Creating coins starts after it is done
Deposit and lock coins for mining.
Creating coins starts after it is done.
:param amount: Amount of coins to lock (in smallest indivisible units)
:param locktime: Locktime in periods
@ -36,14 +32,16 @@ class Miner:
def mine(self, address: str=None):
with self.blockchain as chain:
address = address or chain.web3.eth.accounts[0]
if not address:
address = chain.web3.eth.accounts[0]
tx = self.escrow.contract.transact({'from': address}).mint()
chain.wait.for_receipt(tx, timeout=self.blockchain.timeout)
def withdraw(self, address: str=None):
with self.blockchain as chain:
address = address or chain.web3.eth.accounts[0]
if not address:
address = chain.web3.eth.accounts[0]
tx = self.escrow.contract.transact({'from': address}).withdrawAll()
chain.wait.for_receipt(tx, timeout=self.blockchain.timeout)

View File

@ -1,11 +1,13 @@
#!/usr/bin/env python3
"""Deploy contracts in tester.
"""
Deploy contracts in tester.
A simple Python script to deploy contracts and then estimate gas for different methods.
"""
import random
from nkms_eth.blockchain import project
from nkms_eth.blockchain import Blockchain
TIMEOUT = 10
MINING_COEFF = [10 ** 5, 10 ** 7]
@ -29,9 +31,7 @@ def main():
# Create an ERC20 token
token, tx = chain.provider.get_or_deploy_contract(
'HumanStandardToken', deploy_args=[
int(1e9) * M, int(1e10) * M, 'NuCypher KMS', 6, 'KMS'],
deploy_transaction={
'from': creator})
int(1e9) * M, int(1e10) * M, 'NuCypher KMS', 6, 'KMS'],deploy_transaction={'from': creator})
chain.wait.for_receipt(tx, timeout=TIMEOUT)
print("Deployed HumanStandardToken, tx hash is", tx)

View File

@ -1,14 +1,25 @@
import pytest
from nkms_eth import blockchain
from nkms_eth.blockchain import TesterBlockchain
from nkms_eth.token import NuCypherKMSToken
from nkms_eth.escrow import Escrow
from nkms_eth.miner import Miner
@pytest.fixture()
def project():
return blockchain.project()
def testerchain():
return TesterBlockchain()
@pytest.fixture()
def chain():
blockchain.DEFAULT_NETWORK = 'tester'
yield blockchain.chain()
blockchain.disconnect()
def token(testerchain):
return NuCypherKMSToken(blockchain=testerchain)
@pytest.fixture()
def escrow(testerchain, token):
return Escrow(blockchain=testerchain, token=token)
@pytest.fixture()
def miner(testerchain, escrow):
return Miner(blockchain=testerchain, escrow=escrow)

View File

@ -0,0 +1,453 @@
import pytest
from ethereum.tester import TransactionFailed
@pytest.fixture()
def token(web3, chain):
creator = web3.eth.accounts[0]
# Create an ERC20 token
token, _ = chain.provider.get_or_deploy_contract(
'NuCypherKMSToken', deploy_args=[10 ** 9, 2 * 10 ** 9],
deploy_transaction={'from': creator})
return token
@pytest.fixture()
def escrow(web3, chain, token):
creator = web3.eth.accounts[0]
# Creator deploys the escrow
escrow, _ = chain.provider.get_or_deploy_contract(
'Escrow', deploy_args=[token.address, 1, 4 * 2 * 10 ** 7, 4, 4, 2],
deploy_transaction={'from': creator})
return escrow
# TODO extract method
def wait_time(chain, wait_hours):
web3 = chain.web3
step = 50
end_timestamp = web3.eth.getBlock(web3.eth.blockNumber).timestamp + wait_hours * 60 * 60
while web3.eth.getBlock(web3.eth.blockNumber).timestamp < end_timestamp:
chain.wait.for_block(web3.eth.blockNumber + step)
def test_escrow(web3, chain, token, escrow):
creator = web3.eth.accounts[0]
ursula = web3.eth.accounts[1]
alice = web3.eth.accounts[2]
# Give Ursula and Alice some coins
tx = token.transact({'from': creator}).transfer(ursula, 10000)
chain.wait.for_receipt(tx)
tx = token.transact({'from': creator}).transfer(alice, 10000)
chain.wait.for_receipt(tx)
assert 10000 == token.call().balanceOf(ursula)
assert 10000 == token.call().balanceOf(alice)
# Ursula and Alice give Escrow rights to transfer
tx = token.transact({'from': ursula}).approve(escrow.address, 2500)
chain.wait.for_receipt(tx)
assert 2500 == token.call().allowance(ursula, escrow.address)
tx = token.transact({'from': alice}).approve(escrow.address, 1100)
chain.wait.for_receipt(tx)
assert 1100 == token.call().allowance(alice, escrow.address)
# Ursula's withdrawal attempt won't succeed because nothing to withdraw
with pytest.raises(TransactionFailed):
tx = escrow.transact({'from': ursula}).withdraw(100)
chain.wait.for_receipt(tx)
# And can't lock because nothing to lock
with pytest.raises(TransactionFailed):
tx = escrow.transact({'from': ursula}).lock(500, 2)
chain.wait.for_receipt(tx)
# Check that nothing is locked
assert 0 == escrow.call().getLockedTokens(ursula)
assert 0 == escrow.call().getLockedTokens(alice)
# Ursula can't lock too low value
# TODO uncomment after completing logic
# with pytest.raises(TransactionFailed):
# tx = escrow.transact({'from': ursula}).deposit(1000, 10)
# chain.wait.for_receipt(tx)
# Ursula and Alice transfer some tokens to the escrow and lock them
tx = escrow.transact({'from': ursula}).deposit(1000, 1)
chain.wait.for_receipt(tx)
assert 1000 == token.call().balanceOf(escrow.address)
assert 9000 == token.call().balanceOf(ursula)
assert 1000 == escrow.call().getLockedTokens(ursula)
assert 1000 == escrow.call().calculateLockedTokens(ursula, 1)
assert 1000 == escrow.call().calculateLockedTokens(ursula, 2)
tx = escrow.transact({'from': ursula}).switchLock()
chain.wait.for_receipt(tx)
assert 500 == escrow.call().calculateLockedTokens(ursula, 2)
tx = escrow.transact({'from': ursula}).switchLock()
chain.wait.for_receipt(tx)
assert 1000 == escrow.call().calculateLockedTokens(ursula, 2)
tx = escrow.transact({'from': alice}).deposit(500, 2)
chain.wait.for_receipt(tx)
assert 1500 == token.call().balanceOf(escrow.address)
assert 9500 == token.call().balanceOf(alice)
assert 500 == escrow.call().getLockedTokens(alice)
assert 500 == escrow.call().calculateLockedTokens(alice, 1)
# Checks locked tokens in next period
wait_time(chain, 1)
assert 1000 == escrow.call().getLockedTokens(ursula)
assert 500 == escrow.call().getLockedTokens(alice)
assert 1500 == escrow.call().getAllLockedTokens()
# Ursula's withdrawal attempt won't succeed
with pytest.raises(TransactionFailed):
tx = escrow.transact({'from': ursula}).withdraw(100)
chain.wait.for_receipt(tx)
assert 1500 == token.call().balanceOf(escrow.address)
assert 9000 == token.call().balanceOf(ursula)
# Ursula can deposit more tokens
tx = escrow.transact({'from': ursula}).confirmActivity()
chain.wait.for_receipt(tx)
tx = escrow.transact({'from': ursula}).deposit(500, 0)
chain.wait.for_receipt(tx)
assert 2000 == token.call().balanceOf(escrow.address)
assert 8500 == token.call().balanceOf(ursula)
# Ursula starts unlocking
tx = escrow.transact({'from': ursula}).switchLock()
chain.wait.for_receipt(tx)
assert 750 == escrow.call().calculateLockedTokens(ursula, 2)
# Wait 1 period and checks locking
wait_time(chain, 1)
assert 1500 == escrow.call().getLockedTokens(ursula)
# Confirm activity and wait 1 period
tx = escrow.transact({'from': ursula}).confirmActivity()
chain.wait.for_receipt(tx)
wait_time(chain, 1)
assert 750 == escrow.call().getLockedTokens(ursula)
assert 0 == escrow.call().calculateLockedTokens(ursula, 1)
# And Ursula can withdraw some tokens
tx = escrow.transact({'from': ursula}).withdraw(100)
chain.wait.for_receipt(tx)
assert 1900 == token.call().balanceOf(escrow.address)
assert 8600 == token.call().balanceOf(ursula)
# But Ursula can't withdraw all without mining for locked value
with pytest.raises(TransactionFailed):
tx = escrow.transact({'from': ursula}).withdrawAll()
chain.wait.for_receipt(tx)
# Ursula can deposit and lock more tokens
tx = escrow.transact({'from': ursula}).deposit(500, 0)
chain.wait.for_receipt(tx)
tx = escrow.transact({'from': ursula}).lock(100, 0)
chain.wait.for_receipt(tx)
# Locked tokens will be updated in next period
# Release rate will be updated too because of end of previous locking
assert 750 == escrow.call().getLockedTokens(ursula)
assert 600 == escrow.call().calculateLockedTokens(ursula, 1)
assert 600 == escrow.call().calculateLockedTokens(ursula, 2)
tx = escrow.transact({'from': ursula}).switchLock()
chain.wait.for_receipt(tx)
assert 300 == escrow.call().calculateLockedTokens(ursula, 2)
assert 0 == escrow.call().calculateLockedTokens(ursula, 3)
wait_time(chain, 1)
assert 600 == escrow.call().getLockedTokens(ursula)
assert 300 == escrow.call().calculateLockedTokens(ursula, 1)
assert 0 == escrow.call().calculateLockedTokens(ursula, 2)
# Ursula can increase lock
tx = escrow.transact({'from': ursula}).lock(500, 2)
chain.wait.for_receipt(tx)
assert 600 == escrow.call().getLockedTokens(ursula)
assert 800 == escrow.call().calculateLockedTokens(ursula, 1)
assert 500 == escrow.call().calculateLockedTokens(ursula, 2)
assert 200 == escrow.call().calculateLockedTokens(ursula, 3)
assert 0 == escrow.call().calculateLockedTokens(ursula, 4)
wait_time(chain, 1)
assert 800 == escrow.call().getLockedTokens(ursula)
# Alice can't deposit too low value (less then rate)
# TODO uncomment after completing logic
# with pytest.raises(TransactionFailed):
# tx = escrow.transact({'from': ursula}).deposit(100, 100)
# chain.wait.for_receipt(tx)
# Alice starts unlocking and increases lock by deposit more tokens
tx = escrow.transact({'from': alice}).deposit(500, 0)
chain.wait.for_receipt(tx)
tx = escrow.transact({'from': alice}).switchLock()
chain.wait.for_receipt(tx)
assert 500 == escrow.call().getLockedTokens(alice)
assert 1000 == escrow.call().calculateLockedTokens(alice, 1)
assert 500 == escrow.call().calculateLockedTokens(alice, 2)
assert 0 == escrow.call().calculateLockedTokens(alice, 3)
wait_time(chain, 1)
assert 1000 == escrow.call().getLockedTokens(alice)
# And increases locked time
tx = escrow.transact({'from': alice}).lock(0, 2)
chain.wait.for_receipt(tx)
assert 1000 == escrow.call().getLockedTokens(alice)
assert 500 == escrow.call().calculateLockedTokens(alice, 1)
assert 0 == escrow.call().calculateLockedTokens(alice, 2)
# Alice increases lock by small amount of tokens
tx = escrow.transact({'from': alice}).deposit(100, 0)
chain.wait.for_receipt(tx)
assert 600 == escrow.call().calculateLockedTokens(alice, 1)
assert 100 == escrow.call().calculateLockedTokens(alice, 2)
assert 0 == escrow.call().calculateLockedTokens(alice, 3)
# # Ursula can't destroy contract
# with pytest.raises(TransactionFailed):
# tx = escrow.transact({'from': ursula}).destroy()
# chain.wait.for_receipt(tx)
#
# # Destroy contract from creator and refund all to Ursula and Alice
# tx = escrow.transact({'from': creator}).destroy()
# chain.wait.for_receipt(tx)
# assert 0 == token.call().balanceOf(escrow.address)
# assert 10000 == token.call().balanceOf(ursula)
# assert 10000 == token.call().balanceOf(alice)
def test_locked_distribution(web3, chain, token, escrow):
NULL_ADDR = '0x' + '0' * 40
creator = web3.eth.accounts[0]
miners = web3.eth.accounts[1:]
amount = token.call().balanceOf(creator) // 2
largest_locked = amount
# Airdrop
for miner in miners:
tx = token.transact({'from': creator}).transfer(miner, amount)
chain.wait.for_receipt(tx)
amount = amount // 2
# Lock
for index, miner in enumerate(miners[::-1]):
balance = token.call().balanceOf(miner)
tx = token.transact({'from': miner}).approve(escrow.address, balance)
chain.wait.for_receipt(tx)
tx = escrow.transact({'from': miner}).deposit(balance, len(miners) - index + 1)
chain.wait.for_receipt(tx)
# Check current period
address_stop, shift = escrow.call().findCumSum(NULL_ADDR, 1, 1)
assert NULL_ADDR == address_stop.lower()
assert 0 == shift
# Wait next period
wait_time(chain, 1)
n_locked = escrow.call().getAllLockedTokens()
assert n_locked > 0
# And confirm activity
for miner in miners:
tx = escrow.transact({'from': miner}).confirmActivity()
chain.wait.for_receipt(tx)
address_stop, shift = escrow.call().findCumSum(NULL_ADDR, n_locked // 3, 1)
assert miners[0].lower() == address_stop.lower()
assert n_locked // 3 == shift
address_stop, shift = escrow.call().findCumSum(NULL_ADDR, largest_locked, 1)
assert miners[1].lower() == address_stop.lower()
assert 0 == shift
address_stop, shift = escrow.call().findCumSum(
miners[1], largest_locked // 2 + 1, 1)
assert miners[2].lower() == address_stop.lower()
assert 1 == shift
address_stop, shift = escrow.call().findCumSum(NULL_ADDR, 1, 10)
assert NULL_ADDR != address_stop.lower()
assert 0 != shift
address_stop, shift = escrow.call().findCumSum(NULL_ADDR, 1, 11)
assert NULL_ADDR == address_stop.lower()
assert 0 == shift
for index, _ in enumerate(miners[:-1]):
address_stop, shift = escrow.call().findCumSum(NULL_ADDR, 1, index + 3)
assert miners[index + 1].lower() == address_stop.lower()
assert 1 == shift
def test_mining(web3, chain, token, escrow):
creator = web3.eth.accounts[0]
ursula = web3.eth.accounts[1]
alice = web3.eth.accounts[2]
policy_manager, _ = chain.provider.get_or_deploy_contract(
'PolicyManagerTest', deploy_args=[token.address, escrow.address],
deploy_transaction={'from': creator})
tx = escrow.transact({'from': creator}).setPolicyManager(policy_manager.address)
chain.wait.for_receipt(tx)
# Give Ursula and Alice some coins
tx = token.transact({'from': creator}).transfer(ursula, 10000)
chain.wait.for_receipt(tx)
tx = token.transact({'from': creator}).transfer(alice, 10000)
chain.wait.for_receipt(tx)
# Ursula can't confirm and mint because no locked tokens
with pytest.raises(TransactionFailed):
tx = escrow.transact({'from': ursula}).mint()
chain.wait.for_receipt(tx)
with pytest.raises(TransactionFailed):
tx = escrow.transact({'from': ursula}).confirmActivity()
chain.wait.for_receipt(tx)
# Ursula and Alice give Escrow rights to transfer
tx = token.transact({'from': ursula}).approve(escrow.address, 2000)
chain.wait.for_receipt(tx)
tx = token.transact({'from': alice}).approve(escrow.address, 500)
chain.wait.for_receipt(tx)
# Ursula and Alice transfer some tokens to the escrow and lock them
tx = escrow.transact({'from': ursula}).deposit(1000, 1)
chain.wait.for_receipt(tx)
tx = escrow.transact({'from': alice}).deposit(500, 2)
chain.wait.for_receipt(tx)
# Using locked tokens starts from next period
assert 0 == escrow.call().getAllLockedTokens()
# Give rights for mining
tx = token.transact({'from': creator}).addMiner(escrow.address)
chain.wait.for_receipt(tx)
assert token.call().isMiner(escrow.address)
# Ursula can't use method from Miner contract
with pytest.raises(TypeError):
tx = escrow.transact({'from': ursula}).mint(ursula, 1, 1, 1, 1, 1)
chain.wait.for_receipt(tx)
# Only Ursula confirm next period
wait_time(chain, 1)
assert 1500 == escrow.call().getAllLockedTokens()
tx = escrow.transact({'from': ursula}).confirmActivity()
chain.wait.for_receipt(tx)
# Checks that no error
tx = escrow.transact({'from': ursula}).confirmActivity()
chain.wait.for_receipt(tx)
# Ursula and Alice mint tokens for last periods
wait_time(chain, 1)
assert 1000 == escrow.call().getAllLockedTokens()
tx = escrow.transact({'from': ursula}).mint()
chain.wait.for_receipt(tx)
tx = escrow.transact({'from': alice}).mint()
chain.wait.for_receipt(tx)
assert 9050 == token.call().balanceOf(ursula)
assert 9521 == token.call().balanceOf(alice)
assert 1 == policy_manager.call().getPeriodsLength(ursula)
assert 1 == policy_manager.call().getPeriodsLength(alice)
period = escrow.call().getCurrentPeriod() - 1
assert period == policy_manager.call().getPeriod(ursula, 0)
assert period == policy_manager.call().getPeriod(alice, 0)
# Only Ursula confirm activity for next period
tx = escrow.transact({'from': ursula}).switchLock()
chain.wait.for_receipt(tx)
tx = escrow.transact({'from': ursula}).confirmActivity()
chain.wait.for_receipt(tx)
# Ursula can't confirm next period because end of locking
wait_time(chain, 1)
assert 500 == escrow.call().getAllLockedTokens()
with pytest.raises(TransactionFailed):
tx = escrow.transact({'from': ursula}).confirmActivity()
chain.wait.for_receipt(tx)
# But Alice can
tx = escrow.transact({'from': alice}).confirmActivity()
chain.wait.for_receipt(tx)
# Ursula mint tokens for next period
wait_time(chain, 1)
assert 500 == escrow.call().getAllLockedTokens()
tx = escrow.transact({'from': ursula}).mint()
chain.wait.for_receipt(tx)
# But Alice can't mining because she did not confirmed activity
with pytest.raises(TransactionFailed):
tx = escrow.transact({'from': alice}).mint()
chain.wait.for_receipt(tx)
assert 9163 == token.call().balanceOf(ursula)
assert 9521 == token.call().balanceOf(alice)
assert 3 == policy_manager.call().getPeriodsLength(ursula)
assert 1 == policy_manager.call().getPeriodsLength(alice)
assert period + 1 == policy_manager.call().getPeriod(ursula, 1)
assert period + 2 == policy_manager.call().getPeriod(ursula, 2)
# Alice confirm next period and mint tokens
tx = escrow.transact({'from': alice}).switchLock()
chain.wait.for_receipt(tx)
tx = escrow.transact({'from': alice}).confirmActivity()
chain.wait.for_receipt(tx)
wait_time(chain, 2)
assert 0 == escrow.call().getAllLockedTokens()
tx = escrow.transact({'from': alice}).mint()
chain.wait.for_receipt(tx)
assert 9163 == token.call().balanceOf(ursula)
assert 9634 == token.call().balanceOf(alice)
assert 3 == policy_manager.call().getPeriodsLength(ursula)
assert 3 == policy_manager.call().getPeriodsLength(alice)
assert period + 3 == policy_manager.call().getPeriod(alice, 1)
assert period + 4 == policy_manager.call().getPeriod(alice, 2)
# Ursula can't confirm and mint because no locked tokens
with pytest.raises(TransactionFailed):
tx = escrow.transact({'from': ursula}).mint()
chain.wait.for_receipt(tx)
with pytest.raises(TransactionFailed):
tx = escrow.transact({'from': ursula}).confirmActivity()
chain.wait.for_receipt(tx)
# Ursula can lock some tokens again
tx = escrow.transact({'from': ursula}).lock(500, 4)
chain.wait.for_receipt(tx)
tx = escrow.transact({'from': ursula}).switchLock()
chain.wait.for_receipt(tx)
assert 500 == escrow.call().getLockedTokens(ursula)
assert 500 == escrow.call().calculateLockedTokens(ursula, 1)
assert 375 == escrow.call().calculateLockedTokens(ursula, 2)
assert 250 == escrow.call().calculateLockedTokens(ursula, 3)
assert 0 == escrow.call().calculateLockedTokens(ursula, 5)
# And can increase lock
tx = escrow.transact({'from': ursula}).lock(100, 0)
chain.wait.for_receipt(tx)
assert 600 == escrow.call().getLockedTokens(ursula)
assert 600 == escrow.call().calculateLockedTokens(ursula, 1)
assert 450 == escrow.call().calculateLockedTokens(ursula, 2)
assert 0 == escrow.call().calculateLockedTokens(ursula, 5)
tx = escrow.transact({'from': ursula}).lock(0, 2)
chain.wait.for_receipt(tx)
assert 600 == escrow.call().getLockedTokens(ursula)
assert 600 == escrow.call().calculateLockedTokens(ursula, 1)
assert 450 == escrow.call().calculateLockedTokens(ursula, 2)
assert 0 == escrow.call().calculateLockedTokens(ursula, 5)
tx = escrow.transact({'from': ursula}).deposit(800, 1)
chain.wait.for_receipt(tx)
assert 1400 == escrow.call().getLockedTokens(ursula)
assert 1400 == escrow.call().calculateLockedTokens(ursula, 1)
assert 1000 == escrow.call().calculateLockedTokens(ursula, 3)
assert 400 == escrow.call().calculateLockedTokens(ursula, 6)
assert 0 == escrow.call().calculateLockedTokens(ursula, 8)
# Alice can withdraw all
tx = escrow.transact({'from': alice}).withdrawAll()
chain.wait.for_receipt(tx)
assert 10134 == token.call().balanceOf(alice)
# TODO test max confirmed periods and miners

View File

@ -0,0 +1,83 @@
import pytest
from ethereum.tester import TransactionFailed
@pytest.fixture()
def token(web3, chain):
creator = web3.eth.accounts[0]
# Create an ERC20 token
token, _ = chain.provider.get_or_deploy_contract(
'NuCypherKMSToken', deploy_args=[10 ** 30, 2 * 10 ** 40],
deploy_transaction={'from': creator})
return token
def test_miner(web3, chain, token):
creator = web3.eth.accounts[0]
ursula = web3.eth.accounts[1]
# Creator deploys the miner
miner, _ = chain.provider.get_or_deploy_contract(
'MinerTest', deploy_args=[token.address, 1, 10 ** 46, 10 ** 7, 10 ** 7],
deploy_transaction={'from': creator})
# Give rights for mining
tx = token.transact({'from': creator}).addMiner(miner.address)
chain.wait.for_receipt(tx)
# Mint some tokens
tx = miner.transact().testMint(ursula, 0, 1000, 2000, 0, 0)
chain.wait.for_receipt(tx)
assert 10 == token.call().balanceOf(ursula)
assert 10 ** 30 + 10 == token.call().totalSupply()
# Mint more tokens
tx = miner.transact().testMint(ursula, 0, 500, 500, 0, 0)
chain.wait.for_receipt(tx)
assert 30 == token.call().balanceOf(ursula)
assert 10 ** 30 + 30 == token.call().totalSupply()
tx = miner.transact().testMint(ursula, 0, 500, 500, 10 ** 7, 0)
chain.wait.for_receipt(tx)
assert 70 == token.call().balanceOf(ursula)
assert 10 ** 30 + 70 == token.call().totalSupply()
tx = miner.transact().testMint(ursula, 0, 500, 500, 2 * 10 ** 7, 0)
chain.wait.for_receipt(tx)
assert 110 == token.call().balanceOf(ursula)
assert 10 ** 30 + 110 == token.call().totalSupply()
def test_inflation_rate(web3, chain, token):
creator = web3.eth.accounts[0]
ursula = web3.eth.accounts[1]
# Creator deploys the miner
miner, _ = chain.provider.get_or_deploy_contract(
'MinerTest', deploy_args=[token.address, 1, 2 * 10 ** 19, 1, 1],
deploy_transaction={'from': creator})
# Give rights for mining
tx = token.transact({'from': creator}).addMiner(miner.address)
chain.wait.for_receipt(tx)
# Mint some tokens
tx = miner.transact().testMint(ursula, 1, 1, 1, 0, 0)
chain.wait.for_receipt(tx)
one_period = token.call().balanceOf(ursula)
# Mint more tokens in the same period
tx = miner.transact().testMint(ursula, 1, 1, 1, 0, 0)
chain.wait.for_receipt(tx)
assert 2 * one_period == token.call().balanceOf(ursula)
# Mint tokens in the next period
tx = miner.transact().testMint(ursula, 2, 1, 1, 0, 0)
chain.wait.for_receipt(tx)
assert 3 * one_period > token.call().balanceOf(ursula)
minted_amount = token.call().balanceOf(ursula) - 2 * one_period
# Mint tokens in the next period
tx = miner.transact().testMint(ursula, 3, 1, 1, 0, 0)
chain.wait.for_receipt(tx)
assert 2 * one_period + 2 * minted_amount > token.call().balanceOf(ursula)

View File

@ -3,13 +3,14 @@ from ethereum.tester import TransactionFailed
@pytest.fixture()
def token(web3, chain):
creator = web3.eth.accounts[0]
# Create an ERC20 token
token, _ = chain.provider.get_or_deploy_contract(
'NuCypherKMSToken', deploy_args=[2 * 10 ** 9],
deploy_transaction={'from': creator})
return token
def token(web3, testerchain):
with testerchain as chain:
creator = web3.eth.accounts[0]
# Create an ERC20 token
token, _ = chain.provider.get_or_deploy_contract(
'NuCypherKMSToken', deploy_args=[10 ** 9, 2 * 10 ** 9],
deploy_transaction={'from': creator})
return token
@pytest.fixture()
@ -18,7 +19,7 @@ def escrow(web3, chain):
node = web3.eth.accounts[1]
# Creator deploys the escrow
escrow, _ = chain.provider.get_or_deploy_contract(
'MinersEscrowForPolicyTest', deploy_args=[node, MINUTES_IN_PERIOD],
'EscrowTest', deploy_args=[node, MINUTES_IN_PERIOD],
deploy_transaction={'from': creator})
return escrow
@ -61,58 +62,60 @@ rate = 20
number_of_periods = 10
def test_create_revoke(web3, chain, token, escrow, policy_manager):
creator = web3.eth.accounts[0]
node = web3.eth.accounts[1]
client = web3.eth.accounts[2]
bad_node = web3.eth.accounts[3]
def test_create_revoke(web3, testerchain, token, escrow, policy_manager):
with testerchain as chain:
# Try create policy for bad node
with pytest.raises(TransactionFailed):
tx = policy_manager.transact({'from': client}).createPolicy(policy_id, bad_node, 1, 1)
chain.wait.for_receipt(tx)
creator = web3.eth.accounts[0]
node = web3.eth.accounts[1]
client = web3.eth.accounts[2]
bad_node = web3.eth.accounts[3]
# Create policy
period = escrow.call().getCurrentPeriod()
tx = policy_manager.transact({'from': client}).createPolicy(policy_id, node, rate, number_of_periods)
chain.wait.for_receipt(tx)
policy = policy_manager.call().policies(policy_id)
assert 200 == token.call().balanceOf(policy_manager.address)
assert 9800 == token.call().balanceOf(client)
assert client == policy[0]
assert node == policy[1]
assert rate == policy[2]
assert period + 1 == policy[3]
assert period + 10 == policy[4]
# Try create policy for bad node
with pytest.raises(TransactionFailed):
tx = policy_manager.transact({'from': client}).createPolicy(policy_id, bad_node, 1, 1)
chain.wait.for_receipt(tx)
# Try to create policy again
with pytest.raises(TransactionFailed):
# Create policy
period = escrow.call().getCurrentPeriod()
tx = policy_manager.transact({'from': client}).createPolicy(policy_id, node, rate, number_of_periods)
chain.wait.for_receipt(tx)
policy = policy_manager.call().policies(policy_id)
assert 200 == token.call().balanceOf(policy_manager.address)
assert 9800 == token.call().balanceOf(client)
assert client == policy[0]
assert node == policy[1]
assert rate == policy[2]
assert period + 1 == policy[3]
assert period + 10 == policy[4]
# Not client try to revoke policy
with pytest.raises(TransactionFailed):
tx = policy_manager.transact({'from': creator}).revokePolicy(policy_id)
# Try to create policy again
with pytest.raises(TransactionFailed):
tx = policy_manager.transact({'from': client}).createPolicy(policy_id, node, rate, number_of_periods)
chain.wait.for_receipt(tx)
# Not client try to revoke policy
with pytest.raises(TransactionFailed):
tx = policy_manager.transact({'from': creator}).revokePolicy(policy_id)
chain.wait.for_receipt(tx)
# Client try to revoke policy
tx = policy_manager.transact({'from': client}).revokePolicy(policy_id)
chain.wait.for_receipt(tx)
policy = policy_manager.call().policies(policy_id)
assert '0x' + '0' * 40 == policy[0]
# Client try to revoke policy
tx = policy_manager.transact({'from': client}).revokePolicy(policy_id)
chain.wait.for_receipt(tx)
policy = policy_manager.call().policies(policy_id)
assert '0x' + '0' * 40 == policy[0]
# Create another policy
period = escrow.call().getCurrentPeriod()
tx = policy_manager.transact({'from': client}).createPolicy(policy_id_2, node, rate, number_of_periods)
chain.wait.for_receipt(tx)
policy = policy_manager.call().policies(policy_id_2)
assert 200 == token.call().balanceOf(policy_manager.address)
assert 9800 == token.call().balanceOf(client)
assert client == policy[0]
assert node == policy[1]
assert rate == policy[2]
assert period + 1 == policy[3]
assert period + 10 == policy[4]
# Create another policy
period = escrow.call().getCurrentPeriod()
tx = policy_manager.transact({'from': client}).createPolicy(policy_id_2, node, rate, number_of_periods)
chain.wait.for_receipt(tx)
policy = policy_manager.call().policies(policy_id_2)
assert 200 == token.call().balanceOf(policy_manager.address)
assert 9800 == token.call().balanceOf(client)
assert client == policy[0]
assert node == policy[1]
assert rate == policy[2]
assert period + 1 == policy[3]
assert period + 10 == policy[4]
def test_reward(web3, chain, token, escrow, policy_manager):

View File

@ -1,57 +0,0 @@
import pytest
from ethereum.tester import TransactionFailed
def test_create_token(web3, chain):
"""
These are tests for standard tokens taken from Consensys github:
https://github.com/ConsenSys/Tokens/
but some of the tests are converted from javascript to python
"""
creator = web3.eth.accounts[1]
account1 = web3.eth.accounts[0]
account2 = web3.eth.accounts[2]
# Create an ERC20 token
token, txhash = chain.provider.get_or_deploy_contract(
'NuCypherKMSToken', deploy_args=[10 ** 9],
deploy_transaction={
'from': creator})
assert txhash is not None
# Account balances
assert token.call().balanceOf(creator) == 10 ** 9
assert token.call().balanceOf(account1) == 0
# Basic properties
assert token.call().name() == 'NuCypher KMS'
assert token.call().decimals() == 18
assert token.call().symbol() == 'KMS'
# Cannot send ethers to the contract
with pytest.raises(TransactionFailed):
tx = web3.eth.sendTransaction({
'from': account1, 'to': token.address, 'value': 10 ** 9})
chain.wait.for_receipt(tx)
# Can transfer tokens
tx = token.transact({'from': creator}).transfer(account1, 10000)
chain.wait.for_receipt(tx)
assert token.call().balanceOf(account1) == 10000
assert token.call().balanceOf(creator) == 10 ** 9 - 10000
tx = token.transact({'from': account1}).transfer(account2, 10)
chain.wait.for_receipt(tx)
assert token.call().balanceOf(account1) == 10000 - 10
assert token.call().balanceOf(account2) == 10
tx = token.transact({'from': account1}).transfer(token.address, 10)
chain.wait.for_receipt(tx)
assert token.call().balanceOf(token.address) == 10
# Can burn own tokens
tx = token.transact({'from': account2}).burn(1)
chain.wait.for_receipt(tx)
assert token.call().balanceOf(account2) == 9
assert token.call().totalSupply() == 10 ** 9 - 1

View File

@ -0,0 +1,85 @@
import pytest
from ethereum.tester import TransactionFailed
def test_create_token(testerchain):
"""
These are tests for standard tokens taken from Consensys github:
https://github.com/ConsenSys/Tokens/
but some of the tests are converted from javascript to python
"""
with testerchain as chain:
creator = chain.web3.eth.accounts[1]
account1 = chain.web3.eth.accounts[0]
account2 = chain.web3.eth.accounts[2]
# Create an ERC20 token
token, txhash = chain.provider.get_or_deploy_contract(
'NuCypherKMSToken', deploy_args=[10 ** 9, 10 ** 10],
deploy_transaction={'from': creator})
assert txhash is not None
# Account balances
assert token.call().balanceOf(creator) == 10 ** 9
assert token.call().balanceOf(account1) == 0
# Basic properties
assert token.call().name() == 'NuCypher KMS'
assert token.call().decimals() == 18
assert token.call().symbol() == 'KMS'
# Cannot send ethers to the contract
with pytest.raises(TransactionFailed):
tx = chain.web3.eth.sendTransaction({
'from': account1, 'to': token.address, 'value': 10 ** 9})
chain.wait.for_receipt(tx)
# Can transfer tokens
tx = token.transact({'from': creator}).transfer(account1, 10000)
chain.wait.for_receipt(tx)
assert token.call().balanceOf(account1) == 10000
assert token.call().balanceOf(creator) == 10 ** 9 - 10000
tx = token.transact({'from': account1}).transfer(account2, 10)
chain.wait.for_receipt(tx)
assert token.call().balanceOf(account1) == 10000 - 10
assert token.call().balanceOf(account2) == 10
tx = token.transact({'from': account1}).transfer(token.address, 10)
chain.wait.for_receipt(tx)
assert token.call().balanceOf(token.address) == 10
# Can't mint tokens without rights
with pytest.raises(TransactionFailed):
tx = token.transact({'from': account1}).mint(account2, 10000)
chain.wait.for_receipt(tx)
# Can't change rights not from owner
with pytest.raises(TransactionFailed):
tx = token.transact({'from': account1}).addMiner(account1)
chain.wait.for_receipt(tx)
with pytest.raises(TransactionFailed):
tx = token.transact({'from': account1}).removeMiner(account1)
chain.wait.for_receipt(tx)
# Give rights for mining
tx = token.transact({'from': creator}).addMiner(account1)
chain.wait.for_receipt(tx)
assert token.call().isMiner(account1)
# And try again
tx = token.transact({'from': account1}).mint(account2, 10000)
chain.wait.for_receipt(tx)
assert token.call().balanceOf(account2) == 10010
assert token.call().totalSupply() == 10 ** 9 + 10000
# Remove rights for mining
tx = token.transact({'from': creator}).removeMiner(account1)
chain.wait.for_receipt(tx)
assert not token.call().isMiner(account1)
# Can burn own tokens
tx = token.transact({'from': account2}).burn(10000)
chain.wait.for_receipt(tx)
assert token.call().balanceOf(account2) == 10
assert token.call().totalSupply() == 10 ** 9

View File

@ -1,4 +1,4 @@
def test_chain_network(testerchain):
with testerchain as blockchain:
assert blockchain.web3.eth.blockNumber >= 0
def test_testerchain_create(testerchain):
with testerchain as chain:
assert chain.web3.eth.blockNumber >= 0

View File

@ -5,8 +5,8 @@ from nkms_eth.escrow import Escrow
from nkms_eth.token import NuCypherKMSToken
def test_create_escrow(testerchain):
token = NuCypherKMSToken(blockchain=testerchain)
def test_create_escrow(testerchain, token):
# token = NuCypherKMSToken(blockchain=testerchain)
with raises(NoKnownAddress):
Escrow.get(blockchain=testerchain, token=token)

65
tests/test_miner.py Normal file
View File

@ -0,0 +1,65 @@
import random
import pytest
M = 10 ** 6
def airdrop(blockchain, token) -> None:
"""
Airdrops from accounts[0] to others
"""
web3 = blockchain.web3
# token = Token.get(blockchain=blockchain)
def txs():
for account in web3.eth.accounts[1:]:
tx = token.contract.transact({'from': web3.eth.accounts[0]}).transfer(account, 10000*M)
yield tx
for tx in txs():
blockchain.chain.wait.for_receipt(tx, timeout=10)
def test_deposit(testerchain, miner, token):
airdrop(testerchain, token)
ursula.lock(amount=1000*M,
locktime=100,
address=testerchain.web3.eth.accounts[1])
def test_select_ursulas(testerchain, miner, escrow, token):
airdrop(testerchain, token)
# Create a random set of miners (we have 9 in total)
for u in testerchain.web3.eth.accounts[1:]:
miner.lock((10 + random.randrange(9000)) * M, 100, u)
testerchain.wait.for_block(testerchain.web3.eth.blockNumber + escrow.BLOCKS_PER_PERIOD)
miners = escrow.sample(3)
assert len(miners) == 3
assert len(set(miners)) == 3
with pytest.raises(Exception):
escrow.sample(100) # Waay more than we have deployed
def test_mine_withdraw(testerchain, miner, token, escrow):
airdrop(testerchain, token)
addr = testerchain.web3.eth.accounts[1]
initial_balance = token.balance(addr)
# Create a random set of miners (we have 9 in total)
for u in testerchain.web3.eth.accounts[1:]:
miner.lock(amount=(10 + random.randrange(9000))*M,
locktime=1,
address=u)
testerchain.chain.wait.for_block(testerchain.web3.eth.blockNumber + 2 * escrow.BLOCKS_PER_PERIOD)
miner.mine(addr)
miner.withdraw(addr)
final_balance = token.balance(addr)
assert final_balance > initial_balance

View File

@ -3,7 +3,7 @@ from populus.contracts.exceptions import NoKnownAddress
from nkms_eth.token import NuCypherKMSToken
def test_get(testerchain):
def test_get_then_create_miner(testerchain):
with raises(NoKnownAddress):
NuCypherKMSToken.get(blockchain=testerchain)

View File

View File

@ -1,73 +0,0 @@
from nkms_eth import token
from nkms_eth import escrow
from nkms_eth import ursula
import random
import pytest
M = 10 ** 6
def airdrop(chain):
"""
Airdrops from accounts[0] to others
"""
web3 = chain.web3
tok = token.get()
txs = [
tok.transact({'from': web3.eth.accounts[0]}).transfer(account, 10000 * M)
for account in web3.eth.accounts[1:]]
for tx in txs:
chain.wait.for_receipt(tx, timeout=10)
def wait_time(chain, wait_hours):
web3 = chain.web3
step = 50
end_timestamp = web3.eth.getBlock(web3.eth.blockNumber).timestamp + wait_hours * 60 * 60
while web3.eth.getBlock(web3.eth.blockNumber).timestamp < end_timestamp:
chain.wait.for_block(web3.eth.blockNumber + step)
def test_deposit(chain):
token.create()
escrow.create()
airdrop(chain)
ursula.lock(1000 * M, 100, chain.web3.eth.accounts[1])
def test_select_ursulas(chain):
token.create()
escrow.create()
airdrop(chain)
# Create a random set of miners (we have 9 in total)
for u in chain.web3.eth.accounts[1:]:
ursula.lock((10 + random.randrange(9000)) * M, 100, u)
wait_time(chain, escrow.HOURS_PER_PERIOD)
miners = escrow.sample(3)
assert len(miners) == 3
assert len(set(miners)) == 3
with pytest.raises(Exception):
escrow.sample(100) # Waay more than we have deployed
def test_mine_withdraw(chain):
token.create()
escrow.create()
airdrop(chain)
addr = chain.web3.eth.accounts[1]
initial_balance = token.balance(addr)
# Create a random set of miners (we have 9 in total)
for u in chain.web3.eth.accounts[1:]:
ursula.lock((10 + random.randrange(9000)) * M, 1, u)
wait_time(chain, 2 * escrow.HOURS_PER_PERIOD)
ursula.mine(addr)
ursula.withdraw(addr)
final_balance = token.balance(addr)
assert final_balance > initial_balance