mirror of https://github.com/nucypher/nucypher.git
[KMS-ETH]- Added calculating locked tokens in future period for findCumSum method
parent
de026cb04e
commit
4f7af1eb9d
|
@ -4,7 +4,7 @@ from nkms_eth import blockchain
|
|||
from nkms_eth import token
|
||||
|
||||
ESCROW_NAME = 'Escrow'
|
||||
MINING_COEFF = [10 ** 9, 50]
|
||||
MINING_COEFF = [10 ** 9, 50, 30]
|
||||
NULL_ADDR = '0x' + '0' * 40
|
||||
|
||||
|
||||
|
@ -40,6 +40,7 @@ def sample(n: int = 10)-> List[str]:
|
|||
escrow = get()
|
||||
n_select = int(n * 1.7) # Select more ursulas
|
||||
n_tokens = escrow.call().getAllLockedTokens()
|
||||
duration = 10
|
||||
|
||||
for _ in range(5): # number of tries
|
||||
points = [0] + sorted(random.randrange(n_tokens) for _ in
|
||||
|
@ -50,7 +51,7 @@ def sample(n: int = 10)-> List[str]:
|
|||
shift = 0
|
||||
|
||||
for delta in deltas:
|
||||
addr, shift = escrow.call().findCumSum(addr, delta + shift)
|
||||
addr, shift = escrow.call().findCumSum(addr, delta + shift, duration)
|
||||
addrs.add(addr)
|
||||
|
||||
if len(addrs) >= n:
|
||||
|
|
|
@ -27,6 +27,7 @@ contract Escrow is Miner, Ownable {
|
|||
uint256 decimals;
|
||||
uint256 lockedValue;
|
||||
uint256 releasePeriod;
|
||||
uint256 releaseRate;
|
||||
ConfirmedPeriodInfo[] confirmedPeriods;
|
||||
uint256 numberConfirmedPeriods;
|
||||
}
|
||||
|
@ -37,6 +38,7 @@ contract Escrow is Miner, Ownable {
|
|||
}
|
||||
|
||||
uint256 constant MAX_PERIODS = 100;
|
||||
uint256 constant MAX_OWNERS = 50000;
|
||||
|
||||
NuCypherKMSToken token;
|
||||
mapping (address => TokenInfo) public tokenInfo;
|
||||
|
@ -44,23 +46,27 @@ contract Escrow is Miner, Ownable {
|
|||
|
||||
uint256 public blocksPerPeriod;
|
||||
mapping (uint256 => PeriodInfo) public lockedPerPeriod;
|
||||
uint256 public releasePeriods;
|
||||
|
||||
/**
|
||||
* @notice The Escrow constructor sets address of token contract and coefficients for mining
|
||||
* @param _token Token contract
|
||||
* @param _miningCoefficient Mining coefficient
|
||||
* @param _blocksPerPeriod Size of one period in blocks
|
||||
* @param _releasePeriods Amount of periods during which tokens will be released
|
||||
**/
|
||||
function Escrow(
|
||||
NuCypherKMSToken _token,
|
||||
uint256 _miningCoefficient,
|
||||
uint256 _blocksPerPeriod
|
||||
uint256 _blocksPerPeriod,
|
||||
uint256 _releasePeriods
|
||||
)
|
||||
Miner(_token, _miningCoefficient)
|
||||
{
|
||||
require(_blocksPerPeriod != 0);
|
||||
token = _token;
|
||||
blocksPerPeriod = _blocksPerPeriod;
|
||||
releasePeriods = _releasePeriods;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,6 +77,7 @@ contract Escrow is Miner, Ownable {
|
|||
function deposit(uint256 _value, uint256 _periods) {
|
||||
require(_value != 0);
|
||||
if (!tokenOwners.valueExists(msg.sender)) {
|
||||
require(tokenOwners.sizeOf() < MAX_OWNERS);
|
||||
tokenOwners.push(msg.sender, true);
|
||||
}
|
||||
tokenInfo[msg.sender].value = tokenInfo[msg.sender].value.add(_value);
|
||||
|
@ -185,7 +192,7 @@ contract Escrow is Miner, Ownable {
|
|||
}
|
||||
}
|
||||
// checks if owner can mine more tokens (before or after release period)
|
||||
if (calculateLockedTokens(_owner, period, lockedValue) == 0) {
|
||||
if (calculateLockedTokens(_owner, period, lockedValue, 1) == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return lockedValue;
|
||||
|
@ -226,22 +233,24 @@ contract Escrow is Miner, Ownable {
|
|||
* @notice Calculate locked tokens value for owner in next period
|
||||
* @param _owner Tokens owner
|
||||
* @param _period Current or future period
|
||||
* @param _currentLockedToken Current locked tokens
|
||||
* @param _lockedTokens Locked tokens in specified period
|
||||
* @param _periods Number of periods after _period that need to calculate
|
||||
* @return Calculated locked tokens in next period
|
||||
**/
|
||||
function calculateLockedTokens(
|
||||
address _owner,
|
||||
uint256 _period,
|
||||
uint256 _currentLockedToken
|
||||
uint256 _lockedTokens,
|
||||
uint256 _periods
|
||||
)
|
||||
public constant returns (uint256)
|
||||
{
|
||||
var nextPeriod = _period + 1;
|
||||
var nextPeriod = _period + _periods;
|
||||
var info = tokenInfo[_owner];
|
||||
if (info.releasePeriod <= nextPeriod) {
|
||||
return 0;
|
||||
} else {
|
||||
return _currentLockedToken;
|
||||
return _lockedTokens;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,7 +263,8 @@ contract Escrow is Miner, Ownable {
|
|||
public constant returns (uint256)
|
||||
{
|
||||
var period = block.number.div(blocksPerPeriod);
|
||||
return calculateLockedTokens(_owner, period, getLockedTokens(_owner));
|
||||
return calculateLockedTokens(
|
||||
_owner, period, getLockedTokens(_owner), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -350,46 +360,55 @@ contract Escrow is Miner, Ownable {
|
|||
}
|
||||
|
||||
/**
|
||||
* @notice Fixedstep in cumsum
|
||||
* @notice Fixed-step in cumulative sum
|
||||
* @param _start Starting point
|
||||
* @param _delta How much to step
|
||||
* @param _periods Amount of periods to get locked tokens
|
||||
* @dev
|
||||
_start
|
||||
v
|
||||
|-------->*--------------->*---->*------------->|
|
||||
| ^
|
||||
| o_stop
|
||||
| stop
|
||||
|
|
||||
| _delta
|
||||
|---------------------------->|
|
||||
|
|
||||
| o_shift
|
||||
| shift
|
||||
| |----->|
|
||||
**/
|
||||
// _blockNumber?
|
||||
function findCumSum(address _start, uint256 _delta)
|
||||
public constant returns (address o_stop, uint256 o_shift)
|
||||
function findCumSum(address _start, uint256 _delta, uint256 _periods)
|
||||
public constant returns (address stop, uint256 shift)
|
||||
{
|
||||
var currentPeriod = block.number / blocksPerPeriod;
|
||||
require(_periods > 0);
|
||||
var currentPeriod = block.number.div(blocksPerPeriod);
|
||||
uint256 distance = 0;
|
||||
uint256 lockedTokens = 0;
|
||||
var current = _start;
|
||||
|
||||
if (current == 0x0)
|
||||
if (current == 0x0) {
|
||||
current = tokenOwners.step(current, true);
|
||||
}
|
||||
|
||||
while (current != 0x0) {
|
||||
var info = tokenInfo[current];
|
||||
var numberConfirmedPeriods = info.numberConfirmedPeriods;
|
||||
if (numberConfirmedPeriods == 0 ||
|
||||
info.confirmedPeriods[numberConfirmedPeriods - 1].period != currentPeriod &&
|
||||
(numberConfirmedPeriods == 1 ||
|
||||
info.confirmedPeriods[numberConfirmedPeriods - 2].period != currentPeriod)) {
|
||||
uint256 lockedTokens = 0;
|
||||
if (numberConfirmedPeriods > 0 &&
|
||||
info.confirmedPeriods[numberConfirmedPeriods - 1].period == currentPeriod) {
|
||||
lockedTokens = info.confirmedPeriods[numberConfirmedPeriods - 1].lockedValue;
|
||||
} else if (numberConfirmedPeriods > 1 &&
|
||||
info.confirmedPeriods[numberConfirmedPeriods - 2].period == currentPeriod) {
|
||||
lockedTokens = info.confirmedPeriods[numberConfirmedPeriods - 2].lockedValue;
|
||||
}
|
||||
if (lockedTokens == 0) {
|
||||
current = tokenOwners.step(current, true);
|
||||
continue;
|
||||
}
|
||||
lockedTokens = info.lockedValue;
|
||||
|
||||
lockedTokens = calculateLockedTokens(current, currentPeriod, lockedTokens, _periods);
|
||||
if (_delta < distance + lockedTokens) {
|
||||
o_stop = current;
|
||||
o_shift = _delta - distance;
|
||||
stop = current;
|
||||
shift = _delta - distance;
|
||||
break;
|
||||
} else {
|
||||
distance += lockedTokens;
|
||||
|
|
|
@ -50,20 +50,22 @@ contract Wallet is Ownable {
|
|||
/**
|
||||
* @notice Calculate locked tokens value in next period
|
||||
* @param _period Current or future period
|
||||
* @param _currentLockedToken Current locked tokens
|
||||
* @param _lockedTokens Locked tokens in specified period
|
||||
* @param _periods Number of periods after _period that need to calculate
|
||||
* @return Calculated locked tokens in next period
|
||||
**/
|
||||
function calculateLockedTokens(
|
||||
uint256 _period,
|
||||
uint256 _currentLockedToken
|
||||
uint256 _lockedTokens,
|
||||
uint256 _periods
|
||||
)
|
||||
public constant returns (uint256)
|
||||
{
|
||||
var nextPeriod = _period + 1;
|
||||
var nextPeriod = _period + _periods;
|
||||
if (releasePeriod <= nextPeriod) {
|
||||
return 0;
|
||||
} else {
|
||||
return _currentLockedToken;
|
||||
return _lockedTokens;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,7 +74,7 @@ contract Wallet is Ownable {
|
|||
**/
|
||||
function calculateLockedTokens() public constant returns (uint256) {
|
||||
var period = block.number.div(blocksPerPeriod);
|
||||
return calculateLockedTokens(period, getLockedTokens());
|
||||
return calculateLockedTokens(period, getLockedTokens(), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,7 +129,7 @@ contract Wallet is Ownable {
|
|||
}
|
||||
}
|
||||
// checks if owner can mine more tokens (before or after release period)
|
||||
if (calculateLockedTokens(period, lockedValueToCheck) == 0) {
|
||||
if (calculateLockedTokens(period, lockedValueToCheck, 1) == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return lockedValueToCheck;
|
||||
|
|
|
@ -13,6 +13,7 @@ import "./Wallet.sol";
|
|||
/**
|
||||
* @notice Contract creates wallets and manage mining for that wallets
|
||||
**/
|
||||
// FIXME bug with big file size. Can't deploy contract even with maximum gas if uncomment
|
||||
contract WalletManager is Miner, Ownable {
|
||||
using LinkedList for LinkedList.Data;
|
||||
using SafeERC20 for NuCypherKMSToken;
|
||||
|
@ -82,7 +83,6 @@ contract WalletManager is Miner, Ownable {
|
|||
confirmActivity(wallet.lockedValue());
|
||||
}
|
||||
|
||||
// FIXME bug with big file size. Can't deploy contract even with maximum gas if uncomment
|
||||
/**
|
||||
* @notice Terminate contract and refund to owners
|
||||
* @dev The called token contracts could try to re-enter this contract.
|
||||
|
@ -227,28 +227,29 @@ contract WalletManager is Miner, Ownable {
|
|||
}
|
||||
|
||||
/**
|
||||
* @notice Fixedstep in cumsum
|
||||
* @notice Fixed-step in cumulative sum
|
||||
* @param _start Starting point
|
||||
* @param _delta How much to step
|
||||
* @param _periods Amount of periods to get locked tokens
|
||||
* @dev
|
||||
_start
|
||||
v
|
||||
|-------->*--------------->*---->*------------->|
|
||||
| ^
|
||||
| o_stop
|
||||
| stop
|
||||
|
|
||||
| _delta
|
||||
|---------------------------->|
|
||||
|
|
||||
| o_shift
|
||||
| shift
|
||||
| |----->|
|
||||
**/
|
||||
// _blockNumber?
|
||||
function findCumSum(address _start, uint256 _delta)
|
||||
public constant returns (address o_stop, uint256 o_shift)
|
||||
function findCumSum(address _start, uint256 _delta, uint256 _periods)
|
||||
public constant returns (address stop, uint256 shift)
|
||||
{
|
||||
require(walletOwners.valueExists(_start));
|
||||
var currentPeriod = block.number / blocksPerPeriod;
|
||||
require(walletOwners.valueExists(_start) && _periods > 0);
|
||||
var currentPeriod = block.number.div(blocksPerPeriod);
|
||||
uint256 distance = 0;
|
||||
uint256 lockedTokens = 0;
|
||||
var current = _start;
|
||||
|
||||
if (current == 0x0) {
|
||||
|
@ -258,17 +259,23 @@ contract WalletManager is Miner, Ownable {
|
|||
while (current != 0x0) {
|
||||
var wallet = wallets[current];
|
||||
var numberConfirmedPeriods = wallet.numberConfirmedPeriods();
|
||||
if (numberConfirmedPeriods == 0 ||
|
||||
wallet.getConfirmedPeriod(numberConfirmedPeriods - 1) != currentPeriod &&
|
||||
(numberConfirmedPeriods == 1 ||
|
||||
wallet.getConfirmedPeriod(numberConfirmedPeriods - 2) != currentPeriod)) {
|
||||
uint256 lockedTokens = 0;
|
||||
if (numberConfirmedPeriods > 0 &&
|
||||
wallet.getConfirmedPeriod(numberConfirmedPeriods - 1) == currentPeriod) {
|
||||
lockedTokens = wallet.getConfirmedPeriodValue(numberConfirmedPeriods - 1);
|
||||
} else if (numberConfirmedPeriods > 1 &&
|
||||
wallet.getConfirmedPeriod(numberConfirmedPeriods - 2) == currentPeriod) {
|
||||
lockedTokens = wallet.getConfirmedPeriodValue(numberConfirmedPeriods - 2);
|
||||
}
|
||||
if (lockedTokens == 0) {
|
||||
current = walletOwners.step(current, true);
|
||||
continue;
|
||||
}
|
||||
lockedTokens = wallet.lockedValue();
|
||||
|
||||
lockedTokens = wallet.calculateLockedTokens(currentPeriod, lockedTokens, _periods);
|
||||
if (_delta < distance + lockedTokens) {
|
||||
o_stop = current;
|
||||
o_shift = _delta - distance;
|
||||
stop = current;
|
||||
shift = _delta - distance;
|
||||
break;
|
||||
} else {
|
||||
distance += lockedTokens;
|
||||
|
|
|
@ -17,7 +17,7 @@ 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, 10 ** 9, 50],
|
||||
'Escrow', deploy_args=[token.address, 10 ** 9, 50, 2],
|
||||
deploy_transaction={'from': creator})
|
||||
return escrow
|
||||
|
||||
|
@ -175,40 +175,55 @@ def test_escrow(web3, chain, token, escrow):
|
|||
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 web3.eth.accounts[1:]:
|
||||
for miner in miners:
|
||||
tx = token.transact({'from': creator}).transfer(miner, amount)
|
||||
chain.wait.for_receipt(tx)
|
||||
amount = amount // 2
|
||||
|
||||
# Lock
|
||||
for addr in web3.eth.accounts[1:][::-1]:
|
||||
balance = token.call().balanceOf(addr)
|
||||
tx = token.transact({'from': addr}).approve(escrow.address, balance)
|
||||
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': addr}).deposit(balance, 100)
|
||||
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
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
n_locked = escrow.call().getAllLockedTokens()
|
||||
assert n_locked > 0
|
||||
|
||||
address_stop, shift = escrow.call().findCumSum(NULL_ADDR, n_locked // 3)
|
||||
assert address_stop.lower() == web3.eth.accounts[1].lower()
|
||||
assert shift == n_locked // 3
|
||||
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)
|
||||
assert address_stop.lower() == web3.eth.accounts[2].lower()
|
||||
assert shift == 0
|
||||
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(
|
||||
web3.eth.accounts[2], largest_locked // 2 + 1)
|
||||
assert address_stop.lower() == web3.eth.accounts[3].lower()
|
||||
assert shift == 1
|
||||
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
|
||||
|
||||
for index, _ in enumerate(miners[:-1]):
|
||||
address_stop, shift = escrow.call().findCumSum(NULL_ADDR, 1, index + 2)
|
||||
assert miners[index + 1].lower() == address_stop.lower()
|
||||
assert 1 == shift
|
||||
|
||||
|
||||
def test_mining(web3, chain, token, escrow):
|
||||
|
|
|
@ -205,42 +205,58 @@ def test_escrow(web3, chain, token, wallet_manager):
|
|||
def test_locked_distribution(web3, chain, token, wallet_manager):
|
||||
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 web3.eth.accounts[1:]:
|
||||
for miner in miners:
|
||||
tx = token.transact({'from': creator}).transfer(miner, amount)
|
||||
chain.wait.for_receipt(tx)
|
||||
amount = amount // 2
|
||||
|
||||
# Lock
|
||||
for addr in web3.eth.accounts[1:][::-1]:
|
||||
balance = token.call().balanceOf(addr)
|
||||
tx = wallet_manager.transact({'from': addr}).createWallet()
|
||||
for index, miner in enumerate(miners[::-1]):
|
||||
balance = token.call().balanceOf(miner)
|
||||
tx = wallet_manager.transact({'from': miner}).createWallet()
|
||||
chain.wait.for_receipt(tx)
|
||||
wallet = wallet_manager.call().wallets(addr)
|
||||
tx = token.transact({'from': addr}).transfer(wallet, balance)
|
||||
wallet = wallet_manager.call().wallets(miner)
|
||||
tx = token.transact({'from': miner}).transfer(wallet, balance)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = wallet_manager.transact({'from': addr}).lock(balance, 100)
|
||||
tx = wallet_manager.transact({'from': miner}).deposit(balance, len(miners) - index + 1)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Check current period
|
||||
address_stop, shift = wallet_manager.call().findCumSum(NULL_ADDR, 1, 1)
|
||||
assert NULL_ADDR == address_stop.lower()
|
||||
assert 0 == shift
|
||||
|
||||
# Wait next period
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
n_locked = wallet_manager.call().getAllLockedTokens()
|
||||
assert n_locked > 0
|
||||
|
||||
address_stop, shift = wallet_manager.call().findCumSum(NULL_ADDR, n_locked // 3)
|
||||
assert address_stop.lower() == web3.eth.accounts[1].lower()
|
||||
assert shift == n_locked // 3
|
||||
address_stop, shift = wallet_manager.call().findCumSum(NULL_ADDR, n_locked // 3, 1)
|
||||
assert miners[0].lower() == address_stop.lower()
|
||||
assert n_locked // 3 == shift
|
||||
|
||||
address_stop, shift = wallet_manager.call().findCumSum(NULL_ADDR, largest_locked)
|
||||
assert address_stop.lower() == web3.eth.accounts[2].lower()
|
||||
assert shift == 0
|
||||
address_stop, shift = wallet_manager.call().findCumSum(NULL_ADDR, largest_locked, 1)
|
||||
assert miners[1].lower() == address_stop.lower()
|
||||
assert 0 == shift
|
||||
|
||||
address_stop, shift = wallet_manager.call().findCumSum(
|
||||
web3.eth.accounts[2], largest_locked // 2 + 1)
|
||||
assert address_stop.lower() == web3.eth.accounts[3].lower()
|
||||
assert shift == 1
|
||||
miners[1], largest_locked // 2 + 1, 1)
|
||||
assert miners[2].lower() == address_stop.lower()
|
||||
assert 1 == shift
|
||||
|
||||
address_stop, shift = wallet_manager.call().findCumSum(NULL_ADDR, 1, 10)
|
||||
assert NULL_ADDR == address_stop.lower()
|
||||
assert 0 == shift
|
||||
|
||||
for index, _ in enumerate(miners[:-1]):
|
||||
address_stop, shift = wallet_manager.call().findCumSum(NULL_ADDR, 1, index + 2)
|
||||
assert miners[index + 1].lower() == address_stop.lower()
|
||||
assert 1 == shift
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="too big contract")
|
||||
|
|
Loading…
Reference in New Issue