diff --git a/.circleci/config.yml b/.circleci/config.yml index b141767ab..79bd43344 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -523,7 +523,7 @@ jobs: at: ~/.local/share/virtualenvs/ - run: name: Install Documentation Build Dependencies - command: pip3 install --user sphinx recommonmark sphinx-rtd-theme + command: pip3 install --user sphinx recommonmark sphinx-rtd-theme aafigure - run: name: Build Sphinx Documentation command: | diff --git a/Pipfile b/Pipfile index d9980b560..1c70ea605 100644 --- a/Pipfile +++ b/Pipfile @@ -54,6 +54,7 @@ bumpversion = "*" sphinx = "*" recommonmark = "*" sphinx_rtd_theme = "*" +aafigure = "*" # CLI nucypher = {editable = true,path = "."} diff --git a/docs/source/architecture/contracts.md b/docs/source/architecture/contracts.md index c656a57d4..57346df02 100644 --- a/docs/source/architecture/contracts.md +++ b/docs/source/architecture/contracts.md @@ -6,8 +6,9 @@ * `NuCypherToken` ERC20 token contract * `MinersEscrow` Holds Ursula's stake, stores information about Ursula's activity, and assigns a reward for participating in the NuCypher network. (The `Issuer` contract is part of the `MinersEscrow`) * `PolicyManager` Holds a policy's fee and distributes fee by periods +* `MiningAdjudicator` Manages [the slashing protocol](slashing) * `Upgradeable` Base contract for [upgrading](upgradeable_proxy_contracts) -* `Dispatcher` Proxy to other contracts. This provides upgrading of the `MinersEscrow` and `PolicyManager` contracts +* `Dispatcher` Proxy to other contracts and provides upgrading of the `MinersEscrow`, `PolicyManager` and `MiningAdjudicator` contracts * `UserEscrow` Locks tokens for predetermined time. Tokens will be unlocked after specified time and all tokens can be used as stake in the `MinersEscrow` contract ## Deployment Procedure @@ -15,19 +16,19 @@ 1. Deploy `NuCypherToken` with all future supply tokens 2. Deploy `MinersEscrow` with a dispatcher targeting it 3. Deploy `PolicyManager` with its own dispatcher, also targeting it -4. Transfer reward tokens to the `MinersEscrow` contract. These tokens are future mining rewards, and initial allocations -5. Run the `initialize()` method to initialize the `MinersEscrow` contract -6. Set the address of the `PolicyManager` contract in the `MinersEscrow` by using the `setPolicyManager(address)` -7. Pre-deposit tokens to the `MinersEscrow` if necessary: +4. Deploy `MiningAdjudicator` with a dispatcher +5. Transfer reward tokens to the `MinersEscrow` contract. These tokens are future mining rewards and initial allocations +6. Run the `initialize()` method to initialize the `MinersEscrow` contract +7. Set the address of the `PolicyManager` contract in the `MinersEscrow` by using the `setPolicyManager(address)` +8. Pre-deposit tokens to the `MinersEscrow` if necessary: * Approve the transfer tokens for the `MinersEscrow` contract using the `approve(address, uint)` method. The parameters are the address of `MinersEscrow` and the amount of tokens for a miner or group of miners; - * Deposit tokens to the `MinersEscrow` contract using the `preDeposit(address[], uint[], uint[])` method. The parameters are the addresses of the miners, the amount of tokens for each miner and the periods during which tokens will be locked for each miner -8. Deploy `UserEscrowProxy` with `UserEscrowLibraryLinker` targeting it -9. Pre-deposit tokens to the `UserEscrow`, and if necessary: - -* Create new instance of the `UserEscrow` contract -* Transfer ownership of the instance of the `UserEscrow` contract to the user -* Approve the transfer of tokens for the `UserEscrow` -* Deposit tokens by the `initialDeposit(uint256, uint256)` method + * Deposit tokens to the `MinersEscrow` contract using the `preDeposit(address[], uint[], uint[])` method. The parameters are the addresses of the miners, the amount of tokens for each miner, and the number of periods during which tokens will be locked for each miner +9. Deploy `UserEscrowProxy` with `UserEscrowLibraryLinker` targeting it +10. Pre-deposit tokens to the `UserEscrow` and, if necessary: + * Create new instance of the `UserEscrow` contract + * Transfer ownership of the instance of the `UserEscrow` contract to the user + * Approve the transfer of tokens for the `UserEscrow` + * Deposit tokens by the `initialDeposit(uint256, uint256)` method ## Alice's Contract Interaction diff --git a/docs/source/architecture/slashing.rst b/docs/source/architecture/slashing.rst new file mode 100644 index 000000000..55d471e44 --- /dev/null +++ b/docs/source/architecture/slashing.rst @@ -0,0 +1,150 @@ +The Slashing Protocol +===================== + +TBD + + +Violations +---------- + +TBD + + +Calculating the slashing penalty +-------------------------------- + +TBD (https://github.com/nucypher/nucypher/issues/803) + + +How slashing affects stake +-------------------------- + +The goal of slashing is to reduce the number of tokens that belongs to a staking offender. +In this case, the main task is not to violate the logic of locking tokens. +The entire stake consists of: + + * tokens which the staker can withdraw at any moment + * tokens locked for a specific period + +A staker may extend the unlock period for any number of portions of their total stake. This divides the stake into smaller parts, each with a unique unlock date in the future. Stakers may also acquire and lock new tokens. The total stake is represented as the sum of all the different sub-stakes active in a given cycle (new cycle every 24h), which includes locked sub-stakes, and any sub-stakes that have passed their unlock date, and can be freely withdrawn. Each sub-stake has a beginning and duration (lock time). When a staker confirms activity each day, the remaining lock time for relevant sub-stakes is reduced. + +Sub stakes get slashed in the order of their remaining lock time, beginning with the shortest – so the first portion of the stake to be slashed is the unlocked portion. After that, if necessary, locked sub-stakes are decreased – the shortest sub stake is decreased by the required amount; if the adjustment of that sub-stake is insufficient to fulfil the required punishment sum, then the next shortest sub-stake is decreased, and so on. Sub-stakes that begin in the next period are checked separately. + +**Example:** + + A staker has 1000 tokens: + * 1st sub stake = 500 tokens locked for 10 periods + * 2nd sub stake = 200 tokens for 2 periods + * 3rd sub stake = 100 tokens locked starting from the next period and locked for 5 periods. The 3rd sub stake is locked for the next period but has not yet been used as a deposit for "work" - not until the next period begins. + * 200 tokens in an unlocked state (still staked, but can be freely withdrawn). + + .. aafig:: + :proportional: + :textual: + + stake + ^ + | + 800| +----+ + | | 3rd| + 700+-----+----+ + 600| +-------------+ + | 2nd | 3rd | + 500+----------+-------------+----------+ + | | + | 1st | + | | period + +-----------------------------------+---> + + +Penalty Scenarios: + +* *Scenario 1*: Staker incurs penalty calculated to be worth **100 tokens**: + + Only the unlocked tokens will be reduced; from 200 to 100. The values of locked sub-stakes will therefore remain unchanged in this punishment scenario. + + Result: + + * 1st sub stake = 500 tokens locked for 10 periods + * 2nd sub stake = 200 tokens for 2 periods + * 3rd sub stake = 100 tokens locked starting from the next period + * 100 tokens in an unlocked state + +* *Scenario 2*: Staker incurs penalty calculated to be worth **300 tokens**: + + The unlocked tokens can only cover 200 tokens. In the current period, 700 tokens are locked and 800 tokens are locked for the next period. Therefore, we should reduce amount of locked tokens for the next period and leave unchanged locked amount in the current period. The 3rd sub stake suits for this purpose but it's not the shortest one. So we take the 2nd sub stake (the shortest), reduce it to 100 tokens and add new sub stake with 100 tokens which active only in the current period. + + Result: + + * 1st sub stake = 500 tokens locked for 10 periods + * 2nd sub stake = 100 tokens for 2 periods + * 3rd sub stake = 100 tokens locked starting from the next period for 5 periods + * 4rd sub stake = 100 tokens for 1 period + * Remaining 0 tokens + + .. aafig:: + :proportional: + :textual: + + stake + ^ + | + 700+-----+----+ + | 4th | 3rd| + 600+-----+----+-------------+ + | 2nd | 3rd | + 500+----------+-------------+----------+ + | | + | 1st | + | | period + +-----------------------------------+---> + +* *Scenario 3*: Staker incurs penalty calculated to be worth **400 tokens**: + + The difference from the previous scenario is that should also decrease locked tokens in the current period. At the first step the 2nd sub stake is reduced to 100 tokens. Next step - adjustment for the next period. The shortest sub stake still the same - the 2nd. And we need to deacrese it from 100 to 0 only for the next period. Will be the same if we change duration of the 2nd sub stake from 2 periods to 1 and the other sub stakes remain unchanged. + + Result: + + * 1st sub stake = 500 tokens locked for 10 periods + * 2nd sub stake = 100 tokens for 1 period + * 3rd sub stake = 100 tokens locked starting from the next period + * Remaining 0 tokens + + .. aafig:: + :proportional: + :textual: + + stake + ^ + | + 600+----------+-------------+ + | 2nd | 3rd | + 500+----------+-------------+----------+ + | | + | 1st | + | | period + +-----------------------------------+---> + +* *Scenario 4*: Staker incurs penalty calculated to be worth **600 tokens**: + + Reducing the unlocked remaining tokens, 3rd sub stakes, and the shortest sub stake (2nd) is not enough, so they are all removed. The next shortest sub stake is the 1st which is reduced from 500 to 400. + + Result: + + * 1st sub stake = 400 tokens locked for 10 periods + * 2nd sub stake = 0 tokens for 2 periods + * 3rd sub stake = 0 tokens locked starting from the next period + * Remaining 0 tokens + + .. aafig:: + :proportional: + :textual: + + stake + ^ + | + 400+-----------------------------------+ + | | + | 1st | + | | period + +-----------------------------------+---> diff --git a/docs/source/conf.py b/docs/source/conf.py index a2fe0faee..14e7c1453 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -51,6 +51,7 @@ extensions = [ 'sphinx.ext.napoleon', 'sphinx.ext.mathjax', 'sphinx.ext.viewcode', + 'aafigure.sphinxext', ] # Add any paths that contain templates here, relative to this directory. diff --git a/docs/source/guides/contribution_guide.rst b/docs/source/guides/contribution_guide.rst index 61d188356..92a13dfce 100644 --- a/docs/source/guides/contribution_guide.rst +++ b/docs/source/guides/contribution_guide.rst @@ -171,7 +171,7 @@ Building Documentation .. note:: - ``sphinx``, ``recommonmark``, and ``sphinx_rtd_theme`` are non-standard dependencies that can be installed + ``sphinx``, ``recommonmark``, ``aafigure`` and ``sphinx_rtd_theme`` are non-standard dependencies that can be installed by running ``pip install -e .[docs]`` from the project directory. diff --git a/docs/source/index.rst b/docs/source/index.rst index 3dc57fa31..9d660dada 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -120,6 +120,7 @@ Whitepapers architecture/contracts architecture/upgradeable_proxy_contracts + architecture/slashing .. toctree:: :maxdepth: 1 diff --git a/setup.py b/setup.py index a9b9cbf14..1f09cd821 100644 --- a/setup.py +++ b/setup.py @@ -92,7 +92,10 @@ DEPLOY_REQUIRES = [ DOCS_REQUIRE = [ 'sphinx', - 'sphinx-autobuild' + 'sphinx-autobuild', + 'recommonmark', + 'aafigure', + 'sphinx_rtd_theme' ] BENCHMARKS_REQUIRE = [