Events for worklock

pull/960/head
szotov 2019-05-23 13:23:19 +03:00
parent e6fff3973b
commit 75865a2388
4 changed files with 118 additions and 6 deletions

View File

@ -56,6 +56,7 @@ contract StakingEscrow is Issuer {
event ReStakeSet(address indexed staker, bool reStake); event ReStakeSet(address indexed staker, bool reStake);
event ReStakeLocked(address indexed staker, uint16 lockUntilPeriod); event ReStakeLocked(address indexed staker, uint16 lockUntilPeriod);
event WorkerSet(address indexed staker, address indexed worker, uint16 indexed startPeriod); event WorkerSet(address indexed staker, address indexed worker, uint16 indexed startPeriod);
event WorkMeasurementSet(address indexed miner, bool measureWork);
struct SubStakeInfo { struct SubStakeInfo {
uint16 firstPeriod; uint16 firstPeriod;
@ -388,10 +389,10 @@ contract StakingEscrow is Issuer {
**/ **/
function setWorkMeasurement(address _staker, bool _measureWork) public returns (uint256) { function setWorkMeasurement(address _staker, bool _measureWork) public returns (uint256) {
require(msg.sender == address(workLock)); require(msg.sender == address(workLock));
MinerInfo storage info = stakerInfo[_staker]; StakerInfo storage info = stakerInfo[_staker];
info.measureWork = _measureWork; info.measureWork = _measureWork;
emit WorkMeasurementSet(_staker, _measureWork);
return info.workDone; return info.workDone;
// TODO event
} }
/** @notice Set worker /** @notice Set worker

View File

@ -12,7 +12,9 @@ import "contracts/MinersEscrow.sol";
contract WorkLock { contract WorkLock {
using SafeMath for uint256; using SafeMath for uint256;
// TODO events event Bid(address indexed miner, uint256 depositedETH, uint256 claimedTokens);
event Claimed(address indexed miner, uint256 claimedTokens);
event Refund(address indexed miner, uint256 refundETH, uint256 workDone);
struct WorkInfo { struct WorkInfo {
uint256 depositedETH; uint256 depositedETH;
@ -32,10 +34,17 @@ contract WorkLock {
uint256 public maxAllowableLockedTokens; uint256 public maxAllowableLockedTokens;
uint256 public allClaimedTokens; uint256 public allClaimedTokens;
uint16 public lockedPeriods; uint16 public lockedPeriods;
// TODO add some getters
mapping(address => WorkInfo) public workInfo; mapping(address => WorkInfo) public workInfo;
// TODO docs /**
* @param _token Token contract
* @param _escrow Escrow contract
* @param _startBidDate Timestamp when bidding starts
* @param _endBidDate Timestamp when bidding will end
* @param _depositRate ETH -> NU rate
* @param _refundRate Work -> ETH rate
* @param _lockedPeriods Number of periods during which claimed tokens will be locked
**/
constructor( constructor(
NuCypherToken _token, NuCypherToken _token,
MinersEscrow _escrow, MinersEscrow _escrow,
@ -80,6 +89,7 @@ contract WorkLock {
allClaimedTokens = allClaimedTokens.add(newClaimedTokens); allClaimedTokens = allClaimedTokens.add(newClaimedTokens);
require(allClaimedTokens <= token.balanceOf(address(this)), require(allClaimedTokens <= token.balanceOf(address(this)),
"Not enough tokens in the contract"); "Not enough tokens in the contract");
emit Bid(msg.sender, msg.value, newClaimedTokens);
} }
/** /**
@ -94,6 +104,7 @@ contract WorkLock {
info.workDone = escrow.setWorkMeasurement(msg.sender, true); info.workDone = escrow.setWorkMeasurement(msg.sender, true);
token.approve(address(escrow), claimedTokens); token.approve(address(escrow), claimedTokens);
escrow.deposit(msg.sender, claimedTokens, lockedPeriods); escrow.deposit(msg.sender, claimedTokens, lockedPeriods);
emit Claimed(msg.sender, claimedTokens);
} }
/** /**
@ -114,8 +125,18 @@ contract WorkLock {
escrow.setWorkMeasurement(msg.sender, false); escrow.setWorkMeasurement(msg.sender, false);
} }
info.depositedETH = info.depositedETH.sub(refundETH); info.depositedETH = info.depositedETH.sub(refundETH);
info.workDone = info.workDone.add(refundETH.mul(refundRate)); workDone = refundETH.mul(refundRate);
info.workDone = info.workDone.add(workDone);
emit Refund(msg.sender, refundETH, workDone);
msg.sender.transfer(refundETH); msg.sender.transfer(refundETH);
} }
/**
* @notice Get remaining work to full refund
**/
function getRemainingWork(address _miner) public view returns (uint256) {
WorkInfo storage info = workInfo[_miner];
return info.depositedETH.mul(refundRate);
}
} }

View File

@ -706,6 +706,7 @@ def test_worker(testerchain, token, escrow_contract):
def test_measure_work(testerchain, token, escrow_contract): def test_measure_work(testerchain, token, escrow_contract):
escrow = escrow_contract(10000) escrow = escrow_contract(10000)
creator, ursula, *everyone_else = testerchain.interface.w3.eth.accounts creator, ursula, *everyone_else = testerchain.interface.w3.eth.accounts
work_measurement_log = escrow.events.WorkMeasurementSet.createFilter(fromBlock='latest')
# Initialize escrow contract # Initialize escrow contract
tx = token.functions.transfer(escrow.address, int(NU(10 ** 9, 'NuNit'))).transact({'from': creator}) tx = token.functions.transfer(escrow.address, int(NU(10 ** 9, 'NuNit'))).transact({'from': creator})
@ -742,6 +743,13 @@ def test_measure_work(testerchain, token, escrow_contract):
stake = escrow.functions.getAllTokens(ursula).call() stake = escrow.functions.getAllTokens(ursula).call()
tx = worklock.functions.setWorkMeasurement(ursula, True).transact() tx = worklock.functions.setWorkMeasurement(ursula, True).transact()
testerchain.wait_for_receipt(tx) testerchain.wait_for_receipt(tx)
events = work_measurement_log.get_all_entries()
assert 1 == len(events)
event_args = events[0]['args']
assert ursula == event_args['miner']
assert event_args['measureWork']
tx = escrow.functions.confirmActivity().transact({'from': ursula}) tx = escrow.functions.confirmActivity().transact({'from': ursula})
testerchain.wait_for_receipt(tx) testerchain.wait_for_receipt(tx)
testerchain.time_travel(hours=2) testerchain.time_travel(hours=2)
@ -768,6 +776,13 @@ def test_measure_work(testerchain, token, escrow_contract):
work_done = escrow.functions.getWorkDone(ursula).call() work_done = escrow.functions.getWorkDone(ursula).call()
tx = worklock.functions.setWorkMeasurement(ursula, False).transact() tx = worklock.functions.setWorkMeasurement(ursula, False).transact()
testerchain.wait_for_receipt(tx) testerchain.wait_for_receipt(tx)
events = work_measurement_log.get_all_entries()
assert 2 == len(events)
event_args = events[1]['args']
assert ursula == event_args['miner']
assert not event_args['measureWork']
tx = escrow.functions.confirmActivity().transact({'from': ursula}) tx = escrow.functions.confirmActivity().transact({'from': ursula})
testerchain.wait_for_receipt(tx) testerchain.wait_for_receipt(tx)
testerchain.time_travel(hours=2) testerchain.time_travel(hours=2)

View File

@ -58,6 +58,10 @@ def test_worklock(testerchain, token_economics):
assert worklock.functions.depositRate().call() == deposit_rate assert worklock.functions.depositRate().call() == deposit_rate
assert worklock.functions.refundRate().call() == refund_rate assert worklock.functions.refundRate().call() == refund_rate
bidding_log = worklock.events.Bid.createFilter(fromBlock='latest')
claim_log = worklock.events.Claimed.createFilter(fromBlock='latest')
refund_log = worklock.events.Refund.createFilter(fromBlock='latest')
# Transfer tokens to WorkLock # Transfer tokens to WorkLock
worklock_supply = 2 * token_economics.maximum_allowed_locked - 1 worklock_supply = 2 * token_economics.maximum_allowed_locked - 1
tx = token.functions.transfer(worklock.address, worklock_supply).transact({'from': creator}) tx = token.functions.transfer(worklock.address, worklock_supply).transact({'from': creator})
@ -107,6 +111,13 @@ def test_worklock(testerchain, token_economics):
assert worklock.functions.workInfo(ursula1).call()[0] == minimum_deposit_eth assert worklock.functions.workInfo(ursula1).call()[0] == minimum_deposit_eth
assert testerchain.interface.w3.eth.getBalance(worklock.address) == minimum_deposit_eth assert testerchain.interface.w3.eth.getBalance(worklock.address) == minimum_deposit_eth
events = bidding_log.get_all_entries()
assert 1 == len(events)
event_args = events[0]['args']
assert event_args['miner'] == ursula1
assert event_args['depositedETH'] == minimum_deposit_eth
assert event_args['claimedTokens'] == token_economics.minimum_allowed_locked
# Second Ursula does first bid # Second Ursula does first bid
assert worklock.functions.workInfo(ursula2).call()[0] == 0 assert worklock.functions.workInfo(ursula2).call()[0] == 0
tx = worklock.functions.bid().transact({'from': ursula2, 'value': maximum_deposit_eth, 'gas_price': 0}) tx = worklock.functions.bid().transact({'from': ursula2, 'value': maximum_deposit_eth, 'gas_price': 0})
@ -116,6 +127,13 @@ def test_worklock(testerchain, token_economics):
assert worklock.functions.workInfo(ursula2).call()[0] == maximum_deposit_eth assert worklock.functions.workInfo(ursula2).call()[0] == maximum_deposit_eth
assert testerchain.interface.w3.eth.getBalance(worklock.address) == maximum_deposit_eth + minimum_deposit_eth assert testerchain.interface.w3.eth.getBalance(worklock.address) == maximum_deposit_eth + minimum_deposit_eth
events = bidding_log.get_all_entries()
assert 2 == len(events)
event_args = events[1]['args']
assert event_args['miner'] == ursula2
assert event_args['depositedETH'] == maximum_deposit_eth
assert event_args['claimedTokens'] == token_economics.maximum_allowed_locked
# Can't bid again with too high ETH # Can't bid again with too high ETH
with pytest.raises((TransactionFailed, ValueError)): with pytest.raises((TransactionFailed, ValueError)):
tx = worklock.functions.bid().transact( tx = worklock.functions.bid().transact(
@ -133,6 +151,13 @@ def test_worklock(testerchain, token_economics):
assert worklock.functions.workInfo(ursula1).call()[0] == 2 * minimum_deposit_eth assert worklock.functions.workInfo(ursula1).call()[0] == 2 * minimum_deposit_eth
assert testerchain.interface.w3.eth.getBalance(worklock.address) == maximum_deposit_eth + 2 * minimum_deposit_eth assert testerchain.interface.w3.eth.getBalance(worklock.address) == maximum_deposit_eth + 2 * minimum_deposit_eth
events = bidding_log.get_all_entries()
assert 3 == len(events)
event_args = events[2]['args']
assert event_args['miner'] == ursula1
assert event_args['depositedETH'] == minimum_deposit_eth
assert event_args['claimedTokens'] == token_economics.minimum_allowed_locked
# Can't bid again: not enough tokens in worklock # Can't bid again: not enough tokens in worklock
with pytest.raises((TransactionFailed, ValueError)): with pytest.raises((TransactionFailed, ValueError)):
tx = worklock.functions.bid().transact( tx = worklock.functions.bid().transact(
@ -167,6 +192,7 @@ def test_worklock(testerchain, token_economics):
assert periods == 0 assert periods == 0
tx = worklock.functions.claim().transact({'from': ursula1, 'gas_price': 0}) tx = worklock.functions.claim().transact({'from': ursula1, 'gas_price': 0})
testerchain.wait_for_receipt(tx) testerchain.wait_for_receipt(tx)
assert worklock.functions.getRemainingWork(ursula1).call() == 2 * minimum_deposit_eth * refund_rate
value, measure_work, work_done, periods = escrow.functions.minerInfo(ursula1).call() value, measure_work, work_done, periods = escrow.functions.minerInfo(ursula1).call()
assert value == 2 * token_economics.minimum_allowed_locked assert value == 2 * token_economics.minimum_allowed_locked
assert measure_work assert measure_work
@ -175,6 +201,12 @@ def test_worklock(testerchain, token_economics):
worklock_supply - 2 * token_economics.minimum_allowed_locked worklock_supply - 2 * token_economics.minimum_allowed_locked
assert token.functions.balanceOf(escrow.address).call() == 2 * token_economics.minimum_allowed_locked assert token.functions.balanceOf(escrow.address).call() == 2 * token_economics.minimum_allowed_locked
events = claim_log.get_all_entries()
assert 1 == len(events)
event_args = events[0]['args']
assert event_args['miner'] == ursula1
assert event_args['claimedTokens'] == 2 * token_economics.minimum_allowed_locked
# Can't claim more than once # Can't claim more than once
with pytest.raises((TransactionFailed, ValueError)): with pytest.raises((TransactionFailed, ValueError)):
tx = worklock.functions.claim().transact({'from': ursula1, 'gas_price': 0}) tx = worklock.functions.claim().transact({'from': ursula1, 'gas_price': 0})
@ -185,6 +217,31 @@ def test_worklock(testerchain, token_economics):
tx = worklock.functions.refund().transact({'from': ursula1, 'gas_price': 0}) tx = worklock.functions.refund().transact({'from': ursula1, 'gas_price': 0})
testerchain.wait_for_receipt(tx) testerchain.wait_for_receipt(tx)
# Second Ursula claims tokens
value, measure_work, _work_done, periods = escrow.functions.minerInfo(ursula2).call()
assert value == 0
assert not measure_work
assert periods == 0
tx = escrow.functions.setWorkDone(ursula2, refund_rate * minimum_deposit_eth).transact()
testerchain.wait_for_receipt(tx)
tx = worklock.functions.claim().transact({'from': ursula2, 'gas_price': 0})
testerchain.wait_for_receipt(tx)
assert worklock.functions.getRemainingWork(ursula2).call() == maximum_deposit_eth * refund_rate
value, measure_work, work_done, periods = escrow.functions.minerInfo(ursula2).call()
assert value == token_economics.maximum_allowed_locked
assert measure_work
assert periods == 2 * token_economics.minimum_locked_periods
assert token.functions.balanceOf(worklock.address).call() == \
worklock_supply - 2 * token_economics.minimum_allowed_locked - token_economics.maximum_allowed_locked
assert token.functions.balanceOf(escrow.address).call() == \
2 * token_economics.minimum_allowed_locked + token_economics.maximum_allowed_locked
events = claim_log.get_all_entries()
assert 2 == len(events)
event_args = events[1]['args']
assert event_args['miner'] == ursula2
assert event_args['claimedTokens'] == token_economics.maximum_allowed_locked
# "Do" some work and partial refund # "Do" some work and partial refund
ursula1_balance = testerchain.interface.w3.eth.getBalance(ursula1) ursula1_balance = testerchain.interface.w3.eth.getBalance(ursula1)
work_done = refund_rate * minimum_deposit_eth + refund_rate // 2 work_done = refund_rate * minimum_deposit_eth + refund_rate // 2
@ -193,11 +250,19 @@ def test_worklock(testerchain, token_economics):
tx = worklock.functions.refund().transact({'from': ursula1, 'gas_price': 0}) tx = worklock.functions.refund().transact({'from': ursula1, 'gas_price': 0})
testerchain.wait_for_receipt(tx) testerchain.wait_for_receipt(tx)
assert worklock.functions.workInfo(ursula1).call()[0] == minimum_deposit_eth assert worklock.functions.workInfo(ursula1).call()[0] == minimum_deposit_eth
assert worklock.functions.getRemainingWork(ursula1).call() == minimum_deposit_eth * refund_rate
assert testerchain.interface.w3.eth.getBalance(ursula1) == ursula1_balance + minimum_deposit_eth assert testerchain.interface.w3.eth.getBalance(ursula1) == ursula1_balance + minimum_deposit_eth
assert testerchain.interface.w3.eth.getBalance(worklock.address) == maximum_deposit_eth + minimum_deposit_eth assert testerchain.interface.w3.eth.getBalance(worklock.address) == maximum_deposit_eth + minimum_deposit_eth
_value, measure_work, _work_done, _periods = escrow.functions.minerInfo(ursula1).call() _value, measure_work, _work_done, _periods = escrow.functions.minerInfo(ursula1).call()
assert measure_work assert measure_work
events = refund_log.get_all_entries()
assert 1 == len(events)
event_args = events[0]['args']
assert event_args['miner'] == ursula1
assert event_args['refundETH'] == minimum_deposit_eth
assert event_args['workDone'] == minimum_deposit_eth * refund_rate
# "Do" more work and full refund # "Do" more work and full refund
ursula1_balance = testerchain.interface.w3.eth.getBalance(ursula1) ursula1_balance = testerchain.interface.w3.eth.getBalance(ursula1)
work_done = refund_rate * 2 * minimum_deposit_eth work_done = refund_rate * 2 * minimum_deposit_eth
@ -206,12 +271,22 @@ def test_worklock(testerchain, token_economics):
tx = worklock.functions.refund().transact({'from': ursula1, 'gas_price': 0}) tx = worklock.functions.refund().transact({'from': ursula1, 'gas_price': 0})
testerchain.wait_for_receipt(tx) testerchain.wait_for_receipt(tx)
assert worklock.functions.workInfo(ursula1).call()[0] == 0 assert worklock.functions.workInfo(ursula1).call()[0] == 0
assert worklock.functions.getRemainingWork(ursula1).call() == 0
assert testerchain.interface.w3.eth.getBalance(ursula1) == ursula1_balance + minimum_deposit_eth assert testerchain.interface.w3.eth.getBalance(ursula1) == ursula1_balance + minimum_deposit_eth
assert testerchain.interface.w3.eth.getBalance(worklock.address) == maximum_deposit_eth assert testerchain.interface.w3.eth.getBalance(worklock.address) == maximum_deposit_eth
_value, measure_work, _work_done, _periods = escrow.functions.minerInfo(ursula1).call() _value, measure_work, _work_done, _periods = escrow.functions.minerInfo(ursula1).call()
assert not measure_work assert not measure_work
events = refund_log.get_all_entries()
assert 2 == len(events)
event_args = events[1]['args']
assert event_args['miner'] == ursula1
assert event_args['refundETH'] == minimum_deposit_eth
assert event_args['workDone'] == minimum_deposit_eth * refund_rate
# Can't refund more tokens # Can't refund more tokens
tx = escrow.functions.setWorkDone(ursula1, 2 * work_done).transact()
testerchain.wait_for_receipt(tx)
with pytest.raises((TransactionFailed, ValueError)): with pytest.raises((TransactionFailed, ValueError)):
tx = worklock.functions.refund().transact({'from': ursula1, 'gas_price': 0}) tx = worklock.functions.refund().transact({'from': ursula1, 'gas_price': 0})
testerchain.wait_for_receipt(tx) testerchain.wait_for_receipt(tx)